diff options
Diffstat (limited to 'src/devices/cpu/tms1000/smc1102.cpp')
-rw-r--r-- | src/devices/cpu/tms1000/smc1102.cpp | 363 |
1 files changed, 363 insertions, 0 deletions
diff --git a/src/devices/cpu/tms1000/smc1102.cpp b/src/devices/cpu/tms1000/smc1102.cpp new file mode 100644 index 00000000000..caf4dc13443 --- /dev/null +++ b/src/devices/cpu/tms1000/smc1102.cpp @@ -0,0 +1,363 @@ +// license:BSD-3-Clause +// copyright-holders:hap +/* + + Suwa Seikosha (now Seiko Epson) SMC1102, SMC1112 + +SMC1102 is a CMOS MCU based on TMS1100, keeping the same ALU and opcode mnemonics. +The stack(CALL/RETN) works a bit differently. They also added a timer, interrupts, +and a built-in LCD controller. + +In the USA, it was marketed by S-MOS Systems, an affiliate of Seiko Group. + +SMC1112 die notes (SMC1102 is assumed to be the same): +- 128x4 RAM array at top-left +- 256*64 8-bit ROM array at the bottom +- 30-term MPLA with 14 microinstructions, and 16 fixed opcodes next to it + (assumed neither of them is supposed to be customized) +- 32x4 LCD RAM at the left +- no output PLA + +TODO: +- add (micro)instructions PLA if it turns out it can be customized +- add halt opcode + +*/ + +#include "emu.h" +#include "smc1102.h" +#include "tms1k_dasm.h" + + +// device definitions +DEFINE_DEVICE_TYPE(SMC1102, smc1102_cpu_device, "smc1102", "Suwa Seikosha SMC1102") // 60-pin QFP or 42-pin DIP +DEFINE_DEVICE_TYPE(SMC1112, smc1112_cpu_device, "smc1112", "Suwa Seikosha SMC1112") // low power version + + +smc1102_cpu_device::smc1102_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), + m_write_segs(*this) +{ } + +smc1102_cpu_device::smc1102_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + smc1102_cpu_device(mconfig, SMC1102, tag, owner, clock, 0 /* o pins */, 8 /* r pins */, 6 /* pc bits */, 8 /* byte width */, 3 /* x width */, 4 /* stack levels */, 11 /* rom width */, address_map_constructor(FUNC(smc1102_cpu_device::rom_11bit), this), 7 /* ram width */, address_map_constructor(FUNC(smc1102_cpu_device::ram_7bit), this)) +{ } + +smc1112_cpu_device::smc1112_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : + smc1102_cpu_device(mconfig, SMC1112, tag, owner, clock, 0, 8, 6, 8, 3, 4, 11, address_map_constructor(FUNC(smc1112_cpu_device::rom_11bit), this), 7, address_map_constructor(FUNC(smc1112_cpu_device::ram_7bit), this)) +{ } + + +// disasm +std::unique_ptr<util::disasm_interface> smc1102_cpu_device::create_disassembler() +{ + return std::make_unique<smc1102_disassembler>(); +} + + +// device_start/reset +void smc1102_cpu_device::device_start() +{ + tms1100_cpu_device::device_start(); + + // zerofill + memset(m_lcd_ram, 0, sizeof(m_lcd_ram)); + m_lcd_sr = 0; + m_inten = false; + m_selin = 0; + m_k_line = false; + + m_div = 0; + m_timer = 0; + m_timeout = false; + m_tmset = 0; + + memset(m_stack, 0, sizeof(m_stack)); + m_sp = 0; + m_pb_stack = 0; + m_cb_stack = 0; + m_x_stack = 0; + m_y_stack = 0; + m_s_stack = 0; + + // register for savestates + save_item(NAME(m_lcd_ram)); + save_item(NAME(m_lcd_sr)); + save_item(NAME(m_inten)); + save_item(NAME(m_selin)); + save_item(NAME(m_k_line)); + + save_item(NAME(m_div)); + save_item(NAME(m_timer)); + save_item(NAME(m_timeout)); + save_item(NAME(m_tmset)); + + save_item(NAME(m_stack)); + save_item(NAME(m_sp)); + save_item(NAME(m_pb_stack)); + save_item(NAME(m_cb_stack)); + save_item(NAME(m_x_stack)); + save_item(NAME(m_y_stack)); + save_item(NAME(m_s_stack)); +} + +void smc1102_cpu_device::device_reset() +{ + tms1100_cpu_device::device_reset(); + + m_inten = false; + m_selin = 0; + m_timeout = false; + + // changed/added fixed instructions (mostly handled in op_extra) + m_fixed_decode[0x0a] = F_EXTRA; // TASR + m_fixed_decode[0x71] = F_EXTRA; // HALT + m_fixed_decode[0x74] = F_EXTRA; // INTEN + m_fixed_decode[0x75] = F_EXTRA; // INTDIS + m_fixed_decode[0x76] = F_RETN; // INTRTN + m_fixed_decode[0x78] = F_EXTRA; // SELIN + m_fixed_decode[0x7b] = F_EXTRA; // TMSET + + m_fixed_decode[0x72] = m_fixed_decode[0x73] = F_EXTRA; // TSG + m_fixed_decode[0x7c] = m_fixed_decode[0x7d] = F_EXTRA; // " +} + +u32 smc1102_cpu_device::decode_micro(offs_t offset) +{ + // TCY, YNEC, TCMIY + static const u16 micro1[3] = { 0x0402, 0x1204, 0x1032 }; + + // 0x00, 0x20, 0x30, 0x70 + static const u16 micro2[0x40] = + { + 0x0904, 0x0898, 0x1104, 0x2821, 0x104a, 0x101a, 0x0909, 0x0849, + 0x0401, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0404, 0x0000, + + 0x0102, 0x0801, 0x0802, 0x1001, 0x306a, 0x303a, 0x2021, 0x2020, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0e04, 0x0e04, 0x0e04, 0x0e04, 0x0899, 0x0099, 0x0819, 0x0804, + + 0x0519, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0519, + 0x0000, 0x0519, 0x0519, 0x0000, 0x0000, 0x0000, 0x0519, 0x0419 + }; + + static const int micro2h[8] = { 0x00, -1, 0x10, 0x20, -1, -1, -1, 0x30 }; + + u16 mask = 0; + + if (offset >= 0x40 && offset < 0x70) + mask = micro1[offset >> 4 & 3]; + else if (offset < 0x80 && (offset & 0xf0) != 0x10) + mask = micro2[micro2h[offset >> 4] | (offset & 0xf)]; + + // does not have M_MTN or M_STSL + const u32 md[14] = { M_AUTA, M_AUTY, M_NE, M_C8, M_CIN, M_CKM, M_15TN, M_NATN, M_ATN, M_CKN, M_CKP, M_MTP, M_YTP, M_STO }; + u32 decode = 0; + + for (int bit = 0; bit < 14; bit++) + if (mask & (1 << bit)) + decode |= md[bit]; + + return decode; +} + + +// interrupt/timer +void smc1102_cpu_device::execute_set_input(int line, int state) +{ + switch (line) + { + case SMC1102_INPUT_LINE_K: + m_k_line = bool(state); + break; + + default: + break; + } +} + +void smc1102_cpu_device::read_opcode() +{ + // return from interrupt + if (m_opcode == 0x76) + { + // restore registers + m_pb = m_pb_stack; + m_cb = m_cb_stack; + m_x = m_x_stack; + m_y = m_y_stack; + m_status = m_s_stack; + } + + // check interrupts (blocked after INTEN) + if (m_opcode != 0x74) + { + const bool taken = (m_selin & 2) ? m_timeout : m_k_line; + m_timeout = false; + + if (m_inten && taken) + { + interrupt(); + return; + } + } + + tms1100_cpu_device::read_opcode(); +} + +void smc1102_cpu_device::interrupt() +{ + standard_irq_callback(0, m_rom_address); + + // save registers + m_pb_stack = m_pb; + m_cb_stack = m_cb; + m_x_stack = m_x; + m_y_stack = m_y; + m_s_stack = m_status; + + // insert CALL to 0 on page 14 + m_opcode = 0xc0; + m_c4 = 0; + m_fixed = m_fixed_decode[m_opcode]; + m_micro = m_micro_decode[m_opcode]; + + m_pb = 0xe; + m_cb = 0; + m_status = 1; + m_inten = false; +} + +void smc1102_cpu_device::execute_run() +{ + while (m_icount > 0) + { + m_icount--; + + // decrement timer + m_div = (m_div + 1) & 0x1fff; + const u16 tmask = (m_selin & 1) ? 0x1ff : 0x1fff; + if ((m_div & tmask) == 0) + { + m_timer = (m_timer - 1) & 0xf; + if (m_timer == 0) + { + m_timer = m_tmset; + m_timeout = true; + } + } + + // overall, LCD refresh rate is 64Hz + if ((m_div & 0x1ff) == 0) + { + for (int i = 0; i < 4; i++) + m_write_segs(i, m_lcd_ram[i]); + } + + // 4 cycles per opcode instead of 6 + switch (m_subcycle) + { + case 2: + execute_one(2); + execute_one(3); + break; + + case 3: + execute_one(4); + execute_one(5); + break; + + default: + execute_one(m_subcycle); + break; + } + m_subcycle = (m_subcycle + 1) & 3; + } +} + + +// opcode deviations +void smc1102_cpu_device::op_call() +{ + // CALL: call subroutine + if (m_status) + { + m_stack[m_sp] = m_ca << 10 | m_pa << 6 | m_pc; + m_sp = (m_sp + 1) % m_stack_levels; + + m_pc = m_opcode & m_pc_mask; + m_pa = m_pb; + m_ca = m_cb; + } +} + +void smc1102_cpu_device::op_retn() +{ + // RETN: return from subroutine + m_sp = (m_stack_levels + m_sp - 1) % m_stack_levels; + + m_pc = m_stack[m_sp] & m_pc_mask; + m_pa = m_pb = m_stack[m_sp] >> 6 & 0xf; + m_ca = m_stack[m_sp] >> 10 & 1; // not CB +} + +void smc1102_cpu_device::op_tasr() +{ + // TASR: transfer A to LCD S/R + m_lcd_sr = m_lcd_sr << 4 | m_a; +} + +void smc1102_cpu_device::op_tsg() +{ + // TSG: transfer LCD S/R to RAM + m_lcd_ram[m_opcode & 3] = m_lcd_sr; +} + +void smc1102_cpu_device::op_intdis() +{ + // INTDIS: disable interrupt + m_inten = false; +} + +void smc1102_cpu_device::op_inten() +{ + // INTEN: enable interrupt after next instruction + m_inten = true; +} + +void smc1102_cpu_device::op_selin() +{ + // SELIN: select interrupt + m_selin = m_a & 3; +} + +void smc1102_cpu_device::op_tmset() +{ + // TMSET: transfer A to timer latch + m_tmset = m_a; +} + +void smc1102_cpu_device::op_halt() +{ + // HALT: stop CPU +} + +void smc1102_cpu_device::op_extra() +{ + switch (m_opcode) + { + case 0x0a: op_tasr(); break; + case 0x71: op_halt(); break; + case 0x74: op_inten(); break; + case 0x75: op_intdis(); break; + case 0x78: op_selin(); break; + case 0x7b: op_tmset(); break; + + case 0x72: case 0x73: case 0x7c: case 0x7d: + op_tsg(); break; + + default: break; + } +} |