From fbba7c25aed54efe1476278bf51ed805f483db70 Mon Sep 17 00:00:00 2001 From: Asnivor <coding@asnitech.co.uk> Date: Mon, 11 Jun 2018 14:35:12 +0100 Subject: [PATCH] ZXHawk: New interrupt implementation --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 3 + .../SinclairSpectrum/Machine/SpectrumBase.cs | 22 ++-- .../Computers/SinclairSpectrum/Machine/ULA.cs | 117 +++++++++--------- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 26 ++-- .../ZX128Plus2a.Screen.cs | 35 ++---- .../Machine/ZXSpectrum48K/ZX48.Screen.cs | 28 ++--- 6 files changed, 104 insertions(+), 127 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 9b8a822c15..8838abe75e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -61,6 +61,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// </summary> public void ExecuteCycle() { + // simulate the ULA clock cycle before the CPU cycle + _machine.ULADevice.CycleClock(TotalExecutedCycles); + // is the next CPU cycle causing a BUSRQ or IORQ? if (BUSRQ > 0) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 3b5eaa6da9..58753c45e5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -147,6 +147,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// </summary> public virtual void ExecuteFrame(bool render, bool renderSound) { + ULADevice.FrameEnd = false; + ULADevice.ULACycleCounter = CurrentFrameCycle; + InputRead = false; _render = render; _renderSound = renderSound; @@ -164,15 +167,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PollInput(); - while (CurrentFrameCycle < ULADevice.FrameLength) + for (;;) { - ULADevice.CheckForInterrupt(CurrentFrameCycle); - + // run the CPU Monitor cycle CPUMon.ExecuteCycle(); // cycle the tape device if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) TapeDevice.TapeCycle(); + + // has frame end been reached? + if (ULADevice.FrameEnd) + break; } OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength; @@ -190,9 +196,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.EndFrame(); FrameCount++; - - // setup for next frame - ULADevice.ResetInterrupt(); if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) TapeDevice.EndFrame(); @@ -202,8 +205,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // is this a lag frame? Spectrum.IsLagFrame = !InputRead; - // FDC debug - + // FDC debug if (UPDDiskDevice != null && UPDDiskDevice.writeDebug) { // only write UPD log every second @@ -222,7 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// </summary> public virtual void HardReset() { - ULADevice.ResetInterrupt(); + //ULADevice.ResetInterrupt(); ROMPaged = 0; SpecialPagingMode = false; RAMPaged = 0; @@ -274,7 +276,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// </summary> public virtual void SoftReset() { - ULADevice.ResetInterrupt(); + //ULADevice.ResetInterrupt(); ROMPaged = 0; SpecialPagingMode = false; RAMPaged = 0; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index e875a09565..2e9ff98b8c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -89,14 +89,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int InterruptLength; /// <summary> - /// Arbitrary offset into the contention array (for memory ops) - /// </summary> - public int MemoryContentionOffset; - - /// <summary> - /// Arbitrary offset into the contention array (for port ops) - /// </summary> - public int PortContentionOffset; + /// Contention offset + /// </summary> + public int ContentionOffset; /// <summary> /// Arbitrary offset for render table generation @@ -137,60 +132,58 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// </summary> protected bool InterruptRaised; - /// <summary> - /// Signs that the interrupt signal has been revoked - /// </summary> - protected bool InterruptRevoked; + public long ULACycleCounter; + public long LastULATick; + public bool FrameEnd; /// <summary> - /// Resets the interrupt - this should happen every frame in order to raise - /// the VBLANK interrupt in the proceding frame - /// </summary> - public virtual void ResetInterrupt() - { - InterruptRaised = false; - InterruptRevoked = false; - } - - /// <summary> - /// Generates an interrupt in the current phase if needed + /// Cycles the ULA clock + /// Handles interrupt generation /// </summary> /// <param name="currentCycle"></param> - public virtual void CheckForInterrupt(long currentCycle) + public virtual void CycleClock(long totalCycles) { - if (InterruptRevoked) + // has more than one cycle past since this last ran + // (this can be true if contention has taken place) + var ticksToProcess = totalCycles - LastULATick; + + // store the current cycle + LastULATick = totalCycles; + + // process the cycles past as well as the upcoming one + for (int i = 0; i < ticksToProcess; i++) { - // interrupt has already been handled - return; - } + ULACycleCounter++; - if (currentCycle <= InterruptStartTime) - { - // interrupt does not need to be raised yet - return; - } - - if (currentCycle > InterruptStartTime + InterruptLength) - { - // interrupt should have already been raised and the cpu may or - // may not have caught it. The time has passed so revoke the signal - InterruptRevoked = true; - _machine.CPU.FlagI = false; - return; - } - - if (InterruptRaised) - { - // INT is raised but not yet revoked - // CPU has NOT handled it yet - return; - } - - // Raise the interrupt - InterruptRaised = true; - _machine.CPU.FlagI = true; - - CalcFlashCounter(); + if (InterruptRaised) + { + // /INT pin is currently being held low + if (ULACycleCounter < InterruptLength + InterruptStartTime) + { + // ULA should still hold the /INT pin low + _machine.CPU.FlagI = true; + } + else + { + // its time (or past time) to stop holding the /INT pin low + _machine.CPU.FlagI = false; + InterruptRaised = false; + } + } + else + { + // interrupt is currently not raised + if (ULACycleCounter == FrameLength + InterruptStartTime) + { + // time to raise the interrupt + InterruptRaised = true; + _machine.CPU.FlagI = true; + FrameEnd = true; + ULACycleCounter = InterruptStartTime; + CalcFlashCounter(); + } + } + } } /// <summary> @@ -329,7 +322,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { for (var t = 0; t < _ula.FrameCycleLength; t++) { - var tStateScreen = t + _ula.RenderTableOffset + _ula.InterruptStartTime; + var tStateScreen = t + _ula.RenderTableOffset;// + _ula.InterruptStartTime; if (tStateScreen < 0) tStateScreen += _ula.FrameCycleLength; @@ -460,7 +453,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // calculate contention values for (int t = 0; t < _ula.FrameCycleLength; t++) { - int shifted = t + _ula.RenderTableOffset + _ula.InterruptStartTime; + int shifted = t + _ula.RenderTableOffset + _ula.ContentionOffset; // _ula.InterruptStartTime; if (shifted < 0) shifted += _ula.FrameCycleLength; shifted %= _ula.FrameCycleLength; @@ -747,7 +740,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// <returns></returns> public int GetContentionValue(int tstate) { - tstate += MemoryContentionOffset; + //tstate += MemoryContentionOffset; if (tstate >= FrameCycleLength) tstate -= FrameCycleLength; @@ -763,7 +756,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// <returns></returns> public int GetPortContentionValue(int tstate) { - tstate += PortContentionOffset; + //tstate += PortContentionOffset; if (tstate >= FrameCycleLength) tstate -= FrameCycleLength; @@ -994,8 +987,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.BeginSection("ULA"); if (ScreenBuffer != null) ser.Sync("ScreenBuffer", ref ScreenBuffer, false); - ser.Sync("FrameLength", ref FrameCycleLength); - ser.Sync("ClockSpeed", ref ClockSpeed); ser.Sync("BorderColor", ref BorderColor); ser.Sync("LastTState", ref LastTState); ser.Sync("flashOn", ref flashOn); @@ -1010,6 +1001,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("flash", ref flash); ser.Sync("palPaper", ref palPaper); ser.Sync("palInk", ref palInk); + + ser.Sync("LastULATick", ref LastULATick); + ser.Sync("ULACycleCounter", ref ULACycleCounter); + ser.Sync("FrameEnd", ref FrameEnd); ser.EndSection(); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs index 54a0fe058f..06b1d7d9ba 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -13,32 +13,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public Screen128(SpectrumBase machine) : base(machine) { + // interrupt + InterruptStartTime = 3; + InterruptLength = 36; + // offsets + RenderTableOffset = 58; + ContentionOffset = 6; + FloatingBusOffset = 1; // timing ClockSpeed = 3546900; FrameCycleLength = 70908; - InterruptStartTime = 34; - InterruptLength = 36; ScanlineTime = 228; - - MemoryContentionOffset = 6; - PortContentionOffset = 6; - RenderTableOffset = -4; - FloatingBusOffset = 1; - BorderLeftTime = 24; BorderRightTime = 24; - FirstPaperLine = 63; FirstPaperTState = 64; - - Border4T = true; - Border4TStage = 2; - // screen layout + Border4T = true; + Border4TStage = 2; ScreenWidth = 256; ScreenHeight = 192; - BorderTopHeight = 55; // 48; - BorderBottomHeight = 56; + BorderTopHeight = 48; // 55; // 48; + BorderBottomHeight = 48; // 56; BorderLeftWidth = 48; BorderRightWidth = 48; ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs index 2611a7fd43..a95d06d8ce 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -13,43 +13,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public Screen128Plus2a(SpectrumBase machine) : base(machine) { + // interrupt + InterruptStartTime = 0; + InterruptLength = 32; + // offsets + RenderTableOffset = 58; + ContentionOffset = 9; + FloatingBusOffset = 1; // timing ClockSpeed = 3546900; FrameCycleLength = 70908; - /* - InterruptStartTime = 31; - InterruptLength = 32; ScanlineTime = 228; - - MemoryContentionOffset = 7; - PortContentionOffset = 7; - RenderTableOffset = 1; - FloatingBusOffset = 1; - */ - - InterruptStartTime = 33; - InterruptLength = 32; - ScanlineTime = 228; - - MemoryContentionOffset = 6; - PortContentionOffset = 6; - RenderTableOffset = -2; - FloatingBusOffset = 1; - BorderLeftTime = 24; BorderRightTime = 24; - FirstPaperLine = 63; FirstPaperTState = 64; - + // screen layout Border4T = true; Border4TStage = 2; - - // screen layout ScreenWidth = 256; ScreenHeight = 192; - BorderTopHeight = 55; - BorderBottomHeight = 56; + BorderTopHeight = 48;// 55; + BorderBottomHeight = 48; // 56; BorderLeftWidth = 48; BorderRightWidth = 48; ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs index 0a4c236d86..d572f44e37 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -13,32 +13,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public Screen48(SpectrumBase machine) : base(machine) { - // timing - ClockSpeed = 3500000; - FrameCycleLength = 69888; - InterruptStartTime = 33; + // interrupt + InterruptStartTime = 3; InterruptLength = 32; + // offsets + RenderTableOffset = 56; + ContentionOffset = 6; + FloatingBusOffset = 1; + // timing + ClockSpeed = 3500000; + FrameCycleLength = 69888; ScanlineTime = 224; - - MemoryContentionOffset = 6; - PortContentionOffset = 6; - RenderTableOffset = -9; // 2; - FloatingBusOffset = 1; - BorderLeftTime = 24; BorderRightTime = 24; - FirstPaperLine = 64; FirstPaperTState = 64; - + // screen layout Border4T = true; Border4TStage = 0; - - // screen layout ScreenWidth = 256; ScreenHeight = 192; - BorderTopHeight = 55;// 48; - BorderBottomHeight = 56; + BorderTopHeight = 48;// 55;// 48; + BorderBottomHeight = 48;// 56; BorderLeftWidth = 48; BorderRightWidth = 48; ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth;