From e0fffdaa5e9de241f7b7c85f49ef41e2f135676e Mon Sep 17 00:00:00 2001 From: Adrian Conlon Date: Sat, 16 May 2026 11:09:03 +0100 Subject: [PATCH] Some performance improvements. About 50Mhz for Z80 emulator. 275Mhz to 325Mhz. Interesting. --- EightBit/BigEndianProcessor.cs | 16 ++++++------ EightBit/Chip.cs | 28 +++++++++++++++++++++ EightBit/IntelProcessor.cs | 6 ++--- EightBit/LittleEndianProcessor.cs | 16 ++++++------ EightBit/Processor.cs | 4 +-- EightBit/Register16.cs | 41 +++++++++---------------------- Intel8080/Intel8080.cs | 15 +++++++---- MC6809/MC6809.cs | 6 ++++- Z80/Z80.cs | 3 ++- 9 files changed, 77 insertions(+), 58 deletions(-) diff --git a/EightBit/BigEndianProcessor.cs b/EightBit/BigEndianProcessor.cs index 837b1af..c38f9cc 100644 --- a/EightBit/BigEndianProcessor.cs +++ b/EightBit/BigEndianProcessor.cs @@ -14,21 +14,21 @@ namespace EightBit public override void PokeWord(ushort address, Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.Bus.Poke(address, value.High); this.Bus.Poke(++address, value.Low); } protected override void FetchInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.High = this.FetchByte(); into.Low = this.FetchByte(); } protected override void GetInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.High = this.MemoryRead(); this.Bus.Address.Increment(); into.Low = this.MemoryRead(); @@ -36,7 +36,7 @@ namespace EightBit protected override void GetPagedInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.High = this.MemoryRead(); ++this.Bus.Address.Low; into.Low = this.MemoryRead(); @@ -44,21 +44,21 @@ namespace EightBit protected override void PopInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.High = this.Pop(); into.Low = this.Pop(); } protected override void PushWord(Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.Push(value.Low); this.Push(value.High); } protected override void SetWord(Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.MemoryWrite(value.High); this.Bus.Address.Increment(); this.MemoryWrite(value.Low); @@ -66,7 +66,7 @@ namespace EightBit protected override void SetPaged(Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.MemoryWrite(value.High); ++this.Bus.Address.Low; this.MemoryWrite(value.Low); diff --git a/EightBit/Chip.cs b/EightBit/Chip.cs index 11df4e6..9039f27 100644 --- a/EightBit/Chip.cs +++ b/EightBit/Chip.cs @@ -4,58 +4,86 @@ namespace EightBit { + using System.Runtime.CompilerServices; + public class Chip : Device { + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Bit(int which) => (byte)(1 << which); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte Bit(byte which) => Bit((int)which); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte SetBit(byte input, byte which) => (byte)(input | which); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte SetBit(byte input, byte which, int condition) => SetBit(input, which, condition != 0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte SetBit(byte input, byte which, bool condition) => condition ? SetBit(input, which) : ClearBit(input, which); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ClearBit(byte input, byte which) => (byte)(input & (byte)~which); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ClearBit(byte input, byte which, int condition) => ClearBit(input, which, condition != 0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte ClearBit(byte input, byte which, bool condition) => SetBit(input, which, !condition); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte HighByte(int value) => (byte)(value >> 8); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte HighByte(ushort value) => HighByte((int)value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte LowByte(int value) => (byte)(value & (int)Mask.Eight); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte LowByte(ushort value) => LowByte((int)value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort PromoteByte(byte value) => (ushort)(value << 8); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte DemoteByte(ushort value) => HighByte(value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort HigherPart(ushort value) => (ushort)(value & 0xff00); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static byte LowerPart(ushort value) => LowByte(value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort MakeWord(byte low, byte high) => (ushort)(PromoteByte(high) | low); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int HighNibble(byte value) => value >> 4; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LowNibble(byte value) => value & 0xf; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int HigherNibble(byte value) => value & 0xf0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int LowerNibble(byte value) => LowNibble(value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int PromoteNibble(byte value) => LowByte(value << 4); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int DemoteNibble(byte value) => HighNibble(value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CountBits(uint value) => System.Numerics.BitOperations.PopCount(value); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EvenParity(uint value) => CountBits(value) % 2 == 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int FindFirstSet(uint value) => value == 0 ? 0 : System.Numerics.BitOperations.TrailingZeroCount(value) + 1; } } diff --git a/EightBit/IntelProcessor.cs b/EightBit/IntelProcessor.cs index 146fb9f..644a2ed 100644 --- a/EightBit/IntelProcessor.cs +++ b/EightBit/IntelProcessor.cs @@ -88,13 +88,12 @@ namespace EightBit protected abstract void EnableInterrupts(); - protected override Register16 IncrementPC() + protected override void IncrementPC() { if (this.HALT.Raised()) { base.IncrementPC(); } - return this.PC; } protected override byte FetchInstruction() @@ -145,7 +144,8 @@ namespace EightBit protected sealed override void Push(byte value) { - this.MemoryWrite(this.SP.Decrement(), value); + this.SP.Decrement(); + this.MemoryWrite(this.SP, value); } protected sealed override byte Pop() diff --git a/EightBit/LittleEndianProcessor.cs b/EightBit/LittleEndianProcessor.cs index b09ec60..573784f 100644 --- a/EightBit/LittleEndianProcessor.cs +++ b/EightBit/LittleEndianProcessor.cs @@ -15,21 +15,21 @@ namespace EightBit public override void PokeWord(ushort address, Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.Bus.Poke(address, value.Low); this.Bus.Poke(++address, value.High); } protected override void FetchInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.Low = this.FetchByte(); into.High = this.FetchByte(); } protected override void GetInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.Low = this.MemoryRead(); this.Bus.Address.Increment(); into.High = this.MemoryRead(); @@ -37,7 +37,7 @@ namespace EightBit protected override void GetPagedInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.Low = this.MemoryRead(); ++this.Bus.Address.Low; into.High = this.MemoryRead(); @@ -45,21 +45,21 @@ namespace EightBit protected override void PopInto(Register16 into) { - ArgumentNullException.ThrowIfNull(into); + //ArgumentNullException.ThrowIfNull(into); into.Low = this.Pop(); into.High = this.Pop(); } protected override void PushWord(Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.Push(value.High); this.Push(value.Low); } protected override void SetWord(Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.MemoryWrite(value.Low); this.Bus.Address.Increment(); this.MemoryWrite(value.High); @@ -67,7 +67,7 @@ namespace EightBit protected override void SetPaged(Register16 value) { - ArgumentNullException.ThrowIfNull(value); + //ArgumentNullException.ThrowIfNull(value); this.MemoryWrite(value.Low); ++this.Bus.Address.Low; this.MemoryWrite(value.High); diff --git a/EightBit/Processor.cs b/EightBit/Processor.cs index 47b2882..c8fe80d 100644 --- a/EightBit/Processor.cs +++ b/EightBit/Processor.cs @@ -201,9 +201,9 @@ namespace EightBit protected virtual byte BusRead() => this.Bus.Read(); // N.B. Should be the only real call into the "Bus.Read" code. - protected virtual Register16 IncrementPC() => this.PC.Increment(); + protected virtual void IncrementPC() => this.PC.Increment(); - protected virtual Register16 DecrementPC() => this.PC.Decrement(); + protected virtual void DecrementPC() => this.PC.Decrement(); protected virtual void ImmediateAddress() { diff --git a/EightBit/Register16.cs b/EightBit/Register16.cs index 92ec474..d239ba0 100644 --- a/EightBit/Register16.cs +++ b/EightBit/Register16.cs @@ -4,13 +4,10 @@ namespace EightBit { - using System.Runtime.InteropServices; - - [StructLayout(LayoutKind.Explicit, Size = 2)] public sealed class Register16 { - [FieldOffset(0)] private byte _low; - [FieldOffset(1)] private byte _high; + private byte _low; + private byte _high; public Register16(byte low, byte high) { @@ -44,26 +41,18 @@ namespace EightBit public Register16(Register16 rhs) { - ArgumentNullException.ThrowIfNull(rhs); + //ArgumentNullException.ThrowIfNull(rhs); this.Low = rhs.Low; this.High = rhs.High; } - public unsafe ushort Word + public ushort Word { - get - { - fixed (byte* bytes = &this._low) - { - return *(ushort*)bytes; - } - } + get => Chip.MakeWord(this._low, this._high); set { - fixed (byte* bytes = &this._low) - { - *(ushort*)bytes = value; - } + this._low = Chip.LowByte(value); + this._high = Chip.HighByte(value); } } @@ -73,7 +62,7 @@ namespace EightBit public static bool operator ==(Register16 left, Register16 right) { - ArgumentNullException.ThrowIfNull(left); + //ArgumentNullException.ThrowIfNull(left); return left.Equals(right); } @@ -93,21 +82,13 @@ namespace EightBit public void Assign(Register16 from) { - ArgumentNullException.ThrowIfNull(from); + //ArgumentNullException.ThrowIfNull(from); this.Low = from.Low; this.High = from.High; } - public Register16 Increment() - { - ++this.Word; - return this; - } + public void Increment() => ++this.Word; - public Register16 Decrement() - { - --this.Word; - return this; - } + public void Decrement() => --this.Word; } } diff --git a/Intel8080/Intel8080.cs b/Intel8080/Intel8080.cs index c9fe7a8..bc3a2e5 100644 --- a/Intel8080/Intel8080.cs +++ b/Intel8080/Intel8080.cs @@ -380,14 +380,19 @@ namespace Intel8080 break; case 3: // 16-bit INC/DEC - _ = q switch + switch(q) { // INC rp - 0 => this.RP(p).Increment(), + case 0: + this.RP(p).Increment(); + break; // DEC rp - 1 => this.RP(p).Decrement(), - _ => throw new NotSupportedException("Invalid operation mode"), - }; + case 1: + this.RP(p).Decrement(); + break; + default: + throw new NotSupportedException("Invalid operation mode"); + } this.Tick(); break; case 4: // 8-bit INC diff --git a/MC6809/MC6809.cs b/MC6809/MC6809.cs index 43eda58..f38b16a 100644 --- a/MC6809/MC6809.cs +++ b/MC6809/MC6809.cs @@ -593,7 +593,11 @@ namespace MC6809 protected override void Push(byte value) => this.PushS(value); - private void Push(Register16 stack, byte value) => this.MemoryWrite(stack.Decrement(), value); + private void Push(Register16 stack, byte value) + { + stack.Decrement(); + this.MemoryWrite(stack, value); + } private void PushS(byte value) => this.Push(this.S, value); diff --git a/Z80/Z80.cs b/Z80/Z80.cs index 62d8016..5978b97 100644 --- a/Z80/Z80.cs +++ b/Z80/Z80.cs @@ -2004,7 +2004,8 @@ namespace Z80 private void RepeatBlockInstruction() { - this.MEMPTR.Assign(this.DecrementPC()); + this.DecrementPC(); + this.MEMPTR.Assign(this.PC); this.DecrementPC(); this.AdjustXY(this.PC.High); }