mirror of
https://github.com/holub/mame
synced 2025-04-24 01:11:11 +03:00
thedealr: Emulate 8742 I/O MCU
This commit is contained in:
parent
16058c2380
commit
28a24a78b1
@ -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<cpu_device> m_maincpu;
|
||||
required_device<cpu_device> m_subcpu;
|
||||
required_device<upi41_cpu_device> m_iocpu;
|
||||
required_device<x1_001_device> m_spritegen;
|
||||
required_device<palette_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<<i))
|
||||
{
|
||||
m_iox_ret = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_iox_status |= IOX_OUT_FULL;
|
||||
break;
|
||||
}
|
||||
// Coin counters set by 0x40 command
|
||||
machine().bookkeeping().coin_counter_w(0, !BIT(data, 1)); // coin1 or service coin
|
||||
machine().bookkeeping().coin_counter_w(1, !BIT(data, 2)); // coupon
|
||||
machine().bookkeeping().coin_counter_w(2, !BIT(data, 3)); // service coin
|
||||
machine().bookkeeping().coin_counter_w(3, !BIT(data, 4)); // coin-out
|
||||
|
||||
// case 0x04: // ? at boot
|
||||
|
||||
case 0x08: // return iox version
|
||||
m_iox_ret = 0x54;
|
||||
m_iox_status |= IOX_OUT_FULL;
|
||||
break;
|
||||
|
||||
case 0x20: // leds
|
||||
m_iox_status |= IOX_WAITDATA;
|
||||
break;
|
||||
|
||||
case 0x40: // coin counters
|
||||
m_iox_status |= IOX_WAITDATA;
|
||||
break;
|
||||
|
||||
case 0x80: // store param?
|
||||
m_iox_status |= IOX_WAITDATA;
|
||||
break;
|
||||
|
||||
case 0x81: // store param?
|
||||
m_iox_status |= IOX_WAITDATA;
|
||||
break;
|
||||
|
||||
case 0xff: // reset
|
||||
iox_reset();
|
||||
break;
|
||||
|
||||
default:
|
||||
logerror("%s: IOX unknown command %02X\n", machine().describe_context(), m_iox_cmd);
|
||||
}
|
||||
}
|
||||
m_iox_p1 = data;
|
||||
}
|
||||
|
||||
// 3401
|
||||
uint8_t thedealr_state::iox_status_r()
|
||||
void thedealr_state::iox_p2_w(uint8_t data)
|
||||
{
|
||||
// bit 0 - Out buff full?
|
||||
// bit 1 - In buff full?
|
||||
// bit 7 - Ready for more data?
|
||||
return m_iox_status;
|
||||
// HACK: MCU sets P2 strobes but then removes them before reading return lines from other half of P2
|
||||
if (data != 0xff)
|
||||
m_iox_p2 = data;
|
||||
}
|
||||
|
||||
uint8_t thedealr_state::iox_p2_r()
|
||||
{
|
||||
// Inputs polled by 0x01 command
|
||||
uint8_t inputs = 0xff;
|
||||
for (int i = 0; i < 4; i++)
|
||||
if (!BIT(m_iox_p2, i))
|
||||
inputs &= m_iox_io[i]->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) )
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user