1 // license:BSD-3-Clause
2 // copyright-holders:Couriersud
3 /***************************************************************************
4
5 Galaxian-derived sound hardware
6
7 ****************************************************************************
8
9 Notes:
10 -----
11
12 - There is currently no way to exactly reproduce the CD4066 switch control
13 mixing. This is changing impedance of the input resistor for e.g.
14 following filters to >> 10M Ohm. These resistors are static values in
15 the discrete core.
16
17
18 TODO:
19 ----
20
21 - Check more schematics for differences.
22
23 ***************************************************************************/
24
25 #include "emu.h"
26 #include "audio/galaxian.h"
27 #include "includes/galaxian.h"
28 #include "speaker.h"
29
30 /*************************************
31 *
32 * Defines
33 *
34 *************************************/
35
36 #define SOUND_CLOCK (GALAXIAN_MASTER_CLOCK/6/2) /* 1.536 MHz */
37 #define RNG_RATE (GALAXIAN_MASTER_CLOCK/3*2) /* RNG clock is XTAL/3*2 see Aaron's note in video/galaxian.c */
38
39 /* 74LS259 */
40 #define GAL_INP_BG_DAC NODE_10 /* at 9M Q4 to Q7 in schematics */
41
42 #define GAL_INP_FS1 NODE_20 /* FS1 9L Q0 */
43 #define GAL_INP_FS2 NODE_21 /* FS2 9L Q1 */
44 #define GAL_INP_FS3 NODE_22 /* FS3 9L Q2 */
45 #define GAL_INP_HIT NODE_23 /* HIT 9L Q3 */
46 //#define GAL_9L_Q4 NODE_24
47 #define GAL_INP_FIRE NODE_25 /* FIRE 9L Q5 */
48 #define GAL_INP_VOL1 NODE_26 /* VOL1 9L Q6 */
49 #define GAL_INP_VOL2 NODE_27 /* VOL2 9L Q7 */
50
51 #define GAL_INP_PITCH NODE_28 /* at 6T in schematics */
52
53 #define TTL_OUT (4.0)
54
55 #define GAL_R15 RES_K(100)
56 #define GAL_R16 RES_K(220)
57 #define GAL_R17 RES_K(470)
58 #define GAL_R18 RES_K(1000)
59 #define GAL_R19 RES_K(330)
60
61 #define GAL_R20 RES_K(15)
62 #define GAL_R21 RES_K(100)
63 #define GAL_R22 RES_K(100)
64 #define GAL_R23 RES_K(470)
65 #define GAL_R24 RES_K(10)
66 #define GAL_R25 RES_K(100)
67 #define GAL_R26 RES_K(330)
68 #define GAL_R27 RES_K(10)
69 #define GAL_R28 RES_K(100)
70 #define GAL_R29 RES_K(220)
71
72 #define GAL_R30 RES_K(10)
73 #define GAL_R31 RES_K(47)
74 #define GAL_R32 RES_K(47)
75 #define GAL_R33 RES_K(10)
76 /*
77 * R34 is given twice on galaxian board and both times as 5.1k. On moon cresta
78 * it is only listed once and given as 15k. This is more in line with recordings
79 */
80 #define GAL_R34 RES_K(5.1)
81 #define MCRST_R34 RES_K(15)
82
83 #define GAL_R35 RES_K(150)
84 #define GAL_R36 RES_K(22)
85 #define GAL_R37 RES_K(470)
86 #define GAL_R38 RES_K(33)
87 #define GAL_R39 RES_K(22)
88
89 /* The hit sound is too low compared with recordings
90 * There may be an issue with the op-amp band filter
91 */
92 #define GAL_R40 (RES_K(2.2)*0.6) /* Volume adjust */
93 #define GAL_R41 RES_K(100)
94 #define GAL_R43 RES_K(2.2)
95 #define GAL_R44 RES_K(10)
96 #define GAL_R45 RES_K(22)
97 #define GAL_R46 RES_K(10)
98 #define GAL_R47 RES_K(2.2)
99 #define GAL_R48 RES_K(2.2)
100 #define GAL_R49 RES_K(10)
101
102 #define GAL_R50 RES_K(22)
103 #define GAL_R51 RES_K(33)
104 #define GAL_R52 RES_K(15)
105
106 #define GAL_R91 RES_K(10)
107
108 #define GAL_C15 CAP_U(1)
109 #define GAL_C17 CAP_U(0.01)
110 #define GAL_C18 CAP_U(0.01)
111 #define GAL_C19 CAP_U(0.01)
112
113 #define GAL_C20 CAP_U(0.1)
114 #define GAL_C21 CAP_U(2.2)
115 #define GAL_C22 CAP_U(0.01)
116 #define GAL_C23 CAP_U(0.01)
117 #define GAL_C25 CAP_U(1)
118 #define GAL_C26 CAP_U(0.01)
119 #define GAL_C27 CAP_U(0.01)
120 #define GAL_C28 CAP_U(47)
121
122 #define GAL_C46 CAP_U(0.1)
123
124
125 /*************************************
126 *
127 * Structures for discrete core
128 *
129 *************************************/
130
131
132 static const discrete_dac_r1_ladder galaxian_bck_dac =
133 {
134 4, // size of ladder
135 {GAL_R18, GAL_R17, GAL_R16, GAL_R15, 0,0,0,0},
136 4.4, // 5V - diode junction (0.6V)
137 GAL_R20, // rBIAS
138 GAL_R19, // rGnd
139 0 // no C
140 };
141
142 static const discrete_555_cc_desc galaxian_bck_vco =
143 {
144 DISC_555_OUT_DC | DISC_555_OUT_CAP,
145 5, // B+ voltage of 555
146 DEFAULT_555_VALUES,
147 0.7 // Q2 junction voltage
148 };
149
150 static const discrete_555_desc galaxian_555_vco_desc =
151 {
152 DISC_555_OUT_ENERGY | DISC_555_OUT_DC,
153 5.0,
154 DEFAULT_555_CHARGE,
155 (5.0 - 0.5) // 10k means no real load
156 };
157
158 static const discrete_555_desc galaxian_555_fire_vco_desc =
159 {
160 DISC_555_OUT_DC,
161 5.0,
162 DEFAULT_555_CHARGE,
163 1.0 // Logic output
164 };
165
166 static const discrete_mixer_desc galaxian_bck_mixer_desc =
167 {
168 DISC_MIXER_IS_RESISTOR,
169 {GAL_R24, GAL_R27, GAL_R30},
170 {0,0,0},
171 {0,0,0,0}, /* no node capacitors */
172 0, 0,
173 GAL_C20,
174 0,
175 0, 1
176 };
177
178 static const discrete_lfsr_desc galaxian_lfsr =
179 {
180 DISC_CLK_IS_FREQ,
181 17, /* Bit Length */
182 0, /* Reset Value */
183 4, /* Use Bit 10 (QC of second LS164) as F0 input 0 */
184 16, /* Use Bit 23 (QH of third LS164) as F0 input 1 */
185 DISC_LFSR_XOR_INV_IN1, /* F0 is XOR */
186 DISC_LFSR_IN0, /* F1 is inverted F0*/
187 DISC_LFSR_REPLACE, /* F2 replaces the shifted register contents */
188 0x000001, /* Everything is shifted into the first bit only */
189 DISC_LFSR_FLAG_OUTPUT_F0, /* Output is result of F0 */
190 0 /* Output bit */
191 };
192
193 static const discrete_mixer_desc galaxian_mixerpre_desc =
194 {
195 DISC_MIXER_IS_RESISTOR,
196 {GAL_R51, 0, GAL_R50, 0, GAL_R34}, /* A, C, C, D */
197 {0, GAL_INP_VOL1, 0, GAL_INP_VOL2, 0},
198 {0,0,0,0,0},
199 0, 0,
200 0,
201 0,
202 0, 1
203 };
204
205 static const discrete_mixer_desc galaxian_mixer_desc =
206 {
207 DISC_MIXER_IS_RESISTOR,
208 {GAL_R34, GAL_R40, GAL_R43}, /* A, C, C, D */
209 {0, 0, 0},
210 {0,0,GAL_C26},
211 0, GAL_R91,
212 0,
213 GAL_C46,
214 0, 1
215 };
216
217 /* moon cresta has different mixing */
218
219 static const discrete_mixer_desc mooncrst_mixer_desc =
220 {
221 DISC_MIXER_IS_RESISTOR,
222 {GAL_R51, 0, GAL_R50, 0, MCRST_R34, GAL_R40, GAL_R43}, /* A, C, C, D */
223 {0, GAL_INP_VOL1, 0, GAL_INP_VOL2, 0, 0, 0},
224 {0,0,0,0,0,0,GAL_C26},
225 0, 0*GAL_R91,
226 0,
227 GAL_C46,
228 0, 1
229 };
230
231 static const discrete_op_amp_filt_info galaxian_bandpass_desc =
232 {
233 GAL_R35, GAL_R36, 0, 0,
234 GAL_R37,
235 GAL_C22, GAL_C23, 0,
236 5.0*GAL_R39/(GAL_R38+GAL_R39),
237 5, 0
238 };
239
240 /*************************************
241 *
242 * Discrete Sound Blocks
243 *
244 *************************************/
245
246
247 static DISCRETE_SOUND_START(galaxian_discrete)
248
249 /************************************************/
250 /* Input register mapping for galaxian */
251 /************************************************/
DISCRETE_INPUT_DATA(GAL_INP_BG_DAC)252 DISCRETE_INPUT_DATA(GAL_INP_BG_DAC)
253
254 /* FS1 to FS3 */
255 DISCRETE_INPUT_LOGIC(GAL_INP_FS1)
256 DISCRETE_INPUT_LOGIC(GAL_INP_FS2)
257 DISCRETE_INPUT_LOGIC(GAL_INP_FS3)
258
259 /* HIT */
260 DISCRETE_INPUTX_DATA(GAL_INP_HIT, TTL_OUT, 0, 0)
261
262 /* FIRE */
263 DISCRETE_INPUT_LOGIC(GAL_INP_FIRE)
264
265 /* Turns on / off resistors in mixer */
266 DISCRETE_INPUTX_DATA(GAL_INP_VOL1, GAL_R49, 0, 0)
267 DISCRETE_INPUTX_DATA(GAL_INP_VOL2, GAL_R52, 0, 0)
268
269 /* Pitch */
270 DISCRETE_INPUT_DATA(GAL_INP_PITCH)
271
272 DISCRETE_TASK_START(0)
273
274 /************************************************/
275 /* NOISE */
276 /************************************************/
277
278 /* since only a sample of the LFSR is latched @V2 we let the lfsr
279 * run at a lower speed
280 */
281 DISCRETE_LFSR_NOISE(NODE_150, 1, 1, RNG_RATE.dvalue()/100, 1.0, 0, 0.5, &galaxian_lfsr)
282 DISCRETE_SQUAREWFIX(NODE_151,1,60*264/2,1.0,50,0.5,0) /* 2V signal */
283 DISCRETE_LOGIC_DFLIPFLOP(NODE_152,1,1,NODE_151,NODE_150)
284 DISCRETE_TASK_END()
285
286 /* Group Background and pitch */
287 DISCRETE_TASK_START(1)
288
289 /************************************************/
290 /* Background */
291 /************************************************/
292
293 DISCRETE_DAC_R1(NODE_100, GAL_INP_BG_DAC, TTL_OUT, &galaxian_bck_dac)
294 DISCRETE_555_CC(NODE_105, 1, NODE_100, GAL_R21, GAL_C15, 0, 0, 0, &galaxian_bck_vco)
295 // Next is mult/add opamp circuit
296 DISCRETE_MULTADD(NODE_110, NODE_105, GAL_R33/RES_3_PARALLEL(GAL_R31,GAL_R32,GAL_R33),
297 -5.0*GAL_R33/GAL_R31)
298 DISCRETE_CLAMP(NODE_111,NODE_110,0.0,5.0)
299 // The three 555
300 DISCRETE_555_ASTABLE_CV(NODE_115, GAL_INP_FS1, GAL_R22, GAL_R23, GAL_C17, NODE_111, &galaxian_555_vco_desc)
301 DISCRETE_555_ASTABLE_CV(NODE_116, GAL_INP_FS2, GAL_R25, GAL_R26, GAL_C18, NODE_111, &galaxian_555_vco_desc)
302 DISCRETE_555_ASTABLE_CV(NODE_117, GAL_INP_FS3, GAL_R28, GAL_R29, GAL_C19, NODE_111, &galaxian_555_vco_desc)
303
304 DISCRETE_MIXER3(NODE_120, 1, NODE_115, NODE_116, NODE_117, &galaxian_bck_mixer_desc)
305
306 /************************************************/
307 /* PITCH */
308 /************************************************/
309
310 /* two cascaded LS164 which are reset to pitch latch value,
311 * thus generating SOUND_CLOCK / (256 - pitch_clock) signal
312 *
313 * One possibility to implement this is
314 * DISCRETE_TRANSFORM3(NODE_130, SOUND_CLOCK, 256, GAL_INP_PITCH, "012-/")
315 * DISCRETE_COUNTER(NODE_132, 1, 0, NODE_130, 0, 15, DISC_COUNT_UP, 0, DISC_CLK_IS_FREQ)
316 * but there is a native choice:
317 */
318 DISCRETE_NOTE(NODE_132, 1, SOUND_CLOCK.dvalue(), GAL_INP_PITCH, 255, 15, DISC_CLK_IS_FREQ)
319
320 /* from the 74393 (counter 2 above) only QA, QC, QD are used.
321 * We decode three here and use SUB_NODE(133,x) below to access.
322 */
323 DISCRETE_BITS_DECODE(NODE_133, NODE_132, 0, 3, TTL_OUT) /* QA-QD 74393 */
324
325 /* End of this task */
326 DISCRETE_TASK_END()
327
328 DISCRETE_TASK_START(1)
329
330 /************************************************/
331 /* HIT */
332 /************************************************/
333
334 /* Not 100% correct - switching causes high impedance input for node_157
335 * this is not emulated */
336 DISCRETE_RCDISC5(NODE_155, NODE_152, GAL_INP_HIT, (GAL_R35 + GAL_R36), GAL_C21)
337 DISCRETE_OP_AMP_FILTER(NODE_157, 1, NODE_155, 0, DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &galaxian_bandpass_desc)
338 DISCRETE_TASK_END()
339
340 DISCRETE_TASK_START(1)
341 /************************************************/
342 /* FIRE */
343 /************************************************/
344
345 DISCRETE_LOGIC_INVERT(NODE_170, GAL_INP_FIRE)
346 DISCRETE_MULTIPLY(NODE_171, TTL_OUT, GAL_INP_FIRE)
347 DISCRETE_MULTIPLY(NODE_172, TTL_OUT, NODE_170) // inverted
348 DISCRETE_RCFILTER(NODE_173, NODE_172, GAL_R47, GAL_C28)
349 /* Mix noise and 163 */
350 DISCRETE_TRANSFORM5(NODE_177, NODE_152, TTL_OUT, 1.0/GAL_R46, NODE_173, 1.0/GAL_R48,
351 "01*2*34*+" )
352 //DISCRETE_MULTIPLY(NODE_174, 1, TTL_OUT, NODE_152)
353 //DISCRETE_MULTIPLY(NODE_175, 1, 1.0/GAL_R46, NODE_174)
354 //DISCRETE_MULTIPLY(NODE_176, 1, 1.0/GAL_R48, NODE_173)
355 //DISCRETE_ADDER2(NODE_177, 1, NODE_175, NODE_176)
356 DISCRETE_MULTIPLY(NODE_178, RES_2_PARALLEL(GAL_R46, GAL_R48), NODE_177)
357
358 DISCRETE_555_ASTABLE_CV(NODE_181, 1, GAL_R44, GAL_R45, GAL_C27, NODE_178, &galaxian_555_fire_vco_desc)
359
360 /* 555 toggles discharge on rc discharge module */
361 DISCRETE_RCDISC5(NODE_182, NODE_181, NODE_171, (GAL_R41), GAL_C25)
362
363 /* End of task */
364 DISCRETE_TASK_END()
365
366 /************************************************/
367 /* FINAL MIX */
368 /************************************************/
369
370 DISCRETE_TASK_START(2)
371 DISCRETE_MIXER5(NODE_279, 1, NODE_133_00, NODE_133_02, NODE_133_02, NODE_133_03, NODE_120, &galaxian_mixerpre_desc)
372 DISCRETE_MIXER3(NODE_280, 1, NODE_279, NODE_157, NODE_182, &galaxian_mixer_desc)
373 DISCRETE_OUTPUT(NODE_280, 32767.0/5.0*5)
374 DISCRETE_TASK_END()
375
376 DISCRETE_SOUND_END
377
378
379 static DISCRETE_SOUND_START(mooncrst_discrete)
380 DISCRETE_IMPORT(galaxian_discrete)
381
382 /************************************************/
383 /* Moon Cresta mixing stage */
384 /************************************************/
385 DISCRETE_DELETE(NODE_279, NODE_279)
386 DISCRETE_REPLACE
387 DISCRETE_MIXER7(NODE_280, 1, NODE_133_00, NODE_133_02, NODE_133_02,NODE_133_03, NODE_120, NODE_157, NODE_182, &mooncrst_mixer_desc)
388 DISCRETE_SOUND_END
389
390 DEFINE_DEVICE_TYPE(GALAXIAN_SOUND, galaxian_sound_device, "galaxian_sound", "Galaxian Custom Sound")
391 DEFINE_DEVICE_TYPE(MOONCRST_SOUND, mooncrst_sound_device, "mooncrst_sound", "Mooncrst Custom Sound")
392
393 galaxian_sound_device::galaxian_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
394 : galaxian_sound_device(mconfig, GALAXIAN_SOUND, tag, owner, clock)
395 {
396 }
397
galaxian_sound_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)398 galaxian_sound_device::galaxian_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
399 : device_t(mconfig, type, tag, owner, clock)
400 , m_discrete(*this, "discrete")
401 , m_lfo_val(0)
402 {
403 }
404
405 //-------------------------------------------------
406 // device_start - device-specific startup
407 //-------------------------------------------------
408
device_start()409 void galaxian_sound_device::device_start()
410 {
411 m_lfo_val = 0;
412
413 save_item(NAME(m_lfo_val));
414 }
415
416 //-------------------------------------------------
417 // machine_add_config - add device configuration
418 //-------------------------------------------------
419
device_add_mconfig(machine_config & config)420 void galaxian_sound_device::device_add_mconfig(machine_config &config)
421 {
422 // sound hardware
423 DISCRETE(config, m_discrete).add_route(ALL_OUTPUTS, ":speaker", 1.0);
424 m_discrete->set_intf(galaxian_discrete);
425 }
426
427 /*************************************
428 *
429 * Write handlers
430 *
431 *************************************/
432
433 /* IC 9J */
pitch_w(uint8_t data)434 void galaxian_sound_device::pitch_w(uint8_t data)
435 {
436 m_discrete->write(GAL_INP_PITCH, data );
437 }
438
lfo_freq_w(offs_t offset,uint8_t data)439 void galaxian_sound_device::lfo_freq_w(offs_t offset, uint8_t data)
440 {
441 uint8_t lfo_val_new = (m_lfo_val & ~(1<<offset)) | ((data & 0x01) << offset);
442
443 if (m_lfo_val != lfo_val_new)
444 {
445 m_lfo_val = lfo_val_new;
446 m_discrete->write(GAL_INP_BG_DAC, m_lfo_val);
447 }
448 }
449
background_enable_w(offs_t offset,uint8_t data)450 void galaxian_sound_device::background_enable_w(offs_t offset, uint8_t data)
451 {
452 m_discrete->write(NODE_RELATIVE(GAL_INP_FS1, offset), data & 0x01);
453 }
454
noise_enable_w(uint8_t data)455 void galaxian_sound_device::noise_enable_w(uint8_t data)
456 {
457 m_discrete->write(GAL_INP_HIT, data & 0x01);
458 }
459
vol_w(offs_t offset,uint8_t data)460 void galaxian_sound_device::vol_w(offs_t offset, uint8_t data)
461 {
462 m_discrete->write(NODE_RELATIVE(GAL_INP_VOL1,offset), data & 0x01);
463 }
464
fire_enable_w(uint8_t data)465 void galaxian_sound_device::fire_enable_w(uint8_t data)
466 {
467 m_discrete->write(GAL_INP_FIRE, data & 0x01);
468 }
469
470 /* FIXME: May be replaced by one call! */
sound_w(offs_t offset,uint8_t data)471 void galaxian_sound_device::sound_w(offs_t offset, uint8_t data)
472 {
473 data &= 0x01;
474 switch (offset & 7)
475 {
476 case 0: /* FS1 (controls 555 timer at 8R) */
477 case 1: /* FS2 (controls 555 timer at 8S) */
478 case 2: /* FS3 (controls 555 timer at 8T) */
479 background_enable_w(offset, data);
480 break;
481
482 case 3: /* HIT */
483 noise_enable_w(data);
484 break;
485
486 case 4: /* n/c */
487 break;
488
489 case 5: /* FIRE */
490 fire_enable_w(data);
491 break;
492
493 case 6: /* VOL1 */
494 case 7: /* VOL2 */
495 vol_w(offset & 1, data);
496 break;
497 }
498 }
499
mooncrst_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)500 mooncrst_sound_device::mooncrst_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
501 : galaxian_sound_device(mconfig, MOONCRST_SOUND, tag, owner, clock)
502 {
503 }
504
device_add_mconfig(machine_config & config)505 void mooncrst_sound_device::device_add_mconfig(machine_config &config)
506 {
507 galaxian_sound_device::device_add_mconfig(config);
508 m_discrete->set_intf(mooncrst_discrete);
509 }
510