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