nosniff Content-Security-Policy: default-src 'none' Content-Type: text/plain; charset=UTF-8 Content-Length: 38910 Content-Disposition: inline; filename="bmcpokr.cpp" Last-Modified: Sun, 04 May 2025 13:46:03 GMT Expires: Sun, 04 May 2025 13:51:03 GMT ETag: "094dde0f078a9ea085ae8498556bd88401142c5e" // license:BSD-3-Clause // copyright-holders:Luca Elia /*************************************************************************** BMC games using a 68k + VDB40817/SYA70521 driver by Luca Elia Similar to bmcbowl, koftball, popobear CPU: 68000 Video: BMC VDB40817 + BMC SYA70521 Sound: M6295 + UM3567 Other: BMC B816140 (CPLD) ***************************************************************************/ #include "emu.h" #include "cpu/m68000/m68000.h" #include "video/ramdac.h" #include "sound/ym2413.h" #include "sound/okim6295.h" #include "machine/nvram.h" #include "machine/ticket.h" #include "machine/timer.h" #include "emupal.h" #include "screen.h" #include "speaker.h" #include "tilemap.h" class bmcpokr_state : public driver_device { public: bmcpokr_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this,"maincpu"), m_hopper(*this,"hopper"), m_videoram(*this, "videoram_%u", 1U), m_scrollram(*this, "scrollram_%u", 1U), m_pixram(*this, "pixram"), m_priority(*this, "priority"), m_layerctrl(*this, "layerctrl"), m_gfxdecode(*this, "gfxdecode"), m_palette(*this, "palette") { } DECLARE_READ_LINE_MEMBER(hopper_r); void bmcpokr(machine_config &config); void mjmaglmp(machine_config &config); protected: virtual void device_post_load() override; private: virtual void machine_start() override; // Devices required_device m_maincpu; required_device m_hopper; required_shared_ptr_array m_videoram; required_shared_ptr_array m_scrollram; required_shared_ptr m_pixram; required_shared_ptr m_priority; required_shared_ptr m_layerctrl; required_device m_gfxdecode; required_device m_palette; // Protection uint16_t m_prot_val; uint16_t prot_r(); void prot_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); uint16_t unk_r(); // I/O uint8_t m_mux; void mux_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0); uint16_t dsw_r(); uint16_t mjmaglmp_dsw_r(); uint16_t mjmaglmp_key_r(); // Interrrupts uint8_t m_irq_enable; void irq_enable_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0); void irq_ack_w(uint8_t data); TIMER_DEVICE_CALLBACK_MEMBER(interrupt); // Video tilemap_t *m_tilemap[2]; template TILE_GET_INFO_MEMBER(get_tile_info); template void videoram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); std::unique_ptr m_pixbitmap; void pixbitmap_redraw(); uint8_t m_pixpal; void pixram_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); void pixpal_w(offs_t offset, uint8_t data, uint8_t mem_mask = ~0); virtual void video_start() override; void draw_layer(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); void bmcpokr_mem(address_map &map); void mjmaglmp_map(address_map &map); void ramdac_map(address_map &map); }; /*************************************************************************** Video Hardware ***************************************************************************/ // Tilemaps template void bmcpokr_state::videoram_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&m_videoram[N][offset]); m_tilemap[N]->mark_tile_dirty(offset); } template TILE_GET_INFO_MEMBER(bmcpokr_state::get_tile_info) { uint16_t data = m_videoram[N][tile_index]; tileinfo.set(0, data, 0, (data & 0x8000) ? TILE_FLIPX : 0); } void bmcpokr_state::video_start() { m_tilemap[0] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(bmcpokr_state::get_tile_info<0>)), TILEMAP_SCAN_ROWS, 8,8, 128,128); m_tilemap[1] = &machine().tilemap().create(*m_gfxdecode, tilemap_get_info_delegate(*this, FUNC(bmcpokr_state::get_tile_info<1>)), TILEMAP_SCAN_ROWS, 8,8, 128,128); m_tilemap[0]->set_transparent_pen(0); m_tilemap[1]->set_transparent_pen(0); m_tilemap[0]->set_scroll_rows(1); m_tilemap[1]->set_scroll_rows(1); m_tilemap[0]->set_scroll_cols(1); m_tilemap[1]->set_scroll_cols(1); m_pixbitmap = std::make_unique(0x400, 0x200); } // 1024 x 512 bitmap. 4 bits per pixel (every byte encodes 2 pixels) + palette register void bmcpokr_state::pixram_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&m_pixram[offset]); int const x = (offset & 0xff) << 2; int const y = (offset >> 8); uint16_t const pixpal = (m_pixpal & 0xf) << 4; uint16_t pen; if (ACCESSING_BITS_8_15) { pen = (data >> 12) & 0xf; m_pixbitmap->pix(y, x + 0) = pen ? pixpal + pen : 0; pen = (data >> 8) & 0xf; m_pixbitmap->pix(y, x + 1) = pen ? pixpal + pen : 0; } if (ACCESSING_BITS_0_7) { pen = (data >> 4) & 0xf; m_pixbitmap->pix(y, x + 2) = pen ? pixpal + pen : 0; pen = (data >> 0) & 0xf; m_pixbitmap->pix(y, x + 3) = pen ? pixpal + pen : 0; } } void bmcpokr_state::pixbitmap_redraw() { uint16_t pixpal = (m_pixpal & 0xf) << 4; int offset = 0; for (int y = 0; y < 512; y++) { for (int x = 0; x < 1024; x += 4) { uint16_t const data = m_pixram[offset++]; uint16_t pen; pen = (data >> 12) & 0xf; m_pixbitmap->pix(y, x + 0) = pen ? pixpal + pen : 0; pen = (data >> 8) & 0xf; m_pixbitmap->pix(y, x + 1) = pen ? pixpal + pen : 0; pen = (data >> 4) & 0xf; m_pixbitmap->pix(y, x + 2) = pen ? pixpal + pen : 0; pen = (data >> 0) & 0xf; m_pixbitmap->pix(y, x + 3) = pen ? pixpal + pen : 0; } } } void bmcpokr_state::pixpal_w(offs_t offset, uint8_t data, uint8_t mem_mask) { uint8_t old = m_pixpal; if (old != COMBINE_DATA(&m_pixpal)) pixbitmap_redraw(); } void bmcpokr_state::device_post_load() { pixbitmap_redraw(); } // Screen update void bmcpokr_state::draw_layer(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect, int layer) { tilemap_t *tmap; uint16_t *scroll; uint16_t ctrl; switch (layer) { case 1: tmap = m_tilemap[0]; scroll = m_scrollram[0]; ctrl = (m_layerctrl[0] >> 8) & 0xff; break; case 2: tmap = m_tilemap[1]; scroll = m_scrollram[1]; ctrl = (m_layerctrl[0] >> 0) & 0xff; break; default: tmap = nullptr; scroll = m_scrollram[2]; ctrl = (m_layerctrl[1] >> 8) & 0xff; break; } if (ctrl == 0x00) return; bool linescroll = (ctrl == 0x1f); rectangle clip = cliprect; for (int y = 0; y < 0x100; y++) { if (linescroll) { if ( (y < cliprect.top()) || (y > cliprect.bottom()) ) continue; clip.sety(y, y); } int sx = (scroll[y] & 0xff) * 4; int sy = ((scroll[y] >> 8) & 0xff) - y; if (tmap) { tmap->set_scrollx(0, sx); tmap->set_scrolly(0, sy); tmap->draw(screen, bitmap, clip, 0, 0); } else { sx = -sx; sy = -sy; copyscrollbitmap_trans(bitmap, *m_pixbitmap, 1, &sx, 1, &sy, cliprect, 0); } if (!linescroll) return; } } uint32_t bmcpokr_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { int layers_ctrl = -1; #ifdef MAME_DEBUG if (screen.machine().input().code_pressed(KEYCODE_Z)) { int msk = 0; if (screen.machine().input().code_pressed(KEYCODE_Q)) msk |= 1; if (screen.machine().input().code_pressed(KEYCODE_W)) msk |= 2; if (screen.machine().input().code_pressed(KEYCODE_A)) msk |= 4; if (msk != 0) layers_ctrl &= msk; } #endif bitmap.fill(m_palette->black_pen(), cliprect); if (layers_ctrl & 2) draw_layer(screen, bitmap, cliprect, 2); /* title: 17, 13/17 dogs: 1b, 13, 17 service: 17 game: 17 */ if (*m_priority & 0x0008) { if (layers_ctrl & 4) draw_layer(screen, bitmap, cliprect, 3); if (layers_ctrl & 1) draw_layer(screen, bitmap, cliprect, 1); } else { if (layers_ctrl & 1) draw_layer(screen, bitmap, cliprect, 1); if (layers_ctrl & 4) draw_layer(screen, bitmap, cliprect, 3); } return 0; } /*************************************************************************** Protection ***************************************************************************/ uint16_t bmcpokr_state::unk_r() { return machine().rand(); } // Hack! uint16_t bmcpokr_state::prot_r() { switch (m_prot_val >> 8) { case 0x00: return 0x1d << 8; case 0x94: return 0x81 << 8; } return 0x00 << 8; } void bmcpokr_state::prot_w(offs_t offset, uint16_t data, uint16_t mem_mask) { COMBINE_DATA(&m_prot_val); // logerror("%s: prot val = %04x\n", machine().describe_context(), m_prot_val); } /*************************************************************************** Memory Maps ***************************************************************************/ void bmcpokr_state::mux_w(offs_t offset, uint8_t data, uint8_t mem_mask) { COMBINE_DATA(&m_mux); m_hopper->motor_w(BIT(data, 0)); // hopper motor machine().bookkeeping().coin_counter_w(1, BIT(data, 1)); // coin-in / key-in machine().bookkeeping().coin_counter_w(2, BIT(data, 2)); // pay-out // data & 0x60 // DSW mux // data & 0x80 // ? always on // popmessage("mux %04x", m_mux); } uint16_t bmcpokr_state::dsw_r() { switch ((m_mux >> 5) & 3) { case 0: return ioport("DSW4")->read() << 8; case 1: return ioport("DSW3")->read() << 8; case 2: return ioport("DSW2")->read() << 8; case 3: return ioport("DSW1")->read() << 8; } return 0xff << 8; } READ_LINE_MEMBER(bmcpokr_state::hopper_r) { // motor off should clear the sense bit (I guess ticket.c should actually do this). // Otherwise a hopper bit stuck low will prevent several keys from being registered. return (m_mux & 0x01) ? m_hopper->line_r() : 1; } void bmcpokr_state::irq_enable_w(offs_t offset, uint8_t data, uint8_t mem_mask) { COMBINE_DATA(&m_irq_enable); } void bmcpokr_state::irq_ack_w(uint8_t data) { for (int i = 1; i < 8; i++) { if (BIT(data, i)) { m_maincpu->set_input_line(i, CLEAR_LINE); } } } void bmcpokr_state::bmcpokr_mem(address_map &map) { map(0x000000, 0x03ffff).rom(); map(0x210000, 0x21ffff).ram().share("nvram"); map(0x280000, 0x287fff).ram().w(FUNC(bmcpokr_state::videoram_w<0>)).share("videoram_1"); map(0x288000, 0x28ffff).ram().w(FUNC(bmcpokr_state::videoram_w<1>)).share("videoram_2"); map(0x290000, 0x297fff).ram(); map(0x2a0000, 0x2dffff).ram().w(FUNC(bmcpokr_state::pixram_w)).share("pixram"); map(0x2ff800, 0x2ff9ff).ram().share("scrollram_1"); map(0x2ffa00, 0x2ffbff).ram().share("scrollram_2"); map(0x2ffc00, 0x2ffdff).ram().share("scrollram_3"); map(0x2ffe00, 0x2fffff).ram(); map(0x320000, 0x320003).ram().share("layerctrl"); map(0x330000, 0x330001).rw(FUNC(bmcpokr_state::prot_r), FUNC(bmcpokr_state::prot_w)); map(0x340000, 0x340001).ram(); // 340001.b, rw map(0x340002, 0x340003).ram(); // 340003.b, w(9d) map(0x340007, 0x340007).w(FUNC(bmcpokr_state::irq_ack_w)); map(0x340009, 0x340009).w(FUNC(bmcpokr_state::irq_enable_w)); map(0x34000e, 0x34000f).ram().share("priority"); // 34000f.b, w (priority?) map(0x340017, 0x340017).w(FUNC(bmcpokr_state::pixpal_w)); map(0x340018, 0x340019).ram(); // 340019.b, w map(0x34001a, 0x34001b).r(FUNC(bmcpokr_state::unk_r)).nopw(); map(0x34001c, 0x34001d).ram(); // 34001d.b, w(0) map(0x350001, 0x350001).w("ramdac", FUNC(ramdac_device::index_w)); map(0x350003, 0x350003).w("ramdac", FUNC(ramdac_device::pal_w)); map(0x350005, 0x350005).w("ramdac", FUNC(ramdac_device::mask_w)); map(0x360000, 0x360001).r(FUNC(bmcpokr_state::dsw_r)); map(0x370000, 0x370001).portr("INPUTS"); map(0x380001, 0x380001).w(FUNC(bmcpokr_state::mux_w)); map(0x390000, 0x390003).w("ymsnd", FUNC(ym2413_device::write)).umask16(0x00ff); map(0x398001, 0x398001).rw("oki", FUNC(okim6295_device::read), FUNC(okim6295_device::write)); map(0x3b0000, 0x3b0001).portr("INPUTS2"); } uint16_t bmcpokr_state::mjmaglmp_dsw_r() { switch ((m_mux >> 4) & 7) { case 7: return ioport("DSW1")->read() << 8; case 6: return ioport("DSW2")->read() << 8; case 5: return ioport("DSW3")->read() << 8; case 3: return ioport("DSW4")->read() << 8; } return 0xff << 8; } uint16_t bmcpokr_state::mjmaglmp_key_r() { uint16_t key = 0x3f; switch ((m_mux >> 4) & 7) { case 0: key = ioport("KEY1")->read(); break; case 1: key = ioport("KEY2")->read(); break; case 2: key = ioport("KEY3")->read(); break; case 3: key = ioport("KEY4")->read(); break; case 4: key = ioport("KEY5")->read(); break; } return ioport("INPUTS")->read() | (key & 0x3f); } void bmcpokr_state::mjmaglmp_map(address_map &map) { map(0x000000, 0x03ffff).rom(); map(0x210000, 0x21ffff).ram().share("nvram"); map(0x280000, 0x287fff).ram().w(FUNC(bmcpokr_state::videoram_w<0>)).share("videoram_1"); map(0x288000, 0x28ffff).ram().w(FUNC(bmcpokr_state::videoram_w<1>)).share("videoram_2"); map(0x290000, 0x297fff).ram(); map(0x2a0000, 0x2dffff).ram().w(FUNC(bmcpokr_state::pixram_w)).share("pixram"); map(0x2ff800, 0x2ff9ff).ram().share("scrollram_1"); map(0x2ffa00, 0x2ffbff).ram().share("scrollram_2"); map(0x2ffc00, 0x2ffdff).ram().share("scrollram_3"); map(0x2ffe00, 0x2fffff).ram(); map(0x320000, 0x320003).ram().share("layerctrl"); map(0x388001, 0x388001).w(FUNC(bmcpokr_state::mux_w)); map(0x390000, 0x390001).r(FUNC(bmcpokr_state::mjmaglmp_dsw_r)); map(0x398000, 0x398001).r(FUNC(bmcpokr_state::mjmaglmp_key_r)); map(0x3c8800, 0x3c8803).w("ymsnd", FUNC(ym2413_device::write)).umask16(0x00ff); map(0x3c9001, 0x3c9001).rw("oki", FUNC(okim6295_device::read), FUNC(okim6295_device::write)); map(0x3c9801, 0x3c9801).w("ramdac", FUNC(ramdac_device::index_w)); map(0x3c9803, 0x3c9803).w("ramdac", FUNC(ramdac_device::pal_w)); map(0x3c9805, 0x3c9805).w("ramdac", FUNC(ramdac_device::mask_w)); map(0x3ca000, 0x3ca001).ram(); // 3ca001.b, rw map(0x3ca002, 0x3ca003).ram(); // 3ca003.b, w(9d) map(0x3ca007, 0x3ca007).w(FUNC(bmcpokr_state::irq_ack_w)); map(0x3ca009, 0x3ca009).w(FUNC(bmcpokr_state::irq_enable_w)); map(0x3ca00e, 0x3ca00f).ram().share("priority"); // 3ca00f.b, w (priority?) map(0x3ca017, 0x3ca017).w(FUNC(bmcpokr_state::pixpal_w)); map(0x3ca018, 0x3ca019).ram(); // 3ca019.b, w map(0x3ca01a, 0x3ca01b).r(FUNC(bmcpokr_state::unk_r)).nopw(); map(0x3ca01c, 0x3ca01d).ram(); // 3ca01d.b, w(0) } /*************************************************************************** Input Ports ***************************************************************************/ static INPUT_PORTS_START( bmcpokr ) PORT_START("INPUTS") // Poker controls: PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_GAMBLE_KEYIN ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // KEY-IN [KEY-IN, credit +500] PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_POKER_HOLD5 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // HOLD 5 PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_POKER_HOLD4 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // HOLD 4 PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_POKER_HOLD2 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // HOLD 2 PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_POKER_HOLD1 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // HOLD 1 [INSTRUCTIONS] PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_POKER_HOLD3 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // HOLD 3 PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_GAMBLE_DEAL ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // n.a. [START, ESC in service mode] PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_GAMBLE_TAKE ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // SCORE PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_GAMBLE_BET ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // BET [BET, credit -1] PORT_BIT( 0x0200, IP_ACTIVE_HIGH,IPT_CUSTOM ) PORT_READ_LINE_MEMBER(bmcpokr_state, hopper_r) // HP [HOPPER, credit -100] PORT_SERVICE_NO_TOGGLE( 0x0400, IP_ACTIVE_LOW ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // ACCOUNT [SERVICE MODE] PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_GAMBLE_KEYOUT ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // KEY-OUT [KEY-OUT, no hopper] PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_GAMBLE_D_UP ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // DOUBLE-UP PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_GAMBLE_LOW ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // SMALL PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_GAMBLE_HIGH ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) // BIG PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x80) PORT_IMPULSE(5) // COIN-IN [COIN-IN, credit +100, coin-jam] // Joystick controls: PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_GAMBLE_KEYIN ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // B2 [KEY-IN, credit +500] PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(1) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // C1 PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(1) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // B1 PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(1) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // 1 (4th) PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_POKER_HOLD1 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // 1 (3rd) [INSTRUCTIONS] PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // A1 PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_GAMBLE_DEAL ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // n.a. [START, ESC in service mode] PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // 2 (3rd) PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_GAMBLE_BET ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // 1 (2nd) [BET, credit -1] // PORT_BIT( 0x0200, IP_ACTIVE_HIGH,IPT_CUSTOM ) PORT_READ_LINE_MEMBER(bmcpokr_state, hopper_r) // HP [HOPPER, credit -100] PORT_SERVICE_NO_TOGGLE( 0x0400, IP_ACTIVE_LOW ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // A2 [SERVICE MODE] PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_GAMBLE_KEYOUT ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // C2 [KEY-OUT, no hopper] PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_GAMBLE_D_UP ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // S1 [START, ESC in service mode] PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT) PORT_PLAYER(2) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // 2 (4th) PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) // 1 (1st) PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_CONDITION("DSW4",0x80,EQUALS,0x00) PORT_IMPULSE(5) // (1st) [COIN-IN, credit +100, coin-jam] PORT_START("INPUTS2") PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_GAMBLE_PAYOUT ) // (2nd) [COIN-OUT, hopper (otherwise pay-error)] PORT_START("DSW1") PORT_DIPNAME( 0x01, 0x00, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION("DIP1:1") PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPSETTING( 0x01, DEF_STR( No ) ) PORT_DIPNAME( 0x02, 0x00, "Doube-Up Game" ) PORT_DIPLOCATION("DIP1:2") PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPSETTING( 0x02, DEF_STR( No ) ) PORT_DIPNAME( 0x04, 0x00, "Slot Machine" ) PORT_DIPLOCATION("DIP1:3") PORT_DIPSETTING( 0x00, "Machinery" ) PORT_DIPSETTING( 0x04, "??" ) PORT_DIPNAME( 0x08, 0x00, "Poker Game" ) PORT_DIPLOCATION("DIP1:4") PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPSETTING( 0x08, DEF_STR( No ) ) PORT_DIPUNKNOWN_DIPLOC( 0x10, 0x10, "DIP1:5" ) PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "DIP1:6" ) PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "DIP1:7" ) PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "DIP1:8" ) PORT_START("DSW2") PORT_DIPNAME( 0x03, 0x03, "Credit Limit" ) PORT_DIPLOCATION("DIP2:1,2") PORT_DIPSETTING( 0x03, "5k" ) PORT_DIPSETTING( 0x02, "10k" ) PORT_DIPSETTING( 0x01, "50k" ) PORT_DIPSETTING( 0x00, "100k" ) PORT_DIPNAME( 0x0c, 0x0c, "Key-In Limit" ) PORT_DIPLOCATION("DIP2:3,4") PORT_DIPSETTING( 0x0c, "5k" ) PORT_DIPSETTING( 0x08, "10k" ) PORT_DIPSETTING( 0x04, "20k" ) PORT_DIPSETTING( 0x00, "50k" ) PORT_DIPNAME( 0x10, 0x10, "Open Cards Mode" ) PORT_DIPLOCATION("DIP2:5") PORT_DIPSETTING( 0x10, "Reels" ) PORT_DIPSETTING( 0x00, "Turn Over" ) PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "DIP2:6" ) PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "DIP2:7" ) PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "DIP2:8" ) PORT_START("DSW3") PORT_DIPNAME( 0x03, 0x03, "Win Rate" ) PORT_DIPLOCATION("DIP3:1,2") PORT_DIPSETTING( 0x02, "96" ) PORT_DIPSETTING( 0x01, "97" ) PORT_DIPSETTING( 0x03, "98" ) PORT_DIPSETTING( 0x00, "99" ) PORT_DIPNAME( 0x0c, 0x0c, "Double-Up Rate" ) PORT_DIPLOCATION("DIP3:3,4") PORT_DIPSETTING( 0x08, "93" ) PORT_DIPSETTING( 0x04, "94" ) PORT_DIPSETTING( 0x00, "95" ) PORT_DIPSETTING( 0x0c, "96" ) PORT_DIPNAME( 0x10, 0x10, "Bonus Bet" ) PORT_DIPLOCATION("DIP3:5") PORT_DIPSETTING( 0x10, "30" ) PORT_DIPSETTING( 0x00, "48" ) PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "DIP3:6" ) PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "DIP3:7" ) PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "DIP3:8" ) PORT_START("DSW4") PORT_DIPNAME( 0x01, 0x01, "Max Bet" ) PORT_DIPLOCATION("DIP4:1") PORT_DIPSETTING( 0x01, "48" ) PORT_DIPSETTING( 0x00, "96" ) PORT_DIPNAME( 0x06, 0x06, "Min Bet" ) PORT_DIPLOCATION("DIP4:2,3") PORT_DIPSETTING( 0x06, "6" ) PORT_DIPSETTING( 0x04, "12" ) PORT_DIPSETTING( 0x02, "18" ) PORT_DIPSETTING( 0x00, "30" ) PORT_DIPNAME( 0x18, 0x18, "Credits Per Coin" ) PORT_DIPLOCATION("DIP4:4,5") PORT_DIPSETTING( 0x10, "10" ) PORT_DIPSETTING( 0x08, "20" ) PORT_DIPSETTING( 0x18, "50" ) PORT_DIPSETTING( 0x00, "100" ) PORT_DIPNAME( 0x60, 0x60, "Credits Per Key-In" ) PORT_DIPLOCATION("DIP4:6,7") PORT_DIPSETTING( 0x40, "10" ) PORT_DIPSETTING( 0x20, "50" ) PORT_DIPSETTING( 0x60, "100" ) PORT_DIPSETTING( 0x00, "500" ) PORT_DIPNAME( 0x80, 0x80, DEF_STR( Controls ) ) PORT_DIPLOCATION("DIP4:8") PORT_DIPSETTING( 0x80, "Poker" ) PORT_DIPSETTING( 0x00, DEF_STR( Joystick ) ) INPUT_PORTS_END static INPUT_PORTS_START( mjmaglmp ) PORT_START("INPUTS") // Joystick controls: PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_START1 ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // START PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // UP PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // DOWN PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // LEFT PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // RIGHT PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // 1P E1 (select) PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_COIN2 ) // NOTE PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_GAMBLE_KEYOUT ) // KEY DOWN PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_OTHER ) PORT_NAME("Pay Out") PORT_CODE(KEYCODE_O) // PAY PORT_BIT( 0x0200, IP_ACTIVE_HIGH,IPT_CUSTOM ) PORT_READ_LINE_MEMBER(bmcpokr_state, hopper_r) // HOPPER PORT_SERVICE_NO_TOGGLE( 0x0400, IP_ACTIVE_LOW ) // ACCOUNT PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("Reset") // RESET PORT_BIT( 0x1000, IP_ACTIVE_LOW, IPT_UNKNOWN ) // (unused) PORT_BIT( 0x2000, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // 1P E2 (bet) PORT_BIT( 0x4000, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_CONDITION("DSW2",0x01,EQUALS,0x00) // 1P E3 (select) PORT_BIT( 0x8000, IP_ACTIVE_LOW, IPT_COIN1 ) // COIN PORT_START("KEY1") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_MAHJONG_A ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_E ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_I ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_MAHJONG_M ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_MAHJONG_KAN ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_START1 ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_START("KEY2") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_MAHJONG_B ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_F ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_J ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_MAHJONG_N ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_MAHJONG_REACH ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_MAHJONG_BET ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_START("KEY3") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_MAHJONG_C ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_G ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_K ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_MAHJONG_CHI ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_MAHJONG_RON ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_START("KEY4") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_MAHJONG_D ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_H ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_L ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_MAHJONG_PON ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_START("KEY5") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_MAHJONG_SCORE ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_MAHJONG_DOUBLE_UP) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_MAHJONG_BIG ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_MAHJONG_SMALL ) PORT_CONDITION("DSW2",0x01,EQUALS,0x01) PORT_START("DSW1") PORT_DIPNAME( 0x01, 0x00, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION("DIP1:1") PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPSETTING( 0x01, DEF_STR( No ) ) PORT_DIPNAME( 0x02, 0x00, "Doube-Up Game" ) PORT_DIPLOCATION("DIP1:2") PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPSETTING( 0x02, DEF_STR( No ) ) PORT_DIPNAME( 0x04, 0x04, "Coin Sw. Function" ) PORT_DIPLOCATION("DIP1:3") PORT_DIPSETTING( 0x00, "Coin" ) PORT_DIPSETTING( 0x04, "Note" ) PORT_DIPNAME( 0x08, 0x08, "Pay Sw. Function" ) PORT_DIPLOCATION("DIP1:4") PORT_DIPSETTING( 0x00, "Pay-Out" ) PORT_DIPSETTING( 0x08, "Key-Down" ) PORT_DIPNAME( 0x10, 0x10, "Game Hint" ) PORT_DIPLOCATION("DIP1:5") PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPSETTING( 0x10, DEF_STR( No ) ) PORT_DIPNAME( 0x20, 0x20, "Direct Double" ) PORT_DIPLOCATION("DIP1:6") PORT_DIPSETTING( 0x00, DEF_STR( Yes ) ) PORT_DIPSETTING( 0x20, DEF_STR( No ) ) PORT_DIPNAME( 0x40, 0x40, "Coin Acceptor" ) PORT_DIPLOCATION("DIP1:7") PORT_DIPSETTING( 0x00, "Mechanical" ) PORT_DIPSETTING( 0x40, "Electronic" ) PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "DIP1:8" ) PORT_START("DSW2") PORT_DIPNAME( 0x01, 0x01, DEF_STR( Controls ) ) PORT_DIPLOCATION("DIP2:1") PORT_DIPSETTING( 0x01, "Keyboard" ) PORT_DIPSETTING( 0x00, DEF_STR( Joystick ) ) PORT_DIPNAME( 0x02, 0x02, "Key-In Limit" ) PORT_DIPLOCATION("DIP2:2") PORT_DIPSETTING( 0x00, "1000" ) PORT_DIPSETTING( 0x02, "5000" ) PORT_DIPNAME( 0x04, 0x04, "Double Lose Pool" ) PORT_DIPLOCATION("DIP2:3") PORT_DIPSETTING( 0x00, "50" ) PORT_DIPSETTING( 0x04, "100" ) PORT_DIPNAME( 0x18, 0x18, "Double Over / Round Bonus" ) PORT_DIPLOCATION("DIP2:4,5") PORT_DIPSETTING( 0x10, "100 / 10" ) PORT_DIPSETTING( 0x18, "200 / 10" ) PORT_DIPSETTING( 0x08, "300 / 15" ) PORT_DIPSETTING( 0x00, "500 / 25" ) PORT_DIPUNKNOWN_DIPLOC( 0x20, 0x20, "DIP2:6" ) PORT_DIPUNKNOWN_DIPLOC( 0x40, 0x40, "DIP2:7" ) PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "DIP2:8" ) PORT_START("DSW3") PORT_DIPNAME( 0x03, 0x03, "Pay-Out Rate" ) PORT_DIPLOCATION("DIP3:1,2") PORT_DIPSETTING( 0x02, "75" ) PORT_DIPSETTING( 0x01, "82" ) PORT_DIPSETTING( 0x03, "85" ) PORT_DIPSETTING( 0x00, "88" ) PORT_DIPNAME( 0x0c, 0x0c, "Double-Up Rate" ) PORT_DIPLOCATION("DIP3:3,4") PORT_DIPSETTING( 0x08, "95" ) PORT_DIPSETTING( 0x04, "96" ) PORT_DIPSETTING( 0x00, "97" ) PORT_DIPSETTING( 0x0c, "98" ) PORT_DIPNAME( 0x30, 0x30, "Game Enhance Type" ) PORT_DIPLOCATION("DIP3:5,6") PORT_DIPSETTING( 0x10, "Small" ) PORT_DIPSETTING( 0x00, "Big" ) PORT_DIPSETTING( 0x30, "Normal" ) PORT_DIPSETTING( 0x20, "Bonus" ) PORT_DIPNAME( 0xc0, 0xc0, "Credit Limit" ) PORT_DIPLOCATION("DIP3:7,8") PORT_DIPSETTING( 0x00, "300" ) PORT_DIPSETTING( 0x80, "500" ) PORT_DIPSETTING( 0x40, "1000" ) PORT_DIPSETTING( 0xc0, "2000" ) PORT_START("DSW4") PORT_DIPNAME( 0x01, 0x01, "Max Bet" ) PORT_DIPLOCATION("DIP4:1") PORT_DIPSETTING( 0x01, "10" ) PORT_DIPSETTING( 0x00, "20" ) PORT_DIPNAME( 0x06, 0x06, "Min Bet" ) PORT_DIPLOCATION("DIP4:2,3") PORT_DIPSETTING( 0x00, "1" ) PORT_DIPSETTING( 0x06, "3" ) PORT_DIPSETTING( 0x04, "6" ) PORT_DIPSETTING( 0x02, "9" ) PORT_DIPNAME( 0x18, 0x18, DEF_STR( Coinage ) ) PORT_DIPLOCATION("DIP4:4,5") PORT_DIPSETTING( 0x00, DEF_STR( 2C_1C ) ) PORT_DIPSETTING( 0x18, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x10, DEF_STR( 1C_2C ) ) PORT_DIPSETTING( 0x08, DEF_STR( 1C_3C ) ) PORT_DIPNAME( 0x60, 0x60, "Credits Per Key-In" ) PORT_DIPLOCATION("DIP4:6,7") PORT_DIPSETTING( 0x40, "5" ) PORT_DIPSETTING( 0x60, "10" ) PORT_DIPSETTING( 0x20, "50" ) PORT_DIPSETTING( 0x00, "100" ) PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x80, "DIP4:8" ) INPUT_PORTS_END /*************************************************************************** Graphics Layout ***************************************************************************/ static const gfx_layout tiles8x8_layout = { 8,8, RGN_FRAC(1,2), 8, { RGN_FRAC(1,2)+0, RGN_FRAC(1,2)+1, RGN_FRAC(1,2)+2, RGN_FRAC(1,2)+3, 0, 1, 2, 3 }, { STEP8(0, 4) }, { STEP8(0, 4*8) }, 8*8*4 }; static GFXDECODE_START( gfx_bmcpokr ) GFXDECODE_ENTRY( "gfx1", 0, tiles8x8_layout, 0, 1 ) GFXDECODE_END /*************************************************************************** Machine Drivers ***************************************************************************/ TIMER_DEVICE_CALLBACK_MEMBER(bmcpokr_state::interrupt) { int scanline = param; if (scanline == 240) if (BIT(m_irq_enable, 2)) m_maincpu->set_input_line(2, ASSERT_LINE); if (scanline == 128) if (BIT(m_irq_enable, 3)) m_maincpu->set_input_line(3, ASSERT_LINE); if (scanline == 64) if (BIT(m_irq_enable, 6)) m_maincpu->set_input_line(6, ASSERT_LINE); } void bmcpokr_state::ramdac_map(address_map &map) { map(0x000, 0x3ff).rw("ramdac", FUNC(ramdac_device::ramdac_pal_r), FUNC(ramdac_device::ramdac_rgb666_w)); } void bmcpokr_state::machine_start() { save_item(NAME(m_prot_val)); save_item(NAME(m_mux)); save_item(NAME(m_irq_enable)); save_item(NAME(m_pixpal)); } void bmcpokr_state::bmcpokr(machine_config &config) { M68000(config, m_maincpu, XTAL(42'000'000) / 4); // 68000 @10.50MHz (42/4) m_maincpu->set_addrmap(AS_PROGRAM, &bmcpokr_state::bmcpokr_mem); TIMER(config, "scantimer", 0).configure_scanline(FUNC(bmcpokr_state::interrupt), "screen", 0, 1); screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); screen.set_refresh(HZ_TO_ATTOSECONDS(58.935)); // HSync - 15.440kHz, VSync - 58.935Hz screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */ screen.set_screen_update(FUNC(bmcpokr_state::screen_update)); screen.set_size(64*8, 32*8); screen.set_visarea(0*8, 60*8-1, 0*8, 30*8-1); screen.set_palette(m_palette); PALETTE(config, m_palette).set_entries(256); ramdac_device &ramdac(RAMDAC(config, "ramdac", 0, m_palette)); ramdac.set_addrmap(0, &bmcpokr_state::ramdac_map); GFXDECODE(config, m_gfxdecode, m_palette, gfx_bmcpokr); NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); TICKET_DISPENSER(config, m_hopper, 0); m_hopper->set_period(attotime::from_msec(10)); m_hopper->set_senses(TICKET_MOTOR_ACTIVE_HIGH, TICKET_STATUS_ACTIVE_LOW, false); // hopper stuck low if too slow SPEAKER(config, "mono").front_center(); YM2413(config, "ymsnd", XTAL(42'000'000) / 12).add_route(ALL_OUTPUTS, "mono", 1.00); // UM3567 @3.50MHz (42/12) OKIM6295(config, "oki", XTAL(42'000'000) / 40, okim6295_device::PIN7_HIGH).add_route(ALL_OUTPUTS, "mono", 1.00); // M6295 @1.05MHz (42/40), pin 7 not verified } void bmcpokr_state::mjmaglmp(machine_config &config) { bmcpokr(config); m_maincpu->set_addrmap(AS_PROGRAM, &bmcpokr_state::mjmaglmp_map); } /*************************************************************************** ROMs Loading ***************************************************************************/ /*************************************************************************** Dongfang Shenlong ("Eastern Dragon") BMC 1999 PCB Layout ---------- BMC-A81212 |---------------------------------------| | CH-A-401 CH-M-301 CH-M-701 | |M11B416256A UM3567 | |42MHz CH-M-201 CH-M-101 M6295 | | VDB40817 1| | HM86171-80 VOL 0| | SYA70521 W| | LM324 7805 A| |DSW1 Y| | 68000 TDA2003 | |DSW2 CH-M-505 CH-M-605 | | 6264 6264 2| |DSW3 CPLD 74HC132 74LS05 2| | 555 W| |DSW4 A| | BATT SW JAMMA Y| |---------------------------------------| Notes: RAM - M11B416256, 6264(x2) VDB40817/SYA70521 - Unknown QFP100 CPLD - unknown PLCC44 chip labelled 'BMC B816140' BATT - 5.5 volt 0.047F super cap 68000 @10.50MHz (42/4) M6295 @1.05MHz (42/40) UM3567 @3.50MHz (42/12) HSync - 15.440kHz VSync - 58.935Hz ***************************************************************************/ ROM_START( bmcpokr ) ROM_REGION( 0x40000, "maincpu", 0 ) /* 68000 Code */ ROM_LOAD16_BYTE( "ch-m-605.u13", 0x000000, 0x20000, CRC(c5c3fcd1) SHA1(b77fef734c290d52ae877a24bb3ee42b24eb5cb8) ) ROM_LOAD16_BYTE( "ch-m-505.u12", 0x000001, 0x20000, CRC(d6effaf1) SHA1(b446d3beb3393bc8b3bcd0d543945e6fb6a375b9) ) ROM_REGION( 0x200000, "gfx1", 0 ) ROM_LOAD16_BYTE( "ch-m-101.u39", 0x000000, 0x80000, CRC(f4b82e0a) SHA1(f545c6ab1375518de06900f02a0eb5af1edeeb47) ) ROM_LOAD16_BYTE( "ch-m-201.u40", 0x000001, 0x80000, CRC(520571cb) SHA1(5c006f10d6192939003f8197e8bb64908a826fc1) ) ROM_LOAD16_BYTE( "ch-m-301.u45", 0x100000, 0x80000, CRC(daba09c3) SHA1(e5d2f92b63288c36faa367a3306d1999264843e8) ) ROM_LOAD16_BYTE( "ch-a-401.u29", 0x100001, 0x80000, CRC(5ee5d39f) SHA1(f6881aa5c755831d885f7adf35a5a094f7302205) ) ROM_REGION( 0x40000, "oki", 0 ) /* Samples */ ROM_LOAD( "ch-m-701.u10", 0x00000, 0x40000, CRC(e01be644) SHA1(b68682786d5b40cb5672cfd7f717adcfb8fac7d3) ) ROM_END /*************************************************************************** Mahjong Magic Lamp (BMC, 2000) PCB Layout ---------- BMC-A70809 |--------------------------------------| |-| 6116 555 | | 0.1UF JA-A-602 JA-A-502 | | 68000 | | TD62003 42MHz | | PAL | | PAL 51C4160 | | SYA70521 | |-| U3567 | | | | HM86171 VDB40817 | |-| VOL DSW4 | | 7805 6295 DSW3 | | AMP JA-A-301 JA-A-401 DSW2 | |-| JA-A-901 JA-A-201 JA-A-101 DSW1 | |--------------------------------------| Notes: 68000 - clock 10.5000MHz [42/4] M6295 - clock 1.0500MHz [42/40]. Pin 7 HIGH U3567 - = YM2413, clock 3.5000MHz [42/12] HM86171 - HMC RAMDAC. Clock input 10.5000MHz [42/4] VDB/SYA - custom QFP100 GFX chips badged with BMC logo 51C4160 - SOJ40 video RAM, possibly 4M DRAM (256k x 16-bit) 555 - 555 Timer DSW1-4 - 8-position DIP switches AMP - NEC uPC1241H VSync - 58.9342Hz HSync - 15.4408kHz ***************************************************************************/ ROM_START( mjmaglmp ) ROM_REGION( 0x40000, "maincpu", 0 ) /* 68000 Code */ ROM_LOAD16_BYTE( "ja-a-602.u10", 0x000000, 0x20000, CRC(b69e235c) SHA1(04e5d0d667de29680e4a35d0d98b587447e54ce3) ) ROM_LOAD16_BYTE( "ja-a-502.u11", 0x000001, 0x20000, CRC(bb609da3) SHA1(ffadc20912e0a9ebe0d1a1f7f94dfaccb48be5c1) ) ROM_REGION( 0x200000, "gfx1", 0 ) ROM_LOAD16_BYTE( "ja-a-101.u41", 0x000000, 0x80000, CRC(7878b9a1) SHA1(7efacb063b47e518c4d3856e90d7532f478e54dd) ) ROM_LOAD16_BYTE( "ja-a-201.u42", 0x000001, 0x80000, CRC(b74f3b2b) SHA1(09724909a14aebc135029d97fafcd215a84f05e3) ) ROM_LOAD16_BYTE( "ja-a-301.u43", 0x100000, 0x80000, CRC(2bbaf65e) SHA1(d792054671671a2e479b89ad29bc7b3f935804f9) ) ROM_LOAD16_BYTE( "ja-a-401.u44", 0x100001, 0x80000, CRC(9292acb1) SHA1(01ce7997305dd5fdc5dc2b801046303a4d8a89c0) ) ROM_REGION( 0x40000, "oki", 0 ) /* Samples */ ROM_LOAD( "ja-a-901.u6", 0x00000, 0x40000, CRC(25f36d00) SHA1(c182348340ca67ad69d1a67c58b47d6371a725c9) ) ROM_END GAME( 1999, bmcpokr, 0, bmcpokr, bmcpokr, bmcpokr_state, empty_init, ROT0, "BMC", "Dongfang Shenlong", MACHINE_SUPPORTS_SAVE ) GAME( 2000, mjmaglmp, 0, mjmaglmp, mjmaglmp, bmcpokr_state, empty_init, ROT0, "BMC", "Mahjong Magic Lamp (v. JAA02)", MACHINE_SUPPORTS_SAVE ) 9 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129
// license:BSD-3-Clause
// copyright-holders:Miodrag Milanovic,Luca Bruno
/***************************************************************************

    luaengine.c

    Controls execution of the core MAME system.

***************************************************************************/

