summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/m6502/st2xxx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/m6502/st2xxx.cpp')
-rw-r--r--src/devices/cpu/m6502/st2xxx.cpp313
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)