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.cpp353
1 files changed, 353 insertions, 0 deletions
diff --git a/src/devices/cpu/tms1000/tms2100.cpp b/src/devices/cpu/tms1000/tms2100.cpp
new file mode 100644
index 00000000000..5169aedb855
--- /dev/null
+++ b/src/devices/cpu/tms1000/tms2100.cpp
@@ -0,0 +1,353 @@
+// license:BSD-3-Clause
+// copyright-holders:hap
+/*
+
+ TMS1000 family - TMS2100, TMS2170, TMS2300, TMS2370
+
+TMS2100 is an enhanced version of TMS1100, adding interrupt, timer, A/D converter, and a 4-level callstack
+- the mpla has a similar layout as TMS1400, terms reduced to 26
+ (looks like it's optimized and not meant to be custom)
+- the opla is the same as TMS1400
+
+Extra functions are controlled with the R register (not mapped to pins):
+- R15: enable external interrupt
+- R16: K/J input select
+- R17: load initial value register with TMA
+- R18: internal/external counter clock control
+- R19: A1/A2 input select
+- R20: enable A/D
+- R21: R0-R3 I/O control
+- R22: R0-R3/ACC2 output select
+- R23: enable decrementer load
+- R24: enable interrupts
+
+TODO:
+- R0-R3 I/O, TRA opcode
+- A/D converter, TADM opcode
+- what happens if decrementer is loaded when IVR is 0?
+
+*/
+
+#include "emu.h"
+#include "tms2100.h"
+#include "tms1k_dasm.h"
+
+
+// device definitions
+DEFINE_DEVICE_TYPE(TMS2100, tms2100_cpu_device, "tms2100", "Texas Instruments TMS2100") // 28-pin DIP, 7 R pins
+DEFINE_DEVICE_TYPE(TMS2170, tms2170_cpu_device, "tms2170", "Texas Instruments TMS2170") // high voltage version, 1 R pin removed for Vpp
+DEFINE_DEVICE_TYPE(TMS2300, tms2300_cpu_device, "tms2300", "Texas Instruments TMS2300") // 40-pin DIP, 15 R pins, J pins
+DEFINE_DEVICE_TYPE(TMS2370, tms2370_cpu_device, "tms2370", "Texas Instruments TMS2370") // high voltage version, 1 R pin removed for Vpp
+
+
+tms2100_cpu_device::tms2100_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 o_pins, u8 r_pins, u8 pc_bits, u8 byte_bits, u8 x_bits, u8 stack_levels, int rom_width, address_map_constructor rom_map, int ram_width, address_map_constructor ram_map) :
+ tms1100_cpu_device(mconfig, type, tag, owner, clock, o_pins, r_pins, pc_bits, byte_bits, x_bits, stack_levels, rom_width, rom_map, ram_width, ram_map)
+{ }
+
+tms2100_cpu_device::tms2100_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ tms2100_cpu_device(mconfig, TMS2100, tag, owner, clock, 8 /* o pins */, 7 /* r pins */, 6 /* pc bits */, 8 /* byte width */, 3 /* x width */, 4 /* stack levels */, 11 /* rom width */, address_map_constructor(FUNC(tms2100_cpu_device::rom_11bit), this), 7 /* ram width */, address_map_constructor(FUNC(tms2100_cpu_device::ram_7bit), this))
+{ }
+
+tms2170_cpu_device::tms2170_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ tms2100_cpu_device(mconfig, TMS2170, tag, owner, clock, 8, 6, 6, 8, 3, 4, 11, address_map_constructor(FUNC(tms2170_cpu_device::rom_11bit), this), 7, address_map_constructor(FUNC(tms2170_cpu_device::ram_7bit), this))
+{ }
+
+
+tms2300_cpu_device::tms2300_cpu_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, u8 o_pins, u8 r_pins, u8 pc_bits, u8 byte_bits, u8 x_bits, u8 stack_levels, int rom_width, address_map_constructor rom_map, int ram_width, address_map_constructor ram_map) :
+ tms2100_cpu_device(mconfig, type, tag, owner, clock, o_pins, r_pins, pc_bits, byte_bits, x_bits, stack_levels, rom_width, rom_map, ram_width, ram_map)
+{ }
+
+tms2300_cpu_device::tms2300_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ tms2300_cpu_device(mconfig, TMS2300, tag, owner, clock, 8, 15, 6, 8, 3, 4, 11, address_map_constructor(FUNC(tms2300_cpu_device::rom_11bit), this), 7, address_map_constructor(FUNC(tms2300_cpu_device::ram_7bit), this))
+{ }
+
+tms2370_cpu_device::tms2370_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ tms2300_cpu_device(mconfig, TMS2370, tag, owner, clock, 8, 14, 6, 8, 3, 4, 11, address_map_constructor(FUNC(tms2370_cpu_device::rom_11bit), this), 7, address_map_constructor(FUNC(tms2370_cpu_device::ram_7bit), this))
+{ }
+
+
+// machine configs
+void tms2100_cpu_device::device_add_mconfig(machine_config &config)
+{
+ // microinstructions PLA, output PLA
+ PLA(config, m_mpla, 8, 16, 26).set_format(pla_device::FMT::BERKELEY);
+ PLA(config, m_opla, 5, 8, 32).set_format(pla_device::FMT::BERKELEY);
+}
+
+
+// disasm
+std::unique_ptr<util::disasm_interface> tms2100_cpu_device::create_disassembler()
+{
+ return std::make_unique<tms2100_disassembler>();
+}
+
+
+// device_start/reset
+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 = 0xff;
+ m_dec = 0xff;
+ m_il = 0;
+ m_int_pending = false;
+ m_r_prev = 0;
+ m_int_pin = 0;
+ m_ec1_pin = 0;
+
+ m_pb_stack = 0;
+ m_cb_stack = 0;
+ m_a_stack = 0;
+ m_ac2_stack = 0;
+ m_x_stack = 0;
+ m_y_stack = 0;
+ m_s_stack = 0;
+ m_sl_stack = 0;
+ m_o_stack = 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_int_pin));
+ save_item(NAME(m_ec1_pin));
+
+ save_item(NAME(m_pb_stack));
+ save_item(NAME(m_cb_stack));
+ save_item(NAME(m_a_stack));
+ save_item(NAME(m_ac2_stack));
+ save_item(NAME(m_x_stack));
+ save_item(NAME(m_y_stack));
+ save_item(NAME(m_s_stack));
+ save_item(NAME(m_sl_stack));
+ save_item(NAME(m_o_stack));
+
+ state_add(++m_state_count, "AC2", m_ac2).formatstr("%01X"); // 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;
+ m_fixed_decode[0x21] = F_TMA;
+ m_fixed_decode[0x26] = F_TAC;
+ m_fixed_decode[0x73] = F_TCA;
+ m_fixed_decode[0x7b] = F_TRA;
+}
+
+
+// interrupt/timer
+void tms2100_cpu_device::execute_set_input(int line, int state)
+{
+ switch (line)
+ {
+ case TMS2100_INPUT_LINE_INT:
+ if (state && !m_int_pin)
+ {
+ // load decrementer if it's not counting
+ if (BIT(m_r, 15) && m_dec == 0xff)
+ m_dec = m_ivr;
+ }
+ m_int_pin = state;
+ break;
+
+ case TMS2100_INPUT_LINE_EC1:
+ if (state && !m_ec1_pin)
+ {
+ // clock decrementer if event counter is enabled
+ if (BIT(m_r, 18))
+ clock_decrementer();
+ }
+ m_ec1_pin = state;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void tms2100_cpu_device::clock_decrementer()
+{
+ if (m_dec != 0xff && --m_dec == 0)
+ {
+ // possible pending interrupt
+ m_int_pending = true;
+
+ // reload decrementer
+ m_dec = BIT(m_r, 23) ? m_ivr : 0xff;
+ }
+}
+
+TIMER_CALLBACK_MEMBER(tms2100_cpu_device::prescaler_timeout)
+{
+ // clock decrementer if internal timer is enabled
+ if (!BIT(m_r, 18))
+ clock_decrementer();
+}
+
+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);
+}
+
+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_stack;
+ m_cb = m_cb_stack;
+ m_a = m_a_stack;
+ m_ac2 = m_ac2_stack;
+ m_x = m_x_stack;
+ m_y = m_y_stack;
+ m_status = m_s_stack;
+ m_status_latch = m_sl_stack;
+ write_o_reg(m_o_stack);
+ }
+
+ // 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()
+{
+ standard_irq_callback(0, m_rom_address);
+
+ // save registers
+ m_pb_stack = m_pb;
+ m_cb_stack = m_cb;
+ m_a_stack = m_a;
+ m_ac2_stack = m_ac2;
+ m_x_stack = m_x;
+ m_y_stack = m_y;
+ m_s_stack = m_status;
+ m_sl_stack = m_status_latch;
+ m_o_stack = m_o_index;
+
+ // insert CALL to 0 on page 0
+ m_opcode = 0xc0;
+ m_c4 = 0;
+ 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;
+}
+
+
+// i/o handling
+u8 tms2100_cpu_device::read_k_input()
+{
+ // select K/J port with R16
+ 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
+ m_x = m_a & m_x_mask;
+}
+
+void tms2100_cpu_device::op_tra()
+{
+ // TRA: transfer R inputs to accumulator
+}
+
+void tms2100_cpu_device::op_tac()
+{
+ // TAC: transfer accumulator to AC2
+ m_ac2 = m_a;
+}
+
+void tms2100_cpu_device::op_tca()
+{
+ // TCA: transfer AC2 to accumulator
+ m_a = m_ac2;
+}
+
+void tms2100_cpu_device::op_tadm()
+{
+ // TADM: transfer A/D register to memory
+}
+
+void tms2100_cpu_device::op_tma()
+{
+ // TMA: if R17 is high, destination is IVR instead of A
+ if (BIT(m_r, 17))
+ {
+ u8 shift = (m_y & 1) * 4;
+ m_ivr = (m_ivr & ~(0xf << shift)) | (m_ram_in << shift);
+ m_micro &= ~M_AUTA; // don't store in A
+ }
+}