{ MOS6502 v1.0 - a MOS 6502 CPU emulator for Delphi 10.1 Berlin+ by Dennis Spreen http://blog.spreendigital.de/2017/03/09/mos6502-delphi/ MIT License Copyright (c) 2017 Gianluca Ghettini (C++ implementation) Copyright (c) 2017 Dennis D. Spreen (Delphi implementation) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. } unit MOS6502; interface type TMOS6502 = class const // Status bits NEGATIVE = $80; OVERFLOW = $40; CONSTANT = $20; BREAK = $10; DECIMAL = $08; INTERRUPT = $04; ZERO = $02; CARRY = $01; // IRQ, reset, NMI vectors IRQVECTORH: Word = $FFFF; IRQVECTORL: Word = $FFFE; RSTVECTORH: Word = $FFFD; RSTVECTORL: Word = $FFFC; NMIVECTORH: Word = $FFFB; NMIVECTORL: Word = $FFFA; type TCodeExec = procedure(Src: Word) of object; TAddrExec = function: Word of object; TInstr = record Addr: TAddrExec; Code: TCodeExec; end; PInstr = ^TInstr; TBusWrite = procedure(Adr: Word; Value: Byte) of object; TBusRead = function(Adr: Word): Byte of object; procedure SET_NEGATIVE(const Value: Boolean); inline; procedure SET_OVERFLOW(const Value: Boolean); inline; procedure SET_CONSTANT(const Value: Boolean); inline; procedure SET_BREAK(const Value: Boolean); inline; procedure SET_DECIMAL(const Value: Boolean); inline; procedure SET_INTERRUPT(const Value: Boolean); inline; procedure SET_ZERO(const Value: Boolean); inline; procedure SET_CARRY(const Value: Boolean); inline; function IF_NEGATIVE: Boolean; inline; function IF_OVERFLOW: Boolean; inline; function IF_CONSTANT: Boolean; inline; function IF_BREAK: Boolean; inline; function IF_DECIMAL: Boolean; inline; function IF_INTERRUPT: Boolean; inline; function IF_ZERO: Boolean; inline; function IF_CARRY: Byte; inline; private // addressing modes function Addr_ACC: Word; // ACCUMULATOR function Addr_IMM: Word; // IMMEDIATE function Addr_ABS: Word; // ABSOLUTE function Addr_ZER: Word; // ZERO PAGE function Addr_ZEX: Word; // INDEXED-X ZERO PAGE function Addr_ZEY: Word; // INDEXED-Y ZERO PAGE function Addr_ABX: Word; // INDEXED-X ABSOLUTE function Addr_ABY: Word; // INDEXED-Y ABSOLUTE function Addr_IMP: Word; // IMPLIED function Addr_REL: Word; // RELATIVE function Addr_INX: Word; // INDEXED-X INDIRECT function Addr_INY: Word; // INDEXED-Y INDIRECT function Addr_ABI: Word; // ABSOLUTE INDIRECT // opcodes (grouped as per datasheet) procedure Op_ADC(Src: Word); procedure Op_AND(Src: Word); procedure Op_ASL(Src: Word); procedure Op_ASL_ACC(Src: Word); procedure Op_BCC(Src: Word); procedure Op_BCS(Src: Word); procedure Op_BEQ(Src: Word); procedure Op_BIT(Src: Word); procedure Op_BMI(Src: Word); procedure Op_BNE(Src: Word); procedure Op_BPL(Src: Word); procedure Op_BRK(Src: Word); procedure Op_BVC(Src: Word); procedure Op_BVS(Src: Word); procedure Op_CLC(Src: Word); procedure Op_CLD(Src: Word); procedure Op_CLI(Src: Word); procedure Op_CLV(Src: Word); procedure Op_CMP(Src: Word); procedure Op_CPX(Src: Word); procedure Op_CPY(Src: Word); procedure Op_DEC(Src: Word); procedure Op_DEX(Src: Word); procedure Op_DEY(Src: Word); procedure Op_EOR(Src: Word); procedure Op_INC(Src: Word); procedure Op_INX(Src: Word); procedure Op_INY(Src: Word); procedure Op_JMP(Src: Word); procedure Op_JSR(Src: Word); procedure Op_LDA(Src: Word); procedure Op_LDX(Src: Word); procedure Op_LDY(Src: Word); procedure Op_LSR(Src: Word); procedure Op_LSR_ACC(Src: Word); procedure Op_NOP(Src: Word); procedure Op_ORA(Src: Word); procedure Op_PHA(Src: Word); procedure Op_PHP(Src: Word); procedure Op_PLA(Src: Word); procedure Op_PLP(Src: Word); procedure Op_ROL(Src: Word); procedure Op_ROL_ACC(Src: Word); procedure Op_ROR(Src: Word); procedure Op_ROR_ACC(Src: Word); procedure Op_RTI(Src: Word); procedure Op_RTS(Src: Word); procedure Op_SBC(Src: Word); procedure Op_SEC(Src: Word); procedure Op_SED(Src: Word); procedure Op_SEI(Src: Word); procedure Op_STA(Src: Word); procedure Op_STX(Src: Word); procedure Op_STY(Src: Word); procedure Op_TAX(Src: Word); procedure Op_TAY(Src: Word); procedure Op_TSX(Src: Word); procedure Op_TXA(Src: Word); procedure Op_TXS(Src: Word); procedure Op_TYA(Src: Word); procedure Op_ILLEGAL(Src: Word); // stack operations procedure StackPush(const Value: Byte); inline; function StackPop: Byte; inline; protected // consumed clock cycles Cycles: Cardinal; InstrTable: Array [0 .. 255] of TInstr; // read/write callbacks Read: TBusRead; Write: TBusWrite; // program counter Pc: Word; // registers A: Byte; // accumulator X: Byte; // X-index Y: Byte; // Y-index // stack pointer Sp: Byte; // status register Status: Byte; IllegalOpcode: Boolean; public constructor Create(R: TBusRead; W: TBusWrite); overload; virtual; procedure NMI; virtual; procedure IRQ; virtual; procedure Reset; virtual; procedure Step; virtual; end; implementation { TMOS6502 } constructor TMOS6502.Create(R: TBusRead; W: TBusWrite); var Instr: TInstr; I: Integer; begin Write := W; Read := R; // fill jump table with ILLEGALs Instr.Addr := Addr_IMP; Instr.code := Op_ILLEGAL; for I := 0 to 256 - 1 do InstrTable[I] := Instr; // insert opcodes Instr.Addr := Addr_IMM; Instr.Code := Op_ADC; InstrTable[$69] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_ADC; InstrTable[$6D] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_ADC; InstrTable[$65] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_ADC; InstrTable[$61] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_ADC; InstrTable[$71] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_ADC; InstrTable[$75] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_ADC; InstrTable[$7D] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_ADC; InstrTable[$79] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_AND; InstrTable[$29] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_AND; InstrTable[$2D] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_AND; InstrTable[$25] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_AND; InstrTable[$21] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_AND; InstrTable[$31] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_AND; InstrTable[$35] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_AND; InstrTable[$3D] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_AND; InstrTable[$39] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_ASL; InstrTable[$0E] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_ASL; InstrTable[$06] := Instr; Instr.Addr := Addr_ACC; Instr.Code := Op_ASL_ACC; InstrTable[$0A] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_ASL; InstrTable[$16] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_ASL; InstrTable[$1E] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BCC; InstrTable[$90] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BCS; InstrTable[$B0] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BEQ; InstrTable[$F0] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_BIT; InstrTable[$2C] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_BIT; InstrTable[$24] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BMI; InstrTable[$30] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BNE; InstrTable[$D0] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BPL; InstrTable[$10] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_BRK; InstrTable[$00] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BVC; InstrTable[$50] := Instr; Instr.Addr := Addr_REL; Instr.Code := Op_BVS; InstrTable[$70] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_CLC; InstrTable[$18] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_CLD; InstrTable[$D8] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_CLI; InstrTable[$58] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_CLV; InstrTable[$B8] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_CMP; InstrTable[$C9] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_CMP; InstrTable[$CD] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_CMP; InstrTable[$C5] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_CMP; InstrTable[$C1] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_CMP; InstrTable[$D1] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_CMP; InstrTable[$D5] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_CMP; InstrTable[$DD] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_CMP; InstrTable[$D9] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_CPX; InstrTable[$E0] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_CPX; InstrTable[$EC] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_CPX; InstrTable[$E4] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_CPY; InstrTable[$C0] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_CPY; InstrTable[$CC] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_CPY; InstrTable[$C4] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_DEC; InstrTable[$CE] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_DEC; InstrTable[$C6] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_DEC; InstrTable[$D6] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_DEC; InstrTable[$DE] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_DEX; InstrTable[$CA] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_DEY; InstrTable[$88] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_EOR; InstrTable[$49] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_EOR; InstrTable[$4D] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_EOR; InstrTable[$45] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_EOR; InstrTable[$41] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_EOR; InstrTable[$51] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_EOR; InstrTable[$55] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_EOR; InstrTable[$5D] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_EOR; InstrTable[$59] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_INC; InstrTable[$EE] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_INC; InstrTable[$E6] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_INC; InstrTable[$F6] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_INC; InstrTable[$FE] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_INX; InstrTable[$E8] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_INY; InstrTable[$C8] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_JMP; InstrTable[$4C] := Instr; Instr.Addr := Addr_ABI; Instr.Code := Op_JMP; InstrTable[$6C] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_JSR; InstrTable[$20] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_LDA; InstrTable[$A9] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_LDA; InstrTable[$AD] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_LDA; InstrTable[$A5] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_LDA; InstrTable[$A1] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_LDA; InstrTable[$B1] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_LDA; InstrTable[$B5] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_LDA; InstrTable[$BD] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_LDA; InstrTable[$B9] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_LDX; InstrTable[$A2] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_LDX; InstrTable[$AE] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_LDX; InstrTable[$A6] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_LDX; InstrTable[$BE] := Instr; Instr.Addr := Addr_ZEY; Instr.Code := Op_LDX; InstrTable[$B6] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_LDY; InstrTable[$A0] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_LDY; InstrTable[$AC] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_LDY; InstrTable[$A4] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_LDY; InstrTable[$B4] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_LDY; InstrTable[$BC] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_LSR; InstrTable[$4E] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_LSR; InstrTable[$46] := Instr; Instr.Addr := Addr_ACC; Instr.Code := Op_LSR_ACC; InstrTable[$4A] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_LSR; InstrTable[$56] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_LSR; InstrTable[$5E] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_NOP; InstrTable[$EA] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_ORA; InstrTable[$09] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_ORA; InstrTable[$0D] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_ORA; InstrTable[$05] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_ORA; InstrTable[$01] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_ORA; InstrTable[$11] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_ORA; InstrTable[$15] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_ORA; InstrTable[$1D] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_ORA; InstrTable[$19] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_PHA; InstrTable[$48] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_PHP; InstrTable[$08] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_PLA; InstrTable[$68] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_PLP; InstrTable[$28] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_ROL; InstrTable[$2E] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_ROL; InstrTable[$26] := Instr; Instr.Addr := Addr_ACC; Instr.Code := Op_ROL_ACC; InstrTable[$2A] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_ROL; InstrTable[$36] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_ROL; InstrTable[$3E] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_ROR; InstrTable[$6E] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_ROR; InstrTable[$66] := Instr; Instr.Addr := Addr_ACC; Instr.Code := Op_ROR_ACC; InstrTable[$6A] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_ROR; InstrTable[$76] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_ROR; InstrTable[$7E] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_RTI; InstrTable[$40] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_RTS; InstrTable[$60] := Instr; Instr.Addr := Addr_IMM; Instr.Code := Op_SBC; InstrTable[$E9] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_SBC; InstrTable[$ED] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_SBC; InstrTable[$E5] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_SBC; InstrTable[$E1] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_SBC; InstrTable[$F1] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_SBC; InstrTable[$F5] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_SBC; InstrTable[$FD] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_SBC; InstrTable[$F9] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_SEC; InstrTable[$38] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_SED; InstrTable[$F8] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_SEI; InstrTable[$78] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_STA; InstrTable[$8D] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_STA; InstrTable[$85] := Instr; Instr.Addr := Addr_INX; Instr.Code := Op_STA; InstrTable[$81] := Instr; Instr.Addr := Addr_INY; Instr.Code := Op_STA; InstrTable[$91] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_STA; InstrTable[$95] := Instr; Instr.Addr := Addr_ABX; Instr.Code := Op_STA; InstrTable[$9D] := Instr; Instr.Addr := Addr_ABY; Instr.Code := Op_STA; InstrTable[$99] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_STX; InstrTable[$8E] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_STX; InstrTable[$86] := Instr; Instr.Addr := Addr_ZEY; Instr.Code := Op_STX; InstrTable[$96] := Instr; Instr.Addr := Addr_ABS; Instr.Code := Op_STY; InstrTable[$8C] := Instr; Instr.Addr := Addr_ZER; Instr.Code := Op_STY; InstrTable[$84] := Instr; Instr.Addr := Addr_ZEX; Instr.Code := Op_STY; InstrTable[$94] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_TAX; InstrTable[$AA] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_TAY; InstrTable[$A8] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_TSX; InstrTable[$BA] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_TXA; InstrTable[$8A] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_TXS; InstrTable[$9A] := Instr; Instr.Addr := Addr_IMP; Instr.Code := Op_TYA; InstrTable[$98] := Instr; end; procedure TMOS6502.SET_NEGATIVE(const Value: Boolean); begin if Value then Status := Status or NEGATIVE else Status := Status and (not NEGATIVE); end; procedure TMOS6502.SET_OVERFLOW(const Value: Boolean); begin if Value then Status := Status or OVERFLOW else Status := Status and (not OVERFLOW); end; procedure TMOS6502.SET_CONSTANT(const Value: Boolean); begin if Value then Status := Status or CONSTANT else Status := Status and (not CONSTANT); end; procedure TMOS6502.SET_BREAK(const Value: Boolean); begin if Value then Status := Status or BREAK else Status := Status and (not BREAK); end; procedure TMOS6502.SET_DECIMAL(const Value: Boolean); begin if Value then Status := Status or DECIMAL else Status := Status and (not DECIMAL); end; procedure TMOS6502.SET_INTERRUPT(const Value: Boolean); begin if Value then Status := Status or INTERRUPT else Status := Status and (not INTERRUPT); end; procedure TMOS6502.SET_ZERO(const Value: Boolean); begin if Value then Status := Status or ZERO else Status := Status and (not ZERO); end; procedure TMOS6502.SET_CARRY(const Value: Boolean); begin if Value then Status := Status or CARRY else Status := Status and (not CARRY); end; function TMOS6502.IF_NEGATIVE: Boolean; begin Result := ((Status and NEGATIVE) <> 0); end; function TMOS6502.IF_OVERFLOW: Boolean; begin Result := ((Status and OVERFLOW) <> 0); end; function TMOS6502.IF_CONSTANT: Boolean; begin Result := ((Status and CONSTANT) <> 0); end; function TMOS6502.IF_BREAK: Boolean; begin Result := ((Status and BREAK) <> 0); end; function TMOS6502.IF_DECIMAL: Boolean; begin Result := ((Status and DECIMAL) <> 0); end; function TMOS6502.IF_INTERRUPT: Boolean; begin Result := ((Status and INTERRUPT) <> 0); end; function TMOS6502.IF_ZERO: Boolean; begin Result := ((Status and ZERO) <> 0); end; function TMOS6502.IF_CARRY: Byte; begin if (Status and CARRY) <> 0 then Result := 1 else Result := 0; end; function TMOS6502.Addr_ACC: Word; begin Result := 0; // not used end; function TMOS6502.Addr_IMM: Word; begin Result := Pc; Inc(Pc); end; function TMOS6502.Addr_ABS: Word; var AddrL, AddrH, Addr: Word; begin AddrL := Read(Pc); Inc(Pc); AddrH := Read(Pc); Inc(Pc); Addr := AddrL + (AddrH shl 8); Result := Addr; end; function TMOS6502.Addr_ZER: Word; begin Result := Read(Pc); Inc(Pc); end; function TMOS6502.Addr_IMP: Word; begin Result := 0; // not used end; function TMOS6502.Addr_REL: Word; var Offset, Addr: Word; begin Offset := Read(Pc); Inc(Pc); if (Offset and $80) <> 0 then Offset := Offset or $FF00; Addr := Pc + Offset; Result := Addr; end; function TMOS6502.Addr_ABI: Word; var AddrL, AddrH, EffL, EffH, Abs, Addr: Word; begin AddrL := Read(Pc); Inc(Pc); AddrH := Read(Pc); Inc(Pc); Abs := (AddrH shl 8) or AddrL; EffL := Read(Abs); EffH := Read((Abs and $FF00) + ((Abs + 1) and $00FF)); Addr := EffL + $100 * EffH; Result := Addr; end; function TMOS6502.Addr_ZEX: Word; var Addr: Word; begin Addr := (Read(Pc) + X) mod 256; Inc(Pc); Result := Addr; end; function TMOS6502.Addr_ZEY: Word; var Addr: Word; begin Addr := (Read(Pc) + Y) mod 256; Inc(Pc); Result := Addr; end; function TMOS6502.Addr_ABX: Word; var AddrL: Word; AddrH: Word; Addr: Word; begin AddrL := Read(Pc); Inc(Pc); AddrH := Read(Pc); Inc(Pc); Addr := AddrL + (AddrH shl 8) + X; Result := Addr; end; function TMOS6502.Addr_ABY: Word; var AddrL: Word; AddrH: Word; Addr: Word; begin AddrL := Read(Pc); Inc(Pc); AddrH := Read(Pc); Inc(Pc); Addr := AddrL + (AddrH shl 8) + Y; Result := Addr; end; function TMOS6502.Addr_INX: Word; var ZeroL, ZeroH: Word; Addr: Word; begin ZeroL := (Read(Pc) + X) mod 256; Inc(Pc); ZeroH := (ZeroL + 1) mod 256; Addr := Read(ZeroL) + (Read(ZeroH) shl 8); Result := Addr; end; function TMOS6502.Addr_INY: Word; var ZeroL, ZeroH: Word; Addr: Word; begin ZeroL := Read(Pc); Inc(Pc); ZeroH := (ZeroL + 1) mod 256; Addr := Read(ZeroL) + (Read(ZeroH) shl 8) + Y; Result := Addr; end; procedure TMOS6502.Reset; begin A := $aa; X := $00; Y := $00; Status := BREAK or INTERRUPT OR ZERO or CONSTANT; Sp := $FD; Pc := (Read(RSTVECTORH) shl 8) + Read(RSTVECTORL); // load PC from reset vector Cycles := 6; // according to the datasheet, the reset routine takes 6 clock cycles IllegalOpcode := false; end; procedure TMOS6502.StackPush(const Value: Byte); begin Write($0100 + Sp, Value); if Sp = $00 then Sp := $FF else Dec(Sp); end; function TMOS6502.StackPop: Byte; begin if Sp = $FF then Sp := $00 else Inc(Sp); Result := Read($0100 + Sp); end; procedure TMOS6502.IRQ; begin if (not IF_INTERRUPT) then begin SET_BREAK(False); StackPush((Pc shr 8) and $FF); StackPush(Pc and $FF); StackPush(Status); SET_INTERRUPT(True); Pc := (Read(IRQVECTORH) shl 8) + Read(IRQVECTORL); end; end; procedure TMOS6502.NMI; begin SET_BREAK(false); StackPush((Pc shr 8) and $FF); StackPush(Pc and $FF); StackPush(Status); SET_INTERRUPT(True); Pc := (Read(NMIVECTORH) shl 8) + Read(NMIVECTORL); end; procedure TMOS6502.Step; var Opcode: Byte; Instr: PInstr; Src: Word; begin // fetch Opcode := Read(Pc); Inc(Pc); // decode and execute Instr := @InstrTable[Opcode]; Src := Instr.Addr; Instr.Code(Src); Inc(Cycles); end; procedure TMOS6502.Op_ILLEGAL(Src: Word); begin IllegalOpcode := true; end; procedure TMOS6502.Op_AND(Src: Word); var M: Byte; Res: Byte; begin M := Read(Src); Res := M and A; SET_NEGATIVE((Res and $80) <> 0); SET_ZERO(Res = 0); A := Res; end; procedure TMOS6502.Op_ASL(Src: Word); var M: Byte; begin M := Read(Src); SET_CARRY((M and $80) <> 0); M := M shl 1; M := M and $FF; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Write(Src, M); end; procedure TMOS6502.Op_ASL_ACC(Src: Word); var M: Byte; begin M := A; SET_CARRY((M and $80) <> 0); M := M shl 1; M := M and $FF; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_BCC(Src: Word); begin if IF_CARRY = 0 then Pc := Src; end; procedure TMOS6502.Op_BCS(Src: Word); begin if IF_CARRY = 1 then Pc := Src; end; procedure TMOS6502.Op_BEQ(Src: Word); begin if IF_ZERO then Pc := Src; end; procedure TMOS6502.Op_BIT(Src: Word); var M: Byte; Res: Byte; begin M := Read(Src); Res := M and A; SET_NEGATIVE((Res and $80) <> 0); Status := (Status and $3F) or (M and $C0); SET_ZERO(Res = 0); end; procedure TMOS6502.Op_BMI(Src: Word); begin if IF_NEGATIVE then Pc := Src; end; procedure TMOS6502.Op_BNE(Src: Word); begin if not (IF_ZERO) then Pc := Src; end; procedure TMOS6502.Op_BPL(Src: Word); begin if not (IF_NEGATIVE) then Pc := Src; end; procedure TMOS6502.Op_BRK(Src: Word); begin Inc(Pc); StackPush((Pc shr 8) and $FF); StackPush(Pc and $FF); StackPush(Status or BREAK); SET_INTERRUPT(True); Pc := (Read(IRQVECTORH) shl 8) + Read(IRQVECTORL); end; procedure TMOS6502.Op_BVC(Src: Word); begin if not (IF_OVERFLOW) then Pc := Src; end; procedure TMOS6502.Op_BVS(Src: Word); begin if IF_OVERFLOW then Pc := Src; end; procedure TMOS6502.Op_CLC(Src: Word); begin SET_CARRY(False); end; procedure TMOS6502.Op_CLD(Src: Word); begin SET_DECIMAL(False); end; procedure TMOS6502.Op_CLI(Src: Word); begin SET_INTERRUPT(False); end; procedure TMOS6502.Op_CLV(Src: Word); begin SET_OVERFLOW(False); end; procedure TMOS6502.Op_CMP(Src: Word); var Tmp: Cardinal; begin Tmp := A - Read(Src); SET_CARRY(Tmp < $100); SET_NEGATIVE((Tmp and $80) <> 0); SET_ZERO((Tmp and $FF)=0); end; procedure TMOS6502.Op_CPX(Src: Word); var Tmp: Cardinal; begin Tmp := X - Read(Src); SET_CARRY(Tmp < $100); SET_NEGATIVE((Tmp and $80) <> 0); SET_ZERO((Tmp and $FF)=0); end; procedure TMOS6502.Op_CPY(Src: Word); var Tmp: Cardinal; begin Tmp := Y - Read(Src); SET_CARRY(Tmp < $100); SET_NEGATIVE((Tmp and $80) <> 0); SET_ZERO((Tmp and $FF)=0); end; procedure TMOS6502.Op_DEC(Src: Word); var M: Byte; begin M := Read(Src); M := M - 1; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Write(Src, M); end; procedure TMOS6502.Op_DEX(Src: Word); var M: Byte; begin M := X; M := M - 1; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); X := M; end; procedure TMOS6502.Op_DEY(Src: Word); var M: Byte; begin M := Y; M := M - 1; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Y := M; end; procedure TMOS6502.Op_EOR(Src: Word); var M: Byte; begin M := Read(Src); M := A xor M; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_INC(Src: Word); var M: Byte; begin M := Read(Src); M := M + 1; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Write(Src, M); end; procedure TMOS6502.Op_INX(Src: Word); var M: Byte; begin M := X; M := M + 1; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); X := M; end; procedure TMOS6502.Op_INY(Src: Word); var M: Byte; begin M := Y; M := M + 1; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Y := M; end; procedure TMOS6502.Op_JMP(Src: Word); begin Pc := Src; end; procedure TMOS6502.Op_JSR(Src: Word); begin Dec(Pc); StackPush((Pc shr 8) and $FF); StackPush(Pc and $FF); Pc := Src; end; procedure TMOS6502.Op_LDA(Src: Word); var M: Byte; begin M := Read(Src); SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_LDX(Src: Word); var M: Byte; begin M := Read(Src); SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); X := M; end; procedure TMOS6502.Op_LDY(Src: Word); var M: Byte; begin M := Read(Src); SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Y := M; end; procedure TMOS6502.Op_LSR(Src: Word); var M: Byte; begin M := Read(Src); SET_CARRY((M and $01) <> 0); M := M shr 1; SET_NEGATIVE(False); SET_ZERO(M = 0); Write(Src, M); end; procedure TMOS6502.Op_LSR_ACC(Src: Word); var M: Byte; begin M := A; SET_CARRY((M and $01) <> 0); M := M shr 1; SET_NEGATIVE(False); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_NOP(Src: Word); begin // no operation end; procedure TMOS6502.Op_ORA(Src: Word); var M: Byte; begin M := Read(Src); M := A or M; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_PHA(Src: Word); begin StackPush(A); end; procedure TMOS6502.Op_PHP(Src: Word); begin StackPush(Status or BREAK); end; procedure TMOS6502.Op_PLA(Src: Word); begin A := StackPop; SET_NEGATIVE((A and $80) <> 0); SET_ZERO(A = 0); end; procedure TMOS6502.Op_PLP(Src: Word); begin Status := StackPop; SET_CONSTANT(True); end; procedure TMOS6502.Op_ROL(Src: Word); var M: Word; begin M := Read(Src); M := M shl 1; if IF_CARRY = 1 then M := M or $01; SET_CARRY(M > $FF); M := M and $FF; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Write(Src, M); end; procedure TMOS6502.Op_ROL_ACC(Src: Word); var M: Word; begin M := A; M := M shl 1; if IF_CARRY = 1 then M := M or $01; SET_CARRY(M > $FF); M := M and $FF; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_ROR(Src: Word); var M: Word; begin M := Read(Src); if IF_CARRY = 1 then M := M or $100; SET_CARRY((M and $01) <> 0); M := M shr 1; M := M and $FF; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Write(Src, M); end; procedure TMOS6502.Op_ROR_ACC(Src: Word); var M: Word; begin M := A; if IF_CARRY = 1 then M := M or $100; SET_CARRY((M and $01) <> 0); M := M shr 1; M := M and $FF; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_RTI(Src: Word); var Lo, Hi: Byte; begin Status := StackPop; Lo := StackPop; Hi := StackPop; Pc := (Hi shl 8) or Lo; end; procedure TMOS6502.Op_RTS(Src: Word); var Lo, Hi: Byte; begin Lo := StackPop; Hi := StackPop; Pc := (Hi shl 8) or Lo + 1; end; procedure TMOS6502.Op_ADC(Src: Word); var M: Byte; Tmp: Cardinal; begin M := Read(Src); Tmp := M + A + IF_CARRY; SET_ZERO((Tmp and $FF)=0); if IF_DECIMAL then begin if (((A and $F) + (M and $F) + IF_CARRY) > 9) then Tmp := Tmp + 6; SET_NEGATIVE((Tmp and $80) <> 0); SET_OVERFLOW( (((A xor M) and $80) = 0) and (((A xor Tmp) and $80) <> 0)); if Tmp > $99 then Tmp := Tmp + $60; SET_CARRY(Tmp > $99); end else begin SET_NEGATIVE((Tmp and $80) <> 0); SET_OVERFLOW( (((A xor M) and $80)=0) and (((A xor Tmp) and $80) <> 0)); SET_CARRY(Tmp > $FF); end; A := Tmp and $FF; end; procedure TMOS6502.Op_SBC(Src: Word); var M: Byte; Tmp: Word; begin M := Read(Src); Tmp := A - M - (1-IF_CARRY); SET_NEGATIVE((Tmp and $80) <> 0); SET_ZERO((Tmp and $FF) = 0); SET_OVERFLOW( (((A xor Tmp) and $80) <> 0) and (((A xor M) and $80) <> 0)); if IF_DECIMAL then begin if (((A and $0F) - (1-IF_CARRY)) < (M and $0F)) then Tmp := Tmp - 6; if Tmp > $99 then Tmp := Tmp - $60; end; SET_CARRY(Tmp < $100); A := (Tmp and $FF); end; procedure TMOS6502.Op_SEC(Src: Word); begin SET_CARRY(True); end; procedure TMOS6502.Op_SED(Src: Word); begin SET_DECIMAL(True); end; procedure TMOS6502.Op_SEI(Src: Word); begin SET_INTERRUPT(True); end; procedure TMOS6502.Op_STA(Src: Word); begin Write(Src, A); end; procedure TMOS6502.Op_STX(Src: Word); begin Write(Src, X); end; procedure TMOS6502.Op_STY(Src: Word); begin Write(Src, Y); end; procedure TMOS6502.Op_TAX(Src: Word); var M: Byte; begin M := A; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); X := M; end; procedure TMOS6502.Op_TAY(Src: Word); var M: Byte; begin M := A; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); Y := M; end; procedure TMOS6502.Op_TSX(Src: Word); var M: Byte; begin M := Sp; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); x := M; end; procedure TMOS6502.Op_TXA(Src: Word); var M: Byte; begin M := X; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; procedure TMOS6502.Op_TXS(Src: Word); begin Sp := X; end; procedure TMOS6502.Op_TYA(Src: Word); var M: Byte; begin M := Y; SET_NEGATIVE((M and $80) <> 0); SET_ZERO(M = 0); A := M; end; end.