1 // license:BSD-3-Clause
2 // copyright-holders:Angelo Salese, Tomasz Slanina, David Haywood
3 /*
4  Two Minute Drill - Taito 1993
5  -----------------------------
6  Half Video, Half Mechanical?
7 (video hw + motion/acceleration sensor ?)
8 
9  preliminary driver by
10   David Haywood
11   Tomasz Slanina
12   Angelo Salese
13 
14 TODO:
15  - understand the ball hit sensor
16  - simulate the sensors (there are still some shutter errors/defender errors that pops up)
17  - Hook-up timers for shutter/defender sensors (check service mode)
18  - Dip-Switches
19 
20  Brief hardware overview:
21  ------------------------
22 
23  Main processor   - 68000 16Mhz
24 
25  Sound            - Yamaha YM2610B
26 
27  Taito custom ICs - TC0400YSC (m68k -> ym2610 communication)
28                   - TC0260DAR (palette chip)
29                   - TC0630FDP (Taito F3 video chip)
30                   - TC0510NIO (known input chip)
31 
32 DAC               -26.6860Mhz
33                   -32.0000Mhz
34 
35 */
36 
37 #include "emu.h"
38 #include "includes/taito_f3.h"
39 
40 #include "cpu/m68000/m68000.h"
41 #include "machine/taitoio.h"
42 #include "sound/2610intf.h"
43 #include "speaker.h"
44 
45 
46 class _2mindril_state : public taito_f3_state
47 {
48 public:
_2mindril_state(const machine_config & mconfig,device_type type,const char * tag)49 	_2mindril_state(const machine_config &mconfig, device_type type, const char *tag) :
50 		taito_f3_state(mconfig, type, tag),
51 		m_in0(*this, "IN0")
52 	{ }
53 
54 	void drill(machine_config &config);
55 
56 	void init_drill();
57 
58 protected:
59 	virtual void machine_start() override;
60 	virtual void machine_reset() override;
61 
62 private:
63 	/* input-related */
64 	required_ioport m_in0;
65 	u8         m_defender_sensor;
66 	u8         m_shutter_sensor;
67 	u16        m_irq_reg;
68 
69 	/* devices */
70 	u8 arm_pwr_r();
71 	u8 sensors_r();
72 	void coins_w(u8 data);
73 	void sensors_w(u16 data);
74 	u16 irq_r();
75 	void irq_w(offs_t offset, u16 data, u16 mem_mask);
76 
77 	INTERRUPT_GEN_MEMBER(vblank_irq);
78 	//INTERRUPT_GEN_MEMBER(drill_device_irq);
79 	void irqhandler(int state);
80 
81 	void drill_map(address_map &map);
82 
83 	#ifdef UNUSED_FUNCTION
84 	enum
85 	{
86 		TIMER_SHUTTER_REQ,
87 		TIMER_DEFENDER_REQ
88 	};
89 
90 protected:
91 	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
92 	#endif
93 };
94 
95 
arm_pwr_r()96 u8 _2mindril_state::arm_pwr_r()
97 {
98 	int arm_pwr = m_in0->read();//throw
99 
100 	if (arm_pwr > 0xe0) return ~0x18;
101 	if (arm_pwr > 0xc0) return ~0x14;
102 	if (arm_pwr > 0x80) return ~0x12;
103 	if (arm_pwr > 0x40) return ~0x10;
104 	else return ~0x00;
105 }
106 
sensors_r()107 u8 _2mindril_state::sensors_r()
108 {
109 	return (m_defender_sensor) | (m_shutter_sensor);
110 }
111 
coins_w(u8 data)112 void _2mindril_state::coins_w(u8 data)
113 {
114 	machine().bookkeeping().coin_counter_w(0, data & 0x04);
115 	machine().bookkeeping().coin_counter_w(1, data & 0x08);
116 	machine().bookkeeping().coin_lockout_w(0, ~data & 0x01);
117 	machine().bookkeeping().coin_lockout_w(1, ~data & 0x02);
118 }
119 
120 /*
121     PORT_DIPNAME( 0x0100, 0x0000, DEF_STR( Unknown ) )//up sensor <- shutter
122     PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
123     PORT_DIPSETTING(      0x0100, DEF_STR( On ) )
124     PORT_DIPNAME( 0x0200, 0x0000, DEF_STR( Unknown ) )//down sensor
125     PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
126     PORT_DIPSETTING(      0x0200, DEF_STR( On ) )
127     PORT_DIPNAME( 0x0400, 0x0000, DEF_STR( Unknown ) )//left sensor <-defender
128     PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
129     PORT_DIPSETTING(      0x0400, DEF_STR( On ) )
130     PORT_DIPNAME( 0x0800, 0x0000, DEF_STR( Unknown ) )//right sensor
131     PORT_DIPSETTING(      0x0000, DEF_STR( Off ) )
132     PORT_DIPSETTING(      0x0800, DEF_STR( On ) )
133 */
134 #ifdef UNUSED_FUNCTION
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)135 void _2mindril_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
136 {
137 	switch (id)
138 	{
139 	case TIMER_SHUTTER_REQ:
140 			m_shutter_sensor = param;
141 			break;
142 	case TIMER_DEFENDER_REQ:
143 			m_defender_sensor = param;
144 			break;
145 	default:
146 			throw emu_fatalerror("Unknown id in _2mindril_state::device_timer");
147 	}
148 }
149 #endif
150 
sensors_w(u16 data)151 void _2mindril_state::sensors_w(u16 data)
152 {
153 	/*---- xxxx ---- ---- select "lamps" (guess)*/
154 	/*---- ---- ---- -x-- lamp*/
155 	if (data & 1)
156 	{
157 		//timer_set( attotime::from_seconds(2), TIMER_SHUTTER_REQ, 0x01);
158 		m_shutter_sensor = 0x01;
159 	}
160 	else if (data & 2)
161 	{
162 		//timer_set( attotime::from_seconds(2), TIMER_SHUTTER_REQ, 0x02);
163 		m_shutter_sensor = 0x02;
164 	}
165 
166 	if (data & 0x1000 || data & 0x4000)
167 	{
168 		//timer_set( attotime::from_seconds(2), TIMER_DEFENDER_REQ, 0x08);
169 		m_defender_sensor = 0x08;
170 	}
171 	else if (data & 0x2000 || data & 0x8000)
172 	{
173 		//timer_set( attotime::from_seconds(2), TIMER_DEFENDER_REQ, 0x04);
174 		m_defender_sensor = 0x04;
175 	}
176 }
177 
irq_r()178 u16 _2mindril_state::irq_r()
179 {
180 	return m_irq_reg;
181 }
182 
irq_w(offs_t offset,u16 data,u16 mem_mask)183 void _2mindril_state::irq_w(offs_t offset, u16 data, u16 mem_mask)
184 {
185 	/*
186 	(note: could rather be irq mask)
187 	---- ---- ---x ---- irq lv 5 ack, 0->1 latch
188 	---- ---- ---- x--- irq lv 4 ack, 0->1 latch
189 	---- ---- -??- -??? connected to the other levels?
190 	*/
191 	if (((m_irq_reg & 8) == 0) && data & 8)
192 		m_maincpu->set_input_line(4, CLEAR_LINE);
193 
194 	if (((m_irq_reg & 0x10) == 0) && data & 0x10)
195 		m_maincpu->set_input_line(5, CLEAR_LINE);
196 
197 	if (data & 0xffe7)
198 		printf("%04x\n",data);
199 
200 	COMBINE_DATA(&m_irq_reg);
201 }
202 
drill_map(address_map & map)203 void _2mindril_state::drill_map(address_map &map)
204 {
205 	map(0x000000, 0x07ffff).rom();
206 	map(0x200000, 0x20ffff).ram();
207 	map(0x300000, 0x3000ff).ram();
208 	map(0x400000, 0x40ffff).ram().share("spriteram");
209 	map(0x410000, 0x41bfff).ram().w(FUNC(_2mindril_state::pf_ram_w)).share("pf_ram");
210 	map(0x41c000, 0x41dfff).ram().w(FUNC(_2mindril_state::textram_w)).share("textram");
211 	map(0x41e000, 0x41ffff).ram().w(FUNC(_2mindril_state::charram_w)).share("charram");
212 	map(0x420000, 0x42ffff).ram().share("line_ram");
213 	map(0x430000, 0x43ffff).ram().w(FUNC(_2mindril_state::pivot_w)).share("pivot_ram");
214 	map(0x460000, 0x46000f).w(FUNC(_2mindril_state::control_0_w));
215 	map(0x460010, 0x46001f).w(FUNC(_2mindril_state::control_1_w));
216 	map(0x500000, 0x501fff).ram().w(m_palette, FUNC(palette_device::write16)).share("palette");
217 	map(0x502022, 0x502023).nopw(); //countinously switches between 0 and 2
218 	map(0x600000, 0x600007).rw("ymsnd", FUNC(ym2610_device::read), FUNC(ym2610_device::write)).umask16(0x00ff);
219 	map(0x60000c, 0x60000d).rw(FUNC(_2mindril_state::irq_r), FUNC(_2mindril_state::irq_w));
220 	map(0x60000e, 0x60000f).ram(); // unknown purpose, zeroed at start-up and nothing else
221 	map(0x700000, 0x70000f).rw("tc0510nio", FUNC(tc0510nio_device::read), FUNC(tc0510nio_device::write)).umask16(0xff00);
222 	map(0x800000, 0x800001).w(FUNC(_2mindril_state::sensors_w));
223 }
224 
225 static INPUT_PORTS_START( drill )
226 	PORT_START("DSW") //Dip-Switches. PCB labelled DIPSWA
227 	PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:1")
228 	PORT_DIPSETTING( 0x01, DEF_STR( Off ) )
229 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
230 	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:2")
231 	PORT_DIPSETTING( 0x02, DEF_STR( Off ) )
232 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
233 	PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:3")
234 	PORT_DIPSETTING( 0x04, DEF_STR( Off ) )
235 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
236 	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:4")
237 	PORT_DIPSETTING( 0x08, DEF_STR( Off ) )
238 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
239 	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:5")
240 	PORT_DIPSETTING( 0x10, DEF_STR( Off ) )
241 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
242 	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:6")
243 	PORT_DIPSETTING( 0x20, DEF_STR( Off ) )
244 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
245 	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:7")
246 	PORT_DIPSETTING( 0x40, DEF_STR( Off ) )
247 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
248 	PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) )  PORT_DIPLOCATION("DIPSWA:8")
249 	PORT_DIPSETTING( 0x80, DEF_STR( Off ) )
250 	PORT_DIPSETTING( 0x00, DEF_STR( On ) )
251 
252 	PORT_START("IN0")//sensors
253 	PORT_BIT( 0xff, 0x00, IPT_DIAL ) PORT_SENSITIVITY(25) PORT_KEYDELTA(20)
254 
255 	PORT_START("COINS")
256 	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_SERVICE )
257 	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SERVICE1 )
258 	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_COIN1 )
259 	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )
260 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Select SW-1")
261 	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Select SW-2")
262 	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Select SW-3")
263 	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Select SW-4")
264 INPUT_PORTS_END
265 
266 static const gfx_layout charlayout =
267 {
268 	8,8,
269 	256,
270 	4,
271 	{ 0,1,2,3 },
272 	{ 20, 16, 28, 24, 4, 0, 12, 8 },
273 	{ STEP8(0,4*8) },
274 	32*8
275 };
276 
277 static const gfx_layout pivotlayout =
278 {
279 	8,8,
280 	2048,
281 	4,
282 	{ 0,1,2,3 },
283 	{ 20, 16, 28, 24, 4, 0, 12, 8 },
284 	{ STEP8(0,4*8) },
285 	32*8
286 };
287 
288 static const gfx_layout layout_6bpp_sprite_hi =
289 {
290 	16,16,
291 	RGN_FRAC(1,1),
292 	6,
293 	{ STEP2(0,1)/**/,0,0,0,0/**/ },
294 	{ STEP4(3*2,-2), STEP4(7*2,-2), STEP4(11*2,-2), STEP4(15*2,-2) },
295 	{ STEP16(0,16*2) },
296 	16*16*2
297 };
298 
299 static const gfx_layout layout_6bpp_tile_hi =
300 {
301 	16,16,
302 	RGN_FRAC(1,1),
303 	6,
304 	{ 8,0/**/,0,0,0,0/**/ },
305 	{ STEP8(7,-1), STEP8(8*2+7,-1) },
306 	{ STEP16(0,8*2*2) },
307 	16*16*2
308 };
309 
310 static GFXDECODE_START( gfx_2mindril )
311 	GFXDECODE_ENTRY( nullptr,      0, charlayout,             0x0000, 0x0400>>4 ) /* Dynamically modified */
312 	GFXDECODE_ENTRY( nullptr,      0, pivotlayout,            0x0000,  0x400>>4 ) /* Dynamically modified */
313 	GFXDECODE_ENTRY( "sprites",    0, gfx_16x16x4_packed_lsb, 0x1000, 0x1000>>4 ) // low 4bpp of 6bpp sprite data
314 	GFXDECODE_ENTRY( "tilemap",    0, gfx_16x16x4_packed_lsb, 0x0000, 0x2000>>4 ) // low 4bpp of 6bpp tilemap data
315 	GFXDECODE_ENTRY( "tilemap_hi", 0, layout_6bpp_tile_hi,    0x0000, 0x2000>>4 ) // hi 2bpp of 6bpp tilemap data
316 	GFXDECODE_ENTRY( "sprites_hi", 0, layout_6bpp_sprite_hi,  0x1000, 0x1000>>4 ) // hi 2bpp of 6bpp sprite data
317 GFXDECODE_END
318 
319 
INTERRUPT_GEN_MEMBER(_2mindril_state::vblank_irq)320 INTERRUPT_GEN_MEMBER(_2mindril_state::vblank_irq)
321 {
322 	device.execute().set_input_line(4, ASSERT_LINE);
323 }
324 
325 #if 0
326 INTERRUPT_GEN_MEMBER(_2mindril_state::drill_device_irq)
327 {
328 	device.execute().set_input_line(5, ASSERT_LINE);
329 }
330 #endif
331 
332 /* WRONG,it does something with 60000c & 700002,likely to be called when the player throws the ball.*/
irqhandler(int state)333 void _2mindril_state::irqhandler(int state)
334 {
335 //  m_maincpu->set_input_line(5, state ? ASSERT_LINE : CLEAR_LINE);
336 }
337 
338 
machine_start()339 void _2mindril_state::machine_start()
340 {
341 	save_item(NAME(m_defender_sensor));
342 	save_item(NAME(m_shutter_sensor));
343 	save_item(NAME(m_irq_reg));
344 }
345 
machine_reset()346 void _2mindril_state::machine_reset()
347 {
348 	m_defender_sensor = 0;
349 	m_shutter_sensor = 0;
350 	m_irq_reg = 0;
351 }
352 
drill(machine_config & config)353 void _2mindril_state::drill(machine_config &config)
354 {
355 	M68000(config, m_maincpu, 16000000);
356 	m_maincpu->set_addrmap(AS_PROGRAM, &_2mindril_state::drill_map);
357 	m_maincpu->set_vblank_int("screen", FUNC(_2mindril_state::vblank_irq));
358 	GFXDECODE(config, m_gfxdecode, m_palette, gfx_2mindril);
359 
360 	tc0510nio_device &tc0510nio(TC0510NIO(config, "tc0510nio", 0));
361 	tc0510nio.read_0_callback().set_ioport("DSW");
362 	tc0510nio.read_1_callback().set(FUNC(_2mindril_state::arm_pwr_r));
363 	tc0510nio.read_2_callback().set(FUNC(_2mindril_state::sensors_r));
364 	tc0510nio.write_4_callback().set(FUNC(_2mindril_state::coins_w));
365 	tc0510nio.read_7_callback().set_ioport("COINS");
366 
367 	SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
368 	m_screen->set_refresh_hz(60);
369 	m_screen->set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* inaccurate, same as Taito F3? (needs screen raw params anyway) */
370 	m_screen->set_size(40*8+48*2, 32*8);
371 	m_screen->set_visarea(46, 40*8-1 + 46, 24, 24+224-1);
372 	m_screen->set_screen_update(FUNC(_2mindril_state::screen_update));
373 	m_screen->screen_vblank().set(FUNC(_2mindril_state::screen_vblank));
374 
375 	PALETTE(config, m_palette).set_format(palette_device::RRRRGGGGBBBBRGBx, 0x2000);
376 
377 	SPEAKER(config, "lspeaker").front_left();
378 	SPEAKER(config, "rspeaker").front_right();
379 
380 	ym2610b_device &ymsnd(YM2610B(config, "ymsnd", 16000000/2));
381 	ymsnd.irq_handler().set(FUNC(_2mindril_state::irqhandler));
382 	ymsnd.add_route(0, "lspeaker", 0.25);
383 	ymsnd.add_route(0, "rspeaker", 0.25);
384 	ymsnd.add_route(1, "lspeaker", 1.0);
385 	ymsnd.add_route(2, "rspeaker", 1.0);
386 }
387 
388 
389 ROM_START( 2mindril )
390 	ROM_REGION( 0x80000, "maincpu", 0 ) /* 68000 Code */
CRC(c58e8e4f)391 	ROM_LOAD16_BYTE( "d58-38.ic11", 0x00000, 0x40000, CRC(c58e8e4f) SHA1(648db679c3bfb5de1cd6c1b1217773a2fe56f11b) ) // Ver 2.93A 1994/02/16 09:45:00
392 	ROM_LOAD16_BYTE( "d58-37.ic9",  0x00001, 0x40000, CRC(19e5cc3c) SHA1(04ac0eef893c579fe90d91d7fd55c5741a2b7460) )
393 
394 	ROM_REGION( 0x200000, "ymsnd", 0 ) /* Samples */
395 	ROM_LOAD( "d58-11.ic31", 0x000000, 0x200000,  CRC(dc26d58d) SHA1(cffb18667da18f5367b02af85a2f7674dd61ae97) )
396 
397 	ROM_REGION( 0x400000, "sprites", ROMREGION_ERASE00 )
398 	ROM_REGION( 0x200000, "sprites_hi", ROMREGION_ERASE00 )
399 
400 	ROM_REGION( 0x400000, "tilemap", 0 )
401 	ROM_LOAD32_WORD( "d58-08.ic27", 0x000000, 0x200000, CRC(9f5a3f52) SHA1(7b696bd823819965b974c853cebc1660750db61e) )
402 	ROM_LOAD32_WORD( "d58-09.ic28", 0x000002, 0x200000, CRC(d8f6a86a) SHA1(d6b2ec309e21064574ee63e025ae4716b1982a98) )
403 
404 	ROM_REGION( 0x200000, "tilemap_hi", 0 )
405 	ROM_LOAD       ( "d58-10.ic29", 0x000000, 0x200000, CRC(74c87e08) SHA1(f39b3a64f8338ccf5ca6eb76cee92a10fe0aad8f) )
406 ROM_END
407 
408 void _2mindril_state::init_drill()
409 {
410 	m_game = TMDRILL;
411 	tile_decode();
412 }
413 
414 GAME( 1993, 2mindril, 0, drill, drill, _2mindril_state, init_drill, ROT0, "Taito America Corporation", "Two Minute Drill (Ver 2.93A 1994/02/16)", MACHINE_NOT_WORKING | MACHINE_IMPERFECT_GRAPHICS | MACHINE_MECHANICAL)
415