#include <limits>
#include "lua.hpp"
#include "luabridge/Source/LuaBridge/LuaBridge.h"
#include <signal.h>
#include "emu.h"
#include "emuopts.h"
#include "osdepend.h"
#include "drivenum.h"
#include "ui/ui.h"
#include "mongoose/mongoose.h"

//**************************************************************************
//  LUA ENGINE
//**************************************************************************

#if !defined(LUA_PROMPT)
#define LUA_PROMPT      "> "
#define LUA_PROMPT2     ">> "
#endif

#if !defined(LUA_MAXINPUT)
#define LUA_MAXINPUT        512
#endif

#define lua_readline(b,p) \
	(fputs(p, stdout), fflush(stdout),  /* show prompt */ \
	fgets(b, LUA_MAXINPUT, stdin) != NULL)  /* get line */

static lua_State *globalL = NULL;

#define luai_writestring(s,l)   fwrite((s), sizeof(char), (l), stdout)
#define luai_writeline()    (luai_writestring("\n", 1), fflush(stdout))

const char *const lua_engine::tname_ioport = "lua.ioport";
lua_engine* lua_engine::luaThis = NULL;

extern "C" {
	int luaopen_lsqlite3(lua_State *L);
}

static void lstop(lua_State *L, lua_Debug *ar)
{
	(void)ar;  /* unused arg. */
	lua_sethook(L, NULL, 0, 0);
	luaL_error(L, "interrupted!");
}


