mos6502-delphi/Source/MOS6502.pas

1637 lines
32 KiB
ObjectPascal

{ 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 <dennis@spreendigital.de> (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.