From 0d47565b9dedb83a1363e69eb87a2e7440970b97 Mon Sep 17 00:00:00 2001 From: Marek Karcz Date: Sun, 17 Apr 2016 22:54:35 -0400 Subject: [PATCH] Multiple changes/general development. Reset option in dbg console. RESET keyword in memory definition file. Command line arguments added. Save snapshot added. Refactoring - huge switch replaced with array of methods. IRQ support. Cycle accurate emulation. Intel HEX format support. --- MKCpu.cpp | 5985 ++++++++++++++++++++++++++++--------------- MKCpu.h | 1094 ++++---- Memory.cpp | 5 - ReadMe.txt | 317 ++- VMachine.cpp | 435 +++- VMachine.h | 14 +- bin2hex.c | 151 +- ehbas.dat | 11 +- main.cpp | 283 +- makefile | 2 +- microchess.dat | 2 +- test_char_io_01.dat | 6 +- tinybasic.asm | 157 +- tinybasic.cfg | 40 + 14 files changed, 5852 insertions(+), 2650 deletions(-) create mode 100644 tinybasic.cfg diff --git a/MKCpu.cpp b/MKCpu.cpp index 2973d49..7d35f92 100644 --- a/MKCpu.cpp +++ b/MKCpu.cpp @@ -1,2012 +1,3973 @@ - -#include -#include "MKCpu.h" -#include "MKGenException.h" - -namespace MKBasic { - -/* - *-------------------------------------------------------------------- - * Method: - * Purpose: - * Arguments: - * Returns: - *-------------------------------------------------------------------- - */ - -/* - *-------------------------------------------------------------------- - * Method: MKCpu() - * Purpose: Default constructor. - * Arguments: n/a - * Returns: n/a - *-------------------------------------------------------------------- - */ -MKCpu::MKCpu() -{ - InitCpu(); -} - -/* - *-------------------------------------------------------------------- - * Method: MKCpu() - * Purpose: Custom constructor. - * Arguments: pmem - pointer to Memory object. - * Returns: n/a - *-------------------------------------------------------------------- - */ -MKCpu::MKCpu(Memory *pmem) -{ - mpMem = pmem; - InitCpu(); -} - -/* - *-------------------------------------------------------------------- - * Method: InitCpu() - * Purpose: Initialize internal variables and flags. - * Arguments: n/a - * Returns: n/a - *-------------------------------------------------------------------- - ADDRMODE_IMM = 0, - ADDRMODE_ABS, - ADDRMODE_ZP, - ADDRMODE_IMP, - ADDRMODE_IND, - ADDRMODE_ABX, - ADDRMODE_ABY, - ADDRMODE_ZPX, - ADDRMODE_ZPY, - ADDRMODE_IZX, - ADDRMODE_IZY, - ADDRMODE_REL, - ADDRMODE_ACC, - */ -void MKCpu::InitCpu() -{ - string saArgFmtTbl[] = {"#$%02x", "$%04x", "$%02x", " ", "($%04x)", "$%04x,X", "$%04x,Y", - "$%02x,X", "$%02x,Y", "($%02x,X)", "($%02x),Y", "$%04x", " ", " "}; - int naAddrModesLen[] = {2, 3, 2, 1, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1}; - // Initialize instructions lengths table based on addressing modes - // Initialize assembly argument formats table based on addressing modes - for (int i=0; i $FFF0) - mpMem->Poke8bit(0xFFFE,0xF0); // LSB - mpMem->Poke8bit(0xFFFF,0xFF); // MSB - // Put RTI opcode at BRK procedure address. - mpMem->Poke8bit(0xFFF0, OPCODE_RTI); - // Set default RESET vector ($FFFC -> $0200) - mpMem->Poke8bit(0xFFFC,0x00); // LSB - mpMem->Poke8bit(0xFFFD,0x02); // MSB - // Set BRK code at the RESET procedure address. - mpMem->Poke8bit(0x0200,OPCODE_BRK); -} - -/* - *-------------------------------------------------------------------- - * Method: SetFlags() - * Purpose: Set CPU status flags ZERO and SIGN based on Acc, X or Y - * Arguments: n/a - * Returns: n/a - *-------------------------------------------------------------------- - */ -void MKCpu::SetFlags(unsigned char reg) -{ - SetFlag((0 == reg), FLAGS_ZERO); - SetFlag(((reg & FLAGS_SIGN) == FLAGS_SIGN), FLAGS_SIGN); - SetFlag(true, FLAGS_UNUSED); -} - -/* - *-------------------------------------------------------------------- - * Method: ShiftLeft() - * Purpose: Arithmetic shift left (1 bit), set Carry flag, shift 0 - * into bit #0. Update flags NZ. - * Arguments: arg8 - 8-bit value - * Returns: 8-bit value after shift - *-------------------------------------------------------------------- - */ -unsigned char MKCpu::ShiftLeft(unsigned char arg8) -{ - // set Carry flag based on original bit #7 - SetFlag(((arg8 & FLAGS_SIGN) == FLAGS_SIGN), FLAGS_CARRY); - arg8 = arg8 << 1; // shift left - arg8 &= 0xFE; // shift 0 into bit #0 - - SetFlags(arg8); - - return arg8; -} - -/* - *-------------------------------------------------------------------- - * Method: ShiftRight() - * Purpose: Logical Shift Right, update flags NZC. - * Arguments: arg8 - byte value - * Returns: unsigned char (byte) - after shift - *-------------------------------------------------------------------- - */ -unsigned char MKCpu::ShiftRight(unsigned char arg8) -{ - SetFlag(((arg8 & 0x01) == 0x01), FLAGS_CARRY); - arg8 = arg8 >> 1; - arg8 &= 0x7F; // unset bit #7 - SetFlags(arg8); - - return arg8; -} - -/* - *-------------------------------------------------------------------- - * Method: RotateLeft() - * Purpose: Rotate left, Carry to bit 0, bit 7 to Carry, update - flags N and Z. - * Arguments: arg8 - byte value to rotate - * Returns: unsigned char (byte) - rotated value - *-------------------------------------------------------------------- - */ -unsigned char MKCpu::RotateLeft(unsigned char arg8) -{ - unsigned char tmp8 = 0; - - tmp8 = arg8; - arg8 = arg8 << 1; - // Carry goes to bit #0. - if (mReg.Flags & FLAGS_CARRY) { - arg8 |= 0x01; - } else { - arg8 &= 0xFE; - } - // Original (before ROL) bit #7 goes to Carry. - SetFlag(((tmp8 & FLAGS_SIGN) == FLAGS_SIGN), FLAGS_CARRY); - SetFlags(arg8); // set flags Z and N - - return arg8; -} - -/* - *-------------------------------------------------------------------- - * Method: RotateRight() - * Purpose: Rotate Right, Carry to bit 7, bit 0 to Carry, update - flags N and Z. - * Arguments: arg8 - byte value to rotate - * Returns: unsigned char (byte) - rotated value - *-------------------------------------------------------------------- - */ -unsigned char MKCpu::RotateRight(unsigned char arg8) -{ - unsigned char tmp8 = 0; - - tmp8 = arg8; - arg8 = arg8 >> 1; - // Carry goes to bit #7. - if (CheckFlag(FLAGS_CARRY)) { - arg8 |= 0x80; - } else { - arg8 &= 0x7F; - } - // Original (before ROR) bit #0 goes to Carry. - SetFlag(((tmp8 & 0x01) == 0x01), FLAGS_CARRY); - SetFlags(arg8); // set flags Z and N - - return arg8; -} - -/* - *-------------------------------------------------------------------- - * Method: GetArg16() - * Purpose: Get 2-byte argument, add offset, increase PC. - * Arguments: addr - address of argument in memory - * offs - offset to be added to returned value - * Returns: 16-bit address - *-------------------------------------------------------------------- - */ -unsigned short MKCpu::GetArg16(unsigned char offs) -{ - unsigned short ret = 0; - - ret = mpMem->Peek16bit(mReg.PtrAddr++); - mReg.PtrAddr++; - ret += offs; - - return ret; -} - -/* - *-------------------------------------------------------------------- - * Method: LogicOpAcc() - * Purpose: Perform logical bitwise operation between memory - * location and Acc, result in Acc. Set flags. - * Arguments: addr - memory location - * logop - logical operation code: LOGOP_OR, LOGOP_AND, - * LOGOP_EOR - * Returns: n/a - *-------------------------------------------------------------------- - */ -void MKCpu::LogicOpAcc(unsigned short addr, int logop) -{ - unsigned char val = 0; - - val = mpMem->Peek8bit(addr); - switch (logop) { - case LOGOP_OR: - mReg.Acc |= val; - break; - case LOGOP_AND: - mReg.Acc &= val; - break; - case LOGOP_EOR: - mReg.Acc ^= val; - break; - default: - break; - } - SetFlags(mReg.Acc); -} - -/* - *-------------------------------------------------------------------- - * Method: ComputeRelJump() - * Purpose: Compute new PC based on relative offset. - * Arguments: offs - relative offset [-128 ($80)..127 ($7F)] - * Returns: unsigned short - new PC (Program Counter). - * NOTE: - * Program Counter (http://www.6502.org/tutorials/6502opcodes.html#PC) - * When the 6502 is ready for the next instruction it increments the - * program counter before fetching the instruction. Once it has the op - * code, it increments the program counter by the length of the - * operand, if any. This must be accounted for when calculating - * branches or when pushing bytes to create a false return address - * (i.e. jump table addresses are made up of addresses-1 when it is - * intended to use an RTS rather than a JMP). - * The program counter is loaded least signifigant byte first. - * Therefore the most signifigant byte must be pushed first when - * creating a false return address. - * When calculating branches a forward branch of 6 skips the following - * 6 bytes so, effectively the program counter points to the address - * that is 8 bytes beyond the address of the branch opcode; - * and a backward branch of $FA (256-6) goes to an address 4 bytes - * before the branch instruction. - *-------------------------------------------------------------------- - */ -unsigned short MKCpu::ComputeRelJump(unsigned char offs) -{ - // NOTE: mReg.PtrAddr (PC) must be at the next opcode (after branch instr.) at this point. - return ComputeRelJump(mReg.PtrAddr, offs); -} - -/* - *-------------------------------------------------------------------- - * Method: ComputeRelJump() - * Purpose: Compute new address after branch based on relative - * offset. - * Arguments: addr - next opcode address (after branch instr.) - * offs - relative offset [-128 ($80)..127 ($7F)] - * Returns: unsigned short - new address for branch jump - *-------------------------------------------------------------------- - */ -unsigned short MKCpu::ComputeRelJump(unsigned short addr, unsigned char offs) -{ - unsigned short newpc = addr; - - if (offs < 0x80) { - newpc += (unsigned short) offs; - } else { - newpc -= (unsigned short) ((unsigned char)(~offs + 1)); // use binary 2's complement instead of arithmetics - } - - return newpc; -} - -/* - *-------------------------------------------------------------------- - * Method: Conv2Bcd() - * Purpose: Convert 16-bit unsigned number to 8-bit BCD - * representation. - * Arguments: v - 16-bit unsigned integer. - * Returns: byte representing BCD code of the 'v'. - *-------------------------------------------------------------------- - */ -unsigned char MKCpu::Conv2Bcd(unsigned short v) -{ - unsigned char arg8 = 0; - arg8 = (unsigned char)((v/10) << 4); - arg8 |= ((unsigned char)(v - (v/10)*10)) & 0x0F; - return arg8; -} - -/* - *-------------------------------------------------------------------- - * Method: Bcd2Num() - * Purpose: Convert 8-bit BCD code to a number. - * Arguments: v - BCD code. - * Returns: 16-bit unsigned integer - *-------------------------------------------------------------------- - */ -unsigned short MKCpu::Bcd2Num(unsigned char v) -{ - unsigned short ret = 0; - ret = 10 * ((v & 0xF0) >> 4) + (unsigned char)(v & 0x0F); - return ret; -} - -/* - *-------------------------------------------------------------------- - * Method: CheckFlag() - * Purpose: Check if given bit flag in CPU status register is set - * or not. - * Arguments: flag - byte with the bit to be checked set and other - * bits not set. - * Returns: bool - *-------------------------------------------------------------------- - */ -bool MKCpu::CheckFlag(unsigned char flag) -{ - return ((mReg.Flags & flag) == flag); -} - -/* - *-------------------------------------------------------------------- - * Method: SetFlag() - * Purpose: Set or unset CPU status flag. - * Arguments: set - if true, set flag, if false, unset flag. - * flag - a byte with a bit set on the position of flag - * being altered and zeroes on other positions. - * Returns: n/a - *-------------------------------------------------------------------- - */ -void MKCpu::SetFlag(bool set, unsigned char flag) -{ - if (set) { - mReg.Flags |= flag; - } else { - mReg.Flags &= ~flag; - } -} - -/* - *-------------------------------------------------------------------- - * Method: AddWithCarry() - * Purpose: Add Acc + Mem with Carry, update flags and Acc. - * Arguments: mem8 - memory argument (byte) - * Returns: byte value Acc + Mem + Carry - *-------------------------------------------------------------------- - */ -unsigned char MKCpu::AddWithCarry(unsigned char mem8) -{ - // This algorithm was adapted from Frodo emulator code. - unsigned short utmp16 = mReg.Acc + mem8 + (CheckFlag(FLAGS_CARRY) ? 1 : 0); - if (CheckFlag(FLAGS_DEC)) { // BCD mode - - unsigned short al = (mReg.Acc & 0x0F) + (mem8 & 0x0F) + (CheckFlag(FLAGS_CARRY) ? 1 : 0); - if (al > 9) al += 6; - unsigned short ah = (mReg.Acc >> 4) + (mem8 >> 4); - if (al > 0x0F) ah++; - SetFlag((utmp16 == 0), FLAGS_ZERO); - SetFlag(((((ah << 4) ^ mReg.Acc) & 0x80) && !((mReg.Acc ^ mem8) & 0x80)), FLAGS_OVERFLOW); - if (ah > 9) ah += 6; - SetFlag((ah > 0x0F), FLAGS_CARRY); - mReg.Acc = (ah << 4) | (al & 0x0f); - SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); - } else { // binary mode - - SetFlag((utmp16 > 0xff), FLAGS_CARRY); - SetFlag((!((mReg.Acc ^ mem8) & 0x80) && ((mReg.Acc ^ utmp16) & 0x80)), FLAGS_OVERFLOW); - mReg.Acc = utmp16 & 0xFF; - SetFlag((mReg.Acc == 0), FLAGS_ZERO); - SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); - } - SetFlag(true, FLAGS_UNUSED); - return mReg.Acc; -} - -/* - *-------------------------------------------------------------------- - * Method: SubWithCarry() - * Purpose: Subtract Acc - Mem with Carry, update flags and Acc. - * Arguments: mem8 - memory argument (byte) - * Returns: byte value Acc - Mem - Carry - *-------------------------------------------------------------------- - */ -unsigned char MKCpu::SubWithCarry(unsigned char mem8) -{ - unsigned short utmp16 = mReg.Acc - mem8 - (CheckFlag(FLAGS_CARRY) ? 0 : 1); - - // This algorithm was adapted from Frodo emulator code. - if (CheckFlag(FLAGS_DEC)) { // BCD mode - - unsigned char al = (mReg.Acc & 0x0F) - (mem8 & 0x0F) - (CheckFlag(FLAGS_CARRY) ? 0 : 1); - unsigned char ah = (mReg.Acc >> 4) - (mem8 >> 4); - if (al & 0x10) { - al -= 6; ah--; - } - if (ah & 0x10) ah -= 6; - SetFlag((utmp16 < 0x100), FLAGS_CARRY); - SetFlag(((mReg.Acc ^ utmp16) & 0x80) && ((mReg.Acc ^ mem8) & 0x80), FLAGS_OVERFLOW); - SetFlag((utmp16 == 0), FLAGS_ZERO); - mReg.Acc = (ah << 4) | (al & 0x0f); - SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); - - } else { // binary mode - - SetFlag((utmp16 < 0x100), FLAGS_CARRY); - SetFlag(((mReg.Acc ^ utmp16) & 0x80) && ((mReg.Acc ^ mem8) & 0x80), FLAGS_OVERFLOW); - mReg.Acc = utmp16 & 0xFF; - SetFlag((mReg.Acc == 0), FLAGS_ZERO); - SetFlag((mReg.Acc & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); - - } - SetFlag(true, FLAGS_UNUSED); - return mReg.Acc; -} - -/* - *-------------------------------------------------------------------- - * Method: ~MKCpu() - * Purpose: Class destructor. - * Arguments: n/a - * Returns: n/a - *-------------------------------------------------------------------- - */ -MKCpu::~MKCpu() -{ - if (mLocalMem) { - if (NULL != mpMem) - delete mpMem; - } -} - -/* - *-------------------------------------------------------------------- - * Method: GetAddrWithMode() - * Purpose: Get address of the argument with specified mode. - * Increment PC. - * Arguments: mode - code of the addressing mode, see eAddrModes. - * Returns: 16-bit address - *-------------------------------------------------------------------- - */ -unsigned short MKCpu::GetAddrWithMode(int mode) -{ - unsigned short arg16 = 0; - - mReg.LastAddrMode = mode; - switch (mode) { - - case ADDRMODE_IMM: - arg16 = mReg.PtrAddr++; - break; - - case ADDRMODE_ABS: - mReg.LastArg = arg16 = GetArg16(0); - break; - - case ADDRMODE_ZP: - mReg.LastArg = arg16 = (unsigned short) mpMem->Peek8bit(mReg.PtrAddr++); - break; - - case ADDRMODE_IMP: - // DO NOTHING - implied mode operates on internal CPU register - break; - - case ADDRMODE_IND: - mReg.LastArg = arg16 = mpMem->Peek16bit(mReg.PtrAddr++); - arg16 = mpMem->Peek16bit(arg16); - break; - - case ADDRMODE_ABX: - arg16 = GetArg16(mReg.IndX); - mReg.LastArg = arg16 - mReg.IndX; - break; - - case ADDRMODE_ABY: - arg16 = GetArg16(mReg.IndY); - mReg.LastArg = arg16 - mReg.IndY; - break; - - case ADDRMODE_ZPX: - mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); - arg16 = (arg16 + mReg.IndX) & 0xFF; - break; - - case ADDRMODE_ZPY: - mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); - arg16 = (arg16 + mReg.IndY) & 0xFF; - break; - - case ADDRMODE_IZX: - mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); - arg16 = (arg16 + mReg.IndX) & 0xFF; - arg16 = mpMem->Peek16bit(arg16); - break; - - case ADDRMODE_IZY: - mReg.LastArg = arg16 = mpMem->Peek8bit(mReg.PtrAddr++); - arg16 = mpMem->Peek16bit(arg16) + mReg.IndY; - break; - - case ADDRMODE_REL: - mReg.LastArg = arg16 = ComputeRelJump(mpMem->Peek8bit(mReg.PtrAddr++)); - break; - - case ADDRMODE_ACC: - // DO NOTHING - acc mode operates on internal CPU register - break; - - default: - throw MKGenException("ERROR: Wrong addressing mode!"); - break; - } - - return arg16; -} - -/* - *-------------------------------------------------------------------- - * Method: GetArgWithMode() - * Purpose: Get argument from address with specified mode. - * Arguments: addr - address in memory - * mode - code of the addressing mode, see eAddrModes. - * Returns: argument - *-------------------------------------------------------------------- - */ -unsigned short MKCpu::GetArgWithMode(unsigned short addr, int mode) -{ - unsigned short arg16 = 0; - - switch (mode) { - - case ADDRMODE_IMM: - arg16 = mpMem->Peek8bit(addr); - break; - - case ADDRMODE_ABS: - arg16 = mpMem->Peek16bit(addr); - break; - - case ADDRMODE_ZP: - arg16 = (unsigned short) mpMem->Peek8bit(addr); - break; - - case ADDRMODE_IMP: - // DO NOTHING - implied mode operates on internal CPU register - break; - - case ADDRMODE_IND: - arg16 = mpMem->Peek16bit(addr); - break; - - case ADDRMODE_ABX: - arg16 = mpMem->Peek16bit(addr); - break; - - case ADDRMODE_ABY: - arg16 = mpMem->Peek16bit(addr); - break; - - case ADDRMODE_ZPX: - arg16 = mpMem->Peek8bit(addr); - break; - - case ADDRMODE_ZPY: - arg16 = mpMem->Peek8bit(addr); - break; - - case ADDRMODE_IZX: - arg16 = mpMem->Peek8bit(addr); - break; - - case ADDRMODE_IZY: - arg16 = mpMem->Peek8bit(addr); - break; - - case ADDRMODE_REL: - arg16 = ComputeRelJump(addr+1, mpMem->Peek8bit(addr)); - break; - - case ADDRMODE_ACC: - // DO NOTHING - acc mode operates on internal CPU register - break; - - default: - break; - } - - return arg16; -} - -/* - *-------------------------------------------------------------------- - * Method: Disassemble() - * Purpose: Disassemble instruction and argument per addressing mode - * Arguments: n/a - internal - * Returns: 0 - *-------------------------------------------------------------------- - */ -unsigned short MKCpu::Disassemble() -{ - char sArg[40]; - char sFmt[20]; - - strcpy(sFmt, "%s "); - strcat(sFmt, mArgFmtTbl[mReg.LastAddrMode].c_str()); - sprintf(sArg, sFmt, - ((mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.length() > 0 - ? (mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.c_str() : "???"), - mReg.LastArg); - for (unsigned int i=0; iPeek8bit(addr++); - addrmode = (mOpCodesMap[(eOpCodes)opcode]).amf.length() > 0 - ? (mOpCodesMap[(eOpCodes)opcode]).addrmode : -1; - - if (addrmode < 0 || NULL == instrbuf) return 0; - switch (mAddrModesLen[addrmode]) - { - case 2: - sprintf(sBuf, "$%02x ", mpMem->Peek8bit(addr)); - break; - - case 3: - sprintf(sBuf, "$%02x $%02x", mpMem->Peek8bit(addr), mpMem->Peek8bit(addr+1)); - break; - - default: - strcpy(sBuf, " "); - break; - } - strcpy(sFmt, "$%04x: $%02x %s %s "); - strcat(sFmt, mArgFmtTbl[addrmode].c_str()); - sprintf(sArg, sFmt, opcaddr, mpMem->Peek8bit(opcaddr), sBuf, - ((mOpCodesMap[(eOpCodes)opcode]).amf.length() > 0 - ? (mOpCodesMap[(eOpCodes)opcode]).amf.c_str() : "???"), - GetArgWithMode(addr,addrmode)); - for (unsigned int i=0; iPeek8bit(mReg.PtrAddr++); - unsigned char arg8 = 0; - unsigned short arg16 = 0; - - SetFlag(false, FLAGS_BRK); // reset BRK flag - we want to detect - mReg.SoftIrq = false; // software interrupt each time it happens - // (non-maskable) - mReg.LastRTS = false; - mReg.LastOpCode = opcode; - mReg.LastAddrMode = ADDRMODE_UND; - mReg.LastArg = 0; - - switch (opcode) { - - case OPCODE_BRK: // software interrupt, Implied ($00 : BRK) - mReg.LastAddrMode = ADDRMODE_IMP; - if (!CheckFlag(FLAGS_IRQ)) { // only if IRQ not masked - arg16 = 0x100; - arg16 += mReg.PtrStack--; - // Note that BRK is really a 2-bytes opcode. Each BRK opcode should be padded by extra byte, - // because the return address put on stack is PC + 1 (where PC is the next address after BRK). - // That means the next opcode after BRK will not be executed upon return from interrupt, - // but the next after that will be. - mReg.PtrAddr++; - mpMem->Poke8bit(arg16, (unsigned char) (((mReg.PtrAddr) & 0xFF00) >> 8)); // HI(PC+1) - HI part of next instr. addr. + 1 - arg16 = 0x100; - arg16 += mReg.PtrStack--; - mpMem->Poke8bit(arg16, (unsigned char) ((mReg.PtrAddr) & 0x00FF)); // LO(PC+1) - LO part of next instr. addr. + 1 - arg16 = 0x100; - arg16 += mReg.PtrStack--; - SetFlag(true, FLAGS_BRK); // The BRK flag that goes on stack is set. - mpMem->Poke8bit(arg16, mReg.Flags); - SetFlag(false, FLAGS_BRK); // The BRK flag that remains in CPU status is unchanged, so unset after putting on stack. - //mReg.SoftIrq = true; - mReg.PtrAddr = mpMem->Peek16bit(0xFFFE); // Load BRK vector into the PC. - SetFlag(true,FLAGS_IRQ); - } else { - mReg.PtrAddr++; - } - break; - - case OPCODE_NOP: // NO oPeration, Implied ($EA : NOP) - mReg.LastAddrMode = ADDRMODE_IMP; - break; - - case OPCODE_LDA_IZX: // LoaD Accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - mReg.Acc = mpMem->Peek8bit(arg16); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDA_ZP: // LoaD Accumulator, Zero Page ($A5 arg : LDA arg ;arg=0..$FF), MEM=arg - mReg.Acc = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDA_IMM: // LoaD Accumulator, Immediate ($A9 arg : LDA #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = mReg.Acc = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDA_ABS: // LoaD Accumulator, Absolute ($AD addrlo addrhi : LDA addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - mReg.Acc = mpMem->Peek8bit(arg16); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDA_IZY: // LoaD Accumulator, Indirect Indexed ($B1 arg : LDA (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - mReg.Acc = mpMem->Peek8bit(arg16); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDA_ZPX: // LoaD Accumulator, Zero Page Indexed, X ($B5 arg : LDA arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - mReg.Acc = mpMem->Peek8bit(arg16); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDA_ABY: // LoaD Accumulator, Absolute Indexed, Y ($B9 addrlo addrhi : LDA addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - mReg.Acc = mpMem->Peek8bit(arg16); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDA_ABX: // LoaD Accumulator, Absolute Indexed, X ($BD addrlo addrhi : LDA addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - mReg.Acc = mpMem->Peek8bit(arg16); - SetFlags(mReg.Acc); - break; - - case OPCODE_LDX_IMM: // LoaD X register, Immediate ($A2 arg : LDX #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = mReg.IndX = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - SetFlags(mReg.IndX); - break; - - case OPCODE_LDX_ZP: // LoaD X register, Zero Page ($A6 arg : LDX arg ;arg=0..$FF), MEM=arg - mReg.IndX = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); - SetFlags(mReg.IndX); - break; - - case OPCODE_LDX_ABS: // LoaD X register, Absolute ($AE addrlo addrhi : LDX addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - mReg.IndX = mpMem->Peek8bit(arg16); - SetFlags(mReg.IndX); - break; - - case OPCODE_LDX_ZPY: // LoaD X register, Zero Page Indexed, Y ($B6 arg : LDX arg,Y ;arg=0..$FF), MEM=arg+Y - arg16 = GetAddrWithMode(ADDRMODE_ZPY); - mReg.IndX = mpMem->Peek8bit(arg16); - SetFlags(mReg.IndX); - break; - - case OPCODE_LDX_ABY: // LoaD X register, Absolute Indexed, Y ($BE addrlo addrhi : LDX addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - mReg.IndX = mpMem->Peek8bit(arg16); - SetFlags(mReg.IndX); - break; - - case OPCODE_LDY_IMM: // LoaD Y register, Immediate ($A0 arg : LDY #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = mReg.IndY = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - SetFlags(mReg.IndY); - break; - - case OPCODE_LDY_ZP: // LoaD Y register, Zero Page ($A4 arg : LDY arg ;arg=0..$FF), MEM=arg - mReg.IndY = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_ZP)); - SetFlags(mReg.IndY); - break; - - case OPCODE_LDY_ABS: // LoaD Y register, Absolute ($AC addrlo addrhi : LDY addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - mReg.IndY = mpMem->Peek8bit(arg16); - SetFlags(mReg.IndY); - break; - - case OPCODE_LDY_ZPX: // LoaD Y register, Zero Page Indexed, X ($B4 arg : LDY arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - mReg.IndY = mpMem->Peek8bit(arg16); - SetFlags(mReg.IndY); - break; - - case OPCODE_LDY_ABX: // LoaD Y register, Absolute Indexed, X ($BC addrlo addrhi : LDY addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - mReg.IndY = mpMem->Peek8bit(arg16); - SetFlags(mReg.IndY); - break; - - case OPCODE_TAX: // Transfer A to X, Implied ($AA : TAX) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.IndX = mReg.Acc; - SetFlags(mReg.IndX); - break; - - case OPCODE_TAY: // Transfer A to Y, Implied ($A8 : TAY) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.IndY = mReg.Acc; - SetFlags(mReg.IndY); - break; - - case OPCODE_TXA: // Transfer X to A, Implied ($8A : TXA) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.Acc = mReg.IndX; - SetFlags(mReg.Acc); - break; - - case OPCODE_TYA: // Transfer Y to A, Implied ($98 : TYA) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.Acc = mReg.IndY; - SetFlags(mReg.Acc); - break; - - case OPCODE_TSX: // Transfer Stack ptr to X, Implied ($BA : TSX) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.IndX = mReg.PtrStack; - SetFlags(mReg.IndX); - break; - - case OPCODE_TXS: // Transfer X to Stack ptr, Implied ($9A : TXS) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.PtrStack = mReg.IndX; - break; - - case OPCODE_STA_IZX: // STore Accumulator, Indexed Indirect ($81 arg : STA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_STA_ZP: // STore Accumulator, Zero Page ($85 arg : STA arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_STA_ABS: // STore Accumulator, Absolute ($8D addrlo addrhi : STA addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_STA_IZY: // STore Accumulator, Indirect Indexed ($91 arg : STA (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_STA_ZPX: // STore Accumulator, Zero Page Indexed, X ($95 arg : STA arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_STA_ABY: // STore Accumulator, Absolute Indexed, Y ($99 addrlo addrhi : STA addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_STA_ABX: // STore Accumulator, Absolute Indexed, X ($9D addrlo addrhi : STA addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_STX_ZP: // STore X register, Zero Page ($86 arg : STX arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - mpMem->Poke8bit(arg16, mReg.IndX); - break; - - case OPCODE_STX_ABS: // STore X register, Absolute ($8E addrlo addrhi : STX addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - mpMem->Poke8bit(arg16, mReg.IndX); - break; - - case OPCODE_STX_ZPY: // STore X register, Zero Page Indexed, Y ($96 arg : STX arg,Y ;arg=0..$FF), MEM=arg+Y - arg16 = GetAddrWithMode(ADDRMODE_ZPY); - mpMem->Poke8bit(arg16, mReg.IndX); - break; - - case OPCODE_STY_ZP: // STore Y register, Zero Page ($84 arg : STY arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - mpMem->Poke8bit(arg16, mReg.IndY); - break; - - case OPCODE_STY_ABS: // STore Y register, Absolute ($8C addrlo addrhi : STY addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - mpMem->Poke8bit(arg16, mReg.IndY); - break; - - case OPCODE_STY_ZPX: // STore Y register, Zero Page Indexed, X ($94 arg : STY arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - mpMem->Poke8bit(arg16, mReg.IndY); - break; - - case OPCODE_BNE_REL: // Branch on Not Equal, Relative ($D0 signoffs : BNE signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (!CheckFlag(FLAGS_ZERO)) { - mReg.PtrAddr = arg16; - } - break; - - case OPCODE_BEQ_REL: // Branch on EQual, Relative ($F0 signoffs : BEQ signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (CheckFlag(FLAGS_ZERO)) { - mReg.PtrAddr = arg16; - } - break; - - case OPCODE_BPL_REL: // Branch on PLus, Relative ($10 signoffs : BPL signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (!CheckFlag(FLAGS_SIGN)) { - mReg.PtrAddr = arg16; - } - break; - - case OPCODE_BMI_REL: // Branch on MInus, Relative ($30 signoffs : BMI signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (CheckFlag(FLAGS_SIGN)) { - mReg.PtrAddr = arg16; - } - break; - - case OPCODE_BVC_REL: // Branch on oVerflow Clear, Relative ($50 signoffs : BVC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (!CheckFlag(FLAGS_OVERFLOW)) { - mReg.PtrAddr = arg16; - } - break; - - case OPCODE_BVS_REL: // Branch on oVerflow Set, Relative ($70 signoffs : BVS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (CheckFlag(FLAGS_OVERFLOW)) { - mReg.PtrAddr = arg16; - } - break; - - case OPCODE_BCC_REL: // Branch on Carry Clear, Relative ($90 signoffs : BCC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (!CheckFlag(FLAGS_CARRY)) { - mReg.PtrAddr = arg16; - } - break; - - case OPCODE_BCS_REL: // Branch on Carry Set, Relative ($B0 signoffs : BCS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - arg16 = GetAddrWithMode(ADDRMODE_REL); - if (CheckFlag(FLAGS_CARRY)) { - mReg.PtrAddr = arg16; - } - break; - - /*** - case OPCODE_BRA: // branch always to a relative 1-byte address offset -128 ($80)..127 ($7F) (OPCODE_BEQ reladdr : BEQ reladdr) - arg8 = mpMem->Peek8bit(mReg.PtrAddr++); - mReg.PtrAddr = ComputeRelJump(arg8); - break; - ***/ - - case OPCODE_INC_ZP: // INCrement memory, Zero Page ($E6 arg : INC arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16) + 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_INC_ABS: // INCrement memory, Absolute ($EE addrlo addrhi : INC addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16) + 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_INC_ZPX: // INCrement memory, Zero Page Indexed, X ($F6 arg : INC arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - arg8 = mpMem->Peek8bit(arg16) + 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_INC_ABX: // INCrement memory, Absolute Indexed, X ($FE addrlo addrhi : INC addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - arg8 = mpMem->Peek8bit(arg16) + 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_INX: // INcrement X, Implied ($E8 : INX) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.IndX++; - SetFlags(mReg.IndX); - break; - - case OPCODE_DEX: // DEcrement X, Implied ($CA : DEX) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.IndX--; - SetFlags(mReg.IndX); - break; - - case OPCODE_INY: // INcrement Y, Implied ($C8 : INY) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.IndY++; - SetFlags(mReg.IndY); - break; - - case OPCODE_DEY: // DEcrement Y, Implied ($88 : DEY) - mReg.LastAddrMode = ADDRMODE_IMP; - mReg.IndY--; - SetFlags(mReg.IndY); - break; - - case OPCODE_JMP_ABS: // JuMP, Absolute ($4C addrlo addrhi : JMP addr ;addr=0..$FFFF), MEM=addr - mReg.PtrAddr = GetAddrWithMode(ADDRMODE_ABS); - break; - - case OPCODE_JMP_IND: // JuMP, Indirect Absolute ($6C addrlo addrhi : JMP (addr) ;addr=0..FFFF), MEM=&addr - mReg.PtrAddr = GetAddrWithMode(ADDRMODE_IND); - break; - - case OPCODE_ORA_IZX: // bitwise OR with Accumulator, Indexed Indirect ($01 arg : ORA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - LogicOpAcc(arg16, LOGOP_OR); - break; - - case OPCODE_ORA_ZP: // bitwise OR with Accumulator, Zero Page ($05 arg : ORA arg ;arg=0..$FF), MEM=arg - LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_OR); - break; - - case OPCODE_ORA_IMM: // bitwise OR with Accumulator, Immediate ($09 arg : ORA #arg ;arg=0..$FF), MEM=PC+1 - arg16 = GetAddrWithMode(ADDRMODE_IMM); - mReg.LastArg = mpMem->Peek8bit(arg16); - LogicOpAcc(arg16, LOGOP_OR); - break; - - case OPCODE_ORA_ABS: // bitwise OR with Accumulator, Absolute ($0D addrlo addrhi : ORA addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - LogicOpAcc(arg16, LOGOP_OR); - break; - - case OPCODE_ORA_IZY: // bitwise OR with Accumulator, Indirect Indexed ($11 arg : ORA (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - LogicOpAcc(arg16, LOGOP_OR); - break; - - case OPCODE_ORA_ZPX: // bitwise OR with Accumulator, Zero Page Indexed, X ($15 arg : ORA arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - LogicOpAcc(arg16, LOGOP_OR); - break; - - case OPCODE_ORA_ABY: // bitwise OR with Accumulator, Absolute Indexed, Y ($19 addrlo addrhi : ORA addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - LogicOpAcc(arg16, LOGOP_OR); - break; - - case OPCODE_ORA_ABX: // bitwise OR with Accumulator, Absolute Indexed, X ($1D addrlo addrhi : ORA addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - LogicOpAcc(arg16, LOGOP_OR); - break; - - case OPCODE_ASL_ZP: // Arithmetic Shift Left, Zero Page ($06 arg : ASL arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_ASL: // Arithmetic Shift Left, Accumulator ($0A : ASL) - mReg.LastAddrMode = ADDRMODE_ACC; - mReg.Acc = ShiftLeft(mReg.Acc); - break; - - case OPCODE_ASL_ABS: // Arithmetic Shift Left, Absolute ($0E addrlo addrhi : ASL addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_ASL_ZPX: // Arithmetic Shift Left, Zero Page Indexed, X ($16 arg : ASL arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_ASL_ABX: // Arithmetic Shift Left, Absolute Indexed, X ($1E addrlo addrhi : ASL addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_JSR_ABS: // Jump to SubRoutine, Absolute ($20 addrlo addrhi : JSR addr ;addr=0..$FFFF), MEM=addr - // PC - next instruction address - // Push PC-1 on stack (HI, then LO). - // Currently PC (mReg.PtrAddr) is at the 1-st argument of JSR. - // Therefore the actual PC-1 used in calculations equals: mReg.PtrAddr+1 - arg16 = 0x100; - arg16 += mReg.PtrStack--; - mpMem->Poke8bit(arg16, (unsigned char) (((mReg.PtrAddr+1) & 0xFF00) >> 8)); // HI(PC-1) - HI part of next instr. addr. - 1 - arg16 = 0x100; - arg16 += mReg.PtrStack--; - mpMem->Poke8bit(arg16, (unsigned char) ((mReg.PtrAddr+1) & 0x00FF)); // LO(PC-1) - LO part of next instr. addr. - 1 - mReg.PtrAddr = GetAddrWithMode(ADDRMODE_ABS); - break; - - case OPCODE_AND_IZX: // bitwise AND with accumulator, Indexed Indirect ($21 arg : AND (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - LogicOpAcc(arg16, LOGOP_AND); - break; - - case OPCODE_AND_ZP: // bitwise AND with accumulator, Zero Page ($25 arg : AND arg ;arg=0..$FF), MEM=arg - LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_AND); - break; - - case OPCODE_AND_IMM: // bitwise AND with accumulator, Immediate ($29 arg : AND #arg ;arg=0..$FF), MEM=PC+1 - arg16 = GetAddrWithMode(ADDRMODE_IMM); - mReg.LastArg = mpMem->Peek8bit(arg16); - LogicOpAcc(arg16, LOGOP_AND); - break; - - case OPCODE_AND_ABS: // bitwise AND with accumulator, Absolute ($2D addrlo addrhi : AND addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - LogicOpAcc(arg16, LOGOP_AND); - break; - - case OPCODE_AND_IZY: // bitwise AND with accumulator, Indirect Indexed ($31 arg : AND (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - LogicOpAcc(arg16, LOGOP_AND); - break; - - case OPCODE_AND_ZPX: // bitwise AND with accumulator, Zero Page Indexed, X ($35 arg : AND arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - LogicOpAcc(arg16, LOGOP_AND); - break; - - case OPCODE_AND_ABY: // bitwise AND with accumulator, Absolute Indexed, Y ($39 addrlo addrhi : AND addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - LogicOpAcc(arg16, LOGOP_AND); - break; - - case OPCODE_AND_ABX: // bitwise AND with accumulator, Absolute Indexed, X ($3D addrlo addrhi : AND addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - LogicOpAcc(arg16, LOGOP_AND); - break; - - case OPCODE_BIT_ZP: // test BITs, Zero Page ($24 arg : BIT arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((arg8 & FLAGS_OVERFLOW) == FLAGS_OVERFLOW, FLAGS_OVERFLOW); - SetFlag((arg8 & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); - arg8 &= mReg.Acc; - SetFlag((arg8 == 0), FLAGS_ZERO); - break; - - case OPCODE_BIT_ABS: // test BITs, Absolute ($2C addrlo addrhi : BIT addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((arg8 & FLAGS_OVERFLOW) == FLAGS_OVERFLOW, FLAGS_OVERFLOW); - SetFlag((arg8 & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); - arg8 &= mReg.Acc; - SetFlag((arg8 == 0), FLAGS_ZERO); - break; - - case OPCODE_ROL_ZP: // ROtate Left, Zero Page ($26 arg : ROL arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - arg8 = RotateLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_ROL: // ROtate Left, Accumulator ($2A : ROL) - mReg.LastAddrMode = ADDRMODE_ACC; - mReg.Acc = RotateLeft(mReg.Acc); - break; - - case OPCODE_ROL_ABS: // ROtate Left, Absolute ($2E addrlo addrhi : ROL addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - arg8 = RotateLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_ROL_ZPX: // ROtate Left, Zero Page Indexed, X ($36 arg : ROL arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - arg8 = mpMem->Peek8bit(arg16); - arg8 = RotateLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_ROL_ABX: // ROtate Left, Absolute Indexed, X ($3E addrlo addrhi : ROL addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - arg8 = mpMem->Peek8bit(arg16); - arg8 = RotateLeft(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_PHP: // PusH Processor status on Stack, Implied ($08 : PHP) - mReg.LastAddrMode = ADDRMODE_IMP; - arg16 = 0x100; - arg16 += mReg.PtrStack--; - arg8 = mReg.Flags | FLAGS_BRK | FLAGS_UNUSED; - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_PHA: // PusH Accumulator, Implied ($48 : PHA) - mReg.LastAddrMode = ADDRMODE_IMP; - arg16 = 0x100; - arg16 += mReg.PtrStack--; - mpMem->Poke8bit(arg16, mReg.Acc); - break; - - case OPCODE_PLP: // PuLl Processor status, Implied ($28 : PLP) - mReg.LastAddrMode = ADDRMODE_IMP; - arg16 = 0x100; - arg16 += ++mReg.PtrStack; - mReg.Flags = mpMem->Peek8bit(arg16) | FLAGS_UNUSED; - break; - - case OPCODE_PLA: // PuLl Accumulator, Implied ($68 : PLA) - mReg.LastAddrMode = ADDRMODE_IMP; - arg16 = 0x100; - arg16 += ++mReg.PtrStack; - mReg.Acc = mpMem->Peek8bit(arg16); - SetFlags(mReg.Acc); - break; - - case OPCODE_CLC: // CLear Carry, Implied ($18 : CLC) - mReg.LastAddrMode = ADDRMODE_IMP; - SetFlag(false, FLAGS_CARRY); - break; - - case OPCODE_SEC: // SEt Carry, Implied ($38 : SEC) - mReg.LastAddrMode = ADDRMODE_IMP; - SetFlag(true, FLAGS_CARRY); - break; - - case OPCODE_CLI: // CLear Interrupt, Implied ($58 : CLI) - mReg.LastAddrMode = ADDRMODE_IMP; - SetFlag(false, FLAGS_IRQ); - break; - - case OPCODE_CLV: // CLear oVerflow, Implied ($B8 : CLV) - mReg.LastAddrMode = ADDRMODE_IMP; - SetFlag(false, FLAGS_OVERFLOW); - break; - - case OPCODE_CLD: // CLear Decimal, Implied ($D8 : CLD) - mReg.LastAddrMode = ADDRMODE_IMP; - SetFlag(false, FLAGS_DEC); - break; - - case OPCODE_SED: // SEt Decimal, Implied ($F8 : SED) - mReg.LastAddrMode = ADDRMODE_IMP; - SetFlag(true, FLAGS_DEC); - break; - - case OPCODE_SEI: // SEt Interrupt, Implied ($78 : SEI) - mReg.LastAddrMode = ADDRMODE_IMP; - SetFlag(true, FLAGS_IRQ); - break; - - /* - * RTI retrieves the Processor Status Word (flags) and the Program Counter from the stack in that order - * (interrupts push the PC first and then the PSW). - * Note that unlike RTS, the return address on the stack is the actual address rather than the address-1. - */ - case OPCODE_RTI: // ReTurn from Interrupt, Implied ($40 : RTI) - mReg.LastAddrMode = ADDRMODE_IMP; - arg16 = 0x100; - arg16 += ++mReg.PtrStack; - mReg.Flags = mpMem->Peek8bit(arg16); - SetFlag(true,FLAGS_UNUSED); - arg16++; mReg.PtrStack++; - mReg.PtrAddr = mpMem->Peek8bit(arg16); - arg16++; mReg.PtrStack++; - mReg.PtrAddr += (mpMem->Peek8bit(arg16) * 0x100); - mReg.SoftIrq = CheckFlag(FLAGS_BRK); - SetFlag(false,FLAGS_IRQ); - break; - - case OPCODE_RTS: // ReTurn from Subroutine, Implied ($60 : RTS) - mReg.LastAddrMode = ADDRMODE_IMP; - if (mReg.PtrStack == 0xFF) { - mReg.LastRTS = true; - break; - } - arg16 = 0x100; - arg16 += ++mReg.PtrStack; - mReg.PtrAddr = mpMem->Peek8bit(arg16); - arg16++; mReg.PtrStack++; - mReg.PtrAddr += (mpMem->Peek8bit(arg16) * 0x100); - mReg.PtrAddr++; - break; - - case OPCODE_EOR_IZX: // bitwise Exclusive OR, Indexed Indirect ($41 arg : EOR (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - LogicOpAcc(arg16, LOGOP_EOR); - break; - - case OPCODE_EOR_ZP: // bitwise Exclusive OR, Zero Page ($45 arg : EOR arg ;arg=0..$FF), MEM=arg - LogicOpAcc(GetAddrWithMode(ADDRMODE_ZP), LOGOP_EOR); - break; - - case OPCODE_EOR_IMM: // bitwise Exclusive OR, Immediate ($49 arg : EOR #arg ;arg=0..$FF), MEM=PC+1 - arg16 = GetAddrWithMode(ADDRMODE_IMM); - mReg.LastArg = mpMem->Peek8bit(arg16); - LogicOpAcc(arg16, LOGOP_EOR); - break; - - case OPCODE_EOR_ABS: // bitwise Exclusive OR, Absolute ($4D addrlo addrhi : EOR addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - LogicOpAcc(arg16, LOGOP_EOR); - break; - - case OPCODE_EOR_IZY: // bitwise Exclusive OR, Indirect Indexed ($51 arg : EOR (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - LogicOpAcc(arg16, LOGOP_EOR); - break; - - case OPCODE_EOR_ZPX: // bitwise Exclusive OR, Zero Page Indexed, X ($55 arg : EOR arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - LogicOpAcc(arg16, LOGOP_EOR); - break; - - case OPCODE_EOR_ABY: // bitwise Exclusive OR, Absolute Indexed, Y ($59 addrlo addrhi : EOR addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - LogicOpAcc(arg16, LOGOP_EOR); - break; - - case OPCODE_EOR_ABX: // bitwise Exclusive OR, Absolute Indexed, X ($5D addrlo addrhi : EOR addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - LogicOpAcc(arg16, LOGOP_EOR); - break; - - case OPCODE_LSR_ZP: // Logical Shift Right, Zero Page ($46 arg : LSR arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftRight(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_LSR: // Logical Shift Right, Accumulator ($4A : LSR) - mReg.LastAddrMode = ADDRMODE_ACC; - mReg.Acc = ShiftRight(mReg.Acc); - break; - - case OPCODE_LSR_ABS: // Logical Shift Right, Absolute ($4E addrlo addrhi : LSR addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftRight(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_LSR_ZPX: // Logical Shift Right, Zero Page Indexed, X ($56 arg : LSR arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftRight(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_LSR_ABX: // Logical Shift Right, Absolute Indexed, X ($5E addrlo addrhi : LSR addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - arg8 = mpMem->Peek8bit(arg16); - arg8 = ShiftRight(arg8); - mpMem->Poke8bit(arg16, arg8); - break; - - case OPCODE_ADC_IZX: // ADd with Carry, Indexed Indirect ($61 arg : ADC (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - AddWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_ADC_ZP: // ADd with Carry, Zero Page ($65 arg : ADC arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - AddWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_ADC_IMM: // ADd with Carry, Immediate ($69 arg : ADC #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - AddWithCarry(mReg.LastArg); - break; - - case OPCODE_ADC_ABS: // ADd with Carry, Absolute ($6D addrlo addrhi : ADC addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - AddWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_ADC_IZY: // ADd with Carry, Indirect Indexed ($71 arg : ADC (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - AddWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_ADC_ZPX: // ADd with Carry, Zero Page Indexed, X ($75 arg : ADC arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - AddWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_ADC_ABY: // ADd with Carry, Absolute Indexed, Y ($79 addrlo addrhi : ADC addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - AddWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_ADC_ABX: // ADd with Carry, Absolute Indexed, X ($7D addrlo addrhi : ADC addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - AddWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_ROR_ZP: // ROtate Right, Zero Page ($66 arg : ROR arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - mpMem->Poke8bit(arg16, RotateRight(arg8)); - break; - - case OPCODE_ROR: // ROtate Right, Accumulator ($6A : ROR) - mReg.LastAddrMode = ADDRMODE_ACC; - mReg.Acc = RotateRight(mReg.Acc); - break; - - case OPCODE_ROR_ABS: // ROtate Right, Absolute ($6E addrlo addrhi : ROR addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - mpMem->Poke8bit(arg16, RotateRight(arg8)); - break; - - case OPCODE_ROR_ZPX: // ROtate Right, Zero Page Indexed, X ($76 arg : ROR arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - arg8 = mpMem->Peek8bit(arg16); - mpMem->Poke8bit(arg16, RotateRight(arg8)); - break; - - case OPCODE_ROR_ABX: // ROtate Right, Absolute Indexed, X ($7E addrlo addrhi : ROR addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - arg8 = mpMem->Peek8bit(arg16); - mpMem->Poke8bit(arg16, RotateRight(arg8)); - break; - - case OPCODE_CPY_IMM: // ComPare Y register, Immediate ($C0 arg : CPY #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = arg8 = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - SetFlag((mReg.IndY >= arg8), FLAGS_CARRY); - arg8 = mReg.IndY - arg8; - SetFlags(arg8); - break; - - case OPCODE_CPY_ZP: // ComPare Y register, Zero Page ($C4 arg : CPY arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.IndY >= arg8), FLAGS_CARRY); - arg8 = mReg.IndY - arg8; - SetFlags(arg8); - break; - - case OPCODE_CPY_ABS: // ComPare Y register, Absolute ($CC addrlo addrhi : CPY addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.IndY >= arg8), FLAGS_CARRY); - arg8 = mReg.IndY - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_IZX: // CoMPare accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_ZP: // CoMPare accumulator, Zero Page ($C5 arg : CMP arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_IMM: // CoMPare accumulator, Immediate ($C9 arg : CMP #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = arg8 = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_ABS: // CoMPare accumulator, Absolute ($CD addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_IZY: // CoMPare accumulator, Indirect Indexed ($D1 arg : CMP (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_ZPX: // CoMPare accumulator, Zero Page Indexed, X ($D5 arg : CMP arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_ABY: // CoMPare accumulator, Absolute Indexed, Y ($D9 addrlo addrhi : CMP addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_CMP_ABX: // CoMPare accumulator, Absolute Indexed, X ($DD addrlo addrhi : CMP addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.Acc >= arg8), FLAGS_CARRY); - arg8 = mReg.Acc - arg8; - SetFlags(arg8); - break; - - case OPCODE_DEC_ZP: // DECrement memory, Zero Page ($C6 arg : DEC arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16) - 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_DEC_ABS: // DECrement memory, Absolute ($CE addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16) - 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_DEC_ZPX: // DECrement memory, Zero Page Indexed, X ($D6 arg : DEC arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - arg8 = mpMem->Peek8bit(arg16) - 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_DEC_ABX: // DECrement memory, Absolute Indexed, X ($DE addrlo addrhi : DEC addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - arg8 = mpMem->Peek8bit(arg16) - 1; - mpMem->Poke8bit(arg16, arg8); - SetFlags(arg8); - break; - - case OPCODE_CPX_IMM: // ComPare X register, Immediate ($E0 arg : CPX #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = arg8 = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - SetFlag((mReg.IndX >= arg8), FLAGS_CARRY); - arg8 = mReg.IndX - arg8; - SetFlags(arg8); - break; - - case OPCODE_CPX_ZP: // ComPare X register, Zero Page ($E4 arg : CPX arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.IndX >= arg8), FLAGS_CARRY); - arg8 = mReg.IndX - arg8; - SetFlags(arg8); - break; - - case OPCODE_CPX_ABS: // ComPare X register, Absolute ($EC addrlo addrhi : CPX addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - arg8 = mpMem->Peek8bit(arg16); - SetFlag((mReg.IndX >= arg8), FLAGS_CARRY); - arg8 = mReg.IndX - arg8; - SetFlags(arg8); - break; - - case OPCODE_SBC_ZP: // SuBtract with Carry, Zero Page ($E5 arg : SBC arg ;arg=0..$FF), MEM=arg - arg16 = GetAddrWithMode(ADDRMODE_ZP); - SubWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_SBC_ABS: // SuBtract with Carry, Absolute ($ED addrlo addrhi : SBC addr ;addr=0..$FFFF), MEM=addr - arg16 = GetAddrWithMode(ADDRMODE_ABS); - SubWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_SBC_IZX: // SuBtract with Carry, Indexed Indirect ($E1 arg : SBC (arg,X) ;arg=0..$FF), MEM=&(arg+X) - arg16 = GetAddrWithMode(ADDRMODE_IZX); - SubWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_SBC_IZY: // SuBtract with Carry, Indirect Indexed ($F1 arg : SBC (arg),Y ;arg=0..$FF), MEM=&arg+Y - arg16 = GetAddrWithMode(ADDRMODE_IZY); - SubWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_SBC_ZPX: // SuBtract with Carry, Zero Page Indexed, X ($F5 arg : SBC arg,X ;arg=0..$FF), MEM=arg+X - arg16 = GetAddrWithMode(ADDRMODE_ZPX); - SubWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_SBC_ABY: // SuBtract with Carry, Absolute Indexed, Y ($F9 addrlo addrhi : SBC addr,Y ;addr=0..$FFFF), MEM=addr+Y - arg16 = GetAddrWithMode(ADDRMODE_ABY); - SubWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_SBC_ABX: // SuBtract with Carry, Absolute Indexed, X ($FD addrlo addrhi : SBC addr,X ;addr=0..$FFFF), MEM=addr+X - arg16 = GetAddrWithMode(ADDRMODE_ABX); - SubWithCarry(mpMem->Peek8bit(arg16)); - break; - - case OPCODE_SBC_IMM: // SuBtract with Carry, Immediate ($E9 arg : SBC #arg ;arg=0..$FF), MEM=PC+1 - mReg.LastArg = mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM)); - SubWithCarry(mReg.LastArg); - break; - - default: - // trap illegal opcode that has unidentified behavior - { - string s = ((mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.length() > 0 - ? (mOpCodesMap[(eOpCodes)mReg.LastOpCode]).amf.c_str() : "???"); - if (s.compare("ILL") == 0) { - mReg.SoftIrq = true; - } - } - break; - } - - Disassemble(); - char histentry[80]; - sprintf(histentry, "$%04x: %-16s \t$%02x | $%02x | $%02x | $%02x | $%02x", - mReg.LastAddr, mReg.LastInstr.c_str(), mReg.Acc, mReg.IndX, mReg.IndY, mReg.Flags, mReg.PtrStack); - Add2History(histentry); - - return &mReg; -} - -/* - *-------------------------------------------------------------------- - * Method: GetRegs() - * Purpose: Return pointer to CPU registers and status. - * Arguments: n/a - * Returns: pointer to Regs structure. - *-------------------------------------------------------------------- - */ -Regs *MKCpu::GetRegs() -{ - return &mReg; -} - -/* - *-------------------------------------------------------------------- - * Method: Add2History() - * Purpose: Add entry to execute history. - * Arguments: s - string (entry) - * Returns: n/a - *-------------------------------------------------------------------- - */ -void MKCpu::Add2History(string s) -{ - mExecHistory.push(s); - while (mExecHistory.size() > 20) mExecHistory.pop(); -} - -/* - *-------------------------------------------------------------------- - * Method: GetExecHistory() - * Purpose: Return queue with execute history. - * Arguments: n/a - * Returns: queue - *-------------------------------------------------------------------- - */ -queue MKCpu::GetExecHistory() -{ - return mExecHistory; -} - -} // namespace MKBasic + +#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 diff --git a/MKCpu.h b/MKCpu.h index 3ccdf4f..df2fd6f 100644 --- a/MKCpu.h +++ b/MKCpu.h @@ -1,462 +1,632 @@ -#ifndef MKCPU_H -#define MKCPU_H - -#include -#include -#include -#include "system.h" -#include "Memory.h" - -using namespace std; - -namespace MKBasic { - -#define DISS_BUF_SIZE 60 // disassembled instruction buffer size - -struct Regs { - unsigned char Acc; // 8-bit accumulator - unsigned short Acc16; // 16-bit accumulator - unsigned char IndX; // 8-bit index register X - unsigned char IndY; // 8-bit index register Y - unsigned short Ptr16; // general purpose 16-bit register - unsigned short PtrAddr; // cpu code counter (PC) - current read/write address - unsigned char PtrStack; // 8-bit stack pointer (0-255). - unsigned char Flags; // CPU flags - bool SoftIrq; // true when interrupted with BRK or trapped opcode - bool LastRTS; // true if RTS encountered and stack empty. - unsigned short LastAddr; // PC at the time of previous op-code - string LastInstr; // instruction and argument executed in previous step - int LastOpCode; // op-code of last instruction - unsigned short LastArg; // argument to the last instruction - int LastAddrMode; // addressing mode of last instruction -}; - -/* - * Virtual CPU, 6502 addressing modes: - - +---------------------+--------------------------+ - | mode | assembler format | - +=====================+==========================+ - | Immediate | #aa | - | Absolute | aaaa | - | Zero Page | aa | Note: - | Implied | | - | Indirect Absolute | (aaaa) | aa = 2 hex digits - | Absolute Indexed,X | aaaa,X | as $FF - | Absolute Indexed,Y | aaaa,Y | - | Zero Page Indexed,X | aa,X | aaaa = 4 hex - | Zero Page Indexed,Y | aa,Y | digits as - | Indexed Indirect | (aa,X) | $FFFF - | Indirect Indexed | (aa),Y | - | Relative | aaaa | Can also be - | Accumulator | A | assembler labels - +---------------------+--------------------------+ - -Short notation: - -imm = #$00 -zp = $00 -zpx = $00,X -zpy = $00,Y -izx = ($00,X) -izy = ($00),Y -abs = $0000 -abx = $0000,X -aby = $0000,Y -ind = ($0000) -rel = $0000 (PC-relative) - -See: 6502AssemblyInOneStep.txt for details. - - */ -enum eAddrModes { - 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, - ADDRMODE_UND, // undetermined (for some illegal codes) - ADDRMODE_LENGTH // should be always last -}; -// assumed little-endian order of bytes (start with least significant) -// MEM - memory location from where the value is read/written, -// & - reference operator (e.g.: &addr means: value under addr memory location) -// PC - program counter (PC+1 means - next memory location after opcode) -enum eOpCodes { - OPCODE_BRK = 0x00, // software interrupt, no arguments ($00 : BRK) - - /* full compatibility with 65C02 (illegal opcodes not supported, will be used for extended functions */ - OPCODE_ORA_IZX = 0x01, // bitwise OR with Accumulator, Indexed Indirect ($01 arg : ORA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_ILL_02 = 0x02, // illegal opcode - OPCODE_ILL_03 = 0x03, // illegal opcode - OPCODE_ILL_04 = 0x04, // illegal opcode - OPCODE_ORA_ZP = 0x05, // bitwise OR with Accumulator, Zero Page ($05 arg : ORA arg ;arg=0..$FF), MEM=arg - OPCODE_ASL_ZP = 0x06, // Arithmetic Shift Left, Zero Page ($06 arg : ASL arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_07 = 0x07, // illegal opcode - OPCODE_PHP = 0x08, // PusH Processor status on Stack, Implied ($08 : PHP) - OPCODE_ORA_IMM = 0x09, // bitwise OR with Accumulator, Immediate ($09 arg : ORA #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_ASL = 0x0A, // Arithmetic Shift Left, Accumulator ($0A : ASL) - OPCODE_ILL_0B = 0x0B, // illegal opcode - OPCODE_ILL_0C = 0x0C, // illegal opcode - OPCODE_ORA_ABS = 0x0D, // bitwise OR with Accumulator, Absolute ($0D addrlo addrhi : ORA addr ;addr=0..$FFFF), MEM=addr - OPCODE_ASL_ABS = 0x0E, // Arithmetic Shift Left, Absolute ($0E addrlo addrhi : ASL addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_0F = 0x0F, // illegal opcode - OPCODE_BPL_REL = 0x10, // Branch on PLus, Relative ($10 signoffs : BPL signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_ORA_IZY = 0x11, // bitwise OR with Accumulator, Indirect Indexed ($11 arg : ORA (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_12 = 0x12, // illegal opcode - OPCODE_ILL_13 = 0x13, // illegal opcode - OPCODE_ILL_14 = 0x14, // illegal opcode - OPCODE_ORA_ZPX = 0x15, // bitwise OR with Accumulator, Zero Page Indexed, X ($15 arg : ORA arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ASL_ZPX = 0x16, // Arithmetic Shift Left, Zero Page Indexed, X ($16 arg : ASL arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ILL_17 = 0x17, // illegal opcode - OPCODE_CLC = 0x18, // CLear Carry, Implied ($18 : CLC) - OPCODE_ORA_ABY = 0x19, // bitwise OR with Accumulator, Absolute Indexed, Y ($19 addrlo addrhi : ORA addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_ILL_1A = 0x1A, // illegal opcode - OPCODE_ILL_1B = 0x1B, // illegal opcode - OPCODE_ILL_1C = 0x1C, // illegal opcode - OPCODE_ORA_ABX = 0x1D, // bitwise OR with Accumulator, Absolute Indexed, X ($1D addrlo addrhi : ORA addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ASL_ABX = 0x1E, // Arithmetic Shift Left, Absolute Indexed, X ($1E addrlo addrhi : ASL addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ILL_1F = 0x1F, // illegal opcode - OPCODE_JSR_ABS = 0x20, // Jump to SubRoutine, Absolute ($20 addrlo addrhi : JSR addr ;addr=0..$FFFF), MEM=addr - OPCODE_AND_IZX = 0x21, // bitwise AND with accumulator, Indexed Indirect ($21 arg : AND (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_ILL_22 = 0x22, // illegal opcode - OPCODE_ILL_23 = 0x23, // illegal opcode - OPCODE_BIT_ZP = 0x24, // test BITs, Zero Page ($24 arg : BIT arg ;arg=0..$FF), MEM=arg - OPCODE_AND_ZP = 0x25, // bitwise AND with accumulator, Zero Page ($25 arg : AND arg ;arg=0..$FF), MEM=arg - OPCODE_ROL_ZP = 0x26, // ROtate Left, Zero Page ($26 arg : ROL arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_27 = 0x27, // illegal opcode - OPCODE_PLP = 0x28, // PuLl Processor status, Implied ($28 : PLP) - OPCODE_AND_IMM = 0x29, // bitwise AND with accumulator, Immediate ($29 arg : AND #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_ROL = 0x2A, // ROtate Left, Accumulator ($2A : ROL) - OPCODE_ILL_2B = 0x2B, // illegal opcode - OPCODE_BIT_ABS = 0x2C, // test BITs, Absolute ($2C addrlo addrhi : BIT addr ;addr=0..$FFFF), MEM=addr - OPCODE_AND_ABS = 0x2D, // bitwise AND with accumulator, Absolute ($2D addrlo addrhi : AND addr ;addr=0..$FFFF), MEM=addr - OPCODE_ROL_ABS = 0x2E, // ROtate Left, Absolute ($2E addrlo addrhi : ROL addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_2F = 0x2F, // illegal opcode - OPCODE_BMI_REL = 0x30, // Branch on MInus, Relative ($30 signoffs : BMI signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_AND_IZY = 0x31, // bitwise AND with accumulator, Indirect Indexed ($31 arg : AND (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_32 = 0x32, // illegal opcode - OPCODE_ILL_33 = 0x33, // illegal opcode - OPCODE_ILL_34 = 0x34, // illegal opcode - OPCODE_AND_ZPX = 0x35, // bitwise AND with accumulator, Zero Page Indexed, X ($35 arg : AND arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ROL_ZPX = 0x36, // ROtate Left, Zero Page Indexed, X ($36 arg : ROL arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ILL_37 = 0x37, // illegal opcode - OPCODE_SEC = 0x38, // SEt Carry, Implied ($38 : SEC) - OPCODE_AND_ABY = 0x39, // bitwise AND with accumulator, Absolute Indexed, Y ($39 addrlo addrhi : AND addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_ILL_3A = 0x3A, // illegal opcode - OPCODE_ILL_3B = 0x3B, // illegal opcode - OPCODE_ILL_3C = 0x3C, // illegal opcode - OPCODE_AND_ABX = 0x3D, // bitwise AND with accumulator, Absolute Indexed, X ($3D addrlo addrhi : AND addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ROL_ABX = 0x3E, // ROtate Left, Absolute Indexed, X ($3E addrlo addrhi : ROL addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ILL_3F = 0x3F, // illegal opcode - OPCODE_RTI = 0x40, // ReTurn from Interrupt, Implied ($40 : RTI) - OPCODE_EOR_IZX = 0x41, // bitwise Exclusive OR, Indexed Indirect ($41 arg : EOR (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_ILL_42 = 0x42, // illegal opcode - OPCODE_ILL_43 = 0x43, // illegal opcode - OPCODE_ILL_44 = 0x44, // illegal opcode - OPCODE_EOR_ZP = 0x45, // bitwise Exclusive OR, Zero Page ($45 arg : EOR arg ;arg=0..$FF), MEM=arg - OPCODE_LSR_ZP = 0x46, // Logical Shift Right, Zero Page ($46 arg : LSR arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_47 = 0x47, // illegal opcode - OPCODE_PHA = 0x48, // PusH Accumulator, Implied ($48 : PHA) - OPCODE_EOR_IMM = 0x49, // bitwise Exclusive OR, Immediate ($49 arg : EOR #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_LSR = 0x4A, // Logical Shift Right, Accumulator ($4A : LSR) - OPCODE_ILL_4B = 0x4B, // illegal opcode - OPCODE_JMP_ABS = 0x4C, // JuMP, Absolute ($4C addrlo addrhi : JMP addr ;addr=0..$FFFF), MEM=addr - OPCODE_EOR_ABS = 0x4D, // bitwise Exclusive OR, Absolute ($4D addrlo addrhi : EOR addr ;addr=0..$FFFF), MEM=addr - OPCODE_LSR_ABS = 0x4E, // Logical Shift Right, Absolute ($4E addrlo addrhi : LSR addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_4F = 0x4F, // illegal opcode - OPCODE_BVC_REL = 0x50, // Branch on oVerflow Clear, Relative ($50 signoffs : BVC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_EOR_IZY = 0x51, // bitwise Exclusive OR, Indirect Indexed ($51 arg : EOR (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_52 = 0x52, // illegal opcode - OPCODE_ILL_53 = 0x53, // illegal opcode - OPCODE_ILL_54 = 0x54, // illegal opcode - OPCODE_EOR_ZPX = 0x55, // bitwise Exclusive OR, Zero Page Indexed, X ($55 arg : EOR arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_LSR_ZPX = 0x56, // Logical Shift Right, Zero Page Indexed, X ($56 arg : LSR arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ILL_57 = 0x57, // illegal opcode - OPCODE_CLI = 0x58, // CLear Interrupt, Implied ($58 : CLI) - OPCODE_EOR_ABY = 0x59, // bitwise Exclusive OR, Absolute Indexed, Y ($59 addrlo addrhi : EOR addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_ILL_5A = 0x5A, // illegal opcode - OPCODE_ILL_5B = 0x5B, // illegal opcode - OPCODE_ILL_5C = 0x5C, // illegal opcode - OPCODE_EOR_ABX = 0x5D, // bitwise Exclusive OR, Absolute Indexed, X ($5D addrlo addrhi : EOR addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_LSR_ABX = 0x5E, // Logical Shift Right, Absolute Indexed, X ($5E addrlo addrhi : LSR addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ILL_5F = 0x5F, // illegal opcode - OPCODE_RTS = 0x60, // ReTurn from Subroutine, Implied ($60 : RTS) - OPCODE_ADC_IZX = 0x61, // ADd with Carry, Indexed Indirect ($61 arg : ADC (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_ILL_62 = 0x62, // illegal opcode - OPCODE_ILL_63 = 0x63, // illegal opcode - OPCODE_ILL_64 = 0x64, // illegal opcode - OPCODE_ADC_ZP = 0x65, // ADd with Carry, Zero Page ($65 arg : ADC arg ;arg=0..$FF), MEM=arg - OPCODE_ROR_ZP = 0x66, // ROtate Right, Zero Page ($66 arg : ROR arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_67 = 0x67, // illegal opcode - OPCODE_PLA = 0x68, // PuLl Accumulator, Implied ($68 : PLA) - OPCODE_ADC_IMM = 0x69, // ADd with Carry, Immediate ($69 arg : ADC #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_ROR = 0x6A, // ROtate Right, Accumulator ($6A : ROR) - OPCODE_ILL_6B = 0x6B, // illegal opcode - OPCODE_JMP_IND = 0x6C, // JuMP, Indirect Absolute ($6C addrlo addrhi : JMP (addr) ;addr=0..FFFF), MEM=&addr - OPCODE_ADC_ABS = 0x6D, // ADd with Carry, Absolute ($6D addrlo addrhi : ADC addr ;addr=0..$FFFF), MEM=addr - OPCODE_ROR_ABS = 0x6E, // ROtate Right, Absolute ($6E addrlo addrhi : ROR addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_6F = 0x6F, // illegal opcode - OPCODE_BVS_REL = 0x70, // Branch on oVerflow Set, Relative ($70 signoffs : BVS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_ADC_IZY = 0x71, // ADd with Carry, Indirect Indexed ($71 arg : ADC (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_72 = 0x72, // illegal opcode - OPCODE_ILL_73 = 0x73, // illegal opcode - OPCODE_ILL_74 = 0x74, // illegal opcode - OPCODE_ADC_ZPX = 0x75, // ADd with Carry, Zero Page Indexed, X ($75 arg : ADC arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ROR_ZPX = 0x76, // ROtate Right, Zero Page Indexed, X ($76 arg : ROR arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ILL_77 = 0x77, // illegal opcode - OPCODE_SEI = 0x78, // SEt Interrupt, Implied ($78 : SEI) - OPCODE_ADC_ABY = 0x79, // ADd with Carry, Absolute Indexed, Y ($79 addrlo addrhi : ADC addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_ILL_7A = 0x7A, // illegal opcode - OPCODE_ILL_7B = 0x7B, // illegal opcode - OPCODE_ILL_7C = 0x7C, // illegal opcode - OPCODE_ADC_ABX = 0x7D, // ADd with Carry, Absolute Indexed, X ($7D addrlo addrhi : ADC addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ROR_ABX = 0x7E, // ROtate Right, Absolute Indexed, X ($7E addrlo addrhi : ROR addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ILL_7F = 0x7F, // illegal opcode - OPCODE_ILL_80 = 0x80, // illegal opcode - OPCODE_STA_IZX = 0x81, // STore Accumulator, Indexed Indirect ($81 arg : STA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_ILL_82 = 0x82, // illegal opcode - OPCODE_ILL_83 = 0x83, // illegal opcode - OPCODE_STY_ZP = 0x84, // STore Y register, Zero Page ($84 arg : STY arg ;arg=0..$FF), MEM=arg - OPCODE_STA_ZP = 0x85, // STore Accumulator, Zero Page ($85 arg : STA arg ;arg=0..$FF), MEM=arg - OPCODE_STX_ZP = 0x86, // STore X register, Zero Page ($86 arg : STX arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_87 = 0x87, // illegal opcode - OPCODE_DEY = 0x88, // DEcrement Y, Implied ($88 : DEY) - OPCODE_ILL_89 = 0x89, // illegal opcode - OPCODE_TXA = 0x8A, // Transfer X to A, Implied ($8A : TXA) - OPCODE_ILL_8B = 0x8B, // illegal opcode - OPCODE_STY_ABS = 0x8C, // STore Y register, Absolute ($8C addrlo addrhi : STY addr ;addr=0..$FFFF), MEM=addr - OPCODE_STA_ABS = 0x8D, // STore Accumulator, Absolute ($8D addrlo addrhi : STA addr ;addr=0..$FFFF), MEM=addr - OPCODE_STX_ABS = 0x8E, // STore X register, Absolute ($8E addrlo addrhi : STX addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_8F = 0x8F, // illegal opcode - OPCODE_BCC_REL = 0x90, // Branch on Carry Clear, Relative ($90 signoffs : BCC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_STA_IZY = 0x91, // STore Accumulator, Indirect Indexed ($91 arg : STA (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_92 = 0x92, // illegal opcode - OPCODE_ILL_93 = 0x93, // illegal opcode - OPCODE_STY_ZPX = 0x94, // STore Y register, Zero Page Indexed, X ($94 arg : STY arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_STA_ZPX = 0x95, // STore Accumulator, Zero Page Indexed, X ($95 arg : STA arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_STX_ZPY = 0x96, // STore X register, Zero Page Indexed, Y ($96 arg : STX arg,Y ;arg=0..$FF), MEM=arg+Y - OPCODE_ILL_97 = 0x97, // illegal opcode - OPCODE_TYA = 0x98, // Transfer Y to A, Implied ($98 : TYA) - OPCODE_STA_ABY = 0x99, // STore Accumulator, Absolute Indexed, Y ($99 addrlo addrhi : STA addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_TXS = 0x9A, // Transfer X to Stack ptr, Implied ($9A : TXS) - OPCODE_ILL_9B = 0x9B, // illegal opcode - OPCODE_ILL_9C = 0x9C, // illegal opcode - OPCODE_STA_ABX = 0x9D, // STore Accumulator, Absolute Indexed, X ($9D addrlo addrhi : STA addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ILL_9E = 0x9E, // illegal opcode - OPCODE_ILL_9F = 0x9F, // illegal opcode - OPCODE_LDY_IMM = 0xA0, // LoaD Y register, Immediate ($A0 arg : LDY #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_LDA_IZX = 0xA1, // LoaD Accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_LDX_IMM = 0xA2, // LoaD X register, Immediate ($A2 arg : LDX #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_ILL_A3 = 0xA3, // illegal opcode - OPCODE_LDY_ZP = 0xA4, // LoaD Y register, Zero Page ($A4 arg : LDY arg ;arg=0..$FF), MEM=arg - OPCODE_LDA_ZP = 0xA5, // LoaD Accumulator, Zero Page ($A5 arg : LDA arg ;arg=0..$FF), MEM=arg - OPCODE_LDX_ZP = 0xA6, // LoaD X register, Zero Page ($A6 arg : LDX arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_A7 = 0xA7, // illegal opcode - OPCODE_TAY = 0xA8, // Transfer A to Y, Implied ($A8 : TAY) - OPCODE_LDA_IMM = 0xA9, // LoaD Accumulator, Immediate ($A9 arg : LDA #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_TAX = 0xAA, // Transfer A to X, Implied ($AA : TAX) - OPCODE_ILL_AB = 0xAB, // illegal opcode - OPCODE_LDY_ABS = 0xAC, // LoaD Y register, Absolute ($AC addrlo addrhi : LDY addr ;addr=0..$FFFF), MEM=addr - OPCODE_LDA_ABS = 0xAD, // LoaD Accumulator, Absolute ($AD addrlo addrhi : LDA addr ;addr=0..$FFFF), MEM=addr - OPCODE_LDX_ABS = 0xAE, // LoaD X register, Absolute ($AE addrlo addrhi : LDX addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_AF = 0xAF, // illegal opcode - OPCODE_BCS_REL = 0xB0, // Branch on Carry Set, Relative ($B0 signoffs : BCS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_LDA_IZY = 0xB1, // LoaD Accumulator, Indirect Indexed ($B1 arg : LDA (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_B2 = 0xB2, // illegal opcode - OPCODE_ILL_B3 = 0xB3, // illegal opcode - OPCODE_LDY_ZPX = 0xB4, // LoaD Y register, Zero Page Indexed, X ($B4 arg : LDY arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_LDA_ZPX = 0xB5, // LoaD Accumulator, Zero Page Indexed, X ($B5 arg : LDA arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_LDX_ZPY = 0xB6, // LoaD X register, Zero Page Indexed, Y ($B6 arg : LDX arg,Y ;arg=0..$FF), MEM=arg+Y - OPCODE_ILL_B7 = 0xB7, // illegal opcode - OPCODE_CLV = 0xB8, // CLear oVerflow, Implied ($B8 : CLV) - OPCODE_LDA_ABY = 0xB9, // LoaD Accumulator, Absolute Indexed, Y ($B9 addrlo addrhi : LDA addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_TSX = 0xBA, // Transfer Stack ptr to X, Implied ($BA : TSX) - OPCODE_ILL_BB = 0xBB, // illegal opcode - OPCODE_LDY_ABX = 0xBC, // LoaD Y register, Absolute Indexed, X ($BC addrlo addrhi : LDY addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_LDA_ABX = 0xBD, // LoaD Accumulator, Absolute Indexed, X ($BD addrlo addrhi : LDA addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_LDX_ABY = 0xBE, // LoaD X register, Absolute Indexed, Y ($BE addrlo addrhi : LDX addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_ILL_BF = 0xBF, // illegal opcode - OPCODE_CPY_IMM = 0xC0, // ComPare Y register, Immediate ($C0 arg : CPY #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_CMP_IZX = 0xC1, // CoMPare accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_ILL_C2 = 0xC2, // illegal opcode - OPCODE_ILL_C3 = 0xC3, // illegal opcode - OPCODE_CPY_ZP = 0xC4, // ComPare Y register, Zero Page ($C4 arg : CPY arg ;arg=0..$FF), MEM=arg - OPCODE_CMP_ZP = 0xC5, // CoMPare accumulator, Zero Page ($C5 arg : CMP arg ;arg=0..$FF), MEM=arg - OPCODE_DEC_ZP = 0xC6, // DECrement memory, Zero Page ($C6 arg : DEC arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_C7 = 0xC7, // illegal opcode - OPCODE_INY = 0xC8, // INcrement Y, Implied ($C8 : INY)OPCODE_INY = 0xC8, // INcrement Y, Implied ($C8 : INY) - OPCODE_CMP_IMM = 0xC9, // CoMPare accumulator, Immediate ($C9 arg : CMP #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_DEX = 0xCA, // DEcrement X, Implied ($CA : DEX) - OPCODE_ILL_CB = 0xCB, // illegal opcode - OPCODE_CPY_ABS = 0xCC, // ComPare Y register, Absolute ($CC addrlo addrhi : CPY addr ;addr=0..$FFFF), MEM=addr - OPCODE_CMP_ABS = 0xCD, // CoMPare accumulator, Absolute ($CD addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr - OPCODE_DEC_ABS = 0xCE, // DECrement memory, Absolute ($CE addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_CF = 0xCF, // illegal opcode - OPCODE_BNE_REL = 0xD0, // Branch on Not Equal, Relative ($D0 signoffs : BNE signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_CMP_IZY = 0xD1, // CoMPare accumulator, Indirect Indexed ($D1 arg : CMP (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_D2 = 0xD2, // illegal opcode - OPCODE_ILL_D3 = 0xD3, // illegal opcode - OPCODE_ILL_D4 = 0xD4, // illegal opcode - OPCODE_CMP_ZPX = 0xD5, // CoMPare accumulator, Zero Page Indexed, X ($D5 arg : CMP arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_DEC_ZPX = 0xD6, // DECrement memory, Zero Page Indexed, X ($D6 arg : DEC arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ILL_D7 = 0xD7, // illegal opcode - OPCODE_CLD = 0xD8, // CLear Decimal, Implied ($D8 : CLD) - OPCODE_CMP_ABY = 0xD9, // CoMPare accumulator, Absolute Indexed, Y ($D9 addrlo addrhi : CMP addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_ILL_DA = 0xDA, // illegal opcode - OPCODE_ILL_DB = 0xDB, // illegal opcode - OPCODE_ILL_DC = 0xDC, // illegal opcode - OPCODE_CMP_ABX = 0xDD, // CoMPare accumulator, Absolute Indexed, X ($DD addrlo addrhi : CMP addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_DEC_ABX = 0xDE, // DECrement memory, Absolute Indexed, X ($DE addrlo addrhi : DEC addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ILL_DF = 0xDF, // illegal opcode - OPCODE_CPX_IMM = 0xE0, // ComPare X register, Immediate ($E0 arg : CPX #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_SBC_IZX = 0xE1, // SuBtract with Carry, Indexed Indirect ($E1 arg : SBC (arg,X) ;arg=0..$FF), MEM=&(arg+X) - OPCODE_ILL_E2 = 0xE2, // illegal opcode - OPCODE_ILL_E3 = 0xE3, // illegal opcode - OPCODE_CPX_ZP = 0xE4, // ComPare X register, Zero Page ($E4 arg : CPX arg ;arg=0..$FF), MEM=arg - OPCODE_SBC_ZP = 0xE5, // SuBtract with Carry, Zero Page ($E5 arg : SBC arg ;arg=0..$FF), MEM=arg - OPCODE_INC_ZP = 0xE6, // INCrement memory, Zero Page ($E6 arg : INC arg ;arg=0..$FF), MEM=arg - OPCODE_ILL_E7 = 0xE7, // illegal opcode - OPCODE_INX = 0xE8, // INcrement X, Implied ($E8 : INX) - OPCODE_SBC_IMM = 0xE9, // SuBtract with Carry, Immediate ($E9 arg : SBC #arg ;arg=0..$FF), MEM=PC+1 - OPCODE_NOP = 0xEA, // NO oPeration, Implied ($EA : NOP) - OPCODE_ILL_EB = 0xEB, // illegal opcode - OPCODE_CPX_ABS = 0xEC, // ComPare X register, Absolute ($EC addrlo addrhi : CPX addr ;addr=0..$FFFF), MEM=addr - OPCODE_SBC_ABS = 0xED, // SuBtract with Carry, Absolute ($ED addrlo addrhi : SBC addr ;addr=0..$FFFF), MEM=addr - OPCODE_INC_ABS = 0xEE, // INCrement memory, Absolute ($EE addrlo addrhi : INC addr ;addr=0..$FFFF), MEM=addr - OPCODE_ILL_EF = 0xEF, // illegal opcode - OPCODE_BEQ_REL = 0xF0, // Branch on EQual, Relative ($F0 signoffs : BEQ signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) - OPCODE_SBC_IZY = 0xF1, // SuBtract with Carry, Indirect Indexed ($F1 arg : SBC (arg),Y ;arg=0..$FF), MEM=&arg+Y - OPCODE_ILL_F2 = 0xF2, // illegal opcode - OPCODE_ILL_F3 = 0xF3, // illegal opcode - OPCODE_ILL_F4 = 0xF4, // illegal opcode - OPCODE_SBC_ZPX = 0xF5, // SuBtract with Carry, Zero Page Indexed, X ($F5 arg : SBC arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_INC_ZPX = 0xF6, // INCrement memory, Zero Page Indexed, X ($F6 arg : INC arg,X ;arg=0..$FF), MEM=arg+X - OPCODE_ILL_F7 = 0xF7, // illegal opcode - OPCODE_SED = 0xF8, // SEt Decimal, Implied ($F8 : SED) - OPCODE_SBC_ABY = 0xF9, // SuBtract with Carry, Absolute Indexed, Y ($F9 addrlo addrhi : SBC addr,Y ;addr=0..$FFFF), MEM=addr+Y - OPCODE_ILL_FA = 0xFA, // illegal opcode - OPCODE_ILL_FB = 0xFB, // illegal opcode - OPCODE_ILL_FC = 0xFC, // illegal opcode - OPCODE_SBC_ABX = 0xFD, // SuBtract with Carry, Absolute Indexed, X ($FD addrlo addrhi : SBC addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_INC_ABX = 0xFE, // INCrement memory, Absolute Indexed, X ($FE addrlo addrhi : INC addr,X ;addr=0..$FFFF), MEM=addr+X - OPCODE_ILL_FF = 0xFF // illegal opcode -}; - -struct OpCode { - int code; // the byte value of the opcode - int addrmode; // addressing mode (see eAddrModes) - int time; // # of cycles - string amf; // assembler mnemonic -}; - -typedef map OpCodesMap; - -/* - *------------------------------------------------------------------------------ - bit -> 7 0 - +---+---+---+---+---+---+---+---+ - | N | V | | B | D | I | Z | C | <-- flag, 0/1 = reset/set - +---+---+---+---+---+---+---+---+ - - - N = NEGATIVE. Set if bit 7 of the accumulator is set. - - V = OVERFLOW. Set if the addition of two like-signed numbers or the - subtraction of two unlike-signed numbers produces a result - greater than +127 or less than -128. - - B = BRK COMMAND. Set if an interrupt caused by a BRK, reset if - caused by an external interrupt. - - D = DECIMAL MODE. Set if decimal mode active. - - I = IRQ DISABLE. Set if maskable interrupts are disabled. - - Z = ZERO. Set if the result of the last operation (load/inc/dec/ - add/sub) was zero. - - C = CARRY. Set if the add produced a carry, or if the subtraction - produced a borrow. Also holds bits after a logical shift. - *------------------------------------------------------------------------------ - */ -enum eCpuFlagMasks { - FLAGS_CARRY = 0x01, // 0: C - FLAGS_ZERO = 0x02, // 1: Z - FLAGS_IRQ = 0x04, // 2: I - FLAGS_DEC = 0x08, // 3: D - FLAGS_BRK = 0x10, // 4: B (Clear if interrupt vectoring, set if BRK or PHP) - FLAGS_UNUSED = 0x20, // 5: Unused flag (always set). - FLAGS_OVERFLOW = 0x40, // 6: V - FLAGS_SIGN = 0x80 // 7: N -}; - -enum eLogicOps { - LOGOP_OR, - LOGOP_AND, - LOGOP_EOR -}; - -class MKCpu -{ - public: - - MKCpu(); - MKCpu(Memory *pmem); - ~MKCpu(); - - Regs *ExecOpcode(unsigned short memaddr); - Regs *GetRegs(); - queue GetExecHistory(); - unsigned short Disassemble(unsigned short addr, - char *instrbuf); // Disassemble instruction in memory, return next instruction addr. - - protected: - - private: - - struct Regs mReg; // CPU registers - Memory *mpMem; // pointer to memory object - bool mLocalMem; // true - memory locally allocated - OpCodesMap mOpCodesMap; // hash table of all opcodes - int mAddrModesLen[ADDRMODE_LENGTH]; // array of instructions lengths per addressing mode - string mArgFmtTbl[ADDRMODE_LENGTH]; // array of instructions assembly formats per addressing mode - queue mExecHistory; // history of last 20 op-codes with arguments and registers statuses - - - void InitCpu(); - void SetFlags(unsigned char reg); // set CPU flags ZERO and SIGN based on Acc, X or Y - unsigned char ShiftLeft(unsigned char arg8); // Arithmetic Shift Left, set Carry flag - unsigned char ShiftRight(unsigned char arg8); // Logical Shift Right, update flags NZC. - unsigned char RotateLeft(unsigned char arg8); // Rotate left, Carry to bit 0, bit 7 to Carry, update flags N and Z. - unsigned char RotateRight(unsigned char arg8); // Rotate left, Carry to bit 7, bit 0 to Carry, update flags N and Z. - unsigned short GetArg16(unsigned char offs); // Get 2-byte argument, add offset, increase PC. - void LogicOpAcc(unsigned short addr, int logop); // Perform logical bitwise operation between memory location and Acc. - // Result in Acc. Set flags. - unsigned short ComputeRelJump(unsigned char offs); // Compute new PC based on relative offset. - unsigned short ComputeRelJump(unsigned short addr, - unsigned char offs); // Compute new address after branch based on relative offset. - unsigned char Conv2Bcd(unsigned short v); // Convert number to BCD representation. - unsigned short Bcd2Num(unsigned char v); // Convert BCD code to number. - bool CheckFlag(unsigned char flag); // Return true if given CPU status flag is set, false otherwise. - void SetFlag(bool set, unsigned char flag); // Set or unset processor status flag. - unsigned char AddWithCarry(unsigned char mem8); // Add With Carry, update flags and Acc. - unsigned char SubWithCarry(unsigned char mem8); // Subtract With Carry, update flags and Acc. - unsigned short GetAddrWithMode(int mode); // Get address of the byte argument with specified addr. mode - unsigned short GetArgWithMode(unsigned short opcaddr, - int mode); // Get argument from address with specified addr. mode - unsigned short Disassemble(); // Disassemble instruction and argument per addressing mode - void Add2History(string s); // add entry to op-codes execute history -}; - -} // namespace MKBasic - -#endif +#ifndef MKCPU_H +#define MKCPU_H + +#include +#include +#include +#include "system.h" +#include "Memory.h" + +using namespace std; + +namespace MKBasic { + +#define DISS_BUF_SIZE 60 // disassembled instruction buffer size + +struct Regs { + unsigned char Acc; // 8-bit accumulator + unsigned short Acc16; // 16-bit accumulator + unsigned char IndX; // 8-bit index register X + unsigned char IndY; // 8-bit index register Y + unsigned short Ptr16; // general purpose 16-bit register + unsigned short PtrAddr; // cpu code counter (PC) - current read/write address + unsigned char PtrStack; // 8-bit stack pointer (0-255). + unsigned char Flags; // CPU flags + bool SoftIrq; // true when interrupted with BRK or trapped opcode + bool LastRTS; // true if RTS encountered and stack empty. + unsigned short LastAddr; // PC at the time of previous op-code + string LastInstr; // instruction and argument executed in previous step + int LastOpCode; // op-code of last instruction + unsigned short LastArg; // argument to the last instruction + int LastAddrMode; // addressing mode of last instruction + bool IrqPending; // pending Interrupt ReQuest (IRQ) + int CyclesLeft; // # of cycles left to complete current opcode + bool PageBoundary; // true if page boundary was crossed +}; + +/* + * Virtual CPU, 6502 addressing modes: + + +---------------------+--------------------------+ + | mode | assembler format | + +=====================+==========================+ + | Immediate | #aa | + | Absolute | aaaa | + | Zero Page | aa | Note: + | Implied | | + | Indirect Absolute | (aaaa) | aa = 2 hex digits + | Absolute Indexed,X | aaaa,X | as $FF + | Absolute Indexed,Y | aaaa,Y | + | Zero Page Indexed,X | aa,X | aaaa = 4 hex + | Zero Page Indexed,Y | aa,Y | digits as + | Indexed Indirect | (aa,X) | $FFFF + | Indirect Indexed | (aa),Y | + | Relative | aaaa | Can also be + | Accumulator | A | assembler labels + +---------------------+--------------------------+ + +Short notation: + +imm = #$00 +zp = $00 +zpx = $00,X +zpy = $00,Y +izx = ($00,X) +izy = ($00),Y +abs = $0000 +abx = $0000,X +aby = $0000,Y +ind = ($0000) +rel = $0000 (PC-relative) + +See: 6502AssemblyInOneStep.txt for details. + + */ +enum eAddrModes { + 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, + ADDRMODE_UND, // undetermined (for some illegal codes) + ADDRMODE_LENGTH // should be always last +}; +// assumed little-endian order of bytes (start with least significant) +// MEM - memory location from where the value is read/written, +// & - reference operator (e.g.: &addr means: value under addr memory location) +// PC - program counter (PC+1 means - next memory location after opcode) +enum eOpCodes { + OPCODE_BRK = 0x00, // software interrupt, no arguments ($00 : BRK) + + /* full compatibility with 65C02 (illegal opcodes not supported, will be used for extended functions */ + OPCODE_ORA_IZX = 0x01, // bitwise OR with Accumulator, Indexed Indirect ($01 arg : ORA (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_ILL_02 = 0x02, // illegal opcode + OPCODE_ILL_03 = 0x03, // illegal opcode + OPCODE_ILL_04 = 0x04, // illegal opcode + OPCODE_ORA_ZP = 0x05, // bitwise OR with Accumulator, Zero Page ($05 arg : ORA arg ;arg=0..$FF), MEM=arg + OPCODE_ASL_ZP = 0x06, // Arithmetic Shift Left, Zero Page ($06 arg : ASL arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_07 = 0x07, // illegal opcode + OPCODE_PHP = 0x08, // PusH Processor status on Stack, Implied ($08 : PHP) + OPCODE_ORA_IMM = 0x09, // bitwise OR with Accumulator, Immediate ($09 arg : ORA #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_ASL = 0x0A, // Arithmetic Shift Left, Accumulator ($0A : ASL) + OPCODE_ILL_0B = 0x0B, // illegal opcode + OPCODE_ILL_0C = 0x0C, // illegal opcode + OPCODE_ORA_ABS = 0x0D, // bitwise OR with Accumulator, Absolute ($0D addrlo addrhi : ORA addr ;addr=0..$FFFF), MEM=addr + OPCODE_ASL_ABS = 0x0E, // Arithmetic Shift Left, Absolute ($0E addrlo addrhi : ASL addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_0F = 0x0F, // illegal opcode + OPCODE_BPL_REL = 0x10, // Branch on PLus, Relative ($10 signoffs : BPL signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_ORA_IZY = 0x11, // bitwise OR with Accumulator, Indirect Indexed ($11 arg : ORA (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_12 = 0x12, // illegal opcode + OPCODE_ILL_13 = 0x13, // illegal opcode + OPCODE_ILL_14 = 0x14, // illegal opcode + OPCODE_ORA_ZPX = 0x15, // bitwise OR with Accumulator, Zero Page Indexed, X ($15 arg : ORA arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ASL_ZPX = 0x16, // Arithmetic Shift Left, Zero Page Indexed, X ($16 arg : ASL arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ILL_17 = 0x17, // illegal opcode + OPCODE_CLC = 0x18, // CLear Carry, Implied ($18 : CLC) + OPCODE_ORA_ABY = 0x19, // bitwise OR with Accumulator, Absolute Indexed, Y ($19 addrlo addrhi : ORA addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_ILL_1A = 0x1A, // illegal opcode + OPCODE_ILL_1B = 0x1B, // illegal opcode + OPCODE_ILL_1C = 0x1C, // illegal opcode + OPCODE_ORA_ABX = 0x1D, // bitwise OR with Accumulator, Absolute Indexed, X ($1D addrlo addrhi : ORA addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ASL_ABX = 0x1E, // Arithmetic Shift Left, Absolute Indexed, X ($1E addrlo addrhi : ASL addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ILL_1F = 0x1F, // illegal opcode + OPCODE_JSR_ABS = 0x20, // Jump to SubRoutine, Absolute ($20 addrlo addrhi : JSR addr ;addr=0..$FFFF), MEM=addr + OPCODE_AND_IZX = 0x21, // bitwise AND with accumulator, Indexed Indirect ($21 arg : AND (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_ILL_22 = 0x22, // illegal opcode + OPCODE_ILL_23 = 0x23, // illegal opcode + OPCODE_BIT_ZP = 0x24, // test BITs, Zero Page ($24 arg : BIT arg ;arg=0..$FF), MEM=arg + OPCODE_AND_ZP = 0x25, // bitwise AND with accumulator, Zero Page ($25 arg : AND arg ;arg=0..$FF), MEM=arg + OPCODE_ROL_ZP = 0x26, // ROtate Left, Zero Page ($26 arg : ROL arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_27 = 0x27, // illegal opcode + OPCODE_PLP = 0x28, // PuLl Processor status, Implied ($28 : PLP) + OPCODE_AND_IMM = 0x29, // bitwise AND with accumulator, Immediate ($29 arg : AND #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_ROL = 0x2A, // ROtate Left, Accumulator ($2A : ROL) + OPCODE_ILL_2B = 0x2B, // illegal opcode + OPCODE_BIT_ABS = 0x2C, // test BITs, Absolute ($2C addrlo addrhi : BIT addr ;addr=0..$FFFF), MEM=addr + OPCODE_AND_ABS = 0x2D, // bitwise AND with accumulator, Absolute ($2D addrlo addrhi : AND addr ;addr=0..$FFFF), MEM=addr + OPCODE_ROL_ABS = 0x2E, // ROtate Left, Absolute ($2E addrlo addrhi : ROL addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_2F = 0x2F, // illegal opcode + OPCODE_BMI_REL = 0x30, // Branch on MInus, Relative ($30 signoffs : BMI signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_AND_IZY = 0x31, // bitwise AND with accumulator, Indirect Indexed ($31 arg : AND (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_32 = 0x32, // illegal opcode + OPCODE_ILL_33 = 0x33, // illegal opcode + OPCODE_ILL_34 = 0x34, // illegal opcode + OPCODE_AND_ZPX = 0x35, // bitwise AND with accumulator, Zero Page Indexed, X ($35 arg : AND arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ROL_ZPX = 0x36, // ROtate Left, Zero Page Indexed, X ($36 arg : ROL arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ILL_37 = 0x37, // illegal opcode + OPCODE_SEC = 0x38, // SEt Carry, Implied ($38 : SEC) + OPCODE_AND_ABY = 0x39, // bitwise AND with accumulator, Absolute Indexed, Y ($39 addrlo addrhi : AND addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_ILL_3A = 0x3A, // illegal opcode + OPCODE_ILL_3B = 0x3B, // illegal opcode + OPCODE_ILL_3C = 0x3C, // illegal opcode + OPCODE_AND_ABX = 0x3D, // bitwise AND with accumulator, Absolute Indexed, X ($3D addrlo addrhi : AND addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ROL_ABX = 0x3E, // ROtate Left, Absolute Indexed, X ($3E addrlo addrhi : ROL addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ILL_3F = 0x3F, // illegal opcode + OPCODE_RTI = 0x40, // ReTurn from Interrupt, Implied ($40 : RTI) + OPCODE_EOR_IZX = 0x41, // bitwise Exclusive OR, Indexed Indirect ($41 arg : EOR (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_ILL_42 = 0x42, // illegal opcode + OPCODE_ILL_43 = 0x43, // illegal opcode + OPCODE_ILL_44 = 0x44, // illegal opcode + OPCODE_EOR_ZP = 0x45, // bitwise Exclusive OR, Zero Page ($45 arg : EOR arg ;arg=0..$FF), MEM=arg + OPCODE_LSR_ZP = 0x46, // Logical Shift Right, Zero Page ($46 arg : LSR arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_47 = 0x47, // illegal opcode + OPCODE_PHA = 0x48, // PusH Accumulator, Implied ($48 : PHA) + OPCODE_EOR_IMM = 0x49, // bitwise Exclusive OR, Immediate ($49 arg : EOR #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_LSR = 0x4A, // Logical Shift Right, Accumulator ($4A : LSR) + OPCODE_ILL_4B = 0x4B, // illegal opcode + OPCODE_JMP_ABS = 0x4C, // JuMP, Absolute ($4C addrlo addrhi : JMP addr ;addr=0..$FFFF), MEM=addr + OPCODE_EOR_ABS = 0x4D, // bitwise Exclusive OR, Absolute ($4D addrlo addrhi : EOR addr ;addr=0..$FFFF), MEM=addr + OPCODE_LSR_ABS = 0x4E, // Logical Shift Right, Absolute ($4E addrlo addrhi : LSR addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_4F = 0x4F, // illegal opcode + OPCODE_BVC_REL = 0x50, // Branch on oVerflow Clear, Relative ($50 signoffs : BVC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_EOR_IZY = 0x51, // bitwise Exclusive OR, Indirect Indexed ($51 arg : EOR (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_52 = 0x52, // illegal opcode + OPCODE_ILL_53 = 0x53, // illegal opcode + OPCODE_ILL_54 = 0x54, // illegal opcode + OPCODE_EOR_ZPX = 0x55, // bitwise Exclusive OR, Zero Page Indexed, X ($55 arg : EOR arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_LSR_ZPX = 0x56, // Logical Shift Right, Zero Page Indexed, X ($56 arg : LSR arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ILL_57 = 0x57, // illegal opcode + OPCODE_CLI = 0x58, // CLear Interrupt, Implied ($58 : CLI) + OPCODE_EOR_ABY = 0x59, // bitwise Exclusive OR, Absolute Indexed, Y ($59 addrlo addrhi : EOR addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_ILL_5A = 0x5A, // illegal opcode + OPCODE_ILL_5B = 0x5B, // illegal opcode + OPCODE_ILL_5C = 0x5C, // illegal opcode + OPCODE_EOR_ABX = 0x5D, // bitwise Exclusive OR, Absolute Indexed, X ($5D addrlo addrhi : EOR addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_LSR_ABX = 0x5E, // Logical Shift Right, Absolute Indexed, X ($5E addrlo addrhi : LSR addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ILL_5F = 0x5F, // illegal opcode + OPCODE_RTS = 0x60, // ReTurn from Subroutine, Implied ($60 : RTS) + OPCODE_ADC_IZX = 0x61, // ADd with Carry, Indexed Indirect ($61 arg : ADC (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_ILL_62 = 0x62, // illegal opcode + OPCODE_ILL_63 = 0x63, // illegal opcode + OPCODE_ILL_64 = 0x64, // illegal opcode + OPCODE_ADC_ZP = 0x65, // ADd with Carry, Zero Page ($65 arg : ADC arg ;arg=0..$FF), MEM=arg + OPCODE_ROR_ZP = 0x66, // ROtate Right, Zero Page ($66 arg : ROR arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_67 = 0x67, // illegal opcode + OPCODE_PLA = 0x68, // PuLl Accumulator, Implied ($68 : PLA) + OPCODE_ADC_IMM = 0x69, // ADd with Carry, Immediate ($69 arg : ADC #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_ROR = 0x6A, // ROtate Right, Accumulator ($6A : ROR) + OPCODE_ILL_6B = 0x6B, // illegal opcode + OPCODE_JMP_IND = 0x6C, // JuMP, Indirect Absolute ($6C addrlo addrhi : JMP (addr) ;addr=0..FFFF), MEM=&addr + OPCODE_ADC_ABS = 0x6D, // ADd with Carry, Absolute ($6D addrlo addrhi : ADC addr ;addr=0..$FFFF), MEM=addr + OPCODE_ROR_ABS = 0x6E, // ROtate Right, Absolute ($6E addrlo addrhi : ROR addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_6F = 0x6F, // illegal opcode + OPCODE_BVS_REL = 0x70, // Branch on oVerflow Set, Relative ($70 signoffs : BVS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_ADC_IZY = 0x71, // ADd with Carry, Indirect Indexed ($71 arg : ADC (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_72 = 0x72, // illegal opcode + OPCODE_ILL_73 = 0x73, // illegal opcode + OPCODE_ILL_74 = 0x74, // illegal opcode + OPCODE_ADC_ZPX = 0x75, // ADd with Carry, Zero Page Indexed, X ($75 arg : ADC arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ROR_ZPX = 0x76, // ROtate Right, Zero Page Indexed, X ($76 arg : ROR arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ILL_77 = 0x77, // illegal opcode + OPCODE_SEI = 0x78, // SEt Interrupt, Implied ($78 : SEI) + OPCODE_ADC_ABY = 0x79, // ADd with Carry, Absolute Indexed, Y ($79 addrlo addrhi : ADC addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_ILL_7A = 0x7A, // illegal opcode + OPCODE_ILL_7B = 0x7B, // illegal opcode + OPCODE_ILL_7C = 0x7C, // illegal opcode + OPCODE_ADC_ABX = 0x7D, // ADd with Carry, Absolute Indexed, X ($7D addrlo addrhi : ADC addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ROR_ABX = 0x7E, // ROtate Right, Absolute Indexed, X ($7E addrlo addrhi : ROR addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ILL_7F = 0x7F, // illegal opcode + OPCODE_ILL_80 = 0x80, // illegal opcode + OPCODE_STA_IZX = 0x81, // STore Accumulator, Indexed Indirect ($81 arg : STA (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_ILL_82 = 0x82, // illegal opcode + OPCODE_ILL_83 = 0x83, // illegal opcode + OPCODE_STY_ZP = 0x84, // STore Y register, Zero Page ($84 arg : STY arg ;arg=0..$FF), MEM=arg + OPCODE_STA_ZP = 0x85, // STore Accumulator, Zero Page ($85 arg : STA arg ;arg=0..$FF), MEM=arg + OPCODE_STX_ZP = 0x86, // STore X register, Zero Page ($86 arg : STX arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_87 = 0x87, // illegal opcode + OPCODE_DEY = 0x88, // DEcrement Y, Implied ($88 : DEY) + OPCODE_ILL_89 = 0x89, // illegal opcode + OPCODE_TXA = 0x8A, // Transfer X to A, Implied ($8A : TXA) + OPCODE_ILL_8B = 0x8B, // illegal opcode + OPCODE_STY_ABS = 0x8C, // STore Y register, Absolute ($8C addrlo addrhi : STY addr ;addr=0..$FFFF), MEM=addr + OPCODE_STA_ABS = 0x8D, // STore Accumulator, Absolute ($8D addrlo addrhi : STA addr ;addr=0..$FFFF), MEM=addr + OPCODE_STX_ABS = 0x8E, // STore X register, Absolute ($8E addrlo addrhi : STX addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_8F = 0x8F, // illegal opcode + OPCODE_BCC_REL = 0x90, // Branch on Carry Clear, Relative ($90 signoffs : BCC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_STA_IZY = 0x91, // STore Accumulator, Indirect Indexed ($91 arg : STA (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_92 = 0x92, // illegal opcode + OPCODE_ILL_93 = 0x93, // illegal opcode + OPCODE_STY_ZPX = 0x94, // STore Y register, Zero Page Indexed, X ($94 arg : STY arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_STA_ZPX = 0x95, // STore Accumulator, Zero Page Indexed, X ($95 arg : STA arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_STX_ZPY = 0x96, // STore X register, Zero Page Indexed, Y ($96 arg : STX arg,Y ;arg=0..$FF), MEM=arg+Y + OPCODE_ILL_97 = 0x97, // illegal opcode + OPCODE_TYA = 0x98, // Transfer Y to A, Implied ($98 : TYA) + OPCODE_STA_ABY = 0x99, // STore Accumulator, Absolute Indexed, Y ($99 addrlo addrhi : STA addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_TXS = 0x9A, // Transfer X to Stack ptr, Implied ($9A : TXS) + OPCODE_ILL_9B = 0x9B, // illegal opcode + OPCODE_ILL_9C = 0x9C, // illegal opcode + OPCODE_STA_ABX = 0x9D, // STore Accumulator, Absolute Indexed, X ($9D addrlo addrhi : STA addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ILL_9E = 0x9E, // illegal opcode + OPCODE_ILL_9F = 0x9F, // illegal opcode + OPCODE_LDY_IMM = 0xA0, // LoaD Y register, Immediate ($A0 arg : LDY #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_LDA_IZX = 0xA1, // LoaD Accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_LDX_IMM = 0xA2, // LoaD X register, Immediate ($A2 arg : LDX #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_ILL_A3 = 0xA3, // illegal opcode + OPCODE_LDY_ZP = 0xA4, // LoaD Y register, Zero Page ($A4 arg : LDY arg ;arg=0..$FF), MEM=arg + OPCODE_LDA_ZP = 0xA5, // LoaD Accumulator, Zero Page ($A5 arg : LDA arg ;arg=0..$FF), MEM=arg + OPCODE_LDX_ZP = 0xA6, // LoaD X register, Zero Page ($A6 arg : LDX arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_A7 = 0xA7, // illegal opcode + OPCODE_TAY = 0xA8, // Transfer A to Y, Implied ($A8 : TAY) + OPCODE_LDA_IMM = 0xA9, // LoaD Accumulator, Immediate ($A9 arg : LDA #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_TAX = 0xAA, // Transfer A to X, Implied ($AA : TAX) + OPCODE_ILL_AB = 0xAB, // illegal opcode + OPCODE_LDY_ABS = 0xAC, // LoaD Y register, Absolute ($AC addrlo addrhi : LDY addr ;addr=0..$FFFF), MEM=addr + OPCODE_LDA_ABS = 0xAD, // LoaD Accumulator, Absolute ($AD addrlo addrhi : LDA addr ;addr=0..$FFFF), MEM=addr + OPCODE_LDX_ABS = 0xAE, // LoaD X register, Absolute ($AE addrlo addrhi : LDX addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_AF = 0xAF, // illegal opcode + OPCODE_BCS_REL = 0xB0, // Branch on Carry Set, Relative ($B0 signoffs : BCS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_LDA_IZY = 0xB1, // LoaD Accumulator, Indirect Indexed ($B1 arg : LDA (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_B2 = 0xB2, // illegal opcode + OPCODE_ILL_B3 = 0xB3, // illegal opcode + OPCODE_LDY_ZPX = 0xB4, // LoaD Y register, Zero Page Indexed, X ($B4 arg : LDY arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_LDA_ZPX = 0xB5, // LoaD Accumulator, Zero Page Indexed, X ($B5 arg : LDA arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_LDX_ZPY = 0xB6, // LoaD X register, Zero Page Indexed, Y ($B6 arg : LDX arg,Y ;arg=0..$FF), MEM=arg+Y + OPCODE_ILL_B7 = 0xB7, // illegal opcode + OPCODE_CLV = 0xB8, // CLear oVerflow, Implied ($B8 : CLV) + OPCODE_LDA_ABY = 0xB9, // LoaD Accumulator, Absolute Indexed, Y ($B9 addrlo addrhi : LDA addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_TSX = 0xBA, // Transfer Stack ptr to X, Implied ($BA : TSX) + OPCODE_ILL_BB = 0xBB, // illegal opcode + OPCODE_LDY_ABX = 0xBC, // LoaD Y register, Absolute Indexed, X ($BC addrlo addrhi : LDY addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_LDA_ABX = 0xBD, // LoaD Accumulator, Absolute Indexed, X ($BD addrlo addrhi : LDA addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_LDX_ABY = 0xBE, // LoaD X register, Absolute Indexed, Y ($BE addrlo addrhi : LDX addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_ILL_BF = 0xBF, // illegal opcode + OPCODE_CPY_IMM = 0xC0, // ComPare Y register, Immediate ($C0 arg : CPY #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_CMP_IZX = 0xC1, // CoMPare accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_ILL_C2 = 0xC2, // illegal opcode + OPCODE_ILL_C3 = 0xC3, // illegal opcode + OPCODE_CPY_ZP = 0xC4, // ComPare Y register, Zero Page ($C4 arg : CPY arg ;arg=0..$FF), MEM=arg + OPCODE_CMP_ZP = 0xC5, // CoMPare accumulator, Zero Page ($C5 arg : CMP arg ;arg=0..$FF), MEM=arg + OPCODE_DEC_ZP = 0xC6, // DECrement memory, Zero Page ($C6 arg : DEC arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_C7 = 0xC7, // illegal opcode + OPCODE_INY = 0xC8, // INcrement Y, Implied ($C8 : INY)OPCODE_INY = 0xC8, // INcrement Y, Implied ($C8 : INY) + OPCODE_CMP_IMM = 0xC9, // CoMPare accumulator, Immediate ($C9 arg : CMP #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_DEX = 0xCA, // DEcrement X, Implied ($CA : DEX) + OPCODE_ILL_CB = 0xCB, // illegal opcode + OPCODE_CPY_ABS = 0xCC, // ComPare Y register, Absolute ($CC addrlo addrhi : CPY addr ;addr=0..$FFFF), MEM=addr + OPCODE_CMP_ABS = 0xCD, // CoMPare accumulator, Absolute ($CD addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr + OPCODE_DEC_ABS = 0xCE, // DECrement memory, Absolute ($CE addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_CF = 0xCF, // illegal opcode + OPCODE_BNE_REL = 0xD0, // Branch on Not Equal, Relative ($D0 signoffs : BNE signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_CMP_IZY = 0xD1, // CoMPare accumulator, Indirect Indexed ($D1 arg : CMP (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_D2 = 0xD2, // illegal opcode + OPCODE_ILL_D3 = 0xD3, // illegal opcode + OPCODE_ILL_D4 = 0xD4, // illegal opcode + OPCODE_CMP_ZPX = 0xD5, // CoMPare accumulator, Zero Page Indexed, X ($D5 arg : CMP arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_DEC_ZPX = 0xD6, // DECrement memory, Zero Page Indexed, X ($D6 arg : DEC arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ILL_D7 = 0xD7, // illegal opcode + OPCODE_CLD = 0xD8, // CLear Decimal, Implied ($D8 : CLD) + OPCODE_CMP_ABY = 0xD9, // CoMPare accumulator, Absolute Indexed, Y ($D9 addrlo addrhi : CMP addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_ILL_DA = 0xDA, // illegal opcode + OPCODE_ILL_DB = 0xDB, // illegal opcode + OPCODE_ILL_DC = 0xDC, // illegal opcode + OPCODE_CMP_ABX = 0xDD, // CoMPare accumulator, Absolute Indexed, X ($DD addrlo addrhi : CMP addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_DEC_ABX = 0xDE, // DECrement memory, Absolute Indexed, X ($DE addrlo addrhi : DEC addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ILL_DF = 0xDF, // illegal opcode + OPCODE_CPX_IMM = 0xE0, // ComPare X register, Immediate ($E0 arg : CPX #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_SBC_IZX = 0xE1, // SuBtract with Carry, Indexed Indirect ($E1 arg : SBC (arg,X) ;arg=0..$FF), MEM=&(arg+X) + OPCODE_ILL_E2 = 0xE2, // illegal opcode + OPCODE_ILL_E3 = 0xE3, // illegal opcode + OPCODE_CPX_ZP = 0xE4, // ComPare X register, Zero Page ($E4 arg : CPX arg ;arg=0..$FF), MEM=arg + OPCODE_SBC_ZP = 0xE5, // SuBtract with Carry, Zero Page ($E5 arg : SBC arg ;arg=0..$FF), MEM=arg + OPCODE_INC_ZP = 0xE6, // INCrement memory, Zero Page ($E6 arg : INC arg ;arg=0..$FF), MEM=arg + OPCODE_ILL_E7 = 0xE7, // illegal opcode + OPCODE_INX = 0xE8, // INcrement X, Implied ($E8 : INX) + OPCODE_SBC_IMM = 0xE9, // SuBtract with Carry, Immediate ($E9 arg : SBC #arg ;arg=0..$FF), MEM=PC+1 + OPCODE_NOP = 0xEA, // NO oPeration, Implied ($EA : NOP) + OPCODE_ILL_EB = 0xEB, // illegal opcode + OPCODE_CPX_ABS = 0xEC, // ComPare X register, Absolute ($EC addrlo addrhi : CPX addr ;addr=0..$FFFF), MEM=addr + OPCODE_SBC_ABS = 0xED, // SuBtract with Carry, Absolute ($ED addrlo addrhi : SBC addr ;addr=0..$FFFF), MEM=addr + OPCODE_INC_ABS = 0xEE, // INCrement memory, Absolute ($EE addrlo addrhi : INC addr ;addr=0..$FFFF), MEM=addr + OPCODE_ILL_EF = 0xEF, // illegal opcode + OPCODE_BEQ_REL = 0xF0, // Branch on EQual, Relative ($F0 signoffs : BEQ signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) + OPCODE_SBC_IZY = 0xF1, // SuBtract with Carry, Indirect Indexed ($F1 arg : SBC (arg),Y ;arg=0..$FF), MEM=&arg+Y + OPCODE_ILL_F2 = 0xF2, // illegal opcode + OPCODE_ILL_F3 = 0xF3, // illegal opcode + OPCODE_ILL_F4 = 0xF4, // illegal opcode + OPCODE_SBC_ZPX = 0xF5, // SuBtract with Carry, Zero Page Indexed, X ($F5 arg : SBC arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_INC_ZPX = 0xF6, // INCrement memory, Zero Page Indexed, X ($F6 arg : INC arg,X ;arg=0..$FF), MEM=arg+X + OPCODE_ILL_F7 = 0xF7, // illegal opcode + OPCODE_SED = 0xF8, // SEt Decimal, Implied ($F8 : SED) + OPCODE_SBC_ABY = 0xF9, // SuBtract with Carry, Absolute Indexed, Y ($F9 addrlo addrhi : SBC addr,Y ;addr=0..$FFFF), MEM=addr+Y + OPCODE_ILL_FA = 0xFA, // illegal opcode + OPCODE_ILL_FB = 0xFB, // illegal opcode + OPCODE_ILL_FC = 0xFC, // illegal opcode + OPCODE_SBC_ABX = 0xFD, // SuBtract with Carry, Absolute Indexed, X ($FD addrlo addrhi : SBC addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_INC_ABX = 0xFE, // INCrement memory, Absolute Indexed, X ($FE addrlo addrhi : INC addr,X ;addr=0..$FFFF), MEM=addr+X + OPCODE_ILL_FF = 0xFF // illegal opcode +}; + +class MKCpu; + +typedef void (MKCpu::*OpCodeHdlrFn)(); + +struct OpCode { + int code; // the byte value of the opcode + int addrmode; // addressing mode (see eAddrModes) + int time; // # of cycles + string amf; // assembler mnemonic + OpCodeHdlrFn pfun; // opcoce handler function +}; + +typedef map OpCodesMap; + +/* + *------------------------------------------------------------------------------ + bit -> 7 0 + +---+---+---+---+---+---+---+---+ + | N | V | | B | D | I | Z | C | <-- flag, 0/1 = reset/set + +---+---+---+---+---+---+---+---+ + + + N = NEGATIVE. Set if bit 7 of the accumulator is set. + + V = OVERFLOW. Set if the addition of two like-signed numbers or the + subtraction of two unlike-signed numbers produces a result + greater than +127 or less than -128. + + B = BRK COMMAND. Set if an interrupt caused by a BRK, reset if + caused by an external interrupt. + + D = DECIMAL MODE. Set if decimal mode active. + + I = IRQ DISABLE. Set if maskable interrupts are disabled. + + Z = ZERO. Set if the result of the last operation (load/inc/dec/ + add/sub) was zero. + + C = CARRY. Set if the add produced a carry, or if the subtraction + produced a borrow. Also holds bits after a logical shift. + *------------------------------------------------------------------------------ + */ +enum eCpuFlagMasks { + FLAGS_CARRY = 0x01, // 0: C + FLAGS_ZERO = 0x02, // 1: Z + FLAGS_IRQ = 0x04, // 2: I + FLAGS_DEC = 0x08, // 3: D + FLAGS_BRK = 0x10, // 4: B (Clear if interrupt vectoring, set if BRK or PHP) + FLAGS_UNUSED = 0x20, // 5: Unused flag (always set). + FLAGS_OVERFLOW = 0x40, // 6: V + FLAGS_SIGN = 0x80 // 7: N +}; + +enum eLogicOps { + LOGOP_OR, + LOGOP_AND, + LOGOP_EOR +}; + +class MKCpu +{ + public: + + bool mExitAtLastRTS; + + MKCpu(); + MKCpu(Memory *pmem); + ~MKCpu(); + + Regs *ExecOpcode(unsigned short memaddr); + Regs *GetRegs(); + void SetRegs(Regs r); + queue GetExecHistory(); + unsigned short Disassemble(unsigned short addr, + char *instrbuf); // Disassemble instruction in memory, return next instruction addr. + void Reset(); // reset CPU + void Interrupt(); // Interrupt ReQuest (IRQ) + + protected: + + private: + + struct Regs mReg; // CPU registers + Memory *mpMem; // pointer to memory object + bool mLocalMem; // true - memory locally allocated + OpCodesMap mOpCodesMap; // hash table of all opcodes + int mAddrModesLen[ADDRMODE_LENGTH]; // array of instructions lengths per addressing mode + string mArgFmtTbl[ADDRMODE_LENGTH]; // array of instructions assembly formats per addressing mode + queue mExecHistory; // history of last 20 op-codes with arguments and registers statuses + + + void InitCpu(); + void SetFlags(unsigned char reg); // set CPU flags ZERO and SIGN based on Acc, X or Y + unsigned char ShiftLeft(unsigned char arg8); // Arithmetic Shift Left, set Carry flag + unsigned char ShiftRight(unsigned char arg8); // Logical Shift Right, update flags NZC. + unsigned char RotateLeft(unsigned char arg8); // Rotate left, Carry to bit 0, bit 7 to Carry, update flags N and Z. + unsigned char RotateRight(unsigned char arg8); // Rotate left, Carry to bit 7, bit 0 to Carry, update flags N and Z. + unsigned short GetArg16(unsigned char offs); // Get 2-byte argument, add offset, increase PC. + void LogicOpAcc(unsigned short addr, int logop); // Perform logical bitwise operation between memory location and Acc. + // Result in Acc. Set flags. + unsigned short ComputeRelJump(unsigned char offs); // Compute new PC based on relative offset. + unsigned short ComputeRelJump(unsigned short addr, + unsigned char offs); // Compute new address after branch based on relative offset. + unsigned char Conv2Bcd(unsigned short v); // Convert number to BCD representation. + unsigned short Bcd2Num(unsigned char v); // Convert BCD code to number. + bool CheckFlag(unsigned char flag); // Return true if given CPU status flag is set, false otherwise. + void SetFlag(bool set, unsigned char flag); // Set or unset processor status flag. + unsigned char AddWithCarry(unsigned char mem8); // Add With Carry, update flags and Acc. + unsigned char SubWithCarry(unsigned char mem8); // Subtract With Carry, update flags and Acc. + unsigned short GetAddrWithMode(int mode); // Get address of the byte argument with specified addr. mode + unsigned short GetArgWithMode(unsigned short opcaddr, + int mode); // Get argument from address with specified addr. mode + unsigned short Disassemble(); // Disassemble instruction and argument per addressing mode + void Add2History(string s); // add entry to op-codes execute history + bool PageBoundary(unsigned short startaddr, + unsigned short endaddr); // detect if page boundary was crossed + + // opcode execute methods + void OpCodeBrk(); + void OpCodeNop(); + void OpCodeLdaIzx(); + void OpCodeLdaZp(); + void OpCodeLdaImm(); + void OpCodeLdaAbs(); + void OpCodeLdaIzy(); + void OpCodeLdaZpx(); + void OpCodeLdaAby(); + void OpCodeLdaAbx(); + void OpCodeLdxImm(); + void OpCodeLdxZp(); + void OpCodeLdxAbs(); + void OpCodeLdxZpy(); + void OpCodeLdxAby(); + void OpCodeLdyImm(); + void OpCodeLdyZp(); + void OpCodeLdyAbs(); + void OpCodeLdyZpx(); + void OpCodeLdyAbx(); + void OpCodeTax(); + void OpCodeTay(); + void OpCodeTxa(); + void OpCodeTya(); + void OpCodeTsx(); + void OpCodeTxs(); + void OpCodeStaIzx(); + void OpCodeStaZp(); + void OpCodeStaAbs(); + void OpCodeStaIzy(); + void OpCodeStaZpx(); + void OpCodeStaAby(); + void OpCodeStaAbx(); + void OpCodeStxZp(); + void OpCodeStxAbs(); + void OpCodeStxZpy(); + void OpCodeStyZp(); + void OpCodeStyAbs(); + void OpCodeStyZpx(); + void OpCodeBneRel(); + void OpCodeBeqRel(); + void OpCodeBplRel(); + void OpCodeBmiRel(); + void OpCodeBvcRel(); + void OpCodeBvsRel(); + void OpCodeBccRel(); + void OpCodeBcsRel(); + void OpCodeIncZp(); + void OpCodeIncAbs(); + void OpCodeIncZpx(); + void OpCodeIncAbx(); + void OpCodeInx(); + void OpCodeDex(); + void OpCodeIny(); + void OpCodeDey(); + void OpCodeJmpAbs(); + void OpCodeJmpInd(); + void OpCodeOraIzx(); + void OpCodeOraZp(); + void OpCodeOraImm(); + void OpCodeOraAbs(); + void OpCodeOraIzy(); + void OpCodeOraZpx(); + void OpCodeOraAby(); + void OpCodeOraAbx(); + void OpCodeAslZp(); + void OpCodeAslAcc(); + void OpCodeAslAbs(); + void OpCodeAslZpx(); + void OpCodeAslAbx(); + void OpCodeJsrAbs(); + void OpCodeAndIzx(); + void OpCodeAndZp(); + void OpCodeAndImm(); + void OpCodeAndAbs(); + void OpCodeAndIzy(); + void OpCodeAndZpx(); + void OpCodeAndAby(); + void OpCodeAndAbx(); + void OpCodeBitZp(); + void OpCodeBitAbs(); + void OpCodeRolZp(); + void OpCodeRolAcc(); + void OpCodeRolAbs(); + void OpCodeRolZpx(); + void OpCodeRolAbx(); + void OpCodePhp(); + void OpCodePha(); + void OpCodePlp(); + void OpCodePla(); + void OpCodeClc(); + void OpCodeSec(); + void OpCodeCli(); + void OpCodeClv(); + void OpCodeCld(); + void OpCodeSed(); + void OpCodeSei(); + void OpCodeRti(); + void OpCodeRts(); + void OpCodeEorIzx(); + void OpCodeEorZp(); + void OpCodeEorImm(); + void OpCodeEorAbs(); + void OpCodeEorIzy(); + void OpCodeEorZpx(); + void OpCodeEorAby(); + void OpCodeEorAbx(); + void OpCodeLsrZp(); + void OpCodeLsrAcc(); + void OpCodeLsrAbs(); + void OpCodeLsrZpx(); + void OpCodeLsrAbx(); + void OpCodeAdcIzx(); + void OpCodeAdcZp(); + void OpCodeAdcImm(); + void OpCodeAdcAbs(); + void OpCodeAdcIzy(); + void OpCodeAdcZpx(); + void OpCodeAdcAby(); + void OpCodeAdcAbx(); + void OpCodeRorZp(); + void OpCodeRorAcc(); + void OpCodeRorAbs(); + void OpCodeRorZpx(); + void OpCodeRorAbx(); + void OpCodeCpyImm(); + void OpCodeCpyZp(); + void OpCodeCpyAbs(); + void OpCodeCmpIzx(); + void OpCodeCmpZp(); + void OpCodeCmpImm(); + void OpCodeCmpAbs(); + void OpCodeCmpIzy(); + void OpCodeCmpZpx(); + void OpCodeCmpAby(); + void OpCodeCmpAbx(); + void OpCodeDecZp(); + void OpCodeDecAbs(); + void OpCodeDecZpx(); + void OpCodeDecAbx(); + void OpCodeCpxImm(); + void OpCodeCpxZp(); + void OpCodeCpxAbs(); + void OpCodeSbcZp(); + void OpCodeSbcAbs(); + void OpCodeSbcIzx(); + void OpCodeSbcIzy(); + void OpCodeSbcZpx(); + void OpCodeSbcAby(); + void OpCodeSbcAbx(); + void OpCodeSbcImm(); + void OpCodeDud(); + +}; + +} // namespace MKBasic + +#endif diff --git a/Memory.cpp b/Memory.cpp index 698eb64..96fdb3a 100644 --- a/Memory.cpp +++ b/Memory.cpp @@ -225,10 +225,7 @@ unsigned char Memory::ReadCharKb(bool nonblock) set_conio_terminal_mode(); #endif static int c = ' '; - //putchar('\n'); if (mIOEcho && isprint(c)) putchar(c); - //else putchar(' '); - //fputs("<-Character Input (CTRL-Y to BREAK) ?\r",stdout); fflush(stdout); if (!nonblock) while(!kbhit()); else c = 0; @@ -239,8 +236,6 @@ unsigned char Memory::ReadCharKb(bool nonblock) kill(getpid(),SIGINT); } #endif - //fputs(" \r",stdout); - //fflush(stdout); mCharIOBufIn[mInBufDataEnd] = c; mInBufDataEnd++; if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0; diff --git a/ReadMe.txt b/ReadMe.txt index 809a5d1..120b8ce 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -49,9 +49,43 @@ Parts of this project is based on or contains 3-rd party work: 2. Format of the memory image definition file. +Emulator recognizes 4 formats of memory image: + +- raw binary, no header, up to 64 kB of raw data, +- binary image with a configuration/snapshot header, +- Intel HEX format, +- plain text memory image definition file. + +Please see detailed description of each format below: + Program can load raw binary image of MOS 6502 opcodes. Binary image is always loaded from address $0000 and can be up to 64 kB long, -so the code must be properly located inside that image. +so the code must be properly located inside that image. Image can be shorter +than 64 kB, user will receive warning in such case, but it will be loaded. +Binary image may have header attached at the top. +It consists of magic keyword 'SNAPSHOT' followed by 15 bytes of data +(subject to change in future versions). +The header data saves the status of CPU and emulation facilities like +character I/O address and enable flag, ROM boundaries and enable flag etc. +This header is added when user saves snapshot of the VM from debug console +menu with command: Y [file_name]. +Header is not mandatory, so the binary image created outside application can +also be used. User will receive warning at startup during image load if +header is missing, but image will be loaded. In this case, user may need +to configure necessary emulation facilities manually in debug console +before executing native 6502 code. +When binary image with a header is loaded, user can continue executing the +program from the place where it was saved by executing command from debug +console of the emulator: + X pc_value +Where: + pc_value - the address currently showing in CPU's PC register. +If the reset vector is set right, execute the 6502 code right from command +line: + mkbasic -r image_name +Above will execute the code set in reset vector without having to start it +from debug console. If 6502 code requires character I/O and/or ROM facilities +then image should include header with proper setup. Depending on your favorite 6502 assembler, you may need to use proper command line arguments or configuration to achieve properly formatted binary file. E.g.: if using CL65 from CC65 package, create configuration file that defines @@ -62,7 +96,18 @@ Two CFG files, one for microchess and one for All Suite from hmc-6502 project are supplied with this project and assembler source code adapted to be compiled with CL65. Other assemblers may need a different approach or may not be able to generate -binary images that are required for this emulator. +binary images in format required for this emulator. In such case you may need +to design your own custom conversion tools to generate such images. + +NOTE: +Simple conversion utility is supplied with this project (bin2hex), which is +described later in this file. + +Emulator recognizes Intel HEX format file. It recognizes properly data +records and end-of-file record only at this time. Similar to binary image +with no header, when Intel HEX file is loaded, user may need to configure +necessary emulation facilities manually in debug console before executing +native 6502 code. Program can also load memory image definition file (plain text), which is a format developed especially for this project. @@ -86,22 +131,25 @@ ENROM ENIO EXEC address +RESET Where: -ADDR - label indicating that starting and run address will follow in - the next line -ORG - label indicating that the address counter will change to the +ADDR - label indicating that starting and run address will follow in + the next line +ORG - label indicating that the address counter will change to the value provided in next line IOADDR - label indicating that character I/O emulation trap address will - follow in the next line -ROMBEGIN - label indicating that the emulated read-only memory start address - will follow in the next line + follow in the next line +ROMBEGIN - label indicating that the emulated read-only memory start + address will follow in the next line ROMEND - label indicating that the emulated read-only memory end address - will follow in the next line -ENROM - enable read-only memory emulation -ENIO - enable character I/O emulation -EXEC - label indicating that the auto-execute address will follow - in the next line + will follow in the next line +ENROM - enable read-only memory emulation +ENIO - enable character I/O emulation +EXEC - label indicating that the auto-execute address will follow + in the next line, 6502 program will auto-execute from that + address after memory definition file is done loading +RESET - initiate CPU reset sequence after loading memory definition file address - decimal or hexadecimal (prefix $) address in memory @@ -275,7 +323,248 @@ If you experience unexpected character input prompt while emulating 6502 code, this may be the case. Reconfigure your IOADDR to be inside ROM in such case and try again. -6. Warranty and License Agreement. +Emulator is "cycle accurate" but not time or speed accurate. +This means that each call to MKCpu::ExecOpcode() method is considered a single +CPU cycle, so depending on the executed opcode, multiple calls (# varies per +opcode and other conditions) are needed to complete the opcode execution and +proceed to the next one. Method returns pointer to the the virtual CPU +registers. One of the members of this structure is named CyclesLeft. When this +variable reaches 0, the opcode execution is considered complete. + +The VMachine class calls the ExecOpcode() method as fast as possible, so it is +not real-time accurate, as already mentioned. To implement real-time accurate +emulation, the MKCpu::ExecOpcode() method calls would have to be synchronized +to some fairly accurate time scale (some kind of timer thread or a different +solution). + +6. Debugger Console Command Reference. + +S - step + Executes single opcode at current address. +C - continue + Continues code execution from current address until BRK. +M - dump memory + Usage: M [startaddr] [endaddr] + Where: startaddr,endaddr - memory addr. in hexadecimal format [0000..FFFF]. + Dumps contents of memory, hexadecimal and ASCII formats." +G - go/continue from new address until BRK + Usage: G [address] + Where: address - memory addr. in hexadecimal format [0000.FFFF]. + Executes code at provided address, interrupted by BRK opcode. +X - execute code from new address until RTS + Usage: X [address] + Where: address - memory addr. in hexadecimal format [0000.FFFF]. + Executes code at provided address, until RTS (last one). +Q - quit + Exits from the emulator/debugger. +A - set address for next step + Usage: A [address] + Where: address - memory addr. in hexadecimal format [0000.FFFF]. + Sets current address to a new value. +N - go number of steps + Usage: N [steps] + Where: steps - number of steps in decimal format + Execute number of opcodes provided in steps argument starting + from current address. +P - IRQ + Send maskable interrupt request to CPU (set the IRQ line LOW). +W - write to memory + Usage: W [address] [hexval] [hexval] ... 100 + Where: address - memory addr. in hexadecimal format [0000.FFFF], + hexval - byte value in hexadecimal format [00.FF]. + Writes provided values to memory starting at specified address. +I - toggle char I/O emulation + Usage: I [address] + Where: address - memory addr. in hexadecimal format [0000.FFFF], + Toggles basic character I/O emulation. When enabled, all writes + to the specified memory address also writes a character code to + to a virtual console. All reads from specified memory address + are interpreted as console character input. +R - show registers + Displays CPU registers, flags and stack. +Y - snapshot + Usage: Y [file_name] + Where: file_name - the name of the output file. + Save snapshot of current CPU and memory in a binary file. +T - show I/O console + Displays/prints the contents of the virtual console screen. + Note that in run mode (commands X, G or C), virtual screen is + displayed automatically in real-time if I/O emulation is enabled. +E - toggle I/O local echo + Toggles local echo on/off when I/O emulation is enabled. +B - blank (clear) screen + Clears the screen, useful when after exiting I/O emulation or + registers animation (long stack) your screen is messed up. +F - toggle registers animation mode + When in multi-step debug mode (command: N), displaying registers + can be suppressed or, when animation mode is enabled - they will + be continuously displayed after each executed step. +J - set registers status animation delay + Usage: J [delay] + Where: delay - time of delay in milliseconds, + Sets the time added at the end of each execution step in multi + step mode (command: N). The default value is 250 ms. +K - toggle ROM emulation + Usage: K [rombegin] [romend] - to enable, + K - to disable, + (OR just use 'K' in both cases and be prompted for arguments.) + Where: + rombegin - hexadecimal address [0200..FFFF], + romend - hexadecimal address [rombegin+1..FFFF]. + Enable/disable ROM emulation and define address range to which the ROM + (read-only memory) will be mapped. Default range: $D000-$DFFF. +L - load memory image + Usage: L [image_type] [image_name] + Where: + image_type - B (binary), H (Intel HEX) OR D (definition), + image_name - name of the image file. + This function allows to load new memory image from either binary + image file, Intel HEX format file or the ASCII definition file. + The binary image is always loaded from address 0x0000 and can be up to + 64kB long. The definition file format is a plain text file that can + contain following keywords and data: + + ADDR This keyword defines the run address of the executable code. + It is optional, but if exists, it must be the 1-st keyword + in the definition file. + Address in decimal or hexadecimal ($xxxx) format must follow + in the next line. + + ORG Changes the current address counter. The line that follows + sets the new address in decimal or hexadecimal format. + Data that follows will be put in memory starting from that + address. This keyword is optional and can be used multiple + times in the definition file. + + IOADDR Defines the address of the character I/O emulation. The + next line sets the address of I/O emulation in decimal or + hexadecimal format. If the I/O emulation is enabled + (see ENIO keyword), then any character written to this + address will be sent to the virtual console. The reading + from that address will invoke character input from the + emulated console. That input procedure is of blocking + type. To invoke non-blocking character procedure, reading + should be performed from IOADDR+1. + + ROMBEGIN Defines the address in memory where the beginning of the + Read Only memory is mapped. The next line that follows this + keyword sets the address in decimal or hexadecimal format. + + ROMEND Defines the address in memory where the end of the Read + Only Memory is mapped. The next line that follows this + keyword sets the address in decimal or hexadecimal format. + + ENIO Putting this keyword in memory definition file enables + rudimentary character I/O emulation and virtual console + emulation. + + ENROM Putting this keyword in memory definition file enables + emulation of Read Only Memory, in range of addresses + defined by ROMBEGIN and ROMEND keywords. + + EXEC Define starting address of code which will be automatically + executed after the memory image is loaded. + The next line that follows this keyword sets the address + in decimal or hexadecimal format. + + RESET Enables auto-reset of the CPU. After loading the memory + definition file, the CPU reset sequence will be initiated. + +O - display op-codes history + Show the history of last executed op-codes/instructions, full with + disassembled mnemonic and argument. +D - diassemble code in memory + Usage: D [startaddr] [endaddr] + Where: startaddr,endaddr - hexadecimal address [0000..FFFF]. + Attempt to disassemble code in specified address range and display + the results (print) on the screen in symbolic form. +0 - reset + Run the processor initialization sequence, just like the real CPU + when its RTS signal is set to LOW and HIGH again. CPU will disable + interrupts, copy address from vector $FFFC to processors PC and will + start executing code. Programmer must put initialization routine + under address pointed by $FFFC vector, which will set the arithmetic + mode, initialize stack, I/O devices and enable IRQ if needed before + jumping to main loop. The reset routine disables trapping last RTS + opcode if stack is empty, so the VM will never return from opcodes + execution loop unless user interrupts with CTRL-C or CTRL-Break. + +NOTE: + 1. If no arguments provided, each command will prompt user to enter + missing data. + 2. It is possible to exit from running program to debugger console + by pressing CTRL-C or CTRL-Pause/Break, which will generate + a "Operator Interrupt". However in the character input mode + use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux). + You may need to press ENTER after that in input mode (DOS) + +7. Command line usage. + +D:\src\wrk\mkbasic>mkbasic -h +Virtual Machine/CPU Emulator (MOS 6502) and Debugger. +Copyright (C) by Marek Karcz 2016. All rights reserved. + + +Usage: + + mkbasic [-h] | [ramdeffile] | [-b ramimage] | [-r ramimage] + OR + mkbasic [-x intelheximage] + +Where: + + ramdeffile - RAM definition file name + intelheximage - Intel HEX format file + ramimage - RAM binary image file name + + +When ran with no arguments, program will load default memory +definition files: default.rom, default.ram and will enter the debug +console menu. +When ramdeffile argument is provided, program will load the memory +definition from the file, set the flags and parameters depending on the +contents of the memory definition file and enter the corresponding mode +of operation as defined in that file. +If used with flag -b or -x, program will load memory from the provided image +file and enter the debug console menu. +If used with flag -r, program will load memory from the provided image +file and execute CPU reset sequence. + +8. Utilities. + +Utility bin2hex is supplied with the project to aid in conversion from raw +binary memory image to one of the plain text formats recognized by emulator +NOTE: In current version, emulator can load raw binary format directly, so + usefulness of this utility is somewhat deprecated. + +D:\src\wrk\mkbasic>bin2hex -h + +Program: bin2hex + Convert binary file to Intel HEX format. +OR + Convert binary file to memory image definition for MKBASIC (VM65) emulator. + +Copyright: Marek Karcz 2016. All rights reserved. +Free for personal and educational use. + +Usage: + + bin2hex -f input -o output [-w addr] [-x exec] [[-s] [-z] | -i] + +Where: + + input - binary file name + output - output file name + addr - starting address to load data (default: 2048) + exec - address to auto-execute code from (default: 2048) + -s - suppress auto-execute statement in output + -z - suppress data blocks with 0-s only + -i - convert to Intel HEX format + NOTE: When this switch is used, addr, exec, -s, -z are ignored, + addr = 0, exec is not set and data blocks with 0-s only + are always suppressed. + +9. Warranty and License Agreement. This software is provided with No Warranty. I (The Author) will not be held responsible for any damage to computer diff --git a/VMachine.cpp b/VMachine.cpp index 69f79a9..5277f13 100644 --- a/VMachine.cpp +++ b/VMachine.cpp @@ -74,7 +74,7 @@ VMachine::~VMachine() * Purpose: Initialize class. * Arguments: n/a * Returns: n/a - *-------------------------------------------------------------------- +f *-------------------------------------------------------------------- */ void VMachine::InitVM() { @@ -82,6 +82,7 @@ void VMachine::InitVM() mpRAM = new Memory(); mAutoExec = false; + mAutoReset = false; mCharIOAddr = CHARIO_ADDR; mCharIOActive = mCharIO = false; if (NULL == mpRAM) { @@ -106,10 +107,11 @@ void VMachine::InitVM() /* *-------------------------------------------------------------------- - * Method: - * Purpose: - * Arguments: - * Returns: + * Method: ClearScreen() + * Purpose: Clear the working are of the VM - DOS. + * This is not a part of virtual display emulation. + * Arguments: n/a + * Returns: n/a *-------------------------------------------------------------------- */ void VMachine::ClearScreen() @@ -151,10 +153,11 @@ void VMachine::ClearScreen() /* *-------------------------------------------------------------------- - * Method: - * Purpose: - * Arguments: - * Returns: + * Method: ScrHome() + * Purpose: Bring the console cursor to home position - DOS. + * This is not a part of virtual display emulation. + * Arguments: n/a + * Returns: n/a *-------------------------------------------------------------------- */ void VMachine::ScrHome() @@ -175,10 +178,11 @@ void VMachine::ScrHome() /* *-------------------------------------------------------------------- - * Method: - * Purpose: - * Arguments: - * Returns: + * Method: ClearScreen() + * Purpose: Clear the working are of the VM - Linux. + * This is not a part of virtual display emulation. + * Arguments: n/a + * Returns: n/a *-------------------------------------------------------------------- */ void VMachine::ClearScreen() @@ -188,10 +192,11 @@ void VMachine::ClearScreen() /* *-------------------------------------------------------------------- - * Method: - * Purpose: - * Arguments: - * Returns: + * Method: ScrHome() + * Purpose: Bring the console cursor to home position - Linux. + * This is not a part of virtual display emulation. + * Arguments: n/a + * Returns: n/a *-------------------------------------------------------------------- */ void VMachine::ScrHome() @@ -203,10 +208,10 @@ void VMachine::ScrHome() /* *-------------------------------------------------------------------- - * Method: - * Purpose: - * Arguments: - * Returns: + * Method: ShowDisp() + * Purpose: Show the emulated virtual text display device contents. + * Arguments: n/a + * Returns: n/a *-------------------------------------------------------------------- */ void VMachine::ShowDisp() @@ -234,7 +239,7 @@ Regs *VMachine::Run() ShowDisp(); while (true) { cpureg = Step(); - if (mCharIO) { + if (cpureg->CyclesLeft == 0 && mCharIO) { ShowDisp(); } if (cpureg->SoftIrq || mOpInterrupt) @@ -264,8 +269,8 @@ Regs *VMachine::Run(unsigned short addr) /* *-------------------------------------------------------------------- * Method: Exec() - * Purpose: Run VM from current address until last RTS. - * NOTE: Stack must be empty! + * Purpose: Run VM from current address until last RTS (if enabled). + * NOTE: Stack must be empty for last RTS to be trapped. * Arguments: n/a * Returns: Pointer to CPU registers and flags. *-------------------------------------------------------------------- @@ -279,7 +284,7 @@ Regs *VMachine::Exec() ShowDisp(); while (true) { cpureg = Step(); - if (mCharIO) { + if (cpureg->CyclesLeft == 0 && mCharIO) { cout << mpDisp->GetLastChar(); cout << flush; } @@ -322,7 +327,7 @@ Regs *VMachine::Step() addr = cpureg->PtrAddr; mRunAddr = addr; - if (mCharIOActive && !mOpInterrupt) { + if (cpureg->CyclesLeft == 0 && mCharIOActive && !mOpInterrupt) { char c = -1; mCharIO = false; while ((c = mpRAM->GetCharOut()) != -1) { @@ -380,35 +385,340 @@ void VMachine::LoadRAM(string ramfname) /* *-------------------------------------------------------------------- - * Method: LoadRAMBin() - * Purpose: Load data from binary image file to the memory. - * Arguments: ramfname - name of the RAM file definition + * Method: HasHdrData() + * Purpose: Check for header in the binary memory image. + * Arguments: File pointer. + * Returns: true if magic keyword found at the beginning of the + * memory image file, false otherwise + *-------------------------------------------------------------------- + */ +bool VMachine::HasHdrData(FILE *fp) +{ + bool ret = false; + int n = 0, l = strlen(HDRMAGICKEY); + char buf[20]; + + memset(buf, 0, 20); + + while (0 == feof(fp) && 0 == ferror(fp)) { + unsigned char val = fgetc(fp); + buf[n] = val; + n++; + if (n >= l) break; + } + ret = (0 == strncmp(buf, HDRMAGICKEY, l)); + + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: LoadHdrData() + * Purpose: Load data from binary image header. + * Arguments: File pointer. + * Returns: bool, true if success, false if error + * + * Details: + * Header of the binary memory image consists of magic keyword + * string followed by the data (unsigned char values). + * It has following format: + * + * MAGIC_KEYWORD + * aabbccddefghijk + * + * Where: + * MAGIC_KEYWORD - text string indicating header, may vary between + * versions thus rendering headers from previous + * versions incompatible - currently: "SNAPSHOT" + * aa - low and hi bytes of execute address (PC) + * bb - low and hi bytes of char IO address + * cc - low and hi bytes of ROM begin address + * dd - low and hi bytes of ROM end address + * e - 0 if char IO is disabled, 1 if enabled + * f - 0 if ROM is disabled, 1 if enabled + * g - value in CPU Acc (accumulator) register + * h - value in CPU X (X index) register + * i - value in CPU Y (Y index) register + * j - value in CPU PS (processor status/flags) + * k - value in CPU SP (stack pointer) register + * + * NOTE: + * If magic keyword was detected, this part is already read and file + * pointer position is at the 1-st byte of data. Therefore this + * method does not have to read and skip the magic keyword. + *-------------------------------------------------------------------- + */ +bool VMachine::LoadHdrData(FILE *fp) +{ + int n = 0, l = 0; + unsigned short rb = 0, re = 0; + Regs r; + bool ret = false; + + while (0 == feof(fp) && 0 == ferror(fp) && n < HDRDATALEN) { + unsigned char val = fgetc(fp); + switch (n) + { + case 1: mRunAddr = l + 256 * val; + break; + case 3: mCharIOAddr = l + 256 * val; + break; + case 5: rb = l + 256 * val; + break; + case 7: re = l + 256 * val; + break; + case 8: mCharIOActive = (val != 0); + break; + case 9: if (val != 0) { + mpRAM->EnableROM(rb, re); + } else { + mpRAM->SetROM(rb, re); + } + break; + case 10: r.Acc = val; + break; + case 11: r.IndX = val; + break; + case 12: r.IndY = val; + break; + case 13: r.Flags = val; + break; + case 14: r.PtrStack = val; + ret = true; + break; + default: break; + } + l = val; + n++; + } + if (ret) { + r.PtrAddr = mRunAddr; + mpCPU->SetRegs(r); + } + + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: SaveHdrData() + * Purpose: Save header data to binary file (memory snapshot). + * Arguments: File pointer, must be opened for writing in binary mode. * Returns: n/a *-------------------------------------------------------------------- */ -void VMachine::LoadRAMBin(string ramfname) +void VMachine::SaveHdrData(FILE *fp) +{ + char buf[20] = {0}; + strcpy(buf, HDRMAGICKEY); + for (unsigned int i = 0; i < strlen(HDRMAGICKEY); i++) { + fputc(buf[i], fp); + } + Regs *reg = mpCPU->GetRegs(); + unsigned char lo = 0, hi = 0; + lo = (unsigned char) (reg->PtrAddr & 0x00FF); + hi = (unsigned char) ((reg->PtrAddr & 0xFF00) >> 8); + fputc(lo, fp); + fputc(hi, fp); + lo = (unsigned char) (mCharIOAddr & 0x00FF); + hi = (unsigned char) ((mCharIOAddr & 0xFF00) >> 8); + fputc(lo, fp); + fputc(hi, fp); + lo = (unsigned char) (GetROMBegin() & 0x00FF); + hi = (unsigned char) ((GetROMBegin() & 0xFF00) >> 8); + fputc(lo, fp); + fputc(hi, fp); + lo = (unsigned char) (GetROMEnd() & 0x00FF); + hi = (unsigned char) ((GetROMEnd() & 0xFF00) >> 8); + fputc(lo, fp); + fputc(hi, fp); + lo = (mCharIOActive ? 1 : 0); + fputc(lo, fp); + lo = (IsROMEnabled() ? 1 : 0); + fputc(lo, fp); + Regs *pregs = mpCPU->GetRegs(); + if (pregs != NULL) { + fputc(pregs->Acc, fp); + fputc(pregs->IndX, fp); + fputc(pregs->IndY, fp); + fputc(pregs->Flags, fp); + fputc(pregs->PtrStack, fp); + } +} + +/* + *-------------------------------------------------------------------- + * Method: SaveSnapshot() + * Purpose: Save current state of the VM and memory image. + * Arguments: String - file name. + * Returns: int, 0 if successful, greater then 0 if not (# of bytes + * not written). + *-------------------------------------------------------------------- + */ +int VMachine::SaveSnapshot(string fname) +{ + FILE *fp = NULL; + int ret = MAX_8BIT_ADDR+1; + + if ((fp = fopen(fname.c_str(), "wb")) != NULL) { + SaveHdrData(fp); + for (int addr = 0; addr < MAX_8BIT_ADDR+1; addr++) { + if (addr != mCharIOAddr && addr != mCharIOAddr+1) { + unsigned char b = mpRAM->Peek8bit((unsigned short)addr); + if (EOF != fputc(b, fp)) ret--; + else break; + } else { + if (EOF != fputc(0, fp)) ret--; + else break; + } + } + fclose(fp); + } + + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: LoadRAMBin() + * Purpose: Load data from binary image file to the memory. + * Arguments: ramfname - name of the RAM file definition + * Returns: int - error code + * 0 - OK + * 1 - WARNING: Unexpected EOF (image shorter than 64kB). + * 2 - WARNING: Unable to open memory image file. + * 3 - WARNING: Problem with binary image header. + * 4 - WARNING: No header found in binary image. + * 5 - WARNING: Problem with binary image header and + * Unexpected EOF (image shorter than 64kB). + * 6 - WARNING: No header found in binary image and + * Unexpected EOF (image shorter than 64kB). + * TO DO: + * - Add fixed size header to binary image with emulator + * configuration data. Presence of the header will be detected + * by magic key at the beginning. Header should also include + * snapshot info, so the program can continue from the place + * where it was frozen/saved. + *-------------------------------------------------------------------- + */ +int VMachine::LoadRAMBin(string ramfname) { FILE *fp = NULL; unsigned short addr = 0x0000; int n = 0; Memory *pm = mpRAM; + int ret = 2; if ((fp = fopen(ramfname.c_str(), "rb")) != NULL) { + if (HasHdrData(fp)) { + ret = (LoadHdrData(fp) ? 0 : 3); + } else { + ret = 4; + rewind(fp); + } + // temporarily disable emulation facilities to allow + // proper memory image initialization + bool tmp1 = mCharIOActive, tmp2 = mpRAM->IsROMEnabled(); + DisableCharIO(); + DisableROM(); while (0 == feof(fp) && 0 == ferror(fp)) { unsigned char val = fgetc(fp); pm->Poke8bit(addr, val); addr++; n++; } fclose(fp); + // restore emulation facilities status + if (tmp1) SetCharIO(mCharIOAddr, false); + if (tmp2) EnableROM(); if (n <= 0xFFFF) { - cout << "WARNING: Unexpected EOF." << endl; + switch (ret) { + case 0: ret = 1; break; + case 3: ret = 5; break; + case 4: ret = 6; break; + default: break; + } } } - else { - cout << "WARNING: Unable to open memory image file: " << ramfname << endl; - cout << "Press [ENTER]..."; - getchar(); - } + + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: LoadRAMHex() + * Purpose: Load data from Intel HEX file format to memory. + * Arguments: hexfname - name of the HEX file + * Returns: int, 0 if OK, >0 - error code: + * 1 - unable to open file + * 2 - syntax error + * 3 - hex format error + *-------------------------------------------------------------------- + */ +int VMachine::LoadRAMHex(string hexfname) +{ + char line[256] = {0}; + FILE *fp = NULL; + int ret = 0; + unsigned int addr = 0; + + bool tmp1 = mCharIOActive, tmp2 = mpRAM->IsROMEnabled(); + DisableCharIO(); + DisableROM(); + if ((fp = fopen(hexfname.c_str(), "r")) != NULL) { + while (0 == feof(fp) && 0 == ferror(fp)) { + line[0] = '\0'; + fgets(line, 256, fp); + if (line[0] == ':') { + if (0 == strcmp(line, HEXEOF)) { + break; // EOF, we are done here. + } + char blen[3] = {0,0,0}; + char baddr[5] = {0,0,0,0,0}; + char brectype[3] = {0,0,0}; + blen[0] = line[1]; + blen[1] = line[2]; + blen[2] = 0; + baddr[0] = line[3]; + baddr[1] = line[4]; + baddr[2] = line[5]; + baddr[3] = line[6]; + baddr[4] = 0; + brectype[0] = line[7]; + brectype[1] = line[8]; + brectype[2] = 0; + unsigned int reclen = 0, rectype = 0; + sscanf(blen, "%02x", &reclen); + sscanf(baddr, "%04x", &addr); + sscanf(brectype, "%02x", &rectype); + if (reclen == 0 && rectype == 1) break; // EOF, we are done here. + if (rectype != 0) continue; // not a data record, next! + for (unsigned int i=9; i=strlen(line)-3) { + ret = 3; // hex format error + break; + } + char dbuf[3] = {0,0,0}; + unsigned int byteval = 0; + Memory *pm = mpRAM; + dbuf[0] = line[i]; + dbuf[1] = line[i+1]; + dbuf[2] = 0; + sscanf(dbuf, "%02x", &byteval); + pm->Poke8bit(addr, (unsigned char)byteval&0x00FF); + } + } else { + ret = 2; // syntax error + break; + } + } + fclose(fp); + } else { + ret = 1; // unable to open file + } + if (tmp1) SetCharIO(mCharIOAddr, false); + if (tmp2) EnableROM(); + + return ret; } /* @@ -506,6 +816,10 @@ void VMachine::LoadMEM(string memfname, Memory *pmem) Memory *pm = pmem; if ((fp = fopen(memfname.c_str(), "r")) != NULL) { + // to ensure proper memory initialization, disable emulation + // of char I/O and ROM + DisableROM(); + DisableCharIO(); while (0 == feof(fp) && 0 == ferror(fp)) { line[0] = '\0'; @@ -593,6 +907,11 @@ void VMachine::LoadMEM(string memfname, Memory *pmem) } continue; } + // auto reset + if (0 == strncmp(line, "RESET", 5)) { + mAutoReset = true; + continue; + } // define ROM begin address if (0 == strncmp(line, "ROMBEGIN", 8)) { line[0] = '\0'; @@ -651,7 +970,7 @@ void VMachine::LoadMEM(string memfname, Memory *pmem) else pm->SetROM(rombegin, romend); } else { - if (enrom) pm->EnableROM(); + if (enrom) EnableROM(); } if (enio) { SetCharIO(mCharIOAddr, false); @@ -796,6 +1115,19 @@ bool VMachine::IsAutoExec() return mAutoExec; } +/* + *-------------------------------------------------------------------- + * Method: IsAutoReset() + * Purpose: Return status of auto-reset flag. + * Arguments: n/a + * Returns: bool - true if auto-exec flag is enabled. + *-------------------------------------------------------------------- + */ +bool VMachine::IsAutoReset() +{ + return mAutoReset; +} + /* *-------------------------------------------------------------------- * Method: @@ -954,4 +1286,33 @@ unsigned short VMachine::Disassemble(unsigned short addr, char *buf) return mpCPU->Disassemble(addr, buf); } +/* + *-------------------------------------------------------------------- + * Method: Reset() + * Purpose: Reset VM and CPU (should never return except operator + * induced interrupt). + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::Reset() +{ + mpCPU->Reset(); + Exec(mpCPU->GetRegs()->PtrAddr); + mpCPU->mExitAtLastRTS = true; +} + +/* + *-------------------------------------------------------------------- + * Method: Interrupt() + * Purpose: Send Interrupt ReQuest to CPU (set the IRQ line LOW). + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::Interrupt() +{ + mpCPU->Interrupt(); +} + } // namespace MKBasic diff --git a/VMachine.h b/VMachine.h index 97d784b..d256289 100644 --- a/VMachine.h +++ b/VMachine.h @@ -15,6 +15,9 @@ #define IOREFRESH 32 #define OPINTERRUPT 25 // operator interrupt code (CTRL-Y) +#define HDRMAGICKEY "SNAPSHOT" +#define HDRDATALEN 15 +#define HEXEOF ":00000001FF" using namespace std; @@ -36,7 +39,8 @@ class VMachine Regs *Step(unsigned short addr); void LoadROM(string romfname); void LoadRAM(string ramfname); - void LoadRAMBin(string ramfname); + int LoadRAMBin(string ramfname); + int LoadRAMHex(string hexfname); unsigned short MemPeek8bit(unsigned short addr); void MemPoke8bit(unsigned short addr, unsigned char v); Regs *GetRegs(); @@ -48,6 +52,7 @@ class VMachine void ClearScreen(); void ScrHome(); bool IsAutoExec(); + bool IsAutoReset(); void EnableROM(); void DisableROM(); void SetROM(unsigned short start, unsigned short end); @@ -60,6 +65,9 @@ class VMachine bool IsOpInterrupt(); queue GetExecHistory(); unsigned short Disassemble(unsigned short addr, char *buf); + void Reset(); + void Interrupt(); + int SaveSnapshot(string fname); protected: @@ -75,9 +83,13 @@ class VMachine bool mCharIO; bool mOpInterrupt; // operator interrupt from console bool mAutoExec; + bool mAutoReset; void LoadMEM(string memfname, Memory *pmem); void ShowDisp(); + bool HasHdrData(FILE *fp); + bool LoadHdrData(FILE *fp); + void SaveHdrData(FILE *fp); }; } // namespace MKBasic diff --git a/bin2hex.c b/bin2hex.c index 5d5cd5f..5dac53f 100644 --- a/bin2hex.c +++ b/bin2hex.c @@ -24,29 +24,51 @@ int g_nStartAddr = 2048; /* $0800 */ int g_nExecAddr = 2048; /* $0800 */ int g_nSuppressAutoExec = 1; int g_nSuppressAllZeroRows = 0; +int g_nConvert2IntelHex = 0; void ScanArgs(int argc, char *argv[]); void ConvertFile(void); +void Convert2IntelHex(void); +/* + *-------------------------------------------------------------------- + * Method: Usage() + * Purpose: Print usage information/help. + * Arguments: char * - program name. + * Returns: n/a + *-------------------------------------------------------------------- + */ void Usage(char *prgn) { - printf("\nProgram: %s\n Convert binary file to memory image definition for MKBASIC (VM65) emulator.\n\n", prgn); + printf("\nProgram: %s\n Convert binary file to Intel HEX format.\nOR\n", prgn); + printf(" Convert binary file to memory image definition for MKBASIC (VM65) emulator.\n\n"); printf("Copyright: Marek Karcz 2016. All rights reserved.\n"); printf("Free for personal and educational use.\n\n"); printf("Usage:\n\n"); - printf(" %s -f input_fname -o output_fname [-w load_addr] [-x exec_addr] [-s] [-z]\n\n", prgn); + printf(" %s -f input -o output [-w addr] [-x exec] [[-s] [-z] | -i]\n\n", prgn); printf("Where:\n\n"); - printf(" input_fname - binary file name\n"); - printf(" output_fname - output file name\n"); - printf(" load_addr - starting address to load data (default: %d)\n", g_nStartAddr); - printf(" exec_addr - address to auto-execute code from (default: %d)\n", g_nExecAddr); - printf(" -s - suppress auto-execute statement in output\n"); - printf(" -z - suppress data blocks with 0-s only\n"); + printf(" input - binary file name\n"); + printf(" output - output file name\n"); + printf(" addr - starting address to load data (default: %d)\n", g_nStartAddr); + printf(" exec - address to auto-execute code from (default: %d)\n", g_nExecAddr); + printf(" -s - suppress auto-execute statement in output\n"); + printf(" -z - suppress data blocks with 0-s only\n"); + printf(" -i - convert to Intel HEX format\n"); + printf(" NOTE: When this switch is used, addr, exec, -s, -z are ignored,\n"); + printf(" addr = 0, exec is not set and data blocks with 0-s only\n"); + printf(" are always suppressed.\n"); printf("\n"); } /* - * bin2hex -f InputFile -o OutputFile -w StartAddr + *-------------------------------------------------------------------- + * Method: ScanArgs() + * Purpose: Scan/parse command line arguments and set internal + * flags and parameters. + * Arguments: int argc - # of command line arguments, + * char *argv[] - array of command line arguments. + * Returns: n/a + *-------------------------------------------------------------------- */ void ScanArgs(int argc, char *argv[]) { @@ -85,11 +107,24 @@ void ScanArgs(int argc, char *argv[]) { g_nSuppressAllZeroRows = 1; } + else if (strcmp(argv[n],"-i") == 0) + { + g_nConvert2IntelHex = 1; + } n++; } } +/* + *-------------------------------------------------------------------- + * Method: ConvertFile() + * Purpose: Convert binary file to plain text memory definition + * file for MKBASIC (VM65) emulator. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ void ConvertFile(void) { FILE *fpi = NULL; @@ -170,6 +205,96 @@ void ConvertFile(void) printf("ERROR: Unable to open input file.\n"); } +/* + *-------------------------------------------------------------------- + * Method: Convert2IntelHex() + * Purpose: Convert binary file to Intel HEX format. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void Convert2IntelHex(void) +{ + FILE *fpi = NULL; + FILE *fpo = NULL; + unsigned char bt[17], cksum; + char hex[80]; + int i, allzero; + unsigned int addr; + + addr = 0; + g_nSuppressAllZeroRows = 1; + g_nExecAddr = 0; + printf("Processing...\n"); + printf("Start address: $%04x\n", addr); + if (NULL != (fpi = fopen(g_szInputFileName,"rb"))) + { + if (NULL != (fpo = fopen(g_szHexFileName,"w"))) + { + while(0 == feof(fpi) && addr <= 0xFFFF) + { + memset(bt, 0, 17); + memset(hex, 0, 80); + if (DEBUG) printf("Reading input file..."); + fread(bt, sizeof(char), 16, fpi); + if (DEBUG) printf("done.\n"); + if (DEBUG) printf("Preparing hex string...\n"); + /* start the Intel HEX data record, all data blocks + generated by this program are 16-bytes long + */ + sprintf(hex, ":10%04x00", addr); + cksum = 0; + allzero = 1; + /* append data to record */ + for(i=0; i<16; i++) + { + cksum += bt[i]; + if (DEBUG) printf("Code: %d\n", bt[i]); + sprintf(hex, "%s%02x", hex, bt[i]); + if (allzero && bt[i]) allzero = 0; + } + cksum = ~cksum + 1; /* Two's complement of modulo 256 sum */ + /* end record with check sum value */ + sprintf(hex, "%s%02x", hex, cksum); + /* output only if non-zero data present in 16-byte block */ + if (0 == g_nSuppressAllZeroRows + || + (g_nSuppressAllZeroRows && 0 == allzero) + ) + { + sprintf(hex, "%s\n", hex); + if (DEBUG) printf("Adding line: %s", hex); + fputs(hex, fpo); + } + addr += 16; + } + /* add EOF */ + hex[0] = 0; + strcpy(hex, ":00000001FF"); + sprintf(hex, "%s\n", hex); + fputs(hex, fpo); + fclose(fpi); + fclose(fpo); + printf("Done.\n"); + printf("End address: $%04x\n", (addr <= 0xFFFF) ? addr : 0xFFFF); + printf("Run address: $%04x\n", g_nExecAddr); + } + else + printf("ERROR: Unable to create output file.\n"); + } + else + printf("ERROR: Unable to open input file.\n"); +} + +/* + *-------------------------------------------------------------------- + * Method: main() + * Purpose: Main program loop/routine. + * Arguments: int argc - # of provided in command line arguments. + * char *argv[] - array of command line arguments. + * Returns: int - always 0. + *-------------------------------------------------------------------- + */ int main(int argc, char *argv[]) { if (argc == 1) @@ -178,8 +303,12 @@ int main(int argc, char *argv[]) ScanArgs(argc, argv); if (*g_szInputFileName == 0 || *g_szHexFileName == 0) Usage(argv[0]); - else - ConvertFile(); + else { + if (0 == g_nConvert2IntelHex) + ConvertFile(); + else + Convert2IntelHex(); + } } return 0; } diff --git a/ehbas.dat b/ehbas.dat index 6c74266..e2b8799 100644 --- a/ehbas.dat +++ b/ehbas.dat @@ -10,13 +10,15 @@ $FFE0 ROMBEGIN $FFC0 ROMEND -$FFDF +$FFFF +;$FFDF ; Enable char IO and ROM ENIO ENROM ; Auto-execute -EXEC -$C000 +;EXEC +;$C000 +RESET ; Data/Code $D8, $A0, $08, $B9, $FB, $E0, $99, $00 $02, $88, $10, $F7, $A2, $FF, $8E, $21 @@ -2062,3 +2064,6 @@ $AD, $E1, $FF, $F0, $0C, $C9, $61, $90 $06, $C9, $7B, $B0, $02, $29, $5F, $38 $60, $18, $60, $8D, $E0, $FF, $29, $FF $60, $00, $00, $00, $00, $00, $00, $00 +ORG +$FFFC +$00 $C0 diff --git a/main.cpp b/main.cpp index c01e2ea..4c7c5d7 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "system.h" #include "MKCpu.h" #include "Memory.h" @@ -24,6 +25,109 @@ int g_stackdisp_lines = 1; bool ShowRegs(Regs *preg, VMachine *pvm, bool ioecho, bool showiostat); void ShowHelp(); +void CmdArgHelp(string prgname); +void CopyrightBanner(); + +/* + *-------------------------------------------------------------------- + * Method: RunSingleInstr() + * Purpose: Execute single instruction of the CPU (all cycles). + * Arguments: addr - unsigned short, instruction address + * Returns: pointer to CPU registers + *-------------------------------------------------------------------- + */ + Regs *RunSingleInstr(unsigned short addr) + { + Regs *ret = NULL; + + do { + ret = pvm->Step(addr); + } while (ret->CyclesLeft > 0); + + return ret; + } + + /* + *-------------------------------------------------------------------- + * Method: RunSingleCurrInstr() + * Purpose: Execute single instruction of the CPU (all cycles) + * at current address. + * Arguments: n/a + * Returns: pointer to CPU registers + *-------------------------------------------------------------------- + */ + Regs *RunSingleCurrInstr() + { + Regs *ret = NULL; + + do { + ret = pvm->Step(); + } while (ret->CyclesLeft > 0); + + return ret; + } + +/* + *-------------------------------------------------------------------- + * Method: PrintLoadBinImgErr() + * Purpose: Print the warning/error message after loading binary + * image. + * Arguments: err - integer, error code + * Returns: n/a + *-------------------------------------------------------------------- + */ +void PrintLoadBinImgErr(int err) +{ + bool pressenter = true; + switch (err) { + case 1: cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl; + break; + case 2: cout << "WARNING: Unable to open memory image file." << endl; + break; + case 3: cout << "WARNING: Problem with binary image header." << endl; + break; + case 4: cout << "WARNING: No header found in binary image." << endl; + break; + case 5: cout << "WARNING: Problem with binary image header." << endl; + cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl; + break; + case 6: cout << "WARNING: No header found in binary image." << endl; + cout << "WARNING: Unexpected EOF (image shorter than 64kB)." << endl; + break; + default: pressenter = false; break; + } + if (pressenter) { + cout << "Press [ENTER]..."; + getchar(); + } +} + +/* + *-------------------------------------------------------------------- + * Method: PrintLoadHexImgErr() + * Purpose: Print the warning/error message after loading Intel HEX + * image. + * Arguments: err - integer, error code + * Returns: n/a + *-------------------------------------------------------------------- + */ +void PrintLoadHexImgErr(int err) +{ + bool pressenter = true; + switch (err) { + case 1: cout << "WARNING: Unable to open file." << endl; + break; + case 2: cout << "ERROR: Syntax error." << endl; + break; + case 3: cout << "ERROR: Intel HEX format error." << endl; + break; + default: pressenter = false; break; + } + if (pressenter) { + cout << "Press [ENTER]..."; + getchar(); + } +} #if defined(LINUX) @@ -233,14 +337,14 @@ void ShowMenu() { cout << "------------------------------------+----------------------------------------" << endl; cout << " C - continue, S - step | A - set address for next step" << endl; - cout << " G - go/cont. from new address | N - go number of steps" << endl; + cout << " G - go/cont. from new address | N - go number of steps, P - IRQ" << endl; cout << " I - toggle char I/O emulation | X - execute from new address" << endl; cout << " T - show I/O console | B - blank (clear) screen" << endl; cout << " E - toggle I/O local echo | F - toggle registers animation" << endl; cout << " J - set animation delay | M - dump memory, W - write memory" << endl; - cout << " K - toggle ROM emulation | R - show registers" << endl; + cout << " K - toggle ROM emulation | R - show registers, Y - snapshot" << endl; cout << " L - load memory image | O - display op-codes history" << endl; - cout << " D - disassemble code in memory | Q - quit, H - help" << endl; + cout << " D - disassemble code in memory | Q - quit, 0 - reset, H - help" << endl; cout << "------------------------------------+----------------------------------------" << endl; } @@ -274,7 +378,7 @@ void ShowMenu() while(step && nsteps > 1 && !brk && !lrts && !opbrk) { \ cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct; \ cout << " \r"; \ - preg = pvm->Step(); \ + preg = RunSingleCurrInstr(); \ if (anim) { \ if (cls & ClsIfDirty) { pvm->ClearScreen(); cls = false; } \ pvm->ScrHome(); \ @@ -292,6 +396,7 @@ void ShowMenu() /* run this program using the console pauser or add your own getch, system("pause") or input loop */ int main(int argc, char** argv) { + bool loadbin = false, loadhex = false, reset = false, execvm = false; #if defined(LINUX) signal(SIGINT, trap_signal); signal(SIGTERM, trap_signal); @@ -301,17 +406,48 @@ int main(int argc, char** argv) { #endif string romfile("dummy.rom"), ramfile("dummy.ram"); if (argc > 1) { - ramfile = argv[1]; + if (argc > 2) { + reset = execvm = loadbin = (0 == strcmp(argv[1], "-r")); // load binary image and reset + if (!loadbin) loadbin = (0 == strcmp(argv[1], "-b")); // just load binary image + if (!loadbin) loadhex = (0 == strcmp(argv[1], "-x")); // just load Intel HEX image + if (loadbin && loadhex) { + cout << "ERROR: Can't load both formats at the same time." << endl; + exit(-1); + } + ramfile = argv[2]; + } else { + if (0 == strcmp(argv[1], "-h")) { + CmdArgHelp(argv[0]); + exit(0); + } + ramfile = argv[1]; + } } try { cout << endl; - pvm = new VMachine(romfile, ramfile); + if (loadbin) { + pvm = new VMachine(romfile, "dummy.ram"); + if (NULL != pvm) { + PrintLoadBinImgErr (pvm->LoadRAMBin(ramfile)); + if (!reset && !execvm) + reset = execvm = pvm->IsAutoReset(); + } + } else if (loadhex) { + pvm = new VMachine(romfile, "dummy.ram"); + if (NULL != pvm) PrintLoadHexImgErr (pvm->LoadRAMHex(ramfile)); + } + else { + pvm = new VMachine(romfile, ramfile); + reset = execvm = pvm->IsAutoReset(); + } + if (NULL == pvm) { + throw MKGenException("Out of memory"); + } pvm->ClearScreen(); - cout << "Virtual Machine/CPU Emulator (MOS 6502) and Debugger." << endl; - cout << "Copyright (C) by Marek Karcz 2016. All rights reserved." << endl; + CopyrightBanner(); string cmd; bool runvm = false, step = false, brk = false, execaddr = false, stop = true; - bool lrts = false, execvm = false, anim = false, enrom = pvm->IsROMEnabled(); + bool lrts = false, anim = false, enrom = pvm->IsROMEnabled(); unsigned int newaddr = pvm->GetRunAddr(), ioaddr = pvm->GetCharIOAddr(), tmpaddr = 0x0000; unsigned int rombegin = pvm->GetROMBegin(), romend = pvm->GetROMEnd(), delay = ANIM_DELAY; int nsteps = 0; @@ -325,11 +461,11 @@ int main(int argc, char** argv) { if (anim) pvm->ClearScreen(); int stct = 1; if (execaddr) { - preg = ((step) ? pvm->Step(newaddr) : pvm->Run(newaddr)); + preg = ((step) ? RunSingleInstr(newaddr) : pvm->Run(newaddr)); RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay); execaddr = false; } else { - preg = ((step) ? pvm->Step() : pvm->Run()); + preg = ((step) ? RunSingleCurrInstr() : pvm->Run()); RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts,anim,delay); } if (step) @@ -338,7 +474,13 @@ int main(int argc, char** argv) { runvm = step = false; newaddr = 0x10000; } else if (execvm) { - preg = (execaddr ? pvm->Exec(newaddr) : pvm->Exec()); + if (reset) { + pvm->Reset(); + preg = pvm->GetRegs(); + reset = false; + } else { + preg = (execaddr ? pvm->Exec(newaddr) : pvm->Exec()); + } execvm = false; execaddr = false; brk = preg->SoftIrq; @@ -368,6 +510,24 @@ int main(int argc, char** argv) { char c = tolower(cmd.c_str()[0]); if (c == 'h') { // display help ShowHelp(); + } else if (c == 'p') { // Interrupt ReQuest + pvm->Interrupt(); + cout << "OK" << endl; + } else if (c == 'y') { // save snapshot of current CPU and memory in binary image + string name; + cout << "Enter file name: "; + cin >> name; + cout << " [" << name << "]" << endl; + if (0 == pvm->SaveSnapshot(name)) { + cout << "OK" << endl; + } else { + cout << "ERROR!" << endl; + cout << "errno=" << errno << endl; + } + } else if (c == '0') { // reset CPU + reset = true; + execvm = true; + runvm = false; } else if (c == 'o') { queue exechist(pvm->GetExecHistory()); cout << "PC : INSTR ACC | X | Y | PS | SP" << endl; @@ -378,8 +538,8 @@ int main(int argc, char** argv) { } } else if (c == 'l') { // load memory image char typ = 0; - while (tolower(typ) != 'b' && tolower(typ) != 'd') { - cout << "Type (B - binary/D - definition): "; + while (tolower(typ) != 'b' && tolower(typ) != 'd' && tolower(typ) != 'h') { + cout << "Type (B - binary/H - Intel HEX/D - definition): "; cin >> typ; } cout << " [" << ((tolower(typ) == 'b') ? "binary" : "definition") << "]" << endl; @@ -387,7 +547,8 @@ int main(int argc, char** argv) { cout << "Memory Image File Name: "; cin >> name; cout << " [" << name << "]" << endl; - if (typ == 'b') pvm->LoadRAMBin(name); + if (typ == 'b') PrintLoadBinImgErr (pvm->LoadRAMBin(name)); + else if (typ == 'h') PrintLoadHexImgErr (pvm->LoadRAMHex(name)); else { pvm->LoadRAM(name); if (pvm->IsAutoExec()) execvm = true; @@ -542,8 +703,63 @@ int main(int argc, char** argv) { /* *-------------------------------------------------------------------- - * Method: ShowHel2p() - * Purpose: Display commands help. + * Method: CopyrightBanner() + * Purpose: Display copyright information. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void CopyrightBanner() +{ + cout << "Virtual Machine/CPU Emulator (MOS 6502) and Debugger." << endl; + cout << "Copyright (C) by Marek Karcz 2016. All rights reserved." << endl; +} + +/* + *-------------------------------------------------------------------- + * Method: CmdArgHelp() + * Purpose: Display command line arguments help/Usage. + * Arguments: prgname - string, program name + * Returns: n/a + *-------------------------------------------------------------------- + */ +void CmdArgHelp(string prgname) +{ + CopyrightBanner(); + + cout << endl << endl; + cout << "Usage:" << endl << endl; + cout << "\t" << prgname; + cout << " [-h] | [ramdeffile] | [-b ramimage] | [-r ramimage]" << endl; + cout << "\tOR" << endl; + cout << "\t" << prgname << " [-x intelheximage]"; + cout << endl << endl; + cout << "Where:" << endl << endl; + cout << "\tramdeffile - RAM definition file name" << endl; + cout << "\tintelheximage - Intel HEX format file" << endl; + cout << "\tramimage - RAM binary image file name" << endl; + cout << R"( + +When ran with no arguments, program will load default memory +definition files: default.rom, default.ram and will enter the debug +console menu. +When ramdeffile argument is provided, program will load the memory +definition from the file, set the flags and parameters depending on the +contents of the memory definition file and enter the corresponding mode +of operation as defined in that file. +If used with flag -b or -x, program will load memory from the provided image +file and enter the debug console menu. +If used with flag -r, program will load memory from the provided image +file and execute CPU reset sequence. + +)"; + cout << endl; +} + +/* + *-------------------------------------------------------------------- + * Method: ShowHelp() + * Purpose: Display Debugger Console Command Reference help. * Arguments: n/a * Returns: n/a *-------------------------------------------------------------------- @@ -579,6 +795,8 @@ N - go number of steps Where: steps - number of steps in decimal format Execute number of opcodes provided in steps argument starting from current address. +P - IRQ + Send maskable interrupt request to CPU (set the IRQ line LOW). W - write to memory Usage: W [address] [hexval] [hexval] ... 100 Where: address - memory addr. in hexadecimal format [0000.FFFF], @@ -593,6 +811,10 @@ I - toggle char I/O emulation are interpreted as console character input. R - show registers Displays CPU registers, flags and stack. +Y - snapshot + Usage: Y [file_name] + Where: file_name - the name of the output file. + Save snapshot of current CPU and memory in a binary file. T - show I/O console Displays/prints the contents of the virtual console screen. Note that in run mode (commands X, G or C), virtual screen is @@ -623,13 +845,13 @@ K - toggle ROM emulation L - load memory image Usage: L [image_type] [image_name] Where: - image_type - B (binary) OR D (definition), + image_type - B (binary), H (Intel HEX) OR D (definition), image_name - name of the image file. This function allows to load new memory image from either binary - image file or the ASCII definition file. The binary image is always - loaded from address 0x0000 and can be up to 64kB long. The definition - file format is a plain text file that can contain following keywords - and data: + image file, Intel HEX format file or the ASCII definition file. + The binary image is always loaded from address 0x0000 and can be up to + 64kB long. The definition file format is a plain text file that can + contain following keywords and data: ADDR This keyword defines the run address of the executable code. It is optional, but if exists, it must be the 1-st keyword @@ -673,6 +895,9 @@ L - load memory image executed after the memory image is loaded. The next line that follows this keyword sets the address in decimal or hexadecimal format. + + RESET Enables auto-reset of the CPU. After loading the memory + definition file, the CPU reset sequence will be initiated. O - display op-codes history Show the history of last executed op-codes/instructions, full with @@ -682,6 +907,16 @@ D - diassemble code in memory Where: startaddr,endaddr - hexadecimal address [0000..FFFF]. Attempt to disassemble code in specified address range and display the results (print) on the screen in symbolic form. +0 - reset + Run the processor initialization sequence, just like the real CPU + when its RTS signal is set to LOW and HIGH again. CPU will disable + interrupts, copy address from vector $FFFC to processors PC and will + start executing code. Programmer must put initialization routine + under address pointed by $FFFC vector, which will set the arithmetic + mode, initialize stack, I/O devices and enable IRQ if needed before + jumping to main loop. The reset routine disables trapping last RTS + opcode if stack is empty, so the VM will never return from opcodes + execution loop unless user interrupts with CTRL-C or CTRL-Break. NOTE: 1. If no arguments provided, each command will prompt user to enter @@ -690,7 +925,7 @@ NOTE: by pressing CTRL-C or CTRL-Pause/Break, which will generate a "Operator Interrupt". However in the character input mode use CTRL-Y combination or CTRL-Break (DOS), CTRL-C (Linux). - You may need to press ENTER after that in input mode (DOS) + You may need to press ENTER after that in input mode (DOS). )"; cout << endl; -} \ No newline at end of file +} diff --git a/makefile b/makefile index d8d1adb..3f37b87 100644 --- a/makefile +++ b/makefile @@ -9,7 +9,7 @@ LIBS = -static-libgcc -m32 -g3 -ltermcap CLIBS = -static-libgcc -m32 -g3 INCS = CXXINCS = -CXXFLAGS = $(CXXINCS) -m32 -std=c++0x -Wall -pedantic -g3 +CXXFLAGS = $(CXXINCS) -m32 -std=c++0x -Wall -pedantic -g3 -fpermissive #CFLAGS = $(INCS) -m32 -std=c++0x -Wall -pedantic -g3 CFLAGS = $(INCS) -m32 -Wall -pedantic -g3 RM = rm -f diff --git a/microchess.dat b/microchess.dat index 3b788d7..4a6ec11 100644 --- a/microchess.dat +++ b/microchess.dat @@ -1,5 +1,5 @@ ; Created with BIN2HEX (C) Marek Karcz 2016. All rights reserved. -; 03/13/16 00:08:59 +; Mon Mar 14 00:17:15 2016 ADDR $0400 ORG diff --git a/test_char_io_01.dat b/test_char_io_01.dat index 392c34c..985ae95 100644 --- a/test_char_io_01.dat +++ b/test_char_io_01.dat @@ -13,6 +13,8 @@ $F0 $06 $8D $00 $E0 $E8 $D0 $F5 $00 $00 $EA $4C $00 $02 $45 $6E $74 $65 $72 $20 $74 $65 $78 $74 $3A $00 $00 $00 $00 $00 $00 $00 +ORG +$FFFC +$00 $02 ENIO -EXEC -$0200 \ No newline at end of file +RESET \ No newline at end of file diff --git a/tinybasic.asm b/tinybasic.asm index 5f9c5e8..280e407 100644 --- a/tinybasic.asm +++ b/tinybasic.asm @@ -54,20 +54,27 @@ ; ; 2/15/2016 ; Ported to my own 6502 emulator. +; +; 3/30/2016 +; +; Changed char input address from blocking to non-blocking. ; ; ;-------------------------------------------------------------------------------------- -;.segment "BASIC" +.segment "BEGN" + + .ORG $0000 + +.segment "BASIC" ; ; Tiny Basic starts here ; - .org $0400 ; Start of Basic. First 1K of ROM is shaded by I/O on the OMS-02 + .ORG $4400 ; Start of Basic. First 1K of ROM is shaded by I/O on the OMS-02 SOBAS: -;CLRSC = ClearScreen C_00BC = $00BC ; These are needed because my assembler C_20 = $20 ; does not hanle standard 6502 notation C_22 = $22 ; properly @@ -86,8 +93,8 @@ SR_V_H = SOBAS + $1F ; Base address for subroutine vector hi byte CV: jmp COLD_S ; Cold start vector WV: jmp WARM_S ; Warm start vector -IN_V: jmp RCCHR ; Input routine address. -OUT_V: jmp SNDCHR ; Output routine address. +IN_V: jmp GetCh ; Input routine address. +OUT_V: jmp PutCh ; Output routine address. BREAK: nop ; Begin dummy break routine clc ; Clear the carry flag rts ; End break routine @@ -247,7 +254,7 @@ LBL003: .byte >ILTBL ;$1B ; $1B - hi byte of IL address COLD_S: lda #$00 ; Load accumulator with lo byte of lower and upper prg memory limits sta $20 ; Store in $20 sta $22 ; Store in $22 - lda #$1C ; Load accumulator with hi byte of lower and upper prg memory limits + lda #$50 ; Load accumulator with hi byte of lower and upper prg memory limits sta $21 ; Store in $21 sta $23 ; Store in $23 ; NOTE: $22,$23 vector will be updated by routine below to be the upper RAM limit for TB. @@ -1288,9 +1295,9 @@ ILTBL: .byte $24, $3A, $91, $27, $10, $E1, $59, $C5, $2A, $56, $10, $11, $2C, ; End of Tiny Basic -;.segment "MAIN" +.segment "MAIN" - .org $0CF0 ; Address of main program + .org $4CF0 ; Address of main program ; Code needs work below here, BIOS must be changed for MKHBC-8-R1 @@ -1316,57 +1323,45 @@ ILTBL: .byte $24, $3A, $91, $27, $10, $E1, $59, $C5, $2A, $56, $10, $11, $2C, ; sta ACIACN ; 2400, 8 bits, 1 stop bit, external Rx clock ;-------------------- -main: - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop +main: cli + cld jsr CLRSC ; Go clear the screen ldy #$00 ; Offset for welcome message and prompt jsr SNDMSG ; Go print it -ST_LP: JSR RCCHR ; Go get a character from the console +ST_LP: JSR GetCh ; Go get a character from the console cmp #$43 ; Check for 'C' BNE IS_WRM ; If not branch to next check jmp COLD_S ; Otherwise cold-start Tiny Basic IS_WRM: cmp #$57 ; Check for 'W' BNE PRMPT ; If not, branch to re-prompt them jmp WARM_S ; Otherwise warm-start Tiny Basic -PRMPT: ldx #$2F ; Offset of prompt +PRMPT: ldy #$00 ; Offset of prompt jsr SNDMSG ; Go print the prompt jmp ST_LP ; Go get the response -;.segment "MESG" +.segment "MESG" - .org $0E00 ; Address of message area + .org $4E00 ; Address of message area MBLK: ; -; The message block begins at $1E00 and is at most 256 bytes long. +; The message block begins at $4E00 and is at most 256 bytes long. ; Messages terminate with an FF. ; - .byte "OMEGA MICRO SYSTEMS" + .byte "MKHBC-8-R2 TINY BASIC 6502 PORT" .byte $0D, $0A ;, $0A - .byte "MKHBC-8-R1 TINY BASIC 6502 PORT" + .byte "Adapted from OMEGA MICRO SYSTEMS." .byte $0D, $0A ;, $0A - .byte "Version: 1.0.2, 1/11/2012" + .byte "Version: 1.0.3, 3/31/2016" .byte $0D, $0A ;, $0A - .byte "(NOTE: Use caps for Basic)" + .byte "(NOTE: Use upper case letters.)" .byte $0D, $0A ;, $0A .byte "Boot ([C]old/[W]arm)? " .byte $07, $FF -;.segment "SUBR" +.segment "SUBR" - .org $0F00 ;address of subroutine area + .org $4F00 ;address of subroutine area SBLK: ; @@ -1377,14 +1372,6 @@ SBLK: StrPtr = $E0 -; Kernel jump table - -GetCh = $FFED -PutCh = $FFF0 -Gets = $FFF3 -Puts = $FFF6 - - ;SNDCHR = PutCh ;RCCHR = GetCh @@ -1426,9 +1413,9 @@ TwoBytePeek: CLRSC: ldx #$19 ; Load X - we're going tp print 25 lines lda #$0D ; CR - jsr SNDCHR ; Send a carriage retuen + jsr PutCh ; Send a carriage retuen lda #$0A ; LF -CSLP: jsr SNDCHR ; Send the line feed +CSLP: jsr PutCh ; Send the line feed dex ; One less to do bne CSLP ; Go send another untill we're done rts ; Return @@ -1440,57 +1427,73 @@ CSLP: jsr SNDCHR ; Send the line feed SNDMSG: lda MBLK,y ; Get a character from teh message block cmp #$FF ; Look for end of message marker beq EXSM ; Finish up if it is - jsr SNDCHR ; Otherwise send the character + jsr PutCh ; Otherwise send the character iny ; Increment the pointer jmp SNDMSG ; Go get next character EXSM: rts ; Return ; -; Get a character from the ACIA +; Get a character from the character input device ; Runs into SNDCHR to provide echo ; -RCCHR: ;lda ACIAST ; Read the ACAI status to (for OMS-02) - ;and #$08 ; Check if there is character in the receiver (for OMS-02) - ;beq RCCHR ; Loop util we get one (for OMS-02) - ;lda ACIARW ; Load it into the accumulator (for OMS-02) - LDA $E000 ; Check if a character typed (for emulator) - BEQ RCCHR ; Loop until we get one (for emulator) - -;RCCHR: jsr GetCh -; jsr SNDCHR ; echo character to the console -; rts - +RCCHR: lda $E001 ; Check if a character typed (for emulator, non-blocking) + beq RCCHR ; Loop until we get one (for emulator) + cmp #'a' ; < 'a'? + bcc SNDCHR ; yes, done + cmp #'{' ; >= '{'? + bcs SNDCHR ; yes, done + and #$5f ; convert to upper case ; -;Send a character to the ACIA +; Send a character to the character output device ; SNDCHR: sta $FE ; Save the character to be printed cmp #$FF ; Check for a bunch of characters - BEQ EXSC ; that we don't want to print to + beq EXSC ; that we don't want to print to cmp #$00 ; the terminal and discard them to - BEQ EXSC ; clean up the output + beq EXSC ; clean up the output cmp #$91 ; - BEQ EXSC ; + beq EXSC ; cmp #$93 ; - BEQ EXSC ; + beq EXSC ; cmp #$80 ; - BEQ EXSC ; - jmp SCLP - jsr PutCh ; MKHBC-8-R1 - lda $fe - rts -SCLP: ;lda ACIAST ; Check ACIA Status (don't need for emulator) - ;and #$10 ; See if transmiter it busy (don't need for emulator) - ;beq SCLP ; Wait for it (don't need for emulator) - lda $FE ; Restore the character - ;sta ACIARW ; Transmit it (for OMS-02) - STA $E000 ; Transmit it (for emulator) + beq EXSC ; +SCLP: lda $FE ; Restore the character + sta $E000 ; Transmit it (for emulator) EXSC: rts ; Return -; .org $1FFC ; Address of reset vector (for 6507 not required for emulator) -;RSVEC -; .byte $F0, $1C ; Reset vector +; Kernel jump table + +;GetCh = $FFED +;PutCh = $FFF0 +;Gets = $FFF3 +;Puts = $FFF6 + +; String I/O not used at this time. + +STRIN: rts +STROUT: rts + +; Vector jumps handlers + +NmiHandle: rti +RstHandle: jmp main +IrqHandle: rti + +.segment "KERN" + + .ORG $FFED + +GetCh: jmp RCCHR +PutCh: jmp SNDCHR +Gets: jmp STRIN +Puts: jmp STROUT + +.segment "VECT" + + .ORG $FFFA + +.byte NmiHandle, RstHandle, IrqHandle + -; .org $3000 ; Address of last byte of EPROM -;EOROM: ;--------------------------- END ---------------------------------------------------------------------- diff --git a/tinybasic.cfg b/tinybasic.cfg new file mode 100644 index 0000000..5382a1a --- /dev/null +++ b/tinybasic.cfg @@ -0,0 +1,40 @@ +######################################################## +# +# File: tinybasic.cfg +# +# Purpose: cc65 configuration file for Tiny Basic +# interpreter. +# +# Author: Marek Karcz +# +# Date created: January 2012 +# +# Revision history: +# +# 2/13/2012 +# Relocated from $0400 to $4400 +# +# 3/31/2016 +# Adapted for MKBASIC (VM65) emulator. +# + +MEMORY { + RAM0: start = $0000, size = $4400, fill = yes; + RAM1: start = $4400, size = $08f0, fill = yes; + RAM2: start = $4cf0, size = $0110, fill = yes; + RAM3: start = $4e00, size = $0100, fill = yes; + RAM4: start = $4f00, size = $b0ed, fill = yes; + KERN: start = $ffed, size = $0d, fill = yes; + VECT: start = $fffa, size = 6; +} + +SEGMENTS { + BEGN: load = RAM0, type = rw; + BASIC: load = RAM1, type = rw; + MAIN: load = RAM2, type = rw; + MESG: load = RAM3, type = rw; + SUBR: load = RAM4, type = rw; + KERN: load = KERN, type = ro; + VECT: load = VECT, type = ro; +} +