static void laction(int i)
{
	signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
                              terminate process (default action) */
	lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}

int lua_engine::report(int status) {
	if (status != LUA_OK && !lua_isnil(m_lua_state, -1))
	{
		const char *msg = lua_tostring(m_lua_state, -1);
		if (msg == NULL) msg = "(error object is not a string)";
		lua_writestringerror("%s\n", msg);
		lua_pop(m_lua_state, 1);
		/* force a complete garbage collection in case of errors */
		lua_gc(m_lua_state, LUA_GCCOLLECT, 0);
	}
	return status;
}


static int traceback (lua_State *L)
{
	const char *msg = lua_tostring(L, 1);
	if (msg)
	luaL_traceback(L, L, msg, 1);
	else if (!lua_isnoneornil(L, 1))
	{  /* is there an error object? */
	if (!luaL_callmeta(L, 1, "__tostring"))  /* try its 'tostring' metamethod */
		lua_pushliteral(L, "(no error message)");
	}
	return 1;
}


int lua_engine::docall(int narg, int nres)
{
	int status;
	int base = lua_gettop(m_lua_state) - narg;  /* function index */
	lua_pushcfunction(m_lua_state, traceback);  /* push traceback function */
	lua_insert(m_lua_state, base);  /* put it under chunk and args */
	globalL = m_lua_state;  /* to be available to 'laction' */
	signal(SIGINT, laction);
	status = lua_pcall(m_lua_state, narg, nres, base);
	signal(SIGINT, SIG_DFL);
	lua_remove(m_lua_state, base);  /* remove traceback function */
	return status;
}

