1 // license:BSD-3-Clause
2 // copyright-holders:Juergen Buchmueller, Derrick Renaud
3 /****************************************************************************
4 *
5 * Phoenix sound hardware simulation - still very ALPHA!
6 *
7 * If you find errors or have suggestions, please mail me.
8 * Juergen Buchmueller <pullmoll@t-online.de>
9 *
10 ****************************************************************************/
11
12
13 #include "emu.h"
14 #include "audio/phoenix.h"
15
16 /****************************************************************************
17 * 4006
18 * Dual 4-bit and dual 5-bit serial-in serial-out shift registers.
19 *
20 * +----------+
21 * 1D5 |1 +--+ 14| VCC
22 * /1Q4 |2 13| 1Q1
23 * CLK |3 12| 2Q0
24 * 2D4 |4 4006 11| 2Q0
25 * 3D4 |5 10| 3Q0
26 * 4D5 |6 9| 4Q0
27 * GND |7 8| 4Q1
28 * +----------+
29 *
30 * [This information is part of the GIICM]
31 *
32 * Pin 8 and 9 are connected to an EXOR gate and the inverted
33 * output (EXNOR) is fed back to pin 1 (and the pseudo polynomial output).
34 *
35 * 1D5 1Q1 2D4 2Q0 3D4 3Q0 4D5 4Q1 4Q0
36 * +--+--+--+--+--+ +--+--+--+--+ +--+--+--+--+ +--+--+--+--+--+
37 * +->| 0| 1| 2| 3| 4|->| 5| 6| 7| 8|->| 9|10|11|12|->|13|14|15|16|17|
38 * | +--+--+--+--+--+ +--+--+--+--+ +--+--+--+--+ +--+--+--+--+--+
39 * | ____ | |
40 * | / |------------+ |
41 * +-----------------------------------------|EXNOR| |
42 * \____|---------------+
43 *
44 ****************************************************************************/
45
46 #define VMIN 0
47 #define VMAX 32767
48
49
50
51 DEFINE_DEVICE_TYPE(PHOENIX_SOUND, phoenix_sound_device, "phoenix_sound", "Phoenix Custom Sound")
52
phoenix_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)53 phoenix_sound_device::phoenix_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
54 : device_t(mconfig, PHOENIX_SOUND, tag, owner, clock)
55 , device_sound_interface(mconfig, *this)
56 , m_discrete(*this, ":discrete")
57 , m_tms(*this, ":tms")
58 {
59 }
60
61 //-------------------------------------------------
62 // device_start - device-specific startup
63 //-------------------------------------------------
64
device_start()65 void phoenix_sound_device::device_start()
66 {
67 int i, j;
68 uint32_t shiftreg;
69
70 m_sound_latch_a = 0;
71 memset(&m_c24_state, 0, sizeof(m_c24_state));
72 memset(&m_c25_state, 0, sizeof(m_c25_state));
73 memset(&m_noise_state, 0, sizeof(m_noise_state));
74
75 m_poly18 = std::make_unique<uint32_t[]>(1ul << (18-5));
76
77 shiftreg = 0;
78 for( i = 0; i < (1ul << (18-5)); i++ )
79 {
80 uint32_t bits = 0;
81 for( j = 0; j < 32; j++ )
82 {
83 bits = (bits >> 1) | (shiftreg << 31);
84 if( ((shiftreg >> 16) & 1) == ((shiftreg >> 17) & 1) )
85 shiftreg = (shiftreg << 1) | 1;
86 else
87 shiftreg <<= 1;
88 }
89 m_poly18[i] = bits;
90 }
91
92 m_channel = stream_alloc(0, 1, machine().sample_rate());
93
94 save_item(NAME(m_sound_latch_a));
95 save_item(NAME(m_c24_state.counter));
96 save_item(NAME(m_c24_state.level));
97 save_item(NAME(m_c25_state.counter));
98 save_item(NAME(m_c25_state.level));
99 save_item(NAME(m_noise_state.counter));
100 save_item(NAME(m_noise_state.polybit));
101 save_item(NAME(m_noise_state.polyoffs));
102 save_item(NAME(m_noise_state.lowpass_counter));
103 save_item(NAME(m_noise_state.lowpass_polybit));
104 }
105
update_c24(int samplerate)106 int phoenix_sound_device::update_c24(int samplerate)
107 {
108 /*
109 * Noise frequency control (Port B):
110 * Bit 6 lo charges C24 (6.8u) via R51 (330) and when
111 * bit 6 is hi, C24 is discharged through R52 (20k)
112 * in approx. 20000 * 6.8e-6 = 0.136 seconds
113 */
114 #define C24 6.8e-6
115 #define R49 1000
116 #define R51 330
117 #define R52 20000
118
119 if( m_sound_latch_a & 0x40 )
120 {
121 if (m_c24_state.level > VMIN)
122 {
123 m_c24_state.counter -= (int)((m_c24_state.level - VMIN) / (R52 * C24));
124 if( m_c24_state.counter <= 0 )
125 {
126 int n = -m_c24_state.counter / samplerate + 1;
127 m_c24_state.counter += n * samplerate;
128 if( (m_c24_state.level -= n) < VMIN)
129 m_c24_state.level = VMIN;
130 }
131 }
132 }
133 else
134 {
135 if (m_c24_state.level < VMAX)
136 {
137 m_c24_state.counter -= (int)((VMAX - m_c24_state.level) / ((R51+R49) * C24));
138 if( m_c24_state.counter <= 0 )
139 {
140 int n = -m_c24_state.counter / samplerate + 1;
141 m_c24_state.counter += n * samplerate;
142 if( (m_c24_state.level += n) > VMAX)
143 m_c24_state.level = VMAX;
144 }
145 }
146 }
147 return VMAX - m_c24_state.level;
148 }
149
update_c25(int samplerate)150 int phoenix_sound_device::update_c25(int samplerate)
151 {
152 /*
153 * Bit 7 hi charges C25 (6.8u) over a R50 (1k) and R53 (330) and when
154 * bit 7 is lo, C25 is discharged through R54 (47k)
155 * in about 47000 * 6.8e-6 = 0.3196 seconds
156 */
157 #define C25 6.8e-6
158 #define R50 1000
159 #define R53 330
160 #define R54 47000
161
162 if( m_sound_latch_a & 0x80 )
163 {
164 if (m_c25_state.level < VMAX)
165 {
166 m_c25_state.counter -= (int)((VMAX - m_c25_state.level) / ((R50+R53) * C25));
167 if( m_c25_state.counter <= 0 )
168 {
169 int n = -m_c25_state.counter / samplerate + 1;
170 m_c25_state.counter += n * samplerate;
171 if( (m_c25_state.level += n) > VMAX )
172 m_c25_state.level = VMAX;
173 }
174 }
175 }
176 else
177 {
178 if (m_c25_state.level > VMIN)
179 {
180 m_c25_state.counter -= (int)((m_c25_state.level - VMIN) / (R54 * C25));
181 if( m_c25_state.counter <= 0 )
182 {
183 int n = -m_c25_state.counter / samplerate + 1;
184 m_c25_state.counter += n * samplerate;
185 if( (m_c25_state.level -= n) < VMIN )
186 m_c25_state.level = VMIN;
187 }
188 }
189 }
190 return m_c25_state.level;
191 }
192
193
noise(int samplerate)194 int phoenix_sound_device::noise(int samplerate)
195 {
196 int vc24 = update_c24(samplerate);
197 int vc25 = update_c25(samplerate);
198 int sum = 0, level, frequency;
199
200 /*
201 * The voltage levels are added and control I(CE) of transistor TR1
202 * (NPN) which then controls the noise clock frequency (linearily?).
203 * level = voltage at the output of the op-amp controlling the noise rate.
204 */
205 if( vc24 < vc25 )
206 level = vc24 + (vc25 - vc24) / 2;
207 else
208 level = vc25 + (vc24 - vc25) / 2;
209
210 frequency = 588 + 6325 * level / 32768;
211
212 /*
213 * NE555: Ra=47k, Rb=1k, C=0.05uF
214 * minfreq = 1.44 / ((47000+2*1000) * 0.05e-6) = approx. 588 Hz
215 * R71 (2700 Ohms) parallel to R73 (47k Ohms) = approx. 2553 Ohms
216 * maxfreq = 1.44 / ((2553+2*1000) * 0.05e-6) = approx. 6325 Hz
217 */
218 m_noise_state.counter -= frequency;
219 if( m_noise_state.counter <= 0 )
220 {
221 int n = (-m_noise_state.counter / samplerate) + 1;
222 m_noise_state.counter += n * samplerate;
223 m_noise_state.polyoffs = (m_noise_state.polyoffs + n) & 0x3ffff;
224 m_noise_state.polybit = (m_poly18[m_noise_state.polyoffs>>5] >> (m_noise_state.polyoffs & 31)) & 1;
225 }
226 if (!m_noise_state.polybit)
227 sum += vc24;
228
229 /* 400Hz crude low pass filter: this is only a guess!! */
230 m_noise_state.lowpass_counter -= 400;
231 if( m_noise_state.lowpass_counter <= 0 )
232 {
233 m_noise_state.lowpass_counter += samplerate;
234 m_noise_state.lowpass_polybit = m_noise_state.polybit;
235 }
236 if (!m_noise_state.lowpass_polybit)
237 sum += vc25;
238
239 return sum;
240 }
241
242
243 /************************************************************************/
244 /* phoenix Sound System Analog emulation */
245 /* */
246 /* NOTE: Sample Rate must be at least 44100 for proper emulation. */
247 /* */
248 /* April 2005, DR. */
249 /************************************************************************/
250
251 static const discrete_555_desc phoenix_effect1_555 =
252 {
253 DISC_555_OUT_COUNT_F_X,
254 5, // B+ voltage of 555
255 DEFAULT_555_VALUES
256 };
257
258 static const discrete_555_desc phoenix_effect2_555 =
259 {
260 DISC_555_OUT_ENERGY,
261 5, // B+ voltage of 555
262 DEFAULT_555_CHARGE,
263 4.0 // loaded output voltage
264 };
265
266 static const discrete_comp_adder_table phoenix_effect2_cap_sel =
267 {
268 DISC_COMP_P_CAPACITOR,
269 CAP_U(0.01), // C18
270 2,
271 {CAP_U(0.47), CAP_U(1)} // C16, C17
272 };
273
274 static const discrete_mixer_desc phoenix_effect2_mixer1 =
275 {
276 DISC_MIXER_IS_RESISTOR,
277 {RES_K(10), RES_K(5.1) + RES_K(5.1), RES_K(5)}, // R42, R45+R46, internal 555 R
278 {0}, // No variable resistor nodes
279 {0}, // No caps
280 0, // No rI
281 RES_K(10), // internal 555
282 0,0, // No Filter
283 0, // not used in resistor network
284 1 // final gain
285 };
286
287 static const discrete_mixer_desc phoenix_effect2_mixer2 =
288 {
289 DISC_MIXER_IS_RESISTOR,
290 {RES_K(5.1), RES_K(5.1)}, // R45, R46
291 {0}, // No variable resistor nodes
292 {0}, // No caps
293 0, // No rI
294 0, // No rF
295 0,0, // No Filter
296 0, // not used in resistor network
297 1 // final gain
298 };
299
300 static const discrete_mixer_desc phoenix_effect2_mixer3 =
301 {
302 DISC_MIXER_IS_RESISTOR,
303 {RES_K(10), RES_K(5.1), RES_K(5)}, // R42, R46, internal 555 R
304 {0}, // No variable resistor nodes
305 {0}, // No caps
306 0, // No rI
307 RES_K(10), // internal 555
308 0,0, // No Filter
309 0, // not used in resistor network
310 1 // final gain
311 };
312
313 static const discrete_mixer_desc phoenix_mixer =
314 {
315 DISC_MIXER_IS_RESISTOR,
316 {RES_K(10+47), RES_K(10+20), RES_K(20), RES_K(20)}, // R19+R21, R38+R47, R67, R68
317 {0}, // No variable resistor nodes
318 {CAP_U(10), CAP_U(10), CAP_U(.1), CAP_U(10)}, // C6, C31, C29, C30
319 0, // No rI
320 RES_K(10), // VR1
321 0, // No Filter
322 CAP_U(10), // C32
323 0, // not used in resistor network
324 40000 // final gain
325 };
326
327 /* Nodes - Inputs */
328 #define PHOENIX_EFFECT_1_DATA NODE_01
329 #define PHOENIX_EFFECT_1_FREQ NODE_02
330 #define PHOENIX_EFFECT_1_FILT NODE_03
331 #define PHOENIX_EFFECT_2_DATA NODE_04
332 #define PHOENIX_EFFECT_2_FREQ NODE_05
333 #define PHOENIX_EFFECT_3_EN NODE_06
334 #define PHOENIX_EFFECT_4_EN NODE_07
335 /* Nodes - Sounds */
336 #define PHOENIX_EFFECT_1_SND NODE_10
337 #define PHOENIX_EFFECT_2_SND NODE_11
338 #define PHOENIX_EFFECT_3_SND 0
339 #define PHOENIX_EFFECT_4_SND 0
340
341
342 DISCRETE_SOUND_START(phoenix_discrete)
343 /************************************************/
344 /* Input register mapping for phoenix */
345 /************************************************/
DISCRETE_INPUT_DATA(PHOENIX_EFFECT_1_DATA)346 DISCRETE_INPUT_DATA (PHOENIX_EFFECT_1_DATA)
347 DISCRETE_INPUT_LOGIC(PHOENIX_EFFECT_1_FREQ)
348 DISCRETE_INPUT_LOGIC(PHOENIX_EFFECT_1_FILT)
349 DISCRETE_INPUT_DATA (PHOENIX_EFFECT_2_DATA)
350 DISCRETE_INPUT_DATA (PHOENIX_EFFECT_2_FREQ)
351 DISCRETE_INPUT_LOGIC(PHOENIX_EFFECT_3_EN)
352 DISCRETE_INPUT_LOGIC(PHOENIX_EFFECT_4_EN)
353
354 /************************************************/
355 /* Effect 1 */
356 /* - shield, bird explode, level 3&4 siren, */
357 /* - level 5 spaceship */
358 /************************************************/
359 /* R22 has been confirmed on real boards as 470 ohm, not 47k in schematics */
360 DISCRETE_RCDISC4(NODE_20, /* IC52 output pin 7 */
361 1, /* ENAB */
362 PHOENIX_EFFECT_1_FREQ, /* Input to O.C. inverter */
363 470, /* R22 */
364 RES_K(100), /* R23 */
365 RES_K(33), /* R24 */
366 CAP_U(6.8), /* C7 */
367 12, /* 12V supply */
368 1) /* Circuit type 1 */
369 DISCRETE_555_ASTABLE_CV(NODE_21, /* IC20 pin 6 */
370 1, /* ENAB */
371 RES_K(47), /* R25 */
372 RES_K(47), /* R26 */
373 CAP_U(.001), /* C8 */
374 NODE_20, /* IC48 pin 5 input */
375 &phoenix_effect1_555)
376 /* LS163 counts rising edge, but the LS14 inverts that */
377 DISCRETE_NOTE(NODE_22, /* IC21 pin 5 output */
378 1, /* ENAB */
379 NODE_21, /* IC13 pin 2 clock input */
380 PHOENIX_EFFECT_1_DATA, /* Pre-load data */
381 0x0f, /* Maximum count of first counter 0-15 (IC13) */
382 1, /* Maximum count of second counter 0-1 (IC21) */
383 DISC_CLK_BY_COUNT | DISC_OUT_IS_ENERGY) /* Module is clocked externally and we anti-alias output */
384 /* When FILT is enabled, the effect is filtered.
385 * While the R20 does decrease the amplitude a little, its main purpose
386 * is to discharge C5 when the filter is disabled. */
387 DISCRETE_SWITCH(NODE_23,
388 1, /* ENAB */
389 PHOENIX_EFFECT_1_FILT,
390 DEFAULT_TTL_V_LOGIC_1,
391 DEFAULT_TTL_V_LOGIC_1 * RES_K(100) / (RES_K(10) + RES_K(100))) /* R20, R19 */
392 DISCRETE_MULTIPLY(NODE_24,
393 NODE_22,
394 NODE_23)
395 DISCRETE_RCFILTER(NODE_25,
396 NODE_24,
397 1.0/(1.0/RES_K(10) + 1.0/RES_K(100)), /* R19, R20 */
398 CAP_U(.047)) /* C5 */
399 DISCRETE_SWITCH(PHOENIX_EFFECT_1_SND,
400 1, /* ENAB */
401 PHOENIX_EFFECT_1_FILT,
402 NODE_24, /* non-filtered */
403 NODE_25) /* filtered */
404
405 /************************************************/
406 /* Effect 2 */
407 /* - bird flying, bird/phoenix/spaceship hit */
408 /* - phoenix wing hit */
409 /************************************************/
410 DISCRETE_COMP_ADDER(NODE_30, /* total capacitance of selected capacitors */
411 PHOENIX_EFFECT_2_FREQ, /* passed selection bits */
412 &phoenix_effect2_cap_sel)
413 /* Part of the frequency select also effects the gain */
414 DISCRETE_TRANSFORM2(NODE_31, /* 0/1 state of PHOENIX_EFFECT_2_FREQ high bit */
415 PHOENIX_EFFECT_2_FREQ, 2, "01&1/") // get bit 0x02
416 DISCRETE_SWITCH(NODE_32, /* voltage level */
417 1, /* ENAB */
418 NODE_31, /* PHOENIX_EFFECT_2_FREQ high bit determines voltage level */
419 DEFAULT_TTL_V_LOGIC_1,
420 DEFAULT_TTL_V_LOGIC_1 / 2)
421 DISCRETE_555_ASTABLE(NODE_33, /* pin 3 output of IC44 */
422 1, /* ENAB */
423 RES_K(47), /* R40 */
424 RES_K(100), /* R41 */
425 NODE_30, /* C16, C17, C18 combined */
426 &phoenix_effect2_555)
427 /* C20 has been confirmed on real boards as 1uF, not 10uF in schematics */
428 DISCRETE_555_ASTABLE(NODE_34, /* pin 3 output of IC51 */
429 1, /* ENAB */
430 RES_K(510), /* R23 */
431 RES_K(510), /* R24 */
432 CAP_U(1), /* C20 */
433 &phoenix_effect2_555)
434 /* R45 & R46 have been confirmed on real boards as 5.1k, not 51k in schematics */
435 /* We need to work backwards here and calculate the voltage at the junction of R42 & R46 */
436 /* If you remove C22 from the real PCB, you can WAVELOG NODE_35 with a gain of 1000 and compare
437 * it against the junction of R42 & R46 on a real PCB. */
438 DISCRETE_MIXER3(NODE_35, /* Voltage at junction of R42 & R46 with C22 removed */
439 1, /* ENAB */
440 NODE_33, /* output from IC44 */
441 NODE_34, /* output from IC51 */
442 5, /* B+ connected internally to pin 5 of 555 */
443 &phoenix_effect2_mixer1)
444 /* Then calculate the voltage going to C22 */
445 /* If you remove C22 from the real PCB, you can WAVELOG NODE_36 with a gain of 1000 and compare
446 * it against the junction of R45 & R46 on a real PCB. */
447 DISCRETE_MIXER2(NODE_36, /* Voltage at junction of R45 & R46 with C22 removed */
448 1, /* ENAB */
449 NODE_34, /* pin 3 output of IC51 */
450 NODE_35, /* Voltage at junction of R42 & R46 with C22 removed */
451 &phoenix_effect2_mixer2)
452 /* C22 charging is R45 in parallel with R46, R42 and the 555 CV internal resistance */
453 DISCRETE_RCFILTER(NODE_37,
454 NODE_36,
455 1.0/ (1.0/RES_K(5.1) + (1.0/(RES_K(5.1) + 1.0/(1.0/RES_K(10) + 1.0/RES_K(5) + 1.0/RES_K(10)) ))),
456 CAP_U(100)) /* R45, R46, R42, internal 555 Rs, C22 */
457 /* Now mix from C22 on */
458 /* You can WAVELOG NODE_38 with a gain of 1000 and compare it against IC50 pin 5 on a real PCB. */
459 DISCRETE_MIXER3(NODE_38, /* control voltage to pin 5 of IC50 */
460 1, /* ENAB */
461 NODE_33, /* pin 3 output of IC44 */
462 NODE_37, /* voltage on C22 */
463 5, /* IC50 internally connected to B+ */
464 &phoenix_effect2_mixer3)
465 DISCRETE_555_ASTABLE_CV(NODE_39, /* IC20 pin 8 output */
466 1, /* ENAB */
467 RES_K(20), /* R47 */
468 RES_K(20), /* R48 */
469 CAP_U(0.001), /* C23 */
470 NODE_38, /* IC50 pin 5 input */
471 &phoenix_effect1_555)
472 DISCRETE_NOTE(NODE_40, /* IC21 pin 9 output */
473 1, /* ENAB */
474 NODE_39, /* IC14 pin 2 clock input */
475 PHOENIX_EFFECT_2_DATA, /* Pre-load data */
476 0x0f, /* Maximum count of first counter 0-15 (IC14) */
477 1, /* Maximum count of second counter 0-1 (IC21) */
478 DISC_CLK_BY_COUNT | DISC_OUT_IS_ENERGY)
479 DISCRETE_MULTIPLY(PHOENIX_EFFECT_2_SND,
480 NODE_40, /* IC21 pin 9 output */
481 NODE_32) /* voltage level selected by high bit of PHOENIX_EFFECT_2_FREQ */
482
483 /************************************************/
484 /* Combine all sound sources. */
485 /************************************************/
486 DISCRETE_MIXER4(NODE_90,
487 1, /* ENAB */
488 PHOENIX_EFFECT_1_SND,
489 PHOENIX_EFFECT_2_SND,
490 PHOENIX_EFFECT_3_SND,
491 PHOENIX_EFFECT_4_SND,
492 &phoenix_mixer)
493
494 DISCRETE_OUTPUT(NODE_90, 1)
495 DISCRETE_SOUND_END
496
497 void phoenix_sound_device::control_a_w(uint8_t data)
498 {
499 m_discrete->write(PHOENIX_EFFECT_2_DATA, data & 0x0f);
500 m_discrete->write(PHOENIX_EFFECT_2_FREQ, (data & 0x30) >> 4);
501 #if 0
502 /* future handling of noise sounds */
503 m_discrete->write(PHOENIX_EFFECT_3_EN , data & 0x40);
504 m_discrete->write(PHOENIX_EFFECT_4_EN , data & 0x80);
505 #endif
506 m_channel->update();
507 m_sound_latch_a = data;
508 }
509
control_b_w(uint8_t data)510 void phoenix_sound_device::control_b_w(uint8_t data)
511 {
512 m_discrete->write(PHOENIX_EFFECT_1_DATA, data & 0x0f);
513 m_discrete->write(PHOENIX_EFFECT_1_FILT, data & 0x20);
514 m_discrete->write(PHOENIX_EFFECT_1_FREQ, data & 0x10);
515
516 /* update the tune that the MM6221AA is playing */
517 m_tms->mm6221aa_tune_w(data >> 6);
518 }
519
520
521 //-------------------------------------------------
522 // sound_stream_update - handle a stream update
523 //-------------------------------------------------
524
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)525 void phoenix_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
526 {
527 auto &buffer = outputs[0];
528 int samplerate = buffer.sample_rate();
529
530 for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
531 {
532 int sum = 0;
533 sum = noise(samplerate) / 2;
534 buffer.put_int_clamp(sampindex, sum, 32768);
535 }
536 }
537