1 // license:GPL-2.0+
2 // copyright-holders:FelipeSanches, Sandro Ronco
3 //
4 // Namco Gator Panic (Wani Wani Panic)
5 // USA version distributed by Data East and renamed "Wacky Gator"
6 //
7 // Driver by Sandro Ronco and
8 // Felipe Correa da Silva Sanches <juca@members.fsf.org>
9 //
10
11 /*
12 Most of this driver is based on guessing since I do not have access to the actual pcb for this game.
13 Once we get a pcb, please review the correctness of the code in this driver before deleting this comment.
14
15 TODO:
16 - IRQ and NMI sources are unknown
17 - proper PMM8713 and steppers emulation
18 */
19
20 #include "emu.h"
21 #include "cpu/m6809/m6809.h"
22 #include "machine/i8255.h"
23 #include "machine/pit8253.h"
24 #include "machine/ticket.h"
25 #include "machine/timer.h"
26 #include "sound/msm5205.h"
27 #include "sound/ym2413.h"
28 #include "speaker.h"
29
30 #include "wackygtr.lh"
31
32
33 class wackygtr_state : public driver_device
34 {
35 public:
wackygtr_state(const machine_config & mconfig,device_type type,const char * tag)36 wackygtr_state(const machine_config &mconfig, device_type type, const char *tag) :
37 driver_device(mconfig, type, tag),
38 m_maincpu(*this, "maincpu"),
39 m_msm(*this, "msm"),
40 m_pit8253(*this, "pit8253%u", 0U),
41 m_ticket(*this, "ticket"),
42 m_samples(*this, "oki"),
43 m_alligator(*this, "alligator%u", 0U),
44 m_digit(*this, "digit%u", 0U),
45 m_lamps(*this, "lamp%u", 0U)
46 { }
47
48 DECLARE_CUSTOM_INPUT_MEMBER(alligators_rear_sensors_r);
49 DECLARE_CUSTOM_INPUT_MEMBER(alligators_front_sensors_r);
50
51 void wackygtr(machine_config &config);
52
53 private:
54 virtual void machine_start() override;
55 virtual void machine_reset() override;
56
57 DECLARE_WRITE_LINE_MEMBER(adpcm_int);
58 void sample_ctrl_w(uint8_t data);
59 void alligators_ctrl1_w(uint8_t data);
60 void alligators_ctrl2_w(uint8_t data);
61
62 void set_lamps(int p, uint8_t value);
63 void status_lamps_w(uint8_t data);
timing_lamps_w(uint8_t data)64 template <unsigned N> void timing_lamps_w(uint8_t data) { set_lamps((N + 1) << 3, data); }
65
66 void set_digits(int p, uint8_t value);
disp_w(uint8_t data)67 template <unsigned N> void disp_w(uint8_t data) { set_digits(N << 1, data); }
68
69 void pmm8713_ck(int i, int state);
DECLARE_WRITE_LINE_MEMBER(alligator_ck)70 template <unsigned N> DECLARE_WRITE_LINE_MEMBER(alligator_ck) { pmm8713_ck(N, state); }
71
irq_ack_w(uint8_t data)72 void irq_ack_w(uint8_t data) { m_maincpu->set_input_line(M6809_IRQ_LINE, CLEAR_LINE); }
firq_ack_w(uint8_t data)73 void firq_ack_w(uint8_t data) { m_maincpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE); }
74
TIMER_DEVICE_CALLBACK_MEMBER(nmi_timer)75 TIMER_DEVICE_CALLBACK_MEMBER(nmi_timer) { m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero); }
76
77 void program_map(address_map &map);
78
79 required_device<cpu_device> m_maincpu;
80 required_device<msm5205_device> m_msm;
81 required_device_array<pit8253_device, 2> m_pit8253;
82 required_device<ticket_dispenser_device> m_ticket;
83 required_memory_region m_samples;
84 output_finder<5> m_alligator;
85 output_finder<8> m_digit;
86 output_finder<32> m_lamps;
87
88 int m_adpcm_sel;
89 uint16_t m_adpcm_pos;
90 uint8_t m_adpcm_ctrl;
91
92 uint8_t m_alligators_ctrl;
93 int m_motors_pos[5];
94 };
95
96
status_lamps_w(uint8_t data)97 void wackygtr_state::status_lamps_w(uint8_t data)
98 {
99 /*
100 ---x xxxx status lamps
101 --x- ---- game over lamp
102 -x-- ---- coin counter
103 x--- ---- ticket dispenser
104 */
105
106 set_lamps(0, data & 0x3f);
107
108 machine().bookkeeping().coin_counter_w(0, BIT(data, 6));
109 m_ticket->motor_w(BIT(data, 7));
110 }
111
sample_ctrl_w(uint8_t data)112 void wackygtr_state::sample_ctrl_w(uint8_t data)
113 {
114 /*
115 --xx xxxx sample index
116 -x-- ---- ???
117 x--- ---- 5205 reset
118 */
119
120 m_adpcm_ctrl = data;
121 m_adpcm_pos = (data & 0x3f) * 0x400;
122 m_adpcm_sel = 0;
123 m_msm->reset_w(BIT(data, 7));
124 }
125
alligators_ctrl1_w(uint8_t data)126 void wackygtr_state::alligators_ctrl1_w(uint8_t data)
127 {
128 m_pit8253[0]->write_gate0(BIT(data, 0));
129 m_pit8253[0]->write_gate1(BIT(data, 1));
130 m_pit8253[0]->write_gate2(BIT(data, 2));
131 m_pit8253[1]->write_gate1(BIT(data, 3));
132 m_pit8253[1]->write_gate2(BIT(data, 4));
133
134 machine().bookkeeping().coin_lockout_w(0, data & 0x40 ? 0 : 1);
135 }
136
alligators_ctrl2_w(uint8_t data)137 void wackygtr_state::alligators_ctrl2_w(uint8_t data)
138 {
139 /*
140 ---- ---x PMM8713 0 U/D
141 ---- --x- PMM8713 1 U/D
142 ---- -x-- PMM8713 2 U/D
143 ---- x--- PMM8713 3 U/D
144 ---x ---- PMM8713 4 U/D
145 */
146
147 m_alligators_ctrl = data & 0x1f;
148 }
149
pmm8713_ck(int i,int state)150 void wackygtr_state::pmm8713_ck(int i, int state)
151 {
152 if (state)
153 {
154 m_motors_pos[i] += (BIT(m_alligators_ctrl, i) ? +1 : -1);
155
156 int alligator_state = m_motors_pos[i] / 10;
157 if (alligator_state > 5) alligator_state = 5;
158 if (alligator_state < 0) alligator_state = 0;
159 m_alligator[i] = alligator_state;
160 }
161 }
162
CUSTOM_INPUT_MEMBER(wackygtr_state::alligators_rear_sensors_r)163 CUSTOM_INPUT_MEMBER(wackygtr_state::alligators_rear_sensors_r)
164 {
165 return ((m_motors_pos[0] < 10) ? 0x01 : 0) |
166 ((m_motors_pos[1] < 10) ? 0x02 : 0) |
167 ((m_motors_pos[2] < 10) ? 0x04 : 0) |
168 ((m_motors_pos[3] < 10) ? 0x08 : 0) |
169 ((m_motors_pos[4] < 10) ? 0x10 : 0) |
170 (m_alligators_ctrl ^ 0x1f);
171 }
172
CUSTOM_INPUT_MEMBER(wackygtr_state::alligators_front_sensors_r)173 CUSTOM_INPUT_MEMBER(wackygtr_state::alligators_front_sensors_r)
174 {
175 return ((m_motors_pos[0] < 5 || m_motors_pos[0] > 55) ? 0x01 : 0) |
176 ((m_motors_pos[1] < 5 || m_motors_pos[1] > 55) ? 0x02 : 0) |
177 ((m_motors_pos[2] < 5 || m_motors_pos[2] > 55) ? 0x04 : 0) |
178 ((m_motors_pos[3] < 5 || m_motors_pos[3] > 55) ? 0x08 : 0) |
179 ((m_motors_pos[4] < 5 || m_motors_pos[4] > 55) ? 0x10 : 0);
180 }
181
machine_start()182 void wackygtr_state::machine_start()
183 {
184 m_alligator.resolve();
185 m_digit.resolve();
186 m_lamps.resolve();
187
188 save_item(NAME(m_adpcm_sel));
189 save_item(NAME(m_adpcm_pos));
190 save_item(NAME(m_adpcm_ctrl));
191 save_item(NAME(m_alligators_ctrl));
192 save_item(NAME(m_motors_pos));
193 }
194
machine_reset()195 void wackygtr_state::machine_reset()
196 {
197 m_adpcm_pos = 0;
198 m_adpcm_sel = 0;
199 m_adpcm_ctrl = 0x80;
200 }
201
set_digits(int p,uint8_t value)202 void wackygtr_state::set_digits(int p, uint8_t value)
203 {
204 static constexpr uint8_t bcd2hex[] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x67, 0, 0, 0, 0, 0, 0 }; // not accurate
205 m_digit[p + 0] = bcd2hex[value & 0x0f];
206 m_digit[p + 1] = bcd2hex[(value >> 4) & 0x0f];
207 }
208
set_lamps(int p,uint8_t value)209 void wackygtr_state::set_lamps(int p, uint8_t value)
210 {
211 for (int i=0; i<8; i++)
212 m_lamps[p + i] = BIT(value, i);
213 }
214
215 static INPUT_PORTS_START( wackygtr )
216 PORT_START("IN0")
PORT_CUSTOM_MEMBER(wackygtr_state,alligators_rear_sensors_r)217 PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(wackygtr_state, alligators_rear_sensors_r)
218 PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_SERVICE)
219 PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_SERVICE1)
220 PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_COIN1)
221
222 PORT_START("IN1")
223 PORT_BIT(0x1f, IP_ACTIVE_LOW, IPT_CUSTOM) PORT_CUSTOM_MEMBER(wackygtr_state, alligators_front_sensors_r)
224 PORT_DIPNAME( 0xe0, 0x00, DEF_STR( Coin_A ) ) PORT_DIPLOCATION("SW:1,2,3")
225 PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) )
226 PORT_DIPSETTING( 0x20, DEF_STR( 1C_1C ) )
227 PORT_DIPSETTING( 0x40, DEF_STR( 1C_1C ) )
228 PORT_DIPSETTING( 0x60, DEF_STR( 2C_1C ) )
229 PORT_DIPSETTING( 0x80, DEF_STR( 1C_1C ) )
230 PORT_DIPSETTING( 0xa0, DEF_STR( 2C_1C ) )
231 PORT_DIPSETTING( 0xc0, DEF_STR( 1C_1C ) )
232 PORT_DIPSETTING( 0xe0, DEF_STR( 2C_1C ) )
233
234 PORT_START("IN2")
235 PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_POKER_HOLD1)
236 PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_POKER_HOLD2)
237 PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_POKER_HOLD3)
238 PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_POKER_HOLD4)
239 PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_POKER_HOLD5)
240 PORT_DIPNAME( 0x20, 0x20, DEF_STR( Test ) ) PORT_DIPLOCATION("SW:4") // For factory Test use ONLY! Do not change
241 PORT_DIPSETTING( 0x00, DEF_STR( On ) )
242 PORT_DIPSETTING( 0x20, DEF_STR( Off ) )
243 PORT_DIPNAME( 0x40, 0x00, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION("SW:5")
244 PORT_DIPSETTING( 0x00, DEF_STR( On ) )
245 PORT_DIPSETTING( 0x40, DEF_STR( Off ) )
246 PORT_DIPNAME( 0x80, 0x80, DEF_STR( Test ) ) PORT_DIPLOCATION("SW:6") // For factory Test use ONLY! Do not change
247 PORT_DIPSETTING( 0x00, DEF_STR( On ) )
248 PORT_DIPSETTING( 0x80, DEF_STR( Off ) )
249 INPUT_PORTS_END
250
251 WRITE_LINE_MEMBER(wackygtr_state::adpcm_int)
252 {
253 if (!(m_adpcm_ctrl & 0x80))
254 {
255 uint8_t data = m_samples->base()[m_adpcm_pos & 0xffff];
256 m_msm->data_w((m_adpcm_sel ? data : (data >> 4)) & 0x0f);
257 m_adpcm_pos += m_adpcm_sel;
258 m_adpcm_sel ^= 1;
259 }
260 }
261
program_map(address_map & map)262 void wackygtr_state::program_map(address_map &map)
263 {
264 map(0x0200, 0x0200).nopr().w(FUNC(wackygtr_state::irq_ack_w));
265 map(0x0400, 0x0400).nopr().w(FUNC(wackygtr_state::firq_ack_w));
266 map(0x0600, 0x0600).w(FUNC(wackygtr_state::disp_w<0>));
267 map(0x0800, 0x0800).w(FUNC(wackygtr_state::disp_w<1>));
268 map(0x0a00, 0x0a00).w(FUNC(wackygtr_state::disp_w<2>));
269 map(0x0c00, 0x0c00).w(FUNC(wackygtr_state::disp_w<3>));
270 map(0x0e00, 0x0e00).w(FUNC(wackygtr_state::sample_ctrl_w));
271
272 map(0x1000, 0x1001).w("ymsnd", FUNC(ym2413_device::write));
273
274 map(0x2000, 0x2003).rw(m_pit8253[0], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
275 map(0x3000, 0x3003).rw(m_pit8253[1], FUNC(pit8253_device::read), FUNC(pit8253_device::write));
276
277 map(0x4000, 0x4003).rw("i8255_0", FUNC(i8255_device::read), FUNC(i8255_device::write));
278 map(0x5000, 0x5003).rw("i8255_1", FUNC(i8255_device::read), FUNC(i8255_device::write));
279 map(0x6000, 0x6003).rw("i8255_2", FUNC(i8255_device::read), FUNC(i8255_device::write));
280
281 map(0x7000, 0x7fff).ram();
282 map(0x8000, 0xffff).rom();
283 }
284
wackygtr(machine_config & config)285 void wackygtr_state::wackygtr(machine_config &config)
286 {
287 MC6809(config, m_maincpu, XTAL(3'579'545)); // HD68B09P
288 m_maincpu->set_addrmap(AS_PROGRAM, &wackygtr_state::program_map);
289 m_maincpu->set_periodic_int(FUNC(wackygtr_state::irq0_line_assert), attotime::from_hz(50)); // FIXME
290
291 TIMER(config, "nmi_timer").configure_periodic(FUNC(wackygtr_state::nmi_timer), attotime::from_hz(100)); // FIXME
292
293 /* Video */
294 config.set_default_layout(layout_wackygtr);
295
296 /* Sound */
297 SPEAKER(config, "mono").front_center();
298 MSM5205(config, m_msm, XTAL(384'000));
299 m_msm->vck_legacy_callback().set(FUNC(wackygtr_state::adpcm_int)); /* IRQ handler */
300 m_msm->set_prescaler_selector(msm5205_device::S48_4B); /* 8 KHz, 4 Bits */
301 m_msm->add_route(ALL_OUTPUTS, "mono", 1.0);
302
303 ym2413_device &ymsnd(YM2413(config, "ymsnd", XTAL(3'579'545)));
304 ymsnd.add_route(ALL_OUTPUTS, "mono", 1.0);
305
306 i8255_device &ppi0(I8255(config, "i8255_0"));
307 ppi0.out_pa_callback().set(FUNC(wackygtr_state::status_lamps_w));
308 ppi0.out_pb_callback().set(FUNC(wackygtr_state::alligators_ctrl1_w));
309 ppi0.out_pc_callback().set(FUNC(wackygtr_state::alligators_ctrl2_w));
310
311 i8255_device &ppi1(I8255(config, "i8255_1"));
312 ppi1.out_pa_callback().set(FUNC(wackygtr_state::timing_lamps_w<0>));
313 ppi1.out_pb_callback().set(FUNC(wackygtr_state::timing_lamps_w<1>));
314 ppi1.out_pc_callback().set(FUNC(wackygtr_state::timing_lamps_w<2>));
315
316 i8255_device &ppi2(I8255(config, "i8255_2"));
317 ppi2.in_pa_callback().set_ioport("IN0");
318 ppi2.in_pb_callback().set_ioport("IN1");
319 ppi2.in_pc_callback().set_ioport("IN2");
320
321 PIT8253(config, m_pit8253[0], 0);
322 m_pit8253[0]->set_clk<0>(XTAL(3'579'545)/16); // this is a guess
323 m_pit8253[0]->out_handler<0>().set(FUNC(wackygtr_state::alligator_ck<0>));
324 m_pit8253[0]->set_clk<1>(XTAL(3'579'545)/16); // this is a guess
325 m_pit8253[0]->out_handler<1>().set(FUNC(wackygtr_state::alligator_ck<1>));
326 m_pit8253[0]->set_clk<2>(XTAL(3'579'545)/16); // this is a guess
327 m_pit8253[0]->out_handler<2>().set(FUNC(wackygtr_state::alligator_ck<2>));
328
329 PIT8253(config, m_pit8253[1], 0);
330 m_pit8253[1]->set_clk<0>(XTAL(3'579'545)/16); // this is a guess
331 m_pit8253[1]->out_handler<0>().set_inputline(m_maincpu, M6809_FIRQ_LINE);
332 m_pit8253[1]->set_clk<1>(XTAL(3'579'545)/16); // this is a guess
333 m_pit8253[1]->out_handler<1>().set(FUNC(wackygtr_state::alligator_ck<3>));
334 m_pit8253[1]->set_clk<2>(XTAL(3'579'545)/16); // this is a guess
335 m_pit8253[1]->out_handler<2>().set(FUNC(wackygtr_state::alligator_ck<4>));
336
337 TICKET_DISPENSER(config, "ticket", attotime::from_msec(200), TICKET_MOTOR_ACTIVE_HIGH, TICKET_STATUS_ACTIVE_HIGH);
338 }
339
340
341 ROM_START( wackygtr )
342 ROM_REGION(0x10000, "maincpu", 0)
343 ROM_LOAD("wp3-pr0.4d", 0x8000, 0x8000, CRC(71ca4437) SHA1(c7d948c5593e6053fd0a65601f6c06871f5861f0))
344
345 ROM_REGION(0x10000, "oki", 0)
346 ROM_LOAD("wp3-vo0.2h", 0x0000, 0x10000, CRC(91c7986f) SHA1(bc9fa0d41c1caa0f909a349f511d022b7e42c6cd))
347 ROM_END
348
349 GAME(1988, wackygtr, 0, wackygtr, wackygtr, wackygtr_state, empty_init, ROT0, "Namco (Data East license)", "Wacky Gator (US)", MACHINE_IS_SKELETON_MECHANICAL | MACHINE_CLICKABLE_ARTWORK)
350