/* mark in error messages for incomplete statements */
#define EOFMARK     "<eof>"
#define marklen     (sizeof(EOFMARK)/sizeof(char) - 1)

int lua_engine::incomplete(int status)
{
	if (status == LUA_ERRSYNTAX)
	{
		size_t lmsg;
		const char *msg = lua_tolstring(m_lua_state, -1, &lmsg);
		if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0)
		{
			lua_pop(m_lua_state, 1);
			return 1;
		}
	}
	return 0;  /* else... */
}

lua_engine::hook::hook()
{
	L = NULL;
	cb = -1;
}

#if defined(SDLMAME_SOLARIS) || defined(__ANDROID__)
#undef _L
#endif

void lua_engine::hook::set(lua_State *_L, int idx)
{
	if (L)
		luaL_unref(L, LUA_REGISTRYINDEX, cb);

	if (lua_isnil(_L, idx)) {
		L = NULL;
		cb = -1;

	} else {
		L = _L;
		lua_pushvalue(_L, idx);
		cb = luaL_ref(_L, LUA_REGISTRYINDEX);
	}
}

lua_State *lua_engine::hook::precall()
{
	lua_State *T = lua_newthread(L);
	lua_rawgeti(T, LUA_REGISTRYINDEX, cb);
	return T;
}

