From f764c137ee924690de9142681bcc0d407776f773 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 5 Jun 2018 17:14:37 +0100 Subject: [PATCH] ZXHawk: 48k timing work --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 334 +++++++++++++----- .../SinclairSpectrum/Machine/SpectrumBase.cs | 8 +- .../Computers/SinclairSpectrum/Machine/ULA.cs | 59 +++- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 2 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 4 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Screen.cs | 2 +- 9 files changed, 311 insertions(+), 104 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 00f581c7aa..1623679213 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -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 } } - /// - /// Called when the first byte of an instruction is fetched - /// - /// - 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 + /// - /// A CPU monitor cycle + /// Handles the ULA and CPU cycle clocks, along with any memory and port contention /// - 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--; + } + */ } + /// + /// Looks up the BUSRQ address that is about to be signalled + /// + /// + 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; + } + /// /// Perfors the actual port contention (if necessary) /// @@ -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; } + /// + /// Called when the first byte of an instruction is fetched + /// + /// + public void OnExecFetch(ushort firstByte) + { + // fetch instruction without incrementing pc + //_cpu.FetchInstruction(_cpu.FetchMemory(firstByte)); + } + #endregion } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index a87c006336..35161bf3a3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -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(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 59abd58290..6a3e7ca04d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -287,6 +287,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public MachineType _machineType; + public int Offset; + /// /// Constructor /// @@ -305,6 +307,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// 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 /// 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 /// public int GetContentionValue() { - var f = _machine.CurrentFrameCycle; - if (f >= FrameCycleLength) - f -= FrameCycleLength; - - return RenderingTable.Renderer[f].ContentionValue; + return GetContentionValue((int)_machine.CurrentFrameCycle); } /// @@ -765,6 +780,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// 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; + } + + /// + /// Returns the contention value for the supplied t-state + /// + /// + 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); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs index bff1ec4334..32a42cea0c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -176,7 +176,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPort(ushort addr) { - CPUMon.ContendPort(addr); + //CPUMon.ContendPort(addr); return; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs index 2423078a83..478fab56c3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -289,7 +289,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPort(ushort addr) { - CPUMon.ContendPort(addr); + //CPUMon.ContendPort(addr); return; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs index d1c30ee3a8..ef2fad7331 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -224,7 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPort(ushort addr) { - CPUMon.ContendPort(addr); + //CPUMon.ContendPort(addr); return; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs index 2006f35c02..45745dcd26 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -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); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs index 127feb84ae..d4170ffd9e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -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 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 f1bc6f89ec..96219e04db 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // timing ClockSpeed = 3500000; FrameCycleLength = 69888; - InterruptStartTime = 32; + InterruptStartTime = 31; InterruptLength = 32; ScanlineTime = 224;