ZXHawk: 48k timing work

This commit is contained in:
Asnivor 2018-06-05 17:14:37 +01:00
parent beae64d563
commit f764c137ee
9 changed files with 311 additions and 104 deletions

View File

@ -9,15 +9,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
{
public class CPUMonitor
{
#region Devices
private SpectrumBase _machine;
private Z80A _cpu;
public MachineType machineType = MachineType.ZXSpectrum48;
public CPUMonitor(SpectrumBase machine)
{
_machine = machine;
_cpu = _machine.CPU;
}
#endregion
#region Lookups
public ushort[] cur_instr => _cpu.cur_instr;
public int instr_pntr => _cpu.instr_pntr;
@ -34,91 +34,240 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
}
}
/// <summary>
/// Called when the first byte of an instruction is fetched
/// </summary>
/// <param name="firstByte"></param>
public void OnExecFetch(ushort firstByte)
#endregion
#region Construction
public CPUMonitor(SpectrumBase machine)
{
// fetch instruction without incrementing pc
//_cpu.FetchInstruction(_cpu.FetchMemory(firstByte));
_machine = machine;
_cpu = _machine.CPU;
}
#endregion
#region State
public bool IsContending = false;
public int ContCounter = -1;
public int portContCounter = 0;
public int portContTotalLen = 0;
public bool portContending = false;
public ushort lastPortAddr;
public int[] portContArr = new int[4];
#endregion
#region Methods
/// <summary>
/// A CPU monitor cycle
/// Handles the ULA and CPU cycle clocks, along with any memory and port contention
/// </summary>
public void Cycle()
public void ExecuteCycle()
{
_machine.ULADevice.RenderScreen((int)_machine.CurrentFrameCycle);
if (portContending)
{
RunPortContention();
}
else
{
// check for upcoming BUSRQ
if (BUSRQ == 0)
return;
ushort addr = 0;
switch (BUSRQ)
// is the next CPU cycle causing a BUSRQ?
if (BUSRQ > 0)
{
// PCh
case 1:
addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8);
break;
// SPh
case 3:
addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8);
break;
// A
case 4:
addr = (ushort)(_cpu.Regs[_cpu.F] | _cpu.Regs[_cpu.A] << 8);
break;
// B
case 6:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// D
case 8:
addr = (ushort)(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8);
break;
// H
case 10:
addr = (ushort)(_cpu.Regs[_cpu.L] | _cpu.Regs[_cpu.H] << 8);
break;
// W
case 12:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
// Ixh
case 16:
addr = (ushort)(_cpu.Regs[_cpu.Ixl] | _cpu.Regs[_cpu.Ixh] << 8);
break;
// Iyh
case 18:
addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 8);
break;
// I
case 21:
addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8);
break;
default:
break;
// is the memory address of the BUSRQ potentially contended?
if (_machine.IsContended(AscertainBUSRQAddress()))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
_cpu.TotalExecutedCycles += cont;
}
}
}
}
_cpu.ExecuteOne();
/*
else if (ContCounter > 0)
{
// still contention cycles to process
IsContending = true;
}
else
{
// is the next CPU cycle causing a BUSRQ?
if (BUSRQ > 0)
{
// is the memory address of the BUSRQ potentially contended?
if (_machine.IsContended(AscertainBUSRQAddress()))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
ContCounter = cont + 1;
IsContending = true;
}
}
}
}
/*
else
{
// no contention cycles to process (so far) on this cycle
IsContending = false;
ContCounter = 0;
if (portContending)
{
// a port operation is still in progress
portContCounter++;
if (portContCounter > 3)
{
// we are now out of the IN/OUT operation
portContCounter = 0;
portContending = false;
}
else
{
// still IN/OUT cycles to process
if (IsPortContended(portContCounter))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
ContCounter = cont + 1;
IsContending = true;
// dont let this fall through
// just manually do the first contention cycle
ContCounter--;
_cpu.TotalExecutedCycles++;
return;
}
}
}
}
if (_machine.IsContended(addr))
_cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue((int)(_machine.CurrentFrameCycle + 1));
// is the next CPU cycle causing a BUSRQ?
if (BUSRQ > 0)
{
// is the memory address of the BUSRQ potentially contended?
if (_machine.IsContended(AscertainBUSRQAddress()))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
ContCounter = cont + 1;
IsContending = true;
}
}
}
/*
// is the next CPU cycle an OUT operation?
else if (cur_instr[instr_pntr] == Z80A.OUT)
{
portContending = true;
lastPortAddr = (ushort)(_cpu.Regs[cur_instr[instr_pntr + 1]] | _cpu.Regs[cur_instr[instr_pntr + 2]] << 8);
portContCounter = 0;
if (IsPortContended(portContCounter))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
ContCounter = cont;
IsContending = true;
}
}
}
// is the next cpu cycle an IN operation?
else if (cur_instr[instr_pntr] == Z80A.IN)
{
portContending = true;
lastPortAddr = (ushort)(_cpu.Regs[cur_instr[instr_pntr + 2]] | _cpu.Regs[cur_instr[instr_pntr + 3]] << 8);
portContCounter = 0;
if (IsPortContended(portContCounter))
{
var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle);
if (cont > 0)
{
ContCounter = cont;
IsContending = true;
}
}
}
*/
/* }*/
/*
// run a CPU cycle if no contention is applicable
if (!IsContending)
{
_cpu.ExecuteOne();
}
else
{
_cpu.TotalExecutedCycles++;
ContCounter--;
}
*/
}
/// <summary>
/// Looks up the BUSRQ address that is about to be signalled
/// </summary>
/// <returns></returns>
private ushort AscertainBUSRQAddress()
{
ushort addr = 0;
switch (BUSRQ)
{
// PCh
case 1:
addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8);
break;
// SPh
case 3:
addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8);
break;
// A
case 4:
addr = (ushort)(_cpu.Regs[_cpu.F] | _cpu.Regs[_cpu.A] << 8);
break;
// B
case 6:
addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8);
break;
// D
case 8:
addr = (ushort)(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8);
break;
// H
case 10:
addr = (ushort)(_cpu.Regs[_cpu.L] | _cpu.Regs[_cpu.H] << 8);
break;
// W
case 12:
addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8);
break;
// Ixh
case 16:
addr = (ushort)(_cpu.Regs[_cpu.Ixl] | _cpu.Regs[_cpu.Ixh] << 8);
break;
// Iyh
case 18:
addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 8);
break;
// I
case 21:
addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8);
break;
}
#region Port Contention
public int portContCounter = 0;
public bool portContending = false;
public ushort lastPortAddr;
return addr;
}
/// <summary>
/// Perfors the actual port contention (if necessary)
/// </summary>
@ -162,10 +311,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// C:1, C:1, C:1, C:1
switch (portContCounter)
{
case 3: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break;
case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break;
case 1: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break;
case 0: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break;
case 3: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break;
case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break;
case 1: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break;
case 0:
_cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f);
portContCounter = 0;
portContending = false;
break;
default:
portContCounter = 0;
portContending = false;
@ -179,10 +332,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// C:1, C:3
switch (portContCounter)
{
case 3: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break;
case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break;
case 3: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break;
case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break;
case 1: break;
case 0: break;
case 0:
portContCounter = 0;
portContending = false;
break;
default:
portContCounter = 0;
portContending = false;
@ -203,7 +359,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
case 3: break;
case 2: break;
case 1: break;
case 0: break;
case 0:
portContCounter = 0;
portContending = false;
break;
default:
portContCounter = 0;
portContending = false;
@ -218,9 +377,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
switch (portContCounter)
{
case 3: break;
case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break;
case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break;
case 1: break;
case 0: break;
case 0:
portContCounter = 0;
portContending = false;
break;
default:
portContCounter = 0;
portContending = false;
@ -247,6 +409,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
lastPortAddr = port;
}
/// <summary>
/// Called when the first byte of an instruction is fetched
/// </summary>
/// <param name="firstByte"></param>
public void OnExecFetch(ushort firstByte)
{
// fetch instruction without incrementing pc
//_cpu.FetchInstruction(_cpu.FetchMemory(firstByte));
}
#endregion
}