void lua_engine::hook::call(lua_engine *engine, lua_State *T, int nparam)
{
	engine->resume(T, nparam, L);
}

void lua_engine::resume(lua_State *L, int nparam, lua_State *root)
{
	int s = lua_resume(L, NULL, nparam);
	switch(s) {
	case LUA_OK:
		if(!root) {
			std::map<lua_State *, std::pair<lua_State *, int> >::iterator i = thread_registry.find(L);
			if(i != thread_registry.end()) {
				luaL_unref(i->second.first, LUA_REGISTRYINDEX, i->second.second);
				thread_registry.erase(i);
			}
		} else
			lua_pop(root, 1);
		break;

	case LUA_YIELD:
		if(root) {
			int id = luaL_ref(root, LUA_REGISTRYINDEX);
			thread_registry[L] = std::pair<lua_State *, int>(root, id);
		}
		break;

	default:
		osd_printf_error("[LUA ERROR] %s\n", lua_tostring(L, -1));
		lua_pop(L, 1);
		break;
	}
}

void lua_engine::resume(void *_L, INT32 param)
{
	resume(static_cast<lua_State *>(_L));
}

int lua_engine::l_ioport_write(lua_State *L)
{
	ioport_field *field = static_cast<ioport_field *>(getparam(L, 1, tname_ioport));
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "value expected");
	field->set_value(lua_tointeger(L, 2));
	return 0;
}

