// license:BSD-3-Clause // copyright-holders:Vas Crabb, Angelo Salese /* VS Mahjong Triangle (c) 1986 Dyna Unmarked board with everything doubled up, to provide a versus experience with a single board 2 players can play separately or one against the other CPU: 2x Z80 RAM: 6x M5M5117P Sound: 2x AY-3-8910 I/O: 2x 8255, 4x 8-dip banks, 1x single dip switch (at position 11m) OSC: 20MHz Notes: - Loosely based off rmhaihai.cpp. Changes needed for merging both implementations seems too trivial to warrant a driver merge, basically just the video HW, the CRTC (448x224 clocked at 20 MHz?) and a few I/O bits looks very similar, the odd screen size is also a thing in srmp2.cpp and probably other Seta HWs. - CPU seems a bit too slow when deciding about what tile to discard, it also takes a bit too much to sync when a player takes a tile from the pond in a VS play. It's most likely btanb unless a gameplay video proves otherwise; - Position 11m on PCB has a single slider switch. It is not read by the CPUs at all, but instead zaps the nearby NVRAM contents if active. TODO: - Upper byte of I/O access seems to be some extra comms state machine (i.e. it more or less mimic the commands that CPUs send between each other). Maybe just a debug port that is read thru external HW? - Pinpoint how HW reads the three lead-filled marked as SW1 (position 1b). It's located next to the key matrix, maybe just ext debugging? */ #include "emu.h" #include "cpu/z80/z80.h" #include "machine/gen_latch.h" #include "machine/i8255.h" #include "machine/nvram.h" #include "sound/ay8910.h" #include "emupal.h" #include "screen.h" #include "speaker.h" #include "tilemap.h" namespace { class vsmjtria_state : public driver_device { public: vsmjtria_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_cpu(*this, "cpu%u", 0U) , m_gfxdecode(*this, "gfxdecode%u", 0U) , m_colorram(*this, "colorram%u", 0U) , m_videoram(*this, "videoram%u", 0U) , m_key{ { *this, "P1_KEY%u", 0U }, { *this, "P2_KEY%u", 0U } } , m_nvram(*this, "nvram%u", 0U) , m_nvram_data(*this, "nvram%u", 0U) , m_sw2(*this, "SW2") { } void vsmjtria(machine_config &config); void init_vsmjtria(); protected: virtual void machine_start() override; virtual void machine_reset() override; virtual void video_start() override; private: required_device_array m_cpu; required_device_array m_gfxdecode; required_shared_ptr_array m_colorram; required_shared_ptr_array m_videoram; required_ioport_array<5> m_key[2]; required_device_array m_nvram; required_shared_ptr_array m_nvram_data; required_ioport m_sw2; tilemap_t *m_bg_tilemap[2]; uint8_t m_keyboard_cmd[2]; template void nmi_set_w(uint8_t data); template void nmi_ack_w(uint8_t data); template void ctrl_w(uint8_t data); template uint8_t keyboard_r(); template void keyboard_w(uint8_t data); template TILE_GET_INFO_MEMBER(get_bg_tile_info); template void videoram_w(offs_t offset, uint8_t data); template void colorram_w(offs_t offset, uint8_t data); template uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); void main_prg_map(address_map &map); void main_io_map(address_map &map); void sub_prg_map(address_map &map); void sub_io_map(address_map &map); }; /* * Video section */ void vsmjtria_state::video_start() { m_bg_tilemap[0] = &machine().tilemap().create(*m_gfxdecode[0], tilemap_get_info_delegate(*this, FUNC(vsmjtria_state::get_bg_tile_info<0>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32); m_bg_tilemap[1] = &machine().tilemap().create(*m_gfxdecode[1], tilemap_get_info_delegate(*this, FUNC(vsmjtria_state::get_bg_tile_info<1>)), TILEMAP_SCAN_ROWS, 8, 8, 64, 32); } template void vsmjtria_state::videoram_w(offs_t offset, uint8_t data) { m_videoram[Which][offset] = data; m_bg_tilemap[Which]->mark_tile_dirty(offset); } template void vsmjtria_state::colorram_w(offs_t offset, uint8_t data) { m_colorram[Which][offset] = data; m_bg_tilemap[Which]->mark_tile_dirty(offset); } template TILE_GET_INFO_MEMBER(vsmjtria_state::get_bg_tile_info) { int attr = m_colorram[Which][tile_index]; int code = m_videoram[Which][tile_index] + ((attr & 0x07) << 8) + ((attr & 0x80) << 4); int color = (attr >> 3); tileinfo.set(0, code, color, 0); } template uint32_t vsmjtria_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { m_bg_tilemap[Which]->draw(screen, bitmap, cliprect, 0, 0); return 0; } /* * I/O */ template void vsmjtria_state::nmi_set_w(uint8_t data) { m_cpu[Which]->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); } template void vsmjtria_state::nmi_ack_w(uint8_t data) { m_cpu[Which]->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); } // TODO: flip screen and coin counter are actually never triggered, are they really connected for this HW? // (ctrl_w fn comes from rmhaihai.cpp assumption of being identical, it's most likely not present here) template void vsmjtria_state::ctrl_w(uint8_t data) { // flip_screen_set(data & 0x01); m_bg_tilemap[Which]->set_flip((data & 1) ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0); // (data & 0x02) is switched on and off in service mode machine().bookkeeping().coin_lockout_w(Which, ~data & 0x04); machine().bookkeeping().coin_counter_w(Which, data & 0x08); } template uint8_t vsmjtria_state::keyboard_r() { uint8_t data = 0x00; for (int key_n = 0; key_n < 5; key_n ++ ) { if (BIT(m_keyboard_cmd[Which], key_n)) data |= m_key[Which][key_n]->read(); } return data; } template void vsmjtria_state::keyboard_w(uint8_t data) { // bits 6-5 are always set when accessed, unknown purpose // 0x60 (at boot only, not read back), then 0x61 -> 0x62 -> 0x64 -> 0x68 -> 0x70 and back to 0x61 m_keyboard_cmd[Which] = data; } void vsmjtria_state::main_prg_map(address_map &map) { map(0x0000, 0x9fff).rom(); map(0xa000, 0xa7ff).ram().share("nvram0"); map(0xa800, 0xafff).ram().w(FUNC(vsmjtria_state::colorram_w<0>)).share(m_colorram[0]); map(0xb000, 0xb7ff).ram().w(FUNC(vsmjtria_state::videoram_w<0>)).share(m_videoram[0]); map(0xc000, 0xdfff).rom().region("cpu0", 0xa000); map(0xe000, 0xffff).rom(); } void vsmjtria_state::main_io_map(address_map &map) { map.global_mask(0xff); map(0x00, 0x03).rw("ppi0", FUNC(i8255_device::read), FUNC(i8255_device::write)); map(0x20, 0x20).r("ay0", FUNC(ay8910_device::data_r)); map(0x20, 0x21).w("ay0", FUNC(ay8910_device::address_data_w)); map(0x60, 0x60).w(FUNC(vsmjtria_state::ctrl_w<0>)); map(0xa0, 0xa0).w(FUNC(vsmjtria_state::nmi_ack_w<0>)); map(0xc0, 0xc0).w(FUNC(vsmjtria_state::nmi_set_w<1>)); map(0xe0, 0xe0).r("latch1", FUNC(generic_latch_8_device::read)).w("latch0", FUNC(generic_latch_8_device::write)); } void vsmjtria_state::sub_prg_map(address_map &map) { map(0x0000, 0x9fff).rom(); map(0xa000, 0xa7ff).ram().share("nvram1"); map(0xa800, 0xafff).ram().w(FUNC(vsmjtria_state::colorram_w<1>)).share(m_colorram[1]); map(0xb000, 0xb7ff).ram().w(FUNC(vsmjtria_state::videoram_w<1>)).share(m_videoram[1]); map(0xc000, 0xdfff).rom().region("cpu1", 0xa000); map(0xe000, 0xffff).rom(); } void vsmjtria_state::sub_io_map(address_map &map) { map.global_mask(0xff); map(0x00, 0x03).rw("ppi1", FUNC(i8255_device::read), FUNC(i8255_device::write)); map(0x20, 0x20).r("ay1", FUNC(ay8910_device::data_r)); map(0x20, 0x21).w("ay1", FUNC(ay8910_device::address_data_w)); map(0x60, 0x60).w(FUNC(vsmjtria_state::ctrl_w<1>)); map(0xa0, 0xa0).w(FUNC(vsmjtria_state::nmi_ack_w<1>)); map(0xc0, 0xc0).w(FUNC(vsmjtria_state::nmi_set_w<0>)); map(0xe0, 0xe0).r("latch0", FUNC(generic_latch_8_device::read)).w("latch1", FUNC(generic_latch_8_device::write)); } static INPUT_PORTS_START( vsmjtria ) // TODO: deduplicate all inputs here // either use a C-style macro or device-ify PORT_START("P1_COIN") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_BIT( 0x1e, IP_ACTIVE_LOW, IPT_UNKNOWN ) // stored at A38F along with bit 0 - seems to be vestigial, bit 2 tested at 02D1, but result discarded PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED ) // masked out after being read PORT_START("P2_COIN") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_BIT( 0x1e, IP_ACTIVE_LOW, IPT_UNKNOWN ) // stored at A38F along with bit 0 - seems to be vestigial, bit 2 tested at 02D1, but result discarded PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED ) // masked out after being read PORT_START("P1_KEY0") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START1 ) PORT_NAME("P1 Single Start") PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_MAHJONG_KAN ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_M ) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_I ) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_E ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_A ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P1_KEY1") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_MAHJONG_REACH ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_N ) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_J ) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_F ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_B ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P1_KEY2") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_MAHJONG_RON ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_CHI ) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_K ) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_G ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_C ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P1_KEY3") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_PON ) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_L ) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_H ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_D ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P1_KEY4") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_FLIP_FLOP ) PORT_PLAYER(1) PORT_NAME("P1 VS Start") PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P2_KEY0") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START2 ) PORT_NAME("P2 Single Start") PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_MAHJONG_KAN ) PORT_PLAYER(2) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_M ) PORT_PLAYER(2) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_I ) PORT_PLAYER(2) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_E ) PORT_PLAYER(2) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_A ) PORT_PLAYER(2) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P2_KEY1") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_MAHJONG_REACH ) PORT_PLAYER(2) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_N ) PORT_PLAYER(2) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_J ) PORT_PLAYER(2) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_F ) PORT_PLAYER(2) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_B ) PORT_PLAYER(2) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P2_KEY2") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_MAHJONG_RON ) PORT_PLAYER(2) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_CHI ) PORT_PLAYER(2) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_K ) PORT_PLAYER(2) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_G ) PORT_PLAYER(2) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_C ) PORT_PLAYER(2) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P2_KEY3") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_PON ) PORT_PLAYER(2) PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_MAHJONG_L ) PORT_PLAYER(2) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_MAHJONG_H ) PORT_PLAYER(2) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_MAHJONG_D ) PORT_PLAYER(2) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("P2_KEY4") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_MAHJONG_FLIP_FLOP ) PORT_PLAYER(2) PORT_NAME("P2 VS Start") PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED ) PORT_START("DSW1") // DSW1 and 2 are for main CPU PORT_DIPNAME( 0x01, 0x01, "Test Mode (P1)" ) PORT_DIPSETTING( 0x01, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x02, DEF_STR( Free_Play ) ) PORT_DIPSETTING( 0x02, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x0c, 0x0c, DEF_STR( Coinage ) ) PORT_DIPSETTING( 0x0c, DEF_STR( 2C_1C ) ) PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x04, DEF_STR( 1C_2C ) ) PORT_DIPSETTING( 0x08, DEF_STR( 1C_3C ) ) PORT_DIPNAME( 0x10, 0x10, "Unknown 1-4" ) PORT_DIPSETTING( 0x10, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x20, "Hide Girls" ) PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x40, "Unknown 1-6" ) PORT_DIPSETTING( 0x40, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x80, "Unknown 1-7" ) PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_START("DSW2") PORT_DIPNAME( 0x01, 0x01, "Unknown 2-0" ) PORT_DIPSETTING( 0x01, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x02, "Unknown 2-1" ) PORT_DIPSETTING( 0x02, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x04, "Unknown 2-2" ) PORT_DIPSETTING( 0x04, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x08, "Unknown 2-3" ) PORT_DIPSETTING( 0x08, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x10, 0x10, "Unknown 2-4" ) PORT_DIPSETTING( 0x10, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x20, "Unknown 2-5" ) PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x40, "Unknown 2-6" ) PORT_DIPSETTING( 0x40, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x80, "Unknown 2-7" ) PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_START("DSW3") // DSW3 and 4 are for sub CPU PORT_DIPNAME( 0x01, 0x01, "Test Mode (P2)" ) PORT_DIPSETTING( 0x01, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x02, DEF_STR( Free_Play ) ) PORT_DIPSETTING( 0x02, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x0c, 0x0c, DEF_STR( Coinage ) ) PORT_DIPSETTING( 0x0c, DEF_STR( 2C_1C ) ) PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x04, DEF_STR( 1C_2C ) ) PORT_DIPSETTING( 0x08, DEF_STR( 1C_3C ) ) PORT_DIPNAME( 0x10, 0x10, "Unknown 3-4" ) PORT_DIPSETTING( 0x10, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x20, "Hide Girls" ) PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x40, "Unknown 3-6" ) PORT_DIPSETTING( 0x40, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x80, "Unknown 3-7" ) PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_START("DSW4") PORT_DIPNAME( 0x01, 0x01, "Unknown 4-0" ) PORT_DIPSETTING( 0x01, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x02, "Unknown 4-1" ) PORT_DIPSETTING( 0x02, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x04, "Unknown 4-2" ) PORT_DIPSETTING( 0x04, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x08, "Unknown 4-3" ) PORT_DIPSETTING( 0x08, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x10, 0x10, "Unknown 4-4" ) PORT_DIPSETTING( 0x10, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x20, 0x20, "Unknown 4-5" ) PORT_DIPSETTING( 0x20, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x40, "Unknown 4-6" ) PORT_DIPSETTING( 0x40, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x80, 0x80, "Unknown 4-7" ) PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) // single slider switch, not read by CPU // grounds NVRAM contents, simulated in machine_reset fn PORT_START("SW2") PORT_DIPNAME( 0x01, 0x00, "Clear NVRAM at boot" ) PORT_DIPSETTING( 0x00, DEF_STR( No ) ) PORT_DIPSETTING( 0x01, DEF_STR( Yes ) ) INPUT_PORTS_END static const gfx_layout charlayout = { 8,8, RGN_FRAC(1,2), 3, { RGN_FRAC(1,2)+4, 0, 4 }, { 0, 1, 2, 3, 8*8+0, 8*8+1, 8*8+2, 8*8+3 }, { 0*8, 1*8, 2*8, 3*8, 4*8, 5*8, 6*8, 7*8 }, 16*8 }; static GFXDECODE_START( gfx_vsmjtria_main ) GFXDECODE_ENTRY( "maingfx", 0, charlayout, 0, 32 ) GFXDECODE_END static GFXDECODE_START( gfx_vsmjtria_sub ) GFXDECODE_ENTRY( "subgfx", 0, charlayout, 0, 32 ) GFXDECODE_END void vsmjtria_state::machine_start() { m_keyboard_cmd[0] = m_keyboard_cmd[1] = 0; save_item(NAME(m_keyboard_cmd)); } void vsmjtria_state::machine_reset() { if (m_sw2->read() & 1) { // Note: there's no direct setter in nvram_device that directly flushes contents for this case scenario for (auto &nvram : m_nvram_data) std::fill_n(&nvram[0], nvram.length(), 0); logerror("machine_reset: flush NVRAM contents\n"); } } void vsmjtria_state::vsmjtria(machine_config &config) { config.set_perfect_quantum("cpu0"); // crude way to make comms work - does it have FIFOs rather than latches? // Both CPUs are LH0080A -> Z80A, nominally 4 MHz // Clock also controls sound tempo, which at /4 sounds too fast already. Z80(config, m_cpu[0], 20_MHz_XTAL / 5); // divider guessed m_cpu[0]->set_addrmap(AS_PROGRAM, &vsmjtria_state::main_prg_map); m_cpu[0]->set_addrmap(AS_IO, &vsmjtria_state::main_io_map); m_cpu[0]->set_vblank_int("lscreen", FUNC(vsmjtria_state::irq0_line_hold)); Z80(config, m_cpu[1], 20_MHz_XTAL / 5); // divider guessed m_cpu[1]->set_addrmap(AS_PROGRAM, &vsmjtria_state::sub_prg_map); m_cpu[1]->set_addrmap(AS_IO, &vsmjtria_state::sub_io_map); m_cpu[1]->set_vblank_int("rscreen", FUNC(vsmjtria_state::irq0_line_hold)); NVRAM(config, m_nvram[0], nvram_device::DEFAULT_ALL_0); NVRAM(config, m_nvram[1], nvram_device::DEFAULT_ALL_0); GENERIC_LATCH_8(config, "latch0"); GENERIC_LATCH_8(config, "latch1"); i8255_device &ppi0(I8255(config, "ppi0")); ppi0.in_pa_callback().set_log("ppi0 pa read"); ppi0.in_pb_callback().set(FUNC(vsmjtria_state::keyboard_r<0>)); ppi0.in_pc_callback().set_ioport("P1_COIN"); ppi0.out_pa_callback().set(FUNC(vsmjtria_state::keyboard_w<0>)); ppi0.out_pb_callback().set_log("ppi0 pb write"); ppi0.out_pc_callback().set_log("ppi0 pc write"); i8255_device &ppi1(I8255(config, "ppi1")); ppi1.in_pa_callback().set_log("ppi1 pa read"); ppi1.in_pb_callback().set(FUNC(vsmjtria_state::keyboard_r<1>)); ppi1.in_pc_callback().set_ioport("P2_COIN"); ppi1.out_pa_callback().set(FUNC(vsmjtria_state::keyboard_w<1>)); ppi1.out_pb_callback().set_log("ppi1 pb write"); ppi1.out_pc_callback().set_log("ppi1 pc write"); PALETTE(config, "palette0", palette_device::RGB_444_PROMS, "mainproms", 256); PALETTE(config, "palette1", palette_device::RGB_444_PROMS, "subproms", 256); GFXDECODE(config, m_gfxdecode[0], "palette0", gfx_vsmjtria_main); GFXDECODE(config, m_gfxdecode[1], "palette1", gfx_vsmjtria_sub); screen_device &lscreen(SCREEN(config, "lscreen", SCREEN_TYPE_RASTER)); lscreen.set_refresh_hz(60); lscreen.set_vblank_time(ATTOSECONDS_IN_USEC(0)); lscreen.set_size(64*8, 32*8); lscreen.set_visarea(4*8, 60*8-1, 2*8, 30*8-1); lscreen.set_screen_update(FUNC(vsmjtria_state::screen_update<0>)); lscreen.set_palette("palette0"); screen_device &rscreen(SCREEN(config, "rscreen", SCREEN_TYPE_RASTER)); rscreen.set_refresh_hz(60); rscreen.set_vblank_time(ATTOSECONDS_IN_USEC(0)); rscreen.set_size(64*8, 32*8); rscreen.set_visarea(4*8, 60*8-1, 2*8, 30*8-1); rscreen.set_screen_update(FUNC(vsmjtria_state::screen_update<1>)); rscreen.set_palette("palette1"); SPEAKER(config, "speaker0").front_left(); // P1 side ay8910_device &ay0(AY8910(config, "ay0", 20_MHz_XTAL / 16)); // divider guessed ay0.port_a_read_callback().set_ioport("DSW2"); ay0.port_b_read_callback().set_ioport("DSW1"); ay0.add_route(ALL_OUTPUTS, "speaker0", 0.33); SPEAKER(config, "speaker1").front_right(); // P2 side ay8910_device &ay1(AY8910(config, "ay1", 20_MHz_XTAL / 16)); // divider guessed ay1.port_a_read_callback().set_ioport("DSW4"); ay1.port_b_read_callback().set_ioport("DSW3"); ay1.add_route(ALL_OUTPUTS, "speaker1", 0.33); } ROM_START( vsmjtria ) ROM_REGION( 0x10000, "cpu0", 0 ) // only 5.n8 and 6.n12 differ, and only for 2 bytes ROM_LOAD( "dyna 5.n8", 0x0000, 0x8000, CRC(526e3e6e) SHA1(db7b116f285761b040bb3b5cb3e00f5bf78606a9) ) ROM_LOAD( "dyna 41.n7", 0x8000, 0x8000, CRC(0c150add) SHA1(856de25fe43b656ea7c3066f838de6581df896c9) ) ROM_REGION( 0x10000, "cpu1", 0 ) ROM_LOAD( "dyna 6.n12", 0x0000, 0x8000, CRC(f1b09d7a) SHA1(89b653ce332f3b1c1c9f7b82e3636dfb329ebd93) ) ROM_LOAD( "dyna 41.n13", 0x8000, 0x8000, CRC(0c150add) SHA1(856de25fe43b656ea7c3066f838de6581df896c9) ) ROM_REGION( 0x20000, "maingfx", 0 ) // main and sub GFX are identical ROM_LOAD( "dyna 1.h4", 0x00000, 0x08000, CRC(3a16a4f8) SHA1(3a3cb6559be96a3070f53d192cc240e3619a749a) ) ROM_LOAD( "dyna 2.h5", 0x08000, 0x08000, CRC(a6c80d40) SHA1(f28efec537f7d5825d9336569fcbec38928eaba2) ) ROM_LOAD( "dyna 3.h7", 0x10000, 0x08000, CRC(30a22f1b) SHA1(52aea2dde753556df0a8d96b5a297ddd34d917cf) ) // 0x18000-0x1ffff empty space filled by the init function ROM_REGION( 0x20000, "subgfx", 0 ) ROM_LOAD( "dyna 1.h16", 0x00000, 0x08000, CRC(3a16a4f8) SHA1(3a3cb6559be96a3070f53d192cc240e3619a749a) ) ROM_LOAD( "dyna 2.h15", 0x08000, 0x08000, CRC(a6c80d40) SHA1(f28efec537f7d5825d9336569fcbec38928eaba2) ) ROM_LOAD( "dyna 3.h13", 0x10000, 0x08000, CRC(30a22f1b) SHA1(52aea2dde753556df0a8d96b5a297ddd34d917cf) ) // 0x18000-0x1ffff empty space filled by the init function ROM_REGION( 0x300, "mainproms", 0 ) // main and sub PROMs are identical ROM_LOAD( "82s129a_bkr.e5", 0x000, 0x100, CRC(7a6f5c49) SHA1(d0699e6c2c5760b62aba563e039f84f811d1c6a7) ) ROM_LOAD( "82s129a_bkg.e4", 0x100, 0x100, CRC(bafdf4e2) SHA1(ddaf98c6d04c9b199f0006ef3f8758ca6b0385ae) ) ROM_LOAD( "82s129a_bkb.e6", 0x200, 0x100, CRC(6dc54155) SHA1(c95d2b1ada45bc8b439b3562ebef451e3e27cb42) ) ROM_REGION( 0x300, "subproms", 0 ) ROM_LOAD( "82s129a_bkr.e15", 0x000, 0x100, CRC(7a6f5c49) SHA1(d0699e6c2c5760b62aba563e039f84f811d1c6a7) ) ROM_LOAD( "82s129a_bkg.e16", 0x100, 0x100, CRC(bafdf4e2) SHA1(ddaf98c6d04c9b199f0006ef3f8758ca6b0385ae) ) ROM_LOAD( "82s129a_bkb.e14", 0x200, 0x100, CRC(6dc54155) SHA1(c95d2b1ada45bc8b439b3562ebef451e3e27cb42) ) ROM_REGION( 0x200, "mainplds", 0 ) // main and sub PLDs are identical, read as PLS153 ROM_LOAD( "ck2605_tp1.d1", 0x000, 0x0eb, CRC(98da4486) SHA1(a5a300978e3e5ccb68e1768a50f3e9d9459ee81d) ) ROM_LOAD( "ck2605_tp2.l3", 0x100, 0x0eb, CRC(89b69b50) SHA1(c5d95488540e10092f9935427a6f680959c413d9) ) ROM_REGION( 0x200, "subplds", 0 ) // main and sub PLDs are identical, read as PLS153 ROM_LOAD( "ck2605_tp1.d19", 0x000, 0x0eb, CRC(98da4486) SHA1(a5a300978e3e5ccb68e1768a50f3e9d9459ee81d) ) ROM_LOAD( "ck2605_tp2.l17", 0x100, 0x0eb, CRC(89b69b50) SHA1(c5d95488540e10092f9935427a6f680959c413d9) ) ROM_END static void unpack_high_bit(int size, uint8_t *rom) { // unpack the high bit of gfx for (int b = size - 0x4000; b >= 0; b -= 0x4000) { if (b) memcpy(rom + b, rom + b/2, 0x2000); for (int a = 0; a < 0x2000;a++) { rom[a + b + 0x2000] = rom[a + b] >> 4; } } } void vsmjtria_state::init_vsmjtria() { unpack_high_bit(memregion("maingfx")->bytes() / 2, memregion("maingfx")->base() + 0x10000); unpack_high_bit(memregion("subgfx")->bytes() / 2, memregion("subgfx")->base() + 0x10000); } } // Anonymous namespace GAME( 1986, vsmjtria, 0, vsmjtria, vsmjtria, vsmjtria_state, init_vsmjtria, ROT0, "Dyna", "VS Mahjong Triangle", MACHINE_SUPPORTS_SAVE ) // puts Home Data in RAM?