From 6e1fc14530f841485fd202e8ef2e341c7d2bf4b0 Mon Sep 17 00:00:00 2001 From: Adrian Conlon <98398945+AdrianConlon@users.noreply.github.com> Date: Mon, 12 May 2025 19:15:34 +0100 Subject: [PATCH] Start tidying up 6809 implementation/testst --- M6502/M6502.csproj | 2 +- MC6809/MC6809.cs | 1596 +++++++++++++++++++++++--------------------- 2 files changed, 837 insertions(+), 761 deletions(-) diff --git a/M6502/M6502.csproj b/M6502/M6502.csproj index 48cceec..6368857 100644 --- a/M6502/M6502.csproj +++ b/M6502/M6502.csproj @@ -8,7 +8,7 @@ False True latest-all - True + False diff --git a/MC6809/MC6809.cs b/MC6809/MC6809.cs index e0a4b34..bb0394b 100644 --- a/MC6809/MC6809.cs +++ b/MC6809/MC6809.cs @@ -3,8 +3,6 @@ // namespace EightBit { - using System; - // Uses some information from: // http://www.cpu-world.com/Arch/6809.html // http://atjs.mbnet.fi/mc6809/Information/6809.htm @@ -24,26 +22,19 @@ namespace EightBit public sealed class MC6809 : BigEndianProcessor { - private const byte RESETvector = 0xfe; // RESET vector - private const byte NMIvector = 0xfc; // NMI vector - private const byte SWIvector = 0xfa; // SWI vector - private const byte IRQvector = 0xf8; // IRQ vector - private const byte FIRQvector = 0xf6; // FIRQ vector - private const byte SWI2vector = 0xf4; // SWI2 vector - private const byte SWI3vector = 0xf2; // SWI3 vector + private const byte RESET_vector = 0xfe; // RESET vector + private const byte NMI_vector = 0xfc; // NMI vector + private const byte SWI_vector = 0xfa; // SWI vector + private const byte IRQ_vector = 0xf8; // IRQ vector + private const byte FIRQ_vector = 0xf6; // FIRQ vector + private const byte SWI2_vector = 0xf4; // SWI2 vector + private const byte SWI3_vector = 0xf2; // SWI3 vector - private byte cc = 0; - private byte dp = 0; + private byte cc; + private byte dp; - private PinLevel nmiLine = PinLevel.Low; - private PinLevel firqLine = PinLevel.Low; - private PinLevel haltLine = PinLevel.Low; - private PinLevel baLine = PinLevel.Low; - private PinLevel bsLine = PinLevel.Low; - private PinLevel rwLine = PinLevel.Low; - - private bool prefix10 = false; - private bool prefix11 = false; + private bool prefix10; + private bool prefix11; public MC6809(Bus bus) : base(bus) @@ -51,85 +42,316 @@ namespace EightBit this.RaisedPOWER += this.MC6809_RaisedPOWER; } - public event EventHandler RaisingNMI; + private void MC6809_RaisedPOWER(object? sender, EventArgs e) + { + this.LowerBA(); + this.LowerBS(); + this.LowerRW(); + } - public event EventHandler RaisedNMI; + #region Pin controls - public event EventHandler LoweringNMI; + #region NMI pin - public event EventHandler LoweredNMI; + private PinLevel nmiLine = PinLevel.Low; - public event EventHandler RaisingFIRQ; + public ref PinLevel NMI => ref this.nmiLine; - public event EventHandler RaisedFIRQ; + public event EventHandler? RaisingNMI; - public event EventHandler LoweringFIRQ; + public event EventHandler? RaisedNMI; - public event EventHandler LoweredFIRQ; + public event EventHandler? LoweringNMI; - public event EventHandler RaisingHALT; + public event EventHandler? LoweredNMI; - public event EventHandler RaisedHALT; + private void OnRaisingNMI() => this.RaisingNMI?.Invoke(this, EventArgs.Empty); - public event EventHandler LoweringHALT; + private void OnRaisedNMI() => this.RaisedNMI?.Invoke(this, EventArgs.Empty); - public event EventHandler LoweredHALT; + private void OnLoweringNMI() => this.LoweringNMI?.Invoke(this, EventArgs.Empty); - public event EventHandler RaisingBA; + private void OnLoweredNMI() => this.LoweredNMI?.Invoke(this, EventArgs.Empty); - public event EventHandler RaisedBA; + public void RaiseNMI() + { + if (this.NMI.Lowered()) + { + this.OnRaisingNMI(); + this.NMI.Raise(); + this.OnRaisedNMI(); + } + } - public event EventHandler LoweringBA; + public void LowerNMI() + { + if (this.NMI.Raised()) + { + this.OnLoweringNMI(); + this.NMI.Lower(); + this.OnLoweredNMI(); + } + } - public event EventHandler LoweredBA; + #endregion - public event EventHandler RaisingBS; + #region FIRQ pin - public event EventHandler RaisedBS; + private PinLevel firqLine = PinLevel.Low; - public event EventHandler LoweringBS; + public ref PinLevel FIRQ => ref this.firqLine; - public event EventHandler LoweredBS; + public event EventHandler? RaisingFIRQ; - public event EventHandler RaisingRW; + public event EventHandler? RaisedFIRQ; - public event EventHandler RaisedRW; + public event EventHandler? LoweringFIRQ; - public event EventHandler LoweringRW; + public event EventHandler? LoweredFIRQ; - public event EventHandler LoweredRW; + private void OnRaisingFIRQ() => this.RaisingFIRQ?.Invoke(this, EventArgs.Empty); - public Register16 D { get; } = new Register16(); + private void OnRaisedFIRQ() => this.RaisedFIRQ?.Invoke(this, EventArgs.Empty); + + private void OnLoweringFIRQ() => this.LoweringFIRQ?.Invoke(this, EventArgs.Empty); + + private void OnLoweredFIRQ() => this.LoweredFIRQ?.Invoke(this, EventArgs.Empty); + + public void RaiseFIRQ() + { + if (this.FIRQ.Lowered()) + { + this.OnRaisingFIRQ(); + this.FIRQ.Raise(); + this.OnRaisedFIRQ(); + } + } + + public void LowerFIRQ() + { + if (this.FIRQ.Raised()) + { + this.OnLoweringFIRQ(); + this.FIRQ.Lower(); + this.OnLoweredFIRQ(); + } + } + + #endregion + + #region HALT pin + + private PinLevel haltLine = PinLevel.Low; + + public ref PinLevel HALT => ref this.haltLine; + + public bool Halted => this.HALT.Lowered(); + + public event EventHandler? RaisingHALT; + + public event EventHandler? RaisedHALT; + + public event EventHandler? LoweringHALT; + + public event EventHandler? LoweredHALT; + + private void OnRaisingHALT() => this.RaisingHALT?.Invoke(this, EventArgs.Empty); + + private void OnRaisedHALT() => this.RaisedHALT?.Invoke(this, EventArgs.Empty); + + private void OnLoweringHALT() => this.LoweringHALT?.Invoke(this, EventArgs.Empty); + + private void OnLoweredHALT() => this.LoweredHALT?.Invoke(this, EventArgs.Empty); + + public void RaiseHALT() + { + if (this.HALT.Lowered()) + { + this.OnRaisingHALT(); + this.HALT.Raise(); + this.OnRaisedHALT(); + } + } + + public void LowerHALT() + { + if (this.HALT.Raised()) + { + this.OnLoweringHALT(); + this.HALT.Lower(); + this.OnLoweredHALT(); + } + } + + public void Halt() + { + this.LowerHALT(); + } + + public void Proceed() + { + this.RaiseHALT(); + } + + #endregion + + #region BA pin + + private PinLevel baLine = PinLevel.Low; + + public ref PinLevel BA => ref this.baLine; + + private PinLevel bsLine = PinLevel.Low; + + public event EventHandler? RaisingBA; + + public event EventHandler? RaisedBA; + + public event EventHandler? LoweringBA; + + public event EventHandler? LoweredBA; + + private void OnRaisingBA() => this.RaisingBA?.Invoke(this, EventArgs.Empty); + + private void OnRaisedBA() => this.RaisedBA?.Invoke(this, EventArgs.Empty); + + private void OnLoweringBA() => this.LoweringBA?.Invoke(this, EventArgs.Empty); + + private void OnLoweredBA() => this.LoweredBA?.Invoke(this, EventArgs.Empty); + + public void RaiseBA() + { + if (this.BA.Lowered()) + { + this.OnRaisingBA(); + this.BA.Raise(); + this.OnRaisedBA(); + } + } + + public void LowerBA() + { + if (this.BA.Raised()) + { + this.OnLoweringBA(); + this.BA.Lower(); + this.OnLoweredBA(); + } + } + + #endregion + + #region BS pin + + public ref PinLevel BS => ref this.bsLine; + + public event EventHandler? RaisingBS; + + public event EventHandler? RaisedBS; + + public event EventHandler? LoweringBS; + + public event EventHandler? LoweredBS; + + private void OnRaisingBS() => this.RaisingBS?.Invoke(this, EventArgs.Empty); + + private void OnRaisedBS() => this.RaisedBS?.Invoke(this, EventArgs.Empty); + + private void OnLoweringBS() => this.LoweringBS?.Invoke(this, EventArgs.Empty); + + private void OnLoweredBS() => this.LoweredBS?.Invoke(this, EventArgs.Empty); + + public void RaiseBS() + { + if (this.BS.Lowered()) + { + this.OnRaisingBS(); + this.BS.Raise(); + this.OnRaisedBS(); + } + } + + public void LowerBS() + { + if (this.BS.Raised()) + { + this.OnLoweringBS(); + this.BS.Lower(); + this.OnLoweredBS(); + } + } + + #endregion + + #region RW pin + + private PinLevel rwLine = PinLevel.Low; + + public ref PinLevel RW => ref this.rwLine; + + public event EventHandler? RaisingRW; + + public event EventHandler? RaisedRW; + + public event EventHandler? LoweringRW; + + public event EventHandler? LoweredRW; + + private void OnRaisingRW() => this.RaisingRW?.Invoke(this, EventArgs.Empty); + + private void OnRaisedRW() => this.RaisedRW?.Invoke(this, EventArgs.Empty); + + private void OnLoweringRW() => this.LoweringRW?.Invoke(this, EventArgs.Empty); + + private void OnLoweredRW() => this.LoweredRW?.Invoke(this, EventArgs.Empty); + + public void RaiseRW() + { + if (this.RW.Lowered()) + { + this.OnRaisingRW(); + this.RW.Raise(); + this.OnRaisedRW(); + } + } + + public void LowerRW() + { + if (this.RW.Raised()) + { + this.OnLoweringRW(); + this.RW.Lower(); + this.OnLoweredRW(); + } + } + + #endregion + + #endregion + + #region Registers + + public Register16 D { get; } = new(); public ref byte A => ref this.D.High; public ref byte B => ref this.D.Low; - public Register16 X { get; } = new Register16(); + public Register16 X { get; } = new(); - public Register16 Y { get; } = new Register16(); + public Register16 Y { get; } = new(); - public Register16 U { get; } = new Register16(); + public Register16 U { get; } = new(); - public Register16 S { get; } = new Register16(); + public Register16 S { get; } = new(); public ref byte DP => ref this.dp; public ref byte CC => ref this.cc; - public bool Halted => this.HALT.Lowered(); + #endregion - public ref PinLevel NMI => ref this.nmiLine; - - public ref PinLevel FIRQ => ref this.firqLine; - - public ref PinLevel HALT => ref this.haltLine; - - public ref PinLevel BA => ref this.baLine; - - public ref PinLevel BS => ref this.bsLine; - - public ref PinLevel RW => ref this.rwLine; + #region Status (etc.) bit twiddling public int EntireRegisterSet => this.CC & (byte)StatusBits.EF; @@ -159,235 +381,6 @@ namespace EightBit private bool GT => !this.LE; // !(Z OR (N XOR V)) - public void Halt() - { - this.LowerHALT(); - } - - public void Proceed() - { - this.RaiseHALT(); - } - - public override void PoweredStep() - { - this.prefix10 = this.prefix11 = false; - if (this.Halted) - { - this.HandleHALT(); - } - else if (this.RESET.Lowered()) - { - this.HandleRESET(); - } - else if (this.NMI.Lowered()) - { - this.HandleNMI(); - } - else if (this.FIRQ.Lowered() && (this.FastInterruptMasked == 0)) - { - this.HandleFIRQ(); - } - else if (this.INT.Lowered() && (this.InterruptMasked == 0)) - { - this.HandleINT(); - } - else - { - this.Execute(this.FetchByte()); - } - } - - public override void Execute() - { - this.LowerBA(); - this.LowerBS(); - var prefixed = this.prefix10 || this.prefix11; - var unprefixed = !prefixed; - if (unprefixed) - { - this.ExecuteUnprefixed(); - } - else - { - if (this.prefix10) - { - this.Execute10(); - } - else - { - this.Execute11(); - } - } - } - - public void RaiseNMI() - { - if (this.NMI.Lowered()) - { - this.OnRaisingNMI(); - this.NMI.Raise(); - this.OnRaisedNMI(); - } - } - - public void LowerNMI() - { - if (this.NMI.Raised()) - { - this.OnLoweringNMI(); - this.NMI.Lower(); - this.OnLoweredNMI(); - } - } - - public void RaiseFIRQ() - { - if (this.FIRQ.Lowered()) - { - this.OnRaisingFIRQ(); - this.FIRQ.Raise(); - this.OnRaisedFIRQ(); - } - } - - public void LowerFIRQ() - { - if (this.FIRQ.Raised()) - { - this.OnLoweringFIRQ(); - this.FIRQ.Lower(); - this.OnLoweredFIRQ(); - } - } - - public void RaiseHALT() - { - if (this.HALT.Lowered()) - { - this.OnRaisingHALT(); - this.HALT.Raise(); - this.OnRaisedHALT(); - } - } - - public void LowerHALT() - { - if (this.HALT.Raised()) - { - this.OnLoweringHALT(); - this.HALT.Lower(); - this.OnLoweredHALT(); - } - } - - public void RaiseBA() - { - if (this.BA.Lowered()) - { - this.OnRaisingBA(); - this.BA.Raise(); - this.OnRaisedBA(); - } - } - - public void LowerBA() - { - if (this.BA.Raised()) - { - this.OnLoweringBA(); - this.BA.Lower(); - this.OnLoweredBA(); - } - } - - public void RaiseBS() - { - if (this.BS.Lowered()) - { - this.OnRaisingBS(); - this.BS.Raise(); - this.OnRaisedBS(); - } - } - - public void LowerBS() - { - if (this.BS.Raised()) - { - this.OnLoweringBS(); - this.BS.Lower(); - this.OnLoweredBS(); - } - } - - public void RaiseRW() - { - if (this.RW.Lowered()) - { - this.OnRaisingRW(); - this.RW.Raise(); - this.OnRaisedRW(); - } - } - - public void LowerRW() - { - if (this.RW.Raised()) - { - this.OnLoweringRW(); - this.RW.Lower(); - this.OnLoweredRW(); - } - } - - private void MC6809_RaisedPOWER(object? sender, EventArgs e) - { - this.LowerBA(); - this.LowerBS(); - this.LowerRW(); - } - - protected override void HandleRESET() - { - base.HandleRESET(); - this.RaiseNMI(); - this.LowerBA(); - this.RaiseBS(); - this.DP = 0; - this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ - this.CC = SetBit(this.CC, StatusBits.FF); // Disable FIRQ - this.Jump(this.GetWordPaged(0xff, RESETvector)); - this.Tick(10); - } - - protected override void HandleINT() - { - base.HandleINT(); - this.LowerBA(); - this.RaiseBS(); - this.SaveEntireRegisterState(); - this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ - this.Jump(this.GetWordPaged(0xff, IRQvector)); - this.Tick(12); - } - - protected override byte Pop() => this.PopS(); - - protected override void Push(byte value) => this.PushS(value); - - protected override void BusWrite() - { - this.LowerRW(); - base.BusWrite(); - } - - protected override byte BusRead() - { - this.RaiseRW(); - return base.BusRead(); - } - 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); @@ -398,247 +391,6 @@ namespace EightBit private static byte ClearBit(byte f, StatusBits flag, int condition) => ClearBit(f, (byte)flag, condition); - private void HandleHALT() - { - this.RaiseBA(); - this.RaiseBS(); - } - - private void HandleNMI() - { - this.RaiseNMI(); - this.LowerBA(); - this.RaiseBS(); - this.SaveEntireRegisterState(); - this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ - this.CC = SetBit(this.CC, StatusBits.FF); // Disable FIRQ - this.Jump(this.GetWordPaged(0xff, NMIvector)); - this.Tick(12); - } - - private void HandleFIRQ() - { - this.RaiseFIRQ(); - this.LowerBA(); - this.RaiseBS(); - this.SavePartialRegisterState(); - this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ - this.CC = SetBit(this.CC, StatusBits.FF); // Disable FIRQ - this.Jump(this.GetWordPaged(0xff, FIRQvector)); - this.Tick(12); - } - - private void OnRaisingNMI() => this.RaisingNMI?.Invoke(this, EventArgs.Empty); - - private void OnRaisedNMI() => this.RaisedNMI?.Invoke(this, EventArgs.Empty); - - private void OnLoweringNMI() => this.LoweringNMI?.Invoke(this, EventArgs.Empty); - - private void OnLoweredNMI() => this.LoweredNMI?.Invoke(this, EventArgs.Empty); - - private void OnRaisingFIRQ() => this.RaisingFIRQ?.Invoke(this, EventArgs.Empty); - - private void OnRaisedFIRQ() => this.RaisedFIRQ?.Invoke(this, EventArgs.Empty); - - private void OnLoweringFIRQ() => this.LoweringFIRQ?.Invoke(this, EventArgs.Empty); - - private void OnLoweredFIRQ() => this.LoweredFIRQ?.Invoke(this, EventArgs.Empty); - - private void OnRaisingHALT() => this.RaisingHALT?.Invoke(this, EventArgs.Empty); - - private void OnRaisedHALT() => this.RaisedHALT?.Invoke(this, EventArgs.Empty); - - private void OnLoweringHALT() => this.LoweringHALT?.Invoke(this, EventArgs.Empty); - - private void OnLoweredHALT() => this.LoweredHALT?.Invoke(this, EventArgs.Empty); - - private void OnRaisingBA() => this.RaisingBA?.Invoke(this, EventArgs.Empty); - - private void OnRaisedBA() => this.RaisedBA?.Invoke(this, EventArgs.Empty); - - private void OnLoweringBA() => this.LoweringBA?.Invoke(this, EventArgs.Empty); - - private void OnLoweredBA() => this.LoweredBA?.Invoke(this, EventArgs.Empty); - - private void OnRaisingBS() => this.RaisingBS?.Invoke(this, EventArgs.Empty); - - private void OnRaisedBS() => this.RaisedBS?.Invoke(this, EventArgs.Empty); - - private void OnLoweringBS() => this.LoweringBS?.Invoke(this, EventArgs.Empty); - - private void OnLoweredBS() => this.LoweredBS?.Invoke(this, EventArgs.Empty); - - private void OnRaisingRW() => this.RaisingRW?.Invoke(this, EventArgs.Empty); - - private void OnRaisedRW() => this.RaisedRW?.Invoke(this, EventArgs.Empty); - - private void OnLoweringRW() => this.LoweringRW?.Invoke(this, EventArgs.Empty); - - private void OnLoweredRW() => this.LoweredRW?.Invoke(this, EventArgs.Empty); - - private void Push(Register16 stack, byte value) - { - stack.Decrement(); - this.MemoryWrite(stack, value); - } - - private void PushS(byte value) => this.Push(this.S, value); - - private void PushWord(Register16 stack, Register16 value) - { - this.Push(stack, value.Low); - this.Push(stack, value.High); - } - - private byte Pop(Register16 stack) - { - var read = this.MemoryRead(stack); - stack.Increment(); - return read; - } - - private byte PopS() => this.Pop(this.S); - - private Register16 PopWord(Register16 stack) - { - var high = this.Pop(stack); - var low = this.Pop(stack); - return new Register16(low, high); - } - - private Register16 RR(int which) - { - switch (which) - { - case 0b00: - return this.X; - case 0b01: - return this.Y; - case 0b10: - return this.U; - case 0b11: - return this.S; - default: - throw new ArgumentOutOfRangeException(nameof(which), which, "Which does not specify a valid register"); - } - } - - private Register16 Address_relative_byte() - { - var offset = (sbyte)this.FetchByte(); - return new Register16(this.PC.Word + offset); - } - - private Register16 Address_relative_word() - { - var offset = (short)this.FetchWord().Word; - return new Register16(this.PC.Word + offset); - } - - private Register16 Address_direct() => new Register16(this.FetchByte(), this.DP); - - private Register16 Address_extended() => this.FetchWord(); - - private Register16 Address_indexed() - { - var type = this.FetchByte(); - var r = this.RR((type & (byte)(Bits.Bit6 | Bits.Bit5)) >> 5); - - var address = new Register16(); - if ((type & (byte)Bits.Bit7) != 0) - { - switch (type & (byte)Mask.Four) - { - case 0b0000: // ,R+ - this.Tick(2); - address.Word = r.Word++; - break; - case 0b0001: // ,R++ - this.Tick(3); - address.Word = r.Word; - r.Word += 2; - break; - case 0b0010: // ,-R - this.Tick(2); - address.Word = --r.Word; - break; - case 0b0011: // ,--R - this.Tick(3); - r.Word -= 2; - address.Word = r.Word; - break; - case 0b0100: // ,R - address.Word = r.Word; - break; - case 0b0101: // B,R - this.Tick(); - address.Word = (ushort)(r.Word + (sbyte)this.B); - break; - case 0b0110: // A,R - this.Tick(); - address.Word = (ushort)(r.Word + (sbyte)this.A); - break; - case 0b1000: // n,R (eight-bit) - this.Tick(); - address.Word = (ushort)(r.Word + (sbyte)this.FetchByte()); - break; - case 0b1001: // n,R (sixteen-bit) - this.Tick(4); - address.Word = (ushort)(r.Word + (short)this.FetchWord().Word); - break; - case 0b1011: // D,R - this.Tick(4); - address.Word = (ushort)(r.Word + this.D.Word); - break; - case 0b1100: // n,PCR (eight-bit) - this.Tick(); - address.Word = this.Address_relative_byte().Word; - break; - case 0b1101: // n,PCR (sixteen-bit) - this.Tick(2); - address.Word = this.Address_relative_word().Word; - break; - case 0b1111: // [n] - this.Tick(2); - address.Word = this.Address_extended().Word; - break; - default: - throw new InvalidOperationException("Invalid index type"); - } - - var indirect = type & (byte)Bits.Bit4; - if (indirect != 0) - { - this.Tick(3); - address.Word = this.GetWord(address).Word; - } - } - else - { - // EA = ,R + 5-bit offset - this.Tick(); - address.Word = new Register16(r.Word + SignExtend(5, (byte)(type & (byte)Mask.Five))).Word; - } - - return address; - } - - private byte AM_immediate_byte() => this.FetchByte(); - - private byte AM_direct_byte() => this.MemoryRead(this.Address_direct()); - - private byte AM_indexed_byte() => this.MemoryRead(this.Address_indexed()); - - private byte AM_extended_byte() => this.MemoryRead(this.Address_extended()); - - private Register16 AM_immediate_word() => this.FetchWord(); - - private Register16 AM_direct_word() => this.GetWord(this.Address_direct()); - - private Register16 AM_indexed_word() => this.GetWord(this.Address_indexed()); - - private Register16 AM_extended_word() => this.GetWord(this.Address_extended()); - private byte AdjustZero(byte datum) => ClearBit(this.CC, StatusBits.ZF, datum); private byte AdjustZero(ushort datum) => ClearBit(this.CC, StatusBits.ZF, datum); @@ -696,8 +448,8 @@ namespace EightBit private byte AdjustAddition(ushort before, ushort data, uint after) { - var result = new Register16(after & (uint)Mask.Sixteen); - this.CC = this.AdjustNZ(result); + this.Intermediate.Word = (ushort)after; + this.CC = this.AdjustNZ(this.Intermediate.Word); this.CC = this.AdjustCarry(after); return this.AdjustOverflow(before, data, after); } @@ -714,14 +466,272 @@ namespace EightBit private byte AdjustSubtraction(ushort before, ushort data, uint after) { - var result = new Register16(after & (uint)Mask.Sixteen); - this.CC = this.AdjustNZ(result); + this.Intermediate.Word = (ushort)after; + this.CC = this.AdjustNZ(this.Intermediate.Word); this.CC = this.AdjustCarry(after); return this.AdjustOverflow(before, data, after); } private byte AdjustSubtraction(Register16 before, Register16 data, uint after) => this.AdjustSubtraction(before.Word, data.Word, after); + #endregion + + #region Interrupt etc. handlers + + protected override void HandleRESET() + { + base.HandleRESET(); + this.RaiseNMI(); + this.LowerBA(); + this.RaiseBS(); + this.DP = 0; + this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ + this.CC = SetBit(this.CC, StatusBits.FF); // Disable FIRQ + this.Jump(this.GetWordPaged(0xff, RESET_vector)); + this.Tick(10); + } + + protected override void HandleINT() + { + base.HandleINT(); + this.LowerBA(); + this.RaiseBS(); + this.SaveEntireRegisterState(); + this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ + this.Jump(this.GetWordPaged(0xff, IRQ_vector)); + this.Tick(12); + } + + private void HandleHALT() + { + this.RaiseBA(); + this.RaiseBS(); + } + + private void HandleNMI() + { + this.RaiseNMI(); + this.LowerBA(); + this.RaiseBS(); + this.SaveEntireRegisterState(); + this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ + this.CC = SetBit(this.CC, StatusBits.FF); // Disable FIRQ + this.Jump(this.GetWordPaged(0xff, NMI_vector)); + this.Tick(12); + } + + private void HandleFIRQ() + { + this.RaiseFIRQ(); + this.LowerBA(); + this.RaiseBS(); + this.SavePartialRegisterState(); + this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ + this.CC = SetBit(this.CC, StatusBits.FF); // Disable FIRQ + this.Jump(this.GetWordPaged(0xff, FIRQ_vector)); + this.Tick(12); + } + + #endregion + + #region Bus control + + protected override void BusWrite() + { + this.LowerRW(); + base.BusWrite(); + } + + protected override byte BusRead() + { + this.RaiseRW(); + return base.BusRead(); + } + + #endregion + + #region Push/Pop + + protected override byte Pop() => this.PopS(); + + protected override void Push(byte value) => this.PushS(value); + + private void Push(Register16 stack, byte value) + { + stack.Decrement(); + this.MemoryWrite(stack, value); + } + + private void PushS(byte value) => this.Push(this.S, value); + + private void PushWord(Register16 stack, Register16 value) + { + this.Push(stack, value.Low); + this.Push(stack, value.High); + } + + private byte Pop(Register16 stack) + { + var read = this.MemoryRead(stack); + stack.Increment(); + return read; + } + + private byte PopS() => this.Pop(this.S); + + private Register16 PopWord(Register16 stack) + { + this.Intermediate.High = this.Pop(stack); + this.Intermediate.Low = this.Pop(stack); + return this.Intermediate; + } + + #endregion + + #region Addressing modes + + private Register16 Address_relative_byte() + { + var offset = (sbyte)this.FetchByte(); + this.Intermediate.Word = (ushort)(this.PC.Word + offset); + return this.Intermediate; + } + + private Register16 Address_relative_word() + { + var offset = (short)this.FetchWord().Word; + this.Intermediate.Word = (ushort)(this.PC.Word + offset); + return this.Intermediate; + } + + private Register16 Address_direct() + { + this.Intermediate.Assign(this.FetchByte(), this.DP); + return this.Intermediate; + } + + private Register16 Address_extended() => this.FetchWord(); + + private Register16 RR(int which) + { + switch (which) + { + case 0b00: + return this.X; + case 0b01: + return this.Y; + case 0b10: + return this.U; + case 0b11: + return this.S; + default: + throw new ArgumentOutOfRangeException(nameof(which), which, "Which does not specify a valid register"); + } + } + + private Register16 Address_indexed() + { + var type = this.FetchByte(); + var r = this.RR((type & (byte)(Bits.Bit6 | Bits.Bit5)) >> 5); + + if ((type & (byte)Bits.Bit7) != 0) + { + switch (type & (byte)Mask.Four) + { + case 0b0000: // ,R+ + this.Tick(2); + this.Intermediate.Word = r.Word++; + break; + case 0b0001: // ,R++ + this.Tick(3); + this.Intermediate.Word = r.Word; + r.Word += 2; + break; + case 0b0010: // ,-R + this.Tick(2); + this.Intermediate.Word = --r.Word; + break; + case 0b0011: // ,--R + this.Tick(3); + r.Word -= 2; + this.Intermediate.Word = r.Word; + break; + case 0b0100: // ,R + this.Intermediate.Word = r.Word; + break; + case 0b0101: // B,R + this.Tick(); + this.Intermediate.Word = (ushort)(r.Word + (sbyte)this.B); + break; + case 0b0110: // A,R + this.Tick(); + this.Intermediate.Word = (ushort)(r.Word + (sbyte)this.A); + break; + case 0b1000: // n,R (eight-bit) + this.Tick(); + this.Intermediate.Word = (ushort)(r.Word + (sbyte)this.FetchByte()); + break; + case 0b1001: // n,R (sixteen-bit) + this.Tick(4); + this.Intermediate.Word = (ushort)(r.Word + (short)this.FetchWord().Word); + break; + case 0b1011: // D,R + this.Tick(4); + this.Intermediate.Word = (ushort)(r.Word + this.D.Word); + break; + case 0b1100: // n,PCR (eight-bit) + this.Tick(); + this.Intermediate.Word = this.Address_relative_byte().Word; + break; + case 0b1101: // n,PCR (sixteen-bit) + this.Tick(2); + this.Intermediate.Word = this.Address_relative_word().Word; + break; + case 0b1111: // [n] + this.Tick(2); + this.Intermediate.Word = this.Address_extended().Word; + break; + default: + throw new InvalidOperationException("Invalid index type"); + } + + var indirect = type & (byte)Bits.Bit4; + if (indirect != 0) + { + this.Tick(3); + this.Intermediate.Word = this.GetWord(this.Intermediate).Word; + } + } + else + { + // EA = ,R + 5-bit offset + this.Tick(); + this.Intermediate.Word = (ushort)(r.Word + SignExtend(5, (byte)(type & (byte)Mask.Five))); + } + + return this.Intermediate; + } + + private byte AM_immediate_byte() => this.FetchByte(); + + private byte AM_direct_byte() => this.MemoryRead(this.Address_direct()); + + private byte AM_indexed_byte() => this.MemoryRead(this.Address_indexed()); + + private byte AM_extended_byte() => this.MemoryRead(this.Address_extended()); + + private Register16 AM_immediate_word() => this.FetchWord(); + + private Register16 AM_direct_word() => this.GetWord(this.Address_direct()); + + private Register16 AM_indexed_word() => this.GetWord(this.Address_indexed()); + + private Register16 AM_extended_word() => this.GetWord(this.Address_extended()); + + #endregion + + #region Load/store 8 or 16-bit data + private byte Through(byte data) { this.CC = ClearBit(this.CC, StatusBits.VF); @@ -744,6 +754,10 @@ namespace EightBit private Register16 ST(Register16 data) => this.Through(data); + #endregion + + #region Branching + private bool Branch(Register16 destination, bool condition) { if (condition) @@ -764,6 +778,10 @@ namespace EightBit } } + #endregion + + #region Save/restore register state + private void SaveEntireRegisterState() { this.CC = SetBit(this.CC, StatusBits.EF); @@ -780,6 +798,116 @@ namespace EightBit private void RestoreRegisterState() => this.PUL(this.S, this.EntireRegisterSet != 0 ? (byte)Mask.Eight : (byte)0b10000001); + private void PSH(Register16 stack, byte data) + { + if ((data & (byte)Bits.Bit7) != 0) + { + this.Tick(2); + this.PushWord(stack, this.PC); + } + + if ((data & (byte)Bits.Bit6) != 0) + { + this.Tick(2); + + // Pushing to the S stack means we must be pushing U + this.PushWord(stack, object.ReferenceEquals(stack, this.S) ? this.U : this.S); + } + + if ((data & (byte)Bits.Bit5) != 0) + { + this.Tick(2); + this.PushWord(stack, this.Y); + } + + if ((data & (byte)Bits.Bit4) != 0) + { + this.Tick(2); + this.PushWord(stack, this.X); + } + + if ((data & (byte)Bits.Bit3) != 0) + { + this.Tick(); + this.Push(stack, this.DP); + } + + if ((data & (byte)Bits.Bit2) != 0) + { + this.Tick(); + this.Push(stack, this.B); + } + + if ((data & (byte)Bits.Bit1) != 0) + { + this.Tick(); + this.Push(stack, this.A); + } + + if ((data & (byte)Bits.Bit0) != 0) + { + this.Tick(); + this.Push(stack, this.CC); + } + } + + private void PUL(Register16 stack, byte data) + { + if ((data & (byte)Bits.Bit0) != 0) + { + this.Tick(); + this.CC = this.Pop(stack); + } + + if ((data & (byte)Bits.Bit1) != 0) + { + this.Tick(); + this.A = this.Pop(stack); + } + + if ((data & (byte)Bits.Bit2) != 0) + { + this.Tick(); + this.B = this.Pop(stack); + } + + if ((data & (byte)Bits.Bit3) != 0) + { + this.Tick(); + this.DP = this.Pop(stack); + } + + if ((data & (byte)Bits.Bit4) != 0) + { + this.Tick(2); + this.X.Word = this.PopWord(stack).Word; + } + + if ((data & (byte)Bits.Bit5) != 0) + { + this.Tick(2); + this.Y.Word = this.PopWord(stack).Word; + } + + if ((data & (byte)Bits.Bit6) != 0) + { + this.Tick(2); + + // Pulling from the S stack means we must be pulling U + (object.ReferenceEquals(stack, this.S) ? this.U : this.S).Word = this.PopWord(stack).Word; + } + + if ((data & (byte)Bits.Bit7) != 0) + { + this.Tick(2); + this.PC.Word = this.PopWord(stack).Word; + } + } + + #endregion + + #region 8-bit register transfers + private ref byte ReferenceTransfer8(int specifier) { switch (specifier) @@ -799,25 +927,151 @@ namespace EightBit private Register16 ReferenceTransfer16(int specifier) { - switch (specifier) + return specifier switch { - case 0b0000: - return this.D; - case 0b0001: - return this.X; - case 0b0010: - return this.Y; - case 0b0011: - return this.U; - case 0b0100: - return this.S; - case 0b0101: - return this.PC; - default: - throw new ArgumentOutOfRangeException(nameof(specifier), specifier, "Invalid specifier"); + 0b0000 => this.D, + 0b0001 => this.X, + 0b0010 => this.Y, + 0b0011 => this.U, + 0b0100 => this.S, + 0b0101 => this.PC, + _ => throw new ArgumentOutOfRangeException(nameof(specifier), specifier, "Invalid specifier"), + }; + } + + private void EXG(byte data) + { + var leftSpecifier = Chip.HighNibble(data); + var leftType = leftSpecifier & (int)Bits.Bit3; + + var rightSpecifier = Chip.LowNibble(data); + var rightType = rightSpecifier & (int)Bits.Bit3; + + if (leftType == 0) + { + var leftRegister = this.ReferenceTransfer16(leftSpecifier); + if (rightType == 0) + { + var rightRegister = this.ReferenceTransfer16(rightSpecifier); + (leftRegister.Word, rightRegister.Word) = (rightRegister.Word, leftRegister.Word); + } + else + { + ref var rightRegister = ref this.ReferenceTransfer8(rightSpecifier); + (leftRegister.Low, rightRegister) = (rightRegister, leftRegister.Low); + leftRegister.High = (byte)Mask.Eight; + } + } + else + { + ref var leftRegister = ref this.ReferenceTransfer8(leftSpecifier); + if (rightType == 0) + { + var rightRegister = this.ReferenceTransfer16(rightSpecifier); + (leftRegister, rightRegister.Low) = (rightRegister.Low, leftRegister); + rightRegister.High = (byte)Mask.Eight; + } + else + { + ref var rightRegister = ref this.ReferenceTransfer8(rightSpecifier); + (leftRegister, rightRegister) = (rightRegister, leftRegister); + } } } + private void TFR(byte data) + { + var sourceSpecifier = Chip.HighNibble(data); + var sourceType = sourceSpecifier & (int)Bits.Bit3; + + var destinationSpecifier = Chip.LowNibble(data); + var destinationType = destinationSpecifier & (int)Bits.Bit3; + + if (sourceType == 0) + { + var sourceRegister = this.ReferenceTransfer16(sourceSpecifier); + if (destinationType == 0) + { + var destinationRegister = this.ReferenceTransfer16(destinationSpecifier); + destinationRegister.Word = sourceRegister.Word; + } + else + { + ref var destinationRegister = ref this.ReferenceTransfer8(destinationSpecifier); + destinationRegister = sourceRegister.Low; + } + } + else + { + ref var sourceRegister = ref this.ReferenceTransfer8(sourceSpecifier); + if (destinationType == 0) + { + var destinationRegister = this.ReferenceTransfer16(destinationSpecifier); + destinationRegister.Low = sourceRegister; + destinationRegister.High = (byte)Mask.Eight; + } + else + { + ref var destinationRegister = ref this.ReferenceTransfer8(destinationSpecifier); + destinationRegister = sourceRegister; + } + } + } + + #endregion + + #region Instruction execution + + public override void PoweredStep() + { + this.prefix10 = this.prefix11 = false; + if (this.Halted) + { + this.HandleHALT(); + } + else if (this.RESET.Lowered()) + { + this.HandleRESET(); + } + else if (this.NMI.Lowered()) + { + this.HandleNMI(); + } + else if (this.FIRQ.Lowered() && (this.FastInterruptMasked == 0)) + { + this.HandleFIRQ(); + } + else if (this.INT.Lowered() && (this.InterruptMasked == 0)) + { + this.HandleINT(); + } + else + { + this.Execute(this.FetchByte()); + } + } + + public override void Execute() + { + this.LowerBA(); + this.LowerBS(); + if (this.prefix10) + { + this.Execute10(); + } + else if (this.prefix11) + { + this.Execute11(); + } + { + this.ExecuteUnprefixed(); + } + } + + #endregion + + #region Instruction dispatching + private void ExecuteUnprefixed() { switch (this.OpCode) @@ -1277,20 +1531,25 @@ namespace EightBit } } + #endregion + + #region Miscellaneous instruction implementations + private byte ADC(byte operand, byte data) => this.ADD(operand, data, (byte)this.Carry); private byte ADD(byte operand, byte data, byte carry = 0) { - var addition = new Register16(operand + data + carry); - this.CC = this.AdjustAddition(operand, data, addition); - return addition.Low; + this.Intermediate.Word = (ushort)(operand + data + carry); + this.CC = this.AdjustAddition(operand, data, this.Intermediate); + return this.Intermediate.Low; } private Register16 ADD(Register16 operand, Register16 data) { var addition = (uint)(operand.Word + data.Word); this.CC = this.AdjustAddition(operand, data, addition); - return new Register16(addition & (uint)Mask.Sixteen); + this.Intermediate.Word = (ushort)(addition & (uint)Mask.Sixteen); + return this.Intermediate; } private byte AndR(byte operand, byte data) => this.Through((byte)(operand & data)); @@ -1359,61 +1618,21 @@ namespace EightBit private byte DEC(byte operand) { - var subtraction = new Register16(operand - 1); - var result = subtraction.Low; + this.Intermediate.Word = (ushort)(operand - 1); + var result = this.Intermediate.Low; this.CC = this.AdjustNZ(result); - this.CC = this.AdjustOverflow(operand, 1, subtraction); + this.CC = this.AdjustOverflow(operand, 1, this.Intermediate); return result; } private byte EorR(byte operand, byte data) => this.Through((byte)(operand ^ data)); - private void EXG(byte data) - { - var leftSpecifier = Chip.HighNibble(data); - var leftType = leftSpecifier & (int)Bits.Bit3; - - var rightSpecifier = Chip.LowNibble(data); - var rightType = rightSpecifier & (int)Bits.Bit3; - - if (leftType == 0) - { - var leftRegister = this.ReferenceTransfer16(leftSpecifier); - if (rightType == 0) - { - var rightRegister = this.ReferenceTransfer16(rightSpecifier); - (leftRegister.Word, rightRegister.Word) = (rightRegister.Word, leftRegister.Word); - } - else - { - ref var rightRegister = ref this.ReferenceTransfer8(rightSpecifier); - (leftRegister.Low, rightRegister) = (rightRegister, leftRegister.Low); - leftRegister.High = (byte)Mask.Eight; - } - } - else - { - ref var leftRegister = ref this.ReferenceTransfer8(leftSpecifier); - if (rightType == 0) - { - var rightRegister = this.ReferenceTransfer16(rightSpecifier); - (leftRegister, rightRegister.Low) = (rightRegister.Low, leftRegister); - rightRegister.High =(byte)Mask.Eight; - } - else - { - ref var rightRegister = ref this.ReferenceTransfer8(rightSpecifier); - (leftRegister, rightRegister) = (rightRegister, leftRegister); - } - } - } - private byte INC(byte operand) { - var addition = new Register16(operand + 1); - var result = addition.Low; + this.Intermediate.Word = (ushort)(operand + 1); + var result = this.Intermediate.Low; this.CC = this.AdjustNZ(result); - this.CC = this.AdjustOverflow(operand, 1, addition); + this.CC = this.AdjustOverflow(operand, 1, this.Intermediate); this.CC = this.AdjustHalfCarry(operand, 1, result); return result; } @@ -1429,130 +1648,24 @@ namespace EightBit private Register16 MUL(byte first, byte second) { - var result = new Register16(first * second); - this.CC = this.AdjustZero(result); - this.CC = SetBit(this.CC, StatusBits.CF, result.Low & (byte)Bits.Bit7); - return result; + this.Intermediate.Word = (ushort)(first * second); + this.CC = this.AdjustZero(this.Intermediate); + this.CC = SetBit(this.CC, StatusBits.CF, this.Intermediate.Low & (byte)Bits.Bit7); + return this.Intermediate; } private byte NEG(byte operand) { this.CC = SetBit(this.CC, StatusBits.VF, operand == (byte)Bits.Bit7); - var result = new Register16(0 - operand); - operand = result.Low; + this.Intermediate.Word = (ushort)(0 - operand); + operand = this.Intermediate.Low; this.CC = this.AdjustNZ(operand); - this.CC = this.AdjustCarry(result); + this.CC = this.AdjustCarry(this.Intermediate); return operand; } private byte OrR(byte operand, byte data) => this.Through((byte)(operand | data)); - private void PSH(Register16 stack, byte data) - { - if ((data & (byte)Bits.Bit7) != 0) - { - this.Tick(2); - this.PushWord(stack, this.PC); - } - - if ((data & (byte)Bits.Bit6) != 0) - { - this.Tick(2); - - // Pushing to the S stack means we must be pushing U - this.PushWord(stack, object.ReferenceEquals(stack, this.S) ? this.U : this.S); - } - - if ((data & (byte)Bits.Bit5) != 0) - { - this.Tick(2); - this.PushWord(stack, this.Y); - } - - if ((data & (byte)Bits.Bit4) != 0) - { - this.Tick(2); - this.PushWord(stack, this.X); - } - - if ((data & (byte)Bits.Bit3) != 0) - { - this.Tick(); - this.Push(stack, this.DP); - } - - if ((data & (byte)Bits.Bit2) != 0) - { - this.Tick(); - this.Push(stack, this.B); - } - - if ((data & (byte)Bits.Bit1) != 0) - { - this.Tick(); - this.Push(stack, this.A); - } - - if ((data & (byte)Bits.Bit0) != 0) - { - this.Tick(); - this.Push(stack, this.CC); - } - } - - private void PUL(Register16 stack, byte data) - { - if ((data & (byte)Bits.Bit0) != 0) - { - this.Tick(); - this.CC = this.Pop(stack); - } - - if ((data & (byte)Bits.Bit1) != 0) - { - this.Tick(); - this.A = this.Pop(stack); - } - - if ((data & (byte)Bits.Bit2) != 0) - { - this.Tick(); - this.B = this.Pop(stack); - } - - if ((data & (byte)Bits.Bit3) != 0) - { - this.Tick(); - this.DP = this.Pop(stack); - } - - if ((data & (byte)Bits.Bit4) != 0) - { - this.Tick(2); - this.X.Word = this.PopWord(stack).Word; - } - - if ((data & (byte)Bits.Bit5) != 0) - { - this.Tick(2); - this.Y.Word = this.PopWord(stack).Word; - } - - if ((data & (byte)Bits.Bit6) != 0) - { - this.Tick(2); - - // Pulling from the S stack means we must be pulling U - (object.ReferenceEquals(stack, this.S) ? this.U : this.S).Word = this.PopWord(stack).Word; - } - - if ((data & (byte)Bits.Bit7) != 0) - { - this.Tick(2); - this.PC.Word = this.PopWord(stack).Word; - } - } - private byte ROL(byte operand) { var carryIn = this.Carry; @@ -1580,16 +1693,16 @@ namespace EightBit private byte SUB(byte operand, byte data, byte carry = 0) { - var subtraction = new Register16(operand - data - carry); - this.CC = this.AdjustSubtraction(operand, data, subtraction); - return subtraction.Low; + this.Intermediate.Word = (ushort)(operand - data - carry); + this.CC = this.AdjustSubtraction(operand, data, this.Intermediate); + return this.Intermediate.Low; } private Register16 SUB(Register16 operand, Register16 data) { var subtraction = (uint)(operand.Word - data.Word); this.CC = this.AdjustSubtraction(operand, data, subtraction); - return new Register16(subtraction & (uint)Mask.Sixteen); + return this.Intermediate; } private byte SEX(byte from) @@ -1603,60 +1716,23 @@ namespace EightBit this.SaveEntireRegisterState(); this.CC = SetBit(this.CC, StatusBits.IF); // Disable IRQ this.CC = SetBit(this.CC, StatusBits.FF); // Disable FIRQ - this.Jump(this.GetWordPaged(0xff, SWIvector)); + this.Jump(this.GetWordPaged(0xff, SWI_vector)); } private void SWI2() { this.SaveEntireRegisterState(); - this.Jump(this.GetWordPaged(0xff, SWI2vector)); + this.Jump(this.GetWordPaged(0xff, SWI2_vector)); } private void SWI3() { this.SaveEntireRegisterState(); - this.Jump(this.GetWordPaged(0xff, SWI3vector)); - } - - private void TFR(byte data) - { - var sourceSpecifier = Chip.HighNibble(data); - var sourceType = sourceSpecifier & (int)Bits.Bit3; - - var destinationSpecifier = Chip.LowNibble(data); - var destinationType = destinationSpecifier & (int)Bits.Bit3; - - if (sourceType == 0) - { - var sourceRegister = this.ReferenceTransfer16(sourceSpecifier); - if (destinationType == 0) - { - var destinationRegister = this.ReferenceTransfer16(destinationSpecifier); - destinationRegister.Word = sourceRegister.Word; - } - else - { - ref var destinationRegister = ref this.ReferenceTransfer8(destinationSpecifier); - destinationRegister = sourceRegister.Low; - } - } - else - { - ref var sourceRegister = ref this.ReferenceTransfer8(sourceSpecifier); - if (destinationType == 0) - { - var destinationRegister = this.ReferenceTransfer16(destinationSpecifier); - destinationRegister.Low = sourceRegister; - destinationRegister.High = (byte)Mask.Eight; - } - else - { - ref var destinationRegister = ref this.ReferenceTransfer8(destinationSpecifier); - destinationRegister = sourceRegister; - } - } + this.Jump(this.GetWordPaged(0xff, SWI3_vector)); } private void TST(byte data) => this.CMP(data, 0); + + #endregion } -} \ No newline at end of file +}