diff options
Diffstat (limited to 'src/devices/cpu/tms1000/tms2100.cpp')
-rw-r--r-- | src/devices/cpu/tms1000/tms2100.cpp | 155 |
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 |