From 0dd5a0299f468293c381660e5f263e392c6b1305 Mon Sep 17 00:00:00 2001 From: makarcz Date: Sat, 20 Feb 2016 18:14:25 -0500 Subject: [PATCH] Virtual Machine 6502 This is the initial development of VM6502 project. Initially it is a VM emulating MOS 6502 opcodes, but in the future my intention is to use the free illegal opcodes to extend its functionality. --- .gitignore | 43 ++ BCDCodes.dev | 62 ++ Display.cpp | 245 +++++++ Display.h | 54 ++ MKBasic.cpp | 13 + MKBasic.dev | 182 ++++++ MKBasic.h | 18 + MKCpu.cpp | 1471 ++++++++++++++++++++++++++++++++++++++++++ MKCpu.h | 424 ++++++++++++ MKGenException.cpp | 20 + MKGenException.h | 23 + Memory.cpp | 310 +++++++++ Memory.h | 55 ++ Notes.txt | 25 + ReadMe.txt | 62 ++ TestBCD.65s | 291 +++++++++ VMachine.cpp | 579 +++++++++++++++++ VMachine.h | 65 ++ bcd.c | 56 ++ dummy.ram | 315 +++++++++ dummy.rom | 315 +++++++++ hello_world.bas | 5 + main.cpp | 333 ++++++++++ t_adc_bcd_01.65s | 99 +++ t_adc_bcd_01.dat | 36 ++ t_sbc_bcd_01.65s | 85 +++ t_sbc_bcd_01.dat | 36 ++ test_char_io_01.65s | 50 ++ test_char_io_01.dat | 15 + testall.asm | 925 ++++++++++++++++++++++++++ testall.dat | 195 ++++++ testbcd.dat | 69 ++ tinybasic.asm | 1496 +++++++++++++++++++++++++++++++++++++++++++ tinybasic.dat | 774 ++++++++++++++++++++++ 34 files changed, 8746 insertions(+) create mode 100644 .gitignore create mode 100644 BCDCodes.dev create mode 100644 Display.cpp create mode 100644 Display.h create mode 100644 MKBasic.cpp create mode 100644 MKBasic.dev create mode 100644 MKBasic.h create mode 100644 MKCpu.cpp create mode 100644 MKCpu.h create mode 100644 MKGenException.cpp create mode 100644 MKGenException.h create mode 100644 Memory.cpp create mode 100644 Memory.h create mode 100644 Notes.txt create mode 100644 ReadMe.txt create mode 100644 TestBCD.65s create mode 100644 VMachine.cpp create mode 100644 VMachine.h create mode 100644 bcd.c create mode 100644 dummy.ram create mode 100644 dummy.rom create mode 100644 hello_world.bas create mode 100644 main.cpp create mode 100644 t_adc_bcd_01.65s create mode 100644 t_adc_bcd_01.dat create mode 100644 t_sbc_bcd_01.65s create mode 100644 t_sbc_bcd_01.dat create mode 100644 test_char_io_01.65s create mode 100644 test_char_io_01.dat create mode 100644 testall.asm create mode 100644 testall.dat create mode 100644 testbcd.dat create mode 100644 tinybasic.asm create mode 100644 tinybasic.dat 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 +