From 274078636e439f3c555f148143a5ef747dec049d Mon Sep 17 00:00:00 2001 From: Curt Coder Date: Wed, 8 Apr 2015 09:01:20 +0300 Subject: [PATCH] mos6530: Rewritten to support time travel. [Curt Coder] --- scripts/src/machine.lua | 1 + src/emu/machine/mos6530n.c | 596 +++++++++++++++++++++++++++++++++++++ src/emu/machine/mos6530n.h | 352 ++++++++++++++++++++++ 3 files changed, 949 insertions(+) create mode 100644 src/emu/machine/mos6530n.c create mode 100644 src/emu/machine/mos6530n.h diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index b60cdc028c3..b0397cbed3c 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1385,6 +1385,7 @@ end if (MACHINES["MIOT6530"]~=null) then files { MAME_DIR .. "src/emu/machine/mos6530.c", + MAME_DIR .. "src/emu/machine/mos6530n.c", } end diff --git a/src/emu/machine/mos6530n.c b/src/emu/machine/mos6530n.c new file mode 100644 index 00000000000..06b4da9a4da --- /dev/null +++ b/src/emu/machine/mos6530n.c @@ -0,0 +1,596 @@ +// license:BSD-3-Clause +// copyright-holders:Curt Coder +/********************************************************************** + + MOS Technology 6530 Memory, I/O, Timer Array emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "mos6530n.h" + + + +//************************************************************************** +// MACROS / CONSTANTS +//************************************************************************** + +#define LOG 0 + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type MOS6530n = &device_creator; + + +DEVICE_ADDRESS_MAP_START( rom_map, 8, mos6530_t ) + AM_RANGE(0x000, 0x3ff) AM_READ(rom_r) +ADDRESS_MAP_END + +DEVICE_ADDRESS_MAP_START( ram_map, 8, mos6530_t ) + AM_RANGE(0x00, 0x3f) AM_READWRITE(ram_r, ram_w) +ADDRESS_MAP_END + +DEVICE_ADDRESS_MAP_START( io_map, 8, mos6530_t ) + AM_RANGE(0x00, 0x00) AM_READWRITE(pa_data_r, pa_data_w) + AM_RANGE(0x01, 0x01) AM_READWRITE(pa_ddr_r, pa_ddr_w) + AM_RANGE(0x02, 0x02) AM_READWRITE(pb_data_r, pb_data_w) + AM_RANGE(0x03, 0x03) AM_READWRITE(pb_ddr_r, pb_ddr_w) + AM_RANGE(0x04, 0x0f) AM_READ(timer_r) + AM_RANGE(0x04, 0x0f) AM_WRITE(timer_w) +ADDRESS_MAP_END + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// mos6530_t - constructor +//------------------------------------------------- + +mos6530_t::mos6530_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, MOS6530n, "MOS6530n", tag, owner, clock, "mos6530n", __FILE__), + m_ram(*this), + m_irq_cb(*this), + m_in_pa_cb(*this), + m_out_pa_cb(*this), + m_in_pb_cb(*this), + m_out_pb_cb(*this), + m_in_pa0_cb(*this), + m_in_pa1_cb(*this), + m_in_pa2_cb(*this), + m_in_pa3_cb(*this), + m_in_pa4_cb(*this), + m_in_pa5_cb(*this), + m_in_pa6_cb(*this), + m_in_pa7_cb(*this), + m_out_pa0_cb(*this), + m_out_pa1_cb(*this), + m_out_pa2_cb(*this), + m_out_pa3_cb(*this), + m_out_pa4_cb(*this), + m_out_pa5_cb(*this), + m_out_pa6_cb(*this), + m_out_pa7_cb(*this), + m_in_pb0_cb(*this), + m_in_pb1_cb(*this), + m_in_pb2_cb(*this), + m_in_pb3_cb(*this), + m_in_pb4_cb(*this), + m_in_pb5_cb(*this), + m_in_pb6_cb(*this), + m_in_pb7_cb(*this), + m_out_pb0_cb(*this), + m_out_pb1_cb(*this), + m_out_pb2_cb(*this), + m_out_pb3_cb(*this), + m_out_pb4_cb(*this), + m_out_pb5_cb(*this), + m_out_pb6_cb(*this), + m_out_pb7_cb(*this), + m_pa_in(0), + m_pb_in(0) +{ + cur_live.tm = attotime::never; + cur_live.state = IDLE; + cur_live.next_state = -1; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void mos6530_t::device_start() +{ + // resolve callbacks + m_irq_cb.resolve_safe(); + m_in_pa_cb.resolve_safe(0xff); + m_out_pa_cb.resolve_safe(); + m_in_pb_cb.resolve_safe(0xff); + m_out_pb_cb.resolve_safe(); + m_in_pa0_cb.resolve_safe(1); + m_in_pa1_cb.resolve_safe(1); + m_in_pa2_cb.resolve_safe(1); + m_in_pa3_cb.resolve_safe(1); + m_in_pa4_cb.resolve_safe(1); + m_in_pa5_cb.resolve_safe(1); + m_in_pa6_cb.resolve_safe(1); + m_in_pa7_cb.resolve_safe(1); + m_out_pa0_cb.resolve_safe(); + m_out_pa1_cb.resolve_safe(); + m_out_pa2_cb.resolve_safe(); + m_out_pa3_cb.resolve_safe(); + m_out_pa4_cb.resolve_safe(); + m_out_pa5_cb.resolve_safe(); + m_out_pa6_cb.resolve_safe(); + m_out_pa7_cb.resolve_safe(); + m_in_pb0_cb.resolve_safe(1); + m_in_pb1_cb.resolve_safe(1); + m_in_pb2_cb.resolve_safe(1); + m_in_pb3_cb.resolve_safe(1); + m_in_pb4_cb.resolve_safe(1); + m_in_pb5_cb.resolve_safe(1); + m_in_pb6_cb.resolve_safe(1); + m_in_pb7_cb.resolve_safe(1); + m_out_pb0_cb.resolve_safe(); + m_out_pb1_cb.resolve_safe(); + m_out_pb2_cb.resolve_safe(); + m_out_pb3_cb.resolve_safe(); + m_out_pb4_cb.resolve_safe(); + m_out_pb5_cb.resolve_safe(); + m_out_pb6_cb.resolve_safe(); + m_out_pb7_cb.resolve_safe(); + + // allocate timer + t_gen = timer_alloc(0); + + // allocate RAM + m_ram.allocate(0x40); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void mos6530_t::device_reset() +{ + m_ie = false; + m_irq = true; + + m_pa_out = 0; + m_pa_ddr = 0; + m_pb_out = 0; + m_pb_ddr = 0; + + update_pa(); + update_pb(); + + live_abort(); +} + + +//------------------------------------------------- +// device_timer - handler timer events +//------------------------------------------------- + +void mos6530_t::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + live_sync(); + live_run(); +} + + +//------------------------------------------------- +// update_pa - +//------------------------------------------------- + +void mos6530_t::update_pa() +{ + UINT8 out = m_pa_out; + UINT8 ddr = m_pa_ddr; + UINT8 data = (out & ddr) | (ddr ^ 0xff); + + m_out_pa_cb(data); + + m_out_pa0_cb(BIT(data, 0)); + m_out_pa1_cb(BIT(data, 1)); + m_out_pa2_cb(BIT(data, 2)); + m_out_pa3_cb(BIT(data, 3)); + m_out_pa4_cb(BIT(data, 4)); + m_out_pa5_cb(BIT(data, 5)); + m_out_pa6_cb(BIT(data, 6)); + m_out_pa7_cb(BIT(data, 7)); +} + + +//------------------------------------------------- +// update_pb - +//------------------------------------------------- + +void mos6530_t::update_pb() +{ + UINT8 out = m_pb_out; + UINT8 ddr = m_pb_ddr; + UINT8 data = (out & ddr) | (ddr ^ 0xff); + + if (m_ie) + { + data &= ~IRQ_TIMER; + data |= m_irq ? 0x00 : IRQ_TIMER; + m_irq_cb(m_irq ? ASSERT_LINE : CLEAR_LINE); + } + + m_out_pb_cb(data); + + m_out_pb0_cb(BIT(data, 0)); + m_out_pb1_cb(BIT(data, 1)); + m_out_pb2_cb(BIT(data, 2)); + m_out_pb3_cb(BIT(data, 3)); + m_out_pb4_cb(BIT(data, 4)); + m_out_pb5_cb(BIT(data, 5)); + m_out_pb6_cb(BIT(data, 6)); + m_out_pb7_cb(BIT(data, 7)); +} + + +//------------------------------------------------- +// pa_w - +//------------------------------------------------- + +void mos6530_t::pa_w(int bit, int state) +{ + m_pa_in &= ~(2 << bit); + + if (state) m_pa_in |= (2 << bit); +} + + +//------------------------------------------------- +// pb_w - +//------------------------------------------------- + +void mos6530_t::pb_w(int bit, int state) +{ + m_pb_in &= ~(2 << bit); + + if (state) m_pb_in |= (2 << bit); +} + + +//------------------------------------------------- +// pa_data_r - +//------------------------------------------------- + +READ8_MEMBER( mos6530_t::pa_data_r ) +{ + UINT8 in = 0; + + if (m_in_pa_cb.isnull()) + { + in |= m_in_pa0_cb.isnull() ? BIT(m_pa_in, 0) : m_in_pa0_cb() << 0; + in |= m_in_pa1_cb.isnull() ? BIT(m_pa_in, 1) : m_in_pa1_cb() << 1; + in |= m_in_pa2_cb.isnull() ? BIT(m_pa_in, 2) : m_in_pa2_cb() << 2; + in |= m_in_pa3_cb.isnull() ? BIT(m_pa_in, 3) : m_in_pa3_cb() << 3; + in |= m_in_pa4_cb.isnull() ? BIT(m_pa_in, 4) : m_in_pa4_cb() << 4; + in |= m_in_pa5_cb.isnull() ? BIT(m_pa_in, 5) : m_in_pa5_cb() << 5; + in |= m_in_pa6_cb.isnull() ? BIT(m_pa_in, 6) : m_in_pa6_cb() << 6; + in |= m_in_pa7_cb.isnull() ? BIT(m_pa_in, 7) : m_in_pa7_cb() << 7; + } + else + { + in = m_in_pa_cb(); + } + + UINT8 out = m_pa_out; + UINT8 ddr = m_pa_ddr; + + return (out & ddr) | (in & (ddr ^ 0xff)); +} + + +//------------------------------------------------- +// pa_data_w - +//------------------------------------------------- + +WRITE8_MEMBER( mos6530_t::pa_data_w ) +{ + m_pa_out = data; + + if (LOG) logerror("%s %s MOS6530 '%s' Port A data %02x\n", machine().time().as_string(), machine().describe_context(), tag(), data); + + update_pa(); +} + + +//------------------------------------------------- +// pa_ddr_w - +//------------------------------------------------- + +WRITE8_MEMBER( mos6530_t::pa_ddr_w ) +{ + m_pa_ddr = data; + + if (LOG) logerror("%s %s MOS6530 '%s' Port A DDR %02x\n", machine().time().as_string(), machine().describe_context(), tag(), data); + + update_pa(); +} + + +//------------------------------------------------- +// pb_data_r - +//------------------------------------------------- + +READ8_MEMBER( mos6530_t::pb_data_r ) +{ + UINT8 in = 0; + + if (m_in_pb_cb.isnull()) + { + in |= m_in_pb0_cb.isnull() ? BIT(m_pb_in, 0) : m_in_pb0_cb() << 0; + in |= m_in_pb1_cb.isnull() ? BIT(m_pb_in, 1) : m_in_pb1_cb() << 1; + in |= m_in_pb2_cb.isnull() ? BIT(m_pb_in, 2) : m_in_pb2_cb() << 2; + in |= m_in_pb3_cb.isnull() ? BIT(m_pb_in, 3) : m_in_pb3_cb() << 3; + in |= m_in_pb4_cb.isnull() ? BIT(m_pb_in, 4) : m_in_pb4_cb() << 4; + in |= m_in_pb5_cb.isnull() ? BIT(m_pb_in, 5) : m_in_pb5_cb() << 5; + in |= m_in_pb6_cb.isnull() ? BIT(m_pb_in, 6) : m_in_pb6_cb() << 6; + in |= m_in_pb7_cb.isnull() ? BIT(m_pb_in, 7) : m_in_pb7_cb() << 7; + } + else + { + in = m_in_pb_cb(); + } + + UINT8 out = m_pb_out; + UINT8 ddr = m_pb_ddr; + + return (out & ddr) | (in & (ddr ^ 0xff)); +} + + +//------------------------------------------------- +// pb_data_w - +//------------------------------------------------- + +WRITE8_MEMBER( mos6530_t::pb_data_w ) +{ + m_pb_out = data; + + if (LOG) logerror("%s %s MOS6530 '%s' Port B data %02x\n", machine().time().as_string(), machine().describe_context(), tag(), data); + + update_pb(); +} + + +//------------------------------------------------- +// pb_ddr_w - +//------------------------------------------------- + +WRITE8_MEMBER( mos6530_t::pb_ddr_w ) +{ + m_pb_ddr = data; + + if (LOG) logerror("%s %s MOS6530 '%s' Port B DDR %02x\n", machine().time().as_string(), machine().describe_context(), tag(), data); + + update_pb(); +} + + +//------------------------------------------------- +// timer_r - +//------------------------------------------------- + +READ8_MEMBER( mos6530_t::timer_r ) +{ + UINT8 data = 0; + + if (offset & 0x01) + { + data = m_irq ? 0x80 : 0x00; + } + else + { + live_sync(); + + data = cur_live.value; + + checkpoint(); + live_run(); + + m_ie = BIT(offset, 3) ? true : false; + } + + if (m_irq) { + m_irq = false; + update_pb(); + } + + return data; +} + + +//------------------------------------------------- +// timer_w - +//------------------------------------------------- + +WRITE8_MEMBER( mos6530_t::timer_w ) +{ + live_sync(); + + m_timer = data; + + switch (offset & 0x03) { + case 0: m_shift = 1; break; + case 1: m_shift = 8; break; + case 2: m_shift = 64; break; + case 3: m_shift = 1024; break; + } + + m_ie = BIT(offset, 3) ? true : false; + + if (LOG) logerror("%s %s MOS6530 '%s' Timer value %02x shift %u IE %u\n", machine().time().as_string(), machine().describe_context(), tag(), data, m_shift, m_ie ? 1 : 0); + + if (m_irq) { + m_irq = false; + update_pb(); + } + + checkpoint(); + + if (cur_live.state != IDLE) { + live_abort(); + } + + live_start(); + live_run(); +} + + +//------------------------------------------------- +// live_start - +//------------------------------------------------- + +void mos6530_t::live_start() +{ + cur_live.period = attotime::from_hz(clock() / m_shift); + cur_live.tm = machine().time() + cur_live.period; + cur_live.state = RUNNING; + cur_live.next_state = -1; + + cur_live.value = m_timer; + cur_live.irq = false; + + checkpoint_live = cur_live; + + live_run(); +} + +void mos6530_t::checkpoint() +{ + checkpoint_live = cur_live; +} + +void mos6530_t::rollback() +{ + cur_live = checkpoint_live; +} + +void mos6530_t::live_delay(int state) +{ + cur_live.next_state = state; + if(cur_live.tm != machine().time()) + t_gen->adjust(cur_live.tm - machine().time()); + else + live_sync(); +} + +void mos6530_t::live_sync() +{ + if(!cur_live.tm.is_never()) { + if(cur_live.tm > machine().time()) { + rollback(); + live_run(machine().time()); + } else { + if(cur_live.next_state != -1) { + cur_live.state = cur_live.next_state; + cur_live.next_state = -1; + } + if(cur_live.state == IDLE) { + cur_live.tm = attotime::never; + } + } + cur_live.next_state = -1; + checkpoint(); + } +} + +void mos6530_t::live_abort() +{ + if(!cur_live.tm.is_never() && cur_live.tm > machine().time()) { + rollback(); + live_run(machine().time()); + } + + cur_live.tm = attotime::never; + cur_live.state = IDLE; + cur_live.next_state = -1; + + cur_live.irq = false; +} + +void mos6530_t::live_run(const attotime &limit) +{ + if(cur_live.state == IDLE || cur_live.next_state != -1) + return; + + for(;;) { + switch(cur_live.state) { + case RUNNING: { + if (cur_live.tm > limit) + return; + + cur_live.value--; + + if (LOG) logerror("%s MOS6530 '%s' timer %02x IRQ 1\n", cur_live.tm.as_string(), tag(), cur_live.value); + + if (!cur_live.value) { + cur_live.period = attotime::from_hz(clock()); + cur_live.state = RUNNING_INTERRUPT; + } + + cur_live.tm += cur_live.period; + break; + } + + case RUNNING_INTERRUPT: { + if (cur_live.tm > limit) + return; + + cur_live.value--; + cur_live.irq = true; + + if (LOG) logerror("%s MOS6530 '%s' timer %02x IRQ 0\n", cur_live.tm.as_string(), tag(), cur_live.value); + + live_delay(RUNNING_SYNCPOINT); + + cur_live.tm += cur_live.period; + return; + } + + case RUNNING_SYNCPOINT: { + logerror("%s MOS6530 '%s' IRQ\n", machine().time().as_string(), tag()); + + m_irq = true; + update_pb(); + + cur_live.state = RUNNING_AFTER_INTERRUPT; + checkpoint(); + break; + } + + case RUNNING_AFTER_INTERRUPT: { + if (cur_live.tm > limit) + return; + + cur_live.value--; + + if (LOG) logerror("%s MOS6530 '%s' timer %02x IRQ 0\n", cur_live.tm.as_string(), tag(), cur_live.value); + + if (!cur_live.value) { + live_abort(); + return; + } + + cur_live.tm += cur_live.period; + break; + } + } + } +} diff --git a/src/emu/machine/mos6530n.h b/src/emu/machine/mos6530n.h new file mode 100644 index 00000000000..88cd4a56558 --- /dev/null +++ b/src/emu/machine/mos6530n.h @@ -0,0 +1,352 @@ +// license:BSD-3-Clause +// copyright-holders:Curt Coder +/********************************************************************** + + MOS Technology 6530 Memory, I/O, Timer Array emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +********************************************************************** + _____ _____ + Vss 1 |* \_/ | 40 PA1 + PA0 2 | | 39 PA2 + phi2 3 | | 38 PA3 + RS0 4 | | 37 PA4 + A9 5 | | 36 PA5 + A8 6 | | 35 PA6 + A7 7 | | 34 PA7 + A6 8 | | 33 DB0 + R/W 9 | | 32 DB1 + A5 10 | MCS6530 | 31 DB2 + A4 11 | | 30 DB3 + A3 12 | | 29 DB4 + A2 13 | | 28 DB5 + A1 14 | | 27 DB6 + A0 15 | | 26 DB7 + _RES 16 | | 25 PB0 + IRQ/PB7 17 | | 24 PB1 + CS1/PB6 18 | | 23 PB2 + CS2/PB5 19 | | 22 PB3 + Vcc 20 |_____________| 21 PB4 + +**********************************************************************/ + +#pragma once + +#ifndef __MOS6530n__ +#define __MOS6530n__ + +#include "emu.h" + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_MOS6530n_IRQ_CB(_write) \ + devcb = &mos6530_t::set_irq_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_IN_PA_CB(_read) \ + devcb = &mos6530_t::set_pa_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_OUT_PA_CB(_write) \ + devcb = &mos6530_t::set_pa_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_IN_PB_CB(_read) \ + devcb = &mos6530_t::set_pb_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_OUT_PB_CB(_write) \ + devcb = &mos6530_t::set_pb_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_IN_PA0_CB(_read) \ + devcb = &mos6530_t::set_pa0_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PA1_CB(_read) \ + devcb = &mos6530_t::set_pa1_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PA2_CB(_read) \ + devcb = &mos6530_t::set_pa2_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PA3_CB(_read) \ + devcb = &mos6530_t::set_pa3_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PA4_CB(_read) \ + devcb = &mos6530_t::set_pa4_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PA5_CB(_read) \ + devcb = &mos6530_t::set_pa5_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PA6_CB(_read) \ + devcb = &mos6530_t::set_pa6_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PA7_CB(_read) \ + devcb = &mos6530_t::set_pa7_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_OUT_PA0_CB(_write) \ + devcb = &mos6530_t::set_pa0_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PA1_CB(_write) \ + devcb = &mos6530_t::set_pa1_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PA2_CB(_write) \ + devcb = &mos6530_t::set_pa2_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PA3_CB(_write) \ + devcb = &mos6530_t::set_pa3_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PA4_CB(_write) \ + devcb = &mos6530_t::set_pa4_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PA5_CB(_write) \ + devcb = &mos6530_t::set_pa5_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PA6_CB(_write) \ + devcb = &mos6530_t::set_pa6_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PA7_CB(_write) \ + devcb = &mos6530_t::set_pa7_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_IN_PB0_CB(_read) \ + devcb = &mos6530_t::set_pb0_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PB1_CB(_read) \ + devcb = &mos6530_t::set_pb1_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PB2_CB(_read) \ + devcb = &mos6530_t::set_pb2_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PB3_CB(_read) \ + devcb = &mos6530_t::set_pb3_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PB4_CB(_read) \ + devcb = &mos6530_t::set_pb4_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PB5_CB(_read) \ + devcb = &mos6530_t::set_pb5_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PB6_CB(_read) \ + devcb = &mos6530_t::set_pb6_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_IN_PB7_CB(_read) \ + devcb = &mos6530_t::set_pb7_rd_callback(*device, DEVCB_##_read); + +#define MCFG_MOS6530n_OUT_PB0_CB(_write) \ + devcb = &mos6530_t::set_pb0_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PB1_CB(_write) \ + devcb = &mos6530_t::set_pb1_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PB2_CB(_write) \ + devcb = &mos6530_t::set_pb2_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PB3_CB(_write) \ + devcb = &mos6530_t::set_pb3_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PB4_CB(_write) \ + devcb = &mos6530_t::set_pb4_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PB5_CB(_write) \ + devcb = &mos6530_t::set_pb5_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PB6_CB(_write) \ + devcb = &mos6530_t::set_pb6_wr_callback(*device, DEVCB_##_write); + +#define MCFG_MOS6530n_OUT_PB7_CB(_write) \ + devcb = &mos6530_t::set_pb7_wr_callback(*device, DEVCB_##_write); + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> mos6530_t + +class mos6530_t : public device_t +{ +public: + // construction/destruction + mos6530_t(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + template static devcb_base &set_irq_wr_callback(device_t &device, _Object object) { return downcast(device).m_irq_cb.set_callback(object); } + template static devcb_base &set_pa_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa_cb.set_callback(object); } + template static devcb_base &set_pa_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa_cb.set_callback(object); } + template static devcb_base &set_pb_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb_cb.set_callback(object); } + template static devcb_base &set_pb_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb_cb.set_callback(object); } + template static devcb_base &set_pa0_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa0_cb.set_callback(object); } + template static devcb_base &set_pa1_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa1_cb.set_callback(object); } + template static devcb_base &set_pa2_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa2_cb.set_callback(object); } + template static devcb_base &set_pa3_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa3_cb.set_callback(object); } + template static devcb_base &set_pa4_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa4_cb.set_callback(object); } + template static devcb_base &set_pa5_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa5_cb.set_callback(object); } + template static devcb_base &set_pa6_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa6_cb.set_callback(object); } + template static devcb_base &set_pa7_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pa7_cb.set_callback(object); } + template static devcb_base &set_pa0_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa0_cb.set_callback(object); } + template static devcb_base &set_pa1_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa1_cb.set_callback(object); } + template static devcb_base &set_pa2_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa2_cb.set_callback(object); } + template static devcb_base &set_pa3_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa3_cb.set_callback(object); } + template static devcb_base &set_pa4_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa4_cb.set_callback(object); } + template static devcb_base &set_pa5_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa5_cb.set_callback(object); } + template static devcb_base &set_pa6_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa6_cb.set_callback(object); } + template static devcb_base &set_pa7_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pa7_cb.set_callback(object); } + template static devcb_base &set_pb0_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb0_cb.set_callback(object); } + template static devcb_base &set_pb1_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb1_cb.set_callback(object); } + template static devcb_base &set_pb2_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb2_cb.set_callback(object); } + template static devcb_base &set_pb3_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb3_cb.set_callback(object); } + template static devcb_base &set_pb4_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb4_cb.set_callback(object); } + template static devcb_base &set_pb5_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb5_cb.set_callback(object); } + template static devcb_base &set_pb6_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb6_cb.set_callback(object); } + template static devcb_base &set_pb7_rd_callback(device_t &device, _Object object) { return downcast(device).m_in_pb7_cb.set_callback(object); } + template static devcb_base &set_pb0_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb0_cb.set_callback(object); } + template static devcb_base &set_pb1_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb1_cb.set_callback(object); } + template static devcb_base &set_pb2_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb2_cb.set_callback(object); } + template static devcb_base &set_pb3_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb3_cb.set_callback(object); } + template static devcb_base &set_pb4_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb4_cb.set_callback(object); } + template static devcb_base &set_pb5_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb5_cb.set_callback(object); } + template static devcb_base &set_pb6_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb6_cb.set_callback(object); } + template static devcb_base &set_pb7_wr_callback(device_t &device, _Object object) { return downcast(device).m_out_pb7_cb.set_callback(object); } + + virtual DECLARE_ADDRESS_MAP(rom_map, 8); + virtual DECLARE_ADDRESS_MAP(ram_map, 8); + virtual DECLARE_ADDRESS_MAP(io_map, 8); + + DECLARE_WRITE_LINE_MEMBER( pa0_w ) { pa_w(0, state); } + DECLARE_WRITE_LINE_MEMBER( pa1_w ) { pa_w(1, state); } + DECLARE_WRITE_LINE_MEMBER( pa2_w ) { pa_w(2, state); } + DECLARE_WRITE_LINE_MEMBER( pa3_w ) { pa_w(3, state); } + DECLARE_WRITE_LINE_MEMBER( pa4_w ) { pa_w(4, state); } + DECLARE_WRITE_LINE_MEMBER( pa5_w ) { pa_w(5, state); } + DECLARE_WRITE_LINE_MEMBER( pa6_w ) { pa_w(6, state); } + DECLARE_WRITE_LINE_MEMBER( pa7_w ) { pa_w(7, state); } + + DECLARE_WRITE_LINE_MEMBER( pb0_w ) { pb_w(0, state); } + DECLARE_WRITE_LINE_MEMBER( pb1_w ) { pb_w(1, state); } + DECLARE_WRITE_LINE_MEMBER( pb2_w ) { pb_w(2, state); } + DECLARE_WRITE_LINE_MEMBER( pb3_w ) { pb_w(3, state); } + DECLARE_WRITE_LINE_MEMBER( pb4_w ) { pb_w(4, state); } + DECLARE_WRITE_LINE_MEMBER( pb5_w ) { pb_w(5, state); } + DECLARE_WRITE_LINE_MEMBER( pb6_w ) { pb_w(6, state); } + DECLARE_WRITE_LINE_MEMBER( pb7_w ) { pb_w(7, state); } + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + + enum + { + IRQ_TIMER = 0x80 + }; + + void update_pa(); + void update_pb(); + void pa_w(int bit, int state); + void pb_w(int bit, int state); + + DECLARE_READ8_MEMBER( rom_r ) { return m_region->base()[offset]; } + DECLARE_READ8_MEMBER( ram_r ) { return m_ram[offset]; } + DECLARE_WRITE8_MEMBER( ram_w ) { m_ram[offset] = data; } + DECLARE_READ8_MEMBER( pa_data_r ); + DECLARE_WRITE8_MEMBER( pa_data_w ); + DECLARE_READ8_MEMBER( pb_data_r ); + DECLARE_WRITE8_MEMBER( pb_data_w ); + DECLARE_READ8_MEMBER( pa_ddr_r ) { return m_pa_ddr; } + DECLARE_WRITE8_MEMBER( pa_ddr_w ); + DECLARE_READ8_MEMBER( pb_ddr_r ) { return m_pb_ddr; } + DECLARE_WRITE8_MEMBER( pb_ddr_w ); + DECLARE_READ8_MEMBER( irq_r ) { return m_irq ? 0x80 : 0x00; } + DECLARE_READ8_MEMBER( timer_r ); + DECLARE_WRITE8_MEMBER( timer_w ); + + optional_shared_ptr m_ram; + + devcb_write_line m_irq_cb; + devcb_read8 m_in_pa_cb; + devcb_write8 m_out_pa_cb; + devcb_read8 m_in_pb_cb; + devcb_write8 m_out_pb_cb; + devcb_read_line m_in_pa0_cb; + devcb_read_line m_in_pa1_cb; + devcb_read_line m_in_pa2_cb; + devcb_read_line m_in_pa3_cb; + devcb_read_line m_in_pa4_cb; + devcb_read_line m_in_pa5_cb; + devcb_read_line m_in_pa6_cb; + devcb_read_line m_in_pa7_cb; + devcb_write_line m_out_pa0_cb; + devcb_write_line m_out_pa1_cb; + devcb_write_line m_out_pa2_cb; + devcb_write_line m_out_pa3_cb; + devcb_write_line m_out_pa4_cb; + devcb_write_line m_out_pa5_cb; + devcb_write_line m_out_pa6_cb; + devcb_write_line m_out_pa7_cb; + devcb_read_line m_in_pb0_cb; + devcb_read_line m_in_pb1_cb; + devcb_read_line m_in_pb2_cb; + devcb_read_line m_in_pb3_cb; + devcb_read_line m_in_pb4_cb; + devcb_read_line m_in_pb5_cb; + devcb_read_line m_in_pb6_cb; + devcb_read_line m_in_pb7_cb; + devcb_write_line m_out_pb0_cb; + devcb_write_line m_out_pb1_cb; + devcb_write_line m_out_pb2_cb; + devcb_write_line m_out_pb3_cb; + devcb_write_line m_out_pb4_cb; + devcb_write_line m_out_pb5_cb; + devcb_write_line m_out_pb6_cb; + devcb_write_line m_out_pb7_cb; + + UINT8 m_pa_in; + UINT8 m_pa_out; + UINT8 m_pa_ddr; + + UINT8 m_pb_in; + UINT8 m_pb_out; + UINT8 m_pb_ddr; + + bool m_ie; + bool m_irq; + + int m_shift; + UINT8 m_timer; + + enum { + IDLE, + RUNNING, + RUNNING_INTERRUPT, + RUNNING_SYNCPOINT, + RUNNING_AFTER_INTERRUPT + }; + + struct live_info { + attotime tm; + attotime period; + int state, next_state; + UINT8 value; + bool irq; + }; + + live_info cur_live, checkpoint_live; + emu_timer *t_gen; + + void live_start(); + void checkpoint(); + void rollback(); + void live_delay(int state); + void live_sync(); + void live_abort(); + void live_run(const attotime &limit = attotime::never); +}; + + +// device type definition +extern const device_type MOS6530n; + + + +#endif