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