diff options
Diffstat (limited to 'src/devices/cpu/m6502/st2xxx.cpp')
-rw-r--r-- | src/devices/cpu/m6502/st2xxx.cpp | 313 |
1 files changed, 296 insertions, 17 deletions
diff --git a/src/devices/cpu/m6502/st2xxx.cpp b/src/devices/cpu/m6502/st2xxx.cpp index ce5a91ba619..a7a5a84be8c 100644 --- a/src/devices/cpu/m6502/st2xxx.cpp +++ b/src/devices/cpu/m6502/st2xxx.cpp @@ -28,20 +28,34 @@ #include "emu.h" #include "st2xxx.h" -st2xxx_device::st2xxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor internal_map, int data_bits, u16 ireq_mask) +#define LOG_IRQ (1 << 1U) +#define LOG_BT (1 << 2U) +//#define VERBOSE (LOG_IRQ | LOG_BT) +#include "logmacro.h" + +st2xxx_device::st2xxx_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor internal_map, int data_bits, bool has_banked_ram) : r65c02_device(mconfig, type, tag, owner, clock) , m_data_config("data", ENDIANNESS_LITTLE, 8, data_bits, 0) , m_in_port_cb{{*this}, {*this}, {*this}, {*this}, {*this}, {*this}, {*this}} , m_out_port_cb{{*this}, {*this}, {*this}, {*this}, {*this}, {*this}, {*this}} + , m_prr_mask(data_bits <= 14 ? 0 : ((u16(1) << (data_bits - 14)) - 1) | (has_banked_ram ? 0x8000 : 0)) + , m_drr_mask(data_bits <= 15 ? 0 : ((u16(1) << (data_bits - 15)) - 1) | (has_banked_ram ? 0x8000 : 0)) , m_pdata{0} , m_pctrl{0} , m_psel{0} , m_pfun{0} , m_pmcr(0) + , m_bten(0) + , m_btsr(0) + , m_bt_mask(0) + , m_bt_ireq(0) + , m_sys(0) , m_ireq(0) , m_iena(0) - , m_ireq_mask(ireq_mask) - , m_sys(0) + , m_lssa(0) + , m_lvpw(0) + , m_lxmax(0) + , m_lymax(0) { program_config.m_internal_map = std::move(internal_map); } @@ -62,6 +76,81 @@ void st2xxx_device::device_resolve_objects() cb.resolve_safe(); } +TIMER_CALLBACK_MEMBER(st2xxx_device::bt_interrupt) +{ + // BTSR must be cleared each time the interrupt is serviced + bool interrupt = (m_btsr == 0); + + m_btsr |= 1 << param; + + unsigned div = st2xxx_bt_divider(param); + assert(div != 0); + m_base_timer[param]->adjust(attotime::from_ticks(32768, div), param); + + if (interrupt) + { + LOGMASKED(LOG_BT, "Interrupt caused by %.1f Hz base timer\n", 32768.0 / div); + m_ireq |= m_bt_ireq; + update_irq_state(); + } +} + +void st2xxx_device::init_base_timer(u16 ireq) +{ + m_bt_ireq = ireq; + + for (int n = 0; n < 8; n++) + { + if (st2xxx_bt_divider(n) != 0) + { + m_bt_mask |= 1 << n; + m_base_timer[n] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(st2xxx_device::bt_interrupt), this)); + } + } + + assert(m_bt_mask != 0); + assert(m_bt_ireq != 0); +} + +void st2xxx_device::save_common_registers() +{ + mi_st2xxx *intf = downcast<mi_st2xxx *>(mintf.get()); + + save_item(NAME(m_pdata)); + save_item(NAME(m_pctrl)); + save_item(NAME(m_psel)); + save_item(NAME(m_pfun)); + save_item(NAME(m_pmcr)); + if (m_prr_mask != 0) + { + if (BIT(st2xxx_sys_mask(), 1)) + { + save_item(NAME(intf->irq_service)); + save_item(NAME(intf->irr_enable)); + save_item(NAME(intf->irr)); + } + save_item(NAME(intf->prr)); + } + if (m_drr_mask != 0) + { + save_item(NAME(intf->drr)); + if (st2xxx_has_dma()) + save_item(NAME(intf->dmr)); + } + if (m_bt_mask != 0) + { + save_item(NAME(m_bten)); + save_item(NAME(m_btsr)); + } + save_item(NAME(m_sys)); + save_item(NAME(m_ireq)); + save_item(NAME(m_iena)); + save_item(NAME(m_lssa)); + save_item(NAME(m_lvpw)); + save_item(NAME(m_lxmax)); + save_item(NAME(m_lymax)); +} + void st2xxx_device::device_reset() { m6502_device::device_reset(); @@ -75,11 +164,23 @@ void st2xxx_device::device_reset() cb(0xff); m_pmcr = 0x80; + // reset bank registers + mi_st2xxx &m = downcast<mi_st2xxx &>(*mintf); + m.irr_enable = false; + m.irr = 0; + m.prr = 0; + m.drr = 0; + m.dmr = 0; + // reset interrupt registers m_ireq = 0; m_iena = 0; update_irq_state(); + // reset base timer + bten_w(0); + m_btsr = 0; + // reset miscellaneous registers m_sys = 0; @@ -93,19 +194,15 @@ void st2xxx_device::device_reset() u8 st2xxx_device::acknowledge_irq() { // IREQH interrupts have priority over IREQL interrupts - for (int level = 8; level < 16; level++) - { - if (BIT(m_ireq & m_iena, level)) - { - m_ireq &= ~(1 << level); - update_irq_state(); - return level; - } - } - for (int level = 0; level < 8; level++) + for (int pri = 0; pri < 16; pri++) { + int level = pri ^ 8; if (BIT(m_ireq & m_iena, level)) { + LOGMASKED(LOG_IRQ, "%s interrupt acknowledged (PC = $%04X, vector = $%04X)\n", + st2xxx_irq_name(level), + PPC, + 0x7ff8 - (level << 1)); m_ireq &= ~(1 << level); update_irq_state(); return level; @@ -207,6 +304,154 @@ void st2xxx_device::pcl_w(u8 data) pctrl_w(6, data); } +u8 st2xxx_device::bten_r() +{ + return m_bten; +} + +void st2xxx_device::bten_w(u8 data) +{ + data &= m_bt_mask; + + for (int n = 0; n < 8; n++) + { + if (BIT(data, n) && !BIT(m_bten, n)) + { + unsigned div = st2xxx_bt_divider(n); + assert(div != 0); + assert(m_base_timer[n] != nullptr); + m_base_timer[n]->adjust(attotime::from_ticks(32768, div), n); + LOGMASKED(LOG_BT, "Base timer %d enabled at %.1f Hz (PC = $%04X)\n", n, 32768.0 / div, PPC); + } + else if (!BIT(data, n) && BIT(m_bten, n)) + { + m_base_timer[n]->adjust(attotime::never); + LOGMASKED(LOG_BT, "Base timer %d disabled (PC = $%04X)\n", n, PPC); + } + } + + m_bten = data; +} + +u8 st2xxx_device::btsr_r() +{ + return m_btsr; +} + +void st2xxx_device::btclr_w(u8 data) +{ + // Write 1 to clear each individual bit + m_btsr &= ~data; +} + +void st2xxx_device::btclr_all_w(u8 data) +{ + // Only bit 7 has any effect + if (BIT(data, 7)) + m_btsr = 0; +} + +u8 st2xxx_device::sys_r() +{ + return m_sys | 0x01; +} + +void st2xxx_device::sys_w(u8 data) +{ + u8 mask = st2xxx_sys_mask(); + m_sys = data & mask; + if (BIT(mask, 1)) + downcast<mi_st2xxx &>(*mintf).irr_enable = BIT(data, 1); +} + +u8 st2xxx_device::irrl_r() +{ + return downcast<mi_st2xxx &>(*mintf).irr & 0xff; +} + +void st2xxx_device::irrl_w(u8 data) +{ + u16 &irr = downcast<mi_st2xxx &>(*mintf).irr; + irr = (data & m_prr_mask) | (irr & 0xff00); +} + +u8 st2xxx_device::irrh_r() +{ + return downcast<mi_st2xxx &>(*mintf).irr >> 8; +} + +void st2xxx_device::irrh_w(u8 data) +{ + u16 &irr = downcast<mi_st2xxx &>(*mintf).irr; + irr = ((u16(data) << 8) & m_prr_mask) | (irr & 0x00ff); +} + +u8 st2xxx_device::prrl_r() +{ + return downcast<mi_st2xxx &>(*mintf).prr & 0xff; +} + +void st2xxx_device::prrl_w(u8 data) +{ + u16 &prr = downcast<mi_st2xxx &>(*mintf).prr; + prr = (data & m_prr_mask) | (prr & 0xff00); +} + +u8 st2xxx_device::prrh_r() +{ + return downcast<mi_st2xxx &>(*mintf).prr >> 8; +} + +void st2xxx_device::prrh_w(u8 data) +{ + u16 &prr = downcast<mi_st2xxx &>(*mintf).prr; + prr = ((u16(data) << 8) & m_prr_mask) | (prr & 0x00ff); +} + +u8 st2xxx_device::drrl_r() +{ + return downcast<mi_st2xxx &>(*mintf).drr & 0xff; +} + +void st2xxx_device::drrl_w(u8 data) +{ + u16 &drr = downcast<mi_st2xxx &>(*mintf).drr; + drr = (data & m_drr_mask) | (drr & 0xff00); +} + +u8 st2xxx_device::drrh_r() +{ + return downcast<mi_st2xxx &>(*mintf).drr >> 8; +} + +void st2xxx_device::drrh_w(u8 data) +{ + u16 &drr = downcast<mi_st2xxx &>(*mintf).drr; + drr = ((u16(data) << 8) & m_drr_mask) | (drr & 0x00ff); +} + +u8 st2xxx_device::dmrl_r() +{ + return downcast<mi_st2xxx &>(*mintf).dmr & 0xff; +} + +void st2xxx_device::dmrl_w(u8 data) +{ + u16 &dmr = downcast<mi_st2xxx &>(*mintf).dmr; + dmr = (data & m_drr_mask) | (dmr & 0xff00); +} + +u8 st2xxx_device::dmrh_r() +{ + return downcast<mi_st2xxx &>(*mintf).dmr >> 8; +} + +void st2xxx_device::dmrh_w(u8 data) +{ + u16 &dmr = downcast<mi_st2xxx &>(*mintf).dmr; + dmr = ((u16(data) << 8) & m_drr_mask) | (dmr & 0x00ff); +} + u8 st2xxx_device::ireql_r() { return m_ireq & 0x00ff; @@ -216,6 +461,11 @@ void st2xxx_device::ireql_w(u8 data) { if ((m_ireq & ~data & 0x00ff) != 0) { + for (int i = 0; i < 8; i++) + { + if (!BIT(data, i) && BIT(m_ireq, i)) + LOGMASKED(LOG_IRQ, "%s interrupt cleared (PC = $%04X)\n", st2xxx_irq_name(i), PPC); + } m_ireq &= data | 0xff00; update_irq_state(); } @@ -230,6 +480,11 @@ void st2xxx_device::ireqh_w(u8 data) { if ((m_ireq & ~(u16(data) << 8) & 0xff00) != 0) { + for (int i = 0; i < 8; i++) + { + if (!BIT(data, i) && BIT(m_ireq, i + 8)) + LOGMASKED(LOG_IRQ, "%s interrupt cleared (PC = $%04X)\n", st2xxx_irq_name(i + 8), PPC); + } m_ireq &= u16(data) << 8 | 0x00ff; update_irq_state(); } @@ -242,8 +497,20 @@ u8 st2xxx_device::ienal_r() void st2xxx_device::ienal_w(u8 data) { - m_iena = (m_iena & 0xff00) | (data & m_ireq_mask); - update_irq_state(); + data &= st2xxx_ireq_mask(); + if (data != (m_iena & 0x00ff)) + { + for (int i = 0; i < 8; i++) + { + if (BIT(data, i) != BIT(m_iena, i)) + LOGMASKED(LOG_IRQ, "%s interrupt %sabled (PC = $%04X)\n", + st2xxx_irq_name(i), + BIT(data, i) ? "en" : "dis", + PPC); + } + m_iena = (m_iena & 0xff00) | data; + update_irq_state(); + } } u8 st2xxx_device::ienah_r() @@ -253,8 +520,20 @@ u8 st2xxx_device::ienah_r() void st2xxx_device::ienah_w(u8 data) { - m_iena = (m_iena & 0x00ff) | ((u16(data) << 8) & m_ireq_mask); - update_irq_state(); + data &= st2xxx_ireq_mask() >> 8; + if (data != (m_iena >> 8)) + { + for (int i = 0; i < 8; i++) + { + if (BIT(data, i) != BIT(m_iena, i + 8)) + LOGMASKED(LOG_IRQ, "%s interrupt %sabled (PC = $%04X)\n", + st2xxx_irq_name(i + 8), + BIT(data, i) ? "en" : "dis", + PPC); + } + m_iena = (m_iena & 0x00ff) | (u16(data) << 8); + update_irq_state(); + } } void st2xxx_device::lssal_w(u8 data) |