// license:BSD-3-Clause // copyright-holders:AJR /*************************************************************************** Panasonic MN1880 series 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. It seems possible that many of the more complicated instructions are not supposed to be repeatable. * Interrupt behavior, including vectoring, has also been guessed at, though the specific sources of each interrupt remain largely unidentified and completely unemulated. Separate interrupt enable registers have been provided for each CPU, though these seem to share the same internal addresses. Repeated instructions are fully interruptible. Interrupt acknowledgment has been implemented as a special invocation of the PI instruction; however, PI usage is 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. It may also be used to pipeline updates to XP, YP, SP or FS. * 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. * The LP register likely defines some sort of stack limit. This has not been implemented. * 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. * When the debugger is stopped after an instruction, its execution may have loaded the output queue but not emptied it yet. Examining the contents of locations about to be written to may show misleading values. Likewise, PCs at which watchpoint hits occur may be incorrectly reported for writes. * Due to the pipelining of writes, an interrupt may be accepted during an instruction which attempts to disable interrupts by setting the IEMASK bit, which may be cleared instead when control reaches the next sequential instruction from a RETI. This sequencing glitch is documented in the MN187XX23 user's manual, along with a failsafe way of setting IEMASK, and similar workarounds in extant program code suggest it is likewise present in the MN1880 series. * The optional MMU, which expands the memory spaces in certain models which contain neither internal ROM nor RAM, has been emulated only to the extent required by psr500, though it likely has a few other features and quirks. ***************************************************************************/ #include "emu.h" #include "mn1880.h" #include "mn1880d.h" // device type definitions DEFINE_DEVICE_TYPE(MN1880, mn1880_device, "mn1880", "Panasonic MN1880") DEFINE_DEVICE_TYPE(MN18801A, mn18801a_device, "mn18801a", "Panasonic MN18801A") ALLOW_SAVE_TYPE(mn1880_device::microstate) mn1880_device::mn1880_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock, bool has_mmu, address_map_constructor data_map) : cpu_device(mconfig, type, tag, owner, clock) , m_program_config("program", ENDIANNESS_BIG, 8, has_mmu ? 21 : 16, 0, 16, 14) , m_data_config("data", ENDIANNESS_LITTLE, 8, has_mmu ? 21 : 16, 0, 16, 14, data_map) , m_has_mmu(has_mmu) , m_cpum(0) , m_ustate(microstate::UNKNOWN) , m_da(0) , m_tmp1(0) , m_tmp2(0) , m_output_queue_state(0xff) , m_icount(0) , m_if(0) , m_irq(0) , m_mmu_bank{0, 0, 0, 0} , m_mmu_enable(0) { } mn1880_device::mn1880_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : mn1880_device(mconfig, MN1880, tag, owner, clock, false, address_map_constructor(FUNC(mn1880_device::internal_data_map), this)) { } mn18801a_device::mn18801a_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : mn1880_device(mconfig, MN18801A, tag, owner, clock, true, address_map_constructor(FUNC(mn18801a_device::internal_data_map), this)) { } u8 mn1880_device::ie0_r() { return (get_active_cpu().ie & 0x00f) | (m_cpu[0].iemask ? 0x10 : 0) | (m_cpu[1].iemask ? 0x20 : 0); } void mn1880_device::ie0_w(u8 data) { cpu_registers &cpu = output_queued() ? m_cpu[m_output_queue_state] : get_active_cpu(); cpu.ie = (cpu.ie & 0xff0) | (data & 0x0f); m_cpu[0].iemask = BIT(data, 4); m_cpu[1].iemask = BIT(data, 5); } u8 mn1880_device::ie1_r() { return (get_active_cpu().ie & 0xff0) >> 4; } void mn1880_device::ie1_w(u8 data) { cpu_registers &cpu = output_queued() ? m_cpu[m_output_queue_state] : get_active_cpu(); cpu.ie = u16(data) << 4 | (cpu.ie & 0x00f); } u8 mn1880_device::cpum_r() { return m_cpum; } void mn1880_device::cpum_w(u8 data) { m_cpum = (data & 0xef) | (m_cpum & 0x10); } u8 mn1880_device::mmu_bank_r(offs_t offset) { return m_mmu_bank[offset]; } void mn1880_device::mmu_bank_w(offs_t offset, u8 data) { m_mmu_bank[offset] = data & 0x7f; } u8 mn1880_device::mmu_enable_r() { return m_mmu_enable; } void mn1880_device::mmu_enable_w(u8 data) { m_mmu_enable = data & 0xc0; } void mn1880_device::internal_data_map(address_map &map) { map(0x0012, 0x0012).rw(FUNC(mn1880_device::ie0_r), FUNC(mn1880_device::ie0_w)); map(0x0015, 0x0015).rw(FUNC(mn1880_device::ie1_r), FUNC(mn1880_device::ie1_w)); map(0x0016, 0x0016).rw(FUNC(mn1880_device::cpum_r), FUNC(mn1880_device::cpum_w)); if (m_has_mmu) { map(0x0040, 0x0043).rw(FUNC(mn1880_device::mmu_bank_r), FUNC(mn1880_device::mmu_bank_w)); map(0x0044, 0x0044).rw(FUNC(mn1880_device::mmu_enable_r), FUNC(mn1880_device::mmu_enable_w)); } } std::unique_ptr mn1880_device::create_disassembler() { return std::make_unique(); } device_memory_interface::space_config_vector mn1880_device::memory_space_config() const { return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config), std::make_pair(AS_DATA, &m_data_config) }; } void mn1880_device::device_start() { space(AS_PROGRAM).cache(m_cache); space(AS_DATA).specific(m_data); set_icountptr(m_icount); using namespace std::placeholders; state_add(MN1880_IP, "IP", [this] () { return get_active_cpu().ip; }, [this] (u16 data) { get_active_cpu().ip = data; } ).noshow(); state_add(STATE_GENPC, "GENPC", [this] () { return get_active_cpu().ip; }, [this] (u16 data) { get_active_cpu().ip = data; } ).noshow(); state_add(STATE_GENPCBASE, "CURPC", [this] () { return get_active_cpu().irp; }, [this] (u16 data) { get_active_cpu().irp = data; } ).noshow(); state_add(MN1880_IR, "IR", [this] () { return get_active_cpu().ir; }, [this] (u8 data) { get_active_cpu().ir = data; } ).noshow(); state_add(MN1880_FS, "FS", [this] () { return get_active_cpu().fs; }, [this] (u8 data) { get_active_cpu().fs = data; } ).noshow(); state_add(STATE_GENFLAGS, "FLAGS", [this] () { return get_active_cpu().fs; }, [this] (u8 data) { get_active_cpu().fs = data; } ).formatstr("%10s").noshow(); state_add(MN1880_XP, "XP", [this] () { return get_active_cpu().xp; }, [this] (u16 data) { get_active_cpu().xp = data; } ).noshow(); state_add(MN1880_YP, "YP", [this] () { return get_active_cpu().yp; }, [this] (u16 data) { get_active_cpu().yp = data; } ).noshow(); state_add(MN1880_XPL, "XPl", [this] () { return get_active_cpu().xp & 0x00ff; }, [this] (u8 data) { setl(get_active_cpu().xp, data); } ).noshow(); state_add(MN1880_XPH, "XPh", [this] () { return (get_active_cpu().xp & 0xff00 >> 8); }, [this] (u8 data) { seth(get_active_cpu().xp, data); } ).noshow(); state_add(MN1880_YPL, "YPl", [this] () { return get_active_cpu().yp & 0x00ff; }, [this] (u8 data) { setl(get_active_cpu().yp, data); } ).noshow(); state_add(MN1880_YPH, "YPh", [this] () { return (get_active_cpu().yp & 0xff00 >> 8); }, [this] (u8 data) { seth(get_active_cpu().yp, data); } ).noshow(); state_add(MN1880_SP, "SP", [this] () { return get_active_cpu().sp; }, [this] (u16 data) { get_active_cpu().sp = data; } ).noshow(); state_add(MN1880_LP, "LP", [this] () { return get_active_cpu().lp; }, [this] (u16 data) { get_active_cpu().lp = data; } ).noshow(); state_add(MN1880_IE, "IE", [this] () { return get_active_cpu().ie; }, [this] (u16 data) { get_active_cpu().ie = data; } ).mask(0xfff).noshow(); state_add(MN1880_IE, "IEMASK", [this] () { return get_active_cpu().iemask; }, [this] (bool data) { get_active_cpu().iemask = data; } ).noshow(); 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(MN1880_IEA + i, util::string_format("IE%c", 'a' + i).c_str(), m_cpu[i].ie).mask(0xfff); state_add(MN1880_IEMASKA + i, util::string_format("IEMASK%c", 'a' + i).c_str(), m_cpu[i].iemask); state_add_divider(MN1880_DIVIDER1 + i); } state_add(MN1880_IF, "IF", m_if).mask(0xfff); 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(STRUCT_MEMBER(m_cpu, ie)); save_item(STRUCT_MEMBER(m_cpu, iemask)); save_item(NAME(m_if)); save_item(NAME(m_irq)); 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_queue_state)); if (m_has_mmu) { save_item(NAME(m_mmu_bank)); save_item(NAME(m_mmu_enable)); } } void mn1880_device::device_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; cpu.ie = 0; cpu.iemask = true; } 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_if = 0; m_irq = 0; m_cpum = 0x0c; m_ustate = microstate::NEXT; m_output_queue_state = 0xff; m_mmu_enable = 0; } bool mn1880_device::memory_translate(int spacenum, int intention, offs_t &address) { switch (spacenum) { case AS_PROGRAM: address = mmu_psen_translate(address); break; case AS_DATA: address = mmu_data_translate(address); break; } return true; } 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; } // 4-bit decimal add with carry (without adjust) 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; } // 4-bit decimal subtract with carry (without adjust) 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, 0)) fs |= 0x80; else fs &= 0x7f; return (data >> 1) | (data & 0x80); } void mn1880_device::cpu_registers::branch(u16 label) { ip = label; fs &= 0xe0; } void mn1880_device::swap_cpus() { if ((m_cpum & 0x03) != (BIT(m_cpum, 4) ? 0x01 : 0x02)) m_cpum ^= 0x10; } void mn1880_device::next_instruction(u8 input) { cpu_registers &cpu = get_active_cpu(); 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() { while (m_icount-- > 0) { cpu_registers &cpu = get_active_cpu(); if (m_ustate == microstate::NEXT) { m_irq = cpu.iemask ? 0 : cpu.ie & m_if; if (m_irq != 0) { cpu.wait = 0; if (!BIT(cpu.fs, 4)) --cpu.ip; if (!output_queued()) m_ustate = microstate::PI_1; } else if (cpu.wait == 0) { if (!BIT(cpu.fs, 4)) debugger_instruction_hook(cpu.irp); if (!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(mmu_psen_translate(cpu.ip)); switch (m_ustate) { case microstate::NEXT: if (cpu.wait != 0) { --cpu.wait; swap_cpus(); } else { if (output_queued()) { m_data.write_byte(mmu_data_translate(m_da), m_tmp1 & 0x00ff); m_output_queue_state = 0xff; } if (m_irq != 0) m_ustate = microstate::PI_1; else { 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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da)); m_da = cpu.xp; if ((cpu.fs & 0x1f) != 0) ++cpu.xp; set_output_queued(); 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(mmu_data_translate(m_da)); m_da = m_tmp2; ++cpu.yp; m_ustate = microstate::MOVL35_3; break; case microstate::MOVL35_3: m_data.write_byte(mmu_data_translate(m_da), m_tmp1); m_da = cpu.yp; m_ustate = microstate::MOVL35_4; break; case microstate::MOVL35_4: m_tmp1 = m_data.read_byte(mmu_data_translate(m_da)); m_da = m_tmp2 + 1; set_output_queued(); 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(mmu_data_translate(m_da)); m_da = cpu.xp; if ((cpu.fs & 0x1f) != 0) ++cpu.xp; set_output_queued(); next_instruction(input); break; case microstate::MOV37_1: ++cpu.ip; m_da = cpu.yp; m_tmp1 = m_data.read_byte(mmu_data_translate(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(mmu_data_translate(m_da)); ++m_da; m_ustate = microstate::MOVL38_3; break; case microstate::MOVL38_3: m_tmp1 |= m_data.read_byte(mmu_data_translate(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(mmu_data_translate(m_da), m_tmp1 & 0x00ff); ++m_da; m_tmp1 >>= 8; set_output_queued(); 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(mmu_data_translate(m_da)); // TODO: read latch instead of terminal cpu.fs = (m_tmp1 & 0x80) | (cpu.fs & 0x7f); m_tmp1 <<= 1; set_output_queued(); 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(mmu_data_translate(m_da))); // TODO: read latch instead of terminal set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da)); // TODO: read latch instead of terminal m_tmp1 = (m_tmp1 << 4) | (m_tmp1 >> 4); set_output_queued(); 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(mmu_data_translate(m_da))); // TODO: read latch instead of terminal set_output_queued(); 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(mmu_data_translate(m_da))); // TODO: read latch instead of terminal set_output_queued(); 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(mmu_data_translate(m_da)); m_da = cpu.xp; m_ustate = microstate::DIV51_3; break; case microstate::DIV51_3: m_tmp1 = m_data.read_byte(mmu_data_translate(m_da)); ++m_da; m_ustate = microstate::DIV51_4; break; case microstate::DIV51_4: m_tmp1 |= m_data.read_byte(mmu_data_translate(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(mmu_data_translate(m_da), m_tmp1 & 0x00ff); ++m_da; cpu.xp = m_da; set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da))) << 8; // TODO: read latch instead of terminal m_ustate = microstate::XCH58_4; break; case microstate::XCH58_4: m_data.write_byte(mmu_data_translate(m_da), m_tmp1 & 0x00ff); m_tmp1 >>= 8; m_da = m_tmp2; set_output_queued(); next_instruction(input); break; case microstate::MUL59_1: m_da = cpu.xp; m_ustate = microstate::MUL59_2; break; case microstate::MUL59_2: m_tmp1 = m_data.read_byte(mmu_data_translate(m_da)); ++m_da; m_ustate = microstate::MUL59_3; break; case microstate::MUL59_3: m_tmp2 = m_data.read_byte(mmu_data_translate(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(mmu_data_translate(m_da), m_tmp1 & 0x00ff); m_da = cpu.xp; if ((cpu.fs & 0x1f) != 0) ++cpu.xp; m_tmp1 >>= 8; set_output_queued(); 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(mmu_data_translate(m_da)); m_da = cpu.xp; ++cpu.xp; m_ustate = microstate::MOVL5C_3; break; case microstate::MOVL5C_3: m_data.write_byte(mmu_data_translate(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(mmu_data_translate(cpu.xp), m_da & 0x00ff); ++cpu.xp; m_tmp1 = input; m_ustate = microstate::MOVL5D_2; break; case microstate::MOVL5D_2: m_data.write_byte(mmu_data_translate(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(mmu_data_translate(m_da)); std::swap(m_da, m_tmp2); m_ustate = microstate::MOVL5E_3; break; case microstate::MOVL5E_3: m_data.write_byte(mmu_data_translate(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(mmu_data_translate(m_da)); m_da = m_tmp2 + 1; set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da)); // TODO: read latch instead of terminal if (u8(m_tmp1) == 0) cpu.fs |= 0x40; else cpu.fs &= 0xbf; set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(m_da)); // TODO: read latch instead of terminal if (u8(m_tmp1) == 0) cpu.fs |= 0x40; else cpu.fs &= 0xbf; set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(m_da)); // TODO: read latch instead of terminal if (u8(m_tmp1) == 0) cpu.fs |= 0x40; else cpu.fs &= 0xbf; set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(m_da)), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(m_da)), m_tmp1, BIT(cpu.fs, 7)); // TODO: read latch instead of terminal set_output_queued(); 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; } set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(m_da)), m_tmp1, BIT(cpu.fs, 7), BIT(cpu.fs, 4)); // TODO: read latch instead of terminal set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(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; } set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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; set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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; set_output_queued(); 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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da)); // TODO: read latch instead of terminal cpu.sp = m_da + 1; m_da = m_tmp2; set_output_queued(); 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(mmu_data_translate(m_da), m_tmp1 >> 8); --m_da; m_ustate = microstate::PUSHBC_3; break; case microstate::PUSHBC_3: m_data.write_byte(mmu_data_translate(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(mmu_data_translate(m_da)); m_da = cpu.sp - 1; m_ustate = microstate::PUSHBD_3; break; case microstate::PUSHBD_3: m_data.write_byte(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da))); } else { if (BIT(cpu.ir, 5)) m_da |= cpu.xp & 0xff00; setl(cpu.xp, m_data.read_byte(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da)), false, false)); else setl(cpu.xp, cpu.addcz(cpu.xp & 0x00ff, m_data.read_byte(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(m_da)); // TODO: read latch instead of terminal set_output_queued(); next_instruction(input); break; case microstate::MOV1N_4: m_tmp1 = m_data.read_byte(mmu_data_translate(m_da)) & ~m_tmp1; // TODO: read latch instead of terminal set_output_queued(); 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(mmu_data_translate(m_da)); ++m_da; m_ustate = microstate::RET_3; break; case microstate::RET_3: cpu.branch(u16(m_data.read_byte(mmu_data_translate(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(mmu_data_translate(m_da)); ++m_da; m_ustate = microstate::RETI_3; break; case microstate::RETI_3: cpu.iemask = false; m_tmp1 = m_data.read_byte(mmu_data_translate(m_da)); ++m_da; m_ustate = microstate::RETI_4; break; case microstate::RETI_4: cpu.ip = u16(m_data.read_byte(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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(mmu_data_translate(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: m_tmp2 = cpu.ip; if (m_irq != 0) { // IRQ0 (first of four external edge inputs?) has the highest priority (after RESET) unsigned level = 32 - count_leading_zeros_32((m_irq - 1) & ~m_irq); (void)standard_irq_callback(level); cpu.ie &= ~(1 << level); // No separate in-service lockout; handler must re-enable specific interrupt m_if &= ~(1 << level); cpu.iemask = true; cpu.ip = 0x0004 + (level << 1); } else cpu.ip = 0x0002; // PI vector unconfirmed if (BIT(m_cpum, 4)) cpu.ip |= 0x0020; m_da = cpu.sp - 1; if ((cpu.fs & 0x1f) != 0) { m_tmp1 = cpu.ir; m_ustate = microstate::PI_2; } else m_ustate = microstate::PI_3; break; case microstate::PI_2: m_data.write_byte(mmu_data_translate(m_da), m_tmp1); --m_da; m_ustate = microstate::PI_3; break; case microstate::PI_3: m_data.write_byte(mmu_data_translate(m_da), m_tmp2 >> 8); --m_da; m_tmp1 = u16(input) << 8; ++cpu.ip; m_ustate = microstate::PI_4; break; case microstate::PI_4: m_data.write_byte(mmu_data_translate(m_da), m_tmp2 & 0x00ff); --m_da; m_tmp2 = cpu.fs; cpu.branch(m_tmp1 | input); // FS repeat bits are cleared here m_ustate = microstate::CALL_3; break; 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 { switch (entry.index()) { case STATE_GENFLAGS: { u8 fs = get_active_cpu().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, BIT(m_cpum, 4) ? ' ' : '*'); break; case MN1880_IPB: str = string_format("%04X%c", m_cpu[1].ip, BIT(m_cpum, 4) ? '*' : ' '); break; } }