//-------------------------------------------------
//  emu_app_name - return application name
//-------------------------------------------------

int lua_engine::l_emu_app_name(lua_State *L)
{
	lua_pushstring(L, emulator_info::get_appname_lower());
	return 1;
}

//-------------------------------------------------
//  emu_app_version - return application version
//-------------------------------------------------

int lua_engine::l_emu_app_version(lua_State *L)
{
	lua_pushstring(L, bare_build_version);
	return 1;
}


//-------------------------------------------------
//  emu_gamename - returns game full name
//-------------------------------------------------

int lua_engine::l_emu_gamename(lua_State *L)
{
	lua_pushstring(L, luaThis->machine().system().description);
	return 1;
}

//-------------------------------------------------
//  emu_romname - returns rom base name
//-------------------------------------------------

int lua_engine::l_emu_romname(lua_State *L)
{
	lua_pushstring(L, luaThis->machine().basename());
	return 1;
}

//-------------------------------------------------
//  emu_pause/emu_unpause - pause/unpause game
//-------------------------------------------------

int lua_engine::l_emu_pause(lua_State *L)
{
	luaThis->machine().pause();
	return 0;
}

int lua_engine::l_emu_unpause(lua_State *L)
{
	luaThis->machine().resume();
	return 0;
}

//-------------------------------------------------
//  emu_keypost - post keys to natural keyboard
//-------------------------------------------------

int lua_engine::l_emu_keypost(lua_State *L)
{
	const char *keys = luaL_checkstring(L,1);
	luaThis->machine().ioport().natkeyboard().post_utf8(keys);
	return 1;
}

int lua_engine::l_emu_time(lua_State *L)
{
	lua_pushnumber(L, luaThis->machine().time().as_double());
	return 1;
}

void lua_engine::emu_after_done(void *_h, INT32 param)
{
	hook *h = static_cast<hook *>(_h);
	h->call(this, h->precall(), 0);
	delete h;
}

int lua_engine::emu_after(lua_State *L)
{
	luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
	struct hook *h = new hook;
	h->set(L, 2);
	machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::emu_after_done), this), 0, h);
	return 0;
}

int lua_engine::l_emu_after(lua_State *L)
{
	return luaThis->emu_after(L);
}

int lua_engine::emu_wait(lua_State *L)
{
	luaL_argcheck(L, lua_isnumber(L, 1), 1, "waiting duration expected");
	machine().scheduler().timer_set(attotime::from_double(lua_tonumber(L, 1)), timer_expired_delegate(FUNC(lua_engine::resume), this), 0, L);
	return lua_yieldk(L, 0, 0, 0);
}

int lua_engine::l_emu_wait(lua_State *L)
{
	return luaThis->emu_wait(L);
}

void lua_engine::output_notifier(const char *outname, INT32 value)
{
	if (hook_output_cb.active()) {
		lua_State *L = hook_output_cb.precall();
		lua_pushstring(L, outname);
		lua_pushnumber(L, value);
		hook_output_cb.call(this, L, 2);
	}
}

void lua_engine::s_output_notifier(const char *outname, INT32 value, void *param)
{
	static_cast<lua_engine *>(param)->output_notifier(outname, value);
}

void lua_engine::emu_hook_output(lua_State *L)
{
	luaL_argcheck(L, lua_isfunction(L, 1), 1, "callback function expected");
	hook_output_cb.set(L, 1);

	if (!output_notifier_set) {
		output_set_notifier(NULL, s_output_notifier, this);
		output_notifier_set = true;
	}
}

int lua_engine::l_emu_hook_output(lua_State *L)
{
	luaThis->emu_hook_output(L);
	return 0;
}

int lua_engine::l_emu_set_hook(lua_State *L)
{
	luaThis->emu_set_hook(L);
	return 0;
}

void lua_engine::emu_set_hook(lua_State *L)
{
	luaL_argcheck(L, lua_isfunction(L, 1) || lua_isnil(L, 1), 1, "callback function expected");
	luaL_argcheck(L, lua_isstring(L, 2), 2, "message (string) expected");
	const char *hookname = luaL_checkstring(L,2);

	if (strcmp(hookname, "output") == 0) {
		hook_output_cb.set(L, 1);
		if (!output_notifier_set) {
			output_set_notifier(NULL, s_output_notifier, this);
			output_notifier_set = true;
		}
	} else if (strcmp(hookname, "frame") == 0) {
		hook_frame_cb.set(L, 1);
	} else {
		lua_writestringerror("%s", "Unknown hook name, aborting.\n");
	}
}

//-------------------------------------------------
//  machine_get_screens - return table of available screens userdata
//  -> manager:machine().screens[":screen"]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_machine_get_screens(const running_machine *r)
{
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef screens_table = luabridge::LuaRef::newTable(L);

	for (device_t *dev = r->first_screen(); dev != NULL; dev = dev->next()) {
		screen_device *sc = dynamic_cast<screen_device *>(dev);
		if (sc && sc->configured() && sc->started() && sc->type()) {
			screens_table[sc->tag()] = sc;
		}
	}

	return screens_table;
}

//-------------------------------------------------
//  machine_get_devices - return table of available devices userdata
//  -> manager:machine().devices[":maincpu"]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_machine_get_devices(const running_machine *r)
{
	running_machine *m = const_cast<running_machine *>(r);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef devs_table = luabridge::LuaRef::newTable(L);

	device_t *root = &(m->root_device());
	devs_table = devtree_dfs(root, devs_table);

	return devs_table;
}

// private helper for get_devices - DFS visit all devices in a running machine
luabridge::LuaRef lua_engine::devtree_dfs(device_t *root, luabridge::LuaRef devs_table)
{
	if (root) {
		for (device_t *dev = root->first_subdevice(); dev != NULL; dev = dev->next()) {
			if (dev && dev->configured() && dev->started()) {
				devs_table[dev->tag()] = dev;
				devtree_dfs(dev, devs_table);
			}
		}
	}
	return devs_table;
}

//-------------------------------------------------
//  device_get_memspaces - return table of available address spaces userdata
//  -> manager:machine().devices[":maincpu"].spaces["program"]
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_dev_get_memspaces(const device_t *d)
{
	device_t *dev = const_cast<device_t *>(d);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef sp_table = luabridge::LuaRef::newTable(L);

	for (address_spacenum sp = AS_0; sp < ADDRESS_SPACES; sp++) {
		if (dev->memory().has_space(sp)) {
			sp_table[dev->memory().space(sp).name()] = &(dev->memory().space(sp));
		}
	}

	return sp_table;
}

//-------------------------------------------------
//  device_get_state - return table of available state userdata
//  -> manager:machine().devices[":maincpu"].state
//-------------------------------------------------

luabridge::LuaRef lua_engine::l_dev_get_states(const device_t *d)
{
	device_t *dev = const_cast<device_t *>(d);
	lua_State *L = luaThis->m_lua_state;
	luabridge::LuaRef st_table = luabridge::LuaRef::newTable(L);
	for (const device_state_entry *s = dev->state().state_first(); s != NULL; s = s->next()) {
		// XXX: refrain from exporting non-visible entries?
		if (s) {
			st_table[s->symbol()] = const_cast<device_state_entry *>(s);
		}
	}

	return st_table;
}

//-------------------------------------------------
//  state_get_value - return value of a device state entry
//  -> manager:machine().devices[":maincpu"].state["PC"].value
//-------------------------------------------------

UINT64 lua_engine::l_state_get_value(const device_state_entry *d)
{
	device_state_interface *state = d->parent_state();
	if(state) {
		luaThis->machine().save().dispatch_presave();
		return state->state_int(d->index());
	} else {
		return 0;
	}
}

//-------------------------------------------------
//  state_set_value - set value of a device state entry
//  -> manager:machine().devices[":maincpu"].state["D0"].value = 0x0c00
//-------------------------------------------------

void lua_engine::l_state_set_value(device_state_entry *d, UINT64 val)
{
	device_state_interface *state = d->parent_state();
	if(state) {
		state->set_state_int(d->index(), val);
		luaThis->machine().save().dispatch_presave();
	}
}

//-------------------------------------------------
//  mem_read - templated memory readers for <sign>,<size>
//  -> manager:machine().devices[":maincpu"].spaces["program"]:read_i8(0xC000)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_addr_space::l_mem_read(lua_State *L)
{
	address_space &sp = luabridge::Stack<address_space &>::get(L, 1);
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected");
	offs_t address = lua_tounsigned(L, 2);
	T mem_content = 0;
	switch(sizeof(mem_content) * 8) {
		case 8:
			mem_content = sp.read_byte(address);
			break;
		case 16:
			if ((address & 1) == 0) {
				mem_content = sp.read_word(address);
			} else {
				mem_content = sp.read_word_unaligned(address);
			}
			break;
		case 32:
			if ((address & 3) == 0) {
				mem_content = sp.read_dword(address);
			} else {
				mem_content = sp.read_dword_unaligned(address);
			}
			break;
		case 64:
			if ((address & 7) == 0) {
				mem_content = sp.read_qword(address);
			} else {
				mem_content = sp.read_qword_unaligned(address);
			}
			break;
		default:
			break;
	}

	if (std::numeric_limits<T>::is_signed) {
		lua_pushinteger(L, mem_content);
	} else {
		lua_pushunsigned(L, mem_content);
	}

	return 1;

}

//-------------------------------------------------
//  mem_write - templated memory writer for <sign>,<size>
//  -> manager:machine().devices[":maincpu"].spaces["program"]:write_u16(0xC000, 0xF00D)
//-------------------------------------------------

