#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 { // use binary 2's complement instead of arithmetics newpc -= (unsigned short) ((unsigned char)(~offs + 1)); } 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, tmp = 0; mReg.PageBoundary = false; 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: mReg.LastArg = tmp = GetArg16(0); //arg16 = GetArg16(mReg.IndX); arg16 = tmp + mReg.IndX; //mReg.LastArg = arg16 - mReg.IndX; mReg.PageBoundary = PageBoundary(tmp, arg16); break; case ADDRMODE_ABY: mReg.LastArg = tmp = GetArg16(0); //arg16 = GetArg16(mReg.IndY); arg16 = tmp + mReg.IndY; //mReg.LastArg = arg16 - mReg.IndY; mReg.PageBoundary = PageBoundary(tmp, arg16); 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++); tmp = mpMem->Peek16bit(arg16); arg16 = tmp + mReg.IndY; mReg.PageBoundary = PageBoundary(tmp, arg16); break; case ADDRMODE_REL: mReg.LastArg = arg16 = ComputeRelJump(mpMem->Peek8bit(mReg.PtrAddr++)); mReg.PageBoundary = PageBoundary(mReg.PtrAddr, arg16); 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; iPoke8bit(arg16, (unsigned char) (((mReg.PtrAddr) & 0xFF00) >> 8)); arg16 = 0x100; arg16 += mReg.PtrStack--; // LO(PC+1) - LO part of next instr. addr. + 1 mpMem->Poke8bit(arg16, (unsigned char) ((mReg.PtrAddr) & 0x00FF)); arg16 = 0x100; arg16 += mReg.PtrStack--; // The BRK flag that goes on stack is set in case of BRK // or cleared in case of IRQ. SetFlag(!mReg.IrqPending, FLAGS_BRK); mpMem->Poke8bit(arg16, mReg.Flags); // The BRK flag that remains in CPU status is unchanged, so unset after // putting on stack. SetFlag(false, FLAGS_BRK); //mReg.SoftIrq = true; // Load IRQ/BRK vector into the PC. mReg.PtrAddr = mpMem->Peek16bit(0xFFFE); SetFlag(true,FLAGS_IRQ); // block interrupts (RTI will clear it) } else { mReg.PtrAddr++; } if (mReg.IrqPending) mReg.IrqPending = false; // let the next request come } /* *-------------------------------------------------------------------- * Method: OpCodeNop() * Purpose: Execute NOP opcode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeNop() { // NO oPeration, Implied ($EA : NOP) mReg.LastAddrMode = ADDRMODE_IMP; } /* *-------------------------------------------------------------------- * Method: OpCodeLdaIzx() * Purpose: Execute LDA opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaIzx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLdaZp() * Purpose: Execute LDA opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaZp() { // LoaD Accumulator, Zero Page ($A5 arg : LDA arg ;arg=0..$FF), // MEM=arg mReg.Acc = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeLdaImm() * Purpose: Execute LDA opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaImm() { // LoaD Accumulator, Immediate ($A9 arg : LDA #arg ;arg=0..$FF), // MEM=PC+1 mReg.Acc = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); mReg.LastArg = mReg.Acc; SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeLdaAbs() * Purpose: Execute LDA opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaAbs() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLdaIzy() * Purpose: Execute LDA opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaIzy() { unsigned short arg16 = 0; // LoaD Accumulator, Indirect Indexed ($B1 arg : LDA (arg),Y // ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); if (mReg.PageBoundary) mReg.CyclesLeft++; mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeLdaZpx() * Purpose: Execute LDA opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaZpx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLdaAby() * Purpose: Execute LDA opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaAby() { unsigned short arg16 = 0; // LoaD Accumulator, Absolute Indexed, Y // ($B9 addrlo addrhi : LDA addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeLdaAbx() * Purpose: Execute LDA opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdaAbx() { unsigned short arg16 = 0; // LoaD Accumulator, Absolute Indexed, X // ($BD addrlo addrhi : LDA addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeLdxImm() * Purpose: Execute LDX opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdxImm() { // LoaD X register, Immediate ($A2 arg : LDX #arg ;arg=0..$FF), // MEM=PC+1 mReg.IndX = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); mReg.LastArg = mReg.IndX; SetFlags(mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeLdxZp() * Purpose: Execute LDX opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdxZp() { // LoaD X register, Zero Page ($A6 arg : LDX arg ;arg=0..$FF), // MEM=arg mReg.IndX = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); SetFlags(mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeLdxAbs() * Purpose: Execute LDX opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdxAbs() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLdxZpy() * Purpose: Execute LDX opcode, ZPY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdxZpy() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLdxAby() * Purpose: Execute LDX opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdxAby() { unsigned short arg16 = 0; // LoaD X register, Absolute Indexed, Y // ($BE addrlo addrhi : LDX addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; mReg.IndX = mpMem->Peek8bit(arg16); SetFlags(mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeLdyImm() * Purpose: Execute LDY opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdyImm() { // LoaD Y register, Immediate ($A0 arg : LDY #arg ;arg=0..$FF), // MEM=PC+1 mReg.IndY = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); mReg.LastArg = mReg.IndY; SetFlags(mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeLdyZp() * Purpose: Execute LDY opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdyZp() { // LoaD Y register, Zero Page ($A4 arg : LDY arg ;arg=0..$FF), // MEM=arg mReg.IndY = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); SetFlags(mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeLdyAbs() * Purpose: Execute LDY opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdyAbs() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLdyZpx() * Purpose: Execute LDY opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdyZpx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLdyAbx() * Purpose: Execute LDY opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLdyAbx() { unsigned short arg16 = 0; // LoaD Y register, Absolute Indexed, X // ($BC addrlo addrhi : LDY addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; mReg.IndY = mpMem->Peek8bit(arg16); SetFlags(mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeTax() * Purpose: Execute TAX opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeTax() { // Transfer A to X, Implied ($AA : TAX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX = mReg.Acc; SetFlags(mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeTay() * Purpose: Execute TAY opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeTay() { // Transfer A to Y, Implied ($A8 : TAY) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndY = mReg.Acc; SetFlags(mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeTxa() * Purpose: Execute TXA opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeTxa() { // Transfer X to A, Implied ($8A : TXA) mReg.LastAddrMode = ADDRMODE_IMP; mReg.Acc = mReg.IndX; SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeTya() * Purpose: Execute TYA opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeTya() { // Transfer Y to A, Implied ($98 : TYA) mReg.LastAddrMode = ADDRMODE_IMP; mReg.Acc = mReg.IndY; SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeTsx() * Purpose: Execute TSX opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeTsx() { // Transfer Stack ptr to X, Implied ($BA : TSX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX = mReg.PtrStack; SetFlags(mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeTxs() * Purpose: Execute TXS opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeTxs() { // Transfer X to Stack ptr, Implied ($9A : TXS) mReg.LastAddrMode = ADDRMODE_IMP; mReg.PtrStack = mReg.IndX; } /* *-------------------------------------------------------------------- * Method: OpCodeStaIzx() * Purpose: Execute STA opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStaIzx() { unsigned short arg16 = 0; // STore Accumulator, Indexed Indirect // ($81 arg : STA (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); mpMem->Poke8bit(arg16, mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeStaZp() * Purpose: Execute STA opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStaZp() { unsigned short arg16 = 0; // STore Accumulator, Zero Page ($85 arg : STA arg ;arg=0..$FF), // MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); mpMem->Poke8bit(arg16, mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeStaAbs() * Purpose: Execute STA opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStaAbs() { unsigned short arg16 = 0; // STore Accumulator, Absolute // ($8D addrlo addrhi : STA addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mpMem->Poke8bit(arg16, mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeStaIzy() * Purpose: Execute STA opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStaIzy() { unsigned short arg16 = 0; // STore Accumulator, Indirect Indexed // ($91 arg : STA (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); mpMem->Poke8bit(arg16, mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeStaZpx() * Purpose: Execute STA opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStaZpx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeStaAby() * Purpose: Execute STA opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStaAby() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeStaAbx() * Purpose: Execute STA opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStaAbx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeStxZp() * Purpose: Execute STX opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStxZp() { unsigned short arg16 = 0; // STore X register, Zero Page ($86 arg : STX arg ;arg=0..$FF), // MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); mpMem->Poke8bit(arg16, mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeStxAbs() * Purpose: Execute STX opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStxAbs() { unsigned short arg16 = 0; // STore X register, Absolute // ($8E addrlo addrhi : STX addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mpMem->Poke8bit(arg16, mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeStxZpy() * Purpose: Execute STX opcode, ZPY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStxZpy() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeStyZp() * Purpose: Execute STY opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStyZp() { unsigned short arg16 = 0; // STore Y register, Zero Page ($84 arg : STY arg ;arg=0..$FF), // MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); mpMem->Poke8bit(arg16, mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeStyAbs() * Purpose: Execute STY opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStyAbs() { unsigned short arg16 = 0; // STore Y register, Absolute // ($8C addrlo addrhi : STY addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); mpMem->Poke8bit(arg16, mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeStyZpx() * Purpose: Execute STY opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeStyZpx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeBneRel() * Purpose: Execute BNE opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBneRel() { unsigned short arg16 = 0; // 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.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeBeqRel() * Purpose: Execute BEQ opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBeqRel() { unsigned short arg16 = 0; // Branch on EQual, Relative ($F0 signoffs : BEQ signoffs // ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (CheckFlag(FLAGS_ZERO)) { mReg.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeBplRel() * Purpose: Execute BPL opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBplRel() { unsigned short arg16 = 0; // Branch on PLus, Relative ($10 signoffs : BPL signoffs // ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (!CheckFlag(FLAGS_SIGN)) { mReg.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeBmiRel() * Purpose: Execute BMI opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBmiRel() { unsigned short arg16 = 0; // Branch on MInus, Relative ($30 signoffs : BMI signoffs // ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) arg16 = GetAddrWithMode(ADDRMODE_REL); if (CheckFlag(FLAGS_SIGN)) { mReg.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeBvcRel() * Purpose: Execute BVC opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBvcRel() { unsigned short arg16 = 0; // 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.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeBvsRel() * Purpose: Execute BVS opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBvsRel() { unsigned short arg16 = 0; // 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.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeBccRel() * Purpose: Execute BCC opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBccRel() { unsigned short arg16 = 0; // 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.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeBcsRel() * Purpose: Execute BCS opcode, REL addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBcsRel() { unsigned short arg16 = 0; // 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.CyclesLeft += (mReg.PageBoundary ? 2 : 1); mReg.PtrAddr = arg16; } } /* *-------------------------------------------------------------------- * Method: OpCodeIncZp() * Purpose: Execute INC opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeIncZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeIncAbs() * Purpose: Execute INC opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeIncAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeIncZpx() * Purpose: Execute INC opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeIncZpx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeIncAbx() * Purpose: Execute INC opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeIncAbx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeInx() * Purpose: Execute INX opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeInx() { // INcrement X, Implied ($E8 : INX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX++; SetFlags(mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeDex() * Purpose: Execute DEX opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeDex() { // DEcrement X, Implied ($CA : DEX) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndX--; SetFlags(mReg.IndX); } /* *-------------------------------------------------------------------- * Method: OpCodeIny() * Purpose: Execute INY opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeIny() { // INcrement Y, Implied ($C8 : INY) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndY++; SetFlags(mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeDey() * Purpose: Execute DEY opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeDey() { // DEcrement Y, Implied ($88 : DEY) mReg.LastAddrMode = ADDRMODE_IMP; mReg.IndY--; SetFlags(mReg.IndY); } /* *-------------------------------------------------------------------- * Method: OpCodeJmpAbs() * Purpose: Execute JMP opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeJmpAbs() { // JuMP, Absolute ($4C addrlo addrhi : JMP addr ;addr=0..$FFFF), // MEM=addr mReg.PtrAddr = GetAddrWithMode(ADDRMODE_ABS); } /* *-------------------------------------------------------------------- * Method: OpCodeJmpInd() * Purpose: Execute JMP opcode, IND addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeJmpInd() { // JuMP, Indirect Absolute // ($6C addrlo addrhi : JMP (addr) ;addr=0..FFFF), MEM=&addr mReg.PtrAddr = GetAddrWithMode(ADDRMODE_IND); } /* *-------------------------------------------------------------------- * Method: OpCodeOraIzx() * Purpose: Execute ORA opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraIzx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeOraZp() * Purpose: Execute ORA opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraZp() { // bitwise OR with Accumulator, Zero Page // ($05 arg : ORA arg ;arg=0..$FF), MEM=arg LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_OR); } /* *-------------------------------------------------------------------- * Method: OpCodeOraImm() * Purpose: Execute ORA opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraImm() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeOraAbs() * Purpose: Execute ORA opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraAbs() { unsigned short arg16 = 0; // bitwise OR with Accumulator, Absolute // ($0D addrlo addrhi : ORA addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); LogicOpAcc(arg16, LOGOP_OR); } /* *-------------------------------------------------------------------- * Method: OpCodeOraIzy() * Purpose: Execute ORA opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraIzy() { unsigned short arg16 = 0; // bitwise OR with Accumulator, Indirect Indexed // ($11 arg : ORA (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_OR); } /* *-------------------------------------------------------------------- * Method: OpCodeOraZpx() * Purpose: Execute ORA opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraZpx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeOraAby() * Purpose: Execute ORA opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraAby() { unsigned short arg16 = 0; // bitwise OR with Accumulator, Absolute Indexed, Y // ($19 addrlo addrhi : ORA addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_OR); } /* *-------------------------------------------------------------------- * Method: OpCodeOraAbx() * Purpose: Execute ORA opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeOraAbx() { unsigned short arg16 = 0; // bitwise OR with Accumulator, Absolute Indexed, X // ($1D addrlo addrhi : ORA addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_OR); } /* *-------------------------------------------------------------------- * Method: OpCodeAslZp() * Purpose: Execute ASL opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAslZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeAslAcc() * Purpose: Execute ASL opcode, ACC addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAslAcc() { // Arithmetic Shift Left, Accumulator ($0A : ASL) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = ShiftLeft(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeAslAbs() * Purpose: Execute ASL opcode, ACC addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAslAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeAslZpx() * Purpose: Execute ASL opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAslZpx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeAslAbx() * Purpose: Execute ASL opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAslAbx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeJsrAbs() * Purpose: Execute JSR opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeJsrAbs() { unsigned short arg16 = 0; // 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--; // HI(PC-1) - HI part of next instr. addr. - 1 mpMem->Poke8bit(arg16, (unsigned char) (((mReg.PtrAddr+1) & 0xFF00) >> 8)); arg16 = 0x100; arg16 += mReg.PtrStack--; // LO(PC-1) - LO part of next instr. addr. - 1 mpMem->Poke8bit(arg16, (unsigned char) ((mReg.PtrAddr+1) & 0x00FF)); mReg.PtrAddr = GetAddrWithMode(ADDRMODE_ABS); } /* *-------------------------------------------------------------------- * Method: OpCodeAndIzx() * Purpose: Execute AND opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndIzx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeAndZp() * Purpose: Execute AND opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndZp() { // bitwise AND with accumulator, Zero Page // ($25 arg : AND arg ;arg=0..$FF), MEM=arg LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_AND); } /* *-------------------------------------------------------------------- * Method: OpCodeAndImm() * Purpose: Execute AND opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndImm() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeAndAbs() * Purpose: Execute AND opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndAbs() { unsigned short arg16 = 0; // bitwise AND with accumulator, Absolute // ($2D addrlo addrhi : AND addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); LogicOpAcc(arg16, LOGOP_AND); } /* *-------------------------------------------------------------------- * Method: OpCodeAndIzy() * Purpose: Execute AND opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndIzy() { unsigned short arg16 = 0; // bitwise AND with accumulator, Indirect Indexed // ($31 arg : AND (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_AND); } /* *-------------------------------------------------------------------- * Method: OpCodeAndZpx() * Purpose: Execute AND opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndZpx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeAndAby() * Purpose: Execute AND opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndAby() { unsigned short arg16 = 0; // bitwise AND with accumulator, Absolute Indexed, Y // ($39 addrlo addrhi : AND addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_AND); } /* *-------------------------------------------------------------------- * Method: OpCodeAndAbx() * Purpose: Execute AND opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAndAbx() { unsigned short arg16 = 0; // bitwise AND with accumulator, Absolute Indexed, X // ($3D addrlo addrhi : AND addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_AND); } /* *-------------------------------------------------------------------- * Method: OpCodeBitZp() * Purpose: Execute BIT opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBitZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeBitAbs() * Purpose: Execute BIT opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeBitAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeRolZp() * Purpose: Execute ROL opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRolZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeRolAcc() * Purpose: Execute ROL opcode, ACC addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRolAcc() { // ROtate Left, Accumulator ($2A : ROL) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = RotateLeft(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeRolAbs() * Purpose: Execute ROL opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRolAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeRolZpx() * Purpose: Execute ROL opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRolZpx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeRolAbx() * Purpose: Execute ROL opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRolAbx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodePhp() * Purpose: Execute PHP opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodePhp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodePha() * Purpose: Execute PHA opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodePha() { unsigned short arg16 = 0; // PusH Accumulator, Implied ($48 : PHA) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += mReg.PtrStack--; mpMem->Poke8bit(arg16, mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodePlp() * Purpose: Execute PLP opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodePlp() { unsigned short arg16 = 0; // PuLl Processor status, Implied ($28 : PLP) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += ++mReg.PtrStack; mReg.Flags = mpMem->Peek8bit(arg16) | FLAGS_UNUSED; } /* *-------------------------------------------------------------------- * Method: OpCodePla() * Purpose: Execute PLA opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodePla() { unsigned short arg16 = 0; // PuLl Accumulator, Implied ($68 : PLA) mReg.LastAddrMode = ADDRMODE_IMP; arg16 = 0x100; arg16 += ++mReg.PtrStack; mReg.Acc = mpMem->Peek8bit(arg16); SetFlags(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeClc() * Purpose: Execute CLC opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeClc() { // CLear Carry, Implied ($18 : CLC) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_CARRY); } /* *-------------------------------------------------------------------- * Method: OpCodeSec() * Purpose: Execute SEC opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSec() { // SEt Carry, Implied ($38 : SEC) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(true, FLAGS_CARRY); } /* *-------------------------------------------------------------------- * Method: OpCodeCli() * Purpose: Execute CLI opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCli() { // CLear Interrupt, Implied ($58 : CLI) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_IRQ); } /* *-------------------------------------------------------------------- * Method: OpCodeClv() * Purpose: Execute CLV opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeClv() { // CLear oVerflow, Implied ($B8 : CLV) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_OVERFLOW); } /* *-------------------------------------------------------------------- * Method: OpCodeCld() * Purpose: Execute CLD opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCld() { // CLear Decimal, Implied ($D8 : CLD) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(false, FLAGS_DEC); } /* *-------------------------------------------------------------------- * Method: OpCodeSed() * Purpose: Execute SED opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSed() { // SEt Decimal, Implied ($F8 : SED) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(true, FLAGS_DEC); } /* *-------------------------------------------------------------------- * Method: OpCodeSei() * Purpose: Execute SEI opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSei() { // SEt Interrupt, Implied ($78 : SEI) mReg.LastAddrMode = ADDRMODE_IMP; SetFlag(true, FLAGS_IRQ); } /* *-------------------------------------------------------------------- * Method: OpCodeRti() * Purpose: Execute RTI opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRti() { unsigned short arg16 = 0; /* * 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. */ // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeRts() * Purpose: Execute RTS opcode, IMPlied addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRts() { unsigned short arg16 = 0; // ReTurn from Subroutine, Implied ($60 : RTS) mReg.LastAddrMode = ADDRMODE_IMP; if (mExitAtLastRTS && mReg.PtrStack == 0xFF) { mReg.LastRTS = true; } else { arg16 = 0x100; arg16 += ++mReg.PtrStack; mReg.PtrAddr = mpMem->Peek8bit(arg16); arg16++; mReg.PtrStack++; mReg.PtrAddr += (mpMem->Peek8bit(arg16) * 0x100); mReg.PtrAddr++; } } /* *-------------------------------------------------------------------- * Method: OpCodeEorIzx() * Purpose: Execute EOR opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorIzx() { unsigned short arg16 = 0; // bitwise Exclusive OR, Indexed Indirect // ($41 arg : EOR (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); LogicOpAcc(arg16, LOGOP_EOR); } /* *-------------------------------------------------------------------- * Method: OpCodeEorZp() * Purpose: Execute EOR opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorZp() { // bitwise Exclusive OR, Zero Page ($45 arg : EOR arg ;arg=0..$FF), // MEM=arg LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_EOR); } /* *-------------------------------------------------------------------- * Method: OpCodeEorImm() * Purpose: Execute EOR opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorImm() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeEorAbs() * Purpose: Execute EOR opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorAbs() { unsigned short arg16 = 0; // bitwise Exclusive OR, Absolute // ($4D addrlo addrhi : EOR addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); LogicOpAcc(arg16, LOGOP_EOR); } /* *-------------------------------------------------------------------- * Method: OpCodeEorIzy() * Purpose: Execute EOR opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorIzy() { unsigned short arg16 = 0; // bitwise Exclusive OR, Indirect Indexed // ($51 arg : EOR (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_EOR); } /* *-------------------------------------------------------------------- * Method: OpCodeEorZpx() * Purpose: Execute EOR opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorZpx() { unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeEorAby() * Purpose: Execute EOR opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorAby() { unsigned short arg16 = 0; // bitwise Exclusive OR, Absolute Indexed, Y // ($59 addrlo addrhi : EOR addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_EOR); } /* *-------------------------------------------------------------------- * Method: OpCodeEorAbx() * Purpose: Execute EOR opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeEorAbx() { unsigned short arg16 = 0; // bitwise Exclusive OR, Absolute Indexed, X // ($5D addrlo addrhi : EOR addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; LogicOpAcc(arg16, LOGOP_EOR); } /* *-------------------------------------------------------------------- * Method: OpCodeLsrZp() * Purpose: Execute LSR opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLsrZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLsrAcc() * Purpose: Execute LSR opcode, ACC addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLsrAcc() { // Logical Shift Right, Accumulator ($4A : LSR) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = ShiftRight(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeLsrAbs() * Purpose: Execute LSR opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLsrAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLsrZpx() * Purpose: Execute LSR opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLsrZpx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeLsrAbx() * Purpose: Execute LSR opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeLsrAbx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcIzx() * Purpose: Execute ADC opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcIzx() { unsigned short arg16 = 0; // ADd with Carry, Indexed Indirect // ($61 arg : ADC (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); AddWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcZp() * Purpose: Execute ADC opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcZp() { unsigned short arg16 = 0; // ADd with Carry, Zero Page ($65 arg : ADC arg ;arg=0..$FF), // MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); AddWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcImm() * Purpose: Execute ADC opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcImm() { // ADd with Carry, Immediate ($69 arg : ADC #arg ;arg=0..$FF), // MEM=PC+1 mReg.LastArg = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); AddWithCarry(mReg.LastArg); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcAbs() * Purpose: Execute ADC opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcAbs() { unsigned short arg16 = 0; // ADd with Carry, Absolute // ($6D addrlo addrhi : ADC addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); AddWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcIzy() * Purpose: Execute ADC opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcIzy() { unsigned short arg16 = 0; // ADd with Carry, Indirect Indexed // ($71 arg : ADC (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); if (mReg.PageBoundary) mReg.CyclesLeft++; AddWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcZpx() * Purpose: Execute ADC opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcZpx() { unsigned short arg16 = 0; // 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)); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcAby() * Purpose: Execute ADC opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcAby() { unsigned short arg16 = 0; // ADd with Carry, Absolute Indexed, Y // ($79 addrlo addrhi : ADC addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; AddWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeAdcAbx() * Purpose: Execute ADC opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeAdcAbx() { unsigned short arg16 = 0; // ADd with Carry, Absolute Indexed, X // ($7D addrlo addrhi : ADC addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; AddWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeRorZp() * Purpose: Execute ROR opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRorZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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)); } /* *-------------------------------------------------------------------- * Method: OpCodeRorAcc() * Purpose: Execute ROR opcode, ACC addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRorAcc() { // ROtate Right, Accumulator ($6A : ROR) mReg.LastAddrMode = ADDRMODE_ACC; mReg.Acc = RotateRight(mReg.Acc); } /* *-------------------------------------------------------------------- * Method: OpCodeRorAbs() * Purpose: Execute ROR opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRorAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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)); } /* *-------------------------------------------------------------------- * Method: OpCodeRorZpx() * Purpose: Execute ROR opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRorZpx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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)); } /* *-------------------------------------------------------------------- * Method: OpCodeRorAbx() * Purpose: Execute ROR opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeRorAbx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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)); } /* *-------------------------------------------------------------------- * Method: OpCodeCpyImm() * Purpose: Execute CPY opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCpyImm() { unsigned char arg8 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCpyZp() * Purpose: Execute CPY opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCpyZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCpyAbs() * Purpose: Execute CPY opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCpyAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpIzx() * Purpose: Execute CMP opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpIzx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpZp() * Purpose: Execute CMP opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpImm() * Purpose: Execute CMP opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpImm() { unsigned char arg8 = 0; // CoMPare accumulator, Immediate ($C9 arg : CMP #arg ;arg=0..$FF), // MEM=PC+1 arg8 = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); mReg.LastArg = arg8; SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpAbs() * Purpose: Execute CMP opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpIzy() * Purpose: Execute CMP opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpIzy() { unsigned char arg8 = 0; unsigned short arg16 = 0; // CoMPare accumulator, Indirect Indexed // ($D1 arg : CMP (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); if (mReg.PageBoundary) mReg.CyclesLeft++; arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpZpx() * Purpose: Execute CMP opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpZpx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpAby() * Purpose: Execute CMP opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpAby() { unsigned char arg8 = 0; unsigned short arg16 = 0; // CoMPare accumulator, Absolute Indexed, Y // ($D9 addrlo addrhi : CMP addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); } /* *-------------------------------------------------------------------- * Method: OpCodeCmpAbx() * Purpose: Execute CMP opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCmpAbx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // CoMPare accumulator, Absolute Indexed, X // ($DD addrlo addrhi : CMP addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; arg8 = mpMem->Peek8bit(arg16); SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); arg8 = mReg.Acc - arg8; SetFlags(arg8); } /* *-------------------------------------------------------------------- * Method: OpCodeDecZp() * Purpose: Execute DEC opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeDecZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeDecAbs() * Purpose: Execute DEC opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeDecAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeDecZpx() * Purpose: Execute DEC opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeDecZpx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeDecAbx() * Purpose: Execute DEC opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeDecAbx() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCpxImm() * Purpose: Execute CPX opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCpxImm() { unsigned char arg8 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCpxZp() * Purpose: Execute CPX opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCpxZp() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeCpxAbs() * Purpose: Execute CPX opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeCpxAbs() { unsigned char arg8 = 0; unsigned short arg16 = 0; // 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); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcZp() * Purpose: Execute SBC opcode, ZP addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcZp() { unsigned short arg16 = 0; // SuBtract with Carry, Zero Page ($E5 arg : SBC arg ;arg=0..$FF), // MEM=arg arg16 = GetAddrWithMode(ADDRMODE_ZP); SubWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcAbs() * Purpose: Execute SBC opcode, ABS addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcAbs() { unsigned short arg16 = 0; // SuBtract with Carry, Absolute // ($ED addrlo addrhi : SBC addr ;addr=0..$FFFF), MEM=addr arg16 = GetAddrWithMode(ADDRMODE_ABS); SubWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcIzx() * Purpose: Execute SBC opcode, IZX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcIzx() { unsigned short arg16 = 0; // SuBtract with Carry, Indexed Indirect // ($E1 arg : SBC (arg,X) ;arg=0..$FF), MEM=&(arg+X) arg16 = GetAddrWithMode(ADDRMODE_IZX); SubWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcIzy() * Purpose: Execute SBC opcode, IZY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcIzy() { unsigned short arg16 = 0; // SuBtract with Carry, Indirect Indexed // ($F1 arg : SBC (arg),Y ;arg=0..$FF), MEM=&arg+Y arg16 = GetAddrWithMode(ADDRMODE_IZY); if (mReg.PageBoundary) mReg.CyclesLeft++; SubWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcZpx() * Purpose: Execute SBC opcode, ZPX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcZpx() { unsigned short arg16 = 0; // 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)); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcAby() * Purpose: Execute SBC opcode, ABY addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcAby() { unsigned short arg16 = 0; // SuBtract with Carry, Absolute Indexed, Y // ($F9 addrlo addrhi : SBC addr,Y ;addr=0..$FFFF), MEM=addr+Y arg16 = GetAddrWithMode(ADDRMODE_ABY); if (mReg.PageBoundary) mReg.CyclesLeft++; SubWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcAbx() * Purpose: Execute SBC opcode, ABX addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcAbx() { unsigned short arg16 = 0; // SuBtract with Carry, Absolute Indexed, X // ($FD addrlo addrhi : SBC addr,X ;addr=0..$FFFF), MEM=addr+X arg16 = GetAddrWithMode(ADDRMODE_ABX); if (mReg.PageBoundary) mReg.CyclesLeft++; SubWithCarry(mpMem->Peek8bit(arg16)); } /* *-------------------------------------------------------------------- * Method: OpCodeSbcImm() * Purpose: Execute SBC opcode, IMM addressing mode. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeSbcImm() { // SuBtract with Carry, Immediate ($E9 arg : SBC #arg ;arg=0..$FF), // MEM=PC+1 mReg.LastArg = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); SubWithCarry(mReg.LastArg); } /* *-------------------------------------------------------------------- * Method: OpCodeDud() * Purpose: Dummy opcode method - do nothing. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::OpCodeDud() { // this method body left intentionally empty } /* *-------------------------------------------------------------------- * Method: ExecOpcode() * Purpose: Execute VM's opcode. * Arguments: memaddr - address of code in virtual memory. * Returns: Pointer to CPU registers and flags structure. * NOTE: * Single call to this routine is considered a single CPU cycle. * All opcodes take more than one cycle to complete, so this * method employs counting the cycles, which are decremented * in mReg.CyclesLeft counter. When mReg.CyclesLeft reaches 0 * the opcode execution can be condidered completed. *-------------------------------------------------------------------- */ Regs *MKCpu::ExecOpcode(unsigned short memaddr) { mReg.PtrAddr = memaddr; mReg.LastAddr = memaddr; unsigned char opcode = OPCODE_BRK; // skip until all the cycles were completed if (mReg.CyclesLeft > 0) { mReg.CyclesLeft--; return &mReg; } // if no IRQ waiting, get the next opcode and advance PC if (!mReg.IrqPending) { opcode = mpMem->Peek8bit(mReg.PtrAddr++); } // load CPU instruction details from map OpCode instrdet = mOpCodesMap[(eOpCodes)opcode]; 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; string s = (instrdet.amf.length() > 0 ? instrdet.amf.c_str() : "???"); if (s.compare("ILL") == 0) { // trap any illegal opcode mReg.SoftIrq = true; } else { // execute legal opcode mReg.CyclesLeft = instrdet.time - 1; OpCodeHdlrFn pfun = instrdet.pfun; if (NULL != pfun) (this->*pfun)(); } 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; } /* *-------------------------------------------------------------------- * Method: Reset() * Purpose: Reset CPU (initiate reset sequence like in real CPU). * Reset vector must point to valid code entry address. * This routine will not start executing code (it is up * to calling function, like from VM), but will initialize * relevant flags (IRQ) and CPU registers (PC). * Also, the internal flag mExitAtLastRTS will be disabled. * Interrupts are by default disabled (SEI) after reset * process. It is programmers responsibility to enable * interrupts if IRQ/BRK is to be used during bootstrap * routine. Programmer should also initialize the stack. * So under the address pointed by $FFFC vector, code * should reside that: * - initializes stack * - initializes I/O * - sets arithmetic mode (CLD or SED) * - enable interrupts (if needed) * - jumps to the never-ending loop/main program. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::Reset() { mExitAtLastRTS = false; SetFlag(true, FLAGS_IRQ); mReg.PtrAddr = mpMem->Peek16bit(0xFFFC); } /* *-------------------------------------------------------------------- * Method: SetRegs() * Purpose: Set CPU registers and flags. * Arguments: Regs structure - pre-defined CPU status. * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::SetRegs(Regs r) { mReg.Acc = r.Acc; mReg.IndX = r.IndX; mReg.IndY = r.IndY; mReg.PtrAddr = r.PtrAddr; mReg.PtrStack = r.PtrStack; mReg.Flags = r.Flags; } /* *-------------------------------------------------------------------- * Method: Interrupt() * Purpose: Interrupt ReQuest. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- */ void MKCpu::Interrupt() { mReg.IrqPending = true; } } // namespace MKBasic