From 2336222c97736b58520dec37ab3d88c50eb7f943 Mon Sep 17 00:00:00 2001 From: Adrian Conlon <98398945+AdrianConlon@users.noreply.github.com> Date: Sun, 4 May 2025 10:53:23 +0100 Subject: [PATCH] Push more core processor handling into base classes. --- EightBit/IntelProcessor.cs | 5 ++++ EightBit/Processor.cs | 9 ++++++-- Intel8080/Intel8080.cs | 18 +++++---------- LR35902/LR35902.cs | 13 +++++------ M6502/Core.cs | 44 +++++++++++++----------------------- M6502/MOS6502.cs | 20 ++++++++-------- Z80/Z80.HarteTest/Checker.cs | 9 +++++++- Z80/Z80.cs | 18 ++++----------- 8 files changed, 63 insertions(+), 73 deletions(-) diff --git a/EightBit/IntelProcessor.cs b/EightBit/IntelProcessor.cs index fbb876e..c3c2fd0 100644 --- a/EightBit/IntelProcessor.cs +++ b/EightBit/IntelProcessor.cs @@ -84,6 +84,10 @@ namespace EightBit } } + protected abstract void DisableInterrupts(); + + protected abstract void EnableInterrupts(); + protected override void IncrementPC() { if (this.HALT.Raised()) @@ -120,6 +124,7 @@ namespace EightBit protected override void HandleRESET() { base.HandleRESET(); + this.DisableInterrupts(); this.Jump(0); } diff --git a/EightBit/Processor.cs b/EightBit/Processor.cs index bfdba75..9305944 100644 --- a/EightBit/Processor.cs +++ b/EightBit/Processor.cs @@ -224,10 +224,15 @@ namespace EightBit protected virtual void DecrementPC() => --this.PC.Word; - protected virtual byte FetchByte() + protected virtual void ImmediateAddress() { this.Bus.Address.Assign(this.PC); - IncrementPC(); + this.IncrementPC(); + } + + protected virtual byte FetchByte() + { + this.ImmediateAddress(); return this.MemoryRead(); } diff --git a/Intel8080/Intel8080.cs b/Intel8080/Intel8080.cs index ec79ce9..1113996 100644 --- a/Intel8080/Intel8080.cs +++ b/Intel8080/Intel8080.cs @@ -7,17 +7,11 @@ namespace Intel8080 { using EightBit; - public class Intel8080 : IntelProcessor + public class Intel8080(Bus bus, InputOutput ports) : IntelProcessor(bus) { - public Intel8080(Bus bus, InputOutput ports) - : base(bus) - { - this.ports = ports; - } - private readonly Register16 af = new(); - private readonly InputOutput ports; + private readonly InputOutput ports = ports; private bool interruptEnable; @@ -62,6 +56,7 @@ namespace Intel8080 } else if (this.HALT.Lowered()) { + _ = this.FetchByte(); this.Execute(0); // NOP } else @@ -73,7 +68,6 @@ namespace Intel8080 protected override void HandleRESET() { base.HandleRESET(); - this.DisableInterrupts(); this.Tick(3); } @@ -172,9 +166,9 @@ namespace Intel8080 private static byte AdjustAuxiliaryCarrySub(byte input, byte before, byte value, int calculation) => ClearBit(input, StatusBits.AC, CalculateHalfCarrySub(before, value, calculation)); - private void DisableInterrupts() => this.interruptEnable = false; + protected override void DisableInterrupts() => this.interruptEnable = false; - private void EnableInterrupts() => this.interruptEnable = true; + protected override void EnableInterrupts() => this.interruptEnable = true; private byte R(int r) => r switch { @@ -489,7 +483,7 @@ namespace Intel8080 this.Tick(10); break; case 2: // JP HL - this.Jump(this.HL.Word); + this.Jump(this.HL); this.Tick(4); break; case 3: // LD SP,HL diff --git a/LR35902/LR35902.cs b/LR35902/LR35902.cs index 3de84a6..d4969fa 100644 --- a/LR35902/LR35902.cs +++ b/LR35902/LR35902.cs @@ -238,7 +238,6 @@ namespace LR35902 protected override void HandleRESET() { base.HandleRESET(); - this.DI(); this.SP.Word = (ushort)(Mask.Sixteen - 1); this.TickMachine(4); } @@ -247,7 +246,7 @@ namespace LR35902 { base.HandleINT(); this.RaiseHALT(); - this.DI(); + this.DisableInterrupts(); this.Restart(this.Bus.Data); } @@ -387,9 +386,9 @@ namespace LR35902 private static byte Set(int n, byte operand) => SetBit(operand, Bit(n)); - private void DI() => this.IME = false; + protected override void DisableInterrupts() => this.IME = false; - private void EI() => this.IME = true; + protected override void EnableInterrupts() => this.IME = true; private void Stop() => this.Stopped = true; @@ -834,10 +833,10 @@ namespace LR35902 this.Execute(this.FetchByte()); break; case 6: // DI - this.DI(); + this.DisableInterrupts(); break; case 7: // EI - this.EI(); + this.EnableInterrupts(); break; default: break; @@ -1147,7 +1146,7 @@ namespace LR35902 private void RetI() { this.Return(); - this.EI(); + this.EnableInterrupts(); } } } diff --git a/M6502/Core.cs b/M6502/Core.cs index 82370fb..c815ac3 100644 --- a/M6502/Core.cs +++ b/M6502/Core.cs @@ -330,7 +330,7 @@ namespace M6502 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 0x09: this.FetchByte(); this.OrR(); break; // ORA (immediate) case 0x0a: this.SwallowRead(); this.A = this.ASL(this.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) @@ -350,7 +350,7 @@ namespace M6502 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 0x29: this.FetchByte(); 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) @@ -371,7 +371,7 @@ namespace M6502 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 0x49: this.FetchByte(); 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) @@ -392,7 +392,7 @@ namespace M6502 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 0x69: this.FetchByte(); 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) @@ -408,7 +408,7 @@ namespace M6502 case 0x7e: this.AbsoluteXAddress(); this.FixupRead(); this.ModifyWrite(this.ROR()); break; // ROR (absolute, X) case 0x81: this.IndexedIndirectXAddress(); this.MemoryWrite(this.A); break; // STA (indexed indirect X) - case 0x82: this.ImmediateRead(); break; // *NOP (immediate) + case 0x82: this.FetchByte(); break; // *NOP (immediate) case 0x84: this.ZeroPageAddress(); this.MemoryWrite(this.Y); break; // STY (zero page) case 0x85: this.ZeroPageAddress(); this.MemoryWrite(this.A); break; // STA (zero page) case 0x86: this.ZeroPageAddress(); this.MemoryWrite(this.X); break; // STX (zero page) @@ -428,14 +428,14 @@ namespace M6502 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 0xa0: this.FetchByte(); 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 0xa2: this.FetchByte(); 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 = this.Through(this.A); break; // TAY (implied) - case 0xa9: this.ImmediateRead(); this.A = this.Through(); break; // LDA (immediate) + case 0xa9: this.FetchByte(); 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) @@ -453,14 +453,14 @@ namespace M6502 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 0xc0: this.FetchByte(); this.CMP(this.Y); break; // CPY (immediate) case 0xc1: this.IndexedIndirectXRead(); this.CMP(this.A); break; // CMP (indexed indirect X) - case 0xc2: this.ImmediateRead(); break; // *NOP (immediate) + case 0xc2: this.FetchByte(); break; // *NOP (immediate) case 0xc4: this.ZeroPageRead(); this.CMP(this.Y); break; // CPY (zero page) case 0xc5: this.ZeroPageRead(); this.CMP(this.A); break; // CMP (zero page) case 0xc6: this.ZeroPageRead(); this.ModifyWrite(this.DEC()); break; // DEC (zero page) case 0xc8: this.SwallowRead(); this.Y = this.INC(this.Y); break; // INY (implied) - case 0xc9: this.ImmediateRead(); this.CMP(this.A); break; // CMP (immediate) + case 0xc9: this.FetchByte(); 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) @@ -476,14 +476,14 @@ namespace M6502 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 0xe0: this.FetchByte(); this.CMP(this.X); break; // CPX (immediate) case 0xe1: this.IndexedIndirectXRead(); this.SBC(); break; // SBC (indexed indirect X) - case 0xe2: this.ImmediateRead(); break; // *NOP (immediate) + case 0xe2: this.FetchByte(); break; // *NOP (immediate) case 0xe4: this.ZeroPageRead(); this.CMP(this.X); break; // CPX (zero page) case 0xe5: this.ZeroPageRead(); this.SBC(); break; // SBC (zero page) case 0xe6: this.ZeroPageRead(); this.ModifyWrite(this.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 0xe9: this.FetchByte(); 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) @@ -541,7 +541,7 @@ namespace M6502 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. + // Can't use "FetchByte", since that would add an extra tick. this.ImmediateAddress(); this.OpCode = this.ReadFromBus(); @@ -661,12 +661,6 @@ namespace M6502 this.Bus.Address.Assign(this.Intermediate); } - protected void ImmediateAddress() - { - this.Bus.Address.Assign(this.PC); - this.IncrementPC(); - } - protected void AbsoluteAddress() => this.FetchWordAddress(); protected void ZeroPageAddress() => this.Bus.Address.Assign(this.FetchByte(), 0); @@ -715,12 +709,6 @@ namespace M6502 #region Address and read - protected void ImmediateRead() - { - this.ImmediateAddress(); - _ = this.MemoryRead(); - } - protected void AbsoluteRead() { this.AbsoluteAddress(); @@ -781,7 +769,7 @@ namespace M6502 protected void Branch(bool condition) { - this.ImmediateRead(); + this.FetchByte(); if (condition) { var relative = (sbyte)this.Bus.Data; diff --git a/M6502/MOS6502.cs b/M6502/MOS6502.cs index 23ccfa8..db26c92 100644 --- a/M6502/MOS6502.cs +++ b/M6502/MOS6502.cs @@ -24,7 +24,7 @@ namespace M6502 case 0x03: this.IndexedIndirectXRead(); this.SLO(); break; // *SLO (indexed indirect X) case 0x04: this.ZeroPageRead(); break; // *NOP (zero page) case 0x07: this.ZeroPageRead(); this.SLO(); break; // *SLO (zero page) - case 0x0b: this.ImmediateRead(); this.ANC(); break; // *ANC (immediate) + case 0x0b: this.FetchByte(); this.ANC(); break; // *ANC (immediate) case 0x0c: this.AbsoluteRead(); break; // *NOP (absolute) case 0x0f: this.AbsoluteRead(); this.SLO(); break; // *SLO (absolute) @@ -40,7 +40,7 @@ namespace M6502 case 0x22: this.Jam(); break; // *JAM case 0x23: this.IndexedIndirectXRead(); this.RLA(); ; break; // *RLA (indexed indirect X) case 0x27: this.ZeroPageRead(); this.RLA(); ; break; // *RLA (zero page) - case 0x2b: this.ImmediateRead(); this.ANC(); break; // *ANC (immediate) + case 0x2b: this.FetchByte(); this.ANC(); break; // *ANC (immediate) case 0x2f: this.AbsoluteRead(); this.RLA(); break; // *RLA (absolute) case 0x32: this.Jam(); break; // *JAM @@ -55,7 +55,7 @@ namespace M6502 case 0x42: this.Jam(); break; // *JAM case 0x43: this.IndexedIndirectXRead(); this.SRE(); break; // *SRE (indexed indirect X) case 0x47: this.ZeroPageRead(); this.SRE(); break; // *SRE (zero page) - case 0x4b: this.ImmediateRead(); this.ASR(); break; // *ASR (immediate) + case 0x4b: this.FetchByte(); this.ASR(); break; // *ASR (immediate) case 0x4f: this.AbsoluteRead(); this.SRE(); break; // *SRE (absolute) case 0x52: this.Jam(); break; // *JAM @@ -70,7 +70,7 @@ namespace M6502 case 0x63: this.IndexedIndirectXRead(); this.RRA(); break; // *RRA (indexed indirect X) case 0x64: this.ZeroPageRead(); break; // *NOP (zero page) case 0x67: this.ZeroPageRead(); this.RRA(); break; // *RRA (zero page) - case 0x6b: this.ImmediateRead(); this.ARR(); break; // *ARR (immediate) + case 0x6b: this.FetchByte(); this.ARR(); break; // *ARR (immediate) case 0x6f: this.AbsoluteRead(); this.RRA(); break; // *RRA (absolute) case 0x72: this.Jam(); break; // *JAM @@ -82,11 +82,11 @@ namespace M6502 case 0x7c: this.AbsoluteXAddress(); this.MaybeFixupRead(); break; // *NOP (absolute, X) case 0x7f: this.AbsoluteXAddress(); this.FixupRead(); this.RRA(); break; // *RRA (absolute, X) - case 0x80: this.ImmediateRead(); break; // *NOP (immediate) + case 0x80: this.FetchByte(); break; // *NOP (immediate) case 0x83: this.IndexedIndirectXAddress(); this.SAX(); break; // *SAX (indexed indirect X) case 0x87: this.ZeroPageAddress(); this.SAX(); break; // *SAX (zero page) - case 0x89: this.ImmediateRead(); break; // *NOP (immediate) - case 0x8b: this.ImmediateRead(); this.ANE(); break; // *ANE (immediate) + case 0x89: this.FetchByte(); break; // *NOP (immediate) + case 0x8b: this.FetchByte(); this.ANE(); break; // *ANE (immediate) case 0x8f: this.AbsoluteAddress(); this.SAX(); break; // *SAX (absolute) case 0x92: this.Jam(); break; // *JAM @@ -99,7 +99,7 @@ namespace M6502 case 0xa3: this.IndexedIndirectXRead(); this.LAX(); break; // *LAX (indexed indirect X) case 0xa7: this.ZeroPageRead(); this.LAX(); break; // *LAX (zero page) - case 0xab: this.ImmediateRead(); this.ATX(); break; // *ATX (immediate) + case 0xab: this.FetchByte(); this.ATX(); break; // *ATX (immediate) case 0xaf: this.AbsoluteRead(); this.LAX(); break; // *LAX (absolute) case 0xb2: this.Jam(); break; // *JAM @@ -110,7 +110,7 @@ namespace M6502 case 0xc3: this.IndexedIndirectXRead(); this.DCP(); break; // *DCP (indexed indirect X) case 0xc7: this.ZeroPageRead(); this.DCP(); break; // *DCP (zero page) - case 0xcb: this.ImmediateRead(); this.AXS(); break; // *AXS (immediate) + case 0xcb: this.FetchByte(); this.AXS(); break; // *AXS (immediate) case 0xcf: this.AbsoluteRead(); this.DCP(); break; // *DCP (absolute) case 0xd2: this.Jam(); break; // *JAM @@ -123,7 +123,7 @@ namespace M6502 case 0xe3: this.IndexedIndirectXRead(); this.ISB(); break; // *ISB (indexed indirect X) case 0xe7: this.ZeroPageRead(); this.ISB(); break; // *ISB (zero page) - case 0xeb: this.ImmediateRead(); this.SBC(); break; // *SBC (immediate) + case 0xeb: this.FetchByte(); this.SBC(); break; // *SBC (immediate) case 0xef: this.AbsoluteRead(); this.ISB(); break; // *ISB (absolute) case 0xf2: this.Jam(); break; // *JAM diff --git a/Z80/Z80.HarteTest/Checker.cs b/Z80/Z80.HarteTest/Checker.cs index a62b927..148f01b 100644 --- a/Z80/Z80.HarteTest/Checker.cs +++ b/Z80/Z80.HarteTest/Checker.cs @@ -228,8 +228,15 @@ var pc_good = this.Check("PC", final.PC, cpu.PC); var sp_good = this.Check("SP", final.SP, cpu.SP); - var a_good = this.Check("A", final.A, cpu.A); + //byte xyMask = 0; + //unchecked + //{ + // xyMask = (byte)~(StatusBits.XF | StatusBits.YF); + //} + //var f_good = this.Check("F", (byte)(final.F & xyMask), (byte)(cpu.F & xyMask)); var f_good = this.Check("F", final.F, cpu.F); + + var a_good = this.Check("A", final.A, cpu.A); var b_good = this.Check("B", final.B, cpu.B); var c_good = this.Check("C", final.C, cpu.C); var d_good = this.Check("D", final.D, cpu.D); diff --git a/Z80/Z80.cs b/Z80/Z80.cs index bff0088..0384c96 100644 --- a/Z80/Z80.cs +++ b/Z80/Z80.cs @@ -158,7 +158,7 @@ namespace Z80 // received from the memory is ignored and an NOP instruction is forced internally to the // CPU.The HALT acknowledge signal is active during this time indicating that the processor // is in the HALT state. - _ = this.ReadInitialOpCode(); + _ = this.FetchInitialOpCode(); this.Execute(0); // NOP handled = true; } @@ -519,7 +519,6 @@ namespace Z80 protected override void HandleRESET() { base.HandleRESET(); - this.DisableInterrupts(); this.IV = this.REFRESH = 0; this.SP.Word = this.AF.Word = (ushort)Mask.Sixteen; this.Tick(3); @@ -708,9 +707,9 @@ namespace Z80 private static byte SET(int n, byte operand) => SetBit(operand, Bit(n)); - private void DisableInterrupts() => this.IFF1 = this.IFF2 = false; + protected override void DisableInterrupts() => this.IFF1 = this.IFF2 = false; - private void EnableInterrupts() => this.IFF1 = this.IFF2 = true; + protected override void EnableInterrupts() => this.IFF1 = this.IFF2 = true; private Register16 HL2() => this._prefixDD ? this.IX : this._prefixFD ? this.IY : this.HL; @@ -1669,13 +1668,6 @@ namespace Z80 private void FetchDisplacement() => this._displacement = (sbyte)this.FetchByte(); - private byte FetchInitialOpCode() - { - var returned = this.ReadInitialOpCode(); - this.IncrementPC(); - return returned; - } - // ** From the Z80 CPU User Manual // Figure 5 depicts the timing during an M1 (op code fetch) cycle. The Program Counter is // placed on the address bus at the beginning of the M1 cycle. One half clock cycle later, the @@ -1688,10 +1680,10 @@ namespace Z80 // before the RD signal becomes inactive. Clock states T3 and T4 of a fetch cycle are used to // _refresh dynamic memories. The CPU uses this time to decode and execute the fetched // instruction so that no other concurrent operation can be performed. - private byte ReadInitialOpCode() + private byte FetchInitialOpCode() { this.LowerM1(); - var returned = this.MemoryRead(this.PC); + var returned = this.FetchByte(); this.RaiseM1(); return returned; }