template <typename T>
int lua_engine::lua_addr_space::l_mem_write(lua_State *L)
{
	address_space &sp = luabridge::Stack<address_space &>::get(L, 1);
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "address (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 3), 3, "value (integer) expected");
	offs_t address = lua_tounsigned(L, 2);
	T val = lua_tounsigned(L, 3);

	switch(sizeof(val) * 8) {
		case 8:
			sp.write_byte(address, val);
			break;
		case 16:
			if ((address & 1) == 0) {
				sp.write_word(address, val);
			} else {
				sp.read_word_unaligned(address, val);
			}
			break;
		case 32:
			if ((address & 3) == 0) {
				sp.write_dword(address, val);
			} else {
				sp.write_dword_unaligned(address, val);
			}
			break;
		case 64:
			if ((address & 7) == 0) {
				sp.write_qword(address, val);
			} else {
				sp.write_qword_unaligned(address, val);
			}
			break;
		default:
			break;
	}

	return 0;
}

//-------------------------------------------------
//  screen_height - return screen visible height
//  -> manager:machine().screens[":screen"]:height()
//-------------------------------------------------

int lua_engine::lua_screen::l_height(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc) {
		return 0;
	}

	lua_pushunsigned(L, sc->visible_area().height());
	return 1;
}

//-------------------------------------------------
//  screen_width - return screen visible width
//  -> manager:machine().screens[":screen"]:width()
//-------------------------------------------------

int lua_engine::lua_screen::l_width(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc) {
		return 0;
	}

	lua_pushunsigned(L, sc->visible_area().width());
	return 1;
}

//-------------------------------------------------
//  draw_box - draw a box on a screen container
//  -> manager:machine().screens[":screen"]:draw_box(x1, y1, x2, y2, bgcolor, linecolor)
//-------------------------------------------------

int lua_engine::lua_screen::l_draw_box(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc) {
		return 0;
	}

	// ensure that we got 6 numerical parameters
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 6), 6, "background color (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 7), 7, "outline color (integer) expected");

	// retrieve all parameters
	float x1, y1, x2, y2;
	x1 = MIN(lua_tounsigned(L, 2) / static_cast<float>(sc->visible_area().width()) , 1.0f);
	y1 = MIN(lua_tounsigned(L, 3) / static_cast<float>(sc->visible_area().height()), 1.0f);
	x2 = MIN(lua_tounsigned(L, 4) / static_cast<float>(sc->visible_area().width()) , 1.0f);
	y2 = MIN(lua_tounsigned(L, 5) / static_cast<float>(sc->visible_area().height()), 1.0f);
	UINT32 bgcolor = lua_tounsigned(L, 6);
	UINT32 fgcolor = lua_tounsigned(L, 7);

	// draw the box
	render_container &rc = sc->container();
	ui_manager &ui = sc->machine().ui();
	ui.draw_outlined_box(&rc, x1, y1, x2, y2, fgcolor, bgcolor);

	return 0;
}

//-------------------------------------------------
//  draw_line - draw a line on a screen container
//  -> manager:machine().screens[":screen"]:draw_line(x1, y1, x2, y2, linecolor)
//-------------------------------------------------

int lua_engine::lua_screen::l_draw_line(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc) {
		return 0;
	}

	// ensure that we got 5 numerical parameters
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "x1 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 3), 3, "y1 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 4), 4, "x2 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 5), 5, "y2 (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 6), 6, "color (integer) expected");

	// retrieve all parameters
	float x1, y1, x2, y2;
	x1 = MIN(lua_tounsigned(L, 2) / static_cast<float>(sc->visible_area().width()) , 1.0f);
	y1 = MIN(lua_tounsigned(L, 3) / static_cast<float>(sc->visible_area().height()), 1.0f);
	x2 = MIN(lua_tounsigned(L, 4) / static_cast<float>(sc->visible_area().width()) , 1.0f);
	y2 = MIN(lua_tounsigned(L, 5) / static_cast<float>(sc->visible_area().height()), 1.0f);
	UINT32 color = lua_tounsigned(L, 6);

	// draw the line
	sc->container().add_line(x1, y1, x2, y2, UI_LINE_WIDTH, rgb_t(color), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
	return 0;
}

//-------------------------------------------------
//  draw_text - draw text on a screen container
//  -> manager:machine().screens[":screen"]:draw_text(x, y, message)
//-------------------------------------------------

int lua_engine::lua_screen::l_draw_text(lua_State *L)
{
	screen_device *sc = luabridge::Stack<screen_device *>::get(L, 1);
	if(!sc) {
		return 0;
	}

	// ensure that we got proper parameters
	luaL_argcheck(L, lua_isnumber(L, 2), 2, "x (integer) expected");
	luaL_argcheck(L, lua_isnumber(L, 3), 3, "y (integer) expected");
	luaL_argcheck(L, lua_isstring(L, 4), 4, "message (string) expected");

	// retrieve all parameters
	float x = MIN(lua_tounsigned(L, 2) / static_cast<float>(sc->visible_area().width()) , 1.0f);
	float y = MIN(lua_tounsigned(L, 3) / static_cast<float>(sc->visible_area().height()), 1.0f);
	const char *msg = luaL_checkstring(L,4);
	// TODO: add optional parameters (colors, etc.)

	// draw the text
	render_container &rc = sc->container();
	ui_manager &ui = sc->machine().ui();
	ui.draw_text_full(&rc, msg, x, y , (1.0f - x),
						JUSTIFY_LEFT, WRAP_WORD, DRAW_NORMAL, UI_TEXT_COLOR,
						UI_TEXT_BG_COLOR, NULL, NULL);

	return 0;
}

void *lua_engine::checkparam(lua_State *L, int idx, const char *tname)
{
	const char *name;

	if(!lua_getmetatable(L, idx))
	return 0;

	lua_rawget(L, LUA_REGISTRYINDEX);
	name = lua_tostring(L, -1);
	if(!name || strcmp(name, tname)) {
	lua_pop(L, 1);
	return 0;
	}
	lua_pop(L, 1);

	return *static_cast<void **>(lua_touserdata(L, idx));
}

void *lua_engine::getparam(lua_State *L, int idx, const char *tname)
{
	void *p = checkparam(L, idx, tname);
	char msg[256];
	sprintf(msg, "%s expected", tname);
	luaL_argcheck(L, p, idx, msg);
	return p;
}

void lua_engine::push(lua_State *L, void *p, const char *tname)
{
	void **pp = static_cast<void **>(lua_newuserdata(L, sizeof(void *)));
	*pp = p;
	luaL_getmetatable(L, tname);
	lua_setmetatable(L, -2);
}

int lua_engine::l_emu_exit(lua_State *L)
{
	luaThis->machine().schedule_exit();
	return 1;
}

int lua_engine::l_emu_start(lua_State *L)
{
	const char *system_name = luaL_checkstring(L,1);

	int index = driver_list::find(system_name);
	if (index != -1) {
		machine_manager::instance()->schedule_new_driver(driver_list::driver(index));
		luaThis->machine().schedule_hard_reset();
	}
	return 1;
}

int lua_engine::luaopen_ioport(lua_State *L)
{
	static const struct luaL_Reg ioport_funcs [] = {
		{ "write",       l_ioport_write },
		{ NULL, NULL }  /* sentinel */
	};

	luaL_newmetatable(L, tname_ioport);
	lua_pushvalue(L, -1);
	lua_pushstring(L, tname_ioport);
	lua_rawset(L, LUA_REGISTRYINDEX);
	lua_pushstring(L, "__index");
	lua_pushvalue(L, -2);
	lua_settable(L, -3);
	luaL_setfuncs(L, ioport_funcs, 0);
	return 1;
}

struct msg {
	astring text;
	int ready;
	astring response;
	int status;
	int done;
} msg;

osd_lock *lock;

void lua_engine::serve_lua()
{
	osd_sleep(osd_ticks_per_second() / 1000 * 50);
	printf("%s v%s - %s\n%s\n%s\n\n", emulator_info::get_applongname(),build_version,emulator_info::get_fulllongname(),emulator_info::get_copyright_info(),LUA_COPYRIGHT);
	fflush(stdout);
	char buff[LUA_MAXINPUT];
	astring oldbuff;

	const char *b = LUA_PROMPT;

	do {
		// Wait for input
		fputs(b, stdout); fflush(stdout);  /* show prompt */
		fgets(buff, LUA_MAXINPUT, stdin);

		// Create message
		osd_lock_acquire(lock);
		if (msg.ready == 0) {
			msg.text = oldbuff;
			if (oldbuff.len()!=0) msg.text.cat("\n");
			msg.text.cat(buff);
			msg.ready = 1;
			msg.done = 0;
		}
		osd_lock_release(lock);

		// Wait for response
		int done = 0;
		do {
			osd_sleep(osd_ticks_per_second() / 1000);
			osd_lock_acquire(lock);
			done = msg.done;
			osd_lock_release(lock);
		} while (done==0);

		// Do action on client side
		osd_lock_acquire(lock);
		if (msg.status == -1){
			b = LUA_PROMPT2;
			oldbuff = msg.response;
		}
		else {
			b = LUA_PROMPT;
			oldbuff = "";
		}
		msg.done = 0;
		osd_lock_release(lock);

	} while (1);
}

static void *serve_lua(void *param)
{
	lua_engine *engine = (lua_engine *)param;
	engine->serve_lua();
	return NULL;
}


//-------------------------------------------------
//  lua_engine - constructor
//-------------------------------------------------

