Add support for sub-M-cycle accuracy. Not sure how useful this is!

This commit is contained in:
Adrian Conlon
2025-08-10 11:32:53 +01:00
parent 072e38f6ee
commit 2e1573b016
+123 -22
View File
@@ -479,9 +479,16 @@ namespace Z80
private void MemoryUpdate(int ticks) private void MemoryUpdate(int ticks)
{ {
Debug.Assert(ticks > 0, "Ticks must be greater than zero");
this.OnWritingMemory(); this.OnWritingMemory();
#if CYCLE_ACCURATE
this.Tick(ticks - 1);
this.LowerMREQ();
this.Tick();
#else
this.Tick(ticks); this.Tick(ticks);
this.LowerMREQ(); this.LowerMREQ();
#endif
this.LowerWR(); this.LowerWR();
this.Tick(); this.Tick();
base.MemoryWrite(); base.MemoryWrite();
@@ -498,30 +505,71 @@ namespace Z80
private void RefreshMemory() private void RefreshMemory()
{ {
Debug.Assert(this.M1.Lowered(), "M1 must be lowered to refresh memory");
this.Bus.Address.Assign(this.REFRESH, this.IV); this.Bus.Address.Assign(this.REFRESH, this.IV);
this.LowerRFSH(); this.LowerRFSH();
#if CYCLE_ACCURATE
this.LowerMREQ();
this.Tick();
#else
this.Tick(); this.Tick();
this.LowerMREQ(); this.LowerMREQ();
#endif
this.RaiseMREQ(); this.RaiseMREQ();
this.RaiseRFSH(); this.RaiseRFSH();
} }
private void SetMemoryRead()
{
this.LowerMREQ();
this.LowerRD();
}
private void ResetMemoryRead()
{
this.RaiseRD();
this.RaiseMREQ();
}
private void MemoryReadWithM1()
{
Debug.Assert(this.M1.Lowered());
this.Tick();
this.SetMemoryRead();
this.Tick();
_ = base.MemoryRead();
this.ResetMemoryRead();
this.RefreshMemory();
}
private void MemoryReadWithoutM1()
{
Debug.Assert(this.M1.Raised());
#if CYCLE_ACCURATE
this.SetMemoryRead();
_ = base.MemoryRead();
this.Tick(2);
#else
this.Tick();
this.SetMemoryRead();
this.Tick();
_ = base.MemoryRead();
#endif
this.ResetMemoryRead();
}
protected override byte MemoryRead() protected override byte MemoryRead()
{ {
this.OnReadingMemory(); this.OnReadingMemory();
this.Tick();
this.LowerMREQ();
this.LowerRD();
this.Tick();
_ = base.MemoryRead();
this.RaiseRD();
this.RaiseMREQ();
if (this.M1.Lowered()) if (this.M1.Lowered())
{ {
this.RefreshMemory(); this.MemoryReadWithM1();
}
else
{
this.MemoryReadWithoutM1();
} }
this.Tick();
this.OnReadMemory(); this.OnReadMemory();
this.Tick();
return this.Bus.Data; return this.Bus.Data;
} }
@@ -1472,7 +1520,22 @@ namespace Z80
if (this._displaced) if (this._displaced)
{ {
this.FetchDisplacement(); this.FetchDisplacement();
this.Execute(this.FetchByte()); {
this.ImmediateAddress();
#if CYCLE_ACCURATE
this.OnReadingMemory();
this.Tick();
this.SetMemoryRead();
var instruction = base.MemoryRead();
this.Tick();
this.ResetMemoryRead();
this.Tick();
this.OnReadMemory();
#else
var instruction = this.MemoryRead();
#endif
this.Execute(instruction);
}
} }
else else
{ {
@@ -1604,7 +1667,11 @@ namespace Z80
this.Restart(0x66); this.Restart(0x66);
} }
private void FetchDisplacement() => this._displacement = (sbyte)this.FetchByte(); private void FetchDisplacement()
{
Debug.Assert(this.M1.Raised(), "Displacement cannot be fetched during an M1 cycle");
this._displacement = (sbyte)this.FetchByte();
}
// ** From the Z80 CPU User Manual // ** From the Z80 CPU User Manual
// Figure 5 depicts the timing during an M1 (op code fetch) cycle. The Program Counter is // Figure 5 depicts the timing during an M1 (op code fetch) cycle. The Program Counter is
@@ -2341,16 +2408,32 @@ namespace Z80
this.WritePort(); this.WritePort();
} }
private void SetIOWrite()
{
this.LowerIORQ();
this.LowerWR();
}
private void ResetIOWrite()
{
this.RaiseWR();
this.RaiseIORQ();
}
private void WritePort() private void WritePort()
{ {
this.MEMPTR.Assign(this.Bus.Address); this.MEMPTR.Assign(this.Bus.Address);
this.Tick(2); this.Tick();
this.LowerIORQ(); #if CYCLE_ACCURATE
this.LowerWR(); this.SetIOWrite();
this.Tick();
#else
this.Tick();
this.SetIOWrite();
#endif
this.Tick(); this.Tick();
this.Ports.Write(this.Bus.Address, this.Bus.Data); this.Ports.Write(this.Bus.Address, this.Bus.Data);
this.RaiseWR(); this.ResetIOWrite();
this.RaiseIORQ();
this.Tick(); this.Tick();
} }
@@ -2367,19 +2450,37 @@ namespace Z80
this.ReadPort(); this.ReadPort();
} }
private void SetIORead()
{
this.LowerIORQ();
this.LowerRD();
}
private void ResetIORead()
{
this.RaiseRD();
this.RaiseIORQ();
}
private void ReadPort() private void ReadPort()
{ {
this.MEMPTR.Assign(this.Bus.Address); this.MEMPTR.Assign(this.Bus.Address);
this.Tick(2); this.Tick();
this.LowerIORQ(); #if CYCLE_ACCURATE
this.LowerRD(); this.SetIORead();
this.Bus.Data = this.Ports.Read(this.Bus.Address); this.Bus.Data = this.Ports.Read(this.Bus.Address);
this.Tick(); this.Tick();
this.RaiseRD(); #else
this.RaiseIORQ(); this.Tick();
this.SetIORead();
this.Bus.Data = this.Ports.Read(this.Bus.Address);
#endif
this.Tick();
this.ResetIORead();
this.Tick(); this.Tick();
} }
#endregion #endregion
} }
} }