mirror of
https://github.com/MoleskiCoder/EightBitNet.git
synced 2024-12-24 09:30:40 +00:00
d58b635626
Signed-off-by: Adrian Conlon <Adrian.conlon@gmail.com>
938 lines
52 KiB
C#
938 lines
52 KiB
C#
namespace EightBit
|
|
{
|
|
using System;
|
|
|
|
public class M6502 : LittleEndianProcessor
|
|
{
|
|
private const byte IRQvector = 0xfe; // IRQ vector
|
|
private const byte RSTvector = 0xfc; // RST vector
|
|
private const byte NMIvector = 0xfa; // NMI vector
|
|
|
|
private byte x = 0;
|
|
private byte y = 0;
|
|
private byte a = 0;
|
|
private byte s = 0;
|
|
private byte p = 0;
|
|
|
|
private ushort intermediate;
|
|
|
|
private bool handlingRESET = false;
|
|
private bool handlingNMI = false;
|
|
private bool handlingINT = false;
|
|
|
|
private PinLevel nmiLine = PinLevel.Low;
|
|
private PinLevel soLine = PinLevel.Low;
|
|
private PinLevel syncLine = PinLevel.Low;
|
|
private PinLevel rdyLine = PinLevel.Low;
|
|
|
|
public M6502(Bus bus)
|
|
: base(bus)
|
|
{
|
|
}
|
|
|
|
public event EventHandler<EventArgs> ExecutingInstruction;
|
|
public event EventHandler<EventArgs> ExecutedInstruction;
|
|
|
|
public event EventHandler<EventArgs> RaisingNMI;
|
|
public event EventHandler<EventArgs> RaisedNMI;
|
|
public event EventHandler<EventArgs> LoweringNMI;
|
|
public event EventHandler<EventArgs> LoweredNMI;
|
|
|
|
public event EventHandler<EventArgs> RaisingSO;
|
|
public event EventHandler<EventArgs> RaisedSO;
|
|
public event EventHandler<EventArgs> LoweringSO;
|
|
public event EventHandler<EventArgs> LoweredSO;
|
|
|
|
public event EventHandler<EventArgs> RaisingSYNC;
|
|
public event EventHandler<EventArgs> RaisedSYNC;
|
|
public event EventHandler<EventArgs> LoweringSYNC;
|
|
public event EventHandler<EventArgs> LoweredSYNC;
|
|
|
|
public event EventHandler<EventArgs> RaisingRDY;
|
|
public event EventHandler<EventArgs> RaisedRDY;
|
|
public event EventHandler<EventArgs> LoweringRDY;
|
|
public event EventHandler<EventArgs> LoweredRDY;
|
|
|
|
public byte X { get => x; set => x = value; }
|
|
public byte Y { get => y; set { y = value; } }
|
|
public byte A { get => a; set { a = value; } }
|
|
public byte S { get => s; set { s = value; } }
|
|
public byte P { get => p; set { p = value; } }
|
|
|
|
private int InterruptMasked => P & (byte)StatusBits.IF;
|
|
private int Decimal => P & (byte)StatusBits.DF;
|
|
private int Negative => P & (byte)StatusBits.NF;
|
|
private int Zero => P & (byte)StatusBits.ZF;
|
|
private int Overflow => P & (byte)StatusBits.VF;
|
|
private int Carry => P & (byte)StatusBits.CF;
|
|
|
|
public ref PinLevel NMI() => ref nmiLine;
|
|
public ref PinLevel SO() => ref soLine;
|
|
public ref PinLevel SYNC() => ref syncLine;
|
|
public ref PinLevel RDY() => ref rdyLine;
|
|
|
|
public override void RaisePOWER()
|
|
{
|
|
base.RaisePOWER();
|
|
X = (byte)Bits.Bit7;
|
|
Y = 0;
|
|
A = 0;
|
|
P = (byte)StatusBits.RF;
|
|
S = (byte)Mask.Mask8;
|
|
LowerSYNC();
|
|
}
|
|
|
|
public virtual void RaiseNMI()
|
|
{
|
|
OnRaisingNMI();
|
|
NMI().Raise();
|
|
OnRaisedNMI();
|
|
}
|
|
|
|
public virtual void LowerNMI()
|
|
{
|
|
OnLoweringNMI();
|
|
NMI().Lower();
|
|
OnLoweredNMI();
|
|
}
|
|
|
|
public virtual void RaiseSO()
|
|
{
|
|
OnRaisingSO();
|
|
SO().Raise();
|
|
OnRaisedSO();
|
|
}
|
|
|
|
public virtual void LowerSO()
|
|
{
|
|
OnLoweringSO();
|
|
SO().Lower();
|
|
OnLoweredSO();
|
|
}
|
|
|
|
protected virtual void RaiseSYNC()
|
|
{
|
|
OnRaisingSYNC();
|
|
SYNC().Raise();
|
|
OnRaisedSYNC();
|
|
}
|
|
|
|
protected virtual void LowerSYNC()
|
|
{
|
|
OnLoweringSYNC();
|
|
SYNC().Lower();
|
|
OnLoweredSYNC();
|
|
}
|
|
|
|
public virtual void RaiseRDY()
|
|
{
|
|
OnRaisingRDY();
|
|
RDY().Raise();
|
|
OnRaisedRDY();
|
|
}
|
|
|
|
public virtual void LowerRDY()
|
|
{
|
|
OnLoweringRDY();
|
|
RDY().Lower();
|
|
OnLoweredRDY();
|
|
}
|
|
|
|
protected virtual void OnExecutingInstruction() => ExecutingInstruction?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnExecutedInstruction() => ExecutedInstruction?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnRaisingNMI() => RaisingNMI?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnRaisedNMI() => RaisedNMI?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnLoweringNMI() => LoweringNMI?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnLoweredNMI() => LoweredNMI?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnRaisingSO() => RaisingSO?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnRaisedSO() => RaisedSO?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnLoweringSO() => LoweringSO?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnLoweredSO() => LoweredSO?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnRaisingSYNC() => RaisingSYNC?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnRaisedSYNC() => RaisedSYNC?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnLoweringSYNC() => LoweringSYNC?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnLoweredSYNC() => LoweredSYNC?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnRaisingRDY() => RaisingRDY?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnRaisedRDY() => RaisedRDY?.Invoke(this, EventArgs.Empty);
|
|
|
|
protected virtual void OnLoweringRDY() => LoweringRDY?.Invoke(this, EventArgs.Empty);
|
|
protected virtual void OnLoweredRDY() => LoweredRDY?.Invoke(this, EventArgs.Empty);
|
|
|
|
public override int Execute()
|
|
{
|
|
RaiseSYNC(); // Instruction fetch has now completed
|
|
|
|
switch (OpCode)
|
|
{
|
|
case 0x00: FetchByte(); Interrupt(); break; // BRK (implied)
|
|
case 0x01: A = OrR(A, AM_IndexedIndirectX()); break; // ORA (indexed indirect X)
|
|
case 0x02: break;
|
|
case 0x03: SLO(AM_IndexedIndirectX()); break; // *SLO (indexed indirect X)
|
|
case 0x04: AM_ZeroPage(); break; // *NOP (zero page)
|
|
case 0x05: A = OrR(A, AM_ZeroPage()); break; // ORA (zero page)
|
|
case 0x06: BusReadModifyWrite(ASL(AM_ZeroPage())); break; // ASL (zero page)
|
|
case 0x07: SLO(AM_ZeroPage()); break; // *SLO (zero page)
|
|
case 0x08: BusRead(); PHP(); break; // PHP (implied)
|
|
case 0x09: A = OrR(A, AM_Immediate()); break; // ORA (immediate)
|
|
case 0x0a: BusRead(); A = ASL(A); break; // ASL A (implied)
|
|
case 0x0b: ANC(AM_Immediate()); break; // *ANC (immediate)
|
|
case 0x0c: AM_Absolute(); break; // *NOP (absolute)
|
|
case 0x0d: A = OrR(A, AM_Absolute()); break; // ORA (absolute)
|
|
case 0x0e: BusReadModifyWrite(ASL(AM_Absolute())); break; // ASL (absolute)
|
|
case 0x0f: SLO(AM_Absolute()); break; // *SLO (absolute)
|
|
|
|
case 0x10: Branch(Negative == 0); break; // BPL (relative)
|
|
case 0x11: A = OrR(A, AM_IndirectIndexedY()); break; // ORA (indirect indexed Y)
|
|
case 0x12: break;
|
|
case 0x13: SLO(AM_IndirectIndexedY()); break; // *SLO (indirect indexed Y)
|
|
case 0x14: AM_ZeroPageX(); break; // *NOP (zero page, X)
|
|
case 0x15: A = OrR(A, AM_ZeroPageX()); break; // ORA (zero page, X)
|
|
case 0x16: BusReadModifyWrite(ASL(AM_ZeroPageX())); break; // ASL (zero page, X)
|
|
case 0x17: SLO(AM_ZeroPageX()); break; // *SLO (zero page, X)
|
|
case 0x18: BusRead(); ClearFlag(ref p, StatusBits.CF); break; // CLC (implied)
|
|
case 0x19: A = OrR(A, AM_AbsoluteY()); break; // ORA (absolute, Y)
|
|
case 0x1a: BusRead(); break; // *NOP (implied)
|
|
case 0x1b: SLO(AM_AbsoluteY()); break; // *SLO (absolute, Y)
|
|
case 0x1c: AM_AbsoluteX(); break; // *NOP (absolute, X)
|
|
case 0x1d: A = OrR(A, AM_AbsoluteX()); break; // ORA (absolute, X)
|
|
case 0x1e: BusReadModifyWrite(ASL(AM_AbsoluteX(PageCrossingBehavior.AlwaysReadTwice))); break; // ASL (absolute, X)
|
|
case 0x1f: SLO(AM_AbsoluteX()); break; // *SLO (absolute, X)
|
|
|
|
case 0x20: JSR(); break; // JSR (absolute)
|
|
case 0x21: A = AndR(A, AM_IndexedIndirectX()); break; // AND (indexed indirect X)
|
|
case 0x22: break;
|
|
case 0x23: RLA(AM_IndexedIndirectX()); break; // *RLA (indexed indirect X)
|
|
case 0x24: BIT(A, AM_ZeroPage()); break; // BIT (zero page)
|
|
case 0x25: A = AndR(A, AM_ZeroPage()); break; // AND (zero page)
|
|
case 0x26: BusReadModifyWrite(ROL(AM_ZeroPage())); break; // ROL (zero page)
|
|
case 0x27: RLA(AM_ZeroPage()); break; // *RLA (zero page)
|
|
case 0x28: BusRead(); GetBytePaged(1, S); PLP(); break; // PLP (implied)
|
|
case 0x29: A = AndR(A, AM_Immediate()); break; // AND (immediate)
|
|
case 0x2a: BusRead(); A = ROL(A); break; // ROL A (implied)
|
|
case 0x2b: ANC(AM_Immediate()); break; // *ANC (immediate)
|
|
case 0x2c: BIT(A, AM_Absolute()); break; // BIT (absolute)
|
|
case 0x2d: A = AndR(A, AM_Absolute()); break; // AND (absolute)
|
|
case 0x2e: BusReadModifyWrite(ROL(AM_Absolute())); break; // ROL (absolute)
|
|
case 0x2f: RLA(AM_Absolute()); break; // *RLA (absolute)
|
|
|
|
case 0x30: Branch(Negative != 0); break; // BMI (relative)
|
|
case 0x31: A = AndR(A, AM_IndirectIndexedY()); break; // AND (indirect indexed Y)
|
|
case 0x32: break;
|
|
case 0x33: RLA(AM_IndirectIndexedY()); break; // *RLA (indirect indexed Y)
|
|
case 0x34: AM_ZeroPageX(); break; // *NOP (zero page, X)
|
|
case 0x35: A = AndR(A, AM_ZeroPageX()); break; // AND (zero page, X)
|
|
case 0x36: BusReadModifyWrite(ROL(AM_ZeroPageX())); break; // ROL (zero page, X)
|
|
case 0x37: RLA(AM_ZeroPageX()); break; // *RLA (zero page, X)
|
|
case 0x38: BusRead(); SetFlag(ref p, StatusBits.CF); break; // SEC (implied)
|
|
case 0x39: A = AndR(A, AM_AbsoluteY()); break; // AND (absolute, Y)
|
|
case 0x3a: BusRead(); break; // *NOP (implied)
|
|
case 0x3b: RLA(AM_AbsoluteY()); break; // *RLA (absolute, Y)
|
|
case 0x3c: AM_AbsoluteX(); break; // *NOP (absolute, X)
|
|
case 0x3d: A = AndR(A, AM_AbsoluteX()); break; // AND (absolute, X)
|
|
case 0x3e: BusReadModifyWrite(ROL(AM_AbsoluteX(PageCrossingBehavior.AlwaysReadTwice))); break; // ROL (absolute, X)
|
|
case 0x3f: RLA(AM_AbsoluteX()); break; // *RLA (absolute, X)
|
|
|
|
case 0x40: BusRead(); RTI(); break; // RTI (implied)
|
|
case 0x41: A = EorR(A, AM_IndexedIndirectX()); break; // EOR (indexed indirect X)
|
|
case 0x42: break;
|
|
case 0x43: SRE(AM_IndexedIndirectX()); break; // *SRE (indexed indirect X)
|
|
case 0x44: AM_ZeroPage(); break; // *NOP (zero page)
|
|
case 0x45: A = EorR(A, AM_ZeroPage()); break; // EOR (zero page)
|
|
case 0x46: BusReadModifyWrite(LSR(AM_ZeroPage())); break; // LSR (zero page)
|
|
case 0x47: SRE(AM_ZeroPage()); break; // *SRE (zero page)
|
|
case 0x48: BusRead(); Push(A); break; // PHA (implied)
|
|
case 0x49: A = EorR(A, AM_Immediate()); break; // EOR (immediate)
|
|
case 0x4a: BusRead(); A = LSR(A); break; // LSR A (implied)
|
|
case 0x4b: ASR(AM_Immediate()); break; // *ASR (immediate)
|
|
case 0x4c: Jump(Address_Absolute()); break; // JMP (absolute)
|
|
case 0x4d: A = EorR(A, AM_Absolute()); break; // EOR (absolute)
|
|
case 0x4e: BusReadModifyWrite(LSR(AM_Absolute())); break; // LSR (absolute)
|
|
case 0x4f: SRE(AM_Absolute()); break; // *SRE (absolute)
|
|
|
|
case 0x50: Branch(Overflow == 0); break; // BVC (relative)
|
|
case 0x51: A = EorR(A, AM_IndirectIndexedY()); break; // EOR (indirect indexed Y)
|
|
case 0x52: break;
|
|
case 0x53: SRE(AM_IndirectIndexedY()); break; // *SRE (indirect indexed Y)
|
|
case 0x54: AM_ZeroPageX(); break; // *NOP (zero page, X)
|
|
case 0x55: A = EorR(A, AM_ZeroPageX()); break; // EOR (zero page, X)
|
|
case 0x56: BusReadModifyWrite(LSR(AM_ZeroPageX())); break; // LSR (zero page, X)
|
|
case 0x57: SRE(AM_ZeroPageX()); break; // *SRE (zero page, X)
|
|
case 0x58: BusRead(); ClearFlag(ref p, StatusBits.IF); break; // CLI (implied)
|
|
case 0x59: A = EorR(A, AM_AbsoluteY()); break; // EOR (absolute, Y)
|
|
case 0x5a: BusRead(); break; // *NOP (implied)
|
|
case 0x5b: SRE(AM_AbsoluteY()); break; // *SRE (absolute, Y)
|
|
case 0x5c: AM_AbsoluteX(); break; // *NOP (absolute, X)
|
|
case 0x5d: A = EorR(A, AM_AbsoluteX()); break; // EOR (absolute, X)
|
|
case 0x5e: BusReadModifyWrite(LSR(AM_AbsoluteX(PageCrossingBehavior.AlwaysReadTwice))); break; // LSR (absolute, X)
|
|
case 0x5f: SRE(AM_AbsoluteX()); break; // *SRE (absolute, X)
|
|
|
|
case 0x60: BusRead(); RTS(); break; // RTS (implied)
|
|
case 0x61: A = ADC(A, AM_IndexedIndirectX()); break; // ADC (indexed indirect X)
|
|
case 0x62: break;
|
|
case 0x63: RRA(AM_IndexedIndirectX()); break; // *RRA (indexed indirect X)
|
|
case 0x64: AM_ZeroPage(); break; // *NOP (zero page)
|
|
case 0x65: A = ADC(A, AM_ZeroPage()); break; // ADC (zero page)
|
|
case 0x66: BusReadModifyWrite(ROR(AM_ZeroPage())); break; // ROR (zero page)
|
|
case 0x67: RRA(AM_ZeroPage()); break; // *RRA (zero page)
|
|
case 0x68: BusRead(); GetBytePaged(1, S); A = Through(Pop()); break; // PLA (implied)
|
|
case 0x69: A = ADC(A, AM_Immediate()); break; // ADC (immediate)
|
|
case 0x6a: BusRead(); A = ROR(A); break; // ROR A (implied)
|
|
case 0x6b: ARR(AM_Immediate()); break; // *ARR (immediate)
|
|
case 0x6c: Jump(Address_Indirect()); break; // JMP (indirect)
|
|
case 0x6d: A = ADC(A, AM_Absolute()); break; // ADC (absolute)
|
|
case 0x6e: BusReadModifyWrite(ROR(AM_Absolute())); break; // ROR (absolute)
|
|
case 0x6f: RRA(AM_Absolute()); break; // *RRA (absolute)
|
|
|
|
case 0x70: Branch(Overflow != 0); break; // BVS (relative)
|
|
case 0x71: A = ADC(A, AM_IndirectIndexedY()); break; // ADC (indirect indexed Y)
|
|
case 0x72: break;
|
|
case 0x73: RRA(AM_IndirectIndexedY()); break; // *RRA (indirect indexed Y)
|
|
case 0x74: AM_ZeroPageX(); break; // *NOP (zero page, X)
|
|
case 0x75: A = ADC(A, AM_ZeroPageX()); break; // ADC (zero page, X)
|
|
case 0x76: BusReadModifyWrite(ROR(AM_ZeroPageX())); break; // ROR (zero page, X)
|
|
case 0x77: RRA(AM_ZeroPageX()); break; // *RRA (zero page, X)
|
|
case 0x78: BusRead(); SetFlag(ref p, StatusBits.IF); break; // SEI (implied)
|
|
case 0x79: A = ADC(A, AM_AbsoluteY()); break; // ADC (absolute, Y)
|
|
case 0x7a: BusRead(); break; // *NOP (implied)
|
|
case 0x7b: RRA(AM_AbsoluteY()); break; // *RRA (absolute, Y)
|
|
case 0x7c: AM_AbsoluteX(); break; // *NOP (absolute, X)
|
|
case 0x7d: A = ADC(A, AM_AbsoluteX()); break; // ADC (absolute, X)
|
|
case 0x7e: BusReadModifyWrite(ROR(AM_AbsoluteX(PageCrossingBehavior.AlwaysReadTwice))); break; // ROR (absolute, X)
|
|
case 0x7f: RRA(AM_AbsoluteX()); break; // *RRA (absolute, X)
|
|
|
|
case 0x80: AM_Immediate(); break; // *NOP (immediate)
|
|
case 0x81: BusWrite(Address_IndexedIndirectX(), A); break; // STA (indexed indirect X)
|
|
case 0x82: AM_Immediate(); break; // *NOP (immediate)
|
|
case 0x83: BusWrite(Address_IndexedIndirectX(), (byte)(A & X)); break; // *SAX (indexed indirect X)
|
|
case 0x84: BusWrite(Address_ZeroPage(), Y); break; // STY (zero page)
|
|
case 0x85: BusWrite(Address_ZeroPage(), A); break; // STA (zero page)
|
|
case 0x86: BusWrite(Address_ZeroPage(), X); break; // STX (zero page)
|
|
case 0x87: BusWrite(Address_ZeroPage(), (byte)(A & X)); break; // *SAX (zero page)
|
|
case 0x88: BusRead(); Y = DEC(Y); break; // DEY (implied)
|
|
case 0x89: AM_Immediate(); break; // *NOP (immediate)
|
|
case 0x8a: BusRead(); A = Through(X); break; // TXA (implied)
|
|
case 0x8b: break;
|
|
case 0x8c: BusWrite(Address_Absolute(), Y); break; // STY (absolute)
|
|
case 0x8d: BusWrite(Address_Absolute(), A); break; // STA (absolute)
|
|
case 0x8e: BusWrite(Address_Absolute(), X); break; // STX (absolute)
|
|
case 0x8f: BusWrite(Address_Absolute(), (byte)(A & X)); break; // *SAX (absolute)
|
|
|
|
case 0x90: Branch(Carry == 0); break; // BCC (relative)
|
|
case 0x91: AM_IndirectIndexedY(); BusWrite(A); break; // STA (indirect indexed Y)
|
|
case 0x92: break;
|
|
case 0x93: break;
|
|
case 0x94: BusWrite(Address_ZeroPageX(), Y); break; // STY (zero page, X)
|
|
case 0x95: BusWrite(Address_ZeroPageX(), A); break; // STA (zero page, X)
|
|
case 0x96: BusWrite(Address_ZeroPageY(), X); break; // STX (zero page, Y)
|
|
case 0x97: BusWrite(Address_ZeroPageY(), (byte)(A & X)); break; // *SAX (zero page, Y)
|
|
case 0x98: BusRead(); A = Through(Y); break; // TYA (implied)
|
|
case 0x99: STA_AbsoluteY(); break; // STA (absolute, Y)
|
|
case 0x9a: BusRead(); S = X; break; // TXS (implied)
|
|
case 0x9b: break;
|
|
case 0x9c: break;
|
|
case 0x9d: STA_AbsoluteX(); break; // STA (absolute, X)
|
|
case 0x9e: break;
|
|
case 0x9f: break;
|
|
|
|
case 0xa0: Y = Through(AM_Immediate()); break; // LDY (immediate)
|
|
case 0xa1: A = Through(AM_IndexedIndirectX()); break; // LDA (indexed indirect X)
|
|
case 0xa2: X = Through(AM_Immediate()); break; // LDX (immediate)
|
|
case 0xa3: A = X = Through(AM_IndexedIndirectX()); break; // *LAX (indexed indirect X)
|
|
case 0xa4: Y = Through(AM_ZeroPage()); break; // LDY (zero page)
|
|
case 0xa5: A = Through(AM_ZeroPage()); break; // LDA (zero page)
|
|
case 0xa6: X = Through(AM_ZeroPage()); break; // LDX (zero page)
|
|
case 0xa7: A = X = Through(AM_ZeroPage()); break; // *LAX (zero page)
|
|
case 0xa8: BusRead(); Y = Through(A); break; // TAY (implied)
|
|
case 0xa9: A = Through(AM_Immediate()); break; // LDA (immediate)
|
|
case 0xaa: BusRead(); X = Through(A); break; // TAX (implied)
|
|
case 0xab: A = X = Through(AM_Immediate()); break; // *ATX (immediate)
|
|
case 0xac: Y = Through(AM_Absolute()); break; // LDY (absolute)
|
|
case 0xad: A = Through(AM_Absolute()); break; // LDA (absolute)
|
|
case 0xae: X = Through(AM_Absolute()); break; // LDX (absolute)
|
|
case 0xaf: A = X = Through(AM_Absolute()); break; // *LAX (absolute)
|
|
|
|
case 0xb0: Branch(Carry != 0); break; // BCS (relative)
|
|
case 0xb1: A = Through(AM_IndirectIndexedY()); break; // LDA (indirect indexed Y)
|
|
case 0xb2: break;
|
|
case 0xb3: A = X = Through(AM_IndirectIndexedY()); break; // *LAX (indirect indexed Y)
|
|
case 0xb4: Y = Through(AM_ZeroPageX()); break; // LDY (zero page, X)
|
|
case 0xb5: A = Through(AM_ZeroPageX()); break; // LDA (zero page, X)
|
|
case 0xb6: X = Through(AM_ZeroPageY()); break; // LDX (zero page, Y)
|
|
case 0xb7: A = X = Through(AM_ZeroPageY()); break; // *LAX (zero page, Y)
|
|
case 0xb8: BusRead(); ClearFlag(ref p, StatusBits.VF); break; // CLV (implied)
|
|
case 0xb9: A = Through(AM_AbsoluteY()); break; // LDA (absolute, Y)
|
|
case 0xba: BusRead(); X = Through(S); break; // TSX (implied)
|
|
case 0xbb: break;
|
|
case 0xbc: Y = Through(AM_AbsoluteX()); break; // LDY (absolute, X)
|
|
case 0xbd: A = Through(AM_AbsoluteX()); break; // LDA (absolute, X)
|
|
case 0xbe: X = Through(AM_AbsoluteY()); break; // LDX (absolute, Y)
|
|
case 0xbf: A = X = Through(AM_AbsoluteY()); break; // *LAX (absolute, Y)
|
|
|
|
case 0xc0: CMP(Y, AM_Immediate()); break; // CPY (immediate)
|
|
case 0xc1: CMP(A, AM_IndexedIndirectX()); break; // CMP (indexed indirect X)
|
|
case 0xc2: AM_Immediate(); break; // *NOP (immediate)
|
|
case 0xc3: DCP(AM_IndexedIndirectX()); break; // *DCP (indexed indirect X)
|
|
case 0xc4: CMP(Y, AM_ZeroPage()); break; // CPY (zero page)
|
|
case 0xc5: CMP(A, AM_ZeroPage()); break; // CMP (zero page)
|
|
case 0xc6: BusReadModifyWrite(DEC(AM_ZeroPage())); break; // DEC (zero page)
|
|
case 0xc7: DCP(AM_ZeroPage()); break; // *DCP (zero page)
|
|
case 0xc8: BusRead(); Y = INC(Y); break; // INY (implied)
|
|
case 0xc9: CMP(A, AM_Immediate()); break; // CMP (immediate)
|
|
case 0xca: BusRead(); X = DEC(X); break; // DEX (implied)
|
|
case 0xcb: AXS(AM_Immediate()); break; // *AXS (immediate)
|
|
case 0xcc: CMP(Y, AM_Absolute()); break; // CPY (absolute)
|
|
case 0xcd: CMP(A, AM_Absolute()); break; // CMP (absolute)
|
|
case 0xce: BusReadModifyWrite(DEC(AM_Absolute())); break; // DEC (absolute)
|
|
case 0xcf: DCP(AM_Absolute()); break; // *DCP (absolute)
|
|
|
|
case 0xd0: Branch(Zero == 0); break; // BNE (relative)
|
|
case 0xd1: CMP(A, AM_IndirectIndexedY()); break; // CMP (indirect indexed Y)
|
|
case 0xd2: break;
|
|
case 0xd3: DCP(AM_IndirectIndexedY()); break; // *DCP (indirect indexed Y)
|
|
case 0xd4: AM_ZeroPageX(); break; // *NOP (zero page, X)
|
|
case 0xd5: CMP(A, AM_ZeroPageX()); break; // CMP (zero page, X)
|
|
case 0xd6: BusReadModifyWrite(DEC(AM_ZeroPageX())); break; // DEC (zero page, X)
|
|
case 0xd7: DCP(AM_ZeroPageX()); break; // *DCP (zero page, X)
|
|
case 0xd8: BusRead(); ClearFlag(ref p, StatusBits.DF); break; // CLD (implied)
|
|
case 0xd9: CMP(A, AM_AbsoluteY()); break; // CMP (absolute, Y)
|
|
case 0xda: BusRead(); break; // *NOP (implied)
|
|
case 0xdb: DCP(AM_AbsoluteY()); break; // *DCP (absolute, Y)
|
|
case 0xdc: AM_AbsoluteX(); break; // *NOP (absolute, X)
|
|
case 0xdd: CMP(A, AM_AbsoluteX()); break; // CMP (absolute, X)
|
|
case 0xde: BusReadModifyWrite(DEC(AM_AbsoluteX(PageCrossingBehavior.AlwaysReadTwice))); break; // DEC (absolute, X)
|
|
case 0xdf: DCP(AM_AbsoluteX()); break; // *DCP (absolute, X)
|
|
|
|
case 0xe0: CMP(X, AM_Immediate()); break; // CPX (immediate)
|
|
case 0xe1: A = SBC(A, AM_IndexedIndirectX()); break; // SBC (indexed indirect X)
|
|
case 0xe2: AM_Immediate(); break; // *NOP (immediate)
|
|
case 0xe3: ISB(AM_IndexedIndirectX()); break; // *ISB (indexed indirect X)
|
|
case 0xe4: CMP(X, AM_ZeroPage()); break; // CPX (zero page)
|
|
case 0xe5: A = SBC(A, AM_ZeroPage()); break; // SBC (zero page)
|
|
case 0xe6: BusReadModifyWrite(INC(AM_ZeroPage())); break; // INC (zero page)
|
|
case 0xe7: ISB(AM_ZeroPage()); break; // *ISB (zero page)
|
|
case 0xe8: BusRead(); X = INC(X); break; // INX (implied)
|
|
case 0xe9: A = SBC(A, AM_Immediate()); break; // SBC (immediate)
|
|
case 0xea: BusRead(); break; // NOP (implied)
|
|
case 0xeb: A = SBC(A, AM_Immediate()); break; // *SBC (immediate)
|
|
case 0xec: CMP(X, AM_Absolute()); break; // CPX (absolute)
|
|
case 0xed: A = SBC(A, AM_Absolute()); break; // SBC (absolute)
|
|
case 0xee: BusReadModifyWrite(INC(AM_Absolute())); break; // *ISB (absolute)
|
|
|
|
case 0xf0: Branch(Zero != 0); break; // BEQ (relative)
|
|
case 0xf1: A = SBC(A, AM_IndirectIndexedY()); break; // SBC (indirect indexed Y)
|
|
case 0xf2: break;
|
|
case 0xf3: ISB(AM_IndirectIndexedY()); break; // *ISB (indirect indexed Y)
|
|
case 0xf4: AM_ZeroPageX(); break; // *NOP (zero page, X)
|
|
case 0xf5: A = SBC(A, AM_ZeroPageX()); break; // SBC (zero page, X)
|
|
case 0xf6: BusReadModifyWrite(INC(AM_ZeroPageX())); break; // INC (zero page, X)
|
|
case 0xf7: ISB(AM_ZeroPageX()); break; // *ISB (zero page, X)
|
|
case 0xf8: BusRead(); SetFlag(ref p, StatusBits.DF); break; // SED (implied)
|
|
case 0xf9: A = SBC(A, AM_AbsoluteY()); break; // SBC (absolute, Y)
|
|
case 0xfa: BusRead(); break; // *NOP (implied)
|
|
case 0xfb: ISB(AM_AbsoluteY()); break; // *ISB (absolute, Y)
|
|
case 0xfc: AM_AbsoluteX(); break; // *NOP (absolute, X)
|
|
case 0xfd: A = SBC(A, AM_AbsoluteX()); break; // SBC (absolute, X)
|
|
case 0xfe: BusReadModifyWrite(INC(AM_AbsoluteX(PageCrossingBehavior.AlwaysReadTwice))); break; // INC (absolute, X)
|
|
case 0xff: ISB(AM_AbsoluteX()); break; // *ISB (absolute, X)
|
|
}
|
|
|
|
return Cycles;
|
|
}
|
|
|
|
public override int Step()
|
|
{
|
|
ResetCycles();
|
|
OnExecutingInstruction();
|
|
if (Powered)
|
|
{
|
|
Tick();
|
|
if (SO().Lowered())
|
|
HandleSO();
|
|
if (RDY().Raised())
|
|
{
|
|
LowerSYNC(); // Instruction fetch beginning
|
|
OpCode = Bus.Read(PC++); // can't use fetchByte
|
|
if (RESET().Lowered())
|
|
HandleRESET();
|
|
else if (NMI().Lowered())
|
|
HandleNMI();
|
|
else if (INT().Lowered() && (InterruptMasked == 0))
|
|
HandleINT();
|
|
Execute();
|
|
}
|
|
}
|
|
OnExecutedInstruction();
|
|
return Cycles;
|
|
}
|
|
|
|
protected override byte Pop() => GetBytePaged(1, ++S);
|
|
|
|
protected override void Push(byte value) => SetBytePaged(1, S--, value);
|
|
|
|
protected override sealed void HandleRESET()
|
|
{
|
|
RaiseRESET();
|
|
handlingRESET = true;
|
|
OpCode = 0x00; // BRK
|
|
}
|
|
|
|
protected override sealed void HandleINT()
|
|
{
|
|
RaiseINT();
|
|
handlingINT = true;
|
|
OpCode = 0x00; // BRK
|
|
}
|
|
|
|
private void HandleNMI()
|
|
{
|
|
RaiseNMI();
|
|
handlingNMI = true;
|
|
OpCode = 0x00; // BRK
|
|
}
|
|
|
|
private void HandleSO()
|
|
{
|
|
RaiseSO();
|
|
P |= (byte)StatusBits.VF;
|
|
}
|
|
|
|
private void Interrupt()
|
|
{
|
|
var reset = handlingRESET;
|
|
var nmi = handlingNMI;
|
|
var irq = handlingINT;
|
|
var hardware = nmi || irq || reset;
|
|
var software = !hardware;
|
|
if (reset)
|
|
{
|
|
DummyPush(HighByte(PC));
|
|
DummyPush(LowByte(PC));
|
|
DummyPush(P);
|
|
}
|
|
else
|
|
{
|
|
PushWord(PC);
|
|
Push((byte)(P | (int)(software ? StatusBits.BF : 0)));
|
|
}
|
|
SetFlag(ref p, StatusBits.IF); // Disable IRQ
|
|
var vector = reset ? RSTvector : (nmi ? NMIvector : IRQvector);
|
|
Jump(GetWordPaged(0xff, vector));
|
|
handlingRESET = handlingNMI = handlingINT = false;
|
|
}
|
|
|
|
private void DummyPush(byte value)
|
|
{
|
|
Tick();
|
|
Bus.Data = value;
|
|
Bus.Address = MakeWord(S--, 1);
|
|
}
|
|
|
|
// Addressing modes
|
|
|
|
private ushort Address_Absolute() => FetchWord();
|
|
|
|
private byte Address_ZeroPage() => FetchByte();
|
|
|
|
private ushort Address_ZeroPageIndirect() => GetWordPaged(0, Address_ZeroPage());
|
|
|
|
private ushort Address_Indirect()
|
|
{
|
|
var address = Address_Absolute();
|
|
return GetWordPaged(HighByte(address), LowByte(address));
|
|
}
|
|
|
|
private byte Address_ZeroPageX()
|
|
{
|
|
var address = Address_ZeroPage();
|
|
BusRead(address);
|
|
return LowByte(address + X);
|
|
}
|
|
|
|
private byte Address_ZeroPageY()
|
|
{
|
|
var address = Address_ZeroPage();
|
|
BusRead(address);
|
|
return LowByte(address + Y);
|
|
}
|
|
|
|
private Tuple<ushort, byte> Address_AbsoluteX()
|
|
{
|
|
var address = Address_Absolute();
|
|
var page = HighByte(address);
|
|
address += X;
|
|
return new Tuple<ushort, byte>(address, page);
|
|
}
|
|
|
|
private Tuple<ushort, byte> Address_AbsoluteY()
|
|
{
|
|
var address = Address_Absolute();
|
|
var page = HighByte(address);
|
|
address += Y;
|
|
return new Tuple<ushort, byte>(address, page);
|
|
}
|
|
|
|
private ushort Address_IndexedIndirectX() => GetWordPaged(0, Address_ZeroPageX());
|
|
|
|
private Tuple<ushort, byte> Address_IndirectIndexedY()
|
|
{
|
|
var address = Address_ZeroPageIndirect();
|
|
var page = HighByte(address);
|
|
address += Y;
|
|
return new Tuple<ushort, byte>(address, page);
|
|
}
|
|
|
|
private ushort Address_relative_byte()
|
|
{
|
|
var offset = (sbyte)FetchByte();
|
|
intermediate = (ushort)(PC + offset);
|
|
return intermediate;
|
|
}
|
|
|
|
//
|
|
|
|
private byte AM_Immediate() => FetchByte();
|
|
|
|
private byte AM_Absolute() => BusRead(Address_Absolute());
|
|
|
|
private byte AM_ZeroPage() => BusRead(Address_ZeroPage());
|
|
|
|
private byte AM_AbsoluteX(PageCrossingBehavior behaviour = PageCrossingBehavior.MaybeReadTwice)
|
|
{
|
|
var crossed = Address_AbsoluteX();
|
|
var address = crossed.Item1;
|
|
var page = crossed.Item2;
|
|
var possible = GetBytePaged(page, LowByte(address));
|
|
if ((behaviour == PageCrossingBehavior.AlwaysReadTwice) || (page != HighByte(address)))
|
|
possible = BusRead(address);
|
|
return possible;
|
|
}
|
|
|
|
private byte AM_AbsoluteY()
|
|
{
|
|
var crossed = Address_AbsoluteY();
|
|
var address = crossed.Item1;
|
|
var page = crossed.Item2;
|
|
var possible = GetBytePaged(page, LowByte(address));
|
|
if (page != HighByte(address))
|
|
possible = BusRead(address);
|
|
return possible;
|
|
}
|
|
|
|
private byte AM_ZeroPageX() => BusRead(Address_ZeroPageX());
|
|
|
|
private byte AM_ZeroPageY() => BusRead(Address_ZeroPageY());
|
|
|
|
private byte AM_IndexedIndirectX() => BusRead(Address_IndexedIndirectX());
|
|
|
|
private byte AM_IndirectIndexedY()
|
|
{
|
|
var crossed = Address_IndirectIndexedY();
|
|
var address = crossed.Item1;
|
|
var page = crossed.Item2;
|
|
var possible = GetBytePaged(page, LowByte(address));
|
|
if (page != HighByte(address))
|
|
possible = BusRead(address);
|
|
return possible;
|
|
}
|
|
|
|
// Flag adjustment
|
|
|
|
public static void SetFlag(ref byte f, StatusBits flag) => SetFlag(ref f, (byte)flag);
|
|
private static void SetFlag(ref byte f, StatusBits flag, int condition) => SetFlag(ref f, (byte)flag, condition);
|
|
private static void SetFlag(ref byte f, StatusBits flag, bool condition) => SetFlag(ref f, (byte)flag, condition);
|
|
public static void ClearFlag(ref byte f, StatusBits flag) => ClearFlag(ref f, (byte)flag);
|
|
private static void ClearFlag(ref byte f, StatusBits flag, int condition) => ClearFlag(ref f, (byte)flag, condition);
|
|
private static void ClearFlag(ref byte f, StatusBits flag, bool condition) => ClearFlag(ref f, (byte)flag, condition);
|
|
|
|
private void AdjustZero(byte datum) => ClearFlag(ref p, StatusBits.ZF, datum);
|
|
private void AdjustNegative(byte datum) => SetFlag(ref p, StatusBits.NF, datum & (byte)StatusBits.NF);
|
|
|
|
private void AdjustNZ(byte datum)
|
|
{
|
|
AdjustZero(datum);
|
|
AdjustNegative(datum);
|
|
}
|
|
|
|
// Miscellaneous
|
|
|
|
private void Branch(bool condition)
|
|
{
|
|
var destination = Address_relative_byte();
|
|
if (condition) {
|
|
BusRead();
|
|
var page = HighByte(PC);
|
|
Jump(destination);
|
|
if (HighByte(PC) != page)
|
|
BusRead(LowByte(PC), page);
|
|
}
|
|
}
|
|
|
|
private byte Through(int data) => Through((byte)data);
|
|
|
|
private byte Through(byte data)
|
|
{
|
|
AdjustNZ(data);
|
|
return data;
|
|
}
|
|
|
|
private void BusReadModifyWrite(byte data)
|
|
{
|
|
// The read will have already taken place...
|
|
BusWrite();
|
|
BusWrite(data);
|
|
}
|
|
|
|
//
|
|
|
|
private byte SBC(byte operand, byte data)
|
|
{
|
|
var returned = SUB(operand, data, ~P & (int)StatusBits.CF);
|
|
|
|
var difference = intermediate;
|
|
AdjustNZ(LowByte(difference));
|
|
SetFlag(ref p, StatusBits.VF, (operand ^ data) & (operand ^ LowByte(difference)) & (int)StatusBits.NF);
|
|
ClearFlag(ref p, StatusBits.CF, HighByte(difference));
|
|
|
|
return returned;
|
|
}
|
|
|
|
private byte SUB(byte operand, byte data, int borrow = 0)
|
|
{
|
|
return Decimal != 0 ? SUB_d(operand, data, borrow) : SUB_b(operand, data, borrow);
|
|
}
|
|
|
|
private byte SUB_b(byte operand, byte data, int borrow)
|
|
{
|
|
intermediate = (ushort)(operand - data - borrow);
|
|
return LowByte(intermediate);
|
|
}
|
|
|
|
private byte SUB_d(byte operand, byte data, int borrow)
|
|
{
|
|
intermediate = (ushort)(operand - data - borrow);
|
|
|
|
byte low = (byte)(LowNibble(operand) - LowNibble(data) - borrow);
|
|
var lowNegative = low & (byte)StatusBits.NF;
|
|
if (lowNegative != 0)
|
|
low -= 6;
|
|
|
|
byte high = (byte)(HighNibble(operand) - HighNibble(data) - (lowNegative >> 7));
|
|
var highNegative = high & (byte)StatusBits.NF;
|
|
if (highNegative != 0)
|
|
high -= 6;
|
|
|
|
return (byte)(PromoteNibble(high) | LowNibble(low));
|
|
}
|
|
|
|
private byte ADC(byte operand, byte data)
|
|
{
|
|
var returned = ADD(operand, data, Carry);
|
|
AdjustNZ(LowByte(intermediate));
|
|
return returned;
|
|
}
|
|
|
|
private byte ADD(byte operand, byte data, int carry = 0)
|
|
{
|
|
return Decimal != 0 ? ADD_d(operand, data, carry) : ADD_b(operand, data, carry);
|
|
}
|
|
|
|
private byte ADD_b(byte operand, byte data, int carry)
|
|
{
|
|
intermediate = (ushort)(operand + data + carry);
|
|
|
|
SetFlag(ref p, StatusBits.VF, ~(operand ^ data) & (operand ^ LowByte(intermediate)) & (int)StatusBits.NF);
|
|
SetFlag(ref p, StatusBits.CF, HighByte(intermediate) & (int)StatusBits.CF);
|
|
|
|
return LowByte(intermediate);
|
|
}
|
|
|
|
private byte ADD_d(byte operand, byte data, int carry)
|
|
{
|
|
intermediate = (ushort)(operand + data + carry);
|
|
|
|
byte low = (byte)(LowNibble(operand) + LowNibble(data) + carry);
|
|
if (low > 9)
|
|
low += 6;
|
|
|
|
byte high = (byte)(HighNibble(operand) + HighNibble(data) + (low > 0xf ? 1 : 0));
|
|
SetFlag(ref p, StatusBits.VF, ~(operand ^ data) & (operand ^ PromoteNibble(high)) & (int)StatusBits.NF);
|
|
|
|
if (high > 9)
|
|
high += 6;
|
|
|
|
SetFlag(ref p, StatusBits.CF, high > 0xf);
|
|
|
|
return (byte)(PromoteNibble(high) | LowNibble(low));
|
|
}
|
|
|
|
private byte AndR(byte operand, byte data) => Through(operand & data);
|
|
|
|
private byte ASL(byte value)
|
|
{
|
|
SetFlag(ref p, StatusBits.CF, value & (byte)Bits.Bit7);
|
|
return Through(value << 1);
|
|
}
|
|
|
|
private void BIT(byte operand, byte data)
|
|
{
|
|
SetFlag(ref p, StatusBits.VF, data & (byte)StatusBits.VF);
|
|
AdjustZero((byte)(operand & data));
|
|
AdjustNegative(data);
|
|
}
|
|
|
|
private void CMP(byte first, byte second)
|
|
{
|
|
intermediate = (ushort)(first - second);
|
|
AdjustNZ(LowByte(intermediate));
|
|
ClearFlag(ref p, StatusBits.CF, HighByte(intermediate));
|
|
}
|
|
|
|
private byte DEC(byte value) => Through(value - 1);
|
|
|
|
byte EorR(byte operand, byte data) => Through(operand ^ data);
|
|
|
|
private byte INC(byte value) => Through(value + 1);
|
|
|
|
private void JSR()
|
|
{
|
|
var low = FetchByte();
|
|
GetBytePaged(1, S); // dummy read
|
|
PushWord(PC);
|
|
PC = MakeWord(low, FetchByte());
|
|
}
|
|
|
|
private byte LSR(byte value)
|
|
{
|
|
SetFlag(ref p, StatusBits.CF, value & (byte)Bits.Bit0);
|
|
return Through(value >> 1);
|
|
}
|
|
|
|
private byte OrR(byte operand, byte data) => Through(operand | data);
|
|
|
|
private void PHP() => Push((byte)(P | (byte)StatusBits.BF));
|
|
|
|
private void PLP() => P = (byte)((Pop() | (byte)StatusBits.RF) & (byte)~StatusBits.BF);
|
|
|
|
private byte ROL(byte operand)
|
|
{
|
|
var carryIn = Carry;
|
|
SetFlag(ref p, StatusBits.CF, operand & (byte)Bits.Bit7);
|
|
var result = (operand << 1) | carryIn;
|
|
return Through(result);
|
|
}
|
|
|
|
private byte ROR(byte operand)
|
|
{
|
|
var carryIn = Carry;
|
|
SetFlag(ref p, StatusBits.CF, operand & (byte)Bits.Bit0);
|
|
var result = (operand >> 1) | (carryIn << 7);
|
|
return Through(result);
|
|
}
|
|
|
|
private void RTI()
|
|
{
|
|
GetBytePaged(1, S); // dummy read
|
|
PLP();
|
|
Return();
|
|
}
|
|
|
|
private void RTS()
|
|
{
|
|
GetBytePaged(1, S); // dummy read
|
|
Return();
|
|
FetchByte();
|
|
}
|
|
|
|
// Undocumented compound instructions
|
|
|
|
private void ANC(byte value)
|
|
{
|
|
A = AndR(A, value);
|
|
SetFlag(ref p, StatusBits.CF, A & (byte)Bits.Bit7);
|
|
}
|
|
|
|
private void ARR(byte value)
|
|
{
|
|
A = AndR(A, value);
|
|
A = ROR(A);
|
|
SetFlag(ref p, StatusBits.CF, A & (byte)Bits.Bit6);
|
|
SetFlag(ref p, StatusBits.VF, ((A & (byte)Bits.Bit6) >> 6) ^ ((A & (byte)Bits.Bit5) >> 5));
|
|
}
|
|
|
|
private void ASR(byte value)
|
|
{
|
|
A = AndR(A, value);
|
|
A = LSR(A);
|
|
}
|
|
|
|
private void AXS(byte value)
|
|
{
|
|
X = Through(SUB((byte)(A & X), value));
|
|
ClearFlag(ref p, StatusBits.CF, HighByte(intermediate));
|
|
}
|
|
|
|
private void DCP(byte value)
|
|
{
|
|
BusReadModifyWrite(DEC(value));
|
|
CMP(A, Bus.Data);
|
|
}
|
|
|
|
private void ISB(byte value)
|
|
{
|
|
BusReadModifyWrite(INC(value));
|
|
A = SBC(A, Bus.Data);
|
|
}
|
|
|
|
private void RLA(byte value)
|
|
{
|
|
BusReadModifyWrite(ROL(value));
|
|
A = AndR(A, Bus.Data);
|
|
}
|
|
|
|
private void RRA(byte value)
|
|
{
|
|
BusReadModifyWrite(ROR(value));
|
|
A = ADC(A, Bus.Data);
|
|
}
|
|
|
|
private void SLO(byte value)
|
|
{
|
|
BusReadModifyWrite(ASL(value));
|
|
A = OrR(A, Bus.Data);
|
|
}
|
|
|
|
private void SRE(byte value)
|
|
{
|
|
BusReadModifyWrite(LSR(value));
|
|
A = EorR(A, Bus.Data);
|
|
}
|
|
|
|
//
|
|
|
|
private void STA_AbsoluteX()
|
|
{
|
|
var crossed = Address_AbsoluteX();
|
|
var address = crossed.Item1;
|
|
var page = crossed.Item2;
|
|
GetBytePaged(page, LowByte(address));
|
|
BusWrite(address, A);
|
|
}
|
|
|
|
private void STA_AbsoluteY()
|
|
{
|
|
var crossed = Address_AbsoluteY();
|
|
var address = crossed.Item1;
|
|
var page = crossed.Item2;
|
|
GetBytePaged(page, LowByte(address));
|
|
BusWrite(address, A);
|
|
}
|
|
}
|
|
}
|