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