1 // license:BSD-3-Clause
2 // copyright-holders:Derrick Renaud
3 /************************************************************************
4  * sprint8 Sound System Analog emulation
5  * Sept 2009, Derrick Renaud
6  ************************************************************************/
7 
8 #include "emu.h"
9 #include "includes/sprint8.h"
10 
11 #include "speaker.h"
12 
13 
14 /* Discrete Sound Input Nodes */
15 #define SPRINT8_CRASH_EN            NODE_01
16 #define SPRINT8_SCREECH_EN          NODE_02
17 #define SPRINT8_ATTRACT_EN          NODE_03
18 #define SPRINT8_MOTOR1_EN           NODE_04
19 #define SPRINT8_MOTOR2_EN           NODE_05
20 #define SPRINT8_MOTOR3_EN           NODE_06
21 #define SPRINT8_MOTOR4_EN           NODE_07
22 #define SPRINT8_MOTOR5_EN           NODE_08
23 #define SPRINT8_MOTOR6_EN           NODE_09
24 #define SPRINT8_MOTOR7_EN           NODE_10
25 #define SPRINT8_MOTOR8_EN           NODE_11
26 
27 /* Discrete Sound Output Nodes */
28 #define SPRINT8_NOISE               NODE_12
29 #define SPRINT8_MOTOR1_SND          NODE_13
30 #define SPRINT8_MOTOR2_SND          NODE_14
31 #define SPRINT8_MOTOR3_SND          NODE_15
32 #define SPRINT8_MOTOR4_SND          NODE_16
33 #define SPRINT8_MOTOR5_SND          NODE_17
34 #define SPRINT8_MOTOR6_SND          NODE_18
35 #define SPRINT8_MOTOR7_SND          NODE_19
36 #define SPRINT8_MOTOR8_SND          NODE_20
37 #define SPRINT8_CRASH_SCREECH_SND   NODE_21
38 #define SPRINT8_AUDIO_1_2           NODE_22
39 #define SPRINT8_AUDIO_3_7           NODE_23
40 #define SPRINT8_AUDIO_5_6           NODE_24
41 #define SPRINT8_AUDIO_4_8           NODE_25
42 
43 /* Adjusters */
44 #define SPRINT8_R132_POT            NODE_29
45 
46 
47 /* Parts List - Resistors */
48 #define SPRINT8_R1      RES_K(47)
49 #define SPRINT8_R3      RES_K(47)
50 #define SPRINT8_R4      RES_K(47)
51 #define SPRINT8_R19     RES_K(1)
52 #define SPRINT8_R20     RES_K(1)
53 #define SPRINT8_R27     RES_K(18)
54 #define SPRINT8_R28     820
55 #define SPRINT8_R29     RES_K(330)
56 #define SPRINT8_R39     RES_K(120)
57 #define SPRINT8_R40     RES_K(22)
58 #define SPRINT8_R41     RES_K(150)
59 #define SPRINT8_R89     RES_K(22)
60 #define SPRINT8_R91     RES_K(47)
61 #define SPRINT8_R93     RES_K(2.2)
62 #define SPRINT8_R96     RES_K(47)
63 #define SPRINT8_R97     RES_K(2.2)
64 #define SPRINT8_R99     RES_K(27)
65 #define SPRINT8_R100    RES_K(1)
66 #define SPRINT8_R101    RES_K(2.2)
67 #define SPRINT8_R132    RES_K(100)
68 #define SPRINT8_R145    RES_K(3.3)
69 #define SPRINT8_R146    RES_K(7.5)
70 #define SPRINT8_R147    100
71 #define SPRINT8_R148    RES_K(1)
72 #define SPRINT8_R149    RES_K(22)
73 
74 /* Parts List - Capacitors */
75 #define SPRINT8_C8      CAP_U(.01)
76 #define SPRINT8_C17     CAP_U(.001)
77 #define SPRINT8_C18     CAP_U(.047)
78 #define SPRINT8_C19     CAP_U(.047)
79 #define SPRINT8_C26     CAP_U(100)
80 #define SPRINT8_C27     CAP_U(.22)
81 #define SPRINT8_C28     CAP_U(.1)
82 #define SPRINT8_C59     CAP_U(.1)
83 #define SPRINT8_C63     CAP_U(.1)
84 #define SPRINT8_C64     CAP_U(.1)
85 #define SPRINT8_C89     CAP_U(.1)
86 #define SPRINT8_C90     CAP_U(.1)
87 
88 #define SPRINT8_HSYNC   15750.0     /* not checked */
89 #define SPRINT8_1V  SPRINT8_HSYNC/2
90 #define SPRINT8_2V  SPRINT8_1V/2
91 
92 
93 static const discrete_lfsr_desc sprint8_lfsr =
94 {
95 	DISC_CLK_IS_FREQ,
96 	16,                 /* Bit Length */
97 	0,                  /* Reset Value */
98 	10,                 /* Use Bit 10 as XOR input 0 */
99 	15,                 /* Use Bit 15 as XOR input 1 */
100 	DISC_LFSR_XNOR,     /* Feedback stage1 is XNOR */
101 	DISC_LFSR_OR,       /* Feedback stage2 is just stage 1 output OR with external feed */
102 	DISC_LFSR_REPLACE,  /* Feedback stage3 replaces the shifted register contents */
103 	0x000001,           /* Everything is shifted into the first bit only */
104 	DISC_LFSR_FLAG_RESET_TYPE_L,    /* Output is not inverted */
105 	12                  /* Output bit */
106 };
107 
108 static const discrete_555_desc sprint8_crash_555a_desc =
109 {
110 	DISC_555_OUT_ENERGY,
111 	5, DEFAULT_555_VALUES
112 };
113 
114 static const discrete_integrate_info sprint8_crash_integrate =
115 {
116 	DISC_INTEGRATE_OP_AMP_1,
117 	SPRINT8_R99, SPRINT8_R97, SPRINT8_R96, SPRINT8_C59,     /*r1, r2, r3, c, */
118 	5, 5,                           /* v1, vP*/
119 	0, 0, 0                         /* no functions */
120 };
121 
122 static const discrete_555_desc sprint8_motor_555a_desc =
123 {
124 	DISC_555_OUT_COUNT_F_X,
125 	5, DEFAULT_555_VALUES
126 };
127 
128 static const discrete_555_desc sprint8_motor_555m_desc =
129 {
130 	DISC_555_OUT_ENERGY | DISC_555_TRIGGER_IS_COUNT,
131 	5, DEFAULT_555_VALUES
132 };
133 
134 static const discrete_op_amp_filt_info sprint8_motor_filter =
135 {
136 	SPRINT8_R27, 0, SPRINT8_R28 + RES_2_PARALLEL(SPRINT8_R19, SPRINT8_R20), 0, SPRINT8_R29, /* r1, r2, r3, r4, rF, */
137 	SPRINT8_C18, SPRINT8_C19, 0,                                    /* c1, c2, c3, */
138 	5.0 * RES_VOLTAGE_DIVIDER(SPRINT8_R19, SPRINT8_R20), 5, 0       /* vRef, vP, vN */
139 };
140 
141 static const discrete_mixer_desc sprint8_crash_screech_mixer =
142 {
143 	DISC_MIXER_IS_RESISTOR,
144 	{SPRINT8_R149, SPRINT8_R91},
145 	{0, NODE_80},                       /* R93 switched in/out of circuit */
146 	{0}, 0, 0, SPRINT8_C64, 0, 0, 1     /* c, rI, rF, cF, cAmp, vRef, gain */
147 };
148 
149 static const discrete_mixer_desc sprint8_mixer =
150 {
151 	DISC_MIXER_IS_RESISTOR,
152 	{SPRINT8_R1 + SPRINT8_R100, SPRINT8_R3, SPRINT8_R4},
153 	{0}, {0}, 0, 0, 0, SPRINT8_C8, 0, 1     /* r_nodes, c, rI, rF, cF, cAmp, vRef, gain */
154 };
155 
156 
157 /************************************************
158  * Car Motor
159  ************************************************/
160 /* The first (astable) 555 generates a quick falling pulse that triggers the second (monostable) 555.
161  * This pulse is passed through C17 and pulled up with R40.  This pulse is too fast to emulate so
162  * we will just tell the monostable it was triggered once and ignore C17/R40.
163  */
164 #define SPRINT8_MOTOR_CIRCUIT(_car)                                                             \
165 DISCRETE_RCFILTER(NODE_RELATIVE(NODE_30, _car - 1), NODE_RELATIVE(SPRINT8_MOTOR1_EN, _car - 1), SPRINT8_R89, SPRINT8_C26)   \
166 DISCRETE_ADDER2(NODE_RELATIVE(NODE_40, _car - 1), 1, NODE_RELATIVE(NODE_30, _car - 1), 0.7) /* add Q21 shift */                 \
167 DISCRETE_555_ASTABLE_CV(NODE_RELATIVE(NODE_50, _car - 1), 1, SPRINT8_R39, 0, SPRINT8_C27, NODE_RELATIVE(NODE_40, _car - 1), &sprint8_motor_555a_desc) \
168 DISCRETE_555_MSTABLE(NODE_RELATIVE(NODE_60, _car - 1), 1, NODE_RELATIVE(NODE_50, _car - 1), SPRINT8_R41, SPRINT8_C28, &sprint8_motor_555m_desc)       \
169 DISCRETE_OP_AMP_FILTER(NODE_RELATIVE(SPRINT8_MOTOR1_SND, _car - 1), 1, NODE_RELATIVE(NODE_60, _car - 1), 0, DISC_OP_AMP_FILTER_IS_BAND_PASS_1M, &sprint8_motor_filter)
170 
171 
172 DISCRETE_SOUND_START( sprint8_discrete )
173 	/************************************************
174 	 * Input register mapping
175 	 ************************************************/
DISCRETE_INPUT_LOGIC(SPRINT8_CRASH_EN)176 	DISCRETE_INPUT_LOGIC(SPRINT8_CRASH_EN)
177 	DISCRETE_INPUT_NOT  (SPRINT8_SCREECH_EN)
178 	DISCRETE_INPUT_NOT  (SPRINT8_ATTRACT_EN)
179 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR1_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
180 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR2_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
181 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR3_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
182 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR4_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
183 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR5_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
184 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR6_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
185 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR7_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
186 	DISCRETE_INPUTX_LOGIC(SPRINT8_MOTOR8_EN, DEFAULT_TTL_V_LOGIC_1, 0, 0)
187 
188 	DISCRETE_TASK_START(0)
189 	DISCRETE_ADJUSTMENT(SPRINT8_R132_POT, 0, SPRINT8_R132, DISC_LINADJ, "R132")
190 
191 	/************************************************
192 	 * Noise Generator, Crash, Screech
193 	 ************************************************/
194 	/* Address line A2 is used to XOR the feedback bits.
195 	 * This can not easily be implemented, so I just set the
196 	 * feedback as XNOR. */
197 	DISCRETE_LFSR_NOISE(SPRINT8_NOISE,          /* IC F7, pin 13 */
198 		1,                                      /* ENAB */
199 		SPRINT8_ATTRACT_EN,                     /* RESET */
200 		SPRINT8_2V, 1, 0, 0.5, &sprint8_lfsr)   /* CLK,AMPL,FEED,BIAS,LFSRTB */
201 
202 	DISCRETE_TASK_END()
203 
204 	DISCRETE_TASK_START(1)
205 	DISCRETE_GAIN(NODE_70, SPRINT8_NOISE, DEFAULT_TTL_V_LOGIC_1 * RES_VOLTAGE_DIVIDER(SPRINT8_R148, SPRINT8_R147))
206 	DISCRETE_CRFILTER_VREF(NODE_71,
207 		NODE_70,                                /* IN0 */
208 		RES_2_PARALLEL(SPRINT8_R148, SPRINT8_R147) + RES_2_PARALLEL(RES_K(5), RES_K(10)),
209 		SPRINT8_C90,
210 		5.0 * RES_VOLTAGE_DIVIDER(RES_K(5), RES_K(10)))     /* ref to 555 CV pin */
211 	DISCRETE_555_ASTABLE_CV(NODE_72,
212 		SPRINT8_SCREECH_EN,                     /* RESET */
213 		SPRINT8_R145, SPRINT8_R146, SPRINT8_C89,
214 		NODE_71,                                /* CTRLV */
215 		&sprint8_crash_555a_desc)
216 	DISCRETE_INTEGRATE(NODE_73, SPRINT8_CRASH_EN, 0, &sprint8_crash_integrate)
217 
218 	DISCRETE_SWITCH(NODE_80,
219 		1,                                      /* ENAB */
220 		SPRINT8_NOISE,                          /* SWITCH */
221 		SPRINT8_R93, 1)                         /* INP0,INP1*/
222 	DISCRETE_SWITCH(NODE_74,                    /* effect of Q20 */
223 		1,                                      /* ENAB */
224 		SPRINT8_NOISE,                          /* SWITCH */
225 		NODE_73, 0)                             /* INP0,INP1*/
226 	DISCRETE_MIXER2(NODE_75,
227 		1,                                      /* ENAB */
228 		NODE_72,
229 		NODE_74,
230 		&sprint8_crash_screech_mixer)
231 
232 	DISCRETE_CRFILTER_VREF(NODE_76,
233 		NODE_75,                                /* IN0 */
234 		SPRINT8_R93 + SPRINT8_R91, SPRINT8_C63,
235 		5)                                      /* VREF */
236 	/* IC E5, pin 14 gain.  Does not simulate minor DC offset caused by R93. */
237 	DISCRETE_TRANSFORM5(NODE_77, NODE_76, 5, SPRINT8_R132_POT, SPRINT8_R101, 1, "01-23/4+*1+")
238 	DISCRETE_CLAMP(SPRINT8_CRASH_SCREECH_SND, NODE_77, 0, 15.0 - 1.5)
239 	DISCRETE_TASK_END()
240 
241 	/************************************************
242 	 * Car Motor
243 	 ************************************************/
244 	DISCRETE_TASK_START(1)
245 	SPRINT8_MOTOR_CIRCUIT(1)
246 	SPRINT8_MOTOR_CIRCUIT(2)
247 	DISCRETE_TASK_END()
248 
249 	DISCRETE_TASK_START(1)
250 	SPRINT8_MOTOR_CIRCUIT(3)
251 	SPRINT8_MOTOR_CIRCUIT(7)
252 	DISCRETE_TASK_END()
253 
254 	DISCRETE_TASK_START(1)
255 	SPRINT8_MOTOR_CIRCUIT(5)
256 	SPRINT8_MOTOR_CIRCUIT(6)
257 	DISCRETE_TASK_END()
258 
259 	DISCRETE_TASK_START(1)
260 	SPRINT8_MOTOR_CIRCUIT(4)
261 	SPRINT8_MOTOR_CIRCUIT(8)
262 	DISCRETE_TASK_END()
263 
264 	/************************************************
265 	 * Final Mix
266 	 ************************************************/
267 	DISCRETE_TASK_START(2)
268 	DISCRETE_MIXER3(SPRINT8_AUDIO_1_2,
269 		SPRINT8_ATTRACT_EN,             /* ENAB */
270 		SPRINT8_CRASH_SCREECH_SND,
271 		SPRINT8_MOTOR1_SND,
272 		SPRINT8_MOTOR2_SND,
273 		&sprint8_mixer)
274 	DISCRETE_MIXER3(SPRINT8_AUDIO_3_7,
275 		SPRINT8_ATTRACT_EN,             /* ENAB */
276 		SPRINT8_CRASH_SCREECH_SND,
277 		SPRINT8_MOTOR3_SND,
278 		SPRINT8_MOTOR7_SND,
279 		&sprint8_mixer)
280 	DISCRETE_MIXER3(SPRINT8_AUDIO_5_6,
281 		SPRINT8_ATTRACT_EN,             /* ENAB */
282 		SPRINT8_CRASH_SCREECH_SND,
283 		SPRINT8_MOTOR5_SND,
284 		SPRINT8_MOTOR6_SND,
285 		&sprint8_mixer)
286 	DISCRETE_MIXER3(SPRINT8_AUDIO_4_8,
287 		SPRINT8_ATTRACT_EN,             /* ENAB */
288 		SPRINT8_CRASH_SCREECH_SND,
289 		SPRINT8_MOTOR4_SND,
290 		SPRINT8_MOTOR8_SND,
291 		&sprint8_mixer)
292 	DISCRETE_OUTPUT(SPRINT8_AUDIO_1_2, 65500.0/8)
293 	DISCRETE_OUTPUT(SPRINT8_AUDIO_3_7, 65500.0/8)
294 	DISCRETE_OUTPUT(SPRINT8_AUDIO_5_6, 65500.0/8)
295 	DISCRETE_OUTPUT(SPRINT8_AUDIO_4_8, 65500.0/8)
296 	DISCRETE_TASK_END()
297 DISCRETE_SOUND_END
298 
299 void sprint8_state::sprint8_audio(machine_config &config)
300 {
301 	/* sound hardware */
302 	/* the proper way is to hook up 4 speakers, but they are not really
303 	 * F/R/L/R speakers.  Though you can pretend the 1-2 mix is the front. */
304 	SPEAKER(config, "speaker_1_2", 0.0, 0.0, 1.0);      // front
305 	SPEAKER(config, "speaker_3_7", -0.2, 0.0, 1.0);     // left
306 	SPEAKER(config, "speaker_5_6",  0.0, 0.0, -0.5);    // back
307 	SPEAKER(config, "speaker_4_8", 0.2, 0.0, 1.0);      // right
308 
309 	DISCRETE(config, m_discrete, sprint8_discrete);
310 	m_discrete->add_route(0, "speaker_1_2", 1.0);
311 	/* volumes on other channels defaulted to off, */
312 	/* user can turn them up if needed. */
313 	/* The game does not sound good with all channels mixed to stereo. */
314 	m_discrete->add_route(1, "speaker_3_7", 0.0);
315 	m_discrete->add_route(2, "speaker_5_6", 0.0);
316 	m_discrete->add_route(3, "speaker_4_8", 0.0);
317 
318 	f9334_device &latch(F9334(config, "latch"));
319 	latch.q_out_cb<0>().set(FUNC(sprint8_state::int_reset_w));
320 	latch.q_out_cb<1>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_CRASH_EN>));
321 	latch.q_out_cb<2>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_SCREECH_EN>));
322 	latch.q_out_cb<5>().set(FUNC(sprint8_state::team_w));
323 	latch.q_out_cb<6>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_ATTRACT_EN>));
324 
325 	f9334_device &motor(F9334(config, "motor"));
326 	motor.q_out_cb<0>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR1_EN>));
327 	motor.q_out_cb<1>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR2_EN>));
328 	motor.q_out_cb<2>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR3_EN>));
329 	motor.q_out_cb<3>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR4_EN>));
330 	motor.q_out_cb<4>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR5_EN>));
331 	motor.q_out_cb<5>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR6_EN>));
332 	motor.q_out_cb<6>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR7_EN>));
333 	motor.q_out_cb<7>().set(m_discrete, FUNC(discrete_device::write_line<SPRINT8_MOTOR8_EN>));
334 }
335 ;
336