From ae8b030e57968f6fdfad9920113e459eb32bd3ba Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 10:19:42 +0000 Subject: [PATCH] Started new port contention methods and increased the auto-tape monitor timeout (to eliminate false-positive stops) --- .../Hardware/Datacorder/DatacorderDevice.cs | 75 ++---------------- .../Machine/SpectrumBase.Port.cs | 76 +++++++++++++++++++ .../Machine/ZXSpectrum48K/ZX48.Port.cs | 76 +++++++++---------- .../Media/Tape/TzxSerializer.cs | 1 + 4 files changed, 121 insertions(+), 107 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index be338e7fb2..f0bba8ffff 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -144,74 +144,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Primary purpose is to detect tape traps and manage auto play (if/when this is ever implemented) /// public void EndFrame() - {/* - if (TapeIsPlaying) - { - - // check whether we need to auto-stop the tape - if (IsMachineAtErrorAddress()) - { - _machine.Spectrum.OSD_TapeStoppedAuto(); - Stop(); - } - - } - else - { - // the tape is not playing - check to see if we need to autostart the tape - if (IsMachineInLoadMode()) - { - _machine.Spectrum.OSD_TapePlayingAuto(); - Play(); - //sw.Start(); - } - } - /* - if (TapeIsPlaying && sw.IsRunning) - { - if (!IsMachineInLoadMode() && sw.ElapsedMilliseconds == 2000) - { - sw.Stop(); - sw.Reset(); - _machine.Spectrum.OSD_TapeStoppedAuto(); - Stop(); - } - } - */ - + { MonitorFrame(); } - /// - /// Checks whether the machine is in a state that is waiting to load tape content - /// - /// - public bool IsMachineInLoadMode() - { - if (!_machine.Spectrum.Settings.AutoLoadTape) - return false; - - if (_cpu.RegPC == 1523) - return true; - - return false; - } - - /// - /// Checks whether the machine has reached the error rom address (and the tape needs to be stopped) - /// - /// - private bool IsMachineAtErrorAddress() - { - //if (!_machine.Spectrum.Settings.AutoLoadTape) - //return false; - - if (_cpu.RegPC == 64464) // 40620) // ERROR_ROM_ADDRESS) - return true; - else - return false; - } - #endregion #region Tape Controls @@ -656,7 +592,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine.Spectrum.OSD_TapePlayingAuto(); } - _monitorTimeOut = 50; + _monitorTimeOut = 500; } } else @@ -676,6 +612,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _monitorTimeOut--; + if (_monitorTimeOut < 2) + { + + } + if (_monitorTimeOut < 0) { Stop(); @@ -688,8 +629,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region State Serialization - private int _tempBlockCount; - /// /// Bizhawk state serialization /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index d52ac067a2..12b3e34f5a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -39,6 +39,82 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { CPU.TotalExecutedCycles += tStates; } + + /// + /// Simulates IO port contention based on the supplied address + /// This method is for 48k machines and should be overridden for other models + /// + /// + public virtual void ContendPortAddress(ushort addr) + { + /* + It takes four T states for the Z80 to read a value from an I/O port, or write a value to a port. As is the case with memory access, + this can be lengthened by the ULA. There are two effects which occur here: + + If the port address being accessed has its low bit reset, the ULA is required to supply the result, which leads to a delay if it is + currently busy handling the screen. + The address of the port being accessed is placed on the data bus. If this is in the range 0x4000 to 0x7fff, the ULA treats this as an + attempted access to contended memory and therefore introduces a delay. If the port being accessed is between 0xc000 and 0xffff, + this effect does not apply, even on a 128K machine if a contended memory bank is paged into the range 0xc000 to 0xffff. + + These two effects combine to lead to the following contention patterns: + + High byte | | + in 40 - 7F? | Low bit | Contention pattern + ------------+---------+------------------- + No | Reset | N:1, C:3 + No | Set | N:4 + Yes | Reset | C:1, C:3 + Yes | Set | C:1, C:1, C:1, C:1 + + The 'Contention pattern' column should be interpreted from left to right. An "N:n" entry means that no delay is applied at this cycle, and the Z80 continues uninterrupted for 'n' T states. A "C:n" entry means that the ULA halts the Z80; the delay is exactly the same as would occur for a contended memory access at this cycle (eg 6 T states at cycle 14335, 5 at 14336, etc on the 48K machine). After this delay, the Z80 then continues for 'n' cycles. + */ + + // is the low bit reset (i.e. is this addressing the ULA)? + bool lowBit = (addr & 0x0001) != 0; + + if ((addr & 0xc000) == 0x4000 || (addr & 0xc000) == 0xC000) + { + // high byte is in 40 - 7F + if (lowBit) + { + // lowbit is set + // C:1, C:1, C:1, C:1 + for (int i = 0; i < 4; i++) + { + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + CPU.TotalExecutedCycles++; + } + } + else + { + // low bit is reset + // C:1, C:3 + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + CPU.TotalExecutedCycles++; + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + CPU.TotalExecutedCycles += 3; + } + } + else + { + // high byte is NOT in 40 - 7F + if (lowBit) + { + // lowbit is set + // C:1, C:1, C:1, C:1 + CPU.TotalExecutedCycles += 4; + } + else + { + // lowbit is reset + // N:1, C:3 + CPU.TotalExecutedCycles++; + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + CPU.TotalExecutedCycles += 3; + } + } + } } } 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 5dc5c2a514..7e0c4edd2e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -17,10 +17,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { InputRead = true; - // It takes four T states for the Z80 to read a value from an I/O port, or write a value to a port - // (not including added ULA contention) - // The Bizhawk Z80A implementation appears to not consume any T-States for this operation - PortContention(4); + // process IO contention + ContendPortAddress(port); int result = 0xFF; @@ -28,8 +26,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; - ULADevice.Contend(port); - // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) { @@ -90,12 +86,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if ((port & 0x100) == 0) { result &= KeyboardDevice.KeyLine[0]; - } + } + + TapeDevice.MonitorRead(); + + var earBit = TapeDevice.GetEarBit(CPU.TotalExecutedCycles); + + if (!earBit) + { + result &= 0xbf; + } + + /* result = result & 0x1f; //mask out lower 4 bits result = result | 0xa0; //set bit 5 & 7 to 1 - - TapeDevice.MonitorRead(); + if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) { @@ -133,7 +139,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } } - + */ } else { @@ -177,45 +183,37 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { - // It takes four T states for the Z80 to read a value from an I/O port, or write a value to a port - // (not including added ULA contention) - // The Bizhawk Z80A implementation appears to not consume any T-States for this operation - PortContention(4); - + // process IO contention + ContendPortAddress(port); // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x01) == 0; + if ((port & 0x0001) != 0) + return; - ULADevice.Contend(port); + // store the last OUT byte + LastULAOutByte = value; - // Only even addresses address the ULA - if (lowBitReset) - { - // store the last OUT byte - LastULAOutByte = value; - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ - /* - Bit 7 6 5 4 3 2 1 0 - +-------------------------------+ - | | | | E | M | Border | - +-------------------------------+ - */ + // Border - LSB 3 bits hold the border colour + if (ULADevice.borderColour != (value & BORDER_BIT)) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); - // Border - LSB 3 bits hold the border colour - if (ULADevice.borderColour != (value & BORDER_BIT)) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + ULADevice.borderColour = value & BORDER_BIT; - ULADevice.borderColour = value & BORDER_BIT; + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); - // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); - - // Tape - //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + // Tape + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); - } } + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index 76c1b1fd97..9d4188e351 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -268,6 +268,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.DataPeriods = new List(); int pauseLen = GetWordValue(data, _position); + int blockLen = GetWordValue(data, _position + 2); _position += 4;