// license:BSD-3-Clause // copyright-holders:Olivier Galibert /*************************************************************************** mcs96.h MCS96, 8098/8398/8798 branch ***************************************************************************/ #include "emu.h" #include "debugger.h" #include "mcs96.h" mcs96_device::mcs96_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, int data_width, const char *shortname, const char *source) : cpu_device(mconfig, type, name, tag, owner, clock, shortname, source), program_config("program", ENDIANNESS_LITTLE, data_width, 16) { } void mcs96_device::device_start() { program = &space(AS_PROGRAM); direct = &program->direct(); m_icountptr = &icount; state_add(STATE_GENPC, "GENPC", PC).noshow(); state_add(STATE_GENPCBASE, "GENPCBASE", PPC).noshow(); state_add(STATE_GENSP, "GENSP", R[0]).noshow(); state_add(STATE_GENFLAGS, "GENFLAGS", PSW).formatstr("%16s").noshow(); state_add(MCS96_PC, "PC", PC); state_add(MCS96_PSW, "PSW", PSW); state_add(MCS96_R, "SP", R[0]); for(int i=1; i<0x74; i++) { char buf[10]; sprintf(buf, "R%02x", i*2+0x18); state_add(MCS96_R+i, buf, R[i]); } memset(R, 0, sizeof(R)); } void mcs96_device::device_reset() { PC = 0x2080; PPC = PC; PSW = 0; pending_irq = 0x00; irq_requested = false; inst_state = STATE_FETCH; } UINT32 mcs96_device::execute_min_cycles() const { return 4; } UINT32 mcs96_device::execute_max_cycles() const { return 33; } UINT32 mcs96_device::execute_input_lines() const { return 1; } void mcs96_device::recompute_bcount(UINT64 event_time) { if(!event_time || event_time >= total_cycles() + icount) { bcount = 0; return; } bcount = total_cycles() + icount - event_time; } void mcs96_device::check_irq() { irq_requested = (PSW & pending_irq) && (PSW & F_I); } void mcs96_device::execute_run() { internal_update(total_cycles()); // if(inst_substate) // do_exec_partial(); while(icount > 0) { while(icount > bcount) { int picount = inst_state >= 0x200 ? -1 : icount; do_exec_full(); if(icount == picount) { fatalerror("Unhandled %x (%04x)\n", inst_state, PPC); } } while(bcount && icount <= bcount) internal_update(total_cycles() + icount - bcount); // if(inst_substate) // do_exec_partial(); } } void mcs96_device::execute_set_input(int inputnum, int state) { switch(inputnum) { case EXINT_LINE: if(state) pending_irq |= 0x80; else pending_irq &= 0x7f; check_irq(); break; } } const address_space_config *mcs96_device::memory_space_config(address_spacenum spacenum) const { return (spacenum == AS_PROGRAM) ? &program_config : NULL; } void mcs96_device::state_import(const device_state_entry &entry) { } void mcs96_device::state_export(const device_state_entry &entry) { } void mcs96_device::state_string_export(const device_state_entry &entry, std::string &str) { switch(entry.index()) { case STATE_GENFLAGS: case MCS96_PSW: strprintf(str, "%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c", PSW & F_Z ? 'Z' : '.', PSW & F_N ? 'N' : '.', PSW & F_V ? 'V' : '.', PSW & F_VT ? 'v' : '.', PSW & F_C ? 'C' : '.', PSW & F_I ? 'I' : '.', PSW & F_ST ? 'S' : '.', PSW & 0x80 ? '7' : '.', PSW & 0x40 ? '6' : '.', PSW & 0x20 ? '5' : '.', PSW & 0x10 ? '4' : '.', PSW & 0x08 ? '3' : '.', PSW & 0x04 ? '2' : '.', PSW & 0x02 ? '1' : '.', PSW & 0x01 ? '0' : '.'); break; } } std::string mcs96_device::regname(UINT8 reg) { char res[32]; switch(reg) { case 0x18: strcpy(res, "sp"); break; case 0x19: strcpy(res, "sph"); break; default: sprintf(res, "%02x", reg); break; } return res; } offs_t mcs96_device::disasm_generic(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options, const disasm_entry *entries) { bool prefix_fe = false; int off = 0; if(oprom[0] == 0xfe && entries[oprom[1]].opcode_fe) { prefix_fe = true; pc++; off++; oprom++; } const disasm_entry &e = entries[oprom[0]]; UINT32 flags = e.flags | DASMFLAG_SUPPORTED; buffer += sprintf(buffer, "%s", prefix_fe ? e.opcode_fe : e.opcode); switch(e.mode) { case DASM_none: flags |= 1; break; case DASM_nop_2: sprintf(buffer, " %02x", oprom[1]); flags |= 2; break; case DASM_rel8: { int delta = oprom[1]; if(delta & 0x80) delta -= 0x100; sprintf(buffer, " %04x", (pc+2+delta) & 0xffff); flags |= 2; break; } case DASM_rel11: { int delta = ((oprom[0] << 8) | oprom[1]) & 0x7ff; if(delta & 0x400) delta -= 0x800; sprintf(buffer, " %04x", (pc+2+delta) & 0xffff); flags |= 2; break; } case DASM_rel16: { int delta = oprom[1] | (oprom[2] << 8); sprintf(buffer, " %04x", (pc+3+delta) & 0xffff); flags |= 3; break; } case DASM_rrel8: { int delta = oprom[2]; if(delta & 0x80) delta -= 0x100; sprintf(buffer, " %s, %04x", regname(oprom[1]).c_str(), (pc+3+delta) & 0xffff); flags |= 3; break; } case DASM_brrel8: { int delta = oprom[2]; if(delta & 0x80) delta -= 0x100; sprintf(buffer, " %d, %s, %04x", oprom[0] & 7, regname(oprom[1]).c_str(), (pc+3+delta) & 0xffff); flags |= 3; break; } case DASM_direct_1: sprintf(buffer, " %s", regname(oprom[1]).c_str()); flags |= 2; break; case DASM_direct_2: sprintf(buffer, " %s, %s", regname(oprom[2]).c_str(), regname(oprom[1]).c_str()); flags |= 3; break; case DASM_direct_3: sprintf(buffer, " %s, %s, %s", regname(oprom[3]).c_str(), regname(oprom[2]).c_str(), regname(oprom[1]).c_str()); flags |= 4; break; case DASM_immed_1b: sprintf(buffer, " #%02x", oprom[1]); flags |= 2; break; case DASM_immed_2b: sprintf(buffer, " %s, #%02x", regname(oprom[2]).c_str(), oprom[1]); flags |= 3; break; case DASM_immed_or_reg_2b: if(oprom[1] >= 0x10) sprintf(buffer, " %s, %s", regname(oprom[2]).c_str(), regname(oprom[1]).c_str()); else sprintf(buffer, " %s, #%02x", regname(oprom[2]).c_str(), oprom[1]); flags |= 3; break; case DASM_immed_3b: sprintf(buffer, " %s, %s, #%02x", regname(oprom[3]).c_str(), regname(oprom[2]).c_str(), oprom[1]); flags |= 4; break; case DASM_immed_1w: sprintf(buffer, " #%02x%02x", oprom[2], oprom[1]); flags |= 3; break; case DASM_immed_2w: sprintf(buffer, " %s, #%02x%02x", regname(oprom[3]).c_str(), oprom[2], oprom[1]); flags |= 4; break; case DASM_immed_3w: sprintf(buffer, " %s, %s, #%02x%02x", regname(oprom[4]).c_str(), regname(oprom[3]).c_str(), oprom[2], oprom[1]); flags |= 5; break; case DASM_indirect_1n: sprintf(buffer, " [%s]", regname(oprom[1]).c_str()); flags |= 2; break; case DASM_indirect_1: if(oprom[1] & 0x01) { sprintf(buffer, " [%s]+", regname(oprom[1]-1).c_str()); flags |= 2; } else { sprintf(buffer, " [%s]", regname(oprom[1]).c_str()); flags |= 2; } break; case DASM_indirect_2: if(oprom[1] & 0x01) { sprintf(buffer, " %s, [%s]+", regname(oprom[2]).c_str(), regname(oprom[1]-1).c_str()); flags |= 3; } else { sprintf(buffer, " %s, [%s]", regname(oprom[2]).c_str(), regname(oprom[1]).c_str()); flags |= 3; } break; case DASM_indirect_3: if(oprom[1] & 0x01) { sprintf(buffer, " %s, %s, [%s]+", regname(oprom[3]).c_str(), regname(oprom[2]).c_str(), regname(oprom[1]-1).c_str()); flags |= 4; } else { sprintf(buffer, " %s, %s, [%s]", regname(oprom[3]).c_str(), regname(oprom[2]).c_str(), regname(oprom[1]).c_str()); flags |= 4; } break; case DASM_indexed_1: if(oprom[1] & 0x01) { if(oprom[1] == 0x01) sprintf(buffer, " %02x%02x", oprom[3], oprom[2]); else sprintf(buffer, " %02x%02x[%s]", oprom[3], oprom[2], regname(oprom[1]-1).c_str()); flags |= 4; } else { int delta = oprom[2]; if(delta & 0x80) delta -= 0x100; if(oprom[1] == 0x00) { if(delta < 0) sprintf(buffer, " %04x", delta & 0xffff); else sprintf(buffer, " %02x", delta); } else { if(delta < 0) sprintf(buffer, " -%02x[%s]", -delta, regname(oprom[1]).c_str()); else sprintf(buffer, " %02x[%s]", delta, regname(oprom[1]).c_str()); } flags |= 3; } break; case DASM_indexed_2: if(oprom[1] & 0x01) { if(oprom[1] == 0x01) sprintf(buffer, " %s, %02x%02x", regname(oprom[4]).c_str(), oprom[3], oprom[2]); else sprintf(buffer, " %s, %02x%02x[%s]", regname(oprom[4]).c_str(), oprom[3], oprom[2], regname(oprom[1]-1).c_str()); flags |= 5; } else { int delta = oprom[2]; if(delta & 0x80) delta -= 0x100; if(oprom[1] == 0x00) { if(delta < 0) sprintf(buffer, " %s, %04x", regname(oprom[3]).c_str(), delta & 0xffff); else sprintf(buffer, " %s, %02x", regname(oprom[3]).c_str(), delta); } else { if(delta < 0) sprintf(buffer, " %s, -%02x[%s]", regname(oprom[3]).c_str(), -delta, regname(oprom[1]).c_str()); else sprintf(buffer, " %s, %02x[%s]", regname(oprom[3]).c_str(), delta, regname(oprom[1]).c_str()); } flags |= 4; } break; case DASM_indexed_3: if(oprom[1] & 0x01) { if(oprom[1] == 0x01) sprintf(buffer, " %s, %s, %02x%02x", regname(oprom[5]).c_str(), regname(oprom[4]).c_str(), oprom[3], oprom[2]); else sprintf(buffer, " %s, %s, %02x%02x[%s]", regname(oprom[5]).c_str(), regname(oprom[4]).c_str(), oprom[3], oprom[2], regname(oprom[1]-1).c_str()); flags |= 6; } else { int delta = oprom[2]; if(delta & 0x80) delta -= 0x100; if(oprom[1] == 0x00) { if(delta < 0) sprintf(buffer, " %s, %s, %04x", regname(oprom[4]).c_str(), regname(oprom[3]).c_str(), delta & 0xffff); else sprintf(buffer, " %s, %s, %02x", regname(oprom[4]).c_str(), regname(oprom[3]).c_str(), delta); } else { if(delta < 0) sprintf(buffer, " %s, %s, -%02x[%s]", regname(oprom[4]).c_str(), regname(oprom[3]).c_str(), -delta, regname(oprom[1]).c_str()); else sprintf(buffer, " %s, %s, %02x[%s]", regname(oprom[4]).c_str(), regname(oprom[3]).c_str(), delta, regname(oprom[1]).c_str()); } flags |= 5; } break; default: fprintf(stderr, "Unhandled dasm mode %d\n", e.mode); abort(); }; return flags+off; } UINT32 mcs96_device::disasm_min_opcode_bytes() const { return 1; } UINT32 mcs96_device::disasm_max_opcode_bytes() const { return 7; } void mcs96_device::io_w8(UINT8 adr, UINT8 data) { switch(adr) { case 0x02: logerror("%s: ad_command %02x (%04x)\n", tag(), data, PPC); break; case 0x03: logerror("%s: hsi_mode %02x (%04x)\n", tag(), data, PPC); break; case 0x04: logerror("%s: hso_time.l %02x (%04x)\n", tag(), data, PPC); break; case 0x05: logerror("%s: hso_time.h %02x (%04x)\n", tag(), data, PPC); break; case 0x06: logerror("%s: hso_command %02x (%04x)\n", tag(), data, PPC); break; case 0x07: logerror("%s: sbuf %02x (%04x)\n", tag(), data, PPC); break; case 0x08: PSW = (PSW & 0xff00) | data; break; case 0x09: logerror("%s: int_pending %02x (%04x)\n", tag(), data, PPC); break; case 0x0a: logerror("%s: watchdog %02x (%04x)\n", tag(), data, PPC); break; case 0x0e: logerror("%s: baud rate %02x (%04x)\n", tag(), data, PPC); break; case 0x0f: logerror("%s: io port 1 %02x (%04x)\n", tag(), data, PPC); break; case 0x10: logerror("%s: io port 2 %02x (%04x)\n", tag(), data, PPC); break; case 0x11: logerror("%s: sp con %02x (%04x)\n", tag(), data, PPC); break; case 0x15: logerror("%s: ioc0 %02x (%04x)\n", tag(), data, PPC); break; case 0x16: logerror("%s: ioc1 %02x (%04x)\n", tag(), data, PPC); break; case 0x17: logerror("%s: pwm control %02x (%04x)\n", tag(), data, PPC); break; } return; } void mcs96_device::io_w16(UINT8 adr, UINT16 data) { switch(adr) { case 0: break; case 4: logerror("%s: hso_time %04x (%04x)\n", tag(), data, PPC); break; default: io_w8(adr, data); io_w8(adr+1, data>>8); break; } return; } UINT8 mcs96_device::io_r8(UINT8 adr) { switch(adr) { case 0x00: return 0x00; case 0x01: return 0x00; case 0x08: return PSW; } UINT8 data = 0x00; logerror("%s: io_r8 %02x, %02x (%04x)\n", tag(), adr, data, PPC); return data; } UINT16 mcs96_device::io_r16(UINT8 adr) { if(adr < 2) return 0x0000; UINT16 data = 0x0000; logerror("%s: io_r16 %02x, %04x (%04x)\n", tag(), adr, data, PPC); return data; } void mcs96_device::reg_w8(UINT8 adr, UINT8 data) { if(adr < 0x18) io_w8(adr, data); else { UINT16 &r = R[(adr - 0x18) >> 1]; if(adr & 0x01) r = (r & 0x00ff) | (data << 8); else r = (r & 0xff00) | data; } } void mcs96_device::reg_w16(UINT8 adr, UINT16 data) { adr &= 0xfe; if(adr < 0x18) io_w16(adr, data); else R[(adr-0x18) >> 1] = data; } UINT8 mcs96_device::reg_r8(UINT8 adr) { if(adr < 0x18) return io_r8(adr); UINT16 data = R[(adr - 0x18) >> 1]; if(adr & 0x01) return data >> 8; else return data; } UINT16 mcs96_device::reg_r16(UINT8 adr) { adr &= 0xfe; if(adr < 0x18) return io_r16(adr); return R[(adr-0x18) >> 1]; } void mcs96_device::any_w8(UINT16 adr, UINT8 data) { if(adr < 0x18) io_w8(adr, data); else if(adr < 0x100) { UINT16 &r = R[(adr - 0x18) >> 1]; if(adr & 0x01) r = (r & 0x00ff) | (data << 8); else r = (r & 0xff00) | data; } else program->write_byte(adr, data); } void mcs96_device::any_w16(UINT16 adr, UINT16 data) { adr &= 0xfffe; if(adr < 0x18) io_w16(adr, data); else if(adr < 0x100) R[(adr-0x18) >> 1] = data; else program->write_word(adr, data); } UINT8 mcs96_device::any_r8(UINT16 adr) { if(adr < 0x18) return io_r8(adr); else if(adr < 0x100) { UINT16 data = R[(adr - 0x18) >> 1]; if(adr & 0x01) return data >> 8; else return data; } else return program->read_byte(adr); } UINT16 mcs96_device::any_r16(UINT16 adr) { adr &= 0xfffe; if(adr < 0x18) return io_r16(adr); else if(adr < 0x100) return R[(adr-0x18) >> 1]; else return program->read_word(adr); } UINT8 mcs96_device::do_addb(UINT8 v1, UINT8 v2) { UINT16 sum = v1+v2; PSW &= ~(F_Z|F_N|F_C|F_V); if(!UINT8(sum)) PSW |= F_Z; else if(INT8(sum) < 0) PSW |= F_N; if(~(v1^v2) & (v1^sum) & 0x80) PSW |= F_V|F_VT; if(sum & 0xff00) PSW |= F_C; return sum; } UINT16 mcs96_device::do_add(UINT16 v1, UINT16 v2) { UINT32 sum = v1+v2; PSW &= ~(F_Z|F_N|F_C|F_V); if(!UINT16(sum)) PSW |= F_Z; else if(INT16(sum) < 0) PSW |= F_N; if(~(v1^v2) & (v1^sum) & 0x8000) PSW |= F_V|F_VT; if(sum & 0xffff0000) PSW |= F_C; return sum; } UINT8 mcs96_device::do_subb(UINT8 v1, UINT8 v2) { UINT16 diff = v1 - v2; PSW &= ~(F_N|F_V|F_Z|F_C); if(!UINT8(diff)) PSW |= F_Z; else if(INT8(diff) < 0) PSW |= F_N; if((v1^v2) & (v1^diff) & 0x80) PSW |= F_V; if(!(diff & 0xff00)) PSW |= F_C; return diff; } UINT16 mcs96_device::do_sub(UINT16 v1, UINT16 v2) { UINT32 diff = v1 - v2; PSW &= ~(F_N|F_V|F_Z|F_C); if(!UINT16(diff)) PSW |= F_Z; else if(INT16(diff) < 0) PSW |= F_N; if((v1^v2) & (v1^diff) & 0x8000) PSW |= F_V; if(!(diff & 0xffff0000)) PSW |= F_C; return diff; } UINT8 mcs96_device::do_addcb(UINT8 v1, UINT8 v2) { UINT16 sum = v1+v2+(PSW & F_C ? 1 : 0); PSW &= ~(F_Z|F_N|F_C|F_V); if(!UINT8(sum)) PSW |= F_Z; else if(INT8(sum) < 0) PSW |= F_N; if(~(v1^v2) & (v1^sum) & 0x80) PSW |= F_V|F_VT; if(sum & 0xff00) PSW |= F_C; return sum; } UINT16 mcs96_device::do_addc(UINT16 v1, UINT16 v2) { UINT32 sum = v1+v2+(PSW & F_C ? 1 : 0); PSW &= ~(F_Z|F_N|F_C|F_V); if(!UINT16(sum)) PSW |= F_Z; else if(INT16(sum) < 0) PSW |= F_N; if(~(v1^v2) & (v1^sum) & 0x8000) PSW |= F_V|F_VT; if(sum & 0xffff0000) PSW |= F_C; return sum; } UINT8 mcs96_device::do_subcb(UINT8 v1, UINT8 v2) { UINT16 diff = v1 - v2 - (PSW & F_C ? 0 : 1); PSW &= ~(F_N|F_V|F_Z|F_C); if(!UINT8(diff)) PSW |= F_Z; else if(INT8(diff) < 0) PSW |= F_N; if((v1^v2) & (v1^diff) & 0x80) PSW |= F_V; if(!(diff & 0xff00)) PSW |= F_C; return diff; } UINT16 mcs96_device::do_subc(UINT16 v1, UINT16 v2) { UINT32 diff = v1 - v2 - (PSW & F_C ? 0 : 1); PSW &= ~(F_N|F_V|F_Z|F_C); if(!UINT16(diff)) PSW |= F_Z; else if(INT16(diff) < 0) PSW |= F_N; if((v1^v2) & (v1^diff) & 0x8000) PSW |= F_V; if(!(diff & 0xffff0000)) PSW |= F_C; return diff; } void mcs96_device::set_nz8(UINT8 v) { PSW &= ~(F_N|F_V|F_Z|F_C); if(!v) PSW |= F_Z; else if(INT8(v) < 0) PSW |= F_N; } void mcs96_device::set_nz16(UINT16 v) { PSW &= ~(F_N|F_V|F_Z|F_C); if(!v) PSW |= F_Z; else if(INT16(v) < 0) PSW |= F_N; } #include "cpu/mcs96/mcs96.inc"