summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/mn1880/mn1880.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/mn1880/mn1880.cpp')
-rw-r--r--src/devices/cpu/mn1880/mn1880.cpp2488
1 files changed, 2441 insertions, 47 deletions
diff --git a/src/devices/cpu/mn1880/mn1880.cpp b/src/devices/cpu/mn1880/mn1880.cpp
index cf837a53d28..32b14a8a640 100644
--- a/src/devices/cpu/mn1880/mn1880.cpp
+++ b/src/devices/cpu/mn1880/mn1880.cpp
@@ -2,9 +2,82 @@
// 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. 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.
+ * In DF mode, MOV (da),(YP) and MOV (XP),(da) apparently need to take
+ the high byte of the direct address from the opposite pointer,
+ despite MN1870 documentation suggesting these use the same pointer.
***************************************************************************/
@@ -14,21 +87,105 @@
// device type definitions
DEFINE_DEVICE_TYPE(MN1880, mn1880_device, "mn1880", "Panasonic MN1880")
+DEFINE_DEVICE_TYPE(MN18801A, mn18801a_device, "mn18801a", "Panasonic MN18801A")
-mn1880_device::mn1880_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
- : cpu_device(mconfig, MN1880, 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)
+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, 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, u32 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, u32 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<util::disasm_interface> mn1880_device::create_disassembler()
{
return std::make_unique<mn1880_disassembler>();
@@ -48,54 +205,2280 @@ 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 get_active_cpu().ip; },
+ [this] (u16 data) { get_active_cpu().ip = data; }
+ ).noshow();
+ state_add<u16>(STATE_GENPC, "GENPC",
+ [this] () { return get_active_cpu().ip; },
+ [this] (u16 data) { get_active_cpu().ip = data; }
+ ).noshow();
+ state_add<u16>(STATE_GENPCBASE, "CURPC",
+ [this] () { return get_active_cpu().irp; },
+ [this] (u16 data) { get_active_cpu().irp = data; }
+ ).noshow();
+ state_add<u8>(MN1880_IR, "IR",
+ [this] () { return get_active_cpu().ir; },
+ [this] (u8 data) { get_active_cpu().ir = data; }
+ ).noshow();
+ state_add<u8>(MN1880_FS, "FS",
+ [this] () { return get_active_cpu().fs; },
+ [this] (u8 data) { get_active_cpu().fs = data; }
+ ).noshow();
+ state_add<u8>(STATE_GENFLAGS, "FLAGS",
+ [this] () { return get_active_cpu().fs; },
+ [this] (u8 data) { get_active_cpu().fs = data; }
+ ).formatstr("%10s").noshow();
+ state_add<u16>(MN1880_XP, "XP",
+ [this] () { return get_active_cpu().xp; },
+ [this] (u16 data) { get_active_cpu().xp = data; }
+ ).noshow();
+ state_add<u16>(MN1880_YP, "YP",
+ [this] () { return get_active_cpu().yp; },
+ [this] (u16 data) { get_active_cpu().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 get_active_cpu().xp & 0x00ff; },
+ [this] (u8 data) { setl(get_active_cpu().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 (get_active_cpu().xp & 0xff00 >> 8); },
+ [this] (u8 data) { seth(get_active_cpu().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 get_active_cpu().yp & 0x00ff; },
+ [this] (u8 data) { setl(get_active_cpu().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 (get_active_cpu().yp & 0xff00 >> 8); },
+ [this] (u8 data) { seth(get_active_cpu().yp, data); }
+ ).noshow();
+ state_add<u16>(MN1880_SP, "SP",
+ [this] () { return get_active_cpu().sp; },
+ [this] (u16 data) { get_active_cpu().sp = data; }
+ ).noshow();
+ state_add<u16>(MN1880_LP, "LP",
+ [this] () { return get_active_cpu().lp; },
+ [this] (u16 data) { get_active_cpu().lp = data; }
).noshow();
- state_add(MN1880_SP, "SP", m_sp);
- state_add(MN1880_LP, "LP", m_lp);
+ state_add<u16>(MN1880_IE, "IE",
+ [this] () { return get_active_cpu().ie; },
+ [this] (u16 data) { get_active_cpu().ie = data; }
+ ).mask(0xfff).noshow();
+ state_add<bool>(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(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));
+ 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()
{
- 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;
+ 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, address_space *&target_space)
+{
+ target_space = &space(spacenum);
+ 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()
{
- m_ip = m_cache.read_word(0);
+ 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.yp & 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.xp & 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;
- debugger_instruction_hook(m_ip);
+ case microstate::DIV51_6:
+ m_ustate = microstate::DIV51_7;
+ break;
- m_icount = 0;
+ 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 & 0xff00;
+ 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 & 0xff00;
+ m_ustate = microstate::ADDRE8_3;
+ break;
+
+ case microstate::ADDRE8_3:
+ if (BIT(cpu.ir, 1))
+ setl(cpu.yp, cpu.addcz(m_tmp1, m_data.read_byte(mmu_data_translate(m_da)), false, false));
+ else
+ setl(cpu.xp, cpu.addcz(m_tmp1, 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) & 0xff00;
+ 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.ip);
+ 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
@@ -103,12 +2486,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 = 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;
}
}