1 // license:BSD-3-Clause
2 // copyright-holders:Zsolt Vasvari
3 /***************************************************************************
4 
5     Exidy Car Polo hardware
6 
7     driver by Zsolt Vasvari
8 
9 ****************************************************************************/
10 
11 #include "emu.h"
12 #include "cpu/m6502/m6502.h"
13 #include "machine/6821pia.h"
14 #include "includes/carpolo.h"
15 
16 
17 /*************************************
18  *
19  *  Interrupt system
20  *
21  *************************************/
22 
23 /* the interrupt system consists of a 74148 priority encoder
24    with the following interrupt priorites.  A lower number
25    indicates a lower priority (coins handled first):
26 
27     7 - player 1 coin
28     6 - player 2 coin
29     5 - player 3 coin
30     4 - player 4 coin
31     3 - ball/screen object collision
32     2 - car/car collision
33     1 - car/goal collision
34     0 - timer               (bit 4=0, bit 6=0)
35     0 - car/ball collision  (bit 4=0, bit 6=1)
36     0 - car/border          (bit 4=1, bit 6=1)
37 
38    After the interrupt is serviced, the code clears the
39    priority encoder's appropriate line by pulling it HI.  This
40    can trigger another interrupt immediately if there were
41    lower priority lines LO.
42 
43    The four coin inputs are latched via 7474 flip-flop's. */
44 
45 #define COIN1_PRIORITY_LINE         7
46 #define COIN2_PRIORITY_LINE         6
47 #define COIN3_PRIORITY_LINE         5
48 #define COIN4_PRIORITY_LINE         4
49 #define BALL_SCREEN_PRIORITY_LINE   3
50 #define CAR_CAR_PRIORITY_LINE       2
51 #define CAR_GOAL_PRIORITY_LINE      1
52 #define PRI0_PRIORTITY_LINE         0
53 
54 /* priority 0 controls three different things */
55 #define TIMER_EXTRA_BITS            0x00
56 #define CAR_BALL_EXTRA_BITS         0x40
57 #define CAR_BORDER_EXTRA_BITS       0x50
58 
59 
ttl74148_3s_cb(uint8_t data)60 void carpolo_state::ttl74148_3s_cb(uint8_t data)
61 {
62 	m_maincpu->set_input_line(M6502_IRQ_LINE, m_ttl74148_3s->output_valid_r() ? CLEAR_LINE : ASSERT_LINE);
63 }
64 
65 
66 /* the outputs of the flip-flops are connected to the priority encoder */
WRITE_LINE_MEMBER(carpolo_state::ttl7474_2s_1_q_cb)67 WRITE_LINE_MEMBER(carpolo_state::ttl7474_2s_1_q_cb)
68 {
69 	m_ttl74148_3s->input_line_w(COIN1_PRIORITY_LINE, state);
70 	m_ttl74148_3s->update();
71 }
72 
WRITE_LINE_MEMBER(carpolo_state::ttl7474_2s_2_q_cb)73 WRITE_LINE_MEMBER(carpolo_state::ttl7474_2s_2_q_cb)
74 {
75 	m_ttl74148_3s->input_line_w(COIN2_PRIORITY_LINE, state);
76 	m_ttl74148_3s->update();
77 }
78 
WRITE_LINE_MEMBER(carpolo_state::ttl7474_2u_1_q_cb)79 WRITE_LINE_MEMBER(carpolo_state::ttl7474_2u_1_q_cb)
80 {
81 	m_ttl74148_3s->input_line_w(COIN3_PRIORITY_LINE, state);
82 	m_ttl74148_3s->update();
83 }
84 
WRITE_LINE_MEMBER(carpolo_state::ttl7474_2u_2_q_cb)85 WRITE_LINE_MEMBER(carpolo_state::ttl7474_2u_2_q_cb)
86 {
87 	m_ttl74148_3s->input_line_w(COIN4_PRIORITY_LINE, state);
88 	m_ttl74148_3s->update();
89 }
90 
91 
generate_ball_screen_interrupt(uint8_t cause)92 void carpolo_state::generate_ball_screen_interrupt(uint8_t cause)
93 {
94 	m_ball_screen_collision_cause = cause;
95 
96 	m_ttl74148_3s->input_line_w(BALL_SCREEN_PRIORITY_LINE, 0);
97 	m_ttl74148_3s->update();
98 }
99 
generate_car_car_interrupt(int car1,int car2)100 void carpolo_state::generate_car_car_interrupt(int car1, int car2)
101 {
102 	m_car_car_collision_cause = ~((1 << (3 - car1)) | (1 << (3 - car2)));
103 
104 	m_ttl74148_3s->input_line_w(CAR_CAR_PRIORITY_LINE, 0);
105 	m_ttl74148_3s->update();
106 }
107 
generate_car_goal_interrupt(int car,int right_goal)108 void carpolo_state::generate_car_goal_interrupt(int car, int right_goal)
109 {
110 	m_car_goal_collision_cause = car | (right_goal ? 0x08 : 0x00);
111 
112 	m_ttl74148_3s->input_line_w(CAR_GOAL_PRIORITY_LINE, 0);
113 	m_ttl74148_3s->update();
114 }
115 
generate_car_ball_interrupt(int car,int car_x,int car_y)116 void carpolo_state::generate_car_ball_interrupt(int car, int car_x, int car_y)
117 {
118 	m_car_ball_collision_cause = car;
119 	m_car_ball_collision_x = car_x;
120 	m_car_ball_collision_y = car_y;
121 
122 	m_priority_0_extension = CAR_BALL_EXTRA_BITS;
123 
124 	m_ttl74148_3s->input_line_w(PRI0_PRIORTITY_LINE, 0);
125 	m_ttl74148_3s->update();
126 }
127 
generate_car_border_interrupt(int car,int horizontal_border)128 void carpolo_state::generate_car_border_interrupt(int car, int horizontal_border)
129 {
130 	m_car_border_collision_cause = car | (horizontal_border ? 0x04 : 0x00);
131 
132 	m_priority_0_extension = CAR_BORDER_EXTRA_BITS;
133 
134 	m_ttl74148_3s->input_line_w(PRI0_PRIORTITY_LINE, 0);
135 	m_ttl74148_3s->update();
136 }
137 
138 
ball_screen_collision_cause_r()139 uint8_t carpolo_state::ball_screen_collision_cause_r()
140 {
141 	/* bit 0 - 0=ball collided with border
142 	   bit 1 - 0=ball collided with goal
143 	   bit 2 - 0=ball collided with score area
144 	   bit 3 - which goal/score collided (0=left, 1=right) */
145 	return m_ball_screen_collision_cause;
146 }
147 
car_ball_collision_x_r()148 uint8_t carpolo_state::car_ball_collision_x_r()
149 {
150 	/* the x coordinate of the colliding pixel */
151 	return m_car_ball_collision_x;
152 }
153 
car_ball_collision_y_r()154 uint8_t carpolo_state::car_ball_collision_y_r()
155 {
156 	/* the y coordinate of the colliding pixel */
157 	return m_car_ball_collision_y;
158 }
159 
car_car_collision_cause_r()160 uint8_t carpolo_state::car_car_collision_cause_r()
161 {
162 	/* bit 0 - car 4 collided
163 	   bit 1 - car 3 collided
164 	   bit 2 - car 2 collided
165 	   bit 3 - car 1 collided */
166 	return m_car_car_collision_cause;
167 }
168 
car_goal_collision_cause_r()169 uint8_t carpolo_state::car_goal_collision_cause_r()
170 {
171 	/* bit 0-1 - which car collided
172 	   bit 2   - horizontal timing bit 1TEC4 (not accessed)
173 	   bit 3   - which goal collided (0=left, 1=right) */
174 	return m_car_goal_collision_cause;
175 }
176 
car_ball_collision_cause_r()177 uint8_t carpolo_state::car_ball_collision_cause_r()
178 {
179 	/* bit 0-1 - which car collided
180 	   bit 2-3 - unconnected */
181 	return m_car_ball_collision_cause;
182 }
183 
car_border_collision_cause_r()184 uint8_t carpolo_state::car_border_collision_cause_r()
185 {
186 	/* bit 0-1 - which car collided
187 	   bit 2   - 0=vertical border, 1=horizontal border */
188 	return m_car_border_collision_cause;
189 }
190 
191 
interrupt_cause_r()192 uint8_t carpolo_state::interrupt_cause_r()
193 {
194 	/* the output of the 148 goes to bits 1-3 (which is priority ^ 7) */
195 	return (m_ttl74148_3s->output_r() << 1) | m_priority_0_extension;
196 }
197 
timer_tick()198 void carpolo_state::timer_tick()
199 {
200 	/* cause the timer interrupt */
201 	m_ttl74148_3s->input_line_w(PRI0_PRIORTITY_LINE, 0);
202 	m_priority_0_extension = TIMER_EXTRA_BITS;
203 
204 	m_ttl74148_3s->update();
205 
206 	/* check the coins here as well - they drive the clock of the flip-flops */
207 	uint8_t port_value = m_in[0]->read();
208 
209 	m_ttl7474_2s_1->clock_w((port_value & 0x01) >> 0);
210 	m_ttl7474_2s_2->clock_w((port_value & 0x02) >> 1);
211 	m_ttl7474_2u_1->clock_w((port_value & 0x04) >> 2);
212 	m_ttl7474_2u_2->clock_w((port_value & 0x08) >> 3);
213 
214 	/* read the steering controls */
215 	for (int player = 0; player < 4; player++)
216 	{
217 		ttl7474_device *movement_flip_flop;
218 		ttl7474_device *dir_flip_flop;
219 
220 		switch (player)
221 		{
222 			default:
223 			case 0: movement_flip_flop = m_ttl7474_1f_1;    dir_flip_flop = m_ttl7474_1f_2; break;
224 			case 1: movement_flip_flop = m_ttl7474_1d_1;    dir_flip_flop = m_ttl7474_1d_2; break;
225 			case 2: movement_flip_flop = m_ttl7474_1c_1;    dir_flip_flop = m_ttl7474_1c_2; break;
226 			case 3: movement_flip_flop = m_ttl7474_1a_1;    dir_flip_flop = m_ttl7474_1a_2; break;
227 		}
228 
229 		port_value = m_dial[player]->read();
230 
231 		if (port_value != m_last_wheel_value[player])
232 		{
233 			/* set the movement direction */
234 			dir_flip_flop->d_w(((port_value - m_last_wheel_value[player]) & 0x80) ? 1 : 0);
235 
236 			m_last_wheel_value[player] = port_value;
237 		}
238 
239 		/* as the wheel moves, both flip-flops are clocked */
240 		movement_flip_flop->clock_w(port_value & 0x01);
241 		dir_flip_flop->clock_w(     port_value & 0x01);
242 	}
243 
244 
245 
246 	/* finally read the accelerator pedals */
247 	port_value = m_pedals->read();
248 
249 	// one line indicates if the pedal is pressed and the other
250 	// how much, resulting in only two different possible levels
251 	m_ttl74153_1k->i0a_w(BIT(port_value, 0) | BIT(port_value, 1));
252 	m_ttl74153_1k->i1a_w(BIT(port_value, 2) | BIT(port_value, 3));
253 	m_ttl74153_1k->i2a_w(BIT(port_value, 4) | BIT(port_value, 5));
254 	m_ttl74153_1k->i3a_w(BIT(port_value, 6) | BIT(port_value, 7));
255 	m_ttl74153_1k->i0b_w(BIT(port_value, 1));
256 	m_ttl74153_1k->i1b_w(BIT(port_value, 3));
257 	m_ttl74153_1k->i2b_w(BIT(port_value, 5));
258 	m_ttl74153_1k->i3b_w(BIT(port_value, 7));
259 }
260 
261 // FIXME: Remove trampolines
262 
WRITE_LINE_MEMBER(carpolo_state::coin1_interrupt_clear_w)263 WRITE_LINE_MEMBER(carpolo_state::coin1_interrupt_clear_w)
264 {
265 	m_ttl7474_2s_1->clear_w(state);
266 }
267 
WRITE_LINE_MEMBER(carpolo_state::coin2_interrupt_clear_w)268 WRITE_LINE_MEMBER(carpolo_state::coin2_interrupt_clear_w)
269 {
270 	m_ttl7474_2s_2->clear_w(state);
271 }
272 
WRITE_LINE_MEMBER(carpolo_state::coin3_interrupt_clear_w)273 WRITE_LINE_MEMBER(carpolo_state::coin3_interrupt_clear_w)
274 {
275 	m_ttl7474_2u_1->clear_w(state);
276 }
277 
WRITE_LINE_MEMBER(carpolo_state::coin4_interrupt_clear_w)278 WRITE_LINE_MEMBER(carpolo_state::coin4_interrupt_clear_w)
279 {
280 	m_ttl7474_2u_2->clear_w(state);
281 }
282 
ball_screen_interrupt_clear_w(uint8_t data)283 void carpolo_state::ball_screen_interrupt_clear_w(uint8_t data)
284 {
285 	m_ttl74148_3s->input_line_w(BALL_SCREEN_PRIORITY_LINE, 1);
286 	m_ttl74148_3s->update();
287 }
288 
car_car_interrupt_clear_w(uint8_t data)289 void carpolo_state::car_car_interrupt_clear_w(uint8_t data)
290 {
291 	m_ttl74148_3s->input_line_w(CAR_CAR_PRIORITY_LINE, 1);
292 	m_ttl74148_3s->update();
293 }
294 
car_goal_interrupt_clear_w(uint8_t data)295 void carpolo_state::car_goal_interrupt_clear_w(uint8_t data)
296 {
297 	m_ttl74148_3s->input_line_w(CAR_GOAL_PRIORITY_LINE, 1);
298 	m_ttl74148_3s->update();
299 }
300 
car_ball_interrupt_clear_w(uint8_t data)301 void carpolo_state::car_ball_interrupt_clear_w(uint8_t data)
302 {
303 	m_ttl74148_3s->input_line_w(PRI0_PRIORTITY_LINE, 1);
304 	m_ttl74148_3s->update();
305 }
306 
car_border_interrupt_clear_w(uint8_t data)307 void carpolo_state::car_border_interrupt_clear_w(uint8_t data)
308 {
309 	m_ttl74148_3s->input_line_w(PRI0_PRIORTITY_LINE, 1);
310 	m_ttl74148_3s->update();
311 }
312 
timer_interrupt_clear_w(uint8_t data)313 void carpolo_state::timer_interrupt_clear_w(uint8_t data)
314 {
315 	m_ttl74148_3s->input_line_w(PRI0_PRIORTITY_LINE, 1);
316 	m_ttl74148_3s->update();
317 }
318 
319 
320 /*************************************
321  *
322  *  Input port handling
323  *
324  *************************************/
325 
WRITE_LINE_MEMBER(carpolo_state::ls153_za_w)326 WRITE_LINE_MEMBER( carpolo_state::ls153_za_w )
327 {
328 	m_ls153_za = state;
329 }
330 
WRITE_LINE_MEMBER(carpolo_state::ls153_zb_w)331 WRITE_LINE_MEMBER( carpolo_state::ls153_zb_w )
332 {
333 	m_ls153_zb = state;
334 }
335 
pia_0_port_a_w(uint8_t data)336 void carpolo_state::pia_0_port_a_w(uint8_t data)
337 {
338 	/* bit 0 - Coin counter
339 	   bit 1 - Player 4 crash sound
340 	   bit 2 - Player 3 crash sound
341 	   bit 3 - Clear steering wheel logic
342 	   bit 4 - Player 2 crash sound
343 	   bit 5 - Score pulse sound
344 	   bit 6 - Player 1 crash sound
345 	   bit 7 - Ball hit pulse sound */
346 
347 	machine().bookkeeping().coin_counter_w(0, data & 0x01);
348 
349 
350 	m_ttl7474_1f_1->clear_w((data & 0x08) >> 3);
351 	m_ttl7474_1d_1->clear_w((data & 0x08) >> 3);
352 	m_ttl7474_1c_1->clear_w((data & 0x08) >> 3);
353 	m_ttl7474_1a_1->clear_w((data & 0x08) >> 3);
354 }
355 
356 
pia_0_port_b_w(uint8_t data)357 void carpolo_state::pia_0_port_b_w(uint8_t data)
358 {
359 	/* bit 0 - Strobe speed bits sound
360 	   bit 1 - Speed bit 0 sound
361 	   bit 2 - Speed bit 1 sound
362 	   bit 3 - Speed bit 2 sound
363 	   bit 6 - Select pedal 0
364 	   bit 7 - Select pdeal 1 */
365 
366 	m_ttl74153_1k->s0_w(BIT(data, 6));
367 	m_ttl74153_1k->s1_w(BIT(data, 7));
368 }
369 
pia_0_port_b_r()370 uint8_t carpolo_state::pia_0_port_b_r()
371 {
372 	/* bit 4 - Pedal bit 0
373 	   bit 5 - Pedal bit 1 */
374 
375 	return (m_ls153_za << 5) | (m_ls153_zb << 4);
376 }
377 
378 
pia_1_port_a_r()379 uint8_t carpolo_state::pia_1_port_a_r()
380 {
381 	uint8_t ret;
382 
383 	/* bit 0 - Player 4 steering input (left or right)
384 	   bit 1 - Player 3 steering input (left or right)
385 	   bit 2 - Player 2 steering input (left or right)
386 	   bit 3 - Player 1 steering input (left or right)
387 	   bit 4 - Player 4 forward/reverse input
388 	   bit 5 - Player 3 forward/reverse input
389 	   bit 6 - Player 2 forward/reverse input
390 	   bit 7 - Player 1 forward/reverse input */
391 
392 	ret = (m_ttl7474_1a_2->output_r() ? 0x01 : 0x00) |
393 			(m_ttl7474_1c_2->output_r() ? 0x02 : 0x00) |
394 			(m_ttl7474_1d_2->output_r() ? 0x04 : 0x00) |
395 			(m_ttl7474_1f_2->output_r() ? 0x08 : 0x00) |
396 			(m_in[2]->read() & 0xf0);
397 
398 	return ret;
399 }
400 
401 
pia_1_port_b_r()402 uint8_t carpolo_state::pia_1_port_b_r()
403 {
404 	uint8_t ret;
405 
406 	/* bit 4 - Player 4 steering input (wheel moving or stopped)
407 	   bit 5 - Player 3 steering input (wheel moving or stopped)
408 	   bit 6 - Player 2 steering input (wheel moving or stopped)
409 	   bit 7 - Player 1 steering input (wheel moving or stopped) */
410 
411 	ret = (m_ttl7474_1a_1->output_r() ? 0x10 : 0x00) |
412 			(m_ttl7474_1c_1->output_r() ? 0x20 : 0x00) |
413 			(m_ttl7474_1d_1->output_r() ? 0x40 : 0x00) |
414 			(m_ttl7474_1f_1->output_r() ? 0x80 : 0x00);
415 
416 	return ret;
417 }
418 
machine_start()419 void carpolo_state::machine_start()
420 {
421 	save_item(NAME(m_ball_screen_collision_cause));
422 	save_item(NAME(m_car_ball_collision_x));
423 	save_item(NAME(m_car_ball_collision_y));
424 	save_item(NAME(m_car_car_collision_cause));
425 	save_item(NAME(m_car_goal_collision_cause));
426 	save_item(NAME(m_car_ball_collision_cause));
427 	save_item(NAME(m_car_border_collision_cause));
428 	save_item(NAME(m_priority_0_extension));
429 	save_item(NAME(m_last_wheel_value));
430 }
431 
machine_reset()432 void carpolo_state::machine_reset()
433 {
434 	/* set up the priority encoder */
435 	m_ttl74148_3s->enable_input_w(0);  /* always enabled */
436 
437 	/* set up the coin handling flip-flops */
438 	m_ttl7474_2s_1->d_w     (1);
439 	m_ttl7474_2s_1->preset_w(1);
440 
441 	m_ttl7474_2s_2->d_w     (1);
442 	m_ttl7474_2s_2->preset_w(1);
443 
444 	m_ttl7474_2u_1->d_w     (1);
445 	m_ttl7474_2u_1->preset_w(1);
446 
447 	m_ttl7474_2u_2->d_w     (1);
448 	m_ttl7474_2u_2->preset_w(1);
449 
450 
451 	/* set up the steering handling flip-flops */
452 	m_ttl7474_1f_1->d_w     (1);
453 	m_ttl7474_1f_1->preset_w(1);
454 
455 	m_ttl7474_1f_2->clear_w (1);
456 	m_ttl7474_1f_2->preset_w(1);
457 
458 	m_ttl7474_1d_1->d_w     (1);
459 	m_ttl7474_1d_1->preset_w(1);
460 
461 	m_ttl7474_1d_2->clear_w (1);
462 	m_ttl7474_1d_2->preset_w(1);
463 
464 	m_ttl7474_1c_1->d_w     (1);
465 	m_ttl7474_1c_1->preset_w(1);
466 
467 	m_ttl7474_1c_2->clear_w (1);
468 	m_ttl7474_1c_2->preset_w(1);
469 
470 	m_ttl7474_1a_1->d_w     (1);
471 	m_ttl7474_1a_1->preset_w(1);
472 
473 	m_ttl7474_1a_2->clear_w (1);
474 	m_ttl7474_1a_2->preset_w(1);
475 }
476