/* *-------------------------------------------------------------------- * Project: VM65 - Virtual Machine/CPU emulator programming * framework. * * File: MKCpu.h * * Purpose: Prototype of MKCpu class. Data structures, enumerations * and constants definitions supporting MKCpu class. * * Date: 8/25/2016 * * Copyright: (C) by Marek Karcz 2016. All rights reserved. * * Contact: makarcz@yahoo.com * * License Agreement and Warranty: This software is provided with No Warranty. I (Marek Karcz) will not be held responsible for any damage to computer systems, data or user's health resulting from use. Please proceed responsibly and exercise common sense. This software is provided in hope that it will be useful. It is free of charge for non-commercial and educational use. Distribution of this software in non-commercial and educational derivative work is permitted under condition that original copyright notices and comments are preserved. Some 3-rd party work included with this project may require separate application for permission from their respective authors/copyright owners. *-------------------------------------------------------------------- */ #ifndef MKCPU_H #define MKCPU_H #include #include #include #include "system.h" #include "Memory.h" using namespace std; namespace MKBasic { #define DISS_BUF_SIZE 60 // disassembled instruction buffer size #define OPCO_HIS_SIZE 20 // size of op-code execute history queue struct Regs { unsigned char Acc; // 8-bit accumulator unsigned short Acc16; // 16-bit accumulator unsigned char IndX; // 8-bit index register X unsigned char IndY; // 8-bit index register Y unsigned short Ptr16; // general purpose 16-bit register unsigned short PtrAddr; // cpu code counter (PC) - current read/write address unsigned char PtrStack; // 8-bit stack pointer (0-255). unsigned char Flags; // CPU flags bool SoftIrq; // true when interrupted with BRK or trapped opcode bool LastRTS; // true if RTS encountered and stack empty. unsigned short LastAddr; // PC at the time of previous op-code //string LastInstr; // instruction and argument executed in previous step int LastOpCode; // op-code of last instruction unsigned short LastArg; // argument to the last instruction int LastAddrMode; // addressing mode of last instruction bool IrqPending; // pending Interrupt ReQuest (IRQ) int CyclesLeft; // # of cycles left to complete current opcode bool PageBoundary; // true if page boundary was crossed }; /* * Virtual CPU, 6502 addressing modes: +---------------------+--------------------------+ | mode | assembler format | +=====================+==========================+ | Immediate | #aa | | Absolute | aaaa | | Zero Page | aa | Note: | Implied | | | Indirect Absolute | (aaaa) | aa = 2 hex digits | Absolute Indexed,X | aaaa,X | as $FF | Absolute Indexed,Y | aaaa,Y | | Zero Page Indexed,X | aa,X | aaaa = 4 hex | Zero Page Indexed,Y | aa,Y | digits as | Indexed Indirect | (aa,X) | $FFFF | Indirect Indexed | (aa),Y | | Relative | aaaa | Can also be | Accumulator | A | assembler labels +---------------------+--------------------------+ Short notation: imm = #$00 zp = $00 zpx = $00,X zpy = $00,Y izx = ($00,X) izy = ($00),Y abs = $0000 abx = $0000,X aby = $0000,Y ind = ($0000) rel = $0000 (PC-relative) See: 6502AssemblyInOneStep.txt for details. */ enum eAddrModes { ADDRMODE_IMM = 0, ADDRMODE_ABS, ADDRMODE_ZP, ADDRMODE_IMP, ADDRMODE_IND, ADDRMODE_ABX, ADDRMODE_ABY, ADDRMODE_ZPX, ADDRMODE_ZPY, ADDRMODE_IZX, ADDRMODE_IZY, ADDRMODE_REL, ADDRMODE_ACC, ADDRMODE_UND, // undetermined (for some illegal codes) ADDRMODE_LENGTH // should be always last }; // assumed little-endian order of bytes (start with least significant) // MEM - memory location from where the value is read/written, // & - reference operator (e.g.: &addr means: value under addr memory location) // PC - program counter (PC+1 means - next memory location after opcode) enum eOpCodes { OPCODE_BRK = 0x00, // software interrupt, no arguments ($00 : BRK) /* full compatibility with 65C02 (illegal opcodes not supported, will be used for extended functions */ OPCODE_ORA_IZX = 0x01, // bitwise OR with Accumulator, Indexed Indirect ($01 arg : ORA (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_ILL_02 = 0x02, // illegal opcode OPCODE_ILL_03 = 0x03, // illegal opcode OPCODE_ILL_04 = 0x04, // illegal opcode OPCODE_ORA_ZP = 0x05, // bitwise OR with Accumulator, Zero Page ($05 arg : ORA arg ;arg=0..$FF), MEM=arg OPCODE_ASL_ZP = 0x06, // Arithmetic Shift Left, Zero Page ($06 arg : ASL arg ;arg=0..$FF), MEM=arg OPCODE_ILL_07 = 0x07, // illegal opcode OPCODE_PHP = 0x08, // PusH Processor status on Stack, Implied ($08 : PHP) OPCODE_ORA_IMM = 0x09, // bitwise OR with Accumulator, Immediate ($09 arg : ORA #arg ;arg=0..$FF), MEM=PC+1 OPCODE_ASL = 0x0A, // Arithmetic Shift Left, Accumulator ($0A : ASL) OPCODE_ILL_0B = 0x0B, // illegal opcode OPCODE_ILL_0C = 0x0C, // illegal opcode OPCODE_ORA_ABS = 0x0D, // bitwise OR with Accumulator, Absolute ($0D addrlo addrhi : ORA addr ;addr=0..$FFFF), MEM=addr OPCODE_ASL_ABS = 0x0E, // Arithmetic Shift Left, Absolute ($0E addrlo addrhi : ASL addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_0F = 0x0F, // illegal opcode OPCODE_BPL_REL = 0x10, // Branch on PLus, Relative ($10 signoffs : BPL signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_ORA_IZY = 0x11, // bitwise OR with Accumulator, Indirect Indexed ($11 arg : ORA (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_12 = 0x12, // illegal opcode OPCODE_ILL_13 = 0x13, // illegal opcode OPCODE_ILL_14 = 0x14, // illegal opcode OPCODE_ORA_ZPX = 0x15, // bitwise OR with Accumulator, Zero Page Indexed, X ($15 arg : ORA arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ASL_ZPX = 0x16, // Arithmetic Shift Left, Zero Page Indexed, X ($16 arg : ASL arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ILL_17 = 0x17, // illegal opcode OPCODE_CLC = 0x18, // CLear Carry, Implied ($18 : CLC) OPCODE_ORA_ABY = 0x19, // bitwise OR with Accumulator, Absolute Indexed, Y ($19 addrlo addrhi : ORA addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_ILL_1A = 0x1A, // illegal opcode OPCODE_ILL_1B = 0x1B, // illegal opcode OPCODE_ILL_1C = 0x1C, // illegal opcode OPCODE_ORA_ABX = 0x1D, // bitwise OR with Accumulator, Absolute Indexed, X ($1D addrlo addrhi : ORA addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ASL_ABX = 0x1E, // Arithmetic Shift Left, Absolute Indexed, X ($1E addrlo addrhi : ASL addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ILL_1F = 0x1F, // illegal opcode OPCODE_JSR_ABS = 0x20, // Jump to SubRoutine, Absolute ($20 addrlo addrhi : JSR addr ;addr=0..$FFFF), MEM=addr OPCODE_AND_IZX = 0x21, // bitwise AND with accumulator, Indexed Indirect ($21 arg : AND (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_ILL_22 = 0x22, // illegal opcode OPCODE_ILL_23 = 0x23, // illegal opcode OPCODE_BIT_ZP = 0x24, // test BITs, Zero Page ($24 arg : BIT arg ;arg=0..$FF), MEM=arg OPCODE_AND_ZP = 0x25, // bitwise AND with accumulator, Zero Page ($25 arg : AND arg ;arg=0..$FF), MEM=arg OPCODE_ROL_ZP = 0x26, // ROtate Left, Zero Page ($26 arg : ROL arg ;arg=0..$FF), MEM=arg OPCODE_ILL_27 = 0x27, // illegal opcode OPCODE_PLP = 0x28, // PuLl Processor status, Implied ($28 : PLP) OPCODE_AND_IMM = 0x29, // bitwise AND with accumulator, Immediate ($29 arg : AND #arg ;arg=0..$FF), MEM=PC+1 OPCODE_ROL = 0x2A, // ROtate Left, Accumulator ($2A : ROL) OPCODE_ILL_2B = 0x2B, // illegal opcode OPCODE_BIT_ABS = 0x2C, // test BITs, Absolute ($2C addrlo addrhi : BIT addr ;addr=0..$FFFF), MEM=addr OPCODE_AND_ABS = 0x2D, // bitwise AND with accumulator, Absolute ($2D addrlo addrhi : AND addr ;addr=0..$FFFF), MEM=addr OPCODE_ROL_ABS = 0x2E, // ROtate Left, Absolute ($2E addrlo addrhi : ROL addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_2F = 0x2F, // illegal opcode OPCODE_BMI_REL = 0x30, // Branch on MInus, Relative ($30 signoffs : BMI signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_AND_IZY = 0x31, // bitwise AND with accumulator, Indirect Indexed ($31 arg : AND (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_32 = 0x32, // illegal opcode OPCODE_ILL_33 = 0x33, // illegal opcode OPCODE_ILL_34 = 0x34, // illegal opcode OPCODE_AND_ZPX = 0x35, // bitwise AND with accumulator, Zero Page Indexed, X ($35 arg : AND arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ROL_ZPX = 0x36, // ROtate Left, Zero Page Indexed, X ($36 arg : ROL arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ILL_37 = 0x37, // illegal opcode OPCODE_SEC = 0x38, // SEt Carry, Implied ($38 : SEC) OPCODE_AND_ABY = 0x39, // bitwise AND with accumulator, Absolute Indexed, Y ($39 addrlo addrhi : AND addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_ILL_3A = 0x3A, // illegal opcode OPCODE_ILL_3B = 0x3B, // illegal opcode OPCODE_ILL_3C = 0x3C, // illegal opcode OPCODE_AND_ABX = 0x3D, // bitwise AND with accumulator, Absolute Indexed, X ($3D addrlo addrhi : AND addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ROL_ABX = 0x3E, // ROtate Left, Absolute Indexed, X ($3E addrlo addrhi : ROL addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ILL_3F = 0x3F, // illegal opcode OPCODE_RTI = 0x40, // ReTurn from Interrupt, Implied ($40 : RTI) OPCODE_EOR_IZX = 0x41, // bitwise Exclusive OR, Indexed Indirect ($41 arg : EOR (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_ILL_42 = 0x42, // illegal opcode OPCODE_ILL_43 = 0x43, // illegal opcode OPCODE_ILL_44 = 0x44, // illegal opcode OPCODE_EOR_ZP = 0x45, // bitwise Exclusive OR, Zero Page ($45 arg : EOR arg ;arg=0..$FF), MEM=arg OPCODE_LSR_ZP = 0x46, // Logical Shift Right, Zero Page ($46 arg : LSR arg ;arg=0..$FF), MEM=arg OPCODE_ILL_47 = 0x47, // illegal opcode OPCODE_PHA = 0x48, // PusH Accumulator, Implied ($48 : PHA) OPCODE_EOR_IMM = 0x49, // bitwise Exclusive OR, Immediate ($49 arg : EOR #arg ;arg=0..$FF), MEM=PC+1 OPCODE_LSR = 0x4A, // Logical Shift Right, Accumulator ($4A : LSR) OPCODE_ILL_4B = 0x4B, // illegal opcode OPCODE_JMP_ABS = 0x4C, // JuMP, Absolute ($4C addrlo addrhi : JMP addr ;addr=0..$FFFF), MEM=addr OPCODE_EOR_ABS = 0x4D, // bitwise Exclusive OR, Absolute ($4D addrlo addrhi : EOR addr ;addr=0..$FFFF), MEM=addr OPCODE_LSR_ABS = 0x4E, // Logical Shift Right, Absolute ($4E addrlo addrhi : LSR addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_4F = 0x4F, // illegal opcode OPCODE_BVC_REL = 0x50, // Branch on oVerflow Clear, Relative ($50 signoffs : BVC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_EOR_IZY = 0x51, // bitwise Exclusive OR, Indirect Indexed ($51 arg : EOR (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_52 = 0x52, // illegal opcode OPCODE_ILL_53 = 0x53, // illegal opcode OPCODE_ILL_54 = 0x54, // illegal opcode OPCODE_EOR_ZPX = 0x55, // bitwise Exclusive OR, Zero Page Indexed, X ($55 arg : EOR arg,X ;arg=0..$FF), MEM=arg+X OPCODE_LSR_ZPX = 0x56, // Logical Shift Right, Zero Page Indexed, X ($56 arg : LSR arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ILL_57 = 0x57, // illegal opcode OPCODE_CLI = 0x58, // CLear Interrupt, Implied ($58 : CLI) OPCODE_EOR_ABY = 0x59, // bitwise Exclusive OR, Absolute Indexed, Y ($59 addrlo addrhi : EOR addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_ILL_5A = 0x5A, // illegal opcode OPCODE_ILL_5B = 0x5B, // illegal opcode OPCODE_ILL_5C = 0x5C, // illegal opcode OPCODE_EOR_ABX = 0x5D, // bitwise Exclusive OR, Absolute Indexed, X ($5D addrlo addrhi : EOR addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_LSR_ABX = 0x5E, // Logical Shift Right, Absolute Indexed, X ($5E addrlo addrhi : LSR addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ILL_5F = 0x5F, // illegal opcode OPCODE_RTS = 0x60, // ReTurn from Subroutine, Implied ($60 : RTS) OPCODE_ADC_IZX = 0x61, // ADd with Carry, Indexed Indirect ($61 arg : ADC (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_ILL_62 = 0x62, // illegal opcode OPCODE_ILL_63 = 0x63, // illegal opcode OPCODE_ILL_64 = 0x64, // illegal opcode OPCODE_ADC_ZP = 0x65, // ADd with Carry, Zero Page ($65 arg : ADC arg ;arg=0..$FF), MEM=arg OPCODE_ROR_ZP = 0x66, // ROtate Right, Zero Page ($66 arg : ROR arg ;arg=0..$FF), MEM=arg OPCODE_ILL_67 = 0x67, // illegal opcode OPCODE_PLA = 0x68, // PuLl Accumulator, Implied ($68 : PLA) OPCODE_ADC_IMM = 0x69, // ADd with Carry, Immediate ($69 arg : ADC #arg ;arg=0..$FF), MEM=PC+1 OPCODE_ROR = 0x6A, // ROtate Right, Accumulator ($6A : ROR) OPCODE_ILL_6B = 0x6B, // illegal opcode OPCODE_JMP_IND = 0x6C, // JuMP, Indirect Absolute ($6C addrlo addrhi : JMP (addr) ;addr=0..FFFF), MEM=&addr OPCODE_ADC_ABS = 0x6D, // ADd with Carry, Absolute ($6D addrlo addrhi : ADC addr ;addr=0..$FFFF), MEM=addr OPCODE_ROR_ABS = 0x6E, // ROtate Right, Absolute ($6E addrlo addrhi : ROR addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_6F = 0x6F, // illegal opcode OPCODE_BVS_REL = 0x70, // Branch on oVerflow Set, Relative ($70 signoffs : BVS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_ADC_IZY = 0x71, // ADd with Carry, Indirect Indexed ($71 arg : ADC (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_72 = 0x72, // illegal opcode OPCODE_ILL_73 = 0x73, // illegal opcode OPCODE_ILL_74 = 0x74, // illegal opcode OPCODE_ADC_ZPX = 0x75, // ADd with Carry, Zero Page Indexed, X ($75 arg : ADC arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ROR_ZPX = 0x76, // ROtate Right, Zero Page Indexed, X ($76 arg : ROR arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ILL_77 = 0x77, // illegal opcode OPCODE_SEI = 0x78, // SEt Interrupt, Implied ($78 : SEI) OPCODE_ADC_ABY = 0x79, // ADd with Carry, Absolute Indexed, Y ($79 addrlo addrhi : ADC addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_ILL_7A = 0x7A, // illegal opcode OPCODE_ILL_7B = 0x7B, // illegal opcode OPCODE_ILL_7C = 0x7C, // illegal opcode OPCODE_ADC_ABX = 0x7D, // ADd with Carry, Absolute Indexed, X ($7D addrlo addrhi : ADC addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ROR_ABX = 0x7E, // ROtate Right, Absolute Indexed, X ($7E addrlo addrhi : ROR addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ILL_7F = 0x7F, // illegal opcode OPCODE_ILL_80 = 0x80, // illegal opcode OPCODE_STA_IZX = 0x81, // STore Accumulator, Indexed Indirect ($81 arg : STA (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_ILL_82 = 0x82, // illegal opcode OPCODE_ILL_83 = 0x83, // illegal opcode OPCODE_STY_ZP = 0x84, // STore Y register, Zero Page ($84 arg : STY arg ;arg=0..$FF), MEM=arg OPCODE_STA_ZP = 0x85, // STore Accumulator, Zero Page ($85 arg : STA arg ;arg=0..$FF), MEM=arg OPCODE_STX_ZP = 0x86, // STore X register, Zero Page ($86 arg : STX arg ;arg=0..$FF), MEM=arg OPCODE_ILL_87 = 0x87, // illegal opcode OPCODE_DEY = 0x88, // DEcrement Y, Implied ($88 : DEY) OPCODE_ILL_89 = 0x89, // illegal opcode OPCODE_TXA = 0x8A, // Transfer X to A, Implied ($8A : TXA) OPCODE_ILL_8B = 0x8B, // illegal opcode OPCODE_STY_ABS = 0x8C, // STore Y register, Absolute ($8C addrlo addrhi : STY addr ;addr=0..$FFFF), MEM=addr OPCODE_STA_ABS = 0x8D, // STore Accumulator, Absolute ($8D addrlo addrhi : STA addr ;addr=0..$FFFF), MEM=addr OPCODE_STX_ABS = 0x8E, // STore X register, Absolute ($8E addrlo addrhi : STX addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_8F = 0x8F, // illegal opcode OPCODE_BCC_REL = 0x90, // Branch on Carry Clear, Relative ($90 signoffs : BCC signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_STA_IZY = 0x91, // STore Accumulator, Indirect Indexed ($91 arg : STA (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_92 = 0x92, // illegal opcode OPCODE_ILL_93 = 0x93, // illegal opcode OPCODE_STY_ZPX = 0x94, // STore Y register, Zero Page Indexed, X ($94 arg : STY arg,X ;arg=0..$FF), MEM=arg+X OPCODE_STA_ZPX = 0x95, // STore Accumulator, Zero Page Indexed, X ($95 arg : STA arg,X ;arg=0..$FF), MEM=arg+X OPCODE_STX_ZPY = 0x96, // STore X register, Zero Page Indexed, Y ($96 arg : STX arg,Y ;arg=0..$FF), MEM=arg+Y OPCODE_ILL_97 = 0x97, // illegal opcode OPCODE_TYA = 0x98, // Transfer Y to A, Implied ($98 : TYA) OPCODE_STA_ABY = 0x99, // STore Accumulator, Absolute Indexed, Y ($99 addrlo addrhi : STA addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_TXS = 0x9A, // Transfer X to Stack ptr, Implied ($9A : TXS) OPCODE_ILL_9B = 0x9B, // illegal opcode OPCODE_ILL_9C = 0x9C, // illegal opcode OPCODE_STA_ABX = 0x9D, // STore Accumulator, Absolute Indexed, X ($9D addrlo addrhi : STA addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ILL_9E = 0x9E, // illegal opcode OPCODE_ILL_9F = 0x9F, // illegal opcode OPCODE_LDY_IMM = 0xA0, // LoaD Y register, Immediate ($A0 arg : LDY #arg ;arg=0..$FF), MEM=PC+1 OPCODE_LDA_IZX = 0xA1, // LoaD Accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_LDX_IMM = 0xA2, // LoaD X register, Immediate ($A2 arg : LDX #arg ;arg=0..$FF), MEM=PC+1 OPCODE_ILL_A3 = 0xA3, // illegal opcode OPCODE_LDY_ZP = 0xA4, // LoaD Y register, Zero Page ($A4 arg : LDY arg ;arg=0..$FF), MEM=arg OPCODE_LDA_ZP = 0xA5, // LoaD Accumulator, Zero Page ($A5 arg : LDA arg ;arg=0..$FF), MEM=arg OPCODE_LDX_ZP = 0xA6, // LoaD X register, Zero Page ($A6 arg : LDX arg ;arg=0..$FF), MEM=arg OPCODE_ILL_A7 = 0xA7, // illegal opcode OPCODE_TAY = 0xA8, // Transfer A to Y, Implied ($A8 : TAY) OPCODE_LDA_IMM = 0xA9, // LoaD Accumulator, Immediate ($A9 arg : LDA #arg ;arg=0..$FF), MEM=PC+1 OPCODE_TAX = 0xAA, // Transfer A to X, Implied ($AA : TAX) OPCODE_ILL_AB = 0xAB, // illegal opcode OPCODE_LDY_ABS = 0xAC, // LoaD Y register, Absolute ($AC addrlo addrhi : LDY addr ;addr=0..$FFFF), MEM=addr OPCODE_LDA_ABS = 0xAD, // LoaD Accumulator, Absolute ($AD addrlo addrhi : LDA addr ;addr=0..$FFFF), MEM=addr OPCODE_LDX_ABS = 0xAE, // LoaD X register, Absolute ($AE addrlo addrhi : LDX addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_AF = 0xAF, // illegal opcode OPCODE_BCS_REL = 0xB0, // Branch on Carry Set, Relative ($B0 signoffs : BCS signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_LDA_IZY = 0xB1, // LoaD Accumulator, Indirect Indexed ($B1 arg : LDA (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_B2 = 0xB2, // illegal opcode OPCODE_ILL_B3 = 0xB3, // illegal opcode OPCODE_LDY_ZPX = 0xB4, // LoaD Y register, Zero Page Indexed, X ($B4 arg : LDY arg,X ;arg=0..$FF), MEM=arg+X OPCODE_LDA_ZPX = 0xB5, // LoaD Accumulator, Zero Page Indexed, X ($B5 arg : LDA arg,X ;arg=0..$FF), MEM=arg+X OPCODE_LDX_ZPY = 0xB6, // LoaD X register, Zero Page Indexed, Y ($B6 arg : LDX arg,Y ;arg=0..$FF), MEM=arg+Y OPCODE_ILL_B7 = 0xB7, // illegal opcode OPCODE_CLV = 0xB8, // CLear oVerflow, Implied ($B8 : CLV) OPCODE_LDA_ABY = 0xB9, // LoaD Accumulator, Absolute Indexed, Y ($B9 addrlo addrhi : LDA addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_TSX = 0xBA, // Transfer Stack ptr to X, Implied ($BA : TSX) OPCODE_ILL_BB = 0xBB, // illegal opcode OPCODE_LDY_ABX = 0xBC, // LoaD Y register, Absolute Indexed, X ($BC addrlo addrhi : LDY addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_LDA_ABX = 0xBD, // LoaD Accumulator, Absolute Indexed, X ($BD addrlo addrhi : LDA addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_LDX_ABY = 0xBE, // LoaD X register, Absolute Indexed, Y ($BE addrlo addrhi : LDX addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_ILL_BF = 0xBF, // illegal opcode OPCODE_CPY_IMM = 0xC0, // ComPare Y register, Immediate ($C0 arg : CPY #arg ;arg=0..$FF), MEM=PC+1 OPCODE_CMP_IZX = 0xC1, // CoMPare accumulator, Indexed Indirect ($A1 arg : LDA (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_ILL_C2 = 0xC2, // illegal opcode OPCODE_ILL_C3 = 0xC3, // illegal opcode OPCODE_CPY_ZP = 0xC4, // ComPare Y register, Zero Page ($C4 arg : CPY arg ;arg=0..$FF), MEM=arg OPCODE_CMP_ZP = 0xC5, // CoMPare accumulator, Zero Page ($C5 arg : CMP arg ;arg=0..$FF), MEM=arg OPCODE_DEC_ZP = 0xC6, // DECrement memory, Zero Page ($C6 arg : DEC arg ;arg=0..$FF), MEM=arg OPCODE_ILL_C7 = 0xC7, // illegal opcode OPCODE_INY = 0xC8, // INcrement Y, Implied ($C8 : INY)OPCODE_INY = 0xC8, // INcrement Y, Implied ($C8 : INY) OPCODE_CMP_IMM = 0xC9, // CoMPare accumulator, Immediate ($C9 arg : CMP #arg ;arg=0..$FF), MEM=PC+1 OPCODE_DEX = 0xCA, // DEcrement X, Implied ($CA : DEX) OPCODE_ILL_CB = 0xCB, // illegal opcode OPCODE_CPY_ABS = 0xCC, // ComPare Y register, Absolute ($CC addrlo addrhi : CPY addr ;addr=0..$FFFF), MEM=addr OPCODE_CMP_ABS = 0xCD, // CoMPare accumulator, Absolute ($CD addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr OPCODE_DEC_ABS = 0xCE, // DECrement memory, Absolute ($CE addrlo addrhi : CMP addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_CF = 0xCF, // illegal opcode OPCODE_BNE_REL = 0xD0, // Branch on Not Equal, Relative ($D0 signoffs : BNE signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_CMP_IZY = 0xD1, // CoMPare accumulator, Indirect Indexed ($D1 arg : CMP (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_D2 = 0xD2, // illegal opcode OPCODE_ILL_D3 = 0xD3, // illegal opcode OPCODE_ILL_D4 = 0xD4, // illegal opcode OPCODE_CMP_ZPX = 0xD5, // CoMPare accumulator, Zero Page Indexed, X ($D5 arg : CMP arg,X ;arg=0..$FF), MEM=arg+X OPCODE_DEC_ZPX = 0xD6, // DECrement memory, Zero Page Indexed, X ($D6 arg : DEC arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ILL_D7 = 0xD7, // illegal opcode OPCODE_CLD = 0xD8, // CLear Decimal, Implied ($D8 : CLD) OPCODE_CMP_ABY = 0xD9, // CoMPare accumulator, Absolute Indexed, Y ($D9 addrlo addrhi : CMP addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_ILL_DA = 0xDA, // illegal opcode OPCODE_ILL_DB = 0xDB, // illegal opcode OPCODE_ILL_DC = 0xDC, // illegal opcode OPCODE_CMP_ABX = 0xDD, // CoMPare accumulator, Absolute Indexed, X ($DD addrlo addrhi : CMP addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_DEC_ABX = 0xDE, // DECrement memory, Absolute Indexed, X ($DE addrlo addrhi : DEC addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ILL_DF = 0xDF, // illegal opcode OPCODE_CPX_IMM = 0xE0, // ComPare X register, Immediate ($E0 arg : CPX #arg ;arg=0..$FF), MEM=PC+1 OPCODE_SBC_IZX = 0xE1, // SuBtract with Carry, Indexed Indirect ($E1 arg : SBC (arg,X) ;arg=0..$FF), MEM=&(arg+X) OPCODE_ILL_E2 = 0xE2, // illegal opcode OPCODE_ILL_E3 = 0xE3, // illegal opcode OPCODE_CPX_ZP = 0xE4, // ComPare X register, Zero Page ($E4 arg : CPX arg ;arg=0..$FF), MEM=arg OPCODE_SBC_ZP = 0xE5, // SuBtract with Carry, Zero Page ($E5 arg : SBC arg ;arg=0..$FF), MEM=arg OPCODE_INC_ZP = 0xE6, // INCrement memory, Zero Page ($E6 arg : INC arg ;arg=0..$FF), MEM=arg OPCODE_ILL_E7 = 0xE7, // illegal opcode OPCODE_INX = 0xE8, // INcrement X, Implied ($E8 : INX) OPCODE_SBC_IMM = 0xE9, // SuBtract with Carry, Immediate ($E9 arg : SBC #arg ;arg=0..$FF), MEM=PC+1 OPCODE_NOP = 0xEA, // NO oPeration, Implied ($EA : NOP) OPCODE_ILL_EB = 0xEB, // illegal opcode OPCODE_CPX_ABS = 0xEC, // ComPare X register, Absolute ($EC addrlo addrhi : CPX addr ;addr=0..$FFFF), MEM=addr OPCODE_SBC_ABS = 0xED, // SuBtract with Carry, Absolute ($ED addrlo addrhi : SBC addr ;addr=0..$FFFF), MEM=addr OPCODE_INC_ABS = 0xEE, // INCrement memory, Absolute ($EE addrlo addrhi : INC addr ;addr=0..$FFFF), MEM=addr OPCODE_ILL_EF = 0xEF, // illegal opcode OPCODE_BEQ_REL = 0xF0, // Branch on EQual, Relative ($F0 signoffs : BEQ signoffs ;signoffs=0..$FF [-128 ($80)..127 ($7F)]) OPCODE_SBC_IZY = 0xF1, // SuBtract with Carry, Indirect Indexed ($F1 arg : SBC (arg),Y ;arg=0..$FF), MEM=&arg+Y OPCODE_ILL_F2 = 0xF2, // illegal opcode OPCODE_ILL_F3 = 0xF3, // illegal opcode OPCODE_ILL_F4 = 0xF4, // illegal opcode OPCODE_SBC_ZPX = 0xF5, // SuBtract with Carry, Zero Page Indexed, X ($F5 arg : SBC arg,X ;arg=0..$FF), MEM=arg+X OPCODE_INC_ZPX = 0xF6, // INCrement memory, Zero Page Indexed, X ($F6 arg : INC arg,X ;arg=0..$FF), MEM=arg+X OPCODE_ILL_F7 = 0xF7, // illegal opcode OPCODE_SED = 0xF8, // SEt Decimal, Implied ($F8 : SED) OPCODE_SBC_ABY = 0xF9, // SuBtract with Carry, Absolute Indexed, Y ($F9 addrlo addrhi : SBC addr,Y ;addr=0..$FFFF), MEM=addr+Y OPCODE_ILL_FA = 0xFA, // illegal opcode OPCODE_ILL_FB = 0xFB, // illegal opcode OPCODE_ILL_FC = 0xFC, // illegal opcode OPCODE_SBC_ABX = 0xFD, // SuBtract with Carry, Absolute Indexed, X ($FD addrlo addrhi : SBC addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_INC_ABX = 0xFE, // INCrement memory, Absolute Indexed, X ($FE addrlo addrhi : INC addr,X ;addr=0..$FFFF), MEM=addr+X OPCODE_ILL_FF = 0xFF // illegal opcode }; class MKCpu; typedef void (MKCpu::*OpCodeHdlrFn)(); struct OpCode { int code; // the byte value of the opcode int addrmode; // addressing mode (see eAddrModes) int time; // # of cycles string amf; // assembler mnemonic OpCodeHdlrFn pfun; // opcoce handler function }; typedef map OpCodesMap; /* *------------------------------------------------------------------------------ bit -> 7 0 +---+---+---+---+---+---+---+---+ | N | V | | B | D | I | Z | C | <-- flag, 0/1 = reset/set +---+---+---+---+---+---+---+---+ N = NEGATIVE. Set if bit 7 of the accumulator is set. V = OVERFLOW. Set if the addition of two like-signed numbers or the subtraction of two unlike-signed numbers produces a result greater than +127 or less than -128. B = BRK COMMAND. Set if an interrupt caused by a BRK, reset if caused by an external interrupt. D = DECIMAL MODE. Set if decimal mode active. I = IRQ DISABLE. Set if maskable interrupts are disabled. Z = ZERO. Set if the result of the last operation (load/inc/dec/ add/sub) was zero. C = CARRY. Set if the add produced a carry, or if the subtraction produced a borrow. Also holds bits after a logical shift. *------------------------------------------------------------------------------ */ enum eCpuFlagMasks { FLAGS_CARRY = 0x01, // 0: C FLAGS_ZERO = 0x02, // 1: Z FLAGS_IRQ = 0x04, // 2: I FLAGS_DEC = 0x08, // 3: D FLAGS_BRK = 0x10, // 4: B (Clear if interrupt vectoring, set if BRK or PHP) FLAGS_UNUSED = 0x20, // 5: Unused flag (always set). FLAGS_OVERFLOW = 0x40, // 6: V FLAGS_SIGN = 0x80 // 7: N }; enum eLogicOps { LOGOP_OR, LOGOP_AND, LOGOP_EOR }; class MKCpu { public: bool mExitAtLastRTS; MKCpu(); MKCpu(Memory *pmem); ~MKCpu(); Regs *ExecOpcode(unsigned short memaddr); Regs *GetRegs(); void SetRegs(Regs r); queue GetExecHistory(); void EnableExecHistory(bool enexehist); bool IsExecHistoryEnabled(); unsigned short Disassemble(unsigned short addr, char *instrbuf); // Disassemble instruction in memory, return next instruction addr. void Reset(); // reset CPU void Interrupt(); // Interrupt ReQuest (IRQ) protected: private: // keeps all needed data to disassemble op-codes in execute history queue struct OpCodeHistItem { unsigned char Acc; // 8-bit accumulator unsigned char IndX; // 8-bit index register X unsigned char IndY; // 8-bit index register Y unsigned char Flags; // CPU flags unsigned char PtrStack; // 8-bit stack pointer (0-255). unsigned short LastAddr; // PC at the time of previous op-code string LastInstr; // instruction and argument executed in previous step int LastOpCode; // op-code of last instruction unsigned short LastArg; // argument to the last instruction int LastAddrMode; // addressing mode of last instruction }; struct Regs mReg; // CPU registers Memory *mpMem; // pointer to memory object bool mLocalMem; // true - memory locally allocated OpCodesMap mOpCodesMap; // hash table of all opcodes int mAddrModesLen[ADDRMODE_LENGTH]; // array of instructions lengths per addressing mode string mArgFmtTbl[ADDRMODE_LENGTH]; // array of instructions assembly formats per addressing mode queue mExecHistory; // keep the op-codes execute history bool mEnableHistory; // enable/disable execute history void InitCpu(); void SetFlags(unsigned char reg); // set CPU flags ZERO and SIGN based on Acc, X or Y unsigned char ShiftLeft(unsigned char arg8); // Arithmetic Shift Left, set Carry flag unsigned char ShiftRight(unsigned char arg8); // Logical Shift Right, update flags NZC. unsigned char RotateLeft(unsigned char arg8); // Rotate left, Carry to bit 0, bit 7 to Carry, update flags N and Z. unsigned char RotateRight(unsigned char arg8); // Rotate left, Carry to bit 7, bit 0 to Carry, update flags N and Z. unsigned short GetArg16(unsigned char offs); // Get 2-byte argument, add offset, increase PC. void LogicOpAcc(unsigned short addr, int logop); // Perform logical bitwise operation between memory location and Acc. // Result in Acc. Set flags. unsigned short ComputeRelJump(unsigned char offs); // Compute new PC based on relative offset. unsigned short ComputeRelJump(unsigned short addr, unsigned char offs); // Compute new address after branch based on relative offset. unsigned char Conv2Bcd(unsigned short v); // Convert number to BCD representation. unsigned short Bcd2Num(unsigned char v); // Convert BCD code to number. bool CheckFlag(unsigned char flag); // Return true if given CPU status flag is set, false otherwise. void SetFlag(bool set, unsigned char flag); // Set or unset processor status flag. unsigned char AddWithCarry(unsigned char mem8); // Add With Carry, update flags and Acc. unsigned char SubWithCarry(unsigned char mem8); // Subtract With Carry, update flags and Acc. unsigned short GetAddrWithMode(int mode); // Get address of the byte argument with specified addr. mode unsigned short GetArgWithMode(unsigned short opcaddr, int mode); // Get argument from address with specified addr. mode unsigned short Disassemble(OpCodeHistItem *histit); // Disassemble op-code exec history item //void Add2History(string s); // add entry to op-codes execute history void Add2History(OpCodeHistItem histitem); // add entry to op-codes execute history bool PageBoundary(unsigned short startaddr, unsigned short endaddr); // detect if page boundary was crossed // opcode execute methods void OpCodeBrk(); void OpCodeNop(); void OpCodeLdaIzx(); void OpCodeLdaZp(); void OpCodeLdaImm(); void OpCodeLdaAbs(); void OpCodeLdaIzy(); void OpCodeLdaZpx(); void OpCodeLdaAby(); void OpCodeLdaAbx(); void OpCodeLdxImm(); void OpCodeLdxZp(); void OpCodeLdxAbs(); void OpCodeLdxZpy(); void OpCodeLdxAby(); void OpCodeLdyImm(); void OpCodeLdyZp(); void OpCodeLdyAbs(); void OpCodeLdyZpx(); void OpCodeLdyAbx(); void OpCodeTax(); void OpCodeTay(); void OpCodeTxa(); void OpCodeTya(); void OpCodeTsx(); void OpCodeTxs(); void OpCodeStaIzx(); void OpCodeStaZp(); void OpCodeStaAbs(); void OpCodeStaIzy(); void OpCodeStaZpx(); void OpCodeStaAby(); void OpCodeStaAbx(); void OpCodeStxZp(); void OpCodeStxAbs(); void OpCodeStxZpy(); void OpCodeStyZp(); void OpCodeStyAbs(); void OpCodeStyZpx(); void OpCodeBneRel(); void OpCodeBeqRel(); void OpCodeBplRel(); void OpCodeBmiRel(); void OpCodeBvcRel(); void OpCodeBvsRel(); void OpCodeBccRel(); void OpCodeBcsRel(); void OpCodeIncZp(); void OpCodeIncAbs(); void OpCodeIncZpx(); void OpCodeIncAbx(); void OpCodeInx(); void OpCodeDex(); void OpCodeIny(); void OpCodeDey(); void OpCodeJmpAbs(); void OpCodeJmpInd(); void OpCodeOraIzx(); void OpCodeOraZp(); void OpCodeOraImm(); void OpCodeOraAbs(); void OpCodeOraIzy(); void OpCodeOraZpx(); void OpCodeOraAby(); void OpCodeOraAbx(); void OpCodeAslZp(); void OpCodeAslAcc(); void OpCodeAslAbs(); void OpCodeAslZpx(); void OpCodeAslAbx(); void OpCodeJsrAbs(); void OpCodeAndIzx(); void OpCodeAndZp(); void OpCodeAndImm(); void OpCodeAndAbs(); void OpCodeAndIzy(); void OpCodeAndZpx(); void OpCodeAndAby(); void OpCodeAndAbx(); void OpCodeBitZp(); void OpCodeBitAbs(); void OpCodeRolZp(); void OpCodeRolAcc(); void OpCodeRolAbs(); void OpCodeRolZpx(); void OpCodeRolAbx(); void OpCodePhp(); void OpCodePha(); void OpCodePlp(); void OpCodePla(); void OpCodeClc(); void OpCodeSec(); void OpCodeCli(); void OpCodeClv(); void OpCodeCld(); void OpCodeSed(); void OpCodeSei(); void OpCodeRti(); void OpCodeRts(); void OpCodeEorIzx(); void OpCodeEorZp(); void OpCodeEorImm(); void OpCodeEorAbs(); void OpCodeEorIzy(); void OpCodeEorZpx(); void OpCodeEorAby(); void OpCodeEorAbx(); void OpCodeLsrZp(); void OpCodeLsrAcc(); void OpCodeLsrAbs(); void OpCodeLsrZpx(); void OpCodeLsrAbx(); void OpCodeAdcIzx(); void OpCodeAdcZp(); void OpCodeAdcImm(); void OpCodeAdcAbs(); void OpCodeAdcIzy(); void OpCodeAdcZpx(); void OpCodeAdcAby(); void OpCodeAdcAbx(); void OpCodeRorZp(); void OpCodeRorAcc(); void OpCodeRorAbs(); void OpCodeRorZpx(); void OpCodeRorAbx(); void OpCodeCpyImm(); void OpCodeCpyZp(); void OpCodeCpyAbs(); void OpCodeCmpIzx(); void OpCodeCmpZp(); void OpCodeCmpImm(); void OpCodeCmpAbs(); void OpCodeCmpIzy(); void OpCodeCmpZpx(); void OpCodeCmpAby(); void OpCodeCmpAbx(); void OpCodeDecZp(); void OpCodeDecAbs(); void OpCodeDecZpx(); void OpCodeDecAbx(); void OpCodeCpxImm(); void OpCodeCpxZp(); void OpCodeCpxAbs(); void OpCodeSbcZp(); void OpCodeSbcAbs(); void OpCodeSbcIzx(); void OpCodeSbcIzy(); void OpCodeSbcZpx(); void OpCodeSbcAby(); void OpCodeSbcAbx(); void OpCodeSbcImm(); void OpCodeDud(); }; } // namespace MKBasic #endif