lua_engine::lua_engine()
{
	m_machine = NULL;
	luaThis = this;
	m_lua_state = luaL_newstate();  /* create state */
	output_notifier_set = false;

	luaL_checkversion(m_lua_state);
	lua_gc(m_lua_state, LUA_GCSTOP, 0);  /* stop collector during initialization */
	luaL_openlibs(m_lua_state);  /* open libraries */

	luaopen_lsqlite3(m_lua_state);

	luaopen_ioport(m_lua_state);

	lua_gc(m_lua_state, LUA_GCRESTART, 0);
	msg.ready = 0;
	msg.status = 0;
	msg.done = 0;
	lock = osd_lock_alloc();
}

//-------------------------------------------------
//  ~lua_engine - destructor
//-------------------------------------------------

lua_engine::~lua_engine()
{
	close();
}


void lua_engine::update_machine()
{
	lua_newtable(m_lua_state);
	if (m_machine!=NULL)
	{
		// Create the ioport array
		ioport_port *port = machine().ioport().first_port();
		while(port) {
			ioport_field *field = port->first_field();
			while(field) {
				if(field->name()) {
					push(m_lua_state, field, tname_ioport);
					lua_setfield(m_lua_state, -2, field->name());
				}
				field = field->next();
			}
			port = port->next();
		}
	}
	lua_setglobal(m_lua_state, "ioport");
}

//-------------------------------------------------
//  initialize - initialize lua hookup to emu engine
//-------------------------------------------------

void lua_engine::initialize()
{
	luabridge::getGlobalNamespace (m_lua_state)
		.beginNamespace ("emu")
			.addCFunction ("app_name",    l_emu_app_name )
			.addCFunction ("app_version", l_emu_app_version )
			.addCFunction ("gamename",    l_emu_gamename )
			.addCFunction ("romname",     l_emu_romname )
			.addCFunction ("keypost",     l_emu_keypost )
			.addCFunction ("hook_output", l_emu_hook_output )
			.addCFunction ("sethook",     l_emu_set_hook )
			.addCFunction ("time",        l_emu_time )
			.addCFunction ("wait",        l_emu_wait )
			.addCFunction ("after",       l_emu_after )
			.addCFunction ("exit",        l_emu_exit )
			.addCFunction ("start",       l_emu_start )
			.addCFunction ("pause",       l_emu_pause )
			.addCFunction ("unpause",     l_emu_unpause )
			.beginClass <machine_manager> ("manager")
				.addFunction ("machine", &machine_manager::machine)
				.addFunction ("options", &machine_manager::options)
			.endClass ()
			.beginClass <running_machine> ("machine")
				.addFunction ("exit", &running_machine::schedule_exit)
				.addFunction ("hard_reset", &running_machine::schedule_hard_reset)
				.addFunction ("soft_reset", &running_machine::schedule_soft_reset)
				.addFunction ("system", &running_machine::system)
				.addProperty <luabridge::LuaRef, void> ("devices", &lua_engine::l_machine_get_devices)
				.addProperty <luabridge::LuaRef, void> ("screens", &lua_engine::l_machine_get_screens)
			.endClass ()
			.beginClass <game_driver> ("game_driver")
				.addData ("name", &game_driver::name)
				.addData ("description", &game_driver::description)
				.addData ("year", &game_driver::year)
				.addData ("manufacturer", &game_driver::manufacturer)
			.endClass ()
			.beginClass <device_t> ("device")
				.addFunction ("name", &device_t::name)
				.addFunction ("shortname", &device_t::shortname)
				.addFunction ("tag", &device_t::tag)
				.addProperty <luabridge::LuaRef, void> ("spaces", &lua_engine::l_dev_get_memspaces)
				.addProperty <luabridge::LuaRef, void> ("state", &lua_engine::l_dev_get_states)
			.endClass()
			.beginClass <lua_addr_space> ("lua_addr_space")
				.addCFunction ("read_i8", &lua_addr_space::l_mem_read<INT8>)
				.addCFunction ("read_u8", &lua_addr_space::l_mem_read<UINT8>)
				.addCFunction ("read_i16", &lua_addr_space::l_mem_read<INT16>)
				.addCFunction ("read_u16", &lua_addr_space::l_mem_read<UINT16>)
				.addCFunction ("read_i32", &lua_addr_space::l_mem_read<INT32>)
				.addCFunction ("read_u32", &lua_addr_space::l_mem_read<UINT32>)
				.addCFunction ("read_i64", &lua_addr_space::l_mem_read<INT64>)
				.addCFunction ("read_u64", &lua_addr_space::l_mem_read<UINT64>)
				.addCFunction ("write_i8", &lua_addr_space::l_mem_write<INT8>)
				.addCFunction ("write_u8", &lua_addr_space::l_mem_write<UINT8>)
				.addCFunction ("write_i16", &lua_addr_space::l_mem_write<INT16>)
				.addCFunction ("write_u16", &lua_addr_space::l_mem_write<UINT16>)
				.addCFunction ("write_i32", &lua_addr_space::l_mem_write<INT32>)
				.addCFunction ("write_u32", &lua_addr_space::l_mem_write<UINT32>)
				.addCFunction ("write_i64", &lua_addr_space::l_mem_write<INT64>)
				.addCFunction ("write_u64", &lua_addr_space::l_mem_write<UINT64>)
			.endClass()
			.deriveClass <address_space, lua_addr_space> ("addr_space")
				.addFunction("name", &address_space::name)
			.endClass()
			.beginClass <lua_screen> ("lua_screen_dev")
				.addCFunction ("draw_box",  &lua_screen::l_draw_box)
				.addCFunction ("draw_line", &lua_screen::l_draw_line)
				.addCFunction ("draw_text", &lua_screen::l_draw_text)
				.addCFunction ("height", &lua_screen::l_height)
				.addCFunction ("width", &lua_screen::l_width)
			.endClass()
			.deriveClass <screen_device, lua_screen> ("screen_dev")
				.addFunction ("frame_number", &screen_device::frame_number)
				.addFunction ("name", &screen_device::name)
				.addFunction ("shortname", &screen_device::shortname)
				.addFunction ("tag", &screen_device::tag)
			.endClass()
			.beginClass <device_state_entry> ("dev_space")
				.addFunction ("name", &device_state_entry::symbol)
				.addProperty <UINT64, UINT64> ("value", &lua_engine::l_state_get_value, &lua_engine::l_state_set_value)
				.addFunction ("is_visible", &device_state_entry::visible)
				.addFunction ("is_divider", &device_state_entry::divider)
			.endClass()
		.endNamespace();

	luabridge::push (m_lua_state, machine_manager::instance());
	lua_setglobal(m_lua_state, "manager");
}

void lua_engine::start_console()
{
	mg_start_thread(::serve_lua, this);
}

//-------------------------------------------------
//  frame_hook - called at each frame refresh, used to draw a HUD
//-------------------------------------------------
bool lua_engine::frame_hook()
{
	bool is_cb_hooked = false;
	if (m_machine != NULL) {
		// invoke registered callback (if any)
		is_cb_hooked = hook_frame_cb.active();
		if (is_cb_hooked) {
			lua_State *L = hook_frame_cb.precall();
			hook_frame_cb.call(this, L, 0);
		}
	}
	return is_cb_hooked;
}

void lua_engine::periodic_check()
{
	osd_lock_acquire(lock);
	if (msg.ready == 1) {
	lua_settop(m_lua_state, 0);
	int status = luaL_loadbuffer(m_lua_state, msg.text.cstr(), strlen(msg.text.cstr()), "=stdin");
	if (incomplete(status)==0)  /* cannot try to add lines? */
	{
		if (status == LUA_OK) status = docall(0, LUA_MULTRET);
		report(status);
		if (status == LUA_OK && lua_gettop(m_lua_state) > 0)   /* any result to print? */
		{
			luaL_checkstack(m_lua_state, LUA_MINSTACK, "too many results to print");
			lua_getglobal(m_lua_state, "print");
			lua_insert(m_lua_state, 1);
			if (lua_pcall(m_lua_state, lua_gettop(m_lua_state) - 1, 0, 0) != LUA_OK)
				lua_writestringerror("%s\n", lua_pushfstring(m_lua_state,
				"error calling " LUA_QL("print") " (%s)",
				lua_tostring(m_lua_state, -1)));
		}
	}
	else
	{
		status = -1;
	}
	msg.status = status;
	msg.response = msg.text;
	msg.text = "";
	msg.ready = 0;
	msg.done = 1;
	}
	osd_lock_release(lock);
}

//-------------------------------------------------
//  close - close and cleanup of lua engine
//-------------------------------------------------

void lua_engine::close()
{
	lua_settop(m_lua_state, 0);  /* clear stack */
	lua_close(m_lua_state);
}

//-------------------------------------------------
//  execute - load and execute script
//-------------------------------------------------

void lua_engine::load_script(const char *filename)
{
	int s = luaL_loadfile(m_lua_state, filename);
	report(s);
	update_machine();
	start();
}

//-------------------------------------------------
//  execute_string - execute script from string
//-------------------------------------------------

void lua_engine::load_string(const char *value)
{
	int s = luaL_loadstring(m_lua_state, value);
	report(s);
	update_machine();
	start();
}

//-------------------------------------------------
//  start - execute the loaded script
//-------------------------------------------------

void lua_engine::start()
{
	resume(m_lua_state);
}


//**************************************************************************
//  LuaBridge Stack specializations
//**************************************************************************

namespace luabridge {
	template <>
	struct Stack <UINT64> {
		static inline void push (lua_State* L, UINT64 value) {
			lua_pushunsigned(L, static_cast <lua_Unsigned> (value));
		}

		static inline UINT64 get (lua_State* L, int index) {
			return static_cast <UINT64> (luaL_checkunsigned (L, index));
		}
	};
}