1 // license:BSD-3-Clause
2 // copyright-holders:Olivier Galibert
3 /*
4    GI SP0250 digital LPC sound synthesizer
5 
6    By O. Galibert.
7 
8    Unimplemented:
9    - Direct Data test mode (pin 7)
10 */
11 
12 #include "emu.h"
13 #include "sp0250.h"
14 
15 //
16 // Input clock is divided by 2 to make ROMCLOCK.
17 // Output is via pulse-width modulation (PWM) over the course of 39 ROMCLOCKs.
18 // 4 PWM periods per frame.
19 //
20 static constexpr int PWM_CLOCKS = 39;
21 
22 
23 DEFINE_DEVICE_TYPE(SP0250, sp0250_device, "sp0250", "GI SP0250 LPC")
24 
sp0250_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)25 sp0250_device::sp0250_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
26 	device_t(mconfig, SP0250, tag, owner, clock),
27 	device_sound_interface(mconfig, *this),
28 	m_pwm_mode(false),
29 	m_pwm_index(PWM_CLOCKS),
30 	m_pwm_count(0),
31 	m_pwm_counts(0),
32 	m_voiced(0),
33 	m_amp(0),
34 	m_lfsr(0x7fff),
35 	m_pitch(0),
36 	m_pcount(0),
37 	m_repeat(0),
38 	m_rcount(0),
39 	m_fifo_pos(0),
40 	m_stream(nullptr),
41 	m_drq(*this)
42 {
43 	for (auto & elem : m_fifo)
44 	{
45 		elem = 0;
46 	}
47 
48 	for (auto & elem : m_filter)
49 	{
50 		elem.F = 0;
51 		elem.B = 0;
52 		elem.z1 = 0;
53 		elem.z2 = 0;
54 	}
55 }
56 
57 //-------------------------------------------------
58 //  device_start - device-specific startup
59 //-------------------------------------------------
60 
device_start()61 void sp0250_device::device_start()
62 {
63 	// output PWM data at the ROMCLOCK frequency
64 	int sample_rate = clock() / 2;
65 	int frame_rate = sample_rate / (4 * PWM_CLOCKS);
66 	m_stream = stream_alloc(0, 1, m_pwm_mode ? sample_rate : frame_rate);
67 
68 	// if a DRQ callback is offered, run a timer at the frame rate
69 	// to ensure the DRQ gets picked up in a timely manner
70 	m_drq.resolve_safe();
71 	if (!m_drq.isnull())
72 	{
73 		m_drq(ASSERT_LINE);
74 		attotime period = attotime::from_hz(frame_rate);
75 		timer_alloc()->adjust(period, 0, period);
76 	}
77 
78 	// PWM state
79 	save_item(NAME(m_pwm_index));
80 	save_item(NAME(m_pwm_count));
81 	save_item(NAME(m_pwm_counts));
82 
83 	// LPC state
84 	save_item(NAME(m_voiced));
85 	save_item(NAME(m_amp));
86 	save_item(NAME(m_lfsr));
87 	save_item(NAME(m_pitch));
88 	save_item(NAME(m_pcount));
89 	save_item(NAME(m_repeat));
90 	save_item(NAME(m_rcount));
91 
92 	save_item(STRUCT_MEMBER(m_filter, F));
93 	save_item(STRUCT_MEMBER(m_filter, B));
94 	save_item(STRUCT_MEMBER(m_filter, z1));
95 	save_item(STRUCT_MEMBER(m_filter, z2));
96 
97 	// FIFO state
98 	save_item(NAME(m_fifo));
99 	save_item(NAME(m_fifo_pos));
100 }
101 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)102 void sp0250_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
103 {
104 	m_stream->update();
105 }
106 
sp0250_ga(uint8_t v)107 static uint16_t sp0250_ga(uint8_t v)
108 {
109 	return (v & 0x1f) << (v>>5);
110 }
111 
sp0250_gc(uint8_t v)112 static int16_t sp0250_gc(uint8_t v)
113 {
114 	// Internal ROM to the chip, cf. manual
115 	static const uint16_t coefs[128] =
116 	{
117 		  0,   9,  17,  25,  33,  41,  49,  57,  65,  73,  81,  89,  97, 105, 113, 121,
118 		129, 137, 145, 153, 161, 169, 177, 185, 193, 201, 203, 217, 225, 233, 241, 249,
119 		257, 265, 273, 281, 289, 297, 301, 305, 309, 313, 317, 321, 325, 329, 333, 337,
120 		341, 345, 349, 353, 357, 361, 365, 369, 373, 377, 381, 385, 389, 393, 397, 401,
121 		405, 409, 413, 417, 421, 425, 427, 429, 431, 433, 435, 437, 439, 441, 443, 445,
122 		447, 449, 451, 453, 455, 457, 459, 461, 463, 465, 467, 469, 471, 473, 475, 477,
123 		479, 481, 482, 483, 484, 485, 486, 487, 488, 489, 490, 491, 492, 493, 494, 495,
124 		496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511
125 	};
126 	int16_t res = coefs[v & 0x7f];
127 
128 	if (!(v & 0x80))
129 		res = -res;
130 	return res;
131 }
132 
load_values()133 void sp0250_device::load_values()
134 {
135 	m_filter[0].B = sp0250_gc(m_fifo[ 0]);
136 	m_filter[0].F = sp0250_gc(m_fifo[ 1]);
137 	m_amp         = sp0250_ga(m_fifo[ 2]);
138 	m_filter[1].B = sp0250_gc(m_fifo[ 3]);
139 	m_filter[1].F = sp0250_gc(m_fifo[ 4]);
140 	m_pitch       =           m_fifo[ 5];
141 	m_filter[2].B = sp0250_gc(m_fifo[ 6]);
142 	m_filter[2].F = sp0250_gc(m_fifo[ 7]);
143 	m_repeat      =           m_fifo[ 8] & 0x3f;
144 	m_voiced      =           m_fifo[ 8] & 0x40;
145 	m_filter[3].B = sp0250_gc(m_fifo[ 9]);
146 	m_filter[3].F = sp0250_gc(m_fifo[10]);
147 	m_filter[4].B = sp0250_gc(m_fifo[11]);
148 	m_filter[4].F = sp0250_gc(m_fifo[12]);
149 	m_filter[5].B = sp0250_gc(m_fifo[13]);
150 	m_filter[5].F = sp0250_gc(m_fifo[14]);
151 	m_fifo_pos = 0;
152 	m_drq(ASSERT_LINE);
153 	m_pcount = 0;
154 	m_rcount = 0;
155 
156 	for (int f = 0; f < 6; f++)
157 		m_filter[f].reset();
158 }
159 
write(uint8_t data)160 void sp0250_device::write(uint8_t data)
161 {
162 	m_stream->update();
163 	if (m_fifo_pos != 15)
164 	{
165 		m_fifo[m_fifo_pos++] = data;
166 		if (m_fifo_pos == 15)
167 			m_drq(CLEAR_LINE);
168 	}
169 	else
170 		logerror("%s: overflow SP0250 FIFO\n", machine().describe_context());
171 }
172 
173 
drq_r()174 uint8_t sp0250_device::drq_r()
175 {
176 	m_stream->update();
177 	return (m_fifo_pos == 15) ? CLEAR_LINE : ASSERT_LINE;
178 }
179 
next()180 int8_t sp0250_device::next()
181 {
182 	if (m_rcount >= m_repeat)
183 	{
184 		if (m_fifo_pos == 15)
185 			load_values();
186 		else
187 		{
188 			// According to http://www.cpcwiki.eu/index.php/SP0256_Measured_Timings
189 			// the SP0250 executes "NOPs" with a repeat count of 1 and unchanged
190 			// pitch while waiting for input
191 			m_repeat = 1;
192 			m_pcount = 0;
193 			m_rcount = 0;
194 		}
195 	}
196 
197 	// 15-bit LFSR algorithm verified by dump from actual hardware
198 	// clocks every cycle regardless of voiced/unvoiced setting
199 	m_lfsr ^= (m_lfsr ^ (m_lfsr >> 1)) << 15;
200 	m_lfsr >>= 1;
201 
202 	int16_t z0;
203 	if (m_voiced)
204 		z0 = (m_pcount == 0) ? m_amp : 0;
205 	else
206 		z0 = (m_lfsr & 1) ? m_amp : -m_amp;
207 
208 	for (int f = 0; f < 6; f++)
209 		z0 = m_filter[f].apply(z0);
210 
211 	// maximum amp value is effectively 13 bits
212 	// reduce to 7 bits; due to filter effects it
213 	// may occasionally clip
214 	int dac = z0 >> 6;
215 	if (dac < -64)
216 		dac = -64;
217 	if (dac > 63)
218 		dac = 63;
219 
220 	// PWM is divided into 4x 5-bit sections; the lower
221 	// bits of the original 7-bit value are added to only
222 	// some of the pulses in the following pattern:
223 	//
224 	//    DAC -64 -> 1,1,1,1
225 	//    DAC -63 -> 2,1,1,1
226 	//    DAC -62 -> 2,1,2,1
227 	//    DAC -61 -> 2,2,2,1
228 	//    DAC -60 -> 2,2,2,2
229 	//    ...
230 	//    DAC  -1 -> 17,17,17,16
231 	//    DAC   0 -> 17,17,17,17
232 	//    DAC   1 -> 18,17,17,17
233 	//    ...
234 	//    DAC  60 -> 32,32,32,32
235 	//    DAC  61 -> 33,32,32,32
236 	//    DAC  62 -> 33,32,33,32
237 	//    DAC  63 -> 33,33,33,32
238 	m_pwm_counts = (((dac + 68 + 3) >> 2) << 0) +
239 				   (((dac + 68 + 1) >> 2) << 8) +
240 				   (((dac + 68 + 2) >> 2) << 16) +
241 				   (((dac + 68 + 0) >> 2) << 24);
242 
243 	if (m_pcount++ == m_pitch)
244 	{
245 		m_pcount = 0;
246 		m_rcount++;
247 	}
248 	return dac;
249 }
250 
251 //-------------------------------------------------
252 //  sound_stream_update - handle a stream update
253 //-------------------------------------------------
254 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)255 void sp0250_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
256 {
257 	auto &output = outputs[0];
258 
259 	if (!m_pwm_mode)
260 	{
261 		for (int sampindex = 0; sampindex < output.samples(); sampindex++)
262 			output.put_int(sampindex, next(), 128);
263 	}
264 	else
265 	{
266 		for (int sampindex = 0; sampindex < output.samples(); )
267 		{
268 			// see where we're at in the current PWM cycle
269 			if (m_pwm_index >= PWM_CLOCKS)
270 			{
271 				m_pwm_index = 0;
272 				if (m_pwm_counts == 0)
273 					next();
274 				m_pwm_count = m_pwm_counts & 0xff;
275 				m_pwm_counts >>= 8;
276 			}
277 
278 			// determine the value to fill and the number of samples remaining
279 			// until it changes
280 			stream_buffer::sample_t value;
281 			int remaining;
282 			if (m_pwm_index < m_pwm_count)
283 			{
284 				value = 1.0;
285 				remaining = m_pwm_count - m_pwm_index;
286 			}
287 			else
288 			{
289 				value = 0.0;
290 				remaining = PWM_CLOCKS - m_pwm_index;
291 			}
292 
293 			// clamp to the number of samples requested and advance the counters
294 			if (remaining > output.samples() - sampindex)
295 				remaining = output.samples() - sampindex;
296 			m_pwm_index += remaining;
297 
298 			// fill the output
299 			while (remaining-- != 0)
300 				outputs[0].put(sampindex++, value);
301 		}
302 	}
303 }
304