1 // license:BSD-3-Clause
2 // copyright-holders:hap
3 /***************************************************************************
4
5 tc8830f.c - Toshiba TC8830F, CMOS voice recording/reproducing LSI
6 1-bit ADM (Adaptive Delta Modulation), similar to TC8801 and T6668.
7
8 Very preliminary...
9
10 TODO:
11 - improve ADM decoder
12 - remaining commands
13 - manual control
14 - chip read
15 - RAM
16 - recording
17
18 ***************************************************************************/
19
20 #include "emu.h"
21 #include "tc8830f.h"
22
23
24 // device type definition
25 DEFINE_DEVICE_TYPE(TC8830F, tc8830f_device, "tc8830f", "Toshiba TC8830F")
26
tc8830f_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)27 tc8830f_device::tc8830f_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
28 : device_t(mconfig, TC8830F, tag, owner, clock),
29 device_sound_interface(mconfig, *this), m_stream(nullptr),
30 m_playing(false),
31 m_address(0),
32 m_stop_address(0),
33 m_bitcount(0),
34 m_bitrate(0),
35 m_prevbits(0),
36 m_delta(1),
37 m_output(0),
38 m_command(0),
39 m_cmd_rw(0),
40 m_phrase(0),
41 m_mem(*this, DEVICE_SELF)
42 {
43 }
44
45
device_start()46 void tc8830f_device::device_start()
47 {
48 // create the stream
49 m_stream = stream_alloc(0, 1, clock() / 0x10);
50
51 // register for savestates
52 save_item(NAME(m_playing));
53 save_item(NAME(m_address));
54 save_item(NAME(m_stop_address));
55 save_item(NAME(m_bitcount));
56 save_item(NAME(m_bitrate));
57 save_item(NAME(m_prevbits));
58 save_item(NAME(m_delta));
59 save_item(NAME(m_output));
60 save_item(NAME(m_command));
61 save_item(NAME(m_cmd_rw));
62 save_item(NAME(m_phrase));
63
64 reset();
65 }
66
67
device_post_load()68 void tc8830f_device::device_post_load()
69 {
70 device_clock_changed();
71 }
72
73
device_clock_changed()74 void tc8830f_device::device_clock_changed()
75 {
76 int divisor = 0x10 * (4 - (m_bitrate & 3));
77 m_stream->set_sample_rate(clock() / divisor);
78 }
79
80
81
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)82 void tc8830f_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
83 {
84 int32_t mix = 0;
85
86 for (int i = 0; i < outputs[0].samples(); i++)
87 {
88 if (m_playing)
89 {
90 // get bit
91 int bit = m_mem[m_address] >> m_bitcount & 1;
92 m_bitcount = (m_bitcount + 1) & 7;
93 if (m_bitcount == 0)
94 {
95 m_address = (m_address + 1) & m_mem.mask();
96 if (m_address == m_stop_address)
97 m_playing = false;
98 }
99
100 // compute sample, ADM decoding
101 // if previous bits are 111 or 000, delta increases exponentially
102 // otherwise, delta decreases linearly
103 if ((m_prevbits & 7) == 7 || (m_prevbits & 7) == 0)
104 {
105 if (m_delta < 0x2000)
106 m_delta <<= 1;
107 }
108 else
109 m_delta -= 8;
110 if (m_delta <= 0)
111 m_delta = 1;
112
113 // determine direction
114 if (bit)
115 m_output += m_delta;
116 else
117 m_output -= m_delta;
118
119 if (m_output > 32767)
120 m_output = 32767;
121 else if (m_output < -32768)
122 m_output = -32768;
123
124 m_prevbits = m_prevbits << 1 | bit;
125 mix = m_output;
126 }
127
128 outputs[0].put_int(i, mix, 32768);
129 }
130 }
131
132
reset()133 void tc8830f_device::reset()
134 {
135 m_stream->update();
136
137 m_playing = false;
138 m_address = 0x100;
139 m_bitcount = 0;
140 m_prevbits = 0;
141 m_delta = 1;
142 m_output = 0;
143 m_cmd_rw = 0;
144
145 // in cpu control, enter play mode and reset bitrate
146 write_p(1);
147 write_p(6);
148 write_p(0);
149 }
150
151
write_p(uint8_t data)152 void tc8830f_device::write_p(uint8_t data)
153 {
154 m_stream->update();
155 data &= 0xf;
156
157 if (m_cmd_rw == 0)
158 {
159 // select command
160 m_command = data;
161 switch (m_command)
162 {
163 // immediate commands
164 case 0x1:
165 break;
166
167 case 0x2:
168 m_playing = true;
169 break;
170
171 case 0x3:
172 m_playing = false;
173 break;
174
175 // multi-nibble commands
176 case 0x4: case 0x5: case 0x6: case 0x7:
177 m_cmd_rw = 1;
178 break;
179
180 case 0x8: case 0x9: case 0xa: case 0xb:
181 logerror("tc8830f: Unemulated command %X\n", m_command);
182 break;
183
184 default:
185 logerror("tc8830f: Invalid command %X\n", m_command);
186 break;
187 }
188 }
189
190 else
191 {
192 // write command
193 switch (m_command)
194 {
195 case 0x4:
196 // ADLD1: set address counter
197 m_address = (m_address & ~(0xf << (m_cmd_rw*4))) | (data << (m_cmd_rw*4));
198 if (m_cmd_rw == 5)
199 {
200 m_address &= m_mem.mask();
201 m_bitcount = 0;
202 m_cmd_rw = -1;
203 }
204 break;
205
206 case 0x5:
207 // ADLD2: set address stop
208 m_stop_address = (m_stop_address & ~(0xf << (m_cmd_rw*4))) | (data << (m_cmd_rw*4));
209 if (m_cmd_rw == 5)
210 {
211 m_stop_address &= m_mem.mask();
212 m_cmd_rw = -1;
213 }
214 break;
215
216 case 0x6:
217 // CNDT: d0-d1: bitrate, d2: enable overflow
218 m_bitrate = data & 3;
219 device_clock_changed();
220 m_cmd_rw = -1;
221 break;
222
223 case 0x7:
224 // LABEL: set phrase
225 if (m_cmd_rw == 1)
226 {
227 m_phrase = (m_phrase & 0x30) | data;
228 }
229 else
230 {
231 m_phrase = (m_phrase & 0x0f) | (data << 4 & 0x30);
232
233 // update addresses and start
234 uint8_t offs = m_phrase * 4;
235 m_address = (m_mem[offs] | m_mem[offs|1]<<8 | m_mem[offs|2]<<16) & m_mem.mask();
236 offs += 4;
237 m_stop_address = (m_mem[offs] | m_mem[offs|1]<<8 | m_mem[offs|2]<<16) & m_mem.mask();
238
239 m_bitcount = 0;
240 m_prevbits = 0;
241 m_delta = 1;
242 m_output = 0;
243 m_playing = true;
244 m_cmd_rw = -1;
245 }
246 break;
247
248 default:
249 m_cmd_rw = -1;
250 break;
251 }
252 m_cmd_rw++;
253 }
254 }
255