From 552de802fe6aff3771613099a39465ecd4efb2eb Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Wed, 10 Jun 2026 15:23:19 +0100 Subject: [PATCH] Performance changes (mainly for Z80) --- EightBit/Register16.cs | 54 +++++++++++++++++++++++++++++--- Z80/Z80.cs | 70 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 111 insertions(+), 13 deletions(-) diff --git a/EightBit/Register16.cs b/EightBit/Register16.cs index 0e79a14..1104f63 100644 --- a/EightBit/Register16.cs +++ b/EightBit/Register16.cs @@ -3,14 +3,28 @@ // using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace EightBit { + [StructLayout(LayoutKind.Sequential)] public sealed class Register16 { + // _low and _high must remain adjacent and in this order private byte _low; private byte _high; + // Whole overlays low/high as a single little-endian ushort. + private ref ushort Whole + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return ref Unsafe.As(ref this._low); + } + } + public Register16(byte low, byte high) { this.Low = low; @@ -47,11 +61,31 @@ namespace EightBit public ushort Joined { - get => Chip.MakeShort(this.Low, this.High); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (BitConverter.IsLittleEndian) + { + return this.Whole; + } + else + { + return Chip.MakeShort(this.Low, this.High); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] set { - this.Low = Chip.LowByte(value); - this.High = Chip.HighByte(value); + if (BitConverter.IsLittleEndian) + { + this.Whole = value; + } + else + { + this.Low = Chip.LowByte(value); + this.High = Chip.HighByte(value); + } } } @@ -87,15 +121,25 @@ namespace EightBit this.High = high; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Assign(Register16 from) { Debug.Assert(from is not null, "from cannot be null"); - this.Low = from.Low; - this.High = from.High; + if (BitConverter.IsLittleEndian) + { + this.Whole = from.Whole; + } + else + { + this.Low = from.Low; + this.High = from.High; + } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Increment() => ++this.Joined; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Decrement() => --this.Joined; } } diff --git a/Z80/Z80.cs b/Z80/Z80.cs index d30c639..4535026 100644 --- a/Z80/Z80.cs +++ b/Z80/Z80.cs @@ -13,6 +13,8 @@ namespace Z80 : base(bus) { this._ports = ports; + this.UpdateRegisterBank(); + this.UpdateAFBank(); this.RaisedPOWER += this.Z80_RaisedPOWER; this.LoweredRESET += this.Z80_LoweredRESET; this.LoweredNMI += this.Z80_LoweredNMI; @@ -34,6 +36,11 @@ namespace Z80 [new Register16(), new Register16(), new Register16()] ]; + private Register16? _af; + private Register16? _bc; + private Register16? _de; + private Register16? _hl; + private RefreshRegister _refresh = new(0x7f); private bool _prefixCB; @@ -59,15 +66,41 @@ namespace Z80 private byte _modifiedF; // In-flight status register. Used to build "Q" - public override Register16 AF => this._accumulatorFlags[this._accumulatorFlagsSet]; + public override Register16 AF + { + get + { + Debug.Assert(this._af is not null); + return this._af; + } + } - private Register16[] CurrentRegisterSet => this._registers[this._registerSet]; + public override Register16 BC + { + get + { + Debug.Assert(this._bc is not null); + return this._bc; + } + } - public override Register16 BC => this.CurrentRegisterSet[(int)RegisterIndex.IndexBC]; + public override Register16 DE + { + get + { + Debug.Assert(this._de is not null); + return this._de; + } + } - public override Register16 DE => this.CurrentRegisterSet[(int)RegisterIndex.IndexDE]; - - public override Register16 HL => this.CurrentRegisterSet[(int)RegisterIndex.IndexHL]; + public override Register16 HL + { + get + { + Debug.Assert(this._hl is not null); + return this._hl; + } + } public Register16 IX { get; } = new(0xffff); @@ -93,9 +126,30 @@ namespace Z80 this.Bus.Address.Assign(this.MEMPTR); } - public void Exx() => this._registerSet ^= 1; + public void Exx() + { + this._registerSet ^= 1; + this.UpdateRegisterBank(); + } - public void ExxAF() => this._accumulatorFlagsSet ^= 1; + public void ExxAF() + { + this._accumulatorFlagsSet ^= 1; + this.UpdateAFBank(); + } + + private void UpdateRegisterBank() + { + var bank = this._registers[this._registerSet]; + this._bc = bank[(int)RegisterIndex.IndexBC]; + this._de = bank[(int)RegisterIndex.IndexDE]; + this._hl = bank[(int)RegisterIndex.IndexHL]; + } + + private void UpdateAFBank() + { + this._af = this._accumulatorFlags[this._accumulatorFlagsSet]; + } public override void Execute() {