// license:GPL-2.0+ // copyright-holders:Norbert Kehrer /*************************************************************************** Super Tank (c) 1981 Video Games GmbH Reverse-engineering and MAME driver by Norbert Kehrer (December 2003). **************************************************************************** Hardware: --------- CPU TMS9980 (Texas Instruments) Sound chip AY-3-8910 Memory map: ----------- >0000 - >07ff Fixed ROM >0800 - >17ff Bank-switched ROM >1800 - >1bff RAM >1efc Input port 1 >1efd Input port 2 >1efe Read: DIP switch port 1 Write: Control port of AY-3-8910 sound chip >1eff Read: DIP switch port 2 Write: Data port of AY-3-8910 >2000 - >3fff Video RAM (CPU view of it, in reality there are 24 KB on the video board) Input ports: ------------ Input port 0, mapped to memory address 0x1efc: 7654 3210 0 Player 2 Right 0 Player 2 Left 0 Player 2 Down 0 Player 2 Up 0 Player 1 Right 0 Player 1 Left 0 Player 1 Down 0 Player 1 Up Input port 1, mapped to memory address 0x1efd: 7654 3210 0 Player 2 Fire 0 Player 1 Fire 0 ?? 0 ?? 0 Start 1 player game 0 Start 2 players game 0 Coin (strobe) 0 ?? DIP switch ports: ----------------- DIP switch port 1, mapped to memory address 0x1efe: 7654 3210 0 Not used (?) 0 Not used (?) 0 Tanks per player: 1 = 5 tanks, 0 = 3 tanks 0 Extra tank: 1 = at 10,000, 0 = at 15,000 pts. 000 Coinage 0 Not used (?) DIP switch port 2, mapped to memory address 0x1eff: 7654 3210 1 ?? 1 ?? 1 ?? 1 ?? 1 ?? 1 ?? 1 ?? 1 ?? CRU lines: ---------- >400 Select bitplane for writes into video RAM (bit 0) >401 Select bitplane for writes into video RAM (bit 1) >402 ROM bank selector (bit 0) >404 ROM bank selector (bit 1) >406 Interrupt acknowledge (clears interrupt line) >407 Watchdog reset (?) >b12 Unknown, maybe some special-hardware sound effect or lights blinking (?) >b13 Unknown, maybe some special-hardware sound effect or lights blinking (?) ***************************************************************************/ #include "emu.h" #include "cpu/tms9900/tms9980a.h" #include "machine/watchdog.h" #include "sound/ay8910.h" #include "screen.h" #include "speaker.h" /* the color PROM is 32 bytes, but it is a repeating every 8 bytes */ #define NUM_PENS (8) class supertnk_state : public driver_device { public: supertnk_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") { } void init_supertnk(); void supertnk(machine_config &config); protected: DECLARE_WRITE8_MEMBER(supertnk_bankswitch_0_w); DECLARE_WRITE8_MEMBER(supertnk_bankswitch_1_w); DECLARE_WRITE8_MEMBER(supertnk_interrupt_ack_w); DECLARE_WRITE8_MEMBER(supertnk_videoram_w); DECLARE_READ8_MEMBER(supertnk_videoram_r); DECLARE_WRITE8_MEMBER(supertnk_bitplane_select_0_w); DECLARE_WRITE8_MEMBER(supertnk_bitplane_select_1_w); uint32_t screen_update_supertnk(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); INTERRUPT_GEN_MEMBER(supertnk_interrupt); virtual void machine_start() override; virtual void machine_reset() override; virtual void video_start() override; void supertnk_io_map(address_map &map); void supertnk_map(address_map &map); private: std::unique_ptr m_videoram[3]; uint8_t m_rom_bank; uint8_t m_bitplane_select; pen_t m_pens[NUM_PENS]; required_device m_maincpu; }; void supertnk_state::machine_start() { membank("bank1")->configure_entries(0, 4, memregion("maincpu")->base() + 0x10000, 0x1000); } /************************************* * * Memory banking * *************************************/ WRITE8_MEMBER(supertnk_state::supertnk_bankswitch_0_w) { m_rom_bank = (m_rom_bank & 0x02) | ((data << 0) & 0x01); membank("bank1")->set_entry(m_rom_bank); } WRITE8_MEMBER(supertnk_state::supertnk_bankswitch_1_w) { m_rom_bank = (m_rom_bank & 0x01) | ((data << 1) & 0x02); membank("bank1")->set_entry(m_rom_bank); } /************************************* * * Interrupt generation * *************************************/ INTERRUPT_GEN_MEMBER(supertnk_state::supertnk_interrupt) { m_maincpu->set_input_line(INT_9980A_LEVEL4, ASSERT_LINE); } WRITE8_MEMBER(supertnk_state::supertnk_interrupt_ack_w) { m_maincpu->set_input_line(INT_9980A_LEVEL4, CLEAR_LINE); } /************************************* * * Video system * *************************************/ void supertnk_state::video_start() { offs_t i; const uint8_t *prom = memregion("proms")->base(); for (i = 0; i < NUM_PENS; i++) { uint8_t data = prom[i]; m_pens[i] = rgb_t(pal1bit(data >> 2), pal1bit(data >> 5), pal1bit(data >> 6)); } m_videoram[0] = std::make_unique(0x2000); m_videoram[1] = std::make_unique(0x2000); m_videoram[2] = std::make_unique(0x2000); } WRITE8_MEMBER(supertnk_state::supertnk_videoram_w) { if (m_bitplane_select > 2) { m_videoram[0][offset] = 0; m_videoram[1][offset] = 0; m_videoram[2][offset] = 0; } else { m_videoram[m_bitplane_select][offset] = data; } } READ8_MEMBER(supertnk_state::supertnk_videoram_r) { uint8_t ret = 0x00; if (m_bitplane_select < 3) ret = m_videoram[m_bitplane_select][offset]; return ret; } WRITE8_MEMBER(supertnk_state::supertnk_bitplane_select_0_w) { m_bitplane_select = (m_bitplane_select & 0x02) | ((data << 0) & 0x01); } WRITE8_MEMBER(supertnk_state::supertnk_bitplane_select_1_w) { m_bitplane_select = (m_bitplane_select & 0x01) | ((data << 1) & 0x02); } uint32_t supertnk_state::screen_update_supertnk(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { offs_t offs; for (offs = 0; offs < 0x2000; offs++) { int i; uint8_t y = offs >> 5; uint8_t x = offs << 3; uint8_t data0 = m_videoram[0][offs]; uint8_t data1 = m_videoram[1][offs]; uint8_t data2 = m_videoram[2][offs]; for (i = 0; i < 8; i++) { uint8_t color = ((data0 & 0x80) >> 5) | ((data1 & 0x80) >> 6) | ((data2 & 0x80) >> 7); bitmap.pix32(y, x) = m_pens[color]; data0 = data0 << 1; data1 = data1 << 1; data2 = data2 << 1; x = x + 1; } } return 0; } /************************************* * * Machine reset * *************************************/ void supertnk_state::machine_reset() { address_space &space = m_maincpu->space(AS_PROGRAM); supertnk_bankswitch_0_w(space, 0, 0); supertnk_bankswitch_1_w(space, 0, 0); supertnk_bitplane_select_0_w(space, 0, 0); supertnk_bitplane_select_1_w(space, 0, 0); } /************************************* * * Memory handlers * *************************************/ void supertnk_state::supertnk_map(address_map &map) { map(0x0000, 0x07ff).rom(); map(0x0800, 0x17ff).bankr("bank1"); map(0x1800, 0x1bff).ram(); map(0x1efc, 0x1efc).portr("JOYS"); map(0x1efd, 0x1efd).portr("INPUTS"); map(0x1efe, 0x1eff).w("aysnd", FUNC(ay8910_device::address_data_w)); map(0x1efe, 0x1efe).portr("DSW"); map(0x1eff, 0x1eff).portr("UNK"); map(0x2000, 0x3fff).rw(this, FUNC(supertnk_state::supertnk_videoram_r), FUNC(supertnk_state::supertnk_videoram_w)); } /************************************* * * Port handlers * *************************************/ void supertnk_state::supertnk_io_map(address_map &map) { map(0x0000, 0x0000).nopw(); map(0x0400, 0x0400).w(this, FUNC(supertnk_state::supertnk_bitplane_select_0_w)); map(0x0401, 0x0401).w(this, FUNC(supertnk_state::supertnk_bitplane_select_1_w)); map(0x0402, 0x0402).w(this, FUNC(supertnk_state::supertnk_bankswitch_0_w)); map(0x0404, 0x0404).w(this, FUNC(supertnk_state::supertnk_bankswitch_1_w)); map(0x0406, 0x0406).w(this, FUNC(supertnk_state::supertnk_interrupt_ack_w)); map(0x0407, 0x0407).w("watchdog", FUNC(watchdog_timer_device::reset_w)); } /************************************* * * Port definitions * *************************************/ static INPUT_PORTS_START( supertnk ) PORT_START("JOYS") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) PORT_START("INPUTS") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_IMPULSE(1) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START1 ) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 ) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_START("DSW") PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x0e, 0x02, DEF_STR( Coinage ) ) PORT_DIPSETTING( 0x02, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x0e, DEF_STR( 1C_2C ) ) PORT_DIPSETTING( 0x0c, DEF_STR( 1C_3C ) ) PORT_DIPSETTING( 0x0a, DEF_STR( 1C_4C ) ) PORT_DIPSETTING( 0x08, DEF_STR( 1C_5C ) ) PORT_DIPSETTING( 0x06, DEF_STR( 1C_6C ) ) PORT_DIPSETTING( 0x04, DEF_STR( 1C_7C ) ) PORT_DIPSETTING( 0x00, DEF_STR( Free_Play ) ) PORT_DIPNAME( 0x10, 0x00, DEF_STR( Bonus_Life ) ) PORT_DIPSETTING( 0x00, "at 15,000 points" ) PORT_DIPSETTING( 0x10, "at 10,000 points" ) PORT_DIPNAME( 0x20, 0x00, DEF_STR( Lives ) ) PORT_DIPSETTING( 0x00, "3" ) PORT_DIPSETTING( 0x20, "5" ) PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x80, DEF_STR( On ) ) PORT_START("UNK") PORT_DIPNAME( 0x01, 0x01, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x02, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x02, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x04, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x04, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x08, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x08, DEF_STR( On ) ) PORT_DIPNAME( 0x10, 0x10, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x10, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x20, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x20, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x40, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x80, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x80, DEF_STR( On ) ) INPUT_PORTS_END /************************************* * * Machine driver * *************************************/ MACHINE_CONFIG_START(supertnk_state::supertnk) // CPU TMS9980A; no line connections MCFG_TMS99xx_ADD("maincpu", TMS9980A, 2598750, supertnk_map, supertnk_io_map) MCFG_DEVICE_VBLANK_INT_DRIVER("screen", supertnk_state, supertnk_interrupt) MCFG_WATCHDOG_ADD("watchdog") /* video hardware */ MCFG_SCREEN_ADD("screen", RASTER) MCFG_SCREEN_SIZE(32*8, 32*8) MCFG_SCREEN_VISIBLE_AREA(0*8, 32*8-1, 0*8, 32*8-1) MCFG_SCREEN_REFRESH_RATE(60) MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(0)) MCFG_SCREEN_UPDATE_DRIVER(supertnk_state, screen_update_supertnk) /* audio hardware */ SPEAKER(config, "mono").front_center(); MCFG_DEVICE_ADD("aysnd", AY8910, 2000000) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50) MACHINE_CONFIG_END /************************************* * * ROM definition * *************************************/ ROM_START( supertnk ) ROM_REGION( 0x14000, "maincpu", 0 ) /* 64k for TMS9980 code + 16k of ROM */ ROM_LOAD( "supertan.2d", 0x00000, 0x0800, CRC(1656a2c1) SHA1(1d49945aed105003a051cfbf646af7a4be1b7e86) ) ROM_LOAD( "supertnk.3d", 0x10800, 0x0800, CRC(8b023a9a) SHA1(1afdc8d75f2ca04153bac20c0e3e123e2a7acdb7) ) ROM_CONTINUE( 0x10000, 0x0800) ROM_LOAD( "supertnk.4d", 0x11800, 0x0800, CRC(b8249e5c) SHA1(ef4bb714b0c1b97890a067f05fc50ab3426ce37f) ) ROM_CONTINUE( 0x11000, 0x0800) ROM_LOAD( "supertnk.8d", 0x12800, 0x0800, CRC(d8175a4f) SHA1(cba7b426773ac86c81a9eac81087a2db268cd0f9) ) ROM_CONTINUE( 0x12000, 0x0800) ROM_LOAD( "supertnk.9d", 0x13800, 0x0800, CRC(a34a494a) SHA1(9b7f0560e9d569ee25eae56f31886d50a3153dcc) ) ROM_CONTINUE( 0x13000, 0x0800) ROM_REGION( 0x0060, "proms", 0 ) ROM_LOAD( "supertnk.clr", 0x0000, 0x0020, CRC(9ae1faee) SHA1(19de4bb8bc389d98c8f8e35c755fad96e1a6a0cd) ) /* color PROM */ ROM_LOAD( "supertnk.s", 0x0020, 0x0020, CRC(91722fcf) SHA1(f77386014b459cc151d2990ac823b91c04e8d319) ) /* unknown - sync? */ ROM_LOAD( "supertnk.t", 0x0040, 0x0020, CRC(154390bd) SHA1(4dc0fd7bd8999d2670c8d93aaada835d2a84d4db) ) /* unknown - sync? */ ROM_END /************************************* * * Game drivers * *************************************/ void supertnk_state::init_supertnk() { /* decode the TMS9980 ROMs */ uint8_t *rom = memregion("maincpu")->base(); size_t len = memregion("maincpu")->bytes(); for (offs_t offs = 0; offs < len; offs++) { rom[offs] = bitswap<8>(rom[offs],0,1,2,3,4,5,6,7); } } GAME( 1981, supertnk, 0, supertnk, supertnk, supertnk_state, init_supertnk, ROT90, "Video Games GmbH", "Super Tank", 0 )