diff --git a/M6502/Core.cs b/M6502/Core.cs
new file mode 100644
index 0000000..33db8de
--- /dev/null
+++ b/M6502/Core.cs
@@ -0,0 +1,1112 @@
+//
+// Copyright (c) Adrian Conlon. All rights reserved.
+//
+
+//using EightBit;
+
+namespace M6502
+{
+ using EightBit;
+
+ public abstract class Core(Bus bus) : LittleEndianProcessor(bus)
+ {
+ #region Pin controls
+
+ #region NMI pin
+
+ public ref PinLevel NMI => ref nmiLine;
+ private PinLevel nmiLine = PinLevel.Low;
+ public event EventHandler? RaisingNMI;
+ public event EventHandler? RaisedNMI;
+ public event EventHandler? LoweringNMI;
+ public event EventHandler? LoweredNMI;
+ 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);
+
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
+ public virtual void RaiseNMI()
+ {
+ if (NMI.Lowered())
+ {
+ OnRaisingNMI();
+ NMI.Raise();
+ OnRaisedNMI();
+ }
+ }
+
+ public virtual void LowerNMI()
+ {
+ if (NMI.Raised())
+ {
+ OnLoweringNMI();
+ NMI.Lower();
+ OnLoweredNMI();
+ }
+ }
+
+ #endregion
+
+ #region SO pin
+
+ public ref PinLevel SO => ref soLine;
+ private PinLevel soLine = PinLevel.Low;
+ public event EventHandler? RaisingSO;
+ public event EventHandler? RaisedSO;
+ public event EventHandler? LoweringSO;
+ public event EventHandler? LoweredSO;
+
+ 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);
+
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
+ public virtual void RaiseSO()
+ {
+ if (SO.Lowered())
+ {
+ OnRaisingSO();
+ SO.Raise();
+ OnRaisedSO();
+ }
+ }
+
+ public virtual void LowerSO()
+ {
+ if (SO.Raised())
+ {
+ OnLoweringSO();
+ SO.Lower();
+ OnLoweredSO();
+ }
+ }
+
+ #endregion
+
+ #region SYNC pin
+
+ public ref PinLevel SYNC => ref syncLine;
+ private PinLevel syncLine = PinLevel.Low;
+ public event EventHandler? RaisingSYNC;
+ public event EventHandler? RaisedSYNC;
+ public event EventHandler? LoweringSYNC;
+ public event EventHandler? LoweredSYNC;
+
+ 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);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
+ protected virtual void RaiseSYNC()
+ {
+ OnRaisingSYNC();
+ SYNC.Raise();
+ OnRaisedSYNC();
+ }
+
+ protected virtual void LowerSYNC()
+ {
+ OnLoweringSYNC();
+ SYNC.Lower();
+ OnLoweredSYNC();
+ }
+
+ #endregion
+
+ #region RDY pin
+
+ public ref PinLevel RDY => ref rdyLine;
+ private PinLevel rdyLine = PinLevel.Low;
+ public event EventHandler? RaisingRDY;
+ public event EventHandler? RaisedRDY;
+ public event EventHandler? LoweringRDY;
+ public event EventHandler? LoweredRDY;
+ 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);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
+ public virtual void RaiseRDY()
+ {
+ if (RDY.Lowered())
+ {
+ OnRaisingRDY();
+ RDY.Raise();
+ OnRaisedRDY();
+ }
+ }
+
+ public virtual void LowerRDY()
+ {
+ if (RDY.Raised())
+ {
+ OnLoweringRDY();
+ RDY.Lower();
+ OnLoweredRDY();
+ }
+ }
+
+ #endregion
+
+ #region RW pin
+
+ public ref PinLevel RW => ref rwLine;
+ private PinLevel rwLine = PinLevel.Low;
+ public event EventHandler? RaisingRW;
+ public event EventHandler? RaisedRW;
+ public event EventHandler? LoweringRW;
+ public event EventHandler? LoweredRW;
+ protected virtual void OnRaisingRW() => RaisingRW?.Invoke(this, EventArgs.Empty);
+ protected virtual void OnRaisedRW() => RaisedRW?.Invoke(this, EventArgs.Empty);
+ protected virtual void OnLoweringRW() => LoweringRW?.Invoke(this, EventArgs.Empty);
+ protected virtual void OnLoweredRW() => LoweredRW?.Invoke(this, EventArgs.Empty);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
+ public virtual void RaiseRW()
+ {
+ if (RW.Lowered())
+ {
+ OnRaisingRW();
+ RW.Raise();
+ OnRaisedRW();
+ }
+ }
+
+ public virtual void LowerRW()
+ {
+ if (RW.Raised())
+ {
+ OnLoweringRW();
+ RW.Lower();
+ OnLoweredRW();
+ }
+ }
+
+ #endregion
+
+ protected override void OnRaisedPOWER()
+ {
+ X = (byte)Bits.Bit7;
+ Y = 0;
+ A = 0;
+ P = (byte)StatusBits.RF;
+ S = (byte)Mask.Eight;
+ LowerSYNC();
+ LowerRW();
+ base.OnRaisedPOWER();
+ }
+
+ #endregion
+
+ #region Interrupts
+
+ private const byte IRQvector = 0xfe; // IRQ vector
+ private const byte RSTvector = 0xfc; // RST vector
+ private const byte NMIvector = 0xfa; // NMI vector
+
+ protected enum InterruptSource { hardware, software };
+
+ protected enum InterruptType { reset, nonReset };
+
+ protected virtual void Interrupt(byte vector, InterruptSource source = InterruptSource.hardware, InterruptType type = InterruptType.nonReset)
+ {
+ if (type == InterruptType.reset)
+ {
+ DummyPush();
+ DummyPush();
+ DummyPush();
+ }
+ else
+ {
+ PushWord(PC);
+ Push((byte)(P | (source == InterruptSource.hardware ? 0 : (byte)StatusBits.BF)));
+ }
+ SetFlag(StatusBits.IF); // Disable IRQ
+ Jump(GetWordPaged(0xff, vector));
+ }
+
+ #region Interrupt etc. handlers
+
+ protected override sealed void HandleRESET()
+ {
+ RaiseRESET();
+ Interrupt(RSTvector, InterruptSource.hardware, InterruptType.reset);
+ }
+
+ protected override sealed void HandleINT()
+ {
+ RaiseINT();
+ Interrupt(IRQvector);
+ }
+
+ private void HandleNMI()
+ {
+ RaiseNMI();
+ Interrupt(NMIvector);
+ }
+
+ private void HandleSO()
+ {
+ RaiseSO();
+ SetFlag(StatusBits.VF);
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Registers
+
+ public byte X { get; set; }
+ public byte Y { get; set; }
+ public byte A { get; set; }
+ public byte S { get; set; }
+ public byte P { get; set; }
+
+ #endregion
+
+ #region Processor state helpers
+
+ protected int InterruptMasked => P & (byte)StatusBits.IF;
+ protected int DecimalMasked => P & (byte)StatusBits.DF;
+ protected int Negative => NegativeTest(P);
+ protected int Zero => ZeroTest(P);
+ protected int Overflow => OverflowTest(P);
+ protected int Carry => CarryTest(P);
+
+ protected static int NegativeTest(byte data) => data & (byte)StatusBits.NF;
+ protected static int ZeroTest(byte data) => data & (byte)StatusBits.ZF;
+ protected static int OverflowTest(byte data) => data & (byte)StatusBits.VF;
+ protected static int CarryTest(byte data) => data & (byte)StatusBits.CF;
+
+ #endregion
+
+ #region Bit/state twiddling
+
+ #region Bit twiddling
+
+ private static byte SetBit(byte f, StatusBits flag) => SetBit(f, (byte)flag);
+
+ private static byte SetBit(byte f, StatusBits flag, int condition) => SetBit(f, (byte)flag, condition);
+
+ private static byte SetBit(byte f, StatusBits flag, bool condition) => SetBit(f, (byte)flag, condition);
+
+ private static byte ClearBit(byte f, StatusBits flag) => ClearBit(f, (byte)flag);
+
+ private static byte ClearBit(byte f, StatusBits flag, int condition) => ClearBit(f, (byte)flag, condition);
+
+ #endregion
+
+ #region State flag twiddling
+
+ protected void SetFlag(StatusBits flag)
+ {
+ P = SetBit(P, flag);
+ }
+
+ protected void SetFlag(StatusBits which, int condition)
+ {
+ P = SetBit(P, which, condition);
+ }
+
+ protected void SetFlag(StatusBits which, bool condition)
+ {
+ P = SetBit(P, which, condition);
+ }
+
+ protected void ResetFlag(StatusBits which)
+ {
+ P = ClearBit(P, which);
+ }
+
+ protected void ResetFlag(StatusBits which, int condition)
+ {
+ P = ClearBit(P, which, condition);
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Cycle wastage
+
+ protected void SwallowRead() => MemoryRead(PC);
+
+ protected void SwallowPop() => MemoryRead(S, 1);
+
+ protected void SwallowFetch() => FetchByte();
+
+ #endregion
+
+ #region Core instruction dispatching
+
+ public override void Execute()
+ {
+ MaybeExecute();
+ }
+
+ protected virtual bool MaybeExecute()
+ {
+ var cycles = Cycles;
+ switch (OpCode)
+ {
+ case 0x00: SwallowFetch(); Interrupt(IRQvector, InterruptSource.software); break; // BRK (implied)
+ case 0x01: IndexedIndirectXRead(); OrR(); break; // ORA (indexed indirect X)
+ case 0x05: ZeroPageRead(); OrR(); break; // ORA (zero page)
+ case 0x06: ZeroPageRead(); ModifyWrite(ASL()); break; // ASL (zero page)
+ case 0x08: SwallowRead(); PHP(); break; // PHP (implied)
+ case 0x09: ImmediateRead(); OrR(); break; // ORA (immediate)
+ case 0x0a: SwallowRead(); A = ASL(A); break; // ASL A (implied)
+ case 0x0d: AbsoluteRead(); OrR(); break; // ORA (absolute)
+ case 0x0e: AbsoluteRead(); ModifyWrite(ASL()); break; // ASL (absolute)
+
+ case 0x10: BranchNot(Negative); break; // BPL (relative)
+ case 0x11: IndirectIndexedYRead(); OrR(); break; // ORA (indirect indexed Y)
+ case 0x15: ZeroPageXRead(); OrR(); break; // ORA (zero page, X)
+ case 0x16: ZeroPageXRead(); ModifyWrite(ASL()); break; // ASL (zero page, X)
+ case 0x18: SwallowRead(); ResetFlag(StatusBits.CF); break; // CLC (implied)
+ case 0x19: AbsoluteYRead(); OrR(); break; // ORA (absolute, Y)
+ case 0x1d: AbsoluteXRead(); OrR(); break; // ORA (absolute, X)
+ case 0x1e: AbsoluteXAddress(); FixupRead(); ModifyWrite(ASL()); break; // ASL (absolute, X)
+
+ case 0x20: JSR(); break; // JSR (absolute)
+ case 0x21: IndexedIndirectXRead(); AndR(); break; // AND (indexed indirect X)
+ case 0x24: ZeroPageRead(); BIT(); break; // BIT (zero page)
+ case 0x25: ZeroPageRead(); AndR(); break; // AND (zero page)
+ case 0x26: ZeroPageRead(); ModifyWrite(ROL()); break; // ROL (zero page)
+ case 0x28: SwallowRead(); PLP(); break; // PLP (implied)
+ case 0x29: ImmediateRead(); AndR(); break; // AND (immediate)
+ case 0x2a: SwallowRead(); A = ROL(A); break; // ROL A (implied)
+ case 0x2c: AbsoluteRead(); BIT(); break; // BIT (absolute)
+ case 0x2d: AbsoluteRead(); AndR(); break; // AND (absolute)
+ case 0x2e: AbsoluteRead(); ModifyWrite(ROL()); break; // ROL (absolute)
+
+ case 0x30: Branch(Negative); break; // BMI (relative)
+ case 0x31: IndirectIndexedYRead(); AndR(); break; // AND (indirect indexed Y)
+ case 0x35: ZeroPageXRead(); AndR(); break; // AND (zero page, X)
+ case 0x36: ZeroPageXRead(); ModifyWrite(ROL()); break; // ROL (zero page, X)
+ case 0x38: SwallowRead(); SetFlag(StatusBits.CF); break; // SEC (implied)
+ case 0x39: AbsoluteYRead(); AndR(); break; // AND (absolute, Y)
+ case 0x3d: AbsoluteXRead(); AndR(); break; // AND (absolute, X)
+ case 0x3e: AbsoluteXAddress(); FixupRead(); ModifyWrite(ROL()); break; // ROL (absolute, X)
+
+ case 0x40: SwallowRead(); RTI(); break; // RTI (implied)
+ case 0x41: IndexedIndirectXRead(); EorR(); break; // EOR (indexed indirect X)
+ case 0x44: ZeroPageRead(); break; // *NOP (zero page)
+ case 0x45: ZeroPageRead(); EorR(); break; // EOR (zero page)
+ case 0x46: ZeroPageRead(); ModifyWrite(LSR()); break; // LSR (zero page)
+ case 0x48: SwallowRead(); Push(A); break; // PHA (implied)
+ case 0x49: ImmediateRead(); EorR(); break; // EOR (immediate)
+ case 0x4a: SwallowRead(); A = LSR(A); break; // LSR A (implied)
+ case 0x4c: AbsoluteAddress(); Jump(Bus.Address); break; // JMP (absolute)
+ case 0x4d: AbsoluteRead(); EorR(); break; // EOR (absolute)
+ case 0x4e: AbsoluteRead(); ModifyWrite(LSR()); break; // LSR (absolute)
+
+ case 0x50: BranchNot(Overflow); break; // BVC (relative)
+ case 0x51: IndirectIndexedYRead(); EorR(); break; // EOR (indirect indexed Y)
+ case 0x54: ZeroPageXRead(); break; // *NOP (zero page, X)
+ case 0x55: ZeroPageXRead(); EorR(); break; // EOR (zero page, X)
+ case 0x56: ZeroPageXRead(); ModifyWrite(LSR()); break; // LSR (zero page, X)
+ case 0x58: SwallowRead(); ResetFlag(StatusBits.IF); break; // CLI (implied)
+ case 0x59: AbsoluteYRead(); EorR(); break; // EOR (absolute, Y)
+ case 0x5d: AbsoluteXRead(); EorR(); break; // EOR (absolute, X)
+ case 0x5e: AbsoluteXAddress(); FixupRead(); ModifyWrite(LSR()); break; // LSR (absolute, X)
+
+ case 0x60: SwallowRead(); Return(); break; // RTS (implied)
+ case 0x61: IndexedIndirectXRead(); ADC(); break; // ADC (indexed indirect X)
+ case 0x65: ZeroPageRead(); ADC(); break; // ADC (zero page)
+ case 0x66: ZeroPageRead(); ModifyWrite(ROR()); break; // ROR (zero page)
+ case 0x68: SwallowRead(); SwallowPop(); A = Through(Pop()); break; // PLA (implied)
+ case 0x69: ImmediateRead(); ADC(); break; // ADC (immediate)
+ case 0x6a: SwallowRead(); A = ROR(A); break; // ROR A (implied)
+ case 0x6c: IndirectAddress(); Jump(Bus.Address); break; // JMP (indirect)
+ case 0x6d: AbsoluteRead(); ADC(); break; // ADC (absolute)
+ case 0x6e: AbsoluteRead(); ModifyWrite(ROR()); break; // ROR (absolute)
+
+ case 0x70: Branch(Overflow); break; // BVS (relative)
+ case 0x71: IndirectIndexedYRead(); ADC(); break; // ADC (indirect indexed Y)
+ case 0x75: ZeroPageXRead(); ADC(); break; // ADC (zero page, X)
+ case 0x76: ZeroPageXRead(); ModifyWrite(ROR()); break; // ROR (zero page, X)
+ case 0x78: SwallowRead(); SetFlag(StatusBits.IF); break; // SEI (implied)
+ case 0x79: AbsoluteYRead(); ADC(); break; // ADC (absolute, Y)
+ case 0x7d: AbsoluteXRead(); ADC(); break; // ADC (absolute, X)
+ case 0x7e: AbsoluteXAddress(); FixupRead(); ModifyWrite(ROR()); break; // ROR (absolute, X)
+
+ case 0x81: IndexedIndirectXAddress(); MemoryWrite(A); break; // STA (indexed indirect X)
+ case 0x82: ImmediateRead(); break; // *NOP (immediate)
+ case 0x84: ZeroPageAddress(); MemoryWrite(Y); break; // STY (zero page)
+ case 0x85: ZeroPageAddress(); MemoryWrite(A); break; // STA (zero page)
+ case 0x86: ZeroPageAddress(); MemoryWrite(X); break; // STX (zero page)
+ case 0x88: SwallowRead(); Y = DEC(Y); break; // DEY (implied)
+ case 0x8a: SwallowRead(); A = Through(X); break; // TXA (implied)
+ case 0x8c: AbsoluteAddress(); MemoryWrite(Y); break; // STY (absolute)
+ case 0x8d: AbsoluteAddress(); MemoryWrite(A); break; // STA (absolute)
+ case 0x8e: AbsoluteAddress(); MemoryWrite(X); break; // STX (absolute)
+
+ case 0x90: BranchNot(Carry); break; // BCC (relative)
+ case 0x91: IndirectIndexedYAddress(); Fixup(); MemoryWrite(A); break; // STA (indirect indexed Y)
+ case 0x94: ZeroPageXAddress(); MemoryWrite(Y); break; // STY (zero page, X)
+ case 0x95: ZeroPageXAddress(); MemoryWrite(A); break; // STA (zero page, X)
+ case 0x96: ZeroPageYAddress(); MemoryWrite(X); break; // STX (zero page, Y)
+ case 0x98: SwallowRead(); A = Through(Y); break; // TYA (implied)
+ case 0x99: AbsoluteYAddress(); Fixup(); MemoryWrite(A); break; // STA (absolute, Y)
+ case 0x9a: SwallowRead(); S = X; break; // TXS (implied)
+ case 0x9d: AbsoluteXAddress(); Fixup(); MemoryWrite(A); break; // STA (absolute, X)
+
+ case 0xa0: ImmediateRead(); Y = Through(); break; // LDY (immediate)
+ case 0xa1: IndexedIndirectXRead(); A = Through(); break; // LDA (indexed indirect X)
+ case 0xa2: ImmediateRead(); X = Through(); break; // LDX (immediate)
+ case 0xa4: ZeroPageRead(); Y = Through(); break; // LDY (zero page)
+ case 0xa5: ZeroPageRead(); A = Through(); break; // LDA (zero page)
+ case 0xa6: ZeroPageRead(); X = Through(); break; // LDX (zero page)
+ case 0xa8: SwallowRead(); Y = Through(A); break; // TAY (implied)
+ case 0xa9: ImmediateRead(); A = Through(); break; // LDA (immediate)
+ case 0xaa: SwallowRead(); X = Through(A); break; // TAX (implied)
+ case 0xac: AbsoluteRead(); Y = Through(); break; // LDY (absolute)
+ case 0xad: AbsoluteRead(); A = Through(); break; // LDA (absolute)
+ case 0xae: AbsoluteRead(); X = Through(); break; // LDX (absolute)
+
+ case 0xb0: Branch(Carry); break; // BCS (relative)
+ case 0xb1: IndirectIndexedYRead(); A = Through(); break; // LDA (indirect indexed Y)
+ case 0xb4: ZeroPageXRead(); Y = Through(); break; // LDY (zero page, X)
+ case 0xb5: ZeroPageXRead(); A = Through(); break; // LDA (zero page, X)
+ case 0xb6: ZeroPageYRead(); X = Through(); break; // LDX (zero page, Y)
+ case 0xb8: SwallowRead(); ResetFlag(StatusBits.VF); break; // CLV (implied)
+ case 0xb9: AbsoluteYRead(); A = Through(); break; // LDA (absolute, Y)
+ case 0xba: SwallowRead(); X = Through(S); break; // TSX (implied)
+ case 0xbc: AbsoluteXRead(); Y = Through(); break; // LDY (absolute, X)
+ case 0xbd: AbsoluteXRead(); A = Through(); break; // LDA (absolute, X)
+ case 0xbe: AbsoluteYRead(); X = Through(); break; // LDX (absolute, Y)
+
+ case 0xc0: ImmediateRead(); CMP(Y); break; // CPY (immediate)
+ case 0xc1: IndexedIndirectXRead(); CMP(A); break; // CMP (indexed indirect X)
+ case 0xc2: ImmediateRead(); break; // *NOP (immediate)
+ case 0xc4: ZeroPageRead(); CMP(Y); break; // CPY (zero page)
+ case 0xc5: ZeroPageRead(); CMP(A); break; // CMP (zero page)
+ case 0xc6: ZeroPageRead(); ModifyWrite(DEC()); break; // DEC (zero page)
+ case 0xc8: SwallowRead(); Y = INC(Y); break; // INY (implied)
+ case 0xc9: ImmediateRead(); CMP(A); break; // CMP (immediate)
+ case 0xca: SwallowRead(); X = DEC(X); break; // DEX (implied)
+ case 0xcc: AbsoluteRead(); CMP(Y); break; // CPY (absolute)
+ case 0xcd: AbsoluteRead(); CMP(A); break; // CMP (absolute)
+ case 0xce: AbsoluteRead(); ModifyWrite(DEC()); break; // DEC (absolute)
+
+ case 0xd0: BranchNot(Zero); break; // BNE (relative)
+ case 0xd1: IndirectIndexedYRead(); CMP(A); break; // CMP (indirect indexed Y)
+ case 0xd4: ZeroPageXRead(); break; // *NOP (zero page, X)
+ case 0xd5: ZeroPageXRead(); CMP(A); break; // CMP (zero page, X)
+ case 0xd6: ZeroPageXRead(); ModifyWrite(DEC()); break; // DEC (zero page, X)
+ case 0xd8: SwallowRead(); ResetFlag(StatusBits.DF); break; // CLD (implied)
+ case 0xd9: AbsoluteYRead(); CMP(A); break; // CMP (absolute, Y)
+ case 0xdd: AbsoluteXRead(); CMP(A); break; // CMP (absolute, X)
+ case 0xde: AbsoluteXAddress(); FixupRead(); ModifyWrite(DEC()); break; // DEC (absolute, X)
+
+ case 0xe0: ImmediateRead(); CMP(X); break; // CPX (immediate)
+ case 0xe1: IndexedIndirectXRead(); SBC(); break; // SBC (indexed indirect X)
+ case 0xe2: ImmediateRead(); break; // *NOP (immediate)
+ case 0xe4: ZeroPageRead(); CMP(X); break; // CPX (zero page)
+ case 0xe5: ZeroPageRead(); SBC(); break; // SBC (zero page)
+ case 0xe6: ZeroPageRead(); ModifyWrite(INC()); break; // INC (zero page)
+ case 0xe8: SwallowRead(); X = INC(X); break; // INX (implied)
+ case 0xe9: ImmediateRead(); SBC(); break; // SBC (immediate)
+ case 0xea: SwallowRead(); break; // NOP (implied)
+ case 0xec: AbsoluteRead(); CMP(X); break; // CPX (absolute)
+ case 0xed: AbsoluteRead(); SBC(); break; // SBC (absolute)
+ case 0xee: AbsoluteRead(); ModifyWrite(INC()); break; // INC (absolute)
+
+ case 0xf0: Branch(Zero); break; // BEQ (relative)
+ case 0xf1: IndirectIndexedYRead(); SBC(); break; // SBC (indirect indexed Y)
+ case 0xf4: ZeroPageXRead(); break; // *NOP (zero page, X)
+ case 0xf5: ZeroPageXRead(); SBC(); break; // SBC (zero page, X)
+ case 0xf6: ZeroPageXRead(); ModifyWrite(INC()); break; // INC (zero page, X)
+ case 0xf8: SwallowRead(); SetFlag(StatusBits.DF); break; // SED (implied)
+ case 0xf9: AbsoluteYRead(); SBC(); break; // SBC (absolute, Y)
+ case 0xfd: AbsoluteXRead(); SBC(); break; // SBC (absolute, X)
+ case 0xfe: AbsoluteXAddress(); FixupRead(); ModifyWrite(INC()); break; // INC (absolute, X)
+ }
+
+ return cycles != Cycles;
+ }
+
+ public override void PoweredStep()
+ {
+ Tick();
+ if (SO.Lowered())
+ {
+ HandleSO();
+ }
+
+ if (RDY.Raised())
+ {
+ FetchInstruction();
+ if (RESET.Lowered())
+ {
+ HandleRESET();
+ }
+ else if (NMI.Lowered())
+ {
+ HandleNMI();
+ }
+ else if (INT.Lowered() && InterruptMasked == 0)
+ {
+ HandleINT();
+ }
+ else
+ {
+ Execute();
+ }
+ }
+ }
+
+ private void FetchInstruction()
+ {
+ // Instruction fetch beginning
+ LowerSYNC();
+
+ System.Diagnostics.Debug.Assert(Cycles == 1, "An extra cycle has occurred");
+
+ // Can't use fetchByte, since that would add an extra tick.
+ ImmediateAddress();
+ OpCode = ReadFromBus();
+
+ System.Diagnostics.Debug.Assert(Cycles == 1, "BUS read has introduced stray cycles");
+
+ // Instruction fetch has now completed
+ RaiseSYNC();
+ }
+
+ #endregion
+
+ #region Bus/Memory access
+
+ protected override sealed void BusWrite()
+ {
+ Tick();
+ WriteToBus();
+ }
+
+ protected override sealed byte BusRead()
+ {
+ Tick();
+ return ReadFromBus();
+ }
+
+ private byte ReadFromBus()
+ {
+ RaiseRW();
+ return base.BusRead();
+ }
+
+ private void WriteToBus()
+ {
+ LowerRW();
+ base.BusWrite();
+ }
+
+ protected abstract void ModifyWrite(byte data);
+
+ #endregion
+
+ #region Stack access
+
+ protected override byte Pop()
+ {
+ RaiseStack();
+ return MemoryRead();
+ }
+
+ protected override void Push(byte value)
+ {
+ LowerStack();
+ MemoryWrite(value);
+ }
+
+ private void UpdateStack(byte position)
+ {
+ Bus.Address.Assign(position, 1);
+ }
+
+ private void LowerStack() => UpdateStack(S--);
+
+ private void RaiseStack() => UpdateStack(++S);
+
+ private void DummyPush()
+ {
+ LowerStack();
+ Tick(); // In place of the memory write
+ }
+
+ #endregion
+
+ #region Addressing modes
+
+ #region Address page fixup
+
+ private byte fixedPage;
+
+ private byte unfixedPage;
+
+ public byte FixedPage
+ {
+ get => fixedPage;
+ protected set => fixedPage = value;
+ }
+
+ public byte UnfixedPage
+ {
+ get => unfixedPage;
+ protected set => unfixedPage = value;
+ }
+
+ public bool Fixed => FixedPage != UnfixedPage;
+
+ protected void MaybeFixup()
+ {
+ if (Bus.Address.High != FixedPage)
+ {
+ Fixup();
+ }
+ }
+
+ protected abstract void Fixup();
+
+ protected void MaybeFixupRead()
+ {
+ MaybeFixup();
+ MemoryRead();
+ }
+
+ protected void FixupRead()
+ {
+ Fixup();
+ MemoryRead();
+ }
+
+ #endregion
+
+ #region Address resolution
+
+ protected void NoteFixedAddress(int address)
+ {
+ NoteFixedAddress((ushort)address);
+ }
+
+ protected void NoteFixedAddress(ushort address)
+ {
+ UnfixedPage = Bus.Address.High;
+ Intermediate.Word = address;
+ FixedPage = Intermediate.High;
+ Bus.Address.Low = Intermediate.Low;
+ }
+
+ protected void GetAddressPaged()
+ {
+ GetWordPaged();
+ Bus.Address.Assign(Intermediate);
+ }
+
+ protected void ImmediateAddress()
+ {
+ Bus.Address.Assign(PC);
+ ++PC.Word;
+ }
+
+ protected void AbsoluteAddress() => FetchWordAddress();
+
+ protected void ZeroPageAddress()
+ {
+ Bus.Address.Assign(FetchByte(), 0);
+ }
+
+ protected void ZeroPageIndirectAddress()
+ {
+ ZeroPageAddress();
+ GetAddressPaged();
+ }
+
+ protected abstract void IndirectAddress();
+
+ protected void ZeroPageWithIndexAddress(byte index)
+ {
+ ZeroPageRead();
+ Bus.Address.Low += index;
+ }
+
+ protected void ZeroPageXAddress() => ZeroPageWithIndexAddress(X);
+
+ protected void ZeroPageYAddress() => ZeroPageWithIndexAddress(Y);
+
+ private void AbsoluteWithIndexAddress(byte index)
+ {
+ AbsoluteAddress();
+ NoteFixedAddress(Bus.Address.Word + index);
+ }
+
+ protected void AbsoluteXAddress() => AbsoluteWithIndexAddress(X);
+
+ protected void AbsoluteYAddress() => AbsoluteWithIndexAddress(Y);
+
+ protected void IndexedIndirectXAddress()
+ {
+ ZeroPageXAddress();
+ GetAddressPaged();
+ }
+
+ protected void IndirectIndexedYAddress()
+ {
+ ZeroPageIndirectAddress();
+ NoteFixedAddress(Bus.Address.Word + Y);
+ }
+
+ #endregion
+
+ #region Address and read
+
+ protected void ImmediateRead()
+ {
+ ImmediateAddress();
+ MemoryRead();
+ }
+
+ protected void AbsoluteRead()
+ {
+ AbsoluteAddress();
+ MemoryRead();
+ }
+
+ protected void ZeroPageRead()
+ {
+ ZeroPageAddress();
+ MemoryRead();
+ }
+
+ protected void ZeroPageXRead()
+ {
+ ZeroPageXAddress();
+ MemoryRead();
+ }
+
+ protected void ZeroPageYRead()
+ {
+ ZeroPageYAddress();
+ MemoryRead();
+ }
+
+ protected void IndexedIndirectXRead()
+ {
+ IndexedIndirectXAddress();
+ MemoryRead();
+ }
+
+ protected void AbsoluteXRead()
+ {
+ AbsoluteXAddress();
+ MaybeFixupRead();
+ }
+
+ protected void AbsoluteYRead()
+ {
+ AbsoluteYAddress();
+ MaybeFixupRead();
+ }
+
+ protected void IndirectIndexedYRead()
+ {
+ IndirectIndexedYAddress();
+ MaybeFixupRead();
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Branching
+
+ protected void BranchNot(int condition) => Branch(condition == 0);
+
+ protected void Branch(int condition) => Branch(condition != 0);
+
+ protected void Branch(bool condition)
+ {
+ ImmediateRead();
+ if (condition)
+ {
+ var relative = (sbyte)Bus.Data;
+ SwallowRead();
+ FixupBranch(relative);
+ Jump(Bus.Address);
+ }
+ }
+
+ protected abstract void FixupBranch(sbyte relative);
+
+ #endregion
+
+ #region Data flag adjustment
+
+ protected void AdjustZero(byte datum) => ResetFlag(StatusBits.ZF, datum);
+
+ protected void AdjustNegative(byte datum) => SetFlag(StatusBits.NF, NegativeTest(datum));
+
+ protected void AdjustNZ(byte datum)
+ {
+ AdjustZero(datum);
+ AdjustNegative(datum);
+ }
+
+ protected byte Through() => Through(Bus.Data);
+
+ protected byte Through(int data) => Through((byte)data);
+
+ protected byte Through(byte data)
+ {
+ AdjustNZ(data);
+ return data;
+ }
+
+ #endregion
+
+ #region Instruction implementations
+
+ #region Instructions with BCD effects
+
+ #region Addition/subtraction
+
+ #region Subtraction
+
+ protected void AdjustOverflowSubtract(byte operand)
+ {
+ var data = Bus.Data;
+ var intermediate = Intermediate.Low;
+ SetFlag(StatusBits.VF, NegativeTest((byte)((operand ^ data) & (operand ^ intermediate))));
+ }
+
+ protected void SBC()
+ {
+ var operand = A;
+ A = SUB(operand, CarryTest((byte)~P));
+
+ AdjustOverflowSubtract(operand);
+ AdjustNZ(Intermediate.Low);
+ ResetFlag(StatusBits.CF, Intermediate.High);
+ }
+
+ private byte SUB(byte operand, int borrow) => DecimalMasked != 0 ? DecimalSUB(operand, borrow) : BinarySUB(operand, borrow);
+
+ protected byte BinarySUB(byte operand, int borrow = 0)
+ {
+ var data = Bus.Data;
+ Intermediate.Word = (ushort)(operand - data - borrow);
+ return Intermediate.Low;
+ }
+
+ private byte DecimalSUB(byte operand, int borrow)
+ {
+ _ = BinarySUB(operand, borrow);
+
+ var data = Bus.Data;
+ var low = (byte)(LowNibble(operand) - LowNibble(data) - borrow);
+ var lowNegative = NegativeTest(low);
+ if (lowNegative != 0)
+ {
+ low -= 6;
+ }
+
+ var high = (byte)(HighNibble(operand) - HighNibble(data) - (lowNegative >> 7));
+ var highNegative = NegativeTest(high);
+ if (highNegative != 0)
+ {
+ high -= 6;
+ }
+
+ return (byte)(PromoteNibble(high) | LowNibble(low));
+ }
+
+ #endregion
+
+ #region Addition
+
+ protected void AdjustOverflowAdd(byte operand)
+ {
+ var data = Bus.Data;
+ var intermediate = Intermediate.Low;
+ SetFlag(StatusBits.VF, NegativeTest((byte)(~(operand ^ data) & (operand ^ intermediate))));
+ }
+
+ protected void ADC()
+ {
+ A = DecimalMasked != 0 ? DecimalADC() : BinaryADC();
+ }
+
+ private byte BinaryADC()
+ {
+ var operand = A;
+ var data = Bus.Data;
+ Intermediate.Word = (ushort)(operand + data + Carry);
+
+ AdjustOverflowAdd(operand);
+ SetFlag(StatusBits.CF, CarryTest(Intermediate.High));
+
+ AdjustNZ(Intermediate.Low);
+
+ return Intermediate.Low;
+ }
+
+ private byte DecimalADC()
+ {
+ var operand = A;
+ var data = Bus.Data;
+
+ var low = (ushort)(LowerNibble(operand) + LowerNibble(data) + Carry);
+ Intermediate.Word = (ushort)(HigherNibble(operand) + HigherNibble(data));
+
+ AdjustZero(LowByte((ushort)(low + Intermediate.Word)));
+
+ if (low > 0x09)
+ {
+ Intermediate.Word += 0x10;
+ low += 0x06;
+ }
+
+ AdjustNegative(Intermediate.Low);
+ AdjustOverflowAdd(operand);
+
+ if (Intermediate.Word > 0x90)
+ Intermediate.Word += 0x60;
+
+ SetFlag(StatusBits.CF, Intermediate.High);
+
+ return (byte)(LowerNibble(LowByte(low)) | HigherNibble(Intermediate.Low));
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+
+ #region Bitwise operations
+
+ protected void OrR() => A = Through(A | Bus.Data);
+
+ protected void AndR() => A = Through(A & Bus.Data);
+
+ protected void EorR() => A = Through(A ^ Bus.Data);
+
+ protected void BIT()
+ {
+ var data = Bus.Data;
+ SetFlag(StatusBits.VF, OverflowTest(data));
+ AdjustZero((byte)(A & data));
+ AdjustNegative(data);
+ }
+
+ #endregion
+
+ protected void CMP(byte first)
+ {
+ var second = Bus.Data;
+ Intermediate.Word = (ushort)(first - second);
+ AdjustNZ(Intermediate.Low);
+ ResetFlag(StatusBits.CF, Intermediate.High);
+ }
+
+ #region Increment/decrement
+
+ protected byte DEC() => DEC(Bus.Data);
+
+ protected byte DEC(byte value) => Through(value - 1);
+
+ protected byte INC() => INC(Bus.Data);
+
+ protected byte INC(byte value) => Through(value + 1);
+
+ #endregion
+
+ #region Stack operations
+
+ private void JSR()
+ {
+ Intermediate.Low = FetchByte();
+ SwallowPop();
+ PushWord(PC);
+ PC.High = FetchByte();
+ PC.Low = Intermediate.Low;
+ }
+
+ private void PHP() => Push(SetBit(P, StatusBits.BF));
+
+ private void PLP()
+ {
+ SwallowPop();
+ P = ClearBit(SetBit(Pop(), StatusBits.RF), StatusBits.BF);
+ }
+
+ private void RTI()
+ {
+ PLP();
+ base.Return();
+ }
+
+ protected override void Return()
+ {
+ SwallowPop();
+ base.Return();
+ SwallowFetch();
+ }
+
+ #endregion
+
+ #region Shift/rotate operations
+
+ #region Shift
+
+ protected byte ASL() => ASL(Bus.Data);
+
+ protected byte ASL(byte value)
+ {
+ SetFlag(StatusBits.CF, NegativeTest(value));
+ return Through(value << 1);
+ }
+
+ protected byte LSR() => LSR(Bus.Data);
+
+ protected byte LSR(byte value)
+ {
+ SetFlag(StatusBits.CF, CarryTest(value));
+ return Through(value >> 1);
+ }
+
+ #endregion
+
+ #region Rotate
+
+ protected byte ROL() => ROL(Bus.Data);
+
+ protected byte ROL(byte value)
+ {
+ var carryIn = Carry;
+ return Through(ASL(value) | carryIn);
+ }
+
+ protected byte ROR() => ROR(Bus.Data);
+
+ protected byte ROR(byte value)
+ {
+ var carryIn = Carry;
+ return Through(LSR(value) | carryIn << 7);
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/M6502/CycleCountedEventArgs.cs b/M6502/CycleCountedEventArgs.cs
index 8cb1eb4..d1e1e51 100644
--- a/M6502/CycleCountedEventArgs.cs
+++ b/M6502/CycleCountedEventArgs.cs
@@ -1,4 +1,4 @@
-namespace EightBit
+namespace M6502
{
public class CycleCountedEventArgs(long cycles, long count) : EventArgs
{
diff --git a/M6502/Disassembler.cs b/M6502/Disassembler.cs
index 899686f..99dd147 100644
--- a/M6502/Disassembler.cs
+++ b/M6502/Disassembler.cs
@@ -2,16 +2,17 @@
// Copyright (c) Adrian Conlon. All rights reserved.
//
-namespace EightBit
+namespace M6502
{
using System.Globalization;
using System.Text;
+ using EightBit;
- public class Disassembler(Bus bus, M6502Core processor, Files.Symbols.Parser symbols)
+ public class Disassembler(Bus bus, Core processor, Symbols.Parser symbols)
{
private readonly Bus bus = bus;
- private readonly M6502Core processor = processor;
- private readonly Files.Symbols.Parser symbols = symbols;
+ private readonly Core processor = processor;
+ private readonly Symbols.Parser symbols = symbols;
private ushort address;
public static string DumpFlags(byte value)
@@ -34,16 +35,16 @@ namespace EightBit
public string Disassemble(ushort current)
{
- this.address = current;
+ address = current;
var output = new StringBuilder();
- var cell = this.bus.Peek(current);
+ var cell = bus.Peek(current);
output.Append(DumpByteValue(cell));
output.Append(' ');
- var next = this.bus.Peek((ushort)(current + 1));
+ var next = bus.Peek((ushort)(current + 1));
var relative = (ushort)(current + 2 + (sbyte)next);
var aaa = (cell & 0b11100000) >> 5;
@@ -59,31 +60,31 @@ namespace EightBit
switch (bbb)
{
case 0b000: // BRK
- output.Append(this.Disassemble_Implied("BRK"));
+ output.Append(Disassemble_Implied("BRK"));
break;
case 0b001: // DOP/NOP (0x04)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b010: // PHP
- output.Append(this.Disassemble_Implied("PHP"));
+ output.Append(Disassemble_Implied("PHP"));
break;
case 0b011: // TOP/NOP (0b00001100, 0x0c)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b100: // BPL
- output.Append(this.Disassemble_Relative("BPL", relative));
+ output.Append(Disassemble_Relative("BPL", relative));
break;
case 0b101: // DOP/NOP (0x14)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b110: // CLC
- output.Append(this.Disassemble_Implied("CLC"));
+ output.Append(Disassemble_Implied("CLC"));
break;
case 0b111: // TOP/NOP (0b00011100, 0x1c)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
default:
- throw new System.InvalidOperationException("Illegal instruction");
+ throw new InvalidOperationException("Illegal instruction");
}
break;
@@ -91,25 +92,25 @@ namespace EightBit
switch (bbb)
{
case 0b000: // JSR
- output.Append(this.Disassemble_Absolute("JSR"));
+ output.Append(Disassemble_Absolute("JSR"));
break;
case 0b010: // PLP
- output.Append(this.Disassemble_Implied("PLP"));
+ output.Append(Disassemble_Implied("PLP"));
break;
case 0b100: // BMI
- output.Append(this.Disassemble_Relative("BMI", relative));
+ output.Append(Disassemble_Relative("BMI", relative));
break;
case 0b101: // DOP/NOP (0x34)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b110: // SEC
- output.Append(this.Disassemble_Implied("SEC"));
+ output.Append(Disassemble_Implied("SEC"));
break;
case 0b111: // TOP/NOP (0b00111100, 0x3c)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
default: // BIT
- output.Append(this.Disassemble_AM_00(bbb, "BIT"));
+ output.Append(Disassemble_AM_00(bbb, "BIT"));
break;
}
@@ -118,31 +119,31 @@ namespace EightBit
switch (bbb)
{
case 0b000: // RTI
- output.Append(this.Disassemble_Implied("RTI"));
+ output.Append(Disassemble_Implied("RTI"));
break;
case 0b001: // DOP/NOP (0x44)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b010: // PHA
- output.Append(this.Disassemble_Implied("PHA"));
+ output.Append(Disassemble_Implied("PHA"));
break;
case 0b011: // JMP
- output.Append(this.Disassemble_Absolute("JMP"));
+ output.Append(Disassemble_Absolute("JMP"));
break;
case 0b100: // BVC
- output.Append(this.Disassemble_Relative("BVC", relative));
+ output.Append(Disassemble_Relative("BVC", relative));
break;
case 0b101: // DOP/NOP (0x54)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b110: // CLI
- output.Append(this.Disassemble_Implied("CLI"));
+ output.Append(Disassemble_Implied("CLI"));
break;
case 0b111: // TOP/NOP (0b01011100, 0x5c)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
default:
- throw new System.InvalidOperationException("Illegal addressing mode");
+ throw new InvalidOperationException("Illegal addressing mode");
}
break;
@@ -150,31 +151,31 @@ namespace EightBit
switch (bbb)
{
case 0b000: // RTS
- output.Append(this.Disassemble_Implied("RTS"));
+ output.Append(Disassemble_Implied("RTS"));
break;
case 0b001: // DOP/NOP (0x64)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b010: // PLA
- output.Append(this.Disassemble_Implied("PLA"));
+ output.Append(Disassemble_Implied("PLA"));
break;
case 0b011: // JMP (abs)
- output.Append(this.Disassemble_Indirect("JMP"));
+ output.Append(Disassemble_Indirect("JMP"));
break;
case 0b100: // BVS
- output.Append(this.Disassemble_Relative("BVS", relative));
+ output.Append(Disassemble_Relative("BVS", relative));
break;
case 0b101: // DOP/NOP (0x74)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b110: // SEI
- output.Append(this.Disassemble_Implied("SEI"));
+ output.Append(Disassemble_Implied("SEI"));
break;
case 0b111: // TOP/NOP (0b01111100, 0x7c)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
default:
- throw new System.InvalidOperationException("Illegal addressing mode");
+ throw new InvalidOperationException("Illegal addressing mode");
}
break;
@@ -182,19 +183,19 @@ namespace EightBit
switch (bbb)
{
case 0b000: // DOP/NOP (0x80)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b010: // DEY
- output.Append(this.Disassemble_Implied("DEY"));
+ output.Append(Disassemble_Implied("DEY"));
break;
case 0b100: // BCC
- output.Append(this.Disassemble_Relative("BCC", relative));
+ output.Append(Disassemble_Relative("BCC", relative));
break;
case 0b110: // TYA
- output.Append(this.Disassemble_Implied("TYA"));
+ output.Append(Disassemble_Implied("TYA"));
break;
default: // STY
- output.Append(this.Disassemble_AM_00(bbb, "STY"));
+ output.Append(Disassemble_AM_00(bbb, "STY"));
break;
}
@@ -203,16 +204,16 @@ namespace EightBit
switch (bbb)
{
case 0b010: // TAY
- output.Append(this.Disassemble_Implied("TAY"));
+ output.Append(Disassemble_Implied("TAY"));
break;
case 0b100: // BCS
- output.Append(this.Disassemble_Relative("BCS", relative));
+ output.Append(Disassemble_Relative("BCS", relative));
break;
case 0b110: // CLV
- output.Append(this.Disassemble_Implied("CLV"));
+ output.Append(Disassemble_Implied("CLV"));
break;
default: // LDY
- output.Append(this.Disassemble_AM_00(bbb, "LDY"));
+ output.Append(Disassemble_AM_00(bbb, "LDY"));
break;
}
@@ -221,22 +222,22 @@ namespace EightBit
switch (bbb)
{
case 0b010: // INY
- output.Append(this.Disassemble_Implied("INY"));
+ output.Append(Disassemble_Implied("INY"));
break;
case 0b100: // BNE
- output.Append(this.Disassemble_Relative("BNE", relative));
+ output.Append(Disassemble_Relative("BNE", relative));
break;
case 0b101: // DOP/NOP (0xd4)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b110: // CLD
- output.Append(this.Disassemble_Implied("CLD"));
+ output.Append(Disassemble_Implied("CLD"));
break;
case 0b111: // TOP/NOP (0b11011100, 0xdc)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
default: // CPY
- output.Append(this.Disassemble_AM_00(bbb, "CPY"));
+ output.Append(Disassemble_AM_00(bbb, "CPY"));
break;
}
@@ -245,22 +246,22 @@ namespace EightBit
switch (bbb)
{
case 0b010: // INX
- output.Append(this.Disassemble_Implied("INX"));
+ output.Append(Disassemble_Implied("INX"));
break;
case 0b100: // BEQ
- output.Append(this.Disassemble_Relative("BEQ", relative));
+ output.Append(Disassemble_Relative("BEQ", relative));
break;
case 0b101: // DOP/NOP (0xf4)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
case 0b110: // SED
- output.Append(this.Disassemble_Implied("SED"));
+ output.Append(Disassemble_Implied("SED"));
break;
case 0b111: // TOP/NOP (0b11111100, 0xfc)
- output.Append(this.Disassemble_AM_00(bbb, "*NOP"));
+ output.Append(Disassemble_AM_00(bbb, "*NOP"));
break;
default: // CPX
- output.Append(this.Disassemble_AM_00(bbb, "CPX"));
+ output.Append(Disassemble_AM_00(bbb, "CPX"));
break;
}
@@ -272,31 +273,31 @@ namespace EightBit
switch (aaa)
{
case 0b000: // ORA
- output.Append(this.Disassemble_AM_01(bbb, "ORA"));
+ output.Append(Disassemble_AM_01(bbb, "ORA"));
break;
case 0b001: // AND
- output.Append(this.Disassemble_AM_01(bbb, "AND"));
+ output.Append(Disassemble_AM_01(bbb, "AND"));
break;
case 0b010: // EOR
- output.Append(this.Disassemble_AM_01(bbb, "EOR"));
+ output.Append(Disassemble_AM_01(bbb, "EOR"));
break;
case 0b011: // ADC
- output.Append(this.Disassemble_AM_01(bbb, "ADC"));
+ output.Append(Disassemble_AM_01(bbb, "ADC"));
break;
case 0b100: // STA
- output.Append(this.Disassemble_AM_01(bbb, "STA"));
+ output.Append(Disassemble_AM_01(bbb, "STA"));
break;
case 0b101: // LDA
- output.Append(this.Disassemble_AM_01(bbb, "LDA"));
+ output.Append(Disassemble_AM_01(bbb, "LDA"));
break;
case 0b110: // CMP
- output.Append(this.Disassemble_AM_01(bbb, "CMP"));
+ output.Append(Disassemble_AM_01(bbb, "CMP"));
break;
case 0b111: // SBC
- output.Append(this.Disassemble_AM_01(bbb, "SBC"));
+ output.Append(Disassemble_AM_01(bbb, "SBC"));
break;
default:
- throw new System.InvalidOperationException("Illegal addressing mode");
+ throw new InvalidOperationException("Illegal addressing mode");
}
break;
@@ -307,10 +308,10 @@ namespace EightBit
switch (bbb)
{
case 0b110: // 0x1a
- output.Append(this.Disassemble_Implied("*NOP"));
+ output.Append(Disassemble_Implied("*NOP"));
break;
default:
- output.Append(this.Disassemble_AM_10(bbb, "ASL"));
+ output.Append(Disassemble_AM_10(bbb, "ASL"));
break;
}
@@ -319,10 +320,10 @@ namespace EightBit
switch (bbb)
{
case 0b110: // 0x3a
- output.Append(this.Disassemble_Implied("*NOP"));
+ output.Append(Disassemble_Implied("*NOP"));
break;
default:
- output.Append(this.Disassemble_AM_10(bbb, "ROL"));
+ output.Append(Disassemble_AM_10(bbb, "ROL"));
break;
}
@@ -331,10 +332,10 @@ namespace EightBit
switch (bbb)
{
case 0b110: // 0x5a
- output.Append(this.Disassemble_Implied("*NOP"));
+ output.Append(Disassemble_Implied("*NOP"));
break;
default:
- output.Append(this.Disassemble_AM_10(bbb, "LSR"));
+ output.Append(Disassemble_AM_10(bbb, "LSR"));
break;
}
@@ -343,10 +344,10 @@ namespace EightBit
switch (bbb)
{
case 0b110: // 0x7a
- output.Append(this.Disassemble_Implied("*NOP"));
+ output.Append(Disassemble_Implied("*NOP"));
break;
default:
- output.Append(this.Disassemble_AM_10(bbb, "ROR"));
+ output.Append(Disassemble_AM_10(bbb, "ROR"));
break;
}
@@ -355,13 +356,13 @@ namespace EightBit
switch (bbb)
{
case 0b010: // TXA
- output.Append(this.Disassemble_Implied("TXA"));
+ output.Append(Disassemble_Implied("TXA"));
break;
case 0b110: // TXS
- output.Append(this.Disassemble_Implied("TXS"));
+ output.Append(Disassemble_Implied("TXS"));
break;
default: // STX
- output.Append(this.Disassemble_AM_10_x(bbb, "STX"));
+ output.Append(Disassemble_AM_10_x(bbb, "STX"));
break;
}
@@ -370,13 +371,13 @@ namespace EightBit
switch (bbb)
{
case 0b010: // TAX
- output.Append(this.Disassemble_Implied("TAX"));
+ output.Append(Disassemble_Implied("TAX"));
break;
case 0b110: // TSX
- output.Append(this.Disassemble_Implied("TSX"));
+ output.Append(Disassemble_Implied("TSX"));
break;
default: // LDX
- output.Append(this.Disassemble_AM_10_x(bbb, "LDX"));
+ output.Append(Disassemble_AM_10_x(bbb, "LDX"));
break;
}
@@ -385,13 +386,13 @@ namespace EightBit
switch (bbb)
{
case 0b010: // DEX
- output.Append(this.Disassemble_Implied("DEX"));
+ output.Append(Disassemble_Implied("DEX"));
break;
case 0b110: // 0xda
- output.Append(this.Disassemble_Implied("*NOP"));
+ output.Append(Disassemble_Implied("*NOP"));
break;
default: // DEC
- output.Append(this.Disassemble_AM_10(bbb, "DEC"));
+ output.Append(Disassemble_AM_10(bbb, "DEC"));
break;
}
@@ -400,19 +401,19 @@ namespace EightBit
switch (bbb)
{
case 0b010: // NOP
- output.Append(this.Disassemble_Implied("NOP"));
+ output.Append(Disassemble_Implied("NOP"));
break;
case 0b110: // 0xfa
- output.Append(this.Disassemble_Implied("*NOP"));
+ output.Append(Disassemble_Implied("*NOP"));
break;
default: // INC
- output.Append(this.Disassemble_AM_10(bbb, "INC"));
+ output.Append(Disassemble_AM_10(bbb, "INC"));
break;
}
break;
default:
- throw new System.InvalidOperationException("Illegal instruction");
+ throw new InvalidOperationException("Illegal instruction");
}
break;
@@ -423,10 +424,10 @@ namespace EightBit
switch (bbb)
{
case 0b010:
- output.Append(this.Disassemble_Immediate("*AAC"));
+ output.Append(Disassemble_Immediate("*AAC"));
break;
default:
- output.Append(this.Disassemble_AM_01(bbb, "*SLO"));
+ output.Append(Disassemble_AM_01(bbb, "*SLO"));
break;
}
@@ -436,28 +437,28 @@ namespace EightBit
switch (bbb)
{
case 0b010:
- output.Append(this.Disassemble_Immediate("*AAC"));
+ output.Append(Disassemble_Immediate("*AAC"));
break;
default:
- output.Append(this.Disassemble_AM_01(bbb, "*RLA"));
+ output.Append(Disassemble_AM_01(bbb, "*RLA"));
break;
}
break;
case 0b010:
- output.Append(this.Disassemble_AM_01(bbb, "*SRE"));
+ output.Append(Disassemble_AM_01(bbb, "*SRE"));
break;
case 0b011:
- output.Append(this.Disassemble_AM_01(bbb, "*RRA"));
+ output.Append(Disassemble_AM_01(bbb, "*RRA"));
break;
case 0b100:
- output.Append(this.Disassemble_AM_11(bbb, "*SAX"));
+ output.Append(Disassemble_AM_11(bbb, "*SAX"));
break;
case 0b101:
- output.Append(this.Disassemble_AM_11(bbb, "*LAX"));
+ output.Append(Disassemble_AM_11(bbb, "*LAX"));
break;
case 0b110:
- output.Append(this.Disassemble_AM_11_x(bbb, "*DCP"));
+ output.Append(Disassemble_AM_11_x(bbb, "*DCP"));
break;
case 0b111:
switch (bbb)
@@ -469,23 +470,23 @@ namespace EightBit
case 0b101:
case 0b110:
case 0b111:
- output.Append(this.Disassemble_AM_01(bbb, "*ISB"));
+ output.Append(Disassemble_AM_01(bbb, "*ISB"));
break;
case 0b010:
- output.Append(this.Disassemble_AM_11(bbb, "*SBC"));
+ output.Append(Disassemble_AM_11(bbb, "*SBC"));
break;
default:
- throw new System.InvalidOperationException("Impossible addressing mode");
+ throw new InvalidOperationException("Impossible addressing mode");
}
break;
default:
- throw new System.InvalidOperationException("Illegal instruction group");
+ throw new InvalidOperationException("Illegal instruction group");
}
break;
default:
- throw new System.InvalidOperationException("Impossible instruction");
+ throw new InvalidOperationException("Impossible instruction");
}
return output.ToString();
@@ -495,27 +496,27 @@ namespace EightBit
#region Label conversions
- private string ConvertAddressAt(ushort absolute) => this.ConvertAddress(this.GetWord(absolute));
+ private string ConvertAddressAt(ushort absolute) => ConvertAddress(GetWord(absolute));
- private string ConvertAddress(ushort absolute) => this.TryGetLabel(absolute, out var label) ? label : "$" + DumpWordValue(absolute);
+ private string ConvertAddress(ushort absolute) => TryGetLabel(absolute, out var label) ? label : "$" + DumpWordValue(absolute);
- private string ConvertZPAddressAt(ushort absolute) => this.ConvertZPAddress(this.GetByte(absolute));
+ private string ConvertZPAddressAt(ushort absolute) => ConvertZPAddress(GetByte(absolute));
- private string ConvertZPAddress(byte absolute) => this.TryGetLabel(absolute, out var label) ? label : "$" + DumpByteValue(absolute);
+ private string ConvertZPAddress(byte absolute) => TryGetLabel(absolute, out var label) ? label : "$" + DumpByteValue(absolute);
private bool TryGetLabel(ushort absolute, out string name)
{
- return this.symbols.TryGetQualifiedLabelByAddress(absolute, out name);
+ return symbols.TryGetQualifiedLabelByAddress(absolute, out name);
}
private string MaybeGetLabel(ushort absolute)
{
- return this.symbols.MaybeGetQualifiedLabelByAddress(absolute);
+ return symbols.MaybeGetQualifiedLabelByAddress(absolute);
}
private string MaybeGetCodeLabel(ushort absolute)
{
- var label = this.MaybeGetLabel(absolute);
+ var label = MaybeGetLabel(absolute);
if (string.IsNullOrEmpty(label))
{
return string.Empty;
@@ -525,7 +526,7 @@ namespace EightBit
private string MaybeGetCodeLabel()
{
- return Pad(this.MaybeGetCodeLabel(this.address), 30);
+ return Pad(MaybeGetCodeLabel(address), 30);
}
#endregion
@@ -534,46 +535,46 @@ namespace EightBit
private bool TryGetConstant(ushort value, out string name)
{
- return this.symbols.TryGetQualifiedEquateValue(value, out name);
+ return symbols.TryGetQualifiedEquateValue(value, out name);
}
- private string ConvertConstantByte(ushort address) => this.ConvertConstant(this.GetByte(address));
+ private string ConvertConstantByte(ushort address) => ConvertConstant(GetByte(address));
- private string ConvertConstant(byte constant) => this.TryGetConstant(constant, out var label) ? label : "$" + DumpByteValue(constant);
+ private string ConvertConstant(byte constant) => TryGetConstant(constant, out var label) ? label : "$" + DumpByteValue(constant);
#endregion
#endregion
- private byte GetByte(ushort absolute) => this.bus.Peek(absolute);
+ private byte GetByte(ushort absolute) => bus.Peek(absolute);
- private ushort GetWord(ushort absolute) => this.processor.PeekWord(absolute).Word;
+ private ushort GetWord(ushort absolute) => processor.PeekWord(absolute).Word;
- private string Dump_Byte(ushort absolute) => DumpByteValue(this.GetByte(absolute));
+ private string Dump_Byte(ushort absolute) => DumpByteValue(GetByte(absolute));
- private string Dump_DByte(ushort absolute) => this.Dump_Byte(absolute) + " " + this.Dump_Byte(++absolute);
+ private string Dump_DByte(ushort absolute) => Dump_Byte(absolute) + " " + Dump_Byte(++absolute);
- private string Disassemble_Implied(string instruction) => $"{Pad()}\t{this.MaybeGetCodeLabel()}" + instruction;
+ private string Disassemble_Implied(string instruction) => $"{Pad()}\t{MaybeGetCodeLabel()}" + instruction;
- private string Disassemble_Absolute(string instruction) => this.AM_Absolute_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_Absolute();
+ private string Disassemble_Absolute(string instruction) => AM_Absolute_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_Absolute();
- private string Disassemble_Indirect(string instruction) => this.AM_Absolute_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " (" + this.AM_Absolute() + ")";
+ private string Disassemble_Indirect(string instruction) => AM_Absolute_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " (" + AM_Absolute() + ")";
- private string Disassemble_Relative(string instruction, ushort absolute) => this.AM_Immediate_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.ConvertAddress(absolute);
+ private string Disassemble_Relative(string instruction, ushort absolute) => AM_Immediate_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " " + ConvertAddress(absolute);
- private string Disassemble_Immediate(string instruction) => this.AM_Immediate_dump() + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_Immediate();
+ private string Disassemble_Immediate(string instruction) => AM_Immediate_dump() + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_Immediate();
- private string Disassemble_AM_00(int bbb, string instruction) => this.AM_00_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_00(bbb);
+ private string Disassemble_AM_00(int bbb, string instruction) => AM_00_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_00(bbb);
- private string Disassemble_AM_01(int bbb, string instruction) => this.AM_01_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_01(bbb);
+ private string Disassemble_AM_01(int bbb, string instruction) => AM_01_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_01(bbb);
- private string Disassemble_AM_10(int bbb, string instruction) => this.AM_10_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_10(bbb);
+ private string Disassemble_AM_10(int bbb, string instruction) => AM_10_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_10(bbb);
- private string Disassemble_AM_10_x(int bbb, string instruction) => this.AM_10_x_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_10_x(bbb);
+ private string Disassemble_AM_10_x(int bbb, string instruction) => AM_10_x_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_10_x(bbb);
- private string Disassemble_AM_11(int bbb, string instruction) => this.AM_11_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_11(bbb);
+ private string Disassemble_AM_11(int bbb, string instruction) => AM_11_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_11(bbb);
- private string Disassemble_AM_11_x(int bbb, string instruction) => this.AM_11_x_dump(bbb) + $"\t{this.MaybeGetCodeLabel()}" + instruction + " " + this.AM_11_x(bbb);
+ private string Disassemble_AM_11_x(int bbb, string instruction) => AM_11_x_dump(bbb) + $"\t{MaybeGetCodeLabel()}" + instruction + " " + AM_11_x(bbb);
private static string Pad(string? value = null, int limit = 10)
@@ -588,180 +589,180 @@ namespace EightBit
return value + padding;
}
- private string AM_Immediate_dump() => Pad(this.Dump_Byte((ushort)(this.address + 1)));
+ private string AM_Immediate_dump() => Pad(Dump_Byte((ushort)(address + 1)));
- private string AM_Immediate() => "#" + this.ConvertConstantByte((ushort)(this.address + 1));
+ private string AM_Immediate() => "#" + ConvertConstantByte((ushort)(address + 1));
- private string AM_Absolute_dump() => Pad(this.Dump_DByte((ushort)(this.address + 1)));
+ private string AM_Absolute_dump() => Pad(Dump_DByte((ushort)(address + 1)));
- private string AM_Absolute() => this.ConvertAddressAt((ushort)(this.address + 1));
+ private string AM_Absolute() => ConvertAddressAt((ushort)(address + 1));
- private string AM_ZeroPage_dump() => Pad(this.Dump_Byte((ushort)(this.address + 1)));
+ private string AM_ZeroPage_dump() => Pad(Dump_Byte((ushort)(address + 1)));
- private string AM_ZeroPage() => this.ConvertZPAddressAt((ushort)(this.address + 1));
+ private string AM_ZeroPage() => ConvertZPAddressAt((ushort)(address + 1));
- private string AM_ZeroPageX_dump() => this.AM_ZeroPage_dump();
+ private string AM_ZeroPageX_dump() => AM_ZeroPage_dump();
- private string AM_ZeroPageX() => this.AM_ZeroPage() + ",X";
+ private string AM_ZeroPageX() => AM_ZeroPage() + ",X";
- private string AM_ZeroPageY_dump() => this.AM_ZeroPage_dump();
+ private string AM_ZeroPageY_dump() => AM_ZeroPage_dump();
- private string AM_ZeroPageY() => this.AM_ZeroPage() + ",Y";
+ private string AM_ZeroPageY() => AM_ZeroPage() + ",Y";
- private string AM_AbsoluteX_dump() => this.AM_Absolute_dump();
+ private string AM_AbsoluteX_dump() => AM_Absolute_dump();
- private string AM_AbsoluteX() => this.AM_Absolute() + ",X";
+ private string AM_AbsoluteX() => AM_Absolute() + ",X";
- private string AM_AbsoluteY_dump() => this.AM_Absolute_dump();
+ private string AM_AbsoluteY_dump() => AM_Absolute_dump();
- private string AM_AbsoluteY() => this.AM_Absolute() + ",Y";
+ private string AM_AbsoluteY() => AM_Absolute() + ",Y";
- private string AM_IndexedIndirectX_dump() => this.AM_ZeroPage_dump();
+ private string AM_IndexedIndirectX_dump() => AM_ZeroPage_dump();
- private string AM_IndexedIndirectX() => "(" + this.ConvertZPAddressAt((ushort)(this.address + 1)) + ",X)";
+ private string AM_IndexedIndirectX() => "(" + ConvertZPAddressAt((ushort)(address + 1)) + ",X)";
- private string AM_IndirectIndexedY_dump() => this.AM_ZeroPage_dump();
+ private string AM_IndirectIndexedY_dump() => AM_ZeroPage_dump();
- private string AM_IndirectIndexedY() => "(" + this.ConvertZPAddressAt((ushort)(this.address + 1)) + "),Y";
+ private string AM_IndirectIndexedY() => "(" + ConvertZPAddressAt((ushort)(address + 1)) + "),Y";
private string AM_00_dump(int bbb) => bbb switch
{
- 0b000 => this.AM_Immediate_dump(),
- 0b001 => this.AM_ZeroPage_dump(),
- 0b011 => this.AM_Absolute_dump(),
- 0b101 => this.AM_ZeroPageX_dump(),
- 0b111 => this.AM_AbsoluteX_dump(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_Immediate_dump(),
+ 0b001 => AM_ZeroPage_dump(),
+ 0b011 => AM_Absolute_dump(),
+ 0b101 => AM_ZeroPageX_dump(),
+ 0b111 => AM_AbsoluteX_dump(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_00(int bbb) => bbb switch
{
- 0b000 => this.AM_Immediate(),
- 0b001 => this.AM_ZeroPage(),
- 0b011 => this.AM_Absolute(),
- 0b101 => this.AM_ZeroPageX(),
- 0b111 => this.AM_AbsoluteX(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_Immediate(),
+ 0b001 => AM_ZeroPage(),
+ 0b011 => AM_Absolute(),
+ 0b101 => AM_ZeroPageX(),
+ 0b111 => AM_AbsoluteX(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_01_dump(int bbb) => bbb switch
{
- 0b000 => this.AM_IndexedIndirectX_dump(),
- 0b001 => this.AM_ZeroPage_dump(),
- 0b010 => this.AM_Immediate_dump(),
- 0b011 => this.AM_Absolute_dump(),
- 0b100 => this.AM_IndirectIndexedY_dump(),
- 0b101 => this.AM_ZeroPageX_dump(),
- 0b110 => this.AM_AbsoluteY_dump(),
- 0b111 => this.AM_AbsoluteX_dump(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_IndexedIndirectX_dump(),
+ 0b001 => AM_ZeroPage_dump(),
+ 0b010 => AM_Immediate_dump(),
+ 0b011 => AM_Absolute_dump(),
+ 0b100 => AM_IndirectIndexedY_dump(),
+ 0b101 => AM_ZeroPageX_dump(),
+ 0b110 => AM_AbsoluteY_dump(),
+ 0b111 => AM_AbsoluteX_dump(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_01(int bbb) => bbb switch
{
- 0b000 => this.AM_IndexedIndirectX(),
- 0b001 => this.AM_ZeroPage(),
- 0b010 => this.AM_Immediate(),
- 0b011 => this.AM_Absolute(),
- 0b100 => this.AM_IndirectIndexedY(),
- 0b101 => this.AM_ZeroPageX(),
- 0b110 => this.AM_AbsoluteY(),
- 0b111 => this.AM_AbsoluteX(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_IndexedIndirectX(),
+ 0b001 => AM_ZeroPage(),
+ 0b010 => AM_Immediate(),
+ 0b011 => AM_Absolute(),
+ 0b100 => AM_IndirectIndexedY(),
+ 0b101 => AM_ZeroPageX(),
+ 0b110 => AM_AbsoluteY(),
+ 0b111 => AM_AbsoluteX(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_10_dump(int bbb) => bbb switch
{
- 0b000 => this.AM_Immediate_dump(),
- 0b001 => this.AM_ZeroPage_dump(),
+ 0b000 => AM_Immediate_dump(),
+ 0b001 => AM_ZeroPage_dump(),
0b010 => string.Empty,
- 0b011 => this.AM_Absolute_dump(),
- 0b101 => this.AM_ZeroPageX_dump(),
- 0b111 => this.AM_AbsoluteX_dump(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b011 => AM_Absolute_dump(),
+ 0b101 => AM_ZeroPageX_dump(),
+ 0b111 => AM_AbsoluteX_dump(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_10(int bbb) => bbb switch
{
- 0b000 => this.AM_Immediate(),
- 0b001 => this.AM_ZeroPage(),
+ 0b000 => AM_Immediate(),
+ 0b001 => AM_ZeroPage(),
0b010 => "A",
- 0b011 => this.AM_Absolute(),
- 0b101 => this.AM_ZeroPageX(),
- 0b111 => this.AM_AbsoluteX(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b011 => AM_Absolute(),
+ 0b101 => AM_ZeroPageX(),
+ 0b111 => AM_AbsoluteX(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_10_x_dump(int bbb) => bbb switch
{
- 0b000 => this.AM_Immediate_dump(),
- 0b001 => this.AM_ZeroPage_dump(),
+ 0b000 => AM_Immediate_dump(),
+ 0b001 => AM_ZeroPage_dump(),
0b010 => string.Empty,
- 0b011 => this.AM_Absolute_dump(),
- 0b101 => this.AM_ZeroPageY_dump(),
- 0b111 => this.AM_AbsoluteY_dump(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b011 => AM_Absolute_dump(),
+ 0b101 => AM_ZeroPageY_dump(),
+ 0b111 => AM_AbsoluteY_dump(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_10_x(int bbb) => bbb switch
{
- 0b000 => this.AM_Immediate(),
- 0b001 => this.AM_ZeroPage(),
+ 0b000 => AM_Immediate(),
+ 0b001 => AM_ZeroPage(),
0b010 => "A",
- 0b011 => this.AM_Absolute(),
- 0b101 => this.AM_ZeroPageY(),
- 0b111 => this.AM_AbsoluteY(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b011 => AM_Absolute(),
+ 0b101 => AM_ZeroPageY(),
+ 0b111 => AM_AbsoluteY(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_11_dump(int bbb) => bbb switch
{
- 0b000 => this.AM_IndexedIndirectX_dump(),
- 0b001 => this.AM_ZeroPage_dump(),
- 0b010 => this.AM_Immediate_dump(),
- 0b011 => this.AM_Absolute_dump(),
- 0b100 => this.AM_IndirectIndexedY_dump(),
- 0b101 => this.AM_ZeroPageY_dump(),
- 0b111 => this.AM_AbsoluteY_dump(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_IndexedIndirectX_dump(),
+ 0b001 => AM_ZeroPage_dump(),
+ 0b010 => AM_Immediate_dump(),
+ 0b011 => AM_Absolute_dump(),
+ 0b100 => AM_IndirectIndexedY_dump(),
+ 0b101 => AM_ZeroPageY_dump(),
+ 0b111 => AM_AbsoluteY_dump(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_11_x_dump(int bbb) => bbb switch
{
- 0b000 => this.AM_IndexedIndirectX_dump(),
- 0b001 => this.AM_ZeroPage_dump(),
- 0b010 => this.AM_Immediate_dump(),
- 0b011 => this.AM_Absolute_dump(),
- 0b100 => this.AM_IndirectIndexedY_dump(),
- 0b101 => this.AM_ZeroPageX_dump(),
- 0b110 => this.AM_AbsoluteY_dump(),
- 0b111 => this.AM_AbsoluteX_dump(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_IndexedIndirectX_dump(),
+ 0b001 => AM_ZeroPage_dump(),
+ 0b010 => AM_Immediate_dump(),
+ 0b011 => AM_Absolute_dump(),
+ 0b100 => AM_IndirectIndexedY_dump(),
+ 0b101 => AM_ZeroPageX_dump(),
+ 0b110 => AM_AbsoluteY_dump(),
+ 0b111 => AM_AbsoluteX_dump(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_11(int bbb) => bbb switch
{
- 0b000 => this.AM_IndexedIndirectX(),
- 0b001 => this.AM_ZeroPage(),
- 0b010 => this.AM_Immediate(),
- 0b011 => this.AM_Absolute(),
- 0b100 => this.AM_IndirectIndexedY(),
- 0b101 => this.AM_ZeroPageY(),
- 0b111 => this.AM_AbsoluteY(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_IndexedIndirectX(),
+ 0b001 => AM_ZeroPage(),
+ 0b010 => AM_Immediate(),
+ 0b011 => AM_Absolute(),
+ 0b100 => AM_IndirectIndexedY(),
+ 0b101 => AM_ZeroPageY(),
+ 0b111 => AM_AbsoluteY(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
private string AM_11_x(int bbb) => bbb switch
{
- 0b000 => this.AM_IndexedIndirectX(),
- 0b001 => this.AM_ZeroPage(),
- 0b010 => this.AM_Immediate(),
- 0b011 => this.AM_Absolute(),
- 0b100 => this.AM_IndirectIndexedY(),
- 0b101 => this.AM_ZeroPageX(),
- 0b110 => this.AM_AbsoluteY(),
- 0b111 => this.AM_AbsoluteX(),
- _ => throw new System.InvalidOperationException("Illegal addressing mode"),
+ 0b000 => AM_IndexedIndirectX(),
+ 0b001 => AM_ZeroPage(),
+ 0b010 => AM_Immediate(),
+ 0b011 => AM_Absolute(),
+ 0b100 => AM_IndirectIndexedY(),
+ 0b101 => AM_ZeroPageX(),
+ 0b110 => AM_AbsoluteY(),
+ 0b111 => AM_AbsoluteX(),
+ _ => throw new InvalidOperationException("Illegal addressing mode"),
};
}
}
diff --git a/M6502/M6502.HarteTest/Checker.cs b/M6502/M6502.HarteTest/Checker.cs
index ac78b70..c5678d4 100644
--- a/M6502/M6502.HarteTest/Checker.cs
+++ b/M6502/M6502.HarteTest/Checker.cs
@@ -6,9 +6,9 @@
{
private TestRunner Runner { get; }
- private EightBit.Files.Symbols.Parser Symbols { get; } = new();
+ private Symbols.Parser Symbols { get; } = new();
- private EightBit.Disassembler Disassembler { get; }
+ private Disassembler Disassembler { get; }
private bool CycleCountMismatch { get; set; }
@@ -29,7 +29,7 @@
public Checker(TestRunner runner)
{
this.Runner = runner;
- this.Disassembler = new(this.Runner, (EightBit.M6502Core)this.Runner.CPU, this.Symbols);
+ this.Disassembler = new(this.Runner, (M6502.Core)this.Runner.CPU, this.Symbols);
}
public void Check(Test test)
@@ -203,8 +203,8 @@
if (!p_good)
{
- this.Messages.Add($"Expected flags: {EightBit.Disassembler.DumpFlags(final.P)}");
- this.Messages.Add($"Actual flags : {EightBit.Disassembler.DumpFlags(cpu.P)}");
+ this.Messages.Add($"Expected flags: {Disassembler.DumpFlags(final.P)}");
+ this.Messages.Add($"Actual flags : {Disassembler.DumpFlags(cpu.P)}");
}
if (final.RAM == null)
diff --git a/M6502/M6502.Symbols/File.cs b/M6502/M6502.Symbols/File.cs
index f155fad..eb05382 100644
--- a/M6502/M6502.Symbols/File.cs
+++ b/M6502/M6502.Symbols/File.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
// file id=0,name="sudoku.s",size=9141,mtime=0x6027C7F0,mod=0
[Section("file", "Files")]
@@ -11,6 +11,6 @@
public DateTime ModificationTime { get; private set; }
[SectionReference("mod")]
- public Symbols.Module? Module { get; private set; }
+ public Module? Module { get; private set; }
}
}
\ No newline at end of file
diff --git a/M6502/M6502.Symbols/IdentifiableSection.cs b/M6502/M6502.Symbols/IdentifiableSection.cs
index cde3e65..7314b1d 100644
--- a/M6502/M6502.Symbols/IdentifiableSection.cs
+++ b/M6502/M6502.Symbols/IdentifiableSection.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
using System;
using System.Collections;
diff --git a/M6502/M6502.Symbols/Information.cs b/M6502/M6502.Symbols/Information.cs
index 298e06e..05390bf 100644
--- a/M6502/M6502.Symbols/Information.cs
+++ b/M6502/M6502.Symbols/Information.cs
@@ -1,37 +1,37 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
//info csym = 0, file = 3, lib = 0, line = 380, mod = 1, scope = 12, seg = 8, span = 356, sym = 61, type = 3
- [Section("info")]
+ [M6502.Symbols.Section("info")]
public sealed class Information(Parser container) : Section(container)
{
- [SectionProperty("csym")]
+ [M6502.Symbols.SectionProperty("csym")]
public int CSymbol { get; private set; }
- [SectionProperty("file")]
+ [M6502.Symbols.SectionProperty("file")]
public int File { get; private set; }
- [SectionProperty("lib")]
+ [M6502.Symbols.SectionProperty("lib")]
public int Library { get; private set; }
- [SectionProperty("line")]
+ [M6502.Symbols.SectionProperty("line")]
public int Line { get; private set; }
- [SectionProperty("mod")]
+ [M6502.Symbols.SectionProperty("mod")]
public int Module { get; private set; }
- [SectionProperty("scope")]
+ [M6502.Symbols.SectionProperty("scope")]
public int Scope { get; private set; }
- [SectionProperty("seg")]
+ [M6502.Symbols.SectionProperty("seg")]
public int Segment { get; private set; }
- [SectionProperty("span")]
+ [M6502.Symbols.SectionProperty("span")]
public int Span { get; private set; }
- [SectionProperty("sym")]
+ [M6502.Symbols.SectionProperty("sym")]
public int Symbol { get; private set; }
- [SectionProperty("type")]
+ [M6502.Symbols.SectionProperty("type")]
public int Type { get; private set; }
public int Count(string key) => this.GetValueT(key);
diff --git a/M6502/M6502.Symbols/Line.cs b/M6502/M6502.Symbols/Line.cs
index f8488d9..ffafad8 100644
--- a/M6502/M6502.Symbols/Line.cs
+++ b/M6502/M6502.Symbols/Line.cs
@@ -1,11 +1,13 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
+ using M6502.Symbols;
+
// line id = 268, file = 1, line = 60, type = 2, count = 1, span = 286 + 195
[Section("line", "Lines")]
public sealed class Line(Parser container) : IdentifiableSection(container)
{
[SectionReference("file")]
- public Symbols.File? File { get; private set; }
+ public File? File { get; private set; }
[SectionProperty("line")]
public int LineNumber { get; private set; }
diff --git a/M6502/M6502.Symbols/Module.cs b/M6502/M6502.Symbols/Module.cs
index 1a7d58f..488695a 100644
--- a/M6502/M6502.Symbols/Module.cs
+++ b/M6502/M6502.Symbols/Module.cs
@@ -1,10 +1,10 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
// mod id=0,name="sudoku.o",file=0
[Section("mod", "Modules")]
public sealed class Module(Parser container) : NamedSection(container)
{
[SectionReference("file")]
- public Symbols.File? File { get; private set; }
+ public File? File { get; private set; }
}
}
\ No newline at end of file
diff --git a/M6502/M6502.Symbols/NamedSection.cs b/M6502/M6502.Symbols/NamedSection.cs
index f5e9b2f..338f19c 100644
--- a/M6502/M6502.Symbols/NamedSection.cs
+++ b/M6502/M6502.Symbols/NamedSection.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
public class NamedSection : IdentifiableSection
{
diff --git a/M6502/M6502.Symbols/Parser.cs b/M6502/M6502.Symbols/Parser.cs
index fe1923b..a3bc620 100644
--- a/M6502/M6502.Symbols/Parser.cs
+++ b/M6502/M6502.Symbols/Parser.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
using System;
using System.Diagnostics;
diff --git a/M6502/M6502.Symbols/ReflectedSectionProperties.cs b/M6502/M6502.Symbols/ReflectedSectionProperties.cs
index 95bfb31..5173ee6 100644
--- a/M6502/M6502.Symbols/ReflectedSectionProperties.cs
+++ b/M6502/M6502.Symbols/ReflectedSectionProperties.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
using System.Diagnostics;
using System.Reflection;
diff --git a/M6502/M6502.Symbols/Scope.cs b/M6502/M6502.Symbols/Scope.cs
index 24c5160..609d250 100644
--- a/M6502/M6502.Symbols/Scope.cs
+++ b/M6502/M6502.Symbols/Scope.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
//scope id = 0, name = "", mod = 0, size = 1137, span = 355 + 354
//scope id = 1, name = "stack", mod = 0, type = scope, size = 7, parent = 0, span = 15
@@ -7,7 +7,7 @@
public sealed class Scope(Parser container) : NamedSection(container)
{
[SectionReference("mod")]
- public Symbols.Module? Module { get; private set; }
+ public Module? Module { get; private set; }
[SectionProperty("type")]
public string? Type { get; private set; }
diff --git a/M6502/M6502.Symbols/Section.cs b/M6502/M6502.Symbols/Section.cs
index 452f2e6..6621b3f 100644
--- a/M6502/M6502.Symbols/Section.cs
+++ b/M6502/M6502.Symbols/Section.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
using System;
using System.Diagnostics;
diff --git a/M6502/M6502.Symbols/SectionAttribute.cs b/M6502/M6502.Symbols/SectionAttribute.cs
index 9c790c9..5877e8c 100644
--- a/M6502/M6502.Symbols/SectionAttribute.cs
+++ b/M6502/M6502.Symbols/SectionAttribute.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
[AttributeUsage(AttributeTargets.Class)]
internal class SectionAttribute(string key, string? referencing = null) : Attribute
diff --git a/M6502/M6502.Symbols/SectionEnumerationAttribute.cs b/M6502/M6502.Symbols/SectionEnumerationAttribute.cs
index 18fd2ba..76d9f40 100644
--- a/M6502/M6502.Symbols/SectionEnumerationAttribute.cs
+++ b/M6502/M6502.Symbols/SectionEnumerationAttribute.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal sealed class SectionEnumerationAttribute(string key) : SectionPropertyAttribute(key, enumeration: true)
diff --git a/M6502/M6502.Symbols/SectionPropertyAttribute.cs b/M6502/M6502.Symbols/SectionPropertyAttribute.cs
index b8a0053..355661b 100644
--- a/M6502/M6502.Symbols/SectionPropertyAttribute.cs
+++ b/M6502/M6502.Symbols/SectionPropertyAttribute.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal class SectionPropertyAttribute(string key, System.Type? type = null, bool enumeration = false, bool hexadecimal = false, bool optional = false, bool many = false) : Attribute
diff --git a/M6502/M6502.Symbols/SectionReferenceAttribute.cs b/M6502/M6502.Symbols/SectionReferenceAttribute.cs
index 8d52ed2..1816ddd 100644
--- a/M6502/M6502.Symbols/SectionReferenceAttribute.cs
+++ b/M6502/M6502.Symbols/SectionReferenceAttribute.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal sealed class SectionReferenceAttribute(string key, bool optional = false) : SectionPropertyAttribute(key, type: typeof(int), optional: optional)
diff --git a/M6502/M6502.Symbols/SectionReferencesAttribute.cs b/M6502/M6502.Symbols/SectionReferencesAttribute.cs
index b0c682c..b0c8e53 100644
--- a/M6502/M6502.Symbols/SectionReferencesAttribute.cs
+++ b/M6502/M6502.Symbols/SectionReferencesAttribute.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
[AttributeUsage(AttributeTargets.Property)]
internal sealed class SectionReferencesAttribute(string key) : SectionPropertyAttribute(key, many: true)
diff --git a/M6502/M6502.Symbols/Segment.cs b/M6502/M6502.Symbols/Segment.cs
index 2a9252e..beba2d8 100644
--- a/M6502/M6502.Symbols/Segment.cs
+++ b/M6502/M6502.Symbols/Segment.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
// seg id=1,name="RODATA",start=0x00F471,size=0x0000,addrsize=absolute,type=ro,oname="sudoku.65b",ooffs=1137
[Section("seg", "Segments")]
diff --git a/M6502/M6502.Symbols/Span.cs b/M6502/M6502.Symbols/Span.cs
index 6f27a92..6ecb334 100644
--- a/M6502/M6502.Symbols/Span.cs
+++ b/M6502/M6502.Symbols/Span.cs
@@ -1,11 +1,11 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
//span id = 351, seg = 7, start = 0, size = 2, type = 2
[Section("span", "Spans")]
public sealed class Span(Parser container) : IdentifiableSection(container)
{
[SectionReference("seg")]
- public Symbols.Segment? Segment { get; private set; }
+ public Segment? Segment { get; private set; }
[SectionProperty("start")]
public int Start { get; private set; }
diff --git a/M6502/M6502.Symbols/Symbol.cs b/M6502/M6502.Symbols/Symbol.cs
index fc1c58b..ae2322b 100644
--- a/M6502/M6502.Symbols/Symbol.cs
+++ b/M6502/M6502.Symbols/Symbol.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
using System.Collections.Generic;
@@ -13,7 +13,7 @@
public int? Size { get; private set; }
[SectionReference("scope")]
- public Symbols.Scope? Scope { get; private set; }
+ public Scope? Scope { get; private set; }
[SectionReferences("def")]
public List? Definitions { get; private set; }
diff --git a/M6502/M6502.Symbols/Type.cs b/M6502/M6502.Symbols/Type.cs
index f07090d..5dd00e4 100644
--- a/M6502/M6502.Symbols/Type.cs
+++ b/M6502/M6502.Symbols/Type.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
// type id = 0, val = "800920"
[Section("type", "Types")]
diff --git a/M6502/M6502.Symbols/Version.cs b/M6502/M6502.Symbols/Version.cs
index ed1ad66..f8e4e02 100644
--- a/M6502/M6502.Symbols/Version.cs
+++ b/M6502/M6502.Symbols/Version.cs
@@ -1,4 +1,4 @@
-namespace EightBit.Files.Symbols
+namespace M6502.Symbols
{
//version major = 2, minor = 0
[Section("version")]
diff --git a/M6502/M6502.Test/Board.cs b/M6502/M6502.Test/Board.cs
index 4ff3e16..3d34e94 100644
--- a/M6502/M6502.Test/Board.cs
+++ b/M6502/M6502.Test/Board.cs
@@ -9,12 +9,13 @@ namespace M6502.Test
using System.Text;
using System.Threading.Tasks;
using EightBit;
+ using M6502;
internal class Board : Bus
{
private readonly Configuration configuration;
private readonly Ram ram = new(0x10000);
- private readonly EightBit.Files.Symbols.Parser symbols = new();
+ private readonly Symbols.Parser symbols = new();
private readonly Disassembler disassembler;
private readonly Profiler profiler;
private readonly MemoryMapping mapping;
@@ -37,7 +38,7 @@ namespace M6502.Test
this.profiler = new(this.CPU, this.disassembler, this.symbols, this.configuration.Profile);
}
- public M6502 CPU { get; }
+ public MOS6502 CPU { get; }
public override void RaisePOWER()
{
diff --git a/M6502/M6502.cs b/M6502/M6502.cs
deleted file mode 100644
index 64fd372..0000000
--- a/M6502/M6502.cs
+++ /dev/null
@@ -1,345 +0,0 @@
-//
-// Copyright (c) Adrian Conlon. All rights reserved.
-//
-
-namespace EightBit
-{
- public class M6502(Bus bus) : M6502Core(bus)
- {
- #region Core instruction dispatching
-
- protected override bool MaybeExecute()
- {
- if (base.MaybeExecute())
- {
- return true;
- }
-
- var cycles = this.Cycles;
- switch (this.OpCode)
- {
- case 0x02: this.Jam(); break; // *JAM
- case 0x03: this.IndexedIndirectXRead(); this.SLO(); break; // *SLO (indexed indirect X)
- case 0x04: this.ZeroPageRead(); break; // *NOP (zero page)
- case 0x07: this.ZeroPageRead(); this.SLO(); break; // *SLO (zero page)
- case 0x0b: this.ImmediateRead(); this.ANC(); break; // *ANC (immediate)
- case 0x0c: this.AbsoluteRead(); break; // *NOP (absolute)
- case 0x0f: this.AbsoluteRead(); this.SLO(); break; // *SLO (absolute)
-
- case 0x12: this.Jam(); break; // *JAM
- case 0x13: this.IndirectIndexedYAddress(); this.FixupRead(); this.SLO(); break; // *SLO (indirect indexed Y)
- case 0x14: this.ZeroPageXRead(); break; // *NOP (zero page, X)
- case 0x17: this.ZeroPageXRead(); this.SLO(); break; // *SLO (zero page, X)
- case 0x1a: this.SwallowRead(); break; // *NOP (implied)
- case 0x1b: this.AbsoluteYAddress(); this.FixupRead(); this.SLO(); break; // *SLO (absolute, Y)
- case 0x1c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
- case 0x1f: this.AbsoluteXAddress(); this.FixupRead(); this.SLO(); break; // *SLO (absolute, X)
-
- case 0x22: this.Jam(); break; // *JAM
- case 0x23: this.IndexedIndirectXRead(); this.RLA(); ; break; // *RLA (indexed indirect X)
- case 0x27: this.ZeroPageRead(); this.RLA(); ; break; // *RLA (zero page)
- case 0x2b: this.ImmediateRead(); this.ANC(); break; // *ANC (immediate)
- case 0x2f: this.AbsoluteRead(); this.RLA(); break; // *RLA (absolute)
-
- case 0x32: this.Jam(); break; // *JAM
- case 0x33: this.IndirectIndexedYAddress(); this.FixupRead(); this.RLA(); break; // *RLA (indirect indexed Y)
- case 0x34: this.ZeroPageXRead(); break; // *NOP (zero page, X)
- case 0x37: this.ZeroPageXRead(); this.RLA(); ; break; // *RLA (zero page, X)
- case 0x3a: this.SwallowRead(); break; // *NOP (implied)
- case 0x3b: this.AbsoluteYAddress(); this.FixupRead(); this.RLA(); break; // *RLA (absolute, Y)
- case 0x3c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
- case 0x3f: this.AbsoluteXAddress(); this.FixupRead(); this.RLA(); break; // *RLA (absolute, X)
-
- case 0x42: this.Jam(); break; // *JAM
- case 0x43: this.IndexedIndirectXRead(); this.SRE(); break; // *SRE (indexed indirect X)
- case 0x47: this.ZeroPageRead(); this.SRE(); break; // *SRE (zero page)
- case 0x4b: this.ImmediateRead(); this.ASR(); break; // *ASR (immediate)
- case 0x4f: this.AbsoluteRead(); this.SRE(); break; // *SRE (absolute)
-
- case 0x52: this.Jam(); break; // *JAM
- case 0x53: this.IndirectIndexedYAddress(); this.FixupRead(); this.SRE(); break; // *SRE (indirect indexed Y)
- case 0x57: this.ZeroPageXRead(); this.SRE(); break; // *SRE (zero page, X)
- case 0x5a: this.SwallowRead(); break; // *NOP (implied)
- case 0x5b: this.AbsoluteYAddress(); this.FixupRead(); this.SRE(); break; // *SRE (absolute, Y)
- case 0x5c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
- case 0x5f: this.AbsoluteXAddress(); this.FixupRead(); this.SRE(); break; // *SRE (absolute, X)
-
- case 0x62: this.Jam(); break; // *JAM
- case 0x63: this.IndexedIndirectXRead(); this.RRA(); break; // *RRA (indexed indirect X)
- case 0x64: this.ZeroPageRead(); break; // *NOP (zero page)
- case 0x67: this.ZeroPageRead(); this.RRA(); break; // *RRA (zero page)
- case 0x6b: this.ImmediateRead(); this.ARR(); break; // *ARR (immediate)
- case 0x6f: this.AbsoluteRead(); this.RRA(); break; // *RRA (absolute)
-
- case 0x72: this.Jam(); break; // *JAM
- case 0x73: this.IndirectIndexedYAddress(); this.FixupRead(); this.RRA(); break; // *RRA (indirect indexed Y)
- case 0x74: this.ZeroPageXRead(); break; // *NOP (zero page, X)
- case 0x77: this.ZeroPageXRead(); this.RRA(); break; // *RRA (zero page, X)
- case 0x7a: this.SwallowRead(); break; // *NOP (implied)
- case 0x7b: this.AbsoluteYAddress(); this.FixupRead(); this.RRA(); break; // *RRA (absolute, Y)
- case 0x7c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
- case 0x7f: this.AbsoluteXAddress(); this.FixupRead(); this.RRA(); break; // *RRA (absolute, X)
-
- case 0x80: this.ImmediateRead(); break; // *NOP (immediate)
- case 0x83: this.IndexedIndirectXAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (indexed indirect X)
- case 0x87: this.ZeroPageAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (zero page)
- case 0x89: this.ImmediateRead(); break; // *NOP (immediate)
- case 0x8b: this.ImmediateRead(); this.ANE(); break; // *ANE (immediate)
- case 0x8f: this.AbsoluteAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (absolute)
-
- case 0x92: this.Jam(); break; // *JAM
- case 0x93: this.IndirectIndexedYAddress(); this.Fixup(); this.SHA(); break; // *SHA (indirect indexed, Y)
- case 0x97: this.ZeroPageYAddress(); this.MemoryWrite((byte)(this.A & this.X)); break; // *SAX (zero page, Y)
- case 0x9b: this.AbsoluteYAddress(); this.Fixup(); this.TAS(); break; // *TAS (absolute, Y)
- case 0x9c: this.AbsoluteXAddress(); this.Fixup(); this.SYA(); break; // *SYA (absolute, X)
- case 0x9e: this.AbsoluteYAddress(); this.Fixup(); this.SXA(); break; // *SXA (absolute, Y)
- case 0x9f: this.AbsoluteYAddress(); this.Fixup(); this.SHA(); break; // *SHA (absolute, Y)
-
- case 0xa3: this.IndexedIndirectXRead(); this.A = this.X = this.Through(); break; // *LAX (indexed indirect X)
- case 0xa7: this.ZeroPageRead(); this.A = this.X = this.Through(); break; // *LAX (zero page)
- case 0xab: this.ImmediateRead(); this.ATX(); break; // *ATX (immediate)
- case 0xaf: this.AbsoluteRead(); this.A = this.X = this.Through(); break; // *LAX (absolute)
-
- case 0xb2: this.Jam(); break; // *JAM
- case 0xb3: this.IndirectIndexedYRead(); this.A = this.X = this.Through(); break; // *LAX (indirect indexed Y)
- case 0xb7: this.ZeroPageYRead(); this.A = this.X = this.Through(); break; // *LAX (zero page, Y)
- case 0xbb: this.AbsoluteYAddress(); this.MaybeFixup(); this.LAS(); break; // *LAS (absolute, Y)
- case 0xbf: this.AbsoluteYRead(); this.A = this.X = this.Through(); break; // *LAX (absolute, Y)
-
- case 0xc3: this.IndexedIndirectXRead(); this.DCP(); break; // *DCP (indexed indirect X)
- case 0xc7: this.ZeroPageRead(); this.DCP(); break; // *DCP (zero page)
- case 0xcb: this.ImmediateRead(); this.AXS(); break; // *AXS (immediate)
- case 0xcf: this.AbsoluteRead(); this.DCP(); break; // *DCP (absolute)
-
- case 0xd2: this.Jam(); break; // *JAM
- case 0xd3: this.IndirectIndexedYAddress(); this.FixupRead(); this.DCP(); break; // *DCP (indirect indexed Y)
- case 0xd7: this.ZeroPageXRead(); this.DCP(); break; // *DCP (zero page, X)
- case 0xda: this.SwallowRead(); break; // *NOP (implied)
- case 0xdb: this.AbsoluteYAddress(); this.FixupRead(); this.DCP(); break; // *DCP (absolute, Y)
- case 0xdc: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
- case 0xdf: this.AbsoluteXAddress(); this.FixupRead(); this.DCP(); break; // *DCP (absolute, X)
-
- case 0xe3: this.IndexedIndirectXRead(); this.ISB(); break; // *ISB (indexed indirect X)
- case 0xe7: this.ZeroPageRead(); this.ISB(); break; // *ISB (zero page)
- case 0xeb: this.ImmediateRead(); this.SBC(); break; // *SBC (immediate)
- case 0xef: this.AbsoluteRead(); this.ISB(); break; // *ISB (absolute)
-
- case 0xf2: this.Jam(); break; // *JAM
- case 0xf3: this.IndirectIndexedYAddress(); this.FixupRead(); this.ISB(); break; // *ISB (indirect indexed Y)
- case 0xf7: this.ZeroPageXRead(); this.ISB(); break; // *ISB (zero page, X)
- case 0xfa: this.SwallowRead(); break; // *NOP (implied)
- case 0xfb: this.AbsoluteYAddress(); this.FixupRead(); this.ISB(); break; // *ISB (absolute, Y)
- case 0xfc: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X)
- case 0xff: this.AbsoluteXAddress(); this.FixupRead(); this.ISB(); break; // *ISB (absolute, X)
- }
-
- return cycles != this.Cycles;
- }
-
- #endregion
-
- #region Bus/Memory Access
-
- protected override void ModifyWrite(byte data)
- {
- // The read will have already taken place...
- this.MemoryWrite(); // Modify cycle
- this.MemoryWrite(data); // Write cycle
- }
-
- #endregion
-
- #region Addressing modes
-
- protected override void IndirectAddress()
- {
- this.AbsoluteAddress();
- this.GetAddressPaged();
- }
-
- #region Address page fixup
-
- protected override void Fixup()
- {
- this.MemoryRead();
- this.Bus.Address.High = this.FixedPage;
- }
-
- protected override void FixupBranch(sbyte relative)
- {
- this.NoteFixedAddress(this.PC.Word + relative);
- this.MaybeFixup();
- }
-
- #endregion
-
- #endregion
-
- #region Instruction implementations
-
- #region Undocumented instructions
-
- #region Undocumented instructions with BCD effects
-
- private void ARR()
- {
- var value = this.Bus.Data;
- if (this.DecimalMasked != 0)
- this.ARR_d(value);
- else
- this.ARR_b(value);
- }
-
- private void ARR_d(byte value)
- {
- // With thanks to https://github.com/TomHarte/CLK
- // What a very strange instruction ARR is...
-
- this.A &= value;
- var unshiftedA = this.A;
- this.A = this.Through((this.A >> 1) | (this.Carry << 7));
- this.SetFlag(StatusBits.VF, OverflowTest((byte)(this.A ^ (this.A << 1))));
-
- if (LowerNibble(unshiftedA) + (unshiftedA & 0x1) > 5)
- this.A = (byte)(LowerNibble((byte)(this.A + 6)) | HigherNibble(this.A));
-
- this.SetFlag(StatusBits.CF, HigherNibble(unshiftedA) + (unshiftedA & 0x10) > 0x50);
-
- if (this.Carry != 0)
- this.A += 0x60;
- }
-
- private void ARR_b(byte value)
- {
- this.A &= value;
- this.A = this.Through((this.A >> 1) | (this.Carry << 7));
- this.SetFlag(StatusBits.CF, OverflowTest(this.A));
- this.SetFlag(StatusBits.VF, OverflowTest((byte)(this.A ^ (this.A << 1))));
- }
-
- #endregion
-
- #region Undocumented instructions with fixup effects
-
- private void StoreFixupEffect(byte data)
- {
- ////var fixedAddress = (byte)(this.Bus.Address.High + 1);
- //var fixedAddress = this.FixedPage + 1;
- //var updated = (byte)(data & fixedAddress);
- //if (this.Fixed)
- //{
- // this.Bus.Address.High = updated;
- //}
-
- //this.MemoryWrite(updated);
-
- byte updated;
- if (this.Fixed)
- {
- updated = (byte)(data & this.FixedPage);
- this.Bus.Address.High = updated;
- }
- else
- {
- updated = (byte)(data & this.UnfixedPage);
- this.Bus.Address.High = updated;
- }
- this.MemoryWrite(updated);
- }
-
- private void SHA() => this.StoreFixupEffect((byte)(this.A & this.X));
-
- private void SYA() => this.StoreFixupEffect(this.Y);
-
- private void SXA() => this.StoreFixupEffect(this.X);
-
- #endregion
-
- private void ANC()
- {
- this.AndR();
- this.SetFlag(StatusBits.CF, NegativeTest(this.A));
- }
-
- private void AXS()
- {
- this.X = this.Through(this.BinarySUB((byte)(this.A & this.X)));
- this.ResetFlag(StatusBits.CF, this.Intermediate.High);
- }
-
- private void Jam()
- {
- this.Bus.Address.Assign(this.PC);
- this.MemoryRead();
- this.MemoryRead(0xff, 0xff);
- this.Bus.Address.Low = 0xfe;
- this.MemoryRead();
- this.MemoryRead();
- this.Bus.Address.Low = 0xff;
- this.MemoryRead();
- this.MemoryRead();
- this.MemoryRead();
- this.MemoryRead();
- this.MemoryRead();
- this.MemoryRead();
- }
-
- private void TAS()
- {
- this.S = (byte)(this.A & this.X);
- this.SHA();
- }
-
- private void LAS() => this.A = this.X = this.S = this.Through(this.MemoryRead() & this.S);
-
- private void ANE() => this.A = this.Through((this.A | 0xee) & this.X & this.Bus.Data);
-
- private void ATX() => this.A = this.X = this.Through((this.A | 0xee) & this.Bus.Data);
-
- private void ASR()
- {
- this.AndR();
- this.A = this.LSR(this.A);
- }
-
- private void ISB()
- {
- this.ModifyWrite(this.INC());
- this.SBC();
- }
-
- private void RLA()
- {
- this.ModifyWrite(this.ROL());
- this.AndR();
- }
-
- private void RRA()
- {
- this.ModifyWrite(this.ROR());
- this.ADC();
- }
-
- private void SLO()
- {
- this.ModifyWrite(this.ASL());
- this.OrR();
- }
-
- private void SRE()
- {
- this.ModifyWrite(this.LSR());
- this.EorR();
- }
-
- private void DCP()
- {
- this.ModifyWrite(this.DEC());
- this.CMP(this.A);
- }
-
- #endregion
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/M6502/M6502Core.cs b/M6502/M6502Core.cs
deleted file mode 100644
index 5acfc5c..0000000
--- a/M6502/M6502Core.cs
+++ /dev/null
@@ -1,1108 +0,0 @@
-//
-// Copyright (c) Adrian Conlon. All rights reserved.
-//
-
-namespace EightBit
-{
- public abstract class M6502Core(Bus bus) : LittleEndianProcessor(bus)
- {
- #region Pin controls
-
- #region NMI pin
-
- public ref PinLevel NMI => ref this.nmiLine;
- private PinLevel nmiLine = PinLevel.Low;
- public event EventHandler? RaisingNMI;
- public event EventHandler? RaisedNMI;
- public event EventHandler? LoweringNMI;
- public event EventHandler? LoweredNMI;
- protected virtual void OnRaisingNMI() => this.RaisingNMI?.Invoke(this, EventArgs.Empty);
- protected virtual void OnRaisedNMI() => this.RaisedNMI?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweringNMI() => this.LoweringNMI?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweredNMI() => this.LoweredNMI?.Invoke(this, EventArgs.Empty);
-
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
- public virtual void RaiseNMI()
- {
- if (this.NMI.Lowered())
- {
- this.OnRaisingNMI();
- this.NMI.Raise();
- this.OnRaisedNMI();
- }
- }
-
- public virtual void LowerNMI()
- {
- if (this.NMI.Raised())
- {
- this.OnLoweringNMI();
- this.NMI.Lower();
- this.OnLoweredNMI();
- }
- }
-
- #endregion
-
- #region SO pin
-
- public ref PinLevel SO => ref this.soLine;
- private PinLevel soLine = PinLevel.Low;
- public event EventHandler? RaisingSO;
- public event EventHandler? RaisedSO;
- public event EventHandler? LoweringSO;
- public event EventHandler? LoweredSO;
-
- protected virtual void OnRaisingSO() => this.RaisingSO?.Invoke(this, EventArgs.Empty);
- protected virtual void OnRaisedSO() => this.RaisedSO?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweringSO() => this.LoweringSO?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweredSO() => this.LoweredSO?.Invoke(this, EventArgs.Empty);
-
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
- public virtual void RaiseSO()
- {
- if (this.SO.Lowered())
- {
- this.OnRaisingSO();
- this.SO.Raise();
- this.OnRaisedSO();
- }
- }
-
- public virtual void LowerSO()
- {
- if (this.SO.Raised())
- {
- this.OnLoweringSO();
- this.SO.Lower();
- this.OnLoweredSO();
- }
- }
-
- #endregion
-
- #region SYNC pin
-
- public ref PinLevel SYNC => ref this.syncLine;
- private PinLevel syncLine = PinLevel.Low;
- public event EventHandler? RaisingSYNC;
- public event EventHandler? RaisedSYNC;
- public event EventHandler? LoweringSYNC;
- public event EventHandler? LoweredSYNC;
-
- protected virtual void OnRaisingSYNC() => this.RaisingSYNC?.Invoke(this, EventArgs.Empty);
- protected virtual void OnRaisedSYNC() => this.RaisedSYNC?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweringSYNC() => this.LoweringSYNC?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweredSYNC() => this.LoweredSYNC?.Invoke(this, EventArgs.Empty);
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
- protected virtual void RaiseSYNC()
- {
- this.OnRaisingSYNC();
- this.SYNC.Raise();
- this.OnRaisedSYNC();
- }
-
- protected virtual void LowerSYNC()
- {
- this.OnLoweringSYNC();
- this.SYNC.Lower();
- this.OnLoweredSYNC();
- }
-
- #endregion
-
- #region RDY pin
-
- public ref PinLevel RDY => ref this.rdyLine;
- private PinLevel rdyLine = PinLevel.Low;
- public event EventHandler? RaisingRDY;
- public event EventHandler? RaisedRDY;
- public event EventHandler? LoweringRDY;
- public event EventHandler? LoweredRDY;
- protected virtual void OnRaisingRDY() => this.RaisingRDY?.Invoke(this, EventArgs.Empty);
- protected virtual void OnRaisedRDY() => this.RaisedRDY?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweringRDY() => this.LoweringRDY?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweredRDY() => this.LoweredRDY?.Invoke(this, EventArgs.Empty);
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
- public virtual void RaiseRDY()
- {
- if (this.RDY.Lowered())
- {
- this.OnRaisingRDY();
- this.RDY.Raise();
- this.OnRaisedRDY();
- }
- }
-
- public virtual void LowerRDY()
- {
- if (this.RDY.Raised())
- {
- this.OnLoweringRDY();
- this.RDY.Lower();
- this.OnLoweredRDY();
- }
- }
-
- #endregion
-
- #region RW pin
-
- public ref PinLevel RW => ref this.rwLine;
- private PinLevel rwLine = PinLevel.Low;
- public event EventHandler? RaisingRW;
- public event EventHandler? RaisedRW;
- public event EventHandler? LoweringRW;
- public event EventHandler? LoweredRW;
- protected virtual void OnRaisingRW() => this.RaisingRW?.Invoke(this, EventArgs.Empty);
- protected virtual void OnRaisedRW() => this.RaisedRW?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweringRW() => this.LoweringRW?.Invoke(this, EventArgs.Empty);
- protected virtual void OnLoweredRW() => this.LoweredRW?.Invoke(this, EventArgs.Empty);
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
- public virtual void RaiseRW()
- {
- if (this.RW.Lowered())
- {
- this.OnRaisingRW();
- this.RW.Raise();
- this.OnRaisedRW();
- }
- }
-
- public virtual void LowerRW()
- {
- if (this.RW.Raised())
- {
- this.OnLoweringRW();
- this.RW.Lower();
- this.OnLoweredRW();
- }
- }
-
- #endregion
-
- protected override void OnRaisedPOWER()
- {
- this.X = (byte)Bits.Bit7;
- this.Y = 0;
- this.A = 0;
- this.P = (byte)StatusBits.RF;
- this.S = (byte)Mask.Eight;
- this.LowerSYNC();
- this.LowerRW();
- base.OnRaisedPOWER();
- }
-
- #endregion
-
- #region Interrupts
-
- private const byte IRQvector = 0xfe; // IRQ vector
- private const byte RSTvector = 0xfc; // RST vector
- private const byte NMIvector = 0xfa; // NMI vector
-
- protected enum InterruptSource { hardware, software };
-
- protected enum InterruptType { reset, non_reset };
-
- protected virtual void Interrupt(byte vector, InterruptSource source = InterruptSource.hardware, InterruptType type = InterruptType.non_reset)
- {
- if (type == InterruptType.reset)
- {
- this.DummyPush();
- this.DummyPush();
- this.DummyPush();
- }
- else
- {
- this.PushWord(this.PC);
- this.Push((byte)(this.P | (source == InterruptSource.hardware ? 0 : (byte)StatusBits.BF)));
- }
- this.SetFlag(StatusBits.IF); // Disable IRQ
- this.Jump(this.GetWordPaged(0xff, vector));
- }
-
- #region Interrupt etc. handlers
-
- protected override sealed void HandleRESET()
- {
- this.RaiseRESET();
- this.Interrupt(RSTvector, InterruptSource.hardware, InterruptType.reset);
- }
-
- protected override sealed void HandleINT()
- {
- this.RaiseINT();
- this.Interrupt(IRQvector);
- }
-
- private void HandleNMI()
- {
- this.RaiseNMI();
- this.Interrupt(NMIvector);
- }
-
- private void HandleSO()
- {
- this.RaiseSO();
- this.SetFlag(StatusBits.VF);
- }
-
- #endregion
-
- #endregion
-
- #region Registers
-
- public byte X { get; set; }
- public byte Y { get; set; }
- public byte A { get; set; }
- public byte S { get; set; }
- public byte P { get; set; }
-
- #endregion
-
- #region Processor state helpers
-
- protected int InterruptMasked => this.P & (byte)StatusBits.IF;
- protected int DecimalMasked => this.P & (byte)StatusBits.DF;
- protected int Negative => NegativeTest(this.P);
- protected int Zero => ZeroTest(this.P);
- protected int Overflow => OverflowTest(this.P);
- protected int Carry => CarryTest(this.P);
-
- protected static int NegativeTest(byte data) => data & (byte)StatusBits.NF;
- protected static int ZeroTest(byte data) => data & (byte)StatusBits.ZF;
- protected static int OverflowTest(byte data) => data & (byte)StatusBits.VF;
- protected static int CarryTest(byte data) => data & (byte)StatusBits.CF;
-
- #endregion
-
- #region Bit/state twiddling
-
- #region Bit twiddling
-
- private static byte SetBit(byte f, StatusBits flag) => SetBit(f, (byte)flag);
-
- private static byte SetBit(byte f, StatusBits flag, int condition) => SetBit(f, (byte)flag, condition);
-
- private static byte SetBit(byte f, StatusBits flag, bool condition) => SetBit(f, (byte)flag, condition);
-
- private static byte ClearBit(byte f, StatusBits flag) => ClearBit(f, (byte)flag);
-
- private static byte ClearBit(byte f, StatusBits flag, int condition) => ClearBit(f, (byte)flag, condition);
-
- #endregion
-
- #region State flag twiddling
-
- protected void SetFlag(StatusBits flag)
- {
- this.P = SetBit(this.P, flag);
- }
-
- protected void SetFlag(StatusBits which, int condition)
- {
- this.P = SetBit(this.P, which, condition);
- }
-
- protected void SetFlag(StatusBits which, bool condition)
- {
- this.P = SetBit(this.P, which, condition);
- }
-
- protected void ResetFlag(StatusBits which)
- {
- this.P = ClearBit(this.P, which);
- }
-
- protected void ResetFlag(StatusBits which, int condition)
- {
- this.P = ClearBit(this.P, which, condition);
- }
-
- #endregion
-
- #endregion
-
- #region Cycle wastage
-
- protected void SwallowRead() => this.MemoryRead(this.PC);
-
- protected void SwallowPop() => this.MemoryRead(this.S, 1);
-
- protected void SwallowFetch() => this.FetchByte();
-
- #endregion
-
- #region Core instruction dispatching
-
- public override void Execute()
- {
- this.MaybeExecute();
- }
-
- protected virtual bool MaybeExecute()
- {
- var cycles = this.Cycles;
- switch (this.OpCode)
- {
- case 0x00: this.SwallowFetch(); this.Interrupt(IRQvector, InterruptSource.software); break; // BRK (implied)
- case 0x01: this.IndexedIndirectXRead(); this.OrR(); break; // ORA (indexed indirect X)
- case 0x05: this.ZeroPageRead(); this.OrR(); break; // ORA (zero page)
- case 0x06: this.ZeroPageRead(); this.ModifyWrite(this.ASL()); break; // ASL (zero page)
- case 0x08: this.SwallowRead(); this.PHP(); break; // PHP (implied)
- case 0x09: this.ImmediateRead(); this.OrR(); break; // ORA (immediate)
- case 0x0a: this.SwallowRead(); A = this.ASL(A); break; // ASL A (implied)
- case 0x0d: this.AbsoluteRead(); this.OrR(); break; // ORA (absolute)
- case 0x0e: this.AbsoluteRead(); this.ModifyWrite(this.ASL()); break; // ASL (absolute)
-
- case 0x10: this.BranchNot(this.Negative); break; // BPL (relative)
- case 0x11: this.IndirectIndexedYRead(); this.OrR(); break; // ORA (indirect indexed Y)
- case 0x15: this.ZeroPageXRead(); this.OrR(); break; // ORA (zero page, X)
- case 0x16: this.ZeroPageXRead(); this.ModifyWrite(this.ASL()); break; // ASL (zero page, X)
- case 0x18: this.SwallowRead(); this.ResetFlag(StatusBits.CF); break; // CLC (implied)
- case 0x19: this.AbsoluteYRead(); this.OrR(); break; // ORA (absolute, Y)
- case 0x1d: this.AbsoluteXRead(); this.OrR(); break; // ORA (absolute, X)
- case 0x1e: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.ASL()); break; // ASL (absolute, X)
-
- case 0x20: this.JSR(); break; // JSR (absolute)
- case 0x21: this.IndexedIndirectXRead(); this.AndR(); break; // AND (indexed indirect X)
- case 0x24: this.ZeroPageRead(); this.BIT(); break; // BIT (zero page)
- case 0x25: this.ZeroPageRead(); this.AndR(); break; // AND (zero page)
- case 0x26: this.ZeroPageRead(); this.ModifyWrite(this.ROL()); break; // ROL (zero page)
- case 0x28: this.SwallowRead(); this.PLP(); break; // PLP (implied)
- case 0x29: this.ImmediateRead(); this.AndR(); break; // AND (immediate)
- case 0x2a: this.SwallowRead(); this.A = this.ROL(this.A); break; // ROL A (implied)
- case 0x2c: this.AbsoluteRead(); this.BIT(); break; // BIT (absolute)
- case 0x2d: this.AbsoluteRead(); this.AndR(); break; // AND (absolute)
- case 0x2e: this.AbsoluteRead(); this.ModifyWrite(this.ROL()); break; // ROL (absolute)
-
- case 0x30: this.Branch(this.Negative); break; // BMI (relative)
- case 0x31: this.IndirectIndexedYRead(); this.AndR(); break; // AND (indirect indexed Y)
- case 0x35: this.ZeroPageXRead(); this.AndR(); break; // AND (zero page, X)
- case 0x36: this.ZeroPageXRead(); this.ModifyWrite(this.ROL()); break; // ROL (zero page, X)
- case 0x38: this.SwallowRead(); this.SetFlag(StatusBits.CF); break; // SEC (implied)
- case 0x39: this.AbsoluteYRead(); this.AndR(); break; // AND (absolute, Y)
- case 0x3d: this.AbsoluteXRead(); this.AndR(); break; // AND (absolute, X)
- case 0x3e: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.ROL()); break; // ROL (absolute, X)
-
- case 0x40: this.SwallowRead(); this.RTI(); break; // RTI (implied)
- case 0x41: this.IndexedIndirectXRead(); this.EorR(); break; // EOR (indexed indirect X)
- case 0x44: this.ZeroPageRead(); break; // *NOP (zero page)
- case 0x45: this.ZeroPageRead(); this.EorR(); break; // EOR (zero page)
- case 0x46: this.ZeroPageRead(); this.ModifyWrite(this.LSR()); break; // LSR (zero page)
- case 0x48: this.SwallowRead(); this.Push(this.A); break; // PHA (implied)
- case 0x49: this.ImmediateRead(); this.EorR(); break; // EOR (immediate)
- case 0x4a: this.SwallowRead(); this.A = this.LSR(this.A); break; // LSR A (implied)
- case 0x4c: this.AbsoluteAddress(); this.Jump(this.Bus.Address); break; // JMP (absolute)
- case 0x4d: this.AbsoluteRead(); this.EorR(); break; // EOR (absolute)
- case 0x4e: this.AbsoluteRead(); this.ModifyWrite(this.LSR()); break; // LSR (absolute)
-
- case 0x50: this.BranchNot(this.Overflow); break; // BVC (relative)
- case 0x51: this.IndirectIndexedYRead(); this.EorR(); break; // EOR (indirect indexed Y)
- case 0x54: this.ZeroPageXRead(); break; // *NOP (zero page, X)
- case 0x55: this.ZeroPageXRead(); this.EorR(); break; // EOR (zero page, X)
- case 0x56: this.ZeroPageXRead(); this.ModifyWrite(this.LSR()); break; // LSR (zero page, X)
- case 0x58: this.SwallowRead(); this.ResetFlag(StatusBits.IF); break; // CLI (implied)
- case 0x59: this.AbsoluteYRead(); this.EorR(); break; // EOR (absolute, Y)
- case 0x5d: this.AbsoluteXRead(); this.EorR(); break; // EOR (absolute, X)
- case 0x5e: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.LSR()); break; // LSR (absolute, X)
-
- case 0x60: this.SwallowRead(); this.Return(); break; // RTS (implied)
- case 0x61: this.IndexedIndirectXRead(); this.ADC(); break; // ADC (indexed indirect X)
- case 0x65: this.ZeroPageRead(); this.ADC(); break; // ADC (zero page)
- case 0x66: this.ZeroPageRead(); this.ModifyWrite(this.ROR()); break; // ROR (zero page)
- case 0x68: this.SwallowRead(); this.SwallowPop(); this.A = this.Through(this.Pop()); break; // PLA (implied)
- case 0x69: this.ImmediateRead(); this.ADC(); break; // ADC (immediate)
- case 0x6a: this.SwallowRead(); this.A = this.ROR(this.A); break; // ROR A (implied)
- case 0x6c: this.IndirectAddress(); this.Jump(this.Bus.Address); break; // JMP (indirect)
- case 0x6d: this.AbsoluteRead(); this.ADC(); break; // ADC (absolute)
- case 0x6e: this.AbsoluteRead(); this.ModifyWrite(this.ROR()); break; // ROR (absolute)
-
- case 0x70: this.Branch(this.Overflow); break; // BVS (relative)
- case 0x71: this.IndirectIndexedYRead(); this.ADC(); break; // ADC (indirect indexed Y)
- case 0x75: this.ZeroPageXRead(); this.ADC(); break; // ADC (zero page, X)
- case 0x76: this.ZeroPageXRead(); this.ModifyWrite(this.ROR()); break; // ROR (zero page, X)
- case 0x78: this.SwallowRead(); this.SetFlag(StatusBits.IF); break; // SEI (implied)
- case 0x79: this.AbsoluteYRead(); this.ADC(); break; // ADC (absolute, Y)
- case 0x7d: this.AbsoluteXRead(); this.ADC(); break; // ADC (absolute, X)
- case 0x7e: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.ROR()); break; // ROR (absolute, X)
-
- case 0x81: this.IndexedIndirectXAddress(); this.MemoryWrite(this.A); break; // STA (indexed indirect X)
- case 0x82: this.ImmediateRead(); break; // *NOP (immediate)
- case 0x84: this.ZeroPageAddress(); this.MemoryWrite(this.Y); break; // STY (zero page)
- case 0x85: this.ZeroPageAddress(); this.MemoryWrite(this.A); break; // STA (zero page)
- case 0x86: this.ZeroPageAddress(); this.MemoryWrite(this.X); break; // STX (zero page)
- case 0x88: this.SwallowRead(); this.Y = this.DEC(this.Y); break; // DEY (implied)
- case 0x8a: this.SwallowRead(); this.A = this.Through(this.X); break; // TXA (implied)
- case 0x8c: this.AbsoluteAddress(); this.MemoryWrite(this.Y); break; // STY (absolute)
- case 0x8d: this.AbsoluteAddress(); this.MemoryWrite(this.A); break; // STA (absolute)
- case 0x8e: this.AbsoluteAddress(); this.MemoryWrite(this.X); break; // STX (absolute)
-
- case 0x90: this.BranchNot(this.Carry); break; // BCC (relative)
- case 0x91: this.IndirectIndexedYAddress(); this.Fixup(); this.MemoryWrite(this.A); break; // STA (indirect indexed Y)
- case 0x94: this.ZeroPageXAddress(); this.MemoryWrite(this.Y); break; // STY (zero page, X)
- case 0x95: this.ZeroPageXAddress(); this.MemoryWrite(this.A); break; // STA (zero page, X)
- case 0x96: this.ZeroPageYAddress(); this.MemoryWrite(this.X); break; // STX (zero page, Y)
- case 0x98: this.SwallowRead(); this.A = this.Through(this.Y); break; // TYA (implied)
- case 0x99: this.AbsoluteYAddress(); this.Fixup(); this.MemoryWrite(this.A); break; // STA (absolute, Y)
- case 0x9a: this.SwallowRead(); this.S = this.X; break; // TXS (implied)
- case 0x9d: this.AbsoluteXAddress(); this.Fixup(); this.MemoryWrite(this.A); break; // STA (absolute, X)
-
- case 0xa0: this.ImmediateRead(); this.Y = this.Through(); break; // LDY (immediate)
- case 0xa1: this.IndexedIndirectXRead(); this.A = this.Through(); break; // LDA (indexed indirect X)
- case 0xa2: this.ImmediateRead(); this.X = this.Through(); break; // LDX (immediate)
- case 0xa4: this.ZeroPageRead(); this.Y = this.Through(); break; // LDY (zero page)
- case 0xa5: this.ZeroPageRead(); this.A = this.Through(); break; // LDA (zero page)
- case 0xa6: this.ZeroPageRead(); this.X = this.Through(); break; // LDX (zero page)
- case 0xa8: this.SwallowRead(); this.Y = Through(this.A); break; // TAY (implied)
- case 0xa9: this.ImmediateRead(); this.A = this.Through(); break; // LDA (immediate)
- case 0xaa: this.SwallowRead(); this.X = this.Through(this.A); break; // TAX (implied)
- case 0xac: this.AbsoluteRead(); this.Y = this.Through(); break; // LDY (absolute)
- case 0xad: this.AbsoluteRead(); this.A = this.Through(); break; // LDA (absolute)
- case 0xae: this.AbsoluteRead(); this.X = this.Through(); break; // LDX (absolute)
-
- case 0xb0: this.Branch(this.Carry); break; // BCS (relative)
- case 0xb1: this.IndirectIndexedYRead(); this.A = this.Through(); break; // LDA (indirect indexed Y)
- case 0xb4: this.ZeroPageXRead(); this.Y = this.Through(); break; // LDY (zero page, X)
- case 0xb5: this.ZeroPageXRead(); this.A = this.Through(); break; // LDA (zero page, X)
- case 0xb6: this.ZeroPageYRead(); this.X = this.Through(); break; // LDX (zero page, Y)
- case 0xb8: this.SwallowRead(); this.ResetFlag(StatusBits.VF); break; // CLV (implied)
- case 0xb9: this.AbsoluteYRead(); this.A = this.Through(); break; // LDA (absolute, Y)
- case 0xba: this.SwallowRead(); this.X = this.Through(this.S); break; // TSX (implied)
- case 0xbc: this.AbsoluteXRead(); this.Y = this.Through(); break; // LDY (absolute, X)
- case 0xbd: this.AbsoluteXRead(); this.A = this.Through(); break; // LDA (absolute, X)
- case 0xbe: this.AbsoluteYRead(); this.X = this.Through(); break; // LDX (absolute, Y)
-
- case 0xc0: this.ImmediateRead(); this.CMP(this.Y); break; // CPY (immediate)
- case 0xc1: this.IndexedIndirectXRead(); this.CMP(this.A); break; // CMP (indexed indirect X)
- case 0xc2: this.ImmediateRead(); break; // *NOP (immediate)
- case 0xc4: this.ZeroPageRead(); this.CMP(this.Y); break; // CPY (zero page)
- case 0xc5: this.ZeroPageRead(); this.CMP(this.A); break; // CMP (zero page)
- case 0xc6: this.ZeroPageRead(); this.ModifyWrite(this.DEC()); break; // DEC (zero page)
- case 0xc8: this.SwallowRead(); this.Y = this.INC(this.Y); break; // INY (implied)
- case 0xc9: this.ImmediateRead(); this.CMP(this.A); break; // CMP (immediate)
- case 0xca: this.SwallowRead(); this.X = this.DEC(this.X); break; // DEX (implied)
- case 0xcc: this.AbsoluteRead(); this.CMP(this.Y); break; // CPY (absolute)
- case 0xcd: this.AbsoluteRead(); this.CMP(this.A); break; // CMP (absolute)
- case 0xce: this.AbsoluteRead(); this.ModifyWrite(this.DEC()); break; // DEC (absolute)
-
- case 0xd0: this.BranchNot(this.Zero); break; // BNE (relative)
- case 0xd1: this.IndirectIndexedYRead(); this.CMP(this.A); break; // CMP (indirect indexed Y)
- case 0xd4: this.ZeroPageXRead(); break; // *NOP (zero page, X)
- case 0xd5: this.ZeroPageXRead(); this.CMP(this.A); break; // CMP (zero page, X)
- case 0xd6: this.ZeroPageXRead(); this.ModifyWrite(this.DEC()); break; // DEC (zero page, X)
- case 0xd8: this.SwallowRead(); this.ResetFlag(StatusBits.DF); break; // CLD (implied)
- case 0xd9: this.AbsoluteYRead(); this.CMP(this.A); break; // CMP (absolute, Y)
- case 0xdd: this.AbsoluteXRead(); this.CMP(this.A); break; // CMP (absolute, X)
- case 0xde: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.DEC()); break; // DEC (absolute, X)
-
- case 0xe0: this.ImmediateRead(); this.CMP(this.X); break; // CPX (immediate)
- case 0xe1: this.IndexedIndirectXRead(); this.SBC(); break; // SBC (indexed indirect X)
- case 0xe2: this.ImmediateRead(); break; // *NOP (immediate)
- case 0xe4: this.ZeroPageRead(); this.CMP(this.X); break; // CPX (zero page)
- case 0xe5: this.ZeroPageRead(); this.SBC(); break; // SBC (zero page)
- case 0xe6: this.ZeroPageRead(); this.ModifyWrite(INC()); break; // INC (zero page)
- case 0xe8: this.SwallowRead(); this.X = this.INC(this.X); break; // INX (implied)
- case 0xe9: this.ImmediateRead(); this.SBC(); break; // SBC (immediate)
- case 0xea: this.SwallowRead(); break; // NOP (implied)
- case 0xec: this.AbsoluteRead(); this.CMP(this.X); break; // CPX (absolute)
- case 0xed: this.AbsoluteRead(); this.SBC(); break; // SBC (absolute)
- case 0xee: this.AbsoluteRead(); this.ModifyWrite(this.INC()); break; // INC (absolute)
-
- case 0xf0: this.Branch(this.Zero); break; // BEQ (relative)
- case 0xf1: this.IndirectIndexedYRead(); this.SBC(); break; // SBC (indirect indexed Y)
- case 0xf4: this.ZeroPageXRead(); break; // *NOP (zero page, X)
- case 0xf5: this.ZeroPageXRead(); this.SBC(); break; // SBC (zero page, X)
- case 0xf6: this.ZeroPageXRead(); this.ModifyWrite(this.INC()); break; // INC (zero page, X)
- case 0xf8: this.SwallowRead(); this.SetFlag(StatusBits.DF); break; // SED (implied)
- case 0xf9: this.AbsoluteYRead(); this.SBC(); break; // SBC (absolute, Y)
- case 0xfd: this.AbsoluteXRead(); this.SBC(); break; // SBC (absolute, X)
- case 0xfe: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.INC()); break; // INC (absolute, X)
- }
-
- return cycles != this.Cycles;
- }
-
- public override void PoweredStep()
- {
- this.Tick();
- if (this.SO.Lowered())
- {
- this.HandleSO();
- }
-
- if (this.RDY.Raised())
- {
- this.FetchInstruction();
- if (this.RESET.Lowered())
- {
- this.HandleRESET();
- }
- else if (this.NMI.Lowered())
- {
- this.HandleNMI();
- }
- else if (this.INT.Lowered() && (this.InterruptMasked == 0))
- {
- this.HandleINT();
- }
- else
- {
- this.Execute();
- }
- }
- }
-
- private void FetchInstruction()
- {
- // Instruction fetch beginning
- this.LowerSYNC();
-
- System.Diagnostics.Debug.Assert(this.Cycles == 1, "An extra cycle has occurred");
-
- // Can't use fetchByte, since that would add an extra tick.
- this.ImmediateAddress();
- this.OpCode = this.ReadFromBus();
-
- System.Diagnostics.Debug.Assert(this.Cycles == 1, "BUS read has introduced stray cycles");
-
- // Instruction fetch has now completed
- this.RaiseSYNC();
- }
-
- #endregion
-
- #region Bus/Memory access
-
- protected override sealed void BusWrite()
- {
- this.Tick();
- this.WriteToBus();
- }
-
- protected override sealed byte BusRead()
- {
- this.Tick();
- return this.ReadFromBus();
- }
-
- private byte ReadFromBus()
- {
- this.RaiseRW();
- return base.BusRead();
- }
-
- private void WriteToBus()
- {
- this.LowerRW();
- base.BusWrite();
- }
-
- protected abstract void ModifyWrite(byte data);
-
- #endregion
-
- #region Stack access
-
- protected override byte Pop()
- {
- this.RaiseStack();
- return this.MemoryRead();
- }
-
- protected override void Push(byte value)
- {
- this.LowerStack();
- this.MemoryWrite(value);
- }
-
- private void UpdateStack(byte position)
- {
- this.Bus.Address.Assign(position, 1);
- }
-
- private void LowerStack() => this.UpdateStack(this.S--);
-
- private void RaiseStack() => this.UpdateStack(++this.S);
-
- private void DummyPush()
- {
- this.LowerStack();
- this.Tick(); // In place of the memory write
- }
-
- #endregion
-
- #region Addressing modes
-
- #region Address page fixup
-
- private byte fixedPage;
-
- private byte unfixedPage;
-
- public byte FixedPage
- {
- get => this.fixedPage;
- protected set => this.fixedPage = value;
- }
-
- public byte UnfixedPage
- {
- get => this.unfixedPage;
- protected set => this.unfixedPage = value;
- }
-
- public bool Fixed => this.FixedPage != this.UnfixedPage;
-
- protected void MaybeFixup()
- {
- if (this.Bus.Address.High != this.FixedPage)
- {
- this.Fixup();
- }
- }
-
- protected abstract void Fixup();
-
- protected void MaybeFixupRead()
- {
- this.MaybeFixup();
- this.MemoryRead();
- }
-
- protected void FixupRead()
- {
- this.Fixup();
- this.MemoryRead();
- }
-
- #endregion
-
- #region Address resolution
-
- protected void NoteFixedAddress(int address)
- {
- this.NoteFixedAddress((ushort)address);
- }
-
- protected void NoteFixedAddress(ushort address)
- {
- this.UnfixedPage = this.Bus.Address.High;
- this.Intermediate.Word = address;
- this.FixedPage = this.Intermediate.High;
- this.Bus.Address.Low = this.Intermediate.Low;
- }
-
- protected void GetAddressPaged()
- {
- this.GetWordPaged();
- this.Bus.Address.Assign(this.Intermediate);
- }
-
- protected void ImmediateAddress()
- {
- this.Bus.Address.Assign(this.PC);
- ++this.PC.Word;
- }
-
- protected void AbsoluteAddress() => this.FetchWordAddress();
-
- protected void ZeroPageAddress()
- {
- this.Bus.Address.Assign(this.FetchByte(), 0);
- }
-
- protected void ZeroPageIndirectAddress()
- {
- this.ZeroPageAddress();
- this.GetAddressPaged();
- }
-
- protected abstract void IndirectAddress();
-
- protected void ZeroPageWithIndexAddress(byte index)
- {
- this.ZeroPageRead();
- this.Bus.Address.Low += index;
- }
-
- protected void ZeroPageXAddress() => this.ZeroPageWithIndexAddress(this.X);
-
- protected void ZeroPageYAddress() => this.ZeroPageWithIndexAddress(this.Y);
-
- private void AbsoluteWithIndexAddress(byte index)
- {
- this.AbsoluteAddress();
- this.NoteFixedAddress(this.Bus.Address.Word + index);
- }
-
- protected void AbsoluteXAddress() => this.AbsoluteWithIndexAddress(X);
-
- protected void AbsoluteYAddress() => this.AbsoluteWithIndexAddress(Y);
-
- protected void IndexedIndirectXAddress()
- {
- this.ZeroPageXAddress();
- this.GetAddressPaged();
- }
-
- protected void IndirectIndexedYAddress()
- {
- this.ZeroPageIndirectAddress();
- this.NoteFixedAddress(this.Bus.Address.Word + Y);
- }
-
- #endregion
-
- #region Address and read
-
- protected void ImmediateRead()
- {
- this.ImmediateAddress();
- this.MemoryRead();
- }
-
- protected void AbsoluteRead()
- {
- this.AbsoluteAddress();
- this.MemoryRead();
- }
-
- protected void ZeroPageRead()
- {
- this.ZeroPageAddress();
- this.MemoryRead();
- }
-
- protected void ZeroPageXRead()
- {
- this.ZeroPageXAddress();
- this.MemoryRead();
- }
-
- protected void ZeroPageYRead()
- {
- this.ZeroPageYAddress();
- this.MemoryRead();
- }
-
- protected void IndexedIndirectXRead()
- {
- this.IndexedIndirectXAddress();
- this.MemoryRead();
- }
-
- protected void AbsoluteXRead()
- {
- this.AbsoluteXAddress();
- this.MaybeFixupRead();
- }
-
- protected void AbsoluteYRead()
- {
- this.AbsoluteYAddress();
- this.MaybeFixupRead();
- }
-
- protected void IndirectIndexedYRead()
- {
- this.IndirectIndexedYAddress();
- this.MaybeFixupRead();
- }
-
- #endregion
-
- #endregion
-
- #region Branching
-
- protected void BranchNot(int condition) => this.Branch(condition == 0);
-
- protected void Branch(int condition) => this.Branch(condition != 0);
-
- protected void Branch(bool condition)
- {
- this.ImmediateRead();
- if (condition)
- {
- var relative = (sbyte)this.Bus.Data;
- this.SwallowRead();
- this.FixupBranch(relative);
- this.Jump(this.Bus.Address);
- }
- }
-
- protected abstract void FixupBranch(sbyte relative);
-
- #endregion
-
- #region Data flag adjustment
-
- protected void AdjustZero(byte datum) => this.ResetFlag(StatusBits.ZF, datum);
-
- protected void AdjustNegative(byte datum) => this.SetFlag(StatusBits.NF, NegativeTest(datum));
-
- protected void AdjustNZ(byte datum)
- {
- this.AdjustZero(datum);
- this.AdjustNegative(datum);
- }
-
- protected byte Through() => this.Through(this.Bus.Data);
-
- protected byte Through(int data) => this.Through((byte)data);
-
- protected byte Through(byte data)
- {
- this.AdjustNZ(data);
- return data;
- }
-
- #endregion
-
- #region Instruction implementations
-
- #region Instructions with BCD effects
-
- #region Addition/subtraction
-
- #region Subtraction
-
- protected void AdjustOverflowSubtract(byte operand)
- {
- var data = Bus.Data;
- var intermediate = this.Intermediate.Low;
- this.SetFlag(StatusBits.VF, NegativeTest((byte)((operand ^ data) & (operand ^ intermediate))));
- }
-
- protected void SBC()
- {
- var operand = this.A;
- A = this.SUB(operand, CarryTest((byte)~this.P));
-
- this.AdjustOverflowSubtract(operand);
- this.AdjustNZ(this.Intermediate.Low);
- this.ResetFlag(StatusBits.CF, this.Intermediate.High);
- }
-
- private byte SUB(byte operand, int borrow) => this.DecimalMasked != 0 ? DecimalSUB(operand, borrow) : BinarySUB(operand, borrow);
-
- protected byte BinarySUB(byte operand, int borrow = 0)
- {
- var data = Bus.Data;
- this.Intermediate.Word = (ushort)(operand - data - borrow);
- return this.Intermediate.Low;
- }
-
- private byte DecimalSUB(byte operand, int borrow)
- {
- _ = this.BinarySUB(operand, borrow);
-
- var data = this.Bus.Data;
- var low = (byte)(LowNibble(operand) - LowNibble(data) - borrow);
- var lowNegative = NegativeTest(low);
- if (lowNegative != 0)
- {
- low -= 6;
- }
-
- var high = (byte)(HighNibble(operand) - HighNibble(data) - (lowNegative >> 7));
- var highNegative = NegativeTest(high);
- if (highNegative != 0)
- {
- high -= 6;
- }
-
- return (byte)(PromoteNibble(high) | LowNibble(low));
- }
-
- #endregion
-
- #region Addition
-
- protected void AdjustOverflowAdd(byte operand)
- {
- var data = Bus.Data;
- var intermediate = this.Intermediate.Low;
- this.SetFlag(StatusBits.VF, NegativeTest((byte)(~(operand ^ data) & (operand ^ intermediate))));
- }
-
- protected void ADC()
- {
- this.A = this.DecimalMasked != 0 ? this.DecimalADC() : this.BinaryADC();
- }
-
- private byte BinaryADC()
- {
- var operand = A;
- var data = Bus.Data;
- this.Intermediate.Word = (ushort)(operand + data + this.Carry);
-
- this.AdjustOverflowAdd(operand);
- this.SetFlag(StatusBits.CF, CarryTest(this.Intermediate.High));
-
- this.AdjustNZ(this.Intermediate.Low);
-
- return this.Intermediate.Low;
- }
-
- private byte DecimalADC()
- {
- var operand = this.A;
- var data = this.Bus.Data;
-
- var low = (ushort)(LowerNibble(operand) + LowerNibble(data) + this.Carry);
- this.Intermediate.Word = (ushort)(HigherNibble(operand) + HigherNibble(data));
-
- this.AdjustZero(LowByte((ushort)(low + this.Intermediate.Word)));
-
- if (low > 0x09)
- {
- this.Intermediate.Word += 0x10;
- low += 0x06;
- }
-
- this.AdjustNegative(this.Intermediate.Low);
- this.AdjustOverflowAdd(operand);
-
- if (this.Intermediate.Word > 0x90)
- this.Intermediate.Word += 0x60;
-
- this.SetFlag(StatusBits.CF, this.Intermediate.High);
-
- return (byte)(LowerNibble(LowByte(low)) | HigherNibble(this.Intermediate.Low));
- }
-
- #endregion
-
- #endregion
-
- #endregion
-
- #region Bitwise operations
-
- protected void OrR() => this.A = this.Through(this.A | this.Bus.Data);
-
- protected void AndR() => this.A = this.Through(this.A & this.Bus.Data);
-
- protected void EorR() => this.A = this.Through(this.A ^ this.Bus.Data);
-
- protected void BIT()
- {
- var data = this.Bus.Data;
- this.SetFlag(StatusBits.VF, OverflowTest(data));
- this.AdjustZero((byte)(this.A & data));
- this.AdjustNegative(data);
- }
-
- #endregion
-
- protected void CMP(byte first)
- {
- var second = Bus.Data;
- this.Intermediate.Word = (ushort)(first - second);
- AdjustNZ(this.Intermediate.Low);
- ResetFlag(StatusBits.CF, this.Intermediate.High);
- }
-
- #region Increment/decrement
-
- protected byte DEC() => this.DEC(this.Bus.Data);
-
- protected byte DEC(byte value) => this.Through(value - 1);
-
- protected byte INC() => this.INC(this.Bus.Data);
-
- protected byte INC(byte value) => this.Through(value + 1);
-
- #endregion
-
- #region Stack operations
-
- private void JSR()
- {
- this.Intermediate.Low = this.FetchByte();
- this.SwallowPop();
- this.PushWord(this.PC);
- this.PC.High = this.FetchByte();
- this.PC.Low = this.Intermediate.Low;
- }
-
- private void PHP() => this.Push(SetBit(this.P, StatusBits.BF));
-
- private void PLP()
- {
- this.SwallowPop();
- this.P = ClearBit(SetBit(this.Pop(), StatusBits.RF), StatusBits.BF);
- }
-
- private void RTI()
- {
- this.PLP();
- base.Return();
- }
-
- protected override void Return()
- {
- this.SwallowPop();
- base.Return();
- this.SwallowFetch();
- }
-
- #endregion
-
- #region Shift/rotate operations
-
- #region Shift
-
- protected byte ASL() => this.ASL(this.Bus.Data);
-
- protected byte ASL(byte value)
- {
- this.SetFlag(StatusBits.CF, NegativeTest(value));
- return this.Through(value << 1);
- }
-
- protected byte LSR() => this.LSR(this.Bus.Data);
-
- protected byte LSR(byte value)
- {
- this.SetFlag(StatusBits.CF, CarryTest(value));
- return this.Through(value >> 1);
- }
-
- #endregion
-
- #region Rotate
-
- protected byte ROL() => this.ROL(this.Bus.Data);
-
- protected byte ROL(byte value)
- {
- var carryIn = this.Carry;
- return this.Through(this.ASL(value) | carryIn);
- }
-
- protected byte ROR() => this.ROR(this.Bus.Data);
-
- protected byte ROR(byte value)
- {
- var carryIn = this.Carry;
- return this.Through(this.LSR(value) | (carryIn << 7));
- }
-
- #endregion
-
- #endregion
-
- #endregion
- }
-}
\ No newline at end of file
diff --git a/M6502/MOS6502.cs b/M6502/MOS6502.cs
new file mode 100644
index 0000000..a69dd49
--- /dev/null
+++ b/M6502/MOS6502.cs
@@ -0,0 +1,347 @@
+//
+// Copyright (c) Adrian Conlon. All rights reserved.
+//
+
+namespace M6502
+{
+ using EightBit;
+
+ public class MOS6502(Bus bus) : Core(bus)
+ {
+ #region Core instruction dispatching
+
+ protected override bool MaybeExecute()
+ {
+ if (base.MaybeExecute())
+ {
+ return true;
+ }
+
+ var cycles = Cycles;
+ switch (OpCode)
+ {
+ case 0x02: Jam(); break; // *JAM
+ case 0x03: IndexedIndirectXRead(); SLO(); break; // *SLO (indexed indirect X)
+ case 0x04: ZeroPageRead(); break; // *NOP (zero page)
+ case 0x07: ZeroPageRead(); SLO(); break; // *SLO (zero page)
+ case 0x0b: ImmediateRead(); ANC(); break; // *ANC (immediate)
+ case 0x0c: AbsoluteRead(); break; // *NOP (absolute)
+ case 0x0f: AbsoluteRead(); SLO(); break; // *SLO (absolute)
+
+ case 0x12: Jam(); break; // *JAM
+ case 0x13: IndirectIndexedYAddress(); FixupRead(); SLO(); break; // *SLO (indirect indexed Y)
+ case 0x14: ZeroPageXRead(); break; // *NOP (zero page, X)
+ case 0x17: ZeroPageXRead(); SLO(); break; // *SLO (zero page, X)
+ case 0x1a: SwallowRead(); break; // *NOP (implied)
+ case 0x1b: AbsoluteYAddress(); FixupRead(); SLO(); break; // *SLO (absolute, Y)
+ case 0x1c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
+ case 0x1f: AbsoluteXAddress(); FixupRead(); SLO(); break; // *SLO (absolute, X)
+
+ case 0x22: Jam(); break; // *JAM
+ case 0x23: IndexedIndirectXRead(); RLA(); ; break; // *RLA (indexed indirect X)
+ case 0x27: ZeroPageRead(); RLA(); ; break; // *RLA (zero page)
+ case 0x2b: ImmediateRead(); ANC(); break; // *ANC (immediate)
+ case 0x2f: AbsoluteRead(); RLA(); break; // *RLA (absolute)
+
+ case 0x32: Jam(); break; // *JAM
+ case 0x33: IndirectIndexedYAddress(); FixupRead(); RLA(); break; // *RLA (indirect indexed Y)
+ case 0x34: ZeroPageXRead(); break; // *NOP (zero page, X)
+ case 0x37: ZeroPageXRead(); RLA(); ; break; // *RLA (zero page, X)
+ case 0x3a: SwallowRead(); break; // *NOP (implied)
+ case 0x3b: AbsoluteYAddress(); FixupRead(); RLA(); break; // *RLA (absolute, Y)
+ case 0x3c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
+ case 0x3f: AbsoluteXAddress(); FixupRead(); RLA(); break; // *RLA (absolute, X)
+
+ case 0x42: Jam(); break; // *JAM
+ case 0x43: IndexedIndirectXRead(); SRE(); break; // *SRE (indexed indirect X)
+ case 0x47: ZeroPageRead(); SRE(); break; // *SRE (zero page)
+ case 0x4b: ImmediateRead(); ASR(); break; // *ASR (immediate)
+ case 0x4f: AbsoluteRead(); SRE(); break; // *SRE (absolute)
+
+ case 0x52: Jam(); break; // *JAM
+ case 0x53: IndirectIndexedYAddress(); FixupRead(); SRE(); break; // *SRE (indirect indexed Y)
+ case 0x57: ZeroPageXRead(); SRE(); break; // *SRE (zero page, X)
+ case 0x5a: SwallowRead(); break; // *NOP (implied)
+ case 0x5b: AbsoluteYAddress(); FixupRead(); SRE(); break; // *SRE (absolute, Y)
+ case 0x5c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
+ case 0x5f: AbsoluteXAddress(); FixupRead(); SRE(); break; // *SRE (absolute, X)
+
+ case 0x62: Jam(); break; // *JAM
+ case 0x63: IndexedIndirectXRead(); RRA(); break; // *RRA (indexed indirect X)
+ case 0x64: ZeroPageRead(); break; // *NOP (zero page)
+ case 0x67: ZeroPageRead(); RRA(); break; // *RRA (zero page)
+ case 0x6b: ImmediateRead(); ARR(); break; // *ARR (immediate)
+ case 0x6f: AbsoluteRead(); RRA(); break; // *RRA (absolute)
+
+ case 0x72: Jam(); break; // *JAM
+ case 0x73: IndirectIndexedYAddress(); FixupRead(); RRA(); break; // *RRA (indirect indexed Y)
+ case 0x74: ZeroPageXRead(); break; // *NOP (zero page, X)
+ case 0x77: ZeroPageXRead(); RRA(); break; // *RRA (zero page, X)
+ case 0x7a: SwallowRead(); break; // *NOP (implied)
+ case 0x7b: AbsoluteYAddress(); FixupRead(); RRA(); break; // *RRA (absolute, Y)
+ case 0x7c: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
+ case 0x7f: AbsoluteXAddress(); FixupRead(); RRA(); break; // *RRA (absolute, X)
+
+ case 0x80: ImmediateRead(); break; // *NOP (immediate)
+ case 0x83: IndexedIndirectXAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (indexed indirect X)
+ case 0x87: ZeroPageAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (zero page)
+ case 0x89: ImmediateRead(); break; // *NOP (immediate)
+ case 0x8b: ImmediateRead(); ANE(); break; // *ANE (immediate)
+ case 0x8f: AbsoluteAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (absolute)
+
+ case 0x92: Jam(); break; // *JAM
+ case 0x93: IndirectIndexedYAddress(); Fixup(); SHA(); break; // *SHA (indirect indexed, Y)
+ case 0x97: ZeroPageYAddress(); MemoryWrite((byte)(A & X)); break; // *SAX (zero page, Y)
+ case 0x9b: AbsoluteYAddress(); Fixup(); TAS(); break; // *TAS (absolute, Y)
+ case 0x9c: AbsoluteXAddress(); Fixup(); SYA(); break; // *SYA (absolute, X)
+ case 0x9e: AbsoluteYAddress(); Fixup(); SXA(); break; // *SXA (absolute, Y)
+ case 0x9f: AbsoluteYAddress(); Fixup(); SHA(); break; // *SHA (absolute, Y)
+
+ case 0xa3: IndexedIndirectXRead(); A = X = Through(); break; // *LAX (indexed indirect X)
+ case 0xa7: ZeroPageRead(); A = X = Through(); break; // *LAX (zero page)
+ case 0xab: ImmediateRead(); ATX(); break; // *ATX (immediate)
+ case 0xaf: AbsoluteRead(); A = X = Through(); break; // *LAX (absolute)
+
+ case 0xb2: Jam(); break; // *JAM
+ case 0xb3: IndirectIndexedYRead(); A = X = Through(); break; // *LAX (indirect indexed Y)
+ case 0xb7: ZeroPageYRead(); A = X = Through(); break; // *LAX (zero page, Y)
+ case 0xbb: AbsoluteYAddress(); MaybeFixup(); LAS(); break; // *LAS (absolute, Y)
+ case 0xbf: AbsoluteYRead(); A = X = Through(); break; // *LAX (absolute, Y)
+
+ case 0xc3: IndexedIndirectXRead(); DCP(); break; // *DCP (indexed indirect X)
+ case 0xc7: ZeroPageRead(); DCP(); break; // *DCP (zero page)
+ case 0xcb: ImmediateRead(); AXS(); break; // *AXS (immediate)
+ case 0xcf: AbsoluteRead(); DCP(); break; // *DCP (absolute)
+
+ case 0xd2: Jam(); break; // *JAM
+ case 0xd3: IndirectIndexedYAddress(); FixupRead(); DCP(); break; // *DCP (indirect indexed Y)
+ case 0xd7: ZeroPageXRead(); DCP(); break; // *DCP (zero page, X)
+ case 0xda: SwallowRead(); break; // *NOP (implied)
+ case 0xdb: AbsoluteYAddress(); FixupRead(); DCP(); break; // *DCP (absolute, Y)
+ case 0xdc: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
+ case 0xdf: AbsoluteXAddress(); FixupRead(); DCP(); break; // *DCP (absolute, X)
+
+ case 0xe3: IndexedIndirectXRead(); ISB(); break; // *ISB (indexed indirect X)
+ case 0xe7: ZeroPageRead(); ISB(); break; // *ISB (zero page)
+ case 0xeb: ImmediateRead(); SBC(); break; // *SBC (immediate)
+ case 0xef: AbsoluteRead(); ISB(); break; // *ISB (absolute)
+
+ case 0xf2: Jam(); break; // *JAM
+ case 0xf3: IndirectIndexedYAddress(); FixupRead(); ISB(); break; // *ISB (indirect indexed Y)
+ case 0xf7: ZeroPageXRead(); ISB(); break; // *ISB (zero page, X)
+ case 0xfa: SwallowRead(); break; // *NOP (implied)
+ case 0xfb: AbsoluteYAddress(); FixupRead(); ISB(); break; // *ISB (absolute, Y)
+ case 0xfc: AbsoluteXAddress(); MaybeFixupRead(); break; // *NOP (absolute, X)
+ case 0xff: AbsoluteXAddress(); FixupRead(); ISB(); break; // *ISB (absolute, X)
+ }
+
+ return cycles != Cycles;
+ }
+
+ #endregion
+
+ #region Bus/Memory Access
+
+ protected override void ModifyWrite(byte data)
+ {
+ // The read will have already taken place...
+ MemoryWrite(); // Modify cycle
+ MemoryWrite(data); // Write cycle
+ }
+
+ #endregion
+
+ #region Addressing modes
+
+ protected override void IndirectAddress()
+ {
+ AbsoluteAddress();
+ GetAddressPaged();
+ }
+
+ #region Address page fixup
+
+ protected override void Fixup()
+ {
+ MemoryRead();
+ Bus.Address.High = FixedPage;
+ }
+
+ protected override void FixupBranch(sbyte relative)
+ {
+ NoteFixedAddress(PC.Word + relative);
+ MaybeFixup();
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Instruction implementations
+
+ #region Undocumented instructions
+
+ #region Undocumented instructions with BCD effects
+
+ private void ARR()
+ {
+ var value = Bus.Data;
+ if (DecimalMasked != 0)
+ ARR_d(value);
+ else
+ ARR_b(value);
+ }
+
+ private void ARR_d(byte value)
+ {
+ // With thanks to https://github.com/TomHarte/CLK
+ // What a very strange instruction ARR is...
+
+ A &= value;
+ var unshiftedA = A;
+ A = Through(A >> 1 | Carry << 7);
+ SetFlag(StatusBits.VF, OverflowTest((byte)(A ^ A << 1)));
+
+ if (LowerNibble(unshiftedA) + (unshiftedA & 0x1) > 5)
+ A = (byte)(LowerNibble((byte)(A + 6)) | HigherNibble(A));
+
+ SetFlag(StatusBits.CF, HigherNibble(unshiftedA) + (unshiftedA & 0x10) > 0x50);
+
+ if (Carry != 0)
+ A += 0x60;
+ }
+
+ private void ARR_b(byte value)
+ {
+ A &= value;
+ A = Through(A >> 1 | Carry << 7);
+ SetFlag(StatusBits.CF, OverflowTest(A));
+ SetFlag(StatusBits.VF, OverflowTest((byte)(A ^ A << 1)));
+ }
+
+ #endregion
+
+ #region Undocumented instructions with fixup effects
+
+ private void StoreFixupEffect(byte data)
+ {
+ ////var fixedAddress = (byte)(this.Bus.Address.High + 1);
+ //var fixedAddress = this.FixedPage + 1;
+ //var updated = (byte)(data & fixedAddress);
+ //if (this.Fixed)
+ //{
+ // this.Bus.Address.High = updated;
+ //}
+
+ //this.MemoryWrite(updated);
+
+ byte updated;
+ if (Fixed)
+ {
+ updated = (byte)(data & FixedPage);
+ Bus.Address.High = updated;
+ }
+ else
+ {
+ updated = (byte)(data & UnfixedPage);
+ Bus.Address.High = updated;
+ }
+ MemoryWrite(updated);
+ }
+
+ private void SHA() => StoreFixupEffect((byte)(A & X));
+
+ private void SYA() => StoreFixupEffect(Y);
+
+ private void SXA() => StoreFixupEffect(X);
+
+ #endregion
+
+ private void ANC()
+ {
+ AndR();
+ SetFlag(StatusBits.CF, NegativeTest(A));
+ }
+
+ private void AXS()
+ {
+ X = Through(BinarySUB((byte)(A & X)));
+ ResetFlag(StatusBits.CF, Intermediate.High);
+ }
+
+ private void Jam()
+ {
+ Bus.Address.Assign(PC);
+ MemoryRead();
+ MemoryRead(0xff, 0xff);
+ Bus.Address.Low = 0xfe;
+ MemoryRead();
+ MemoryRead();
+ Bus.Address.Low = 0xff;
+ MemoryRead();
+ MemoryRead();
+ MemoryRead();
+ MemoryRead();
+ MemoryRead();
+ MemoryRead();
+ }
+
+ private void TAS()
+ {
+ S = (byte)(A & X);
+ SHA();
+ }
+
+ private void LAS() => A = X = S = Through(MemoryRead() & S);
+
+ private void ANE() => A = Through((A | 0xee) & X & Bus.Data);
+
+ private void ATX() => A = X = Through((A | 0xee) & Bus.Data);
+
+ private void ASR()
+ {
+ AndR();
+ A = LSR(A);
+ }
+
+ private void ISB()
+ {
+ ModifyWrite(INC());
+ SBC();
+ }
+
+ private void RLA()
+ {
+ ModifyWrite(ROL());
+ AndR();
+ }
+
+ private void RRA()
+ {
+ ModifyWrite(ROR());
+ ADC();
+ }
+
+ private void SLO()
+ {
+ ModifyWrite(ASL());
+ OrR();
+ }
+
+ private void SRE()
+ {
+ ModifyWrite(LSR());
+ EorR();
+ }
+
+ private void DCP()
+ {
+ ModifyWrite(DEC());
+ CMP(A);
+ }
+
+ #endregion
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/M6502/ProfileEventArgs.cs b/M6502/ProfileEventArgs.cs
index 28dbab6..51f2e6d 100644
--- a/M6502/ProfileEventArgs.cs
+++ b/M6502/ProfileEventArgs.cs
@@ -1,4 +1,4 @@
-namespace EightBit
+namespace M6502
{
public sealed class ProfileEventArgs(string output) : EventArgs
{
diff --git a/M6502/ProfileInstructionEventArgs.cs b/M6502/ProfileInstructionEventArgs.cs
index 4517db5..5003741 100644
--- a/M6502/ProfileInstructionEventArgs.cs
+++ b/M6502/ProfileInstructionEventArgs.cs
@@ -1,4 +1,4 @@
-namespace EightBit
+namespace M6502
{
public sealed class ProfileInstructionEventArgs(byte instruction, long cycles, long count) : EventArgs
{
diff --git a/M6502/ProfileLineEventArgs.cs b/M6502/ProfileLineEventArgs.cs
index d9b6c3a..c3fa54e 100644
--- a/M6502/ProfileLineEventArgs.cs
+++ b/M6502/ProfileLineEventArgs.cs
@@ -1,4 +1,4 @@
-namespace EightBit
+namespace M6502
{
public sealed class ProfileLineEventArgs(ushort address, string source, long cycles, long count, Dictionary cycleDistributions) : CycleCountedEventArgs(cycles, count)
{
diff --git a/M6502/ProfileScopeEventArgs.cs b/M6502/ProfileScopeEventArgs.cs
index d75ab33..ca3043a 100644
--- a/M6502/ProfileScopeEventArgs.cs
+++ b/M6502/ProfileScopeEventArgs.cs
@@ -1,4 +1,4 @@
-namespace EightBit
+namespace M6502
{
public sealed class ProfileScopeEventArgs(int id, long cycles, long count) : CycleCountedEventArgs(cycles, count)
{
diff --git a/M6502/Profiler.cs b/M6502/Profiler.cs
index 9834006..4eb1603 100644
--- a/M6502/Profiler.cs
+++ b/M6502/Profiler.cs
@@ -1,4 +1,4 @@
-namespace EightBit
+namespace M6502
{
using System.Diagnostics;
@@ -9,9 +9,9 @@
private readonly Dictionary[] addressCycleDistributions = new Dictionary[0x10000]; // Addresses -> cycles -> counts
private readonly Dictionary scopeCycles = []; // ID -> Cycles
- private readonly M6502 processor;
+ private readonly MOS6502 processor;
private readonly Disassembler disassembler;
- private readonly Files.Symbols.Parser symbols;
+ private readonly Symbols.Parser symbols;
private ushort executingAddress;
private byte executingInstruction;
@@ -19,7 +19,7 @@
private long totalCycles = -1;
private bool totalCyclesValid;
- public Profiler(M6502 processor, Disassembler disassembler, Files.Symbols.Parser symbols, bool activate)
+ public Profiler(MOS6502 processor, Disassembler disassembler, Symbols.Parser symbols, bool activate)
{
ArgumentNullException.ThrowIfNull(processor);
ArgumentNullException.ThrowIfNull(disassembler);
@@ -31,8 +31,8 @@
if (activate)
{
- this.processor.RaisingSYNC += this.Processor_RaisingSYNC;
- this.processor.ExecutedInstruction += this.Processor_ExecutedInstruction;
+ this.processor.RaisingSYNC += Processor_RaisingSYNC;
+ this.processor.ExecutedInstruction += Processor_ExecutedInstruction;
}
}
@@ -62,140 +62,140 @@
{
get
{
- Debug.Assert(this.totalCyclesValid);
- return this.totalCycles;
+ Debug.Assert(totalCyclesValid);
+ return totalCycles;
}
private set
{
- Debug.Assert(!this.totalCyclesValid);
- this.totalCycles = value;
- this.totalCyclesValid = true;
+ Debug.Assert(!totalCyclesValid);
+ totalCycles = value;
+ totalCyclesValid = true;
}
}
public void Generate()
{
- this.OnStartingOutput();
+ OnStartingOutput();
try
{
- this.EmitProfileInformation();
+ EmitProfileInformation();
}
finally
{
- this.OnFinishedOutput();
+ OnFinishedOutput();
}
}
private void EmitProfileInformation()
{
- this.TotalCycles = this.instructionCycles.Sum();
+ TotalCycles = instructionCycles.Sum();
- this.EmitProfileLineInformation();
- this.EmitProfileScopeInformation();
- this.EmitProfileInstructionInformation();
+ EmitProfileLineInformation();
+ EmitProfileScopeInformation();
+ EmitProfileInstructionInformation();
}
private void EmitProfileScopeInformation()
{
- this.OnStartingScopeOutput();
+ OnStartingScopeOutput();
try
{
- foreach (var (id, cycles) in this.scopeCycles)
+ foreach (var (id, cycles) in scopeCycles)
{
- var symbol = this.symbols.LookupLabelByID(id);
+ var symbol = symbols.LookupLabelByID(id);
Debug.Assert(symbol != null);
- var available = this.ExtractCycleDistribution((ushort)symbol.Value, out var _,out var _, out var count);
+ var available = ExtractCycleDistribution((ushort)symbol.Value, out var _, out var _, out var count);
Debug.Assert(available);
- this.OnEmitScope(id, cycles, count);
+ OnEmitScope(id, cycles, count);
}
}
finally
{
- this.OnFinishedScopeOutput();
+ OnFinishedScopeOutput();
}
}
private void EmitProfileLineInformation()
{
- this.OnStartingLineOutput();
+ OnStartingLineOutput();
try
{
// For each memory address
for (var i = 0; i < 0x10000; ++i)
{
var address = (ushort)i;
- var available = this.ExtractCycleDistribution(address, out var cycleDistributions, out var cycles, out var count);
+ var available = ExtractCycleDistribution(address, out var cycleDistributions, out var cycles, out var count);
if (available)
{
// Dump a profile/disassembly line
- var source = this.disassembler.Disassemble(address);
+ var source = disassembler.Disassemble(address);
Debug.Assert(cycleDistributions != null);
- this.OnEmitLine(address, source, cycles, count, cycleDistributions);
+ OnEmitLine(address, source, cycles, count, cycleDistributions);
}
}
}
finally
{
- this.OnFinishedLineOutput();
+ OnFinishedLineOutput();
}
}
private void EmitProfileInstructionInformation()
{
- this.OnStartingInstructionOutput();
+ OnStartingInstructionOutput();
try
{
// For each instruction
for (var i = 0; i < 0x100; ++i)
{
// If there are any cycles associated
- var cycles = this.instructionCycles[i];
+ var cycles = instructionCycles[i];
if (cycles > 0)
{
- var count = this.instructionCounts[i];
+ var count = instructionCounts[i];
Debug.Assert(count > 0);
var instruction = (byte)i;
// Emit an instruction event
- this.OnEmitInstruction(instruction, cycles, count);
+ OnEmitInstruction(instruction, cycles, count);
}
}
}
finally
{
- this.OnFinishedInstructionOutput();
+ OnFinishedInstructionOutput();
}
}
private void Processor_RaisingSYNC(object? sender, EventArgs e)
{
- this.executingAddress = this.processor.Bus.Address.Word;
- ++this.instructionCounts[this.executingInstruction = this.processor.Bus.Data];
+ executingAddress = processor.Bus.Address.Word;
+ ++instructionCounts[executingInstruction = processor.Bus.Data];
}
private void Processor_ExecutedInstruction(object? sender, EventArgs e)
{
- var cycles = this.processor.Cycles;
+ var cycles = processor.Cycles;
{
- var addressDistribution = this.addressCycleDistributions[this.executingAddress];
+ var addressDistribution = addressCycleDistributions[executingAddress];
if (addressDistribution == null)
{
- this.addressCycleDistributions[this.executingAddress] = addressDistribution = [];
+ addressCycleDistributions[executingAddress] = addressDistribution = [];
}
_ = addressDistribution.TryGetValue(cycles, out var current);
addressDistribution[cycles] = ++current;
}
- this.instructionCycles[this.executingInstruction] += cycles;
+ instructionCycles[executingInstruction] += cycles;
{
- var scope = this.symbols.LookupScopeByAddress(this.executingAddress);
+ var scope = symbols.LookupScopeByAddress(executingAddress);
if (scope != null)
{
var id = scope.ID;
// Current will be initialised to zero, if absent
- _ = this.scopeCycles.TryGetValue(id, out var current);
- this.scopeCycles[id] = current + cycles;
+ _ = scopeCycles.TryGetValue(id, out var current);
+ scopeCycles[id] = current + cycles;
}
}
}
@@ -216,7 +216,7 @@
foreach (var (cycle, count) in cycleDistribution)
{
hitCount += count;
- cycleCount += (cycle * count);
+ cycleCount += cycle * count;
}
Debug.Assert(hitCount > 0);
@@ -227,57 +227,57 @@
private void OnStartingOutput()
{
- this.StartingOutput?.Invoke(this, EventArgs.Empty);
+ StartingOutput?.Invoke(this, EventArgs.Empty);
}
private void OnFinishedOutput()
{
- this.FinishedOutput?.Invoke(this, EventArgs.Empty);
+ FinishedOutput?.Invoke(this, EventArgs.Empty);
}
private void OnStartingLineOutput()
{
- this.StartingLineOutput?.Invoke(this, EventArgs.Empty);
+ StartingLineOutput?.Invoke(this, EventArgs.Empty);
}
private void OnFinishedLineOutput()
{
- this.FinishedLineOutput?.Invoke(this, EventArgs.Empty);
+ FinishedLineOutput?.Invoke(this, EventArgs.Empty);
}
private void OnStartingInstructionOutput()
{
- this.StartingInstructionOutput?.Invoke(this, EventArgs.Empty);
+ StartingInstructionOutput?.Invoke(this, EventArgs.Empty);
}
private void OnFinishedInstructionOutput()
{
- this.FinishedInstructionOutput?.Invoke(this, EventArgs.Empty);
+ FinishedInstructionOutput?.Invoke(this, EventArgs.Empty);
}
private void OnStartingScopeOutput()
{
- this.StartingScopeOutput?.Invoke(this, EventArgs.Empty);
+ StartingScopeOutput?.Invoke(this, EventArgs.Empty);
}
private void OnFinishedScopeOutput()
{
- this.FinishedScopeOutput?.Invoke(this, EventArgs.Empty);
+ FinishedScopeOutput?.Invoke(this, EventArgs.Empty);
}
private void OnEmitLine(ushort address, string source, long cycles, long count, Dictionary cycleDistributions)
{
- this.EmitLine?.Invoke(this, new ProfileLineEventArgs(address, source, cycles, count, cycleDistributions));
+ EmitLine?.Invoke(this, new ProfileLineEventArgs(address, source, cycles, count, cycleDistributions));
}
private void OnEmitScope(int id, long cycles, long count)
{
- this.EmitScope?.Invoke(this, new ProfileScopeEventArgs(id, cycles, count));
+ EmitScope?.Invoke(this, new ProfileScopeEventArgs(id, cycles, count));
}
private void OnEmitInstruction(byte instruction, long cycles, long count)
{
- this.EmitInstruction?.Invoke(this, new ProfileInstructionEventArgs(instruction, cycles, count));
+ EmitInstruction?.Invoke(this, new ProfileInstructionEventArgs(instruction, cycles, count));
}
}
}
\ No newline at end of file
diff --git a/M6502/StatusBits.cs b/M6502/StatusBits.cs
index ab678ca..4e9b350 100644
--- a/M6502/StatusBits.cs
+++ b/M6502/StatusBits.cs
@@ -2,8 +2,10 @@
// Copyright (c) Adrian Conlon. All rights reserved.
//
-namespace EightBit
+namespace M6502
{
+ using EightBit;
+
[Flags]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32", Justification = "Must be castable to byte")]
public enum StatusBits : byte
diff --git a/M6502/W65C02.cs b/M6502/W65C02.cs
deleted file mode 100644
index e5001be..0000000
--- a/M6502/W65C02.cs
+++ /dev/null
@@ -1,303 +0,0 @@
-//
-// Copyright (c) Adrian Conlon. All rights reserved.
-//
-
-namespace EightBit
-{
- public class W65C02(Bus bus) : M6502Core(bus)
- {
- private bool _stopped;
- private bool _waiting;
-
- private bool Stopped
- {
- get => this._stopped; set => this._stopped = value;
- }
-
- private bool Waiting
- {
- get => this._waiting; set => this._waiting = value;
- }
-
- private bool Paused => this.Stopped || this.Waiting;
-
- #region Interrupts
-
- protected override void Interrupt(byte vector, InterruptSource source, InterruptType type)
- {
- base.Interrupt(vector, source, type);
- this.ResetFlag(StatusBits.DF); // Disable decimal mode (Change from M6502)
- }
-
- #endregion
-
- #region Core instruction dispatching
-
- protected override bool MaybeExecute()
- {
- if (base.MaybeExecute())
- {
- return true;
- }
-
- var cycles = this.Cycles;
- switch (this.OpCode)
- {
- case 0x02: this.SwallowFetch(); break; // NOP
- case 0x03: break; // null
- case 0x04: this.ZeroPageRead(); this.TSB(); break; // TSB zp
- case 0x07: this.ZeroPageRead(); this.RMB(Chip.Bit(0)); break; // RMB0 zp
- case 0x0b: break; // null
- case 0x0c: this.AbsoluteRead(); this.TSB(); break; // TSB a
- case 0x0f: this.ZeroPageRead(); this.BBR(Chip.Bit(0)); break; // BBR0 r
-
- case 0x12: this.ZeroPageIndirectAddress(); this.OrR(); break; // ORA (zp),y
- case 0x13: break; // null
- case 0x14: this.ZeroPageRead(); this.TRB(); break; // TRB zp
- case 0x17: this.ZeroPageRead(); this.RMB(Chip.Bit(1)); break; // RMB1 zp
- case 0x1a: this.SwallowRead(); this.A = this.INC(this.A); break; // INC A
- case 0x1b: break; // null
- case 0x1c: this.AbsoluteRead(); this.TRB(); break; // TRB a
- case 0x1f: this.ZeroPageRead(); this.BBR(Chip.Bit(1)); break; // BBR1 r
-
- case 0x22: this.SwallowFetch(); break; // NOP
- case 0x23: break; // null
- case 0x27: this.ZeroPageRead(); this.RMB(Chip.Bit(2)); break; // RMB2 zp
- case 0x2b: break; // null
- case 0x2f: this.ZeroPageRead(); this.BBR(Chip.Bit(2)); break; // BBR2 r
-
- case 0x32: this.ZeroPageIndirectRead(); this.AndR(); break; // AND (zp)
- case 0x33: break; // null
- case 0x34: break; // BIT zp,x
- case 0x37: this.ZeroPageRead(); this.RMB(Chip.Bit(3)); break; // RMB3 zp
- case 0x3a: this.SwallowRead(); this.A = this.DEC(this.A); break; // DEC A
- case 0x3b: break; // null
- case 0x3c: break; // BIT a,x
- case 0x3f: this.ZeroPageRead(); this.BBR(Chip.Bit(3)); break; // BBR3 r
-
- case 0x42: this.SwallowFetch(); break; // NOP
- case 0x43: break; // null
- case 0x47: this.ZeroPageRead(); this.RMB(Chip.Bit(4)); break; // RMB4 zp
- case 0x4b: break; // null
- case 0x4f: this.ZeroPageRead(); this.BBR(Chip.Bit(4)); break; // BBR4 r
-
- case 0x52: this.ZeroPageIndirectRead(); this.EorR(); break; // EOR (zp)
- case 0x53: break; // null
- case 0x57: this.ZeroPageRead(); this.RMB(Chip.Bit(5)); break; // RMB5 zp
- case 0x5a: this.SwallowRead(); this.Push(this.Y); break; // PHY s
- case 0x5b: break; // null
- case 0x5c: break; // null
- case 0x5f: this.ZeroPageRead(); this.BBR(Chip.Bit(5)); break; // BBR5 r
-
- case 0x62: this.SwallowFetch(); break; // *NOP
- case 0x63: break; // null
- case 0x64: this.ZeroPageAddress(); this.MemoryWrite(0); break; // STZ zp
- case 0x67: this.ZeroPageRead(); this.RMB(Chip.Bit(6)); break; // RMB6 zp
- case 0x6b: break; // null
- case 0x6f: this.ZeroPageRead(); this.BBR(Chip.Bit(6)); break; // BBR6 r
-
- case 0x72: this.ZeroPageIndirectRead(); this.ADC(); break; // ADC (zp)
- case 0x73: break; // null
- case 0x74: this.ZeroPageXAddress(); this.MemoryWrite(0); break; // STZ zp,x
- case 0x77: this.ZeroPageRead(); this.RMB(Chip.Bit(7)); break; // RMB7 zp
- case 0x7a: this.SwallowRead(); this.SwallowPop(); this.Y = this.Through(this.Pop()); break; // PLY s
- case 0x7b: break; // null
- case 0x7c: break; // JMP (a,x)
- case 0x7f: this.ZeroPageRead(); this.BBR(Chip.Bit(7)); break; // BBR7 r
-
- case 0x80: Branch(true); break; // BRA r
- case 0x83: break; // null
- case 0x87: this.ZeroPageRead(); this.SMB(Chip.Bit(0)); break; // SMB0 zp
- case 0x89: break; // BIT # (TBC)
- case 0x8b: break; // null
- case 0x8f: this.ZeroPageRead(); this.BBS(Chip.Bit(0)); break; // BBS0 r
-
- case 0x92: ZeroPageIndirectAddress(); this.MemoryWrite(this.A); break; // STA (zp)
- case 0x93: break; // null
- case 0x97: this.ZeroPageRead(); this.SMB(Chip.Bit(1)); break; // SMB1 zp
- case 0x9b: break; // null
- case 0x9c: this.AbsoluteAddress(); this.MemoryWrite(0); break; // STZ a
- case 0x9e: this.AbsoluteXAddress(); this.MemoryWrite(0); break; // STZ a,x
- case 0x9f: this.ZeroPageRead(); this.BBS(Chip.Bit(1)); break; // BBS1 r
-
- case 0xa3: break; // null
- case 0xa7: this.ZeroPageRead(); this.SMB(Chip.Bit(2)); break; // SMB2 zp
- case 0xab: break; // null
- case 0xaf: this.ZeroPageRead(); this.BBS(Chip.Bit(2)); break; // BBS2 r
-
- case 0xb2: ZeroPageIndirectRead(); this.A = this.Through(); break; // LDA (zp)
- case 0xb3: break; // null
- case 0xb7: this.ZeroPageRead(); this.SMB(Chip.Bit(3)); break; // SMB3 zp
- case 0xbb: break; // null
- case 0xbf: this.ZeroPageRead(); this.BBS(Chip.Bit(3)); break; // BBS3 r
-
- case 0xc3: break; // null
- case 0xc7: this.ZeroPageRead(); this.SMB(Chip.Bit(4)); break; // SMB4 zp
- case 0xcb: this.SwallowRead(); this.Waiting = true; break; // WAI i
- case 0xcf: this.ZeroPageRead(); this.BBS(Chip.Bit(4)); break; // BBS4 r
-
- case 0xd2: this.ZeroPageIndirectRead(); this.CMP(this.A); break; // CMP (zp)
- case 0xd3: break; // null
- case 0xd7: this.ZeroPageRead(); this.SMB(Chip.Bit(5)); break; // SMB5 zp
- case 0xda: this.SwallowRead(); this.Push(this.X); break; // PHX s
- case 0xdb: this.SwallowRead(); this.Stopped = true; break; // STP i
- case 0xdc: this.SwallowRead(); break; // null
- case 0xdf: this.ZeroPageRead(); this.BBS(Chip.Bit(5)); break; // BBS5 r
-
- case 0xe3: break; // null
- case 0xe7: this.ZeroPageRead(); this.SMB(Chip.Bit(6)); break; // SMB6 zp
- case 0xeb: break; // null
- case 0xef: this.ZeroPageRead(); this.BBS(Chip.Bit(6)); break; // BBS6 r
-
- case 0xf2: this.ZeroPageIndirectRead(); this.SBC(); break; // SBC (zp)
- case 0xf3: break; // null
- case 0xf7: this.ZeroPageRead(); this.SMB(Chip.Bit(7)); break; // SMB7 zp
- case 0xfa: this.SwallowRead(); this.SwallowPop(); this.X = this.Through(this.Pop()); break; // PLX s
- case 0xfb: break; // null
- case 0xfc: break; // null
- case 0xff: this.ZeroPageRead(); this.BBS(Chip.Bit(7)); break; // BBS7 r
- }
-
- return cycles != this.Cycles;
- }
-
- public override void PoweredStep()
- {
- if (!this.Paused)
- {
- base.PoweredStep();
- }
- }
-
- protected override void OnLoweredRESET()
- {
- base.OnLoweredRESET();
- this.Stopped = this.Waiting = false;
- }
-
- protected override void OnLoweredINT()
- {
- base.OnLoweredINT();
- this.Waiting = false;
- }
-
- protected override void OnLoweredNMI()
- {
- base.OnLoweredNMI();
- this.Waiting = false;
- }
-
- #endregion
-
- #region Bus/Memory Access
-
- protected override void ModifyWrite(byte data)
- {
- // The read will have already taken place...
- this.MemoryRead(); // Modify cycle (Change from M6502)
- this.MemoryWrite(data); // Write cycle
- }
-
- #endregion
-
- #region Addressing modes
-
- #region Address page fixup
-
- private readonly Register16 lastFetchAddress = new();
-
- protected override byte FetchByte()
- {
- this.lastFetchAddress.Assign(this.PC);
- return base.FetchByte();
- }
-
- protected override void Fixup()
- {
- var fixingLow = this.Bus.Address.Low;
- this.MemoryRead(this.lastFetchAddress);
- this.Bus.Address.Assign(fixingLow, this.FixedPage);
- }
-
- protected override void FixupBranch(sbyte relative)
- {
- this.NoteFixedAddress(this.PC.Word + relative);
- this.lastFetchAddress.Assign(this.Bus.Address); // Effectively negate the use of "lastFetchAddress" for branch fixup usages
- this.MaybeFixup();
- }
-
- #endregion
-
- #region Address resolution
-
- protected void GetAddress()
- {
- this.GetWordPaged();
-
- if (this.Bus.Address.Low == 0)
- {
- this.Bus.Address.High++;
- }
-
- this.Bus.Address.Assign(this.Intermediate.Low, this.MemoryRead());
- }
-
- protected override void IndirectAddress()
- {
- this.AbsoluteAddress();
- this.GetAddress();
- }
-
- #endregion
-
- #region Address and read
-
- private void ZeroPageIndirectRead()
- {
- this.ZeroPageIndirectAddress();
- this.MemoryRead();
- }
-
- #endregion
-
- #endregion
-
- private void RMB(byte flag)
- {
- this.MemoryRead();
- this.Bus.Data &= (byte)~flag;
- this.MemoryWrite();
- }
-
- private void SMB(byte flag)
- {
- this.MemoryRead();
- this.Bus.Data |= flag;
- this.MemoryWrite();
- }
-
- private void BBS(byte flag)
- {
- this.MemoryRead();
- this.Branch(this.Bus.Data & flag);
- }
-
- private void BBR(byte flag)
- {
- this.MemoryRead();
- this.BranchNot(this.Bus.Data & flag);
- }
-
- private void TSB()
- {
- this.AdjustZero((byte)(this.A & this.Bus.Data));
- this.ModifyWrite((byte)(this.A | this.Bus.Data));
- }
-
- private void TRB()
- {
- this.AdjustZero((byte)(this.A & this.Bus.Data));
- this.ModifyWrite((byte)(~this.A & this.Bus.Data));
- }
- }
-}
\ No newline at end of file
diff --git a/M6502/WDC65C02.cs b/M6502/WDC65C02.cs
new file mode 100644
index 0000000..4563732
--- /dev/null
+++ b/M6502/WDC65C02.cs
@@ -0,0 +1,305 @@
+//
+// Copyright (c) Adrian Conlon. All rights reserved.
+//
+
+namespace M6502
+{
+ using EightBit;
+
+ public class WDC65C02(Bus bus) : Core(bus)
+ {
+ private bool _stopped;
+ private bool _waiting;
+
+ private bool Stopped
+ {
+ get => _stopped; set => _stopped = value;
+ }
+
+ private bool Waiting
+ {
+ get => _waiting; set => _waiting = value;
+ }
+
+ private bool Paused => Stopped || Waiting;
+
+ #region Interrupts
+
+ protected override void Interrupt(byte vector, InterruptSource source, InterruptType type)
+ {
+ base.Interrupt(vector, source, type);
+ ResetFlag(StatusBits.DF); // Disable decimal mode (Change from MOS6502)
+ }
+
+ #endregion
+
+ #region Core instruction dispatching
+
+ protected override bool MaybeExecute()
+ {
+ if (base.MaybeExecute())
+ {
+ return true;
+ }
+
+ var cycles = Cycles;
+ switch (OpCode)
+ {
+ case 0x02: SwallowFetch(); break; // NOP
+ case 0x03: break; // null
+ case 0x04: ZeroPageRead(); TSB(); break; // TSB zp
+ case 0x07: ZeroPageRead(); RMB(Bit(0)); break; // RMB0 zp
+ case 0x0b: break; // null
+ case 0x0c: AbsoluteRead(); TSB(); break; // TSB a
+ case 0x0f: ZeroPageRead(); BBR(Bit(0)); break; // BBR0 r
+
+ case 0x12: ZeroPageIndirectAddress(); OrR(); break; // ORA (zp),y
+ case 0x13: break; // null
+ case 0x14: ZeroPageRead(); TRB(); break; // TRB zp
+ case 0x17: ZeroPageRead(); RMB(Bit(1)); break; // RMB1 zp
+ case 0x1a: SwallowRead(); A = INC(A); break; // INC A
+ case 0x1b: break; // null
+ case 0x1c: AbsoluteRead(); TRB(); break; // TRB a
+ case 0x1f: ZeroPageRead(); BBR(Bit(1)); break; // BBR1 r
+
+ case 0x22: SwallowFetch(); break; // NOP
+ case 0x23: break; // null
+ case 0x27: ZeroPageRead(); RMB(Bit(2)); break; // RMB2 zp
+ case 0x2b: break; // null
+ case 0x2f: ZeroPageRead(); BBR(Bit(2)); break; // BBR2 r
+
+ case 0x32: ZeroPageIndirectRead(); AndR(); break; // AND (zp)
+ case 0x33: break; // null
+ case 0x34: break; // BIT zp,x
+ case 0x37: ZeroPageRead(); RMB(Bit(3)); break; // RMB3 zp
+ case 0x3a: SwallowRead(); A = DEC(A); break; // DEC A
+ case 0x3b: break; // null
+ case 0x3c: break; // BIT a,x
+ case 0x3f: ZeroPageRead(); BBR(Bit(3)); break; // BBR3 r
+
+ case 0x42: SwallowFetch(); break; // NOP
+ case 0x43: break; // null
+ case 0x47: ZeroPageRead(); RMB(Bit(4)); break; // RMB4 zp
+ case 0x4b: break; // null
+ case 0x4f: ZeroPageRead(); BBR(Bit(4)); break; // BBR4 r
+
+ case 0x52: ZeroPageIndirectRead(); EorR(); break; // EOR (zp)
+ case 0x53: break; // null
+ case 0x57: ZeroPageRead(); RMB(Bit(5)); break; // RMB5 zp
+ case 0x5a: SwallowRead(); Push(Y); break; // PHY s
+ case 0x5b: break; // null
+ case 0x5c: break; // null
+ case 0x5f: ZeroPageRead(); BBR(Bit(5)); break; // BBR5 r
+
+ case 0x62: SwallowFetch(); break; // *NOP
+ case 0x63: break; // null
+ case 0x64: ZeroPageAddress(); MemoryWrite(0); break; // STZ zp
+ case 0x67: ZeroPageRead(); RMB(Bit(6)); break; // RMB6 zp
+ case 0x6b: break; // null
+ case 0x6f: ZeroPageRead(); BBR(Bit(6)); break; // BBR6 r
+
+ case 0x72: ZeroPageIndirectRead(); ADC(); break; // ADC (zp)
+ case 0x73: break; // null
+ case 0x74: ZeroPageXAddress(); MemoryWrite(0); break; // STZ zp,x
+ case 0x77: ZeroPageRead(); RMB(Bit(7)); break; // RMB7 zp
+ case 0x7a: SwallowRead(); SwallowPop(); Y = Through(Pop()); break; // PLY s
+ case 0x7b: break; // null
+ case 0x7c: break; // JMP (a,x)
+ case 0x7f: ZeroPageRead(); BBR(Bit(7)); break; // BBR7 r
+
+ case 0x80: Branch(true); break; // BRA r
+ case 0x83: break; // null
+ case 0x87: ZeroPageRead(); SMB(Bit(0)); break; // SMB0 zp
+ case 0x89: break; // BIT # (TBC)
+ case 0x8b: break; // null
+ case 0x8f: ZeroPageRead(); BBS(Bit(0)); break; // BBS0 r
+
+ case 0x92: ZeroPageIndirectAddress(); MemoryWrite(A); break; // STA (zp)
+ case 0x93: break; // null
+ case 0x97: ZeroPageRead(); SMB(Bit(1)); break; // SMB1 zp
+ case 0x9b: break; // null
+ case 0x9c: AbsoluteAddress(); MemoryWrite(0); break; // STZ a
+ case 0x9e: AbsoluteXAddress(); MemoryWrite(0); break; // STZ a,x
+ case 0x9f: ZeroPageRead(); BBS(Bit(1)); break; // BBS1 r
+
+ case 0xa3: break; // null
+ case 0xa7: ZeroPageRead(); SMB(Bit(2)); break; // SMB2 zp
+ case 0xab: break; // null
+ case 0xaf: ZeroPageRead(); BBS(Bit(2)); break; // BBS2 r
+
+ case 0xb2: ZeroPageIndirectRead(); A = Through(); break; // LDA (zp)
+ case 0xb3: break; // null
+ case 0xb7: ZeroPageRead(); SMB(Bit(3)); break; // SMB3 zp
+ case 0xbb: break; // null
+ case 0xbf: ZeroPageRead(); BBS(Bit(3)); break; // BBS3 r
+
+ case 0xc3: break; // null
+ case 0xc7: ZeroPageRead(); SMB(Bit(4)); break; // SMB4 zp
+ case 0xcb: SwallowRead(); Waiting = true; break; // WAI i
+ case 0xcf: ZeroPageRead(); BBS(Bit(4)); break; // BBS4 r
+
+ case 0xd2: ZeroPageIndirectRead(); CMP(A); break; // CMP (zp)
+ case 0xd3: break; // null
+ case 0xd7: ZeroPageRead(); SMB(Bit(5)); break; // SMB5 zp
+ case 0xda: SwallowRead(); Push(X); break; // PHX s
+ case 0xdb: SwallowRead(); Stopped = true; break; // STP i
+ case 0xdc: SwallowRead(); break; // null
+ case 0xdf: ZeroPageRead(); BBS(Bit(5)); break; // BBS5 r
+
+ case 0xe3: break; // null
+ case 0xe7: ZeroPageRead(); SMB(Bit(6)); break; // SMB6 zp
+ case 0xeb: break; // null
+ case 0xef: ZeroPageRead(); BBS(Bit(6)); break; // BBS6 r
+
+ case 0xf2: ZeroPageIndirectRead(); SBC(); break; // SBC (zp)
+ case 0xf3: break; // null
+ case 0xf7: ZeroPageRead(); SMB(Bit(7)); break; // SMB7 zp
+ case 0xfa: SwallowRead(); SwallowPop(); X = Through(Pop()); break; // PLX s
+ case 0xfb: break; // null
+ case 0xfc: break; // null
+ case 0xff: ZeroPageRead(); BBS(Bit(7)); break; // BBS7 r
+ }
+
+ return cycles != Cycles;
+ }
+
+ public override void PoweredStep()
+ {
+ if (!Paused)
+ {
+ base.PoweredStep();
+ }
+ }
+
+ protected override void OnLoweredRESET()
+ {
+ base.OnLoweredRESET();
+ Stopped = Waiting = false;
+ }
+
+ protected override void OnLoweredINT()
+ {
+ base.OnLoweredINT();
+ Waiting = false;
+ }
+
+ protected override void OnLoweredNMI()
+ {
+ base.OnLoweredNMI();
+ Waiting = false;
+ }
+
+ #endregion
+
+ #region Bus/Memory Access
+
+ protected override void ModifyWrite(byte data)
+ {
+ // The read will have already taken place...
+ MemoryRead(); // Modify cycle (Change from MOS6502)
+ MemoryWrite(data); // Write cycle
+ }
+
+ #endregion
+
+ #region Addressing modes
+
+ #region Address page fixup
+
+ private readonly Register16 lastFetchAddress = new();
+
+ protected override byte FetchByte()
+ {
+ lastFetchAddress.Assign(PC);
+ return base.FetchByte();
+ }
+
+ protected override void Fixup()
+ {
+ var fixingLow = Bus.Address.Low;
+ MemoryRead(lastFetchAddress);
+ Bus.Address.Assign(fixingLow, FixedPage);
+ }
+
+ protected override void FixupBranch(sbyte relative)
+ {
+ NoteFixedAddress(PC.Word + relative);
+ lastFetchAddress.Assign(Bus.Address); // Effectively negate the use of "lastFetchAddress" for branch fixup usages
+ MaybeFixup();
+ }
+
+ #endregion
+
+ #region Address resolution
+
+ protected void GetAddress()
+ {
+ GetWordPaged();
+
+ if (Bus.Address.Low == 0)
+ {
+ Bus.Address.High++;
+ }
+
+ Bus.Address.Assign(Intermediate.Low, MemoryRead());
+ }
+
+ protected override void IndirectAddress()
+ {
+ AbsoluteAddress();
+ GetAddress();
+ }
+
+ #endregion
+
+ #region Address and read
+
+ private void ZeroPageIndirectRead()
+ {
+ ZeroPageIndirectAddress();
+ MemoryRead();
+ }
+
+ #endregion
+
+ #endregion
+
+ private void RMB(byte flag)
+ {
+ MemoryRead();
+ Bus.Data &= (byte)~flag;
+ MemoryWrite();
+ }
+
+ private void SMB(byte flag)
+ {
+ MemoryRead();
+ Bus.Data |= flag;
+ MemoryWrite();
+ }
+
+ private void BBS(byte flag)
+ {
+ MemoryRead();
+ Branch(Bus.Data & flag);
+ }
+
+ private void BBR(byte flag)
+ {
+ MemoryRead();
+ BranchNot(Bus.Data & flag);
+ }
+
+ private void TSB()
+ {
+ AdjustZero((byte)(A & Bus.Data));
+ ModifyWrite((byte)(A | Bus.Data));
+ }
+
+ private void TRB()
+ {
+ AdjustZero((byte)(A & Bus.Data));
+ ModifyWrite((byte)(~A & Bus.Data));
+ }
+ }
+}
\ No newline at end of file