View File

@ -166,14 +166,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
while (CurrentFrameCycle < ULADevice.FrameLength)
{
// check for interrupt
ULADevice.CheckForInterrupt(CurrentFrameCycle);
// run a single CPU instruction
CPU.ExecuteOne();
// check contention for next cycle
CPUMon.Cycle();
CPUMon.ExecuteCycle();
// cycle the tape device
if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded)
TapeDevice.TapeCycle();

View File

@ -287,6 +287,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// </summary>
public MachineType _machineType;
public int Offset;
/// <summary>
/// Constructor
/// </summary>
@ -305,6 +307,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="machineType"></param>
private void InitRenderer(MachineType machineType)
{
switch (machineType)
{
case MachineType.ZXSpectrum16:
case MachineType.ZXSpectrum48:
Offset = 0;
break;
}
for (var t = 0; t < _ula.FrameCycleLength; t++)
{
var tStateScreen = t + _ula.InterruptStartTime;
@ -438,7 +448,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// calculate contention values
for (int t = 0; t < _ula.FrameCycleLength; t++)
{
int shifted = (t + 1) + _ula.InterruptStartTime;
int shifted = (t + 1) + _ula.InterruptStartTime + Offset;
if (shifted < 0)
shifted += _ula.FrameCycleLength;
shifted %= _ula.FrameCycleLength;
@ -466,7 +476,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// calculate floating bus values
for (int t = 0; t < _ula.FrameCycleLength; t++)
{
int shifted = (t + 1) + _ula.InterruptStartTime;
int shifted = (t + 10) + _ula.InterruptStartTime;
if (shifted < 0)
shifted += _ula.FrameCycleLength;
shifted %= _ula.FrameCycleLength;
@ -580,7 +590,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
if (toCycle > FrameCycleLength)
toCycle = FrameCycleLength;
if (LastTState > toCycle)
LastTState = 0;
LastTState = toCycle - 2;
if (toCycle < 0)
toCycle = 0;
// render the required number of cycles
for (int t = LastTState; t < toCycle; t++)
@ -701,6 +713,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public void ReadFloatingBus(int tstate, ref int result)
{
int off = 1;
tstate += off;
if (tstate >= RenderingTable.Renderer.Length)
tstate -= RenderingTable.Renderer.Length;
if (tstate < 0)
tstate += RenderingTable.Renderer.Length;
var item = RenderingTable.Renderer[tstate];
switch (RenderingTable._machineType)
@ -752,11 +771,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public int GetContentionValue()
{
var f = _machine.CurrentFrameCycle;
if (f >= FrameCycleLength)
f -= FrameCycleLength;
return RenderingTable.Renderer[f].ContentionValue;
return GetContentionValue((int)_machine.CurrentFrameCycle);
}
/// <summary>
@ -765,6 +780,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <returns></returns>
public int GetContentionValue(int tstate)
{
int off = 5;
tstate += off;
if (tstate >= FrameCycleLength)
tstate -= FrameCycleLength;
if (tstate < 0)
tstate += FrameCycleLength;
return RenderingTable.Renderer[tstate].ContentionValue;
}
/// <summary>
/// Returns the contention value for the supplied t-state
/// </summary>
/// <returns></returns>
public int GetPortContentionValue(int tstate)
{
int off = 1;
tstate += off;
if (tstate >= FrameCycleLength)
tstate -= FrameCycleLength;
@ -926,6 +960,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
protected void SetupScreenSize()
{
BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth;
BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight;
VirtualHeight = BufferHeight;
VirtualWidth = BufferWidth;
ScreenBuffer = new int[BufferWidth * BufferHeight];
switch (borderType)
{
case ZXSpectrum.BorderType.Full:
@ -987,7 +1027,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public void SyncState(Serializer ser)
{
ser.BeginSection("ULA");
ser.Sync("ScreenBuffer", ref ScreenBuffer, false);
if (ScreenBuffer != null)
ser.Sync("ScreenBuffer", ref ScreenBuffer, false);
ser.Sync("FrameLength", ref FrameCycleLength);
ser.Sync("ClockSpeed", ref ClockSpeed);
ser.Sync("BorderColor", ref BorderColor);

View File

@ -176,7 +176,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="addr"></param>
public override void ContendPort(ushort addr)
{
CPUMon.ContendPort(addr);
//CPUMon.ContendPort(addr);
return;
}
}

View File

@ -289,7 +289,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="addr"></param>
public override void ContendPort(ushort addr)
{
CPUMon.ContendPort(addr);
//CPUMon.ContendPort(addr);
return;
}
}

View File

@ -224,7 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
/// <param name="addr"></param>
public override void ContendPort(ushort addr)
{
CPUMon.ContendPort(addr);
//CPUMon.ContendPort(addr);
return;
}
}

View File

@ -110,8 +110,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
public override void WriteMemory(ushort addr, byte value)
{
// update ULA screen buffer if necessary BEFORE T1 write
if ((addr & 49152) == 16384 && _render)
ULADevice.RenderScreen((int)CurrentFrameCycle);
//if ((addr & 49152) == 16384 && _render)
//ULADevice.RenderScreen((int)CurrentFrameCycle);
ContendMemory(addr);
WriteBus(addr, value);

View File

@ -110,7 +110,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
*/
// Border - LSB 3 bits hold the border colour
ULADevice.RenderScreen((int)CurrentFrameCycle);
//ULADevice.RenderScreen((int)CurrentFrameCycle);
ULADevice.BorderColor = value & BORDER_BIT;
// Buzzer

View File

@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum
// timing
ClockSpeed = 3500000;
FrameCycleLength = 69888;
InterruptStartTime = 32;
InterruptStartTime = 31;
InterruptLength = 32;
ScanlineTime = 224;