Turns out using lambdas to control pins is lovely and correct, but terribly slow. Back to a more traditional method.

This commit is contained in:
Adrian Conlon
2025-03-24 20:18:04 +00:00
parent d4dc99b454
commit 3d6b549c76
12 changed files with 184 additions and 326 deletions

View File

@ -14,7 +14,6 @@ namespace EightBit
public override void PokeWord(ushort address, Register16 value) public override void PokeWord(ushort address, Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.Bus.Poke(address, value.High); this.Bus.Poke(address, value.High);
this.Bus.Poke(++address, value.Low); this.Bus.Poke(++address, value.Low);
} }
@ -51,14 +50,12 @@ namespace EightBit
protected override void PushWord(Register16 value) protected override void PushWord(Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.Push(value.Low); this.Push(value.Low);
this.Push(value.High); this.Push(value.High);
} }
protected override void SetWord(Register16 value) protected override void SetWord(Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.MemoryWrite(value.High); this.MemoryWrite(value.High);
++this.Bus.Address.Word; ++this.Bus.Address.Word;
this.MemoryWrite(value.Low); this.MemoryWrite(value.Low);
@ -66,7 +63,6 @@ namespace EightBit
protected override void SetWordPaged(Register16 value) protected override void SetWordPaged(Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.MemoryWrite(value.High); this.MemoryWrite(value.High);
++this.Bus.Address.Low; ++this.Bus.Address.Low;
this.MemoryWrite(value.Low); this.MemoryWrite(value.Low);

View File

@ -28,7 +28,6 @@ namespace EightBit
public byte Peek(Register16 absolute) public byte Peek(Register16 absolute)
{ {
ArgumentNullException.ThrowIfNull(absolute);
return this.Peek(absolute.Word); return this.Peek(absolute.Word);
} }
@ -38,7 +37,6 @@ namespace EightBit
public void Poke(Register16 absolute, byte value) public void Poke(Register16 absolute, byte value)
{ {
ArgumentNullException.ThrowIfNull(absolute);
this.Poke(absolute.Word, value); this.Poke(absolute.Word, value);
} }
@ -58,7 +56,6 @@ namespace EightBit
public byte Read(Register16 absolute) public byte Read(Register16 absolute)
{ {
ArgumentNullException.ThrowIfNull(absolute);
return this.Read(absolute.Low, absolute.High); return this.Read(absolute.Low, absolute.High);
} }
@ -89,7 +86,6 @@ namespace EightBit
public void Write(Register16 absolute, byte value) public void Write(Register16 absolute, byte value)
{ {
ArgumentNullException.ThrowIfNull(absolute);
this.Write(absolute.Low, absolute.High, value); this.Write(absolute.Low, absolute.High, value);
} }
@ -133,7 +129,6 @@ namespace EightBit
protected ref byte Reference(Register16 absolute) protected ref byte Reference(Register16 absolute)
{ {
ArgumentNullException.ThrowIfNull(absolute);
return ref this.Reference(absolute.Word); return ref this.Reference(absolute.Word);
} }

View File

@ -25,9 +25,9 @@ namespace EightBit
{ {
if (this.POWER.Lowered()) if (this.POWER.Lowered())
{ {
this.OnRaisingPOWER(); RaisingPOWER?.Invoke(this, EventArgs.Empty);
this.POWER.Raise(); this.POWER.Raise();
this.OnRaisedPOWER(); RaisedPOWER?.Invoke(this, EventArgs.Empty);
} }
} }
@ -35,18 +35,10 @@ namespace EightBit
{ {
if (this.POWER.Raised()) if (this.POWER.Raised())
{ {
this.OnLoweringPOWER(); LoweringPOWER?.Invoke(this, EventArgs.Empty);
this.POWER.Lower(); this.POWER.Lower();
this.OnLoweredPOWER(); LoweredPOWER?.Invoke(this, EventArgs.Empty);
} }
} }
protected virtual void OnRaisingPOWER() => RaisingPOWER?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedPOWER() => RaisedPOWER?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweringPOWER() => LoweringPOWER?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredPOWER() => LoweredPOWER?.Invoke(this, EventArgs.Empty);
} }
} }

View File

@ -7,7 +7,7 @@
<GenerateDocumentationFile>False</GenerateDocumentationFile> <GenerateDocumentationFile>False</GenerateDocumentationFile>
<SignAssembly>False</SignAssembly> <SignAssembly>False</SignAssembly>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild> <EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<AnalysisLevel>latest-all</AnalysisLevel> <AnalysisLevel>latest-recommended</AnalysisLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">

View File

