#include #include "MKCpu.h" #include "MKGenException.h" namespace MKBasic { /* *-------------------------------------------------------------------- * Method: * Purpose: * Arguments: * Returns: *-------------------------------------------------------------------- */ /* *-------------------------------------------------------------------- * Method: MKCpu() * Purpose: Default constructor. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ MKCpu::MKCpu() { InitCpu(); } /* *-------------------------------------------------------------------- * Method: MKCpu() * Purpose: Custom constructor. * Arguments: pmem - pointer to Memory object. * Returns: n/a *-------------------------------------------------------------------- */ MKCpu::MKCpu(Memory *pmem) { mpMem = pmem; InitCpu(); } /* *-------------------------------------------------------------------- * Method: InitCpu() * Purpose: Initialize internal variables and flags. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- ADDRMODE_IMM = 0, ADDRMODE_ABS, ADDRMODE_ZP, ADDRMODE_IMP, ADDRMODE_IND, ADDRMODE_ABX, ADDRMODE_ABY, ADDRMODE_ZPX, ADDRMODE_ZPY, ADDRMODE_IZX, ADDRMODE_IZY, ADDRMODE_REL, ADDRMODE_ACC, */ void MKCpu::InitCpu() { string saArgFmtTbl[] = {"#$%02x", "$%04x", "$%02x", " ", "($%04x)", "$%04x,X", "$%04x,Y", "$%02x,X", "$%02x,Y", "($%02x,X)", "($%02x),Y", "$%04x", " ", " "}; int naAddrModesLen[] = {2, 3, 2, 1, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1}; // Initialize instructions lengths table based on addressing modes // Initialize assembly argument formats table based on addressing modes for (int i=0; i $FFF0) mpMem->Poke8bit(0xFFFE,0xF0); // LSB mpMem->Poke8bit(0xFFFF,0xFF); // MSB // Put RTI opcode at BRK procedure address. mpMem->Poke8bit(0xFFF0, OPCODE_RTI); // Set default RESET vector ($FFFC -> $0200) mpMem->Poke8bit(0xFFFC,0x00); // LSB mpMem->Poke8bit(0xFFFD,0x02); // MSB // Set BRK code at the RESET procedure address. mpMem->Poke8bit(0x0200,OPCODE_BRK); } /* *-------------------------------------------------------------------- * Method: SetFlags() * Purpose: Set CPU status flags ZERO and SIGN based on Acc, X or Y * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::SetFlags(unsigned char reg) { SetFlag((0 == reg), FLAGS_ZERO); SetFlag(((reg & FLAGS_SIGN) == FLAGS_SIGN), FLAGS_SIGN); SetFlag(true, FLAGS_UNUSED); } /* *-------------------------------------------------------------------- * Method: ShiftLeft() * Purpose: Arithmetic shift left (1 bit), set Carry flag, shift 0 * into bit #0. Update flags NZ. * Arguments: arg8 - 8-bit value * Returns: 8-bit value after shift *-------------------------------------------------------------------- */ unsigned char MKCpu::ShiftLeft(unsigned char arg8) { // set Carry flag based on original bit #7 SetFlag(((arg8 & FLAGS_SIGN) == FLAGS_SIGN), FLAGS_CARRY); arg8 = arg8 << 1; // shift left arg8 &= 0xFE; // shift 0 into bit #0 SetFlags(arg8); return arg8; } /* *-------------------------------------------------------------------- * Method: ShiftRight() * Purpose: Logical Shift Right, update flags NZC. * Arguments: arg8 - byte value * Returns: unsigned char (byte) - after shift *-------------------------------------------------------------------- */ unsigned char MKCpu::ShiftRight(unsigned char arg8) { SetFlag(((arg8 & 0x01) == 0x01), FLAGS_CARRY); arg8 = arg8 >> 1; arg8 &= 0x7F; // unset bit #7 SetFlags(arg8); return arg8; } /* *-------------------------------------------------------------------- * Method: RotateLeft() * Purpose: Rotate left, Carry to bit 0, bit 7 to Carry, update flags N and Z. * Arguments: arg8 - byte value to rotate * Returns: unsigned char (byte) - rotated value *-------------------------------------------------------------------- */ unsigned char MKCpu::RotateLeft(unsigned char arg8) { unsigned char tmp8 = 0; tmp8 = arg8; arg8 = arg8 << 1; // Carry goes to bit #0. if (mReg.Flags & FLAGS_CARRY) { arg8 |= 0x01; } else { arg8 &= 0xFE; } // Original (before ROL) bit #7 goes to Carry. SetFlag(((tmp8 & FLAGS_SIGN) == FLAGS_SIGN), FLAGS_CARRY); SetFlags(arg8); // set flags Z and N return arg8; } /* *-------------------------------------------------------------------- * Method: RotateRight() * Purpose: Rotate Right, Carry to bit 7, bit 0 to Carry, update flags N and Z. * Arguments: arg8 - byte value to rotate * Returns: unsigned char (byte) - rotated value *-------------------------------------------------------------------- */ unsigned char MKCpu::RotateRight(unsigned char arg8) { unsigned char tmp8 = 0; tmp8 = arg8; arg8 = arg8 >> 1; // Carry goes to bit #7. if (CheckFlag(FLAGS_CARRY)) { arg8 |= 0x80; } else { arg8 &= 0x7F; } // Original (before ROR) bit #0 goes to Carry. SetFlag(((tmp8 & 0x01) == 0x01), FLAGS_CARRY); SetFlags(arg8); // set flags Z and N return arg8; } /* *-------------------------------------------------------------------- * Method: GetArg16() * Purpose: Get 2-byte argument, add offset, increase PC. * Arguments: addr - address of argument in memory * offs - offset to be added to returned value * Returns: 16-bit address *-------------------------------------------------------------------- */ unsigned short MKCpu::GetArg16(unsigned char offs) { unsigned short ret = 0; ret = mpMem->Peek16bit(mReg.PtrAddr++); mReg.PtrAddr++; ret += offs; return ret; } /* *-------------------------------------------------------------------- * Method: LogicOpAcc() * Purpose: Perform logical bitwise operation between memory * location and Acc, result in Acc. Set flags. * Arguments: addr - memory location * logop - logical operation code: LOGOP_OR, LOGOP_AND, * LOGOP_EOR * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::LogicOpAcc(unsigned short addr, int logop) { unsigned char val = 0; val = mpMem->Peek8bit(addr); switch (logop) { case LOGOP_OR: mReg.Acc |= val; break; case LOGOP_AND: mReg.Acc &= val; break; case LOGOP_EOR: mReg.Acc ^= val; break; default: break; } SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: ComputeRelJump() * Purpose: Compute new PC based on relative offset. * Arguments: offs - relative offset [-128 ($80)..127 ($7F)] * Returns: unsigned short - new PC (Program Counter). * NOTE: * Program Counter (http://www.6502.org/tutorials/6502opcodes.html#PC) * When the 6502 is ready for the next instruction it increments the * program counter before fetching the instruction. Once it has the op * code, it increments the program counter by the length of the * operand, if any. This must be accounted for when calculating * branches or when pushing bytes to create a false return address * (i.e. jump table addresses are made up of addresses-1 when it is * intended to use an RTS rather than a JMP). * The program counter is loaded least signifigant byte first. * Therefore the most signifigant byte must be pushed first when * creating a false return address. * When calculating branches a forward branch of 6 skips the following * 6 bytes so, effectively the program counter points to the address * that is 8 bytes beyond the address of the branch opcode; * and a backward branch of $FA (256-6) goes to an address 4 bytes * before the branch instruction. *-------------------------------------------------------------------- */ unsigned short MKCpu::ComputeRelJump(unsigned char offs) { // NOTE: mReg.PtrAddr (PC) must be at the next opcode (after branch instr.) at this point. return ComputeRelJump(mReg.PtrAddr, offs); } /* *-------------------------------------------------------------------- * Method: ComputeRelJump() * Purpose: Compute new address after branch based on relative * offset. * Arguments: addr - next opcode address (after branch instr.) * offs - relative offset [-128 ($80)..127 ($7F)] * Returns: unsigned short - new address for branch jump *-------------------------------------------------------------------- */ unsigned short MKCpu::ComputeRelJump(unsigned short addr, unsigned char offs) { unsigned short newpc = addr; if (offs < 0x80) { newpc += (unsigned short) offs; } else { newpc -= (unsigned short) ((unsigned char)(~offs + 1)); // use binary 2's complement instead of arithmetics } return newpc; } /* *-------------------------------------------------------------------- * Method: Conv2Bcd() * Purpose: Convert 16-bit unsigned number to 8-bit BCD * representation. * Arguments: v - 16-bit unsigned integer. * Returns: byte representing BCD code of the 'v'. *-------------------------------------------------------------------- */ unsigned char MKCpu::Conv2Bcd(unsigned short v) { unsigned char arg8 = 0; arg8 = (unsigned char)((v/10) << 4); arg8 |= ((unsigned char)(v - (v/10)*10)) & 0x0F; return arg8; } /* *-------------------------------------------------------------------- * Method: Bcd2Num() * Purpose: Convert 8-bit BCD code to a number. * Arguments: v - BCD code. * Returns: 16-bit unsigned integer *-------------------------------------------------------------------- */ unsigned short MKCpu::Bcd2Num(unsigned char v) { unsigned short ret = 0; ret = 10 * ((v & 0xF0) >> 4) + (unsigned char)(v & 0x0F); return ret; } /* *-------------------------------------------------------------------- * Method: CheckFlag() * Purpose: Check if given bit flag in CPU status register is set * or not. * Arguments: flag - byte with the bit to be checked set and other * bits not set. * Returns: bool *-------------------------------------------------------------------- */ bool MKCpu::CheckFlag(unsigned char flag) { return ((mReg.Flags & flag) == flag); } /* *-------------------------------------------------------------------- * Method: SetFlag() * Purpose: Set or unset CPU status flag. * Arguments: set - if true, set flag, if false, unset flag. * flag - a byte with a bit set on the position of flag * being altered and zeroes on other positions. * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::SetFlag(bool set, unsigned char flag) { if (set) { mReg.Flags |= flag; } else { mReg.Flags &= ~flag; } } /* *-------------------------------------------------------------------- * Method: AddWithCarry() * Purpose: Add Acc + Mem with Carry, update flags and Acc. * Arguments: mem8 - memory argument (byte) * Returns: byte value Acc + Mem + Carry *-------------------------------------------------------------------- */ unsigned char MKCpu::AddWithCarry(unsigned char mem8) { // This algorithm was adapted from Frodo emulator code. unsigned short utmp16 = mReg.Acc + mem8 + (CheckFlag(FLAGS_CARRY) ? 1 : 0); if (CheckFlag(FLAGS_DEC)) { // BCD mode unsigned short al = (mReg.Acc & 0x0F) + (mem8 & 0x0F) + (CheckFlag(FLAGS_CARRY) ? 1 : 0); if (al > 9) al += 6; unsigned short ah = (mReg.Acc >> 4) + (mem8 >> 4); if (al > 0x0F) ah++; SetFlag((utmp16 == 0), FLAGS_ZERO); SetFlag(((((ah << 4) ^ mReg.Acc) & 0x80) && !((mReg.Acc ^ mem8) & 0x80)), FLAGS_OVERFLOW); if (ah > 9) ah += 6; SetFlag((ah > 0x0F), FLAGS_CARRY); mReg.Acc = (ah << 4) | (al & 0x0f); SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); } else { // binary mode SetFlag((utmp16 > 0xff), FLAGS_CARRY); SetFlag((!((mReg.Acc ^ mem8) & 0x80) && ((mReg.Acc ^ utmp16) & 0x80)), FLAGS_OVERFLOW); mReg.Acc = utmp16 & 0xFF; SetFlag((mReg.Acc == 0), FLAGS_ZERO); SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); } SetFlag(true, FLAGS_UNUSED); return mReg.Acc; } /* *-------------------------------------------------------------------- * Method: SubWithCarry() * Purpose: Subtract Acc - Mem with Carry, update flags and Acc. * Arguments: mem8 - memory argument (byte) * Returns: byte value Acc - Mem - Carry *-------------------------------------------------------------------- */ unsigned char MKCpu::SubWithCarry(unsigned char mem8) { unsigned short utmp16 = mReg.Acc - mem8 - (CheckFlag(FLAGS_CARRY) ? 0 : 1); // This algorithm was adapted from Frodo emulator code. if (CheckFlag(FLAGS_DEC)) { // BCD mode unsigned char al = (mReg.Acc & 0x0F) - (mem8 & 0x0F) - (CheckFlag(FLAGS_CARRY) ? 0 : 1); unsigned char ah = (mReg.Acc >> 4) - (mem8 >> 4); if (al & 0x10) { al -= 6; ah--; } if (ah & 0x10) ah -= 6; SetFlag((utmp16 < 0x100), FLAGS_CARRY); SetFlag(((mReg.Acc ^ utmp16) & 0x80) && ((mReg.Acc ^ mem8) & 0x80), FLAGS_OVERFLOW); SetFlag((utmp16 == 0), FLAGS_ZERO); mReg.Acc = (ah << 4) | (al & 0x0f); SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); } else { // binary mode SetFlag((utmp16 < 0x100), FLAGS_CARRY); SetFlag(((mReg.Acc ^ utmp16) & 0x80) && ((mReg.Acc ^ mem8) & 0x80), FLAGS_OVERFLOW); mReg.Acc = utmp16 & 0xFF; SetFlag((mReg.Acc == 0), FLAGS_ZERO); SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); } SetFlag(true, FLAGS_UNUSED); return mReg.Acc; } /* *-------------------------------------------------------------------- * Method: ~MKCpu() * Purpose: Class destructor. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ MKCpu::~MKCpu() { if (mLocalMem) { if (NULL != mpMem) delete mpMem; } } /* *-------------------------------------------------------------------- * Method: GetAddrWithMode() * Purpose: Get address of the argument with specified mode. * Increment PC. * Arguments: mode - code of the addressing mode, see eAddrModes. * Returns: 16-bit address *-------------------------------------------------------------------- */ unsigned short MKCpu::GetAddrWithMode(int mode) { unsigned short arg16 = 0; mReg.LastAddrMode = mode; switch (mode) { case ADDRMODE_IMM: arg16 = mReg.PtrAddr++; break; case ADDRMODE_ABS: mReg.LastArg = arg16 = GetArg16(0); break; case ADDRMODE_ZP: mReg.LastArg = arg16 = (unsigned short) mpMem->Peek8bit(mReg.PtrAddr++); break; case ADDRMODE_IMP: // DO NOTHING - implied mode operates on internal CPU register break; case ADDRMODE_IND: mReg.LastArg = arg16 = mpMem->Peek16bit(mReg.PtrAddr++); arg16 = mpMem->Peek16bit(arg16); break; case ADDRMODE_ABX: arg16 = GetArg16(mReg.IndX); mReg.LastArg = arg16 - mReg.IndX; break; case ADDRMODE_ABY: arg16 = GetArg16(mReg.IndY); mReg.LastArg = arg16 - mReg.IndY; break; case ADDRMODE_ZPX: mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); arg16 = (arg16 + mReg.IndX) & 0xFF; break; case ADDRMODE_ZPY: mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); arg16 = (arg16 + mReg.IndY) & 0xFF; break; case ADDRMODE_IZX: mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); arg16 = (arg16 + mReg.IndX) & 0xFF; arg16 = mpMem->Peek16bit(arg16); break; case ADDRMODE_IZY: mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); arg16 = mpMem->Peek16bit(arg16) + mReg.IndY; break; case ADDRMODE_REL: mReg.LastArg = arg16 = ComputeRelJump(mpMem->Peek8bit(mReg.PtrAddr++)); break; case ADDRMODE_ACC: // DO NOTHING - acc mode operates on internal CPU register break; default: throw MKGenException("ERROR: Wrong addressing mode!"); break; } return arg16; } /* *-------------------------------------------------------------------- * Method: GetArgWithMode() * Purpose: Get argument from address with specified mode. * Arguments: addr - address in memory * mode - code of the addressing mode, see eAddrModes. * Returns: argument *-------------------------------------------------------------------- */ unsigned short MKCpu::GetArgWithMode(unsigned short addr, int mode) { unsigned short arg16 = 0; switch (mode) { case ADDRMODE_IMM: arg16 = mpMem->Peek8bit(addr); break; case ADDRMODE_ABS: arg16 = mpMem->Peek16bit(addr); break; case ADDRMODE_ZP: arg16 = (unsigned short) mpMem->Peek8bit(addr); break; case ADDRMODE_IMP: // DO NOTHING - implied mode operates on internal CPU register break; case ADDRMODE_IND: arg16 = mpMem->Peek16bit(addr); break; case ADDRMODE_ABX: arg16 = mpMem->Peek16bit(addr); break; case ADDRMODE_ABY: arg16 = mpMem->Peek16bit(addr); break; case ADDRMODE_ZPX: arg16 = mpMem->Peek8bit(addr); break; case ADDRMODE_ZPY: arg16 = mpMem->Peek8bit(addr); break; case ADDRMODE_IZX: arg16 = mpMem->Peek8bit(addr); break; case ADDRMODE_IZY: arg16 = mpMem->Peek8bit(addr); break; case ADDRMODE_REL: arg16 = ComputeRelJump(addr+1, mpMem->Peek8bit(addr)); break; case ADDRMODE_ACC: // DO NOTHING - acc mode operates on internal CPU register break; default: break; } return arg16; } /* *-------------------------------------------------------------------- * Method: Disassemble() * Purpose: Disassemble instruction and argument per addressing mode * Arguments: n/a - internal * Returns: 0 *-------------------------------------------------------------------- */ unsigned short MKCpu::Disassemble() { char sArg[40]; char sFmt[20]; strcpy(sFmt, "%s "); strcat(sFmt, mArgFmtTbl[mReg.LastAddrMode].c_str()); sprintf(sArg, sFmt, ((mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.length() > 0 ? (mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.c_str() : "???"), mReg.LastArg); for (unsigned int i=0; iPeek8bit(addr++); addrmode = (mOpCodesMap[(eOpCodes)opcode]).amf.length() > 0 ? (mOpCodesMap[(eOpCodes)opcode]).addrmode : -1; if (addrmode < 0 || NULL == instrbuf) return 0; switch (mAddrModesLen[addrmode]) { case 2: sprintf(sBuf, "$%02x ", mpMem->Peek8bit(addr)); break; case 3: sprintf(sBuf, "$%02x $%02x", mpMem->Peek8bit(addr), mpMem->Peek8bit(addr+1)); break; default: strcpy(sBuf, " "); break; } strcpy(sFmt, "$%04x: $%02x %s %s "); strcat(sFmt, mArgFmtTbl[addrmode].c_str()); sprintf(sArg, sFmt, opcaddr, mpMem->Peek8bit(opcaddr), sBuf, ((mOpCodesMap[(eOpCodes)opcode]).amf.length() > 0 ? (mOpCodesMap[(eOpCodes)opcode]).amf.c_str() : "???"), GetArgWithMode(addr,addrmode)); for (unsigned int i=0; iPeek8bit(mReg.PtrAddr++); unsigned char arg8 = 0; unsigned short arg16 = 0; SetFlag(false, FLAGS_BRK); // reset BRK flag - we want to detect mReg.SoftIrq = false; // software interrupt each time it happens // (non-maskable) mReg.LastRTS = false; mReg.LastOpCode = opcode; mReg.LastAddrMode = ADDRMODE_UND; mReg.LastArg = 0; switch (opcode) { case OPCODE_BRK: // software interrupt, Implied ($00 : BRK) mReg.LastAddrMode = ADDRMODE_IMP; if (!CheckFlag(FLAGS_IRQ)) { // only if IRQ not masked arg16 = 0x100; arg16 += mReg.PtrStack--; // Note that BRK is really a 2-bytes opcode. Each BRK opcode should be padded by extra byte, // because the return address put on stack is PC + 1 (where PC is the next address after BRK). // That means the next opcode after BRK will not be executed upon return from interrupt, // but the next after that will be. mReg.PtrAddr++; mpMem->Poke8bit(arg16, (unsigned char) (((mReg.PtrAddr) & 0xFF00) >> 8)); // HI(PC+1) - HI part of next instr. addr. + 1 arg16 = 0x100; arg16 += mReg.PtrStack--; mpMem->Poke8bit(arg16, (unsigned char) ((mReg.PtrAddr) & 0x00FF)); // LO(PC+1) - LO part of next instr. addr. + 1 arg16 = 0x100; arg16 += mReg.PtrStack--; SetFlag(true, FLAGS_BRK); // The BRK flag that goes on stack is set. mpMem->Poke8bit(arg16, mReg.Flags); SetFlag(false, FLAGS_BRK); // The BRK flag that remains in CPU status is unchanged, so unset after putting on stack. //mReg.SoftIrq = true; mReg.PtrAddr = mpMem->Peek16bit(0xFFFE); // Load BRK vector into the PC. SetFlag(true,FLAGS_IRQ); } else { mReg.PtrAddr++; } break; case OPCODE_NOP: // NO oPeration, Implied ($EA : NOP) mReg.LastAddrMode = ADDRMODE_IMP; break; case OPCODE_LDA_IZX: // LoaD Accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); break; case OPCODE_LDA_ZP: // LoaD Accumulator, Zero Page ($A5 arg : LDA arg ;arg=0..$FF), MEM=arg mReg.Acc = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); SetFlags(mReg.Acc); break; case OPCODE_LDA_IMM: // LoaD Accumulator, Immediate ($A9 arg : LDA #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = mReg.Acc = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SetFlags(mReg.Acc); break; case OPCODE_LDA_ABS: // LoaD Accumulator, Absolute ($AD addrlo addrhi : LDA addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); break; case OPCODE_LDA_IZY: // LoaD Accumulator, Indirect Indexed ($B1 arg : LDA (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); break; case OPCODE_LDA_ZPX: // LoaD Accumulator, Zero Page Indexed, X ($B5 arg : LDA arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); break; case OPCODE_LDA_ABY: // LoaD Accumulator, Absolute Indexed, Y ($B9 addrlo addrhi : LDA addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); break; case OPCODE_LDA_ABX: // LoaD Accumulator, Absolute Indexed, X ($BD addrlo addrhi : LDA addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); break; case OPCODE_LDX_IMM: // LoaD X register, Immediate ($A2 arg : LDX #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = mReg.IndX = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SetFlags(mReg.IndX); break; case OPCODE_LDX_ZP: // LoaD X register, Zero Page ($A6 arg : LDX arg ;arg=0..$FF), MEM=arg mReg.IndX = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); SetFlags(mReg.IndX); break; case OPCODE_LDX_ABS: // LoaD X register, Absolute ($AE addrlo addrhi : LDX addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mReg.IndX = mpMem->Peek8bit(arg16); SetFlags(mReg.IndX); break; case OPCODE_LDX_ZPY: // LoaD X register, Zero Page Indexed, Y ($B6 arg : LDX arg,Y ;arg=0..$FF), MEM=arg+Y arg16 = GetAddrWithMode(ADDRMODE_ZPY); mReg.IndX = mpMem->Peek8bit(arg16); SetFlags(mReg.IndX); break; case OPCODE_LDX_ABY: // LoaD X register, Absolute Indexed, Y ($BE addrlo addrhi : LDX addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); mReg.IndX = mpMem->Peek8bit(arg16); SetFlags(mReg.IndX); break; case OPCODE_LDY_IMM: // LoaD Y register, Immediate ($A0 arg : LDY #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = mReg.IndY = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SetFlags(mReg.IndY); break; case OPCODE_LDY_ZP: // LoaD Y register, Zero Page ($A4 arg : LDY arg ;arg=0..$FF), MEM=arg mReg.IndY = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); SetFlags(mReg.IndY); break; case OPCODE_LDY_ABS: // LoaD Y register, Absolute ($AC addrlo addrhi : LDY addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mReg.IndY = mpMem->Peek8bit(arg16); SetFlags(mReg.IndY); break; case OPCODE_LDY_ZPX: // LoaD Y register, Zero Page Indexed, X ($B4 arg : LDY arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); mReg.IndY = mpMem->Peek8bit(arg16); SetFlags(mReg.IndY); break; case OPCODE_LDY_ABX: // LoaD Y register, Absolute Indexed, X ($BC addrlo addrhi : LDY addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); mReg.IndY = mpMem->Peek8bit(arg16); SetFlags(mReg.IndY); break; case OPCODE_TAX: // Transfer A to X, Implied ($AA : TAX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX = mReg.Acc; SetFlags(mReg.IndX); break; case OPCODE_TAY: // Transfer A to Y, Implied ($A8 : TAY) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndY = mReg.Acc; SetFlags(mReg.IndY); break; case OPCODE_TXA: // Transfer X to A, Implied ($8A : TXA) mReg.LastAddrMode = ADDRMODE_IMP; mReg.Acc = mReg.IndX; SetFlags(mReg.Acc); break; case OPCODE_TYA: // Transfer Y to A, Implied ($98 : TYA) mReg.LastAddrMode = ADDRMODE_IMP; mReg.Acc = mReg.IndY; SetFlags(mReg.Acc); break; case OPCODE_TSX: // Transfer Stack ptr to X, Implied ($BA : TSX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX = mReg.PtrStack; SetFlags(mReg.IndX); break; case OPCODE_TXS: // Transfer X to Stack ptr, Implied ($9A : TXS) mReg.LastAddrMode = ADDRMODE_IMP; mReg.PtrStack = mReg.IndX; break; case OPCODE_STA_IZX: // STore Accumulator, Indexed Indirect ($81 arg : STA (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_STA_ZP: // STore Accumulator, Zero Page ($85 arg : STA arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_STA_ABS: // STore Accumulator, Absolute ($8D addrlo addrhi : STA addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_STA_IZY: // STore Accumulator, Indirect Indexed ($91 arg : STA (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_STA_ZPX: // STore Accumulator, Zero Page Indexed, X ($95 arg : STA arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_STA_ABY: // STore Accumulator, Absolute Indexed, Y ($99 addrlo addrhi : STA addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_STA_ABX: // STore Accumulator, Absolute Indexed, X ($9D addrlo addrhi : STA addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_STX_ZP: // STore X register, Zero Page ($86 arg : STX arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); mpMem->Poke8bit(arg16, mReg.IndX); break; case OPCODE_STX_ABS: // STore X register, Absolute ($8E addrlo addrhi : STX addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mpMem->Poke8bit(arg16, mReg.IndX); break; case OPCODE_STX_ZPY: // STore X register, Zero Page Indexed, Y ($96 arg : STX arg,Y ;arg=0..$FF), MEM=arg+Y arg16 = GetAddrWithMode(ADDRMODE_ZPY); mpMem->Poke8bit(arg16, mReg.IndX); break; case OPCODE_STY_ZP: // STore Y register, Zero Page ($84 arg : STY arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); mpMem->Poke8bit(arg16, mReg.IndY); break; case OPCODE_STY_ABS: // STore Y register, Absolute ($8C addrlo addrhi : STY addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mpMem->Poke8bit(arg16, mReg.IndY); break; case OPCODE_STY_ZPX: // STore Y register, Zero Page Indexed, X ($94 arg : STY arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); mpMem->Poke8bit(arg16, mReg.IndY); break; case OPCODE_BNE_REL: // Branch on Not Equal, Relative ($D0 signoffs : BNE signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (!CheckFlag(FLAGS_ZERO)) { mReg.PtrAddr = arg16; } break; case OPCODE_BEQ_REL: // Branch on EQual, Relative ($F0 signoffs : BEQ signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (CheckFlag(FLAGS_ZERO)) { mReg.PtrAddr = arg16; } break; case OPCODE_BPL_REL: // Branch on PLus, Relative ($10 signoffs : BPL signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (!CheckFlag(FLAGS_SIGN)) { mReg.PtrAddr = arg16; } break; case OPCODE_BMI_REL: // Branch on MInus, Relative ($30 signoffs : BMI signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (CheckFlag(FLAGS_SIGN)) { mReg.PtrAddr = arg16; } break; case OPCODE_BVC_REL: // Branch on oVerflow Clear, Relative ($50 signoffs : BVC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (!CheckFlag(FLAGS_OVERFLOW)) { mReg.PtrAddr = arg16; } break; case OPCODE_BVS_REL: // Branch on oVerflow Set, Relative ($70 signoffs : BVS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (CheckFlag(FLAGS_OVERFLOW)) { mReg.PtrAddr = arg16; } break; case OPCODE_BCC_REL: // Branch on Carry Clear, Relative ($90 signoffs : BCC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (!CheckFlag(FLAGS_CARRY)) { mReg.PtrAddr = arg16; } break; case OPCODE_BCS_REL: // Branch on Carry Set, Relative ($B0 signoffs : BCS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (CheckFlag(FLAGS_CARRY)) { mReg.PtrAddr = arg16; } break; /*** case OPCODE_BRA: // branch always to a relative 1-byte address offset -128 ($80)..127 ($7F) (OPCODE_BEQ reladdr : BEQ reladdr) arg8 = mpMem->Peek8bit(mReg.PtrAddr++); mReg.PtrAddr = ComputeRelJump(arg8); break; ***/ case OPCODE_INC_ZP: // INCrement memory, Zero Page ($E6 arg : INC arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16) + 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_INC_ABS: // INCrement memory, Absolute ($EE addrlo addrhi : INC addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16) + 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_INC_ZPX: // INCrement memory, Zero Page Indexed, X ($F6 arg : INC arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); arg8 = mpMem->Peek8bit(arg16) + 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_INC_ABX: // INCrement memory, Absolute Indexed, X ($FE addrlo addrhi : INC addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); arg8 = mpMem->Peek8bit(arg16) + 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_INX: // INcrement X, Implied ($E8 : INX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX++; SetFlags(mReg.IndX); break; case OPCODE_DEX: // DEcrement X, Implied ($CA : DEX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX--; SetFlags(mReg.IndX); break; case OPCODE_INY: // INcrement Y, Implied ($C8 : INY) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndY++; SetFlags(mReg.IndY); break; case OPCODE_DEY: // DEcrement Y, Implied ($88 : DEY) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndY--; SetFlags(mReg.IndY); break; case OPCODE_JMP_ABS: // JuMP, Absolute ($4C addrlo addrhi : JMP addr ;addr=0..$FFFF), MEM=addr mReg.PtrAddr = GetAddrWithMode(ADDRMODE_ABS); break; case OPCODE_JMP_IND: // JuMP, Indirect Absolute ($6C addrlo addrhi : JMP (addr) ;addr=0..FFFF), MEM=&addr mReg.PtrAddr = GetAddrWithMode(ADDRMODE_IND); break; case OPCODE_ORA_IZX: // bitwise OR with Accumulator, Indexed Indirect ($01 arg : ORA (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); LogicOpAcc(arg16, LOGOP_OR); break; case OPCODE_ORA_ZP: // bitwise OR with Accumulator, Zero Page ($05 arg : ORA arg ;arg=0..$FF), MEM=arg LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_OR); break; case OPCODE_ORA_IMM: // bitwise OR with Accumulator, Immediate ($09 arg : ORA #arg ;arg=0..$FF), MEM=PC+1 arg16 = GetAddrWithMode(ADDRMODE_IMM); mReg.LastArg = mpMem->Peek8bit(arg16); LogicOpAcc(arg16, LOGOP_OR); break; case OPCODE_ORA_ABS: // bitwise OR with Accumulator, Absolute ($0D addrlo addrhi : ORA addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); LogicOpAcc(arg16, LOGOP_OR); break; case OPCODE_ORA_IZY: // bitwise OR with Accumulator, Indirect Indexed ($11 arg : ORA (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); LogicOpAcc(arg16, LOGOP_OR); break; case OPCODE_ORA_ZPX: // bitwise OR with Accumulator, Zero Page Indexed, X ($15 arg : ORA arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); LogicOpAcc(arg16, LOGOP_OR); break; case OPCODE_ORA_ABY: // bitwise OR with Accumulator, Absolute Indexed, Y ($19 addrlo addrhi : ORA addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); LogicOpAcc(arg16, LOGOP_OR); break; case OPCODE_ORA_ABX: // bitwise OR with Accumulator, Absolute Indexed, X ($1D addrlo addrhi : ORA addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); LogicOpAcc(arg16, LOGOP_OR); break; case OPCODE_ASL_ZP: // Arithmetic Shift Left, Zero Page ($06 arg : ASL arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_ASL: // Arithmetic Shift Left, Accumulator ($0A : ASL) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = ShiftLeft(mReg.Acc); break; case OPCODE_ASL_ABS: // Arithmetic Shift Left, Absolute ($0E addrlo addrhi : ASL addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_ASL_ZPX: // Arithmetic Shift Left, Zero Page Indexed, X ($16 arg : ASL arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_ASL_ABX: // Arithmetic Shift Left, Absolute Indexed, X ($1E addrlo addrhi : ASL addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_JSR_ABS: // Jump to SubRoutine, Absolute ($20 addrlo addrhi : JSR addr ;addr=0..$FFFF), MEM=addr // PC - next instruction address // Push PC-1 on stack (HI, then LO). // Currently PC (mReg.PtrAddr) is at the 1-st argument of JSR. // Therefore the actual PC-1 used in calculations equals: mReg.PtrAddr+1 arg16 = 0x100; arg16 += mReg.PtrStack--; mpMem->Poke8bit(arg16, (unsigned char) (((mReg.PtrAddr+1) & 0xFF00) >> 8)); // HI(PC-1) - HI part of next instr. addr. - 1 arg16 = 0x100; arg16 += mReg.PtrStack--; mpMem->Poke8bit(arg16, (unsigned char) ((mReg.PtrAddr+1) & 0x00FF)); // LO(PC-1) - LO part of next instr. addr. - 1 mReg.PtrAddr = GetAddrWithMode(ADDRMODE_ABS); break; case OPCODE_AND_IZX: // bitwise AND with accumulator, Indexed Indirect ($21 arg : AND (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); LogicOpAcc(arg16, LOGOP_AND); break; case OPCODE_AND_ZP: // bitwise AND with accumulator, Zero Page ($25 arg : AND arg ;arg=0..$FF), MEM=arg LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_AND); break; case OPCODE_AND_IMM: // bitwise AND with accumulator, Immediate ($29 arg : AND #arg ;arg=0..$FF), MEM=PC+1 arg16 = GetAddrWithMode(ADDRMODE_IMM); mReg.LastArg = mpMem->Peek8bit(arg16); LogicOpAcc(arg16, LOGOP_AND); break; case OPCODE_AND_ABS: // bitwise AND with accumulator, Absolute ($2D addrlo addrhi : AND addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); LogicOpAcc(arg16, LOGOP_AND); break; case OPCODE_AND_IZY: // bitwise AND with accumulator, Indirect Indexed ($31 arg : AND (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); LogicOpAcc(arg16, LOGOP_AND); break; case OPCODE_AND_ZPX: // bitwise AND with accumulator, Zero Page Indexed, X ($35 arg : AND arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); LogicOpAcc(arg16, LOGOP_AND); break; case OPCODE_AND_ABY: // bitwise AND with accumulator, Absolute Indexed, Y ($39 addrlo addrhi : AND addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); LogicOpAcc(arg16, LOGOP_AND); break; case OPCODE_AND_ABX: // bitwise AND with accumulator, Absolute Indexed, X ($3D addrlo addrhi : AND addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); LogicOpAcc(arg16, LOGOP_AND); break; case OPCODE_BIT_ZP: // test BITs, Zero Page ($24 arg : BIT arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); SetFlag((arg8 & FLAGS_OVERFLOW) == FLAGS_OVERFLOW, FLAGS_OVERFLOW); SetFlag((arg8 & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); arg8 &= mReg.Acc; SetFlag((arg8 == 0), FLAGS_ZERO); break; case OPCODE_BIT_ABS: // test BITs, Absolute ($2C addrlo addrhi : BIT addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); SetFlag((arg8 & FLAGS_OVERFLOW) == FLAGS_OVERFLOW, FLAGS_OVERFLOW); SetFlag((arg8 & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); arg8 &= mReg.Acc; SetFlag((arg8 == 0), FLAGS_ZERO); break; case OPCODE_ROL_ZP: // ROtate Left, Zero Page ($26 arg : ROL arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); arg8 = RotateLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_ROL: // ROtate Left, Accumulator ($2A : ROL) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = RotateLeft(mReg.Acc); break; case OPCODE_ROL_ABS: // ROtate Left, Absolute ($2E addrlo addrhi : ROL addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); arg8 = RotateLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_ROL_ZPX: // ROtate Left, Zero Page Indexed, X ($36 arg : ROL arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); arg8 = mpMem->Peek8bit(arg16); arg8 = RotateLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_ROL_ABX: // ROtate Left, Absolute Indexed, X ($3E addrlo addrhi : ROL addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); arg8 = mpMem->Peek8bit(arg16); arg8 = RotateLeft(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_PHP: // PusH Processor status on Stack, Implied ($08 : PHP) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += mReg.PtrStack--; arg8 = mReg.Flags | FLAGS_BRK | FLAGS_UNUSED; mpMem->Poke8bit(arg16, arg8); break; case OPCODE_PHA: // PusH Accumulator, Implied ($48 : PHA) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += mReg.PtrStack--; mpMem->Poke8bit(arg16, mReg.Acc); break; case OPCODE_PLP: // PuLl Processor status, Implied ($28 : PLP) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += ++mReg.PtrStack; mReg.Flags = mpMem->Peek8bit(arg16) | FLAGS_UNUSED; break; case OPCODE_PLA: // PuLl Accumulator, Implied ($68 : PLA) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += ++mReg.PtrStack; mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); break; case OPCODE_CLC: // CLear Carry, Implied ($18 : CLC) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_CARRY); break; case OPCODE_SEC: // SEt Carry, Implied ($38 : SEC) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(true, FLAGS_CARRY); break; case OPCODE_CLI: // CLear Interrupt, Implied ($58 : CLI) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_IRQ); break; case OPCODE_CLV: // CLear oVerflow, Implied ($B8 : CLV) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_OVERFLOW); break; case OPCODE_CLD: // CLear Decimal, Implied ($D8 : CLD) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_DEC); break; case OPCODE_SED: // SEt Decimal, Implied ($F8 : SED) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(true, FLAGS_DEC); break; case OPCODE_SEI: // SEt Interrupt, Implied ($78 : SEI) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(true, FLAGS_IRQ); break; /* * RTI retrieves the Processor Status Word (flags) and the Program Counter from the stack in that order * (interrupts push the PC first and then the PSW). * Note that unlike RTS, the return address on the stack is the actual address rather than the address-1. */ case OPCODE_RTI: // ReTurn from Interrupt, Implied ($40 : RTI) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += ++mReg.PtrStack; mReg.Flags = mpMem->Peek8bit(arg16); SetFlag(true,FLAGS_UNUSED); arg16++; mReg.PtrStack++; mReg.PtrAddr = mpMem->Peek8bit(arg16); arg16++; mReg.PtrStack++; mReg.PtrAddr += (mpMem->Peek8bit(arg16) * 0x100); mReg.SoftIrq = CheckFlag(FLAGS_BRK); SetFlag(false,FLAGS_IRQ); break; case OPCODE_RTS: // ReTurn from Subroutine, Implied ($60 : RTS) mReg.LastAddrMode = ADDRMODE_IMP; if (mReg.PtrStack == 0xFF) { mReg.LastRTS = true; break; } arg16 = 0x100; arg16 += ++mReg.PtrStack; mReg.PtrAddr = mpMem->Peek8bit(arg16); arg16++; mReg.PtrStack++; mReg.PtrAddr += (mpMem->Peek8bit(arg16) * 0x100); mReg.PtrAddr++; break; case OPCODE_EOR_IZX: // bitwise Exclusive OR, Indexed Indirect ($41 arg : EOR (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); LogicOpAcc(arg16, LOGOP_EOR); break; case OPCODE_EOR_ZP: // bitwise Exclusive OR, Zero Page ($45 arg : EOR arg ;arg=0..$FF), MEM=arg LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_EOR); break; case OPCODE_EOR_IMM: // bitwise Exclusive OR, Immediate ($49 arg : EOR #arg ;arg=0..$FF), MEM=PC+1 arg16 = GetAddrWithMode(ADDRMODE_IMM); mReg.LastArg = mpMem->Peek8bit(arg16); LogicOpAcc(arg16, LOGOP_EOR); break; case OPCODE_EOR_ABS: // bitwise Exclusive OR, Absolute ($4D addrlo addrhi : EOR addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); LogicOpAcc(arg16, LOGOP_EOR); break; case OPCODE_EOR_IZY: // bitwise Exclusive OR, Indirect Indexed ($51 arg : EOR (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); LogicOpAcc(arg16, LOGOP_EOR); break; case OPCODE_EOR_ZPX: // bitwise Exclusive OR, Zero Page Indexed, X ($55 arg : EOR arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); LogicOpAcc(arg16, LOGOP_EOR); break; case OPCODE_EOR_ABY: // bitwise Exclusive OR, Absolute Indexed, Y ($59 addrlo addrhi : EOR addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); LogicOpAcc(arg16, LOGOP_EOR); break; case OPCODE_EOR_ABX: // bitwise Exclusive OR, Absolute Indexed, X ($5D addrlo addrhi : EOR addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); LogicOpAcc(arg16, LOGOP_EOR); break; case OPCODE_LSR_ZP: // Logical Shift Right, Zero Page ($46 arg : LSR arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftRight(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_LSR: // Logical Shift Right, Accumulator ($4A : LSR) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = ShiftRight(mReg.Acc); break; case OPCODE_LSR_ABS: // Logical Shift Right, Absolute ($4E addrlo addrhi : LSR addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftRight(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_LSR_ZPX: // Logical Shift Right, Zero Page Indexed, X ($56 arg : LSR arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftRight(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_LSR_ABX: // Logical Shift Right, Absolute Indexed, X ($5E addrlo addrhi : LSR addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); arg8 = mpMem->Peek8bit(arg16); arg8 = ShiftRight(arg8); mpMem->Poke8bit(arg16, arg8); break; case OPCODE_ADC_IZX: // ADd with Carry, Indexed Indirect ($61 arg : ADC (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); AddWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_ADC_ZP: // ADd with Carry, Zero Page ($65 arg : ADC arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); AddWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_ADC_IMM: // ADd with Carry, Immediate ($69 arg : ADC #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); AddWithCarry(mReg.LastArg); break; case OPCODE_ADC_ABS: // ADd with Carry, Absolute ($6D addrlo addrhi : ADC addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); AddWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_ADC_IZY: // ADd with Carry, Indirect Indexed ($71 arg : ADC (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); AddWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_ADC_ZPX: // ADd with Carry, Zero Page Indexed, X ($75 arg : ADC arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); AddWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_ADC_ABY: // ADd with Carry, Absolute Indexed, Y ($79 addrlo addrhi : ADC addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); AddWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_ADC_ABX: // ADd with Carry, Absolute Indexed, X ($7D addrlo addrhi : ADC addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); AddWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_ROR_ZP: // ROtate Right, Zero Page ($66 arg : ROR arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); mpMem->Poke8bit(arg16, RotateRight(arg8)); break; case OPCODE_ROR: // ROtate Right, Accumulator ($6A : ROR) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = RotateRight(mReg.Acc); break; case OPCODE_ROR_ABS: // ROtate Right, Absolute ($6E addrlo addrhi : ROR addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); mpMem->Poke8bit(arg16, RotateRight(arg8)); break; case OPCODE_ROR_ZPX: // ROtate Right, Zero Page Indexed, X ($76 arg : ROR arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); arg8 = mpMem->Peek8bit(arg16); mpMem->Poke8bit(arg16, RotateRight(arg8)); break; case OPCODE_ROR_ABX: // ROtate Right, Absolute Indexed, X ($7E addrlo addrhi : ROR addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); arg8 = mpMem->Peek8bit(arg16); mpMem->Poke8bit(arg16, RotateRight(arg8)); break; case OPCODE_CPY_IMM: // ComPare Y register, Immediate ($C0 arg : CPY #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = arg8 = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SetFlag((mReg.IndY >= arg8), FLAGS_CARRY); arg8 = mReg.IndY - arg8; SetFlags(arg8); break; case OPCODE_CPY_ZP: // ComPare Y register, Zero Page ($C4 arg : CPY arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.IndY >= arg8), FLAGS_CARRY); arg8 = mReg.IndY - arg8; SetFlags(arg8); break; case OPCODE_CPY_ABS: // ComPare Y register, Absolute ($CC addrlo addrhi : CPY addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.IndY >= arg8), FLAGS_CARRY); arg8 = mReg.IndY - arg8; SetFlags(arg8); break; case OPCODE_CMP_IZX: // CoMPare accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_CMP_ZP: // CoMPare accumulator, Zero Page ($C5 arg : CMP arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_CMP_IMM: // CoMPare accumulator, Immediate ($C9 arg : CMP #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = arg8 = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_CMP_ABS: // CoMPare accumulator, Absolute ($CD addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_CMP_IZY: // CoMPare accumulator, Indirect Indexed ($D1 arg : CMP (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_CMP_ZPX: // CoMPare accumulator, Zero Page Indexed, X ($D5 arg : CMP arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_CMP_ABY: // CoMPare accumulator, Absolute Indexed, Y ($D9 addrlo addrhi : CMP addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_CMP_ABX: // CoMPare accumulator, Absolute Indexed, X ($DD addrlo addrhi : CMP addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); break; case OPCODE_DEC_ZP: // DECrement memory, Zero Page ($C6 arg : DEC arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16) - 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_DEC_ABS: // DECrement memory, Absolute ($CE addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16) - 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_DEC_ZPX: // DECrement memory, Zero Page Indexed, X ($D6 arg : DEC arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); arg8 = mpMem->Peek8bit(arg16) - 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_DEC_ABX: // DECrement memory, Absolute Indexed, X ($DE addrlo addrhi : DEC addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); arg8 = mpMem->Peek8bit(arg16) - 1; mpMem->Poke8bit(arg16, arg8); SetFlags(arg8); break; case OPCODE_CPX_IMM: // ComPare X register, Immediate ($E0 arg : CPX #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = arg8 = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SetFlag((mReg.IndX >= arg8), FLAGS_CARRY); arg8 = mReg.IndX - arg8; SetFlags(arg8); break; case OPCODE_CPX_ZP: // ComPare X register, Zero Page ($E4 arg : CPX arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.IndX >= arg8), FLAGS_CARRY); arg8 = mReg.IndX - arg8; SetFlags(arg8); break; case OPCODE_CPX_ABS: // ComPare X register, Absolute ($EC addrlo addrhi : CPX addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.IndX >= arg8), FLAGS_CARRY); arg8 = mReg.IndX - arg8; SetFlags(arg8); break; case OPCODE_SBC_ZP: // SuBtract with Carry, Zero Page ($E5 arg : SBC arg ;arg=0..$FF), MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); SubWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_SBC_ABS: // SuBtract with Carry, Absolute ($ED addrlo addrhi : SBC addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); SubWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_SBC_IZX: // SuBtract with Carry, Indexed Indirect ($E1 arg : SBC (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); SubWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_SBC_IZY: // SuBtract with Carry, Indirect Indexed ($F1 arg : SBC (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); SubWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_SBC_ZPX: // SuBtract with Carry, Zero Page Indexed, X ($F5 arg : SBC arg,X ;arg=0..$FF), MEM=arg+X arg16 = GetAddrWithMode(ADDRMODE_ZPX); SubWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_SBC_ABY: // SuBtract with Carry, Absolute Indexed, Y ($F9 addrlo addrhi : SBC addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); SubWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_SBC_ABX: // SuBtract with Carry, Absolute Indexed, X ($FD addrlo addrhi : SBC addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); SubWithCarry(mpMem->Peek8bit(arg16)); break; case OPCODE_SBC_IMM: // SuBtract with Carry, Immediate ($E9 arg : SBC #arg ;arg=0..$FF), MEM=PC+1 mReg.LastArg = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SubWithCarry(mReg.LastArg); break; default: // trap illegal opcode that has unidentified behavior { string s = ((mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.length() > 0 ? (mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.c_str() : "???"); if (s.compare("ILL") == 0) { mReg.SoftIrq = true; } } break; } Disassemble(); char histentry[80]; sprintf(histentry, "$%04x: %-16s \t$%02x | $%02x | $%02x | $%02x | $%02x", mReg.LastAddr, mReg.LastInstr.c_str(), mReg.Acc, mReg.IndX, mReg.IndY, mReg.Flags, mReg.PtrStack); Add2History(histentry); return &mReg; } /* *-------------------------------------------------------------------- * Method: GetRegs() * Purpose: Return pointer to CPU registers and status. * Arguments: n/a * Returns: pointer to Regs structure. *-------------------------------------------------------------------- */ Regs *MKCpu::GetRegs() { return &mReg; } /* *-------------------------------------------------------------------- * Method: Add2History() * Purpose: Add entry to execute history. * Arguments: s - string (entry) * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::Add2History(string s) { mExecHistory.push(s); while (mExecHistory.size() > 20) mExecHistory.pop(); } /* *-------------------------------------------------------------------- * Method: GetExecHistory() * Purpose: Return queue with execute history. * Arguments: n/a * Returns: queue *-------------------------------------------------------------------- */ queue MKCpu::GetExecHistory() { return mExecHistory; } } // namespace MKBasic