summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/tms1000/tms2100.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/tms1000/tms2100.cpp')
-rw-r--r--src/devices/cpu/tms1000/tms2100.cpp155
1 files changed, 154 insertions, 1 deletions
diff --git a/src/devices/cpu/tms1000/tms2100.cpp b/src/devices/cpu/tms1000/tms2100.cpp
index efe561e787e..fc9b16bb3bc 100644
--- a/src/devices/cpu/tms1000/tms2100.cpp
+++ b/src/devices/cpu/tms1000/tms2100.cpp
@@ -22,7 +22,6 @@ Extra functions are controlled with the R register (not mapped to pins):
- R24: enable interrupts
TODO:
-- timer interrupt
- external interrupt (INT pin)
- event counter (EC1 pin)
- R0-R3 I/O, TRA opcode
@@ -88,13 +87,44 @@ void tms2100_cpu_device::device_start()
{
tms1100_cpu_device::device_start();
+ m_prescaler = timer_alloc(FUNC(tms2100_cpu_device::prescaler_timeout), this);
+ reset_prescaler();
+
// zerofill
m_ac2 = 0;
m_ivr = 0;
+ m_dec = 0xff;
+ m_il = 0;
+ m_int_pending = false;
+ m_r_prev = 0;
+
+ m_pb_save = 0;
+ m_cb_save = 0;
+ m_a_save = 0;
+ m_ac2_save = 0;
+ m_x_save = 0;
+ m_y_save = 0;
+ m_s_save = 0;
+ m_sl_save = 0;
+ m_o_save = 0;
// register for savestates
save_item(NAME(m_ac2));
save_item(NAME(m_ivr));
+ save_item(NAME(m_dec));
+ save_item(NAME(m_il));
+ save_item(NAME(m_int_pending));
+ save_item(NAME(m_r_prev));
+
+ save_item(NAME(m_pb_save));
+ save_item(NAME(m_cb_save));
+ save_item(NAME(m_a_save));
+ save_item(NAME(m_ac2_save));
+ save_item(NAME(m_x_save));
+ save_item(NAME(m_y_save));
+ save_item(NAME(m_s_save));
+ save_item(NAME(m_sl_save));
+ save_item(NAME(m_o_save));
state_add(++m_state_count, "AC2", m_ac2).formatstr("%01X"); // 9
}
@@ -103,6 +133,9 @@ void tms2100_cpu_device::device_reset()
{
tms1100_cpu_device::device_reset();
+ m_il = 0;
+ m_int_pending = false;
+
// changed/added fixed instructions
m_fixed_decode[0x09] = F_TAX;
m_fixed_decode[0x0e] = F_TADM;
@@ -113,6 +146,92 @@ void tms2100_cpu_device::device_reset()
}
+// interrupt/timer
+void tms2100_cpu_device::read_opcode()
+{
+ // return from interrupt
+ if ((m_fixed & F_RETN) && m_il == 1)
+ {
+ m_il = 0;
+
+ // restore registers
+ m_pb = m_pb_save;
+ m_cb = m_cb_save;
+ m_a = m_a_save;
+ m_ac2 = m_ac2_save;
+ m_x = m_x_save;
+ m_y = m_y_save;
+ m_status = m_s_save;
+ m_status_latch = m_sl_save;
+ write_o_reg(m_o_save);
+ }
+
+ // interrupt pending (blocked during jump opcodes)
+ if (m_int_pending && !(m_fixed & (F_BR | F_CALL | F_RETN)))
+ {
+ m_int_pending = false;
+
+ // interrupt enabled
+ if (BIT(m_r, 24))
+ {
+ interrupt();
+ return;
+ }
+ }
+
+ tms1100_cpu_device::read_opcode();
+}
+
+void tms2100_cpu_device::interrupt()
+{
+ // save registers
+ m_pb_save = m_pb;
+ m_cb_save = m_cb;
+ m_a_save = m_a;
+ m_ac2_save = m_ac2;
+ m_x_save = m_x;
+ m_y_save = m_y;
+ m_s_save = m_status;
+ m_sl_save = m_status_latch;
+ m_o_save = m_o_index;
+
+ // insert CALL to 0
+ m_opcode = 0xc0;
+ m_fixed = m_fixed_decode[m_opcode];
+ m_micro = m_micro_decode[m_opcode];
+
+ m_pb = 0;
+ m_cb = 0;
+ m_status = 1;
+ m_il |= 1;
+}
+
+TIMER_CALLBACK_MEMBER(tms2100_cpu_device::prescaler_timeout)
+{
+ // clock decrementer if internal timer is enabled
+ if (!BIT(m_r, 18) && m_dec != 0xff)
+ {
+ if (--m_dec == 0)
+ {
+ // possible pending interrupt
+ m_int_pending = true;
+
+ // reload decrementer
+ if (BIT(m_r, 23))
+ m_dec = m_ivr;
+ }
+ }
+}
+
+void tms2100_cpu_device::reset_prescaler()
+{
+ // prescaler divider is a mask option
+ static const u16 div[4] = { 32, 128, 256, 1024 };
+ attotime period = attotime::from_ticks(div[m_option_dec_div & 3] * 6, clock());
+ m_prescaler->adjust(period, 0, period);
+}
+
+
// i/o handling
u8 tms2100_cpu_device::read_k_input()
{
@@ -120,8 +239,42 @@ u8 tms2100_cpu_device::read_k_input()
return (BIT(m_r, 16) ? m_read_j() : m_read_k()) & 0xf;
}
+void tms2100_cpu_device::write_r_output(u32 data)
+{
+ if (m_r != m_r_prev)
+ {
+ // R15 falling edge: reset decrementer
+ if (BIT(m_r_prev, 15) && !BIT(m_r, 15))
+ m_dec = 0xff;
+
+ // R23 rising edge: load decrementer
+ if (!BIT(m_r_prev, 23) && BIT(m_r, 23))
+ m_dec = m_ivr;
+
+ m_r_prev = m_r;
+ }
+
+ tms1100_cpu_device::write_r_output(data);
+}
+
// opcode deviations
+void tms2100_cpu_device::op_call()
+{
+ // CALL: take IL/ILC into account
+ // it can only do 1 extra call inside an interrupt routine
+ if (m_status)
+ m_il = ((m_il << 1) | (m_il & 1)) & 7;
+ tms1100_cpu_device::op_call();
+}
+
+void tms2100_cpu_device::op_retn()
+{
+ // RETN: take IL/ILC into account
+ m_il >>= 1;
+ tms1100_cpu_device::op_retn();
+}
+
void tms2100_cpu_device::op_tax()
{
// TAX: transfer accumulator to X register