// license:BSD-3-Clause // copyright-holders:Andrew Gardner /* Funai / Gakken Interstellar Laser Fantasy laserdisc hardware Driver by Andrew Gardner with help from Daphne Source Notes: Holding down the TEST switch while hitting reset will bring up the Self Test. Hit TEST switch again for color and monitor calibration. This is somewhat strange hardware : More z80's than necessary 3 bpp sprites 6-pin dip switches with odd handling inverted DIP logic? CPU2 maps RAM over where its ROM lives Todo: How does one best make one DIP switch bit from address 0x02 tie to two bits from address 0x03? Get real ROM labels! The current labels are unfortunately a bit odd. Add sprite drawing. Convert to tilemaps. Make it work - this one should be close right now :/. */ #include "emu.h" #include "cpu/z80/z80.h" #include "machine/gen_latch.h" #include "machine/ldv1000.h" #include "render.h" #include "speaker.h" class istellar_state : public driver_device { public: istellar_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_laserdisc(*this, "laserdisc") , m_tile_ram(*this, "tile_ram"), m_tile_control_ram(*this, "tile_ctrl_ram"), m_sprite_ram(*this, "sprite_ram"), m_maincpu(*this, "maincpu"), m_subcpu(*this, "sub"), m_gfxdecode(*this, "gfxdecode"), m_palette(*this, "palette") { } DECLARE_DRIVER_INIT(istellar); void istellar(machine_config &config); private: required_device m_laserdisc; required_shared_ptr m_tile_ram; required_shared_ptr m_tile_control_ram; required_shared_ptr m_sprite_ram; DECLARE_READ8_MEMBER(z80_2_ldp_read); DECLARE_READ8_MEMBER(z80_2_unknown_read); DECLARE_WRITE8_MEMBER(z80_2_ldp_write); virtual void machine_start() override; uint32_t screen_update_istellar(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); DECLARE_WRITE_LINE_MEMBER(vblank_irq); required_device m_maincpu; required_device m_subcpu; required_device m_gfxdecode; required_device m_palette; void z80_0_io(address_map &map); void z80_0_mem(address_map &map); void z80_1_io(address_map &map); void z80_1_mem(address_map &map); void z80_2_io(address_map &map); void z80_2_mem(address_map &map); }; /* There is only 1 crystal on the stack of 3 boards - speed is unknown, the following is Daphne's guess */ #define GUESSED_CLOCK (3072000) /* VIDEO GOODS */ uint32_t istellar_state::screen_update_istellar(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { int x, y; /* clear */ bitmap.fill(0, cliprect); /* Draw tiles */ for (y = 0; y < 32; y++) { for (x = 0; x < 32; x++) { int tile = m_tile_ram[x+y*32]; int attr = m_tile_control_ram[x+y*32]; m_gfxdecode->gfx(0)->transpen(bitmap,cliprect,tile,attr & 0x0f,0, 0, x*8, y*8, 0); } } /* Draw sprites */ return 0; } void istellar_state::machine_start() { } /* MEMORY HANDLERS */ /* Z80 1 R/W */ /* Z80 2 R/W */ READ8_MEMBER(istellar_state::z80_2_ldp_read) { uint8_t readResult = m_laserdisc->status_r(); logerror("CPU2 : reading LDP : %x\n", readResult); return readResult; } READ8_MEMBER(istellar_state::z80_2_unknown_read) { logerror("CPU2 : c000!\n"); return 0x00; } WRITE8_MEMBER(istellar_state::z80_2_ldp_write) { logerror("CPU2 : writing LDP : 0x%x\n", data); m_laserdisc->data_w(data); } /* PROGRAM MAPS */ void istellar_state::z80_0_mem(address_map &map) { map(0x0000, 0x9fff).rom(); map(0xa000, 0xa7ff).ram(); map(0xa800, 0xabff).ram().share("tile_ram"); map(0xac00, 0xafff).ram().share("tile_ctrl_ram"); map(0xb000, 0xb3ff).ram().share("sprite_ram"); } void istellar_state::z80_1_mem(address_map &map) { map(0x0000, 0x1fff).rom(); map(0x4000, 0x47ff).ram(); } void istellar_state::z80_2_mem(address_map &map) { map(0x0000, 0x17ff).rom(); map(0x1800, 0x1fff).ram(); map(0xc000, 0xc000).r(this, FUNC(istellar_state::z80_2_unknown_read)); /* Seems to be thrown away every time it's read - maybe interrupt related? */ } /* IO MAPS */ void istellar_state::z80_0_io(address_map &map) { map.global_mask(0xff); map(0x00, 0x00).portr("IN0"); map(0x02, 0x02).portr("DSW1"); map(0x03, 0x03).portr("DSW2"); /*AM_RANGE(0x04,0x04) AM_WRITE(volatile_palette_write)*/ map(0x05, 0x05).r("latch1", FUNC(generic_latch_8_device::read)).w("latch2", FUNC(generic_latch_8_device::write)); } void istellar_state::z80_1_io(address_map &map) { map.global_mask(0xff); map(0x00, 0x00).noprw(); /*AM_READWRITE(z80_1_slatch_read,z80_1_slatch_write)*/ map(0x01, 0x01).noprw(); /*AM_READWRITE(z80_1_nmienable,z80_1_soundwrite_front)*/ map(0x02, 0x02).noprw(); /*AM_WRITE(z80_1_soundwrite_rear)*/ } void istellar_state::z80_2_io(address_map &map) { map.global_mask(0xff); map(0x00, 0x00).rw(this, FUNC(istellar_state::z80_2_ldp_read), FUNC(istellar_state::z80_2_ldp_write)); map(0x01, 0x01).r("latch2", FUNC(generic_latch_8_device::read)).w("latch1", FUNC(generic_latch_8_device::write)); map(0x02, 0x02).r("latch2", FUNC(generic_latch_8_device::acknowledge_r)); /* AM_RANGE(0x03,0x03) AM_WRITE(z80_2_ldtrans_write)*/ } /* PORTS */ static INPUT_PORTS_START( istellar ) /* TEST MODE might display a 0 for a short and a 1 for an open circuit? If so, everything below is inverted. */ PORT_START("DSW1") PORT_DIPNAME( 0x01, 0x01, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION("SW1:!1") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x01, DEF_STR( On ) ) PORT_DIPNAME( 0x0e, 0x00, DEF_STR( Coin_A ) ) PORT_DIPLOCATION("SW1:!2,!3,!4") PORT_DIPSETTING( 0x02, DEF_STR( 2C_1C ) ) PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x06, DEF_STR( 2C_3C ) ) PORT_DIPSETTING( 0x08, DEF_STR( 1C_2C ) ) PORT_DIPSETTING( 0x04, DEF_STR( 1C_3C ) ) PORT_DIPSETTING( 0x0c, DEF_STR( 1C_6C ) ) PORT_DIPNAME( 0x30, 0x00, DEF_STR( Lives ) ) PORT_DIPLOCATION("SW1:!5,!6") PORT_DIPSETTING( 0x00, "3" ) PORT_DIPSETTING( 0x10, "4" ) PORT_DIPSETTING( 0x20, "5" ) PORT_DIPSETTING( 0x30, "10" ) PORT_DIPNAME( 0x40, 0x00, "Barrier UFO" ) PORT_DIPLOCATION("SW2:!1") PORT_DIPSETTING( 0x40, DEF_STR( No ) ) PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPNAME( 0x80, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW2:!2") PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) /* NOTE - bit 0x80 in the above read is combined with bits 0x03 in the below read to form the Coin_B settings. I'm unaware of what mechanism MAME will use to make this work right? */ /* "In case of inter-stellar upright type the coin switch 2 is not used." Quoth the manual. */ PORT_START("DSW2") PORT_DIPNAME( 0x01, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW2:!3") PORT_DIPSETTING( 0x01, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x02, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW2:!4") PORT_DIPSETTING( 0x02, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_DIPNAME( 0x04, 0x00, DEF_STR( Unused ) ) PORT_DIPLOCATION("SW2:!5") /* Maybe SERVICE? */ PORT_DIPSETTING( 0x04, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_SERVICE_NO_TOGGLE( 0x08, IP_ACTIVE_HIGH ) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_START2 ) PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_START1 ) PORT_START("IN0") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_4WAY PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_4WAY PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_4WAY PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_4WAY PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_BUTTON2 ) /* SERVICE might be hanging out back here */ INPUT_PORTS_END static const gfx_layout istellar_gfx_layout = { 8,8, RGN_FRAC(1,3), 3, { RGN_FRAC(0,3), RGN_FRAC(1,3), RGN_FRAC(2,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( istellar ) GFXDECODE_ENTRY( "gfx1", 0, istellar_gfx_layout, 0x0, 0x20 ) GFXDECODE_END WRITE_LINE_MEMBER(istellar_state::vblank_irq) { if (state) { /* Interrupt presumably comes from VBlank */ m_maincpu->set_input_line(0, HOLD_LINE); /* Interrupt presumably comes from the LDP's status strobe */ m_subcpu->set_input_line(0, ASSERT_LINE); } } /* DRIVER */ MACHINE_CONFIG_START(istellar_state::istellar) /* main cpu */ MCFG_CPU_ADD("maincpu", Z80, GUESSED_CLOCK) MCFG_CPU_PROGRAM_MAP(z80_0_mem) MCFG_CPU_IO_MAP(z80_0_io) /* sound cpu */ MCFG_CPU_ADD("audiocpu", Z80, GUESSED_CLOCK) MCFG_CPU_PROGRAM_MAP(z80_1_mem) MCFG_CPU_IO_MAP(z80_1_io) /* ldp comm cpu */ MCFG_CPU_ADD("sub", Z80, GUESSED_CLOCK) MCFG_CPU_PROGRAM_MAP(z80_2_mem) MCFG_CPU_IO_MAP(z80_2_io) MCFG_GENERIC_LATCH_8_ADD("latch1") MCFG_GENERIC_LATCH_8_ADD("latch2") MCFG_GENERIC_LATCH_DATA_PENDING_CB(INPUTLINE("sub", INPUT_LINE_NMI)) MCFG_GENERIC_LATCH_SEPARATE_ACKNOWLEDGE(true) MCFG_LASERDISC_LDV1000_ADD("laserdisc") MCFG_LASERDISC_OVERLAY_DRIVER(256, 256, istellar_state, screen_update_istellar) MCFG_LASERDISC_OVERLAY_PALETTE("palette") /* video hardware */ MCFG_LASERDISC_SCREEN_ADD_NTSC("screen", "laserdisc") MCFG_SCREEN_VBLANK_CALLBACK(WRITELINE(istellar_state, vblank_irq)) // Daphne says "TODO: get the real interstellar resistor values" MCFG_PALETTE_ADD_RRRRGGGGBBBB_PROMS("palette", "proms", 256) MCFG_GFXDECODE_ADD("gfxdecode", "palette", istellar) /* sound hardware */ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker") MCFG_SOUND_MODIFY("laserdisc") MCFG_SOUND_ROUTE(0, "lspeaker", 1.0) MCFG_SOUND_ROUTE(1, "rspeaker", 1.0) MACHINE_CONFIG_END /* There is a photo of the PCB with blurry IC locations and labels. Comments reflect what I can (barely) see. */ ROM_START( istellar ) /* Main program CPU */ ROM_REGION( 0xa000, "maincpu", 0 ) ROM_LOAD( "rom2.top", 0x0000, 0x2000, CRC(5d643381) SHA1(75ca52c28a52f534eda00c18b0db97e9923ff670) ) /* At IC location C63 (top board) - label ? */ ROM_LOAD( "rom3.top", 0x2000, 0x2000, CRC(ce5a2b09) SHA1(2de6a6e993c3411577ac0c834db8aaf16fb007ed) ) /* At IC location C64 (top board) - label ? */ ROM_LOAD( "rom4.top", 0x4000, 0x2000, CRC(7c2cb1f1) SHA1(ffd92510c03c2d35a59d233883c2b9f57394a51c) ) /* At IC location C65 (top board) - label ? */ ROM_LOAD( "rom5.top", 0x6000, 0x2000, CRC(354377f6) SHA1(bcf95b7ee1b47854e10baf24b0d8af3d56738b99) ) /* At IC location C66 (top board) - label ? */ ROM_LOAD( "rom6.top", 0x8000, 0x2000, CRC(0319bf40) SHA1(f324626e457c3eb7d6b74bc6afbfcc3aab2b3c72) ) /* At IC location C67 (top board) - label ? */ /* Sound CPU */ ROM_REGION( 0x2000, "audiocpu", 0 ) ROM_LOAD( "rom1.top", 0x0000, 0x2000, CRC(4f34fb1d) SHA1(56ca19344c84c5989d0be797e2759f84760310be) ) /* At IC location C62 (top board) - label ? */ /* LDP Communications CPU */ ROM_REGION( 0x2000, "sub", 0 ) ROM_LOAD( "rom11.bot", 0x0000, 0x2000, CRC(165cbc57) SHA1(39463888f22ec3125f0686066d923a9aae79a8f7) ) /* At IC location C12 (bottom board) - label IS11 */ /* Tiles */ ROM_REGION( 0x6000, "gfx1", 0 ) ROM_LOAD( "rom9.bot", 0x0000, 0x2000, CRC(9d79acb6) SHA1(72af972695face0016afce8a26c629d963e86d48) ) /* At IC location C47? (bottom board) - label ? */ ROM_LOAD( "rom8.bot", 0x2000, 0x2000, CRC(e9c9e490) SHA1(79aa35552b984018bc723adece5c40a0833a313c) ) /* At IC location C48? (bottom board) - label ? */ ROM_LOAD( "rom7.bot", 0x4000, 0x2000, CRC(1447ce3a) SHA1(8545cec108df6adab303802b1407c89b2dceba21) ) /* At IC location C49? (bottom board) - label ? */ /* Color PROMs */ ROM_REGION( 0x300, "proms", 0 ) ROM_LOAD( "red6b.bot", 0x000, 0x100, CRC(5c52f844) SHA1(a8a3d91f3247ad13c805d8d8288b07f3cdaf1189) ) /* At IC location C63? (bottom board) - label ? */ ROM_LOAD( "green6c.bot", 0x100, 0x100, CRC(7d8c845c) SHA1(04ae2ca0cc6679e21346ce34e9e01aa5bf4e2067) ) /* At IC location C62? (bottom board) - label ? */ ROM_LOAD( "blue6d.bot", 0x200, 0x100, CRC(5ebb81f9) SHA1(285d60f2894c524ca80fc68ad7c2dfd9093a67ea) ) /* At IC location C61? (bottom board) - label ? */ DISK_REGION( "laserdisc" ) DISK_IMAGE_READONLY( "istellar", 0, NO_DUMP ) ROM_END DRIVER_INIT_MEMBER(istellar_state,istellar) { //m_z80_2_nmi_enable = 0; #if 0 { uint8_t *ROM = memregion("maincpu")->base(); ROM[0x4465] = 0x00; ROM[0x4466] = 0x00; ROM[0x4478] = 0x00; ROM[0x4479] = 0x00; ROM[0x43b4] = 0x00; ROM[0x43b5] = 0x00; ROM[0x4409] = 0x20; ROM[0x46de] = 0x00; ROM[0x46df] = 0x00; } #endif } // YEAR NAME PARENT MACHINE INPUT STATE INIT MONITOR COMPANY FULLNAME FLAGS) GAME( 1983, istellar, 0, istellar, istellar, istellar_state, istellar, ROT0, "Funai/Gakken", "Interstellar Laser Fantasy", MACHINE_NOT_WORKING | MACHINE_NO_SOUND)