2024-07-22 11:18:36 +01:00
// <copyright file="M6502Core.cs" company="Adrian Conlon">
2024-06-29 21:20:25 +01:00
// Copyright (c) Adrian Conlon. All rights reserved.
// </copyright>
namespace EightBit
{
2024-07-22 11:18:36 +01:00
public abstract class M6502Core ( Bus bus ) : LittleEndianProcessor ( bus )
2024-06-29 21:20:25 +01:00
{
#region Instruction execution events
public event EventHandler < EventArgs > ? ExecutingInstruction ;
public event EventHandler < EventArgs > ? ExecutedInstruction ;
protected virtual void OnExecutedInstruction ( ) = > this . ExecutedInstruction ? . Invoke ( this , EventArgs . Empty ) ;
protected virtual void OnExecutingInstruction ( ) = > this . ExecutingInstruction ? . Invoke ( this , EventArgs . Empty ) ;
#endregion
#region Pin controls
#region NMI pin
public ref PinLevel NMI = > ref this . nmiLine ;
private PinLevel nmiLine = PinLevel . Low ;
public event EventHandler < EventArgs > ? RaisingNMI ;
public event EventHandler < EventArgs > ? RaisedNMI ;
public event EventHandler < EventArgs > ? LoweringNMI ;
public event EventHandler < EventArgs > ? 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 < EventArgs > ? RaisingSO ;
public event EventHandler < EventArgs > ? RaisedSO ;
public event EventHandler < EventArgs > ? LoweringSO ;
public event EventHandler < EventArgs > ? 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 < EventArgs > ? RaisingSYNC ;
public event EventHandler < EventArgs > ? RaisedSYNC ;
public event EventHandler < EventArgs > ? LoweringSYNC ;
public event EventHandler < EventArgs > ? 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 < EventArgs > ? RaisingRDY ;
public event EventHandler < EventArgs > ? RaisedRDY ;
public event EventHandler < EventArgs > ? LoweringRDY ;
public event EventHandler < EventArgs > ? 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 < EventArgs > ? RaisingRW ;
public event EventHandler < EventArgs > ? RaisedRW ;
public event EventHandler < EventArgs > ? LoweringRW ;
public event EventHandler < EventArgs > ? 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
2024-07-22 11:18:36 +01:00
protected enum InterruptSource { hardware , software } ;
2024-06-29 21:20:25 +01:00
2024-07-22 11:18:36 +01:00
protected enum InterruptType { reset , non_reset } ;
2024-06-29 21:20:25 +01:00
2024-07-22 11:18:36 +01:00
protected virtual void Interrupt ( byte vector , InterruptSource source = InterruptSource . hardware , InterruptType type = InterruptType . non_reset )
2024-06-29 21:20:25 +01:00
{
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
2024-06-30 12:30:07 +01:00
this . Jump ( this . GetWordPaged ( 0xff , vector ) ) ;
2024-06-29 21:20:25 +01:00
}
#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
2024-06-30 12:30:07 +01:00
protected void SwallowRead ( ) = > this . MemoryRead ( this . PC ) ;
2024-06-29 21:20:25 +01:00
2024-06-29 22:27:39 +01:00
protected void SwallowPop ( ) = > this . MemoryRead ( this . S , 1 ) ;
2024-06-29 21:20:25 +01:00
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 )
{
2024-07-18 11:38:02 +01:00
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 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 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 ( A ) ; break ; // STA (indexed indirect X)
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 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 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 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 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)
2024-06-29 21:20:25 +01:00
}
return cycles ! = this . Cycles ;
}
public override int Step ( )
{
this . ResetCycles ( ) ;
this . OnExecutingInstruction ( ) ;
if ( this . Powered )
{
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 ( ) ;
}
}
}
this . OnExecutedInstruction ( ) ;
return this . Cycles ;
}
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 ( ) ;
}
2024-07-22 11:18:36 +01:00
protected abstract void ModifyWrite ( byte data ) ;
2024-06-29 21:20:25 +01:00
#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 )
{
2024-07-01 23:27:35 +01:00
this . Bus . Address . Assign ( position , 1 ) ;
2024-06-29 21:20:25 +01:00
}
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 ;
public byte FixedPage
{
get = > this . fixedPage ;
2024-07-22 11:18:36 +01:00
protected set = > this . fixedPage = value ;
2024-06-29 21:20:25 +01:00
}
protected void MaybeFixup ( )
{
if ( this . Bus . Address . High ! = this . FixedPage )
{
this . Fixup ( ) ;
}
}
2024-07-22 11:18:36 +01:00
protected abstract void Fixup ( ) ;
2024-06-29 21:20:25 +01:00
2024-06-29 22:27:39 +01:00
protected void MaybeFixupRead ( )
2024-06-29 21:20:25 +01:00
{
this . MaybeFixup ( ) ;
this . MemoryRead ( ) ;
}
2024-06-29 22:27:39 +01:00
protected void FixupRead ( )
2024-06-29 21:20:25 +01:00
{
this . Fixup ( ) ;
this . MemoryRead ( ) ;
}
#endregion
#region Address resolution
2024-07-22 11:18:36 +01:00
protected void NoteFixedAddress ( int address )
2024-06-29 21:20:25 +01:00
{
this . NoteFixedAddress ( ( ushort ) address ) ;
}
2024-07-22 11:18:36 +01:00
protected void NoteFixedAddress ( ushort address )
2024-06-29 21:20:25 +01:00
{
this . Intermediate . Word = address ;
this . FixedPage = this . Intermediate . High ;
this . Bus . Address . Low = this . Intermediate . Low ;
}
2024-07-22 11:18:36 +01:00
protected void GetAddressPaged ( )
2024-07-01 23:27:35 +01:00
{
this . GetWordPaged ( ) ;
this . Bus . Address . Assign ( this . Intermediate ) ;
}
2024-06-30 12:30:07 +01:00
protected void ImmediateAddress ( )
{
2024-07-01 23:27:35 +01:00
this . Bus . Address . Assign ( this . PC ) ;
2024-06-30 12:30:07 +01:00
+ + this . PC . Word ;
}
2024-06-29 21:20:25 +01:00
2024-06-30 12:30:07 +01:00
protected void AbsoluteAddress ( ) = > this . FetchWordAddress ( ) ;
2024-06-29 21:20:25 +01:00
protected void ZeroPageAddress ( )
{
2024-07-01 23:27:35 +01:00
this . Bus . Address . Assign ( this . FetchByte ( ) , 0 ) ;
2024-06-29 21:20:25 +01:00
}
protected void ZeroPageIndirectAddress ( )
{
this . ZeroPageAddress ( ) ;
2024-07-01 23:27:35 +01:00
this . GetAddressPaged ( ) ;
2024-06-29 21:20:25 +01:00
}
2024-07-22 11:18:36 +01:00
protected abstract void IndirectAddress ( ) ;
2024-06-29 21:20:25 +01:00
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 ( ) ;
2024-07-01 23:27:35 +01:00
this . GetAddressPaged ( ) ;
2024-06-29 21:20:25 +01:00
}
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 ( ) ;
2024-06-29 22:27:39 +01:00
this . MaybeFixupRead ( ) ;
2024-06-29 21:20:25 +01:00
}
protected void AbsoluteYRead ( )
{
this . AbsoluteYAddress ( ) ;
2024-06-29 22:27:39 +01:00
this . MaybeFixupRead ( ) ;
2024-06-29 21:20:25 +01:00
}
protected void IndirectIndexedYRead ( )
{
this . IndirectIndexedYAddress ( ) ;
2024-06-29 22:27:39 +01:00
this . MaybeFixupRead ( ) ;
2024-06-29 21:20:25 +01:00
}
#endregion
#endregion
#region Branching
private void BranchNot ( int condition ) = > this . Branch ( condition = = 0 ) ;
private void Branch ( int condition ) = > this . Branch ( condition ! = 0 ) ;
private void Branch ( bool condition )
{
this . ImmediateRead ( ) ;
if ( condition )
{
var relative = ( sbyte ) this . Bus . Data ;
2024-06-29 22:27:39 +01:00
this . SwallowRead ( ) ;
2024-07-22 11:18:36 +01:00
this . FixupBranch ( relative ) ;
2024-06-30 12:30:07 +01:00
this . Jump ( this . Bus . Address ) ;
2024-06-29 21:20:25 +01:00
}
}
2024-07-22 11:18:36 +01:00
protected abstract void FixupBranch ( sbyte relative ) ;
2024-06-29 21:20:25 +01:00
#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 ) ;
private 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 ( ) ;
2024-06-29 22:27:39 +01:00
this . SwallowPop ( ) ;
2024-06-29 21:20:25 +01:00
this . PushWord ( this . PC ) ;
2024-07-04 08:49:27 +01:00
this . PC . High = this . FetchByte ( ) ;
this . PC . Low = this . Intermediate . Low ;
2024-06-29 21:20:25 +01:00
}
private void PHP ( ) = > this . Push ( SetBit ( this . P , StatusBits . BF ) ) ;
private void PLP ( )
{
2024-06-29 22:27:39 +01:00
this . SwallowPop ( ) ;
2024-06-29 21:20:25 +01:00
this . P = ClearBit ( SetBit ( this . Pop ( ) , StatusBits . RF ) , StatusBits . BF ) ;
}
private void RTI ( )
{
this . PLP ( ) ;
2024-06-29 22:27:39 +01:00
base . Return ( ) ;
2024-06-29 21:20:25 +01:00
}
2024-06-29 22:27:39 +01:00
protected override void Return ( )
2024-06-29 21:20:25 +01:00
{
2024-06-29 22:27:39 +01:00
this . SwallowPop ( ) ;
base . Return ( ) ;
2024-06-29 21:20:25 +01:00
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
}
}