// license:BSD-3-Clause // copyright-holders:Sandro Ronco /****************************************************************************** Hitachi HD61700 cpu core emulation. by Sandro Ronco This CPU core is based on documentations works done by: - Piotr Piatek ( http://www.pisi.com.pl/piotr433/pb1000he.htm ) - BLUE ( http://www.geocities.jp/hd61700lab/ ) The HD61700 has 0x1800 bytes of internal ROM accessed as 16bit with address shift by -1 (mapped from 0x0000 to 0x0c00), the external memory is accessed with a 8bit data bus, this core emulate all memory as 16bit access with address shifted by -1 and in the memory above 0x0c00 only the lower 8bit are used. TODO: - dasm don't decode some mnemonics - CPU fast/slow mode - probably other minor things ******************************************************************************/ #include "emu.h" #include "hd61700.h" #include "hd61700d.h" // internal ROM #define INT_ROM 0x0c00 // 5 bit registers #define REG_SX m_regsir[0] #define REG_SY m_regsir[1] #define REG_SZ m_regsir[2] #define READ_SREG(a) (m_regsir[((a) >> 5) & 0x03] & 0x1f) #define WRITE_SREG(a,d) (m_regsir[((a) >> 5) & 0x03] = (d) & 0x1f) // 8 bit registers #define REG_PE m_reg8bit[0] #define REG_PD m_reg8bit[1] #define REG_IB m_reg8bit[2] #define REG_UA m_reg8bit[3] #define REG_IA m_reg8bit[4] #define REG_IE m_reg8bit[5] #define REG_TM m_reg8bit[7] #define READ_REG8(a) (m_reg8bit[(a) & 0x07]) #define WRITE_REG8(a,d) (m_reg8bit[(a) & 0x07] = d) // 16 bit registers #define REG_IX m_reg16bit[0] #define REG_IY m_reg16bit[1] #define REG_IZ m_reg16bit[2] #define REG_US m_reg16bit[3] #define REG_SS m_reg16bit[4] #define REG_KY m_reg16bit[5] // main registers #define READ_REG(a) (m_regmain[(a) & 0x1f]) #define WRITE_REG(a,d) (m_regmain[(a) & 0x1f] = d) #define COPY_REG(d,s) (m_regmain[(d) & 0x1f] = m_regmain[(s) & 0x1f]) #define REG_GET16(r) (m_regmain[(r) & 0x1f] | (m_regmain[((r) + 1) & 0x1f] << 8)) #define REG_PUT16(r,d) do { m_regmain[(r) & 0x1f] = (uint8_t)(d); m_regmain[((r) + 1) & 0x1f] = (uint8_t)((d) >> 8); } while(0) // opcode #define GET_REG_IDX(a,b) (((a << 2) & 0x04) | ((b >> 5) & 0x03)) #define RESTORE_REG(o,r,pr) r = (o & 0x02) ? r : pr #define COND_WRITE_REG(o,a,d) if (o & 0x08) WRITE_REG(a,d) #define GET_IM3(d) (((d >> 5) & 0x07) + 1) // flags #define SET_FLAG_C m_flags |= FLAG_C #define CLEAR_FLAG_Z m_flags |= FLAG_Z #define CLEAR_FLAG_LZ m_flags |= FLAG_LZ #define CLEAR_FLAG_UZ m_flags |= FLAG_UZ #define CLEAR_FLAGS m_flags &= ~(FLAG_Z | FLAG_C | FLAG_LZ | FLAG_UZ) #define CHECK_FLAG_Z(d) if((d) != 0) CLEAR_FLAG_Z #define CHECK_FLAG_C(d,l) if ((d) > l) SET_FLAG_C #define CHECK_FLAGB_LZ(d) if(((d) & 0x0f) != 0) CLEAR_FLAG_LZ #define CHECK_FLAGB_UZ(d) if(((d) & 0xf0) != 0) CLEAR_FLAG_UZ #define CHECK_FLAGW_LZ(d) if(((d) & 0x0f00) != 0) CLEAR_FLAG_LZ #define CHECK_FLAGW_UZ(d) if(((d) & 0xf000) != 0) CLEAR_FLAG_UZ #define CHECK_FLAGB_UZ_LZ(d) do{ CHECK_FLAGB_LZ(d); CHECK_FLAGB_UZ(d); } while(0) #define CHECK_FLAGW_UZ_LZ(d) do{ CHECK_FLAGW_LZ(d); CHECK_FLAGW_UZ(d); } while(0) //CPU state #define CPU_FAST 0x01 #define CPU_SLP 0x02 /* HD61700 IRQ vector */ static const uint16_t irq_vector[] = { 0x0032, 0x0042, 0x0052, 0x0062, 0x0072 }; //************************************************************************** // HD61700 DEVICE //************************************************************************** DEFINE_DEVICE_TYPE(HD61700, hd61700_cpu_device, "hd61700", "Hitachi HD61700") //------------------------------------------------- // hd61700_cpu_device - constructor //------------------------------------------------- hd61700_cpu_device::hd61700_cpu_device(const machine_config &mconfig, const char *tag, device_t *owner, const XTAL &clock) : cpu_device(mconfig, HD61700, tag, owner, clock) , m_program_config("program", ENDIANNESS_BIG, 16, 18, -1) , m_ppc(0x0000) , m_curpc(0x0000) , m_pc(0) , m_flags(0) , m_lcd_ctrl_cb(*this) , m_lcd_read_cb(*this) , m_lcd_write_cb(*this) , m_kb_read_cb(*this) , m_kb_write_cb(*this) , m_port_read_cb(*this) , m_port_write_cb(*this) { } device_memory_interface::space_config_vector hd61700_cpu_device::memory_space_config() const { return space_config_vector { std::make_pair(AS_PROGRAM, &m_program_config) }; } //------------------------------------------------- // device_start - start up the device //------------------------------------------------- void hd61700_cpu_device::device_start() { m_program = &space(AS_PROGRAM); m_sec_timer = timer_alloc(FUNC(hd61700_cpu_device::second_tick), this); m_sec_timer->adjust(attotime::from_seconds(1), 0, attotime::from_seconds(1)); m_lcd_ctrl_cb.resolve_safe(); m_lcd_read_cb.resolve_safe(0xff); m_lcd_write_cb.resolve_safe(); m_kb_read_cb.resolve_safe(0xff); m_kb_write_cb.resolve_safe(); m_port_read_cb.resolve_safe(0xff); m_port_write_cb.resolve_safe(); // save state save_item(NAME(m_ppc)); save_item(NAME(m_curpc)); save_item(NAME(m_pc)); save_item(NAME(m_flags)); save_item(NAME(m_fetch_addr)); save_item(NAME(m_irq_status)); save_item(NAME(m_state)); save_item(NAME(prev_ua)); save_item(NAME(m_regsir)); save_item(NAME(m_reg8bit)); save_item(NAME(m_reg16bit)); save_item(NAME(m_regmain)); save_item(NAME(m_lines_status)); memset(m_regsir, 0, sizeof(m_regsir)); memset(m_reg8bit, 0, sizeof(m_reg8bit)); memset(m_reg16bit, 0, sizeof(m_reg16bit)); memset(m_regmain, 0, sizeof(m_regmain)); // register state for debugger state_add(HD61700_PC, "pc", m_pc).callimport().callexport().formatstr("%04X"); state_add(HD61700_F, "f", m_flags).callimport().callexport().formatstr("%02X"); state_add(HD61700_SX, "SX", REG_SX).callimport().callexport().formatstr("%02X"); state_add(HD61700_SY, "SY", REG_SY).callimport().callexport().formatstr("%02X"); state_add(HD61700_SZ, "SZ", REG_SZ).callimport().callexport().formatstr("%02X"); state_add(HD61700_PE, "pe", REG_PE).callimport().callexport().formatstr("%02X"); state_add(HD61700_PD, "pd", REG_PD).callimport().callexport().formatstr("%02X"); state_add(HD61700_IB, "ib", REG_IB).callimport().callexport().formatstr("%02X"); state_add(HD61700_UA, "ua", REG_UA).callimport().callexport().formatstr("%02X"); state_add(HD61700_IA, "ia", REG_IA).callimport().callexport().formatstr("%02X"); state_add(HD61700_IE, "ie", REG_IE).callimport().callexport().formatstr("%02X"); state_add(HD61700_TM, "tm", REG_TM).callimport().callexport().formatstr("%02X"); state_add(HD61700_IX, "ix", REG_IX).callimport().callexport().formatstr("%04X"); state_add(HD61700_IY, "iy", REG_IY).callimport().callexport().formatstr("%04X"); state_add(HD61700_IZ, "iz", REG_IZ).callimport().callexport().formatstr("%04X"); state_add(HD61700_US, "us", REG_US).callimport().callexport().formatstr("%04X"); state_add(HD61700_SS, "ss", REG_SS).callimport().callexport().formatstr("%04X"); state_add(HD61700_KY, "ky", REG_KY).callimport().callexport().formatstr("%04X"); for (int ireg=0; ireg<32; ireg++) { state_add(HD61700_MAINREG + ireg, string_format("R%d", ireg).c_str(), m_regmain[ireg]).callimport().callexport().formatstr("%02X"); } state_add(STATE_GENPC, "GENPC", m_curpc).formatstr("%8s").noshow(); state_add(STATE_GENPCBASE, "CURPC", m_ppc).formatstr("%8s").noshow(); state_add(STATE_GENFLAGS, "GENFLAGS", m_flags).mask(0xff).formatstr("%8s").noshow(); // set our instruction counter set_icountptr(m_icount); } //------------------------------------------------- // device_reset - reset up the device //------------------------------------------------- void hd61700_cpu_device::device_reset() { set_pc(0x0000); m_flags = FLAG_SW; m_state = 0; m_irq_status = 0; prev_ua = 0; memset(m_regsir, 0, sizeof(m_regsir)); memset(m_reg8bit, 0, sizeof(m_reg8bit)); memset(m_reg16bit, 0, sizeof(m_reg16bit)); memset(m_regmain, 0, sizeof(m_regmain)); for (auto & elem : m_lines_status) elem = CLEAR_LINE; } TIMER_CALLBACK_MEMBER(hd61700_cpu_device::second_tick) { REG_TM++; if ((REG_TM & 0x3f) == 60) { REG_TM = (REG_TM & 0xc0) + 0x40; if (((REG_IE >> 3) & (1 << HD61700_TIMER_INT))) { REG_IB |= (1 << HD61700_TIMER_INT); if (REG_IB & 0x20) { m_state &= ~CPU_SLP; m_flags |= FLAG_APO; } } } } //------------------------------------------------- // state_import - import state into the device, // after it has been set //------------------------------------------------- void hd61700_cpu_device::state_import(const device_state_entry &entry) { switch (entry.index()) { case HD61700_PC: set_pc(m_pc); break; } } //------------------------------------------------- // state_string_export - export state as a string // for the debugger //------------------------------------------------- void hd61700_cpu_device::state_string_export(const device_state_entry &entry, std::string &str) const { switch (entry.index()) { case STATE_GENFLAGS: str = string_format("%c%c%c%c%c%c", m_flags & FLAG_Z ? '.' : 'Z', m_flags & FLAG_C ? 'C' : '.', m_flags & FLAG_LZ ? '.' : 'L', m_flags & FLAG_UZ ? '.' : 'U', m_flags & FLAG_SW ? 'S' : '.', m_flags & FLAG_APO ? 'A' : '.' ); break; } } //------------------------------------------------- // disassemble - call the disassembly // helper function //------------------------------------------------- std::unique_ptr hd61700_cpu_device::create_disassembler() { return std::make_unique(); } //------------------------------------------------- // check_irqs - check if need interrupts //------------------------------------------------- bool hd61700_cpu_device::check_irqs() { for (int i = 4; i >= 0; i--) { if (BIT(REG_IB, i) && !BIT(m_irq_status, i)) { m_irq_status |= (1 << i); push(REG_SS, (uint8_t)(m_pc >> 8)); push(REG_SS, (uint8_t)m_pc); set_pc(irq_vector[i]); m_icount -= 12; return true; } } return false; } //------------------------------------------------- // execute - execute for the provided number of // cycles //------------------------------------------------- void hd61700_cpu_device::execute_run() { do { m_ppc = m_curpc; debugger_instruction_hook(m_curpc); // verify that CPU is not in sleep if (m_state & CPU_SLP) { m_icount -= 6; } else { check_irqs(); // instruction fetch uint8_t op = read_op(); // execute the instruction switch (op) { case 0x00: //adc case 0x01: //sbc case 0x08: //ad case 0x09: //sb { uint8_t arg = read_op(); uint8_t src = READ_REG(get_sir_im8(arg)); uint16_t res = READ_REG(arg) + ((op & 1) ? -src : +src); COND_WRITE_REG(op, arg, (uint8_t)res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); check_optional_jr(arg); m_icount -= 3; } break; case 0x02: //ld { uint8_t arg = read_op(); COPY_REG(arg, get_sir_im8(arg)); check_optional_jr(arg); m_icount -= 3; } break; case 0x04: //anc case 0x05: //nac case 0x06: //orc case 0x07: //xrc case 0x0c: //an case 0x0d: //na case 0x0e: //or case 0x0f: //xr { uint8_t arg = read_op(); uint8_t res = make_logic(op, READ_REG(arg), READ_REG(get_sir_im8(arg))); COND_WRITE_REG(op, arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); //na(c) and or(c) always set C flag if ((op & 3) == 1 || (op & 3) == 2) SET_FLAG_C; check_optional_jr(arg); m_icount -= 3; } break; case 0x0a: //adb case 0x0b: //sbb { uint8_t arg = read_op(); uint16_t res; if (op & 0x01) res = make_bcd_sub(READ_REG(arg), READ_REG(get_sir_im8(arg))); else res = make_bcd_add(READ_REG(arg), READ_REG(get_sir_im8(arg))); WRITE_REG(arg, res & 0xff); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); check_optional_jr(arg); m_icount -= 3; } break; case 0x10: //st { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); uint16_t offset = REG_GET16(src); mem_writebyte(REG_UA >> 4, offset, READ_REG(arg)); check_optional_jr(arg); m_icount -= 8; } break; case 0x11: //ld { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); uint16_t offset = REG_GET16(src); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, offset)); check_optional_jr(arg); m_icount -= 8; } break; case 0x12: //stl { uint8_t arg = read_op(); m_lcd_write_cb(0, READ_REG(arg)); check_optional_jr(arg); m_icount -= 11; } break; case 0x13: //ldl { uint8_t arg = read_op(); uint8_t res = m_lcd_read_cb(0); WRITE_REG(arg, res); check_optional_jr(arg); m_icount -= 11; } break; case 0x14: //ppo/pfl { uint8_t arg = read_op(); if (arg & 0x40) { m_flags = (m_flags & 0x0f) | (READ_REG(arg) & 0xf0); } else { m_lcd_ctrl_cb(READ_REG(arg)); } check_optional_jr(arg); m_icount -= 3; } break; case 0x15: //psr { uint8_t arg = read_op(); WRITE_SREG(arg, READ_REG(arg)); check_optional_jr(arg); m_icount -= 3; } break; case 0x16: //pst case 0x17: //pst { uint8_t arg = read_op(); uint8_t src = READ_REG(arg); uint8_t idx = GET_REG_IDX(op, arg); switch (idx) { case 0: //PE case 1: //PD WRITE_REG8(idx, src); m_port_write_cb(REG_PD & REG_PE); break; case 2: //IB REG_IB = (REG_IB & 0x1f) | (src & 0xe0); break; case 3: //UA WRITE_REG8(idx, src); break; case 4: //IA m_kb_write_cb(src); WRITE_REG8(idx, src); break; case 5: //IE REG_IB &= 0xe0 | (src >> 3); m_irq_status &= src >> 3; WRITE_REG8(idx, src); break; case 6: //TM case 7: //TM // read-only break; } check_optional_jr(arg); m_icount -= 3; } break; case 0x18: { uint8_t arg = read_op(); uint8_t op1 = (arg >> 5) & 0x03; switch (op1) { case 0x00: //rod case 0x02: //bid { uint8_t src = READ_REG(arg); uint8_t res = src >> 1; if (!(op1 & 0x02)) res = res | ((m_flags & FLAG_C) ? 0x80 : 0x00); WRITE_REG(arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); if (src & 0x01) SET_FLAG_C; } break; case 0x01: //rou case 0x03: //biu { uint8_t src = READ_REG(arg); uint8_t res = src << 1; if (!(op1 & 0x02)) res = res | ((m_flags & FLAG_C) ? 0x01 : 0x00); WRITE_REG(arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); if (src&0x80) SET_FLAG_C; } break; } check_optional_jr(arg); m_icount -= 3; } break; case 0x1a: { uint8_t arg = read_op(); uint8_t op1 = (arg >> 5) & 0x03; switch (op1) { case 0x00: //did case 0x01: //diu { uint8_t res; if (op1 & 0x01) res = READ_REG(arg) << 4; else res = READ_REG(arg) >> 4; WRITE_REG(arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); } break; case 0x02: //byd case 0x03: //byu { WRITE_REG(arg, 0); CLEAR_FLAGS; break; } } check_optional_jr(arg); m_icount -= 3; } break; case 0x1b: //cmp/inv { uint8_t arg = read_op(); uint8_t res = ~READ_REG(arg); if (!(arg & 0x40)) res++; WRITE_REG(arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); if (res || (arg & 0x40)) SET_FLAG_C; check_optional_jr(arg); m_icount -= 3; } break; case 0x1c: //gpo/gfl { uint8_t arg = read_op(); uint8_t src; if (arg & 0x40) { src = m_flags; } else { src = m_port_read_cb(); src &= ~REG_PE; } WRITE_REG(arg, src); check_optional_jr(arg); m_icount -= 3; } break; case 0x1d: //gsr { uint8_t arg = read_op(); WRITE_REG(arg, READ_SREG(arg)); check_optional_jr(arg); m_icount -= 3; } break; case 0x1e: //gst case 0x1f: //gst { uint8_t arg = read_op(); uint8_t idx = GET_REG_IDX(op, arg); WRITE_REG(arg, READ_REG8(idx)); check_optional_jr(arg); m_icount -= 3; } break; case 0x20: //st case 0x22: //sti { uint8_t arg = read_op(); uint16_t prev_ir = REG_IX; REG_IX += get_sign_mreg(arg); mem_writebyte(REG_UA >> 4, REG_IX++, READ_REG(arg)); RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 8; } break; case 0x21: //st case 0x23: //sti { uint8_t arg = read_op(); uint16_t prev_ir = REG_IZ; REG_IZ += get_sign_mreg(arg); mem_writebyte(REG_UA >> 6, REG_IZ++, READ_REG(arg)); RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 8; } break; case 0x24: //std { uint8_t arg = read_op(); REG_IX += get_sign_mreg(arg); mem_writebyte(REG_UA >> 4, REG_IX, READ_REG(arg)); m_icount -= 6; } break; case 0x25: //std { uint8_t arg = read_op(); REG_IZ += get_sign_mreg(arg); mem_writebyte(REG_UA >> 6, REG_IZ, READ_REG(arg)); m_icount -= 6; } break; case 0x26: //phs push(REG_SS, READ_REG(read_op())); m_icount -= 9; break; case 0x27: //phu push(REG_US, READ_REG(read_op())); m_icount -= 9; break; case 0x28: //ld case 0x2a: //ldi { uint8_t arg = read_op(); uint16_t prev_ir = REG_IX; REG_IX += get_sign_mreg(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, REG_IX++)); RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 8; } break; case 0x29: //ld case 0x2b: //ldi { uint8_t arg = read_op(); uint16_t prev_ir = REG_IZ; REG_IZ += get_sign_mreg(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 6, REG_IZ++)); RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 8; } break; case 0x2c: //ldd { uint8_t arg = read_op(); REG_IX += get_sign_mreg(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, REG_IX)); m_icount -= 6; } break; case 0x2d: //ldd { uint8_t arg = read_op(); REG_IZ += get_sign_mreg(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 6, REG_IZ)); m_icount -= 6; } break; case 0x2e: //pps { uint8_t arg = read_op(); WRITE_REG(arg, pop(REG_SS)); m_icount -= 11; } break; case 0x2f: //ppu { uint8_t arg = read_op(); WRITE_REG(arg, pop(REG_US)); m_icount -= 11; } break; case 0x30: //jp z case 0x31: //jp nc case 0x32: //jp lz case 0x33: //jp uz case 0x34: //jp nz case 0x35: //jp c case 0x36: //jp nlz case 0x37: //unconditional jp { uint8_t lsb = read_op(); if (m_pc < INT_ROM) read_op(); uint8_t msb = read_op(); if (check_cond(op)) set_pc((msb << 8) | lsb); m_icount -= 3; } break; case 0x38: //adc case 0x3a: //sbc case 0x3c: //ad case 0x3e: //sb { uint8_t arg = read_op(); uint16_t offset = REG_IX + get_sign_mreg(arg); uint8_t src = mem_readbyte(REG_UA >> 4, offset); uint16_t res = src + ((op & 2) ? -READ_REG(arg) : +READ_REG(arg)) ; if (op & 4) mem_writebyte(REG_UA >> 4, offset, res & 0xff); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); m_icount -= 9; } break; case 0x39: //adc case 0x3b: //sbc case 0x3d: //ad case 0x3f: //sb { uint8_t arg = read_op(); uint16_t offset = REG_IZ + get_sign_mreg(arg); uint8_t src = mem_readbyte(REG_UA >> 6, offset); uint16_t res = src + ((op & 2) ? -READ_REG(arg) : +READ_REG(arg)) ; if (op & 4) mem_writebyte(REG_UA >> 6, offset, res & 0xff); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); m_icount -= 9; } break; case 0x40: //adc case 0x41: //sbc case 0x48: //ad case 0x49: //sb { uint8_t arg = read_op(); uint8_t src = read_op(); uint16_t res = READ_REG(arg) + ((op & 1) ? -src : +src); COND_WRITE_REG(op, arg, (uint8_t)res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); check_optional_jr(arg); m_icount -= 3; } break; case 0x42: //ld { uint8_t arg = read_op(); uint8_t src = read_op(); WRITE_REG(arg, src); check_optional_jr(arg); m_icount -= 3; } break; case 0x44: //anc case 0x45: //nac case 0x46: //orc case 0x47: //xrc case 0x4c: //an case 0x4d: //na case 0x4e: //or case 0x4f: //xr { uint8_t arg = read_op(); uint8_t src = read_op(); uint8_t res = make_logic(op, READ_REG(arg), src); COND_WRITE_REG(op, arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); //na(c) and or(c) always set C flag if ((op & 3) == 1 || (op & 3) == 2) SET_FLAG_C; check_optional_jr(arg); m_icount -= 3; } break; case 0x4a: //adb case 0x4b: //sbb { uint8_t arg = read_op(); uint8_t src = read_op(); uint16_t res; if (op & 0x01) res = make_bcd_sub(READ_REG(arg), src); else res = make_bcd_add(READ_REG(arg), src); WRITE_REG(arg, (uint8_t)res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); check_optional_jr(arg); m_icount -= 3; } break; case 0x50: //st { uint8_t arg = read_op(); uint8_t src = read_op(); uint16_t offset = REG_GET16(READ_SREG(arg)); mem_writebyte(REG_UA >> 4, offset, src); m_icount -= 8; } break; case 0x51: //ld { uint8_t arg = read_op(); uint8_t src = read_op(); WRITE_REG(arg, src); m_icount -= 8; } break; case 0x52: //stl { uint8_t arg = read_op(); m_lcd_write_cb(0, arg); m_icount -= 12; } break; case 0x54: //ppo/pfl { uint8_t arg = read_op(); uint8_t src = read_op(); if (arg & 0x40) { m_flags = (m_flags & 0x0f) | (src & 0xf0); } else { m_lcd_ctrl_cb(src); } m_icount -= 3; } break; case 0x55: //psr { uint8_t arg = read_op(); WRITE_SREG(arg, arg); m_icount -= 3; } break; case 0x56: //pst case 0x57: //pst { uint8_t arg = read_op(); uint8_t src = read_op(); uint8_t idx = GET_REG_IDX(op, arg); switch (idx) { case 0: //PE case 1: //PD WRITE_REG8(idx, src); m_port_write_cb(REG_PD & REG_PE); break; case 2: //IB REG_IB = (REG_IB & 0x1f) | (src & 0xe0); break; case 3: //UA WRITE_REG8(idx, src); break; case 4: //IA m_kb_write_cb(src); WRITE_REG8(idx, src); break; case 5: //IE REG_IB &= 0xe0 | (src >> 3); m_irq_status &= src >> 3; WRITE_REG8(idx, src); break; case 6: //TM case 7: //TM // read-only break; } m_icount -= 3; } break; case 0x58: //bups case 0x59: //bdns { uint8_t arg = read_op(); uint16_t res; for(;;) { uint8_t tmp = mem_readbyte(REG_UA >> 4, REG_IX); mem_writebyte(REG_UA >> 6, REG_IZ, tmp); res = tmp - arg; if (REG_IX == REG_IY || !res) break; REG_IX += ((op & 1) ? -1 : +1); REG_IZ += ((op & 1) ? -1 : +1); m_icount -= 6; } CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); m_icount -= 9; } break; case 0x5c: //sup case 0x5d: //sdn { uint8_t arg = read_op(); uint16_t res; for(;;) { res = mem_readbyte(REG_UA >> 4, REG_IX) - arg; if (REG_IX == REG_IY || !res) break; REG_IX += ((op & 1) ? -1 : +1); m_icount -= 6; } CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); m_icount -= 9; } break; case 0x60: //st case 0x62: //sti { uint8_t arg = read_op(); uint16_t prev_ir = REG_IX; REG_IX += get_sign_im8(arg); mem_writebyte(REG_UA >> 4, REG_IX++, READ_REG(arg)); RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 8; } break; case 0x61: //st case 0x63: //sti { uint8_t arg = read_op(); uint16_t prev_ir = REG_IZ; REG_IZ += get_sign_im8(arg); mem_writebyte(REG_UA >> 6, REG_IZ++, READ_REG(arg)); RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 8; } break; case 0x64: //std { uint8_t arg = read_op(); REG_IX += get_sign_im8(arg); mem_writebyte(REG_UA >> 4, REG_IX, READ_REG(arg)); m_icount -= 6; } break; case 0x65: //std { uint8_t arg = read_op(); REG_IZ += get_sign_im8(arg); mem_writebyte(REG_UA >> 6, REG_IZ, READ_REG(arg)); m_icount -= 6; } break; case 0x68: //ld case 0x6a: //ldi { uint8_t arg = read_op(); uint16_t prev_ir = REG_IX; REG_IX += get_sign_im8(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, REG_IX++)); RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 8; } break; case 0x69: //ld case 0x6b: //ldi { uint8_t arg = read_op(); uint16_t prev_ir = REG_IZ; REG_IZ += get_sign_im8(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 6, REG_IZ++)); RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 8; } break; case 0x6c: //ldd { uint8_t arg = read_op(); REG_IX += get_sign_im8(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, REG_IX)); m_icount -= 6; } break; case 0x6d: //ldd { uint8_t arg = read_op(); REG_IZ += get_sign_im8(arg); WRITE_REG(arg, mem_readbyte(REG_UA >> 6, REG_IZ)); m_icount -= 6; } break; case 0x70: //cal z case 0x71: //cal nc case 0x72: //cal lz case 0x73: //cal uz case 0x74: //cal nz case 0x75: //cal c case 0x76: //cal nlz case 0x77: //unconditional cal { uint8_t lsb = read_op(); if (m_pc < INT_ROM) read_op(); uint8_t msb = read_op(); if (check_cond(op)) { m_pc--; push(REG_SS, (uint8_t)(m_pc >> 8)); push(REG_SS, (uint8_t)m_pc); set_pc((msb << 8) | lsb); m_icount -= 6; } m_icount -= 3; } break; case 0x78: //adc case 0x7a: //sbc case 0x7c: //ad case 0x7e: //sb { uint8_t arg = read_op(); uint16_t offset = REG_IX + get_sign_im8(arg); uint8_t src = mem_readbyte(REG_UA >> 4, offset); uint16_t res = src + ((op & 2) ? -READ_REG(arg) : +READ_REG(arg)) ; if (op & 4) mem_writebyte(REG_UA >> 4, offset, (uint8_t)res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); m_icount -= 9; } break; case 0x79: //adc case 0x7b: //sbc case 0x7d: //ad case 0x7f: //sb { uint8_t arg = read_op(); uint16_t offset = REG_IZ + get_sign_im8(arg); uint8_t src = mem_readbyte(REG_UA >> 6, offset); uint16_t res = src + ((op & 2) ? -READ_REG(arg) : +READ_REG(arg)) ; if (op & 4) mem_writebyte(REG_UA >> 6, offset, (uint8_t)res); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); m_icount -= 9; } break; case 0x80: //adcw case 0x81: //sbcw case 0x88: //adw case 0x89: //sbw { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); uint32_t res = REG_GET16(arg) + ((op & 0x01) ? -REG_GET16(src) : +REG_GET16(src)); if (op & 0x08) REG_PUT16(arg, (uint16_t)res); CLEAR_FLAGS; CHECK_FLAG_Z((uint16_t)res); CHECK_FLAGW_UZ_LZ(res); CHECK_FLAG_C(res, 0xffff); check_optional_jr(arg); m_icount -= 8; } break; case 0x82: //ldw { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); COPY_REG(arg, src); COPY_REG(arg + 1, src + 1); check_optional_jr(arg); m_icount -= 8; } break; case 0x84: //ancw case 0x85: //nacw case 0x86: //orcw case 0x87: //xrcw case 0x8c: //anw case 0x8d: //naw case 0x8e: //orw case 0x8f: //xrw { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); uint8_t res0 = make_logic(op, READ_REG(arg), READ_REG(src)); COND_WRITE_REG(op, arg, res0); uint8_t res1 = make_logic(op, READ_REG(arg + 1), READ_REG(src + 1)); COND_WRITE_REG(op, arg+1, res1); CLEAR_FLAGS; CHECK_FLAG_Z(res0 || res1); CHECK_FLAGB_UZ_LZ(res1); //na(c) and or(c) always set C flag if ((op & 3) == 1 || (op & 3) == 2) SET_FLAG_C; check_optional_jr(arg); m_icount -= 8; } break; case 0x8a: //adbw case 0x8b: //sbbw { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); uint16_t res0, res1; if (op & 0x01) res0 = make_bcd_sub(READ_REG(arg), READ_REG(src)); else res0 = make_bcd_add(READ_REG(arg), READ_REG(src)); WRITE_REG(arg, (uint8_t)res0); res1 = (res0 > 0xff) ? 1 : 0; if (op & 0x01) res1 = make_bcd_sub(READ_REG(arg + 1), READ_REG(src + 1) + res1); else res1 = make_bcd_add(READ_REG(arg + 1), READ_REG(src + 1) + res1); WRITE_REG(arg + 1, (uint8_t)res1); CLEAR_FLAGS; CHECK_FLAG_Z(res0 || res1); CHECK_FLAGB_UZ_LZ(res1); CHECK_FLAG_C(res1, 0xff); check_optional_jr(arg); m_icount -= 8; } break; case 0x90: //stw { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); uint16_t offset = REG_GET16(src); mem_writebyte(REG_UA >> 4, offset, READ_REG(arg)); mem_writebyte(REG_UA >> 4, offset + 1, READ_REG(arg + 1)); check_optional_jr(arg); m_icount -= 11; } break; case 0x91: //ldw { uint8_t arg = read_op(); uint8_t src = get_sir_im8(arg); uint16_t offset = REG_GET16(src); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, offset)); WRITE_REG(arg + 1, mem_readbyte(REG_UA >> 4, offset + 1)); check_optional_jr(arg); m_icount -= 11; } break; case 0x92: //stlw { uint8_t arg = read_op(); m_lcd_write_cb(0, READ_REG(arg)); m_lcd_write_cb(0, READ_REG(arg + 1)); check_optional_jr(arg); m_icount -= 19; } break; case 0x93: //ldcw { uint8_t arg = read_op(); uint8_t reg0 = m_lcd_read_cb(0); uint8_t reg1 = m_lcd_read_cb(0); WRITE_REG(arg, reg0); WRITE_REG(arg + 1, reg1); check_optional_jr(arg); m_icount -= 19; } break; case 0x96: //pre case 0x97: //pre { uint8_t arg = read_op(); uint8_t idx = GET_REG_IDX(op, arg); if (idx < 5) m_reg16bit[idx] = REG_GET16(arg); check_optional_jr(arg); m_icount -= 8; } break; case 0x98: //rodw { uint8_t arg = read_op(); uint8_t op1 = (arg >> 5) & 0x03; switch (op1) { case 0x00: //rodw case 0x02: //bidw { uint16_t src = REG_GET16(arg - 1); uint16_t res = src >> 1; if (!(op1 & 0x02)) res = res | ((m_flags & FLAG_C) ? 0x8000 : 0x0000); REG_PUT16(arg - 1, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint16_t)res); CHECK_FLAGB_UZ_LZ(res); if (src&0x01) SET_FLAG_C; } break; case 0x01: //rouw case 0x03: //biuw { uint16_t src = REG_GET16(arg); uint16_t res = src << 1; if (!(op1 & 0x02)) res = res | ((m_flags & FLAG_C) ? 0x0001 : 0x0000); REG_PUT16(arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint16_t)res); CHECK_FLAGW_UZ_LZ(res); if (src&0x8000) SET_FLAG_C; } break; } check_optional_jr(arg); m_icount -= 11; } break; case 0x9a: { uint8_t arg = read_op(); uint8_t op1 = (arg >> 5) & 0x03; switch (op1) { case 0x00: //didw { uint16_t src = REG_GET16(arg - 1) >> 4; REG_PUT16(arg - 1, src); CLEAR_FLAGS; CHECK_FLAG_Z((uint16_t)src); CHECK_FLAGB_UZ_LZ(src); } break; case 0x01: //diuw { uint16_t src = REG_GET16(arg) << 4; REG_PUT16(arg, src); CLEAR_FLAGS; CHECK_FLAG_Z((uint16_t)src); CHECK_FLAGW_UZ_LZ(src); } break; case 0x02: //bydw { uint8_t src = READ_REG(arg); WRITE_REG(arg, 0); WRITE_REG(arg - 1, src); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)src); CHECK_FLAGB_UZ_LZ(src); } break; case 0x03: //byuw { uint8_t src = READ_REG(arg); WRITE_REG(arg, 0); WRITE_REG(arg + 1, src); CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)src); CHECK_FLAGB_UZ_LZ(src); } break; } check_optional_jr(arg); m_icount -= 11; } break; case 0x9b: //cmpw/invw { uint8_t arg = read_op(); uint16_t res = ~REG_GET16(arg); if (!(arg & 0x40)) res++; REG_PUT16(arg, res); CLEAR_FLAGS; CHECK_FLAG_Z((uint16_t)res); CHECK_FLAGW_UZ_LZ(res); if (res || (arg & 0x40)) SET_FLAG_C; check_optional_jr(arg); m_icount -= 8; } break; case 0x9c: //gpow/gflw { uint8_t arg = read_op(); uint8_t reg0, reg1; if (arg&0x40) { reg0 = reg1 = m_flags; } else { reg0 = m_port_read_cb() & ~REG_PE; reg1 = m_port_read_cb() & ~REG_PE; } WRITE_REG(arg, reg0); WRITE_REG(arg + 1, reg1); check_optional_jr(arg); m_icount -= 8; } break; case 0x9e: //gre case 0x9f: //gre { uint8_t arg = read_op(); uint8_t idx = GET_REG_IDX(op, arg); uint16_t src; if (idx >= 5) { uint16_t port = m_kb_read_cb(0); src = (REG_KY & 0x0f00) | (port & 0xf0ff); } else src = m_reg16bit[idx]; REG_PUT16(arg, src); check_optional_jr(arg); m_icount -= 8; } break; case 0xa0: //stw case 0xa2: //stiw { uint8_t arg = read_op(); uint16_t prev_ir = REG_IX; uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); mem_writebyte(REG_UA >> 4, REG_IX++, READ_REG(arg)); mem_writebyte(REG_UA >> 4, REG_IX++, READ_REG(arg + 1)); RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 11; } break; case 0xa1: //stw case 0xa3: //stiw { uint8_t arg = read_op(); uint16_t prev_ir = REG_IZ; uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); mem_writebyte(REG_UA >> 6, REG_IZ++, READ_REG(arg)); mem_writebyte(REG_UA >> 6, REG_IZ++, READ_REG(arg + 1)); RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 11; } break; case 0xa4: //stdw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); mem_writebyte(REG_UA >> 4, REG_IX--, READ_REG(arg)); mem_writebyte(REG_UA >> 4, REG_IX, READ_REG(arg - 1)); m_icount -= 9; } break; case 0xa5: //stdw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); mem_writebyte(REG_UA >> 6, REG_IZ--, READ_REG(arg)); mem_writebyte(REG_UA >> 6, REG_IZ, READ_REG(arg - 1)); m_icount -= 9; } break; case 0xa6: //phsw { uint8_t arg = read_op(); push(REG_SS, READ_REG(arg)); push(REG_SS, READ_REG(arg - 1)); m_icount -= 12; } break; case 0xa7: //phuw { uint8_t arg = read_op(); push(REG_US, READ_REG(arg)); push(REG_US, READ_REG(arg - 1)); m_icount -= 12; } break; case 0xa8: //ldw case 0xaa: //ldiw { uint8_t arg = read_op(); uint16_t prev_ir = REG_IX; uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, REG_IX++)); WRITE_REG(arg + 1, mem_readbyte(REG_UA >> 4, REG_IX++)); RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 11; } break; case 0xa9: //ldw case 0xab: //ldiw { uint8_t arg = read_op(); uint16_t prev_ir = REG_IZ; uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); WRITE_REG(arg, mem_readbyte(REG_UA >> 6, REG_IZ++)); WRITE_REG(arg + 1, mem_readbyte(REG_UA >> 6, REG_IZ++)); RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 11; } break; case 0xac: //lddw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); WRITE_REG(arg, mem_readbyte(REG_UA >> 4, REG_IX--)); WRITE_REG(arg - 1, mem_readbyte(REG_UA >> 4, REG_IX)); m_icount -= 9; } break; case 0xad: //lddw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); WRITE_REG(arg, mem_readbyte(REG_UA >> 6, REG_IZ--)); WRITE_REG(arg - 1, mem_readbyte(REG_UA >> 6, REG_IZ)); m_icount -= 9; } break; case 0xae: //ppsw case 0xaf: //ppuw { uint8_t arg = read_op(); WRITE_REG(arg, pop((op & 1) ? REG_US : REG_SS)); WRITE_REG(arg + 1, pop((op & 1) ? REG_US : REG_SS)); m_icount -= 14; } break; case 0xb0: //jr z case 0xb1: //jr nc case 0xb2: //jr lz case 0xb3: //jr uz case 0xb4: //jr nz case 0xb5: //jr c case 0xb6: //jr nlz case 0xb7: //unconditional jr { uint8_t arg = read_op(); uint32_t new_pc = (m_pc - 1) + get_im_7(arg); if (check_cond(op)) set_pc(new_pc); m_icount -= 3; } break; case 0xb8: //adcw case 0xbc: //adw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); uint16_t offset = REG_IX + ((arg & 0x80) ? -ir_inc : +ir_inc); uint16_t src0 = mem_readbyte(REG_UA >> 4, offset) + READ_REG(arg); uint16_t src1 = mem_readbyte(REG_UA >> 4, offset + 1) + READ_REG(arg + 1) + ((src0 > 0xff) ? 1 : 0); if (op&0x04) { mem_writebyte(REG_UA >> 4, offset, (uint8_t)src0); mem_writebyte(REG_UA >> 4, offset + 1, (uint8_t)src1); } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(src1); CHECK_FLAG_Z(src0 || src1); CHECK_FLAG_C(src1, 0xff); m_icount -= 15; } break; case 0xba: //sbcw case 0xbe: //sbw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); uint16_t offset = REG_IX + ((arg & 0x80) ? -ir_inc : +ir_inc); uint16_t src0 = mem_readbyte(REG_UA >> 4, offset) - READ_REG(arg); uint16_t src1 = mem_readbyte(REG_UA >> 4, offset+1) - READ_REG(arg + 1) - ((src0 > 0xff) ? 1 : 0); if (op&0x04) { mem_writebyte(REG_UA >> 4, offset, (uint8_t)src0); mem_writebyte(REG_UA >> 4, offset + 1, (uint8_t)src1); } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(src1); CHECK_FLAG_Z(src0 || src1); CHECK_FLAG_C(src1, 0xff); m_icount -= 15; } break; case 0xb9: //adcw case 0xbd: //adw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); uint16_t offset = REG_IZ + ((arg & 0x80) ? -ir_inc : +ir_inc); uint16_t src0 = mem_readbyte(REG_UA >> 6, offset) + READ_REG(arg); uint16_t src1 = mem_readbyte(REG_UA >> 6, offset + 1) + READ_REG(arg + 1) + ((src0 > 0xff) ? 1 : 0); if (op & 0x04) { mem_writebyte(REG_UA >> 6, offset, (uint8_t)src0); mem_writebyte(REG_UA >> 6, offset + 1, (uint8_t)src1); } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(src1); CHECK_FLAG_Z(src0 || src1); CHECK_FLAG_C(src1, 0xff); m_icount -= 15; } break; case 0xbb: //sbcw case 0xbf: //sbw { uint8_t arg = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg)); uint16_t offset = REG_IZ + ((arg & 0x80) ? -ir_inc : +ir_inc); uint16_t src0 = mem_readbyte(REG_UA >> 6, offset) - READ_REG(arg); uint16_t src1 = mem_readbyte(REG_UA >> 6, offset + 1) - READ_REG(arg + 1) - ((src0 > 0xff) ? 1 : 0); if (op&0x04) { mem_writebyte(REG_UA >> 6, offset, (uint8_t)src0); mem_writebyte(REG_UA >> 6, offset + 1, (uint8_t)src1); } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(src1); CHECK_FLAG_Z(src0 || src1); CHECK_FLAG_C(src1, 0xff); m_icount -= 15; } break; case 0xc0: //adbcm case 0xc1: //sbbcm case 0xc8: //adbm case 0xc9: //sbbm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t dst = arg; uint8_t src = get_sir_im8(arg, arg1); uint16_t res = 0; uint8_t c = 0; uint8_t f = 0; for (int n = GET_IM3(arg1); n > 0; n--) { if (op & 0x01) res = make_bcd_sub(READ_REG(dst), READ_REG(src) + c); else res = make_bcd_add(READ_REG(dst), READ_REG(src) + c); c = (res > 0xff) ? 1 : 0; COND_WRITE_REG(op, dst, (uint8_t)res); f |= (uint8_t)res; dst++; src++; m_icount -= 5; } CLEAR_FLAGS; CHECK_FLAG_Z(f); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); check_optional_jr(arg); m_icount -= 2; } break; case 0xc2: //ldm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t dst = arg; uint8_t src = get_sir_im8(arg, arg1); for (int n = GET_IM3(arg1); n > 0; n--) { COPY_REG(dst++, src++); m_icount -= 5; } check_optional_jr(arg); m_icount -= 2; } break; case 0xc4: //ancm case 0xc5: //nacm case 0xc6: //orcm case 0xc7: //xrcm case 0xcc: //anm case 0xcd: //nam case 0xce: //orm case 0xcf: //xrm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t dst = arg; uint8_t src = get_sir_im8(arg, arg1); uint8_t res = 0; uint8_t f = 0; for (int n = GET_IM3(arg1); n > 0; n--) { res = make_logic(op, READ_REG(dst), READ_REG(src)); COND_WRITE_REG(op, dst, res); f |= res; dst++; src++; m_icount -= 3; } CLEAR_FLAGS; CHECK_FLAG_Z(f); CHECK_FLAGB_UZ_LZ(res); //na(c) and or(c) always set C flag if ((op & 3) == 1 || (op & 3) == 2) SET_FLAG_C; check_optional_jr(arg); } break; case 0xca: //adbm case 0xcb: //sbbm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t dst = arg; uint16_t res = 0; uint8_t src = arg1 & 0x1f; uint8_t f = 0; for (int n = GET_IM3(arg1); n > 0; n--) { if (op & 0x01) res = make_bcd_sub(READ_REG(dst), src); else res = make_bcd_add(READ_REG(dst), src); src = (res > 0xff) ? 1 : 0; COND_WRITE_REG(op, dst, (uint8_t)res); f |= (uint8_t)res; dst++; m_icount -= 5; } CLEAR_FLAGS; CHECK_FLAG_Z(f); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); check_optional_jr(arg); m_icount -= 2; } break; case 0xd0: //stw { uint8_t arg = read_op(); uint16_t offset = REG_GET16(READ_SREG(arg)); mem_writebyte(REG_UA >> 4, offset, read_op()); mem_writebyte(REG_UA >> 4, offset + 1, read_op()); m_icount -= 11; } break; case 0xd1: //ldw { uint8_t arg = read_op(); uint8_t reg0 = read_op(); uint8_t reg1 = read_op(); WRITE_REG(arg, reg0); WRITE_REG(arg + 1, reg1); m_icount -= 11; } break; case 0xd2: //stlm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); for (int n = GET_IM3(arg1); n > 0; n--) { m_lcd_write_cb(0, READ_REG(arg)); arg++; m_icount -= 8; } m_icount -= 3; } break; case 0xd3: //ldlm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); for (int n = GET_IM3(arg1); n > 0; n--) { uint8_t src = m_lcd_read_cb(0); WRITE_REG(arg, src++); m_icount -= 8; } m_icount -= 3; } break; case 0xd6: //pre case 0xd7: //pre { uint8_t arg = read_op(); uint8_t lsb = read_op(); uint8_t msb = read_op(); uint8_t idx = GET_REG_IDX(op, arg); if (idx < 5) m_reg16bit[idx] = (msb << 8) | lsb; m_icount -= 8; } break; case 0xd8: //bup case 0xd9: //bdn for(;;) { uint8_t src = mem_readbyte(REG_UA >> 4, REG_IX); mem_writebyte(REG_UA >> 6, REG_IZ, src); if (REG_IX == REG_IY) break; REG_IX += ((op & 1) ? -1 : +1); REG_IZ += ((op & 1) ? -1 : +1); m_icount -= 6; } m_icount -= 9; break; case 0xda: { uint8_t arg = read_op(); uint8_t op1 = (arg >> 5) & 0x03; switch (op1) { case 0x00: //didm { uint8_t arg1 = read_op(); uint8_t r1 = 0; uint8_t r2 = 0; for (int n = GET_IM3(arg1); n > 0; n--) { r2 = r1; r1 = READ_REG(arg); r2 = (r1 >> 4) | (r2 << 4); WRITE_REG(arg--, r2); m_icount -= 5; } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(r2); CHECK_FLAG_Z(r2); } break; case 0x01: //dium { uint8_t arg1 = read_op(); uint8_t r1 = 0; uint8_t r2 = 0; for (int n = GET_IM3(arg1); n > 0; n--) { r2 = r1; r1 = READ_REG(arg); r2 = (r1 << 4) | (r2 >> 4); WRITE_REG(arg++, r2); m_icount -= 5; } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(r2); CHECK_FLAG_Z(r2); } break; case 0x02: //bydm { uint8_t arg1 = read_op(); uint8_t r1 = 0; uint8_t r2 = 0; uint8_t f = 0; for (int n = GET_IM3(arg1); n > 0; n--) { r2 = r1; r1 = READ_REG(arg); WRITE_REG(arg--, r2); f |= r2; m_icount -= 5; } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(r2); CHECK_FLAG_Z(f); } break; case 0x03: //byum { uint8_t arg1 = read_op(); uint8_t r1 = 0; uint8_t r2 = 0; uint8_t f = 0; for (int n = GET_IM3(arg1); n > 0; n--) { r2 = r1; r1 = READ_REG(arg); WRITE_REG(arg++, r2); f |= r2; m_icount -= 5; } CLEAR_FLAGS; CHECK_FLAGB_UZ_LZ(r2); CHECK_FLAG_Z(f); } break; } } break; case 0xdb: //cmpm/invm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t r1 = 0; uint8_t f = 0; uint8_t r2 = BIT(~arg, 6); for (int n = GET_IM3(arg1); n > 0; n--) { r1 = r2 + (~READ_REG(arg)); WRITE_REG(arg++, r1); if (r1) r2 = 0; f |= r1; m_icount -= 5; } CLEAR_FLAGS; CHECK_FLAG_Z(f); CHECK_FLAGB_UZ_LZ(r1); if (f != 0 || BIT(arg, 6)) SET_FLAG_C; } break; case 0xdc: //sup case 0xdd: //sdn { uint8_t arg = read_op(); uint16_t res; for(;;) { res = mem_readbyte(REG_UA >> 4, REG_IX) - READ_REG(arg); if (REG_IX == REG_IY || !res) break; REG_IX += ((op & 1) ? -1 : +1); m_icount -= 6; } CLEAR_FLAGS; CHECK_FLAG_Z((uint8_t)res); CHECK_FLAGB_UZ_LZ(res); CHECK_FLAG_C(res, 0xff); m_icount -= 9; } break; case 0xde: //jp { uint8_t arg = read_op(); set_pc(REG_GET16(arg)); m_icount -= 5; } break; case 0xdf: //jp { uint8_t arg = read_op(); uint16_t offset = REG_GET16(arg); uint8_t lsb = mem_readbyte(REG_UA >> 4, offset); uint8_t msb = mem_readbyte(REG_UA >> 4, offset + 1); set_pc((msb << 8) | lsb); m_icount -= 5; } break; case 0xe0: //stm case 0xe2: //stim { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint16_t prev_ir = REG_IX; uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { mem_writebyte(REG_UA >> 4, REG_IX++, READ_REG(arg++)); m_icount -= 3; } RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 5; } break; case 0xe1: //stm case 0xe3: //stim { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint16_t prev_ir = REG_IZ; uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { mem_writebyte(REG_UA >> 6, REG_IZ++, READ_REG(arg++)); m_icount -= 3; } RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 5; } break; case 0xe4: //stdm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { mem_writebyte(REG_UA >> 4, REG_IX--, READ_REG(arg--)); m_icount -= 3; } REG_IX++;//todo m_icount -= 3; } break; case 0xe5: //stdm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { mem_writebyte(REG_UA >> 6, REG_IZ--, READ_REG(arg--)); m_icount -= 3; } REG_IZ++; m_icount -= 3; } break; case 0xe6: //phsm case 0xe7: //phum { uint8_t arg = read_op(); uint8_t arg1 = read_op(); for (int n = GET_IM3(arg1); n > 0; n--) { push((op & 1) ? REG_US : REG_SS, READ_REG(arg--)); m_icount -= 3; } m_icount -= 3; } break; case 0xe8: //ldm case 0xea: //ldim { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint16_t prev_ir = REG_IX; uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { WRITE_REG(arg++, mem_readbyte(REG_UA >> 4, REG_IX++)); m_icount -= 3; } RESTORE_REG(op, REG_IX, prev_ir); m_icount -= 5; } break; case 0xe9: //ldm case 0xeb: //ldim { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint16_t prev_ir = REG_IZ; uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { WRITE_REG(arg++, mem_readbyte(REG_UA >> 6, REG_IZ++)); m_icount -= 3; } RESTORE_REG(op, REG_IZ, prev_ir); m_icount -= 5; } break; case 0xec: //lddm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IX += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { WRITE_REG(arg--, mem_readbyte(REG_UA >> 4, REG_IX--)); m_icount -= 3; } REG_IX++; m_icount -= 3; } break; case 0xed: //lddm { uint8_t arg = read_op(); uint8_t arg1 = read_op(); uint8_t ir_inc = READ_REG(get_sir_im8(arg, arg1)); REG_IZ += ((arg & 0x80) ? -ir_inc : +ir_inc); for (int n = GET_IM3(arg1); n > 0; n--) { WRITE_REG(arg--, mem_readbyte(REG_UA >> 6, REG_IZ--)); m_icount -= 3; } REG_IZ++; m_icount -= 3; } break; case 0xee: //ppsm case 0xef: //ppum { uint8_t arg = read_op(); uint8_t arg1 = read_op(); for (int n = GET_IM3(arg1); n > 0; n--) { WRITE_REG(arg++, pop((op & 1) ? REG_US : REG_SS)); m_icount -= 3; } m_icount -= 5; } break; case 0xf0: //rtn z case 0xf1: //rtn nc case 0xf2: //rtn lz case 0xf3: //rtn uz case 0xf4: //rtn nz case 0xf5: //rtn c case 0xf6: //rtn nlz case 0xf7: //unconditional rtn { if (check_cond(op)) { uint8_t lsb = pop(REG_SS); uint8_t msb = pop(REG_SS); set_pc(((msb << 8) | lsb) + 1); } m_icount -= 3; } break; case 0xf8: //nop m_icount -= 3; break; case 0xf9: //clt REG_TM = 0; m_icount -= 3; break; case 0xfa: //fst m_state |= CPU_FAST; m_icount -= 3; break; case 0xfb: //slw m_state &= ~CPU_FAST; m_icount -= 3; break; case 0xfd: //rtni { uint8_t lsb = pop(REG_SS); uint8_t msb = pop(REG_SS); set_pc((msb << 8) | lsb); m_icount -= 5; } [[fallthrough]]; case 0xfc: //cani for (uint8_t arg = 0x10; arg > 0; arg >>= 1) { if (REG_IB & arg) { REG_IB &= ~arg; m_irq_status &= ~arg; break; } } m_icount -= 3; break; case 0xfe: //off set_pc(0); REG_UA = REG_IA = 0; REG_IX = REG_IY = REG_IZ = 0; REG_PE = 0; REG_IE = (REG_IE & 0x0c) | ((REG_IB >> 1) & 0x10); REG_IB &= 0xe3; if (m_flags & FLAG_SW) m_flags |= FLAG_APO; else m_flags &= ~FLAG_APO; m_state |= CPU_SLP; m_irq_status = 0; m_lcd_ctrl_cb(0); m_kb_write_cb(0); m_icount -= 3; break; case 0xff: //trp m_pc--; push(REG_SS, (uint8_t)(m_pc >> 8)); push(REG_SS, (uint8_t)m_pc); set_pc(0x0022); m_icount -= 9; break; case 0x03: case 0x19: case 0x43: case 0x53: case 0x5a: case 0x5b: case 0x5e: case 0x5f: case 0x66: case 0x67: case 0x6e: case 0x6f: case 0x83: case 0x94: case 0x95: case 0x99: case 0x9d: case 0xc3: case 0xd4: case 0xd5: logerror( "%06x: illegal instruction %02x encountered\n", m_pc, op ); break; default: logerror( "%06x: unimplemented instruction %02x encountered\n", m_pc, op ); break; } } //if is in the internal ROM align the pc if (!WORD_ALIGNED(m_fetch_addr) && m_pc < INT_ROM) set_pc((m_fetch_addr + 1) >> 1); m_icount -= 3; } while (m_icount > 0); } //------------------------------------------------- // execute_set_input //------------------------------------------------- void hd61700_cpu_device::execute_set_input(int inputnum, int state) { switch (inputnum) { case INPUT_LINE_RESET: REG_UA = 0; REG_IA = 0; REG_IX = 0; REG_IY = 0; REG_IZ = 0; REG_PE = 0; REG_IE = 0; REG_IB = 0; m_state &= ~(CPU_SLP | CPU_FAST); set_pc(0x0000); break; case HD61700_KEY_INT: //level sensitive line if (BIT(REG_IE >> 3, inputnum) && state != CLEAR_LINE) REG_IB |= (1 << inputnum); break; case HD61700_INT1: //edge sensitive line if (BIT(REG_IE >> 3, inputnum) && m_lines_status[inputnum] != state) REG_IB |= (1 << inputnum); if (m_lines_status[inputnum] == CLEAR_LINE && state != CLEAR_LINE) REG_IE = (REG_IE & 0xfd) | 0x02; //rising edge else if (m_lines_status[inputnum] != CLEAR_LINE && state == CLEAR_LINE) REG_IE = (REG_IE & 0xfd) | 0x00; //falling edge REG_KY = (REG_KY & 0xf7ff) | ((state != CLEAR_LINE) << 11); break; case HD61700_INT2: //level sensitive line if (BIT(REG_IE >> 3, inputnum) && state != CLEAR_LINE) REG_IB |= (1 << inputnum); REG_IE = (REG_IE & 0xfe) | (state != CLEAR_LINE); REG_KY = (REG_KY & 0xfbff) | ((state != CLEAR_LINE) << 10); break; case HD61700_ON_INT: //level sensitive line if (BIT(REG_IE >> 3, inputnum) && state != CLEAR_LINE) REG_IB |= (1 << inputnum); REG_KY = (REG_KY & 0xfdff) | ((state != CLEAR_LINE) << 9); break; case HD61700_SW: //level sensitive line if (state != CLEAR_LINE) { if ((m_state & CPU_SLP) || m_lines_status[HD61700_SW] == CLEAR_LINE) { m_state &= ~(CPU_SLP | CPU_FAST); m_flags &= ~FLAG_APO; set_pc(0x0001); } } m_flags |= (state != CLEAR_LINE) << 3; break; } m_lines_status[inputnum] = state; } //************************************************************************** // INLINE HELPERS //************************************************************************** inline void hd61700_cpu_device::set_pc(int32_t new_pc) { m_curpc = (m_curpc & 0x30000) | new_pc; m_pc = new_pc & 0xffff; m_ppc = m_curpc; m_fetch_addr = new_pc << 1; } inline uint8_t hd61700_cpu_device::read_op() { uint16_t data; uint32_t addr18 = make_18bit_addr(m_irq_status ? 0 : prev_ua, m_pc); if (m_pc <= INT_ROM) { data = m_program->read_word(addr18); if (!(m_fetch_addr & 1)) data >>= 8; } else { if (m_fetch_addr & 1) data = m_program->read_word(addr18 + 1); else data = m_program->read_word(addr18); } m_fetch_addr += (m_pc > INT_ROM) ? 2 : 1; m_pc = m_fetch_addr >> 1; m_curpc = make_18bit_addr(m_irq_status ? 0 : prev_ua, m_pc); prev_ua = REG_UA; return (uint8_t)data; } inline uint8_t hd61700_cpu_device::mem_readbyte(uint8_t segment, uint16_t offset) { return (uint8_t)m_program->read_word(make_18bit_addr(segment, offset)); } inline void hd61700_cpu_device::mem_writebyte(uint8_t segment, uint16_t offset, uint8_t data) { m_program->write_word(make_18bit_addr(segment, offset), data); } inline uint32_t hd61700_cpu_device::make_18bit_addr(uint8_t segment, uint16_t offset) { if (offset >= ((REG_IB>>6) & 0x03) * 0x4000) return (uint32_t)((offset | ((segment & 0x03) << 16)) & 0x3ffff); else return offset; } inline void hd61700_cpu_device::push(uint16_t &offset, uint8_t data) { offset--; mem_writebyte(REG_UA >> 2, offset, data); } inline uint8_t hd61700_cpu_device::pop(uint16_t &offset) { uint8_t data = mem_readbyte(REG_UA >> 2, offset); offset++; return data; } inline int hd61700_cpu_device::check_cond(uint32_t op) { switch (op & 0x07) { case 0x00: // Z set if (!(m_flags & FLAG_Z)) return 1; break; case 0x01: // NC set if (!(m_flags & FLAG_C)) return 1; break; case 0x02: // LZ set if (!(m_flags & FLAG_LZ)) return 1; break; case 0x03: // UZ set if (!(m_flags & FLAG_UZ)) return 1; break; case 0x04: // NZ set if (m_flags & FLAG_Z) return 1; break; case 0x05: // C set if (m_flags & FLAG_C) return 1; break; case 0x06: // NLZ clear if (m_flags & FLAG_LZ) return 1; break; case 0x07: // unconditional return 1; } return 0; } inline uint8_t hd61700_cpu_device::make_logic(uint8_t type, uint8_t d1, uint8_t d2) { switch (type & 3) { case 0: //and return d1 & d2; case 1: //nand return ~(d1 & d2); case 2: //or return d1 | d2; case 3: //xor return d1 ^ d2; default: return 0x00; } } inline void hd61700_cpu_device::check_optional_jr(uint8_t arg) { if (arg & 0x80) { if (m_pc < INT_ROM && WORD_ALIGNED(m_fetch_addr)) read_op(); uint8_t arg1 = read_op(); uint32_t new_pc = m_pc + get_im_7(arg1) - 1; set_pc(new_pc); m_icount -= 3; } } inline uint8_t hd61700_cpu_device::get_sir_im8(uint8_t arg) { if (((arg >> 5) & 0x03) == 0x03) { return read_op() & 0x1f; } else { return READ_SREG(arg); } } inline uint8_t hd61700_cpu_device::get_sir_im8(uint8_t arg, uint8_t arg1) { if (((arg >> 5) & 0x03) == 0x03) { return arg1 & 0x1f; } else { return READ_SREG(arg); } } inline int hd61700_cpu_device::get_sign_mreg(uint8_t arg) { int res = READ_REG(get_sir_im8(arg)); if (arg & 0x80) res = -res; return res; } inline int hd61700_cpu_device::get_sign_im8(uint8_t arg) { int res = read_op(); if (arg & 0x80) res = -res; return res; } inline int hd61700_cpu_device::get_im_7(uint8_t data) { if (data & 0x80) return 0x80 - data; else return data; } inline uint16_t hd61700_cpu_device::make_bcd_sub(uint8_t arg1, uint8_t arg2) { uint32_t ret = (arg1 & 0x0f) - (arg2 & 0x0f); uint8_t carry; if (ret > 0x09) { ret = (ret - 0x06) & 0x0f; carry = 1; } else carry = 0; ret += ((arg1 & 0xf0) - (arg2 & 0xf0) - (carry << 4)); if (ret > 0x9f) { ret = (uint8_t)(ret - 0x60); carry = 1; } else carry = 0; ret -= carry << 8; return ret; } inline uint16_t hd61700_cpu_device::make_bcd_add(uint8_t arg1, uint8_t arg2) { uint32_t ret = (arg1 & 0x0f) + (arg2 & 0x0f); uint8_t carry; if (ret > 0x09) { ret = (ret + 0x06) & 0x0f; carry = 1; } else carry = 0; ret += ((arg1 & 0xf0) + (arg2 & 0xf0) + (carry << 4)); if (ret > 0x9f) { ret = (uint8_t)(ret + 0x60); carry = 1; } else carry = 0; ret += carry << 8; return ret; }