// license:BSD-3-Clause // copyright-holders:Tomasz Slanina, David Haywood /*************************************************************************** Koi Koi Part 2 --------------- driver by Tomasz Slanina David Haywood TODO: - map missing inputs (temp mapped to z-x-left shift) - is there (still..) some kind of protection ? timers looks weird (2nd player timer is frozen) (this seems fixed now -AS) - colors (afaik color(?) prom outputs are connected to one of pals), Missing color prom apparently. Basic hw is... z80 (possibly xtal/4) ay3-8910 (possibly xtal/8) 15.468xtal 1 dsw (8) 2kx8 SRAM (x2) Few words about inputs (code @ $21bd): -------------------------------------- There's four reads in a row of input port 3 - 32 possibilities. But only 14 are valid - two lookup tables are used to decode the inputs. For example, for one of input keys game expects data: 0,0,8,0 It's encoded (in internal lookup table, as well as in input_tab[]) as 0x68 : - bits 0-4 = data to return (valid values are 1,2,4,8,$10 - only one bit set) - bits 5-7 = read cycle (1-4) to return above data All other reads should return 0. Also, every other call, code at $21bd must return 0 (2 in 2nd read cycle) to prevent disabling inputs. ***************************************************************************/ #include "emu.h" #include "cpu/z80/z80.h" #include "sound/ay8910.h" #include "screen.h" #include "speaker.h" #define KOIKOI_CRYSTAL 15468000 static const int input_tab[]= { 0x22, 0x64, 0x44, 0x68, 0x30, 0x50, 0x70, 0x48, 0x28, 0x21, 0x41, 0x82, 0x81, 0x42 }; class koikoi_state : public driver_device { public: koikoi_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_videoram(*this, "videoram"), m_maincpu(*this, "maincpu"), m_gfxdecode(*this, "gfxdecode") { } /* memory pointers */ required_shared_ptr m_videoram; /* video-related */ tilemap_t *m_tmap; /* misc */ int m_inputcnt; int m_inputval; int m_inputlen; int m_ioram[8]; DECLARE_WRITE8_MEMBER(vram_w); DECLARE_READ8_MEMBER(io_r); DECLARE_WRITE8_MEMBER(io_w); DECLARE_READ8_MEMBER(input_r); DECLARE_WRITE8_MEMBER(unknown_w); TILE_GET_INFO_MEMBER(get_tile_info); virtual void machine_start() override; virtual void machine_reset() override; virtual void video_start() override; DECLARE_PALETTE_INIT(koikoi); uint32_t screen_update_koikoi(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); required_device m_maincpu; required_device m_gfxdecode; void koikoi(machine_config &config); void koikoi_io_map(address_map &map); void koikoi_map(address_map &map); }; /************************************* * * Video emulation * *************************************/ TILE_GET_INFO_MEMBER(koikoi_state::get_tile_info) { int code = m_videoram[tile_index] | ((m_videoram[tile_index + 0x400] & 0x40) << 2); int color = (m_videoram[tile_index + 0x400] & 0x1f); int flip = (m_videoram[tile_index + 0x400] & 0x80) ? (TILEMAP_FLIPX | TILEMAP_FLIPY) : 0; SET_TILE_INFO_MEMBER(0, code, color, flip); } PALETTE_INIT_MEMBER(koikoi_state, koikoi) { const uint8_t *color_prom = memregion("proms")->base(); int i; /* create a lookup table for the palette */ for (i = 0; i < 0x10; i++) { int bit0, bit1, bit2; int r, g, b; /* red component */ bit0 = (color_prom[i] >> 0) & 0x01; bit1 = (color_prom[i] >> 1) & 0x01; bit2 = (color_prom[i] >> 2) & 0x01; r = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; /* green component */ bit0 = (color_prom[i] >> 3) & 0x01; bit1 = (color_prom[i] >> 4) & 0x01; bit2 = (color_prom[i] >> 5) & 0x01; g = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; /* blue component */ bit0 = 0; bit1 = (color_prom[i] >> 6) & 0x01; bit2 = (color_prom[i] >> 7) & 0x01; b = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; palette.set_indirect_color(i, rgb_t(r, g, b)); } /* color_prom now points to the beginning of the lookup table */ color_prom += 0x20; /* characters/sprites */ for (i = 0; i < 0x100; i++) { uint8_t ctabentry = color_prom[i] & 0x0f; palette.set_pen_indirect(i, ctabentry); } } void koikoi_state::video_start() { m_tmap = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(FUNC(koikoi_state::get_tile_info),this), TILEMAP_SCAN_ROWS, 8, 8, 32, 32); } uint32_t koikoi_state::screen_update_koikoi(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { m_tmap->draw(screen, bitmap, cliprect, 0, 0); return 0; } /************************************* * * Memory handlers * *************************************/ WRITE8_MEMBER(koikoi_state::vram_w) { m_videoram[offset] = data; m_tmap->mark_tile_dirty(offset & 0x3ff); } READ8_MEMBER(koikoi_state::input_r) { if (m_inputcnt < 0) return 0; if (!m_inputcnt) { int key = ioport("IN1")->read(); int keyval = 0; //we must return 0 (0x2 in 2nd read) to clear 4 bit at $6600 and allow next read if (key) { while (!(key & 1)) { key >>= 1; keyval++; } } m_inputval = input_tab[keyval] & 0x1f; m_inputlen = input_tab[keyval] >> 5; } if (m_inputlen == ++m_inputcnt) //return expected value { return m_inputval ^ 0xff; } if (m_inputcnt > 4) //end of cycle { m_inputcnt = -1; } return 0xff; //return 0^0xff } WRITE8_MEMBER(koikoi_state::unknown_w) { //xor'ed mux select, player 1 = 1,2,4,8, player 2 = 0x10, 0x20, 0x40, 0x80 } READ8_MEMBER(koikoi_state::io_r) { if (!offset) return ioport("IN0")->read() ^ m_ioram[4]; //coin return 0; } WRITE8_MEMBER(koikoi_state::io_w) { if (offset == 7 && data == 0) m_inputcnt = 0; //reset read cycle counter m_ioram[offset] = data; } /************************************* * * Address maps * *************************************/ void koikoi_state::koikoi_map(address_map &map) { map(0x0000, 0x2fff).rom(); map(0x6000, 0x67ff).ram(); map(0x7000, 0x77ff).ram().w(this, FUNC(koikoi_state::vram_w)).share("videoram"); map(0x8000, 0x8000).portr("DSW"); map(0x9000, 0x9007).rw(this, FUNC(koikoi_state::io_r), FUNC(koikoi_state::io_w)); } void koikoi_state::koikoi_io_map(address_map &map) { map.global_mask(0xff); map(0x02, 0x02).nopw(); //watchdog map(0x03, 0x03).r("aysnd", FUNC(ay8910_device::data_r)); map(0x06, 0x07).w("aysnd", FUNC(ay8910_device::data_address_w)); } /************************************* * * Input ports * *************************************/ static INPUT_PORTS_START( koikoi ) PORT_START("DSW") PORT_DIPNAME( 0x03, 0x01, "Timer C" ) PORT_DIPSETTING( 0x00, "50" ) PORT_DIPSETTING( 0x01, "70" ) PORT_DIPSETTING( 0x02, "90" ) PORT_DIPSETTING( 0x03, "110" ) PORT_DIPNAME( 0x0c, 0x04, "Timer M" ) PORT_DIPSETTING( 0x00, "120" ) PORT_DIPSETTING( 0x04, "150" ) PORT_DIPSETTING( 0x08, "180" ) PORT_DIPSETTING( 0x0c, "210" ) PORT_DIPNAME( 0x10, 0x00, DEF_STR( Coin_A ) ) PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x10, DEF_STR( 1C_2C ) ) PORT_DIPNAME( 0x20, 0x00, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x20, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x00, DEF_STR( Unknown ) ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x00, "Test Mode" ) PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x80, DEF_STR( On ) ) PORT_START("IN0") PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_COIN1 ) PORT_BIT( 0xbf, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_START("IN1") PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_HANAFUDA_A ) PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_HANAFUDA_B ) PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_HANAFUDA_C ) PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_HANAFUDA_D ) PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_HANAFUDA_E ) PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_HANAFUDA_F ) PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_HANAFUDA_G ) PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_HANAFUDA_H ) PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_Z) PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_X) PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_CODE(KEYCODE_LSHIFT) PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_START1 ) PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_START2 ) INPUT_PORTS_END /************************************* * * Graphic definitions * *************************************/ static const gfx_layout tilelayout = { 8, 8, RGN_FRAC(1,3), 3, { RGN_FRAC(2,3), RGN_FRAC(1,3), RGN_FRAC(0,3) }, { 0, 1, 2, 3, 4, 5, 6, 7 }, { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 }, 8*8 }; static GFXDECODE_START( gfx_koikoi ) GFXDECODE_ENTRY( "gfx1", 0x0000, tilelayout, 0, 32 ) GFXDECODE_END /************************************* * * Machine driver * *************************************/ void koikoi_state::machine_start() { save_item(NAME(m_inputcnt)); save_item(NAME(m_inputval)); save_item(NAME(m_inputlen)); save_item(NAME(m_ioram)); } void koikoi_state::machine_reset() { int i; m_inputcnt = -1; m_inputval = 0; m_inputlen = 0; for (i = 0; i < 8; i++) m_ioram[i] = 0; } MACHINE_CONFIG_START(koikoi_state::koikoi) /* basic machine hardware */ MCFG_DEVICE_ADD("maincpu", Z80,KOIKOI_CRYSTAL/4) /* ?? */ MCFG_DEVICE_PROGRAM_MAP(koikoi_map) MCFG_DEVICE_IO_MAP(koikoi_io_map) /* video hardware */ MCFG_SCREEN_ADD("screen", RASTER) MCFG_SCREEN_REFRESH_RATE(60) MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(0)) MCFG_SCREEN_SIZE(32*8, 32*8) MCFG_SCREEN_VISIBLE_AREA(0*8, 32*8-1, 0*8, 32*8-1) MCFG_SCREEN_UPDATE_DRIVER(koikoi_state, screen_update_koikoi) MCFG_SCREEN_PALETTE("palette") MCFG_SCREEN_VBLANK_CALLBACK(INPUTLINE("maincpu", INPUT_LINE_NMI)) MCFG_DEVICE_ADD("gfxdecode", GFXDECODE, "palette", gfx_koikoi) MCFG_PALETTE_ADD("palette", 8*32) MCFG_PALETTE_INDIRECT_ENTRIES(16) MCFG_PALETTE_INIT_OWNER(koikoi_state, koikoi) /* sound hardware */ SPEAKER(config, "mono").front_center(); MCFG_DEVICE_ADD("aysnd", AY8910, KOIKOI_CRYSTAL/8) MCFG_AY8910_PORT_B_READ_CB(READ8(*this, koikoi_state, input_r)) MCFG_AY8910_PORT_A_WRITE_CB(WRITE8(*this, koikoi_state, unknown_w)) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.10) MACHINE_CONFIG_END /************************************* * * ROM definition(s) * *************************************/ ROM_START( koikoi ) ROM_REGION( 0x10000, "maincpu", 0 ) /* code */ ROM_LOAD( "ic56", 0x0000, 0x1000, CRC(bdc68f9d) SHA1(c45fbc95abb37f750acc1d9f3b35ad0f41af097d) ) ROM_LOAD( "ic55", 0x1000, 0x1000, CRC(fe09248a) SHA1(c192795678068e387bd406f5cd1c5aba5f5ef66a) ) ROM_LOAD( "ic54", 0x2000, 0x1000, CRC(925fc57c) SHA1(4c79df92b6617fe84e61359c8e6e3b907b138777) ) ROM_REGION( 0x3000, "gfx1", 0 ) /* gfx */ ROM_LOAD( "ic33", 0x0000, 0x1000, CRC(9e4d563b) SHA1(63664dcffc2eb198a161c73131b95a66b2067424) ) ROM_LOAD( "ic26", 0x1000, 0x1000, CRC(79cb1e93) SHA1(4d08b3d88727b437673f7a51d47396f19bbc3caa) ) ROM_LOAD( "ic18", 0x2000, 0x1000, CRC(c209362d) SHA1(0620c19fe72e8407db0f487b6413c5d45ac8046c) ) ROM_REGION( 0x0120, "proms", 0 ) ROM_LOAD( "prom.x", 0x000, 0x020, NO_DUMP ) /* hand crafted color table */ ROM_FILL( 0x00, 1, 0x18 ) ROM_FILL( 0x01, 1, 0xff ) ROM_FILL( 0x02, 1, 0x06 ) ROM_FILL( 0x03, 1, 0x3f ) ROM_FILL( 0x04, 1, 0xb6 ) ROM_FILL( 0x05, 1, 0x10 ) ROM_FILL( 0x06, 1, 0x2f ) ROM_FILL( 0x07, 1, 0x04 ) ROM_FILL( 0x08, 1, 0x8b ) ROM_FILL( 0x09, 1, 0x3f ) ROM_FILL( 0x0a, 1, 0x07 ) ROM_FILL( 0x0b, 1, 0x00 ) // unused ROM_FILL( 0x0c, 1, 0x2f ) ROM_FILL( 0x0d, 1, 0xff ) ROM_FILL( 0x0e, 1, 0x00 ) ROM_FILL( 0x0f, 1, 0x0b ) ROM_LOAD( "prom.ic23", 0x020, 0x100, CRC(f1d169a6) SHA1(5ee4b1dfe61e8b97a90cc113ba234298189f1a73) ) ROM_REGION( 0x0a00, "plds", 0 ) ROM_LOAD( "pal16r8-10_pink.ic9", 0x0000, 0x0104, CRC(9f8fdb95) SHA1(cdcdb1a6baef18961cf6c75fba0c3aba47f3edbb) ) ROM_LOAD( "pal16r8-10_green.ic15", 0x0200, 0x0104, CRC(da7b8b95) SHA1(a4eb12f2365ff2b6057e4a2e225e8f879a961d45) ) ROM_LOAD( "pal16r8a_yellow.ic8", 0x0400, 0x0104, CRC(7d8da540) SHA1(28925d1fb4ef670e9c9d24860b67fdff8791c6a9) ) ROM_LOAD( "pal16r8a_brown.ic11", 0x0600, 0x0104, CRC(fff46363) SHA1(97f673c862e9d5b12cac283000a779c465c76828) ) ROM_LOAD( "pal16r8a_red.ic10", 0x0800, 0x0104, CRC(027ad661) SHA1(fa5aafe6deb3a9865498152b92dd3776ea10a51d) ) ROM_END /************************************* * * Game driver(s) * *************************************/ GAME( 1982, koikoi, 0, koikoi, koikoi, koikoi_state, empty_init, ROT270, "Kiwako", "Koi Koi Part 2", MACHINE_WRONG_COLORS | MACHINE_SUPPORTS_SAVE )