1 // license:BSD-3-Clause
2 // copyright-holders:Stefan Jokisch
3 /***************************************************************************
4 
5     Atari Drag Race Driver
6 
7 ***************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/dragrace.h"
11 
12 #include "cpu/m6800/m6800.h"
13 #include "machine/74259.h"
14 #include "sound/discrete.h"
15 #include "speaker.h"
16 
17 #include "dragrace.lh"
18 
19 
TIMER_DEVICE_CALLBACK_MEMBER(dragrace_state::dragrace_frame_callback)20 TIMER_DEVICE_CALLBACK_MEMBER(dragrace_state::dragrace_frame_callback)
21 {
22 	static const char *const portnames[] = { "P1", "P2" };
23 
24 	for (int i = 0; i < 2; i++)
25 	{
26 		switch (ioport(portnames[i])->read())
27 		{
28 			case 0x01: m_gear[i] = 1; break;
29 			case 0x02: m_gear[i] = 2; break;
30 			case 0x04: m_gear[i] = 3; break;
31 			case 0x08: m_gear[i] = 4; break;
32 			case 0x10: m_gear[i] = 0; break;
33 		}
34 	}
35 	output().set_value("P1gear", m_gear[0]);
36 	output().set_value("P2gear", m_gear[1]);
37 
38 	/* watchdog is disabled during service mode */
39 	m_watchdog->watchdog_enable(ioport("IN0")->read() & 0x20);
40 }
41 
42 
speed1_w(uint8_t data)43 void dragrace_state::speed1_w(uint8_t data)
44 {
45 	unsigned freq = ~data & 0x1f;
46 	m_discrete->write(DRAGRACE_MOTOR1_DATA, freq);
47 
48 	// the tachometers are driven from the same frequency generator that creates the engine sound
49 	output().set_value("tachometer", freq);
50 }
51 
speed2_w(uint8_t data)52 void dragrace_state::speed2_w(uint8_t data)
53 {
54 	unsigned freq = ~data & 0x1f;
55 	m_discrete->write(DRAGRACE_MOTOR2_DATA, freq);
56 
57 	// the tachometers are driven from the same frequency generator that creates the engine sound
58 	output().set_value("tachometer2", freq);
59 }
60 
dragrace_input_r(offs_t offset)61 uint8_t dragrace_state::dragrace_input_r(offs_t offset)
62 {
63 	int val = ioport("IN2")->read();
64 	static const char *const portnames[] = { "IN0", "IN1" };
65 
66 	uint8_t maskA = 1 << (offset % 8);
67 	uint8_t maskB = 1 << (offset / 8);
68 
69 	for (int i = 0; i < 2; i++)
70 	{
71 		int in = ioport(portnames[i])->read();
72 
73 		if (m_gear[i] != 0)
74 			in &= ~(1 << m_gear[i]);
75 
76 		if (in & maskA)
77 			val |= 1 << i;
78 	}
79 
80 	return (val & maskB) ? 0xff : 0x7f;
81 }
82 
83 
dragrace_steering_r()84 uint8_t dragrace_state::dragrace_steering_r()
85 {
86 	int bitA[2];
87 	int bitB[2];
88 	static const char *const dialnames[] = { "DIAL1", "DIAL2" };
89 
90 	for (int i = 0; i < 2; i++)
91 	{
92 		int dial = ioport(dialnames[i])->read();
93 
94 		bitA[i] = ((dial + 1) / 2) & 1;
95 		bitB[i] = ((dial + 0) / 2) & 1;
96 	}
97 
98 	return
99 		(bitA[0] << 0) | (bitB[0] << 1) |
100 		(bitA[1] << 2) | (bitB[1] << 3);
101 }
102 
103 
dragrace_scanline_r()104 uint8_t dragrace_state::dragrace_scanline_r()
105 {
106 	return (m_screen->vpos() ^ 0xf0) | 0x0f;
107 }
108 
109 
dragrace_map(address_map & map)110 void dragrace_state::dragrace_map(address_map &map)
111 {
112 	map(0x0080, 0x00ff).ram();
113 	map(0x0800, 0x083f).r(FUNC(dragrace_state::dragrace_input_r));
114 	map(0x0900, 0x0907).w("latch_f5", FUNC(addressable_latch_device::write_d0));
115 	map(0x0908, 0x090f).w("latch_a5", FUNC(addressable_latch_device::write_d0));
116 	map(0x0910, 0x0917).w("latch_h5", FUNC(addressable_latch_device::write_d0));
117 	map(0x0918, 0x091f).w("latch_e5", FUNC(addressable_latch_device::write_d0));
118 	map(0x0920, 0x0927).w("latch_f5", FUNC(addressable_latch_device::clear));
119 	map(0x0928, 0x092f).w("latch_a5", FUNC(addressable_latch_device::clear));
120 	map(0x0930, 0x0937).w("latch_h5", FUNC(addressable_latch_device::clear));
121 	map(0x0938, 0x093f).w("latch_e5", FUNC(addressable_latch_device::clear));
122 	map(0x0a00, 0x0aff).writeonly().share("playfield_ram");
123 	map(0x0b00, 0x0bff).writeonly().share("position_ram");
124 	map(0x0c00, 0x0c00).r(FUNC(dragrace_state::dragrace_steering_r));
125 	map(0x0d00, 0x0d00).r(FUNC(dragrace_state::dragrace_scanline_r));
126 	map(0x0e00, 0x0eff).w(m_watchdog, FUNC(watchdog_timer_device::reset_w));
127 	map(0x1000, 0x1fff).rom(); /* program */
128 	map(0xf800, 0xffff).rom(); /* program mirror */
129 }
130 
131 
132 static INPUT_PORTS_START( dragrace )
133 	PORT_START("IN0")
134 	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Player 1 Gas") PORT_PLAYER(1)
135 	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 1 gear 1 */
136 	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 1 gear 2 */
137 	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 1 gear 3 */
138 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 1 gear 4 */
139 	PORT_SERVICE( 0x20, IP_ACTIVE_LOW )
140 	PORT_DIPNAME( 0xc0, 0x80, "Extended Play" )
141 	PORT_DIPSETTING( 0x00, "6.9 seconds" )
142 	PORT_DIPSETTING( 0x80, "5.9 seconds" )
143 	PORT_DIPSETTING( 0x40, "4.9 seconds" )
144 	PORT_DIPSETTING( 0xc0, "Never" )
145 
146 	PORT_START("IN1")
147 	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Player 2 Gas") PORT_PLAYER(2)
148 	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 2 gear 1 */
149 	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 2 gear 2 */
150 	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 2 gear 3 */
151 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) /* player 2 gear 4 */
152 	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED )
153 	PORT_DIPNAME( 0xc0, 0x80, "Number Of Heats" )
154 	PORT_DIPSETTING( 0xc0, "3" )
155 	PORT_DIPSETTING( 0x80, "4" )
156 	PORT_DIPSETTING( 0x00, "5" )
157 
158 	PORT_START("IN2")
159 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) /* IN0 connects here */
160 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) /* IN1 connects here */
161 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_COIN1 )
162 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_COIN2 )
163 	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_START1 )
164 	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_START2 )
165 	PORT_DIPNAME( 0xc0, 0x40, DEF_STR( Coinage ) )
166 	PORT_DIPSETTING( 0xc0, DEF_STR( 2C_1C ) )
167 	PORT_DIPSETTING( 0x40, DEF_STR( 1C_1C ) )
168 	PORT_DIPSETTING( 0x80, DEF_STR( 1C_2C ) )
169 	PORT_DIPSETTING( 0x00, DEF_STR( Free_Play ) )
170 
171 	PORT_START("DIAL1")
172 	PORT_BIT( 0xff, 0x00, IPT_DIAL_V ) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_PLAYER(1)
173 
174 	PORT_START("DIAL2")
175 	PORT_BIT( 0xff, 0x00, IPT_DIAL_V ) PORT_SENSITIVITY(25) PORT_KEYDELTA(10) PORT_PLAYER(2)
176 
177 	PORT_START("P1")
178 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Player 1 Gear 1") PORT_PLAYER(1)
179 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Player 1 Gear 2") PORT_PLAYER(1)
180 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Player 1 Gear 3") PORT_PLAYER(1)
181 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Player 1 Gear 4") PORT_PLAYER(1)
182 	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Player 1 Neutral") PORT_PLAYER(1)
183 
184 	PORT_START("P2")
185 	PORT_BIT(0x01, IP_ACTIVE_HIGH, IPT_BUTTON2 ) PORT_NAME("Player 2 Gear 1") PORT_PLAYER(2)
186 	PORT_BIT(0x02, IP_ACTIVE_HIGH, IPT_BUTTON3 ) PORT_NAME("Player 2 Gear 2") PORT_PLAYER(2)
187 	PORT_BIT(0x04, IP_ACTIVE_HIGH, IPT_BUTTON4 ) PORT_NAME("Player 2 Gear 3") PORT_PLAYER(2)
188 	PORT_BIT(0x08, IP_ACTIVE_HIGH, IPT_BUTTON5 ) PORT_NAME("Player 2 Gear 4") PORT_PLAYER(2)
189 	PORT_BIT(0x10, IP_ACTIVE_HIGH, IPT_BUTTON6 ) PORT_NAME("Player 2 Neutral") PORT_PLAYER(2)
190 
191 	PORT_START("MOTOR1")
192 	PORT_ADJUSTER( 81, "Motor 1 RPM" )
193 
194 	PORT_START("MOTOR2")
195 	PORT_ADJUSTER( 85, "Motor 2 RPM" )
196 INPUT_PORTS_END
197 
198 
199 static const gfx_layout dragrace_tile_layout1 =
200 {
201 	16, 16,   /* width, height */
202 	0x40,     /* total         */
203 	1,        /* planes        */
204 	{ 0 },    /* plane offsets */
205 	{
206 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
207 		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87
208 	},
209 	{
210 		0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38,
211 		0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78
212 	},
213 	0x100      /* increment */
214 };
215 
216 
217 static const gfx_layout dragrace_tile_layout2 =
218 {
219 	16, 16,   /* width, height */
220 	0x20,     /* total         */
221 	2,        /* planes        */
222 	{         /* plane offsets */
223 		0x0000, 0x2000
224 	},
225 	{
226 		0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
227 		0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87
228 	},
229 	{
230 		0x00, 0x08, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38,
231 		0x40, 0x48, 0x50, 0x58, 0x60, 0x68, 0x70, 0x78
232 	},
233 	0x100      /* increment */
234 };
235 
236 
237 static GFXDECODE_START( gfx_dragrace )
238 	GFXDECODE_ENTRY( "gfx1", 0, dragrace_tile_layout1, 0, 4 )
239 	GFXDECODE_ENTRY( "gfx2", 0, dragrace_tile_layout2, 8, 2 )
240 GFXDECODE_END
241 
242 
dragrace_palette(palette_device & palette) const243 void dragrace_state::dragrace_palette(palette_device &palette) const
244 {
245 	palette.set_pen_color(0, rgb_t(0xff, 0xff, 0xff));   // 2 color tiles
246 	palette.set_pen_color(1, rgb_t(0x00, 0x00, 0x00));
247 	palette.set_pen_color(2, rgb_t(0x00, 0x00, 0x00));
248 	palette.set_pen_color(3, rgb_t(0xff, 0xff, 0xff));
249 	palette.set_pen_color(4, rgb_t(0x00, 0x00, 0x00));
250 	palette.set_pen_color(5, rgb_t(0x00, 0x00, 0x00));
251 	palette.set_pen_color(6, rgb_t(0xff, 0xff, 0xff));
252 	palette.set_pen_color(7, rgb_t(0xff, 0xff, 0xff));
253 	palette.set_pen_color(8, rgb_t(0xff, 0xff, 0xff));   // 4 color tiles
254 	palette.set_pen_color(9, rgb_t(0xb0, 0xb0, 0xb0));
255 	palette.set_pen_color(10,rgb_t(0x5f, 0x5f, 0x5f));
256 	palette.set_pen_color(11,rgb_t(0x00, 0x00, 0x00));
257 	palette.set_pen_color(12,rgb_t(0xff, 0xff, 0xff));
258 	palette.set_pen_color(13,rgb_t(0x5f, 0x5f, 0x5f));
259 	palette.set_pen_color(14,rgb_t(0xb0, 0xb0, 0xb0));
260 	palette.set_pen_color(15,rgb_t(0x00, 0x00, 0x00));
261 }
262 
263 
machine_start()264 void dragrace_state::machine_start()
265 {
266 	save_item(NAME(m_gear));
267 }
268 
machine_reset()269 void dragrace_state::machine_reset()
270 {
271 	m_gear[0] = 0;
272 	m_gear[1] = 0;
273 }
274 
dragrace(machine_config & config)275 void dragrace_state::dragrace(machine_config &config)
276 {
277 	/* basic machine hardware */
278 	M6800(config, m_maincpu, 12.096_MHz_XTAL / 12);
279 	m_maincpu->set_addrmap(AS_PROGRAM, &dragrace_state::dragrace_map);
280 	m_maincpu->set_periodic_int(FUNC(dragrace_state::irq0_line_hold), attotime::from_hz(4*60));
281 
282 	WATCHDOG_TIMER(config, m_watchdog).set_vblank_count("screen", 8);
283 
284 	TIMER(config, "frame_timer").configure_periodic(FUNC(dragrace_state::dragrace_frame_callback), attotime::from_hz(60));
285 
286 	/* video hardware */
287 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
288 	m_screen->set_refresh_hz(60);
289 	m_screen->set_size(256, 262);
290 	m_screen->set_visarea(0, 255, 0, 239);
291 	m_screen->set_screen_update(FUNC(dragrace_state::screen_update_dragrace));
292 	m_screen->set_palette("palette");
293 
294 	GFXDECODE(config, m_gfxdecode, "palette", gfx_dragrace);
295 	PALETTE(config, "palette", FUNC(dragrace_state::dragrace_palette), 16);
296 
297 	/* sound hardware */
298 	SPEAKER(config, "lspeaker").front_left();
299 	SPEAKER(config, "rspeaker").front_right();
300 
301 	DISCRETE(config, m_discrete, dragrace_discrete);
302 	m_discrete->add_route(0, "lspeaker", 1.0);
303 	m_discrete->add_route(1, "rspeaker", 1.0);
304 
305 	f9334_device &latch_f5(F9334(config, "latch_f5")); // F5
306 	latch_f5.parallel_out_cb().set(FUNC(dragrace_state::speed1_w)).mask(0x1f); // set 3SPEED1-7SPEED1
307 	latch_f5.q_out_cb<5>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_EXPLODE1_EN>)); // Explosion1 enable
308 	latch_f5.q_out_cb<6>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_SCREECH1_EN>)); // Screech1 enable
309 
310 	f9334_device &latch_a5(F9334(config, "latch_a5")); // A5
311 	latch_a5.q_out_cb<1>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_KLEXPL1_EN>)); // KLEXPL1 enable
312 	latch_a5.q_out_cb<3>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_MOTOR1_EN>)); // Motor1 enable
313 	latch_a5.q_out_cb<4>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_ATTRACT_EN>)); // Attract enable
314 	latch_a5.q_out_cb<5>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_LOTONE_EN>)); // LoTone enable
315 	latch_a5.q_out_cb<7>().set_output("led0"); // Player 1 Start Lamp
316 
317 	f9334_device &latch_h5(F9334(config, "latch_h5")); // H5
318 	latch_h5.parallel_out_cb().set(FUNC(dragrace_state::speed2_w)).mask(0x1f); // set 3SPEED2-7SPEED2
319 	latch_h5.q_out_cb<5>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_EXPLODE2_EN>)); // Explosion2 enable
320 	latch_h5.q_out_cb<6>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_SCREECH2_EN>)); // Screech2 enable
321 
322 	f9334_device &latch_e5(F9334(config, "latch_e5")); // E5
323 	latch_e5.q_out_cb<1>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_KLEXPL2_EN>)); // KLEXPL2 enable
324 	latch_e5.q_out_cb<3>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_MOTOR2_EN>)); // Motor2 enable
325 	latch_e5.q_out_cb<5>().set(m_discrete, FUNC(discrete_device::write_line<DRAGRACE_HITONE_EN>)); // HiTone enable
326 	latch_e5.q_out_cb<7>().set_output("led1"); // Player 2 Start Lamp
327 }
328 
329 
330 ROM_START( dragrace )
331 	ROM_REGION( 0x10000, "maincpu", 0 )
332 	ROM_LOAD( "8513.c1", 0x1000, 0x0800, CRC(543bbb30) SHA1(646a41d1124c8365f07a93de38af007895d7d263) )
333 	ROM_LOAD( "8514.a1", 0x1800, 0x0800, CRC(ad218690) SHA1(08ba5f4fa4c75d8dad1a7162888d44b3349cbbe4) )
334 	ROM_RELOAD(          0xf800, 0x0800 )
335 
336 	ROM_REGION( 0x800, "gfx1", 0 )   /* 2 color tiles */
337 	ROM_LOAD( "8519dr.j0", 0x000, 0x200, CRC(aa221ba0) SHA1(450acbf349d77a790a25f3e303c31b38cc426a38) )
338 	ROM_LOAD( "8521dr.k0", 0x200, 0x200, CRC(0cb33f12) SHA1(d50cb55391aec03e064eecad1624d50d4c30ccab) )
339 	ROM_LOAD( "8520dr.r0", 0x400, 0x200, CRC(ee1ae6a7) SHA1(83491095260c8b7c616ff17ec1e888d05620f166) )
340 
341 	ROM_REGION( 0x800, "gfx2", 0 )   /* 4 color tiles */
342 	ROM_LOAD( "8515dr.e0", 0x000, 0x200, CRC(9510a59e) SHA1(aea0782b919279efe55a07007bd55a16f7f59239) )
343 	ROM_LOAD( "8517dr.h0", 0x200, 0x200, CRC(8b5bff1f) SHA1(fdcd719c66bff7c4b9f3d56d1e635259dd8add61) )
344 	ROM_LOAD( "8516dr.l0", 0x400, 0x200, CRC(d1e74af1) SHA1(f55a3bfd7d152ac9af128697f55c9a0c417779f5) )
345 	ROM_LOAD( "8518dr.n0", 0x600, 0x200, CRC(b1369028) SHA1(598a8779982d532c9f34345e793a79fcb29cac62) )
346 
347 	ROM_REGION( 0x100, "sync", 0 )  /* sync prom located at L8, it's a 82s129 */
348 	ROM_LOAD( "l8.bin", 0x000, 0x100, CRC(3610b453) SHA1(9e33ee04f22a9174c29fafb8e71781fa330a7a08) )
349 ROM_END
350 
351 
352 GAMEL( 1977, dragrace, 0, dragrace, dragrace, dragrace_state, empty_init, 0, "Atari (Kee Games)", "Drag Race", MACHINE_SUPPORTS_SAVE, layout_dragrace )
353