// license:BSD-3-Clause // copyright-holders:Andrew Gardner /* Funai / Gakken Esh's Aurunmilla laserdisc hardware Driver by Andrew Gardner with help from Daphne Source Notes: (dumper's note) Esh's Aurunmilla can be played in an Interstellar cabinet by swapping the main pcb and the laserdisc. Sound & video pcbs are the same. Control panel is different however. Esh's Aurunmilla can be a 2 player game, while Interstellar is a 1 player only game. Hold down TEST while resetting the machine - pops up test mode where... The DIPs are software-controlled. Two joysticks appear in the IO TEST, but the photos of the control panel I've seen show only 1. Eshb has some junk in the IO TEST screen. Maybe a bad dump? Todo: - LD TROUBLE appears at POST. Sync/timing issue? - Performance spike after some time of gameplay, CPU comms gets corrupt? - How to init NVRAM? Current defaults definitely aren't. - Wrong overlay colors; - Convert to tilemaps (see next ToDo for feasibility). - Rumor has it there's an analog beep hanging off 0xf5? Implement it and finish off 0xf5 bits. - NVRAM range 0xe000-0xe800 might be too large. It doesn't seem to write past 0xe600... - Maybe some of the IPT_UNKNOWNs do something? - Hook up LED's to the MAME lamp system. */ #include "emu.h" #include "cpu/z80/z80.h" #include "machine/ldv1000.h" #include "machine/nvram.h" #include "sound/beep.h" #include "emupal.h" #include "speaker.h" class esh_state : public driver_device { public: esh_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_laserdisc(*this, "laserdisc") , m_screen(*this, "screen") , m_tile_ram(*this, "tile_ram") , m_tile_control_ram(*this, "tile_ctrl_ram") , m_maincpu(*this, "maincpu") , m_gfxdecode(*this, "gfxdecode") , m_beep(*this, "beeper") , m_palette(*this, "palette") { } void esh(machine_config &config); void init_esh(); protected: virtual void machine_start() override; //virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; private: required_device m_laserdisc; required_device m_screen; required_shared_ptr m_tile_ram; required_shared_ptr m_tile_control_ram; bool m_ld_video_visible; DECLARE_READ8_MEMBER(ldp_read); DECLARE_WRITE8_MEMBER(ldp_write); DECLARE_WRITE8_MEMBER(misc_write); DECLARE_WRITE8_MEMBER(led_writes); DECLARE_WRITE8_MEMBER(nmi_line_w); bool m_nmi_enable; void esh_palette(palette_device &palette) const; uint32_t screen_update_esh(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); INTERRUPT_GEN_MEMBER(vblank_callback_esh); DECLARE_WRITE_LINE_MEMBER(ld_command_strobe_cb); required_device m_maincpu; required_device m_gfxdecode; required_device m_beep; required_device m_palette; void z80_0_io(address_map &map); void z80_0_mem(address_map &map); }; /* From daphne */ #define PCB_CLOCK (18432000) /* VIDEO GOODS */ uint32_t esh_state::screen_update_esh(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect) { int charx, chary; const uint8_t pal_bank = m_ld_video_visible == true ? 0x10 : 0x00; const uint32_t trans_mask = m_ld_video_visible == true ? 0 : -1; gfx_element *gfx;// = m_gfxdecode->gfx(0); /* clear */ bitmap.fill(0, cliprect); /* Draw tiles */ for (charx = 0; charx < 32; charx++) { for (chary = 0; chary < 32; chary++) { int current_screen_character = (chary*32) + charx; int palIndex = (m_tile_control_ram[current_screen_character] & 0x0f); int tileOffs = (m_tile_control_ram[current_screen_character] & 0x10) >> 4; bool blinkLine = bool((m_tile_control_ram[current_screen_character] & 0x40) >> 6); bool blinkChar = bool((m_tile_control_ram[current_screen_character] & 0x80) >> 7); // TODO: blink timing if(blinkChar == true && m_screen->frame_number() & 8) { if(trans_mask == 0) continue; for(int yi=0;yi<8;yi++) for(int xi=0;xi<8;xi++) bitmap.pix32(yi+chary*8, xi+charx*8) = m_palette->pen(palIndex * 8 + pal_bank * 0x100); continue; } if(blinkLine == true && m_screen->frame_number() & 8) gfx = m_gfxdecode->gfx(1); else gfx = m_gfxdecode->gfx(0); gfx->transpen(bitmap,cliprect, m_tile_ram[current_screen_character] + (0x100 * tileOffs), palIndex + pal_bank, 0, 0, charx*8, chary*8, trans_mask); } } /* Draw sprites */ return 0; } /* MEMORY HANDLERS */ READ8_MEMBER(esh_state::ldp_read) { return m_laserdisc->status_r(); } WRITE8_MEMBER(esh_state::ldp_write) { m_laserdisc->data_w(data); } WRITE8_MEMBER(esh_state::misc_write) { /* Bit 0 unknown */ // if (data & 0x02) // logerror("BEEP!\n"); m_beep->set_state(BIT(data, 1)); // polarity unknown /* Bit 2 unknown */ m_ld_video_visible = bool(!((data & 0x08) >> 3)); /* Bits 4-7 unknown */ /* They cycle through a repeating pattern though */ } WRITE8_MEMBER(esh_state::led_writes) { switch(offset) { case 0x00: logerror("WRITING 0x%x to P1's START LED\n", data); break; case 0x01: logerror("WRITING 0x%x to P2's START LED\n", data); break; case 0x02: logerror("WRITING 0x%x to P1's BUTTON1 LED\n", data); break; case 0x03: logerror("WRITING 0x%x to P1's BUTTON2 LED\n", data); break; case 0x04: logerror("WRITING 0x%x to P2's BUTTON1 LED\n", data); break; case 0x05: logerror("WRITING 0x%x to P2's BUTTON2 LED\n", data); break; case 0x06: /* Likely coming soon */ break; case 0x07: /* Likely coming soon */ break; } } WRITE8_MEMBER(esh_state::nmi_line_w) { // 0 -> 1 transition enables this, else disabled? m_nmi_enable = (data & 1) == 1; //if (data == 0x00) // m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); //if (data == 0x01) // m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); if (data & 0xfe) logerror("NMI line unknown bit set %02x\n",data); } /* PROGRAM MAPS */ void esh_state::z80_0_mem(address_map &map) { map(0x0000, 0x3fff).rom(); map(0xe000, 0xe7ff).ram().share("nvram"); map(0xf000, 0xf3ff).ram().share("tile_ram"); map(0xf400, 0xf7ff).ram().share("tile_ctrl_ram"); } /* IO MAPS */ void esh_state::z80_0_io(address_map &map) { map.global_mask(0xff); map(0xf0, 0xf0).portr("IN0"); map(0xf1, 0xf1).portr("IN1"); map(0xf2, 0xf2).portr("IN2"); map(0xf3, 0xf3).portr("IN3"); map(0xf4, 0xf4).rw(FUNC(esh_state::ldp_read), FUNC(esh_state::ldp_write)); map(0xf5, 0xf5).w(FUNC(esh_state::misc_write)); /* Continuously writes repeating patterns */ map(0xf8, 0xfd).w(FUNC(esh_state::led_writes)); map(0xfe, 0xfe).w(FUNC(esh_state::nmi_line_w)); /* Both 0xfe and 0xff flip quickly between 0 and 1 */ map(0xff, 0xff).noprw(); /* (they're probably not NMI enables - likely LED's like their neighbors :) */ } /* (someday 0xf8-0xff will probably be a single handler) */ /* PORTS */ static INPUT_PORTS_START( esh ) PORT_START("IN0") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START1 ) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 ) PORT_SERVICE( 0x10, IP_ACTIVE_LOW ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_START("IN1") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(1) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_START("IN2") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_START("IN3") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) INPUT_PORTS_END void esh_state::esh_palette(palette_device &palette) const { uint8_t const *const color_prom = memregion("proms")->base(); // Oddly enough, the top 4 bits of each byte is 0 <- ??? for (int i = 0; i < palette.entries(); i++) { int bit0, bit1, bit2; // Presumably resistor values would help here // red component bit0 = BIT(color_prom[i+0x100], 0); bit1 = BIT(color_prom[i+0x100], 1); bit2 = BIT(color_prom[i+0x100], 2); int const r = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; // green component bit0 = 0; //BIT(color_prom[i+0x100], 0); bit1 = BIT(color_prom[i+0x100], 3); bit2 = BIT(color_prom[i+0x100], 4); int const g = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; // blue component // TODO: actually opaque flag int b; if((color_prom[i+0x100] >> 7) & 1) b = 0xff; else { bit0 = 0; //BIT(color_prom[i+0x100], 5); bit1 = BIT(color_prom[i+0x100], 5); bit2 = BIT(color_prom[i+0x100], 6); b = 0x21 * bit0 + 0x47 * bit1 + 0x97 * bit2; } palette.set_pen_color(i, rgb_t(r, g, b)); } } static const gfx_layout esh_gfx_layout = { 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_esh ) GFXDECODE_ENTRY("gfx1", 0, esh_gfx_layout, 0x0, 0x20) GFXDECODE_ENTRY("gfx2", 0, esh_gfx_layout, 0x0, 0x20) GFXDECODE_END INTERRUPT_GEN_MEMBER(esh_state::vblank_callback_esh) { // IRQ device.execute().set_input_line(0, HOLD_LINE); } // TODO: 0xfe NMI enabled after writing to LD command port, NMI reads LD port. WRITE_LINE_MEMBER(esh_state::ld_command_strobe_cb) { if(m_nmi_enable) m_maincpu->set_input_line(INPUT_LINE_NMI, state ? ASSERT_LINE : CLEAR_LINE); } void esh_state::machine_start() { } /* DRIVER */ MACHINE_CONFIG_START(esh_state::esh) /* main cpu */ MCFG_DEVICE_ADD("maincpu", Z80, PCB_CLOCK/6) /* The denominator is a Daphne guess based on PacMan's hardware */ MCFG_DEVICE_PROGRAM_MAP(z80_0_mem) MCFG_DEVICE_IO_MAP(z80_0_io) MCFG_DEVICE_VBLANK_INT_DRIVER("screen", esh_state, vblank_callback_esh) NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); MCFG_LASERDISC_LDV1000_ADD("laserdisc") MCFG_LASERDISC_LDV1000_COMMAND_STROBE_CB(WRITELINE(*this, esh_state, ld_command_strobe_cb)) MCFG_LASERDISC_OVERLAY_DRIVER(256, 256, esh_state, screen_update_esh) MCFG_LASERDISC_OVERLAY_PALETTE(m_palette) /* video hardware */ MCFG_LASERDISC_SCREEN_ADD_NTSC("screen", "laserdisc") PALETTE(config, m_palette, FUNC(esh_state::esh_palette), 256); GFXDECODE(config, m_gfxdecode, m_palette, gfx_esh); /* sound hardware */ SPEAKER(config, "lspeaker").front_left(); SPEAKER(config, "rspeaker").front_right(); MCFG_DEVICE_MODIFY("laserdisc") MCFG_SOUND_ROUTE(0, "lspeaker", 1.0) MCFG_SOUND_ROUTE(1, "rspeaker", 1.0) SPEAKER(config, "mono").front_center(); MCFG_DEVICE_ADD("beeper", BEEP, 2000) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.25) MACHINE_CONFIG_END // we just disable even lines so we can simulate line blinking #define ROM_INTERLACED_GFX \ ROM_REGION( 0x3000, "gfx2", 0 ) \ ROM_COPY( "gfx1", 0, 0, 0x3000 ) \ ROMX_FILL( 0, 0x3000, 0x00, ROM_SKIP(1) ) ROM_START( esh ) /* Main program CPU */ ROM_REGION( 0x4000, "maincpu", 0 ) ROM_LOAD( "is1.h8", 0x0000, 0x2000, CRC(114c912b) SHA1(7c033a102d046199f3e2c6787579dac5b5295d50) ) ROM_LOAD( "is2.f8", 0x2000, 0x2000, CRC(0e3b6e62) SHA1(5e8160180e20705e727329f9d70305fcde176a25) ) /* Tiles */ ROM_REGION( 0x3000, "gfx1", 0 ) ROM_LOAD( "a.m3", 0x0000, 0x1000, CRC(a04736d8) SHA1(3b642b5d7168cf4a09328eee54c532be815d2bcf) ) ROM_LOAD( "b.l3", 0x1000, 0x1000, CRC(9366dde7) SHA1(891db65384d47d13355b2eea37f57c34bc775c8f) ) ROM_LOAD( "c.k3", 0x2000, 0x1000, CRC(a936ef01) SHA1(bcacb281ccb72ceb57fb6a79380cc3a9688743c4) ) ROM_INTERLACED_GFX /* Color (+other) PROMs */ ROM_REGION( 0x400, "proms", 0 ) ROM_LOAD( "rgb.j1", 0x000, 0x200, CRC(1e9f795f) SHA1(61a58694929fa39b2412bc9244e5681d65a0eacb) ) ROM_LOAD( "h.c5", 0x200, 0x100, CRC(abde5e4b) SHA1(9dd3a7fd523b519ac613b9f08ae9cc962992cf5d) ) /* Video timing? */ ROM_LOAD( "v.c6", 0x300, 0x100, CRC(7157ba22) SHA1(07355f30efe46196d216356eda48a59fc622e43f) ) DISK_REGION( "laserdisc" ) DISK_IMAGE_READONLY( "esh_ver2_en", 0, SHA1(c04709d95fd92259f013ec1cd28e3e36a163abe1) ) ROM_END ROM_START( esha ) /* Main program CPU */ ROM_REGION( 0x4000, "maincpu", 0 ) ROM_LOAD( "is1.h8", 0x0000, 0x2000, CRC(114c912b) SHA1(7c033a102d046199f3e2c6787579dac5b5295d50) ) ROM_LOAD( "is2.f8", 0x2000, 0x2000, CRC(7a562f49) SHA1(acfa49b3b3d96b001a5dbdee39cbb0ca80be1763) ) // sldh /* Tiles */ ROM_REGION( 0x3000, "gfx1", 0 ) ROM_LOAD( "a.m3", 0x0000, 0x1000, CRC(a04736d8) SHA1(3b642b5d7168cf4a09328eee54c532be815d2bcf) ) ROM_LOAD( "b.l3", 0x1000, 0x1000, CRC(9366dde7) SHA1(891db65384d47d13355b2eea37f57c34bc775c8f) ) ROM_LOAD( "c.k3", 0x2000, 0x1000, CRC(a936ef01) SHA1(bcacb281ccb72ceb57fb6a79380cc3a9688743c4) ) ROM_INTERLACED_GFX /* Color (+other) PROMs */ ROM_REGION( 0x400, "proms", 0 ) ROM_LOAD( "rgb.j1", 0x000, 0x200, CRC(1e9f795f) SHA1(61a58694929fa39b2412bc9244e5681d65a0eacb) ) ROM_LOAD( "h.c5", 0x200, 0x100, CRC(abde5e4b) SHA1(9dd3a7fd523b519ac613b9f08ae9cc962992cf5d) ) /* Video timing? */ ROM_LOAD( "v.c6", 0x300, 0x100, CRC(7157ba22) SHA1(07355f30efe46196d216356eda48a59fc622e43f) ) DISK_REGION( "laserdisc" ) DISK_IMAGE_READONLY( "esh_ver2_en", 0, SHA1(c04709d95fd92259f013ec1cd28e3e36a163abe1) ) ROM_END ROM_START( eshb ) /* Main program CPU */ ROM_REGION( 0x4000, "maincpu", 0 ) ROM_LOAD( "1.h8", 0x0000, 0x2000, CRC(8d27d363) SHA1(529d8e4283e736edb5a9193df1ed8d0164471864) ) /* Hand-written ROM label */ ROM_LOAD( "is2.f8", 0x2000, 0x2000, CRC(0e3b6e62) SHA1(5e8160180e20705e727329f9d70305fcde176a25) ) /* Tiles */ ROM_REGION( 0x3000, "gfx1", 0 ) ROM_LOAD( "a.m3", 0x0000, 0x1000, CRC(a04736d8) SHA1(3b642b5d7168cf4a09328eee54c532be815d2bcf) ) ROM_LOAD( "b.l3", 0x1000, 0x1000, CRC(9366dde7) SHA1(891db65384d47d13355b2eea37f57c34bc775c8f) ) ROM_LOAD( "c.k3", 0x2000, 0x1000, CRC(a936ef01) SHA1(bcacb281ccb72ceb57fb6a79380cc3a9688743c4) ) ROM_INTERLACED_GFX /* Color (+other) PROMs */ ROM_REGION( 0x400, "proms", 0 ) ROM_LOAD( "rgb.j1", 0x000, 0x200, CRC(1e9f795f) SHA1(61a58694929fa39b2412bc9244e5681d65a0eacb) ) ROM_LOAD( "h.c5", 0x200, 0x100, CRC(abde5e4b) SHA1(9dd3a7fd523b519ac613b9f08ae9cc962992cf5d) ) /* Video timing? */ ROM_LOAD( "v.c6", 0x300, 0x100, CRC(7157ba22) SHA1(07355f30efe46196d216356eda48a59fc622e43f) ) DISK_REGION( "laserdisc" ) DISK_IMAGE_READONLY( "esh_ver2_en", 0, SHA1(c04709d95fd92259f013ec1cd28e3e36a163abe1) ) ROM_END void esh_state::init_esh() { } // YEAR NAME PARENT MACHINE INPUT STATE INIT MONITOR COMPANY FULLNAME FLAGS GAME( 1983, esh, 0, esh, esh, esh_state, init_esh, ROT0, "Funai/Gakken", "Esh's Aurunmilla (set 1)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_COLORS) GAME( 1983, esha, esh, esh, esh, esh_state, init_esh, ROT0, "Funai/Gakken", "Esh's Aurunmilla (set 2)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_COLORS) GAME( 1983, eshb, esh, esh, esh, esh_state, init_esh, ROT0, "Funai/Gakken", "Esh's Aurunmilla (set 3)", MACHINE_NOT_WORKING|MACHINE_IMPERFECT_COLORS)