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