mos6502-delphi/Source/MOS6502.pas

1611 lines
31 KiB
ObjectPascal

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.