diff options
author | 2021-04-27 14:06:19 -0400 | |
---|---|---|
committer | 2021-04-27 14:06:19 -0400 | |
commit | e9e66e0224b86d0383643f7a52ce2639421d2a23 (patch) | |
tree | e64bf46c1a878fdfd531723bf15c10885708b548 | |
parent | 6b420210335ddefbc04de81cf8ca33f03858f316 (diff) |
mn1880: Add preliminary CPU emulation
-rw-r--r-- | src/devices/cpu/mn1880/mn1880.cpp | 2299 | ||||
-rw-r--r-- | src/devices/cpu/mn1880/mn1880.h | 169 | ||||
-rw-r--r-- | src/mame/drivers/basssta.cpp | 32 | ||||
-rw-r--r-- | src/mame/drivers/drumsta.cpp | 10 | ||||
-rw-r--r-- | src/mame/drivers/macpci.cpp | 24 | ||||
-rw-r--r-- | src/mame/includes/macpci.h | 1 |
6 files changed, 2474 insertions, 61 deletions
diff --git a/src/devices/cpu/mn1880/mn1880.cpp b/src/devices/cpu/mn1880/mn1880.cpp index cf837a53d28..f967e9a9853 100644 --- a/src/devices/cpu/mn1880/mn1880.cpp +++ b/src/devices/cpu/mn1880/mn1880.cpp @@ -2,9 +2,58 @@ // copyright-holders:AJR /*************************************************************************** - Panasonic MN1880 family + Panasonic MN1880 series - Currently this device is just a stub with no actual execution core. + This is a cycle-by-cycle emulation of the "8-Bit Dual Microcomputer" + architecture, which has two independent CPUs sharing the same memory + spaces and execution core, with execution phases interleaved when both + CPUs are operating. Pipelining permits operand fetches to overlap with + data writes. + + Known issues: + * Only one generic device type has been provided, with no internal + RAM or ROM. Though the broad functional descriptions of many actual + models are known, more detailed documentation is not easy to find. + * Almost no internal special function registers have been emulated, + again owing to lack of documentation. It is unknown to what extent + their mapping differs between models. + * Instruction timings are based mostly on those documented for the + MN1870, which lacks a number of MN1880 instructions and differs in + some other important ways. Many have been guessed at. + * Some instruction behavior, especially for repeated cases, has been + guessed at and may not be strictly correct. + * No interrupts have been emulated. It remains unclear exactly how + MN1880 interrupts are enabled and prioritized, or what their sources + might be, though they are obviously internally vectored through the + table at the start of the program space. + * The PI (software interrupt) instruction is likewise unemulated, + since its vector is uncertain; though possibly implicitly inserted + when an interrupt is acknowledged, explicit uses of it are + nonexistent in extant code. + * The output queue has been implemented only for memory writes, even + though the MN1870 documentation shows it as applicable for + instructions that do none of those. + * Every cycle fetches a byte from the instruction space, whether it + is needed for execution or not. This fetching may not happen quite + so continuously on actual hardware (a few dummy fetches may be + unavoidable), but it is more or less continuous on some other + microcontrollers with Harvard-like architectures such as MCS-51. + * Data writes occur simultaneously with program fetches. Harvard + architecture makes instruction and data spaces independent from the + code's perspective, but simultaneous access is obviously impossible + when both addresses are external since there is at most one external + address bus. Contention should slow prefetching and execution down. + * Additional wait states for external memory, if any, are not emulated. + * When execution is stopped in the debugger, IP already points to the + byte following the opcode which has been loaded into IR. This at + least seems consistent with the prefetch model and the handling of + repeated instructions. + * There is no way to focus on one of the two CPUs in the debugger when + both are executing. This is not an issue on systems that simply + disable CPUb from the start. + * The debugger will not single-step through repeated instructions. + Making MAME's context-insensitive disassembler produce any sensible + output for these would be very difficult. ***************************************************************************/ @@ -15,20 +64,43 @@ // device type definitions DEFINE_DEVICE_TYPE(MN1880, mn1880_device, "mn1880", "Panasonic MN1880") -mn1880_device::mn1880_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) - : cpu_device(mconfig, MN1880, tag, owner, clock) +ALLOW_SAVE_TYPE(mn1880_device::microstate) + +mn1880_device::mn1880_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor data_map) + : cpu_device(mconfig, type, tag, owner, clock) , m_program_config("program", ENDIANNESS_BIG, 8, 16, 0) - , m_data_config("data", ENDIANNESS_LITTLE, 8, 16, 0) - , m_ip(0) - , m_fs(0) - , m_xp(0) - , m_yp(0) - , m_sp(0) - , m_lp(0) + , m_data_config("data", ENDIANNESS_LITTLE, 8, 16, 0, data_map) + , m_cpu_select(false) + , m_cpum(0) + , m_ustate(microstate::UNKNOWN) + , m_da(0) + , m_tmp1(0) + , m_tmp2(0) + , m_output_queued(false) , m_icount(0) { } +mn1880_device::mn1880_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : mn1880_device(mconfig, MN1880, tag, owner, clock, address_map_constructor(FUNC(mn1880_device::internal_data_map), this)) +{ +} + +u8 mn1880_device::cpum_r() +{ + return m_cpum; +} + +void mn1880_device::cpum_w(u8 data) +{ + m_cpum = data; +} + +void mn1880_device::internal_data_map(address_map &map) +{ + map(0x0016, 0x0016).rw(FUNC(mn1880_device::cpum_r), FUNC(mn1880_device::cpum_w)); +} + std::unique_ptr<util::disasm_interface> mn1880_device::create_disassembler() { return std::make_unique<mn1880_disassembler>(); @@ -48,54 +120,2178 @@ void mn1880_device::device_start() space(AS_DATA).specific(m_data); set_icountptr(m_icount); - state_add(MN1880_IP, "IP", m_ip); - state_add(STATE_GENPC, "GENPC", m_ip).noshow(); - state_add(STATE_GENPCBASE, "CURPC", m_ip).noshow(); - state_add(MN1880_FS, "FS", m_fs); - state_add(STATE_GENFLAGS, "FLAGS", m_fs).formatstr("%10s").noshow(); - state_add(MN1880_XP, "XP", m_xp); - state_add(MN1880_YP, "YP", m_yp); + using namespace std::placeholders; + state_add<u16>(MN1880_IP, "IP", + [this] () { return m_cpu[int(m_cpu_select)].ip; }, + [this] (u16 data) { m_cpu[int(m_cpu_select)].ip = data; } + ).noshow(); + state_add<u16>(STATE_GENPC, "GENPC", + [this] () { return m_cpu[int(m_cpu_select)].ip; }, + [this] (u16 data) { m_cpu[int(m_cpu_select)].ip = data; } + ).noshow(); + state_add<u16>(STATE_GENPCBASE, "CURPC", + [this] () { return m_cpu[int(m_cpu_select)].irp; }, + [this] (u16 data) { m_cpu[int(m_cpu_select)].irp = data; } + ).noshow(); + state_add<u8>(MN1880_IR, "IR", + [this] () { return m_cpu[int(m_cpu_select)].ir; }, + [this] (u8 data) { m_cpu[int(m_cpu_select)].ir = data; } + ).noshow(); + state_add<u8>(MN1880_FS, "FS", + [this] () { return m_cpu[int(m_cpu_select)].fs; }, + [this] (u8 data) { m_cpu[int(m_cpu_select)].fs = data; } + ).noshow(); + state_add<u8>(STATE_GENFLAGS, "FLAGS", + [this] () { return m_cpu[int(m_cpu_select)].fs; }, + [this] (u8 data) { m_cpu[int(m_cpu_select)].fs = data; } + ).formatstr("%10s").noshow(); + state_add<u16>(MN1880_XP, "XP", + [this] () { return m_cpu[int(m_cpu_select)].xp; }, + [this] (u16 data) { m_cpu[int(m_cpu_select)].xp = data; } + ).noshow(); + state_add<u16>(MN1880_YP, "YP", + [this] () { return m_cpu[int(m_cpu_select)].yp; }, + [this] (u16 data) { m_cpu[int(m_cpu_select)].yp = data; } + ).noshow(); state_add<u8>(MN1880_XPL, "XPl", - [this] () { return m_xp & 0x00ff; }, - [this] (u8 data) { m_xp = (m_xp & 0xff00) | data; } + [this] () { return m_cpu[int(m_cpu_select)].xp & 0x00ff; }, + [this] (u8 data) { setl(m_cpu[int(m_cpu_select)].xp, data); } ).noshow(); state_add<u8>(MN1880_XPH, "XPh", - [this] () { return (m_xp & 0xff00) >> 8; }, - [this] (u8 data) { m_xp = (m_xp & 0x00ff) | u16(data) << 8; } + [this] () { return (m_cpu[int(m_cpu_select)].xp & 0xff00 >> 8); }, + [this] (u8 data) { seth(m_cpu[int(m_cpu_select)].xp, data); } ).noshow(); state_add<u8>(MN1880_YPL, "YPl", - [this] () { return m_yp & 0x00ff; }, - [this] (u8 data) { m_yp = (m_yp & 0xff00) | data; } + [this] () { return m_cpu[int(m_cpu_select)].yp & 0x00ff; }, + [this] (u8 data) { setl(m_cpu[int(m_cpu_select)].yp, data); } ).noshow(); state_add<u8>(MN1880_YPH, "YPh", - [this] () { return (m_yp & 0xff00) >> 8; }, - [this] (u8 data) { m_yp = (m_yp & 0x00ff) | u16(data) << 8; } + [this] () { return (m_cpu[int(m_cpu_select)].yp & 0xff00 >> 8); }, + [this] (u8 data) { seth(m_cpu[int(m_cpu_select)].yp, data); } + ).noshow(); + state_add<u16>(MN1880_SP, "SP", + [this] () { return m_cpu[int(m_cpu_select)].sp; }, + [this] (u16 data) { m_cpu[int(m_cpu_select)].sp = data; } + ).noshow(); + state_add<u16>(MN1880_LP, "LP", + [this] () { return m_cpu[int(m_cpu_select)].lp; }, + [this] (u16 data) { m_cpu[int(m_cpu_select)].lp = data; } ).noshow(); - state_add(MN1880_SP, "SP", m_sp); - state_add(MN1880_LP, "LP", m_lp); - save_item(NAME(m_ip)); - save_item(NAME(m_fs)); - save_item(NAME(m_xp)); - save_item(NAME(m_yp)); - save_item(NAME(m_sp)); - save_item(NAME(m_lp)); + for (int i = 0; i < 2; i++) + { + state_add(MN1880_IPA + i, util::string_format("IP%c", 'a' + i).c_str(), m_cpu[i].ip).formatstr("%5s"); + state_add(MN1880_IRA + i, util::string_format("IR%c", 'a' + i).c_str(), m_cpu[i].ir); + state_add(MN1880_FSA + i, util::string_format("FS%c", 'a' + i).c_str(), m_cpu[i].fs); + state_add(MN1880_XPA + i, util::string_format("XP%c", 'a' + i).c_str(), m_cpu[i].xp); + state_add(MN1880_YPA + i, util::string_format("YP%c", 'a' + i).c_str(), m_cpu[i].yp); + state_add(MN1880_SPA + i, util::string_format("SP%c", 'a' + i).c_str(), m_cpu[i].sp); + state_add(MN1880_LPA + i, util::string_format("LP%c", 'a' + i).c_str(), m_cpu[i].lp); + state_add_divider(MN1880_DIVIDER1 + i); + } + + state_add(MN1880_CPUM, "CPUM", m_cpum); + + save_item(STRUCT_MEMBER(m_cpu, ip)); + save_item(STRUCT_MEMBER(m_cpu, irp)); + save_item(STRUCT_MEMBER(m_cpu, ir)); + save_item(STRUCT_MEMBER(m_cpu, fs)); + save_item(STRUCT_MEMBER(m_cpu, xp)); + save_item(STRUCT_MEMBER(m_cpu, yp)); + save_item(STRUCT_MEMBER(m_cpu, sp)); + save_item(STRUCT_MEMBER(m_cpu, lp)); + save_item(NAME(m_cpu_select)); + save_item(NAME(m_cpum)); + save_item(NAME(m_ustate)); + save_item(NAME(m_da)); + save_item(NAME(m_tmp1)); + save_item(NAME(m_tmp2)); + save_item(NAME(m_output_queued)); } void mn1880_device::device_reset() { - m_fs &= 0xc0; // CF & ZF might or might not be cleared as well - m_sp = 0x0100; - // TBD: is LP also initialized at reset? + for (cpu_registers &cpu : m_cpu) + { + cpu.fs &= 0xc0; // CF & ZF might or might not be cleared as well + cpu.fs |= 0x10; // HACK: skip fake first instruction in debugger + cpu.ir = 0xf6; + cpu.wait = 0; + } + m_cpu[0].ip = 0x0000; + m_cpu[1].ip = 0x0020; + + // TBD: exactly what are SP and LP initialized to for each CPU? + m_cpu[0].sp = 0x0100; + m_cpu[0].lp = 0x0060; + m_cpu[1].sp = 0x0200; + m_cpu[1].lp = 0x0160; + + m_cpum = 0x0c; + m_ustate = microstate::NEXT; + m_output_queued = false; +} + +const mn1880_device::microstate mn1880_device::s_decode_map[256] = +{ + microstate::NOP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, + microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, microstate::REP_1, + microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, + microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, microstate::CLRSET_1, + microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, + microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, microstate::T1B_1, + microstate::UNKNOWN, microstate::MOVL31_1, microstate::UNKNOWN, microstate::MOVL31_1, microstate::MOVL34_1, microstate::MOVL35_1, microstate::MOV36_1, microstate::MOV37_1, + microstate::MOVL38_1, microstate::MOVL39_1, microstate::MOVL38_1, microstate::MOVL39_1, microstate::ASL_1, microstate::ASL_1, microstate::ASR_1, microstate::ASR_1, + microstate::DEC40_1, microstate::DEC40_1, microstate::NOT_1, microstate::NOT_1, microstate::CMPM44_1, microstate::CMPM45_1, microstate::XCH4_1, microstate::XCH4_1, + microstate::INC48_1, microstate::INC48_1, microstate::CLR4A_1, microstate::CLR4A_1, microstate::ROL_1, microstate::ROL_1, microstate::ROR_1, microstate::ROR_1, + microstate::CMPM50_1, microstate::DIV51_1, microstate::CMPM52_1, microstate::MOVDA_1, microstate::MOV54_1, microstate::MOV55_1, microstate::MOV56_1, microstate::MOV56_1, + microstate::XCH58_1, microstate::MUL59_1, microstate::XCH58_1, microstate::UNKNOWN, microstate::MOVL5C_1, microstate::MOVL5D_1, microstate::MOVL5E_1, microstate::MOVL5F_1, + microstate::CMP_1, microstate::CMP_1, microstate::CMP_1, microstate::CMP_1, microstate::AND_1, microstate::AND_1, microstate::AND_1, microstate::AND_1, + microstate::XOR_1, microstate::XOR_1, microstate::XOR_1, microstate::XOR_1, microstate::OR_1, microstate::OR_1, microstate::OR_1, microstate::OR_1, + microstate::SUBC_1, microstate::SUBC_1, microstate::SUBC_1, microstate::SUBC_1, microstate::SUBD_1, microstate::SUBD_1, microstate::SUBD_1, microstate::SUBD_1, + microstate::ADDC_1, microstate::ADDC_1, microstate::ADDC_1, microstate::ADDC_1, microstate::ADDD_1, microstate::ADDD_1, microstate::ADDD_1, microstate::ADDD_1, + microstate::BR80_1, microstate::BR80_1, microstate::BR80_1, microstate::BR80_1, microstate::CMPL_1, microstate::CLRFS_1, microstate::CMPL_1, microstate::CLRFS_1, + microstate::BR88_1, microstate::BR88_1, microstate::BR88_1, microstate::BR88_1, microstate::UNKNOWN, microstate::SETFS_1, microstate::UNKNOWN, microstate::SETFS_1, + microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, + microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, microstate::CALL90_1, + microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, + microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, microstate::BRA0_1, + microstate::UNKNOWN, microstate::POPFS_1, microstate::UNKNOWN, microstate::POPFS_1, microstate::POPB4_1, microstate::POPB5_1, microstate::POPB4_1, microstate::POPB7_1, + microstate::UNKNOWN, microstate::PUSHFS_1, microstate::UNKNOWN, microstate::PUSHFS_1, microstate::PUSHBC_1, microstate::PUSHBD_1, microstate::PUSHBC_1, microstate::PUSHBF_1, + microstate::SUBCL_1, microstate::DIVC1_1, microstate::SUBCL_1, microstate::UNKNOWN, microstate::XCHC4_1, microstate::XCHC5_1, microstate::XCHC4_1, microstate::XCHC5_1, + microstate::ADDCL_1, microstate::MULC9_1, microstate::ADDCL_1, microstate::UNKNOWN, microstate::MOVCC_1, microstate::MOVCD_1, microstate::MOVCC_1, microstate::MOVCD_1, + microstate::CMPD0_1, microstate::CMPD1_1, microstate::CMPD0_1, microstate::CMPD1_1, microstate::XCHD4_1, microstate::XCHD4_1, microstate::UNKNOWN, microstate::XCHD7_1, + microstate::MOVD8_1, microstate::MOVD9_1, microstate::MOVD8_1, microstate::MOVD9_1, microstate::MOVDC_1, microstate::MOVDD_1, microstate::MOVDC_1, microstate::MOVDD_1, + microstate::LOOP_1, microstate::LOOP_1, microstate::LOOP_1, microstate::LOOP_1, microstate::DECE4_1, microstate::INCE5_1, microstate::DECE4_1, microstate::INCE5_1, + microstate::ADDRE8_1, microstate::ADDRE9_1, microstate::ADDRE8_1, microstate::ADDRE9_1, microstate::ADDREC_1, microstate::ADDRED_1, microstate::ADDREC_1, microstate::ADDRED_1, + microstate::CMPBF0_1, microstate::CMPBF1_1, microstate::MOV1_1, microstate::WAIT_1, microstate::RET_1, microstate::RETI_1, microstate::BR_1, microstate::CALL_1, + microstate::CMPBF0_1, microstate::CMPBF1_1, microstate::MOV1_1, microstate::PUSHFB_1, microstate::BRFC_1, microstate::CALLFD_1, microstate::RDTBL_1, microstate::PI_1 +}; + +const u16 mn1880_device::s_input_queue_map[16] = +{ + //FEDCBA9876543210 + 0b0000000000000000, // 0x + 0b1111111111111111, // 1x + 0b1111111111111111, // 2x + 0b1010010101111010, // 3x + 0b1010101010111010, // 4x + 0b1110010011101101, // 5x + 0b1110111011101110, // 6x + 0b1110111011101110, // 7x + 0b0000111101001111, // 8x + 0b0000000000000000, // 9x + 0b1111111111111111, // Ax + 0b0000000000000000, // Bx + 0b1111111010101110, // Cx + 0b0000111100001111, // Dx + 0b1010111100000000, // Ex + 0b0000111101001111 // Fx +}; + +const u8 mn1880_device::s_branch_fs[4] = +{ + 0x00, 0x80, 0x40, 0xc0 +}; + +u8 mn1880_device::cpu_registers::addcz(u8 data1, u8 data2, bool carry, bool holdz) +{ + if ((data1 + data2 + (carry ? 1 : 0)) >= 0x100) + fs |= 0x80; + else + fs &= 0x7f; + data1 += data2 + (carry ? 1 : 0); + if (u8(data1) != 0) + fs &= 0xbf; + else if (!holdz) + fs |= 0x40; + return data1; +} + +u8 mn1880_device::cpu_registers::adddcz(u8 data1, u8 data2, bool carry) +{ + if (((data1 & 0x0f) + (data2 & 0x0f) + (carry ? 1 : 0)) >= 0x0a) + fs |= 0x80; + else + fs &= 0x7f; + data1 = (data1 & 0xf0) | ((data1 + data2 + (carry ? 1 : 0)) & 0x0f); + if (u8(data1 & 0x0f) == 0) + fs |= 0x40; + else + fs &= 0xbf; + return data1; +} + +u8 mn1880_device::cpu_registers::subcz(u8 data1, u8 data2, bool carry, bool holdz) +{ + if (data1 < data2 + (carry ? 1 : 0)) + fs |= 0x80; + else + fs &= 0x7f; + data1 -= data2 + (carry ? 1 : 0); + if (u8(data1) != 0) + fs &= 0xbf; + else if (!holdz) + fs |= 0x40; + return data1; +} + +u8 mn1880_device::cpu_registers::subdcz(u8 data1, u8 data2, bool carry) +{ + if ((data1 & 0x0f) < (data2 & 0x0f) + (carry ? 1 : 0)) + fs |= 0x80; + else + fs &= 0x7f; + data1 = (data1 & 0xf0) | ((data1 - data2 - (carry ? 1 : 0)) & 0x0f); + if (u8(data1 & 0x0f) == 0) + fs |= 0x40; + else + fs &= 0xbf; + return data1; +} + +u8 mn1880_device::cpu_registers::rolc(u8 data) +{ + if (BIT(fs, 7)) + { + if (!BIT(data, 7)) + fs &= 0x7f; + return (data << 1) | 0x01; + } + else + { + if (BIT(data, 7)) + fs |= 0x80; + return data << 1; + } +} + +u8 mn1880_device::cpu_registers::rorc(u8 data) +{ + if (BIT(fs, 7)) + { + if (!BIT(data, 0)) + fs &= 0x7f; + return (data >> 1) | 0x80; + } + else + { + if (BIT(data, 0)) + fs |= 0x80; + return data >> 1; + } +} + +u8 mn1880_device::cpu_registers::asrc(u8 data) +{ + if (BIT(data, 7)) + { + if (!BIT(data, 0)) + fs &= 0x7f; + return (data >> 1) | 0x80; + } + else + { + if (BIT(data, 0)) + fs |= 0x80; + return data >> 1; + } +} + +void mn1880_device::cpu_registers::branch(u16 label) +{ + ip = label; + fs &= 0xe0; +} + +void mn1880_device::swap_cpus() +{ + if ((m_cpum & 0x03) != (m_cpu_select ? 0x01 : 0x02)) + m_cpu_select = !m_cpu_select; +} + +void mn1880_device::next_instruction(u8 input) +{ + cpu_registers &cpu = m_cpu[int(m_cpu_select)]; + if ((cpu.fs & 0x0f) != 0) + cpu.fs = (cpu.fs - 1) | 0x10; + else + { + cpu.fs &= 0xe0; + cpu.irp = cpu.ip++; + cpu.ir = input; + } + + m_ustate = microstate::NEXT; + swap_cpus(); } void mn1880_device::execute_run() { - m_ip = m_cache.read_word(0); + while (m_icount-- > 0) + { + cpu_registers &cpu = m_cpu[int(m_cpu_select)]; + + if (m_ustate == microstate::NEXT && cpu.wait == 0) + { + if (!BIT(cpu.fs, 4)) + debugger_instruction_hook(cpu.irp); + if (!m_output_queued && !BIT(s_input_queue_map[cpu.ir >> 4], cpu.ir & 0x0f)) + m_ustate = s_decode_map[cpu.ir]; + } + + u8 input = m_cache.read_byte(cpu.ip); + switch (m_ustate) + { + case microstate::NEXT: + if (cpu.wait != 0) + { + --cpu.wait; + swap_cpus(); + } + else + { + if (m_output_queued) + { + m_data.write_byte(m_da, m_tmp1 & 0x00ff); + m_output_queued = false; + } + if (BIT(s_input_queue_map[cpu.ir >> 4], cpu.ir & 0x0f)) + { + ++cpu.ip; + m_da = input; + } + m_ustate = s_decode_map[cpu.ir]; + } + break; + + case microstate::NOP_1: + next_instruction(input); + break; + + case microstate::REP_1: + cpu.fs = (cpu.fs & 0xe0) | (cpu.ir & 0x0f); + cpu.irp = cpu.ip++; + cpu.ir = input; + m_ustate = microstate::NEXT; + break; + + case microstate::CLRSET_1: + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal + if (BIT(cpu.ir, 3)) + m_tmp1 |= 1 << (cpu.ir & 0x07); + else + m_tmp1 &= ~(1 << (cpu.ir & 0x07)); + m_ustate = microstate::MOV56_2; + break; + + case microstate::T1B_1: + ++cpu.ip; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_tmp1 = m_data.read_byte(m_da) & (1 << (cpu.ir & 0x07)); + m_tmp2 = cpu.ip + s8(input); + m_ustate = microstate::CMPBF1_3; + break; + + case microstate::MOVL31_1: + ++cpu.ip; + m_tmp1 = (m_da & 0xff) | u16(input) << 8; + m_ustate = microstate::MOVL31_2; + break; + + case microstate::MOVL31_2: + if (BIT(cpu.ir, 1)) + cpu.yp = m_tmp1; + else + cpu.xp = m_tmp1; + next_instruction(input); + break; + + case microstate::MOVL34_1: + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + m_ustate = microstate::MOVL34_2; + break; + + case microstate::MOVL34_2: + m_tmp1 = m_data.read_byte(m_da); + m_tmp2 = m_da + 1; + m_da = cpu.xp; + m_ustate = microstate::MOVL34_3; + break; + + case microstate::MOVL34_3: + m_data.write_byte(m_da, m_tmp1); + m_da = m_tmp2; + ++cpu.xp; + m_ustate = microstate::MOVL34_4; + break; + + case microstate::MOVL34_4: + m_tmp1 = m_data.read_byte(m_da); + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MOVL35_1: + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_tmp2 = m_da; + m_da = cpu.yp; + m_ustate = microstate::MOVL35_2; + break; + + case microstate::MOVL35_2: + m_tmp1 = m_data.read_byte(m_da); + m_da = m_tmp2; + ++cpu.yp; + m_ustate = microstate::MOVL35_3; + break; + + case microstate::MOVL35_3: + m_data.write_byte(m_da, m_tmp1); + m_da = cpu.yp; + m_ustate = microstate::MOVL35_4; + break; + + case microstate::MOVL35_4: + m_tmp1 = m_data.read_byte(cpu.yp); + m_da = m_tmp2 + 1; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MOV36_1: + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::MOV36_2; + break; + + case microstate::MOV36_2: + m_tmp1 = m_data.read_byte(m_da); + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MOV37_1: + ++cpu.ip; + m_da = cpu.yp; + m_tmp1 = m_data.read_byte(m_da); + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + m_ustate = microstate::MOV56_2; + break; + + case microstate::MOVL38_1: + if (BIT(cpu.fs, 5)) + m_da |= (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0xff00; + m_ustate = microstate::MOVL38_2; + break; + + case microstate::MOVL38_2: + m_tmp1 = m_data.read_byte(m_da); + ++m_da; + m_ustate = microstate::MOVL38_3; + break; + + case microstate::MOVL38_3: + m_tmp1 |= m_data.read_byte(m_da) << 8; + m_ustate = microstate::MOVL31_2; // TODO: output queue + break; + + case microstate::MOVL39_1: + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0xff00; + m_ustate = microstate::MOVL39_2; + break; + + case microstate::MOVL39_2: + m_tmp1 = BIT(cpu.ir, 1) ? cpu.yp : cpu.xp; + m_ustate = microstate::MOVL39_3; + break; + + case microstate::MOVL39_3: + m_data.write_byte(m_da, m_tmp1 & 0x00ff); + ++m_da; + m_tmp1 >>= 8; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::ASL_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + m_da = cpu.xp; + m_ustate = microstate::ASL_2; + break; + + case microstate::ASL_2: + m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal + cpu.fs = (m_tmp1 & 0x80) | (cpu.fs & 0x7f); + m_tmp1 <<= 1; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::ASR_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + m_da = cpu.xp; + m_ustate = microstate::ASR_2; + break; + + case microstate::ASR_2: + m_tmp1 = cpu.asrc(m_data.read_byte(m_da)); // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::DEC40_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_tmp1 = 0; + if (!BIT(cpu.fs, 4)) + cpu.fs |= 0x80; + m_ustate = microstate::SUBC_3; + break; + + case microstate::NOT_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + m_da = cpu.xp; + m_tmp1 = 0xff; + m_ustate = microstate::XOR_3; + break; + + case microstate::CMPM44_1: + m_tmp1 = m_da & 0x00ff; + m_da = cpu.xp; + m_ustate = microstate::CMPM44_2; + break; + + case microstate::CMPM44_2: + ++cpu.ip; + m_tmp1 &= m_data.read_byte(m_da); + m_tmp2 = input; + m_ustate = microstate::CMPM44_3; + break; + + case microstate::CMPM44_3: + (void)cpu.subcz(m_tmp1, m_tmp2, false, false); + m_ustate = microstate::NOP_1; // TODO: output queue (but what?) + break; + + case microstate::CMPM45_1: + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::CMPM45_2; + break; + + case microstate::CMPM45_2: + ++cpu.ip; + m_tmp1 = input; + m_ustate = microstate::CMPM44_2; + break; + + case microstate::CMPM50_1: + m_tmp2 = m_data.read_byte(cpu.yp); + m_tmp1 = m_da & 0x00ff; + m_da = cpu.xp; + m_ustate = microstate::CMPM50_2; + break; + + case microstate::CMPM50_2: + m_tmp2 &= m_tmp1; + m_ustate = microstate::CMPM50_3; + break; + + case microstate::CMPM50_3: + m_tmp1 &= m_data.read_byte(m_da); + m_ustate = microstate::CMPM44_3; + break; + + case microstate::CMPM52_1: + ++cpu.ip; + m_tmp1 = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + m_ustate = microstate::CMPM50_2; + break; + + case microstate::CMPM52_2: + ++cpu.ip; + m_tmp2 = m_data.read_byte(m_da); + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::CMPM50_2; + break; + + case microstate::XCH4_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::XCH4_2; + break; + + case microstate::XCH4_2: + m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal + m_tmp1 = (m_tmp1 << 4) | (m_tmp1 >> 4); + m_output_queued = true; + next_instruction(input); + break; + + case microstate::INC48_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_tmp1 = 0; + if (!BIT(cpu.fs, 4)) + cpu.fs |= 0x80; + m_ustate = microstate::ADDC_3; + break; + + case microstate::CLR4A_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_tmp1 = 0; + m_ustate = microstate::AND_3; + break; + + case microstate::ROL_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::ROL_2; + break; + + case microstate::ROL_2: + m_tmp1 = cpu.rolc(m_data.read_byte(m_da)); // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::ROR_1: + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::ROR_2; + break; + + case microstate::ROR_2: + m_tmp1 = cpu.rorc(m_data.read_byte(m_da)); // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::DIV51_1: + m_da = cpu.yp; + m_ustate = microstate::DIV51_2; + break; + + case microstate::DIV51_2: + m_tmp2 = m_data.read_byte(m_da); + m_da = cpu.xp; + m_ustate = microstate::DIV51_3; + break; + + case microstate::DIV51_3: + m_tmp1 = m_data.read_byte(m_da); + ++m_da; + m_ustate = microstate::DIV51_4; + break; + + case microstate::DIV51_4: + m_tmp1 |= m_data.read_byte(m_da) << 8; + --m_da; + m_ustate = microstate::DIV51_5; + break; + + case microstate::DIV51_5: + m_ustate = microstate::DIV51_6; + break; + + case microstate::DIV51_6: + m_ustate = microstate::DIV51_7; + break; + + case microstate::DIV51_7: + m_ustate = microstate::DIV51_8; + break; + + case microstate::DIV51_8: + m_ustate = microstate::DIV51_9; + break; + + case microstate::DIV51_9: + if (m_tmp2 == 0) + { + // TBD: documentation doesn't define what happens when the divisor is zero + logerror("%04X: %04X divided by zero (XP = %04X, YP = %04X)\n", cpu.irp, m_tmp1, cpu.xp, cpu.yp); + m_tmp1 = 0xff; + m_tmp2 = 0xff; + } + else + m_tmp1 /= std::exchange(m_tmp2, m_tmp1 % m_tmp2); + m_ustate = microstate::DIV51_10; + break; + + case microstate::DIV51_10: + m_data.write_byte(m_da, m_tmp1 & 0x00ff); + ++m_da; + cpu.xp = m_da; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MOVDA_1: + ++cpu.ip; + m_da = (m_da << 8) | input; + m_ustate = microstate::MOVDA_2; + break; + + case microstate::MOVDA_2: + m_tmp1 = m_data.read_byte(m_da); + ++cpu.ip; + m_da = input; + m_ustate = microstate::MOVDA_3; + break; + + case microstate::MOVDA_3: + ++cpu.ip; + m_da = (m_da << 8) | input; + m_ustate = microstate::MOV56_2; + break; + + case microstate::MOV54_1: + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + m_ustate = microstate::MOV36_2; + break; + + case microstate::MOV55_1: + m_data.write_byte(cpu.xp, m_da & 0x00ff); + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + next_instruction(input); + break; + + case microstate::MOV56_1: + ++cpu.ip; + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + m_tmp1 = m_data.read_byte(m_da); + } + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::MOV56_2; + break; + + case microstate::MOV56_2: + m_data.write_byte(m_da, m_tmp1); + next_instruction(input); + break; + + case microstate::XCH58_1: + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_tmp2 = input; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + m_ustate = microstate::XCH58_2; + break; + + case microstate::XCH58_2: + m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal + if (BIT(cpu.ir, 1)) + { + std::swap(m_da, m_tmp2); + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_tmp2 = cpu.yp; + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::XCH58_3; + break; + + case microstate::XCH58_3: + m_tmp1 |= u16(m_data.read_byte(m_da)) << 8; // TODO: read latch instead of terminal + m_ustate = microstate::XCH58_4; + break; + + case microstate::XCH58_4: + m_data.write_byte(m_da, m_tmp1 & 0x00ff); + m_tmp1 >>= 8; + m_da = m_tmp2; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MUL59_1: + m_da = cpu.xp; + m_ustate = microstate::MUL59_2; + break; - debugger_instruction_hook(m_ip); + case microstate::MUL59_2: + m_tmp1 = m_data.read_byte(m_da); + ++m_da; + m_ustate = microstate::MUL59_3; + break; - m_icount = 0; + case microstate::MUL59_3: + m_tmp2 = m_data.read_byte(m_da); + m_ustate = microstate::MUL59_4; + break; + + case microstate::MUL59_4: + m_ustate = microstate::MUL59_5; + break; + + case microstate::MUL59_5: + m_ustate = microstate::MUL59_6; + break; + + case microstate::MUL59_6: + m_ustate = microstate::MUL59_7; + break; + + case microstate::MUL59_7: + m_tmp1 *= m_tmp2; + m_da = cpu.xp; + ++cpu.xp; + m_ustate = microstate::MUL59_8; + break; + + case microstate::MUL59_8: + m_data.write_byte(m_da, m_tmp1 & 0x00ff); + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + m_tmp1 >>= 8; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MOVL5C_1: + m_da = cpu.yp; + ++cpu.yp; + m_ustate = microstate::MOVL5C_2; + break; + + case microstate::MOVL5C_2: + m_tmp1 = m_data.read_byte(m_da); + m_da = cpu.xp; + ++cpu.xp; + m_ustate = microstate::MOVL5C_3; + break; + + case microstate::MOVL5C_3: + m_data.write_byte(m_da, m_tmp1); + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + m_ustate = microstate::MOVL34_4; + break; + + case microstate::MOVL5D_1: + ++cpu.ip; + m_data.write_byte(cpu.xp, m_da & 0x00ff); + ++cpu.xp; + m_tmp1 = input; + m_ustate = microstate::MOVL5D_2; + break; + + case microstate::MOVL5D_2: + m_data.write_byte(cpu.xp, m_tmp1); + ++cpu.xp; + next_instruction(input); + break; + + case microstate::MOVL5E_1: + ++cpu.ip; + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + m_tmp2 = input; + if (BIT(cpu.fs, 5)) + m_tmp2 |= cpu.xp & 0xff00; + m_ustate = microstate::MOVL5E_2; + break; + + case microstate::MOVL5E_2: + m_tmp1 = m_data.read_byte(m_da); + std::swap(m_da, m_tmp2); + m_ustate = microstate::MOVL5E_3; + break; + + case microstate::MOVL5E_3: + m_data.write_byte(m_da, m_tmp1); + std::swap(m_da, m_tmp2); + ++m_da; + m_ustate = microstate::MOVL5E_4; + break; + + case microstate::MOVL5E_4: + m_tmp1 = m_data.read_byte(m_da); + m_da = m_tmp2 + 1; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MOVL5F_1: + ++cpu.ip; + m_tmp1 = m_da & 0x00ff; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::MOVL5F_2; + break; + + case microstate::MOVL5F_2: + ++cpu.ip; + m_data.write_byte(m_da, m_tmp1); + m_tmp1 = input; + ++m_da; + m_ustate = microstate::MOV56_2; + break; + + case microstate::CMP_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::CMP_2; + break; + + case microstate::CMP_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + m_da = cpu.xp; + } + m_ustate = microstate::CMP_3; + if (!BIT(cpu.fs, 4)) + cpu.fs &= 0x7f; + break; + + case microstate::CMP_3: + (void)cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); + m_ustate = microstate::NOP_1; // TODO: output queue (but just what is the output?) + break; + + case microstate::AND_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::AND_2; + break; + + case microstate::AND_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::AND_3; + break; + + case microstate::AND_3: + m_tmp1 &= m_data.read_byte(m_da); // TODO: read latch instead of terminal + if (u8(m_tmp1) == 0) + cpu.fs |= 0x40; + else + cpu.fs &= 0xbf; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::XOR_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::XOR_2; + break; + + case microstate::XOR_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::XOR_3; + break; + + case microstate::XOR_3: + m_tmp1 ^= m_data.read_byte(m_da); // TODO: read latch instead of terminal + if (u8(m_tmp1) == 0) + cpu.fs |= 0x40; + else + cpu.fs &= 0xbf; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::OR_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::OR_2; + break; + + case microstate::OR_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::OR_3; + break; + + case microstate::OR_3: + m_tmp1 |= m_data.read_byte(m_da); // TODO: read latch instead of terminal + if (u8(m_tmp1) == 0) + cpu.fs |= 0x40; + else + cpu.fs &= 0xbf; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::SUBC_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::SUBC_2; + break; + + case microstate::SUBC_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::SUBC_3; + break; + + case microstate::SUBC_3: + m_tmp1 = cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::SUBD_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::SUBD_2; + break; + + case microstate::SUBD_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::SUBD_3; + break; + + case microstate::SUBD_3: + m_tmp1 = cpu.subdcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7)); // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::SUBD_4: + // Decimal adjust + if (BIT(cpu.fs, 7)) + { + m_tmp1 = (m_tmp1 & 0xf0) | ((m_tmp1 - 0x06) & 0x0f); + if ((m_tmp1 & 0x0f) == 0) + cpu.fs |= 0x40; + else + cpu.fs &= 0xbf; + } + m_output_queued = true; + next_instruction(input); + break; + + case microstate::ADDC_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::ADDC_2; + break; + + case microstate::ADDC_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::ADDC_3; + break; + + case microstate::ADDC_3: + m_tmp1 = cpu.addcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::ADDD_1: + if (BIT(cpu.ir, 0)) + m_tmp1 = m_da & 0x00ff; + else + { + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + { + m_da = cpu.yp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + } + } + m_ustate = microstate::ADDD_2; + break; + + case microstate::ADDD_2: + if (!BIT(cpu.ir, 0)) + m_tmp1 = m_data.read_byte(m_da); + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + { + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + } + m_ustate = microstate::ADDD_3; + break; + + case microstate::ADDD_3: + m_tmp1 = cpu.adddcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7)); // TODO: read latch instead of terminal + m_ustate = microstate::ADDD_4; + break; + + case microstate::ADDD_4: + // Decimal adjust + if (BIT(cpu.fs, 7)) + { + m_tmp1 = (m_tmp1 & 0xf0) | ((m_tmp1 + 0x06) & 0x0f); + if ((m_tmp1 & 0x0f) == 0) + cpu.fs |= 0x40; + else + cpu.fs &= 0xbf; + } + m_output_queued = true; + next_instruction(input); + break; + + case microstate::BR80_1: + if ((cpu.fs & s_branch_fs[cpu.ir & 0x03]) != 0) + cpu.branch(cpu.ip + s8(m_da & 0x00ff)); + m_ustate = microstate::BR_2; + break; + + case microstate::CMPL_1: + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + m_da = cpu.yp; + m_ustate = microstate::CMPL_2; + break; + + case microstate::CMPL_2: + m_tmp1 = m_data.read_byte(m_da); + m_tmp2 = m_da; + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + m_da = cpu.xp; + m_ustate = microstate::CMPL_3; + if (!BIT(cpu.fs, 4)) + cpu.fs &= 0x7f; + break; + + case microstate::CMPL_3: + (void)cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); + std::swap(m_da, m_tmp2); + ++m_da; + m_ustate = microstate::CMPL_4; + break; + + case microstate::CMPL_4: + m_tmp1 = m_data.read_byte(m_da); + if (!BIT(cpu.ir, 1)) + cpu.yp = m_da + 1; + m_da = m_tmp2 + 1; + m_ustate = microstate::CMPL_5; + break; + + case microstate::CMPL_5: + (void)cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), true); + if (!BIT(cpu.ir, 1)) + cpu.xp = m_da + 1; + m_ustate = microstate::NOP_1; // TODO: output queue (if any...) + break; + + case microstate::CLRFS_1: + cpu.fs &= ~(1 << (cpu.ir & 0x07)); + next_instruction(input); + break; + + case microstate::BR88_1: + if ((cpu.fs & s_branch_fs[cpu.ir & 0x03]) == 0) + cpu.branch(cpu.ip + s8(m_da & 0x00ff)); + m_ustate = microstate::BR_2; + break; + + case microstate::SETFS_1: + cpu.fs |= 1 << (cpu.ir & 0x07); + next_instruction(input); + break; + + case microstate::SUBCL_1: + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + m_da = cpu.yp; + m_ustate = microstate::SUBCL_2; + break; + + case microstate::SUBCL_2: + m_tmp1 = m_data.read_byte(m_da); + m_tmp2 = m_da; + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + m_da = cpu.xp; + m_ustate = microstate::SUBCL_3; + break; + + case microstate::SUBCL_3: + m_tmp1 = cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal + ++m_tmp2; + m_ustate = microstate::SUBCL_4; + break; + + case microstate::SUBCL_4: + m_data.write_byte(m_da, m_tmp1); + std::swap(m_da, m_tmp2); + m_ustate = microstate::SUBCL_5; + break; + + case microstate::SUBCL_5: + m_tmp1 = m_data.read_byte(m_da); + if (!BIT(cpu.ir, 1)) + cpu.yp = m_da + 1; + m_da = m_tmp2 + 1; + m_ustate = microstate::SUBCL_6; + break; + + case microstate::SUBCL_6: + m_tmp1 = cpu.subcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), true); // TODO: read latch instead of terminal + if (!BIT(cpu.ir, 1)) + cpu.xp = m_da + 1; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::ADDCL_1: + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + } + else + m_da = cpu.yp; + m_ustate = microstate::ADDCL_2; + break; + + case microstate::ADDCL_2: + m_tmp1 = m_data.read_byte(m_da); + m_tmp2 = m_da; + if (BIT(cpu.ir, 1)) + { + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + } + else + m_da = cpu.xp; + m_ustate = microstate::ADDCL_3; + break; + + case microstate::ADDCL_3: + m_tmp1 = cpu.addcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal + ++m_tmp2; + m_ustate = microstate::ADDCL_4; + break; + + case microstate::ADDCL_4: + m_data.write_byte(m_da, m_tmp1); + std::swap(m_da, m_tmp2); + m_ustate = microstate::ADDCL_5; + break; + + case microstate::ADDCL_5: + m_tmp1 = m_data.read_byte(m_da); + if (!BIT(cpu.ir, 1)) + cpu.yp = m_da + 1; + m_da = m_tmp2 + 1; + m_ustate = microstate::ADDCL_6; + break; + + case microstate::ADDCL_6: + m_tmp1 = cpu.addcz(m_data.read_byte(m_da), m_tmp1, BIT(cpu.fs, 7), true); // TODO: read latch instead of terminal + if (!BIT(cpu.ir, 1)) + cpu.xp = m_da + 1; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::CALL90_1: + ++cpu.ip; + m_tmp1 = (cpu.ip & 0xf000) | u16(cpu.ir & 0x0f) << 8 | input; + m_tmp2 = cpu.ip; + m_da = cpu.sp - 1; + m_ustate = microstate::CALL90_2; + break; + + case microstate::CALL90_2: + m_data.write_byte(m_da, m_tmp2 >> 8); + --m_da; + cpu.branch(m_tmp1); + m_ustate = microstate::CALL_3; + break; + + case microstate::BRA0_1: + cpu.branch((cpu.ip & 0xf000) | u16(cpu.ir & 0x0f) << 8 | (m_da & 0x00ff)); + m_ustate = microstate::BR_2; + break; + + case microstate::POPFS_1: + m_da = cpu.sp; + m_ustate = microstate::POPFS_2; + break; + + case microstate::POPFS_2: + cpu.fs = m_data.read_byte(m_da); // TODO: read latch instead of terminal + cpu.sp = m_da + 1; + m_ustate = microstate::NOP_1; // TODO: output queue (only FS?) + break; + + case microstate::POPB4_1: + m_da = cpu.sp; + m_ustate = microstate::POPB4_2; + break; + + case microstate::POPB4_2: + (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) = m_data.read_byte(m_da); + ++m_da; + m_ustate = microstate::POPB4_3; + break; + + case microstate::POPB4_3: + (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) |= m_data.read_byte(m_da) << 8; + cpu.sp = m_da + 1; + m_ustate = microstate::NOP_1; // TODO: output queue (only XP/YP?) + break; + + case microstate::POPB5_1: + ++cpu.ip; + m_da = cpu.sp; + m_tmp2 = input; + if (BIT(cpu.fs, 5)) + m_tmp2 |= cpu.xp & 0xff00; + m_ustate = microstate::POPB5_2; + break; + + case microstate::POPB5_2: + m_tmp1 = m_data.read_byte(m_da); // TODO: read latch instead of terminal + cpu.sp = m_da + 1; + m_da = m_tmp2; + m_output_queued = true; + next_instruction(input); + break; + + case microstate::POPB7_1: + m_da = cpu.sp; + m_tmp2 = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + m_ustate = microstate::POPB5_2; + break; + + case microstate::PUSHFS_1: + m_da = cpu.sp - 1; + m_tmp1 = cpu.fs; + m_ustate = microstate::PUSHBC_3; + break; + + case microstate::PUSHBC_1: + m_da = cpu.sp - 1; + m_tmp1 = BIT(cpu.ir, 1) ? cpu.yp : cpu.xp; + m_ustate = microstate::PUSHBC_2; + break; + + case microstate::PUSHBC_2: + m_data.write_byte(m_da, m_tmp1 >> 8); + --m_da; + m_ustate = microstate::PUSHBC_3; + break; + + case microstate::PUSHBC_3: + m_data.write_byte(m_da, m_tmp1 & 0x00ff); + cpu.sp = m_da; + m_ustate = microstate::NOP_1; // TODO: output queue (only SP?) + break; + + case microstate::PUSHBD_1: + ++cpu.ip; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::PUSHBD_2; + break; + + case microstate::PUSHBD_2: + m_tmp1 = m_data.read_byte(m_da); + m_da = cpu.sp - 1; + m_ustate = microstate::PUSHBD_3; + break; + + case microstate::PUSHBD_3: + m_data.write_byte(m_da, m_tmp1 & 0x00ff); + cpu.sp = m_da; + m_ustate = microstate::NOP_1; // TODO: output queue (only SP?) + break; + + case microstate::PUSHBF_1: + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + --cpu.xp; + m_ustate = microstate::PUSHBD_2; + break; + + case microstate::DIVC1_1: + m_tmp2 = m_da & 0x00ff; + m_ustate = microstate::DIVC1_2; + break; + + case microstate::DIVC1_2: + m_da = cpu.xp; + m_ustate = microstate::DIV51_3; + break; + + case microstate::XCHC4_1: + if (BIT(cpu.ir, 1)) + cpu.yp = (cpu.yp >> 8) | (cpu.yp << 8); + else + cpu.xp = (cpu.xp >> 8) | (cpu.xp << 8); + m_ustate = microstate::XCHC4_2; + break; + + case microstate::XCHC4_2: + m_ustate = microstate::NOP_1; // TODO: output queue (only XPl/YPl?) + break; + + case microstate::XCHC5_1: + if (BIT(cpu.fs, 5)) + m_da |= (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0xff00; + m_tmp2 = m_data.read_byte(m_da); // TODO: read latch instead of terminal + m_tmp1 = (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0x00ff; + m_ustate = microstate::XCHC5_2; + break; + + case microstate::XCHC5_2: + m_data.write_byte(m_da, m_tmp1); + setl(BIT(cpu.ir, 1) ? cpu.yp : cpu.xp, m_tmp2); + next_instruction(input); + break; + + case microstate::MOVCC_1: + if (BIT(cpu.ir, 1)) + { + if (BIT(cpu.ir, 5)) + m_da |= cpu.yp & 0xff00; + setl(cpu.yp, m_data.read_byte(m_da)); + } + else + { + if (BIT(cpu.ir, 5)) + m_da |= cpu.xp & 0xff00; + setl(cpu.xp, m_data.read_byte(m_da)); + } + next_instruction(input); + break; + + case microstate::MOVCD_1: + if (BIT(cpu.fs, 5)) + m_da |= (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0xff00; + m_ustate = microstate::MOVCD_2; + break; + + case microstate::MOVCD_2: + setl(BIT(cpu.ir, 1) ? cpu.yp : cpu.xp, m_data.read_byte(m_da)); + next_instruction(input); // TODO: output queue + break; + + case microstate::MULC9_1: + m_tmp2 = m_da & 0x00ff; + m_da = cpu.xp; + m_ustate = microstate::MULC9_2; + break; + + case microstate::MULC9_2: + m_tmp1 = m_data.read_byte(m_da); + m_ustate = microstate::MULC9_3; + break; + + case microstate::MULC9_3: + m_ustate = microstate::MUL59_4; + break; + + case microstate::CMPD0_1: + if (BIT(cpu.fs, 5)) + m_da |= (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0xff00; + m_ustate = microstate::CMPD0_2; + break; + + case microstate::CMPD0_2: + (void)cpu.subcz((BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0x00ff, m_data.read_byte(m_da), false, false); + m_ustate = microstate::CMPD1_2; + break; + + case microstate::CMPD1_1: + (void)cpu.subcz((BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0x00ff, m_da & 0x00ff, false, false); + m_ustate = microstate::CMPD1_2; + break; + + case microstate::CMPD1_2: + next_instruction(input); + break; + + case microstate::XCHD4_1: + m_tmp1 = std::exchange(BIT(cpu.ir, 0) ? cpu.sp : cpu.lp, cpu.xp); + m_ustate = microstate::XCHD4_2; + break; + + case microstate::XCHD4_2: + cpu.xp = m_tmp1; + next_instruction(input); + break; + + case microstate::XCHD7_1: + m_tmp1 = std::exchange(cpu.yp, cpu.xp); + m_ustate = microstate::XCHD4_2; + break; + + case microstate::MOVD8_1: + setl(BIT(cpu.ir, 1) ? cpu.yp : cpu.xp, m_da & 0x00ff); + next_instruction(input); + break; + + case microstate::MOVD9_1: + seth(BIT(cpu.ir, 1) ? cpu.yp : cpu.xp, m_da & 0x00ff); + next_instruction(input); + break; + + case microstate::MOVDC_1: + if (BIT(cpu.ir, 1)) + cpu.yp = cpu.xp; + else + cpu.sp = cpu.xp; + next_instruction(input); + break; + + case microstate::MOVDD_1: + if (BIT(cpu.ir, 1)) + cpu.xp = cpu.yp; + else + cpu.xp = cpu.sp; + next_instruction(input); + break; + + case microstate::LOOP_1: + ++cpu.ip; + if (BIT(cpu.ir, 0)) + { + if (BIT(cpu.ir, 1)) + ++cpu.yp; + else + ++cpu.xp; + } + m_da = cpu.sp; + m_tmp2 = cpu.ip + s8(input); + m_ustate = microstate::LOOP_2; + break; + + case microstate::LOOP_2: + m_tmp1 = m_data.read_byte(m_da) - 1; // TODO: read latch instead of terminal + if (m_tmp1 != 0) + cpu.branch(m_tmp2); + else + cpu.sp = m_da + 1; + m_ustate = microstate::MOV56_2; + break; + + case microstate::DECE4_1: + if (BIT(cpu.ir, 1)) + --cpu.yp; + else + --cpu.xp; + next_instruction(input); + break; + + case microstate::INCE5_1: + if (BIT(cpu.ir, 1)) + ++cpu.yp; + else + ++cpu.xp; + next_instruction(input); + break; + + case microstate::ADDRE8_1: + ++cpu.ip; + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0x00ff; + m_tmp2 = input; + m_ustate = microstate::ADDRE8_2; + break; + + case microstate::ADDRE8_2: + m_tmp1 = m_data.read_byte(m_da); + m_da = m_tmp2; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0x00ff; + m_ustate = microstate::ADDRE8_3; + break; + + case microstate::ADDRE8_3: + if (BIT(cpu.ir, 1)) + setl(cpu.yp, cpu.addcz(cpu.yp & 0x00ff, m_data.read_byte(m_da), false, false)); + else + setl(cpu.xp, cpu.addcz(cpu.xp & 0x00ff, m_data.read_byte(m_da), false, false)); + m_ustate = microstate::NOP_1; // TODO: output queue (XPl only?) + break; + + case microstate::ADDRE9_1: + ++cpu.ip; + m_tmp2 = m_da & 0x00ff; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0x00ff; + m_ustate = microstate::ADDRE9_2; + break; + + case microstate::ADDRE9_2: + m_tmp1 = m_data.read_byte(m_da); + m_ustate = microstate::ADDRE9_3; + break; + + case microstate::ADDRE9_3: + setl(BIT(cpu.ir, 1) ? cpu.yp : cpu.xp, cpu.addcz(m_tmp1, m_tmp2, false, false)); + m_ustate = microstate::NOP_1; // TODO: output queue (XPl/YPl only?) + break; + + case microstate::ADDREC_1: + m_tmp1 = (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0x00ff; + m_tmp2 = (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) >> 8; + m_ustate = microstate::ADDRE9_3; + break; + + case microstate::ADDRED_1: + m_tmp1 = (BIT(cpu.ir, 1) ? cpu.yp : cpu.xp) & 0x00ff; + m_tmp2 = m_da & 0x00ff; + m_ustate = microstate::ADDRE9_3; + break; + + case microstate::CMPBF0_1: + ++cpu.ip; + m_tmp1 = m_data.read_byte(cpu.xp) ^ (m_da & 0x00ff); + m_tmp2 = cpu.ip + s8(input); + m_ustate = microstate::CMPBF1_3; + break; + + case microstate::CMPBF1_1: + ++cpu.ip; + m_tmp1 = m_da & 0x00ff; + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::CMPBF1_2; + break; + + case microstate::CMPBF1_2: + ++cpu.ip; + m_tmp1 ^= m_data.read_byte(m_da); + m_tmp2 = cpu.ip + s8(input); + m_ustate = microstate::CMPBF1_3; + break; + + case microstate::CMPBF1_3: + if (BIT(cpu.ir, 3) ? m_tmp1 == 0 : m_tmp1 != 0) + cpu.branch(m_tmp2); + m_ustate = microstate::BR_2; + break; + + case microstate::MOV1_1: + ++cpu.ip; + if (BIT(cpu.fs, 5)) + m_da |= cpu.yp & 0xff00; + m_tmp2 = input; + m_tmp1 = 1 << (m_tmp2 & 0x07); + m_ustate = microstate::MOV1_2; + break; + + case microstate::MOV1_2: + ++cpu.ip; + m_tmp1 &= m_data.read_byte(m_da); + m_da = input; + if (BIT(cpu.fs, 5)) + m_da |= cpu.xp & 0xff00; + m_ustate = microstate::MOV1_3; + break; + + case microstate::MOV1_3: + if (BIT(cpu.ir, 3) ? m_tmp1 == 0 : m_tmp1 != 0) + m_ustate = microstate::MOV1_4; + else + m_ustate = microstate::MOV1N_4; + m_tmp1 = 1 << ((m_tmp2 >> 4) & 0x07); + break; + + case microstate::MOV1_4: + m_tmp1 |= m_data.read_byte(m_da); // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::MOV1N_4: + m_tmp1 = m_data.read_byte(m_da) & ~m_tmp1; // TODO: read latch instead of terminal + m_output_queued = true; + next_instruction(input); + break; + + case microstate::WAIT_1: + ++cpu.ip; + m_tmp1 = (m_da << 8) | input; + m_ustate = microstate::WAIT_2; + break; + + case microstate::WAIT_2: + cpu.wait = m_tmp1; + next_instruction(input); + break; + + case microstate::RET_1: + m_da = cpu.sp; + m_ustate = microstate::RET_2; + break; + + case microstate::RET_2: + m_tmp1 = m_data.read_byte(m_da); + ++m_da; + m_ustate = microstate::RET_3; + break; + + case microstate::RET_3: + cpu.branch(u16(m_data.read_byte(m_da)) << 8 | m_tmp1); + cpu.sp = m_da + 1; + m_ustate = microstate::BR_2; + break; + + case microstate::RETI_1: + m_da = cpu.sp; + m_ustate = microstate::RETI_2; + break; + + case microstate::RETI_2: + cpu.fs = m_data.read_byte(m_da); + ++m_da; + m_ustate = microstate::RETI_3; + break; + + case microstate::RETI_3: + m_tmp1 = m_data.read_byte(m_da); + ++m_da; + m_ustate = microstate::RETI_4; + break; + + case microstate::RETI_4: + cpu.ip = u16(m_data.read_byte(m_da)) << 8 | m_tmp1; + if ((cpu.fs & 0x1f) != 0) + { + ++m_da; + m_ustate = microstate::RETI_5; + } + else + { + cpu.sp = m_da + 1; + m_ustate = microstate::BR_2; + } + break; + + case microstate::RETI_5: + cpu.ir = m_data.read_byte(m_da); + cpu.sp = m_da + 1; + m_ustate = microstate::NEXT; + swap_cpus(); + break; + + case microstate::BR_1: + cpu.branch((m_da & 0x00ff) << 8 | input); + m_ustate = microstate::BR_2; + break; + + case microstate::BR_2: + next_instruction(input); + break; + + case microstate::CALL_1: + ++cpu.ip; + m_tmp1 = input; + m_tmp2 = cpu.ip + 1; + m_da = cpu.sp - 1; + m_ustate = microstate::CALL_2; + break; + + case microstate::CALL_2: + m_data.write_byte(m_da, m_tmp2 >> 8); + --m_da; + cpu.branch(m_tmp1 << 8 | input); + m_ustate = microstate::CALL_3; + break; + + case microstate::CALL_3: + m_data.write_byte(m_da, m_tmp2 & 0x00ff); + cpu.sp = m_da; + next_instruction(input); + break; + + case microstate::PUSHFB_1: + m_tmp1 = m_da & 0x00ff; + m_da = cpu.sp - 1; + m_ustate = microstate::PUSHBC_3; + break; + + case microstate::BRFC_1: + cpu.branch(cpu.xp); + m_ustate = microstate::BR_2; + break; + + case microstate::CALLFD_1: + m_tmp1 = cpu.xp; + m_tmp2 = cpu.ip; + m_da = cpu.sp - 1; + m_ustate = microstate::CALL90_2; + break; + + case microstate::RDTBL_1: + m_ustate = microstate::RDTBL_2; + break; + + case microstate::RDTBL_2: + m_tmp2 = std::exchange(cpu.ip, cpu.yp); + if ((cpu.fs & 0x1f) != 0) + ++cpu.yp; + m_ustate = microstate::RDTBL_3; + break; + + case microstate::RDTBL_3: + m_tmp1 = input; + m_da = cpu.xp; + if ((cpu.fs & 0x1f) != 0) + ++cpu.xp; + cpu.ip = m_tmp2; + m_ustate = microstate::MOV56_2; + break; + + case microstate::PI_1: + case microstate::UNKNOWN: + logerror("%04X: Unknown or unemulated instruction %02X encountered\n", cpu.irp, cpu.ir); + next_instruction(input); + break; + } + } } void mn1880_device::state_string_export(const device_state_entry &entry, std::string &str) const @@ -103,12 +2299,23 @@ void mn1880_device::state_string_export(const device_state_entry &entry, std::st switch (entry.index()) { case STATE_GENFLAGS: - str = string_format("%c%c%c%c:RC=%-2d", - BIT(m_fs, 7) ? 'C' : '.', // Carry flag - BIT(m_fs, 6) ? 'Z' : '.', // Zero flag - BIT(m_fs, 5) ? 'D' : '.', // Direct flag - BIT(m_fs, 4) ? 'A' : '.', // Auto-repeat flag - m_fs & 0x0f); + { + u8 fs = m_cpu[int(m_cpu_select)].fs; + str = string_format("%c%c%c%c RC=%-2d", + BIT(fs, 7) ? 'C' : '.', // Carry flag + BIT(fs, 6) ? 'Z' : '.', // Zero flag + BIT(fs, 5) ? 'D' : '.', // Direct flag + BIT(fs, 4) ? 'A' : '.', // Auto-repeat flag + fs & 0x0f); + break; + } + + case MN1880_IPA: + str = string_format("%04X%c", m_cpu[0].ip, m_cpu_select ? ' ' : '*'); + break; + + case MN1880_IPB: + str = string_format("%04X%c", m_cpu[1].ip, m_cpu_select ? '*' : ' '); break; } } diff --git a/src/devices/cpu/mn1880/mn1880.h b/src/devices/cpu/mn1880/mn1880.h index e4ab2427f46..c1cb5415e3c 100644 --- a/src/devices/cpu/mn1880/mn1880.h +++ b/src/devices/cpu/mn1880/mn1880.h @@ -12,19 +12,30 @@ public: mn1880_device(const machine_config &config, const char *tag, device_t *owner, u32 clock); enum { - MN1880_IP, MN1880_FS, - MN1880_XP, MN1880_YP, - MN1880_XPL, MN1880_XPH, MN1880_YPL, MN1880_YPH, - MN1880_SP, MN1880_LP + MN1880_IP, MN1880_IPA, MN1880_IPB, + MN1880_IR, MN1880_IRA, MN1880_IRB, + MN1880_FS, MN1880_FSA, MN1880_FSB, + MN1880_XP, MN1880_XPA, MN1880_XPB, + MN1880_YP, MN1880_YPA, MN1880_YPB, + MN1880_XPL, MN1880_XPH, + MN1880_YPL, MN1880_YPH, + MN1880_SP, MN1880_SPA, MN1880_SPB, + MN1880_LP, MN1880_LPA, MN1880_LPB, + MN1880_DIVIDER1, MN1880_DIVIDER2, + MN1880_CPUM }; protected: + mn1880_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock, address_map_constructor data_map); + // device-level overrides virtual void device_start() override; virtual void device_reset() override; // device_execute_interface overrides virtual void execute_run() override; + virtual uint64_t execute_clocks_to_cycles(uint64_t clocks) const noexcept override { return (clocks + 4 - 1) / 4; } + virtual uint64_t execute_cycles_to_clocks(uint64_t cycles) const noexcept override { return (cycles * 4); } // device_disasm_interface overrides virtual std::unique_ptr<util::disasm_interface> create_disassembler() override; @@ -36,6 +47,142 @@ protected: virtual void state_string_export(const device_state_entry &entry, std::string &str) const override; private: + struct cpu_registers + { + cpu_registers() : ip(0), irp(0), ir(0), fs(0), xp(0), yp(0), sp(0), lp(0), wait(0) { } + + u8 addcz(u8 data1, u8 data2, bool carry, bool holdz); + u8 adddcz(u8 data1, u8 data2, bool carry); + u8 subcz(u8 data1, u8 data2, bool carry, bool holdz); + u8 subdcz(u8 data1, u8 data2, bool carry); + u8 rolc(u8 data); + u8 rorc(u8 data); + u8 asrc(u8 data); + void branch(u16 label); + + u16 ip; + u16 irp; + u8 ir; + u8 fs; + u16 xp; + u16 yp; + u16 sp; + u16 lp; + u16 wait; + }; + + enum class microstate : u8 { + NEXT, + NOP_1, + REP_1, + CLRSET_1, + T1B_1, + MOVL31_1, MOVL31_2, + MOVL34_1, MOVL34_2, MOVL34_3, MOVL34_4, + MOVL35_1, MOVL35_2, MOVL35_3, MOVL35_4, + MOV36_1, MOV36_2, + MOV37_1, + MOVL38_1, MOVL38_2, MOVL38_3, + MOVL39_1, MOVL39_2, MOVL39_3, + ASL_1, ASL_2, + ASR_1, ASR_2, + DEC40_1, + NOT_1, + CMPM44_1, CMPM44_2, CMPM44_3, + CMPM45_1, CMPM45_2, + XCH4_1, XCH4_2, + INC48_1, + CLR4A_1, + ROL_1, ROL_2, + ROR_1, ROR_2, + CMPM50_1, CMPM50_2, CMPM50_3, + CMPM52_1, CMPM52_2, + DIV51_1, DIV51_2, DIV51_3, DIV51_4, DIV51_5, DIV51_6, DIV51_7, DIV51_8, DIV51_9, DIV51_10, + MOVDA_1, MOVDA_2, MOVDA_3, + MOV54_1, + MOV55_1, + MOV56_1, MOV56_2, + XCH58_1, XCH58_2, XCH58_3, XCH58_4, + MUL59_1, MUL59_2, MUL59_3, MUL59_4, MUL59_5, MUL59_6, MUL59_7, MUL59_8, + MOVL5C_1, MOVL5C_2, MOVL5C_3, + MOVL5D_1, MOVL5D_2, + MOVL5E_1, MOVL5E_2, MOVL5E_3, MOVL5E_4, + MOVL5F_1, MOVL5F_2, + CMP_1, CMP_2, CMP_3, + AND_1, AND_2, AND_3, + XOR_1, XOR_2, XOR_3, + OR_1, OR_2, OR_3, + SUBC_1, SUBC_2, SUBC_3, + SUBD_1, SUBD_2, SUBD_3, SUBD_4, + ADDC_1, ADDC_2, ADDC_3, + ADDD_1, ADDD_2, ADDD_3, ADDD_4, + BR80_1, BR88_1, + CMPL_1, CMPL_2, CMPL_3, CMPL_4, CMPL_5, + CLRFS_1, SETFS_1, + CALL90_1, CALL90_2, + BRA0_1, + POPFS_1, POPFS_2, + POPB4_1, POPB4_2, POPB4_3, + POPB5_1, POPB5_2, + POPB7_1, + PUSHFS_1, + PUSHBC_1, PUSHBC_2, PUSHBC_3, + PUSHBD_1, PUSHBD_2, PUSHBD_3, + PUSHBF_1, + SUBCL_1, SUBCL_2, SUBCL_3, SUBCL_4, SUBCL_5, SUBCL_6, + DIVC1_1, DIVC1_2, + XCHC4_1, XCHC4_2, + XCHC5_1, XCHC5_2, + ADDCL_1, ADDCL_2, ADDCL_3, ADDCL_4, ADDCL_5, ADDCL_6, + MULC9_1, MULC9_2, MULC9_3, + MOVCC_1, + MOVCD_1, MOVCD_2, + CMPD0_1, CMPD0_2, + CMPD1_1, CMPD1_2, + XCHD4_1, XCHD4_2, + XCHD7_1, + MOVD8_1, + MOVD9_1, + MOVDC_1, + MOVDD_1, + LOOP_1, LOOP_2, + DECE4_1, + INCE5_1, + ADDRE8_1, ADDRE8_2, ADDRE8_3, + ADDRE9_1, ADDRE9_2, ADDRE9_3, + ADDREC_1, + ADDRED_1, + CMPBF0_1, + CMPBF1_1, CMPBF1_2, CMPBF1_3, + MOV1_1, MOV1_2, MOV1_3, MOV1_4, MOV1N_4, + WAIT_1, WAIT_2, + RET_1, RET_2, RET_3, + RETI_1, RETI_2, RETI_3, RETI_4, RETI_5, + BR_1, BR_2, + CALL_1, CALL_2, CALL_3, + PUSHFB_1, + BRFC_1, + CALLFD_1, + RDTBL_1, RDTBL_2, RDTBL_3, + PI_1, + UNKNOWN + }; + + static void setl(u16 &pr, u8 data) { pr = (pr & 0xff00) | data; } + static void seth(u16 &pr, u8 data) { pr = (pr & 0x00ff) | (data << 8); } + + u8 cpum_r(); + void cpum_w(u8 data); + + void internal_data_map(address_map &map); + + void swap_cpus(); + void next_instruction(u8 input); + + static const microstate s_decode_map[256]; + static const u16 s_input_queue_map[16]; + static const u8 s_branch_fs[4]; + // address spaces address_space_config m_program_config; address_space_config m_data_config; @@ -43,12 +190,14 @@ private: memory_access<16, 0, 0, ENDIANNESS_LITTLE>::specific m_data; // internal state - u16 m_ip; - u8 m_fs; - u16 m_xp; - u16 m_yp; - u16 m_sp; - u16 m_lp; + cpu_registers m_cpu[2]; + bool m_cpu_select; + u8 m_cpum; + microstate m_ustate; + u16 m_da; + u16 m_tmp1; + u16 m_tmp2; + bool m_output_queued; s32 m_icount; }; diff --git a/src/mame/drivers/basssta.cpp b/src/mame/drivers/basssta.cpp index e2b043b43e2..c6aa1bea917 100644 --- a/src/mame/drivers/basssta.cpp +++ b/src/mame/drivers/basssta.cpp @@ -8,7 +8,7 @@ #include "emu.h" #include "cpu/mn1880/mn1880.h" -//#include "machine/eeprompar.h" +#include "machine/eeprompar.h" class basssta_state : public driver_device { @@ -44,10 +44,34 @@ void basssta_state::sbasssta_prog(address_map &map) void basssta_state::bassstr_data(address_map &map) { + map(0x0001, 0x0001).noprw(); + map(0x0003, 0x0003).noprw(); + map(0x000f, 0x000f).noprw(); + map(0x0015, 0x0015).noprw(); + map(0x001f, 0x001f).noprw(); + map(0x0060, 0x03cf).ram(); + map(0x8000, 0x87ff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write)); } void basssta_state::sbasssta_data(address_map &map) { + map(0x0000, 0x0003).nopr(); + map(0x0001, 0x0001).nopw(); + map(0x0003, 0x0003).nopw(); + map(0x000f, 0x000f).noprw(); + map(0x0012, 0x0015).noprw(); + map(0x001c, 0x001c).nopr(); + map(0x001e, 0x001e).nopw(); + map(0x001f, 0x001f).noprw(); + map(0x0030, 0x0030).nopw(); + map(0x0032, 0x0032).nopw(); + map(0x0034, 0x0034).nopw(); + map(0x0036, 0x0036).nopw(); + map(0x005d, 0x005d).noprw(); + map(0x0060, 0x07ff).ram(); // TODO: probably internal to CPU + map(0x6000, 0x7fff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write)); + map(0x8001, 0x8004).nopw(); + map(0xc000, 0xc000).nopr(); } @@ -56,9 +80,11 @@ INPUT_PORTS_END void basssta_state::bassstr(machine_config &config) { - MN1880(config, m_maincpu, 8000000); // type and clock unknown (custom silkscreen) + MN1880(config, m_maincpu, 8000000); // type and clock unknown (custom silkscreen; might be MN18P83220) m_maincpu->set_addrmap(AS_PROGRAM, &basssta_state::bassstr_prog); m_maincpu->set_addrmap(AS_DATA, &basssta_state::bassstr_data); + + EEPROM_2816(config, "eeprom"); } void basssta_state::sbasssta(machine_config &config) @@ -67,7 +93,7 @@ void basssta_state::sbasssta(machine_config &config) m_maincpu->set_addrmap(AS_PROGRAM, &basssta_state::sbasssta_prog); m_maincpu->set_addrmap(AS_DATA, &basssta_state::sbasssta_data); - // TODO: Microchip 28C64AF EEPROM + EEPROM_2864(config, "eeprom"); // Microchip 28C64AF-15I/L } ROM_START(bassstr) diff --git a/src/mame/drivers/drumsta.cpp b/src/mame/drivers/drumsta.cpp index 6906ab24d83..8eadf95c9ea 100644 --- a/src/mame/drivers/drumsta.cpp +++ b/src/mame/drivers/drumsta.cpp @@ -9,7 +9,7 @@ #include "emu.h" #include "cpu/adsp2100/adsp2100.h" #include "cpu/mn1880/mn1880.h" -//#include "machine/eeprompar.h" +#include "machine/eeprompar.h" class drumsta_state : public driver_device { @@ -39,6 +39,12 @@ void drumsta_state::drumsta_prog(address_map &map) void drumsta_state::drumsta_data(address_map &map) { + map(0x0001, 0x0001).noprw(); + map(0x0003, 0x0003).noprw(); + map(0x000e, 0x000f).noprw(); + map(0x0015, 0x0015).noprw(); + map(0x0060, 0x031f).ram(); + map(0xb000, 0xb7ff).rw("eeprom", FUNC(eeprom_parallel_28xx_device::read), FUNC(eeprom_parallel_28xx_device::write)); } @@ -53,7 +59,7 @@ void drumsta_state::drumsta(machine_config &config) ADSP2181(config, m_dsp, 16000000).set_disable(); // clock unknown - // TODO: Atmel AT28C64 EEPROM + EEPROM_2864(config, "eeprom"); // Atmel AT28C64 } ROM_START(drumsta) diff --git a/src/mame/drivers/macpci.cpp b/src/mame/drivers/macpci.cpp index 269b4b19568..d68d1cc7511 100644 --- a/src/mame/drivers/macpci.cpp +++ b/src/mame/drivers/macpci.cpp @@ -87,6 +87,29 @@ void macpci_state::cdmcu_mem(address_map &map) map(0x0000, 0xffff).rom().region("cdrom", 0); } +void macpci_state::cdmcu_data(address_map &map) +{ + map(0x0000, 0x0001).noprw(); + map(0x0003, 0x0003).noprw(); + map(0x0004, 0x0008).nopw(); + map(0x0008, 0x0008).nopr(); + map(0x0009, 0x0009).noprw(); + map(0x000f, 0x000f).noprw(); + map(0x0012, 0x0013).ram(); + map(0x0015, 0x0015).noprw(); + map(0x001f, 0x0021).nopw(); + map(0x0031, 0x0031).noprw(); + map(0x0033, 0x0033).nopw(); + map(0x0036, 0x0036).noprw(); + map(0x0060, 0x031f).ram(); + map(0x802a, 0x802a).nopw(); + map(0x802f, 0x8034).nopw(); + map(0x8032, 0x8032).nopr(); + map(0x8035, 0x8035).lr8(NAME([]() { return 0x40; })); + map(0x8037, 0x8037).noprw(); + map(0x8038, 0x8039).nopw(); +} + /* Input ports */ static INPUT_PORTS_START( pippin ) INPUT_PORTS_END @@ -149,6 +172,7 @@ void macpci_state::pippin(machine_config &config) mn1880_device &cdmcu(MN1880(config, "cdmcu", 8388608)); // type and clock unknown cdmcu.set_addrmap(AS_PROGRAM, &macpci_state::cdmcu_mem); + cdmcu.set_addrmap(AS_DATA, &macpci_state::cdmcu_data); } /* ROM definition */ diff --git a/src/mame/includes/macpci.h b/src/mame/includes/macpci.h index 22823aeb53f..bc7e6e53293 100644 --- a/src/mame/includes/macpci.h +++ b/src/mame/includes/macpci.h @@ -146,6 +146,7 @@ private: void init_pippin(); void pippin_mem(address_map &map); void cdmcu_mem(address_map &map); + void cdmcu_data(address_map &map); // wait states for accessing the VIA int m_via_cycles; |