1 // license:BSD-3-Clause
2 // copyright-holders:Derrick Renaud, Couriersud, Aaron Giles
3 /*************************************************************************
4 
5     VIC Dual Game board
6 
7 *************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/vicdual.h"
11 
12 #include "audio/nl_brdrline.h"
13 #include "audio/nl_frogs.h"
14 
15 
16 /************************************************************************
17  * headon Sound System Analog emulation
18  * July 2007, couriersud
19  ************************************************************************/
20 
21 #define HEADON_HISPEED_CC_EN    NODE_01
22 #define HEADON_HISPEED_PC_EN    NODE_02
23 #define HEADON_CAR_ON_EN        NODE_03
24 #define HEADON_CRASH_EN         NODE_04
25 #define HEADON_SCREECH1_EN      NODE_05
26 #define HEADON_SCREECH2_EN      NODE_06
27 #define HEADON_BONUS_EN         NODE_07
28 
29 #define HEADON_COMP_CAR_OUT     NODE_200
30 #define HEADON_PLAYER_CAR_OUT   NODE_201
31 #define HEADON_CRASH_OUT        NODE_202
32 #define HEADON_SCREECH1_OUT     NODE_203
33 #define HEADON_SCREECH2_OUT     NODE_204
34 #define HEADON_BONUS_OUT        NODE_205
35 
36 
37 static const discrete_mixer_desc headon_mixer =
38 {
39 	DISC_MIXER_IS_RESISTOR,
40 	{RES_K(130), RES_K(130), RES_K(100), RES_K(100), RES_K(100), RES_K(10)},   // 130 = 390/3, Bonus Res is dummy
41 	{0,0,0,0,0},    // no variable resistors
42 	{0,0,0,0,CAP_N(470),0},
43 	0, RES_K(100),
44 	0,
45 	CAP_U(1),       // not in schematics, used to suppress DC
46 	0, 1
47 };
48 
49 static const discrete_mixer_desc headon_crash_mixer =
50 {
51 	DISC_MIXER_IS_OP_AMP,
52 	{RES_K(50), RES_K(10)},   // Resistors, in fact variable resistors (100k)
53 	{0,0,0,0,0},    // no variable resistors
54 	{CAP_N(100),CAP_U(1)},
55 	0, RES_K(100),
56 	0,
57 	CAP_U(1)*0,     // not in schematics, used to suppress DC
58 	0, 1
59 };
60 
61 static const discrete_dss_inverter_osc_node::description headon_inverter_osc_1 =
62 {
63 	DEFAULT_CD40XX_VALUES(12),
64 	discrete_dss_inverter_osc_node::IS_TYPE4
65 };
66 
67 static const discrete_dss_inverter_osc_node::description headon_inverter_osc_2 =
68 {
69 	DEFAULT_CD40XX_VALUES(12),
70 	discrete_dss_inverter_osc_node::IS_TYPE5 | discrete_dss_inverter_osc_node::OUT_IS_LOGIC
71 };
72 
73 static const discrete_555_desc headon_555_bonus =
74 {
75 	DISC_555_OUT_ENERGY | DISC_555_OUT_DC,
76 	12,
77 	DEFAULT_555_CHARGE,
78 	12.0-0.5
79 };
80 
81 static const discrete_555_desc headon_555_crash =
82 {
83 	DISC_555_OUT_SQW | DISC_555_OUT_DC | DISC_555_TRIGGER_IS_LOGIC,
84 	12,
85 	DEFAULT_555_CHARGE,
86 	12.0-0.5
87 };
88 
89 static const discrete_555_cc_desc headon_555cc =
90 {
91 	DISC_555_OUT_SQW | DISC_555_OUT_DC,
92 	12,     // B+ voltage of 555
93 	DEFAULT_555_VALUES,
94 	0.6     // Q16, Q10 Vbe
95 };
96 
97 
98 /*
99  * From : http://www.vego.nl/8/08/03/08_08_03.htm
100  *
101  *- voeding:  -7 V, clock-frequency:  2.267 Hz
102  *- voeding:  -8 V, clock-frequency:  8.731 Hz
103  *- voeding:  -9 V, clock-frequency: 16,38 kHz
104  *- voeding: -10 V, clock-frequency: 23,53 kHz
105  *- voeding: -11 V, clock-frequency: 32,56 kHz
106  *- voeding: -12 V, clock-frequency: 38,34 kHz
107  *- voeding: -13 V, clock-frequency: 40,00 kHz
108  *- voeding: -14 V, clock-frequency: 37,80 kHz
109  *- voeding: -15 V, clock-frequency: 33,17 kHz
110  *
111  *  However all other mame sources say 100kHz.
112  */
113 
114 #define MM5837_CLOCK_12V 100000
115 
116 static const discrete_lfsr_desc mm5837_lfsr =
117 {
118 	DISC_CLK_IS_FREQ,
119 	17,                   /* Bit Length */
120 	0,                    /* Reset Value */
121 	13,                   /* Use Bit 14 as F0 input 0 */
122 	16,                   /* Use Bit 17 as F0 input 1 */
123 	DISC_LFSR_XOR,        /* F0 is XOR */
124 	DISC_LFSR_NOT_IN0,    /* F1 is inverted F0*/
125 	DISC_LFSR_REPLACE,    /* F2 replaces the shifted register contents */
126 	0x000001,             /* Everything is shifted into the first bit only */
127 	0,                    /* Flags */
128 	16                    /* Output bit */
129 };
130 
131 static const discrete_op_amp_filt_info headon_sallen_key_info =
132 {
133 	RES_K(15), RES_K(15), 0, 0, 0,
134 	CAP_N(470), CAP_N(47), 0
135 };
136 
137 static DISCRETE_SOUND_START(headon_discrete)
138 	/************************************************
139 	 * Input register mapping for headon
140 	 *
141 	 ************************************************/
DISCRETE_INPUT_LOGIC(HEADON_HISPEED_CC_EN)142 	DISCRETE_INPUT_LOGIC(HEADON_HISPEED_CC_EN)
143 	DISCRETE_INPUT_LOGIC(HEADON_HISPEED_PC_EN)
144 	DISCRETE_INPUT_LOGIC(HEADON_CAR_ON_EN)
145 	DISCRETE_INPUT_LOGIC(HEADON_CRASH_EN)
146 	DISCRETE_INPUT_LOGIC(HEADON_SCREECH1_EN)
147 	DISCRETE_INPUT_LOGIC(HEADON_SCREECH2_EN)
148 	DISCRETE_INPUT_LOGIC(HEADON_BONUS_EN)
149 
150 	/************************************************
151 	 * CAR Sound generation Player Car
152 	 * The ramp values are taken from a
153 	 * SWITCHER CAD III simulation of the
154 	 * respective circuit. Using ramps may not be
155 	 * 100% accurate but comes very close.
156 	 ************************************************/
157 
158 	DISCRETE_RAMP(NODE_20, 1, HEADON_CAR_ON_EN, (12-10.8)/7, 12, 10.8, 12)
159 	DISCRETE_RAMP(NODE_21, 1, HEADON_HISPEED_PC_EN, 2.0 / 0.8, 0, -2, 0)
160 	DISCRETE_ADDER2(NODE_22, 1, NODE_20, NODE_21)
161 
162 #define HO_R56      RES_K(10)
163 #define HO_R72      RES_K(1)
164 #define HO_C31      CAP_N(100)
165 
166 	DISCRETE_555_CC(NODE_25, HEADON_CAR_ON_EN, NODE_22, HO_R56, HO_C31, 0, 0, HO_R72, &headon_555cc)
167 	DISCRETE_COUNTER(NODE_26, 1, 0, NODE_25, 0, 1, DISC_COUNT_UP, 0, DISC_CLK_ON_R_EDGE) //divide by 2
168 	DISCRETE_COUNTER(NODE_27, 1, 0, NODE_25, 0, 3, DISC_COUNT_UP, 0, DISC_CLK_ON_R_EDGE) //divide by 4
169 	DISCRETE_COUNTER(NODE_28, 1, 0, NODE_25, 0, 2, DISC_COUNT_UP, 0, DISC_CLK_ON_R_EDGE) //divide by 3
170 	DISCRETE_TRANSFORM5(NODE_29,NODE_26,NODE_27,NODE_28,1,2,"13>24=+0+")
171 	DISCRETE_MULTIPLY(HEADON_PLAYER_CAR_OUT, NODE_29, 12 / 3)
172 
173 	/************************************************
174 	 * CAR Sound generation Computer Car
175 	 ************************************************/
176 
177 	DISCRETE_RAMP(NODE_30, 1, HEADON_CAR_ON_EN, (12-10.8)/7, 12, 10.8, 12)
178 	DISCRETE_RAMP(NODE_31, 1, HEADON_HISPEED_CC_EN, 2.0 / 0.8, 0, -2, 0)
179 	DISCRETE_ADDER2(NODE_32, 1, NODE_30, NODE_31)
180 
181 #define HO_R43      RES_K(10)
182 #define HO_R35      RES_K(1)
183 #define HO_C20      CAP_N(100)
184 
185 	DISCRETE_555_CC(NODE_35, HEADON_CAR_ON_EN, NODE_32, HO_R43, HO_C20, 0, 0, HO_R35, &headon_555cc)
186 	DISCRETE_COUNTER(NODE_36, 1, 0, NODE_35, 0, 1, DISC_COUNT_UP, 0, DISC_CLK_ON_R_EDGE) //divide by 2
187 	DISCRETE_COUNTER(NODE_37, 1, 0, NODE_35, 0, 3, DISC_COUNT_UP, 0, DISC_CLK_ON_R_EDGE) //divide by 4
188 	DISCRETE_COUNTER(NODE_38, 1, 0, NODE_35, 0, 2, DISC_COUNT_UP, 0, DISC_CLK_ON_R_EDGE) //divide by 3
189 	DISCRETE_TRANSFORM5(NODE_39,NODE_36,NODE_37,NODE_38,1,2,"13>24=+0+")
190 	DISCRETE_MULTIPLY(HEADON_COMP_CAR_OUT, NODE_39, 12 / 3)
191 
192 	/************************************************
193 	 * Screech #1
194 	 ************************************************/
195 
196 	DISCRETE_MULTIPLY(NODE_50,HEADON_SCREECH1_EN,12)
197 	DISCRETE_LFSR_NOISE(NODE_51, 1, 1, MM5837_CLOCK_12V, 12.0, 0, 6.0, &mm5837_lfsr)
198 	DISCRETE_INVERTER_OSC(HEADON_SCREECH1_OUT,NODE_50,NODE_51,RES_K(10),RES_K(100),CAP_N(47),RES_K(10),&headon_inverter_osc_1)
199 
200 	/************************************************
201 	 * Screech #2
202 	 ************************************************/
203 
204 	DISCRETE_MULTIPLY(NODE_60,HEADON_SCREECH2_EN,12)
205 	DISCRETE_INVERTER_OSC(HEADON_SCREECH2_OUT,NODE_60,NODE_51,RES_K(10),RES_K(100),CAP_N(57),RES_K(10),&headon_inverter_osc_1)
206 
207 	/************************************************
208 	 * Bonus
209 	 ************************************************/
210 
211 	DISCRETE_LOGIC_INVERT(NODE_70, HEADON_BONUS_EN)
212 	DISCRETE_MULTIPLY(NODE_71,NODE_70,12)
213 	DISCRETE_INVERTER_OSC(NODE_73,NODE_71,0,RES_K(22),RES_M(1),CAP_N(470),RES_M(10),&headon_inverter_osc_2)
214 
215 	/* FIXME: the following is a bit of a hack
216 	 * The NE555 is operating at a frequency of 400Hz
217 	 * The output of the oscillator is connectred through a 150K resistor to
218 	 * the discharge pin.
219 	 * The simulation gives a frequency of roughly 600Hz if the osc output is high.
220 	 * This is equivalent to R1 being 47k || 150k = 35K
221 	 * The simulation gives a frequency of roughly 375Hz if the osc output is low.
222 	 * This is not emulated exactly. We will just use 200k for R1.
223 	 *
224 	 */
225 	DISCRETE_TRANSFORM3(NODE_74,NODE_73,200000,165000,"102*-")
226 	DISCRETE_555_ASTABLE(NODE_75, 1, NODE_74, RES_K(100), CAP_N(10), &headon_555_bonus)
227 	DISCRETE_MULTIPLY(HEADON_BONUS_OUT,NODE_75,HEADON_BONUS_EN)
228 
229 	/************************************************
230 	 * Crash
231 	 * FIXME: Just a prototype several filter missing
232 	 ************************************************/
233 
234 	DISCRETE_LOGIC_INVERT(NODE_80, HEADON_CRASH_EN)
235 	DISCRETE_555_MSTABLE(NODE_81, 1, NODE_80, RES_K(470), CAP_U(1), &headon_555_crash)
236 	// Mix with noise
237 	DISCRETE_MULTIPLY(NODE_84, NODE_81, NODE_51)
238 	// Taken from simulation
239 	// Center frequency is 500 Hz
240 	// roughly 6db per octave
241 	DISCRETE_FILTER1(NODE_85, 1, NODE_84, 500, DISC_FILTER_BANDPASS)
242 
243 
244 	DISCRETE_555_MSTABLE(NODE_86, 1, NODE_80, RES_K(470), CAP_U(2.2), &headon_555_crash)
245 	// Mix with noise
246 	DISCRETE_MULTIPLY(NODE_87, NODE_86, NODE_51)
247 	// Sallen Key filter ...
248 	// http://www.t-linespeakers.org/tech/filters/Sallen-Key.html
249 	// f = w / 2 / pi  = 1 / ( 2 * pi * 15k*sqrt(470n*47n)) = 71 Hz
250 	// Q = 1/2 * sqrt(470n/47n)= 1.58
251 	DISCRETE_SALLEN_KEY_FILTER(NODE_88, 1, NODE_87, DISC_SALLEN_KEY_LOW_PASS, &headon_sallen_key_info)
252 
253 	DISCRETE_MIXER2(NODE_95, 1, NODE_85, NODE_88, &headon_crash_mixer)
254 	DISCRETE_TRANSFORM2(HEADON_CRASH_OUT, NODE_95, 12, "01/")
255 
256 	/************************************************
257 	 * Mixer Stage
258 	 ************************************************/
259 
260 	DISCRETE_MIXER6(NODE_210, 1, HEADON_PLAYER_CAR_OUT, HEADON_COMP_CAR_OUT,
261 					HEADON_SCREECH1_OUT, HEADON_SCREECH2_OUT,
262 					HEADON_BONUS_OUT, HEADON_CRASH_OUT, &headon_mixer)
263 
264 	DISCRETE_OUTPUT(NODE_210, 37000.0 / 12.0)
265 	//DISCRETE_CSVLOG3(HEADON_CRASH_EN,NODE_81,NODE_80)
266 
267 DISCRETE_SOUND_END
268 
269 void vicdual_state::headon_audio(machine_config &config)
270 {
271 	DISCRETE(config, m_discrete, headon_discrete);
272 	m_discrete->add_route(ALL_OUTPUTS, "mono", 1.0);
273 }
274 
headon_audio_w(uint8_t data)275 void vicdual_state::headon_audio_w(uint8_t data)
276 {
277 	if (m_discrete == nullptr)
278 		return;
279 	m_discrete->write(HEADON_HISPEED_PC_EN, data & 0x01);
280 	m_discrete->write(HEADON_SCREECH1_EN, data & 0x02);
281 	m_discrete->write(HEADON_CRASH_EN, data & 0x04);
282 	m_discrete->write(HEADON_HISPEED_CC_EN, data & 0x08);
283 	m_discrete->write(HEADON_SCREECH2_EN, data & 0x10);
284 	m_discrete->write(HEADON_BONUS_EN, data & 0x20);
285 	m_discrete->write(HEADON_CAR_ON_EN, data & 0x40);
286 
287 }
288 
invho2_audio_w(uint8_t data)289 void vicdual_state::invho2_audio_w(uint8_t data)
290 {
291 	if (m_discrete == nullptr)
292 		return;
293 	m_discrete->write(HEADON_HISPEED_PC_EN, data & 0x10);
294 	m_discrete->write(HEADON_SCREECH1_EN, data & 0x08);
295 	m_discrete->write(HEADON_CRASH_EN, data & 0x80);
296 	m_discrete->write(HEADON_HISPEED_CC_EN, data & 0x40);
297 	m_discrete->write(HEADON_SCREECH2_EN, data & 0x04);
298 	m_discrete->write(HEADON_BONUS_EN, data & 0x02);
299 	m_discrete->write(HEADON_CAR_ON_EN, data & 0x20);
300 
301 }
302 
303 
304 /*************************************
305  *
306  *  Netlist-based Vic Dual Audio
307  *
308  *************************************/
309 
vicdual_audio_device_base(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,u32 clock,u8 inputs_mask,void (* netlist)(netlist::nlparse_t &),double output_scale)310 vicdual_audio_device_base::vicdual_audio_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 inputs_mask, void (*netlist)(netlist::nlparse_t &), double output_scale) :
311 	device_t(mconfig, type, tag, owner, clock),
312 	device_mixer_interface(mconfig, *this),
313 	m_input_line(*this, "sound_nl:in_%u", 0),
314 	m_inputs_mask(inputs_mask),
315 	m_netlist(netlist),
316 	m_output_scale(output_scale)
317 {
318 }
319 
device_add_mconfig(machine_config & config)320 void vicdual_audio_device_base::device_add_mconfig(machine_config &config)
321 {
322 	NETLIST_SOUND(config, "sound_nl", 48000)
323 		.set_source(m_netlist)
324 		.add_route(ALL_OUTPUTS, *this, 1.0);
325 
326 	if (BIT(m_inputs_mask, 0))
327 		NETLIST_LOGIC_INPUT(config, m_input_line[0], "I_SOUND_0.IN", 0);
328 	if (BIT(m_inputs_mask, 1))
329 		NETLIST_LOGIC_INPUT(config, m_input_line[1], "I_SOUND_1.IN", 0);
330 	if (BIT(m_inputs_mask, 2))
331 		NETLIST_LOGIC_INPUT(config, m_input_line[2], "I_SOUND_2.IN", 0);
332 	if (BIT(m_inputs_mask, 3))
333 		NETLIST_LOGIC_INPUT(config, m_input_line[3], "I_SOUND_3.IN", 0);
334 	if (BIT(m_inputs_mask, 4))
335 		NETLIST_LOGIC_INPUT(config, m_input_line[4], "I_SOUND_4.IN", 0);
336 	if (BIT(m_inputs_mask, 5))
337 		NETLIST_LOGIC_INPUT(config, m_input_line[5], "I_SOUND_5.IN", 0);
338 	if (BIT(m_inputs_mask, 6))
339 		NETLIST_LOGIC_INPUT(config, m_input_line[6], "I_SOUND_6.IN", 0);
340 	if (BIT(m_inputs_mask, 7))
341 		NETLIST_LOGIC_INPUT(config, m_input_line[7], "I_SOUND_7.IN", 0);
342 
343 	NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "OUTPUT").set_mult_offset(m_output_scale, 0.0);
344 }
345 
device_start()346 void vicdual_audio_device_base::device_start()
347 {
348 	save_item(NAME(m_input_state));
349 }
350 
write(u8 value)351 void vicdual_audio_device_base::write(u8 value)
352 {
353 	if (value != m_input_state)
354 	{
355 		m_input_state = value;
356 		for (int index = 0; index < 8; index++)
357 			if (m_input_line[index] != nullptr)
358 				m_input_line[index]->write_line(BIT(m_input_state, index));
359 	}
360 }
361 
362 
363 /*************************************
364  *
365  *  Borderline/Tranquilizer Gun
366  *
367  *************************************/
368 
369 DEFINE_DEVICE_TYPE(BORDERLINE_AUDIO, borderline_audio_device, "borderline_audio", "Borderline Sound Board")
370 
borderline_audio_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)371 borderline_audio_device::borderline_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
372 	vicdual_audio_device_base(mconfig, BORDERLINE_AUDIO, tag, owner, clock, 0xff, NETLIST_NAME(brdrline), 1.0)
373 {
374 }
375 
376 
377 
378 /*************************************
379  *
380  *  Frogs
381  *
382  *************************************/
383 
384 DEFINE_DEVICE_TYPE(FROGS_AUDIO, frogs_audio_device, "frogs_audio", "Frogs Sound Board")
385 
frogs_audio_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)386 frogs_audio_device::frogs_audio_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
387 	vicdual_audio_device_base(mconfig, FROGS_AUDIO, tag, owner, clock, 0xff, NETLIST_NAME(frogs), 1.0)
388 {
389 }
390