From 28a24a78b14ac008da0fe113ff4dd9ea83c15316 Mon Sep 17 00:00:00 2001 From: AJR Date: Sat, 2 Jul 2022 10:23:43 -0400 Subject: [PATCH] thedealr: Emulate 8742 I/O MCU --- src/mame/seta/thedealr.cpp | 252 +++++++++++++------------------------ 1 file changed, 86 insertions(+), 166 deletions(-) diff --git a/src/mame/seta/thedealr.cpp b/src/mame/seta/thedealr.cpp index 03dc7b6dcc1..f1f2a1971b5 100644 --- a/src/mame/seta/thedealr.cpp +++ b/src/mame/seta/thedealr.cpp @@ -30,8 +30,8 @@ #include "emu.h" #include "cpu/m6502/r65c02.h" +#include "cpu/mcs48/mcs48.h" #include "machine/nvram.h" -#include "machine/timer.h" #include "machine/watchdog.h" #include "sound/ay8910.h" #include "video/x1_001.h" @@ -47,9 +47,10 @@ public: driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_subcpu(*this, "subcpu"), + m_iocpu(*this, "iocpu"), m_spritegen(*this, "spritegen"), m_palette(*this, "palette"), - m_iox_io(*this, "IOX"), + m_iox_io(*this, "IOX%u", 0U), m_leds(*this, "led%u", 0U) { } @@ -57,19 +58,15 @@ public: private: // IOX - uint8_t iox_r(); - void iox_w(uint8_t data); - uint8_t iox_status_r(); - uint8_t m_iox_cmd, m_iox_ret, m_iox_status, m_iox_leds, m_iox_coins; - void iox_reset(); + uint8_t m_iox_p1, m_iox_p2, m_iox_leds; + void iox_p1_w(uint8_t data); + void iox_p2_w(uint8_t data); + uint8_t iox_p2_r(); // memory map uint8_t irq_ack_r(); void unk_w(uint8_t data); - // machine - TIMER_DEVICE_CALLBACK_MEMBER(thedealr_interrupt); - // video void thedealr_palette(palette_device &palette) const; uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); @@ -79,14 +76,14 @@ private: void thedealr_sub(address_map &map); virtual void machine_start() override; - virtual void machine_reset() override; // devices required_device m_maincpu; required_device m_subcpu; + required_device m_iocpu; required_device m_spritegen; required_device m_palette; - optional_ioport m_iox_io; + required_ioport_array<4> m_iox_io; output_finder<8> m_leds; }; @@ -124,138 +121,52 @@ WRITE_LINE_MEMBER(thedealr_state::screen_vblank) /*************************************************************************** - IOX (i8742 MCU) Simulation + IOX (i8742 MCU) Emulation ***************************************************************************/ -#define IOX_OUT_FULL 0x01 -#define IOX_IN_FULL 0x02 -#define IOX_WAITDATA 0x80 - -void thedealr_state::iox_reset() +void thedealr_state::iox_p1_w(uint8_t data) { - m_iox_status = 0x00; - m_iox_ret = 0x00; - m_iox_cmd = 0xff; - m_iox_leds = 0x00; - m_iox_coins = 0x00; -} + // Periodic interrupt generated by internal timer + if (!BIT(m_iox_p1, 7) && BIT(data, 7)) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); -void thedealr_state::machine_reset() -{ - iox_reset(); -} - -// 3400 -uint8_t thedealr_state::iox_r() -{ - uint8_t ret = m_iox_ret; - m_iox_status &= ~IOX_OUT_FULL; - - logerror("%s: IOX read %02X\n", machine().describe_context(), ret); - return ret; -} -void thedealr_state::iox_w(uint8_t data) -{ - if (m_iox_status & IOX_WAITDATA) + // LEDs set by 0x20 command + if (!BIT(m_iox_p1, 5) && BIT(data, 5)) { - m_iox_status &= ~IOX_WAITDATA; - logerror("%s: IOX data %02X <- %02X\n", machine().describe_context(), m_iox_cmd, data); + m_iox_leds = (m_iox_leds << 1) | BIT(data, 6); - switch (m_iox_cmd) - { - case 0x20: // leds - m_iox_leds = data; - m_leds[0] = BIT(data, 0); // bet - m_leds[1] = BIT(data, 1); // deal - m_leds[2] = BIT(data, 2); - m_leds[3] = BIT(data, 3); - m_leds[4] = BIT(data, 4); // hold 1-5? - m_leds[5] = BIT(data, 5); - m_leds[6] = BIT(data, 6); - m_leds[7] = BIT(data, 7); - break; - - case 0x40: // coin counters - m_iox_coins = data; - machine().bookkeeping().coin_counter_w(0, (~data) & 0x02); // coin1 or service coin - machine().bookkeeping().coin_counter_w(1, (~data) & 0x04); // coupon - machine().bookkeeping().coin_counter_w(2, (~data) & 0x08); // service coin - machine().bookkeeping().coin_counter_w(3, (~data) & 0x10); // coin-out - if ((~data) & 0xe1) - logerror("%s: unknown bits written to command %02X: %02X\n", machine().describe_context(), m_iox_cmd, data); - break; - - default: - logerror("%s: data for unknown command written %02X = %02X\n", machine().describe_context(), m_iox_cmd, data); - break; - } - -// popmessage("LED: %02X COIN: %02X", m_iox_leds, m_iox_coins); + // LED 0 = bet + // LED 1 = deal + // LED 4 = hold 1-5? + for (int i = 0; i < 8; i++) + m_leds[i] = BIT(m_iox_leds, i); } - else - { - m_iox_cmd = data; - logerror("%s: IOX command %02X\n", machine().describe_context(), m_iox_cmd); - switch (m_iox_cmd) - { - case 0x01: // inputs? - { - uint16_t buttons = m_iox_io->read(); - m_iox_ret = 0; - for (int i = 0; i < 16; ++i) - { - if (buttons & (1<read(); + return inputs; } /*************************************************************************** @@ -266,7 +177,8 @@ uint8_t thedealr_state::iox_status_r() uint8_t thedealr_state::irq_ack_r() { - m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); + if (!machine().side_effects_disabled()) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); return 0; } @@ -293,8 +205,7 @@ void thedealr_state::thedealr_main(address_map &map) map(0x2801, 0x2801).portr("DSW4"); map(0x2c00, 0x2c00).portr("DSW3"); - map(0x3400, 0x3400).rw(FUNC(thedealr_state::iox_r), FUNC(thedealr_state::iox_w)); - map(0x3401, 0x3401).r(FUNC(thedealr_state::iox_status_r)); + map(0x3400, 0x3401).rw(m_iocpu, FUNC(upi41_cpu_device::upi41_master_r), FUNC(upi41_cpu_device::upi41_master_w)); map(0x3000, 0x3000).ram(); // rw, comm in test mode map(0x3001, 0x3001).ram(); // rw, "" @@ -342,23 +253,33 @@ void thedealr_state::thedealr_sub(address_map &map) ***************************************************************************/ static INPUT_PORTS_START( thedealr ) - PORT_START("IOX") - PORT_BIT( 0x0001, IP_ACTIVE_HIGH, IPT_POKER_HOLD5 ) // HL5 (hold 5) - PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_GAMBLE_HALF ) // 1/2 (half gamble) - PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_GAMBLE_LOW ) PORT_NAME("Small") // SML (small) - PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_SERVICE1 ) PORT_NAME("Reset") // RST (reset) - PORT_BIT( 0x0010, IP_ACTIVE_HIGH, IPT_GAMBLE_KEYOUT ) // PAY - PORT_BIT( 0x0020, IP_ACTIVE_HIGH, IPT_SERVICE2 ) // (unused?) - PORT_BIT( 0x0040, IP_ACTIVE_HIGH, IPT_GAMBLE_BET ) // BET (bet) - PORT_BIT( 0x0080, IP_ACTIVE_HIGH, IPT_POKER_CANCEL ) // MET (cancel? keep pressed to show stats) - PORT_BIT( 0x0100, IP_ACTIVE_HIGH, IPT_GAMBLE_HIGH ) PORT_NAME("Big") // BIG (big) - PORT_BIT( 0x0200, IP_ACTIVE_HIGH, IPT_GAMBLE_D_UP ) // D.U (double up?) - PORT_BIT( 0x0400, IP_ACTIVE_HIGH, IPT_GAMBLE_DEAL ) PORT_NAME("Deal / Draw") // D.D (deal/draw, advance in test mode, trigger leds) - PORT_BIT( 0x0800, IP_ACTIVE_HIGH, IPT_GAMBLE_TAKE ) // T.S (take score?) - PORT_BIT( 0x1000, IP_ACTIVE_HIGH, IPT_POKER_HOLD4 ) // HL4 (hold 4) - PORT_BIT( 0x2000, IP_ACTIVE_HIGH, IPT_POKER_HOLD3 ) // HL3 (hold 3) - PORT_BIT( 0x4000, IP_ACTIVE_HIGH, IPT_POKER_HOLD2 ) // HL2 (hold 2) - PORT_BIT( 0x8000, IP_ACTIVE_HIGH, IPT_POKER_HOLD1 ) // HL1 (hold 1) + PORT_START("IOX0") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_POKER_HOLD5 ) // HL5 (hold 5) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_GAMBLE_HALF ) // 1/2 (half gamble) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_GAMBLE_LOW ) PORT_NAME("Small") // SML (small) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("Reset") // RST (reset) + + PORT_START("IOX1") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_GAMBLE_KEYOUT ) // PAY + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) // (unused?) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_GAMBLE_BET ) // BET (bet) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_GAMBLE_BOOK ) // MET (meters: keep pressed to show stats) + + PORT_START("IOX2") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_GAMBLE_HIGH ) PORT_NAME("Big") // BIG (big) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_GAMBLE_D_UP ) // D.U (double up?) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_GAMBLE_DEAL ) PORT_NAME("Deal / Draw") // D.D (deal/draw, advance in test mode, trigger leds) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_GAMBLE_TAKE ) // T.S (take score?) + + PORT_START("IOX3") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_POKER_HOLD4 ) // HL4 (hold 4) + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_POKER_HOLD3 ) // HL3 (hold 3) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_POKER_HOLD2 ) // HL2 (hold 2) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_POKER_HOLD1 ) // HL1 (hold 1) // DRP in test mode is an optional (via DSW) coin drop sensor? PORT_START("COINS") @@ -516,26 +437,21 @@ void thedealr_state::machine_start() { m_leds.resolve(); - save_item(NAME(m_iox_status)); - save_item(NAME(m_iox_ret)); - save_item(NAME(m_iox_cmd)); + m_iox_p1 = 0xff; + m_iox_p2 = 0xff; + m_iox_leds = 0x00; + + save_item(NAME(m_iox_p1)); + save_item(NAME(m_iox_p2)); save_item(NAME(m_iox_leds)); - save_item(NAME(m_iox_coins)); } /* It takes $19 IRQs to cycle through the IOX routines (read version/inputs, write outputs). With a single IRQ per frame this would translate to acknowledging inputs only every 0.4 seconds! - Hence we generate more IRQs than that in a frame. Besides, with a single VBLANK IRQ, + Hence the 8742 generates more IRQs than that in a frame. Besides, with a single VBLANK IRQ, the two 65C02 fail to sync properly on boot with error $26 and/or $27 (commram[0]) !? */ -TIMER_DEVICE_CALLBACK_MEMBER(thedealr_state::thedealr_interrupt) -{ - int scanline = param; - - if((scanline % 8) == 0) - m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); -} void thedealr_state::thedealr(machine_config &config) { @@ -543,11 +459,16 @@ void thedealr_state::thedealr(machine_config &config) R65C02(config, m_maincpu, 16_MHz_XTAL / 8); // 2 MHz? m_maincpu->set_addrmap(AS_PROGRAM, &thedealr_state::thedealr_main); - TIMER(config, "scantimer").configure_scanline(FUNC(thedealr_state::thedealr_interrupt), "screen", 0, 1); - R65C02(config, m_subcpu, 16_MHz_XTAL / 8); // 2 MHz? m_subcpu->set_addrmap(AS_PROGRAM, &thedealr_state::thedealr_sub); + I8742(config, m_iocpu, 16_MHz_XTAL / 2); + m_iocpu->p1_out_cb().set(FUNC(thedealr_state::iox_p1_w)); + m_iocpu->p2_out_cb().set(FUNC(thedealr_state::iox_p2_w)); + m_iocpu->p2_in_cb().set(FUNC(thedealr_state::iox_p2_r)); + + config.set_maximum_quantum(attotime::from_hz(700)); // needed to avoid boot failure with "IOX STATUS ERR" + NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0); WATCHDOG_TIMER(config, "watchdog"); @@ -590,7 +511,6 @@ ROM_START( thedealr ) ROM_REGION( 0x10000, "subcpu", 0 ) ROM_LOAD( "xb3_002", 0x00000, 0x10000, CRC(53a37fa4) SHA1(2adfea2dd08f298cda885bc72606d03f8af886a0) ) - // To do: hook up ROM_REGION( 0x0800, "iocpu", 0 ) ROM_LOAD( "x0-009", 0x0000, 0x0800, CRC(e8b86d5a) SHA1(ad12e8f4411c30cd691792c6b0b3429db786d8b5) )