@ -20,6 +20,7 @@ namespace EightBit
{ {
this._decodedOpCodes[i] = new((byte)i); this._decodedOpCodes[i] = new((byte)i);
} }
this.RaisedPOWER += this.IntelProcessor_RaisedPOWER;
} }
public event EventHandler<EventArgs>? RaisingHALT; public event EventHandler<EventArgs>? RaisingHALT;
@ -67,9 +68,10 @@ namespace EightBit
{ {
if (this.HALT.Lowered()) if (this.HALT.Lowered())
{ {
this.OnRaisingHALT(); RaisingHALT?.Invoke(this, EventArgs.Empty);
this.HALT.Raise(); this.HALT.Raise();
this.OnRaisedHALT(); ++this.PC.Word; // Release the PC from HALT instruction
RaisedHALT?.Invoke(this, EventArgs.Empty);
} }
} }
@ -77,9 +79,10 @@ namespace EightBit
{ {
if (this.HALT.Raised()) if (this.HALT.Raised())
{ {
this.OnLoweringHALT(); LoweringHALT?.Invoke(this, EventArgs.Empty);
this.HALT.Lower(); this.HALT.Lower();
this.OnLoweredHALT(); --this.PC.Word; // Keep the PC on the HALT instruction (i.e. executing NOP)
LoweredHALT?.Invoke(this, EventArgs.Empty);
} }
} }
@ -102,27 +105,10 @@ namespace EightBit
return HalfCarryTableSub[index & (int)Mask.Three]; return HalfCarryTableSub[index & (int)Mask.Three];
} }
protected override void OnRaisedPOWER() private void IntelProcessor_RaisedPOWER(object? sender, EventArgs e)
{ {
this.PC.Word = this.SP.Word = this.AF.Word = this.BC.Word = this.DE.Word = this.HL.Word = (ushort)Mask.Sixteen; this.PC.Word = this.SP.Word = this.AF.Word = this.BC.Word = this.DE.Word = this.HL.Word = (ushort)Mask.Sixteen;
this.RaiseHALT(); this.RaiseHALT();
base.OnRaisedPOWER();
}
protected virtual void OnRaisingHALT() => RaisingHALT?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedHALT()
{
++this.PC.Word; // Release the PC from HALT instruction
RaisedHALT?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnLoweringHALT() => LoweringHALT?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredHALT()
{
--this.PC.Word; // Keep the PC on the HALT instruction (i.e. executing NOP)
LoweredHALT?.Invoke(this, EventArgs.Empty);
} }
protected override void HandleRESET() protected override void HandleRESET()

View File

@ -15,7 +15,6 @@ namespace EightBit
public override void PokeWord(ushort address, Register16 value) public override void PokeWord(ushort address, Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.Bus.Poke(address, value.Low); this.Bus.Poke(address, value.Low);
this.Bus.Poke(++address, value.High); this.Bus.Poke(++address, value.High);
} }
@ -52,14 +51,12 @@ namespace EightBit
protected override void PushWord(Register16 value) protected override void PushWord(Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.Push(value.High); this.Push(value.High);
this.Push(value.Low); this.Push(value.Low);
} }
protected override void SetWord(Register16 value) protected override void SetWord(Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.MemoryWrite(value.Low); this.MemoryWrite(value.Low);
++this.Bus.Address.Word; ++this.Bus.Address.Word;
this.MemoryWrite(value.High); this.MemoryWrite(value.High);
@ -67,7 +64,6 @@ namespace EightBit
protected override void SetWordPaged(Register16 value) protected override void SetWordPaged(Register16 value)
{ {
ArgumentNullException.ThrowIfNull(value);
this.MemoryWrite(value.Low); this.MemoryWrite(value.Low);
++this.Bus.Address.Low; ++this.Bus.Address.Low;
this.MemoryWrite(value.High); this.MemoryWrite(value.High);

View File

@ -10,57 +10,9 @@ namespace EightBit
public event EventHandler<EventArgs>? ExecutingInstruction; public event EventHandler<EventArgs>? ExecutingInstruction;
public event EventHandler<EventArgs>? ExecutedInstruction; public event EventHandler<EventArgs>? ExecutedInstruction;
protected virtual void OnExecutedInstruction() => ExecutedInstruction?.Invoke(this, EventArgs.Empty);
protected virtual void OnExecutingInstruction() => ExecutingInstruction?.Invoke(this, EventArgs.Empty);
#endregion #endregion
#region Bus events
#region Memory events
#region Memory read events
public event EventHandler<EventArgs>? ReadingMemory;
public event EventHandler<EventArgs>? ReadMemory;
protected virtual void OnReadingMemory() => ReadingMemory?.Invoke(this, EventArgs.Empty);
protected virtual void OnReadMemory() => ReadMemory?.Invoke(this, EventArgs.Empty);
#endregion
#region Memory write events
public event EventHandler<EventArgs>? WritingMemory;
public event EventHandler<EventArgs>? WrittenMemory;
protected virtual void OnWritingMemory() => WritingMemory?.Invoke(this, EventArgs.Empty);
protected virtual void OnWrittenMemory() => WrittenMemory?.Invoke(this, EventArgs.Empty);
#endregion
#endregion
#region IO events
#region IO read events
public event EventHandler<EventArgs>? ReadingIO;
public event EventHandler<EventArgs>? ReadIO;
protected virtual void OnReadingIO() => ReadingIO?.Invoke(this, EventArgs.Empty);
protected virtual void OnReadIO() => ReadIO?.Invoke(this, EventArgs.Empty);
#endregion
#region IO write events
public event EventHandler<EventArgs>? WritingIO;
public event EventHandler<EventArgs>? WrittenIO;
protected virtual void OnWritingIO() => WritingIO?.Invoke(this, EventArgs.Empty);
protected virtual void OnWrittenIO() => WrittenIO?.Invoke(this, EventArgs.Empty);
#endregion
#endregion
#endregion
private PinLevel _resetLine; private PinLevel _resetLine;
private PinLevel _intLine; private PinLevel _intLine;
@ -108,12 +60,12 @@ namespace EightBit
public virtual int Step() public virtual int Step()
{ {
this.ResetCycles(); this.ResetCycles();
this.OnExecutingInstruction(); ExecutingInstruction?.Invoke(this, EventArgs.Empty);
if (this.Powered) if (this.Powered)
{ {
this.PoweredStep(); this.PoweredStep();
} }
this.OnExecutedInstruction(); ExecutedInstruction?.Invoke(this, EventArgs.Empty);
return this.Cycles; return this.Cycles;
} }
@ -149,9 +101,9 @@ namespace EightBit
{ {
if (this.RESET.Lowered()) if (this.RESET.Lowered())
{ {
this.OnRaisingRESET(); RaisingRESET?.Invoke(this, EventArgs.Empty);
this.RESET.Raise(); this.RESET.Raise();
this.OnRaisedRESET(); RaisedRESET?.Invoke(this, EventArgs.Empty);
} }
} }
@ -159,9 +111,9 @@ namespace EightBit
{ {
if (this.RESET.Raised()) if (this.RESET.Raised())
{ {
this.OnLoweringRESET(); LoweringRESET?.Invoke(this, EventArgs.Empty);
this.RESET.Lower(); this.RESET.Lower();
this.OnLoweredRESET(); LoweredRESET?.Invoke(this, EventArgs.Empty);
} }
} }
@ -170,9 +122,9 @@ namespace EightBit
{ {
if (this.INT.Lowered()) if (this.INT.Lowered())
{ {
this.OnRaisingINT(); RaisingINT?.Invoke(this, EventArgs.Empty);
this.INT.Raise(); this.INT.Raise();
this.OnRaisedINT(); RaisedINT?.Invoke(this, EventArgs.Empty);
} }
} }
@ -180,9 +132,9 @@ namespace EightBit
{ {
if (this.INT.Raised()) if (this.INT.Raised())
{ {
this.OnLoweringINT(); LoweringINT?.Invoke(this, EventArgs.Empty);
this.INT.Lower(); this.INT.Lower();
this.OnLoweredINT(); LoweredINT?.Invoke(this, EventArgs.Empty);
} }
} }
@ -226,13 +178,11 @@ namespace EightBit
protected void MemoryWrite(Register16 address, byte data) protected void MemoryWrite(Register16 address, byte data)
{ {
ArgumentNullException.ThrowIfNull(address);
this.MemoryWrite(address.Low, address.High, data); this.MemoryWrite(address.Low, address.High, data);
} }
protected void MemoryWrite(Register16 address) protected void MemoryWrite(Register16 address)
{ {
ArgumentNullException.ThrowIfNull(address);
this.MemoryWrite(address.Low, address.High); this.MemoryWrite(address.Low, address.High);
} }
@ -260,7 +210,6 @@ namespace EightBit
protected byte MemoryRead(Register16 address) protected byte MemoryRead(Register16 address)
{ {
ArgumentNullException.ThrowIfNull(address);
return this.MemoryRead(address.Low, address.High); return this.MemoryRead(address.Low, address.High);
} }
@ -286,7 +235,6 @@ namespace EightBit
protected Register16 GetWordPaged(Register16 address) protected Register16 GetWordPaged(Register16 address)
{ {
ArgumentNullException.ThrowIfNull(address);
return this.GetWordPaged(address.High, address.Low); return this.GetWordPaged(address.High, address.Low);
} }
@ -300,7 +248,6 @@ namespace EightBit
protected void SetWordPaged(Register16 address, Register16 value) protected void SetWordPaged(Register16 address, Register16 value)
{ {
ArgumentNullException.ThrowIfNull(address);
this.SetWordPaged(address.High, address.Low, value); this.SetWordPaged(address.High, address.Low, value);
} }
@ -354,7 +301,6 @@ namespace EightBit
protected void Jump(Register16 destination) protected void Jump(Register16 destination)
{ {
ArgumentNullException.ThrowIfNull(destination);
this.PC.Assign(destination); this.PC.Assign(destination);
} }

View File

@ -46,7 +46,6 @@ namespace EightBit
public Register16(Register16 rhs) public Register16(Register16 rhs)
{ {
ArgumentNullException.ThrowIfNull(rhs);
this.Low = rhs.Low; this.Low = rhs.Low;
this.High = rhs.High; this.High = rhs.High;
} }
@ -68,7 +67,6 @@ namespace EightBit
public static bool operator ==(Register16 left, Register16 right) public static bool operator ==(Register16 left, Register16 right)
{ {
ArgumentNullException.ThrowIfNull(left);
return left.Equals(right); return left.Equals(right);
} }
@ -88,7 +86,6 @@ namespace EightBit
public void Assign(Register16 from) public void Assign(Register16 from)
{ {
ArgumentNullException.ThrowIfNull(from);
this.Assign(from._low, from._high); this.Assign(from._low, from._high);
} }
} }

View File

@ -8,8 +8,14 @@ namespace M6502
{ {
using EightBit; using EightBit;
public abstract class Core(Bus bus) : LittleEndianProcessor(bus) public abstract class Core : LittleEndianProcessor
{ {
protected Core(Bus bus)
: base(bus)
{
this.RaisedPOWER += this.Core_RaisedPOWER;
}
#region Pin controls #region Pin controls
#region NMI pin #region NMI pin
@ -104,16 +110,22 @@ namespace M6502
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
protected virtual void RaiseSYNC() protected virtual void RaiseSYNC()
{ {
this.OnRaisingSYNC(); if (this.SYNC.Lowered())
this.SYNC.Raise(); {
this.OnRaisedSYNC(); RaisingSYNC?.Invoke(this, EventArgs.Empty);
this.SYNC.Raise();
RaisedSYNC?.Invoke(this, EventArgs.Empty);
}
} }
protected virtual void LowerSYNC() protected virtual void LowerSYNC()
{ {
this.OnLoweringSYNC(); if (this.SYNC.Raised())
this.SYNC.Lower(); {
this.OnLoweredSYNC(); LoweringSYNC?.Invoke(this, EventArgs.Empty);
this.SYNC.Lower();
LoweredSYNC?.Invoke(this, EventArgs.Empty);
}
} }
#endregion #endregion
@ -190,7 +202,7 @@ namespace M6502
#endregion #endregion
protected override void OnRaisedPOWER() private void Core_RaisedPOWER(object? sender, EventArgs e)
{ {
this.X = (byte)Bits.Bit7; this.X = (byte)Bits.Bit7;
this.Y = 0; this.Y = 0;
@ -199,7 +211,6 @@ namespace M6502
this.S = (byte)Mask.Eight; this.S = (byte)Mask.Eight;
this.LowerSYNC(); this.LowerSYNC();
this.LowerRW(); this.LowerRW();
base.OnRaisedPOWER();
} }
#endregion #endregion
@ -551,9 +562,7 @@ namespace M6502
private void FetchInstruction() private void FetchInstruction()
{ {
// Instruction fetch beginning
this.LowerSYNC(); this.LowerSYNC();
System.Diagnostics.Debug.Assert(this.Cycles == 1, "An extra cycle has occurred"); 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.
@ -561,8 +570,6 @@ namespace M6502
this.OpCode = this.ReadFromBus(); this.OpCode = this.ReadFromBus();
System.Diagnostics.Debug.Assert(this.Cycles == 1, "BUS read has introduced stray cycles"); System.Diagnostics.Debug.Assert(this.Cycles == 1, "BUS read has introduced stray cycles");
// Instruction fetch has now completed
this.RaiseSYNC(); this.RaiseSYNC();
} }

View File

@ -282,13 +282,9 @@
} }
} }
private void Runner_ReadByte(object? sender, EventArgs e) => this.AddActualReadCycle(this.Runner.Address, this.Runner.Data); private void Runner_ReadByte(object? sender, EventArgs e) => this.AddActualCycle(this.Runner.Address, this.Runner.Data, "read");
private void Runner_WrittenByte(object? sender, EventArgs e) => this.AddActualWriteCycle(this.Runner.Address, this.Runner.Data); private void Runner_WrittenByte(object? sender, EventArgs e) => this.AddActualCycle(this.Runner.Address, this.Runner.Data, "write");
private void AddActualReadCycle(EightBit.Register16 address, byte value) => this.AddActualCycle(address, value, "read");
private void AddActualWriteCycle(EightBit.Register16 address, byte value) => this.AddActualCycle(address, value, "write");
private void AddActualCycle(EightBit.Register16 address, byte value, string action) => this.AddActualCycle(address.Word, value, action); private void AddActualCycle(EightBit.Register16 address, byte value, string action) => this.AddActualCycle(address.Word, value, action);

View File

@ -98,21 +98,30 @@ namespace Z80.FuseTest
public override void Initialize() public override void Initialize()
{ {
this.ReadByte += this.Event_ReadByte;
this.WrittenByte += this.Event_WrittenByte;
this.ports.ReadPort += this.Ports_ReadPort; this.ports.ReadPort += this.Ports_ReadPort;
this.ports.WrittenPort += this.Ports_WrittenPort; this.ports.WrittenPort += this.Ports_WrittenPort;
this.cpu.ExecutedInstruction += this.Cpu_ExecutedInstruction; this.cpu.ExecutedInstruction += this.Cpu_ExecutedInstruction;
} }
protected override void OnReadByte() private void Ports_ReadPort(object? sender, EventArgs e) => this.AddActualEvent("PR");
{ private void Ports_WrittenPort(object? sender, EventArgs e) => this.AddActualEvent("PW");
this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "MR", this.Address.Word, this.Data)); private void Event_ReadByte(object? sender, EventArgs e) => this.AddActualEvent("MR");
base.OnReadByte(); private void Event_WrittenByte(object? sender, EventArgs e) => this.AddActualEvent("MW");
}
protected override void OnWrittenByte() private void AddActualEvent(string specifier)
{ {
this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "MW", this.Address.Word, this.Data)); var address = this.Address.Word;
base.OnWrittenByte(); var cycles = this.totalCycles + this.cpu.Cycles;
if (specifier.EndsWith('C'))
{
this.actualEvents.Add(new TestEvent(cycles, specifier, address));
}
else
{
this.actualEvents.Add(new TestEvent(cycles, specifier, address, this.Data));
}
} }
private static void DumpDifference(string description, byte expected, byte actual) private static void DumpDifference(string description, byte expected, byte actual)
@ -121,11 +130,12 @@ namespace Z80.FuseTest
Console.Error.WriteLine(output); Console.Error.WriteLine(output);
} }
private void Ports_WrittenPort(object? sender, EightBit.PortEventArgs e) => this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "PW", this.Address.Word, this.Data)); private void Cpu_ExecutedInstruction(object? sender, EventArgs e)
{
private void Ports_ReadPort(object? sender, EightBit.PortEventArgs e) => this.actualEvents.Add(new TestEvent(this.totalCycles + this.cpu.Cycles, "PR", this.Address.Word, this.Data)); var output = $"**** Cycle count: {this.cpu.Cycles}";
Console.Out.WriteLine(output);
private void Cpu_ExecutedInstruction(object? sender, EventArgs e) => this.totalCycles += this.cpu.Cycles; this.totalCycles += this.cpu.Cycles;
}
private static void DumpDifference(string highDescription, string lowDescription, EightBit.Register16 expected, EightBit.Register16 actual) private static void DumpDifference(string highDescription, string lowDescription, EightBit.Register16 expected, EightBit.Register16 actual)
{ {

View File

@ -5,12 +5,17 @@
namespace Z80 namespace Z80
{ {
using EightBit; using EightBit;
using System.Diagnostics;
public class Z80 : IntelProcessor
public class Z80(Bus bus, InputOutput ports) : IntelProcessor(bus)
{ {
private readonly InputOutput _ports = ports; public Z80(Bus bus, InputOutput ports)
: base(bus)
{
this._ports = ports;
this.RaisedPOWER += this.Z80_RaisedPOWER;
}
private readonly InputOutput _ports;
private readonly Register16[] _accumulatorFlags = [new Register16(), new Register16()]; private readonly Register16[] _accumulatorFlags = [new Register16(), new Register16()];
private readonly Register16[][] _registers = private readonly Register16[][] _registers =
@ -161,7 +166,7 @@ namespace Z80
} }
} }
protected override void OnRaisedPOWER() private void Z80_RaisedPOWER(object? sender, EventArgs e)
{ {
this.RaiseM1(); this.RaiseM1();
this.RaiseRFSH(); this.RaiseRFSH();
@ -183,8 +188,6 @@ namespace Z80
base.ResetWorkingRegisters(); base.ResetWorkingRegisters();
this.ResetPrefixes(); this.ResetPrefixes();
base.OnRaisedPOWER();
} }
private void ResetPrefixes() private void ResetPrefixes()
@ -192,34 +195,7 @@ namespace Z80
this._prefixCB = this._prefixDD = this._prefixED = this._prefixFD = false; this._prefixCB = this._prefixDD = this._prefixED = this._prefixFD = false;
} }
private static void WithPinChange(Func<bool> check, Action enter, Action leave, Action task) #region Z80 specific pins
{
if (check())
{
enter();
try
{
task();
}
finally
{
leave();
}
}
}
private static byte WithPin(Action enter, Action leave, Func<byte> task)
{
enter();
try
{
return task();
}
finally
{
leave();
}
}
#region NMI pin #region NMI pin
@ -235,22 +211,14 @@ namespace Z80
public ref PinLevel NMI => ref this._nmiLine; public ref PinLevel NMI => ref this._nmiLine;
protected virtual void OnRaisingNMI() => RaisingNMI?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedNMI() => RaisedNMI?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweringNMI() => LoweringNMI?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredNMI() => LoweredNMI?.Invoke(this, EventArgs.Empty);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
public virtual void RaiseNMI() public virtual void RaiseNMI()
{ {
if (this.NMI.Lowered()) if (this.NMI.Lowered())
{ {
this.OnRaisingNMI(); RaisingNMI?.Invoke(this, EventArgs.Empty);
this.NMI.Raise(); this.NMI.Raise();
this.OnRaisedNMI(); RaisedNMI?.Invoke(this, EventArgs.Empty);
} }
} }
@ -258,9 +226,9 @@ namespace Z80
{ {
if (this.NMI.Raised()) if (this.NMI.Raised())
{ {
this.OnLoweringNMI(); LoweringNMI?.Invoke(this, EventArgs.Empty);
this.NMI.Lower(); this.NMI.Lower();
this.OnLoweredNMI(); LoweredNMI?.Invoke(this, EventArgs.Empty);
} }
} }
@ -280,14 +248,6 @@ namespace Z80
public ref PinLevel M1 => ref this._m1Line; public ref PinLevel M1 => ref this._m1Line;
protected virtual void OnRaisingM1() => RaisingM1?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedM1()
{
++this.REFRESH;
RaisedM1?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnLoweringM1() => LoweringM1?.Invoke(this, EventArgs.Empty); protected virtual void OnLoweringM1() => LoweringM1?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredM1() => LoweredM1?.Invoke(this, EventArgs.Empty); protected virtual void OnLoweredM1() => LoweredM1?.Invoke(this, EventArgs.Empty);
@ -295,17 +255,23 @@ namespace Z80
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
public virtual void RaiseM1() public virtual void RaiseM1()
{ {
WithPinChange(() => this.M1.Lowered(), this.OnRaisingM1, this.OnRaisedM1, () => this.M1.Raise()); if (this.M1.Lowered())
{
RaisingM1?.Invoke(this, EventArgs.Empty);
this.M1.Raise();
++this.REFRESH;
RaisedM1?.Invoke(this, EventArgs.Empty);
}
} }
public virtual void LowerM1() public virtual void LowerM1()
{ {
WithPinChange(() => this.M1.Raised(), this.OnLoweringM1, this.OnLoweredM1, () => this.M1.Lower()); if (this.M1.Raised())
} {
LoweringM1?.Invoke(this, EventArgs.Empty);
private byte WithM1(Func<byte> function) this.M1.Lower();
{ LoweredM1?.Invoke(this, EventArgs.Empty);
return WithPin(this.LowerM1, this.RaiseM1, function); }
} }
#endregion #endregion
@ -329,28 +295,25 @@ namespace Z80
public ref PinLevel RFSH => ref this._rfshLine; public ref PinLevel RFSH => ref this._rfshLine;
protected virtual void OnRaisingRFSH() => RaisingRFSH?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedRFSH() => RaisedRFSH?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweringRFSH() => LoweringRFSH?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredRFSH() => LoweredRFSH?.Invoke(this, EventArgs.Empty);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
public virtual void RaiseRFSH() public virtual void RaiseRFSH()
{ {
WithPinChange(() => this.RFSH.Lowered(), this.OnRaisingRFSH, this.OnRaisedRFSH, () => this.RFSH.Raise()); if (this.RFSH.Lowered())
{
RaisingRFSH?.Invoke(this, EventArgs.Empty);
this.RFSH.Raise();
RaisedRFSH?.Invoke(this, EventArgs.Empty);
}
} }
public virtual void LowerRFSH() public virtual void LowerRFSH()
{ {
WithPinChange(() => this.RFSH.Raised(), this.OnLoweringRFSH, this.OnLoweredRFSH, () => this.RFSH.Lower()); if (this.RFSH.Raised())
} {
LoweringRFSH?.Invoke(this, EventArgs.Empty);
private byte WithRFSH(Func<byte> function) this.RFSH.Lower();
{ LoweredRFSH?.Invoke(this, EventArgs.Empty);
return WithPin(this.LowerRFSH, this.RaiseRFSH, function); }
} }
#endregion #endregion
@ -369,28 +332,25 @@ namespace Z80
public ref PinLevel MREQ => ref this._mreqLine; public ref PinLevel MREQ => ref this._mreqLine;
protected virtual void OnLoweringMREQ() => LoweringMREQ?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredMREQ() => LoweredMREQ?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisingMREQ() => RaisingMREQ?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedMREQ() => RaisedMREQ?.Invoke(this, EventArgs.Empty);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
public virtual void RaiseMREQ() public virtual void RaiseMREQ()
{ {
WithPinChange(() => this.MREQ.Lowered(), this.OnRaisingMREQ, this.OnRaisedMREQ, () => this.MREQ.Raise()); if (this.MREQ.Lowered())
{
RaisingMREQ?.Invoke(this, EventArgs.Empty);
this.MREQ.Raise();
RaisedMREQ?.Invoke(this, EventArgs.Empty);
}
} }
public virtual void LowerMREQ() public virtual void LowerMREQ()
{ {
WithPinChange(() => this.MREQ.Raised(), this.OnLoweringMREQ, this.OnLoweredMREQ, () => this.MREQ.Lower()); if (this.MREQ.Raised())
} {
LoweringMREQ?.Invoke(this, EventArgs.Empty);
private byte WithMREQ(Func<byte> function) this.MREQ.Lower();
{ LoweredMREQ?.Invoke(this, EventArgs.Empty);
return WithPin(this.LowerMREQ, this.RaiseMREQ, function); }
} }
#endregion #endregion
@ -409,29 +369,25 @@ namespace Z80
public ref PinLevel IORQ => ref this._iorqLine; public ref PinLevel IORQ => ref this._iorqLine;
protected virtual void OnLoweringIORQ() => LoweringIORQ?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredIORQ() => LoweredIORQ?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisingIORQ() => RaisingIORQ?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedIORQ() => RaisedIORQ?.Invoke(this, EventArgs.Empty);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
public virtual void RaiseIORQ() public virtual void RaiseIORQ()
{ {
WithPinChange(() => this.IORQ.Lowered(), this.OnRaisingIORQ, this.OnRaisedIORQ, () => this.IORQ.Raise()); if (this.IORQ.Lowered())
{
RaisingIORQ?.Invoke(this, EventArgs.Empty);
this.IORQ.Raise();
RaisedIORQ?.Invoke(this, EventArgs.Empty);
}
} }
public virtual void LowerIORQ() public virtual void LowerIORQ()
{ {
WithPinChange(() => this.IORQ.Raised(), this.OnLoweringIORQ, this.OnLoweredIORQ, () => this.IORQ.Lower()); if (this.IORQ.Raised())
} {
LoweringIORQ?.Invoke(this, EventArgs.Empty);
private byte WithIORQ(Func<byte> function) this.IORQ.Lower();
{ LoweredIORQ?.Invoke(this, EventArgs.Empty);
return WithPin(this.LowerIORQ, this.RaiseIORQ, function); }
} }
#endregion #endregion
@ -450,28 +406,25 @@ namespace Z80
public ref PinLevel RD => ref this._rdLine; public ref PinLevel RD => ref this._rdLine;
protected virtual void OnLoweringRD() => LoweringRD?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredRD() => LoweredRD?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisingRD() => RaisingRD?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedRD() => RaisedRD?.Invoke(this, EventArgs.Empty);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
public virtual void RaiseRD() public virtual void RaiseRD()
{ {
WithPinChange(() => this.RD.Lowered(), this.OnRaisingRD, this.OnRaisedRD, () => this.RD.Raise()); if (this.RD.Lowered())
{
RaisingRD?.Invoke(this, EventArgs.Empty);
this.RD.Raise();
RaisedRD?.Invoke(this, EventArgs.Empty);
}
} }
public virtual void LowerRD() public virtual void LowerRD()
{ {
WithPinChange(() => this.RD.Raised(), this.OnLoweringRD, this.OnLoweredRD, () => this.RD.Lower()); if (this.RD.Raised())
} {
LoweringRD?.Invoke(this, EventArgs.Empty);
private byte WithRD(Func<byte> function) this.RD.Lower();
{ LoweredRD?.Invoke(this, EventArgs.Empty);
return WithPin(this.LowerRD, this.RaiseRD, function); }
} }
#endregion #endregion
@ -490,63 +443,50 @@ namespace Z80
public ref PinLevel WR => ref this._wrLine; public ref PinLevel WR => ref this._wrLine;
protected virtual void OnLoweringWR() => LoweringWR?.Invoke(this, EventArgs.Empty);
protected virtual void OnLoweredWR() => LoweredWR?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisingWR() => RaisingWR?.Invoke(this, EventArgs.Empty);
protected virtual void OnRaisedWR() => RaisedWR?.Invoke(this, EventArgs.Empty);
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1030:Use events where appropriate", Justification = "The word 'raise' is used in an electrical sense")]
public virtual void RaiseWR() public virtual void RaiseWR()
{ {
WithPinChange(() => this.WR.Lowered(), this.OnRaisingWR, this.OnRaisedWR, () => this.WR.Raise()); if (this.WR.Lowered())
{
RaisingWR?.Invoke(this, EventArgs.Empty);
this.WR.Raise();
RaisedWR?.Invoke(this, EventArgs.Empty);
}
} }
public virtual void LowerWR() public virtual void LowerWR()
{ {
WithPinChange(() => this.WR.Raised(), this.OnLoweringWR, this.OnLoweredWR, () => this.WR.Lower()); if (this.WR.Raised())
}
private byte WithWR(Func<byte> function)
{
this.LowerWR();
try
{ {
return function(); LoweringWR?.Invoke(this, EventArgs.Empty);
} this.WR.Lower();
finally LoweredWR?.Invoke(this, EventArgs.Empty);
{
this.RaiseWR();
} }
} }
#endregion #endregion
#endregion
protected override void MemoryWrite() protected override void MemoryWrite()
{ {
_ = this.WithMREQ(() => this.LowerMREQ();
{ this.LowerWR();
return this.WithWR(() => this.Tick(3);
{ base.MemoryWrite();
this.Tick(3); this.RaiseWR();
base.MemoryWrite(); this.RaiseMREQ();
return 0;
});
});
} }
protected override byte MemoryRead() protected override byte MemoryRead()
{ {
return this.WithMREQ(() => this.LowerMREQ();
{ this.LowerRD();
return this.WithRD(() => this.Tick(3);
{ var returned = base.MemoryRead();
this.Tick(3); this.RaiseRD();
return base.MemoryRead(); this.RaiseMREQ();
}); return returned;
});
} }
protected override void HandleRESET() protected override void HandleRESET()
@ -562,13 +502,11 @@ namespace Z80
{ {
base.HandleINT(); base.HandleINT();
var data = this.WithM1(() => this.LowerM1();
{ this.LowerIORQ();
return this.WithIORQ(() => var data = this.Bus.Data;
{ this.RaiseIORQ();
return this.Bus.Data; this.RaiseM1();
});
});
this.DisableInterrupts(); this.DisableInterrupts();
this.Tick(5); this.Tick(5);
@ -1723,18 +1661,17 @@ namespace Z80
private byte ReadInitialOpCode() private byte ReadInitialOpCode()
{ {
this.Tick(); this.Tick();
var returned = this.WithM1(() =>
{ this.LowerM1();
return this.MemoryRead(this.PC); var returned = this.MemoryRead(this.PC);
}); this.RaiseM1();
this.Bus.Address.Assign(this.REFRESH, this.IV); this.Bus.Address.Assign(this.REFRESH, this.IV);
_ = this.WithRFSH(() => this.LowerRFSH();
{ this.LowerMREQ();
return this.WithMREQ(() => this.RaiseMREQ();
{ this.RaiseRFSH();
return 0;
});
});
return returned; return returned;
} }