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