BizHawk/EMU7800/Core/M6502.cs

1099 lines
34 KiB
C#

/*
* M6502.cs
*
* CPU emulator for the MOS Technology 6502 microprocessor.
*
* Copyright © 2003-2005 Mike Murphy
*
*/
using System;
namespace EMU7800.Core
{
public sealed class M6502
{
delegate void OpcodeHandler();
OpcodeHandler[] Opcodes;
const ushort
// non-maskable interrupt vector
NMI_VEC = 0xfffa,
// reset vector
RST_VEC = 0xfffc,
// interrupt request vector
IRQ_VEC = 0xfffe;
readonly MachineBase M;
AddressSpace Mem { get { return M.Mem; } }
public ulong Clock { get; set; }
public int RunClocks { get; set; }
public int RunClocksMultiple { get; private set; }
public bool EmulatorPreemptRequest { get; set; }
public bool Jammed { get; set; }
public bool IRQInterruptRequest { get; set; }
public bool NMIInterruptRequest { get; set; }
// 16-bit register
// program counter
public ushort PC { get; set; }
// 8-bit registers
// accumulator
public byte A { get; set; }
// x index register
public byte X { get; set; }
// y index register
public byte Y { get; set; }
// stack pointer
public byte S { get; set; }
// processor status
public byte P { get; set; }
public void Reset()
{
Jammed = false;
// clear the stack
S = 0xff;
fI = fZ = true;
// reset the program counter
PC = WORD(Mem[RST_VEC], Mem[RST_VEC + 1]);
clk(6);
Log("{0} (PC:${1:x4}) reset", this, PC);
}
public override String ToString()
{
return "M6502 CPU";
}
public void Execute()
{
EmulatorPreemptRequest = false;
while (RunClocks > 0 && !EmulatorPreemptRequest && !Jammed)
{
if (NMIInterruptRequest)
{
InterruptNMI();
NMIInterruptRequest = false;
}
else if (IRQInterruptRequest)
{
InterruptIRQ();
IRQInterruptRequest = false;
}
else
{
Opcodes[Mem[PC++]]();
}
}
}
private M6502()
{
InstallOpcodes();
Clock = 0;
RunClocks = 0;
RunClocksMultiple = 1;
// initialize processor status, bit 5 is always set
P = 1 << 5;
}
public M6502(MachineBase m, int runClocksMultiple) : this()
{
if (m == null)
throw new ArgumentNullException("m");
if (runClocksMultiple <= 0)
throw new ArgumentException("runClocksMultiple must be greater than zero.");
M = m;
RunClocksMultiple = runClocksMultiple;
}
static byte MSB(ushort u16)
{
return (byte)(u16 >> 8);
}
static byte LSB(ushort u16)
{
return (byte)u16;
}
static ushort WORD(byte lsb, byte msb)
{
return (ushort)(lsb | msb << 8);
}
// Processor Status Flag Bits
//
// Flag bit setters and getters
void fset(byte flag, bool value)
{
P = (byte)(value ? P | flag : P & ~flag);
}
bool fget(byte flag)
{
return (P & flag) != 0;
}
// Carry: set if the add produced a carry, if the subtraction
// produced a borrow. Also used in shift instructions.
bool fC
{
get { return fget(1 << 0); }
set { fset(1 << 0, value); }
}
// Zero: set if the result of the last operation was zero
bool fZ
{
get { return fget(1 << 1); }
set { fset(1 << 1, value); }
}
// Irq Disable: set if maskable interrupts are disabled
bool fI
{
get { return fget(1 << 2); }
set { fset(1 << 2, value); }
}
// Decimal Mode: set if decimal mode active
bool fD
{
get { return fget(1 << 3); }
set { fset(1 << 3, value); }
}
// Brk: set if an interrupt caused by a BRK instruction,
// reset if caused by an internal interrupt
bool fB
{
get { return fget(1 << 4); }
set { fset(1 << 4, value); }
}
// 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.
bool fV
{
get { return fget(1 << 6); }
set { fset(1 << 6, value); }
}
// Negative: set if bit 7 of the accumulator is set
bool fN
{
get { return fget(1 << 7); }
set { fset(1 << 7, value); }
}
void set_fNZ(byte u8)
{
fN = (u8 & 0x80) != 0;
fZ = (u8 & 0xff) == 0;
}
byte pull()
{
S++;
return Mem[(ushort)(0x0100 + S)];
}
void push(byte data)
{
Mem[(ushort)(0x0100 + S)] = data;
S--;
}
void clk(int ticks)
{
Clock += (ulong)ticks;
RunClocks -= (ticks*RunClocksMultiple);
}
void InterruptNMI()
{
push(MSB(PC));
push(LSB(PC));
fB = false;
push(P);
fI = true;
PC = WORD(Mem[NMI_VEC], Mem[NMI_VEC + 1]);
clk(7);
}
void InterruptIRQ()
{
if (IRQInterruptRequest && !fI)
{
push(MSB(PC));
push(LSB(PC));
fB = false;
push(P);
fI = true;
PC = WORD(Mem[IRQ_VEC], Mem[IRQ_VEC + 1]);
}
clk(7);
}
void br(bool cond, ushort ea)
{
if (cond)
{
clk( (MSB(PC) == MSB(ea)) ? 1 : 2 );
PC = ea;
}
}
// Relative: Bxx $aa (branch instructions only)
ushort aREL()
{
var bo = (sbyte)Mem[PC];
PC++;
return (ushort)(PC + bo);
}
// Zero Page: $aa
ushort aZPG()
{
return WORD(Mem[PC++], 0x00);
}
// Zero Page Indexed,X: $aa,X
ushort aZPX()
{
return WORD((byte)(Mem[PC++] + X), 0x00);
}
// Zero Page Indexed,Y: $aa,Y
ushort aZPY()
{
return WORD((byte)(Mem[PC++] + Y), 0x00);
}
// Absolute: $aaaa
ushort aABS()
{
var lsb = Mem[PC++];
var msb = Mem[PC++];
return WORD(lsb, msb);
}
// Absolute Indexed,X: $aaaa,X
ushort aABX(int eclk)
{
var ea = aABS();
if (LSB(ea) + X > 0xff)
{
clk(eclk);
}
return (ushort)(ea + X);
}
// Absolute Indexed,Y: $aaaa,Y
ushort aABY(int eclk)
{
var ea = aABS();
if (LSB(ea) + Y > 0xff)
{
clk(eclk);
}
return (ushort)(ea + Y);
}
// Indexed Indirect: ($aa,X)
ushort aIDX()
{
var zpa = (byte)(Mem[PC++] + X);
var lsb = Mem[zpa++];
var msb = Mem[zpa];
return WORD(lsb, msb);
}
// Indirect Indexed: ($aa),Y
ushort aIDY(int eclk)
{
var zpa = Mem[PC++];
var lsb = Mem[zpa++];
var msb = Mem[zpa];
if (lsb + Y > 0xff)
{
clk(eclk);
}
return (ushort)(WORD(lsb, msb) + Y);
}
// Indirect Absolute: ($aaaa) (only used by JMP)
ushort aIND()
{
var ea = aABS();
var lsb = Mem[ea];
ea = WORD((byte)(LSB(ea) + 1), MSB(ea)); // NMOS 6502/7 quirk: does not fetch across page boundaries
var msb = Mem[ea];
return WORD(lsb, msb);
}
// aACC = Accumulator
// aIMM = Immediate
// aIMP = Implied
// ADC: Add with carry
void iADC(byte mem)
{
var c = fC ? 1 : 0;
var sum = A + mem + c;
fV = (~(A ^ mem) & (A ^ (sum & 0xff)) & 0x80) != 0;
if (fD)
{
// NMOS 6502/7 quirk: The N, V, and Z flags reflect the binary result, not the BCD result
var lo = (A & 0xf) + (mem & 0xf) + c;
var hi = (A >> 4) + (mem >> 4);
if (lo > 9)
{
lo += 6;
hi++;
}
if (hi > 9)
{
hi += 6;
}
A = (byte)((lo & 0xf) | (hi << 4));
fC = (hi & 0x10) != 0;
}
else
{
A = (byte)sum;
fC = (sum & 0x100) != 0;
}
set_fNZ((byte)sum);
}
// AND: Logical and
void iAND(byte mem)
{
A &= mem;
set_fNZ(A);
}
// ASL: Arithmetic shift left: C <- [7][6][5][4][3][2][1][0] <- 0
byte iASL(byte mem)
{
fC = (mem & 0x80) != 0;
mem <<= 1;
set_fNZ(mem);
return mem;
}
// BIT: Bit test
void iBIT(byte mem)
{
fN = (mem & 0x80) != 0;
fV = (mem & 0x40) != 0;
fZ = (mem & A) == 0;
}
// BRK Force Break (cause software interrupt)
void iBRK()
{
PC++;
fB = true;
push(MSB(PC));
push(LSB(PC));
push(P);
fI = true;
var lsb = Mem[IRQ_VEC];
var msb = Mem[IRQ_VEC+1];
PC = WORD(lsb, msb);
}
// CLC: Clear carry flag
void iCLC()
{
fC = false;
}
// CLD: Clear decimal mode
void iCLD()
{
fD = false;
}
// CLI: Clear interrupt disable */
void iCLI()
{
fI = false;
}
// CLV: Clear overflow flag
void iCLV()
{
fV = false;
}
// CMP: Compare accumulator
void iCMP(byte mem)
{
fC = A >= mem;
set_fNZ((byte)(A - mem));
}
// CPX: Compare index X
void iCPX(byte mem)
{
fC = X >= mem;
set_fNZ((byte)(X - mem));
}
// CPY: Compare index Y
void iCPY(byte mem)
{
fC = Y >= mem;
set_fNZ((byte)(Y - mem));
}
// DEC: Decrement memory
byte iDEC(byte mem)
{
mem--;
set_fNZ(mem);
return mem;
}
// DEX: Decrement index x
void iDEX()
{
X--;
set_fNZ(X);
}
// DEY: Decrement index y
void iDEY()
{
Y--;
set_fNZ(Y);
}
// EOR: Logical exclusive or
void iEOR(byte mem)
{
A ^= mem;
set_fNZ(A);
}
// INC: Increment memory
byte iINC(byte mem)
{
mem++;
set_fNZ(mem);
return mem;
}
// INX: Increment index x
void iINX()
{
X++;
set_fNZ(X);
}
// INY: Increment index y
void iINY()
{
Y++;
set_fNZ(Y);
}
// JMP Jump to address
void iJMP(ushort ea)
{
PC = ea;
}
// JSR Jump to subroutine
void iJSR(ushort ea)
{
PC--; // NMOS 6502/7 quirk: iRTS compensates
push(MSB(PC));
push(LSB(PC));
PC = ea;
}
// LDA: Load accumulator
void iLDA(byte mem)
{
A = mem;
set_fNZ(A);
}
// LDX: Load index X
void iLDX(byte mem)
{
X = mem;
set_fNZ(X);
}
// LDY: Load index Y
void iLDY(byte mem)
{
Y = mem;
set_fNZ(Y);
}
// LSR: Logic shift right: 0 -> [7][6][5][4][3][2][1][0] -> C
byte iLSR(byte mem)
{
fC = (mem & 0x01) != 0;
mem >>= 1;
set_fNZ(mem);
return mem;
}
// NOP: No operation
void iNOP()
{
if (M.NOPRegisterDumping)
{
Log("NOP: {0}", M6502DASM.GetRegisters(this));
}
}
// ORA: Logical inclusive or
void iORA(byte mem)
{
A |= mem;
set_fNZ(A);
}
// PHA: Push accumulator
void iPHA()
{
push(A);
}
// PHP: Push processor status (flags)
void iPHP()
{
push(P);
}
// PLA: Pull accumuator
void iPLA()
{
A = pull();
set_fNZ(A);
}
// PLP: Pull processor status (flags)
void iPLP()
{
P = pull();
fB = true;
}
// ROL: Rotate left: new C <- [7][6][5][4][3][2][1][0] <- C
byte iROL(byte mem)
{
var d0 = (byte)(fC ? 0x01 : 0x00);
fC = (mem & 0x80) != 0;
mem <<= 1;
mem |= d0;
set_fNZ(mem);
return mem;
}
// ROR: Rotate right: C -> [7][6][5][4][3][2][1][0] -> new C
byte iROR(byte mem)
{
var d7 = (byte)(fC ? 0x80 : 0x00);
fC = (mem & 0x01) != 0;
mem >>= 1;
mem |= d7;
set_fNZ(mem);
return mem;
}
// RTI: Return from interrupt
void iRTI()
{
P = pull();
var lsb = pull();
var msb = pull();
PC = WORD(lsb, msb);
fB = true;
}
// RTS: Return from subroutine
void iRTS()
{
var lsb = pull();
var msb = pull();
PC = WORD(lsb, msb);
PC++; // NMOS 6502/7 quirk: iJSR compensates
}
// SBC: Subtract with carry (borrow)
void iSBC(byte mem)
{
var c = fC ? 0 : 1;
var sum = A - mem - c;
fV = ((A ^ mem) & (A ^ (sum & 0xff)) & 0x80) != 0;
if (fD)
{
// NMOS 6502/7 quirk: The N, V, and Z flags reflect the binary result, not the BCD result
var lo = (A & 0xf) - (mem & 0xf) - c;
var hi = (A >> 4) - (mem >> 4);
if ((lo & 0x10) != 0)
{
lo -= 6;
hi--;
}
if ((hi & 0x10) != 0)
{
hi -= 6;
}
A = (byte)((lo & 0xf) | (hi << 4));
}
else
{
A = (byte)sum;
}
fC = (sum & 0x100) == 0;
set_fNZ((byte)sum);
}
// SEC: Set carry flag
void iSEC()
{
fC = true;
}
// SED: Set decimal mode
void iSED()
{
fD = true;
}
// SEI: Set interrupt disable
void iSEI()
{
fI = true;
}
// STA: Store accumulator
byte iSTA()
{
return A;
}
// STX: Store index X
byte iSTX()
{
return X;
}
// STY: Store index Y
byte iSTY()
{
return Y;
}
// TAX: Transfer accumlator to index X
void iTAX()
{
X = A;
set_fNZ(X);
}
// TAY: Transfer accumlator to index Y
void iTAY()
{
Y = A;
set_fNZ(Y);
}
// TSX: Transfer stack to index X
void iTSX()
{
X = S;
set_fNZ(X);
}
// TXA: Transfer index X to accumlator
void iTXA()
{
A = X;
set_fNZ(A);
}
// TXS: Transfer index X to stack
void iTXS()
{
S = X;
// No flags set..! Weird, huh?
}
// TYA: Transfer index Y to accumulator
void iTYA()
{
A = Y;
set_fNZ(A);
}
// Illegal opcodes
// KIL: Jam the processor
void iKIL()
{
Jammed = true;
Log("{0}: Processor jammed!", this);
}
// LAX: Load accumulator and index x
void iLAX(byte mem)
{
A = X = mem;
set_fNZ(A);
}
// ISB: Increment and subtract with carry
void iISB(byte mem)
{
mem++;
iSBC(mem);
}
// RLA: Rotate left and logical and accumulator
// new C <- [7][6][5][4][3][2][1][0] <- C
void iRLA(byte mem)
{
var d0 = (byte)(fC ? 0x01 : 0x00);
fC = (mem & 0x80) != 0;
mem <<= 1;
mem |= d0;
A &= mem;
set_fNZ(A);
}
// SAX: logical and accumulator with index X and store
byte iSAX()
{
return (byte)(A & X);
}
void InstallOpcodes()
{
Opcodes = new OpcodeHandler[0x100];
ushort EA;
Opcodes[0x65] = delegate { EA = aZPG(); clk(3); iADC(Mem[EA]); };
Opcodes[0x75] = delegate { EA = aZPX(); clk(4); iADC(Mem[EA]); };
Opcodes[0x61] = delegate { EA = aIDX(); clk(6); iADC(Mem[EA]); };
Opcodes[0x71] = delegate { EA = aIDY(1); clk(5); iADC(Mem[EA]); };
Opcodes[0x79] = delegate { EA = aABY(1); clk(4); iADC(Mem[EA]); };
Opcodes[0x6d] = delegate { EA = aABS(); clk(4); iADC(Mem[EA]); };
Opcodes[0x7d] = delegate { EA = aABX(1); clk(4); iADC(Mem[EA]); };
Opcodes[0x69] = delegate { /*aIMM*/ clk(2); iADC(Mem[PC++]); };
Opcodes[0x25] = delegate { EA = aZPG(); clk(3); iAND(Mem[EA]); }; // may be 2 clk
Opcodes[0x35] = delegate { EA = aZPX(); clk(4); iAND(Mem[EA]); }; // may be 3 clk
Opcodes[0x21] = delegate { EA = aIDX(); clk(6); iAND(Mem[EA]); };
Opcodes[0x31] = delegate { EA = aIDY(1); clk(5); iAND(Mem[EA]); };
Opcodes[0x2d] = delegate { EA = aABS(); clk(4); iAND(Mem[EA]); };
Opcodes[0x39] = delegate { EA = aABY(1); clk(4); iAND(Mem[EA]); };
Opcodes[0x3d] = delegate { EA = aABX(1); clk(4); iAND(Mem[EA]); };
Opcodes[0x29] = delegate { /*aIMM*/ clk(2); iAND(Mem[PC++]); };
Opcodes[0x06] = delegate { EA = aZPG(); clk(5); Mem[EA] = iASL(Mem[EA]); };
Opcodes[0x16] = delegate { EA = aZPX(); clk(6); Mem[EA] = iASL(Mem[EA]); };
Opcodes[0x0e] = delegate { EA = aABS(); clk(6); Mem[EA] = iASL(Mem[EA]); };
Opcodes[0x1e] = delegate { EA = aABX(0); clk(7); Mem[EA] = iASL(Mem[EA]); };
Opcodes[0x0a] = delegate { /*aACC*/ clk(2); A = iASL(A); };
Opcodes[0x24] = delegate { EA = aZPG(); clk(3); iBIT(Mem[EA]); };
Opcodes[0x2c] = delegate { EA = aABS(); clk(4); iBIT(Mem[EA]); };
Opcodes[0x10] = delegate { EA = aREL(); clk(2); br(!fN, EA); /* BPL */ };
Opcodes[0x30] = delegate { EA = aREL(); clk(2); br( fN, EA); /* BMI */ };
Opcodes[0x50] = delegate { EA = aREL(); clk(2); br(!fV, EA); /* BVC */ };
Opcodes[0x70] = delegate { EA = aREL(); clk(2); br( fV, EA); /* BVS */ };
Opcodes[0x90] = delegate { EA = aREL(); clk(2); br(!fC, EA); /* BCC */ };
Opcodes[0xb0] = delegate { EA = aREL(); clk(2); br( fC, EA); /* BCS */ };
Opcodes[0xd0] = delegate { EA = aREL(); clk(2); br(!fZ, EA); /* BNE */ };
Opcodes[0xf0] = delegate { EA = aREL(); clk(2); br( fZ, EA); /* BEQ */ };
Opcodes[0x00] = delegate { /*aIMP*/ clk(7); iBRK(); };
Opcodes[0x18] = delegate { /*aIMP*/ clk(2); iCLC(); };
Opcodes[0xd8] = delegate { /*aIMP*/ clk(2); iCLD(); };
Opcodes[0x58] = delegate { /*aIMP*/ clk(2); iCLI(); };
Opcodes[0xb8] = delegate { /*aIMP*/ clk(2); iCLV(); };
Opcodes[0xc5] = delegate { EA = aZPG(); clk(3); iCMP(Mem[EA]); };
Opcodes[0xd5] = delegate { EA = aZPX(); clk(4); iCMP(Mem[EA]); };
Opcodes[0xc1] = delegate { EA = aIDX(); clk(6); iCMP(Mem[EA]); };
Opcodes[0xd1] = delegate { EA = aIDY(1); clk(5); iCMP(Mem[EA]); };
Opcodes[0xcd] = delegate { EA = aABS(); clk(4); iCMP(Mem[EA]); };
Opcodes[0xdd] = delegate { EA = aABX(1); clk(4); iCMP(Mem[EA]); };
Opcodes[0xd9] = delegate { EA = aABY(1); clk(4); iCMP(Mem[EA]); };
Opcodes[0xc9] = delegate { /*aIMM*/ clk(2); iCMP(Mem[PC++]); };
Opcodes[0xe4] = delegate { EA = aZPG(); clk(3); iCPX(Mem[EA]); };
Opcodes[0xec] = delegate { EA = aABS(); clk(4); iCPX(Mem[EA]); };
Opcodes[0xe0] = delegate { /*aIMM*/ clk(2); iCPX(Mem[PC++]); };
Opcodes[0xc4] = delegate { EA = aZPG(); clk(3); iCPY(Mem[EA]); };
Opcodes[0xcc] = delegate { EA = aABS(); clk(4); iCPY(Mem[EA]); };
Opcodes[0xc0] = delegate { /*aIMM*/ clk(2); iCPY(Mem[PC++]); };
Opcodes[0xc6] = delegate { EA = aZPG(); clk(5); Mem[EA] = iDEC(Mem[EA]); };
Opcodes[0xd6] = delegate { EA = aZPX(); clk(6); Mem[EA] = iDEC(Mem[EA]); };
Opcodes[0xce] = delegate { EA = aABS(); clk(6); Mem[EA] = iDEC(Mem[EA]); };
Opcodes[0xde] = delegate { EA = aABX(0); clk(7); Mem[EA] = iDEC(Mem[EA]); };
Opcodes[0xca] = delegate { /*aIMP*/ clk(2); iDEX(); };
Opcodes[0x88] = delegate { /*aIMP*/ clk(2); iDEY(); };
Opcodes[0x45] = delegate { EA = aZPG(); clk(3); iEOR(Mem[EA]); };
Opcodes[0x55] = delegate { EA = aZPX(); clk(4); iEOR(Mem[EA]); };
Opcodes[0x41] = delegate { EA = aIDX(); clk(6); iEOR(Mem[EA]); };
Opcodes[0x51] = delegate { EA = aIDY(1); clk(5); iEOR(Mem[EA]); };
Opcodes[0x4d] = delegate { EA = aABS(); clk(4); iEOR(Mem[EA]); };
Opcodes[0x5d] = delegate { EA = aABX(1); clk(4); iEOR(Mem[EA]); };
Opcodes[0x59] = delegate { EA = aABY(1); clk(4); iEOR(Mem[EA]); };
Opcodes[0x49] = delegate { /*aIMM*/ clk(2); iEOR(Mem[PC++]); };
Opcodes[0xe6] = delegate { EA = aZPG(); clk(5); Mem[EA] = iINC(Mem[EA]); };
Opcodes[0xf6] = delegate { EA = aZPX(); clk(6); Mem[EA] = iINC(Mem[EA]); };
Opcodes[0xee] = delegate { EA = aABS(); clk(6); Mem[EA] = iINC(Mem[EA]); };
Opcodes[0xfe] = delegate { EA = aABX(0); clk(7); Mem[EA] = iINC(Mem[EA]); };
Opcodes[0xe8] = delegate { /*aIMP*/ clk(2); iINX(); };
Opcodes[0xc8] = delegate { /*aIMP*/ clk(2); iINY(); };
Opcodes[0xa5] = delegate { EA = aZPG(); clk(3); iLDA(Mem[EA]); };
Opcodes[0xb5] = delegate { EA = aZPX(); clk(4); iLDA(Mem[EA]); };
Opcodes[0xa1] = delegate { EA = aIDX(); clk(6); iLDA(Mem[EA]); };
Opcodes[0xb1] = delegate { EA = aIDY(1); clk(5); iLDA(Mem[EA]); };
Opcodes[0xad] = delegate { EA = aABS(); clk(4); iLDA(Mem[EA]); };
Opcodes[0xbd] = delegate { EA = aABX(1); clk(4); iLDA(Mem[EA]); };
Opcodes[0xb9] = delegate { EA = aABY(1); clk(4); iLDA(Mem[EA]); };
Opcodes[0xa9] = delegate { /*aIMM*/ clk(2); iLDA(Mem[PC++]); };
Opcodes[0xa6] = delegate { EA = aZPG(); clk(3); iLDX(Mem[EA]); };
Opcodes[0xb6] = delegate { EA = aZPY(); clk(4); iLDX(Mem[EA]); };
Opcodes[0xae] = delegate { EA = aABS(); clk(4); iLDX(Mem[EA]); };
Opcodes[0xbe] = delegate { EA = aABY(1); clk(4); iLDX(Mem[EA]); };
Opcodes[0xa2] = delegate { /*aIMM*/ clk(2); iLDX(Mem[PC++]); };
Opcodes[0xa4] = delegate { EA = aZPG(); clk(3); iLDY(Mem[EA]); };
Opcodes[0xb4] = delegate { EA = aZPX(); clk(4); iLDY(Mem[EA]); };
Opcodes[0xac] = delegate { EA = aABS(); clk(4); iLDY(Mem[EA]); };
Opcodes[0xbc] = delegate { EA = aABX(1); clk(4); iLDY(Mem[EA]); };
Opcodes[0xa0] = delegate { /*aIMM*/ clk(2); iLDY(Mem[PC++]); };
Opcodes[0x46] = delegate { EA = aZPG(); clk(5); Mem[EA] = iLSR(Mem[EA]); };
Opcodes[0x56] = delegate { EA = aZPX(); clk(6); Mem[EA] = iLSR(Mem[EA]); };
Opcodes[0x4e] = delegate { EA = aABS(); clk(6); Mem[EA] = iLSR(Mem[EA]); };
Opcodes[0x5e] = delegate { EA = aABX(0); clk(7); Mem[EA] = iLSR(Mem[EA]); };
Opcodes[0x4a] = delegate { /*aACC*/ clk(2); A = iLSR(A); };
Opcodes[0x4c] = delegate { EA = aABS(); clk(3); iJMP(EA); };
Opcodes[0x6c] = delegate { EA = aIND(); clk(5); iJMP(EA); };
Opcodes[0x20] = delegate { EA = aABS(); clk(6); iJSR(EA); };
Opcodes[0xea] = delegate { /*aIMP*/ clk(2); iNOP(); };
Opcodes[0x05] = delegate { EA = aZPG(); clk(3); iORA(Mem[EA]); }; // may be 2 clk
Opcodes[0x15] = delegate { EA = aZPX(); clk(4); iORA(Mem[EA]); }; // may be 3 clk
Opcodes[0x01] = delegate { EA = aIDX(); clk(6); iORA(Mem[EA]); };
Opcodes[0x11] = delegate { EA = aIDY(1); clk(5); iORA(Mem[EA]); };
Opcodes[0x0d] = delegate { EA = aABS(); clk(4); iORA(Mem[EA]); };
Opcodes[0x1d] = delegate { EA = aABX(1); clk(4); iORA(Mem[EA]); };
Opcodes[0x19] = delegate { EA = aABY(1); clk(4); iORA(Mem[EA]); };
Opcodes[0x09] = delegate { /*aIMM*/ clk(2); iORA(Mem[PC++]); };
Opcodes[0x48] = delegate { /*aIMP*/ clk(3); iPHA(); };
Opcodes[0x68] = delegate { /*aIMP*/ clk(4); iPLA(); };
Opcodes[0x08] = delegate { /*aIMP*/ clk(3); iPHP(); };
Opcodes[0x28] = delegate { /*aIMP*/ clk(4); iPLP(); };
Opcodes[0x26] = delegate { EA = aZPG(); clk(5); Mem[EA] = iROL(Mem[EA]); };
Opcodes[0x36] = delegate { EA = aZPX(); clk(6); Mem[EA] = iROL(Mem[EA]); };
Opcodes[0x2e] = delegate { EA = aABS(); clk(6); Mem[EA] = iROL(Mem[EA]); };
Opcodes[0x3e] = delegate { EA = aABX(0); clk(7); Mem[EA] = iROL(Mem[EA]); };
Opcodes[0x2a] = delegate { /*aACC*/ clk(2); A = iROL(A); };
Opcodes[0x66] = delegate { EA = aZPG(); clk(5); Mem[EA] = iROR(Mem[EA]); };
Opcodes[0x76] = delegate { EA = aZPX(); clk(6); Mem[EA] = iROR(Mem[EA]); };
Opcodes[0x6e] = delegate { EA = aABS(); clk(6); Mem[EA] = iROR(Mem[EA]); };
Opcodes[0x7e] = delegate { EA = aABX(0); clk(7); Mem[EA] = iROR(Mem[EA]); };
Opcodes[0x6a] = delegate { /*aACC*/ clk(2); A = iROR(A); };
Opcodes[0x40] = delegate { /*aIMP*/ clk(6); iRTI(); };
Opcodes[0x60] = delegate { /*aIMP*/ clk(6); iRTS(); };
Opcodes[0xe5] = delegate { EA = aZPG(); clk(3); iSBC(Mem[EA]); };
Opcodes[0xf5] = delegate { EA = aZPX(); clk(4); iSBC(Mem[EA]); };
Opcodes[0xe1] = delegate { EA = aIDX(); clk(6); iSBC(Mem[EA]); };
Opcodes[0xf1] = delegate { EA = aIDY(1); clk(5); iSBC(Mem[EA]); };
Opcodes[0xed] = delegate { EA = aABS(); clk(4); iSBC(Mem[EA]); };
Opcodes[0xfd] = delegate { EA = aABX(1); clk(4); iSBC(Mem[EA]); };
Opcodes[0xf9] = delegate { EA = aABY(1); clk(4); iSBC(Mem[EA]); };
Opcodes[0xe9] = delegate { /*aIMM*/ clk(2); iSBC(Mem[PC++]); };
Opcodes[0x38] = delegate { /*aIMP*/ clk(2); iSEC(); };
Opcodes[0xf8] = delegate { /*aIMP*/ clk(2); iSED(); };
Opcodes[0x78] = delegate { /*aIMP*/ clk(2); iSEI(); };
Opcodes[0x85] = delegate { EA = aZPG(); clk(3); Mem[EA] = iSTA(); };
Opcodes[0x95] = delegate { EA = aZPX(); clk(4); Mem[EA] = iSTA(); };
Opcodes[0x81] = delegate { EA = aIDX(); clk(6); Mem[EA] = iSTA(); };
Opcodes[0x91] = delegate { EA = aIDY(0); clk(6); Mem[EA] = iSTA(); };
Opcodes[0x8d] = delegate { EA = aABS(); clk(4); Mem[EA] = iSTA(); };
Opcodes[0x99] = delegate { EA = aABY(0); clk(5); Mem[EA] = iSTA(); };
Opcodes[0x9d] = delegate { EA = aABX(0); clk(5); Mem[EA] = iSTA(); };
Opcodes[0x86] = delegate { EA = aZPG(); clk(3); Mem[EA] = iSTX(); };
Opcodes[0x96] = delegate { EA = aZPY(); clk(4); Mem[EA] = iSTX(); };
Opcodes[0x8e] = delegate { EA = aABS(); clk(4); Mem[EA] = iSTX(); };
Opcodes[0x84] = delegate { EA = aZPG(); clk(3); Mem[EA] = iSTY(); };
Opcodes[0x94] = delegate { EA = aZPX(); clk(4); Mem[EA] = iSTY(); };
Opcodes[0x8c] = delegate { EA = aABS(); clk(4); Mem[EA] = iSTY(); };
Opcodes[0xaa] = delegate { /*aIMP*/ clk(2); iTAX(); };
Opcodes[0xa8] = delegate { /*aIMP*/ clk(2); iTAY(); };
Opcodes[0xba] = delegate { /*aIMP*/ clk(2); iTSX(); };
Opcodes[0x8a] = delegate { /*aIMP*/ clk(2); iTXA(); };
Opcodes[0x9a] = delegate { /*aIMP*/ clk(2); iTXS(); };
Opcodes[0x98] = delegate { /*aIMP*/ clk(2); iTYA(); };
// Illegal opcodes
foreach (int opCode in new ushort[] { 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, 0x72, 0x92, 0xb2, 0xd2, 0xf2 })
{
Opcodes[opCode] = delegate { clk(2); iKIL(); };
}
Opcodes[0x3f] = delegate { EA = aABX(0); clk(4); iRLA(Mem[EA]); };
Opcodes[0xa7] = delegate { EA = aZPX(); clk(3); iLAX(Mem[EA]); };
Opcodes[0xb3] = delegate { EA = aIDY(0); clk(6); iLAX(Mem[EA]); };
Opcodes[0xef] = delegate { EA = aABS(); clk(6); iISB(Mem[EA]); };
Opcodes[0x0c] = delegate { EA = aABS(); clk(2); iNOP(); };
foreach (int opCode in new ushort[] { 0x1c, 0x3c, 0x5c, 0x7c, 0x9c, 0xdc, 0xfc })
{
Opcodes[opCode] = delegate { EA = aABX(0); clk(2); iNOP(); };
}
Opcodes[0x83] = delegate { EA = aIDX(); clk(6); Mem[EA] = iSAX(); };
Opcodes[0x87] = delegate { EA = aZPG(); clk(3); Mem[EA] = iSAX(); };
Opcodes[0x8f] = delegate { EA = aABS(); clk(4); Mem[EA] = iSAX(); };
Opcodes[0x97] = delegate { EA = aZPY(); clk(4); Mem[EA] = iSAX(); };
Opcodes[0xa3] = delegate { EA = aIDX(); clk(6); iLAX(Mem[EA]); };
Opcodes[0xb7] = delegate { EA = aZPY(); clk(4); iLAX(Mem[EA]); };
Opcodes[0xaf] = delegate { EA = aABS(); clk(5); iLAX(Mem[EA]); };
Opcodes[0xbf] = delegate { EA = aABY(0); clk(6); iLAX(Mem[EA]); };
Opcodes[0xff] = delegate { EA = aABX(0); clk(7); iISB(Mem[EA]); };
OpcodeHandler opNULL = () => Log("{0}:**UNKNOWN OPCODE: ${1:x2} at ${2:x4}\n", this, Mem[(ushort)(PC - 1)], PC - 1);
for (var i=0; i < Opcodes.Length; i++)
{
if (Opcodes[i] == null)
{
Opcodes[i] = opNULL;
}
}
}
#region Serialization Members
public M6502(DeserializationContext input, MachineBase m, int runClocksMultiple) : this(m, runClocksMultiple)
{
if (input == null)
throw new ArgumentNullException("input");
input.CheckVersion(1);
Clock = input.ReadUInt64();
RunClocks = input.ReadInt32();
RunClocksMultiple = input.ReadInt32();
EmulatorPreemptRequest = input.ReadBoolean();
Jammed = input.ReadBoolean();
IRQInterruptRequest = input.ReadBoolean();
NMIInterruptRequest = input.ReadBoolean();
PC = input.ReadUInt16();
A = input.ReadByte();
X = input.ReadByte();
Y = input.ReadByte();
S = input.ReadByte();
P = input.ReadByte();
}
public void GetObjectData(SerializationContext output)
{
if (output == null)
throw new ArgumentNullException("output");
output.WriteVersion(1);
output.Write(Clock);
output.Write(RunClocks);
output.Write(RunClocksMultiple);
output.Write(EmulatorPreemptRequest);
output.Write(Jammed);
output.Write(IRQInterruptRequest);
output.Write(NMIInterruptRequest);
output.Write(PC);
output.Write(A);
output.Write(X);
output.Write(Y);
output.Write(S);
output.Write(P);
}
#endregion
#region Helpers
void Log(string format, params object[] args)
{
if (M == null || M.Logger == null)
return;
M.Logger.WriteLine(format, args);
}
#endregion
}
}