diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96374c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/BCDCodes.dev b/BCDCodes.dev new file mode 100644 index 0000000..8a12981 --- /dev/null +++ b/BCDCodes.dev @@ -0,0 +1,62 @@ +[Project] +FileName=BCDCodes.dev +Name=BCDCodes +Type=1 +Ver=2 +ObjFiles= +Includes= +Libs= +PrivateResource= +ResourceIncludes= +MakeIncludes= +Compiler= +CppCompiler= +Linker= +IsCpp=0 +Icon= +ExeOutput= +ObjectOutput= +LogOutput= +LogOutputEnabled=0 +OverrideOutput=0 +OverrideOutputName= +HostApplication= +UseCustomMakefile=0 +CustomMakefile= +CommandLine= +Folders= +IncludeVersionInfo=0 +SupportXPThemes=0 +CompilerSet=3 +CompilerSettings=0000000100000000000000000 +UnitCount=1 + +[VersionInfo] +Major=1 +Minor=0 +Release=0 +Build=0 +LanguageID=1033 +CharsetID=1252 +CompanyName= +FileVersion= +FileDescription=Developed using the Dev-C++ IDE +InternalName= +LegalCopyright= +LegalTrademarks= +OriginalFilename= +ProductName= +ProductVersion= +AutoIncBuildNr=0 +SyncProduct=1 + +[Unit1] +FileName=bcd.c +CompileCpp=0 +Folder= +Compile=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + diff --git a/Display.cpp b/Display.cpp new file mode 100644 index 0000000..1c36c90 --- /dev/null +++ b/Display.cpp @@ -0,0 +1,245 @@ +#include "Display.h" +#include +#include +#include +#include + +using namespace std; + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ + +namespace MKBasic { + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +Display::Display() +{ + InitScr(); +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +Display::~Display() +{ +} + +/* + *-------------------------------------------------------------------- + * Method: InitScr() + * Purpose: Initialize screen. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void Display::InitScr() +{ + ClrScr(); +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void Display::ScrollUp() +{ + for (int row=0; row= SCREENDIM_ROW) { + ScrollUp(); + mCursorCoord.row = SCREENDIM_ROW-1; + } + } else if (c == SCREENSPECCHARS_CR) { + mCursorCoord.col = 0; + } else if (c == SCREENSPECCHARS_TB) { + mCursorCoord.col += TABSIZE; + if (mCursorCoord.col >= SCREENDIM_COL) { + mCursorCoord.col = SCREENDIM_COL-1; // must work on it some more + } + } else if (c == SCREENSPECCHARS_BS) { + if (mCursorCoord.col > 0) mCursorCoord.col--; + } else if (c == SCREENSPECCHARS_BE) { + // no action + } + else { + mScreen[mCursorCoord.col][mCursorCoord.row] = c; + mCursorCoord.col++; + if (mCursorCoord.col >= SCREENDIM_COL) { + mCursorCoord.col = 0; + mCursorCoord.row++; + if (mCursorCoord.row >= SCREENDIM_ROW) { + ScrollUp(); + mCursorCoord.row = SCREENDIM_ROW-1; + } + } + } + } +} + +/* + *-------------------------------------------------------------------- + * Method: ClrScr() + * Purpose: Fill the screen with spaces. Set cursor in left-upper + * corner. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void Display::ClrScr() +{ + for (int col=0; colPoke8bit(0xFFFE,0xF0); + mpMem->Poke8bit(0xFFFF,0xFF); + // Put RTI opcode at BRK address. + mpMem->Poke8bit(0xFFF0, OPCODE_RTI); +} + +/* + *-------------------------------------------------------------------- + * 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); +} + +/* + *-------------------------------------------------------------------- + * 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) +{ + unsigned short newpc = mReg.PtrAddr; // PtrAddr must be at the next + // opcode at this point + 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 shamelessly ripped from Frodo emulator code. + Well, maybe not totally shamelessly, I put up a fight to roll my own. + I gave up after one day of trying to make it right based only on MOS documentation. + For my defense I have this - Frodo also does not work 100% as real metal MOS 6502). + And so doesn't Kowalski's emulator. + E.g.: + Real CPU - Rockwell 6502 AP in BCD mode: + 80 + f0 and C=0 gives d0 and N=1 V=1 Z=0 C=1 (F9) + Kowalski's 6502 emulator gives: d0 and N=0, V=0, Z=0, C=1 + My emulator gives: d0 and N=0, V=1, Z=0, C=1 + */ + 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) & FLAGS_SIGN) == FLAGS_SIGN), FLAGS_SIGN); + 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); + } else { // binary mode + + SetFlag((utmp16 > 0xff), FLAGS_CARRY); + SetFlag((!((mReg.Acc ^ mem8) & 0x80) && ((mReg.Acc ^ utmp16) & 0x80)), FLAGS_OVERFLOW); + SetFlag((utmp16 == 0), FLAGS_ZERO); + SetFlag(((utmp16 & 0xFF) & FLAGS_SIGN), FLAGS_SIGN); + mReg.Acc = utmp16 & 0xFF; + } + 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 shamelessly ripped from Frodo emulator code. + See my comments in AddWithCarry() method. + This method returned the same results when testing BCD mode as Rockwell 6502 AP CPU. + Kowalski's emulator returned different results. + My method also passes BCD mode behavior test by Bruce Clark (TestBCD.65s). + */ + 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); + SetFlag(((utmp16 & 0xFF) & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); + mReg.Acc = (ah << 4) | (al & 0x0f); + + } else { // binary mode + + SetFlag((utmp16 < 0x100), FLAGS_CARRY); + SetFlag(((mReg.Acc ^ utmp16) & 0x80) && ((mReg.Acc ^ mem8) & 0x80), FLAGS_OVERFLOW); + SetFlag((utmp16 == 0), FLAGS_ZERO); + SetFlag(((utmp16 & 0xFF) & FLAGS_SIGN) == FLAGS_SIGN, FLAGS_SIGN); + mReg.Acc = utmp16 & 0xFF; + + } + 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; + + switch (mode) { + + case ADDRMODE_IMM: + arg16 = mReg.PtrAddr++; + break; + + case ADDRMODE_ABS: + arg16 = GetArg16(0); + break; + + case ADDRMODE_ZP: + arg16 = (unsigned short) mpMem->Peek8bit(mReg.PtrAddr++); + break; + + case ADDRMODE_IMP: + // implied mode is an internal CPU register + break; + + case ADDRMODE_IND: + arg16 = mpMem->Peek16bit(mReg.PtrAddr++); + arg16 = mpMem->Peek16bit(arg16); + break; + + case ADDRMODE_ABX: + arg16 = GetArg16(mReg.IndX); + break; + + case ADDRMODE_ABY: + arg16 = GetArg16(mReg.IndY); + break; + + case ADDRMODE_ZPX: + arg16 = (mpMem->Peek8bit(mReg.PtrAddr++) + mReg.IndX) & 0xFF; + break; + + case ADDRMODE_ZPY: + arg16 = (mpMem->Peek8bit(mReg.PtrAddr++) + mReg.IndY) & 0xFF; + break; + + case ADDRMODE_IZX: + arg16 = (mpMem->Peek8bit(mReg.PtrAddr++) + mReg.IndX) & 0xFF; + arg16 = mpMem->Peek16bit(arg16); + break; + + case ADDRMODE_IZY: + arg16 = mpMem->Peek8bit(mReg.PtrAddr++); + arg16 = mpMem->Peek16bit(arg16) + mReg.IndY; + break; + + case ADDRMODE_REL: + arg16 = ComputeRelJump(mpMem->Peek8bit(mReg.PtrAddr++)); + break; + + case ADDRMODE_ACC: + // acc mode is an internal CPU register + break; + + default: + throw MKGenException("ERROR: Wrong addressing mode!"); + break; + } + + return arg16; +} + +/* + *-------------------------------------------------------------------- + * Method: ExecOpcode() + * Purpose: Execute VM's opcode. + * Arguments: memaddr - address of code in virtual memory. + * Returns: Pointer to CPU registers and flags structure. + *-------------------------------------------------------------------- + */ +Regs *MKCpu::ExecOpcode(unsigned short memaddr) +{ + mReg.PtrAddr = memaddr; + unsigned char opcode = mpMem->Peek8bit(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; + + switch (opcode) { + + case OPCODE_BRK: // software interrupt, Implied ($00 : BRK) + 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. + break; + + case OPCODE_NOP: // NO oPeration, Implied ($EA : NOP) + 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.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.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.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.IndX = mReg.Acc; + SetFlags(mReg.IndX); + break; + + case OPCODE_TAY: // Transfer A to Y, Implied ($A8 : TAY) + mReg.IndY = mReg.Acc; + SetFlags(mReg.IndY); + break; + + case OPCODE_TXA: // Transfer X to A, Implied ($8A : TXA) + mReg.Acc = mReg.IndX; + SetFlags(mReg.Acc); + break; + + case OPCODE_TYA: // Transfer Y to A, Implied ($98 : TYA) + mReg.Acc = mReg.IndY; + SetFlags(mReg.Acc); + break; + + case OPCODE_TSX: // Transfer Stack ptr to X, Implied ($BA : TSX) + mReg.IndX = mReg.PtrStack; + SetFlags(mReg.IndX); + break; + + case OPCODE_TXS: // Transfer X to Stack ptr, Implied ($9A : TXS) + 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.IndX++; + SetFlags(mReg.IndX); + break; + + case OPCODE_DEX: // DEcrement X, Implied ($CA : DEX) + mReg.IndX--; + SetFlags(mReg.IndX); + break; + + case OPCODE_INY: // INcrement Y, Implied ($C8 : INY) + mReg.IndY++; + SetFlags(mReg.IndY); + break; + + case OPCODE_DEY: // DEcrement Y, Implied ($88 : DEY) + 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 + LogicOpAcc(GetAddrWithMode(ADDRMODE_IMM), 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.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 + LogicOpAcc(GetAddrWithMode(ADDRMODE_IMM), 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); + arg8 &= mReg.Acc; + SetFlags(arg8); + mReg.Flags |= (arg8 & FLAGS_OVERFLOW); + 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); + arg8 &= mReg.Acc; + SetFlags(arg8); + mReg.Flags |= (arg8 & FLAGS_OVERFLOW); + 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.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) + arg16 = 0x100; + arg16 += mReg.PtrStack--; + arg8 = mReg.Flags | FLAGS_BRK; + mpMem->Poke8bit(arg16, arg8); + break; + + case OPCODE_PHA: // PusH Accumulator, Implied ($48 : PHA) + arg16 = 0x100; + arg16 += mReg.PtrStack--; + mpMem->Poke8bit(arg16, mReg.Acc); + break; + + case OPCODE_PLP: // PuLl Processor status, Implied ($28 : PLP) + arg16 = 0x100; + arg16 += ++mReg.PtrStack; + mReg.Flags = mpMem->Peek8bit(arg16); + break; + + case OPCODE_PLA: // PuLl Accumulator, Implied ($68 : PLA) + arg16 = 0x100; + arg16 += ++mReg.PtrStack; + mReg.Acc = mpMem->Peek8bit(arg16); + SetFlags(mReg.Acc); + break; + + case OPCODE_CLC: // CLear Carry, Implied ($18 : CLC) + SetFlag(false, FLAGS_CARRY); + break; + + case OPCODE_SEC: // SEt Carry, Implied ($38 : SEC) + SetFlag(true, FLAGS_CARRY); + break; + + case OPCODE_CLI: // CLear Interrupt, Implied ($58 : CLI) + SetFlag(false, FLAGS_IRQ); + break; + + case OPCODE_CLV: // CLear oVerflow, Implied ($B8 : CLV) + SetFlag(false, FLAGS_OVERFLOW); + break; + + case OPCODE_CLD: // CLear Decimal, Implied ($D8 : CLD) + SetFlag(false, FLAGS_DEC); + break; + + case OPCODE_SED: // SEt Decimal, Implied ($F8 : SED) + SetFlag(true, FLAGS_DEC); + break; + + case OPCODE_SEI: // SEt Interrupt, Implied ($78 : SEI) + 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) + arg16 = 0x100; + arg16 += ++mReg.PtrStack; + mReg.Flags = mpMem->Peek8bit(arg16); + arg16++; mReg.PtrStack++; + mReg.PtrAddr = mpMem->Peek8bit(arg16); + arg16++; mReg.PtrStack++; + mReg.PtrAddr += (mpMem->Peek8bit(arg16) * 0x100); + mReg.SoftIrq = CheckFlag(FLAGS_BRK); + break; + + case OPCODE_RTS: // ReTurn from Subroutine, Implied ($60 : RTS) + 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 + LogicOpAcc(GetAddrWithMode(ADDRMODE_IMM), 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.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 + AddWithCarry(mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM))); + 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.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 + 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 + 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 + 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 + SubWithCarry(mpMem->Peek8bit(GetAddrWithMode(ADDRMODE_IMM))); + break; + + default: + break; + } + + 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; +} + +} // namespace MKBasic diff --git a/MKCpu.h b/MKCpu.h new file mode 100644 index 0000000..ddcdf94 --- /dev/null +++ b/MKCpu.h @@ -0,0 +1,424 @@ +#ifndef MKCPU_H +#define MKCPU_H + +#include "Memory.h" + +namespace MKBasic { + +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 - 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 + bool LastRTS; // true if RTS encountered and stack empty. +}; + +/* + * 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 +}; +// 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 +}; + +/* + *------------------------------------------------------------------------------ + 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(); + + protected: + + private: + + struct Regs mReg; // CPU registers + Memory *mpMem; // pointer to memory object + bool mLocalMem; // true - memory locally allocated + + 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 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 +}; + +} // namespace MKBasic + +#endif diff --git a/MKGenException.cpp b/MKGenException.cpp new file mode 100644 index 0000000..2ab9ac3 --- /dev/null +++ b/MKGenException.cpp @@ -0,0 +1,20 @@ +#include "MKGenException.h" + +namespace MKBasic { + +MKGenException::MKGenException() +{ + msCause = "Ouch!"; +} + +MKGenException::MKGenException(string cause) +{ + msCause = cause; +} + +string MKGenException::GetCause() +{ + return msCause; +} + +} // namespace MKBasic diff --git a/MKGenException.h b/MKGenException.h new file mode 100644 index 0000000..ce10000 --- /dev/null +++ b/MKGenException.h @@ -0,0 +1,23 @@ +#ifndef MKGENEXCEPTION_H +#define MKGENEXCEPTION_H + +#include +#include + +using namespace std; + +namespace MKBasic { + +class MKGenException : public exception { + public: + MKGenException(); + MKGenException(string cause); + string GetCause(); + + private: + string msCause; +}; + +} // namespace MKBasic + +#endif diff --git a/Memory.cpp b/Memory.cpp new file mode 100644 index 0000000..b9c56d0 --- /dev/null +++ b/Memory.cpp @@ -0,0 +1,310 @@ +#include "Memory.h" +#include +#include + +//#define DBG 1 +#if defined (DBG) +#include +using namespace std; +#endif + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ + +namespace MKBasic { + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +Memory::Memory() +{ + Initialize(); +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +Memory::~Memory() +{ +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void Memory::Initialize() +{ + unsigned short addr = 0; + for (int i=0; i < 0xFFFF; i++) { + m8bitMem[addr++] = 0; + } + mCharIOAddr = CHARIO_ADDR; + mCharIOActive = false; + mIOEcho = false; + mInBufDataBegin = mInBufDataEnd = 0; + mOutBufDataBegin = mOutBufDataEnd = 0; + mROMBegin = ROM_BEGIN; + mROMEnd = ROM_END; + mROMActive = false; +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void Memory::EnableROM() +{ + mROMActive = true; +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void Memory::EnableROM(unsigned short start, unsigned short end) +{ + if (mROMEnd > mROMBegin) { + mROMBegin = start; + mROMEnd = end; + } + EnableROM(); +} + +/* + *-------------------------------------------------------------------- + * Method: ReadCharKb() + * Purpose: If char I/O active, read character from console + * (non-blocking) and put in an input FIFO buffer. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +unsigned char Memory::ReadCharKb() +{ + unsigned char ret = 0; + if (mCharIOActive) { + int c; + putchar('?'); + while(!kbhit()); + c = getch(); + if (mIOEcho) putchar(c); + mCharIOBufIn[mInBufDataEnd] = c; + mInBufDataEnd++; + if (mInBufDataEnd >= CHARIO_BUF_SIZE) mInBufDataEnd = 0; + ret = c; + } + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: GetCharIn() + * Purpose: Return character from the emulated character I/O FIFO + * input buffer or -1 if FIFO empty or char I/O not active. + * Arguments: n/a + * Returns: character + *-------------------------------------------------------------------- + */ +char Memory::GetCharIn() +{ + char ret = -1; + if (mCharIOActive) { + if (mInBufDataEnd != mInBufDataBegin) { + ret = mCharIOBufIn[mInBufDataBegin]; + mInBufDataBegin++; + if (mInBufDataBegin >= CHARIO_BUF_SIZE) mInBufDataBegin = 0; + } + } + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: GetCharOut() + * Purpose: Return character from the emulated character I/O FIFO + * output buffer or -1 if FIFO empty or char I/O not + * active. + * Arguments: n/a + * Returns: character + *-------------------------------------------------------------------- + */ +char Memory::GetCharOut() +{ + char ret = -1; + if (mCharIOActive) { + if (mOutBufDataEnd != mOutBufDataBegin) { + ret = mCharIOBufOut[mOutBufDataBegin]; + mOutBufDataBegin++; + if (mOutBufDataBegin >= CHARIO_BUF_SIZE) mOutBufDataBegin = 0; + } + } + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: PutCharIO() + * Purpose: Put character in the output char I/O FIFO buffer. + * Arguments: c - character + * Returns: n/a + *-------------------------------------------------------------------- + */ +void Memory::PutCharIO(char c) +{ + if (mCharIOActive) { + mCharIOBufOut[mOutBufDataEnd] = c; + mOutBufDataEnd++; + if (mOutBufDataEnd >= CHARIO_BUF_SIZE) mOutBufDataEnd = 0; + } +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +unsigned char Memory::Peek8bit(unsigned short addr) +{ + if (mCharIOActive && addr == mCharIOAddr) { +#if defined (DBG) + cout << "DBG: Peek8bit($" << hex << addr << ") BEFORE ReadCharKb()" << endl; + cout << "DBG: m8bitMem[$" << hex << addr << "] = $" << hex << (unsigned short)m8bitMem[addr] << endl; + for (unsigned int a = 0xFFF0; a < 0x10000; a++) { + cout << "DBG: m8bitMem[$" << hex << a << "] = $" << hex << (unsigned short)m8bitMem[a] << endl; + } +#endif + m8bitMem[addr] = ReadCharKb(); +#if defined (DBG) + cout << "************************" << endl; + cout << "DBG: Peek8bit($" << hex << addr << ") AFTER ReadCharKb()" << endl; + cout << "DBG: m8bitMem[$" << hex << addr << "] = $" << hex << (unsigned short)m8bitMem[addr] << endl; + for (unsigned int a = 0xFFF0; a < 0x10000; a++) { + cout << "DBG: m8bitMem[$" << hex << a << "] = $" << hex << (unsigned short)m8bitMem[a] << endl; + } +#endif + } + + return m8bitMem[addr]; +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +unsigned short Memory::Peek16bit(unsigned short addr) +{ + unsigned short ret = 0; + + if (mCharIOActive && addr == mCharIOAddr) { +#if defined (DBG) + cout << "DBG: Peek16bit(" << addr << ")" << endl; +#endif + m8bitMem[addr] = ReadCharKb(); + } + + ret = m8bitMem[addr++]; + ret += m8bitMem[addr] * 256; + + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: Poke8bit() + * Purpose: Write byte to specified memory location. + * If the memory location is mapped to character I/O, + * write value to output buffer and memory location. + * If the memory location is protected (ROM), do not + * write the value. + * Arguments: addr - (0x0000..0xffff) memory address, + * val - value (0x00..0xff) + * Returns: n/a + *-------------------------------------------------------------------- + */ +void Memory::Poke8bit(unsigned short addr, unsigned char val) +{ + if (mCharIOActive && addr == mCharIOAddr) + PutCharIO(val); + if (!mROMActive || (addr < ROM_BEGIN || addr > ROM_END)) { + m8bitMem[addr] = val; + } +} + +/* + *-------------------------------------------------------------------- + * Method: SetCharIO() + * Purpose: Activates and sets an address of basic character I/O + * emulation (console). + * Arguments: addr - address of I/O area (0x0000..0xFFFF) + * Returns: n/a + *-------------------------------------------------------------------- + */ +void Memory::SetCharIO(unsigned short addr, bool echo) +{ + mCharIOAddr = addr; + mCharIOActive = true; + mIOEcho = echo; +} + +/* + *-------------------------------------------------------------------- + * Method: DisableCharIO() + * Purpose: Deactivates basic character I/O emulation (console). + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void Memory::DisableCharIO() +{ + mCharIOActive = false; +} + +/* + *-------------------------------------------------------------------- + * Method: GetCharIOAddr() + * Purpose: Returns current address of basic character I/O area. + * Arguments: n/a + * Returns: address of I/O area + *-------------------------------------------------------------------- + */ +unsigned short Memory::GetCharIOAddr() +{ + return mCharIOAddr; +} + +} // namespace MKBasic diff --git a/Memory.h b/Memory.h new file mode 100644 index 0000000..e75533c --- /dev/null +++ b/Memory.h @@ -0,0 +1,55 @@ +#ifndef MEMORY_H +#define MEMORY_H + +#define MAX_8BIT_ADDR 0xFFFF +#define CHARIO_ADDR 0xE000 +#define CHARIO_BUF_SIZE 256 +#define ROM_BEGIN 0xD000 +#define ROM_END 0xDFFF + +namespace MKBasic { + +class Memory +{ + public: + + Memory(); + ~Memory(); + + void Initialize(); + unsigned char Peek8bit(unsigned short addr); + unsigned short Peek16bit(unsigned short addr); + void Poke8bit(unsigned short addr, unsigned char val); + void SetCharIO(unsigned short addr, bool echo); + void DisableCharIO(); + unsigned short GetCharIOAddr(); + char GetCharIn(); + char GetCharOut(); + void EnableROM(); + void EnableROM(unsigned short start, unsigned short end); + + protected: + + private: + + unsigned char m8bitMem[MAX_8BIT_ADDR+1]; + char mCharIOBufIn[CHARIO_BUF_SIZE]; + char mCharIOBufOut[CHARIO_BUF_SIZE]; + unsigned int mInBufDataBegin; + unsigned int mInBufDataEnd; + unsigned int mOutBufDataBegin; + unsigned int mOutBufDataEnd; + unsigned short mCharIOAddr; + bool mCharIOActive; + bool mIOEcho; + unsigned short mROMBegin; + unsigned short mROMEnd; + bool mROMActive; + + unsigned char ReadCharKb(); + void PutCharIO(char c); +}; + +} // namespace MKBasic + +#endif diff --git a/Notes.txt b/Notes.txt new file mode 100644 index 0000000..b2d55fc --- /dev/null +++ b/Notes.txt @@ -0,0 +1,25 @@ + +http://visual6502.org/wiki/index.php?title=6502DecimalMode + + +NV-BDIZC + +Tests for ADC +00 + 00 and C=0 gives 00 and N=0 V=0 Z=1 C=0 (simulate) +79 + 00 and C=1 gives 80 and N=1 V=1 Z=0 C=0 (simulate) +24 + 56 and C=0 gives 80 and N=1 V=1 Z=0 C=0 (simulate) +93 + 82 and C=0 gives 75 and N=0 V=1 Z=0 C=1 (simulate) +89 + 76 and C=0 gives 65 and N=0 V=0 Z=0 C=1 (simulate) +89 + 76 and C=1 gives 66 and N=0 V=0 Z=1 C=1 (simulate) +80 + f0 and C=0 gives d0 and N=0 V=1 Z=0 C=1 (simulate) +80 + fa and C=0 gives e0 and N=1 V=0 Z=0 C=1 (simulate) +2f + 4f and C=0 gives 74 and N=0 V=0 Z=0 C=0 (simulate) +6f + 00 and C=1 gives 76 and N=0 V=0 Z=0 C=0 (simulate) +Tests for SBC +00 - 00 and C=0 gives 99 and N=1 V=0 Z=0 C=0 (simulate) +00 - 00 and C=1 gives 00 and N=0 V=0 Z=1 C=1 (simulate) +00 - 01 and C=1 gives 99 and N=1 V=0 Z=0 C=0 (simulate) +0a - 00 and C=1 gives 0a and N=0 V=0 Z=0 C=1 (simulate) +0b - 00 and C=0 gives 0a and N=0 V=0 Z=0 C=1 (simulate) +9a - 00 and C=1 gives 9a and N=1 V=0 Z=0 C=1 (simulate) +9b - 00 and C=0 gives 9a and N=1 V=0 Z=0 C=1 (simulate) \ No newline at end of file diff --git a/ReadMe.txt b/ReadMe.txt new file mode 100644 index 0000000..13c82ec --- /dev/null +++ b/ReadMe.txt @@ -0,0 +1,62 @@ + +Project: MKBasic +Author: Copyright (C) Marek Karcz 2016. All rights reserved. +Purpose: +MOS 6502 emulator, Virtual CPU/Machine and potentially retro-style 8-bit computer emulator, +MOS-6502-compatible virtual computer featuring BASIC interpreter, machine code monitor etc. + +Memory images extensions: .RAM, .ROM + +Format of the memory definition file: + +; comment +ADDR +address +data +ORG +address + +Where: +ADDR - label indicating that starting address will follow in next + line +ORG - label indicating that the address counter will change to the + value provided in next line +address - decimal or hexadecimal (prefix $) address in memory + +E.g: +ADDR +$200 + +or + +ADDR +512 + +changes the default start address (256) to 512. + +ORG +49152 + +moves address counter to address 49152, following data will be +loaded from that address forward + +data - the multi-line stream of decimal of hexadecimal ($xx) values + of size unsigned char (byte: 0-255) separated with spaces + or commas. + +E.g.: +$00 $00 $00 $00 +$00 $00 $00 $00 + +or + +$00,$00,$00,$00 + +or + +0 0 0 0 + +or + +0,0,0,0 +0 0 0 0 \ No newline at end of file diff --git a/TestBCD.65s b/TestBCD.65s new file mode 100644 index 0000000..b0f3f22 --- /dev/null +++ b/TestBCD.65s @@ -0,0 +1,291 @@ +; Verify decimal mode behavior +; Written by Bruce Clark. This code is public domain. +; +; Returns: +; ERROR = 0 if the test passed +; ERROR = 1 if the test failed +; +; This routine requires 17 bytes of RAM -- 1 byte each for: +; AR, CF, DA, DNVZC, ERROR, HA, HNVZC, N1, N1H, N1L, N2, N2L, NF, VF, and ZF +; and 2 bytes for N2H +; +; Variables: +; N1 and N2 are the two numbers to be added or subtracted +; N1H, N1L, N2H, and N2L are the upper 4 bits and lower 4 bits of N1 and N2 +; DA and DNVZC are the actual accumulator and flag results in decimal mode +; HA and HNVZC are the accumulator and flag results when N1 and N2 are +; added or subtracted using binary arithmetic +; AR, NF, VF, ZF, and CF are the predicted decimal mode accumulator and +; flag results, calculated using binary arithmetic +; +; This program takes approximately 1 minute at 1 MHz (a few seconds more on +; a 65C02 than a 6502 or 65816) +; + *=$0300 + +AR: .DB 0 +CF: .DB 0 +DA: .DB 0 +DNVZC: .DB 0 +ERROR: .DB 0 +HA: .DB 0 +HNVZC: .DB 0 +N1: .DB 0 +N1H: .DB 0 +N1L: .DB 0 +N2: .DB 0 +N2L: .DB 0 +NF: .DB 0 +VF: .DB 0 +ZF: .DB 0 +N2H: .DB 0,0 + + *=$0400 + +TEST: LDY #1 ; initialize Y (used to loop through carry flag values) + STY ERROR ; store 1 in ERROR until the test passes + LDA #0 ; initialize N1 and N2 + STA N1 + STA N2 +LOOP1: LDA N2 ; N2L = N2 & $0F + AND #$0F ; [1] see text + STA N2L + LDA N2 ; N2H = N2 & $F0 + AND #$F0 ; [2] see text + STA N2H + ORA #$0F ; N2H+1 = (N2 & $F0) + $0F + STA N2H+1 +LOOP2: LDA N1 ; N1L = N1 & $0F + AND #$0F ; [3] see text + STA N1L + LDA N1 ; N1H = N1 & $F0 + AND #$F0 ; [4] see text + STA N1H + JSR ADD + JSR A6502 + JSR COMPARE + BNE DONE + JSR SUB + JSR S6502 + JSR COMPARE + BNE DONE + INC N1 ; [5] see text + BNE LOOP2 ; loop through all 256 values of N1 + INC N2 ; [6] see text + BNE LOOP1 ; loop through all 256 values of N2 + DEY + BPL LOOP1 ; loop through both values of the carry flag + LDA #0 ; test passed, so store 0 in ERROR + STA ERROR +DONE: RTS + BRK + +; Calculate the actual decimal mode accumulator and flags, the accumulator +; and flag results when N1 is added to N2 using binary arithmetic, the +; predicted accumulator result, the predicted carry flag, and the predicted +; V flag +; +ADD: SED ; decimal mode + CPY #1 ; set carry if Y = 1, clear carry if Y = 0 + LDA N1 + ADC N2 + STA DA ; actual accumulator result in decimal mode + PHP + PLA + STA DNVZC ; actual flags result in decimal mode + CLD ; binary mode + CPY #1 ; set carry if Y = 1, clear carry if Y = 0 + LDA N1 + ADC N2 + STA HA ; accumulator result of N1+N2 using binary arithmetic + + PHP + PLA + STA HNVZC ; flags result of N1+N2 using binary arithmetic + CPY #1 + LDA N1L + ADC N2L + CMP #$0A + LDX #0 + BCC A1 + INX + ADC #5 ; add 6 (carry is set) + AND #$0F + SEC +A1: ORA N1H +; +; if N1L + N2L < $0A, then add N2 & $F0 +; if N1L + N2L >= $0A, then add (N2 & $F0) + $0F + 1 (carry is set) +; + ADC N2H,X + PHP + BCS A2 + CMP #$A0 + BCC A3 +A2: ADC #$5F ; add $60 (carry is set) + SEC +A3: STA AR ; predicted accumulator result + PHP + PLA + STA CF ; predicted carry result + PLA +; +; note that all 8 bits of the P register are stored in VF +; + STA VF ; predicted V flags + RTS + +; Calculate the actual decimal mode accumulator and flags, and the +; accumulator and flag results when N2 is subtracted from N1 using binary +; arithmetic +; +SUB: SED ; decimal mode + CPY #1 ; set carry if Y = 1, clear carry if Y = 0 + LDA N1 + SBC N2 + STA DA ; actual accumulator result in decimal mode + PHP + PLA + STA DNVZC ; actual flags result in decimal mode + CLD ; binary mode + CPY #1 ; set carry if Y = 1, clear carry if Y = 0 + LDA N1 + SBC N2 + STA HA ; accumulator result of N1-N2 using binary arithmetic + + PHP + PLA + STA HNVZC ; flags result of N1-N2 using binary arithmetic + RTS + +; Calculate the predicted SBC accumulator result for the 6502 and 65816 + +; +SUB1: CPY #1 ; set carry if Y = 1, clear carry if Y = 0 + LDA N1L + SBC N2L + LDX #0 + BCS S11 + INX + SBC #5 ; subtract 6 (carry is clear) + AND #$0F + CLC +S11: ORA N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + SBC N2H,X + BCS S12 + SBC #$5F ; subtract $60 (carry is clear) +S12: STA AR + RTS + +; Calculate the predicted SBC accumulator result for the 6502 and 65C02 + +; +SUB2: CPY #1 ; set carry if Y = 1, clear carry if Y = 0 + LDA N1L + SBC N2L + LDX #0 + BCS S21 + INX + AND #$0F + CLC +S21: ORA N1H +; +; if N1L - N2L >= 0, then subtract N2 & $F0 +; if N1L - N2L < 0, then subtract (N2 & $F0) + $0F + 1 (carry is clear) +; + SBC N2H,X + BCS S22 + SBC #$5F ; subtract $60 (carry is clear) +S22: CPX #0 + BEQ S23 + SBC #6 +S23: STA AR ; predicted accumulator result + RTS + +; Compare accumulator actual results to predicted results +; +; Return: +; Z flag = 1 (BEQ branch) if same +; Z flag = 0 (BNE branch) if different +; +COMPARE: + LDA DA + CMP AR + BNE C1 + LDA DNVZC ; [7] see text + EOR NF + AND #$80 ; mask off N flag + BNE C1 + LDA DNVZC ; [8] see text + EOR VF + AND #$40 ; mask off V flag + BNE C1 ; [9] see text + LDA DNVZC + EOR ZF ; mask off Z flag + AND #2 + BNE C1 ; [10] see text + LDA DNVZC + EOR CF + AND #1 ; mask off C flag +C1: RTS + +; These routines store the predicted values for ADC and SBC for the 6502, +; 65C02, and 65816 in AR, CF, NF, VF, and ZF + +A6502: LDA VF +; +; since all 8 bits of the P register were stored in VF, bit 7 of VF contains +; the N flag for NF +; + STA NF + LDA HNVZC + STA ZF + RTS + +S6502: JSR SUB1 + LDA HNVZC + STA NF + STA VF + STA ZF + STA CF + RTS + +A65C02: LDA AR + PHP + PLA + STA NF + STA ZF + RTS + +S65C02: JSR SUB2 + LDA AR + PHP + PLA + STA NF + STA ZF + LDA HNVZC + STA VF + STA CF + RTS + +A65816: LDA AR + PHP + PLA + STA NF + STA ZF + RTS + +S65816: JSR SUB1 + LDA AR + PHP + PLA + STA NF + STA ZF + LDA HNVZC + STA VF + STA CF + RTS diff --git a/VMachine.cpp b/VMachine.cpp new file mode 100644 index 0000000..73c7f13 --- /dev/null +++ b/VMachine.cpp @@ -0,0 +1,579 @@ +#include +#include +#include +#include +#include "VMachine.h" +#include "MKGenException.h" + +using namespace std; + +namespace MKBasic { + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ + +/* + *-------------------------------------------------------------------- + * Method: VMachine() + * Purpose: Default class constructor. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +VMachine::VMachine() +{ + InitVM(); +} + +/* + *-------------------------------------------------------------------- + * Method: VMachine() + * Purpose: Custom class constructor. + * Arguments: romfname - name of the ROM definition file + * ramfname - name of the RAM definition file + * Returns: n/a + *-------------------------------------------------------------------- + */ +VMachine::VMachine(string romfname, string ramfname) +{ + InitVM(); + LoadROM(romfname); + LoadRAM(ramfname); +} + +/* + *-------------------------------------------------------------------- + * Method: ~VMachine() + * Purpose: Class destructor. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +VMachine::~VMachine() +{ + delete mpDisp; + delete mpCPU; + delete mpROM; + delete mpRAM; +} + +/* + *-------------------------------------------------------------------- + * Method: InitVM() + * Purpose: Initialize class. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::InitVM() +{ + mpRAM = new Memory(); + mRunAddr = 0x200; + mCharIOAddr = CHARIO_ADDR; + mCharIOActive = mCharIO = false; + if (NULL == mpRAM) { + throw MKGenException("Unable to initialize VM (RAM)."); + } + mpROM = new Memory(); + if (NULL == mpROM) { + throw MKGenException("Unable to initialize VM (ROM)."); + } + mpCPU = new MKCpu(mpRAM); + if (NULL == mpCPU) { + throw MKGenException("Unable to initialize VM (CPU)."); + } + mpDisp = new Display(); + if (NULL == mpDisp) { + throw MKGenException("Unable to initialize VM (Display)."); + } +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void VMachine::ClearScreen() +{ + HANDLE hStdOut; + CONSOLE_SCREEN_BUFFER_INFO csbi; + DWORD count; + DWORD cellCount; + COORD homeCoords = { 0, 0 }; + + hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); + if (hStdOut == INVALID_HANDLE_VALUE) return; + + /* Get the number of cells in the current buffer */ + if (!GetConsoleScreenBufferInfo( hStdOut, &csbi )) return; + cellCount = csbi.dwSize.X *csbi.dwSize.Y; + + /* Fill the entire buffer with spaces */ + if (!FillConsoleOutputCharacter( + hStdOut, + (TCHAR) ' ', + cellCount, + homeCoords, + &count + )) return; + + /* Fill the entire buffer with the current colors and attributes */ + if (!FillConsoleOutputAttribute( + hStdOut, + csbi.wAttributes, + cellCount, + homeCoords, + &count + )) return; + + /* Move the cursor home */ + SetConsoleCursorPosition( hStdOut, homeCoords ); +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void VMachine::ScrHome() +{ + HANDLE hStdOut; + COORD homeCoords = { 0, 0 }; + + hStdOut = GetStdHandle( STD_OUTPUT_HANDLE ); + if (hStdOut == INVALID_HANDLE_VALUE) return; + + /* Move the cursor home */ + SetConsoleCursorPosition( hStdOut, homeCoords ); +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void VMachine::ShowDisp() +{ + if (mCharIOActive) { +#if defined (WINDOWS) + //ClearScreen(); + ScrHome(); +#elif defined (LINUX) + system("clear"); +#endif + mpDisp->ShowScr(); + } +} + +/* + *-------------------------------------------------------------------- + * Method: Run() + * Purpose: Run VM until software break instruction. + * Arguments: n/a + * Returns: Pointer to CPU registers and flags. + *-------------------------------------------------------------------- + */ +Regs *VMachine::Run() +{ + Regs *cpureg = NULL; + +#if defined (WINDOWS) + ClearScreen(); +#elif defined (LINUX) + system("clear"); +#endif + ShowDisp(); + while (true) { + cpureg = Step(); + if (mCharIO) { + ShowDisp(); + } + if (cpureg->SoftIrq) + break; + } + + ShowDisp(); + + return cpureg; +} + +/* + *-------------------------------------------------------------------- + * Method: Run() + * Purpose: Run VM from specified address until software break + * instruction. + * Arguments: addr - start execution address + * Returns: Pointer to CPU registers and flags. + *-------------------------------------------------------------------- + */ +Regs *VMachine::Run(unsigned short addr) +{ + mRunAddr = addr; + return Run(); +} + +/* + *-------------------------------------------------------------------- + * Method: Exec() + * Purpose: Run VM from current address until last RTS. + * NOTE: Stack must be empty! + * Arguments: n/a + * Returns: Pointer to CPU registers and flags. + *-------------------------------------------------------------------- + */ +Regs *VMachine::Exec() +{ + Regs *cpureg = NULL; + +#if defined (WINDOWS) + ClearScreen(); +#elif defined (LINUX) + system("clear"); +#endif + ShowDisp(); + while (true) { + cpureg = Step(); + if (mCharIO) { + ShowDisp(); + } + if (cpureg->LastRTS) break; + } + + ShowDisp(); + + return cpureg; +} + +/* + *-------------------------------------------------------------------- + * Method: Exec() + * Purpose: Run VM from specified address until RTS. + * Arguments: addr - start execution address + * Returns: Pointer to CPU registers and flags. + *-------------------------------------------------------------------- + */ +Regs *VMachine::Exec(unsigned short addr) +{ + mRunAddr = addr; + return Exec(); +} + +/* + *-------------------------------------------------------------------- + * Method: Step() + * Purpose: Execute single opcode. + * Arguments: n/a + * Returns: Pointer to CPU registers and flags. + *-------------------------------------------------------------------- + */ +Regs *VMachine::Step() +{ + unsigned short addr = mRunAddr; + Regs *cpureg = NULL; + + cpureg = mpCPU->ExecOpcode(addr); + addr = cpureg->PtrAddr; + mRunAddr = addr; + + if (mCharIOActive) { + char c = -1; + mCharIO = false; + while ((c = mpRAM->GetCharOut()) != -1) { + mpDisp->PutChar(c); + mCharIO = true; + } + } + + return cpureg; +} + +/* + *-------------------------------------------------------------------- + * Method: Step() + * Purpose: Execute single opcode. + * Arguments: addr (unsigned short) - opcode address + * Returns: Pointer to CPU registers and flags. + *-------------------------------------------------------------------- + */ +Regs *VMachine::Step(unsigned short addr) +{ + mRunAddr = addr; + return Step(); +} + +/* + *-------------------------------------------------------------------- + * Method: LoadROM() + * Purpose: Load data from memory definition file to the memory. + * Arguments: romfname - name of the ROM file definition + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::LoadROM(string romfname) +{ + LoadMEM(romfname, mpROM); +} + +/* + *-------------------------------------------------------------------- + * Method: LoadRAM() + * Purpose: Load data from memory definition file to the memory. + * Arguments: ramfname - name of the RAM file definition + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::LoadRAM(string ramfname) +{ + LoadMEM(ramfname, mpRAM); + mpRAM->EnableROM(); +} + +/* + *-------------------------------------------------------------------- + * Method: LoadMEM() + * Purpose: Load data from memory definition file to the memory. + * Arguments: memfname - name of memory definition file + * pmem - pointer to memory object + * Returns: n/a + * Details: + * Format of the memory definition file: + * ; comment + * ADDR + * address + * data + * ORG + * address + * + * Where: + * ADDR - label indicating that starting address will follow in next + * line + * ORG - label indicating that the address counter will change to the + * value provided in next line + * address - decimal or hexadecimal (prefix $) address in memory + * E.g: + * ADDR + * $200 + * + * or + * + * ADDR + * 512 + * + * changes the default start address (256) to 512. + * + * ORG + * 49152 + * + * moves address counter to address 49152, following data will be + * loaded from that address forward + * + * data - the multi-line stream of decimal of hexadecimal ($xx) values + * of size unsigned char (byte: 0-255) separated with spaces + * or commas. + * E.g.: + * $00 $00 $00 $00 + * $00 $00 $00 $00 + * + * or + * + * $00,$00,$00,$00 + * + * or + * + * 0 0 0 0 + * + * or + * + * 0,0,0,0 + * 0 0 0 0 + *-------------------------------------------------------------------- + */ +void VMachine::LoadMEM(string memfname, Memory *pmem) +{ + FILE *fp = NULL; + char line[256] = "\0"; + unsigned short addr = 0x200; + unsigned int nAddr; + Memory *pm = pmem; + + if ((fp = fopen(memfname.c_str(), "r")) != NULL) { + fgets(line, 256, fp); + if (0 == strcmp(line, "ADDR")) { + line[0] = '\0'; + fgets(line, 256, fp); + if (*line == '$') { + sscanf(line+1, "%04x", &nAddr); + addr = nAddr; + } else { + addr = (unsigned short) atoi(line); + } + mRunAddr = addr; + } + while (0 == feof(fp) && 0 == ferror(fp)) + { + line[0] = '\0'; + fgets(line, 256, fp); + if (0 == strncmp(line, "ORG", 3)) { + line[0] = '\0'; + fgets(line, 256, fp); + if (*line == '$') { + sscanf(line+1, "%04x", &nAddr); + addr = nAddr; + } else { + addr = (unsigned short) atoi(line); + } + continue; + } + if (';' == *line) continue; // skip comment lines + char *s = strtok (line, " ,"); + while (NULL != s) { + unsigned int nVal; + if (*s == '$') { + sscanf(s+1, "%02x", &nVal); + pm->Poke8bit(addr++, (unsigned short)nVal); + } else { + pm->Poke8bit(addr++, (unsigned short)atoi(s)); + } + s = strtok(NULL, " ,"); + } + } + } + else { + cout << "WARNING: Unable to open memory definition file: " << memfname << endl; + cout << "Press [ENTER]..."; + getchar(); + //throw MKGenException("Unable to open memory definition file: " + memfname); + } +} + +/* + *-------------------------------------------------------------------- + * Method: MemPeek8bit() + * Purpose: Read value from specified RAM address. + * Arguments: addr - RAM address (0..0xFFFF) + * Returns: unsigned short - value read from specified RAM address + *-------------------------------------------------------------------- + */ +unsigned short VMachine::MemPeek8bit(unsigned short addr) +{ + unsigned short ret = 0; + + ret = (unsigned short)mpRAM->Peek8bit(addr); + + return ret; +} + +/* + *-------------------------------------------------------------------- + * Method: MemPoke8bit() + * Purpose: Write value to specified RAM address. + * Arguments: addr - RAM address (0..0xFFFF) + * v - 8-bit byte value + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::MemPoke8bit(unsigned short addr, unsigned char v) +{ + mpRAM->Poke8bit(addr, v); +} + +/* + *-------------------------------------------------------------------- + * Method: GetRegs() + * Purpose: Return pointer to CPU status register. + * Arguments: n/a + * Returns: pointer to status register + *-------------------------------------------------------------------- + */ +Regs *VMachine::GetRegs() +{ + return mpCPU->GetRegs(); +} + +/* + *-------------------------------------------------------------------- + * Method: SetCharIO() + * Purpose: Activates and sets an address of basic character I/O + * emulation (console). + * Arguments: addr - address of I/O area (0x0000..0xFFFF) + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::SetCharIO(unsigned short addr, bool echo) +{ + mCharIOAddr = addr; + mCharIOActive = true; + mpRAM->SetCharIO(addr, echo); + mpDisp->ClrScr(); +} + +/* + *-------------------------------------------------------------------- + * Method: DisableCharIO() + * Purpose: Deactivates basic character I/O emulation (console). + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::DisableCharIO() +{ + mCharIOActive = false; + mpRAM->DisableCharIO(); +} + +/* + *-------------------------------------------------------------------- + * Method: GetCharIOAddr() + * Purpose: Returns current address of basic character I/O area. + * Arguments: n/a + * Returns: address of I/O area + *-------------------------------------------------------------------- + */ +unsigned short VMachine::GetCharIOAddr() +{ + return mCharIOAddr; +} + +/* + *-------------------------------------------------------------------- + * Method: GetCharIOActive() + * Purpose: Returns status of character I/O emulation. + * Arguments: n/a + * Returns: true if I/O emulation active + *-------------------------------------------------------------------- + */ +bool VMachine::GetCharIOActive() +{ + return mCharIOActive; +} + +/* + *-------------------------------------------------------------------- + * Method: ShowIO() + * Purpose: Show contents of emulated char I/O. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void VMachine::ShowIO() +{ + if (mCharIOActive) + mpDisp->ShowScr(); +} + +} // namespace MKBasic diff --git a/VMachine.h b/VMachine.h new file mode 100644 index 0000000..d18d793 --- /dev/null +++ b/VMachine.h @@ -0,0 +1,65 @@ +#ifndef VMACHINE_H +#define VMACHINE_H + +#include +#include "MKCpu.h" +#include "Memory.h" +#include "Display.h" + +#define WINDOWS 1 +#if defined (WINDOWS) +#include +#endif +#define IOREFRESH 32 + +using namespace std; + +namespace MKBasic { + +class VMachine +{ + public: + VMachine(); + VMachine(string romfname, string ramfname); + ~VMachine(); + + void InitVM(); + Regs *Run(); + Regs *Run(unsigned short addr); + Regs *Exec(); + Regs *Exec(unsigned short addr); + Regs *Step(); + Regs *Step(unsigned short addr); + void LoadROM(string romfname); + void LoadRAM(string ramfname); + unsigned short MemPeek8bit(unsigned short addr); + void MemPoke8bit(unsigned short addr, unsigned char v); + Regs *GetRegs(); + void SetCharIO(unsigned short addr, bool echo); + void DisableCharIO(); + unsigned short GetCharIOAddr(); + bool GetCharIOActive(); + void ShowIO(); + void ClearScreen(); + void ScrHome(); + + protected: + + private: + + MKCpu *mpCPU; + Memory *mpROM; + Memory *mpRAM; + Display *mpDisp; + unsigned short mRunAddr; + unsigned short mCharIOAddr; + bool mCharIOActive; + bool mCharIO; + + void LoadMEM(string memfname, Memory *pmem); + void ShowDisp(); +}; + +} // namespace MKBasic + +#endif diff --git a/bcd.c b/bcd.c new file mode 100644 index 0000000..9dc76eb --- /dev/null +++ b/bcd.c @@ -0,0 +1,56 @@ + +#include +#include +#include + + +unsigned char conv2bcd(unsigned short v) +{ + unsigned char arg8 = 0; + arg8 = (unsigned char)((v/10) << 4); + arg8 |= ((unsigned char)(v - (v/10)*10)) & 0x0F; + return arg8; +} + +char *conv24bitbin(unsigned char v) +{ + static char retbuf[5]; + int i=3; + + memset(retbuf, '0', 4); + retbuf[4]=0; + if (v == 0) return retbuf; + while (v > 0 && i >= 0) { + int r = v % 2; + retbuf[i] = ((r==0) ? '0' : '1'); + v = v/2; + i--; + } + + return retbuf; +} + +/* run this program using the console pauser or add your own getch, system("pause") or input loop */ + +int main(int argc, char *argv[]) +{ + unsigned short v = 0; + + for (v = 0; v < 100; v++) { + unsigned char cv = conv2bcd(v), hinyb, lonyb; + hinyb = (cv & 0xF0) >> 4; + lonyb = cv & 0x0F; + char buf_hinyb[5], buf_lonyb[5]; + strcpy(buf_hinyb, conv24bitbin(hinyb)); + strcpy(buf_lonyb, conv24bitbin(lonyb)); + printf("%d (dec) \t= %4s(%d) %4s(%d) (BCD, dec:%d)\n", v, + buf_hinyb, + hinyb, + buf_lonyb, + lonyb, + cv); + } + + return 0; +} + diff --git a/dummy.ram b/dummy.ram new file mode 100644 index 0000000..9ce490e --- /dev/null +++ b/dummy.ram @@ -0,0 +1,315 @@ +; +; test program #1 +; address: $0200 +; load Acc with value 12 +; write Acc to address $c000 (49152) +; +; nop +; nop +; lda #$0c +; sta $c000 +; brk +; +$EA $EA $A9 $0c $8D $00 $c0 $00 $00 +; +; test program #2 +; address: $0400 +; copy 0-terminated string from +; address $d000 to $0200 +; "Hello World!" +; +; ORG=$0400 +; hello: +; ldx #0 +; loop: +; lda $d000,x +; beq $06 ;branch to end (+6) if A=0 +; sta $0200,x +; inx +; bne $f5 ; branch to loop (-11) if X<>0 +; end: +; brk +ORG +$0400 +$A2 $00 +$BD $00 $d0 +$F0 $06 +$9D $00 $02 +$E8 +$D0 $F5 +$00 $00 +; data +; address: $d000 +ORG +$D000 +;DEC: 53248 +; "Hello World!" +72 101 108 108 111 32 87 111 114 108 100 33 0 +; +; test program #3 - copy Hello World! string to $0300 +; using different assembly instructions +; address: $0500 +; +; ORG=$0500 ;dec: 1280 +; hello: +; lda #0 +; sta $05 +; ldx $05 +; loop: +; lda $d000,x +; sta $0300,x +; beq end ;(+6) +; inx +; beq end ;(+3) +; jmp loop +; end: +; brk +ORG +$0500 +;DEC: 1280 +$A9 $00 +$85 $05 +$A6 $05 +$BD $00 $d0 +$9D $00 $03 +$F0 $06 +$E8 +$F0 $03 +$4C $06 $05 +$00 $00 +; +; test program #4 +; left-shift memory location $05 at zero page, +; then location $06 using zero page indexed addressing, +; then memory location $c001 (outside zero page) using absolute addressing +; then location $c002 using indexed absolute addressing +; and finally left-shift Acc. +; stop after each step for debugging +; exit loop when Acc=0 +; +; start: +; lda #$ff +; ldx #$01 +; sta $05 +; sta $05,x +; sta $c000,x +; inx +; sta $c000,x +; ldx #$01 +; loop2: +; brk +; asl $05 +; asl $05,x +; asl $c001 +; asl $c001,x +; asl +; bne loop2 ;(-15 or $f1) +; brk +ORG +$0600 +$A9 $FF +$A2 $01 +$85 $05 +$95 $05 +$9D $00 $C0 +$E8 +$9D $00 $C0 +$A2 $01 +$00 $00 +$06 $05 +$16 $05 +$0E $01 $C0 +$1E $01 $C0 +$0A +$D0 $F1 +$00 $00 +; +; test program #5 +; Test ORA opcode with various arguments and addressing modes. +; At each break, the contents of Acc should equal $AA. +; +; start: +; lda #$aa ;%10101010 +; sta $05 +; sta $aa +; lda #$00 +; tax +; ora ($05,x) +; brk +; lda #$00 +; ora $05 +; brk +; lda #$00 +; ora #$aa +; brk +; lda #$00 +; ora $0005 +; brk +; lda #$05 +; sta $06 +; lda #$00 +; sta $07 +; tay +; ora ($06),y +; brk +; lda #$00 +; tax +; ora $05,x +; brk +; lda #$00 +; tay +; ora $0005,y +; brk +; lda #$00 +; tax +; ora $0005,x +; brk +ORG +$0700 +$A9 $AA +$85 $05 +$85 $AA +$A9 $00 +$AA +$01 $05 +$00 $00 +$A9 $00 +$05 $05 +$00 $00 +$A9 $00 +$09 $AA +$00 $00 +$A9 $00 +$0D $05 $00 +$00 $00 +$A9 $05 +$85 $06 +$A9 $00 +$85 $07 +$A8 +$11 $06 +$00 $00 +$A9 $00 +$AA +$15 $05 +$00 $00 +$A9 $00 +$A8 +$19 $05 $00 +$00 $00 +$A9 $00 +$AA +$1D $05 $00 +$00 $00 +; +; test program #6 +; Test JSR opcode. +; After each break examine memory at $c000 and $c001. +; After 1-st break, $c000 should equal $dd. +; Return address-1 ($0802) should be on stack. +; After 2-nd break, PC counter should be at $0805. +; After 3-rd break, $c000 should equal $ee. +; Return address-1 ($0807) should be on stack. +; After 4-th break, PC counter should be at $080a. +; +; start: +; jsr sub1 +; brk +; jsr sub2 +; brk +; brk +; brk +; sub1: +; lda #$dd +; sta $c000 +; brk +; rts +; sub2: +; lda #$ee +; sta $c000 +; brk +; rts +; +ORG +$0800 +$20 $0B $08 +$00 $00 +$20 $13 $08 +$00 +$00 +$00 +$A9 $DD +$8D $00 $C0 +$00 $00 +$60 +$A9 $EE +$8D $00 $C0 +$00 $00 +$60 +; +; test program #7 +; Test ADC opcode. +; Expected results: +; First break: Acc=$01, Carry=1 +; 2-nd break: Acc=$02, Carry=1 +; 3-rd break: Acc=$22, Carry=0 +; 4-th break: Acc=$23, Carry=0 +; +; start: +; clc +; lda #$ff +; adc #$02 +; brk +; sec +; lda #$ff +; adc #$02 +; brk +; clc +; lda #$20 +; adc #$02 +; brk +; sec +; lda #$20 +; adc #$02 +; brk +; +ORG +$0900 +$18 +$A9 $FF +$69 $02 +$00 $00 +$38 +$A9 $FF +$69 $02 +$00 $00 +$18 +$A9 $20 +$69 $02 +$00 $00 +$38 +$A9 $20 +$69 $02 +$00 $00 +; +; test program #8 +; Test ROR opcode. +; +; start: +; sec +; lda #$00 +; loop: +; ror +; brk +; bcc loop ;(-5 -> $FB) +; brk +; +ORG +$0920 +$38 +$A9 $00 +$6A +$00 $00 +$90 $FB +$00 $00 +; \ No newline at end of file diff --git a/dummy.rom b/dummy.rom new file mode 100644 index 0000000..9ce490e --- /dev/null +++ b/dummy.rom @@ -0,0 +1,315 @@ +; +; test program #1 +; address: $0200 +; load Acc with value 12 +; write Acc to address $c000 (49152) +; +; nop +; nop +; lda #$0c +; sta $c000 +; brk +; +$EA $EA $A9 $0c $8D $00 $c0 $00 $00 +; +; test program #2 +; address: $0400 +; copy 0-terminated string from +; address $d000 to $0200 +; "Hello World!" +; +; ORG=$0400 +; hello: +; ldx #0 +; loop: +; lda $d000,x +; beq $06 ;branch to end (+6) if A=0 +; sta $0200,x +; inx +; bne $f5 ; branch to loop (-11) if X<>0 +; end: +; brk +ORG +$0400 +$A2 $00 +$BD $00 $d0 +$F0 $06 +$9D $00 $02 +$E8 +$D0 $F5 +$00 $00 +; data +; address: $d000 +ORG +$D000 +;DEC: 53248 +; "Hello World!" +72 101 108 108 111 32 87 111 114 108 100 33 0 +; +; test program #3 - copy Hello World! string to $0300 +; using different assembly instructions +; address: $0500 +; +; ORG=$0500 ;dec: 1280 +; hello: +; lda #0 +; sta $05 +; ldx $05 +; loop: +; lda $d000,x +; sta $0300,x +; beq end ;(+6) +; inx +; beq end ;(+3) +; jmp loop +; end: +; brk +ORG +$0500 +;DEC: 1280 +$A9 $00 +$85 $05 +$A6 $05 +$BD $00 $d0 +$9D $00 $03 +$F0 $06 +$E8 +$F0 $03 +$4C $06 $05 +$00 $00 +; +; test program #4 +; left-shift memory location $05 at zero page, +; then location $06 using zero page indexed addressing, +; then memory location $c001 (outside zero page) using absolute addressing +; then location $c002 using indexed absolute addressing +; and finally left-shift Acc. +; stop after each step for debugging +; exit loop when Acc=0 +; +; start: +; lda #$ff +; ldx #$01 +; sta $05 +; sta $05,x +; sta $c000,x +; inx +; sta $c000,x +; ldx #$01 +; loop2: +; brk +; asl $05 +; asl $05,x +; asl $c001 +; asl $c001,x +; asl +; bne loop2 ;(-15 or $f1) +; brk +ORG +$0600 +$A9 $FF +$A2 $01 +$85 $05 +$95 $05 +$9D $00 $C0 +$E8 +$9D $00 $C0 +$A2 $01 +$00 $00 +$06 $05 +$16 $05 +$0E $01 $C0 +$1E $01 $C0 +$0A +$D0 $F1 +$00 $00 +; +; test program #5 +; Test ORA opcode with various arguments and addressing modes. +; At each break, the contents of Acc should equal $AA. +; +; start: +; lda #$aa ;%10101010 +; sta $05 +; sta $aa +; lda #$00 +; tax +; ora ($05,x) +; brk +; lda #$00 +; ora $05 +; brk +; lda #$00 +; ora #$aa +; brk +; lda #$00 +; ora $0005 +; brk +; lda #$05 +; sta $06 +; lda #$00 +; sta $07 +; tay +; ora ($06),y +; brk +; lda #$00 +; tax +; ora $05,x +; brk +; lda #$00 +; tay +; ora $0005,y +; brk +; lda #$00 +; tax +; ora $0005,x +; brk +ORG +$0700 +$A9 $AA +$85 $05 +$85 $AA +$A9 $00 +$AA +$01 $05 +$00 $00 +$A9 $00 +$05 $05 +$00 $00 +$A9 $00 +$09 $AA +$00 $00 +$A9 $00 +$0D $05 $00 +$00 $00 +$A9 $05 +$85 $06 +$A9 $00 +$85 $07 +$A8 +$11 $06 +$00 $00 +$A9 $00 +$AA +$15 $05 +$00 $00 +$A9 $00 +$A8 +$19 $05 $00 +$00 $00 +$A9 $00 +$AA +$1D $05 $00 +$00 $00 +; +; test program #6 +; Test JSR opcode. +; After each break examine memory at $c000 and $c001. +; After 1-st break, $c000 should equal $dd. +; Return address-1 ($0802) should be on stack. +; After 2-nd break, PC counter should be at $0805. +; After 3-rd break, $c000 should equal $ee. +; Return address-1 ($0807) should be on stack. +; After 4-th break, PC counter should be at $080a. +; +; start: +; jsr sub1 +; brk +; jsr sub2 +; brk +; brk +; brk +; sub1: +; lda #$dd +; sta $c000 +; brk +; rts +; sub2: +; lda #$ee +; sta $c000 +; brk +; rts +; +ORG +$0800 +$20 $0B $08 +$00 $00 +$20 $13 $08 +$00 +$00 +$00 +$A9 $DD +$8D $00 $C0 +$00 $00 +$60 +$A9 $EE +$8D $00 $C0 +$00 $00 +$60 +; +; test program #7 +; Test ADC opcode. +; Expected results: +; First break: Acc=$01, Carry=1 +; 2-nd break: Acc=$02, Carry=1 +; 3-rd break: Acc=$22, Carry=0 +; 4-th break: Acc=$23, Carry=0 +; +; start: +; clc +; lda #$ff +; adc #$02 +; brk +; sec +; lda #$ff +; adc #$02 +; brk +; clc +; lda #$20 +; adc #$02 +; brk +; sec +; lda #$20 +; adc #$02 +; brk +; +ORG +$0900 +$18 +$A9 $FF +$69 $02 +$00 $00 +$38 +$A9 $FF +$69 $02 +$00 $00 +$18 +$A9 $20 +$69 $02 +$00 $00 +$38 +$A9 $20 +$69 $02 +$00 $00 +; +; test program #8 +; Test ROR opcode. +; +; start: +; sec +; lda #$00 +; loop: +; ror +; brk +; bcc loop ;(-5 -> $FB) +; brk +; +ORG +$0920 +$38 +$A9 $00 +$6A +$00 $00 +$90 $FB +$00 $00 +; \ No newline at end of file diff --git a/hello_world.bas b/hello_world.bas new file mode 100644 index 0000000..00635af --- /dev/null +++ b/hello_world.bas @@ -0,0 +1,5 @@ +10 LET A=1 +20 PR A;") HELLO WORLD FROM MKHBC!" +30 LET A=A+1 +40 IF A=0 THEN END +50 GOTO 20 diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..9c5c7ed --- /dev/null +++ b/main.cpp @@ -0,0 +1,333 @@ +#include +#include +#include +#include "MKCpu.h" +#include "Memory.h" +#include "Display.h" +#include "VMachine.h" +#include "MKGenException.h" + +using namespace std; +using namespace MKBasic; + +/* + *-------------------------------------------------------------------- + * Method: ShowHelp() + * Purpose: Display commands help. + * Arguments: n/a + * Returns: n/a + *-------------------------------------------------------------------- + */ +void ShowHelp() +{ + cout << "Virtual Machine/CPU emulator/Debugger Command Reference." << endl << endl; + cout << "S - step" << endl; + cout << " Executes single opcode at current address." << endl; + cout << "C - continue" << endl; + cout << " Continues code execution from current address until BRK." << endl; + cout << "D - dump memory" << endl; + cout << " Usage: D [startaddr] [endaddr]" << endl; + cout << " Where: startaddr,endaddr - memory addr. in hexadecimal format [0000..FFFF]." << endl; + cout << " Dumps contents of memory, hexadecimal and ASCII formats." << endl; + cout << "G - go/continue from new address until BRK" << endl; + cout << " Usage: G [address]" << endl; + cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]." << endl; + cout << " Executes code at provided address, interrupted by BRK opcode." << endl; + cout << "X - execute code from new address until RTS" << endl; + cout << " Usage: X [address]" << endl; + cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]." << endl; + cout << " Executes code at provided address, until RTS (last one)." << endl; + cout << "Q - quit" << endl; + cout << " Exits from the emulator/debugger." << endl; + cout << "A - set address for next step" << endl; + cout << " Usage: A [address]" << endl; + cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]." << endl; + cout << " Sets current address to a new value." << endl; + cout << "N - go number of steps" << endl; + cout << " Usage: N [steps]" << endl; + cout << " Where: steps - number of steps in decimal format" << endl; + cout << " Execute number of opcodes provided in steps argument starting" << endl; + cout << " from current address." << endl; + cout << "W - write to memory" << endl; + cout << " Usage: W [address] [hexval] [hexval] ... 100" << endl; + cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]," << endl; + cout << " hexval - byte value in hexadecimal format [00.FF]." << endl; + cout << " Writes provided values to memory starting at specified address." << endl; + cout << "I - toggle char I/O emulation" << endl; + cout << " Usage: I [address]" << endl; + cout << " Where: address - memory addr. in hexadecimal format [0000.FFFF]," << endl; + cout << " Toggles basic character I/O emulation. When enabled, all writes" << endl; + cout << " to the specified memory address also writes a character code to" << endl; + cout << " to a virtual console. All reads from specified memory address" << endl; + cout << " are interpreted as console character input." << endl; + cout << "R - regs" << endl; + cout << " Displays CPU registers and flags." << endl; + cout << "T - show I/O console" << endl; + cout << " Displays/prints the contents of the virtual console screen." << endl; + cout << " Note that in run mode (commands X, G or C), virtual screen is" << endl; + cout << " displayed automatically in real-time if I/O emulation is enabled." << endl; + cout << "E - toggle I/O local echo" << endl; + cout << " Toggles local echo on/off when I/O emulation is enabled." << endl; + cout << "B - blank (clear) screen" << endl; + cout << " Clears the screen, useful when after exiting I/O emulation" << endl; + cout << " your screen is messed up." << endl; + cout << "NOTE:" << endl; + cout << " If no arguments provided, each command will prompt user to enter" << endl; + cout << " missing data." << endl; + cout << endl; +} + +/* + *-------------------------------------------------------------------- + * Method: PromptNewAddress() + * Purpose: Prompt user to enter 16-bit address (hex) in console. + * Arguments: prompt - prompt text + * Returns: unsigned int - address entered by user + *-------------------------------------------------------------------- + */ +unsigned int PromptNewAddress(string prompt) +{ + unsigned int newaddr = 0x10000; + + while (newaddr > 0xFFFF) { + cout << prompt; + cin >> hex >> newaddr; + } + + return newaddr; +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void ShowRegs(Regs *preg, VMachine *pvm, unsigned short ioaddr, bool ioecho) +{ + cout << "Registers:" << endl; + cout << " Acc: $" << hex << (unsigned short)preg->Acc << "\t(%" << bitset<8>((int)preg->Acc) << ")" << endl; + cout << " X: $" << hex << (unsigned short)preg->IndX << endl; + cout << " Y: $" << hex << (unsigned short)preg->IndY << endl; + cout << " Addr: $" << hex << preg->PtrAddr << endl; + cout << " Acc16: $" << hex << preg->Acc16 << endl; + cout << " Ptr16: $" << hex << preg->Ptr16 << endl; + cout << " Stack: $" << hex << (unsigned short)preg->PtrStack << endl; + cout << " Flags: NV-BDIZC" << endl; + cout << " " << bitset<8>((int)preg->Flags) << endl; + cout << endl << "I/O status: " << (pvm->GetCharIOActive() ? "enabled" : "disabled") << ", "; + cout << " at: $" << hex << ioaddr << ", "; + cout << " local echo: " << (ioecho ? "ON" : "OFF") << "." << endl; + // cout << "-------------------------------------------------------------------------------" << endl; +} + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +void ShowMenu() +{ + cout << "---------------------------------------------------------------------------" << endl; + cout << "S - step | C - continue, D - dump memory | G - go/continue from new address" << endl; + cout << "Q - quit | A - set address for next step | N - go number of steps" << endl; + cout << "H - help | I - toggle char I/O emulation | W - write to memory" << endl; + cout << "R - regs | T - show I/O console | E - toggle I/O local echo" << endl; + cout << " | X - execute from new address | B - blank (clear) screen" << endl; + cout << "---------------------------------------------------------------------------" << endl; +} + + +/* + *-------------------------------------------------------------------- + * Method: + * Purpose: + * Arguments: + * Returns: + *-------------------------------------------------------------------- + */ +#define RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts) \ + brk = preg->SoftIrq; \ + lrts = preg->LastRTS; \ + while(step && nsteps > 1 && !brk && !lrts) { \ + cout << "addr: $" << hex << preg->PtrAddr << ", step: " << dec << stct << "\r"; \ + preg = pvm->Step(); \ + brk = preg->SoftIrq; \ + nsteps--; \ + stct++; \ + } + +/* run this program using the console pauser or add your own getch, system("pause") or input loop */ + +int main(int argc, char** argv) { + string romfile("dummy.rom"), ramfile("dummy.ram"); + if (argc > 1) { + ramfile = argv[1]; + } + try { + cout << endl; + VMachine *pvm = new VMachine(romfile, ramfile); + pvm->ClearScreen(); + cout << "Welcome to Virtual Machine/CPU Emulator (6502)/Debugger." << endl; + cout << "Copyright (C) by Marek Karcz 2016. All rights reserved." << endl; + string cmd; + bool runvm = false, step = false, brk = false, execaddr = false, stop = true; + bool ioecho = false, lrts = false, execvm = false; + unsigned int newaddr = 0x10000, ioaddr = 0xE000, tmpaddr = 0x0000; + int nsteps = 0; + while (true) { + Regs *preg = pvm->GetRegs(); + if (runvm) { + int stct = 1; + if (execaddr) { + preg = ((step) ? pvm->Step(newaddr) : pvm->Run(newaddr)); + RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts); + execaddr = false; + newaddr = 0x10000; + } else { + preg = ((step) ? pvm->Step() : pvm->Run()); + RUNSTEPS(step,nsteps,brk,preg,stct,pvm,lrts); + } + if (step) + cout << "\rExecuted " << dec << stct << ((stct == 1) ? " step." : " steps.") << " " << endl; + nsteps = 0; + runvm = step = false; + } else if (execvm) { + preg = (execaddr ? pvm->Exec(newaddr) : pvm->Exec()); + execvm = false; + brk = preg->SoftIrq; + lrts = preg->LastRTS; + } + if (brk || stop || lrts) { + cout << endl; + if (brk) { + cout << "BRK at " << hex << preg->PtrAddr << endl; + brk = stop = lrts = false; + } else if (lrts) { + cout << "FINISHED at " << hex << ((newaddr > 0xFFFF) ? preg->PtrAddr : newaddr) << endl; + brk = stop = lrts = false; + } else if (stop) { + cout << "STOPPED at " << hex << ((newaddr > 0xFFFF) ? preg->PtrAddr : newaddr) << endl; + brk = stop = lrts = false; + } + ShowRegs(preg,pvm,ioaddr,ioecho); + } + ShowMenu(); + cout << "> "; + cin >> cmd; + char c = tolower(cmd.c_str()[0]); + if (c == 'h') { + ShowHelp(); + } else if (c == 'b') { + pvm->ClearScreen(); + } else if (c == 'r') { + stop = true; + } else if (c == 'e') { + if (pvm->GetCharIOActive()) { + ioecho = !ioecho; + cout << "I/O echo is " << (ioecho ? "activated." : "deactivated.") << endl; + pvm->SetCharIO(ioaddr, ioecho); + } else { + cout << "ERROR: I/O is deactivated." << endl; + } + } else if (c == 't') { + if (pvm->GetCharIOActive()) { + pvm->ShowIO(); + } else { + cout << "ERROR: I/O is deactivated." << endl; + } + } else if (c == 'i') { + if (pvm->GetCharIOActive()) { + pvm->DisableCharIO(); + cout << "I/O deactivated." << endl; + } else { + ioaddr = PromptNewAddress("Address (0..FFFF): "); + cout << " [" << hex << ioaddr << "]" << endl; + pvm->SetCharIO(ioaddr, ioecho); + cout << "I/O activated." << endl; + } + } else if (c == 'w') { + tmpaddr = PromptNewAddress("Address (0..FFFF): "); + cout << " [" << hex << tmpaddr << "]" << endl; + cout << "Enter hex bytes [00..FF] values separated with NL or spaces, end with [100]:" << endl; + unsigned short v = 0; + while (true) { + cin >> hex >> v; + cout << " " << hex << v; + if (v > 0xFF) break; + pvm->MemPoke8bit(tmpaddr++, v & 0xFF); + }; + cout << endl; + } else if (c == 'a') { + execaddr = stop = true; + newaddr = PromptNewAddress("Address (0..FFFF): "); + cout << " [" << hex << newaddr << "]" << endl; + } else if (c == 's') { + runvm = step = stop = true; + } else if (c == 'n') { + nsteps = 0; + while (nsteps < 1) { + cout << "# of steps [n>1]: "; + cin >> dec >> nsteps; + } + cout << " [" << dec << nsteps << "]" << endl; + runvm = step = stop = true; + } else if (c == 'c') { + runvm = true; + } else if (c == 'g') { + runvm = true; + execaddr = true; + newaddr = PromptNewAddress("Address (0..FFFF): "); + cout << " [" << hex << newaddr << "]" << endl; + } else if (c == 'x') { + execvm = true; + execaddr = true; + newaddr = PromptNewAddress("Address (0..FFFF): "); + cout << " [" << hex << newaddr << "]" << endl; + } else if (c == 'q') { + break; + } else if (c == 'd') { + unsigned int addrbeg = 0x10000, addrend = 0x10000; + cout << "Enter address range (0..0xFFFF)..." << endl; + addrbeg = PromptNewAddress("Start address (0..FFFF): "); + cout << " [" << hex << addrbeg << "]" << endl; + addrend = PromptNewAddress("End address (0..FFFF): "); + cout << " [" << hex << addrend << "]" << endl; + cout << endl; + for (unsigned int addr = addrbeg; addr <= addrend; addr+=16) { + cout << "\t|"; + for (unsigned int j=0; j < 16; j++) { + unsigned int hv = (unsigned int)pvm->MemPeek8bit(addr+j); + if (hv < 16) { + cout << 0; + } + cout << hex << hv << " "; + } + cout << "|"; + for (int j=0; j < 16; j++) { + char cc = (char)pvm->MemPeek8bit(addr+j); + if (isprint(cc)) + cout << cc; + else + cout << "?"; + } + cout << '\r'; + cout << hex << addr; + cout << endl; + } + } + } + } + catch (MKGenException& ex) { + cout << "ERROR: " << ex.GetCause() << endl; + } + catch (...) { + cout << "ERROR: Fatal." << endl; + } + return 0; +} diff --git a/t_adc_bcd_01.65s b/t_adc_bcd_01.65s new file mode 100644 index 0000000..6266710 --- /dev/null +++ b/t_adc_bcd_01.65s @@ -0,0 +1,99 @@ +; ADC, test decimal mode. +; +; NV-BDIZC +; ??1110?? +; +; The results I got on Rockwell 6502 AP +; +; 00 + 00 and C=0 gives 00 and N=0 V=0 Z=1 C=0 (3A) +; 79 + 00 and C=1 gives 80 and N=1 V=1 Z=0 C=0 (F8) +; 24 + 56 and C=0 gives 80 and N=1 V=1 Z=0 C=0 (F8) +; 93 + 82 and C=0 gives 75 and N=0 V=1 Z=0 C=1 (79) +; 89 + 76 and C=0 gives 65 and N=0 V=0 Z=0 C=1 (39) +; 89 + 76 and C=1 gives 66 and N=0 V=0 Z=0 C=1 (39) +; 80 + f0 and C=0 gives d0 and N=1 V=1 Z=0 C=1 (F9) +; 80 + fa and C=0 gives e0 and N=1 V=0 Z=0 C=1 (B9) +; 2f + 4f and C=0 gives 74 and N=0 V=0 Z=0 C=0 (38) +; 6f + 00 and C=1 gives 76 and N=0 V=0 Z=0 C=0 (38) + +RES=$0300 + + *=$0200 + + SED + CLC + LDA #$00 + ADC #$00 + STA RES + PHP + PLA + STA RES+1 + SEC + LDA #$79 + ADC #$00 + STA RES+2 + PHP + PLA + STA RES+3 + CLC + LDA #$24 + ADC #$56 + STA RES+4 + PHP + PLA + STA RES+5 + CLC + LDA #$93 + ADC #$82 + STA RES+6 + PHP + PLA + STA RES+7 + CLC + LDA #$89 + ADC #$76 + STA RES+8 + PHP + PLA + STA RES+9 + SEC + LDA #$89 + ADC #$76 + STA RES+10 + PHP + PLA + STA RES+11 + CLC + LDA #$80 + ADC #$F0 + STA RES+12 + PHP + PLA + STA RES+13 + CLC + LDA #$80 + ADC #$FA + STA RES+14 + PHP + PLA + STA RES+15 + CLC + LDA #$2F + ADC #$4F + STA RES+16 + PHP + PLA + STA RES+17 + SEC + LDA #$6F + ADC #$00 + STA RES+18 + PHP + PLA + STA RES+19 + BRK + + *=$0300 + + .DS 20 + \ No newline at end of file diff --git a/t_adc_bcd_01.dat b/t_adc_bcd_01.dat new file mode 100644 index 0000000..52375ca --- /dev/null +++ b/t_adc_bcd_01.dat @@ -0,0 +1,36 @@ +; Test ADC BCD mode. +ORG +$0200 +$F8 $18 $A9 $00 $69 $00 $8D $00 +$03 $08 $68 $8D $01 $03 $38 $A9 +$79 $69 $00 $8D $02 $03 $08 $68 +$8D $03 $03 $18 $A9 $24 $69 $56 +$8D $04 $03 $08 $68 $8D $05 $03 +$18 $A9 $93 $69 $82 $8D $06 $03 +$08 $68 $8D $07 $03 $18 $A9 $89 +$69 $76 $8D $08 $03 $08 $68 $8D +$09 $03 $38 $A9 $89 $69 $76 $8D +$0A $03 $08 $68 $8D $0B $03 $18 +$A9 $80 $69 $F0 $8D $0C $03 $08 +$68 $8D $0D $03 $18 $A9 $80 $69 +$FA $8D $0E $03 $08 $68 $8D $0F +$03 $18 $A9 $2F $69 $4F $8D $10 +$03 $08 $68 $8D $11 $03 $38 $A9 +$6F $69 $00 $8D $12 $03 $08 $68 +$8D $13 $03 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 + diff --git a/t_sbc_bcd_01.65s b/t_sbc_bcd_01.65s new file mode 100644 index 0000000..e78c36c --- /dev/null +++ b/t_sbc_bcd_01.65s @@ -0,0 +1,85 @@ +; SBC, test decimal mode. +; +; NV-BDIZC +; ??1110?? +; +; Expected results (I got on Rockwell 6502 AP): +; 00 - 00 and C=0 gives 99 and N=1 V=0 Z=0 C=0 (B8) +; 00 - 00 and C=1 gives 00 and N=0 V=0 Z=1 C=1 (3B) +; 00 - 01 and C=1 gives 99 and N=1 V=0 Z=0 C=0 (B8) +; 0a - 00 and C=1 gives 0a and N=0 V=0 Z=0 C=1 (39) +; 0b - 00 and C=0 gives 0a and N=0 V=0 Z=0 C=1 (39) +; 9a - 00 and C=1 gives 9a and N=1 V=0 Z=0 C=1 (B9) +; 9b - 00 and C=0 gives 9a and N=1 V=0 Z=0 C=1 (B9) +; + + *=$0200 + + SED + CLC + LDA #$00 + SBC #$00 + STA SBT1A + PHP + PLA + STA SBT1F + SEC + LDA #$00 + SBC #$00 + STA SBT2A + PHP + PLA + STA SBT2F + SEC + LDA #$00 + SBC #$01 + STA SBT3A + PHP + PLA + STA SBT3F + SEC + LDA #$0A + SBC #$00 + STA SBT4A + PHP + PLA + STA SBT4F + CLC + LDA #$0B + SBC #$00 + STA SBT5A + PHP + PLA + STA SBT5F + SEC + LDA #$9A + SBC #$00 + STA SBT6A + PHP + PLA + STA SBT6F + CLC + LDA #$9B + SBC #$00 + STA SBT7A + PHP + PLA + STA SBT7F + BRK + + *=$0300 + +SBT1A: .DB 0 +SBT1F: .DB 0 +SBT2A: .DB 0 +SBT2F: .DB 0 +SBT3A: .DB 0 +SBT3F: .DB 0 +SBT4A: .DB 0 +SBT4F: .DB 0 +SBT5A: .DB 0 +SBT5F: .DB 0 +SBT6A: .DB 0 +SBT6F: .DB 0 +SBT7A: .DB 0 +SBT7F: .DB 0 \ No newline at end of file diff --git a/t_sbc_bcd_01.dat b/t_sbc_bcd_01.dat new file mode 100644 index 0000000..941c928 --- /dev/null +++ b/t_sbc_bcd_01.dat @@ -0,0 +1,36 @@ +; Test BCD mode. +ORG +$0200 +$F8 $18 $A9 $00 $E9 $00 $8D $00 +$03 $08 $68 $8D $01 $03 $38 $A9 +$00 $E9 $00 $8D $02 $03 $08 $68 +$8D $03 $03 $38 $A9 $00 $E9 $01 +$8D $04 $03 $08 $68 $8D $05 $03 +$38 $A9 $0A $E9 $00 $8D $06 $03 +$08 $68 $8D $07 $03 $18 $A9 $0B +$E9 $00 $8D $08 $03 $08 $68 $8D +$09 $03 $38 $A9 $9A $E9 $00 $8D +$0A $03 $08 $68 $8D $0B $03 $18 +$A9 $9B $E9 $00 $8D $0C $03 $08 +$68 $8D $0D $03 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 + diff --git a/test_char_io_01.65s b/test_char_io_01.65s new file mode 100644 index 0000000..343d122 --- /dev/null +++ b/test_char_io_01.65s @@ -0,0 +1,50 @@ +; Basic test of char I/O emulation + + .ORG $0200 + +CHRGET = $E000 +PUTCH = $E000 +TXTBUF = $0400 +CR = $0D +NL = $0A + +START: LDX #$00 +PR1: LDA PROMPT,X ;print prompt + BEQ L0 + STA PUTCH + INX + BNE PR1 +L0: LDX #$00 +GETTXT: LDA CHRGET ;get text from input + BEQ GETTXT + CMP #CR + BEQ L1 + CMP #NL + BEQ L1 + STA PUTCH ;echo char + STA TXTBUF,X ;store char + INX + BNE GETTXT +L1: LDA #NL ; add line break + STA TXTBUF,X + STA PUTCH + INX + LDA #CR + STA TXTBUF,X + STA PUTCH + INX + LDA #$00 ; add null + STA TXTBUF,X + TAX +PRINT: LDA TXTBUF,X ; print to output + BEQ L2 + STA PUTCH + INX + BNE PRINT +L2: BRK + NOP + JMP START +PROMPT: .DB "Enter text:",0 + + + \ No newline at end of file diff --git a/test_char_io_01.dat b/test_char_io_01.dat new file mode 100644 index 0000000..7fdc22b --- /dev/null +++ b/test_char_io_01.dat @@ -0,0 +1,15 @@ +; I/O test for MKBASIC VM. +ORG +$0200 +$A2 $00 $BD $4E $02 $F0 $06 $8D +$00 $E0 $E8 $D0 $F5 $A2 $00 $AD +$00 $E0 $F0 $FB $C9 $0D $F0 $0D +$C9 $0A $F0 $09 $8D $00 $E0 $9D +$00 $04 $E8 $D0 $EA $A9 $0A $9D +$00 $04 $8D $00 $E0 $E8 $A9 $0D +$9D $00 $04 $8D $00 $E0 $E8 $A9 +$00 $9D $00 $04 $AA $BD $00 $04 +$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 diff --git a/testall.asm b/testall.asm new file mode 100644 index 0000000..197a441 --- /dev/null +++ b/testall.asm @@ -0,0 +1,925 @@ + .ORG $4000 +start: +; EXPECTED FINAL RESULTS: $0210 = FF +; (any other number will be the +; test that failed) + +; initialize: + LDA #$00 + STA $0210 + ; store each test's expected + LDA #$55 + STA $0200 + LDA #$AA + STA $0201 + LDA #$FF + STA $0202 + LDA #$6E + STA $0203 + LDA #$42 + STA $0204 + LDA #$33 + STA $0205 + LDA #$9D + STA $0206 + LDA #$7F + STA $0207 + LDA #$A5 + STA $0208 + LDA #$1F + STA $0209 + LDA #$CE + STA $020A + LDA #$29 + STA $020B + LDA #$42 + STA $020C + LDA #$6C + STA $020D + LDA #$42 + STA $020E + + +; expected result: $022A = 0x55 +test00: + LDA #85 + LDX #42 + LDY #115 + STA $81 + LDA #$01 + STA $61 + LDA #$7E + LDA $81 + STA $0910 + LDA #$7E + LDA $0910 + STA $56,X + LDA #$7E + LDA $56,X + STY $60 + STA ($60),Y + LDA #$7E + LDA ($60),Y + STA $07ff,X + LDA #$7E + LDA $07ff,X + STA $07ff,Y + LDA #$7E + LDA $07ff,Y + STA ($36,X) + LDA #$7E + LDA ($36,X) + STX $50 + LDX $60 + LDY $50 + STX $0913 + LDX #$22 + LDX $0913 + STY $0914 + LDY #$99 + LDY $0914 + STY $2D,X + STX $77,Y + LDY #$99 + LDY $2D,X + LDX #$22 + LDX $77,Y + LDY #$99 + LDY $08A0,X + LDX #$22 + LDX $08A1,Y + STA $0200,X + +; CHECK test00: + LDA $022A + CMP $0200 + BEQ test00pass + JMP theend +test00pass: + LDA #$FE + STA $0210 + + +; expected result: $A9 = 0xAA +test01: + ; imm + LDA #85 + AND #83 + ORA #56 + EOR #17 + + ; zpg + STA $99 + LDA #185 + STA $10 + LDA #231 + STA $11 + LDA #57 + STA $12 + LDA $99 + AND $10 + ORA $11 + EOR $12 + + ; zpx + LDX #16 + STA $99 + LDA #188 + STA $20 + LDA #49 + STA $21 + LDA #23 + STA $22 + LDA $99 + AND $10,X + ORA $11,X + EOR $12,X + + ; abs + STA $99 + LDA #111 + STA $0110 + LDA #60 + STA $0111 + LDA #39 + STA $0112 + LDA $99 + AND $0110 + ORA $0111 + EOR $0112 + + ; abx + STA $99 + LDA #138 + STA $0120 + LDA #71 + STA $0121 + LDA #143 + STA $0122 + LDA $99 + AND $0110,X + ORA $0111,X + EOR $0112,X + + ; aby + LDY #32 + STA $99 + LDA #115 + STA $0130 + LDA #42 + STA $0131 + LDA #241 + STA $0132 + LDA $99 + AND $0110,Y + ORA $0111,Y + EOR $0112,Y + + ; idx + STA $99 + LDA #112 + STA $30 + LDA #$01 + STA $31 + LDA #113 + STA $32 + LDA #$01 + STA $33 + LDA #114 + STA $34 + LDA #$01 + STA $35 + LDA #197 + STA $0170 + LDA #124 + STA $0171 + LDA #161 + STA $0172 + LDA $99 + AND ($20,X) + ORA ($22,X) + EOR ($24,X) + + ; idy + STA $99 + LDA #96 + STA $40 + LDA #$01 + STA $41 + LDA #97 + STA $42 + LDA #$01 + STA $43 + LDA #98 + STA $44 + LDA #$01 + STA $45 + LDA #55 + STA $0250 + LDA #35 + STA $0251 + LDA #157 + STA $0252 + LDA $99 + LDY #$F0 + AND ($40),Y + ORA ($42),Y + EOR ($44),Y + + STA $A9 + +; CHECK test01 + LDA $A9 + CMP $0201 + BEQ test02 + LDA #$01 + STA $0210 + JMP theend + + +; expected result: $71 = 0xFF +test02: + LDA #$FF + LDX #$00 + + STA $90 + INC $90 + INC $90 + LDA $90 + LDX $90 + + STA $90,X + INC $90,X + LDA $90,X + LDX $91 + + STA $0190,X + INC $0192 + LDA $0190,X + LDX $0192 + + STA $0190,X + INC $0190,X + LDA $0190,X + LDX $0193 + + STA $0170,X + DEC $0170,X + LDA $0170,X + LDX $0174 + + STA $0170,X + DEC $0173 + LDA $0170,X + LDX $0173 + + STA $70,X + DEC $70,X + LDA $70,X + LDX $72 + + STA $70,X + DEC $71 + DEC $71 + +; CHECK test02 + LDA $71 + CMP $0202 + BEQ test03 + LDA #$02 + STA $0210 + JMP theend + + +; expected result: $01DD = 0x6E +test03: + LDA #$4B + LSR + ASL + + STA $50 + ASL $50 + ASL $50 + LSR $50 + LDA $50 + + LDX $50 + ORA #$C9 + STA $60 + ASL $4C,X + LSR $4C,X + LSR $4C,X + LDA $4C,X + + LDX $60 + ORA #$41 + STA $012E + LSR $0100,X + LSR $0100,X + ASL $0100,X + LDA $0100,X + + LDX $012E + ORA #$81 + STA $0100,X + LSR $0136 + LSR $0136 + ASL $0136 + LDA $0100,X + + ; rol & ror + + ROL + ROL + ROR + STA $70 + + LDX $70 + ORA #$03 + STA $0C,X + ROL $C0 + ROR $C0 + ROR $C0 + LDA $0C,X + + LDX $C0 + STA $D0 + ROL $75,X + ROL $75,X + ROR $75,X + LDA $D0 + + LDX $D0 + STA $0100,X + ROL $01B7 + ROL $01B7 + ROL $01B7 + ROR $01B7 + LDA $0100,X + + LDX $01B7 + STA $01DD + ROL $0100,X + ROR $0100,X + ROR $0100,X + +; CHECK test03 + LDA $01DD + CMP $0203 + BEQ test04 + LDA #$03 + STA $0210 + JMP theend + + +; expected result: $40 = 0x42 +test04: + LDA #$E8 ;originally:#$7C + STA $20 + LDA #$42 ;originally:#$02 + STA $21 + LDA #$00 + ORA #$03 + JMP jump1 + ORA #$FF ; not done +jump1: + ORA #$30 + JSR subr + ORA #$42 + JMP ($0020) + ORA #$FF ; not done +subr: + STA $30 + LDX $30 + LDA #$00 + RTS +final: + STA $0D,X + +; CHECK test04 + LDA $40 + CMP $0204 + BEQ test05 + LDA #$04 + STA $0210 + JMP theend + + +; expected result: $40 = 0x33 +test05: + LDA #$35 + + TAX + DEX + DEX + INX + TXA + + TAY + DEY + DEY + INY + TYA + + TAX + LDA #$20 + TXS + LDX #$10 + TSX + TXA + + STA $40 + +; CHECK test05 + LDA $40 + CMP $0205 + BEQ test06 + LDA #$05 + STA $0210 + JMP theend + + +; expected result: $30 = 9D +test06: + +; RESET TO CARRY FLAG = 0 + ROL + + LDA #$6A + STA $50 + LDA #$6B + STA $51 + LDA #$A1 + STA $60 + LDA #$A2 + STA $61 + + LDA #$FF + ADC #$FF + ADC #$FF + SBC #$AE + + STA $40 + LDX $40 + ADC $00,X + SBC $01,X + + ADC $60 + SBC $61 + + STA $0120 + LDA #$4D + STA $0121 + LDA #$23 + ADC $0120 + SBC $0121 + + STA $F0 + LDX $F0 + LDA #$64 + STA $0124 + LDA #$62 + STA $0125 + LDA #$26 + ADC $0100,X + SBC $0101,X + + STA $F1 + LDY $F1 + LDA #$E5 + STA $0128 + LDA #$E9 + STA $0129 + LDA #$34 + ADC $0100,Y + SBC $0101,Y + + STA $F2 + LDX $F2 + LDA #$20 + STA $70 + LDA #$01 + STA $71 + LDA #$24 + STA $72 + LDA #$01 + STA $73 + ADC ($41,X) + SBC ($3F,X) + + STA $F3 + LDY $F3 + LDA #$DA + STA $80 + LDA #$00 + STA $81 + LDA #$DC + STA $82 + LDA #$00 + STA $83 + LDA #$AA + ADC ($80),Y + SBC ($82),Y + STA $30 + +; CHECK test06 + LDA $30 + CMP $0206 + BEQ test07 + LDA #$06 + STA $0210 + JMP theend + + +; expected result: $15 = 0x7F +test07: + ; prepare memory + LDA #$00 + STA $34 + LDA #$FF + STA $0130 + LDA #$99 + STA $019D + LDA #$DB + STA $0199 + LDA #$2F + STA $32 + LDA #$32 + STA $4F + LDA #$30 + STA $33 + LDA #$70 + STA $AF + LDA #$18 + STA $30 + + ; imm + CMP #$18 + BEQ beq1 ; taken + AND #$00 ; not done +beq1: + ; zpg + ORA #$01 + CMP $30 + BNE bne1 ; taken + AND #$00 ; not done +bne1: + ; abs + LDX #$00 + CMP $0130 + BEQ beq2 ; not taken + STA $40 + LDX $40 +beq2: + ; zpx + CMP $27,X + BNE bne2 ; not taken + ORA #$84 + STA $41 + LDX $41 +bne2: + ; abx + AND #$DB + CMP $0100,X + BEQ beq3 ; taken + AND #$00 ; not done +beq3: + ; aby + STA $42 + LDY $42 + AND #$00 + CMP $0100,Y + BNE bne3 ; taken + ORA #$0F ; not done +bne3: + ; idx + STA $43 + LDX $43 + ORA #$24 + CMP ($40,X) + BEQ beq4 ; not taken + ORA #$7F +beq4: + ; idy + STA $44 + LDY $44 + EOR #$0F + CMP ($33),Y + BNE bne4 ; not taken + LDA $44 + STA $15 +bne4: + +; CHECK test07 + LDA $15 + CMP $0207 + BEQ test08 + LDA #$07 + STA $0210 + JMP theend + + +; expected result: $42 = 0xA5 +test08: + ; prepare memory + LDA #$A5 + STA $20 + STA $0120 + LDA #$5A + STA $21 + + ; cpx imm... + LDX #$A5 + CPX #$A5 + BEQ b1 ; taken + LDX #$01 ; not done +b1: + ; cpx zpg... + CPX $20 + BEQ b2 ; taken + LDX #$02 ; not done +b2: + ; cpx abs... + CPX $0120 + BEQ b3 ; taken + LDX #$03 ; not done +b3: + ; cpy imm... + STX $30 + LDY $30 + CPY #$A5 + BEQ b4 ; taken + LDY #$04 ; not done +b4: + ; cpy zpg... + CPY $20 + BEQ b5 ; taken + LDY #$05 ; not done +b5: + ; cpy abs... + CPY $0120 + BEQ b6 ; taken + LDY #$06 ; not done +b6: + ; bit zpg... + STY $31 + LDA $31 + BIT $20 + BNE b7 ; taken + LDA #$07 ; not done +b7: + ; bit abs... + BIT $0120 + BNE b8 ; taken + LDA #$08 ; not done +b8: + BIT $21 + BNE b9 ; not taken + STA $42 +b9: + +; CHECK test08 + LDA $42 + CMP $0208 + BEQ test09 + LDA #$08 + STA $0210 + JMP theend + + +; expected result: $80 = 0x1F +test09: + ; prepare memory + LDA #$54 + STA $32 + LDA #$B3 + STA $A1 + LDA #$87 + STA $43 + + ; BPL + LDX #$A1 + BPL bpl1 ; not taken + LDX #$32 +bpl1: + LDY $00,X + BPL bpl2 ; taken + LDA #$05 ; not done + LDX $A1 ; not done +bpl2: + + ; BMI + BMI bmi1 ; not taken + SBC #$03 +bmi1: + BMI bmi2 ; taken + LDA #$41 ; not done +bmi2: + + ; BVC + EOR #$30 + STA $32 + ADC $00,X + BVC bvc1 ; not taken + LDA #$03 +bvc1: + STA $54 + LDX $00,Y + ADC $51,X + BVC bvc2 ; taken + LDA #$E5 ; not done +bvc2: + + ; BVS + ADC $40,X + BVS bvs1 ; not taken + STA $0001,Y + ADC $55 +bvs1: + BVS bvs2 ; taken + LDA #$00 +bvs2: + + ; BCC + ADC #$F0 + BCC bcc1 ; not taken + STA $60 + ADC $43 +bcc1: + BCC bcc2 ; taken + LDA #$FF +bcc2: + + ; BCS + ADC $54 + BCS bcs1 ; not taken + ADC #$87 + LDX $60 +bcs1: + BCS bcs2 ; taken + LDA #$00 ; not done +bcs2: + STA $73,X + +; CHECK test09 + LDA $80 + CMP $0209 + BEQ test10 + LDA #$09 + STA $0210 + JMP theend + + +; expected result: $30 = 0xCE +test10: + +; RESET TO CARRY = 0 & OVERFLOW = 0 + ADC #$00 + + LDA #$99 + ADC #$87 + CLC + NOP + BCC t10bcc1 ; taken + ADC #$60 ; not done + ADC #$93 ; not done +t10bcc1: + SEC + NOP + BCC t10bcc2 ; not taken + CLV +t10bcc2: + BVC t10bvc1 ; taken + LDA #$00 ; not done +t10bvc1: + ADC #$AD + NOP + STA $30 + +; CHECK test10 + LDA $30 + CMP $020A + BEQ test11 + LDA #$0A + STA $0210 + JMP theend + + +; expected result: $30 = 0x29 +test11: + +; RESET TO CARRY = 0 & ZERO = 0 + ADC #$01 + + LDA #$27 + ADC #$01 + SEC + PHP + CLC + PLP + ADC #$00 + PHA + LDA #$00 + PLA + STA $30 + +; CHECK test11 + LDA $30 + CMP $020B + BEQ test12 + LDA #$0B + STA $0210 + JMP theend + + +; expected result: $33 = 0x42 +test12: + CLC + LDA #$42 + BCC runstuff + STA $33 + BCS t12end +runstuff: + LDA #$45 + PHA + LDA #$61 + PHA + SEC + PHP + CLC + RTI +t12end: + +; CHECK test12 + LDA $33 + CMP $020C + BEQ test13 + LDA #$0C + STA $0210 + JMP theend + + +; expected result: $21 = 0x6C (simulator) +; $21 = 0x0C (ours) +test13: + +; RESET TO CARRY = 0 & ZERO = 0 + ADC #$01 + + SEI + SED + PHP + PLA + STA $20 + CLI + CLD + PHP + PLA + ADC $20 + STA $21 + +; CHECK test13 + LDA $21 + CMP $020D + BEQ test14 + LDA #$0D + STA $0210 + JMP theend + + +; expect result: $60 = 0x42 +test14: + ; !!! NOTICE: BRK doesn't work in this + ; simulator, so commented instructions + ; are what should be executed... + ;JMP pass_intrp + LDA #$41 + STA $60 + ;RTI + ;pass_intrp: + ;LDA #$FF + ;STA $60 + ;BRK (two bytes) + INC $60 + +; CHECK test14 + LDA $60 + CMP $020E + BEQ suiteafinal + LDA #$0E + STA $0210 + JMP theend + +suiteafinal: + ; IF $0210 == 0xFE, INCREMENT + ; (checking that it didn't + ; happen to wander off and + ; not run our instructions + ; to say which tests failed...) + LDA #$FE + CMP $0210 + BNE theend + INC $0210 +theend: + BRK + ;JMP theend \ No newline at end of file diff --git a/testall.dat b/testall.dat new file mode 100644 index 0000000..29cafa8 --- /dev/null +++ b/testall.dat @@ -0,0 +1,195 @@ +; Test 6502 emulation. +ORG +$4000 +$A9 $00 $8D $10 $02 $A9 $55 $8D +$00 $02 $A9 $AA $8D $01 $02 $A9 +$FF $8D $02 $02 $A9 $6E $8D $03 +$02 $A9 $42 $8D $04 $02 $A9 $33 +$8D $05 $02 $A9 $9D $8D $06 $02 +$A9 $7F $8D $07 $02 $A9 $A5 $8D +$08 $02 $A9 $1F $8D $09 $02 $A9 +$CE $8D $0A $02 $A9 $29 $8D $0B +$02 $A9 $42 $8D $0C $02 $A9 $6C +$8D $0D $02 $A9 $42 $8D $0E $02 +$A9 $55 $A2 $2A $A0 $73 $85 $81 +$A9 $01 $85 $61 $A9 $7E $A5 $81 +$8D $10 $09 $A9 $7E $AD $10 $09 +$95 $56 $A9 $7E $B5 $56 $84 $60 +$91 $60 $A9 $7E $B1 $60 $9D $FF +$07 $A9 $7E $BD $FF $07 $99 $FF +$07 $A9 $7E $B9 $FF $07 $81 $36 +$A9 $7E $A1 $36 $86 $50 $A6 $60 +$A4 $50 $8E $13 $09 $A2 $22 $AE +$13 $09 $8C $14 $09 $A0 $99 $AC +$14 $09 $94 $2D $96 $77 $A0 $99 +$B4 $2D $A2 $22 $B6 $77 $A0 $99 +$BC $A0 $08 $A2 $22 $BE $A1 $08 +$9D $00 $02 $AD $2A $02 $CD $00 +$02 $F0 $03 $4C $C0 $45 $A9 $FE +$8D $10 $02 $A9 $55 $29 $53 $09 +$38 $49 $11 $85 $99 $A9 $B9 $85 +$10 $A9 $E7 $85 $11 $A9 $39 $85 +$12 $A5 $99 $25 $10 $05 $11 $45 +$12 $A2 $10 $85 $99 $A9 $BC $85 +$20 $A9 $31 $85 $21 $A9 $17 $85 +$22 $A5 $99 $35 $10 $15 $11 $55 +$12 $85 $99 $A9 $6F $8D $10 $01 +$A9 $3C $8D $11 $01 $A9 $27 $8D +$12 $01 $A5 $99 $2D $10 $01 $0D +$11 $01 $4D $12 $01 $85 $99 $A9 +$8A $8D $20 $01 $A9 $47 $8D $21 +$01 $A9 $8F $8D $22 $01 $A5 $99 +$3D $10 $01 $1D $11 $01 $5D $12 +$01 $A0 $20 $85 $99 $A9 $73 $8D +$30 $01 $A9 $2A $8D $31 $01 $A9 +$F1 $8D $32 $01 $A5 $99 $39 $10 +$01 $19 $11 $01 $59 $12 $01 $85 +$99 $A9 $70 $85 $30 $A9 $01 $85 +$31 $A9 $71 $85 $32 $A9 $01 $85 +$33 $A9 $72 $85 $34 $A9 $01 $85 +$35 $A9 $C5 $8D $70 $01 $A9 $7C +$8D $71 $01 $A9 $A1 $8D $72 $01 +$A5 $99 $21 $20 $01 $22 $41 $24 +$85 $99 $A9 $60 $85 $40 $A9 $01 +$85 $41 $A9 $61 $85 $42 $A9 $01 +$85 $43 $A9 $62 $85 $44 $A9 $01 +$85 $45 $A9 $37 $8D $50 $02 $A9 +$23 $8D $51 $02 $A9 $9D $8D $52 +$02 $A5 $99 $A0 $F0 $31 $40 $11 +$42 $51 $44 $85 $A9 $A5 $A9 $CD +$01 $02 $F0 $08 $A9 $01 $8D $10 +$02 $4C $C0 $45 $A9 $FF $A2 $00 +$85 $90 $E6 $90 $E6 $90 $A5 $90 +$A6 $90 $95 $90 $F6 $90 $B5 $90 +$A6 $91 $9D $90 $01 $EE $92 $01 +$BD $90 $01 $AE $92 $01 $9D $90 +$01 $FE $90 $01 $BD $90 $01 $AE +$93 $01 $9D $70 $01 $DE $70 $01 +$BD $70 $01 $AE $74 $01 $9D $70 +$01 $CE $73 $01 $BD $70 $01 $AE +$73 $01 $95 $70 $D6 $70 $B5 $70 +$A6 $72 $95 $70 $C6 $71 $C6 $71 +$A5 $71 $CD $02 $02 $F0 $08 $A9 +$02 $8D $10 $02 $4C $C0 $45 $A9 +$4B $4A $0A $85 $50 $06 $50 $06 +$50 $46 $50 $A5 $50 $A6 $50 $09 +$C9 $85 $60 $16 $4C $56 $4C $56 +$4C $B5 $4C $A6 $60 $09 $41 $8D +$2E $01 $5E $00 $01 $5E $00 $01 +$1E $00 $01 $BD $00 $01 $AE $2E +$01 $09 $81 $9D $00 $01 $4E $36 +$01 $4E $36 $01 $0E $36 $01 $BD +$00 $01 $2A $2A $6A $85 $70 $A6 +$70 $09 $03 $95 $0C $26 $C0 $66 +$C0 $66 $C0 $B5 $0C $A6 $C0 $85 +$D0 $36 $75 $36 $75 $76 $75 $A5 +$D0 $A6 $D0 $9D $00 $01 $2E $B7 +$01 $2E $B7 $01 $2E $B7 $01 $6E +$B7 $01 $BD $00 $01 $AE $B7 $01 +$8D $DD $01 $3E $00 $01 $7E $00 +$01 $7E $00 $01 $AD $DD $01 $CD +$03 $02 $F0 $08 $A9 $03 $8D $10 +$02 $4C $C0 $45 $A9 $E8 $85 $20 +$A9 $42 $85 $21 $A9 $00 $09 $03 +$4C $D5 $42 $09 $FF $09 $30 $20 +$E1 $42 $09 $42 $6C $20 $00 $09 +$FF $85 $30 $A6 $30 $A9 $00 $60 +$95 $0D $A5 $40 $CD $04 $02 $F0 +$08 $A9 $04 $8D $10 $02 $4C $C0 +$45 $A9 $35 $AA $CA $CA $E8 $8A +$A8 $88 $88 $C8 $98 $AA $A9 $20 +$9A $A2 $10 $BA $8A $85 $40 $A5 +$40 $CD $05 $02 $F0 $08 $A9 $05 +$8D $10 $02 $4C $C0 $45 $2A $A9 +$6A $85 $50 $A9 $6B $85 $51 $A9 +$A1 $85 $60 $A9 $A2 $85 $61 $A9 +$FF $69 $FF $69 $FF $E9 $AE $85 +$40 $A6 $40 $75 $00 $F5 $01 $65 +$60 $E5 $61 $8D $20 $01 $A9 $4D +$8D $21 $01 $A9 $23 $6D $20 $01 +$ED $21 $01 $85 $F0 $A6 $F0 $A9 +$64 $8D $24 $01 $A9 $62 $8D $25 +$01 $A9 $26 $7D $00 $01 $FD $01 +$01 $85 $F1 $A4 $F1 $A9 $E5 $8D +$28 $01 $A9 $E9 $8D $29 $01 $A9 +$34 $79 $00 $01 $F9 $01 $01 $85 +$F2 $A6 $F2 $A9 $20 $85 $70 $A9 +$01 $85 $71 $A9 $24 $85 $72 $A9 +$01 $85 $73 $61 $41 $E1 $3F $85 +$F3 $A4 $F3 $A9 $DA $85 $80 $A9 +$00 $85 $81 $A9 $DC $85 $82 $A9 +$00 $85 $83 $A9 $AA $71 $80 $F1 +$82 $85 $30 $A5 $30 $CD $06 $02 +$F0 $08 $A9 $06 $8D $10 $02 $4C +$C0 $45 $A9 $00 $85 $34 $A9 $FF +$8D $30 $01 $A9 $99 $8D $9D $01 +$A9 $DB $8D $99 $01 $A9 $2F $85 +$32 $A9 $32 $85 $4F $A9 $30 $85 +$33 $A9 $70 $85 $AF $A9 $18 $85 +$30 $C9 $18 $F0 $02 $29 $00 $09 +$01 $C5 $30 $D0 $02 $29 $00 $A2 +$00 $CD $30 $01 $F0 $04 $85 $40 +$A6 $40 $D5 $27 $D0 $06 $09 $84 +$85 $41 $A6 $41 $29 $DB $DD $00 +$01 $F0 $02 $29 $00 $85 $42 $A4 +$42 $29 $00 $D9 $00 $01 $D0 $02 +$09 $0F $85 $43 $A6 $43 $09 $24 +$C1 $40 $F0 $02 $09 $7F $85 $44 +$A4 $44 $49 $0F $D1 $33 $D0 $04 +$A5 $44 $85 $15 $A5 $15 $CD $07 +$02 $F0 $08 $A9 $07 $8D $10 $02 +$4C $C0 $45 $A9 $A5 $85 $20 $8D +$20 $01 $A9 $5A $85 $21 $A2 $A5 +$E0 $A5 $F0 $02 $A2 $01 $E4 $20 +$F0 $02 $A2 $02 $EC $20 $01 $F0 +$02 $A2 $03 $86 $30 $A4 $30 $C0 +$A5 $F0 $02 $A0 $04 $C4 $20 $F0 +$02 $A0 $05 $CC $20 $01 $F0 $02 +$A0 $06 $84 $31 $A5 $31 $24 $20 +$D0 $02 $A9 $07 $2C $20 $01 $D0 +$02 $A9 $08 $24 $21 $D0 $02 $85 +$42 $A5 $42 $CD $08 $02 $F0 $08 +$A9 $08 $8D $10 $02 $4C $C0 $45 +$A9 $54 $85 $32 $A9 $B3 $85 $A1 +$A9 $87 $85 $43 $A2 $A1 $10 $02 +$A2 $32 $B4 $00 $10 $04 $A9 $05 +$A6 $A1 $30 $02 $E9 $03 $30 $02 +$A9 $41 $49 $30 $85 $32 $75 $00 +$50 $02 $A9 $03 $85 $54 $B6 $00 +$75 $51 $50 $02 $A9 $E5 $75 $40 +$70 $05 $99 $01 $00 $65 $55 $70 +$02 $A9 $00 $69 $F0 $90 $04 $85 +$60 $65 $43 $90 $02 $A9 $FF $65 +$54 $B0 $04 $69 $87 $A6 $60 $B0 +$02 $A9 $00 $95 $73 $A5 $80 $CD +$09 $02 $F0 $08 $A9 $09 $8D $10 +$02 $4C $C0 $45 $69 $00 $A9 $99 +$69 $87 $18 $EA $90 $04 $69 $60 +$69 $93 $38 $EA $90 $01 $B8 $50 +$02 $A9 $00 $69 $AD $EA $85 $30 +$A5 $30 $CD $0A $02 $F0 $08 $A9 +$0A $8D $10 $02 $4C $C0 $45 $69 +$01 $A9 $27 $69 $01 $38 $08 $18 +$28 $69 $00 $48 $A9 $00 $68 $85 +$30 $A5 $30 $CD $0B $02 $F0 $08 +$A9 $0B $8D $10 $02 $4C $C0 $45 +$18 $A9 $42 $90 $04 $85 $33 $B0 +$0A $A9 $45 $48 $A9 $61 $48 $38 +$08 $18 $40 $A5 $33 $CD $0C $02 +$F0 $08 $A9 $0C $8D $10 $02 $4C +$C0 $45 $69 $01 $78 $F8 $08 $68 +$85 $20 $58 $D8 $08 $68 $65 $20 +$85 $21 $A5 $21 $CD $0D $02 $F0 +$08 $A9 $0D $8D $10 $02 $4C $C0 +$45 $A9 $41 $85 $60 $E6 $60 $A5 +$60 $CD $0E $02 $F0 $08 $A9 $0E +$8D $10 $02 $4C $C0 $45 $A9 $FE +$CD $10 $02 $D0 $03 $EE $10 $02 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 diff --git a/testbcd.dat b/testbcd.dat new file mode 100644 index 0000000..8675722 --- /dev/null +++ b/testbcd.dat @@ -0,0 +1,69 @@ +; Program disassembly from $0400 to $0600 2/9/2016 +; Test BCD mode. +ORG +$0400 +$A0 $01 $8C $04 $03 $A9 $00 $8D +$07 $03 $8D $0A $03 $AD $0A $03 +$29 $0F $8D $0B $03 $AD $0A $03 +$29 $F0 $8D $0F $03 $09 $0F $8D +$10 $03 $AD $07 $03 $29 $0F $8D +$09 $03 $AD $07 $03 $29 $F0 $8D +$08 $03 $20 $5E $04 $20 $47 $05 +$20 $18 $05 $D0 $1D $20 $B1 $04 +$20 $54 $05 $20 $18 $05 $D0 $12 +$EE $07 $03 $D0 $D5 $EE $0A $03 +$D0 $BB $88 $10 $B8 $A9 $00 $8D +$04 $03 $00 $00 $00 $00 $F8 $C0 +$01 $AD $07 $03 $6D $0A $03 $8D +$02 $03 $08 $68 $8D $03 $03 $D8 +$C0 $01 $AD $07 $03 $6D $0A $03 +$8D $05 $03 $08 $68 $8D $06 $03 +$C0 $01 $AD $09 $03 $6D $0B $03 +$C9 $0A $A2 $00 $90 $06 $E8 $69 +$05 $29 $0F $38 $0D $08 $03 $7D +$0F $03 $08 $B0 $04 $C9 $A0 $90 +$03 $69 $5F $38 $8D $00 $03 $08 +$68 $8D $01 $03 $68 $8D $0D $03 +$60 $F8 $C0 $01 $AD $07 $03 $ED +$0A $03 $8D $02 $03 $08 $68 $8D +$03 $03 $D8 $C0 $01 $AD $07 $03 +$ED $0A $03 $8D $05 $03 $08 $68 +$8D $06 $03 $60 $C0 $01 $AD $09 +$03 $ED $0B $03 $A2 $00 $B0 $06 +$E8 $E9 $05 $29 $0F $18 $0D $08 +$03 $FD $0F $03 $B0 $02 $E9 $5F +$8D $00 $03 $60 $C0 $01 $AD $09 +$03 $ED $0B $03 $A2 $00 $B0 $04 +$E8 $29 $0F $18 $0D $08 $03 $FD +$0F $03 $B0 $02 $E9 $5F $E0 $00 +$F0 $02 $E9 $06 $8D $00 $03 $60 +$AD $02 $03 $CD $00 $03 $D0 $26 +$AD $03 $03 $4D $0C $03 $29 $80 +$D0 $1C $AD $03 $03 $4D $0D $03 +$29 $40 $D0 $12 $AD $03 $03 $4D +$0E $03 $29 $02 $D0 $08 $AD $03 +$03 $4D $01 $03 $29 $01 $60 $AD +$0D $03 $8D $0C $03 $AD $06 $03 +$8D $0E $03 $60 $20 $D4 $04 $AD +$06 $03 $8D $0C $03 $8D $0D $03 +$8D $0E $03 $8D $01 $03 $60 $AD +$00 $03 $08 $68 $8D $0C $03 $8D +$0E $03 $60 $20 $F4 $04 $AD $00 +$03 $08 $68 $8D $0C $03 $8D $0E +$03 $AD $06 $03 $8D $0D $03 $8D +$01 $03 $60 $AD $00 $03 $08 $68 +$8D $0C $03 $8D $0E $03 $60 $20 +$D4 $04 $AD $00 $03 $08 $68 $8D +$0C $03 $8D $0E $03 $AD $06 $03 +$8D $0D $03 $8D $01 $03 $60 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 + diff --git a/tinybasic.asm b/tinybasic.asm new file mode 100644 index 0000000..5f9c5e8 --- /dev/null +++ b/tinybasic.asm @@ -0,0 +1,1496 @@ +;---------------------------------------------------------------------------------- +; Tiny Basic port to MKHBC-8-R1 6502 computer +; by Marek Karcz 2012. +; +; Based on the work by: +; +; ================= ORIGINAL HEADER ====================================== +; +; OMS-02 Firmware +; +; Bill O'Neill - Last update: 2008/12/06 +; +; Tiny Basic and minimal Monitor (BIOS) +; +; This version is for the assembler that comes with +; Michal Kowalski's 6502 emulator. To run it, load it +; into the emulator, assemle it, begin debug mode then, +; set the PC to $1CF0 then run. +; +; The emulator was used to help figure out Tiny Basic +; and this code is only documented to that extent. +; +; It should be easy enough to configure this to run any- +; where in memory or convert it to assemble with any 6502 +; assembler. The current location is being used as this is +; to be placed into a controller I designed (OMS-02) around +; a 6507 CPU (a 6502 variant) that has only 8k off memory. +; The memory map for the OMS-02 is; +; +; $0000-$0FFF RAM +; $1000-$13FF I/O space (ACIA is at $1200) +; $1400-$1FFF Tiny Basic and simple monitor +; +; +; Next steps: +; Write a BREAK routine that will enable a break if any charater is typed at the console +; More comments to document this code +; Investigate using assembler variables and directives to make it easy to re-locate this code +; +; ============ END OF ORIGINAL HEADER ========================================= +; +; Revision history (MKHBC-8-R1 port): +; +; 1/4/2012: +; TB port created. +; +; 1/5/2012: +; Version 1.0.1. +; Added 2-byte peek routine. +; +; 1/11/2012: +; Figured out jump addresses for routines and used symbolic labels +; instead of hardcoded addresses. Relocated TB to $0400 (from $1400). +; +; 2/15/2016 +; Ported to my own 6502 emulator. +; +; +;-------------------------------------------------------------------------------------- + +;.segment "BASIC" + +; +; Tiny Basic starts here +; + .org $0400 ; 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 +C_24 = $24 ; +C_2A = $2A ; I may just fix this someday - HA! +C_2C = $2C ; +C_2E = $2E ; +C_B8 = $B8 ; +C_BC = $BC ; +C_C1 = $C1 ; +C_C2 = $C2 ; +C_C6 = $C6 ; + +SR_V_L = SOBAS + $1E ; Base address for subroutine vector lo byte +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. +BREAK: nop ; Begin dummy break routine + clc ; Clear the carry flag + rts ; End break routine +; +; Some codes +; +BSC: .byte $5f ; Backspace code +LSC: .byte $18 ; Line cancel code +PCC: .byte $80 ; Pad character control +TMC: .byte $00 ; Tape mode control +;SSS: .byte $04 ; Spare Stack size. (was $04 but documentation suggests $20 ?) +SSS: .byte $20 ; Spare Stack size. (was $04 but documentation suggests $20 ?) + +; +; Code fragment +; Seems to store or retreive a byte to/from memory and the accumulator +; --- +; M.K.: I think this code it is used from Tiny Basic programs by calling USR to store/retrieve +; data (since TB does not support arrays, this is one way to emulate them). +; This location is at exactly 20 bytes distance from the start of TB code. +; TB programs typically make USR calls to locations: +; SOBAS+20 +; SOBAS+24 +; E.g: +; LET S=_SOBAS_ REM (_SOBAS_ is TB start) +; LET B=_ARRAY_SIZE_+2 (_ARRAY_SIZE_ is the size of 2-dim array) +; Get byte from location V(I,J), store in Z. +; LET Z=USR(S+20,V+B*I+J,0) +; Store Z in location V(I,J) +; LET Z=USR(S+24,V+B*I+J,Z) +; From TB documentation: +; For your convenience two subroutines have been included in the TINY BASIC interpreter to access memory. +; If S contains the address of the beginning of the TINY BASIC interpreter (256 for standard 6800, 512 +; for standard 6502, etc.), then location S+20 (hex 0114) is the entry point of a subroutine to read one +; byte from the memory address in the index register, and location S+24 (hex 0118) is the entry point of +; a subroutine to store one byte into memory. +;------------------------------------------------- +; USR (address) +; USR (address,Xreg) +; USR (address,Xreg,Areg) +; +; This function is actually a machine-language subroutine call to the address in the first argument. +; If the second argument is included the index registers contain that value on entry to the subroutine, +; with the most significant part in X. If the third argument is included, the accumulators contain that +; value on entry to the subroutine, with the least significant part in A. On exit, the value in the +; Accumulators (for the 6800; A and Y for the 6502) becomes the value of the function, with the least +; significant part in A. All three arguments are evaluated as normal expressions. +; It should be noted that machine language subroutine addresses are 16-bit Binary numbers. TINY BASIC +; evaluates all expressions to 16-bit binary numbers, so any valid expression may be used to define a +; subroutine address. However, most addresses are expressed in hexadecimal whereas TINY BASIC only accepts +; numerical constants in decimal. Thus to jump to a subroutine at hex address 40AF, you must code USR(16559). +; Hex address FFB5 is similarly 65461 in decimal, though the equivalent (-75) may be easier to use. +; + +OneBytePeek: + + stx $C3 ; read one byte from memory address in the index register + bcc LBL008 + +OneBytePoke: + + stx $C3 ; store one byte into memory + sta (C_C2),Y + rts +LBL008: lda (C_C2),Y + ldy #$00 + rts + + + ; These seem to be addresses associated with the IL branch instructions + ;.byte $62,$15, $64,$15, $D8,$15, $05,$16, $33,$16, $FD,$15 + .byte JUMP01 + .byte LBL027 + .byte JUMP02 + .byte JUMP03 + .byte JUMP04 + .byte JUMP05 + + ; NOTE: The original comment below is obsolete now, since I found the jump + ; addresses locations and put them in this table. Now assembler will + ; take care of the reallocation. + ; START OF ORIGINAL COMMENT + ; This appears to be a table of + ; execution addresses to jump to ML routines + ; that handle the the IL instructions. + ; You cannot add code to or relocate this program + ; without updating these + ; END OF ORIGINAL COMMENT + + ;.byte $9F,$17, $42,$1B, $3F,$1B, $7A,$17, $FC,$18, $95,$17, $9F,$17, $9F,$17 + .byte LBL067 + .byte LBL132 + .byte JUMP06 + .byte JUMP07 + .byte LBL035 + .byte LBL097 + .byte LBL067 + .byte LBL067 + + ;.byte $BD,$1A, $C1,$1A, $8A,$1A, $9B,$1A, $E9,$1A, $61,$17, $51,$17, $41,$1A + .byte JUMP08 + .byte JUMP09 + .byte JUMP10 + .byte JUMP11 + .byte JUMP12 + .byte JUMP13 + .byte JUMP14 + .byte LBL071 + + ;.byte $52,$1A, $4F,$1A, $62,$1A, $E7,$19, $CD,$16, $06,$17, $9F,$17, $15,$18 + .byte JUMP15 + .byte JUMP16 + .byte JUMP17 + .byte JUMP18 + .byte JUMP19 + .byte JUMP20 + .byte LBL067 + .byte JUMP21 + + ;.byte $A7,$17, $B7,$16, $BF,$16, $83,$18, $A1,$16, $9F,$17, $9F,$17, $A8,$18 + .byte JUMP22 + .byte JUMP23 + .byte LBL047 + .byte LBL081 + .byte LBL013 + .byte LBL067 + .byte LBL067 + .byte JUMP24 + + ;.byte $4F,$1B, $4D,$1B, $07,$19, $AA,$14, $37,$17, $BD,$14, $1B,$1B, $B1,$1A + .byte JUMP25 + .byte JUMP26 + .byte JUMP27 + .byte MEM_T2 + .byte JUMP28 + .byte WARM_S + .byte JUMP29 + .byte JUMP30 + + .byte $20,$41, $54,$20 ; No idea ???? + + .byte $80 ; No idea + +LBL002: .byte ILTBL ;$1B ; $1B - hi byte of IL address + +; +;Begin Cold Start +; +; +; +; Load start of free ram ($1C00) into locations $0020 and $0022 +; The memory between $1000 and $1Bff (3 kB) is free for assembler programs +; that can be loaded and used simultaneously with TB. +; $1C00 to the end of writable memory is the BASIC memory space. +; +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 + 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. +; +; +; Begin test for free ram +; As a result, memory locations $20,$21 will contain lo,hi byte order address of lower RAM boundary +; and $22,$23 upper RAM boundary respectively. +; + + ldy #$01 ; Load register Y with $01 +MEM_T: lda (C_22),Y ; Load accumulator With the contents of a byte of memory + tax ; Save it to X + eor #$FF ; Next 4 instuctions test to see if this memeory location + sta (C_22),Y ; is ram by trying to write something new to it - new value + cmp (C_22),Y ; gets created by XORing the old value with $FF - store the + php ; result of the test on the stack to look at later + txa ; Retrieve the old memory value + sta (C_22),Y ; Put it back where it came from + inc $22 ; Increment $22 (for next memory location) + bne SKP_PI ; Goto $14A6 if we don't need to increment page + inc $23 ; Increment $23 (for next memory page) +SKP_PI: plp ; Now look at the result of the memory test + beq MEM_T ; Go test the next mempry location if the last one was ram + dey ; If last memory location did not test as ram, decrement Y (should be $00 now) +MEM_T2: cld ; Make sure we're not in decimal mode + lda $20 ; Load up the low-order by of the start of free ram + adc SSS ; Add to the spare stack size + sta $24 ; Store the result in $0024 + tya ; Retrieve Y + adc $21 ; And add it to the high order byte of the start of free ram (this does not look right) + sta $25 ; Store the result in $0025 + tya ; Retrieve Y again + sta (C_20),Y ; Store A in the first byte of program memory + iny ; Increment Y + sta (C_20),Y ; Store A in the second byte of program memory +; +;Begin Warm Start; +; +WARM_S: lda $22 + sta $C6 + sta $26 + lda $23 + sta $C7 + sta $27 + jsr LBL001 ; Go print CR, LF and pad charachters +LBL014: lda LBL002 + sta $2A + lda LBL003 + sta $2B + lda #$80 + sta $C1 + lda #$30 + sta $C0 + ldx #$00 + stx $BE + stx $C2 + dex + txs +LBL006: cld + jsr LBL004 ; Go read a byte from the TBIL table + jsr LBL005 + jmp LBL006 +; +; +; + .byte $83 ; No idea about this + .byte $65 ; No idea about this +; +; +; Routine to service the TBIL Instructions +; +LBL005: cmp #$30 ; + bcs LBL011 ; If it's $30 or higher, it's a Branch or Jump - go handle it + cmp #$08 ; + bcc LBL007 ; If it's less than $08 it's a stack exchange - go handle it + asl ; Multiply the OP code by 2 + tax ; Transfer it to X +LBL022: lda SR_V_H,X ; Get the hi byte of the OP Code handling routine + pha ; and save it on the stack + lda SR_V_L,X ; Get the lo byte + pha ; and save it on the stack + php ; save the processor status too + rti ; now go execute the OP Code handling routine +; +; +; Routine to handle the stack exchange +; +LBL007: adc $C1 + tax + lda (C_C1),Y + pha + lda $00,X + sta (C_C1),Y + pla + sta $00,X + rts +; +; +; +LBL015: jsr LBL001 ; Go print CR, LF and pad charachters + lda #$21 ; Load an ASCII DC2 + jsr OUT_V ; Go print it + lda $2A ; Load the current TBIL pointer (lo) + sec ; Set the carry flag + sbc LBL002 ; Subtract the TBIL table origin (lo) + tax ; Move the difference to X + lda $2B ; Load the current TBIL pointer (hi) + sbc LBL003 ; Subtract the TBIL table origin (hi) + jsr LBL010 + lda $BE + beq LBL012 + lda #$7E + sta $2A + lda #$20 + sta $2B + jsr LBL013 + ldx $28 + lda $29 + jsr LBL010 +LBL012: lda #$07 ; ASCII Bell + jsr OUT_V ; Go ring Bell + jsr LBL001 ; Go print CR, LF and pad charachters +LBL060: lda $26 + sta $C6 + lda $27 + sta $C7 + jmp LBL014 +; +; +; +LBL115: ldx #$7C +LBL048: cpx $C1 +LBL019: bcc LBL015 + ldx $C1 + inc $C1 + inc $C1 + clc + rts +; +; +; +JUMP01: dec $BD +LBL027: lda $BD + beq LBL015 +LBL017: lda $BC + sta $2A + lda $BD + sta $2B + rts +; +; Branch handling routine +; +LBL011: cmp #$40 ; + bcs LBL016 ; If it's not a Jump, go to branch handler + pha + jsr LBL004 ; Go read a byte from the TBIL table + adc LBL002 + sta $BC + pla + pha + and #$07 + adc LBL003 + sta $BD + pla + and #$08 + bne LBL017 + lda $BC + ldx $2A + sta $2A + stx $BC + lda $BD + ldx $2B + sta $2B + stx $BD +LBL126: lda $C6 + sbc #$01 + sta $C6 + bcs LBL018 + dec $C7 +LBL018: cmp $24 + lda $C7 + sbc $25 + bcc LBL019 + lda $BC + sta (C_C6),Y + iny + lda $BD + sta (C_C6),Y + rts +; +;Branch handler +; +LBL016: pha + lsr + lsr + lsr + lsr + and #$0E + tax + pla + cmp #$60 + and #$1F + bcs LBL020 + ora #$E0 +LBL020: clc + beq LBL021 + adc $2A + sta $BC + tya + adc $2B +LBL021: sta $BD + JMP LBL022 +; +; +; +JUMP02: lda $2C + sta $B8 + lda $2D + sta $B9 +LBL025: jsr LBL023 + jsr LBL024 + eor (C_2A),Y + tax + jsr LBL004 ; Go read a byte from the TBIL table + txa + beq LBL025 + asl + beq LBL026 + lda $B8 + sta $2C + lda $B9 + sta $2D +LBL028: jmp LBL027 +JUMP05: jsr LBL023 + cmp #$0D + bne LBL028 +LBL026: rts +; +; +; +JUMP03: jsr LBL023 + cmp #$5B + bcs LBL028 + cmp #$41 + bcc LBL028 + asl + jsr LBL029 +LBL024: ldy #$00 + lda (C_2C),Y + inc $2C + bne LBL030 + inc $2D +LBL030: cmp #$0D + clc + rts +; +; + +LBL031: jsr LBL024 +LBL023: lda (C_2C),Y + cmp #$20 + beq LBL031 + cmp #$3A + clc + bpl LBL032 + cmp #$30 +LBL032: rts +; +; +; +JUMP04: jsr LBL023 + bcc LBL028 + sty $BC + sty $BD +LBL033: lda $BC + ldx $BD + asl $BC + rol $BD + asl $BC + rol $BD + clc + adc $BC + sta $BC + txa + adc $BD + asl $BC + rol + sta $BD + jsr LBL024 + and #$0F + adc $BC + sta $BC + tya + adc $BD + sta $BD + jsr LBL023 + bcs LBL033 + jmp LBL034 +LBL061: jsr LBL035 + lda $BC + ora $BD + beq LBL036 +LBL065: lda $20 + sta $2C + lda $21 + sta $2D +LBL040: jsr LBL037 + beq LBL038 + lda $28 + cmp $BC + lda $29 + sbc $BD + bcs LBL038 +LBL039: jsr LBL024 + bne LBL039 + jmp LBL040 +LBL038: lda $28 + eor $BC + bne LBL041 + lda $29 + eor $BD +LBL041: rts +; +; +; +LBL043: jsr LBL042 +LBL013: jsr LBL004 ; Entry point for TBIL PC (print literal) - Go read a byte from the TBIL table + bpl LBL043 ; +LBL042: inc $BF + bmi LBL044 + jmp OUT_V ; Go print it +LBL044: dec $BF +LBL045: rts +; +; +; +LBL046: cmp #$22 + beq LBL045 + jsr LBL042 +JUMP23: jsr LBL024 + bne LBL046 +LBL036: jmp LBL015 +LBL047: lda #$20 + jsr LBL042 + lda $BF + and #$87 + bmi LBL045 + bne LBL047 + rts +; +; +; +JUMP19: ldx #$7B + jsr LBL048 + inc $C1 + inc $C1 + inc $C1 + sec + lda $03,X + sbc $00,X + sta $00,X + lda $04,X + sbc $01,X + bvc LBL052 + eor #$80 + ora #$01 +LBL052: bmi LBL053 + bne LBL054 + ora $00,X + beq LBL049 +LBL054: lsr $02,X +LBL049: lsr $02,X +LBL053: lsr $02,X + bcc LBL050 +LBL004: ldy #$00 ; Read a byte from the TBIL Table + lda (C_2A),Y ; + inc $2A ; Increment TBIL Table pointer as required + bne LBL051 ; + inc $2B ; +LBL051: ora #$00 ; Check for $00 and set the 'Z' flag acordingly +LBL050: rts ; Return +; +; +; +JUMP20: lda $BE + beq LBL055 +LBL056: jsr LBL024 + bne LBL056 + jsr LBL037 + beq LBL057 +LBL062: jsr LBL058 + jsr BREAK + bcs LBL059 + lda $C4 + sta $2A + lda $C5 + sta $2B + rts +; +; +; +LBL059: lda LBL002 + sta $2A + lda LBL003 + sta $2B +LBL057: jmp LBL015 +LBL055: sta $BF + jmp LBL060 +JUMP28: lda $20 + sta $2C + lda $21 + sta $2D + jsr LBL037 + beq LBL057 + lda $2A + sta $C4 + lda $2B + sta $C5 +LBL058: lda #$01 + sta $BE + rts +; +; +; +JUMP14: jsr LBL061 + beq LBL062 +LBL066: lda $BC + sta $28 + lda $BD + sta $29 + jmp LBL015 +JUMP13: jsr LBL063 + jsr LBL064 + jsr LBL065 + bne LBL066 + rts +; +; +; +LBL037: jsr LBL024 + sta $28 + jsr LBL024 + sta $29 + ora $28 + rts +; +; +; +JUMP07: jsr LBL035 + jsr LBL034 +LBL034: lda $BD +LBL131: jsr LBL029 + lda $BC +LBL029: ldx $C1 + dex + sta $00,X + stx $C1 + cpx $C0 + bne LBL067 +LBL068: jmp LBL015 +LBL097: ldx $C1 + cpx #$80 + bpl LBL068 + lda $00,X + inc $C1 +LBL067: rts +; +; +; +LBL010: sta $BD + stx $BC + jmp LBL069 +JUMP22: ldx $C1 ; entry point to TBIL PN (print number) + lda $01,X + bpl LBL070 + jsr LBL071 + lda #$2D + jsr LBL042 +LBL070: jsr LBL035 +LBL069: lda #$1F + sta $B8 + sta $BA + lda #$2A + sta $B9 + sta $BB + ldx $BC + ldy $BD + sec +LBL072: inc $B8 + txa + sbc #$10 + tax + tya + sbc #$27 + tay + bcs LBL072 +LBL073: dec $B9 + txa + adc #$E8 + tax + tya + adc #$03 + tay + bcc LBL073 + txa +LBL074: sec + inc $BA + sbc #$64 + bcs LBL074 + dey + bpl LBL074 +LBL075: dec $BB + adc #$0A + bcc LBL075 + ora #$30 + sta $BC + lda #$20 + sta $BD + ldx #$FB +LBL199: stx $C3 + lda $BD,X + ora $BD + cmp #$20 + beq LBL076 + ldy #$30 + sty $BD + ora $BD + jsr LBL042 +LBL076: ldx $C3 + inx + bne LBL199 + rts +; +; +; +JUMP21: lda $2D + pha + lda $2C + pha + lda $20 + sta $2C + lda $21 + sta $2D + lda $24 + ldx $25 + jsr LBL077 + beq LBL078 + jsr LBL077 +LBL078: lda $2C + sec + sbc $B6 + lda $2D + sbc $B7 + bcs LBL079 + jsr LBL037 + beq LBL079 + ldx $28 + lda $29 + jsr LBL010 + lda #$20 +LBL080: jsr LBL042 + jsr BREAK + bcs LBL079 + jsr LBL024 + bne LBL080 + jsr LBL081 + jmp LBL078 +LBL077: sta $B6 + inc $B6 + bne LBL082 + inx +LBL082: stx $B7 + ldy $C1 + cpy #$80 + beq LBL083 + jsr LBL061 +LBL099: lda $2C + ldx $2D + sec + sbc #$02 + bcs LBL084 + dex +LBL084: sta $2C + jmp LBL085 +LBL079: pla + sta $2C + pla + sta $2D +LBL083: rts +LBL081: lda $BF + bmi LBL083 +; +; +; Routine to handle CR, LF and pad characters in the ouput +; +LBL001: lda #$0D ; Load up a CR + jsr OUT_V ; Go print it + lda PCC ; Load the pad character code + and #$7F ; Test to see - + sta $BF ; how many pad charachters to print + beq LBL086 ; Skip if 0 +LBL088: jsr LBL087 ; Go print pad charcter + dec $BF ; One less + bne LBL088 ; Loop until 0 +LBL086: lda #$0A ; Load up a LF + jmp LBL089 ; Go print it +; +; +; +LBL092: ldy TMC +LBL091: sty $BF + bcs LBL090 +JUMP24: lda #$30 ; Entry pont for TBIL GL (get input line) + sta $2C + sta $C0 + sty $2D + jsr LBL034 +LBL090: eor $80 + sta $80 + jsr IN_V + ldy #$00 + ldx $C0 + and #$7F + beq LBL090 + cmp #$7F + beq LBL090 + cmp #$13 + beq LBL091 + cmp #$0A + beq LBL092 + cmp LSC + beq LBL093 + cmp BSC + bne LBL094 + cpx #$30 + bne LBL095 +LBL093: ldx $2C + sty $BF + lda #$0D +LBL094: cpx $C1 + bmi LBL096 + lda #$07 + jsr LBL042 + jmp LBL090 +LBL096: sta $00,X + inx + inx +LBL095: dex + stx $C0 + cmp #$0D + bne LBL090 + jsr LBL081 +LBL035: jsr LBL097 + sta $BC + jsr LBL097 + sta $BD + rts +; +; +; +JUMP27: jsr LBL098 + jsr LBL061 + php + jsr LBL099 + sta $B8 + stx $B9 + lda $BC + sta $B6 + lda $BD + sta $B7 + ldx #$00 + plp + bne LBL100 + jsr LBL037 + dex + dex +LBL101: dex + jsr LBL024 + bne LBL101 +LBL100: sty $28 + sty $29 + jsr LBL098 + lda #$0D + cmp (C_2C),Y + beq LBL102 + inx + inx + inx +LBL103: inx + iny + cmp (C_2C),Y + bne LBL103 + lda $B6 + sta $28 + lda $B7 + sta $29 +LBL102: lda $B8 + sta $BC + lda $B9 + sta $BD + clc + ldy #$00 + txa + beq LBL104 + bpl LBL105 + adc $2E + sta $B8 + lda $2F + sbc #$00 + sta $B9 +LBL109: lda (C_2E),Y + sta (C_B8),Y + ldx $2E + cpx $24 + bne LBL106 + lda $2F + cmp $25 + beq LBL107 +LBL106: inx + stx $2E + bne LBL108 + inc $2F +LBL108: inc $B8 + bne LBL109 + inc $B9 + bne LBL109 +LBL105: adc $24 + sta $B8 + sta $2E + tya + adc $25 + sta $B9 + sta $2F + lda $2E + sbc $C6 + lda $2F + sbc $C7 + bcc LBL110 + dec $2A + jmp LBL015 +LBL110: lda (C_24),Y + sta (C_2E),Y + ldx $24 + bne LBL111 + dec $25 +LBL111: dec $24 + ldx $2E + bne LBL112 + dec $2F +LBL112: dex + stx $2E + cpx $BC + bne LBL110 + ldx $2F + cpx $BD + bne LBL110 +LBL107: lda $B8 + sta $24 + lda $B9 + sta $25 +LBL104: lda $28 + ora $29 + beq LBL113 + lda $28 + sta (C_BC),Y + iny + lda $29 + sta (C_BC),Y +LBL114: iny + sty $B6 + jsr LBL024 + php + ldy $B6 + sta (C_BC),Y + plp + bne LBL114 +LBL113: jmp LBL014 +JUMP18: jsr LBL115 + lda $03,X + and #$80 + beq LBL116 + lda #$FF +LBL116: sta $BC + sta $BD + pha + adc $02,X + sta $02,X + pla + pha + adc $03,X + sta $03,X + pla + eor $01,X + sta $BB + bpl LBL117 + jsr LBL118 +LBL117: ldy #$11 + lda $00,X + ora $01,X + bne LBL119 + jmp LBL015 +LBL119: sec + lda $BC + sbc $00,X + pha + lda $BD + sbc $01,X + pha + eor $BD + bmi LBL120 + pla + sta $BD + pla + sta $BC + sec + jmp LBL121 +LBL120: pla + pla + clc +LBL121: rol $02,X + rol $03,X + rol $BC + rol $BD + dey + bne LBL119 + lda $BB + bpl LBL122 +LBL071: ldx $C1 +LBL118: sec + tya + sbc $00,X + sta $00,X + tya + sbc $01,X + sta $01,X +LBL122: rts +; +; +; +JUMP16: jsr LBL071 +JUMP15: jsr LBL115 + lda $00,X + adc $02,X + sta $02,X + lda $01,X + adc $03,X + sta $03,X + rts +; +; +; +JUMP17: jsr LBL115 + ldy #$10 + lda $02,X + sta $BC + lda $03,X + sta $BD +LBL124: asl $02,X + rol $03,X + rol $BC + rol $BD + bcc LBL123 + clc + lda $02,X + adc $00,X + sta $02,X + lda $03,X + adc $01,X + sta $03,X +LBL123: dey + bne LBL124 + rts +; +; +; +JUMP10: jsr LBL097 + tax + lda $00,X + ldy $01,X + dec $C1 + ldx $C1 + sty $00,X + jmp LBL029 +JUMP11: ldx #$7D + jsr LBL048 + lda $01,X + pha + lda $00,X + pha + jsr LBL097 + tax + pla + sta $00,X + pla + sta $01,X + rts +JUMP30: jsr LBL063 + lda $BC + sta $2A + lda $BD + sta $2B + rts +; +; +; +JUMP08: ldx #$2C ; Entry point to Save Basic Pointer SB + bne LBL125 +JUMP09: ldx #$2E +LBL125: lda $00,X + cmp #$80 + bcs LBL098 + lda $01,X + bne LBL098 + lda $2C + sta $2E + lda $2D + sta $2F + rts +; +; +; +LBL098: lda $2C + ldy $2E + sty $2C + sta $2E + lda $2D + ldy $2F + sty $2D + sta $2F + ldy #$00 + rts +; +; +; +JUMP12: lda $28 + sta $BC + lda $29 + sta $BD + jsr LBL126 + lda $C6 + sta $26 + lda $C7 +LBL064: sta $27 +LBL129: rts +; +; +; +LBL063: lda (C_C6),Y + sta $BC + jsr LBL127 + lda (C_C6),Y + sta $BD +LBL127: inc $C6 + bne LBL128 + inc $C7 +LBL128: lda $22 + cmp $C6 + lda $23 + sbc $C7 + bcs LBL129 + jmp LBL015 +JUMP29: jsr LBL130 + sta $BC + tya + jmp LBL131 +LBL130: jsr LBL035 + lda $BC + sta $B6 + jsr LBL035 + lda $BD + sta $B7 + ldy $BC + jsr LBL035 + ldx $B7 + lda $B6 + clc + jmp (C_00BC) +JUMP06: jsr LBL132 +LBL132: jsr LBL004 ; Go read a byte from the TBIL Table + jmp LBL029 +LBL085: stx $2D + cpx #$00 + rts +; +; +; +JUMP26: ldy #$02 +JUMP25: sty $BC + ldy #$29 + sty $BD + ldy #$00 + lda (C_BC),Y + cmp #$08 + bne LBL133 + jmp LBL117 +LBL133: rts +; +; +; Subroutine to decide which pad character to print +; +LBL089: jsr OUT_V ; Entry point with a charater to print first +LBL087: lda #$FF ; Normal entry point - Set pad to $FF + bit PCC ; Check if the pad flag is on + bmi LBL134 ; Skip it if not + lda #$00 ; set pad to $00 +LBL134: jmp OUT_V ; Go print it + + +; +; TBIL Tables +; +ILTBL: .byte $24, $3A, $91, $27, $10, $E1, $59, $C5, $2A, $56, $10, $11, $2C, $8B, $4C + .byte $45, $D4, $A0, $80, $BD, $30, $BC, $E0, $13, $1D, $94, $47, $CF, $88, $54 + .byte $CF, $30, $BC, $E0, $10, $11, $16, $80, $53, $55, $C2, $30, $BC, $E0, $14 + .byte $16, $90, $50, $D2, $83, $49, $4E, $D4, $E5, $71, $88, $BB, $E1, $1D, $8F + .byte $A2, $21, $58, $6F, $83, $AC, $22, $55, $83, $BA, $24, $93, $E0, $23, $1D + .byte $30, $BC, $20, $48, $91, $49, $C6, $30, $BC, $31, $34, $30, $BC, $84, $54 + .byte $48, $45, $CE, $1C, $1D, $38, $0D, $9A, $49, $4E, $50, $55, $D4, $A0, $10 + .byte $E7, $24, $3F, $20, $91, $27, $E1, $59, $81, $AC, $30, $BC, $13, $11, $82 + .byte $AC, $4D, $E0, $1D, $89, $52, $45, $54, $55, $52, $CE, $E0, $15, $1D, $85 + .byte $45, $4E, $C4, $E0, $2D, $98, $4C, $49, $53, $D4, $EC, $24, $00, $00, $00 + .byte $00, $0A, $80, $1F, $24, $93, $23, $1D, $30, $BC, $E1, $50, $80, $AC, $59 + .byte $85, $52, $55, $CE, $38, $0A, $86, $43, $4C, $45, $41, $D2, $2B, $84, $52 + .byte $45, $CD, $1D, $A0, $80, $BD, $38, $14, $85, $AD, $30, $D3, $17, $64, $81 + .byte $AB, $30, $D3, $85, $AB, $30, $D3, $18, $5A, $85, $AD, $30, $D3, $19, $54 + .byte $2F, $30, $E2, $85, $AA, $30, $E2, $1A, $5A, $85, $AF, $30, $E2, $1B, $54 + .byte $2F, $98, $52, $4E, $C4, $0A, $80, $80, $12, $0A, $09, $29, $1A, $0A, $1A + .byte $85, $18, $13, $09, $80, $12, $01, $0B, $31, $30, $61, $72, $0B, $04, $02 + .byte $03, $05, $03, $1B, $1A, $19, $0B, $09, $06, $0A, $00, $00, $1C, $17, $2F + .byte $8F, $55, $53, $D2, $80, $A8, $30, $BC, $31, $2A, $31, $2A, $80, $A9, $2E + .byte $2F, $A2, $12, $2F, $C1, $2F, $80, $A8, $30, $BC, $80, $A9, $2F, $83, $AC + .byte $38, $BC, $0B, $2F, $80, $A8, $52, $2F, $84, $BD, $09, $02, $2F, $8E, $BC + .byte $84, $BD, $09, $93, $2F, $84, $BE, $09, $05, $2F, $09, $91, $2F, $80, $BE + .byte $84, $BD, $09, $06, $2F, $84, $BC, $09, $95, $2F, $09, $04, $2F, $00, $00 + .byte $00 +; +; End of Tiny Basic + + +;.segment "MAIN" + + .org $0CF0 ; Address of main program + +; Code needs work below here, BIOS must be changed for MKHBC-8-R1 + +;FBLK: +; +; Set some symbols +; +;ACIARW = $1200 ; Base address of ACIA +;ACIAST = ACIARW+$01 ; ACIA status register +;ACIACM = ACIARW+$02 ; ACIA commnad register +;ACIACN = ACIARW+$03 ; ACIA control register + + +; +; Begin base system initialization +; +; jmp main ; no 6522 on MKHBC-8-R1 +;-------------------- +; sta ACIAST ; Do a soft reset on the ACIA +; lda #$0B ; Set it up for : +; sta ACIACM ; no echo, no parity, RTS low, No IRQ, DTR low +; lda #$1A ; and : +; 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 + 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 + 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 + jsr SNDMSG ; Go print the prompt + jmp ST_LP ; Go get the response + +;.segment "MESG" + + .org $0E00 ; Address of message area +MBLK: + +; +; The message block begins at $1E00 and is at most 256 bytes long. +; Messages terminate with an FF. +; + .byte "OMEGA MICRO SYSTEMS" + .byte $0D, $0A ;, $0A + .byte "MKHBC-8-R1 TINY BASIC 6502 PORT" + .byte $0D, $0A ;, $0A + .byte "Version: 1.0.2, 1/11/2012" + .byte $0D, $0A ;, $0A + .byte "(NOTE: Use caps for Basic)" + .byte $0D, $0A ;, $0A + .byte "Boot ([C]old/[W]arm)? " + .byte $07, $FF + +;.segment "SUBR" + + .org $0F00 ;address of subroutine area + +SBLK: +; +; Begin BIOS subroutines +; + +; M.O.S. API defines. + +StrPtr = $E0 + +; Kernel jump table + +GetCh = $FFED +PutCh = $FFF0 +Gets = $FFF3 +Puts = $FFF6 + + +;SNDCHR = PutCh +;RCCHR = GetCh + + +; +; Clear the screen +; +ESC = $1b + +; 2-BYTE PEEK USR FUNCTION +; For TINY BASIC IL ASSEMBLER VERSION 0 + +TwoBytePeek: + + STX $C3 ;($C2=00) + LDA ($C2),Y ;GET MSB + PHA ;SAVE IT + INY + LDA ($C2),Y ;GET LSB + TAX + PLA + TAY ;Y=MSB + TXA + RTS + + +;ClrScrCode: + +; .BYTE ESC,"[2J",0 ;clear screen sequence (ANSI). + +;ClearScreen: + +; lda #ClrScrCode +; sta StrPtr+1 +; jsr Puts +; rts + +CLRSC: ldx #$19 ; Load X - we're going tp print 25 lines + lda #$0D ; CR + jsr SNDCHR ; Send a carriage retuen + lda #$0A ; LF +CSLP: jsr SNDCHR ; Send the line feed + dex ; One less to do + bne CSLP ; Go send another untill we're done + rts ; Return + +; +; Print a message. +; This sub expects the messsage offset from MBLK in X. +; +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 + iny ; Increment the pointer + jmp SNDMSG ; Go get next character +EXSM: rts ; Return + +; +; Get a character from the ACIA +; 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 + +; +;Send a character to the ACIA +; +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 + cmp #$00 ; the terminal and discard them to + BEQ EXSC ; clean up the output + cmp #$91 ; + BEQ EXSC ; + cmp #$93 ; + 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) +EXSC: rts ; Return + +; .org $1FFC ; Address of reset vector (for 6507 not required for emulator) +;RSVEC +; .byte $F0, $1C ; Reset vector + +; .org $3000 ; Address of last byte of EPROM +;EOROM: + +;--------------------------- END ---------------------------------------------------------------------- diff --git a/tinybasic.dat b/tinybasic.dat new file mode 100644 index 0000000..fca0f34 --- /dev/null +++ b/tinybasic.dat @@ -0,0 +1,774 @@ +; Tiny Basic for MKBASIC 6502 emulator. +; Run address: $0CF0 +; I/O emulation: $E000 +ORG +$0400 +$4C $85 $04 $4C $BD $04 $4C $2C +$0F $4C $31 $0F $EA $18 $60 $5F +$18 $80 $00 $20 $86 $C3 $90 $05 +$86 $C3 $91 $C2 $60 $B1 $C2 $A0 +$00 $60 $62 $05 $64 $05 $D8 $05 +$05 $06 $33 $06 $FD $05 $9F $07 +$42 $0B $3F $0B $7A $07 $FC $08 +$95 $07 $9F $07 $9F $07 $BD $0A +$C1 $0A $8A $0A $9B $0A $E9 $0A +$61 $07 $51 $07 $41 $0A $52 $0A +$4F $0A $62 $0A $E7 $09 $CD $06 +$06 $07 $9F $07 $15 $08 $A7 $07 +$B7 $06 $BF $06 $83 $08 $A1 $06 +$9F $07 $9F $07 $A8 $08 $4F $0B +$4D $0B $07 $09 $AA $04 $37 $07 +$BD $04 $1B $0B $B1 $0A $20 $41 +$54 $20 $80 $70 $0B $A9 $00 $85 +$20 $85 $22 $A9 $1C $85 $21 $85 +$23 $A0 $01 $B1 $22 $AA $49 $FF +$91 $22 $D1 $22 $08 $8A $91 $22 +$E6 $22 $D0 $02 $E6 $23 $28 $F0 +$EA $88 $D8 $A5 $20 $6D $13 $04 +$85 $24 $98 $65 $21 $85 $25 $98 +$91 $20 $C8 $91 $20 $A5 $22 $85 +$C6 $85 $26 $A5 $23 $85 $C7 $85 +$27 $20 $87 $08 $AD $83 $04 $85 +$2A $AD $84 $04 $85 $2B $A9 $80 +$85 $C1 $A9 $30 $85 $C0 $A2 $00 +$86 $BE $86 $C2 $CA $9A $D8 $20 +$F9 $06 $20 $F2 $04 $4C $E6 $04 +$83 $65 $C9 $30 $B0 $7B $C9 $08 +$90 $0C $0A $AA $BD $1F $04 $48 +$BD $1E $04 $48 $08 $40 $65 $C1 +$AA $B1 $C1 $48 $B5 $00 $91 $C1 +$68 $95 $00 $60 $20 $87 $08 $A9 +$21 $20 $09 $04 $A5 $2A $38 $ED +$83 $04 $AA $A5 $2B $ED $84 $04 +$20 $A0 $07 $A5 $BE $F0 $12 $A9 +$7E $85 $2A $A9 $20 $85 $2B $20 +$A1 $06 $A6 $28 $A5 $29 $20 $A0 +$07 $A9 $07 $20 $09 $04 $20 $87 +$08 $A5 $26 $85 $C6 $A5 $27 $85 +$C7 $4C $CC $04 $A2 $7C $E4 $C1 +$90 $BA $A6 $C1 $E6 $C1 $E6 $C1 +$18 $60 $C6 $BD $A5 $BD $F0 $AC +$A5 $BC $85 $2A $A5 $BD $85 $2B +$60 $C9 $40 $B0 $43 $48 $20 $F9 +$06 $6D $83 $04 $85 $BC $68 $48 +$29 $07 $6D $84 $04 $85 $BD $68 +$29 $08 $D0 $DC $A5 $BC $A6 $2A +$85 $2A $86 $BC $A5 $BD $A6 $2B +$85 $2B $86 $BD $A5 $C6 $E9 $01 +$85 $C6 $B0 $02 $C6 $C7 $C5 $24 +$A5 $C7 $E5 $25 $90 $AA $A5 $BC +$91 $C6 $C8 $A5 $BD $91 $C6 $60 +$48 $4A $4A $4A $4A $29 $0E $AA +$68 $C9 $60 $29 $1F $B0 $02 $09 +$E0 $18 $F0 $07 $65 $2A $85 $BC +$98 $65 $2B $85 $BD $4C $FC $04 +$A5 $2C $85 $B8 $A5 $2D $85 $B9 +$20 $25 $06 $20 $14 $06 $51 $2A +$AA $20 $F9 $06 $8A $F0 $F1 $0A +$F0 $12 $A5 $B8 $85 $2C $A5 $B9 +$85 $2D $4C $64 $05 $20 $25 $06 +$C9 $0D $D0 $F6 $60 $20 $25 $06 +$C9 $5B $B0 $EE $C9 $41 $90 $EA +$0A $20 $87 $07 $A0 $00 $B1 $2C +$E6 $2C $D0 $02 $E6 $2D $C9 $0D +$18 $60 $20 $14 $06 $B1 $2C $C9 +$20 $F0 $F7 $C9 $3A $18 $10 $02 +$C9 $30 $60 $20 $25 $06 $90 $C2 +$84 $BC $84 $BD $A5 $BC $A6 $BD +$06 $BC $26 $BD $06 $BC $26 $BD +$18 $65 $BC $85 $BC $8A $65 $BD +$06 $BC $2A $85 $BD $20 $14 $06 +$29 $0F $65 $BC $85 $BC $98 $65 +$BD $85 $BD $20 $25 $06 $B0 $D4 +$4C $80 $07 $20 $FC $08 $A5 $BC +$05 $BD $F0 $48 $A5 $20 $85 $2C +$A5 $21 $85 $2D $20 $6D $07 $F0 +$12 $A5 $28 $C5 $BC $A5 $29 $E5 +$BD $B0 $08 $20 $14 $06 $D0 $FB +$4C $7C $06 $A5 $28 $45 $BC $D0 +$04 $A5 $29 $45 $BD $60 $20 $A6 +$06 $20 $F9 $06 $10 $F8 $E6 $BF +$30 $03 $4C $09 $04 $C6 $BF $60 +$C9 $22 $F0 $FB $20 $A6 $06 $20 +$14 $06 $D0 $F4 $4C $14 $05 $A9 +$20 $20 $A6 $06 $A5 $BF $29 $87 +$30 $E5 $D0 $F3 $60 $A2 $7B $20 +$56 $05 $E6 $C1 $E6 $C1 $E6 $C1 +$38 $B5 $03 $F5 $00 $95 $00 $B5 +$04 $F5 $01 $50 $04 $49 $80 $09 +$01 $30 $0A $D0 $04 $15 $00 $F0 +$02 $56 $02 $56 $02 $56 $02 $90 +$0C $A0 $00 $B1 $2A $E6 $2A $D0 +$02 $E6 $2B $09 $00 $60 $A5 $BE +$F0 $28 $20 $14 $06 $D0 $FB $20 +$6D $07 $F0 $1B $20 $4C $07 $20 +$0C $04 $B0 $09 $A5 $C4 $85 $2A +$A5 $C5 $85 $2B $60 $AD $83 $04 +$85 $2A $AD $84 $04 $85 $2B $4C +$14 $05 $85 $BF $4C $49 $05 $A5 +$20 $85 $2C $A5 $21 $85 $2D $20 +$6D $07 $F0 $EB $A5 $2A $85 $C4 +$A5 $2B $85 $C5 $A9 $01 $85 $BE +$60 $20 $6B $06 $F0 $BE $A5 $BC +$85 $28 $A5 $BD $85 $29 $4C $14 +$05 $20 $FD $0A $20 $FA $0A $20 +$74 $06 $D0 $EA $60 $20 $14 $06 +$85 $28 $20 $14 $06 $85 $29 $05 +$28 $60 $20 $FC $08 $20 $80 $07 +$A5 $BD $20 $87 $07 $A5 $BC $A6 +$C1 $CA $95 $00 $86 $C1 $E4 $C0 +$D0 $0D $4C $14 $05 $A6 $C1 $E0 +$80 $10 $F7 $B5 $00 $E6 $C1 $60 +$85 $BD $86 $BC $4C $B8 $07 $A6 +$C1 $B5 $01 $10 $08 $20 $41 $0A +$A9 $2D $20 $A6 $06 $20 $FC $08 +$A9 $1F $85 $B8 $85 $BA $A9 $2A +$85 $B9 $85 $BB $A6 $BC $A4 $BD +$38 $E6 $B8 $8A $E9 $10 $AA $98 +$E9 $27 $A8 $B0 $F4 $C6 $B9 $8A +$69 $E8 $AA $98 $69 $03 $A8 $90 +$F4 $8A $38 $E6 $BA $E9 $64 $B0 +$F9 $88 $10 $F6 $C6 $BB $69 $0A +$90 $FA $09 $30 $85 $BC $A9 $20 +$85 $BD $A2 $FB $86 $C3 $B5 $BD +$05 $BD $C9 $20 $F0 $09 $A0 $30 +$84 $BD $05 $BD $20 $A6 $06 $A6 +$C3 $E8 $D0 $E8 $60 $A5 $2D $48 +$A5 $2C $48 $A5 $20 $85 $2C $A5 +$21 $85 $2D $A5 $24 $A6 $25 $20 +$5B $08 $F0 $03 $20 $5B $08 $A5 +$2C $38 $E5 $B6 $A5 $2D $E5 $B7 +$B0 $42 $20 $6D $07 $F0 $3D $A6 +$28 $A5 $29 $20 $A0 $07 $A9 $20 +$20 $A6 $06 $20 $0C $04 $B0 $2C +$20 $14 $06 $D0 $F3 $20 $83 $08 +$4C $2F $08 $85 $B6 $E6 $B6 $D0 +$01 $E8 $86 $B7 $A4 $C1 $C0 $80 +$F0 $18 $20 $6B $06 $A5 $2C $A6 +$2D $38 $E9 $02 $B0 $01 $CA $85 +$2C $4C $48 $0B $68 $85 $2C $68 +$85 $2D $60 $A5 $BF $30 $FB $A9 +$0D $20 $09 $04 $AD $11 $04 $29 +$7F $85 $BF $F0 $07 $20 $64 $0B +$C6 $BF $D0 $F9 $A9 $0A $4C $61 +$0B $AC $12 $04 $84 $BF $B0 $0B +$A9 $30 $85 $2C $85 $C0 $84 $2D +$20 $80 $07 $45 $80 $85 $80 $20 +$06 $04 $A0 $00 $A6 $C0 $29 $7F +$F0 $F1 $C9 $7F $F0 $ED $C9 $13 +$F0 $DA $C9 $0A $F0 $D3 $CD $10 +$04 $F0 $09 $CD $0F $04 $D0 $0A +$E0 $30 $D0 $16 $A6 $2C $84 $BF +$A9 $0D $E4 $C1 $30 $08 $A9 $07 +$20 $A6 $06 $4C $B3 $08 $95 $00 +$E8 $E8 $CA $86 $C0 $C9 $0D $D0 +$BA $20 $83 $08 $20 $95 $07 $85 +$BC $20 $95 $07 $85 $BD $60 $20 +$D6 $0A $20 $6B $06 $08 $20 $6D +$08 $85 $B8 $86 $B9 $A5 $BC $85 +$B6 $A5 $BD $85 $B7 $A2 $00 $28 +$D0 $0B $20 $6D $07 $CA $CA $CA +$20 $14 $06 $D0 $FA $84 $28 $84 +$29 $20 $D6 $0A $A9 $0D $D1 $2C +$F0 $11 $E8 $E8 $E8 $E8 $C8 $D1 +$2C $D0 $FA $A5 $B6 $85 $28 $A5 +$B7 $85 $29 $A5 $B8 $85 $BC $A5 +$B9 $85 $BD $18 $A0 $00 $8A $F0 +$6E $10 $29 $65 $2E $85 $B8 $A5 +$2F $E9 $00 $85 $B9 $B1 $2E $91 +$B8 $A6 $2E $E4 $24 $D0 $06 $A5 +$2F $C5 $25 $F0 $4A $E8 $86 $2E +$D0 $02 $E6 $2F $E6 $B8 $D0 $E5 +$E6 $B9 $D0 $E1 $65 $24 $85 $B8 +$85 $2E $98 $65 $25 $85 $B9 $85 +$2F $A5 $2E $E5 $C6 $A5 $2F $E5 +$C7 $90 $05 $C6 $2A $4C $14 $05 +$B1 $24 $91 $2E $A6 $24 $D0 $02 +$C6 $25 $C6 $24 $A6 $2E $D0 $02 +$C6 $2F $CA $86 $2E $E4 $BC $D0 +$E7 $A6 $2F $E4 $BD $D0 $E1 $A5 +$B8 $85 $24 $A5 $B9 $85 $25 $A5 +$28 $05 $29 $F0 $17 $A5 $28 $91 +$BC $C8 $A5 $29 $91 $BC $C8 $84 +$B6 $20 $14 $06 $08 $A4 $B6 $91 +$BC $28 $D0 $F2 $4C $CC $04 $20 +$54 $05 $B5 $03 $29 $80 $F0 $02 +$A9 $FF $85 $BC $85 $BD $48 $75 +$02 $95 $02 $68 $48 $75 $03 $95 +$03 $68 $55 $01 $85 $BB $10 $03 +$20 $43 $0A $A0 $11 $B5 $00 $15 +$01 $D0 $03 $4C $14 $05 $38 $A5 +$BC $F5 $00 $48 $A5 $BD $F5 $01 +$48 $45 $BD $30 $0A $68 $85 $BD +$68 $85 $BC $38 $4C $32 $0A $68 +$68 $18 $36 $02 $36 $03 $26 $BC +$26 $BD $88 $D0 $D9 $A5 $BB $10 +$0D $A6 $C1 $38 $98 $F5 $00 $95 +$00 $98 $F5 $01 $95 $01 $60 $20 +$41 $0A $20 $54 $05 $B5 $00 $75 +$02 $95 $02 $B5 $01 $75 $03 $95 +$03 $60 $20 $54 $05 $A0 $10 $B5 +$02 $85 $BC $B5 $03 $85 $BD $16 +$02 $36 $03 $26 $BC $26 $BD $90 +$0D $18 $B5 $02 $75 $00 $95 $02 +$B5 $03 $75 $01 $95 $03 $88 $D0 +$E6 $60 $20 $95 $07 $AA $B5 $00 +$B4 $01 $C6 $C1 $A6 $C1 $94 $00 +$4C $87 $07 $A2 $7D $20 $56 $05 +$B5 $01 $48 $B5 $00 $48 $20 $95 +$07 $AA $68 $95 $00 $68 $95 $01 +$60 $20 $FD $0A $A5 $BC $85 $2A +$A5 $BD $85 $2B $60 $A2 $2C $D0 +$02 $A2 $2E $B5 $00 $C9 $80 $B0 +$0D $B5 $01 $D0 $09 $A5 $2C $85 +$2E $A5 $2D $85 $2F $60 $A5 $2C +$A4 $2E $84 $2C $85 $2E $A5 $2D +$A4 $2F $84 $2D $85 $2F $A0 $00 +$60 $A5 $28 $85 $BC $A5 $29 $85 +$BD $20 $9C $05 $A5 $C6 $85 $26 +$A5 $C7 $85 $27 $60 $B1 $C6 $85 +$BC $20 $08 $0B $B1 $C6 $85 $BD +$E6 $C6 $D0 $02 $E6 $C7 $A5 $22 +$C5 $C6 $A5 $23 $E5 $C7 $B0 $E4 +$4C $14 $05 $20 $24 $0B $85 $BC +$98 $4C $82 $07 $20 $FC $08 $A5 +$BC $85 $B6 $20 $FC $08 $A5 $BD +$85 $B7 $A4 $BC $20 $FC $08 $A6 +$B7 $A5 $B6 $18 $6C $BC $00 $20 +$42 $0B $20 $F9 $06 $4C $87 $07 +$86 $2D $E0 $00 $60 $A0 $02 $84 +$BC $A0 $29 $84 $BD $A0 $00 $B1 +$BC $C9 $08 $D0 $03 $4C $0B $0A +$60 $20 $09 $04 $A9 $FF $2C $11 +$04 $30 $02 $A9 $00 $4C $09 $04 +$24 $3A $91 $27 $10 $E1 $59 $C5 +$2A $56 $10 $11 $2C $8B $4C $45 +$D4 $A0 $80 $BD $30 $BC $E0 $13 +$1D $94 $47 $CF $88 $54 $CF $30 +$BC $E0 $10 $11 $16 $80 $53 $55 +$C2 $30 $BC $E0 $14 $16 $90 $50 +$D2 $83 $49 $4E $D4 $E5 $71 $88 +$BB $E1 $1D $8F $A2 $21 $58 $6F +$83 $AC $22 $55 $83 $BA $24 $93 +$E0 $23 $1D $30 $BC $20 $48 $91 +$49 $C6 $30 $BC $31 $34 $30 $BC +$84 $54 $48 $45 $CE $1C $1D $38 +$0D $9A $49 $4E $50 $55 $D4 $A0 +$10 $E7 $24 $3F $20 $91 $27 $E1 +$59 $81 $AC $30 $BC $13 $11 $82 +$AC $4D $E0 $1D $89 $52 $45 $54 +$55 $52 $CE $E0 $15 $1D $85 $45 +$4E $C4 $E0 $2D $98 $4C $49 $53 +$D4 $EC $24 $00 $00 $00 $00 $0A +$80 $1F $24 $93 $23 $1D $30 $BC +$E1 $50 $80 $AC $59 $85 $52 $55 +$CE $38 $0A $86 $43 $4C $45 $41 +$D2 $2B $84 $52 $45 $CD $1D $A0 +$80 $BD $38 $14 $85 $AD $30 $D3 +$17 $64 $81 $AB $30 $D3 $85 $AB +$30 $D3 $18 $5A $85 $AD $30 $D3 +$19 $54 $2F $30 $E2 $85 $AA $30 +$E2 $1A $5A $85 $AF $30 $E2 $1B +$54 $2F $98 $52 $4E $C4 $0A $80 +$80 $12 $0A $09 $29 $1A $0A $1A +$85 $18 $13 $09 $80 $12 $01 $0B +$31 $30 $61 $72 $0B $04 $02 $03 +$05 $03 $1B $1A $19 $0B $09 $06 +$0A $00 $00 $1C $17 $2F $8F $55 +$53 $D2 $80 $A8 $30 $BC $31 $2A +$31 $2A $80 $A9 $2E $2F $A2 $12 +$2F $C1 $2F $80 $A8 $30 $BC $80 +$A9 $2F $83 $AC $38 $BC $0B $2F +$80 $A8 $52 $2F $84 $BD $09 $02 +$2F $8E $BC $84 $BD $09 $93 $2F +$84 $BE $09 $05 $2F $09 $91 $2F +$80 $BE $84 $BD $09 $06 $2F $84 +$BC $09 $95 $2F $09 $04 $2F $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$EA $EA $EA $EA $EA $EA $EA $EA +$EA $EA $EA $EA $EA $20 $0D $0F +$A0 $00 $20 $1D $0F $20 $2C $0F +$C9 $43 $D0 $03 $4C $85 $04 $C9 +$57 $D0 $03 $4C $BD $04 $A2 $2F +$20 $1D $0F $4C $05 $0D $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$4F $4D $45 $47 $41 $20 $4D $49 +$43 $52 $4F $20 $53 $59 $53 $54 +$45 $4D $53 $0D $0A $4D $4B $48 +$42 $43 $2D $38 $2D $52 $31 $20 +$54 $49 $4E $59 $20 $42 $41 $53 +$49 $43 $20 $36 $35 $30 $32 $20 +$50 $4F $52 $54 $0D $0A $56 $65 +$72 $73 $69 $6F $6E $3A $20 $31 +$2E $30 $2E $32 $2C $20 $31 $2F +$31 $31 $2F $32 $30 $31 $32 $0D +$0A $28 $4E $4F $54 $45 $3A $20 +$55 $73 $65 $20 $63 $61 $70 $73 +$20 $66 $6F $72 $20 $42 $61 $73 +$69 $63 $29 $0D $0A $42 $6F $6F +$74 $20 $28 $5B $43 $5D $6F $6C +$64 $2F $5B $57 $5D $61 $72 $6D +$29 $3F $20 $07 $FF $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$86 $C3 $B1 $C2 $48 $C8 $B1 $C2 +$AA $68 $A8 $8A $60 $A2 $19 $A9 +$0D $20 $31 $0F $A9 $0A $20 $31 +$0F $CA $D0 $FA $60 $B9 $00 $0E +$C9 $FF $F0 $07 $20 $31 $0F $C8 +$4C $1D $0F $60 $AD $00 $E0 $F0 +$FB $85 $FE $C9 $FF $F0 $1E $C9 +$00 $F0 $1A $C9 $91 $F0 $16 $C9 +$93 $F0 $12 $C9 $80 $F0 $0E $4C +$50 $0F $20 $F0 $FF $A5 $FE $60 +$A5 $FE $8D $00 $E0 $60 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +$00 $00 $00 $00 $00 $00 $00 $00 +