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