//============================================================================== // .ooooo. .o .ooo // d88' `8. o888 .88' // .ooooo. ooo. .oo. .oo. oooo oooo Y88.. .8' 888 d88' // d88' `88b `888P"Y88bP"Y88b `888 `888 `88888b. 888 d888P"Ybo. // 888ooo888 888 888 888 888 888 .8' ``88b 888 Y88[ ]88 // 888 .o 888 888 888 888 888 `8. .88P 888 `Y88 88P // `Y8bod8P' o888o o888o o888o `V88V"V8P' `boood8' o888o `88bod8' // // A Portable C++ WDC 65C816 Emulator //------------------------------------------------------------------------------ // Copyright (C),2016 Andrew John Jacobs // All rights reserved. // // This work is made available under the terms of the Creative Commons // Attribution-NonCommercial-ShareAlike 4.0 International license. Open the // following URL to see the details. // // http://creativecommons.org/licenses/by-nc-sa/4.0/ //------------------------------------------------------------------------------ #ifdef CHIPKIT # include "WProgram.h" #else # include # include using namespace std; #endif #include "emu816.h" union emu816::FLAGS emu816::p; emu816::Bit emu816::e; union emu816::REGS emu816::a; union emu816::REGS emu816::x; union emu816::REGS emu816::y; union emu816::REGS emu816::sp; union emu816::REGS emu816::dp; emu816::Word emu816::pc; emu816::Byte emu816::pbr; emu816::Byte emu816::dbr; bool emu816::stopped; bool emu816::interrupted; unsigned long emu816::cycles; bool emu816::trace; //============================================================================== // Not used. emu816::emu816() { } // Not used emu816::~emu816() { } // Reset the state of emulator void emu816::reset(bool trace) { e = 1; pbr = 0x00; dbr = 0x00; dp.w = 0x0000; sp.w = 0x0100; pc = getWord(0xfffc); p.b = 0x34; stopped = false; interrupted = false; emu816::trace = trace; } // Execute a single instruction or invoke an interrupt void emu816::step() { // Check for NMI/IRQ SHOWPC(); switch (getByte (join(pbr, pc++))) { case 0x00: op_brk(am_immb()); break; case 0x01: op_ora(am_dpix()); break; case 0x02: op_cop(am_immb()); break; case 0x03: op_ora(am_srel()); break; case 0x04: op_tsb(am_dpag()); break; case 0x05: op_ora(am_dpag()); break; case 0x06: op_asl(am_dpag()); break; case 0x07: op_ora(am_dpil()); break; case 0x08: op_php(am_impl()); break; case 0x09: op_ora(am_immm()); break; case 0x0a: op_asla(am_acc()); break; case 0x0b: op_phd(am_impl()); break; case 0x0c: op_tsb(am_absl()); break; case 0x0d: op_ora(am_absl()); break; case 0x0e: op_asl(am_absl()); break; case 0x0f: op_ora(am_alng()); break; case 0x10: op_bpl(am_rela()); break; case 0x11: op_ora(am_dpiy()); break; case 0x12: op_ora(am_dpgi()); break; case 0x13: op_ora(am_sriy()); break; case 0x14: op_trb(am_dpag()); break; case 0x15: op_ora(am_dpgx()); break; case 0x16: op_asl(am_dpgx()); break; case 0x17: op_ora(am_dily()); break; case 0x18: op_clc(am_impl()); break; case 0x19: op_ora(am_absy()); break; case 0x1a: op_inca(am_acc()); break; case 0x1b: op_tcs(am_impl()); break; case 0x1c: op_trb(am_absl()); break; case 0x1d: op_ora(am_absx()); break; case 0x1e: op_asl(am_absx()); break; case 0x1f: op_ora(am_alnx()); break; case 0x20: op_jsr(am_absl()); break; case 0x21: op_and(am_dpix()); break; case 0x22: op_jsl(am_alng()); break; case 0x23: op_and(am_srel()); break; case 0x24: op_bit(am_dpag()); break; case 0x25: op_and(am_dpag()); break; case 0x26: op_rol(am_dpag()); break; case 0x27: op_and(am_dpil()); break; case 0x28: op_plp(am_impl()); break; case 0x29: op_and(am_immm()); break; case 0x2a: op_rola(am_acc()); break; case 0x2b: op_pld(am_impl()); break; case 0x2c: op_bit(am_absl()); break; case 0x2d: op_and(am_absl()); break; case 0x2e: op_rol(am_absl()); break; case 0x2f: op_and(am_alng()); break; case 0x30: op_bmi(am_rela()); break; case 0x31: op_and(am_dpiy()); break; case 0x32: op_and(am_dpgi()); break; case 0x33: op_and(am_sriy()); break; case 0x34: op_bit(am_dpgx()); break; case 0x35: op_and(am_dpgx()); break; case 0x36: op_rol(am_dpgx()); break; case 0x37: op_and(am_dily()); break; case 0x38: op_sec(am_impl()); break; case 0x39: op_and(am_absy()); break; case 0x3a: op_deca(am_acc()); break; case 0x3b: op_tsc(am_impl()); break; case 0x3c: op_bit(am_absx()); break; case 0x3d: op_and(am_absx()); break; case 0x3e: op_rol(am_absx()); break; case 0x3f: op_and(am_alnx()); break; case 0x40: op_rti(am_impl()); break; case 0x41: op_eor(am_dpix()); break; case 0x42: op_wdm(am_immb()); break; case 0x43: op_eor(am_srel()); break; case 0x44: op_mvp(am_immw()); break; case 0x45: op_eor(am_dpag()); break; case 0x46: op_lsr(am_dpag()); break; case 0x47: op_eor(am_dpil()); break; case 0x48: op_pha(am_impl()); break; case 0x49: op_eor(am_immm()); break; case 0x4a: op_lsra(am_impl()); break; case 0x4b: op_phk(am_impl()); break; case 0x4c: op_jmp(am_absl()); break; case 0x4d: op_eor(am_absl()); break; case 0x4e: op_lsr(am_absl()); break; case 0x4f: op_eor(am_alng()); break; case 0x50: op_bvc(am_rela()); break; case 0x51: op_eor(am_dpiy()); break; case 0x52: op_eor(am_dpgi()); break; case 0x53: op_eor(am_sriy()); break; case 0x54: op_mvn(am_immw()); break; case 0x55: op_eor(am_dpgx()); break; case 0x56: op_lsr(am_dpgx()); break; case 0x57: op_eor(am_dpil()); break; case 0x58: op_cli(am_impl()); break; case 0x59: op_eor(am_absy()); break; case 0x5a: op_phy(am_impl()); break; case 0x5b: op_tcd(am_impl()); break; case 0x5c: op_jmp(am_alng()); break; case 0x5d: op_eor(am_absx()); break; case 0x5e: op_lsr(am_absx()); break; case 0x5f: op_eor(am_alnx()); break; case 0x60: op_rts(am_impl()); break; case 0x61: op_adc(am_dpix()); break; case 0x62: op_per(am_lrel()); break; case 0x63: op_adc(am_srel()); break; case 0x64: op_stz(am_dpag()); break; case 0x65: op_adc(am_dpag()); break; case 0x66: op_ror(am_dpag()); break; case 0x67: op_adc(am_dpil()); break; case 0x68: op_pla(am_impl()); break; case 0x69: op_adc(am_immm()); break; case 0x6a: op_rora(am_impl()); break; case 0x6b: op_rtl(am_impl()); break; case 0x6c: op_jmp(am_absi()); break; case 0x6d: op_adc(am_absl()); break; case 0x6e: op_ror(am_absl()); break; case 0x6f: op_adc(am_alng()); break; case 0x70: op_bvs(am_rela()); break; case 0x71: op_adc(am_dpiy()); break; case 0x72: op_adc(am_dpgi()); break; case 0x73: op_adc(am_sriy()); break; case 0x74: op_stz(am_dpgx()); break; case 0x75: op_adc(am_dpgx()); break; case 0x76: op_ror(am_dpgx()); break; case 0x77: op_adc(am_dily()); break; case 0x78: op_sei(am_impl()); break; case 0x79: op_adc(am_absy()); break; case 0x7a: op_ply(am_impl()); break; case 0x7b: op_tdc(am_impl()); break; case 0x7c: op_jmp(am_abxi()); break; case 0x7d: op_adc(am_absx()); break; case 0x7e: op_ror(am_absx()); break; case 0x7f: op_adc(am_alnx()); break; case 0x80: op_bra(am_rela()); break; case 0x81: op_sta(am_dpix()); break; case 0x82: op_brl(am_lrel()); break; case 0x83: op_sta(am_srel()); break; case 0x84: op_sty(am_dpag()); break; case 0x85: op_sta(am_dpag()); break; case 0x86: op_stx(am_dpag()); break; case 0x87: op_sta(am_dpil()); break; case 0x88: op_dey(am_impl()); break; case 0x89: op_biti(am_immm()); break; case 0x8a: op_txa(am_impl()); break; case 0x8b: op_phb(am_impl()); break; case 0x8c: op_sty(am_absl()); break; case 0x8d: op_sta(am_absl()); break; case 0x8e: op_stx(am_absl()); break; case 0x8f: op_sta(am_alng()); break; case 0x90: op_bcc(am_rela()); break; case 0x91: op_sta(am_dpiy()); break; case 0x92: op_sta(am_dpgi()); break; case 0x93: op_sta(am_sriy()); break; case 0x94: op_sty(am_dpgx()); break; case 0x95: op_sta(am_dpgx()); break; case 0x96: op_stx(am_dpgy()); break; case 0x97: op_sta(am_dily()); break; case 0x98: op_tya(am_impl()); break; case 0x99: op_sta(am_absy()); break; case 0x9a: op_txs(am_impl()); break; case 0x9b: op_txy(am_impl()); break; case 0x9c: op_stz(am_absl()); break; case 0x9d: op_sta(am_absx()); break; case 0x9e: op_stz(am_absx()); break; case 0x9f: op_sta(am_alnx()); break; case 0xa0: op_ldy(am_immx()); break; case 0xa1: op_lda(am_dpix()); break; case 0xa2: op_ldx(am_immx()); break; case 0xa3: op_lda(am_srel()); break; case 0xa4: op_ldy(am_dpag()); break; case 0xa5: op_lda(am_dpag()); break; case 0xa6: op_ldx(am_dpag()); break; case 0xa7: op_lda(am_dpil()); break; case 0xa8: op_tay(am_impl()); break; case 0xa9: op_lda(am_immm()); break; case 0xaa: op_tax(am_impl()); break; case 0xab: op_plb(am_impl()); break; case 0xac: op_ldy(am_absl()); break; case 0xad: op_lda(am_absl()); break; case 0xae: op_ldx(am_absl()); break; case 0xaf: op_lda(am_alng()); break; case 0xb0: op_bcs(am_rela()); break; case 0xb1: op_lda(am_dpiy()); break; case 0xb2: op_lda(am_dpgi()); break; case 0xb3: op_lda(am_sriy()); break; case 0xb4: op_ldy(am_dpgx()); break; case 0xb5: op_lda(am_dpgx()); break; case 0xb6: op_ldx(am_dpgy()); break; case 0xb7: op_lda(am_dily()); break; case 0xb8: op_clv(am_impl()); break; case 0xb9: op_lda(am_absy()); break; case 0xba: op_tsx(am_impl()); break; case 0xbb: op_tyx(am_impl()); break; case 0xbc: op_ldy(am_absx()); break; case 0xbd: op_lda(am_absx()); break; case 0xbe: op_ldx(am_absy()); break; case 0xbf: op_lda(am_alnx()); break; case 0xc0: op_cpy(am_immx()); break; case 0xc1: op_cmp(am_dpix()); break; case 0xc2: op_rep(am_immb()); break; case 0xc3: op_cmp(am_srel()); break; case 0xc4: op_cpy(am_dpag()); break; case 0xc5: op_cmp(am_dpag()); break; case 0xc6: op_dec(am_dpag()); break; case 0xc7: op_cmp(am_dpil()); break; case 0xc8: op_iny(am_impl()); break; case 0xc9: op_cmp(am_immm()); break; case 0xca: op_dex(am_impl()); break; case 0xcb: op_wai(am_impl()); break; case 0xcc: op_cpy(am_absl()); break; case 0xcd: op_cmp(am_absl()); break; case 0xce: op_dec(am_absl()); break; case 0xcf: op_cmp(am_alng()); break; case 0xd0: op_bne(am_rela()); break; case 0xd1: op_cmp(am_dpiy()); break; case 0xd2: op_cmp(am_dpgi()); break; case 0xd3: op_cmp(am_sriy()); break; case 0xd4: op_pei(am_dpag()); break; case 0xd5: op_cmp(am_dpgx()); break; case 0xd6: op_dec(am_dpgx()); break; case 0xd7: op_cmp(am_dily()); break; case 0xd8: op_cld(am_impl()); break; case 0xd9: op_cmp(am_absy()); break; case 0xda: op_phx(am_impl()); break; case 0xdb: op_stp(am_impl()); break; case 0xdc: op_jmp(am_abil()); break; case 0xdd: op_cmp(am_absx()); break; case 0xde: op_dec(am_absx()); break; case 0xdf: op_cmp(am_alnx()); break; case 0xe0: op_cpx(am_immx()); break; case 0xe1: op_sbc(am_dpix()); break; case 0xe2: op_sep(am_immb()); break; case 0xe3: op_sbc(am_srel()); break; case 0xe4: op_cpx(am_dpag()); break; case 0xe5: op_sbc(am_dpag()); break; case 0xe6: op_inc(am_dpag()); break; case 0xe7: op_sbc(am_dpil()); break; case 0xe8: op_inx(am_impl()); break; case 0xe9: op_sbc(am_immm()); break; case 0xea: op_nop(am_impl()); break; case 0xeb: op_xba(am_impl()); break; case 0xec: op_cpx(am_absl()); break; case 0xed: op_sbc(am_absl()); break; case 0xee: op_inc(am_absl()); break; case 0xef: op_sbc(am_alng()); break; case 0xf0: op_beq(am_rela()); break; case 0xf1: op_sbc(am_dpiy()); break; case 0xf2: op_sbc(am_dpgi()); break; case 0xf3: op_sbc(am_sriy()); break; case 0xf4: op_pea(am_immw()); break; case 0xf5: op_sbc(am_dpgx()); break; case 0xf6: op_inc(am_dpgx()); break; case 0xf7: op_sbc(am_dily()); break; case 0xf8: op_sed(am_impl()); break; case 0xf9: op_sbc(am_absy()); break; case 0xfa: op_plx(am_impl()); break; case 0xfb: op_xce(am_impl()); break; case 0xfc: op_jsr(am_abxi()); break; case 0xfd: op_sbc(am_absx()); break; case 0xfe: op_inc(am_absx()); break; case 0xff: op_sbc(am_alnx()); break; } } //============================================================================== // Debugging Utilities //------------------------------------------------------------------------------ // The ChipKIT versions of the debugging functions output to the serial monitor // rather than standard output. #ifdef CHIPKIT // The current PC and opcode byte void emu816::show() { Serial.print (toHex(pbr, 2)); Serial.print (':'); Serial.print (toHex(pc, 4)); Serial.print (' '); Serial.print (toHex(getByte(join(pbr, pc)), 2)); } // Display the operand bytes void emu816::bytes(unsigned int count) { if (count > 0) { Serial.print(' '); Serial.print(toHex(getByte(bank(pbr) | (pc + 0)), 2)); } else Serial.print(" "); if (count > 1) { Serial.print(' '); Serial.print(toHex(getByte(bank(pbr) | (pc + 1)), 2)); } else Serial.print(" "); if (count > 2) { Serial.print(' '); Serial.print(toHex(getByte(bank(pbr) | (pc + 2)), 2)); } else Serial.print(" "); Serial.print(' '); } // Display registers and top of stack void emu816::dump(const char *mnem, Addr ea) { Serial.print(mnem); Serial.print(" {"); Serial.print(toHex(ea, 4)); Serial.print('}'); Serial.print(" E="); Serial.print(toHex(e, 1)); Serial.print(" P="); Serial.print(p.f_n ? 'N' : '.'); Serial.print(p.f_v ? 'V' : '.'); Serial.print(p.f_m ? 'M' : '.'); Serial.print(p.f_x ? 'X' : '.'); Serial.print(p.f_d ? 'D' : '.'); Serial.print(p.f_i ? 'I' : '.'); Serial.print(p.f_z ? 'Z' : '.'); Serial.print(p.f_c ? 'C' : '.'); Serial.print(" A="); if (e || p.f_m) { Serial.print(toHex(hi(a.w), 2)); Serial.print('['); } else { Serial.print('['); Serial.print(toHex(hi(a.w), 2)); } Serial.print(toHex(a.b, 2)); Serial.print(']'); Serial.print(" X="); if (e || p.f_x) { Serial.print(toHex(hi(x.w), 2)); Serial.print('['); } else { Serial.print('['); Serial.print(toHex(hi(x.w), 2)); } Serial.print(toHex(x.b, 2)); Serial.print(']'); Serial.print(" Y="); if (e || p.f_x) { Serial.print(toHex(hi(y.w), 2)); Serial.print('['); } else { Serial.print('['); Serial.print(toHex(hi(y.w), 2)); } Serial.print(toHex(y.b, 2)); Serial.print(']'); Serial.print(" DP="); Serial.print(toHex(dp.w, 4)); Serial.print(" SP="); if (e) { Serial.print(toHex(hi(sp.w), 2)); Serial.print('['); } else { Serial.print('['); Serial.print(toHex(hi(sp.w), 2)); } Serial.print(toHex(sp.b, 2)); Serial.print(']'); Serial.print(" {"); Serial.print(' '); Serial.print(toHex(getByte(sp.w + 1), 2)); Serial.print(' '); Serial.print(toHex(getByte(sp.w + 2), 2)); Serial.print(' '); Serial.print(toHex(getByte(sp.w + 3), 2)); Serial.print(' '); Serial.print(toHex(getByte(sp.w + 4), 2)); Serial.print(" }"); Serial.print(" DBR="); Serial.print(toHex(dbr, 2)); } #else // The current PC and opcode byte void emu816::show() { // cout << '{' << toHex(cycles, 4) << "} "; cout << toHex(pbr, 2); cout << ':' << toHex(pc, 4); cout << ' ' << toHex(getByte(join(pbr, pc)), 2); } // Display the operand bytes void emu816::bytes(unsigned int count) { if (count > 0) cout << ' ' << toHex(getByte(bank(pbr) | (pc + 0)), 2); else cout << " "; if (count > 1) cout << ' ' << toHex(getByte(bank(pbr) | (pc + 1)), 2); else cout << " "; if (count > 2) cout << ' ' << toHex(getByte(bank(pbr) | (pc + 2)), 2); else cout << " "; cout << ' '; } // Display registers and top of stack void emu816::dump(const char *mnem, Addr ea) { cout << mnem << " {"; cout << toHex(ea >> 16, 2) << ':'; cout << toHex(ea, 4) << '}'; cout << " E=" << toHex(e, 1); cout << " P=" << (p.f_n ? 'N' : '.') << (p.f_v ? 'V' : '.') << (p.f_m ? 'M' : '.') << (p.f_x ? 'X' : '.') << (p.f_d ? 'D' : '.') << (p.f_i ? 'I' : '.') << (p.f_z ? 'Z' : '.') << (p.f_c ? 'C' : '.'); cout << " A="; if (e || p.f_m) cout << toHex(hi(a.w), 2) << '['; else cout << '[' << toHex(hi(a.w), 2); cout << toHex(a.b, 2) << ']'; cout << " X="; if (e || p.f_x) cout << toHex(hi(x.w), 2) << '['; else cout << '[' << toHex(hi(x.w), 2); cout << toHex(x.b, 2) << ']'; cout << " Y="; if (e || p.f_x) cout << toHex(hi(y.w), 2) << '['; else cout << '[' << toHex(hi(y.w), 2); cout << toHex(y.b, 2) << ']'; cout << " DP=" << toHex(dp.w, 4); cout << " SP="; if (e) cout << toHex(hi(sp.w), 2) << '['; else cout << '[' << toHex(hi(sp.w), 2); cout << toHex(sp.b, 2) << ']'; cout << " {"; cout << ' ' << toHex(getByte(sp.w + 1), 2); cout << ' ' << toHex(getByte(sp.w + 2), 2); cout << ' ' << toHex(getByte(sp.w + 3), 2); cout << ' ' << toHex(getByte(sp.w + 4), 2); cout << " }"; cout << " DBR=" << toHex(dbr, 2) << endl; } #endif