From d012472999889132abc03ba9ae9d890a6174c11d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 23 Nov 2017 17:26:15 +0000 Subject: [PATCH 001/339] Initial commit. 48K spectrum only. --- ...izHawkSystemIdToCoreSystemEnumConverter.cs | 6 + BizHawk.Client.ApiHawk/Classes/ClientApi.cs | 4 +- BizHawk.Client.Common/Api/CoreSystem.cs | 3 +- BizHawk.Client.Common/Global.cs | 2 + BizHawk.Client.Common/RomLoader.cs | 12 + BizHawk.Client.Common/SystemInfo.cs | 5 + BizHawk.Client.Common/config/PathEntry.cs | 8 +- BizHawk.Client.EmuHawk/FileLoader.cs | 2 +- BizHawk.Client.EmuHawk/MainForm.cs | 3 +- .../config/ControllerConfig.cs | 2 +- .../config/FirmwaresConfig.cs | 1 + BizHawk.Emulation.Common/Database/Database.cs | 18 +- .../Database/FirmwareDatabase.cs | 3 + .../BizHawk.Emulation.Cores.csproj | 23 + .../Computers/SinclairSpectrum/Buzzer.cs | 245 +++++ .../Computers/SinclairSpectrum/IKeyboard.cs | 65 ++ .../SinclairSpectrum/Machine/MachineType.cs | 16 + .../Machine/SpectrumBase.Input.cs | 32 + .../Machine/SpectrumBase.Memory.cs | 147 +++ .../Machine/SpectrumBase.Port.cs | 173 ++++ .../Machine/SpectrumBase.Screen.cs | 910 ++++++++++++++++++ .../Machine/SpectrumBase.Sound.cs | 17 + .../SinclairSpectrum/Machine/SpectrumBase.cs | 174 ++++ .../Machine/ZXSpectrum48K/ZX48.Keyboard.cs | 92 ++ .../Machine/ZXSpectrum48K/ZX48.cs | 38 + .../Computers/SinclairSpectrum/Pulse.cs | 26 + .../Computers/SinclairSpectrum/Tape.cs | 52 + .../ZXSpectrum.Controllers.cs | 85 ++ .../ZXSpectrum.IDebuggable.cs | 147 +++ .../SinclairSpectrum/ZXSpectrum.IEmulator.cs | 51 + .../ZXSpectrum.IInputPollable.cs | 26 + .../ZXSpectrum.IMemoryDomains.cs | 68 ++ .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 95 ++ .../ZXSpectrum.ISoundProvider.cs | 10 + .../SinclairSpectrum/ZXSpectrum.IStatable.cs | 75 ++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 135 +++ 36 files changed, 2761 insertions(+), 10 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Buzzer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/IKeyboard.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Pulse.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs diff --git a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs index 31b65c5df5..fcce1118cf 100644 --- a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs +++ b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs @@ -96,6 +96,9 @@ namespace BizHawk.Client.ApiHawk case "WSWAN": return CoreSystem.WonderSwan; + case "ZXSpectrum": + return CoreSystem.ZXSpectrum; + case "VB": case "NGP": case "DNGP": @@ -205,6 +208,9 @@ namespace BizHawk.Client.ApiHawk case CoreSystem.WonderSwan: return "WSWAN"; + case CoreSystem.ZXSpectrum: + return "ZXSpectrum"; + default: throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value.ToString())); } diff --git a/BizHawk.Client.ApiHawk/Classes/ClientApi.cs b/BizHawk.Client.ApiHawk/Classes/ClientApi.cs index 12ee67277c..767d755105 100644 --- a/BizHawk.Client.ApiHawk/Classes/ClientApi.cs +++ b/BizHawk.Client.ApiHawk/Classes/ClientApi.cs @@ -427,11 +427,11 @@ namespace BizHawk.Client.ApiHawk } else { - return SystemInfo.DualGB; + return SystemInfo.DualGB; } default: - return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(Global.Emulator.SystemId)); + return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(Global.Emulator.SystemId)); } } } diff --git a/BizHawk.Client.Common/Api/CoreSystem.cs b/BizHawk.Client.Common/Api/CoreSystem.cs index 132a832b98..9bdc7d8232 100644 --- a/BizHawk.Client.Common/Api/CoreSystem.cs +++ b/BizHawk.Client.Common/Api/CoreSystem.cs @@ -29,6 +29,7 @@ WonderSwan, Libretro, VirtualBoy, - NeoGeoPocket + NeoGeoPocket, + ZXSpectrum } } diff --git a/BizHawk.Client.Common/Global.cs b/BizHawk.Client.Common/Global.cs index b592f1696b..edd287ef49 100644 --- a/BizHawk.Client.Common/Global.cs +++ b/BizHawk.Client.Common/Global.cs @@ -149,6 +149,8 @@ namespace BizHawk.Client.Common return SystemInfo.VirtualBoy; case "NGP": return SystemInfo.NeoGeoPocket; + case "ZXSpectrum": + return SystemInfo.ZXSpectrum; } } } diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 99e355081e..cd1ce95f22 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -19,6 +19,7 @@ using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.Saturn; using BizHawk.Emulation.Cores.Sony.PSP; using BizHawk.Emulation.Cores.Sony.PSX; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; using BizHawk.Emulation.DiscSystem; using GPGX64 = BizHawk.Emulation.Cores.Consoles.Sega.gpgx; @@ -657,6 +658,13 @@ namespace BizHawk.Client.Common (C64.C64Settings)GetCoreSettings(), (C64.C64SyncSettings)GetCoreSyncSettings()); break; + case "ZXSpectrum": + nextEmulator = new ZXSpectrum( + nextComm, + xmlGame.Assets.Select(a => a.Value).First(), + (ZXSpectrum.ZXSpectrumSettings)GetCoreSettings(), + (ZXSpectrum.ZXSpectrumSyncSettings)GetCoreSyncSettings()); + break; case "PSX": var entries = xmlGame.AssetFullPaths; var discs = new List(); @@ -990,6 +998,10 @@ namespace BizHawk.Client.Common var c64 = new C64(nextComm, Enumerable.Repeat(rom.RomData, 1), rom.GameInfo, GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = c64; break; + case "ZXSpectrum": + var zx = new ZXSpectrum(nextComm, rom.FileData, GetCoreSettings(), GetCoreSyncSettings()); + nextEmulator = zx; + break; case "GBA": if (Global.Config.GBA_UsemGBA) { diff --git a/BizHawk.Client.Common/SystemInfo.cs b/BizHawk.Client.Common/SystemInfo.cs index 82414adc50..2d65f1729c 100644 --- a/BizHawk.Client.Common/SystemInfo.cs +++ b/BizHawk.Client.Common/SystemInfo.cs @@ -188,6 +188,11 @@ namespace BizHawk.Client.Common /// public static SystemInfo NeoGeoPocket { get; } = new SystemInfo("Neo-Geo Pocket", CoreSystem.NeoGeoPocket, 1); + /// + /// Gets the instance for ZXSpectrum + /// + public static SystemInfo ZXSpectrum { get; } = new SystemInfo("ZX Spectrum", CoreSystem.ZXSpectrum, 2); + #endregion Get SystemInfo /// diff --git a/BizHawk.Client.Common/config/PathEntry.cs b/BizHawk.Client.Common/config/PathEntry.cs index 0be34e26c7..75f5fd0bc6 100644 --- a/BizHawk.Client.Common/config/PathEntry.cs +++ b/BizHawk.Client.Common/config/PathEntry.cs @@ -290,7 +290,13 @@ namespace BizHawk.Client.Common new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, - new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Base", Path = Path.Combine(".", "PSX"), Ordinal = 0 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Base", Path = Path.Combine(".", "C64"), Ordinal = 0 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "ROM", Path = ".", Ordinal = 1 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, + + new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Base", Path = Path.Combine(".", "PSX"), Ordinal = 0 }, new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "ROM", Path = ".", Ordinal = 1 }, new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 }, diff --git a/BizHawk.Client.EmuHawk/FileLoader.cs b/BizHawk.Client.EmuHawk/FileLoader.cs index 0009646fb4..db3c00d76d 100644 --- a/BizHawk.Client.EmuHawk/FileLoader.cs +++ b/BizHawk.Client.EmuHawk/FileLoader.cs @@ -51,7 +51,7 @@ namespace BizHawk.Client.EmuHawk return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", - ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS" + ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX" }; } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 170dbb2322..750c70fcf8 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -2077,7 +2077,7 @@ namespace BizHawk.Client.EmuHawk if (VersionInfo.DeveloperBuild) { return FormatFilter( - "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;%ARCH%", + "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;%ARCH%", "Music Files", "*.psf;*.minipsf;*.sid;*.nsf", "Disc Images", "*.cue;*.ccd;*.mds;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", @@ -2105,6 +2105,7 @@ namespace BizHawk.Client.EmuHawk "Apple II", "*.dsk;*.do;*.po;%ARCH%", "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", + "Sinclair ZX Spectrum", "*.tzx;*.tap;%ARCH%", "All Files", "*.*"); } diff --git a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs index ab488b1724..9800e5ec13 100644 --- a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs +++ b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs @@ -176,7 +176,7 @@ namespace BizHawk.Client.EmuHawk if (buckets[0].Count > 0) { - string tabname = Global.Emulator.SystemId == "C64" ? "Keyboard" : "Console"; // hack + string tabname = (Global.Emulator.SystemId == "C64" || Global.Emulator.SystemId == "ZXSpectrum") ? "Keyboard" : "Console"; // hack tt.TabPages.Add(tabname); tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[0], tt.Size)); } diff --git a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs index 5b2e5eca92..7b016a8cab 100644 --- a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs +++ b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs @@ -52,6 +52,7 @@ namespace BizHawk.Client.EmuHawk { "GBC", "Game Boy Color" }, { "PCFX", "PC-FX" }, { "32X", "32X" }, + { "ZXSpectrum", "ZX Spectrum" } }; public string TargetSystem = null; diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index df1a2562c7..d2d2bfccee 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; using BizHawk.Common.BufferExtensions; +using System.Linq; namespace BizHawk.Emulation.Common { @@ -298,12 +299,23 @@ namespace BizHawk.Emulation.Common case ".D64": case ".T64": case ".G64": - case ".CRT": - case ".TAP": + case ".CRT": game.System = "C64"; break; - case ".Z64": + case ".TZX": + game.System = "ZXSpectrum"; + break; + + case ".TAP": + byte[] head = File.ReadAllBytes(fileName).Take(8).ToArray(); + if (System.Text.Encoding.Default.GetString(head).Contains("C64-TAPE")) + game.System = "C64"; + else + game.System = "ZXSpectrum"; + break; + + case ".Z64": case ".V64": case ".N64": game.System = "N64"; diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index dde7623f46..7d8eb5fe63 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -50,6 +50,9 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("AB16F56989B27D89BABE5F89C5A8CB3DA71A82F0", 16384, "C64", "Drive1541", "drive-1541.bin", "1541 Disk Drive Rom"); FirmwareAndOption("D3B78C3DBAC55F5199F33F3FE0036439811F7FB3", 16384, "C64", "Drive1541II", "drive-1541ii.bin", "1541-II Disk Drive Rom"); + // ZX Spectrum + FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); + // for saturn, we think any bios region can pretty much run any iso // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 05baf2d9d0..63380c7fa3 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -256,6 +256,26 @@ + + + + + + + + + + + + + + + + + + + + Atari2600.cs @@ -1328,6 +1348,8 @@ + + @@ -1338,6 +1360,7 @@ + "$(SolutionDir)subwcrev.bat" "$(ProjectDir)" diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Buzzer.cs new file mode 100644 index 0000000000..6a34619dcd --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Buzzer.cs @@ -0,0 +1,245 @@ + +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the piezoelectric buzzer used in the Spectrum to produce sound + /// The beeper is controlled by rapidly toggling bit 4 of port &FE + /// + /// For the purposes of emulation this devices is locked to a frame + /// a list of Pulses is built up over the course of the frame and outputted at the end of the frame + /// + public class Buzzer : ISoundProvider + { + /// + /// Supplied values are right for 48K spectrum + /// These will deviate for 128k and up (as there are more T-States per frame) + /// + public int SampleRate = 44100; //35000; + public int SamplesPerFrame = 882; //699; + public int TStatesPerSample = 79; //100; + + public BlipBuffer BlipL { get; set; } + public BlipBuffer BlipR { get; set; } + + private SpectrumBase _machine; + + private long _frameStart; + private bool _tapeMode; + private int _tStatesPerFrame; + + public SpeexResampler resampler { get; set; } + + /// + /// Pulses collected during the last frame + /// + public List Pulses { get; private set; } + + /// + /// The last pulse + /// + public bool LastPulse { get; private set; } + + /// + /// The last T-State (cpu cycle) that the last pulse was received + /// + public int LastPulseTState { get; set; } + + #region Construction & Initialisation + + public Buzzer(SpectrumBase machine) + { + _machine = machine; + } + + /// + /// Initialises the buzzer + /// + public void Init() + { + _tStatesPerFrame = _machine.UlaFrameCycleCount; + Pulses = new List(1000); + } + + #endregion + + /// + /// When the pulse value from the EAR output changes it is processed here + /// + /// + /// + public void ProcessPulseValue(bool fromTape, bool earPulse) + { + if (!fromTape && _tapeMode) + { + // tape mode is active but the pulse value came from an OUT instruction + // do not process the value + //return; + } + + if (earPulse == LastPulse) + { + // no change detected + return; + } + + // set the lastpulse + LastPulse = earPulse; + + // get where we are in the frame + var currentULACycle = _machine.CurrentFrameCycle; + var currentBuzzerCycle = currentULACycle <= _tStatesPerFrame ? currentULACycle : _tStatesPerFrame; + var length = currentBuzzerCycle - LastPulseTState; + + if (length == 0) + { + // the first T-State has changed the pulse + // do not add it + } + else if (length > 0) + { + // add the pulse + Pulse p = new Pulse + { + State = !earPulse, + Length = length + }; + Pulses.Add(p); + } + + // set the last pulse tstate + LastPulseTState = currentBuzzerCycle; + } + + /// + /// New frame starts + /// + public void StartFrame() + { + //DiscardSamples(); + Pulses.Clear(); + LastPulseTState = 0; + } + + /// + /// Frame is completed + /// + public void EndFrame() + { + // store the last pulse information + if (LastPulseTState <= _tStatesPerFrame - 1) + { + Pulse p = new Pulse + { + State = LastPulse, + Length = _tStatesPerFrame - LastPulseTState + }; + Pulses.Add(p); + } + + // create the sample array + var firstSampleOffset = _frameStart % TStatesPerSample == 0 ? 0 : TStatesPerSample - (_frameStart + TStatesPerSample) % TStatesPerSample; + var samplesInFrame = (_tStatesPerFrame - firstSampleOffset - 1) / TStatesPerSample + 1; + var samples = new short[samplesInFrame]; + + // convert pulses to samples + var sampleIndex = 0; + var currentEnd = _frameStart; + + foreach (var pulse in Pulses) + { + var firstSample = currentEnd % TStatesPerSample == 0 + ? currentEnd : currentEnd + TStatesPerSample - currentEnd % TStatesPerSample; + + for (var i = firstSample; i < currentEnd + pulse.Length; i += TStatesPerSample) + { + samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 2) : (short)0; + + //resampler.EnqueueSample(samples[sampleIndex - 1], samples[sampleIndex - 1]); + + } + + + currentEnd += pulse.Length; + } + + // fill the _sampleBuffer for ISoundProvider + soundBufferContains = (int)samplesInFrame; + + if (soundBuffer.Length != soundBufferContains) + soundBuffer = new short[soundBufferContains]; + + samples.CopyTo(soundBuffer, 0); + + _frameStart += _tStatesPerFrame; + } + + /// + /// When the spectrum is set to receive tape input, the EAR output on the ULA is disabled + /// (so no buzzer sound is emitted) + /// + /// + public void SetTapeMode(bool tapeMode) + { + _tapeMode = tapeMode; + } + + + #region ISoundProvider + + private short[] soundBuffer = new short[882]; + private int soundBufferContains = 0; + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + short[] stereoBuffer = new short[soundBuffer.Length * 2]; + int index = 0; + for (int i = 0; i < soundBufferContains; i++) + { + stereoBuffer[index++] = soundBuffer[i]; + stereoBuffer[index++] = soundBuffer[i]; + } + + samples = stereoBuffer; + } + + public void DiscardSamples() + { + soundBufferContains = 0; + soundBuffer = new short[SamplesPerFrame]; + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + // convert to stereo + short[] stereoBuffer = new short[soundBufferContains * 2]; + int index = 0; + for (int i = 0; i < soundBufferContains; i++) + { + stereoBuffer[index++] = soundBuffer[i]; + stereoBuffer[index++] = soundBuffer[i]; + } + + samples = stereoBuffer; + nsamp = soundBufferContains; + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/IKeyboard.cs new file mode 100644 index 0000000000..f053d044aa --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/IKeyboard.cs @@ -0,0 +1,65 @@ + + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a spectrum keyboard + /// + public interface IKeyboard + { + /// + /// The calling spectrumbase class + /// + SpectrumBase _machine { get; } + + /// + /// The keyboard matrix for a particular spectrum model + /// + string[] KeyboardMatrix { get; set; } + + /// + /// For 16/48k models + /// + bool Issue2 { get; set; } + + /// + /// The current keyboard line status + /// + //byte[] LineStatus { get; set; } + + /// + /// Sets the spectrum key status + /// + /// + /// + void SetKeyStatus(string key, bool isPressed); + + /// + /// Gets the status of a spectrum key + /// + /// + /// + bool GetKeyStatus(string key); + + /// + /// Returns the query byte + /// + /// + /// + byte GetLineStatus(byte lines); + + /// + /// Reads a keyboard byte + /// + /// + /// + byte ReadKeyboardByte(ushort addr); + + /// + /// Looks up a key in the keyboard matrix and returns the relevent byte value + /// + /// + /// + byte GetByteFromKeyMatrix(string key); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs new file mode 100644 index 0000000000..829f1a0e5b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public enum MachineType + { + /// + /// Sinclair Spectrum 48K model + /// + ZXSpectrum48 + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs new file mode 100644 index 0000000000..a9eedbc44d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -0,0 +1,32 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Handles all ZX-level input + /// + public abstract partial class SpectrumBase + { + private readonly bool[] _keyboardPressed = new bool[64]; + int _pollIndex; + private bool _restorePressed; + + + public void PollInput() + { + Spectrum.InputCallbacks.Call(); + + // scan keyboard + _pollIndex = 0; + + for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++) + { + string key = KeyboardDevice.KeyboardMatrix[i]; + bool prevState = KeyboardDevice.GetKeyStatus(key); + bool currState = Spectrum._controller.IsPressed(key); + + //if (currState != prevState) + KeyboardDevice.SetKeyStatus(key, currState); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs new file mode 100644 index 0000000000..d37ab3622b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Memory * + /// + public abstract partial class SpectrumBase + { + /// + /// Byte array of total system memory (ROM + RAM + paging) + /// + public byte[] RAM { get; set; } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public virtual byte ReadMemory(ushort addr) + { + var data = RAM[addr]; + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Reads a byte of data from a specified memory address + /// (with no memory contention) + /// + /// + /// + public virtual byte PeekMemory(ushort addr) + { + var data = RAM[addr]; + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public virtual void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + if (!CPU.IFF1) + { + + } + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + else + { + // uncontended RAM - do nothing + } + + /* + + // Check whether memory is ROM or RAM + switch (addr & 0xC000) + { + case 0x0000: + // Do nothing - we cannot write to ROM + return; + case 0x4000: + // Address is RAM - apply contention if neccessary + var delay = GetContentionValue(_frameCycles); + CPU.TotalExecutedCycles += delay; + break; + } + */ + RAM[addr] = value; + } + + /// + /// Writes a byte of data to a specified memory address + /// (without contention) + /// + /// + /// + public virtual void PokeMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + + RAM[addr] = value; + } + + /// + /// Fills memory from buffer + /// + /// + /// + public virtual void FillMemory(byte[] buffer, ushort startAddress) + { + buffer?.CopyTo(RAM, startAddress); + } + + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// + /// + /// + public virtual byte FetchScreenMemory(ushort addr) + { + var value = RAM[(addr & 0x3FFF) + 0x4000]; + return value; + } + + /// + /// Returns the memory contention value for the specified T-State (cycle) + /// The ZX Spectrum memory access is contended when the ULA is accessing the lower 16k of RAM + /// + /// + /// + public virtual byte GetContentionValue(int cycle) + { + var val = _renderingCycleTable[cycle % UlaFrameCycleCount].ContentionDelay; + return val; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs new file mode 100644 index 0000000000..29f7c2b6eb --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Port Access * + /// + public abstract partial class SpectrumBase + { + /// + /// The last OUT data that was sent to the ULA + /// + protected byte LastULAOutByte; + + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public virtual byte ReadPort(ushort port) + { + CPU.TotalExecutedCycles += 4; + + byte result = 0xFF; + + // get the high byte from Regs[6] + ushort high = CPU.Regs[6]; + + // combine the low byte (passed in as port) and the high byte (maybe not needed) + ushort word = Convert.ToUInt16(((byte)port << 8 | (byte)high)); + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + + // Kempston Joystick + //not implemented yet + + if (lowBitReset) + { + // Even I/O address so get input + // The high byte indicates which half-row of keys is being polled + /* + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + */ + + // read keyboard input + if (high != 0) + result = KeyboardDevice.GetLineStatus((byte)high); + + var ear = TapeDevice.GetEarBit(CurrentFrameCycle); + if (!ear) + { + result = (byte)(result & Convert.ToInt32("10111111", 2)); + } + + /* + + bool tapeIsPlaying = false; + int tapeBit = 0; + + if (tapeIsPlaying) + { + if (tapeBit == 0) + { + // reset is EAR ON + result = (byte)(result & ~(TAPE_BIT)); + } + else + { + // set is EAR OFF + result |= TAPE_BIT; + } + } + else + { + if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result = (byte)(result & ~(TAPE_BIT)); + } + else + { + result |= TAPE_BIT; + } + } + /* + // read EAR pulse from tape device + //todo + + bool earBit = false; + + if (earBit) + tapeBit = Convert.ToInt32("11111111", 2); + else + tapeBit = Convert.ToInt32("10111111", 2); + + //var earBit = _tapeDevice.GetEarBit(_cpu.Tacts); + + if (!earBit) + result = (byte)(result & tapeBit); + */ + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate + // Kemptson Mouse + + + // if unused port the floating memory bus should be returned (still todo) + } + + return result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public virtual void WritePort(ushort port, byte value) + { + CPU.TotalExecutedCycles += 4; + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + BorderColour = value & BORDER_BIT; + + // Buzzer + var beepVal = (int)value & (EAR_BIT); + + if (((int)value & MIC_BIT) == 0) + beepVal = (int)value & (MIC_BIT); + + //var micval = (int)value & (MIC_BIT); + + + // if tape is not playing + BuzzerDevice.ProcessPulseValue(false, (beepVal) != 0); + + // tape + //_tapeDevice.ProcessMicBit((data & 0x08) != 0); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs new file mode 100644 index 0000000000..4023bf112c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -0,0 +1,910 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Screen * + /// - A goodly portion of the screen rendering code has been taken from: + /// - https://github.com/Dotneteer/spectnetide + /// - (MIT Licensed) + /// + public abstract partial class SpectrumBase : IVideoProvider + { + #region State + + /// + /// The main screen buffer + /// + protected byte[] _frameBuffer; + + /// + /// Pixel and attribute info stored while rendering the screen + /// + protected byte _pixelByte1; + protected byte _pixelByte2; + protected byte _attrByte1; + protected byte _attrByte2; + protected int _xPos; + protected int _yPos; + protected int[] _flashOffColors; + protected int[] _flashOnColors; + protected ScreenRenderingCycle[] _renderingCycleTable; + protected bool _flashPhase; + + #endregion + + #region Statics + + /// + /// The standard ULA palette + /// + private static readonly int[] ULAPalette = + { + Colors.ARGB(0x00, 0x00, 0x00), // Black + Colors.ARGB(0x00, 0x00, 0xD7), // Blue + Colors.ARGB(0xD7, 0x00, 0x00), // Red + Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta + Colors.ARGB(0x00, 0xD7, 0x00), // Green + Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan + Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow + Colors.ARGB(0xD7, 0xD7, 0xD7), // White + Colors.ARGB(0x00, 0x00, 0x00), // Bright Black + Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue + Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red + Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta + Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green + Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan + Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow + Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White + }; + + #endregion + + #region ScreenConfig + + /// + /// The number of displayed pixels in a display row + /// + protected int DisplayWidth = 256; + + /// + /// Number of display lines + /// + protected int DisplayLines = 192; + + /// + /// The number of frames after the flash is toggled + /// + protected int FlashToggleFrames = 25; + + /// + /// Number of lines used for vertical sync + /// + protected int VerticalSyncLines = 8; + + /// + /// The number of top border lines that are not visible + /// when rendering the screen + /// + protected int NonVisibleBorderTopLines = 8; + + /// + /// The number of border lines before the display + /// + protected int BorderTopLines = 48; + + /// + /// The number of border lines after the display + /// + protected int BorderBottomLines = 48; + + /// + /// The number of bottom border lines that are not visible + /// when rendering the screen + /// + protected int NonVisibleBorderBottomLines = 8; + + /// + /// The total number of lines in the screen + /// + protected int ScreenLines; + + /// + /// The first screen line that contains the top left display pixel + /// + protected int FirstDisplayLine; + + /// + /// The last screen line that contains the bottom right display pixel + /// + protected int LastDisplayLine; + + /// + /// The number of border pixels to the left of the display + /// + protected int BorderLeftPixels = 48; + + /// + /// The number of border pixels to the right of the display + /// + protected int BorderRightPixels = 48; + + /// + /// The total width of the screen in pixels + /// + protected int ScreenWidth; + + /// + /// Horizontal blanking time (HSync+blanking). + /// Given in Z80 clock cycles. + /// + protected int HorizontalBlankingTime = 40; + + /// + /// The time of displaying left part of the border. + /// Given in Z80 clock cycles. + /// + protected int BorderLeftTime = 24; + + /// + /// The time of displaying a pixel row. + /// Given in Z80 clock cycles. + /// + protected int DisplayLineTime = 128; + + /// + /// The time of displaying right part of the border. + /// Given in Z80 clock cycles. + /// + protected int BorderRightTime = 24; + + /// + /// The time used to render the nonvisible right part of the border. + /// Given in Z80 clock cycles. + /// + protected int NonVisibleBorderRightTime = 8; + + /// + /// The time of displaying a full screen line. + /// Given in Z80 clock cycles. + /// + protected int ScreenLineTime; + + /// + /// The time the data of a particular pixel should be prefetched + /// before displaying it. + /// Given in Z80 clock cycles. + /// + protected int PixelDataPrefetchTime = 2; + + /// + /// The time the data of a particular pixel attribute should be prefetched + /// before displaying it. + /// Given in Z80 clock cycles. + /// + protected int AttributeDataPrefetchTime = 1; + + /// + /// The tact within the line that should display the first pixel. + /// Given in Z80 clock cycles. + /// + protected int FirstPixelCycleInLine; + + /// + /// The tact in which the top left pixel should be displayed. + /// Given in Z80 clock cycles. + /// + protected int FirstDisplayPixelCycle; + + /// + /// The tact in which the top left screen pixel (border) should be displayed + /// + protected int FirstScreenPixelCycle; + + /// + /// Defines the number of Z80 clock cycles used for the full rendering + /// of the screen. + /// + public int UlaFrameCycleCount; + + /// + /// The last rendered ULA cycle + /// + public int LastRenderedULACycle; + + + /// + /// This structure defines information related to a particular T-State + /// (cycle) of ULA screen rendering + /// + [StructLayout(LayoutKind.Explicit)] + public struct ScreenRenderingCycle + { + /// + /// Tha rendering phase to be applied for the particular tact + /// + [FieldOffset(0)] + public ScreenRenderingPhase Phase; + + /// + /// Display memory contention delay + /// + [FieldOffset(1)] + public byte ContentionDelay; + + /// + /// Display memory address used in the particular tact + /// + [FieldOffset(2)] + public ushort PixelByteToFetchAddress; + + /// + /// Display memory address used in the particular tact + /// + [FieldOffset(4)] + public ushort AttributeToFetchAddress; + + /// + /// Pixel X coordinate + /// + [FieldOffset(6)] + public ushort XPos; + + /// + /// Pixel Y coordinate + /// + [FieldOffset(8)] + public ushort YPos; + } + + /// + /// This enumeration defines the particular phases of ULA rendering + /// + public enum ScreenRenderingPhase : byte + { + /// + /// The ULA does not do any rendering + /// + None, + + /// + /// The ULA simple sets the border color to display the current pixel. + /// + Border, + + /// + /// The ULA sets the border color to display the current pixel. It + /// prepares to display the fist pixel in the row with prefetching the + /// corresponding byte from the display memory. + /// + BorderAndFetchPixelByte, + + /// + /// The ULA sets the border color to display the current pixel. It has + /// already fetched the 8 pixel bits to display. It carries on + /// preparing to display the fist pixel in the row with prefetching the + /// corresponding attribute byte from the display memory. + /// + BorderAndFetchPixelAttribute, + + /// + /// The ULA displays the next two pixels of Byte1 sequentially during a + /// single Z80 clock cycle. + /// + DisplayByte1, + + /// + /// The ULA displays the next two pixels of Byte1 sequentially during a + /// single Z80 clock cycle. It prepares to display the pixels of the next + /// byte in the row with prefetching the corresponding byte from the + /// display memory. + /// + DisplayByte1AndFetchByte2, + + /// + /// The ULA displays the next two pixels of Byte1 sequentially during a + /// single Z80 clock cycle. It prepares to display the pixels of the next + /// byte in the row with prefetching the corresponding attribute from the + /// display memory. + /// + DisplayByte1AndFetchAttribute2, + + /// + /// The ULA displays the next two pixels of Byte2 sequentially during a + /// single Z80 clock cycle. + /// + DisplayByte2, + + /// + /// The ULA displays the next two pixels of Byte2 sequentially during a + /// single Z80 clock cycle. It prepares to display the pixels of the next + /// byte in the row with prefetching the corresponding byte from the + /// display memory. + /// + DisplayByte2AndFetchByte1, + + /// + /// The ULA displays the next two pixels of Byte2 sequentially during a + /// single Z80 clock cycle. It prepares to display the pixels of the next + /// byte in the row with prefetching the corresponding attribute from the + /// display memory. + /// + DisplayByte2AndFetchAttribute1 + } + + #endregion + + #region Border + + private int _borderColour; + + /// + /// Gets or sets the ULA border color + /// + public int BorderColour + { + get { return _borderColour; } + set { _borderColour = value & 0x07; } + } + + protected virtual void ResetBorder() + { + BorderColour = 0; + } + + #endregion + + #region Screen Methods + + /// + /// ULA renders the screen between two specified T-States (cycles) + /// + /// + /// + public void RenderScreen(int fromCycle, int toCycle) + { + // Adjust cycle boundaries + fromCycle = fromCycle % UlaFrameCycleCount; + toCycle = toCycle % UlaFrameCycleCount; + + // Do rendering action for cycles based on the rendering phase + for (int curr = fromCycle; curr <= toCycle; curr++) + { + var ulaCycle = _renderingCycleTable[curr]; + _xPos = ulaCycle.XPos; + _yPos = ulaCycle.YPos; + + switch (ulaCycle.Phase) + { + case ScreenRenderingPhase.None: + // --- Invisible screen area, nothing to do + break; + + case ScreenRenderingPhase.Border: + // --- Fetch the border color from ULA and set the corresponding border pixels + SetPixels(BorderColour, BorderColour); + break; + + case ScreenRenderingPhase.BorderAndFetchPixelByte: + // --- Fetch the border color from ULA and set the corresponding border pixels + SetPixels(BorderColour, BorderColour); + // --- Obtain the future pixel byte + _pixelByte1 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); + break; + + case ScreenRenderingPhase.BorderAndFetchPixelAttribute: + // --- Fetch the border color from ULA and set the corresponding border pixels + SetPixels(BorderColour, BorderColour); + // --- Obtain the future attribute byte + _attrByte1 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); + break; + + case ScreenRenderingPhase.DisplayByte1: + // --- Display bit 7 and 6 according to the corresponding color + SetPixels( + GetColor(_pixelByte1 & 0x80, _attrByte1), + GetColor(_pixelByte1 & 0x40, _attrByte1)); + // --- Shift in the subsequent bits + _pixelByte1 <<= 2; + break; + + case ScreenRenderingPhase.DisplayByte1AndFetchByte2: + // --- Display bit 7 and 6 according to the corresponding color + SetPixels( + GetColor(_pixelByte1 & 0x80, _attrByte1), + GetColor(_pixelByte1 & 0x40, _attrByte1)); + // --- Shift in the subsequent bits + _pixelByte1 <<= 2; + // --- Obtain the next pixel byte + _pixelByte2 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); + break; + + case ScreenRenderingPhase.DisplayByte1AndFetchAttribute2: + // --- Display bit 7 and 6 according to the corresponding color + SetPixels( + GetColor(_pixelByte1 & 0x80, _attrByte1), + GetColor(_pixelByte1 & 0x40, _attrByte1)); + // --- Shift in the subsequent bits + _pixelByte1 <<= 2; + // --- Obtain the next attribute + _attrByte2 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); + break; + + case ScreenRenderingPhase.DisplayByte2: + // --- Display bit 7 and 6 according to the corresponding color + SetPixels( + GetColor(_pixelByte2 & 0x80, _attrByte2), + GetColor(_pixelByte2 & 0x40, _attrByte2)); + // --- Shift in the subsequent bits + _pixelByte2 <<= 2; + break; + + case ScreenRenderingPhase.DisplayByte2AndFetchByte1: + // --- Display bit 7 and 6 according to the corresponding color + SetPixels( + GetColor(_pixelByte2 & 0x80, _attrByte2), + GetColor(_pixelByte2 & 0x40, _attrByte2)); + // --- Shift in the subsequent bits + _pixelByte2 <<= 2; + // --- Obtain the next pixel byte + _pixelByte1 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); + break; + + case ScreenRenderingPhase.DisplayByte2AndFetchAttribute1: + // --- Display bit 7 and 6 according to the corresponding color + SetPixels( + GetColor(_pixelByte2 & 0x80, _attrByte2), + GetColor(_pixelByte2 & 0x40, _attrByte2)); + // --- Shift in the subsequent bits + _pixelByte2 <<= 2; + // --- Obtain the next attribute + _attrByte1 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); + break; + } + } + } + + /// + /// Tests whether the specified cycle is in the visible area of the screen. + /// + /// Line index + /// Tacts index within the line + /// + /// True, if the tact is visible on the screen; otherwise, false + /// + public virtual bool IsCycleVisible(int line, int cycleInLine) + { + var firstVisibleLine = VerticalSyncLines + NonVisibleBorderTopLines; + var lastVisibleLine = firstVisibleLine + BorderTopLines + DisplayLines + BorderBottomLines; + return + line >= firstVisibleLine + && line < lastVisibleLine + && cycleInLine >= HorizontalBlankingTime + && cycleInLine < ScreenLineTime - NonVisibleBorderRightTime; + } + + /// + /// Tests whether the cycle is in the display area of the screen. + /// + /// Line index + /// Tacts index within the line + /// + /// True, if the tact is within the display area of the screen; otherwise, false. + /// + public virtual bool IsCycleInDisplayArea(int line, int cycleInLine) + { + return line >= FirstDisplayLine + && line <= LastDisplayLine + && cycleInLine >= FirstPixelCycleInLine + && cycleInLine < FirstPixelCycleInLine + DisplayLineTime; + } + + /// + /// Sets the two adjacent screen pixels belonging to the specified cycle to the given + /// color + /// + /// Color index of the first pixel + /// Color index of the second pixel + protected virtual void SetPixels(int colorIndex1, int colorIndex2) + { + var pos = _yPos * ScreenWidth + _xPos; + _frameBuffer[pos++] = (byte)colorIndex1; + _frameBuffer[pos] = (byte)colorIndex2; + } + + /// + /// Gets the color index for the specified pixel value according + /// to the given color attribute + /// + /// 0 for paper pixel, non-zero for ink pixel + /// Color attribute + /// + protected virtual int GetColor(int pixelValue, byte attr) + { + var offset = (pixelValue == 0 ? 0 : 0x100) + attr; + return _flashPhase + ? _flashOnColors[offset] + : _flashOffColors[offset]; + } + + /// + /// Resets the ULA cycle to start screen rendering from the beginning + /// + protected virtual void ResetULACycle() + { + LastRenderedULACycle = -1; + } + + /// + /// Initializes the ULA cycle table + /// + protected virtual void InitULACycleTable() + { + _renderingCycleTable = new ScreenRenderingCycle[UlaFrameCycleCount]; + + // loop through every cycle + for (var cycle = 0; cycle < UlaFrameCycleCount; cycle++) + { + var line = cycle / ScreenLineTime; + var cycleInLine = cycle % ScreenLineTime; + + var cycleItem = new ScreenRenderingCycle + { + Phase = ScreenRenderingPhase.None, + ContentionDelay = 0 + }; + + if (IsCycleVisible(line, cycleInLine)) + { + // calculate pixel positions + cycleItem.XPos = (ushort)((cycleInLine - HorizontalBlankingTime) * 2); + cycleItem.YPos = (ushort)(line - VerticalSyncLines - NonVisibleBorderTopLines); + + if (!IsCycleInDisplayArea(line, cycleInLine)) + { + // we are in the border + cycleItem.Phase = ScreenRenderingPhase.Border; + // set the border colour + if (line >= FirstDisplayLine && + line <= LastDisplayLine) + { + if (cycleInLine == FirstPixelCycleInLine - PixelDataPrefetchTime) + { + // left or right border beside the display area + // fetch the first pixel data byte of the current line (2 cycles away) + cycleItem.Phase = ScreenRenderingPhase.BorderAndFetchPixelByte; + cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); + cycleItem.ContentionDelay = 6; + } + else if (cycleInLine == FirstPixelCycleInLine - AttributeDataPrefetchTime) + { + // fetch the first attribute data byte of the current line (1 cycle away) + cycleItem.Phase = ScreenRenderingPhase.BorderAndFetchPixelAttribute; + cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); + cycleItem.ContentionDelay = 5; + } + } + } + else + { + var pixelCycle = cycleInLine - FirstPixelCycleInLine; + // the ULA will perform a different action based on the current cycle (T-State) + switch (pixelCycle & 7) + { + case 0: + // Display the current cycle pixels + cycleItem.Phase = ScreenRenderingPhase.DisplayByte1; + cycleItem.ContentionDelay = 4; + break; + case 1: + // Display the current cycle pixels + cycleItem.Phase = ScreenRenderingPhase.DisplayByte1; + cycleItem.ContentionDelay = 3; + break; + case 2: + // While displaying the current cycle pixels, we need to prefetch the + // pixel data byte 2 cycles away + cycleItem.Phase = ScreenRenderingPhase.DisplayByte1AndFetchByte2; + cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); + cycleItem.ContentionDelay = 2; + break; + case 3: + // While displaying the current cycle pixels, we need to prefetch the + // attribute data byte 1 cycle away + cycleItem.Phase = ScreenRenderingPhase.DisplayByte1AndFetchAttribute2; + cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); + cycleItem.ContentionDelay = 1; + break; + case 4: + case 5: + // Display the current cycle pixels + cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; + break; + case 6: + if (cycleInLine < FirstPixelCycleInLine + DisplayLineTime - 2) + { + // There are still more bytes to display in this line. + // While displaying the current cycle pixels, we need to prefetch the + // pixel data byte 2 cycles away + cycleItem.Phase = ScreenRenderingPhase.DisplayByte2AndFetchByte1; + cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); + cycleItem.ContentionDelay = 6; + } + else + { + // Last byte in this line. + // Display the current cycle pixels + cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; + } + break; + case 7: + if (cycleInLine < FirstPixelCycleInLine + DisplayLineTime - 1) + { + // There are still more bytes to display in this line. + // While displaying the current cycle pixels, we need to prefetch the + // attribute data byte 1 cycle away + cycleItem.Phase = ScreenRenderingPhase.DisplayByte2AndFetchAttribute1; + cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); + cycleItem.ContentionDelay = 5; + } + else + { + // Last byte in this line. + // Display the current cycle pixels + cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; + } + break; + } + } + } + + // Store the calulated cycle item + _renderingCycleTable[cycle] = cycleItem; + } + } + + /// + /// Calculates the pixel address for the specified line and tact within + /// the line + /// + /// Line index + /// Tacts index within the line + /// ZX spectrum screen memory address + /// + /// Memory address bits: + /// C0-C2: pixel count within a byte -- not used in address calculation + /// C3-C7: pixel byte within a line + /// V0-V7: pixel line address + /// + /// Direct Pixel Address (da) + /// ================================================================= + /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | + /// ================================================================= + /// | 0 | 0 | 0 |V7 |V6 |V5 |V4 |V3 |V2 |V1 |V0 |C7 |C6 |C5 |C4 |C3 | + /// ================================================================= + /// | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0xF81F + /// ================================================================= + /// | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x0700 + /// ================================================================= + /// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0x00E0 + /// ================================================================= + /// + /// Spectrum Pixel Address + /// ================================================================= + /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | + /// ================================================================= + /// | 0 | 0 | 0 |V7 |V6 |V2 |V1 |V0 |V5 |V4 |V3 |C7 |C6 |C5 |C4 |C3 | + /// ================================================================= + /// + protected virtual ushort CalculatePixelByteAddress(int line, int cycleInLine) + { + var row = line - FirstDisplayLine; + var column = 2 * (cycleInLine - (HorizontalBlankingTime + BorderLeftTime)); + var da = 0x4000 | (column >> 3) | (row << 5); + return (ushort)((da & 0xF81F) // --- Reset V5, V4, V3, V2, V1 + | ((da & 0x0700) >> 3) // --- Keep V5, V4, V3 only + | ((da & 0x00E0) << 3)); // --- Exchange the V2, V1, V0 bit + // --- group with V5, V4, V3 + } + + /// + /// Calculates the pixel attribute address for the specified line and + /// tact within the line + /// + /// Line index + /// Tacts index within the line + /// ZX spectrum screen memory address + /// + /// Memory address bits: + /// C0-C2: pixel count within a byte -- not used in address calculation + /// C3-C7: pixel byte within a line + /// V0-V7: pixel line address + /// + /// Spectrum Attribute Address + /// ================================================================= + /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | + /// ================================================================= + /// | 0 | 1 | 0 | 1 | 1 | 0 |V7 |V6 |V5 |V4 |V3 |C7 |C6 |C5 |C4 |C3 | + /// ================================================================= + /// + protected virtual ushort CalculateAttributeAddress(int line, int cycleInLine) + { + var row = line - FirstDisplayLine; + var column = 2 * (cycleInLine - (HorizontalBlankingTime + BorderLeftTime)); + var da = (column >> 3) | ((row >> 3) << 5); + return (ushort)(0x5800 + da); + } + + #endregion + + #region Initialisation + + /// + /// Initialises the screen configuration calculations + /// + protected virtual void InitScreenConfig() + { + ScreenLines = BorderTopLines + DisplayLines + BorderBottomLines; + FirstDisplayLine = VerticalSyncLines + NonVisibleBorderTopLines + BorderTopLines; + LastDisplayLine = FirstDisplayLine + DisplayLines - 1; + ScreenWidth = BorderLeftPixels + DisplayWidth + BorderRightPixels; + FirstPixelCycleInLine = HorizontalBlankingTime + BorderLeftTime; + ScreenLineTime = FirstPixelCycleInLine + DisplayLineTime + BorderRightTime + NonVisibleBorderRightTime; + UlaFrameCycleCount = (FirstDisplayLine + DisplayLines + BorderBottomLines + NonVisibleBorderTopLines) * ScreenLineTime; + FirstScreenPixelCycle = (VerticalSyncLines + NonVisibleBorderTopLines) * ScreenLineTime + HorizontalBlankingTime; + } + + /// + /// Inits the screen + /// + protected virtual void InitScreen() + { + //BorderDevice.Reset(); + _flashPhase = false; + + _frameBuffer = new byte[ScreenWidth * ScreenLines]; + + InitULACycleTable(); + + // --- Calculate color conversion table + _flashOffColors = new int[0x200]; + _flashOnColors = new int[0x200]; + + for (var attr = 0; attr < 0x100; attr++) + { + var ink = (attr & 0x07) | ((attr & 0x40) >> 3); + var paper = ((attr & 0x38) >> 3) | ((attr & 0x40) >> 3); + _flashOffColors[attr] = paper; + _flashOffColors[0x100 + attr] = ink; + _flashOnColors[attr] = (attr & 0x80) != 0 ? ink : paper; + _flashOnColors[0x100 + attr] = (attr & 0x80) != 0 ? paper : ink; + } + + FrameCount = 0; + } + + #endregion + + #region VBLANK Interrupt + + /// + /// The longest instruction cycle count + /// + protected const int LONGEST_OP_CYCLES = 23; + + /// + /// The ULA cycle to raise the interrupt at + /// + protected int InterruptCycle = 32; + + /// + /// Signs that an interrupt has been raised in this frame. + /// + protected bool InterruptRaised; + + /// + /// Signs that the interrupt signal has been revoked + /// + protected bool InterruptRevoked; + + /// + /// Resets the interrupt - this should happen every frame in order to raise + /// the VBLANK interrupt in the proceding frame + /// + public virtual void ResetInterrupt() + { + InterruptRaised = false; + InterruptRevoked = false; + } + + /// + /// Generates an interrupt in the current phase if needed + /// + /// + protected virtual void CheckForInterrupt(int currentCycle) + { + if (InterruptRevoked) + { + // interrupt has already been handled + return; + } + + if (currentCycle < InterruptCycle) + { + // interrupt does not need to be raised yet + return; + } + + if (currentCycle > InterruptCycle + LONGEST_OP_CYCLES) + { + // 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; + //CPU.IFF1 = true; + CPU.FlagI = false; + //CPU.NonMaskableInterruptPending = true; + } + + if (InterruptRaised) + { + // INT is raised but not yet revoked + // CPU has NOT handled it yet + return; + } + + // if CPU is masking the interrupt do not raise it + //if (!CPU.IFF1) + //return; + + // Raise the interrupt + InterruptRaised = true; + //CPU.IFF1 = false; + //CPU.IFF2 = false; + CPU.FlagI = true; + FrameCount++; + } + + #endregion + + #region IVideoProvider + + public int VirtualWidth => ScreenWidth; + public int VirtualHeight => ScreenLines; + public int BufferWidth => ScreenWidth; + public int BufferHeight => ScreenLines; + public int BackgroundColor => ULAPalette[BorderColour]; + + public int VsyncNumerator + { + get { return 3500000; } + } + + public int VsyncDenominator + { + get { return UlaFrameCycleCount; } + } + /* + public int VsyncNumerator => NullVideo.DefaultVsyncNum; + public int VsyncDenominator => NullVideo.DefaultVsyncDen; + */ + public int[] GetVideoBuffer() + { + // convert the generated _framebuffer into ARGB colours via the ULAPalette + int[] trans = new int[_frameBuffer.Length]; + for (int i = 0; i < _frameBuffer.Length; i++) + trans[i] = ULAPalette[_frameBuffer[i]]; + return trans; //_frameBuffer; + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs new file mode 100644 index 0000000000..994e32df80 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Sound * + /// + public abstract partial class SpectrumBase + { + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs new file mode 100644 index 0000000000..759f9402d1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -0,0 +1,174 @@ +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components.Z80A; +using System; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Main properties / fields / contruction* + /// + public abstract partial class SpectrumBase + { + /// + /// The calling ZXSpectrum class (piped in via constructor) + /// + protected ZXSpectrum Spectrum { get; set; } + + /// + /// Reference to the instantiated Z80 cpu (piped in via constructor) + /// + protected Z80A CPU { get; set; } + + /// + /// The spectrum buzzer/beeper + /// + public Buzzer BuzzerDevice { get; set; } + + /// + /// The spectrum keyboard + /// + public virtual IKeyboard KeyboardDevice { get; set; } + + /// + /// The spectrum datacorder device + /// + public virtual Tape TapeDevice { get; set; } + + /// + /// Signs whether the frame has ended + /// + public bool FrameCompleted; + + /// + /// Overflow from the previous frame (in Z80 cycles) + /// + public int OverFlow; + + /// + /// The total number of frames rendered + /// + public int FrameCount; + + /// + /// The current cycle (T-State) that we are at in the frame + /// + public int _frameCycles; + + /// + /// Stores where we are in the frame after each CPU cycle + /// + public int LastFrameStartCPUTick; + + /// + /// Gets the current frame cycle according to the CPU tick count + /// + public virtual int CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick; + + /// + /// Mask constants + /// + protected const int BORDER_BIT = 0x07; + protected const int EAR_BIT = 0x10; + protected const int MIC_BIT = 0x08; + protected const int TAPE_BIT = 0x40; + + /// + /// Executes a single frame + /// + public virtual void ExecuteFrame() + { + FrameCompleted = false; + BuzzerDevice.StartFrame(); + + PollInput(); + + while (CurrentFrameCycle <= UlaFrameCycleCount) + { + // check for interrupt + CheckForInterrupt(CurrentFrameCycle); + + // run a single CPU instruction + CPU.ExecuteOne(); + + // run a rendering cycle according to the current CPU cycle count + var lastCycle = CurrentFrameCycle; + RenderScreen(LastRenderedULACycle + 1, lastCycle); + LastRenderedULACycle = lastCycle; + + // test + if (CPU.IFF1) + { + //Random rnd = new Random(); + //ushort rU = (ushort)rnd.Next(0x4000, 0x8000); + //PokeMemory(rU, (byte)rnd.Next(7)); + } + } + + // we have reached the end of a frame + LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; + LastRenderedULACycle = OverFlow; + + BuzzerDevice.EndFrame(); + + + + FrameCount++; + + // setup for next frame + OverFlow = CurrentFrameCycle % UlaFrameCycleCount; + ResetInterrupt(); + FrameCompleted = true; + + if (FrameCount % FlashToggleFrames == 0) + { + _flashPhase = !_flashPhase; + } + + RenderScreen(0, OverFlow); + } + + /// + /// Executes one cycle of the emulated machine + /// + public virtual void ExecuteCycle() + { + // check for interrupt + CheckForInterrupt(CurrentFrameCycle); + + // run a single CPU instruction + CPU.ExecuteOne(); + + // run a rendering cycle according to the current CPU cycle count + var lastCycle = CurrentFrameCycle; + RenderScreen(LastRenderedULACycle + 1, lastCycle); + + // has the frame completed? + FrameCompleted = CurrentFrameCycle >= UlaFrameCycleCount; + + if (CurrentFrameCycle > 50000) + { + + } + } + + /// + /// Hard reset of the emulated machine + /// + public virtual void HardReset() + { + ResetBorder(); + ResetInterrupt(); + + } + + /// + /// Soft reset of the emulated machine + /// + public virtual void SoftReset() + { + ResetBorder(); + ResetInterrupt(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs new file mode 100644 index 0000000000..ada3db6803 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The 48k keyboard device + /// + public class Keyboard48 : IKeyboard + { + public SpectrumBase _machine { get; set; } + public string[] KeyboardMatrix { get; set; } + private readonly byte[] LineStatus; + public bool Issue2 { get; set; } + + + public Keyboard48(SpectrumBase machine) + { + _machine = machine; + + KeyboardMatrix = new string[] + { + // 0xfefe - 0 - 4 + "Key Caps Shift", "Key Z", "Key X", "Key C", "Key V", + // 0xfdfe - 5 - 9 + "Key A", "Key S", "Key D", "Key F", "Key G", + // 0xfbfe - 10 - 14 + "Key Q", "Key W", "Key E", "Key R", "Key T", + // 0xf7fe - 15 - 19 + "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", + // 0xeffe - 20 - 24 + "Key 0", "Key 9", "Key 8", "Key 7", "Key 6", + // 0xdffe - 25 - 29 + "Key P", "Key O", "Key I", "Key U", "Key Y", + // 0xbffe - 30 - 34 + "Key Return", "Key L", "Key K", "Key J", "Key H", + // 0x7ffe - 35 - 39 + "Key Space", "Key Sym Shift", "Key M", "Key N", "Key B" + }; + + LineStatus = new byte[8]; + } + + public void SetKeyStatus(string key, bool isPressed) + { + byte keyByte = GetByteFromKeyMatrix(key); + var lineIndex = keyByte / 5; + var lineMask = 1 << keyByte % 5; + + LineStatus[lineIndex] = isPressed ? (byte)(LineStatus[lineIndex] | lineMask) + : (byte)(LineStatus[lineIndex] & ~lineMask); + } + + public bool GetKeyStatus(string key) + { + byte keyByte = GetByteFromKeyMatrix(key); + var lineIndex = keyByte / 5; + var lineMask = 1 << keyByte % 5; + return (LineStatus[lineIndex] & lineMask) != 0; + } + + public byte GetLineStatus(byte lines) + { + byte status = 0; + lines = (byte)~lines; + var lineIndex = 0; + while (lines > 0) + { + if ((lines & 0x01) != 0) + status |= LineStatus[lineIndex]; + lineIndex++; + lines >>= 1; + } + var result = (byte)~status; + return result; + } + + public byte ReadKeyboardByte(ushort addr) + { + return GetLineStatus((byte)(addr >> 8)); + } + + public byte GetByteFromKeyMatrix(string key) + { + int index = Array.IndexOf(KeyboardMatrix, key); + return (byte)index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs new file mode 100644 index 0000000000..1b598f0474 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -0,0 +1,38 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class ZX48 : SpectrumBase + { + /// + /// Main constructor + /// + /// + /// + public ZX48(ZXSpectrum spectrum, Z80A cpu) + { + Spectrum = spectrum; + CPU = cpu; + + RAM = new byte[0x4000 + 0xC000]; + + InitScreenConfig(); + InitScreen(); + + ResetULACycle(); + + BuzzerDevice = new Buzzer(this); + BuzzerDevice.Init(); + + KeyboardDevice = new Keyboard48(this); + + TapeDevice = new Tape(); + TapeDevice.Init(this); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Pulse.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Pulse.cs new file mode 100644 index 0000000000..af14d5cea7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Pulse.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The MIC and EAR pins in the spectrum deal in on/off pulses of varying lengths + /// This struct therefore represents 1 of these pulses + /// + public struct Pulse + { + /// + /// True: High State + /// False: Low State + /// + public bool State { get; set; } + + /// + /// Pulse length in Z80 T-States (cycles) + /// + public int Length { get; set; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs new file mode 100644 index 0000000000..222a3bb257 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the tape device (or DATACORDER as AMSTRAD liked to call it) + /// + public class Tape + { + protected bool _micBitState; + + public SpectrumBase _machine { get; set; } + public Buzzer _buzzer { get; set; } + + + public virtual void Init(SpectrumBase machine) + { + _machine = machine; + _buzzer = machine.BuzzerDevice; + Reset(); + } + + public virtual void Reset() + { + _micBitState = true; + } + + + /// + /// the EAR bit read from tape + /// + /// + /// + public virtual bool GetEarBit(int cpuCycles) + { + return false; + } + + /// + /// Processes the mic bit change + /// + /// + public virtual void ProcessMicBit(bool micBit) + { + + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs new file mode 100644 index 0000000000..6d06bc54c3 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -0,0 +1,85 @@ + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum + { + + /// + /// The standard 48K Spectrum keyboard + /// https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/ZXSpectrum48k.jpg/1200px-ZXSpectrum48k.jpg + /// + private static readonly ControllerDefinition ZXSpectrumControllerDefinition48 = new ControllerDefinition + { + Name = "ZXSpectrum Controller 48K", + BoolButtons = + { + // Joystick interface (not yet emulated) - Could be Kempston/Cursor/Protek + "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", + // Keyboard - row 1 + "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", + // Keyboard - row 2 + "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", + // Keyboard - row 3 + "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", + // Keyboard - row 4 + "Key Caps Shift", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Sym Shift", "Key Space", + // Tape functions + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" + } + }; + + /// + /// The newer spectrum keyboard (models 48k+, 128k) + /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg + /// + private static readonly ControllerDefinition ZXSpectrumControllerDefinition128 = new ControllerDefinition + { + Name = "ZXSpectrum Controller 48KPlus", + BoolButtons = + { + // Joystick interface (not yet emulated) - Could be Kempston/Cursor/Protek + "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", + // Keyboard - row 1 + "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", + // Keyboard - row 2 + "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", + // Keyboard - row 3 + "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", + // Keyboard - row 4 + "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", + // Keyboard - row 5 + "Key Symbol Shift", "Key Semi-Colon", "Key Inverted-Comma", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", "Key Symbol Shift", + // Tape functions + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" + } + }; + + /// + /// The amstrad models - same as the previous keyboard layout but with built in ZX Interface 2 joystick ports + /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg + /// + private static readonly ControllerDefinition ZXSpectrumControllerDefinitionPlus = new ControllerDefinition + { + Name = "ZXSpectrum Controller 48KPlus", + BoolButtons = + { + // Joystick interface (not yet emulated) + "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", + // Keyboard - row 1 + "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", + // Keyboard - row 2 + "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", + // Keyboard - row 3 + "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", + // Keyboard - row 4 + "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", + // Keyboard - row 5 + "Key Symbol Shift", "Key Semi-Colon", "Key Inverted-Comma", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", "Key Symbol Shift", + // Tape functions + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" + } + }; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs new file mode 100644 index 0000000000..b473bac3f8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + return new Dictionary + { + ["A"] = _cpu.Regs[_cpu.A], + ["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8), + ["B"] = _cpu.Regs[_cpu.B], + ["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8), + ["C"] = _cpu.Regs[_cpu.C], + ["D"] = _cpu.Regs[_cpu.D], + ["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8), + ["E"] = _cpu.Regs[_cpu.E], + ["F"] = _cpu.Regs[_cpu.F], + ["H"] = _cpu.Regs[_cpu.H], + ["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8), + ["I"] = _cpu.Regs[_cpu.I], + ["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8), + ["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["L"] = _cpu.Regs[_cpu.L], + ["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8), + ["R"] = _cpu.Regs[_cpu.R], + ["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8), + ["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8), + ["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8), + ["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8), + ["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["Flag C"] = _cpu.FlagC, + ["Flag N"] = _cpu.FlagN, + ["Flag P/V"] = _cpu.FlagP, + ["Flag 3rd"] = _cpu.Flag3, + ["Flag H"] = _cpu.FlagH, + ["Flag 5th"] = _cpu.Flag5, + ["Flag Z"] = _cpu.FlagZ, + ["Flag S"] = _cpu.FlagS + }; + } + + public void SetCpuRegister(string register, int value) + { + switch (register) + { + default: + throw new InvalidOperationException(); + case "A": + _cpu.Regs[_cpu.A] = (ushort)value; + break; + case "AF": + _cpu.Regs[_cpu.F] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00); + break; + case "B": + _cpu.Regs[_cpu.B] = (ushort)value; + break; + case "BC": + _cpu.Regs[_cpu.C] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00); + break; + case "C": + _cpu.Regs[_cpu.C] = (ushort)value; + break; + case "D": + _cpu.Regs[_cpu.D] = (ushort)value; + break; + case "DE": + _cpu.Regs[_cpu.E] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00); + break; + case "E": + _cpu.Regs[_cpu.E] = (ushort)value; + break; + case "F": + _cpu.Regs[_cpu.F] = (ushort)value; + break; + case "H": + _cpu.Regs[_cpu.H] = (ushort)value; + break; + case "HL": + _cpu.Regs[_cpu.L] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00); + break; + case "I": + _cpu.Regs[_cpu.I] = (ushort)value; + break; + case "IX": + _cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00); + break; + case "IY": + _cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00); + break; + case "L": + _cpu.Regs[_cpu.L] = (ushort)value; + break; + case "PC": + _cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00); + break; + case "R": + _cpu.Regs[_cpu.R] = (ushort)value; + break; + case "Shadow AF": + _cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00); + break; + case "Shadow BC": + _cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00); + break; + case "Shadow DE": + _cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00); + break; + case "Shadow HL": + _cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00); + break; + case "SP": + _cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00); + break; + } + } + + public IMemoryCallbackSystem MemoryCallbacks { get; } + + public bool CanStep(StepType type) => false; + + [FeatureNotImplemented] + public void Step(StepType type) + { + throw new NotImplementedException(); + } + + public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs new file mode 100644 index 0000000000..8b10763146 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -0,0 +1,51 @@ +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum : IEmulator + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition { get; set; } + + public void FrameAdvance(IController controller, bool render, bool renderSound) + { + _controller = controller; + + if (_tracer.Enabled) + { + _cpu.TraceCallback = s => _tracer.Put(s); + } + else + { + _cpu.TraceCallback = null; + } + + _machine.ExecuteFrame(); + } + + public int Frame => _machine.FrameCount; + + public string SystemId => "ZXSpectrum"; + + public bool DeterministicEmulation => true; + + public void ResetCounters() + { + _machine.FrameCount = 0; + _lagCount = 0; + _isLag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + if (_machine != null) + { + _machine = null; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs new file mode 100644 index 0000000000..70a3b15712 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs @@ -0,0 +1,26 @@ + +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum : IInputPollable + { + public int LagCount + { + get { return _lagCount; } + set { _lagCount = value; } + } + + public bool IsLagFrame + { + get { return _isLag; } + set { _isLag = value; } + } + + public IInputCallbackSystem InputCallbacks { get; } + + private int _lagCount = 0; + private bool _isLag = true; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs new file mode 100644 index 0000000000..956c5eb206 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum //: IMemoryDomains + { + private MemoryDomainList memoryDomains; + private readonly Dictionary _byteArrayDomains = new Dictionary(); + private bool _memoryDomainsInit = false; + + private void SetupMemoryDomains() + { + var domains = new List + { + new MemoryDomainDelegate("System Bus", 0x10000, MemoryDomain.Endian.Little, + (addr) => + { + if (addr < 0 || addr >= 65536) + { + throw new ArgumentOutOfRangeException(); + } + return _cpu.ReadMemory((ushort)addr); + }, + (addr, value) => + { + if (addr < 0 || addr >= 65536) + { + throw new ArgumentOutOfRangeException(); + } + + _cpu.WriteMemory((ushort)addr, value); + }, 1) + }; + + SyncAllByteArrayDomains(); + + memoryDomains = new MemoryDomainList(_byteArrayDomains.Values.Concat(domains).ToList()); + (ServiceProvider as BasicServiceProvider).Register(memoryDomains); + + _memoryDomainsInit = true; + + + } + + private void SyncAllByteArrayDomains() + { + SyncByteArrayDomain("Main RAM", _machine.RAM); + } + + private void SyncByteArrayDomain(string name, byte[] data) + { + if (_memoryDomainsInit) + { + var m = _byteArrayDomains[name]; + m.Data = data; + } + else + { + var m = new MemoryDomainByteArray(name, MemoryDomain.Endian.Little, data, true, 1); + _byteArrayDomains.Add(name, m); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs new file mode 100644 index 0000000000..32db755068 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -0,0 +1,95 @@ +using System; +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System.ComponentModel; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum : ISettable + { + internal ZXSpectrumSettings Settings = new ZXSpectrumSettings(); + internal ZXSpectrumSyncSettings SyncSettings = new ZXSpectrumSyncSettings(); + + public ZXSpectrumSettings GetSettings() + { + return Settings.Clone(); + } + + public ZXSpectrumSyncSettings GetSyncSettings() + { + return SyncSettings.Clone(); + } + + public bool PutSettings(ZXSpectrumSettings o) + { + Settings = o; + return false; + } + + public bool PutSyncSettings(ZXSpectrumSyncSettings o) + { + SyncSettings = o; + return false; + } + + + + public class ZXSpectrumSettings + { + [DisplayName("Spectrum model")] + [Description("The model of spectrum to be emulated")] + [DefaultValue(MachineType.ZXSpectrum48)] + public MachineType MachineType { get; set; } + + [DisplayName("Border type")] + [Description("Select how to show the border area")] + [DefaultValue(BorderType.Full)] + public BorderType BorderType { get; set; } + + [DisplayName("Tape Load Speed")] + [Description("Select how fast the spectrum loads the game from tape")] + [DefaultValue(TapeLoadSpeed.Accurate)] + public TapeLoadSpeed TapeLoadSpeed { get; set; } + + public ZXSpectrumSettings Clone() + { + return (ZXSpectrumSettings)MemberwiseClone(); + } + + public ZXSpectrumSettings() + { + BizHawk.Common.SettingsUtil.SetDefaultValues(this); + } + } + + public class ZXSpectrumSyncSettings + { + public ZXSpectrumSyncSettings Clone() + { + return (ZXSpectrumSyncSettings)MemberwiseClone(); + } + } + + /// + /// The size of the Spectrum border + /// + public enum BorderType + { + Full, + Medium, + Small + } + + /// + /// The speed at which the tape is loaded + /// + public enum TapeLoadSpeed + { + Accurate, + Fast, + Fastest + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs new file mode 100644 index 0000000000..81dad0d813 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs @@ -0,0 +1,10 @@ +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum //: ISoundProvider + { + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs new file mode 100644 index 0000000000..e41c43b832 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -0,0 +1,75 @@ +using System.IO; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum : IStatable + { + public bool BinarySaveStatesPreferred + { + get { return true; } + } + + public void SaveStateText(TextWriter writer) + { + SyncState(new Serializer(writer)); + } + + public void LoadStateText(TextReader reader) + { + SyncState(new Serializer(reader)); + } + + public void SaveStateBinary(BinaryWriter bw) + { + SyncState(new Serializer(bw)); + } + + public void LoadStateBinary(BinaryReader br) + { + SyncState(new Serializer(br)); + } + + public byte[] SaveStateBinary() + { + MemoryStream ms = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + return ms.ToArray(); + } + + private void SyncState(Serializer ser) + { + byte[] core = null; + if (ser.IsWriter) + { + var ms = new MemoryStream(); + ms.Close(); + core = ms.ToArray(); + } + _cpu.SyncState(ser); + + ser.BeginSection("ZXSpectrum"); + byte[] ram = new byte[_machine.RAM.Length]; + _machine.RAM.CopyTo(ram, 0); + ser.Sync("RAM", ref ram, false); + //_vdp.SyncState(ser); + //PSG.SyncState(ser); + //ser.Sync("RAM", ref _ram, false); + ser.Sync("Frame", ref _machine.FrameCount); + ser.Sync("LagCount", ref _lagCount); + ser.Sync("IsLag", ref _isLag); + ser.EndSection(); + + if (ser.IsReader) + { + //SyncAllByteArrayDomains(); + } + } + + private byte[] _stateBuffer; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs new file mode 100644 index 0000000000..750c8475d6 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -0,0 +1,135 @@ +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components; +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + [Core( + "ZXHawk", + "Asnivor", + isPorted: false, + isReleased: false)] + [ServiceNotApplicable(typeof(IDriveLight))] + public partial class ZXSpectrum : IDebuggable, IInputPollable, IStatable, IRegionable + { + [CoreConstructor("ZXSpectrum")] + public ZXSpectrum(CoreComm comm, byte[] file, object settings, object syncSettings) + { + PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); + PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); + + var ser = new BasicServiceProvider(this); + ServiceProvider = ser; + InputCallbacks = new InputCallbackSystem(); + + CoreComm = comm; + + _cpu = new Z80A(); + + _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; + + switch (Settings.MachineType) + { + case MachineType.ZXSpectrum48: + ControllerDefinition = ZXSpectrumControllerDefinition48; + Init(MachineType.ZXSpectrum48, Settings.BorderType, Settings.TapeLoadSpeed); + break; + default: + throw new InvalidOperationException("Machine not yet emulated"); + } + + + + _cpu.MemoryCallbacks = MemoryCallbacks; + + HardReset = _machine.HardReset; + SoftReset = _machine.SoftReset; + + _cpu.FetchMemory = _machine.ReadMemory; + _cpu.ReadMemory = _machine.ReadMemory; + _cpu.WriteMemory = _machine.WriteMemory; + _cpu.ReadHardware = _machine.ReadPort; + _cpu.WriteHardware = _machine.WritePort; + + ser.Register(_tracer); + ser.Register(_cpu); + ser.Register(_machine); + ser.Register(_machine.BuzzerDevice); + + HardReset(); + + + + List romDis = new List(); + List disas = new List(); + for (int i = 0x00; i < 0x4000; i++) + { + DISA d = new DISA(); + ushort size; + d.Dis = _cpu.Disassemble((ushort)i, _machine.ReadMemory, out size); + d.Size = size; + disas.Add(d); + romDis.Add(d.Dis); + //i = i + size - 1; + //romDis.Add(s); + } + } + + public class DISA + { + public ushort Size { get; set; } + public string Dis { get; set; } + } + + //private int _cyclesPerFrame; + + public Action HardReset; + public Action SoftReset; + + private readonly Z80A _cpu; + private byte[] _systemRom; + private readonly TraceBuffer _tracer; + public IController _controller; + private SpectrumBase _machine; + + + + + + private byte[] GetFirmware(int length, params string[] names) + { + var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("ZXSpectrum", n, false)).FirstOrDefault(b => b != null && b.Length == length); + if (result == null) + { + throw new MissingFirmwareException($"At least one of these firmwares is required: {string.Join(", ", names)}"); + } + + return result; + } + + + private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed) + { + // setup the emulated model based on the MachineType + switch (machineType) + { + case MachineType.ZXSpectrum48: + _machine = new ZX48(this, _cpu); + _systemRom = GetFirmware(0x4000, "48ROM"); + _machine.FillMemory(_systemRom, 0); + break; + } + } + + #region IRegionable + + public DisplayType Region => DisplayType.PAL; + + #endregion + + + } +} From 30483f30036f0a4aebd948a4d0e19836df2131cf Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 24 Nov 2017 18:43:04 +0000 Subject: [PATCH 002/339] Started tape impl. --- .../BizHawk.Emulation.Cores.csproj | 31 +- .../SinclairSpectrum/{ => Hardware}/Buzzer.cs | 0 .../Hardware/DefaultTapeProvider.cs | 135 ++ .../{ => Hardware/Interfaces}/IKeyboard.cs | 0 .../Interfaces/ISaveToTapeProvider.cs | 35 + .../Interfaces/ISupportsTapeBlockPlayback.cs | 39 + .../ISupportsTapeBlockSetPlayback.cs | 21 + .../Interfaces/ITapeContentProvider.cs | 25 + .../Hardware/Interfaces/ITapeData.cs | 24 + .../Interfaces/ITapeDataSerialization.cs | 27 + .../Hardware/Interfaces/ITapeProvider.cs | 47 + .../SinclairSpectrum/Hardware/Tape.cs | 691 ++++++++ .../Hardware/TapeBlockSetPlayer.cs | 102 ++ .../Hardware/TapeDataBlockPlayer.cs | 218 +++ .../Hardware/TapeFilePlayer.cs | 113 ++ .../Machine/SpectrumBase.Memory.cs | 120 +- .../Machine/SpectrumBase.Port.cs | 10 +- .../Machine/SpectrumBase.Screen.cs | 21 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 16 +- .../Machine/ZXSpectrum48K/ZX48.cs | 123 +- .../Media/Tape/TAP/TapDataBlock.cs | 90 ++ .../Media/Tape/TAP/TapPlayer.cs | 85 + .../Media/Tape/TAP/TapReader.cs | 52 + .../Media/Tape/TZX/BlockAbstraction.cs | 172 ++ .../Media/Tape/TZX/DataBlocks.cs | 1411 +++++++++++++++++ .../SinclairSpectrum/Media/Tape/TZX/Info.cs | 250 +++ .../SinclairSpectrum/Media/Tape/TZX/Types.cs | 282 ++++ .../Media/Tape/TZX/TzxException.cs | 28 + .../Media/Tape/TZX/TzxHeader.cs | 68 + .../Media/Tape/TZX/TzxPlayer.cs | 83 + .../Media/Tape/TZX/TzxReader.cs | 125 ++ .../Computers/SinclairSpectrum/RomData.cs | 47 + .../Computers/SinclairSpectrum/Tape.cs | 52 - .../ZXSpectrum.IMemoryDomains.cs | 2 +- .../ZXSpectrum.ISoundProvider.cs | 10 - .../SinclairSpectrum/ZXSpectrum.IStatable.cs | 6 +- .../SinclairSpectrum/ZXSpectrum.Util.cs | 16 + .../Computers/SinclairSpectrum/ZXSpectrum.cs | 17 +- 38 files changed, 4451 insertions(+), 143 deletions(-) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/{ => Hardware}/Buzzer.cs (100%) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/{ => Hardware/Interfaces}/IKeyboard.cs (100%) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 63380c7fa3..2e488bd9e9 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -256,8 +256,19 @@ - - + + + + + + + + + + + + + @@ -265,8 +276,20 @@ + + + + + + + + + + + - + + @@ -274,8 +297,8 @@ - + Atari2600.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Buzzer.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs new file mode 100644 index 0000000000..f9bbf30846 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class DefaultTapeProvider : ITapeProvider + { + public const string RESOURCE_FOLDER = "TzxResources"; + public const string DEFAULT_SAVE_FILE_DIR = @"C:\Temp\ZxSpectrumSavedFiles"; + public const string DEFAULT_NAME = "SavedFile"; + public const string DEFAULT_EXT = ".tzx"; + private string _suggestedName; + private string _fullFileName; + private int _dataBlockCount; + + private byte[] _file; + + /// + /// The directory files should be saved to + /// + public string SaveFileFolder { get; } + + + + public DefaultTapeProvider(byte[] file, string saveFolder = null) + { + SaveFileFolder = string.IsNullOrWhiteSpace(saveFolder) + ? DEFAULT_SAVE_FILE_DIR + : saveFolder; + + _file = file; + } + + /// + /// The component provider should be able to reset itself + /// + /// + /* + public override void Reset() + { + _dataBlockCount = 0; + _suggestedName = null; + _fullFileName = null; + } + */ + + /// + /// Tha tape set to load the content from + /// + public string TapeSetName { get; set; } + + /// + /// Gets a binary reader that provider TZX content + /// + /// BinaryReader instance to obtain the content from + public BinaryReader GetTapeContent() + { + Stream stream = new MemoryStream(_file); + var reader = new BinaryReader(stream); + return reader; + } + + /// + /// Creates a tape file with the specified name + /// + /// + public void CreateTapeFile() + { + //Reset(); + } + + /// + /// This method sets the name of the file according to the + /// Spectrum SAVE HEADER information + /// + /// + public void SetName(string name) + { + _suggestedName = name; + } + + /// + /// Appends the TZX block to the tape file + /// + /// + public void SaveTapeBlock(ITapeDataSerialization block) + { + if (_dataBlockCount == 0) + { + if (!Directory.Exists(SaveFileFolder)) + { + Directory.CreateDirectory(SaveFileFolder); + } + var baseFileName = $"{_suggestedName ?? DEFAULT_NAME}_{DateTime.Now:yyyyMMdd_HHmmss}{DEFAULT_EXT}"; + _fullFileName = Path.Combine(SaveFileFolder, baseFileName); + using (var writer = new BinaryWriter(File.Create(_fullFileName))) + { + var header = new TzxHeader(); + header.WriteTo(writer); + } + } + _dataBlockCount++; + + var stream = File.Open(_fullFileName, FileMode.Append); + using (var writer = new BinaryWriter(stream)) + { + block.WriteTo(writer); + } + } + + /// + /// The tape provider can finalize the tape when all + /// TZX blocks are written. + /// + public void FinalizeTapeFile() + { + } + + /// + /// Obtains the specified resource stream ot the given assembly + /// + /// Assembly to get the resource stream from + /// Resource name + private static Stream GetFileResource(Assembly asm, string resourceName) + { + var resourceFullName = $"{asm.GetName().Name}.{RESOURCE_FOLDER}.{resourceName}"; + return asm.GetManifestResourceStream(resourceFullName); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/IKeyboard.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs new file mode 100644 index 0000000000..05a4de9e06 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs @@ -0,0 +1,35 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This interface describes the behavior of an object that + /// provides tape content + /// + public interface ISaveToTapeProvider + { + /// + /// Creates a tape file with the specified name + /// + /// + void CreateTapeFile(); + + /// + /// This method sets the name of the file according to the + /// Spectrum SAVE HEADER information + /// + /// + void SetName(string name); + + /// + /// Appends the tape block to the tape file + /// + /// + void SaveTapeBlock(ITapeDataSerialization block); + + /// + /// The tape provider can finalize the tape when all + /// tape blocks are written. + /// + void FinalizeTapeFile(); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs new file mode 100644 index 0000000000..969944fa92 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This interface represents that the implementing class supports + /// emulating tape playback of a single tape block + /// + public interface ISupportsTapeBlockPlayback + { + /// + /// The current playing phase + /// + PlayPhase PlayPhase { get; } + + /// + /// The tact count of the CPU when playing starts + /// + long StartCycle { get; } + + /// + /// Initializes the player + /// + void InitPlay(long startCycle); + + /// + /// Gets the EAR bit value for the specified tact + /// + /// Tacts to retrieve the EAR bit + /// + /// The EAR bit value to play back + /// + bool GetEarBit(long currentCycle); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs new file mode 100644 index 0000000000..1312761e51 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This interface represents that the implementing class supports + /// emulating tape playback of a set of subsequent tape blocks + /// + public interface ISupportsTapeBlockSetPlayback : ISupportsTapeBlockPlayback + { + /// + /// Moves the player to the next playable block + /// + /// Tacts time to start the next block + void NextBlock(long currentCycle); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs new file mode 100644 index 0000000000..62f8c6f814 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs @@ -0,0 +1,25 @@ + +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This interface describes the behavior of an object that + /// provides tape content + /// + public interface ITapeContentProvider + { + /// + /// Tha tape set to load the content from + /// + string TapeSetName { get; set; } + + /// + /// Gets a binary reader that provides tape content + /// + /// BinaryReader instance to obtain the content from + BinaryReader GetTapeContent(); + + void Reset(); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs new file mode 100644 index 0000000000..5879c57536 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represetns the data in the tape + /// + public interface ITapeData + { + /// + /// Block Data + /// + byte[] Data { get; } + + /// + /// Pause after this block (given in milliseconds) + /// + ushort PauseAfter { get; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs new file mode 100644 index 0000000000..9f48e9097e --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Defines the serialization operations of a TZX record + /// + public interface ITapeDataSerialization + { + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + void ReadFrom(BinaryReader reader); + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + void WriteTo(BinaryWriter writer); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs new file mode 100644 index 0000000000..d82bc3ee64 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs @@ -0,0 +1,47 @@ +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This interface describes the behavior of an object that + /// provides TZX tape content + /// + public interface ITapeProvider + { + /// + /// Tha tape set to load the content from + /// + string TapeSetName { get; set; } + + /// + /// Gets a binary reader that provider TZX content + /// + /// BinaryReader instance to obtain the content from + BinaryReader GetTapeContent(); + + /// + /// Creates a tape file with the specified name + /// + /// + void CreateTapeFile(); + + /// + /// This method sets the name of the file according to the + /// Spectrum SAVE HEADER information + /// + /// + void SetName(string name); + + /// + /// Appends the TZX block to the tape file + /// + /// + void SaveTapeBlock(ITapeDataSerialization block); + + /// + /// The tape provider can finalize the tape when all + /// TZX blocks are written. + /// + void FinalizeTapeFile(); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs new file mode 100644 index 0000000000..f493c7bda9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs @@ -0,0 +1,691 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /* + * Much of the TAPE implementation has been taken from: https://github.com/Dotneteer/spectnetide + * + * MIT License + + Copyright (c) 2017 Istvan Novak + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + */ + + /// + /// Represents the tape device (or DATACORDER as AMSTRAD liked to call it) + /// + public class Tape + { + private SpectrumBase _machine { get; set; } + private Z80A _cpu { get; set; } + private Buzzer _buzzer { get; set; } + + private TapeOperationMode _currentMode; + private TapeFilePlayer _tapePlayer; + private bool _micBitState; + private long _lastMicBitActivityCycle; + private SavePhase _savePhase; + private int _pilotPulseCount; + private int _bitOffset; + private byte _dataByte; + private int _dataLength; + private byte[] _dataBuffer; + private int _dataBlockCount; + private MicPulseType _prevDataPulse; + + /// + /// Number of tacts after save mod can be exited automatically + /// + public const int SAVE_STOP_SILENCE = 17500000; + + /// + /// The address of the ERROR routine in the Spectrum ROM + /// + public const ushort ERROR_ROM_ADDRESS = 0x0008; + + /// + /// The maximum distance between two scans of the EAR bit + /// + public const int MAX_TACT_JUMP = 10000; + + /// + /// The width tolerance of save pulses + /// + public const int SAVE_PULSE_TOLERANCE = 24; + + /// + /// Minimum number of pilot pulses before SYNC1 + /// + public const int MIN_PILOT_PULSE_COUNT = 3000; + + /// + /// Lenght of the data buffer to allocate for the SAVE operation + /// + public const int DATA_BUFFER_LENGTH = 0x10000; + + /// + /// Gets the TZX tape content provider + /// + public ITapeContentProvider ContentProvider { get; } + + /// + /// Gets the tape Save provider + /// + public ISaveToTapeProvider SaveToTapeProvider { get; } + + /// + /// The TapeFilePlayer that can playback tape content + /// + public TapeFilePlayer TapeFilePlayer => _tapePlayer; + + /// + /// The current operation mode of the tape + /// + public TapeOperationMode CurrentMode => _currentMode; + + + public virtual void Init(SpectrumBase machine) + { + _machine = machine; + _cpu = _machine.CPU; + _buzzer = machine.BuzzerDevice; + Reset(); + } + + public Tape(ITapeContentProvider contentProvider, ISaveToTapeProvider saveToTapeProvider) + { + ContentProvider = contentProvider; + SaveToTapeProvider = saveToTapeProvider; + } + + public virtual void Reset() + { + ContentProvider?.Reset(); + _tapePlayer = null; + _currentMode = TapeOperationMode.Passive; + _savePhase = SavePhase.None; + _micBitState = true; + } + + public void CPUFrameCompleted() + { + SetTapeMode(); + if (CurrentMode == TapeOperationMode.Load + //&& HostVm.ExecuteCycleOptions.FastTapeMode + && TapeFilePlayer != null + && TapeFilePlayer.PlayPhase != PlayPhase.Completed + && _machine.Spectrum.Get16BitPC() == _machine.RomData.LoadBytesRoutineAddress) + { + /* + if (FastLoadFromTzx()) + { + FastLoadCompleted?.Invoke(this, EventArgs.Empty); + } + */ + } + } + + /// + /// Sets the current tape mode according to the current PC register + /// and the MIC bit state + /// + public void SetTapeMode() + { + switch (_currentMode) + { + case TapeOperationMode.Passive: + if (_machine.Spectrum.Get16BitPC() == _machine.RomData.LoadBytesRoutineAddress) + { + EnterLoadMode(); + } + else if (_machine.Spectrum.Get16BitPC() == _machine.RomData.SaveBytesRoutineAddress) + { + EnterSaveMode(); + } + + var res = _machine.Spectrum.Get16BitPC(); + + return; + case TapeOperationMode.Save: + if (_machine.Spectrum.Get16BitPC() == ERROR_ROM_ADDRESS + || (int)(_cpu.TotalExecutedCycles - _lastMicBitActivityCycle) > SAVE_STOP_SILENCE) + { + LeaveSaveMode(); + } + return; + case TapeOperationMode.Load: + if ((_tapePlayer?.Eof ?? false) || _machine.Spectrum.Get16BitPC() == ERROR_ROM_ADDRESS) + { + LeaveLoadMode(); + } + return; + } + } + + /// + /// Puts the device in save mode. From now on, every MIC pulse is recorded + /// + private void EnterSaveMode() + { + _currentMode = TapeOperationMode.Save; + _savePhase = SavePhase.None; + _micBitState = true; + _lastMicBitActivityCycle = _cpu.TotalExecutedCycles; + _pilotPulseCount = 0; + _prevDataPulse = MicPulseType.None; + _dataBlockCount = 0; + SaveToTapeProvider?.CreateTapeFile(); + } + + /// + /// Leaves the save mode. Stops recording MIC pulses + /// + private void LeaveSaveMode() + { + _currentMode = TapeOperationMode.Passive; + SaveToTapeProvider?.FinalizeTapeFile(); + } + + /// + /// Puts the device in load mode. From now on, EAR pulses are played by a device + /// + private void EnterLoadMode() + { + _currentMode = TapeOperationMode.Load; + + var contentReader = ContentProvider?.GetTapeContent(); + if (contentReader == null) return; + + // --- Play the content + _tapePlayer = new TapeFilePlayer(contentReader); + _tapePlayer.ReadContent(); + _tapePlayer.InitPlay(_cpu.TotalExecutedCycles); + _buzzer.SetTapeMode(true); + } + + /// + /// Leaves the load mode. Stops the device that playes EAR pulses + /// + private void LeaveLoadMode() + { + _currentMode = TapeOperationMode.Passive; + _tapePlayer = null; + ContentProvider?.Reset(); + _buzzer.SetTapeMode(false); + } + + + /// + /// the EAR bit read from tape + /// + /// + /// + public virtual bool GetEarBit(int cpuCycles) + { + if (_currentMode != TapeOperationMode.Load) + { + return true; + } + + var earBit = _tapePlayer?.GetEarBit(cpuCycles) ?? true; + _buzzer.ProcessPulseValue(true, earBit); + return earBit; + } + + /// + /// Processes the mic bit change + /// + /// + public virtual void ProcessMicBit(bool micBit) + { + if (_currentMode != TapeOperationMode.Save + || _micBitState == micBit) + { + return; + } + + var length = _cpu.TotalExecutedCycles - _lastMicBitActivityCycle; + + // --- Classify the pulse by its width + var pulse = MicPulseType.None; + if (length >= TapeDataBlockPlayer.BIT_0_PL - SAVE_PULSE_TOLERANCE + && length <= TapeDataBlockPlayer.BIT_0_PL + SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.Bit0; + } + else if (length >= TapeDataBlockPlayer.BIT_1_PL - SAVE_PULSE_TOLERANCE + && length <= TapeDataBlockPlayer.BIT_1_PL + SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.Bit1; + } + if (length >= TapeDataBlockPlayer.PILOT_PL - SAVE_PULSE_TOLERANCE + && length <= TapeDataBlockPlayer.PILOT_PL + SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.Pilot; + } + else if (length >= TapeDataBlockPlayer.SYNC_1_PL - SAVE_PULSE_TOLERANCE + && length <= TapeDataBlockPlayer.SYNC_1_PL + SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.Sync1; + } + else if (length >= TapeDataBlockPlayer.SYNC_2_PL - SAVE_PULSE_TOLERANCE + && length <= TapeDataBlockPlayer.SYNC_2_PL + SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.Sync2; + } + else if (length >= TapeDataBlockPlayer.TERM_SYNC - SAVE_PULSE_TOLERANCE + && length <= TapeDataBlockPlayer.TERM_SYNC + SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.TermSync; + } + else if (length < TapeDataBlockPlayer.SYNC_1_PL - SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.TooShort; + } + else if (length > TapeDataBlockPlayer.PILOT_PL + 2 * SAVE_PULSE_TOLERANCE) + { + pulse = MicPulseType.TooLong; + } + + _micBitState = micBit; + _lastMicBitActivityCycle = _cpu.TotalExecutedCycles; + + // --- Lets process the pulse according to the current SAVE phase and pulse width + var nextPhase = SavePhase.Error; + switch (_savePhase) + { + case SavePhase.None: + if (pulse == MicPulseType.TooShort || pulse == MicPulseType.TooLong) + { + nextPhase = SavePhase.None; + } + else if (pulse == MicPulseType.Pilot) + { + _pilotPulseCount = 1; + nextPhase = SavePhase.Pilot; + } + break; + case SavePhase.Pilot: + if (pulse == MicPulseType.Pilot) + { + _pilotPulseCount++; + nextPhase = SavePhase.Pilot; + } + else if (pulse == MicPulseType.Sync1 && _pilotPulseCount >= MIN_PILOT_PULSE_COUNT) + { + nextPhase = SavePhase.Sync1; + } + break; + case SavePhase.Sync1: + if (pulse == MicPulseType.Sync2) + { + nextPhase = SavePhase.Sync2; + } + break; + case SavePhase.Sync2: + if (pulse == MicPulseType.Bit0 || pulse == MicPulseType.Bit1) + { + // --- Next pulse starts data, prepare for receiving it + _prevDataPulse = pulse; + nextPhase = SavePhase.Data; + _bitOffset = 0; + _dataByte = 0; + _dataLength = 0; + _dataBuffer = new byte[DATA_BUFFER_LENGTH]; + } + break; + case SavePhase.Data: + if (pulse == MicPulseType.Bit0 || pulse == MicPulseType.Bit1) + { + if (_prevDataPulse == MicPulseType.None) + { + // --- We are waiting for the second half of the bit pulse + _prevDataPulse = pulse; + nextPhase = SavePhase.Data; + } + else if (_prevDataPulse == pulse) + { + // --- We received a full valid bit pulse + nextPhase = SavePhase.Data; + _prevDataPulse = MicPulseType.None; + + // --- Add this bit to the received data + _bitOffset++; + _dataByte = (byte)(_dataByte * 2 + (pulse == MicPulseType.Bit0 ? 0 : 1)); + if (_bitOffset == 8) + { + // --- We received a full byte + _dataBuffer[_dataLength++] = _dataByte; + _dataByte = 0; + _bitOffset = 0; + } + } + } + else if (pulse == MicPulseType.TermSync) + { + // --- We received the terminating pulse, the datablock has been completed + nextPhase = SavePhase.None; + _dataBlockCount++; + + // --- Create and save the data block + var dataBlock = new TzxStandardSpeedDataBlock + { + Data = _dataBuffer, + DataLength = (ushort)_dataLength + }; + + // --- If this is the first data block, extract the name from the header + if (_dataBlockCount == 1 && _dataLength == 0x13) + { + // --- It's a header! + var sb = new StringBuilder(16); + for (var i = 2; i <= 11; i++) + { + sb.Append((char)_dataBuffer[i]); + } + var name = sb.ToString().TrimEnd(); + SaveToTapeProvider?.SetName(name); + } + SaveToTapeProvider?.SaveTapeBlock(dataBlock); + } + break; + } + _savePhase = nextPhase; + } + } + + /// + /// This enum represents the operation mode of the tape + /// + public enum TapeOperationMode : byte + { + /// + /// The tape device is passive + /// + Passive = 0, + + /// + /// The tape device is saving information (MIC pulses) + /// + Save, + + /// + /// The tape device generates EAR pulses from a player + /// + Load + } + + /// + /// This class represents a spectrum tape header + /// + public class SpectrumTapeHeader + { + private const int HEADER_LEN = 19; + private const int TYPE_OFFS = 1; + private const int NAME_OFFS = 2; + private const int NAME_LEN = 10; + private const int DATA_LEN_OFFS = 12; + private const int PAR1_OFFS = 14; + private const int PAR2_OFFS = 16; + private const int CHK_OFFS = 18; + + /// + /// The bytes of the header + /// + public byte[] HeaderBytes { get; } + + /// + /// Initializes a new instance of the class. + /// + public SpectrumTapeHeader() + { + HeaderBytes = new byte[HEADER_LEN]; + for (var i = 0; i < HEADER_LEN; i++) HeaderBytes[i] = 0x00; + CalcChecksum(); + } + + /// + /// Initializes a new instance with the specified header data. + /// + /// Header data + public SpectrumTapeHeader(byte[] header) + { + if (header == null) throw new ArgumentNullException(nameof(header)); + if (header.Length != HEADER_LEN) + { + throw new ArgumentException($"Header must be exactly {HEADER_LEN} bytes long"); + } + HeaderBytes = new byte[HEADER_LEN]; + header.CopyTo(HeaderBytes, 0); + CalcChecksum(); + } + + /// + /// Gets or sets the type of the header + /// + public byte Type + { + get { return HeaderBytes[TYPE_OFFS]; } + set + { + HeaderBytes[TYPE_OFFS] = (byte)(value & 0x03); + CalcChecksum(); + } + } + + /// + /// Gets or sets the program name + /// + public string Name + { + get + { + var name = new StringBuilder(NAME_LEN + 4); + for (var i = NAME_OFFS; i < NAME_OFFS + NAME_LEN; i++) + { + name.Append((char)HeaderBytes[i]); + } + return name.ToString().TrimEnd(); + } + set + { + if (value == null) throw new ArgumentNullException(nameof(value)); + if (value.Length > NAME_LEN) value = value.Substring(0, NAME_LEN); + else if (value.Length < NAME_LEN) value = value.PadRight(NAME_LEN, ' '); + for (var i = NAME_OFFS; i < NAME_OFFS + NAME_LEN; i++) + { + HeaderBytes[i] = (byte)value[i - NAME_OFFS]; + } + CalcChecksum(); + } + } + + /// + /// Gets or sets the Data Length + /// + public ushort DataLength + { + get { return GetWord(DATA_LEN_OFFS); } + set { SetWord(DATA_LEN_OFFS, value); } + } + + /// + /// Gets or sets Parameter1 + /// + public ushort Parameter1 + { + get { return GetWord(PAR1_OFFS); } + set { SetWord(PAR1_OFFS, value); } + } + + /// + /// Gets or sets Parameter2 + /// + public ushort Parameter2 + { + get { return GetWord(PAR2_OFFS); } + set { SetWord(PAR2_OFFS, value); } + } + + /// + /// Gets the value of checksum + /// + public byte Checksum => HeaderBytes[CHK_OFFS]; + + /// + /// Calculate the checksum + /// + private void CalcChecksum() + { + var chk = 0x00; + for (var i = 0; i < HEADER_LEN - 1; i++) chk ^= HeaderBytes[i]; + HeaderBytes[CHK_OFFS] = (byte)chk; + } + + /// + /// Gets the word value from the specified offset + /// + private ushort GetWord(int offset) => + (ushort)(HeaderBytes[offset] + 256 * HeaderBytes[offset + 1]); + + /// + /// Sets the word value at the specified offset + /// + private void SetWord(int offset, ushort value) + { + HeaderBytes[offset] = (byte)(value & 0xff); + HeaderBytes[offset + 1] = (byte)(value >> 8); + CalcChecksum(); + } + } + + /// + /// This enum defines the MIC pulse types according to their widths + /// + public enum MicPulseType : byte + { + /// + /// No pulse information + /// + None = 0, + + /// + /// Too short to be a valid pulse + /// + TooShort, + + /// + /// Too long to be a valid pulse + /// + TooLong, + + /// + /// PILOT pulse (Length: 2168 cycles) + /// + Pilot, + + /// + /// SYNC1 pulse (Length: 667 cycles) + /// + Sync1, + + /// + /// SYNC2 pulse (Length: 735 cycles) + /// + Sync2, + + /// + /// BIT0 pulse (Length: 855 cycles) + /// + Bit0, + + /// + /// BIT1 pulse (Length: 1710 cycles) + /// + Bit1, + + /// + /// TERM_SYNC pulse (Length: 947 cycles) + /// + TermSync + } + + /// + /// Represents the playing phase of the current block + /// + public enum PlayPhase + { + /// + /// The player is passive + /// + None = 0, + + /// + /// Pilot signals + /// + Pilot, + + /// + /// Sync signals at the end of the pilot + /// + Sync, + + /// + /// Bits in the data block + /// + Data, + + /// + /// Short terminating sync signal before pause + /// + TermSync, + + /// + /// Pause after the data block + /// + Pause, + + /// + /// The entire block has been played back + /// + Completed + } + + /// + /// This enumeration defines the phases of the SAVE operation + /// + public enum SavePhase : byte + { + /// No SAVE operation is in progress + None = 0, + + /// Emitting PILOT impulses + Pilot, + + /// Emitting SYNC1 impulse + Sync1, + + /// Emitting SYNC2 impulse + Sync2, + + /// Emitting BIT0/BIT1 impulses + Data, + + /// Unexpected pulse detected + Error + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs new file mode 100644 index 0000000000..372a752bf1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class is responsible to "play" a tape file. + /// + public class TapeBlockSetPlayer : ISupportsTapeBlockSetPlayback + { + /// + /// All data blocks that can be played back + /// + public List DataBlocks { get; } + + /// + /// Signs that the player completed playing back the file + /// + public bool Eof { get; private set; } + + /// + /// Gets the currently playing block's index + /// + public int CurrentBlockIndex { get; private set; } + + /// + /// The current playable block + /// + public ISupportsTapeBlockPlayback CurrentBlock => DataBlocks[CurrentBlockIndex]; + + /// + /// The current playing phase + /// + public PlayPhase PlayPhase { get; private set; } + + /// + /// The cycle count of the CPU when playing starts + /// + public long StartCycle { get; private set; } + + public TapeBlockSetPlayer(List dataBlocks) + { + DataBlocks = dataBlocks; + Eof = dataBlocks.Count == 0; + } + + /// + /// Initializes the player + /// + public void InitPlay(long startTact) + { + CurrentBlockIndex = -1; + NextBlock(startTact); + PlayPhase = PlayPhase.None; + StartCycle = startTact; + } + + /// + /// Gets the EAR bit value for the specified cycle + /// + /// Cycles to retrieve the EAR bit + /// + /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block + /// + public bool GetEarBit(long currentCycle) + { + // --- Check for EOF + if (CurrentBlockIndex == DataBlocks.Count - 1 + && (CurrentBlock.PlayPhase == PlayPhase.Pause || CurrentBlock.PlayPhase == PlayPhase.Completed)) + { + Eof = true; + } + if (CurrentBlockIndex >= DataBlocks.Count || CurrentBlock == null) + { + // --- After all playable block played back, there's nothing more to do + PlayPhase = PlayPhase.Completed; + return true; + } + var earbit = CurrentBlock.GetEarBit(currentCycle); + if (CurrentBlock.PlayPhase == PlayPhase.Completed) + { + NextBlock(currentCycle); + } + return earbit; + } + + /// + /// Moves the current block index to the next playable block + /// + /// Cycles time to start the next block + public void NextBlock(long currentCycle) + { + if (CurrentBlockIndex >= DataBlocks.Count - 1) + { + PlayPhase = PlayPhase.Completed; + Eof = true; + return; + } + CurrentBlockIndex++; + CurrentBlock.InitPlay(currentCycle); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs new file mode 100644 index 0000000000..547d7698b9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs @@ -0,0 +1,218 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the standard speed data block in a tape file + /// + public class TapeDataBlockPlayer : ISupportsTapeBlockPlayback, ITapeData + { + /// + /// Pause after this block (default: 1000ms) + /// + public ushort PauseAfter { get; } + + /// + /// Block Data + /// + public byte[] Data { get; } + + /// + /// Initializes a new instance + /// + public TapeDataBlockPlayer(byte[] data, ushort pauseAfter) + { + PauseAfter = pauseAfter; + Data = data; + } + + /// + /// Pilot pulse length + /// + public const int PILOT_PL = 2168; + + /// + /// Pilot pulses in the ROM header block + /// + public const int HEADER_PILOT_COUNT = 8063; + + /// + /// Pilot pulses in the ROM data block + /// + public const int DATA_PILOT_COUNT = 3223; + + /// + /// Sync 1 pulse length + /// + public const int SYNC_1_PL = 667; + + /// + /// Sync 2 pulse lenth + /// + public const int SYNC_2_PL = 735; + + /// + /// Bit 0 pulse length + /// + public const int BIT_0_PL = 855; + + /// + /// Bit 1 pulse length + /// + public const int BIT_1_PL = 1710; + + /// + /// End sync pulse length + /// + public const int TERM_SYNC = 947; + + /// + /// 1 millisecond pause + /// + public const int PAUSE_MS = 3500; + + private int _pilotEnds; + private int _sync1Ends; + private int _sync2Ends; + private int _bitStarts; + private int _bitPulseLength; + private bool _currentBit; + private long _termSyncEnds; + private long _pauseEnds; + + /// + /// The index of the currently playing byte + /// + public int ByteIndex { get; private set; } + + /// + /// The mask of the currently playing bit in the current byte + /// + public byte BitMask { get; private set; } + + /// + /// The current playing phase + /// + public PlayPhase PlayPhase { get; private set; } + + /// + /// The cycle count of the CPU when playing starts + /// + public long StartCycle { get; private set; } + + /// + /// Last cycle queried + /// + public long LastCycle { get; private set; } + + /// + /// Initializes the player + /// + public void InitPlay(long startTact) + { + PlayPhase = PlayPhase.Pilot; + StartCycle = LastCycle = startTact; + _pilotEnds = ((Data[0] & 0x80) == 0 ? HEADER_PILOT_COUNT : DATA_PILOT_COUNT) * PILOT_PL; + _sync1Ends = _pilotEnds + SYNC_1_PL; + _sync2Ends = _sync1Ends + SYNC_2_PL; + ByteIndex = 0; + BitMask = 0x80; + } + + /// + /// Gets the EAR bit value for the specified cycle + /// + /// Tacts to retrieve the EAR bit + /// + /// The EAR bit value to play back + /// + public bool GetEarBit(long currentCycle) + { + var pos = (int)(currentCycle - StartCycle); + LastCycle = currentCycle; + + if (PlayPhase == PlayPhase.Pilot || PlayPhase == PlayPhase.Sync) + { + // --- Generate the appropriate pilot or sync EAR bit + if (pos <= _pilotEnds) + { + // --- Alternating pilot pulses + return (pos / PILOT_PL) % 2 == 0; + } + if (pos <= _sync1Ends) + { + // --- 1st sync pulse + PlayPhase = PlayPhase.Sync; + return false; + } + if (pos <= _sync2Ends) + { + // --- 2nd sync pulse + PlayPhase = PlayPhase.Sync; + return true; + } + PlayPhase = PlayPhase.Data; + _bitStarts = _sync2Ends; + _currentBit = (Data[ByteIndex] & BitMask) != 0; + _bitPulseLength = _currentBit ? BIT_1_PL : BIT_0_PL; + } + if (PlayPhase == PlayPhase.Data) + { + // --- Data block playback + // --- Generate current bit pulse + var bitPos = pos - _bitStarts; + if (bitPos < _bitPulseLength) + { + // --- First pulse of the bit + return false; + } + if (bitPos < 2 * _bitPulseLength) + { + // --- Second pulse of the bit + return true; + } + + // --- Move to the next bit, or byte + if ((BitMask >>= 1) == 0) + { + BitMask = 0x80; + ByteIndex++; + } + + // --- Prepare the next bit + if (ByteIndex < Data.Length) + { + _bitStarts += 2 * _bitPulseLength; + _currentBit = (Data[ByteIndex] & BitMask) != 0; + _bitPulseLength = _currentBit ? BIT_1_PL : BIT_0_PL; + // --- We're in the first pulse of the next bit + return false; + } + + // --- We've played back all data bytes, send terminating pulse + PlayPhase = PlayPhase.TermSync; + _termSyncEnds = currentCycle + TERM_SYNC; + return false; + } + + if (PlayPhase == PlayPhase.TermSync) + { + if (currentCycle < _termSyncEnds) + { + return false; + } + + // --- We've played back all data, not, it's pause time + PlayPhase = PlayPhase.Pause; + _pauseEnds = currentCycle + PAUSE_MS * PauseAfter; + return true; + } + + // --- We need to produce pause signs + if (currentCycle > _pauseEnds) + { + PlayPhase = PlayPhase.Completed; + } + return true; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs new file mode 100644 index 0000000000..933899a6f5 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class TapeFilePlayer : ISupportsTapeBlockPlayback + { + private readonly BinaryReader _reader; + private TapeBlockSetPlayer _player; + + /// + /// Data blocks to play back + /// + public List DataBlocks { get; private set; } + + /// + /// Signs that the player completed playing back the file + /// + public bool Eof => _player.Eof; + + /// + /// Initializes the player from the specified reader + /// + /// BinaryReader instance to get tape file data from + public TapeFilePlayer(BinaryReader reader) + { + _reader = reader; + } + + /// + /// Reads in the content of the tape file so that it can be played + /// + /// True, if read was successful; otherwise, false + public bool ReadContent() + { + // --- First try TzxReader + var tzxReader = new TzxReader(_reader); + var readerFound = false; + try + { + readerFound = tzxReader.ReadContent(); + } + catch (Exception) + { + // --- This exception is intentionally ingnored + } + + if (readerFound) + { + // --- This is a .TZX format + DataBlocks = tzxReader.DataBlocks.Where(b => b is ISupportsTapeBlockPlayback) + .Cast() + .ToList(); + _player = new TapeBlockSetPlayer(DataBlocks); + return true; + } + + // --- Let's assume .TAP tap format + _reader.BaseStream.Seek(0, SeekOrigin.Begin); + var tapReader = new TapReader(_reader); + readerFound = tapReader.ReadContent(); + DataBlocks = tapReader.DataBlocks.Cast() + .ToList(); + _player = new TapeBlockSetPlayer(DataBlocks); + return readerFound; + } + + /// + /// Gets the currently playing block's index + /// + public int CurrentBlockIndex => _player.CurrentBlockIndex; + + /// + /// The current playable block + /// + public ISupportsTapeBlockPlayback CurrentBlock => _player.CurrentBlock; + + /// + /// The current playing phase + /// + public PlayPhase PlayPhase => _player.PlayPhase; + + /// + /// The tact count of the CPU when playing starts + /// + public long StartCycle => _player.StartCycle; + + /// + /// Initializes the player + /// + public void InitPlay(long startCycle) + { + _player.InitPlay(startCycle); + } + + /// + /// Gets the EAR bit value for the specified cycle + /// + /// Tacts to retrieve the EAR bit + /// + /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block + /// + public bool GetEarBit(long currentCycle) => _player.GetEarBit(currentCycle); + + /// + /// Moves the current block index to the next playable block + /// + /// Tacts time to start the next block + public void NextBlock(long currentCycle) => _player.NextBlock(currentCycle); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index d37ab3622b..a466c965d7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -13,9 +13,52 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public abstract partial class SpectrumBase { /// - /// Byte array of total system memory (ROM + RAM + paging) + /// ROM Banks + /// + public byte[] ROM0 = new byte[0x4000]; + public byte[] ROM1 = new byte[0x4000]; + public byte[] ROM2 = new byte[0x4000]; + public byte[] ROM3 = new byte[0x4000]; + + /// + /// RAM Banks /// - public byte[] RAM { get; set; } + public byte[] RAM0 = new byte[0x4000]; // Bank 0 + public byte[] RAM1 = new byte[0x4000]; // Bank 1 + public byte[] RAM2 = new byte[0x4000]; // Bank 2 + public byte[] RAM3 = new byte[0x4000]; // Bank 3 + public byte[] RAM4 = new byte[0x4000]; // Bank 4 + public byte[] RAM5 = new byte[0x4000]; // Bank 5 + public byte[] RAM6 = new byte[0x4000]; // Bank 6 + public byte[] RAM7 = new byte[0x4000]; // Bank 7 + + /// + /// Represents the addressable memory space of the spectrum + /// All banks for the emulated system should be added during initialisation + /// + public Dictionary Memory = new Dictionary(); + + /// + /// Simulates reading from the bus + /// Paging should be handled here + /// + /// + /// + public virtual byte ReadBus(ushort addr) + { + throw new NotImplementedException("Must be overriden"); + } + + /// + /// Simulates writing to the bus + /// Paging should be handled here + /// + /// + /// + public virtual void WriteBus(ushort addr, byte value) + { + throw new NotImplementedException("Must be overriden"); + } /// /// Reads a byte of data from a specified memory address @@ -25,16 +68,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual byte ReadMemory(ushort addr) { - var data = RAM[addr]; - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - return data; + throw new NotImplementedException("Must be overriden"); } - + /* /// /// Reads a byte of data from a specified memory address /// (with no memory contention) @@ -43,9 +79,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual byte PeekMemory(ushort addr) { - var data = RAM[addr]; + var data = ReadBus(addr); return data; } + */ /// /// Writes a byte of data to a specified memory address @@ -55,44 +92,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void WriteMemory(ushort addr, byte value) { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - else if (addr < 0xC000) - { - if (!CPU.IFF1) - { - - } - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - else - { - // uncontended RAM - do nothing - } - - /* - - // Check whether memory is ROM or RAM - switch (addr & 0xC000) - { - case 0x0000: - // Do nothing - we cannot write to ROM - return; - case 0x4000: - // Address is RAM - apply contention if neccessary - var delay = GetContentionValue(_frameCycles); - CPU.TotalExecutedCycles += delay; - break; - } - */ - RAM[addr] = value; + throw new NotImplementedException("Must be overriden"); } + /* /// /// Writes a byte of data to a specified memory address /// (without contention) @@ -106,9 +109,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Do nothing - we cannot write to ROM return; } - - RAM[addr] = value; + + WriteBus(addr, value); } + */ /// /// Fills memory from buffer @@ -117,7 +121,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void FillMemory(byte[] buffer, ushort startAddress) { - buffer?.CopyTo(RAM, startAddress); + //buffer?.CopyTo(RAM, startAddress); + } + + /// + /// Sets up the ROM + /// + /// + /// + public virtual void InitROM(RomData romData) + { + RomData = romData; + // for 16/48k machines only ROM0 is used (no paging) + RomData.RomBytes?.CopyTo(ROM0, 0); } /// @@ -128,7 +144,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual byte FetchScreenMemory(ushort addr) { - var value = RAM[(addr & 0x3FFF) + 0x4000]; + var value = RAM0[(addr & 0x3FFF)];// + 0x4000]; return value; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 29f7c2b6eb..6cefe2f954 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -32,7 +32,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ushort high = CPU.Regs[6]; // combine the low byte (passed in as port) and the high byte (maybe not needed) - ushort word = Convert.ToUInt16(((byte)port << 8 | (byte)high)); + ushort word = Convert.ToUInt16((port << 8 | high)); // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address @@ -152,7 +152,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Border - LSB 3 bits hold the border colour BorderColour = value & BORDER_BIT; - + /* // Buzzer var beepVal = (int)value & (EAR_BIT); @@ -160,13 +160,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum beepVal = (int)value & (MIC_BIT); //var micval = (int)value & (MIC_BIT); - + // if tape is not playing BuzzerDevice.ProcessPulseValue(false, (beepVal) != 0); + */ // tape //_tapeDevice.ProcessMicBit((data & 0x08) != 0); + + BuzzerDevice.ProcessPulseValue(false, (value & 0x10) != 0); + TapeDevice.ProcessMicBit((value & 0x08) != 0); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index 4023bf112c..bdada81b35 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -9,12 +9,27 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* + * Much of the SCREEN implementation has been taken from: https://github.com/Dotneteer/spectnetide + * + * MIT License + + Copyright (c) 2017 Istvan Novak + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + */ + /// /// The abstract class that all emulated models will inherit from /// * Screen * - /// - A goodly portion of the screen rendering code has been taken from: - /// - https://github.com/Dotneteer/spectnetide - /// - (MIT Licensed) /// public abstract partial class SpectrumBase : IVideoProvider { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 759f9402d1..53fe905d01 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -13,12 +13,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The calling ZXSpectrum class (piped in via constructor) /// - protected ZXSpectrum Spectrum { get; set; } + public ZXSpectrum Spectrum { get; set; } /// /// Reference to the instantiated Z80 cpu (piped in via constructor) /// - protected Z80A CPU { get; set; } + public Z80A CPU { get; set; } + + /// + /// ROM and extended info + /// + public RomData RomData { get; set; } /// /// The spectrum buzzer/beeper @@ -35,6 +40,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual Tape TapeDevice { get; set; } + /// + /// The tape provider + /// + public virtual ITapeProvider TapeProvider { get; set; } + /// /// Signs whether the frame has ended /// @@ -111,7 +121,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.EndFrame(); - + TapeDevice.CPUFrameCompleted(); FrameCount++; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 1b598f0474..32bc6bf1b3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -9,17 +9,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public class ZX48 : SpectrumBase { + #region Construction + /// /// Main constructor /// /// /// - public ZX48(ZXSpectrum spectrum, Z80A cpu) + public ZX48(ZXSpectrum spectrum, Z80A cpu, byte[] file) { Spectrum = spectrum; CPU = cpu; - RAM = new byte[0x4000 + 0xC000]; + // init addressable memory from ROM and RAM banks + Memory.Add(0, ROM0); + Memory.Add(1, RAM0); + Memory.Add(2, RAM1); + Memory.Add(3, RAM2); + + //RAM = new byte[0x4000 + 0xC000]; InitScreenConfig(); InitScreen(); @@ -31,8 +39,117 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new Keyboard48(this); - TapeDevice = new Tape(); + TapeProvider = new DefaultTapeProvider(file); + + TapeDevice = new Tape(null, null); TapeDevice.Init(this); } + + #endregion + + #region MemoryMapping + + /* 48K Spectrum has NO memory paging + * + * 0xffff +--------+ + | Bank 2 | + | | + | | + | | + 0xc000 +--------+ + | Bank 1 | + | | + | | + | | + 0x8000 +--------+ + | Bank 0 | + | | + | | + | screen | + 0x4000 +--------+ + | ROM 0 | + | | + | | + | | + 0x0000 +--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + return bank[index]; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + bank[index] = value; + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + + #endregion + + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs new file mode 100644 index 0000000000..3c4858b533 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs @@ -0,0 +1,90 @@ + +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class describes a TAP Block + /// + public sealed class TapDataBlock : + ITapeData, + ITapeDataSerialization, + ISupportsTapeBlockPlayback + { + private TapeDataBlockPlayer _player; + + /// + /// Block Data + /// + public byte[] Data { get; private set; } + + /// + /// Pause after this block (given in milliseconds) + /// + public ushort PauseAfter => 1000; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public void ReadFrom(BinaryReader reader) + { + var length = reader.ReadUInt16(); + Data = reader.ReadBytes(length); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public void WriteTo(BinaryWriter writer) + { + writer.Write((ushort)Data.Length); + writer.Write(Data); + } + + /// + /// The index of the currently playing byte + /// + /// This proprty is made public for test purposes + public int ByteIndex => _player.ByteIndex; + + /// + /// The mask of the currently playing bit in the current byte + /// + public byte BitMask => _player.BitMask; + + /// + /// The current playing phase + /// + public PlayPhase PlayPhase => _player.PlayPhase; + + /// + /// The tact count of the CPU when playing starts + /// + public long StartCycle => _player.StartCycle; + + /// + /// Last tact queried + /// + public long LastCycle => _player.LastCycle; + + /// + /// Initializes the player + /// + public void InitPlay(long startTact) + { + _player = new TapeDataBlockPlayer(Data, PauseAfter); + _player.InitPlay(startTact); + } + + /// + /// Gets the EAR bit value for the specified tact + /// + /// Tacts to retrieve the EAR bit + /// + /// The EAR bit value to play back + /// + public bool GetEarBit(long currentTact) => _player.GetEarBit(currentTact); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs new file mode 100644 index 0000000000..faa7d23c02 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs @@ -0,0 +1,85 @@ + +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class is responsible to "play" a TAP file. + /// + public class TapPlayer : TapReader, ISupportsTapeBlockPlayback + { + private TapeBlockSetPlayer _player; + + /// + /// Signs that the player completed playing back the file + /// + public bool Eof => _player.Eof; + + /// + /// Initializes the player from the specified reader + /// + /// BinaryReader instance to get TZX file data from + public TapPlayer(BinaryReader reader) : base(reader) + { + } + + /// + /// Reads in the content of the TZX file so that it can be played + /// + /// True, if read was successful; otherwise, false + public override bool ReadContent() + { + var success = base.ReadContent(); + + var blocks = DataBlocks.Cast() + .ToList(); + _player = new TapeBlockSetPlayer(blocks); + return success; + } + + /// + /// Gets the currently playing block's index + /// + public int CurrentBlockIndex => _player.CurrentBlockIndex; + + /// + /// The current playable block + /// + public ISupportsTapeBlockPlayback CurrentBlock => _player.CurrentBlock; + + /// + /// The current playing phase + /// + public PlayPhase PlayPhase => _player.PlayPhase; + + /// + /// The tact count of the CPU when playing starts + /// + public long StartCycle => _player.StartCycle; + + /// + /// Initializes the player + /// + public void InitPlay(long startCycle) + { + _player.InitPlay(startCycle); + } + + /// + /// Gets the EAR bit value for the specified tact + /// + /// Tacts to retrieve the EAR bit + /// + /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block + /// + public bool GetEarBit(long currentCycle) => _player.GetEarBit(currentCycle); + + /// + /// Moves the current block index to the next playable block + /// + /// Tacts time to start the next block + public void NextBlock(long currentCycle) => _player.NextBlock(currentCycle); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs new file mode 100644 index 0000000000..b915daf0fc --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs @@ -0,0 +1,52 @@ + +using System.Collections.Generic; +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class reads a TAP file + /// + public class TapReader + { + private readonly BinaryReader _reader; + + /// + /// Data blocks of this TZX file + /// + public IList DataBlocks { get; } + + /// + /// Initializes the player from the specified reader + /// + /// + public TapReader(BinaryReader reader) + { + _reader = reader; + DataBlocks = new List(); + } + + /// + /// Reads in the content of the TZX file so that it can be played + /// + /// True, if read was successful; otherwise, false + public virtual bool ReadContent() + { + try + { + while (_reader.BaseStream.Position != _reader.BaseStream.Length) + { + var tapBlock = new TapDataBlock(); + tapBlock.ReadFrom(_reader); + DataBlocks.Add(tapBlock); + } + return true; + } + catch + { + // --- This exception is intentionally ignored + return false; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs new file mode 100644 index 0000000000..a5145928e9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs @@ -0,0 +1,172 @@ + +using System; +using System.IO; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class describes a TZX Block + /// + public abstract class TzxDataBlockBase : ITapeDataSerialization + { + /// + /// The ID of the block + /// + public abstract int BlockId { get; } + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public abstract void ReadFrom(BinaryReader reader); + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public abstract void WriteTo(BinaryWriter writer); + + /// + /// Override this method to check the content of the block + /// + public virtual bool IsValid => true; + + /// + /// Reads the specified number of words from the reader. + /// + /// Reader to obtain the input from + /// Number of words to get + /// Word array read from the input + public static ushort[] ReadWords(BinaryReader reader, int count) + { + var result = new ushort[count]; + var bytes = reader.ReadBytes(2 * count); + for (var i = 0; i < count; i++) + { + result[i] = (ushort)(bytes[i * 2] + bytes[i * 2 + 1] << 8); + } + return result; + } + + /// + /// Writes the specified array of words to the writer + /// + /// Output + /// Word array + public static void WriteWords(BinaryWriter writer, ushort[] words) + { + foreach (var word in words) + { + writer.Write(word); + } + } + + /// + /// Converts the provided bytes to an ASCII string + /// + /// Bytes to convert + /// First byte offset + /// Number of bytes + /// ASCII string representation + public static string ToAsciiString(byte[] bytes, int offset = 0, int count = -1) + { + if (count < 0) count = bytes.Length - offset; + var sb = new StringBuilder(); + for (var i = offset; i < count; i++) + { + sb.Append(Convert.ToChar(bytes[i])); + } + return sb.ToString(); + } + } + + /// + /// Base class for all TZX block type with data length of 3 bytes + /// + public abstract class Tzx3ByteDataBlockBase : TzxDataBlockBase + { + /// + /// Used bits in the last byte (other bits should be 0) + /// + /// + /// (e.g. if this is 6, then the bits used(x) in the last byte are: + /// xxxxxx00, where MSb is the leftmost bit, LSb is the rightmost bit) + /// + public byte LastByteUsedBits { get; set; } + + /// + /// Lenght of block data + /// + public byte[] DataLength { get; set; } + + /// + /// Block Data + /// + public byte[] Data { get; set; } + + /// + /// Override this method to check the content of the block + /// + public override bool IsValid => GetLength() == Data.Length; + + /// + /// Calculates data length + /// + protected int GetLength() + { + return DataLength[0] + DataLength[1] << 8 + DataLength[2] << 16; + } + } + + /// + /// This class represents a TZX data block with empty body + /// + public abstract class TzxBodylessDataBlockBase : TzxDataBlockBase + { + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + } + } + + /// + /// This class represents a deprecated block + /// + public abstract class TzxDeprecatedDataBlockBase : TzxDataBlockBase + { + /// + /// Reads through the block infromation, and does not store it + /// + /// Stream to read the block from + public abstract void ReadThrough(BinaryReader reader); + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + throw new InvalidOperationException("Deprecated TZX data blocks cannot be written."); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs new file mode 100644 index 0000000000..18ce828a09 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs @@ -0,0 +1,1411 @@ + +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxArchiveInfoDataBlock : Tzx3ByteDataBlockBase + { + /// + /// Length of the whole block (without these two bytes) + /// + public ushort Length { get; set; } + + /// + /// Number of text strings + /// + public byte StringCount { get; set; } + + /// + /// List of text strings + /// + public TzxText[] TextStrings { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x32; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Length = reader.ReadUInt16(); + StringCount = reader.ReadByte(); + TextStrings = new TzxText[StringCount]; + for (var i = 0; i < StringCount; i++) + { + var text = new TzxText(); + text.ReadFrom(reader); + TextStrings[i] = text; + } + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Length); + writer.Write(StringCount); + foreach (var text in TextStrings) + { + text.WriteTo(writer); + } + } + } + + /// + /// This block was created to support the Commodore 64 standard + /// ROM and similar tape blocks. + /// + public class TzxC64RomTypeDataBlock : TzxDeprecatedDataBlockBase + { + /// + /// The ID of the block + /// + public override int BlockId => 0x16; + + /// + /// Reads through the block infromation, and does not store it + /// + /// Stream to read the block from + public override void ReadThrough(BinaryReader reader) + { + var length = reader.ReadInt32(); + reader.ReadBytes(length - 4); + } + } + + /// + /// This block is made to support another type of encoding that is + /// commonly used by the C64. + /// + public class TzxC64TurboTapeDataBlock : TzxDeprecatedDataBlockBase + { + /// + /// The ID of the block + /// + public override int BlockId => 0x17; + + /// + /// Reads through the block infromation, and does not store it + /// + /// Stream to read the block from + public override void ReadThrough(BinaryReader reader) + { + var length = reader.ReadInt32(); + reader.ReadBytes(length - 4); + } + } + + /// + /// This block is an analogue of the CALL Subroutine statement. + /// + /// + /// It basically executes a sequence of blocks that are somewhere + /// else and then goes back to the next block. Because more than + /// one call can be normally used you can include a list of sequences + /// to be called. The 'nesting' of call blocks is also not allowed + /// for the simplicity reasons. You can, of course, use the CALL + /// blocks in the LOOP sequences and vice versa. + /// + public class TzxCallSequenceDataBlock : TzxDataBlockBase + { + /// + /// Number of group name + /// + public byte NumberOfCalls { get; set; } + + /// + /// Group name bytes + /// + public ushort[] BlockOffsets { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x26; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + NumberOfCalls = reader.ReadByte(); + BlockOffsets = ReadWords(reader, NumberOfCalls); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(NumberOfCalls); + WriteWords(writer, BlockOffsets); + } + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxCswRecordingDataBlock : TzxDataBlockBase + { + /// + /// Block length (without these four bytes) + /// + public uint BlockLength { get; set; } + + /// + /// Pause after this block + /// + public ushort PauseAfter { get; set; } + + /// + /// Sampling rate + /// + public byte[] SamplingRate { get; set; } + + /// + /// Compression type + /// + /// + /// 0x01=RLE, 0x02=Z-RLE + /// + public byte CompressionType { get; set; } + + /// + /// Number of stored pulses (after decompression, for validation purposes) + /// + public uint PulseCount { get; set; } + + /// + /// CSW data, encoded according to the CSW file format specification + /// + public byte[] Data { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x18; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + BlockLength = reader.ReadUInt32(); + PauseAfter = reader.ReadUInt16(); + SamplingRate = reader.ReadBytes(3); + CompressionType = reader.ReadByte(); + PulseCount = reader.ReadUInt32(); + var length = (int)BlockLength - 4 /* PauseAfter*/ - 3 /* SamplingRate */ + - 1 /* CompressionType */ - 4 /* PulseCount */; + Data = reader.ReadBytes(length); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(BlockLength); + writer.Write(PauseAfter); + writer.Write(SamplingRate); + writer.Write(CompressionType); + writer.Write(PulseCount); + writer.Write(Data); + } + + /// + /// Override this method to check the content of the block + /// + public override bool IsValid => BlockLength == 4 + 3 + 1 + 4 + Data.Length; + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxCustomInfoDataBlock : Tzx3ByteDataBlockBase + { + /// + /// Identification string (in ASCII) + /// + public byte[] Id { get; set; } + + /// + /// String representation of the ID + /// + public string IdText => ToAsciiString(Id); + + /// + /// Length of the custom info + /// + public uint Length { get; set; } + + /// + /// Custom information + /// + public byte[] CustomInfo { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x35; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Id = reader.ReadBytes(10); + Length = reader.ReadUInt32(); + CustomInfo = reader.ReadBytes((int)Length); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Id); + writer.Write(Length); + writer.Write(CustomInfo); + } + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxDirectRecordingDataBlock : Tzx3ByteDataBlockBase + { + /// + /// Number of T-states per sample (bit of data) + /// + public ushort TactsPerSample { get; set; } + + /// + /// Pause after this block + /// + public ushort PauseAfter { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x15; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + TactsPerSample = reader.ReadUInt16(); + PauseAfter = reader.ReadUInt16(); + LastByteUsedBits = reader.ReadByte(); + DataLength = reader.ReadBytes(3); + Data = reader.ReadBytes(GetLength()); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(TactsPerSample); + writer.Write(PauseAfter); + writer.Write(LastByteUsedBits); + writer.Write(DataLength); + writer.Write(Data); + } + } + + /// + /// This is a special block that would normally be generated only by emulators. + /// + public class TzxEmulationInfoDataBlock : TzxDeprecatedDataBlockBase + { + /// + /// The ID of the block + /// + public override int BlockId => 0x34; + + /// + /// Reads through the block infromation, and does not store it + /// + /// Stream to read the block from + public override void ReadThrough(BinaryReader reader) + { + reader.ReadBytes(8); + } + } + + /// + /// Represents a generalized data block in a TZX file + /// + public class TzxGeneralizedDataBlock : TzxDataBlockBase + { + /// + /// Block length (without these four bytes) + /// + public uint BlockLength { get; set; } + + /// + /// Pause after this block + /// + public ushort PauseAfter { get; set; } + + /// + /// Total number of symbols in pilot/sync block (can be 0) + /// + public uint Totp { get; set; } + + /// + /// Maximum number of pulses per pilot/sync symbol + /// + public byte Npp { get; set; } + + /// + /// Number of pilot/sync symbols in the alphabet table (0=256) + /// + public byte Asp { get; set; } + + /// + /// Total number of symbols in data stream (can be 0) + /// + public uint Totd { get; set; } + + /// + /// Maximum number of pulses per data symbol + /// + public byte Npd { get; set; } + + /// + /// Number of data symbols in the alphabet table (0=256) + /// + public byte Asd { get; set; } + + /// + /// Pilot and sync symbols definition table + /// + /// + /// This field is present only if Totp > 0 + /// + public TzxSymDef[] PilotSymDef { get; set; } + + /// + /// Pilot and sync data stream + /// + /// + /// This field is present only if Totd > 0 + /// + public TzxPrle[] PilotStream { get; set; } + + /// + /// Data symbols definition table + /// + /// + /// This field is present only if Totp > 0 + /// + public TzxSymDef[] DataSymDef { get; set; } + + /// + /// Data stream + /// + /// + /// This field is present only if Totd > 0 + /// + public TzxPrle[] DataStream { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x19; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + BlockLength = reader.ReadUInt32(); + PauseAfter = reader.ReadUInt16(); + Totp = reader.ReadUInt32(); + Npp = reader.ReadByte(); + Asp = reader.ReadByte(); + Totd = reader.ReadUInt32(); + Npd = reader.ReadByte(); + Asd = reader.ReadByte(); + + PilotSymDef = new TzxSymDef[Asp]; + for (var i = 0; i < Asp; i++) + { + var symDef = new TzxSymDef(Npp); + symDef.ReadFrom(reader); + PilotSymDef[i] = symDef; + } + + PilotStream = new TzxPrle[Totp]; + for (var i = 0; i < Totp; i++) + { + PilotStream[i].Symbol = reader.ReadByte(); + PilotStream[i].Repetitions = reader.ReadUInt16(); + } + + DataSymDef = new TzxSymDef[Asd]; + for (var i = 0; i < Asd; i++) + { + var symDef = new TzxSymDef(Npd); + symDef.ReadFrom(reader); + DataSymDef[i] = symDef; + } + + DataStream = new TzxPrle[Totd]; + for (var i = 0; i < Totd; i++) + { + DataStream[i].Symbol = reader.ReadByte(); + DataStream[i].Repetitions = reader.ReadUInt16(); + } + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(BlockLength); + writer.Write(PauseAfter); + writer.Write(Totp); + writer.Write(Npp); + writer.Write(Asp); + writer.Write(Totd); + writer.Write(Npd); + writer.Write(Asd); + for (var i = 0; i < Asp; i++) + { + PilotSymDef[i].WriteTo(writer); + } + for (var i = 0; i < Totp; i++) + { + writer.Write(PilotStream[i].Symbol); + writer.Write(PilotStream[i].Repetitions); + } + + for (var i = 0; i < Asd; i++) + { + DataSymDef[i].WriteTo(writer); + } + + for (var i = 0; i < Totd; i++) + { + writer.Write(DataStream[i].Symbol); + writer.Write(DataStream[i].Repetitions); + } + } + } + + /// + /// This block is generated when you merge two ZX Tape files together. + /// + /// + /// It is here so that you can easily copy the files together and use + /// them. Of course, this means that resulting file would be 10 bytes + /// longer than if this block was not used. All you have to do if + /// you encounter this block ID is to skip next 9 bytes. + /// + public class TzxGlueDataBlock : TzxDataBlockBase + { + /// + /// Value: { "XTape!", 0x1A, MajorVersion, MinorVersion } + /// + /// + /// Just skip these 9 bytes and you will end up on the next ID. + /// + public byte[] Glue { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x5A; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Glue = reader.ReadBytes(9); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Glue); + } + } + + /// + /// This indicates the end of a group. This block has no body. + /// + public class TzxGroupEndDataBlock : TzxBodylessDataBlockBase + { + /// + /// The ID of the block + /// + public override int BlockId => 0x22; + } + + /// + /// This block marks the start of a group of blocks which are + /// to be treated as one single (composite) block. + /// + public class TzxGroupStartDataBlock : TzxDataBlockBase + { + /// + /// Number of group name + /// + public byte Length { get; set; } + + /// + /// Group name bytes + /// + public byte[] Chars { get; set; } + + /// + /// Gets the group name + /// + public string GroupName => ToAsciiString(Chars); + + /// + /// The ID of the block + /// + public override int BlockId => 0x21; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Length = reader.ReadByte(); + Chars = reader.ReadBytes(Length); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Length); + writer.Write(Chars); + } + } + + /// + /// + /// + public class TzxHardwareInfoDataBlock : TzxDataBlockBase + { + /// + /// Number of machines and hardware types for which info is supplied + /// + public byte HwCount { get; set; } + + /// + /// List of machines and hardware + /// + public TzxHwInfo[] HwInfo { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x33; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + HwCount = reader.ReadByte(); + HwInfo = new TzxHwInfo[HwCount]; + for (var i = 0; i < HwCount; i++) + { + var hw = new TzxHwInfo(); + hw.ReadFrom(reader); + HwInfo[i] = hw; + } + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(HwCount); + foreach (var hw in HwInfo) + { + hw.WriteTo(writer); + } + } + } + + /// + /// This block will enable you to jump from one block to another within the file. + /// + /// + /// Jump 0 = 'Loop Forever' - this should never happen + /// Jump 1 = 'Go to the next block' - it is like NOP in assembler + /// Jump 2 = 'Skip one block' + /// Jump -1 = 'Go to the previous block' + /// + public class TzxJumpDataBlock : TzxDataBlockBase + { + /// + /// Relative jump value + /// + /// + /// + public short Jump { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x23; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Jump = reader.ReadInt16(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Jump); + } + } + + /// + /// It means that the utility should jump back to the start + /// of the loop if it hasn't been run for the specified number + /// of times. + /// + public class TzxLoopEndDataBlock : TzxBodylessDataBlockBase + { + /// + /// The ID of the block + /// + public override int BlockId => 0x25; + } + + /// + /// If you have a sequence of identical blocks, or of identical + /// groups of blocks, you can use this block to tell how many + /// times they should be repeated. + /// + public class TzxLoopStartDataBlock : TzxDataBlockBase + { + /// + /// Number of repetitions (greater than 1) + /// + public ushort Loops { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x24; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Loops = reader.ReadUInt16(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Loops); + } + } + + /// + /// This will enable the emulators to display a message for a given time. + /// + /// + /// This should not stop the tape and it should not make silence. If the + /// time is 0 then the emulator should wait for the user to press a key. + /// + public class TzxMessageDataBlock : TzxDataBlockBase + { + /// + /// Time (in seconds) for which the message should be displayed + /// + public byte Time { get; set; } + + /// + /// Length of the description + /// + public byte MessageLength { get; set; } + + /// + /// The description bytes + /// + public byte[] Message; + + /// + /// The string form of description + /// + public string MessageText => ToAsciiString(Message); + + /// + /// The ID of the block + /// + public override int BlockId => 0x31; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Time = reader.ReadByte(); + MessageLength = reader.ReadByte(); + Message = reader.ReadBytes(MessageLength); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Time); + writer.Write(MessageLength); + writer.Write(Message); + } + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxPulseSequenceDataBlock : TzxDataBlockBase + { + /// + /// Pause after this block + /// + public byte PulseCount { get; set; } + + /// + /// Lenght of block data + /// + public ushort[] PulseLengths { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x13; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + PulseCount = reader.ReadByte(); + PulseLengths = ReadWords(reader, PulseCount); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(PulseCount); + WriteWords(writer, PulseLengths); + } + + /// + /// Override this method to check the content of the block + /// + public override bool IsValid => PulseCount == PulseLengths.Length; + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxPureDataBlock : Tzx3ByteDataBlockBase + { + /// + /// Length of the zero bit + /// + public ushort ZeroBitPulseLength { get; set; } + + /// + /// Length of the one bit + /// + public ushort OneBitPulseLength { get; set; } + + /// + /// Pause after this block + /// + public ushort PauseAfter { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x14; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + ZeroBitPulseLength = reader.ReadUInt16(); + OneBitPulseLength = reader.ReadUInt16(); + LastByteUsedBits = reader.ReadByte(); + PauseAfter = reader.ReadUInt16(); + DataLength = reader.ReadBytes(3); + Data = reader.ReadBytes(GetLength()); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(ZeroBitPulseLength); + writer.Write(OneBitPulseLength); + writer.Write(LastByteUsedBits); + writer.Write(PauseAfter); + writer.Write(DataLength); + writer.Write(Data); + } + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxPureToneDataBlock : TzxDataBlockBase + { + /// + /// Pause after this block + /// + public ushort PulseLength { get; private set; } + + /// + /// Lenght of block data + /// + public ushort PulseCount { get; private set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x12; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + PulseLength = reader.ReadUInt16(); + PulseCount = reader.ReadUInt16(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(PulseLength); + writer.Write(PulseCount); + } + } + + /// + /// This block indicates the end of the Called Sequence. + /// The next block played will be the block after the last + /// CALL block + /// + public class TzxReturnFromSequenceDataBlock : TzxBodylessDataBlockBase + { + /// + /// The ID of the block + /// + public override int BlockId => 0x27; + } + + /// + /// Pause (silence) or 'Stop the Tape' block + /// + public class TzxSelectDataBlock : TzxDataBlockBase + { + /// + /// Length of the whole block (without these two bytes) + /// + public ushort Length { get; set; } + + /// + /// Number of selections + /// + public byte SelectionCount { get; set; } + + /// + /// List of selections + /// + public TzxSelect[] Selections { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x28; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Length = reader.ReadUInt16(); + SelectionCount = reader.ReadByte(); + Selections = new TzxSelect[SelectionCount]; + foreach (var selection in Selections) + { + selection.ReadFrom(reader); + } + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Length); + writer.Write(SelectionCount); + foreach (var selection in Selections) + { + selection.WriteTo(writer); + } + } + } + + /// + /// This block sets the current signal level to the specified value (high or low). + /// + public class TzxSetSignalLevelDataBlock : TzxDataBlockBase + { + /// + /// Length of the block without these four bytes + /// + public uint Lenght { get; } = 1; + + /// + /// Signal level (0=low, 1=high) + /// + public byte SignalLevel { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x2B; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + reader.ReadUInt32(); + SignalLevel = reader.ReadByte(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Lenght); + writer.Write(SignalLevel); + } + } + + /// + /// Pause (silence) or 'Stop the Tape' block + /// + public class TzxSilenceDataBlock : TzxDataBlockBase + { + /// + /// Duration of silence + /// + /// + /// This will make a silence (low amplitude level (0)) for a given time + /// in milliseconds. If the value is 0 then the emulator or utility should + /// (in effect) STOP THE TAPE, i.e. should not continue loading until + /// the user or emulator requests it. + /// + public ushort Duration { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x20; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Duration = reader.ReadUInt16(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Duration); + } + } + + /// + /// This block was created to support the Commodore 64 standard + /// ROM and similar tape blocks. + /// + public class TzxSnapshotBlock : TzxDeprecatedDataBlockBase + { + /// + /// The ID of the block + /// + public override int BlockId => 0x40; + + /// + /// Reads through the block infromation, and does not store it + /// + /// Stream to read the block from + public override void ReadThrough(BinaryReader reader) + { + var length = reader.ReadInt32(); + length = length & 0x00FFFFFF; + reader.ReadBytes(length); + } + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxStandardSpeedDataBlock : TzxDataBlockBase, ISupportsTapeBlockPlayback, ITapeData + { + private TapeDataBlockPlayer _player; + + /// + /// Pause after this block (default: 1000ms) + /// + public ushort PauseAfter { get; set; } = 1000; + + /// + /// Lenght of block data + /// + public ushort DataLength { get; set; } + + /// + /// Block Data + /// + public byte[] Data { get; set; } + + /// + /// The ID of the block + /// + public override int BlockId => 0x10; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + PauseAfter = reader.ReadUInt16(); + DataLength = reader.ReadUInt16(); + Data = reader.ReadBytes(DataLength); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write((byte)BlockId); + writer.Write(PauseAfter); + writer.Write(DataLength); + writer.Write(Data, 0, DataLength); + } + + /// + /// The index of the currently playing byte + /// + /// This proprty is made public for test purposes + public int ByteIndex => _player.ByteIndex; + + /// + /// The mask of the currently playing bit in the current byte + /// + public byte BitMask => _player.BitMask; + + /// + /// The current playing phase + /// + public PlayPhase PlayPhase => _player.PlayPhase; + + /// + /// The tact count of the CPU when playing starts + /// + public long StartCycle=> _player.StartCycle; + + /// + /// Last tact queried + /// + public long LastTact => _player.LastCycle; + + /// + /// Initializes the player + /// + public void InitPlay(long startCycle) + { + _player = new TapeDataBlockPlayer(Data, PauseAfter); + _player.InitPlay(startCycle); + } + + /// + /// Gets the EAR bit value for the specified tact + /// + /// Tacts to retrieve the EAR bit + /// + /// The EAR bit value to play back + /// + public bool GetEarBit(long currentCycle) => _player.GetEarBit(currentCycle); + } + + /// + /// When this block is encountered, the tape will stop ONLY if + /// the machine is an 48K Spectrum. + /// + /// + /// This block is to be used for multiloading games that load one + /// level at a time in 48K mode, but load the entire tape at once + /// if in 128K mode. This block has no body of its own, but follows + /// the extension rule. + /// + public class TzxStopTheTape48DataBlock : TzxDataBlockBase + { + /// + /// Length of the block without these four bytes (0) + /// + public uint Lenght { get; } = 0; + + /// + /// The ID of the block + /// + public override int BlockId => 0x2A; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + reader.ReadUInt32(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Lenght); + } + } + + /// + /// This is meant to identify parts of the tape, so you know where level 1 starts, + /// where to rewind to when the game ends, etc. + /// + /// + /// This description is not guaranteed to be shown while the tape is playing, + /// but can be read while browsing the tape or changing the tape pointer. + /// + public class TzxTextDescriptionDataBlock : TzxDataBlockBase + { + /// + /// Length of the description + /// + public byte DescriptionLength { get; set; } + + /// + /// The description bytes + /// + public byte[] Description; + + /// + /// The string form of description + /// + public string DescriptionText => ToAsciiString(Description); + + /// + /// The ID of the block + /// + public override int BlockId => 0x30; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + DescriptionLength = reader.ReadByte(); + Description = reader.ReadBytes(DescriptionLength); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(DescriptionLength); + writer.Write(Description); + } + } + + /// + /// Represents the standard speed data block in a TZX file + /// + public class TzxTurboSpeedDataBlock : Tzx3ByteDataBlockBase + { + /// + /// Length of pilot pulse + /// + public ushort PilotPulseLength { get; set; } + + /// + /// Length of the first sync pulse + /// + public ushort Sync1PulseLength { get; set; } + + /// + /// Length of the second sync pulse + /// + public ushort Sync2PulseLength { get; set; } + + /// + /// Length of the zero bit + /// + public ushort ZeroBitPulseLength { get; set; } + + /// + /// Length of the one bit + /// + public ushort OneBitPulseLength { get; set; } + + /// + /// Length of the pilot tone + /// + public ushort PilotToneLength { get; set; } + + /// + /// Pause after this block + /// + public ushort PauseAfter { get; set; } + + public TzxTurboSpeedDataBlock() + { + PilotPulseLength = 2168; + Sync1PulseLength = 667; + Sync2PulseLength = 735; + ZeroBitPulseLength = 855; + OneBitPulseLength = 1710; + PilotToneLength = 8063; + LastByteUsedBits = 8; + } + + /// + /// The ID of the block + /// + public override int BlockId => 0x11; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + PilotPulseLength = reader.ReadUInt16(); + Sync1PulseLength = reader.ReadUInt16(); + Sync2PulseLength = reader.ReadUInt16(); + ZeroBitPulseLength = reader.ReadUInt16(); + OneBitPulseLength = reader.ReadUInt16(); + PilotToneLength = reader.ReadUInt16(); + LastByteUsedBits = reader.ReadByte(); + PauseAfter = reader.ReadUInt16(); + DataLength = reader.ReadBytes(3); + Data = reader.ReadBytes(GetLength()); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(PilotPulseLength); + writer.Write(Sync1PulseLength); + writer.Write(Sync2PulseLength); + writer.Write(ZeroBitPulseLength); + writer.Write(OneBitPulseLength); + writer.Write(PilotToneLength); + writer.Write(LastByteUsedBits); + writer.Write(PauseAfter); + writer.Write(DataLength); + writer.Write(Data); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs new file mode 100644 index 0000000000..0a83aa3bfc --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs @@ -0,0 +1,250 @@ + +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This blocks contains information about the hardware that the programs on this tape use. + /// + public class TzxHwInfo : ITapeDataSerialization + { + /// + /// Hardware type + /// + public byte HwType { get; set; } + + /// + /// Hardwer Id + /// + public byte HwId { get; set; } + + /// + /// Information about the tape + /// + /// + /// 00 - The tape RUNS on this machine or with this hardware, + /// but may or may not use the hardware or special features of the machine. + /// 01 - The tape USES the hardware or special features of the machine, + /// such as extra memory or a sound chip. + /// 02 - The tape RUNS but it DOESN'T use the hardware + /// or special features of the machine. + /// 03 - The tape DOESN'T RUN on this machine or with this hardware. + /// + public byte TapeInfo; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public void ReadFrom(BinaryReader reader) + { + HwType = reader.ReadByte(); + HwId = reader.ReadByte(); + TapeInfo = reader.ReadByte(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public void WriteTo(BinaryWriter writer) + { + writer.Write(HwType); + writer.Write(HwId); + writer.Write(TapeInfo); + } + } + + /// + /// Symbol repetitions + /// + public struct TzxPrle + { + /// + /// Symbol represented + /// + public byte Symbol; + + /// + /// Number of repetitions + /// + public ushort Repetitions; + } + + /// + /// This block represents an extremely wide range of data encoding techniques. + /// + /// + /// The basic idea is that each loading component (pilot tone, sync pulses, data) + /// is associated to a specific sequence of pulses, where each sequence (wave) can + /// contain a different number of pulses from the others. In this way we can have + /// a situation where bit 0 is represented with 4 pulses and bit 1 with 8 pulses. + /// + public class TzxSymDef : ITapeDataSerialization + { + /// + /// Bit 0 - Bit 1: Starting symbol polarity + /// + /// + /// 00: opposite to the current level (make an edge, as usual) - default + /// 01: same as the current level(no edge - prolongs the previous pulse) + /// 10: force low level + /// 11: force high level + /// + public byte SymbolFlags; + + /// + /// The array of pulse lengths + /// + public ushort[] PulseLengths; + + public TzxSymDef(byte maxPulses) + { + PulseLengths = new ushort[maxPulses]; + } + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public void ReadFrom(BinaryReader reader) + { + SymbolFlags = reader.ReadByte(); + PulseLengths = TzxDataBlockBase.ReadWords(reader, PulseLengths.Length); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public void WriteTo(BinaryWriter writer) + { + writer.Write(SymbolFlags); + TzxDataBlockBase.WriteWords(writer, PulseLengths); + } + } + + /// + /// This is meant to identify parts of the tape, so you know where level 1 starts, + /// where to rewind to when the game ends, etc. + /// + /// + /// This description is not guaranteed to be shown while the tape is playing, + /// but can be read while browsing the tape or changing the tape pointer. + /// + public class TzxText : ITapeDataSerialization + { + /// + /// Text identification byte. + /// + /// + /// 00 - Full title + /// 01 - Software house/publisher + /// 02 - Author(s) + /// 03 - Year of publication + /// 04 - Language + /// 05 - Game/utility type + /// 06 - Price + /// 07 - Protection scheme/loader + /// 08 - Origin + /// FF - Comment(s) + /// + public byte Type { get; set; } + + /// + /// Length of the description + /// + public byte Length { get; set; } + + /// + /// The description bytes + /// + public byte[] TextBytes; + + /// + /// The string form of description + /// + public string Text => TzxDataBlockBase.ToAsciiString(TextBytes); + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public void ReadFrom(BinaryReader reader) + { + Type = reader.ReadByte(); + Length = reader.ReadByte(); + TextBytes = reader.ReadBytes(Length); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public void WriteTo(BinaryWriter writer) + { + writer.Write(Type); + writer.Write(Length); + writer.Write(TextBytes); + } + } + + /// + /// This block represents select structure + /// + public class TzxSelect : ITapeDataSerialization + { + /// + /// Bit 0 - Bit 1: Starting symbol polarity + /// + /// + /// 00: opposite to the current level (make an edge, as usual) - default + /// 01: same as the current level(no edge - prolongs the previous pulse) + /// 10: force low level + /// 11: force high level + /// + public ushort BlockOffset; + + /// + /// Length of the description + /// + public byte DescriptionLength { get; set; } + + /// + /// The description bytes + /// + public byte[] Description; + + /// + /// The string form of description + /// + public string DescriptionText => TzxDataBlockBase.ToAsciiString(Description); + + public TzxSelect(byte length) + { + DescriptionLength = length; + } + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public void ReadFrom(BinaryReader reader) + { + BlockOffset = reader.ReadUInt16(); + DescriptionLength = reader.ReadByte(); + Description = reader.ReadBytes(DescriptionLength); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public void WriteTo(BinaryWriter writer) + { + writer.Write(BlockOffset); + writer.Write(DescriptionLength); + writer.Write(Description); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs new file mode 100644 index 0000000000..8742a35679 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs @@ -0,0 +1,282 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Identified AD or DA converter types + /// + public enum TzxAdOrDaConverterType : byte + { + HarleySystemsAdc8P2 = 0x00, + BlackboardElectronics = 0x01 + } + + /// + /// Identified computer types + /// + public enum TzxComputerType : byte + { + ZxSpectrum16 = 0x00, + ZxSpectrum48OrPlus = 0x01, + ZxSpectrum48Issue1 = 0x02, + ZxSpectrum128 = 0x03, + ZxSpectrum128P2 = 0x04, + ZxSpectrum128P2AOr3 = 0x05, + Tc2048 = 0x06, + Ts2068 = 0x07, + Pentagon128 = 0x08, + SamCoupe = 0x09, + DidaktikM = 0x0A, + DidaktikGama = 0x0B, + Zx80 = 0x0C, + Zx81 = 0x0D, + ZxSpectrum128Spanish = 0x0E, + ZxSpectrumArabic = 0x0F, + Tk90X = 0x10, + Tk95 = 0x11, + Byte = 0x12, + Elwro800D3 = 0x13, + ZsScorpion256 = 0x14, + AmstradCpc464 = 0x15, + AmstradCpc664 = 0x16, + AmstradCpc6128 = 0x17, + AmstradCpc464P = 0x18, + AmstradCpc6128P = 0x19, + JupiterAce = 0x1A, + Enterprise = 0x1B, + Commodore64 = 0x1C, + Commodore128 = 0x1D, + InvesSpectrumP = 0x1E, + Profi = 0x1F, + GrandRomMax = 0x20, + Kay1024 = 0x21, + IceFelixHc91 = 0x22, + IceFelixHc2000 = 0x23, + AmaterskeRadioMistrum = 0x24, + Quorum128 = 0x25, + MicroArtAtm = 0x26, + MicroArtAtmTurbo2 = 0x27, + Chrome = 0x28, + ZxBadaloc = 0x29, + Ts1500 = 0x2A, + Lambda = 0x2B, + Tk65 = 0x2C, + Zx97 = 0x2D + } + + /// + /// Identified digitizer types + /// + public enum TzxDigitizerType : byte + { + RdDigitalTracer = 0x00, + DkTronicsLightPen = 0x01, + MicrographPad = 0x02, + RomnticRobotVideoface = 0x03 + } + + /// + /// Identified EPROM programmer types + /// + public enum TzxEpromProgrammerType : byte + { + OrmeElectronics = 0x00 + } + + /// + /// Identified external storage types + /// + public enum TzxExternalStorageType : byte + { + ZxMicroDrive = 0x00, + OpusDiscovery = 0x01, + MgtDisciple = 0x02, + MgtPlusD = 0x03, + RobotronicsWafaDrive = 0x04, + TrDosBetaDisk = 0x05, + ByteDrive = 0x06, + Watsford = 0x07, + Fiz = 0x08, + Radofin = 0x09, + DidaktikDiskDrive = 0x0A, + BsDos = 0x0B, + ZxSpectrumP3DiskDrive = 0x0C, + JloDiskInterface = 0x0D, + TimexFdd3000 = 0x0E, + ZebraDiskDrive = 0x0F, + RamexMillenia = 0x10, + Larken = 0x11, + KempstonDiskInterface = 0x12, + Sandy = 0x13, + ZxSpectrumP3EHardDisk = 0x14, + ZxAtaSp = 0x15, + DivIde = 0x16, + ZxCf = 0x17 + } + + /// + /// Identified graphics types + /// + public enum TzxGraphicsType : byte + { + WrxHiRes = 0x00, + G007 = 0x01, + Memotech = 0x02, + LambdaColour = 0x03 + } + + /// + /// Represents the hardware types that can be defined + /// + public enum TzxHwType : byte + { + Computer = 0x00, + ExternalStorage = 0x01, + RomOrRamTypeAddOn = 0x02, + SoundDevice = 0x03, + JoyStick = 0x04, + Mouse = 0x05, + OtherController = 0x06, + SerialPort = 0x07, + ParallelPort = 0x08, + Printer = 0x09, + Modem = 0x0A, + Digitizer = 0x0B, + NetworkAdapter = 0x0C, + Keyboard = 0x0D, + AdOrDaConverter = 0x0E, + EpromProgrammer = 0x0F, + Graphics = 0x10 + } + + /// + /// Identified joystick types + /// + public enum TzxJoystickType + { + Kempston = 0x00, + ProtekCursor = 0x01, + Sinclair2Left = 0x02, + Sinclair1Right = 0x03, + Fuller = 0x04 + } + + /// + /// Identified keyboard and keypad types + /// + public enum TzxKeyboardType : byte + { + KeypadForZxSpectrum128K = 0x00 + } + + /// + /// Identified modem types + /// + public enum TzxModemTypes : byte + { + PrismVtx5000 = 0x00, + Westridge2050 = 0x01 + } + + /// + /// Identified mouse types + /// + public enum TzxMouseType : byte + { + AmxMouse = 0x00, + KempstonMouse = 0x01 + } + + /// + /// Identified network adapter types + /// + public enum TzxNetworkAdapterType : byte + { + ZxInterface1 = 0x00 + } + + /// + /// Identified other controller types + /// + public enum TzxOtherControllerType : byte + { + Trisckstick = 0x00, + ZxLightGun = 0x01, + ZebraGraphicTablet = 0x02, + DefnederLightGun = 0x03 + } + + /// + /// Identified parallel port types + /// + public enum TzxParallelPortType : byte + { + KempstonS = 0x00, + KempstonE = 0x01, + ZxSpectrum3P = 0x02, + Tasman = 0x03, + DkTronics = 0x04, + Hilderbay = 0x05, + InesPrinterface = 0x06, + ZxLprintInterface3 = 0x07, + MultiPrint = 0x08, + OpusDiscovery = 0x09, + Standard8255 = 0x0A + } + + /// + /// Identified printer types + /// + public enum TzxPrinterType : byte + { + ZxPrinter = 0x00, + GenericPrinter = 0x01, + EpsonCompatible = 0x02 + } + + /// + /// Identifier ROM or RAM add-on types + /// + public enum TzxRomRamAddOnType : byte + { + SamRam = 0x00, + MultifaceOne = 0x01, + Multiface128K = 0x02, + MultifaceP3 = 0x03, + MultiPrint = 0x04, + Mb02 = 0x05, + SoftRom = 0x06, + Ram1K = 0x07, + Ram16K = 0x08, + Ram48K = 0x09, + Mem8To16KUsed = 0x0A + } + + /// + /// Identified serial port types + /// + public enum TzxSerialPortType : byte + { + ZxInterface1 = 0x00, + ZxSpectrum128 = 0x01 + } + + /// + /// Identified sound device types + /// + public enum TzxSoundDeviceType : byte + { + ClassicAy = 0x00, + FullerBox = 0x01, + CurrahMicroSpeech = 0x02, + SpectDrum = 0x03, + MelodikAyAcbStereo = 0x04, + AyAbcStereo = 0x05, + RamMusinMachine = 0x06, + Covox = 0x07, + GeneralSound = 0x08, + IntecEdiB8001 = 0x09, + ZonXAy = 0x0A, + QuickSilvaAy = 0x0B, + JupiterAce = 0x0C + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs new file mode 100644 index 0000000000..8ebe4921e5 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs @@ -0,0 +1,28 @@ +using System; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class represents a TZX-related exception + /// + public class TzxException : Exception + { + /// + /// Initializes the exception with the specified message + /// + /// Exception message + public TzxException(string message) : base(message) + { + } + + /// + /// Initializes the exception with the specified message + /// and inner exception + /// + /// Exception message + /// Inner exception + public TzxException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs new file mode 100644 index 0000000000..e6901b4d7b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the header of the TZX file + /// + public class TzxHeader : TzxDataBlockBase + { + public static IReadOnlyList TzxSignature = + new ReadOnlyCollection(new byte[] { 0x5A, 0x58, 0x54, 0x61, 0x70, 0x65, 0x21 }); + public byte[] Signature { get; private set; } + public byte Eot { get; private set; } + public byte MajorVersion { get; private set; } + public byte MinorVersion { get; private set; } + + public TzxHeader(byte majorVersion = 1, byte minorVersion = 20) + { + Signature = TzxSignature.ToArray(); + Eot = 0x1A; + MajorVersion = majorVersion; + MinorVersion = minorVersion; + } + + /// + /// The ID of the block + /// + public override int BlockId => 0x00; + + /// + /// Reads the content of the block from the specified binary stream. + /// + /// Stream to read the block from + public override void ReadFrom(BinaryReader reader) + { + Signature = reader.ReadBytes(7); + Eot = reader.ReadByte(); + MajorVersion = reader.ReadByte(); + MinorVersion = reader.ReadByte(); + } + + /// + /// Writes the content of the block to the specified binary stream. + /// + /// Stream to write the block to + public override void WriteTo(BinaryWriter writer) + { + writer.Write(Signature); + writer.Write(Eot); + writer.Write(MajorVersion); + writer.Write(MinorVersion); + } + + #region Overrides of TzxDataBlockBase + + /// + /// Override this method to check the content of the block + /// + public override bool IsValid => Signature.SequenceEqual(TzxSignature) + && Eot == 0x1A + && MajorVersion == 1; + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs new file mode 100644 index 0000000000..55797b80df --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs @@ -0,0 +1,83 @@ +using System.IO; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class is responsible to "play" a TZX file. + /// + public class TzxPlayer : TzxReader, ISupportsTapeBlockPlayback + { + private TapeBlockSetPlayer _player; + + /// + /// Signs that the player completed playing back the file + /// + public bool Eof => _player.Eof; + + /// + /// Initializes the player from the specified reader + /// + /// BinaryReader instance to get TZX file data from + public TzxPlayer(BinaryReader reader) : base(reader) + { + } + + /// + /// Reads in the content of the TZX file so that it can be played + /// + /// True, if read was successful; otherwise, false + public override bool ReadContent() + { + var success = base.ReadContent(); + var blocks = DataBlocks.Where(b => b is ISupportsTapeBlockPlayback) + .Cast() + .ToList(); + _player = new TapeBlockSetPlayer(blocks); + return success; + } + + /// + /// Gets the currently playing block's index + /// + public int CurrentBlockIndex => _player.CurrentBlockIndex; + + /// + /// The current playable block + /// + public ISupportsTapeBlockPlayback CurrentBlock => _player.CurrentBlock; + + /// + /// The current playing phase + /// + public PlayPhase PlayPhase => _player.PlayPhase; + + /// + /// The tact count of the CPU when playing starts + /// + public long StartCycle => _player.StartCycle; + + /// + /// Initializes the player + /// + public void InitPlay(long startTact) + { + _player.InitPlay(startTact); + } + + /// + /// Gets the EAR bit value for the specified tact + /// + /// Tacts to retrieve the EAR bit + /// + /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block + /// + public bool GetEarBit(long currentTact) => _player.GetEarBit(currentTact); + + /// + /// Moves the current block index to the next playable block + /// + /// Tacts time to start the next block + public void NextBlock(long currentTact) => _player.NextBlock(currentTact); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs new file mode 100644 index 0000000000..ad7ea786ab --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This class reads a TZX file + /// + public class TzxReader + { + private readonly BinaryReader _reader; + + public static Dictionary DataBlockTypes = new Dictionary + { + {0x10, typeof(TzxStandardSpeedDataBlock)}, + {0x11, typeof(TzxTurboSpeedDataBlock)}, + {0x12, typeof(TzxPureToneDataBlock)}, + {0x13, typeof(TzxPulseSequenceDataBlock)}, + {0x14, typeof(TzxPureDataBlock)}, + {0x15, typeof(TzxDirectRecordingDataBlock)}, + {0x16, typeof(TzxC64RomTypeDataBlock)}, + {0x17, typeof(TzxC64TurboTapeDataBlock)}, + {0x18, typeof(TzxCswRecordingDataBlock)}, + {0x19, typeof(TzxGeneralizedDataBlock)}, + {0x20, typeof(TzxSilenceDataBlock)}, + {0x21, typeof(TzxGroupStartDataBlock)}, + {0x22, typeof(TzxGroupEndDataBlock)}, + {0x23, typeof(TzxJumpDataBlock)}, + {0x24, typeof(TzxLoopStartDataBlock)}, + {0x25, typeof(TzxLoopEndDataBlock)}, + {0x26, typeof(TzxCallSequenceDataBlock)}, + {0x27, typeof(TzxReturnFromSequenceDataBlock)}, + {0x28, typeof(TzxSelectDataBlock)}, + {0x2A, typeof(TzxStopTheTape48DataBlock)}, + {0x2B, typeof(TzxSetSignalLevelDataBlock)}, + {0x30, typeof(TzxTextDescriptionDataBlock)}, + {0x31, typeof(TzxMessageDataBlock)}, + {0x32, typeof(TzxArchiveInfoDataBlock)}, + {0x33, typeof(TzxHardwareInfoDataBlock)}, + {0x34, typeof(TzxEmulationInfoDataBlock)}, + {0x35, typeof(TzxCustomInfoDataBlock)}, + {0x40, typeof(TzxSnapshotBlock)}, + {0x5A, typeof(TzxGlueDataBlock)}, + }; + + /// + /// Data blocks of this TZX file + /// + public IList DataBlocks { get; } + + /// + /// Major version number of the file + /// + public byte MajorVersion { get; private set; } + + /// + /// Minor version number of the file + /// + public byte MinorVersion { get; private set; } + + /// + /// Initializes the player from the specified reader + /// + /// + public TzxReader(BinaryReader reader) + { + _reader = reader; + DataBlocks = new List(); + } + + /// + /// Reads in the content of the TZX file so that it can be played + /// + /// True, if read was successful; otherwise, false + public virtual bool ReadContent() + { + var header = new TzxHeader(); + try + { + header.ReadFrom(_reader); + if (!header.IsValid) + { + throw new TzxException("Invalid TZX header"); + } + MajorVersion = header.MajorVersion; + MinorVersion = header.MinorVersion; + + while (_reader.BaseStream.Position != _reader.BaseStream.Length) + { + var blockType = _reader.ReadByte(); + Type type; + if (!DataBlockTypes.TryGetValue(blockType, out type)) + { + throw new TzxException($"Unkonwn TZX block type: {blockType}"); + } + + try + { + var block = Activator.CreateInstance(type) as TzxDataBlockBase; + if (block.GetType() == typeof(TzxDeprecatedDataBlockBase)) + { + ((TzxDeprecatedDataBlockBase)block as TzxDeprecatedDataBlockBase).ReadThrough(_reader); + } + else + { + block?.ReadFrom(_reader); + } + DataBlocks.Add(block); + } + catch (Exception ex) + { + throw new TzxException($"Cannot read TZX data block {type}.", ex); + } + } + return true; + } + catch + { + // --- This exception is intentionally ignored + return false; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs new file mode 100644 index 0000000000..cbdfcf42e7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + + public class RomData + { + /// + /// ROM Contents + /// + public byte[] RomBytes { get; set; } + + /// + /// Useful ROM addresses that are needed during tape operations + /// + public ushort SaveBytesRoutineAddress { get; set; } + public ushort SaveBytesResumeAddress { get; set; } + public ushort LoadBytesRoutineAddress { get; set; } + public ushort LoadBytesResumeAddress { get; set; } + public ushort LoadBytesInvalidHeaderAddress { get; set; } + + public static RomData InitROM(MachineType machineType, byte[] rom) + { + RomData RD = new RomData(); + RD.RomBytes = new byte[rom.Length]; + RD.RomBytes = rom; + + switch (machineType) + { + case MachineType.ZXSpectrum48: + RD.SaveBytesRoutineAddress = 0x04C2; + RD.SaveBytesResumeAddress = 0x0000; + RD.LoadBytesRoutineAddress = 0x056C; + RD.LoadBytesResumeAddress = 0x05E2; + RD.LoadBytesInvalidHeaderAddress = 0x05B6; + break; + } + + return RD; + } + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs deleted file mode 100644 index 222a3bb257..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Tape.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents the tape device (or DATACORDER as AMSTRAD liked to call it) - /// - public class Tape - { - protected bool _micBitState; - - public SpectrumBase _machine { get; set; } - public Buzzer _buzzer { get; set; } - - - public virtual void Init(SpectrumBase machine) - { - _machine = machine; - _buzzer = machine.BuzzerDevice; - Reset(); - } - - public virtual void Reset() - { - _micBitState = true; - } - - - /// - /// the EAR bit read from tape - /// - /// - /// - public virtual bool GetEarBit(int cpuCycles) - { - return false; - } - - /// - /// Processes the mic bit change - /// - /// - public virtual void ProcessMicBit(bool micBit) - { - - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs index 956c5eb206..e223f4aeeb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs @@ -48,7 +48,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private void SyncAllByteArrayDomains() { - SyncByteArrayDomain("Main RAM", _machine.RAM); + //SyncByteArrayDomain("Main RAM", _machine.RAM); } private void SyncByteArrayDomain(string name, byte[] data) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs deleted file mode 100644 index 81dad0d813..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs +++ /dev/null @@ -1,10 +0,0 @@ -using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Components; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public partial class ZXSpectrum //: ISoundProvider - { - - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index e41c43b832..8de32cfa15 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -53,9 +53,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.SyncState(ser); ser.BeginSection("ZXSpectrum"); - byte[] ram = new byte[_machine.RAM.Length]; - _machine.RAM.CopyTo(ram, 0); - ser.Sync("RAM", ref ram, false); + //byte[] ram = new byte[_machine.RAM.Length]; + //_machine.RAM.CopyTo(ram, 0); + //ser.Sync("RAM", ref ram, false); //_vdp.SyncState(ser); //PSG.SyncState(ser); //ser.Sync("RAM", ref _ram, false); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs new file mode 100644 index 0000000000..52bce8d20f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum + { + public ushort Get16BitPC() + { + return Convert.ToUInt16(_cpu.PCh << 8 | _cpu.PCl); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 750c8475d6..a77c7a501c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -35,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { case MachineType.ZXSpectrum48: ControllerDefinition = ZXSpectrumControllerDefinition48; - Init(MachineType.ZXSpectrum48, Settings.BorderType, Settings.TapeLoadSpeed); + Init(MachineType.ZXSpectrum48, Settings.BorderType, Settings.TapeLoadSpeed, _file); break; default: throw new InvalidOperationException("Machine not yet emulated"); @@ -90,14 +90,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public Action SoftReset; private readonly Z80A _cpu; - private byte[] _systemRom; + //private byte[] _systemRom; private readonly TraceBuffer _tracer; public IController _controller; private SpectrumBase _machine; - - - + private byte[] _file; private byte[] GetFirmware(int length, params string[] names) { @@ -111,15 +109,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } - private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed) + private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, byte[] file) { // setup the emulated model based on the MachineType switch (machineType) { case MachineType.ZXSpectrum48: - _machine = new ZX48(this, _cpu); - _systemRom = GetFirmware(0x4000, "48ROM"); - _machine.FillMemory(_systemRom, 0); + _machine = new ZX48(this, _cpu, file); + var _systemRom = GetFirmware(0x4000, "48ROM"); + var romData = RomData.InitROM(machineType, _systemRom); + _machine.InitROM(romData); break; } } From 7287afc5da2fb12b8b1cf3db4909a92a434c4a04 Mon Sep 17 00:00:00 2001 From: Asnvior Date: Tue, 28 Nov 2017 19:28:22 +0000 Subject: [PATCH 003/339] More SynState work --- BizHawk.Client.Common/config/PathEntry.cs | 2 +- .../SinclairSpectrum/Hardware/Buzzer.cs | 94 +++++-- .../Hardware/DefaultTapeProvider.cs | 8 +- .../Hardware/Interfaces/IKeyboard.cs | 4 + .../Hardware/Interfaces/ITapeProvider.cs | 6 + .../SinclairSpectrum/Hardware/Tape.cs | 182 +++++++++++-- .../Hardware/TapeBlockSetPlayer.cs | 2 +- .../Hardware/TapeFilePlayer.cs | 4 + .../Machine/SpectrumBase.Input.cs | 31 ++- .../Machine/SpectrumBase.Memory.cs | 11 +- .../Machine/SpectrumBase.Port.cs | 71 +----- .../SinclairSpectrum/Machine/SpectrumBase.cs | 114 ++++++--- .../Machine/ZXSpectrum48K/ZX48.Keyboard.cs | 45 +++- .../Machine/ZXSpectrum48K/ZX48.cs | 35 ++- .../Computers/SinclairSpectrum/RomData.cs | 62 ++++- .../ZXSpectrum.IMemoryDomains.cs | 25 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 10 +- .../SinclairSpectrum/ZXSpectrum.IStatable.cs | 14 +- .../SinclairSpectrum/ZXSpectrum.Util.cs | 240 +++++++++++++++++- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- 20 files changed, 753 insertions(+), 209 deletions(-) diff --git a/BizHawk.Client.Common/config/PathEntry.cs b/BizHawk.Client.Common/config/PathEntry.cs index 75f5fd0bc6..22fcad0a76 100644 --- a/BizHawk.Client.Common/config/PathEntry.cs +++ b/BizHawk.Client.Common/config/PathEntry.cs @@ -290,7 +290,7 @@ namespace BizHawk.Client.Common new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, - new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Base", Path = Path.Combine(".", "C64"), Ordinal = 0 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Base", Path = Path.Combine(".", "ZXSpectrum"), Ordinal = 0 }, new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "ROM", Path = ".", Ordinal = 1 }, new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs index 6a34619dcd..ace668868f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs @@ -1,8 +1,10 @@  +using BizHawk.Common; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components; using System; using System.Collections.Generic; +using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -19,20 +21,49 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Supplied values are right for 48K spectrum /// These will deviate for 128k and up (as there are more T-States per frame) /// - public int SampleRate = 44100; //35000; - public int SamplesPerFrame = 882; //699; - public int TStatesPerSample = 79; //100; + //public int SampleRate = 44100; //35000; + //public int SamplesPerFrame = 882; //699; + //public int TStatesPerSample = 79; //100; + + /// + /// Sample Rate + /// This usually has to be 44100 for ISoundProvider + /// + public int SampleRate + { + get { return _sampleRate; } + set { _sampleRate = value; } + } + + /// + /// Number of samples in one frame + /// + public int SamplesPerFrame + { + get { return _samplesPerFrame; } + set { _samplesPerFrame = value; } + } - public BlipBuffer BlipL { get; set; } - public BlipBuffer BlipR { get; set; } + /// + /// Number of TStates in each sample + /// + public int TStatesPerSample + { + get { return _tStatesPerSample; } + set { _tStatesPerSample = value; } + } private SpectrumBase _machine; + /// + /// State fields + /// private long _frameStart; private bool _tapeMode; private int _tStatesPerFrame; - - public SpeexResampler resampler { get; set; } + private int _sampleRate; + private int _samplesPerFrame; + private int _tStatesPerSample; /// /// Pulses collected during the last frame @@ -42,7 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The last pulse /// - public bool LastPulse { get; private set; } + public bool LastPulse { get; set; } /// /// The last T-State (cpu cycle) that the last pulse was received @@ -59,9 +90,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Initialises the buzzer /// - public void Init() + public void Init(int sampleRate, int tStatesPerFrame) { - _tStatesPerFrame = _machine.UlaFrameCycleCount; + _sampleRate = sampleRate; + _tStatesPerFrame = tStatesPerFrame; + + // get divisors + var divs = from a in Enumerable.Range(2, _tStatesPerFrame / 2) + where _tStatesPerFrame % a == 0 + select a; + + // get the highest int value under 120 (this will be TStatesPerSample) + _tStatesPerSample = divs.Where(a => a < 100).Last(); + + // get _samplesPerFrame + _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + Pulses = new List(1000); } @@ -206,16 +250,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void GetSamplesAsync(short[] samples) { - throw new NotSupportedException("Async is not available"); - short[] stereoBuffer = new short[soundBuffer.Length * 2]; - int index = 0; - for (int i = 0; i < soundBufferContains; i++) - { - stereoBuffer[index++] = soundBuffer[i]; - stereoBuffer[index++] = soundBuffer[i]; - } - - samples = stereoBuffer; + throw new NotSupportedException("Async is not available"); } public void DiscardSamples() @@ -241,5 +276,24 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + + public void SyncState(Serializer ser) + { + ser.BeginSection("Buzzer"); + ser.Sync("_frameStart", ref _frameStart); + ser.Sync("_tapeMode", ref _tapeMode); + ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); + ser.Sync("_sampleRate", ref _sampleRate); + ser.Sync("_samplesPerFrame", ref _samplesPerFrame); + ser.Sync("_tStatesPerSample", ref _tStatesPerSample); + + ser.Sync("soundBuffer", ref soundBuffer, false); + ser.Sync("soundBufferContains", ref soundBufferContains); + ser.EndSection(); + } + + } + + } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs index f9bbf30846..2ee8ef6f63 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs @@ -25,7 +25,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public string SaveFileFolder { get; } - + public DefaultTapeProvider(byte[] file, string saveFolder = null) { @@ -40,14 +40,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The component provider should be able to reset itself /// /// - /* - public override void Reset() + + public void Reset() { _dataBlockCount = 0; _suggestedName = null; _fullFileName = null; } - */ + /// /// Tha tape set to load the content from diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs index f053d044aa..fe4f188ca6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs @@ -1,5 +1,7 @@  +using BizHawk.Common; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// @@ -61,5 +63,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// byte GetByteFromKeyMatrix(string key); + + void SyncState(Serializer ser); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs index d82bc3ee64..91a4457a31 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs @@ -43,5 +43,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// TZX blocks are written. /// void FinalizeTapeFile(); + + /// + /// Provider can reset itself + /// + void Reset(); + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs index f493c7bda9..203fef91b2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs @@ -1,4 +1,5 @@ -using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Common; +using BizHawk.Emulation.Cores.Components.Z80A; using System; using System.Collections.Generic; using System.Linq; @@ -78,14 +79,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public const int DATA_BUFFER_LENGTH = 0x10000; /// - /// Gets the TZX tape content provider + /// Gets the tape content provider /// - public ITapeContentProvider ContentProvider { get; } - - /// - /// Gets the tape Save provider - /// - public ISaveToTapeProvider SaveToTapeProvider { get; } + public ITapeProvider TapeProvider { get; } /// /// The TapeFilePlayer that can playback tape content @@ -98,6 +94,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public TapeOperationMode CurrentMode => _currentMode; + private bool _fastLoad = false; + + public virtual void Init(SpectrumBase machine) { _machine = machine; @@ -106,15 +105,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Reset(); } - public Tape(ITapeContentProvider contentProvider, ISaveToTapeProvider saveToTapeProvider) + public Tape(ITapeProvider tapeProvider) { - ContentProvider = contentProvider; - SaveToTapeProvider = saveToTapeProvider; + TapeProvider = tapeProvider; } public virtual void Reset() { - ContentProvider?.Reset(); + TapeProvider?.Reset(); _tapePlayer = null; _currentMode = TapeOperationMode.Passive; _savePhase = SavePhase.None; @@ -125,17 +123,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { SetTapeMode(); if (CurrentMode == TapeOperationMode.Load - //&& HostVm.ExecuteCycleOptions.FastTapeMode + && _fastLoad && TapeFilePlayer != null && TapeFilePlayer.PlayPhase != PlayPhase.Completed - && _machine.Spectrum.Get16BitPC() == _machine.RomData.LoadBytesRoutineAddress) + && _cpu.RegPC == 1529) //_machine.RomData.LoadBytesRoutineAddress) { - /* + if (FastLoadFromTzx()) { - FastLoadCompleted?.Invoke(this, EventArgs.Empty); + //FastLoadCompleted?.Invoke(this, EventArgs.Empty); } - */ + } } @@ -148,27 +146,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (_currentMode) { case TapeOperationMode.Passive: - if (_machine.Spectrum.Get16BitPC() == _machine.RomData.LoadBytesRoutineAddress) + if (_cpu.RegPC == 1523) // _machine.RomData.LoadBytesRoutineAddress) //1529 { EnterLoadMode(); } - else if (_machine.Spectrum.Get16BitPC() == _machine.RomData.SaveBytesRoutineAddress) + else if (_cpu.RegPC == 2416) // _machine.RomData.SaveBytesRoutineAddress) { EnterSaveMode(); } - var res = _machine.Spectrum.Get16BitPC(); + var res = _cpu.RegPC; + var res2 = _machine.Spectrum.RegPC; return; case TapeOperationMode.Save: - if (_machine.Spectrum.Get16BitPC() == ERROR_ROM_ADDRESS + if (_cpu.RegPC == ERROR_ROM_ADDRESS || (int)(_cpu.TotalExecutedCycles - _lastMicBitActivityCycle) > SAVE_STOP_SILENCE) { LeaveSaveMode(); } return; case TapeOperationMode.Load: - if ((_tapePlayer?.Eof ?? false) || _machine.Spectrum.Get16BitPC() == ERROR_ROM_ADDRESS) + if ((_tapePlayer?.Eof ?? false) || _cpu.RegPC == ERROR_ROM_ADDRESS) { LeaveLoadMode(); } @@ -188,7 +187,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _pilotPulseCount = 0; _prevDataPulse = MicPulseType.None; _dataBlockCount = 0; - SaveToTapeProvider?.CreateTapeFile(); + TapeProvider?.CreateTapeFile(); } /// @@ -197,7 +196,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private void LeaveSaveMode() { _currentMode = TapeOperationMode.Passive; - SaveToTapeProvider?.FinalizeTapeFile(); + TapeProvider?.FinalizeTapeFile(); } /// @@ -207,7 +206,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _currentMode = TapeOperationMode.Load; - var contentReader = ContentProvider?.GetTapeContent(); + var contentReader = TapeProvider?.GetTapeContent(); if (contentReader == null) return; // --- Play the content @@ -224,10 +223,116 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _currentMode = TapeOperationMode.Passive; _tapePlayer = null; - ContentProvider?.Reset(); + TapeProvider?.Reset(); _buzzer.SetTapeMode(false); } + /// + /// Loads the next TZX player block instantly without emulation + /// EAR bit processing + /// + /// True, if fast load is operative + private bool FastLoadFromTzx() + { + var c = _machine.Spectrum; + + var blockType = TapeFilePlayer.CurrentBlock.GetType(); + bool canFlash = TapeFilePlayer.CurrentBlock is ITapeData; + + // --- Check, if we can load the current block in a fast way + if (!(TapeFilePlayer.CurrentBlock is ITapeData) + || TapeFilePlayer.PlayPhase == PlayPhase.Completed) + { + // --- We cannot play this block + return false; + } + + var currentData = TapeFilePlayer.CurrentBlock as ITapeData; + + var regs = _cpu.Regs; + + //regs.AF = regs._AF_; + //c.Set16BitAF(c.Get16BitAF_()); + _cpu.A = _cpu.A_s; + _cpu.F = _cpu.F_s; + + // --- Check if the operation is LOAD or VERIFY + var isVerify = (c.RegAF & 0xFF01) == 0xFF00; + + // --- At this point IX contains the address to load the data, + // --- DE shows the #of bytes to load. A contains 0x00 for header, + // --- 0xFF for data block + var data = currentData.Data; + if (data[0] != regs[_cpu.A]) + { + // --- This block has a different type we're expecting + regs[_cpu.A] = (byte)(regs[_cpu.A] ^ regs[_cpu.L]); + regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.Z; + regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.C; + c.RegPC = _machine.RomData.LoadBytesInvalidHeaderAddress; + + // --- Get the next block + TapeFilePlayer.NextBlock(_cpu.TotalExecutedCycles); + return true; + } + + // --- It is time to load the block + var curIndex = 1; + //var memory = _machine.me MemoryDevice; + regs[_cpu.H] = regs[_cpu.A]; + while (c.RegDE > 0) + { + var de16 = c.RegDE; + var ix16 = c.RegIX; + if (curIndex > data.Length - 1) + { + // --- No more data to read + //break; + } + + regs[_cpu.L] = data[curIndex]; + if (isVerify && regs[_cpu.L] != _machine.ReadBus(c.RegIX)) + { + // --- Verify failed + regs[_cpu.A] = (byte)(_machine.ReadBus(c.RegIX) ^ regs[_cpu.L]); + regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.Z; + regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.C; + c.RegPC = _machine.RomData.LoadBytesInvalidHeaderAddress; + return true; + } + + // --- Store the loaded data byte + _machine.WriteBus(c.RegIX, (byte)regs[_cpu.L]); + regs[_cpu.H] ^= regs[_cpu.L]; + curIndex++; + //regs.IX++; + //c.Set16BitIX((ushort)((int)c.Get16BitIX() + 1)); + c.RegIX++; + //regs.DE--; + //c.Set16BitDE((ushort)((int)c.Get16BitDE() - 1)); + //_cpu.Regs[_cpu.E]--; + c.RegDE--; + var te = c.RegDE; + } + + // --- Check the parity byte at the end of the data stream + if (curIndex > data.Length - 1 || regs[_cpu.H] != data[curIndex]) + { + // --- Carry is reset to sign an error + regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.C; + } + else + { + // --- Carry is set to sign success + regs[_cpu.F] |= (byte)ZXSpectrum.FlagsSetMask.C; + } + c.RegPC = _machine.RomData.LoadBytesResumeAddress; + + // --- Get the next block + TapeFilePlayer.NextBlock(_cpu.TotalExecutedCycles); + return true; + } + /// /// the EAR bit read from tape @@ -398,14 +503,35 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum sb.Append((char)_dataBuffer[i]); } var name = sb.ToString().TrimEnd(); - SaveToTapeProvider?.SetName(name); + TapeProvider?.SetName(name); } - SaveToTapeProvider?.SaveTapeBlock(dataBlock); + TapeProvider?.SaveTapeBlock(dataBlock); } break; } _savePhase = nextPhase; } + + public void SyncState(Serializer ser) + { + ser.BeginSection("TapeDevice"); + ser.Sync("_micBitState", ref _micBitState); + ser.Sync("_lastMicBitActivityCycle", ref _lastMicBitActivityCycle); + ser.Sync("_pilotPulseCount", ref _pilotPulseCount); + ser.Sync("_bitOffset", ref _bitOffset); + ser.Sync("_dataByte", ref _dataByte); + ser.Sync("_dataLength", ref _dataLength); + ser.Sync("_dataBlockCount", ref _dataBlockCount); + ser.Sync("_dataBuffer", ref _dataBuffer, false); + ser.SyncEnum("_currentMode", ref _currentMode); + ser.SyncEnum("_savePhase", ref _savePhase); + ser.SyncEnum("_prevDataPulse", ref _prevDataPulse); + /* + private TapeFilePlayer _tapePlayer; + */ + + ser.EndSection(); + } } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs index 372a752bf1..c08b488acf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs @@ -36,7 +36,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The cycle count of the CPU when playing starts /// public long StartCycle { get; private set; } - + public TapeBlockSetPlayer(List dataBlocks) { DataBlocks = dataBlocks; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs index 933899a6f5..25c75df0f8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs @@ -5,6 +5,10 @@ using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// This class recognizes .TZX and .TAP files, and playes back + /// the content accordingly. + /// public class TapeFilePlayer : ISupportsTapeBlockPlayback { private readonly BinaryReader _reader; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index a9eedbc44d..01ac5ab51a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -6,27 +6,42 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public abstract partial class SpectrumBase { - private readonly bool[] _keyboardPressed = new bool[64]; - int _pollIndex; - private bool _restorePressed; + string Play = "Play Tape"; + string Stop = "Stop Tape"; + string RTZ = "RTZ Tape"; + string Record = "Record Tape"; - public void PollInput() { Spectrum.InputCallbacks.Call(); - // scan keyboard - _pollIndex = 0; - for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++) { string key = KeyboardDevice.KeyboardMatrix[i]; bool prevState = KeyboardDevice.GetKeyStatus(key); bool currState = Spectrum._controller.IsPressed(key); - //if (currState != prevState) + if (currState != prevState) KeyboardDevice.SetKeyStatus(key, currState); } + + // Tape control + if (Spectrum._controller.IsPressed(Play)) + { + + } + if (Spectrum._controller.IsPressed(Stop)) + { + + } + if (Spectrum._controller.IsPressed(RTZ)) + { + + } + if (Spectrum._controller.IsPressed(Record)) + { + + } } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index a466c965d7..d993b15f32 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -144,10 +144,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual byte FetchScreenMemory(ushort addr) { - var value = RAM0[(addr & 0x3FFF)];// + 0x4000]; + //var value = RAM0[(addr & 0x3FFF)];// + 0x4000]; + var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); return value; } + /// + /// Helper function to refresh memory array (probably not the best way to do things) + /// + public virtual void ReInitMemory() + { + throw new NotImplementedException("Must be overriden"); + } + /// /// Returns the memory contention value for the specified T-State (cycle) /// The ZX Spectrum memory access is contended when the ULA is accessing the lower 16k of RAM diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 6cefe2f954..a8b715a214 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -56,58 +56,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // read keyboard input if (high != 0) result = KeyboardDevice.GetLineStatus((byte)high); - - var ear = TapeDevice.GetEarBit(CurrentFrameCycle); + + var ear = TapeDevice.GetEarBit(CPU.TotalExecutedCycles); if (!ear) { result = (byte)(result & Convert.ToInt32("10111111", 2)); } - - /* - - bool tapeIsPlaying = false; - int tapeBit = 0; - - if (tapeIsPlaying) - { - if (tapeBit == 0) - { - // reset is EAR ON - result = (byte)(result & ~(TAPE_BIT)); - } - else - { - // set is EAR OFF - result |= TAPE_BIT; - } - } - else - { - if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) - { - result = (byte)(result & ~(TAPE_BIT)); - } - else - { - result |= TAPE_BIT; - } - } - /* - // read EAR pulse from tape device - //todo - - bool earBit = false; - - if (earBit) - tapeBit = Convert.ToInt32("11111111", 2); - else - tapeBit = Convert.ToInt32("10111111", 2); - - //var earBit = _tapeDevice.GetEarBit(_cpu.Tacts); - - if (!earBit) - result = (byte)(result & tapeBit); - */ } else { @@ -152,25 +106,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Border - LSB 3 bits hold the border colour BorderColour = value & BORDER_BIT; - /* - // Buzzer - var beepVal = (int)value & (EAR_BIT); - - if (((int)value & MIC_BIT) == 0) - beepVal = (int)value & (MIC_BIT); - - //var micval = (int)value & (MIC_BIT); + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); - // if tape is not playing - BuzzerDevice.ProcessPulseValue(false, (beepVal) != 0); - */ - - // tape - //_tapeDevice.ProcessMicBit((data & 0x08) != 0); - - BuzzerDevice.ProcessPulseValue(false, (value & 0x10) != 0); - TapeDevice.ProcessMicBit((value & 0x08) != 0); + // Tape + TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 53fe905d01..6183843006 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -1,4 +1,5 @@ -using BizHawk.Emulation.Common; +using BizHawk.Common; +using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components.Z80A; using System; @@ -105,14 +106,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var lastCycle = CurrentFrameCycle; RenderScreen(LastRenderedULACycle + 1, lastCycle); LastRenderedULACycle = lastCycle; - - // test - if (CPU.IFF1) - { - //Random rnd = new Random(); - //ushort rU = (ushort)rnd.Next(0x4000, 0x8000); - //PokeMemory(rU, (byte)rnd.Next(7)); - } + } // we have reached the end of a frame @@ -137,30 +131,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RenderScreen(0, OverFlow); } - - /// - /// Executes one cycle of the emulated machine - /// - public virtual void ExecuteCycle() - { - // check for interrupt - CheckForInterrupt(CurrentFrameCycle); - - // run a single CPU instruction - CPU.ExecuteOne(); - - // run a rendering cycle according to the current CPU cycle count - var lastCycle = CurrentFrameCycle; - RenderScreen(LastRenderedULACycle + 1, lastCycle); - - // has the frame completed? - FrameCompleted = CurrentFrameCycle >= UlaFrameCycleCount; - - if (CurrentFrameCycle > 50000) - { - - } - } /// /// Hard reset of the emulated machine @@ -168,8 +138,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public virtual void HardReset() { ResetBorder(); - ResetInterrupt(); - + ResetInterrupt(); } /// @@ -180,5 +149,80 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ResetBorder(); ResetInterrupt(); } + + public void SyncState(Serializer ser) + { + ser.BeginSection("ZXMachine"); + ser.Sync("FrameCompleted", ref FrameCompleted); + ser.Sync("OverFlow", ref OverFlow); + ser.Sync("FrameCount", ref FrameCount); + ser.Sync("_frameCycles", ref _frameCycles); + ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick); + ser.Sync("LastULAOutByte", ref LastULAOutByte); + ser.Sync("_flashPhase", ref _flashPhase); + ser.Sync("_frameBuffer", ref _frameBuffer, false); + ser.Sync("_flashOffColors", ref _flashOffColors, false); + ser.Sync("_flashOnColors", ref _flashOnColors, false); + ser.Sync("InterruptCycle", ref InterruptCycle); + ser.Sync("InterruptRaised", ref InterruptRaised); + ser.Sync("InterruptRevoked", ref InterruptRevoked); + ser.Sync("UlaFrameCycleCount", ref UlaFrameCycleCount); + ser.Sync("FirstScreenPixelCycle", ref FirstScreenPixelCycle); + ser.Sync("FirstDisplayPixelCycle", ref FirstDisplayPixelCycle); + ser.Sync("FirstPixelCycleInLine", ref FirstPixelCycleInLine); + ser.Sync("AttributeDataPrefetchTime", ref AttributeDataPrefetchTime); + ser.Sync("PixelDataPrefetchTime", ref PixelDataPrefetchTime); + ser.Sync("ScreenLineTime", ref ScreenLineTime); + ser.Sync("NonVisibleBorderRightTime", ref NonVisibleBorderRightTime); + ser.Sync("BorderRightTime", ref BorderRightTime); + ser.Sync("DisplayLineTime", ref DisplayLineTime); + ser.Sync("BorderLeftTime", ref BorderLeftTime); + ser.Sync("HorizontalBlankingTime", ref HorizontalBlankingTime); + ser.Sync("ScreenWidth", ref ScreenWidth); + ser.Sync("BorderRightPixels", ref BorderRightPixels); + ser.Sync("BorderLeftPixels", ref BorderLeftPixels); + ser.Sync("FirstDisplayLine", ref FirstDisplayLine); + ser.Sync("ScreenLines", ref ScreenLines); + ser.Sync("NonVisibleBorderBottomLines", ref NonVisibleBorderBottomLines); + ser.Sync("BorderBottomLines", ref BorderBottomLines); + ser.Sync("BorderTopLines", ref BorderTopLines); + ser.Sync("NonVisibleBorderTopLines", ref NonVisibleBorderTopLines); + ser.Sync("VerticalSyncLines", ref VerticalSyncLines); + ser.Sync("FlashToggleFrames", ref FlashToggleFrames); + ser.Sync("DisplayLines", ref DisplayLines); + ser.Sync("DisplayWidth", ref DisplayWidth); + ser.Sync("_pixelByte1", ref _pixelByte1); + ser.Sync("_pixelByte2", ref _pixelByte2); + ser.Sync("_attrByte1", ref _attrByte1); + ser.Sync("_attrByte2", ref _attrByte2); + ser.Sync("_xPos", ref _xPos); + ser.Sync("_yPos", ref _yPos); + ser.Sync("DisplayWidth", ref DisplayWidth); + ser.Sync("DisplayWidth", ref DisplayWidth); + ser.Sync("DisplayWidth", ref DisplayWidth); + ser.Sync("DisplayWidth", ref DisplayWidth); + ser.Sync("_borderColour", ref _borderColour); + ser.Sync("ROM0", ref ROM0, false); + ser.Sync("ROM1", ref ROM1, false); + ser.Sync("ROM2", ref ROM2, false); + ser.Sync("ROM3", ref ROM3, false); + ser.Sync("RAM0", ref RAM0, false); + ser.Sync("RAM1", ref RAM1, false); + ser.Sync("RAM2", ref RAM2, false); + ser.Sync("RAM3", ref RAM3, false); + ser.Sync("RAM4", ref RAM4, false); + ser.Sync("RAM5", ref RAM5, false); + ser.Sync("RAM6", ref RAM6, false); + ser.Sync("RAM7", ref RAM7, false); + + RomData.SyncState(ser); + KeyboardDevice.SyncState(ser); + BuzzerDevice.SyncState(ser); + TapeDevice.SyncState(ser); + + ser.EndSection(); + + ReInitMemory(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs index ada3db6803..501dd6186b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs @@ -1,4 +1,5 @@ -using System; +using BizHawk.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,10 +13,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class Keyboard48 : IKeyboard { public SpectrumBase _machine { get; set; } - public string[] KeyboardMatrix { get; set; } - private readonly byte[] LineStatus; + private byte[] LineStatus; public bool Issue2 { get; set; } + private string[] _keyboardMatrix; + public string[] KeyboardMatrix + { + get { return _keyboardMatrix; } + set { _keyboardMatrix = value; } + } public Keyboard48(SpectrumBase machine) { @@ -51,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var lineMask = 1 << keyByte % 5; LineStatus[lineIndex] = isPressed ? (byte)(LineStatus[lineIndex] | lineMask) - : (byte)(LineStatus[lineIndex] & ~lineMask); + : (byte)(LineStatus[lineIndex] & ~lineMask); } public bool GetKeyStatus(string key) @@ -64,18 +70,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public byte GetLineStatus(byte lines) { - byte status = 0; - lines = (byte)~lines; - var lineIndex = 0; - while (lines > 0) + lock(this) { - if ((lines & 0x01) != 0) - status |= LineStatus[lineIndex]; - lineIndex++; - lines >>= 1; + byte status = 0; + lines = (byte)~lines; + var lineIndex = 0; + while (lines > 0) + { + if ((lines & 0x01) != 0) + status |= LineStatus[lineIndex]; + lineIndex++; + lines >>= 1; + } + var result = (byte)~status; + + return result; } - var result = (byte)~status; - return result; } public byte ReadKeyboardByte(ushort addr) @@ -88,5 +98,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int index = Array.IndexOf(KeyboardMatrix, key); return (byte)index; } + + public void SyncState(Serializer ser) + { + ser.BeginSection("Keyboard"); + ser.Sync("LineStatus", ref LineStatus, false); + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 32bc6bf1b3..ffbc38c13c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -22,10 +22,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CPU = cpu; // init addressable memory from ROM and RAM banks + /* Memory.Add(0, ROM0); Memory.Add(1, RAM0); Memory.Add(2, RAM1); Memory.Add(3, RAM2); + */ + ReInitMemory(); //RAM = new byte[0x4000 + 0xC000]; @@ -35,13 +38,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ResetULACycle(); BuzzerDevice = new Buzzer(this); - BuzzerDevice.Init(); + BuzzerDevice.Init(44100, UlaFrameCycleCount); KeyboardDevice = new Keyboard48(this); TapeProvider = new DefaultTapeProvider(file); - TapeDevice = new Tape(null, null); + TapeDevice = new Tape(TapeProvider); TapeDevice.Init(this); } @@ -147,6 +150,34 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = RAM0; + else + Memory.Add(1, RAM0); + + if (Memory.ContainsKey(2)) + Memory[2] = RAM1; + else + Memory.Add(2, RAM1); + + if (Memory.ContainsKey(3)) + Memory[3] = RAM2; + else + Memory.Add(3, RAM2); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM3; + else + Memory.Add(4, RAM3); + } + #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs index cbdfcf42e7..97dffe206b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs @@ -1,4 +1,5 @@ -using System; +using BizHawk.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,16 +13,48 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// ROM Contents /// - public byte[] RomBytes { get; set; } - + public byte[] RomBytes + { + get { return _romBytes; } + set { _romBytes = value; } + } + /// /// Useful ROM addresses that are needed during tape operations /// - public ushort SaveBytesRoutineAddress { get; set; } - public ushort SaveBytesResumeAddress { get; set; } - public ushort LoadBytesRoutineAddress { get; set; } - public ushort LoadBytesResumeAddress { get; set; } - public ushort LoadBytesInvalidHeaderAddress { get; set; } + public ushort SaveBytesRoutineAddress + { + get { return _saveBytesRoutineAddress; } + set { _saveBytesRoutineAddress = value; } + } + public ushort LoadBytesRoutineAddress + { + get { return _loadBytesRoutineAddress; } + set { _loadBytesRoutineAddress = value; } + } + public ushort SaveBytesResumeAddress + { + get { return _saveBytesResumeAddress; } + set { _saveBytesResumeAddress = value; } + } + public ushort LoadBytesResumeAddress + { + get { return _loadBytesResumeAddress; } + set { _loadBytesResumeAddress = value; } + } + public ushort LoadBytesInvalidHeaderAddress + { + get { return _loadBytesInvalidHeaderAddress; } + set { _loadBytesInvalidHeaderAddress = value; } + } + + private byte[] _romBytes; + private ushort _saveBytesRoutineAddress; + private ushort _loadBytesRoutineAddress; + private ushort _saveBytesResumeAddress; + private ushort _loadBytesResumeAddress; + private ushort _loadBytesInvalidHeaderAddress; + public static RomData InitROM(MachineType machineType, byte[] rom) { @@ -34,7 +67,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case MachineType.ZXSpectrum48: RD.SaveBytesRoutineAddress = 0x04C2; RD.SaveBytesResumeAddress = 0x0000; - RD.LoadBytesRoutineAddress = 0x056C; + RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C; RD.LoadBytesResumeAddress = 0x05E2; RD.LoadBytesInvalidHeaderAddress = 0x05B6; break; @@ -43,5 +76,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return RD; } + public void SyncState(Serializer ser) + { + ser.BeginSection("RomData"); + ser.Sync("RomBytes", ref _romBytes, false); + ser.Sync("_saveBytesRoutineAddress", ref _saveBytesRoutineAddress); + ser.Sync("_loadBytesRoutineAddress", ref _loadBytesRoutineAddress); + ser.Sync("_saveBytesResumeAddress", ref _saveBytesResumeAddress); + ser.Sync("_loadBytesResumeAddress", ref _loadBytesResumeAddress); + ser.Sync("_loadBytesInvalidHeaderAddress", ref _loadBytesInvalidHeaderAddress); + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs index e223f4aeeb..85083f3fcc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs @@ -23,7 +23,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { throw new ArgumentOutOfRangeException(); } - return _cpu.ReadMemory((ushort)addr); + return _machine.ReadBus((ushort)addr); }, (addr, value) => { @@ -32,7 +32,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum throw new ArgumentOutOfRangeException(); } - _cpu.WriteMemory((ushort)addr, value); + _machine.WriteBus((ushort)addr, value); }, 1) }; @@ -47,13 +47,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } private void SyncAllByteArrayDomains() - { - //SyncByteArrayDomain("Main RAM", _machine.RAM); + { + + SyncByteArrayDomain("ROM0", _machine.ROM0); + SyncByteArrayDomain("ROM1", _machine.ROM1); + SyncByteArrayDomain("ROM2", _machine.ROM2); + SyncByteArrayDomain("ROM3", _machine.ROM3); + SyncByteArrayDomain("RAM0", _machine.RAM0); + SyncByteArrayDomain("RAM1", _machine.RAM1); + SyncByteArrayDomain("RAM2", _machine.RAM2); + SyncByteArrayDomain("RAM3", _machine.RAM3); + SyncByteArrayDomain("RAM4", _machine.RAM4); + SyncByteArrayDomain("RAM5", _machine.RAM5); + SyncByteArrayDomain("RAM6", _machine.RAM6); + SyncByteArrayDomain("RAM7", _machine.RAM7); + } private void SyncByteArrayDomain(string name, byte[] data) { - if (_memoryDomainsInit) + + if (_memoryDomainsInit || _byteArrayDomains.ContainsKey(name)) { var m = _byteArrayDomains[name]; m.Data = data; @@ -63,6 +77,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var m = new MemoryDomainByteArray(name, MemoryDomain.Endian.Little, data, true, 1); _byteArrayDomains.Add(name, m); } + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 32db755068..33dddae230 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -48,10 +48,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DefaultValue(BorderType.Full)] public BorderType BorderType { get; set; } - [DisplayName("Tape Load Speed")] - [Description("Select how fast the spectrum loads the game from tape")] - [DefaultValue(TapeLoadSpeed.Accurate)] - public TapeLoadSpeed TapeLoadSpeed { get; set; } + public ZXSpectrumSettings Clone() { @@ -66,6 +63,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class ZXSpectrumSyncSettings { + [DisplayName("Tape Load Speed")] + [Description("Select how fast the spectrum loads the game from tape")] + [DefaultValue(TapeLoadSpeed.Accurate)] + public TapeLoadSpeed TapeLoadSpeed { get; set; } + public ZXSpectrumSyncSettings Clone() { return (ZXSpectrumSyncSettings)MemberwiseClone(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index 8de32cfa15..a9c407ca14 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -53,23 +53,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.SyncState(ser); ser.BeginSection("ZXSpectrum"); - //byte[] ram = new byte[_machine.RAM.Length]; - //_machine.RAM.CopyTo(ram, 0); - //ser.Sync("RAM", ref ram, false); - //_vdp.SyncState(ser); - //PSG.SyncState(ser); - //ser.Sync("RAM", ref _ram, false); + _cpu.SyncState(ser); + _machine.SyncState(ser); ser.Sync("Frame", ref _machine.FrameCount); ser.Sync("LagCount", ref _lagCount); ser.Sync("IsLag", ref _isLag); + //ser.Sync("_memoryDomainsInit", ref _memoryDomainsInit); + ser.EndSection(); if (ser.IsReader) { - //SyncAllByteArrayDomains(); + SyncAllByteArrayDomains(); } } - - private byte[] _stateBuffer; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs index 52bce8d20f..893edbae8a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -8,9 +8,245 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZXSpectrum { - public ushort Get16BitPC() + /* + * CPU Helper Methods + */ + + public ushort RegPC { - return Convert.ToUInt16(_cpu.PCh << 8 | _cpu.PCl); + get { return (ushort)((_cpu.Regs[0] << 8 | _cpu.Regs[1])); } + set + { + _cpu.Regs[1] = (ushort)(value & 0xFF); + _cpu.Regs[0] = (ushort)((value >> 8) & 0xFF); + } + } + + public ushort RegIX + { + get { return (ushort)((_cpu.Regs[15] << 8 | _cpu.Regs[16] )); } + set + { + _cpu.Regs[16] = (ushort)(value & 0xFF); + _cpu.Regs[15] = (ushort)((value >> 8) & 0xFF); + } + } + + public ushort RegDE + { + get { return (ushort)((_cpu.Regs[8] << 8 | _cpu.Regs[9] )); } + set + { + _cpu.Regs[9] = (ushort)(value & 0xFF); + _cpu.Regs[8] = (ushort)((value >> 8) & 0xFF); + } + } + + public ushort RegAF + { + get { return (ushort)((_cpu.Regs[4] << 8 | _cpu.Regs[5])); } + set + { + _cpu.Regs[5] = (ushort)(value & 0xFF); + _cpu.Regs[4] = (ushort)((value >> 8) & 0xFF); + } + } + + + /// + /// Gets the IX word value + /// + /// + public ushort Get16BitIX() + { + return Convert.ToUInt16(_cpu.Regs[_cpu.Ixh] | _cpu.Regs[_cpu.Ixl] << 8); + } + + /// + /// Set the IX word value + /// + /// + /// + public void Set16BitIX(ushort IX) + { + _cpu.Regs[_cpu.Ixh] = (ushort)(IX & 0xFF); + _cpu.Regs[_cpu.Ixl] = (ushort)((IX >> 8) & 0xff); + } + + /// + /// Gets the AF word value + /// + /// + public ushort Get16BitAF() + { + return Convert.ToUInt16(_cpu.Regs[_cpu.A] | _cpu.Regs[_cpu.F] << 8); + } + + /// + /// Set the AF word value + /// + /// + /// + public void Set16BitAF(ushort AF) + { + _cpu.Regs[_cpu.A] = (ushort)(AF & 0xFF); + _cpu.Regs[_cpu.F] = (ushort)((AF >> 8) & 0xff); + } + + /// + /// Gets the AF shadow word value + /// + /// + public ushort Get16BitAF_() + { + return Convert.ToUInt16(_cpu.Regs[_cpu.A_s] | _cpu.Regs[_cpu.F_s] << 8); + } + + /// + /// Set the AF shadow word value + /// + /// + /// + public void Set16BitAF_(ushort AF_) + { + _cpu.Regs[_cpu.A_s] = (ushort)(AF_ & 0xFF); + _cpu.Regs[_cpu.F_s] = (ushort)((AF_ >> 8) & 0xff); + } + + /// + /// Gets the DE word value + /// + /// + public ushort Get16BitDE() + { + return Convert.ToUInt16(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8); + } + + /// + /// Set the DE word value + /// + /// + /// + public void Set16BitDE(ushort DE) + { + _cpu.Regs[_cpu.D] = (ushort)(DE & 0xFF); + _cpu.Regs[_cpu.E] = (ushort)((DE >> 8) & 0xff); + } + + + /// + /// Z80 Status Indicator Flag Reset masks + /// + /// + [Flags] + public enum FlagsResetMask : byte + { + /// Sign Flag + S = 0x7F, + + /// Zero Flag + Z = 0xBF, + + /// This flag is not used. + R5 = 0xDF, + + /// Half Carry Flag + H = 0xEF, + + /// This flag is not used. + R3 = 0xF7, + + /// Parity/Overflow Flag + PV = 0xFB, + + /// Add/Subtract Flag + N = 0xFD, + + /// Carry Flag + C = 0xFE, + } + + /// + /// Z80 Status Indicator Flag Set masks + /// + /// + [Flags] + public enum FlagsSetMask : byte + { + /// Sign Flag + /// + /// The Sign Flag (S) stores the state of the most-significant bit of + /// the Accumulator (bit 7). When the Z80 CPU performs arithmetic + /// operations on signed numbers, the binary twos complement notation + /// is used to represent and process numeric information. + /// + S = 0x80, + + /// + /// Zero Flag + /// + /// + /// The Zero Flag is set (1) or cleared (0) if the result generated by + /// the execution of certain instructions is 0. For 8-bit arithmetic and + /// logical operations, the Z flag is set to a 1 if the resulting byte in + /// the Accumulator is 0. If the byte is not 0, the Z flag is reset to 0. + /// + Z = 0x40, + + /// This flag is not used. + R5 = 0x20, + + /// Half Carry Flag + /// + /// The Half Carry Flag (H) is set (1) or cleared (0) depending on the + /// Carry and Borrow status between bits 3 and 4 of an 8-bit arithmetic + /// operation. This flag is used by the Decimal Adjust Accumulator (DAA) + /// instruction to correct the result of a packed BCD add or subtract operation. + /// + H = 0x10, + + /// This flag is not used. + R3 = 0x08, + + /// Parity/Overflow Flag + /// + /// The Parity/Overflow (P/V) Flag is set to a specific state depending on + /// the operation being performed. For arithmetic operations, this flag + /// indicates an overflow condition when the result in the Accumulator is + /// greater than the maximum possible number (+127) or is less than the + /// minimum possible number (–128). This overflow condition is determined by + /// examining the sign bits of the operands. + /// + PV = 0x04, + + /// Add/Subtract Flag + /// + /// The Add/Subtract Flag (N) is used by the Decimal Adjust Accumulator + /// instruction (DAA) to distinguish between the ADD and SUB instructions. + /// For ADD instructions, N is cleared to 0. For SUB instructions, N is set to 1. + /// + N = 0x02, + + /// Carry Flag + /// + /// The Carry Flag (C) is set or cleared depending on the operation being performed. + /// + C = 0x01, + + /// + /// Combination of S, Z, and PV + /// + SZPV = S | Z | PV, + + /// + /// Combination of N, and H + /// + NH = N | H, + + /// + /// Combination of R3, and R5 + /// + R3R5 = R3 | R5 } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index a77c7a501c..cb94079bad 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -35,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { case MachineType.ZXSpectrum48: ControllerDefinition = ZXSpectrumControllerDefinition48; - Init(MachineType.ZXSpectrum48, Settings.BorderType, Settings.TapeLoadSpeed, _file); + Init(MachineType.ZXSpectrum48, Settings.BorderType, SyncSettings.TapeLoadSpeed, file); break; default: throw new InvalidOperationException("Machine not yet emulated"); From dba8e1f0496dce38cee526455c92c97601238fe9 Mon Sep 17 00:00:00 2001 From: Asnvior Date: Tue, 28 Nov 2017 20:09:00 +0000 Subject: [PATCH 004/339] Added readme --- .../BizHawk.Emulation.Cores.csproj | 1 + .../Computers/SinclairSpectrum/readme.md | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 2e488bd9e9..1026644554 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1373,6 +1373,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md new file mode 100644 index 0000000000..eacc65afa1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -0,0 +1,29 @@ +## ZXHawk + +At this moment this is still *very* experimental and needs a lot more work. + +### Implemented and sorta working +* IEmulator +* ZX Spectrum 48k model +* ULA video output (implementing IVideoProvider) +* ULA Mode 1 VBLANK interrupt generation +* Beeper/Buzzer output (implementing ISoundProvider) +* Keyboard input (implementing IInputPollable) +* Tape device that will load spectrum games in realtime (*.tzx and *.tap) +* IStatable (although this is not currently working/implemented properly during tape load operations) + +### Some progress +* ISettable - There are some Settings and SyncSettings instantiated, although they are not really used and I haven't yet figured out how to wire these up to the front-end yet + +### Not working +* Interrupt Mode 2 (Z80A) - usually invokes a soft reboot when the game raises one (can be seen in 'Chaos - Battle of the Wizards' after initial game setup when the game board tries to load) +* IMemoryDomains - I started looking at this but didn't really know what I was doing yet +* IDebuggable +* Default keyboard keymappings (you have to configure yourself in the core controller settings) +* Joystick support (I still need to implement a Kemptston joystick and interface) +* Manual tape device control (at the moment the tape device detects when the spectrum goes into 'loadbytes' mode and auto-plays the tape. This is not ideal and manual control should be implemented so the user can start/stop manually, return to zero etc..) +* Only standard spectrum tape blocks currently work. Any fancy SpeedLock encoded (and similar) blocks do not + +### Known bugs +* The 'return' keyboard key is acting the same as Space/Break when doing a BASIC RUN or LOAD "" command. The upshot of this is that upon boot, when you go to load the attached spectrum cassette (you have to type: "J", then "SYMSHIFT + P", then "SYMSHIFT + P", then RETURN) it more often than not interrupts the load routine. You then have to try again but hitting the RETURN key at the end of the sequence for as small a time as possible. Rinse and repeat until the load process starts. Clearly NOT ideal. +* Audible 'popping' from the emulated buzzer after a load state operation From d09e73b592a5a44b546f1b301e00f2f529fbb29c Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 29 Nov 2017 16:28:08 -0500 Subject: [PATCH 005/339] z80: implement data bus -needed for ZXspectrum mode 2 interrupts -use with FetchDB function --- .../CPUs/Z80A/Interrupts.cs | 18 +++++++++--------- .../CPUs/Z80A/Operations.cs | 11 +++++++++++ BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs | 2 +- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 12 ++++++++++++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 8dc1cd24ce..88e0a264c2 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -89,22 +89,22 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, + FTCH_DB, + TR, Z, DB, + TR, W, I, + IDLE, DEC16, SPl, SPh, WR, SPl, SPh, PCh, IDLE, DEC16, SPl, SPh, - WR, SPl, SPh, PCl, - IDLE, - ASGN, PCl, 0, - TR, PCh, I, - IDLE, + WR, SPl, SPh, PCl, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, + RD, PCl, Z, W, + INC16, Z, W, + IDLE, + RD, PCh, Z, W, IDLE, - RD, W, PCl, PCh, IDLE, - TR16, PCl, PCh, Z, W, OP }; } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs index cfaf517ec7..81f9e1b93a 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs @@ -8,31 +8,37 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public void Read_Func(ushort dest, ushort src_l, ushort src_h) { Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); + Regs[DB] = Regs[dest]; } public void I_Read_Func(ushort dest, ushort src_l, ushort src_h, ushort inc) { Regs[dest] = ReadMemory((ushort)((Regs[src_l] | (Regs[src_h] << 8)) + inc)); + Regs[DB] = Regs[dest]; } public void Write_Func(ushort dest_l, ushort dest_h, ushort src) { + Regs[DB] = Regs[src]; WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]); } public void I_Write_Func(ushort dest_l, ushort dest_h, ushort inc, ushort src) { + Regs[DB] = Regs[src]; WriteMemory((ushort)((Regs[dest_l] | (Regs[dest_h] << 8)) + inc), (byte)Regs[src]); } public void OUT_Func(ushort dest, ushort src) { + Regs[DB] = Regs[src]; WriteHardware(Regs[dest], (byte)(Regs[src])); } public void IN_Func(ushort dest, ushort src) { Regs[dest] = ReadHardware(Regs[src]); + Regs[DB] = Regs[dest]; } public void TR_Func(ushort dest, ushort src) @@ -738,5 +744,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Flag3 = (Regs[A] & 0x08) != 0; } } + + public void FTCH_DB_Func() + { + Regs[DB] = FetchDB(); + } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index 628243c19e..14e447bc65 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -6,7 +6,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public partial class Z80A { // registers - // note these are not constants. When shadows are used, they will be changed accordingly public ushort PCl = 0; public ushort PCh = 1; public ushort SPl = 2; @@ -40,6 +39,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public ushort E_s = 29; public ushort H_s = 30; public ushort L_s = 31; + public ushort DB = 32; public ushort[] Regs = new ushort[36]; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 46abee9809..623a2aa126 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -74,6 +74,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort SET_FL_IR = 59; public const ushort I_BIT = 60; public const ushort HL_BIT = 61; + public const ushort FTCH_DB = 62; public byte temp_R; @@ -106,6 +107,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public Func ReadHardware; public Action WriteHardware; + // Data BUs + // Interrupting Devices are responsible for putting a value onto the data bus + // for as long as the interrupt is valid + public Func FetchDB; + //this only calls when the first byte of an instruction is fetched. public Action OnExecFetch; @@ -339,6 +345,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case HALT: halted = true; + // NOTE: Check how halt state effects the DB + Regs[DB] = 0xFF; + if (EI_pending > 0) { EI_pending--; @@ -595,6 +604,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case SET_FL_IR: SET_FL_IR_Func(cur_instr[instr_pntr++]); break; + case FTCH_DB: + FTCH_DB_Func(); + break; } totalExecutedCycles++; } From 7428e8e6737a452fcb0e52a1485d109bb2641f0d Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 29 Nov 2017 16:30:54 -0500 Subject: [PATCH 006/339] ZX Spectrum: Draft DB access --- .../Machine/SpectrumBase.Memory.cs | 354 +++++++++--------- 1 file changed, 182 insertions(+), 172 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index d993b15f32..5b371bbc7f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -1,172 +1,182 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// The abstract class that all emulated models will inherit from - /// * Memory * - /// - public abstract partial class SpectrumBase - { - /// - /// ROM Banks - /// - public byte[] ROM0 = new byte[0x4000]; - public byte[] ROM1 = new byte[0x4000]; - public byte[] ROM2 = new byte[0x4000]; - public byte[] ROM3 = new byte[0x4000]; - - /// - /// RAM Banks - /// - public byte[] RAM0 = new byte[0x4000]; // Bank 0 - public byte[] RAM1 = new byte[0x4000]; // Bank 1 - public byte[] RAM2 = new byte[0x4000]; // Bank 2 - public byte[] RAM3 = new byte[0x4000]; // Bank 3 - public byte[] RAM4 = new byte[0x4000]; // Bank 4 - public byte[] RAM5 = new byte[0x4000]; // Bank 5 - public byte[] RAM6 = new byte[0x4000]; // Bank 6 - public byte[] RAM7 = new byte[0x4000]; // Bank 7 - - /// - /// Represents the addressable memory space of the spectrum - /// All banks for the emulated system should be added during initialisation - /// - public Dictionary Memory = new Dictionary(); - - /// - /// Simulates reading from the bus - /// Paging should be handled here - /// - /// - /// - public virtual byte ReadBus(ushort addr) - { - throw new NotImplementedException("Must be overriden"); - } - - /// - /// Simulates writing to the bus - /// Paging should be handled here - /// - /// - /// - public virtual void WriteBus(ushort addr, byte value) - { - throw new NotImplementedException("Must be overriden"); - } - - /// - /// Reads a byte of data from a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public virtual byte ReadMemory(ushort addr) - { - throw new NotImplementedException("Must be overriden"); - } - /* - /// - /// Reads a byte of data from a specified memory address - /// (with no memory contention) - /// - /// - /// - public virtual byte PeekMemory(ushort addr) - { - var data = ReadBus(addr); - return data; - } - */ - - /// - /// Writes a byte of data to a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public virtual void WriteMemory(ushort addr, byte value) - { - throw new NotImplementedException("Must be overriden"); - } - - /* - /// - /// Writes a byte of data to a specified memory address - /// (without contention) - /// - /// - /// - public virtual void PokeMemory(ushort addr, byte value) - { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - - WriteBus(addr, value); - } - */ - - /// - /// Fills memory from buffer - /// - /// - /// - public virtual void FillMemory(byte[] buffer, ushort startAddress) - { - //buffer?.CopyTo(RAM, startAddress); - } - - /// - /// Sets up the ROM - /// - /// - /// - public virtual void InitROM(RomData romData) - { - RomData = romData; - // for 16/48k machines only ROM0 is used (no paging) - RomData.RomBytes?.CopyTo(ROM0, 0); - } - - /// - /// ULA reads the memory at the specified address - /// (No memory contention) - /// - /// - /// - public virtual byte FetchScreenMemory(ushort addr) - { - //var value = RAM0[(addr & 0x3FFF)];// + 0x4000]; - var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); - return value; - } - - /// - /// Helper function to refresh memory array (probably not the best way to do things) - /// - public virtual void ReInitMemory() - { - throw new NotImplementedException("Must be overriden"); - } - - /// - /// Returns the memory contention value for the specified T-State (cycle) - /// The ZX Spectrum memory access is contended when the ULA is accessing the lower 16k of RAM - /// - /// - /// - public virtual byte GetContentionValue(int cycle) - { - var val = _renderingCycleTable[cycle % UlaFrameCycleCount].ContentionDelay; - return val; - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Memory * + /// + public abstract partial class SpectrumBase + { + /// + /// ROM Banks + /// + public byte[] ROM0 = new byte[0x4000]; + public byte[] ROM1 = new byte[0x4000]; + public byte[] ROM2 = new byte[0x4000]; + public byte[] ROM3 = new byte[0x4000]; + + /// + /// RAM Banks + /// + public byte[] RAM0 = new byte[0x4000]; // Bank 0 + public byte[] RAM1 = new byte[0x4000]; // Bank 1 + public byte[] RAM2 = new byte[0x4000]; // Bank 2 + public byte[] RAM3 = new byte[0x4000]; // Bank 3 + public byte[] RAM4 = new byte[0x4000]; // Bank 4 + public byte[] RAM5 = new byte[0x4000]; // Bank 5 + public byte[] RAM6 = new byte[0x4000]; // Bank 6 + public byte[] RAM7 = new byte[0x4000]; // Bank 7 + + /// + /// Represents the addressable memory space of the spectrum + /// All banks for the emulated system should be added during initialisation + /// + public Dictionary Memory = new Dictionary(); + + /// + /// Simulates reading from the bus + /// Paging should be handled here + /// + /// + /// + public virtual byte ReadBus(ushort addr) + { + throw new NotImplementedException("Must be overriden"); + } + + /// + /// Pushes a value onto the data bus that should be valid as long as the interrupt is true + /// + /// + /// + public virtual byte PushBus() + { + throw new NotImplementedException("Must be overriden"); + } + + /// + /// Simulates writing to the bus + /// Paging should be handled here + /// + /// + /// + public virtual void WriteBus(ushort addr, byte value) + { + throw new NotImplementedException("Must be overriden"); + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public virtual byte ReadMemory(ushort addr) + { + throw new NotImplementedException("Must be overriden"); + } + /* + /// + /// Reads a byte of data from a specified memory address + /// (with no memory contention) + /// + /// + /// + public virtual byte PeekMemory(ushort addr) + { + var data = ReadBus(addr); + return data; + } + */ + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public virtual void WriteMemory(ushort addr, byte value) + { + throw new NotImplementedException("Must be overriden"); + } + + /* + /// + /// Writes a byte of data to a specified memory address + /// (without contention) + /// + /// + /// + public virtual void PokeMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + + WriteBus(addr, value); + } + */ + + /// + /// Fills memory from buffer + /// + /// + /// + public virtual void FillMemory(byte[] buffer, ushort startAddress) + { + //buffer?.CopyTo(RAM, startAddress); + } + + /// + /// Sets up the ROM + /// + /// + /// + public virtual void InitROM(RomData romData) + { + RomData = romData; + // for 16/48k machines only ROM0 is used (no paging) + RomData.RomBytes?.CopyTo(ROM0, 0); + } + + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// + /// + /// + public virtual byte FetchScreenMemory(ushort addr) + { + //var value = RAM0[(addr & 0x3FFF)];// + 0x4000]; + var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); + return value; + } + + /// + /// Helper function to refresh memory array (probably not the best way to do things) + /// + public virtual void ReInitMemory() + { + throw new NotImplementedException("Must be overriden"); + } + + /// + /// Returns the memory contention value for the specified T-State (cycle) + /// The ZX Spectrum memory access is contended when the ULA is accessing the lower 16k of RAM + /// + /// + /// + public virtual byte GetContentionValue(int cycle) + { + var val = _renderingCycleTable[cycle % UlaFrameCycleCount].ContentionDelay; + return val; + } + } +} From 07b9e1243c5738cbe1cfcd9f3385c5d09707ed1f Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 29 Nov 2017 16:31:53 -0500 Subject: [PATCH 007/339] ZX Spectrum draft DB access --- .../Machine/ZXSpectrum48K/ZX48.cs | 382 +++++++++--------- 1 file changed, 196 insertions(+), 186 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index ffbc38c13c..57e5e14c1a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -1,186 +1,196 @@ -using BizHawk.Emulation.Cores.Components.Z80A; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public class ZX48 : SpectrumBase - { - #region Construction - - /// - /// Main constructor - /// - /// - /// - public ZX48(ZXSpectrum spectrum, Z80A cpu, byte[] file) - { - Spectrum = spectrum; - CPU = cpu; - - // init addressable memory from ROM and RAM banks - /* - Memory.Add(0, ROM0); - Memory.Add(1, RAM0); - Memory.Add(2, RAM1); - Memory.Add(3, RAM2); - */ - ReInitMemory(); - - //RAM = new byte[0x4000 + 0xC000]; - - InitScreenConfig(); - InitScreen(); - - ResetULACycle(); - - BuzzerDevice = new Buzzer(this); - BuzzerDevice.Init(44100, UlaFrameCycleCount); - - KeyboardDevice = new Keyboard48(this); - - TapeProvider = new DefaultTapeProvider(file); - - TapeDevice = new Tape(TapeProvider); - TapeDevice.Init(this); - } - - #endregion - - #region MemoryMapping - - /* 48K Spectrum has NO memory paging - * - * 0xffff +--------+ - | Bank 2 | - | | - | | - | | - 0xc000 +--------+ - | Bank 1 | - | | - | | - | | - 0x8000 +--------+ - | Bank 0 | - | | - | | - | screen | - 0x4000 +--------+ - | ROM 0 | - | | - | | - | | - 0x0000 +--------+ - */ - - /// - /// Simulates reading from the bus (no contention) - /// Paging should be handled here - /// - /// - /// - public override byte ReadBus(ushort addr) - { - int divisor = addr / 0x4000; - // paging logic goes here - - var bank = Memory[divisor]; - var index = addr % 0x4000; - return bank[index]; - } - - /// - /// Simulates writing to the bus (no contention) - /// Paging should be handled here - /// - /// - /// - public override void WriteBus(ushort addr, byte value) - { - int divisor = addr / 0x4000; - // paging logic goes here - - var bank = Memory[divisor]; - var index = addr % 0x4000; - bank[index] = value; - } - - /// - /// Reads a byte of data from a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public override byte ReadMemory(ushort addr) - { - var data = ReadBus(addr); - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - return data; - } - - /// - /// Writes a byte of data to a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public override void WriteMemory(ushort addr, byte value) - { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - else if (addr < 0xC000) - { - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - - WriteBus(addr, value); - } - - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = RAM0; - else - Memory.Add(1, RAM0); - - if (Memory.ContainsKey(2)) - Memory[2] = RAM1; - else - Memory.Add(2, RAM1); - - if (Memory.ContainsKey(3)) - Memory[3] = RAM2; - else - Memory.Add(3, RAM2); - - if (Memory.ContainsKey(4)) - Memory[4] = RAM3; - else - Memory.Add(4, RAM3); - } - - - #endregion - - - } -} +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class ZX48 : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX48(ZXSpectrum spectrum, Z80A cpu, byte[] file) + { + Spectrum = spectrum; + CPU = cpu; + + // init addressable memory from ROM and RAM banks + /* + Memory.Add(0, ROM0); + Memory.Add(1, RAM0); + Memory.Add(2, RAM1); + Memory.Add(3, RAM2); + */ + ReInitMemory(); + + //RAM = new byte[0x4000 + 0xC000]; + + InitScreenConfig(); + InitScreen(); + + ResetULACycle(); + + BuzzerDevice = new Buzzer(this); + BuzzerDevice.Init(44100, UlaFrameCycleCount); + + KeyboardDevice = new Keyboard48(this); + + TapeProvider = new DefaultTapeProvider(file); + + TapeDevice = new Tape(TapeProvider); + TapeDevice.Init(this); + } + + #endregion + + #region MemoryMapping + + /* 48K Spectrum has NO memory paging + * + * 0xffff +--------+ + | Bank 2 | + | | + | | + | | + 0xc000 +--------+ + | Bank 1 | + | | + | | + | | + 0x8000 +--------+ + | Bank 0 | + | | + | | + | screen | + 0x4000 +--------+ + | ROM 0 | + | | + | | + | | + 0x0000 +--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + return bank[index]; + } + + /// + /// Pushes a value onto the data bus that should be valid as long as the interrupt is true + /// + /// + /// + public override byte PushBus() + { + return 0xFF; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + bank[index] = value; + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = RAM0; + else + Memory.Add(1, RAM0); + + if (Memory.ContainsKey(2)) + Memory[2] = RAM1; + else + Memory.Add(2, RAM1); + + if (Memory.ContainsKey(3)) + Memory[3] = RAM2; + else + Memory.Add(3, RAM2); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM3; + else + Memory.Add(4, RAM3); + } + + + #endregion + + + } +} From 30061a35362df160a97290290e302c01283d64c4 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 29 Nov 2017 16:32:34 -0500 Subject: [PATCH 008/339] ZX Spectrum draft DB Access --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 271 +++++++++--------- 1 file changed, 137 insertions(+), 134 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index cb94079bad..98617170a5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -1,134 +1,137 @@ -using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Components; -using BizHawk.Emulation.Cores.Components.Z80A; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - [Core( - "ZXHawk", - "Asnivor", - isPorted: false, - isReleased: false)] - [ServiceNotApplicable(typeof(IDriveLight))] - public partial class ZXSpectrum : IDebuggable, IInputPollable, IStatable, IRegionable - { - [CoreConstructor("ZXSpectrum")] - public ZXSpectrum(CoreComm comm, byte[] file, object settings, object syncSettings) - { - PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); - PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); - - var ser = new BasicServiceProvider(this); - ServiceProvider = ser; - InputCallbacks = new InputCallbackSystem(); - - CoreComm = comm; - - _cpu = new Z80A(); - - _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; - - switch (Settings.MachineType) - { - case MachineType.ZXSpectrum48: - ControllerDefinition = ZXSpectrumControllerDefinition48; - Init(MachineType.ZXSpectrum48, Settings.BorderType, SyncSettings.TapeLoadSpeed, file); - break; - default: - throw new InvalidOperationException("Machine not yet emulated"); - } - - - - _cpu.MemoryCallbacks = MemoryCallbacks; - - HardReset = _machine.HardReset; - SoftReset = _machine.SoftReset; - - _cpu.FetchMemory = _machine.ReadMemory; - _cpu.ReadMemory = _machine.ReadMemory; - _cpu.WriteMemory = _machine.WriteMemory; - _cpu.ReadHardware = _machine.ReadPort; - _cpu.WriteHardware = _machine.WritePort; - - ser.Register(_tracer); - ser.Register(_cpu); - ser.Register(_machine); - ser.Register(_machine.BuzzerDevice); - - HardReset(); - - - - List romDis = new List(); - List disas = new List(); - for (int i = 0x00; i < 0x4000; i++) - { - DISA d = new DISA(); - ushort size; - d.Dis = _cpu.Disassemble((ushort)i, _machine.ReadMemory, out size); - d.Size = size; - disas.Add(d); - romDis.Add(d.Dis); - //i = i + size - 1; - //romDis.Add(s); - } - } - - public class DISA - { - public ushort Size { get; set; } - public string Dis { get; set; } - } - - //private int _cyclesPerFrame; - - public Action HardReset; - public Action SoftReset; - - private readonly Z80A _cpu; - //private byte[] _systemRom; - private readonly TraceBuffer _tracer; - public IController _controller; - private SpectrumBase _machine; - - private byte[] _file; - - private byte[] GetFirmware(int length, params string[] names) - { - var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("ZXSpectrum", n, false)).FirstOrDefault(b => b != null && b.Length == length); - if (result == null) - { - throw new MissingFirmwareException($"At least one of these firmwares is required: {string.Join(", ", names)}"); - } - - return result; - } - - - private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, byte[] file) - { - // setup the emulated model based on the MachineType - switch (machineType) - { - case MachineType.ZXSpectrum48: - _machine = new ZX48(this, _cpu, file); - var _systemRom = GetFirmware(0x4000, "48ROM"); - var romData = RomData.InitROM(machineType, _systemRom); - _machine.InitROM(romData); - break; - } - } - - #region IRegionable - - public DisplayType Region => DisplayType.PAL; - - #endregion - - - } -} +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components; +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + [Core( + "ZXHawk", + "Asnivor", + isPorted: false, + isReleased: false)] + [ServiceNotApplicable(typeof(IDriveLight))] + public partial class ZXSpectrum : IDebuggable, IInputPollable, IStatable, IRegionable + { + [CoreConstructor("ZXSpectrum")] + public ZXSpectrum(CoreComm comm, byte[] file, object settings, object syncSettings) + { + PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); + PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); + + var ser = new BasicServiceProvider(this); + ServiceProvider = ser; + InputCallbacks = new InputCallbackSystem(); + + CoreComm = comm; + + _cpu = new Z80A(); + + _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; + + switch (Settings.MachineType) + { + case MachineType.ZXSpectrum48: + ControllerDefinition = ZXSpectrumControllerDefinition48; + Init(MachineType.ZXSpectrum48, Settings.BorderType, SyncSettings.TapeLoadSpeed, file); + break; + default: + throw new InvalidOperationException("Machine not yet emulated"); + } + + + + _cpu.MemoryCallbacks = MemoryCallbacks; + + HardReset = _machine.HardReset; + SoftReset = _machine.SoftReset; + + _cpu.FetchMemory = _machine.ReadMemory; + _cpu.ReadMemory = _machine.ReadMemory; + _cpu.WriteMemory = _machine.WriteMemory; + _cpu.ReadHardware = _machine.ReadPort; + _cpu.WriteHardware = _machine.WritePort; + _cpu.FetchDB = _machine.PushBus; + + ser.Register(_tracer); + ser.Register(_cpu); + ser.Register(_machine); + ser.Register(_machine.BuzzerDevice); + + HardReset(); + + + + List romDis = new List(); + List disas = new List(); + for (int i = 0x00; i < 0x4000; i++) + { + DISA d = new DISA(); + ushort size; + //d.Dis = _cpu.Disassemble((ushort)i, _machine.ReadMemory, out size); + // d.Size = size; + // disas.Add(d); + //romDis.Add(d.Dis); + //i = i + size - 1; + //romDis.Add(s); + } + + SetupMemoryDomains(); + } + + public class DISA + { + public ushort Size { get; set; } + public string Dis { get; set; } + } + + //private int _cyclesPerFrame; + + public Action HardReset; + public Action SoftReset; + + private readonly Z80A _cpu; + //private byte[] _systemRom; + private readonly TraceBuffer _tracer; + public IController _controller; + private SpectrumBase _machine; + + private byte[] _file; + + private byte[] GetFirmware(int length, params string[] names) + { + var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("ZXSpectrum", n, false)).FirstOrDefault(b => b != null && b.Length == length); + if (result == null) + { + throw new MissingFirmwareException($"At least one of these firmwares is required: {string.Join(", ", names)}"); + } + + return result; + } + + + private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, byte[] file) + { + // setup the emulated model based on the MachineType + switch (machineType) + { + case MachineType.ZXSpectrum48: + _machine = new ZX48(this, _cpu, file); + var _systemRom = GetFirmware(0x4000, "48ROM"); + var romData = RomData.InitROM(machineType, _systemRom); + _machine.InitROM(romData); + break; + } + } + + #region IRegionable + + public DisplayType Region => DisplayType.PAL; + + #endregion + + + } +} From b38760caeb22fb859c5d17869148946aac7ca248 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 30 Nov 2017 09:41:30 +0000 Subject: [PATCH 009/339] Internal facility to use DiagROM --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 45 +++++++------------ 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 98617170a5..806b02ccc3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -3,6 +3,7 @@ using BizHawk.Emulation.Cores.Components; using BizHawk.Emulation.Cores.Components.Z80A; using System; using System.Collections.Generic; +using System.IO; using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum @@ -29,13 +30,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu = new Z80A(); - _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; - + _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; + + _file = file; + switch (Settings.MachineType) { case MachineType.ZXSpectrum48: ControllerDefinition = ZXSpectrumControllerDefinition48; - Init(MachineType.ZXSpectrum48, Settings.BorderType, SyncSettings.TapeLoadSpeed, file); + Init(MachineType.ZXSpectrum48, Settings.BorderType, SyncSettings.TapeLoadSpeed, _file); break; default: throw new InvalidOperationException("Machine not yet emulated"); @@ -62,46 +65,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum HardReset(); - - - List romDis = new List(); - List disas = new List(); - for (int i = 0x00; i < 0x4000; i++) - { - DISA d = new DISA(); - ushort size; - //d.Dis = _cpu.Disassemble((ushort)i, _machine.ReadMemory, out size); - // d.Size = size; - // disas.Add(d); - //romDis.Add(d.Dis); - //i = i + size - 1; - //romDis.Add(s); - } - SetupMemoryDomains(); } - - public class DISA - { - public ushort Size { get; set; } - public string Dis { get; set; } - } - - //private int _cyclesPerFrame; - + public Action HardReset; public Action SoftReset; private readonly Z80A _cpu; - //private byte[] _systemRom; private readonly TraceBuffer _tracer; public IController _controller; private SpectrumBase _machine; private byte[] _file; + + public bool DiagRom = false; + private byte[] GetFirmware(int length, params string[] names) { + if (DiagRom & File.Exists(Directory.GetCurrentDirectory() + @"\DiagROM.v28")) + { + var rom = File.ReadAllBytes(Directory.GetCurrentDirectory() + @"\DiagROM.v28"); + return rom; + } + var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("ZXSpectrum", n, false)).FirstOrDefault(b => b != null && b.Length == length); if (result == null) { From 64bb08cbb763509393fb4c9c7c0b190f60cd96c6 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 30 Nov 2017 12:08:36 +0000 Subject: [PATCH 010/339] un-refactored input code and added some +keyboard combinations --- .../Hardware/Interfaces/IKeyboard.cs | 18 +- .../Machine/SpectrumBase.Input.cs | 28 ++- .../Machine/SpectrumBase.Port.cs | 82 ++++++- .../Machine/ZXSpectrum48K/ZX48.Keyboard.cs | 212 +++++++++++++++++- .../ZXSpectrum.Controllers.cs | 69 +----- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- 6 files changed, 326 insertions(+), 85 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs index fe4f188ca6..0240fd52f5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs @@ -20,14 +20,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string[] KeyboardMatrix { get; set; } /// - /// For 16/48k models + /// Other keyboard keys that are not in the matrix + /// (usually keys derived from key combos) /// - bool Issue2 { get; set; } + string[] NonMatrixKeys { get; set; } /// - /// The current keyboard line status + /// Represents the spectrum key state /// - //byte[] LineStatus { get; set; } + int[] KeyLine { get; set; } + + /// + /// There are some slight differences in how PortIN and PortOUT functions + /// between Issue2 and Issue3 keyboards (16k/48k spectrum only) + /// It is possible that some very old games require Issue2 emulation + /// + bool IsIssue2Keyboard { get; set; } /// /// Sets the spectrum key status @@ -64,6 +72,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// byte GetByteFromKeyMatrix(string key); + + void SyncState(Serializer ser); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 01ac5ab51a..d9fb158b9f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -15,14 +15,29 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { Spectrum.InputCallbacks.Call(); - for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++) - { - string key = KeyboardDevice.KeyboardMatrix[i]; - bool prevState = KeyboardDevice.GetKeyStatus(key); - bool currState = Spectrum._controller.IsPressed(key); + lock (this) + { + // parse single keyboard matrix keys + for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++) + { + string key = KeyboardDevice.KeyboardMatrix[i]; + //bool prevState = KeyboardDevice.GetKeyStatus(key); + bool currState = Spectrum._controller.IsPressed(key); - if (currState != prevState) + //if (currState != prevState) KeyboardDevice.SetKeyStatus(key, currState); + } + + // non matrix keys + foreach (string k in KeyboardDevice.NonMatrixKeys) + { + if (!k.StartsWith("Key")) + continue; + + bool currState = Spectrum._controller.IsPressed(k); + + KeyboardDevice.SetKeyStatus(k, currState); + } } // Tape control @@ -45,3 +60,4 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } } + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index a8b715a214..a37b24d22d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -26,18 +26,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { CPU.TotalExecutedCycles += 4; - byte result = 0xFF; + int result = 0xFF; // get the high byte from Regs[6] ushort high = CPU.Regs[6]; // combine the low byte (passed in as port) and the high byte (maybe not needed) - ushort word = Convert.ToUInt16((port << 8 | high)); + //ushort word = Convert.ToUInt16((port << 8 | high)); + int word = (high << 8) + port; // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; - + bool lowBitReset = (port & 0x0001) == 0; + // Kempston Joystick //not implemented yet @@ -53,15 +54,80 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B */ + if ((word & 0x8000) == 0) + result &= KeyboardDevice.KeyLine[7]; + + if ((word & 0x4000) == 0) + result &= KeyboardDevice.KeyLine[6]; + + if ((word & 0x2000) == 0) + result &= KeyboardDevice.KeyLine[5]; + + if ((word & 0x1000) == 0) + result &= KeyboardDevice.KeyLine[4]; + + if ((word & 0x800) == 0) + result &= KeyboardDevice.KeyLine[3]; + + if ((word & 0x400) == 0) + result &= KeyboardDevice.KeyLine[2]; + + if ((word & 0x200) == 0) + result &= KeyboardDevice.KeyLine[1]; + + if ((word & 0x100) == 0) + result &= KeyboardDevice.KeyLine[0]; + + result = result & 0x1f; //mask out lower 4 bits + result = result | 0xa0; //set bit 5 & 7 to 1 + + if (TapeDevice.CurrentMode == TapeOperationMode.Load) + { + if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (KeyboardDevice.IsIssue2Keyboard) + { + if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + else + { + if ((LastULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + /* // read keyboard input if (high != 0) - result = KeyboardDevice.GetLineStatus((byte)high); + result &= KeyboardDevice.GetLineStatus((byte)high); var ear = TapeDevice.GetEarBit(CPU.TotalExecutedCycles); if (!ear) { result = (byte)(result & Convert.ToInt32("10111111", 2)); } + */ } else { @@ -75,7 +141,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // if unused port the floating memory bus should be returned (still todo) } - return result; + return (byte)(result & 0xff); } /// @@ -103,10 +169,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum | | | | E | M | Border | +-------------------------------+ */ - + // Border - LSB 3 bits hold the border colour BorderColour = value & BORDER_BIT; - + // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs index 501dd6186b..c20faa7fa4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs @@ -13,9 +13,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class Keyboard48 : IKeyboard { public SpectrumBase _machine { get; set; } - private byte[] LineStatus; - public bool Issue2 { get; set; } + private byte[] LineStatus; private string[] _keyboardMatrix; + private int[] _keyLine; + private bool _isIssue2Keyboard; + private string[] _nonMatrixKeys; + + public bool IsIssue2Keyboard + { + get { return _isIssue2Keyboard; } + set { _isIssue2Keyboard = value; } + } + + public int[] KeyLine + { + get { return _keyLine; } + set { _keyLine = value; } + } public string[] KeyboardMatrix { @@ -23,6 +37,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum set { _keyboardMatrix = value; } } + public string[] NonMatrixKeys + { + get { return _nonMatrixKeys; } + set { _nonMatrixKeys = value; } + } + public Keyboard48(SpectrumBase machine) { _machine = machine; @@ -44,23 +64,185 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // 0xbffe - 30 - 34 "Key Return", "Key L", "Key K", "Key J", "Key H", // 0x7ffe - 35 - 39 - "Key Space", "Key Sym Shift", "Key M", "Key N", "Key B" + "Key Space", "Key Symbol Shift", "Key M", "Key N", "Key B" }; + var nonMatrix = new List(); + foreach (var key in ZXSpectrum.ZXSpectrumControllerDefinition.BoolButtons) + { + if (!KeyboardMatrix.Any(s => s == key)) + nonMatrix.Add(key); + } + NonMatrixKeys = nonMatrix.ToArray(); + LineStatus = new byte[8]; + _keyLine = new int[] { 255, 255, 255, 255, 255, 255, 255, 255 }; + IsIssue2Keyboard = false; } - public void SetKeyStatus(string key, bool isPressed) + public void SetKeyStatus(string key, bool isPressed) { + /* byte keyByte = GetByteFromKeyMatrix(key); var lineIndex = keyByte / 5; var lineMask = 1 << keyByte % 5; LineStatus[lineIndex] = isPressed ? (byte)(LineStatus[lineIndex] | lineMask) : (byte)(LineStatus[lineIndex] & ~lineMask); + */ + + int k = GetByteFromKeyMatrix(key); + + if (isPressed) + { + switch (k) + { + // 0xfefe - 0 - 4 + case 0: _keyLine[0] = (_keyLine[0] & ~(0x01)); break; + case 1: _keyLine[0] = (_keyLine[0] & ~(0x02)); break; + case 2: _keyLine[0] = (_keyLine[0] & ~(0x04)); break; + case 3: _keyLine[0] = (_keyLine[0] & ~(0x08)); break; + case 4: _keyLine[0] = (_keyLine[0] & ~(0x10)); break; + // 0xfdfe - 5 - 9 + case 5: _keyLine[1] = (_keyLine[1] & ~(0x01)); break; + case 6: _keyLine[1] = (_keyLine[1] & ~(0x02)); break; + case 7: _keyLine[1] = (_keyLine[1] & ~(0x04)); break; + case 8: _keyLine[1] = (_keyLine[1] & ~(0x08)); break; + case 9: _keyLine[1] = (_keyLine[1] & ~(0x10)); break; + // 0xfbfe - 10 - 14 + case 10: _keyLine[2] = (_keyLine[2] & ~(0x01)); break; + case 11: _keyLine[2] = (_keyLine[2] & ~(0x02)); break; + case 12: _keyLine[2] = (_keyLine[2] & ~(0x04)); break; + case 13: _keyLine[2] = (_keyLine[2] & ~(0x08)); break; + case 14: _keyLine[2] = (_keyLine[2] & ~(0x10)); break; + // 0xf7fe - 15 - 19 + case 15: _keyLine[3] = (_keyLine[3] & ~(0x01)); break; + case 16: _keyLine[3] = (_keyLine[3] & ~(0x02)); break; + case 17: _keyLine[3] = (_keyLine[3] & ~(0x04)); break; + case 18: _keyLine[3] = (_keyLine[3] & ~(0x08)); break; + case 19: _keyLine[3] = (_keyLine[3] & ~(0x10)); break; + // 0xeffe - 20 - 24 + case 20: _keyLine[4] = (_keyLine[4] & ~(0x01)); break; + case 21: _keyLine[4] = (_keyLine[4] & ~(0x02)); break; + case 22: _keyLine[4] = (_keyLine[4] & ~(0x04)); break; + case 23: _keyLine[4] = (_keyLine[4] & ~(0x08)); break; + case 24: _keyLine[4] = (_keyLine[4] & ~(0x10)); break; + // 0xdffe - 25 - 29 + case 25: _keyLine[5] = (_keyLine[5] & ~(0x01)); break; + case 26: _keyLine[5] = (_keyLine[5] & ~(0x02)); break; + case 27: _keyLine[5] = (_keyLine[5] & ~(0x04)); break; + case 28: _keyLine[5] = (_keyLine[5] & ~(0x08)); break; + case 29: _keyLine[5] = (_keyLine[5] & ~(0x10)); break; + // 0xbffe - 30 - 34 + case 30: _keyLine[6] = (_keyLine[6] & ~(0x01)); break; + case 31: _keyLine[6] = (_keyLine[6] & ~(0x02)); break; + case 32: _keyLine[6] = (_keyLine[6] & ~(0x04)); break; + case 33: _keyLine[6] = (_keyLine[6] & ~(0x08)); break; + case 34: _keyLine[6] = (_keyLine[6] & ~(0x10)); break; + // 0x7ffe - 35 - 39 + case 35: _keyLine[7] = (_keyLine[7] & ~(0x01)); break; + case 36: _keyLine[7] = (_keyLine[7] & ~(0x02)); break; + case 37: _keyLine[7] = (_keyLine[7] & ~(0x04)); break; + case 38: _keyLine[7] = (_keyLine[7] & ~(0x08)); break; + case 39: _keyLine[7] = (_keyLine[7] & ~(0x10)); break; + } + } + else + { + switch (k) + { + // 0xfefe - 0 - 4 + case 0: _keyLine[0] = (_keyLine[0] | (0x01)); break; + case 1: _keyLine[0] = (_keyLine[0] | (0x02)); break; + case 2: _keyLine[0] = (_keyLine[0] | (0x04)); break; + case 3: _keyLine[0] = (_keyLine[0] | (0x08)); break; + case 4: _keyLine[0] = (_keyLine[0] | (0x10)); break; + // 0xfdfe - 5 - 9 + case 5: _keyLine[1] = (_keyLine[1] | (0x01)); break; + case 6: _keyLine[1] = (_keyLine[1] | (0x02)); break; + case 7: _keyLine[1] = (_keyLine[1] | (0x04)); break; + case 8: _keyLine[1] = (_keyLine[1] | (0x08)); break; + case 9: _keyLine[1] = (_keyLine[1] | (0x10)); break; + // 0xfbfe - 10 - 14 + case 10: _keyLine[2] = (_keyLine[2] | (0x01)); break; + case 11: _keyLine[2] = (_keyLine[2] | (0x02)); break; + case 12: _keyLine[2] = (_keyLine[2] | (0x04)); break; + case 13: _keyLine[2] = (_keyLine[2] | (0x08)); break; + case 14: _keyLine[2] = (_keyLine[2] | (0x10)); break; + // 0xf7fe - 15 - 19 + case 15: _keyLine[3] = (_keyLine[3] | (0x01)); break; + case 16: _keyLine[3] = (_keyLine[3] | (0x02)); break; + case 17: _keyLine[3] = (_keyLine[3] | (0x04)); break; + case 18: _keyLine[3] = (_keyLine[3] | (0x08)); break; + case 19: _keyLine[3] = (_keyLine[3] | (0x10)); break; + // 0xeffe - 20 - 24 + case 20: _keyLine[4] = (_keyLine[4] | (0x01)); break; + case 21: _keyLine[4] = (_keyLine[4] | (0x02)); break; + case 22: _keyLine[4] = (_keyLine[4] | (0x04)); break; + case 23: _keyLine[4] = (_keyLine[4] | (0x08)); break; + case 24: _keyLine[4] = (_keyLine[4] | (0x10)); break; + // 0xdffe - 25 - 29 + case 25: _keyLine[5] = (_keyLine[5] | (0x01)); break; + case 26: _keyLine[5] = (_keyLine[5] | (0x02)); break; + case 27: _keyLine[5] = (_keyLine[5] | (0x04)); break; + case 28: _keyLine[5] = (_keyLine[5] | (0x08)); break; + case 29: _keyLine[5] = (_keyLine[5] | (0x10)); break; + // 0xbffe - 30 - 34 + case 30: _keyLine[6] = (_keyLine[6] | (0x01)); break; + case 31: _keyLine[6] = (_keyLine[6] | (0x02)); break; + case 32: _keyLine[6] = (_keyLine[6] | (0x04)); break; + case 33: _keyLine[6] = (_keyLine[6] | (0x08)); break; + case 34: _keyLine[6] = (_keyLine[6] | (0x10)); break; + // 0x7ffe - 35 - 39 + case 35: _keyLine[7] = (_keyLine[7] | (0x01)); break; + case 36: _keyLine[7] = (_keyLine[7] | (0x02)); break; + case 37: _keyLine[7] = (_keyLine[7] | (0x04)); break; + case 38: _keyLine[7] = (_keyLine[7] | (0x08)); break; + case 39: _keyLine[7] = (_keyLine[7] | (0x10)); break; + } + } + + // Combination keys that are not in the keyboard matrix + // but are available on the Spectrum+, 128k +2 & +3 + // (GetByteFromKeyMatrix() should return 255) + // Processed after the matrix keys - only presses handled (unpressed get done above) + if (k == 255) + { + if (isPressed) + { + switch (key) + { + // Delete key (simulates Caps Shift + 0) + case "Key Delete": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x1); + break; + // Cursor left (simulates Caps Shift + 5) + case "Key Left Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[3] = _keyLine[3] & ~(0x10); + break; + // Cursor right (simulates Caps Shift + 8) + case "Key Right Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x04); + break; + // Cursor up (simulates Caps Shift + 7) + case "Key Up Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x08); + break; + // Cursor down (simulates Caps Shift + 6) + case "Key Down Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x10); + break; + } + } + } } - public bool GetKeyStatus(string key) + public bool GetKeyStatus(string key) { byte keyByte = GetByteFromKeyMatrix(key); var lineIndex = keyByte / 5; @@ -68,8 +250,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return (LineStatus[lineIndex] & lineMask) != 0; } - public byte GetLineStatus(byte lines) + public byte GetLineStatus(byte lines) { + /* lock(this) { byte status = 0; @@ -86,9 +269,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return result; } + */ + + switch (lines) + { + case 0xfe: return (byte)KeyLine[0]; + case 0xfd: return (byte)KeyLine[1]; + case 0xfb: return (byte)KeyLine[2]; + case 0xf7: return (byte)KeyLine[3]; + case 0xef: return (byte)KeyLine[4]; + case 0xdf: return (byte)KeyLine[5]; + case 0xbf: return (byte)KeyLine[6]; + case 0x7f: return (byte)KeyLine[7]; + default: return 0; + } } - public byte ReadKeyboardByte(ushort addr) + public byte ReadKeyboardByte(ushort addr) { return GetLineStatus((byte)(addr >> 8)); } @@ -103,6 +300,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { ser.BeginSection("Keyboard"); ser.Sync("LineStatus", ref LineStatus, false); + ser.Sync("_keyLine", ref _keyLine, false); ser.EndSection(); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index 6d06bc54c3..d0f40166ec 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -4,79 +4,30 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZXSpectrum - { + { /// - /// The standard 48K Spectrum keyboard + /// Controller mapping includes all keyboard keys from the following models: /// https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/ZXSpectrum48k.jpg/1200px-ZXSpectrum48k.jpg + /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg /// - private static readonly ControllerDefinition ZXSpectrumControllerDefinition48 = new ControllerDefinition + public static readonly ControllerDefinition ZXSpectrumControllerDefinition = new ControllerDefinition { - Name = "ZXSpectrum Controller 48K", + Name = "ZXSpectrum Controller", BoolButtons = { // Joystick interface (not yet emulated) - Could be Kempston/Cursor/Protek "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", // Keyboard - row 1 - "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", + /*"Key True Video", "Key Inv Video",*/ "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", // Keyboard - row 2 - "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", + "Key Delete", /*"Key Graph",*/ "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", // Keyboard - row 3 - "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", + /*"Key Extend Mode", "Key Edit",*/ "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", // Keyboard - row 4 - "Key Caps Shift", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Sym Shift", "Key Space", - // Tape functions - "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" - } - }; - - /// - /// The newer spectrum keyboard (models 48k+, 128k) - /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg - /// - private static readonly ControllerDefinition ZXSpectrumControllerDefinition128 = new ControllerDefinition - { - Name = "ZXSpectrum Controller 48KPlus", - BoolButtons = - { - // Joystick interface (not yet emulated) - Could be Kempston/Cursor/Protek - "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", - // Keyboard - row 1 - "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", - // Keyboard - row 2 - "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", - // Keyboard - row 3 - "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", - // Keyboard - row 4 - "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", + "Key Caps Shift", /*"Key Caps Lock",*/ "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", /*"Key Period",*/ // Keyboard - row 5 - "Key Symbol Shift", "Key Semi-Colon", "Key Inverted-Comma", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", "Key Symbol Shift", - // Tape functions - "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" - } - }; - - /// - /// The amstrad models - same as the previous keyboard layout but with built in ZX Interface 2 joystick ports - /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg - /// - private static readonly ControllerDefinition ZXSpectrumControllerDefinitionPlus = new ControllerDefinition - { - Name = "ZXSpectrum Controller 48KPlus", - BoolButtons = - { - // Joystick interface (not yet emulated) - "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", - // Keyboard - row 1 - "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", - // Keyboard - row 2 - "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", - // Keyboard - row 3 - "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", - // Keyboard - row 4 - "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", - // Keyboard - row 5 - "Key Symbol Shift", "Key Semi-Colon", "Key Inverted-Comma", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", "Key Symbol Shift", + "Key Symbol Shift", /*"Key Semi-Colon", "Key Quote",*/ "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", /*"Key Comma",*/ // Tape functions "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 806b02ccc3..1a56bdfd4d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (Settings.MachineType) { case MachineType.ZXSpectrum48: - ControllerDefinition = ZXSpectrumControllerDefinition48; + ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum48, Settings.BorderType, SyncSettings.TapeLoadSpeed, _file); break; default: From 0ac60123202f1f969e0890b7c7a40b26c62ebf7d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 1 Dec 2017 11:36:57 +0000 Subject: [PATCH 011/339] small refactor --- .../Hardware/Interfaces/IKeyboard.cs | 5 ++ .../Machine/SpectrumBase.Input.cs | 8 ++- .../Machine/SpectrumBase.Port.cs | 51 +++++++------- .../Machine/ZXSpectrum48K/ZX48.Keyboard.cs | 69 ++++++++++++------- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- .../Computers/SinclairSpectrum/readme.md | 2 +- 6 files changed, 83 insertions(+), 54 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs index 0240fd52f5..83b8782226 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs @@ -30,6 +30,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// int[] KeyLine { get; set; } + /// + /// Resets the line status + /// + void ResetLineStatus(); + /// /// There are some slight differences in how PortIN and PortOUT functions /// between Issue2 and Issue3 keyboards (16k/48k spectrum only) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index d9fb158b9f..c1818cb61a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -20,12 +20,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // parse single keyboard matrix keys for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++) { + + string key = KeyboardDevice.KeyboardMatrix[i]; - //bool prevState = KeyboardDevice.GetKeyStatus(key); + bool prevState = KeyboardDevice.GetKeyStatus(key); bool currState = Spectrum._controller.IsPressed(key); - //if (currState != prevState) - KeyboardDevice.SetKeyStatus(key, currState); + if (currState != prevState) + KeyboardDevice.SetKeyStatus(key, currState); } // non matrix keys diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index a37b24d22d..600a1c259e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -33,17 +33,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // combine the low byte (passed in as port) and the high byte (maybe not needed) //ushort word = Convert.ToUInt16((port << 8 | high)); + int word = (high << 8) + port; + //port += (ushort)(CPU.Regs[CPU.A] << 8); + // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; + bool lowBitReset = (word & 0x0001) == 0; // Kempston Joystick //not implemented yet - if (lowBitReset) - { + if (port == 254) + { // Even I/O address so get input // The high byte indicates which half-row of keys is being polled /* @@ -54,33 +57,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B */ - if ((word & 0x8000) == 0) + if (high == 0xfe) + result &= KeyboardDevice.KeyLine[0]; + if (high == 0xfd) + result &= KeyboardDevice.KeyLine[1]; + if (high == 0xfb) + result &= KeyboardDevice.KeyLine[2]; + if (high == 0xf7) + result &= KeyboardDevice.KeyLine[3]; + if (high == 0xef) + result &= KeyboardDevice.KeyLine[4]; + if (high == 0xdf) + result &= KeyboardDevice.KeyLine[5]; + if (high == 0xbf) + result &= KeyboardDevice.KeyLine[6]; + if (high == 0x7f) result &= KeyboardDevice.KeyLine[7]; - if ((word & 0x4000) == 0) - result &= KeyboardDevice.KeyLine[6]; - - if ((word & 0x2000) == 0) - result &= KeyboardDevice.KeyLine[5]; - - if ((word & 0x1000) == 0) - result &= KeyboardDevice.KeyLine[4]; - - if ((word & 0x800) == 0) - result &= KeyboardDevice.KeyLine[3]; - - if ((word & 0x400) == 0) - result &= KeyboardDevice.KeyLine[2]; - - if ((word & 0x200) == 0) - result &= KeyboardDevice.KeyLine[1]; - - if ((word & 0x100) == 0) - result &= KeyboardDevice.KeyLine[0]; result = result & 0x1f; //mask out lower 4 bits result = result | 0xa0; //set bit 5 & 7 to 1 + if (TapeDevice.CurrentMode == TapeOperationMode.Load) { if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) @@ -128,6 +126,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = (byte)(result & Convert.ToInt32("10111111", 2)); } */ + } else { @@ -140,8 +139,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // if unused port the floating memory bus should be returned (still todo) } - - return (byte)(result & 0xff); + + return (byte)result; } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs index c20faa7fa4..096408a101 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs @@ -82,6 +82,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void SetKeyStatus(string key, bool isPressed) { + int k = GetByteFromKeyMatrix(key); + /* byte keyByte = GetByteFromKeyMatrix(key); var lineIndex = keyByte / 5; @@ -90,57 +92,65 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LineStatus[lineIndex] = isPressed ? (byte)(LineStatus[lineIndex] | lineMask) : (byte)(LineStatus[lineIndex] & ~lineMask); */ + if (k != 255) + { + var lineIndex = k / 5; + var lineMask = 1 << k % 5; - int k = GetByteFromKeyMatrix(key); - + _keyLine[lineIndex] = isPressed + ? (byte)(_keyLine[lineIndex] & ~lineMask) + : (byte)(_keyLine[lineIndex] | lineMask); + } + + /* if (isPressed) { switch (k) { // 0xfefe - 0 - 4 - case 0: _keyLine[0] = (_keyLine[0] & ~(0x01)); break; + case 0: _keyLine[0] = (_keyLine[0] & ~(0x1)); break; case 1: _keyLine[0] = (_keyLine[0] & ~(0x02)); break; case 2: _keyLine[0] = (_keyLine[0] & ~(0x04)); break; case 3: _keyLine[0] = (_keyLine[0] & ~(0x08)); break; case 4: _keyLine[0] = (_keyLine[0] & ~(0x10)); break; // 0xfdfe - 5 - 9 - case 5: _keyLine[1] = (_keyLine[1] & ~(0x01)); break; + case 5: _keyLine[1] = (_keyLine[1] & ~(0x1)); break; case 6: _keyLine[1] = (_keyLine[1] & ~(0x02)); break; case 7: _keyLine[1] = (_keyLine[1] & ~(0x04)); break; case 8: _keyLine[1] = (_keyLine[1] & ~(0x08)); break; case 9: _keyLine[1] = (_keyLine[1] & ~(0x10)); break; // 0xfbfe - 10 - 14 - case 10: _keyLine[2] = (_keyLine[2] & ~(0x01)); break; + case 10: _keyLine[2] = (_keyLine[2] & ~(0x1)); break; case 11: _keyLine[2] = (_keyLine[2] & ~(0x02)); break; case 12: _keyLine[2] = (_keyLine[2] & ~(0x04)); break; case 13: _keyLine[2] = (_keyLine[2] & ~(0x08)); break; case 14: _keyLine[2] = (_keyLine[2] & ~(0x10)); break; // 0xf7fe - 15 - 19 - case 15: _keyLine[3] = (_keyLine[3] & ~(0x01)); break; + case 15: _keyLine[3] = (_keyLine[3] & ~(0x1)); break; case 16: _keyLine[3] = (_keyLine[3] & ~(0x02)); break; case 17: _keyLine[3] = (_keyLine[3] & ~(0x04)); break; case 18: _keyLine[3] = (_keyLine[3] & ~(0x08)); break; case 19: _keyLine[3] = (_keyLine[3] & ~(0x10)); break; // 0xeffe - 20 - 24 - case 20: _keyLine[4] = (_keyLine[4] & ~(0x01)); break; + case 20: _keyLine[4] = (_keyLine[4] & ~(0x1)); break; case 21: _keyLine[4] = (_keyLine[4] & ~(0x02)); break; case 22: _keyLine[4] = (_keyLine[4] & ~(0x04)); break; case 23: _keyLine[4] = (_keyLine[4] & ~(0x08)); break; case 24: _keyLine[4] = (_keyLine[4] & ~(0x10)); break; // 0xdffe - 25 - 29 - case 25: _keyLine[5] = (_keyLine[5] & ~(0x01)); break; + case 25: _keyLine[5] = (_keyLine[5] & ~(0x1)); break; case 26: _keyLine[5] = (_keyLine[5] & ~(0x02)); break; case 27: _keyLine[5] = (_keyLine[5] & ~(0x04)); break; case 28: _keyLine[5] = (_keyLine[5] & ~(0x08)); break; case 29: _keyLine[5] = (_keyLine[5] & ~(0x10)); break; // 0xbffe - 30 - 34 - case 30: _keyLine[6] = (_keyLine[6] & ~(0x01)); break; + case 30: _keyLine[6] = (_keyLine[6] & ~(0x1)); break; case 31: _keyLine[6] = (_keyLine[6] & ~(0x02)); break; case 32: _keyLine[6] = (_keyLine[6] & ~(0x04)); break; case 33: _keyLine[6] = (_keyLine[6] & ~(0x08)); break; case 34: _keyLine[6] = (_keyLine[6] & ~(0x10)); break; // 0x7ffe - 35 - 39 - case 35: _keyLine[7] = (_keyLine[7] & ~(0x01)); break; + case 35: _keyLine[7] = (_keyLine[7] & ~(0x1)); break; case 36: _keyLine[7] = (_keyLine[7] & ~(0x02)); break; case 37: _keyLine[7] = (_keyLine[7] & ~(0x04)); break; case 38: _keyLine[7] = (_keyLine[7] & ~(0x08)); break; @@ -152,49 +162,49 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (k) { // 0xfefe - 0 - 4 - case 0: _keyLine[0] = (_keyLine[0] | (0x01)); break; + case 0: _keyLine[0] = (_keyLine[0] | (0x1)); break; case 1: _keyLine[0] = (_keyLine[0] | (0x02)); break; case 2: _keyLine[0] = (_keyLine[0] | (0x04)); break; case 3: _keyLine[0] = (_keyLine[0] | (0x08)); break; case 4: _keyLine[0] = (_keyLine[0] | (0x10)); break; // 0xfdfe - 5 - 9 - case 5: _keyLine[1] = (_keyLine[1] | (0x01)); break; + case 5: _keyLine[1] = (_keyLine[1] | (0x1)); break; case 6: _keyLine[1] = (_keyLine[1] | (0x02)); break; case 7: _keyLine[1] = (_keyLine[1] | (0x04)); break; case 8: _keyLine[1] = (_keyLine[1] | (0x08)); break; case 9: _keyLine[1] = (_keyLine[1] | (0x10)); break; // 0xfbfe - 10 - 14 - case 10: _keyLine[2] = (_keyLine[2] | (0x01)); break; + case 10: _keyLine[2] = (_keyLine[2] | (0x1)); break; case 11: _keyLine[2] = (_keyLine[2] | (0x02)); break; case 12: _keyLine[2] = (_keyLine[2] | (0x04)); break; case 13: _keyLine[2] = (_keyLine[2] | (0x08)); break; case 14: _keyLine[2] = (_keyLine[2] | (0x10)); break; // 0xf7fe - 15 - 19 - case 15: _keyLine[3] = (_keyLine[3] | (0x01)); break; + case 15: _keyLine[3] = (_keyLine[3] | (0x1)); break; case 16: _keyLine[3] = (_keyLine[3] | (0x02)); break; case 17: _keyLine[3] = (_keyLine[3] | (0x04)); break; case 18: _keyLine[3] = (_keyLine[3] | (0x08)); break; case 19: _keyLine[3] = (_keyLine[3] | (0x10)); break; // 0xeffe - 20 - 24 - case 20: _keyLine[4] = (_keyLine[4] | (0x01)); break; + case 20: _keyLine[4] = (_keyLine[4] | (0x1)); break; case 21: _keyLine[4] = (_keyLine[4] | (0x02)); break; case 22: _keyLine[4] = (_keyLine[4] | (0x04)); break; case 23: _keyLine[4] = (_keyLine[4] | (0x08)); break; case 24: _keyLine[4] = (_keyLine[4] | (0x10)); break; // 0xdffe - 25 - 29 - case 25: _keyLine[5] = (_keyLine[5] | (0x01)); break; + case 25: _keyLine[5] = (_keyLine[5] | (0x1)); break; case 26: _keyLine[5] = (_keyLine[5] | (0x02)); break; case 27: _keyLine[5] = (_keyLine[5] | (0x04)); break; case 28: _keyLine[5] = (_keyLine[5] | (0x08)); break; case 29: _keyLine[5] = (_keyLine[5] | (0x10)); break; // 0xbffe - 30 - 34 - case 30: _keyLine[6] = (_keyLine[6] | (0x01)); break; + case 30: _keyLine[6] = (_keyLine[6] | (0x1)); break; case 31: _keyLine[6] = (_keyLine[6] | (0x02)); break; case 32: _keyLine[6] = (_keyLine[6] | (0x04)); break; case 33: _keyLine[6] = (_keyLine[6] | (0x08)); break; case 34: _keyLine[6] = (_keyLine[6] | (0x10)); break; // 0x7ffe - 35 - 39 - case 35: _keyLine[7] = (_keyLine[7] | (0x01)); break; + case 35: _keyLine[7] = (_keyLine[7] | (0x1)); break; case 36: _keyLine[7] = (_keyLine[7] | (0x02)); break; case 37: _keyLine[7] = (_keyLine[7] | (0x04)); break; case 38: _keyLine[7] = (_keyLine[7] | (0x08)); break; @@ -202,6 +212,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + */ + // Combination keys that are not in the keyboard matrix // but are available on the Spectrum+, 128k +2 & +3 // (GetByteFromKeyMatrix() should return 255) @@ -247,12 +259,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum byte keyByte = GetByteFromKeyMatrix(key); var lineIndex = keyByte / 5; var lineMask = 1 << keyByte % 5; - return (LineStatus[lineIndex] & lineMask) != 0; + + return (_keyLine[lineIndex] & lineMask) == 0; + } + + public void ResetLineStatus() + { + lock (this) + { + for (int i = 0; i < KeyLine.Length; i++) + KeyLine[i] = 255; + } } public byte GetLineStatus(byte lines) { - /* + lock(this) { byte status = 0; @@ -261,7 +283,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum while (lines > 0) { if ((lines & 0x01) != 0) - status |= LineStatus[lineIndex]; + status |= (byte)_keyLine[lineIndex]; lineIndex++; lines >>= 1; } @@ -269,8 +291,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return result; } - */ - + + /* switch (lines) { case 0xfe: return (byte)KeyLine[0]; @@ -283,6 +305,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case 0x7f: return (byte)KeyLine[7]; default: return 0; } + */ } public byte ReadKeyboardByte(ushort addr) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 1a56bdfd4d..9add8bdeec 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -56,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.WriteMemory = _machine.WriteMemory; _cpu.ReadHardware = _machine.ReadPort; _cpu.WriteHardware = _machine.WritePort; - _cpu.FetchDB = _machine.PushBus; + _cpu.FetchDB = _machine.PushBus; ser.Register(_tracer); ser.Register(_cpu); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index eacc65afa1..3b2fbec9d2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -7,6 +7,7 @@ At this moment this is still *very* experimental and needs a lot more work. * ZX Spectrum 48k model * ULA video output (implementing IVideoProvider) * ULA Mode 1 VBLANK interrupt generation +* IM2 Interrupts and DataBus implementation (thanks Aloysha) * Beeper/Buzzer output (implementing ISoundProvider) * Keyboard input (implementing IInputPollable) * Tape device that will load spectrum games in realtime (*.tzx and *.tap) @@ -16,7 +17,6 @@ At this moment this is still *very* experimental and needs a lot more work. * ISettable - There are some Settings and SyncSettings instantiated, although they are not really used and I haven't yet figured out how to wire these up to the front-end yet ### Not working -* Interrupt Mode 2 (Z80A) - usually invokes a soft reboot when the game raises one (can be seen in 'Chaos - Battle of the Wizards' after initial game setup when the game board tries to load) * IMemoryDomains - I started looking at this but didn't really know what I was doing yet * IDebuggable * Default keyboard keymappings (you have to configure yourself in the core controller settings) From a9f8828063a9b794b57d42fddfc014badec3e8b4 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 1 Dec 2017 08:20:18 -0500 Subject: [PATCH 012/339] z80: fix port access behaviour --- .../CPUs/Z80A/Operations.cs | 8 ++++---- .../CPUs/Z80A/Tables_Direct.cs | 20 +++++++++---------- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 4 ++-- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs index 81f9e1b93a..adf7452c29 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs @@ -29,15 +29,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WriteMemory((ushort)((Regs[dest_l] | (Regs[dest_h] << 8)) + inc), (byte)Regs[src]); } - public void OUT_Func(ushort dest, ushort src) + public void OUT_Func(ushort dest_l, ushort dest_h, ushort src) { Regs[DB] = Regs[src]; - WriteHardware(Regs[dest], (byte)(Regs[src])); + WriteHardware((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)(Regs[src])); } - public void IN_Func(ushort dest, ushort src) + public void IN_Func(ushort dest, ushort src_l, ushort src_h) { - Regs[dest] = ReadHardware(Regs[src]); + Regs[dest] = ReadHardware((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); Regs[DB] = Regs[dest]; } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 71b39d6018..e708b02bac 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -452,13 +452,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - RD, ALU, PCl, PCh, + RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, TR, W, A, - OUT, ALU, A, - TR, Z, ALU, - INC16, Z, ALU, + OUT, Z, W, A, + IDLE, + INC16, Z, W, IDLE, IDLE, OP}; @@ -469,9 +469,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - OUT, dest, src, - IDLE, TR16, Z, W, C, B, + OUT, Z, W, src, + IDLE, INC16, Z, W, IDLE, OP}; @@ -481,12 +481,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - RD, ALU, PCl, PCh, + RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, TR, W, A, - IN, A, ALU, - TR, Z, ALU, + IN, A, Z, W, + IDLE, INC16, Z, W, IDLE, IDLE, @@ -498,7 +498,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - IN, dest, src, + IN, dest, src, B, IDLE, TR16, Z, W, C, B, INC16, Z, W, diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 623a2aa126..182056f026 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -578,10 +578,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = iff2; break; case OUT: - OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case IN: - IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case NEG: NEG_8_Func(cur_instr[instr_pntr++]); From f1fee7dc6ea5b1ee1f48264e419e828e7a60cb85 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 1 Dec 2017 08:51:18 -0500 Subject: [PATCH 013/339] TI832: fix port accesses --- BizHawk.Emulation.Cores/Calculator/TI83.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BizHawk.Emulation.Cores/Calculator/TI83.cs b/BizHawk.Emulation.Cores/Calculator/TI83.cs index cfe82f74eb..403f0fa7b8 100644 --- a/BizHawk.Emulation.Cores/Calculator/TI83.cs +++ b/BizHawk.Emulation.Cores/Calculator/TI83.cs @@ -137,6 +137,8 @@ namespace BizHawk.Emulation.Cores.Calculators private void WriteHardware(ushort addr, byte value) { + addr &= 0xFF; + switch (addr) { case 0: // PORT_LINK @@ -232,6 +234,8 @@ namespace BizHawk.Emulation.Cores.Calculators private byte ReadHardware(ushort addr) { + addr &= 0xFF; + switch (addr) { case 0: // PORT_LINK From 42db9479393615f162e733e59573ac92c76d30a2 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 1 Dec 2017 14:34:45 +0000 Subject: [PATCH 014/339] updated keyscanning code --- .../Machine/SpectrumBase.Port.cs | 58 ++++++++++++------- .../Machine/ZXSpectrum48K/ZX48.Keyboard.cs | 5 +- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 600a1c259e..d42f7301f9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -24,29 +24,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual byte ReadPort(ushort port) { - CPU.TotalExecutedCycles += 4; - int result = 0xFF; - // get the high byte from Regs[6] - ushort high = CPU.Regs[6]; - - // combine the low byte (passed in as port) and the high byte (maybe not needed) - //ushort word = Convert.ToUInt16((port << 8 | high)); - - int word = (high << 8) + port; - - //port += (ushort)(CPU.Regs[CPU.A] << 8); - // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address - bool lowBitReset = (word & 0x0001) == 0; + bool lowBitReset = (port & 0x0001) == 0; + + ContendPort((ushort)port); + // Kempston Joystick - //not implemented yet + if (port == 0x1f) + { - if (port == 254) - { + } + else if (lowBitReset) + { // Even I/O address so get input // The high byte indicates which half-row of keys is being polled /* @@ -57,6 +50,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B */ + result &= KeyboardDevice.GetLineStatus((byte)(port >> 8)); + /* if (high == 0xfe) result &= KeyboardDevice.KeyLine[0]; if (high == 0xfd) @@ -73,7 +68,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result &= KeyboardDevice.KeyLine[6]; if (high == 0x7f) result &= KeyboardDevice.KeyLine[7]; - +*/ result = result & 0x1f; //mask out lower 4 bits result = result | 0xa0; //set bit 5 & 7 to 1 @@ -150,11 +145,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void WritePort(ushort port, byte value) { - CPU.TotalExecutedCycles += 4; - // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; + bool lowBitReset = (port & 0x01) == 0; + + ContendPort(port); // Only even addresses address the ULA if (lowBitReset) @@ -179,5 +174,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } } + + /// + /// Apply I/O contention if necessary + /// + /// + public virtual void ContendPort(ushort port) + { + var lowBit = (port & 0x0001) != 0; + var ulaHigh = (port & 0xc000) == 0x4000; + var cfc = CurrentFrameCycle; + if (cfc < 1) + cfc = 1; + + if (ulaHigh) + { + CPU.TotalExecutedCycles += GetContentionValue(cfc - 1); + } + else + { + if (!lowBit) + CPU.TotalExecutedCycles += GetContentionValue(cfc); + } + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs index 096408a101..afa886675a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs @@ -273,8 +273,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } public byte GetLineStatus(byte lines) - { - + { lock(this) { byte status = 0; @@ -287,7 +286,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum lineIndex++; lines >>= 1; } - var result = (byte)~status; + var result = (byte)status; return result; } From fb8fd2ae900922f600c58aff3d817c8c259f6b24 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 1 Dec 2017 15:34:47 +0000 Subject: [PATCH 015/339] Fixed input detection --- .../Machine/SpectrumBase.Port.cs | 56 ++++++++----------- .../Machine/ZXSpectrum48K/ZX48.Keyboard.cs | 12 +--- 2 files changed, 26 insertions(+), 42 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index d42f7301f9..b5d0b8bdee 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -30,8 +30,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; - ContendPort((ushort)port); - + ContendPort((ushort)port); // Kempston Joystick if (port == 0x1f) @@ -48,27 +47,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B - */ + */ - result &= KeyboardDevice.GetLineStatus((byte)(port >> 8)); - /* - if (high == 0xfe) - result &= KeyboardDevice.KeyLine[0]; - if (high == 0xfd) - result &= KeyboardDevice.KeyLine[1]; - if (high == 0xfb) - result &= KeyboardDevice.KeyLine[2]; - if (high == 0xf7) - result &= KeyboardDevice.KeyLine[3]; - if (high == 0xef) - result &= KeyboardDevice.KeyLine[4]; - if (high == 0xdf) - result &= KeyboardDevice.KeyLine[5]; - if (high == 0xbf) - result &= KeyboardDevice.KeyLine[6]; - if (high == 0x7f) + if ((port & 0x8000) == 0) result &= KeyboardDevice.KeyLine[7]; -*/ + + if ((port & 0x4000) == 0) + result &= KeyboardDevice.KeyLine[6]; + + if ((port & 0x2000) == 0) + result &= KeyboardDevice.KeyLine[5]; + + if ((port & 0x1000) == 0) + result &= KeyboardDevice.KeyLine[4]; + + if ((port & 0x800) == 0) + result &= KeyboardDevice.KeyLine[3]; + + if ((port & 0x400) == 0) + result &= KeyboardDevice.KeyLine[2]; + + if ((port & 0x200) == 0) + result &= KeyboardDevice.KeyLine[1]; + + if ((port & 0x100) == 0) + result &= KeyboardDevice.KeyLine[0]; result = result & 0x1f; //mask out lower 4 bits result = result | 0xa0; //set bit 5 & 7 to 1 @@ -110,17 +113,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } } - /* - // read keyboard input - if (high != 0) - result &= KeyboardDevice.GetLineStatus((byte)high); - - var ear = TapeDevice.GetEarBit(CPU.TotalExecutedCycles); - if (!ear) - { - result = (byte)(result & Convert.ToInt32("10111111", 2)); - } - */ } else diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs index afa886675a..e04eedc217 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs @@ -77,21 +77,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LineStatus = new byte[8]; _keyLine = new int[] { 255, 255, 255, 255, 255, 255, 255, 255 }; - IsIssue2Keyboard = false; + IsIssue2Keyboard = true; } public void SetKeyStatus(string key, bool isPressed) { int k = GetByteFromKeyMatrix(key); - - /* - byte keyByte = GetByteFromKeyMatrix(key); - var lineIndex = keyByte / 5; - var lineMask = 1 << keyByte % 5; - - LineStatus[lineIndex] = isPressed ? (byte)(LineStatus[lineIndex] | lineMask) - : (byte)(LineStatus[lineIndex] & ~lineMask); - */ + if (k != 255) { var lineIndex = k / 5; From 048c65cd7eddfc4d7bc72824a4cff05d1727cca8 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 1 Dec 2017 17:33:56 +0000 Subject: [PATCH 016/339] Implemented Kempston Joystick (hardcoded J1) --- .../BizHawk.Emulation.Cores.csproj | 1 + .../Hardware/KempstonJoystick.cs | 75 +++++++++++++++++++ .../Machine/SpectrumBase.Input.cs | 10 +++ .../Machine/SpectrumBase.Port.cs | 4 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 5 ++ .../Machine/ZXSpectrum48K/ZX48.cs | 1 + 6 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/KempstonJoystick.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 251a064158..13af57d15a 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -266,6 +266,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/KempstonJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/KempstonJoystick.cs new file mode 100644 index 0000000000..5da278df76 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/KempstonJoystick.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class KempstonJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + public readonly string[] _bitPos = new string[] + { + "P1 Right", + "P1 Left", + "P1 Down", + "P1 Up", + "P1 Button" + }; + + /* + Active bits high + 0 0 0 F U D L R + */ + public int JoyLine + { + get { return _joyLine; } + set { _joyLine = value; } + } + + public KempstonJoystick(SpectrumBase machine) + { + _machine = machine; + _joyLine = 0; + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + if (isPressed) + _joyLine |= (1 << pos); + else + _joyLine &= ~(1 << pos); + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + return (_joyLine & (1 << pos)) != 0; + } + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(_bitPos, key); + return index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index c1818cb61a..9f0c736069 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -42,6 +42,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + // J1 + foreach (string j in KempstonDevice._bitPos) + { + bool prevState = KempstonDevice.GetJoyInput(j); + bool currState = Spectrum._controller.IsPressed(j); + + if (currState != prevState) + KempstonDevice.SetJoyInput(j, currState); + } + // Tape control if (Spectrum._controller.IsPressed(Play)) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index b5d0b8bdee..be31fead3f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -33,9 +33,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ContendPort((ushort)port); // Kempston Joystick - if (port == 0x1f) + if ((port & 0xe0) == 0 || (port & 0x20) == 0) { - + return (byte)KempstonDevice.JoyLine; } else if (lowBitReset) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 6183843006..7cd878d1cb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -46,6 +46,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual ITapeProvider TapeProvider { get; set; } + /// + /// Kempston joystick + /// + public virtual KempstonJoystick KempstonDevice { get; set; } + /// /// Signs whether the frame has ended /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 57e5e14c1a..3deae820b1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -41,6 +41,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.Init(44100, UlaFrameCycleCount); KeyboardDevice = new Keyboard48(this); + KempstonDevice = new KempstonJoystick(this); TapeProvider = new DefaultTapeProvider(file); From 2b880d863b60bdc22d205a0bafdfaedab367a992 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 1 Dec 2017 17:40:45 +0000 Subject: [PATCH 017/339] updated readme --- BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 3b2fbec9d2..bbced33297 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -10,20 +10,19 @@ At this moment this is still *very* experimental and needs a lot more work. * IM2 Interrupts and DataBus implementation (thanks Aloysha) * Beeper/Buzzer output (implementing ISoundProvider) * Keyboard input (implementing IInputPollable) +* Kempston joystick (mapped to J1 currently) * Tape device that will load spectrum games in realtime (*.tzx and *.tap) * IStatable (although this is not currently working/implemented properly during tape load operations) +* IMemoryDomains (I think) ### Some progress * ISettable - There are some Settings and SyncSettings instantiated, although they are not really used and I haven't yet figured out how to wire these up to the front-end yet ### Not working -* IMemoryDomains - I started looking at this but didn't really know what I was doing yet * IDebuggable * Default keyboard keymappings (you have to configure yourself in the core controller settings) -* Joystick support (I still need to implement a Kemptston joystick and interface) * Manual tape device control (at the moment the tape device detects when the spectrum goes into 'loadbytes' mode and auto-plays the tape. This is not ideal and manual control should be implemented so the user can start/stop manually, return to zero etc..) * Only standard spectrum tape blocks currently work. Any fancy SpeedLock encoded (and similar) blocks do not ### Known bugs -* The 'return' keyboard key is acting the same as Space/Break when doing a BASIC RUN or LOAD "" command. The upshot of this is that upon boot, when you go to load the attached spectrum cassette (you have to type: "J", then "SYMSHIFT + P", then "SYMSHIFT + P", then RETURN) it more often than not interrupts the load routine. You then have to try again but hitting the RETURN key at the end of the sequence for as small a time as possible. Rinse and repeat until the load process starts. Clearly NOT ideal. * Audible 'popping' from the emulated buzzer after a load state operation From 38ee7147b3051cdba13b8dced7cb2e433ead52de Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 4 Dec 2017 09:42:08 +0000 Subject: [PATCH 018/339] _frameBuffer is now populated with correct data immediately, rather than converted during the IVideoProvider cycle --- .../SinclairSpectrum/Machine/SpectrumBase.Screen.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index bdada81b35..8a4bedf024 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -38,7 +38,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The main screen buffer /// - protected byte[] _frameBuffer; + protected int[] _frameBuffer; /// /// Pixel and attribute info stored while rendering the screen @@ -531,8 +531,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum protected virtual void SetPixels(int colorIndex1, int colorIndex2) { var pos = _yPos * ScreenWidth + _xPos; - _frameBuffer[pos++] = (byte)colorIndex1; - _frameBuffer[pos] = (byte)colorIndex2; + _frameBuffer[pos++] = ULAPalette[colorIndex1]; + _frameBuffer[pos] = ULAPalette[colorIndex2]; } /// @@ -785,7 +785,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //BorderDevice.Reset(); _flashPhase = false; - _frameBuffer = new byte[ScreenWidth * ScreenLines]; + _frameBuffer = new int[ScreenWidth * ScreenLines]; InitULACycleTable(); @@ -912,6 +912,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ public int[] GetVideoBuffer() { + return _frameBuffer; + // convert the generated _framebuffer into ARGB colours via the ULAPalette int[] trans = new int[_frameBuffer.Length]; for (int i = 0; i < _frameBuffer.Length; i++) From 6d22b06c21c549ff042c68ac8c5e8ed2cdfca8da Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 4 Dec 2017 14:05:04 +0000 Subject: [PATCH 019/339] Added virtual pad to UI --- .../BizHawk.Client.EmuHawk.csproj | 3 +- .../VirtualPads/schema/ZXSpectrumSchema.cs | 241 ++++++++++++++++++ .../ZXSpectrum.Controllers.cs | 12 +- 3 files changed, 249 insertions(+), 7 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 2a860dd082..fdce3477bf 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -1197,6 +1197,7 @@ + UserControl @@ -2173,4 +2174,4 @@ - + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs new file mode 100644 index 0000000000..fbcb9b9e57 --- /dev/null +++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs @@ -0,0 +1,241 @@ +using System.Collections.Generic; +using System.Drawing; + +using BizHawk.Emulation.Common; +using System.Linq; + +namespace BizHawk.Client.EmuHawk +{ + [Schema("ZXSpectrum")] + class ZXSpectrumSchema : IVirtualPadSchema + { + public IEnumerable GetPadSchemas(IEmulator core) + { + yield return KempstonJoystick(1); + yield return Keyboard(); + yield return TapeDevice(); + } + + private static PadSchema KempstonJoystick(int controller) + { + return new PadSchema + { + DisplayName = "Player " + controller + " (Kempton Joystick)", + IsConsole = false, + DefaultSize = new Size(174, 74), + MaxSize = new Size(174, 74), + Buttons = new[] + { + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Up", + DisplayName = "", + Icon = Properties.Resources.BlueUp, + Location = new Point(23, 15), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Down", + DisplayName = "", + Icon = Properties.Resources.BlueDown, + Location = new Point(23, 36), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Left", + DisplayName = "", + Icon = Properties.Resources.Back, + Location = new Point(2, 24), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Right", + DisplayName = "", + Icon = Properties.Resources.Forward, + Location = new Point(44, 24), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Button", + DisplayName = "B", + Location = new Point(124, 24), + Type = PadSchema.PadInputType.Boolean + } + } + }; + } + + private class ButtonLayout + { + public string Name { get; set; } + public string DisName { get; set; } + public double WidthFactor { get; set; } + public int Row { get; set; } + public bool IsActive = true; + } + + private static PadSchema Keyboard() + { + List bls = new List + { + new ButtonLayout { Name = "Key True Video", DisName = "TV", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Inv Video", DisName = "IV", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 1", DisName = "1", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 2", DisName = "2", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 3", DisName = "3", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 4", DisName = "4", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 5", DisName = "5", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 6", DisName = "6", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 7", DisName = "7", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 8", DisName = "8", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 9", DisName = "9", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 0", DisName = "0", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Break", DisName = "BREAK", Row = 0, WidthFactor = 1.5 }, + + new ButtonLayout { Name = "Key Delete", DisName = "DEL", Row = 1, WidthFactor = 1.5 }, + new ButtonLayout { Name = "Key Graph", DisName = "GR", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Q", DisName = "Q", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key W", DisName = "W", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key E", DisName = "E", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key R", DisName = "R", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key T", DisName = "T", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Y", DisName = "Y", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key U", DisName = "U", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key I", DisName = "I", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key O", DisName = "O", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key P", DisName = "P", Row = 1, WidthFactor = 1 }, + + new ButtonLayout { Name = "Key Extend Mode", DisName = "EM", Row = 2, WidthFactor = 1.5 }, + new ButtonLayout { Name = "Key Edit", DisName = "ED", Row = 2, WidthFactor = 1.25}, + new ButtonLayout { Name = "Key A", DisName = "A", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key S", DisName = "S", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key D", DisName = "D", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key F", DisName = "F", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key G", DisName = "G", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key H", DisName = "H", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key J", DisName = "J", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key K", DisName = "K", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key L", DisName = "L", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Return", DisName = "ENTER", Row = 2, WidthFactor = 1.75 }, + + new ButtonLayout { Name = "Key Caps Shift", DisName = "CAPS-S", Row = 3, WidthFactor = 2.25 }, + new ButtonLayout { Name = "Key Caps Lock", DisName = "CL", Row = 3, WidthFactor = 1}, + new ButtonLayout { Name = "Key Z", DisName = "Z", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key X", DisName = "X", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key C", DisName = "C", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key V", DisName = "V", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key B", DisName = "B", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key N", DisName = "N", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key M", DisName = "M", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Period", DisName = ".", Row = 3, WidthFactor = 1}, + new ButtonLayout { Name = "Key Caps Shift", DisName = "CAPS-S", Row = 3, WidthFactor = 2.25 }, + + new ButtonLayout { Name = "Key Symbol Shift", DisName = "SS", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Semi-Colon", DisName = ";", Row = 4, WidthFactor = 1}, + new ButtonLayout { Name = "Key Quote", DisName = "\"", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Left Cursor", DisName = "←", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Right Cursor", DisName = "→", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Space", DisName = "SPACE", Row = 4, WidthFactor = 4.5 }, + new ButtonLayout { Name = "Key Up Cursor", DisName = "↑", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Down Cursor", DisName = "↓", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Comma", DisName = ",", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Symbol Shift", DisName = "SS", Row = 4, WidthFactor = 1 }, + }; + + PadSchema ps = new PadSchema + { + DisplayName = "Keyboard", + IsConsole = false, + DefaultSize = new Size(500, 170) + }; + + List btns = new List(); + + int rowHeight = 29; //24 + int stdButtonWidth = 29; //24 + int yPos = 18; + int xPos = 22; + int currRow = 0; + + foreach (var b in bls) + { + if (b.Row > currRow) + { + currRow++; + yPos += rowHeight; + xPos = 22; + } + + int txtLength = b.DisName.Length; + int btnSize = System.Convert.ToInt32((double)stdButtonWidth * b.WidthFactor); + + + string disp = b.DisName; + if (txtLength == 1) + disp = " " + disp; + + switch(b.DisName) + { + case "SPACE": disp = " " + disp + " "; break; + case "I": disp = " " + disp + " "; break; + case "W": disp = b.DisName; break; + } + + + if (b.IsActive) + { + PadSchema.ButtonSchema btn = new PadSchema.ButtonSchema(); + btn.Name = b.Name; + btn.DisplayName = disp; + btn.Location = new Point(xPos, yPos); + btn.Type = PadSchema.PadInputType.Boolean; + btns.Add(btn); + } + + xPos += btnSize; + } + + ps.Buttons = btns.ToArray(); + return ps; + } + + private static PadSchema TapeDevice() + { + return new PadSchema + { + DisplayName = "DATACORDER", + IsConsole = false, + DefaultSize = new Size(174, 74), + MaxSize = new Size(174, 74), + Buttons = new[] + { + new PadSchema.ButtonSchema + { + Name = "Play Tape", + Icon = Properties.Resources.Play, + Location = new Point(23, 22), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "Stop Tape", + Icon = Properties.Resources.Stop, + Location = new Point(53, 22), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "RTZ Tape", + Icon = Properties.Resources.BackMore, + Location = new Point(83, 22), + Type = PadSchema.PadInputType.Boolean + } + } + }; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index d0f40166ec..018c9b67b1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -16,18 +16,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Name = "ZXSpectrum Controller", BoolButtons = { - // Joystick interface (not yet emulated) - Could be Kempston/Cursor/Protek + // Kempston Joystick (P1) "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", // Keyboard - row 1 - /*"Key True Video", "Key Inv Video",*/ "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", + "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", // Keyboard - row 2 - "Key Delete", /*"Key Graph",*/ "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", + "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", // Keyboard - row 3 - /*"Key Extend Mode", "Key Edit",*/ "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", + "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", // Keyboard - row 4 - "Key Caps Shift", /*"Key Caps Lock",*/ "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", /*"Key Period",*/ + "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", // Keyboard - row 5 - "Key Symbol Shift", /*"Key Semi-Colon", "Key Quote",*/ "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", /*"Key Comma",*/ + "Key Symbol Shift", "Key Semi-Colon", "Key Quote", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", // Tape functions "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" } From f92269657d5786abf2dd8cdf6c4d0dceaa93fb4a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 4 Dec 2017 15:40:27 +0000 Subject: [PATCH 020/339] Added core UI menu and fixed up settings / syncsettings --- BizHawk.Client.EmuHawk/MainForm.Designer.cs | 406 ++++++++++-------- BizHawk.Client.EmuHawk/MainForm.Events.cs | 20 +- BizHawk.Client.EmuHawk/MainForm.cs | 8 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 37 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 4 +- 5 files changed, 268 insertions(+), 207 deletions(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index d2c636448a..dcb8272627 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -73,7 +73,7 @@ this.LoadCurrentSlotMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SaveRAMSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.FlushSaveRAMMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.MovieSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ReadonlyMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator15 = new System.Windows.Forms.ToolStripSeparator(); @@ -192,14 +192,13 @@ this.GbaCoreSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.VbaNextCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MgbaCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SGBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); - this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SgbSameBoyMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); - this.GBGambatteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBGBHawkMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGambatteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGBHawkMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem16 = new System.Windows.Forms.ToolStripSeparator(); this.allowGameDBCoreOverridesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); @@ -239,9 +238,8 @@ this.coreToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.quickNESToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.nesHawkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator34 = new System.Windows.Forms.ToolStripSeparator(); - this.toolStripSeparator35 = new System.Windows.Forms.ToolStripSeparator(); - this.NESPPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator34 = new System.Windows.Forms.ToolStripSeparator(); + this.NESPPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.NESNametableViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.NESGameGenieCodesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.musicRipperToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -279,6 +277,12 @@ this.SMSdisplayNtscToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayPalToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayAutoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerStandardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerPaddleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerLightPhaserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerSportsPadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerKeyboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMStoolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.SMSenableBIOSToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSEnableFMChipMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -303,14 +307,14 @@ this.AtariSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.A7800SubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.A7800ControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.A7800FilterSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBSubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.A7800FilterSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.GBcoreSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.LoadGBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator28 = new System.Windows.Forms.ToolStripSeparator(); this.GBGPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBPrinterViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBPrinterViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.GBASubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.GBACoreSelectionSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.GBAmGBAMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -332,6 +336,7 @@ this.SnesOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ColecoSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ColecoControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator35 = new System.Windows.Forms.ToolStripSeparator(); this.ColecoSkipBiosMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.N64SubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.N64PluginSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -374,6 +379,8 @@ this.ForumsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.FeaturesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton(); this.EmuStatus = new System.Windows.Forms.ToolStripStatusLabel(); @@ -445,13 +452,8 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.SMSControllerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerStandardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerPaddleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerLightPhaserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerSportsPadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerKeyboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.MainformMenu.SuspendLayout(); + this.preferencesToolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); + this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); this.SuspendLayout(); @@ -488,7 +490,8 @@ this.pCFXToolStripMenuItem, this.virtualBoyToolStripMenuItem, this.neoGeoPocketToolStripMenuItem, - this.HelpSubMenu}); + this.HelpSubMenu, + this.zXSpectrumToolStripMenuItem}); this.MainformMenu.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Flow; this.MainformMenu.Location = new System.Drawing.Point(0, 0); this.MainformMenu.Name = "MainformMenu"; @@ -910,26 +913,26 @@ this.LoadCurrentSlotMenuItem.Size = new System.Drawing.Size(178, 22); this.LoadCurrentSlotMenuItem.Text = "Load Current Slot"; this.LoadCurrentSlotMenuItem.Click += new System.EventHandler(this.LoadCurrentSlotMenuItem_Click); - - // - // SaveRAMSubMenu - // - this.SaveRAMSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.FlushSaveRAMMenuItem}); - this.SaveRAMSubMenu.Name = "SaveRAMSubMenu"; - this.SaveRAMSubMenu.Size = new System.Drawing.Size(159, 22); - this.SaveRAMSubMenu.Text = "Save &RAM"; - this.SaveRAMSubMenu.DropDownOpened += new System.EventHandler(this.SaveRAMSubMenu_DropDownOpened); - // - // FlushSaveRAMMenuItem - // - this.FlushSaveRAMMenuItem.Name = "FlushSaveRAMMenuItem"; - this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(156, 22); - this.FlushSaveRAMMenuItem.Text = "&Flush Save Ram"; - this.FlushSaveRAMMenuItem.Click += new System.EventHandler(this.FlushSaveRAMMenuItem_Click); - // toolStripMenuItem2 - // - this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + // + // SaveRAMSubMenu + // + this.SaveRAMSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.FlushSaveRAMMenuItem}); + this.SaveRAMSubMenu.Name = "SaveRAMSubMenu"; + this.SaveRAMSubMenu.Size = new System.Drawing.Size(159, 22); + this.SaveRAMSubMenu.Text = "Save &RAM"; + this.SaveRAMSubMenu.DropDownOpened += new System.EventHandler(this.SaveRAMSubMenu_DropDownOpened); + // + // FlushSaveRAMMenuItem + // + this.FlushSaveRAMMenuItem.Name = "FlushSaveRAMMenuItem"; + this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(156, 22); + this.FlushSaveRAMMenuItem.Text = "&Flush Save Ram"; + this.FlushSaveRAMMenuItem.Click += new System.EventHandler(this.FlushSaveRAMMenuItem_Click); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; this.toolStripMenuItem2.Size = new System.Drawing.Size(156, 6); // // MovieSubMenu @@ -1124,7 +1127,7 @@ // this.RecordAVMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.RecordHS; this.RecordAVMenuItem.Name = "RecordAVMenuItem"; - this.RecordAVMenuItem.Size = new System.Drawing.Size(223, 22); + this.RecordAVMenuItem.Size = new System.Drawing.Size(225, 22); this.RecordAVMenuItem.Text = "&Record AVI/WAV"; this.RecordAVMenuItem.Click += new System.EventHandler(this.RecordAVMenuItem_Click); // @@ -1132,7 +1135,7 @@ // this.ConfigAndRecordAVMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.AVI; this.ConfigAndRecordAVMenuItem.Name = "ConfigAndRecordAVMenuItem"; - this.ConfigAndRecordAVMenuItem.Size = new System.Drawing.Size(223, 22); + this.ConfigAndRecordAVMenuItem.Size = new System.Drawing.Size(225, 22); this.ConfigAndRecordAVMenuItem.Text = "Config and Record AVI/WAV"; this.ConfigAndRecordAVMenuItem.Click += new System.EventHandler(this.ConfigAndRecordAVMenuItem_Click); // @@ -1140,26 +1143,26 @@ // this.StopAVIMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Stop; this.StopAVIMenuItem.Name = "StopAVIMenuItem"; - this.StopAVIMenuItem.Size = new System.Drawing.Size(223, 22); + this.StopAVIMenuItem.Size = new System.Drawing.Size(225, 22); this.StopAVIMenuItem.Text = "&Stop AVI/WAV"; this.StopAVIMenuItem.Click += new System.EventHandler(this.StopAVMenuItem_Click); // // toolStripSeparator19 // this.toolStripSeparator19.Name = "toolStripSeparator19"; - this.toolStripSeparator19.Size = new System.Drawing.Size(220, 6); + this.toolStripSeparator19.Size = new System.Drawing.Size(222, 6); // // CaptureOSDMenuItem // this.CaptureOSDMenuItem.Name = "CaptureOSDMenuItem"; - this.CaptureOSDMenuItem.Size = new System.Drawing.Size(223, 22); + this.CaptureOSDMenuItem.Size = new System.Drawing.Size(225, 22); this.CaptureOSDMenuItem.Text = "Capture OSD"; this.CaptureOSDMenuItem.Click += new System.EventHandler(this.CaptureOSDMenuItem_Click); // // SynclessRecordingMenuItem // this.SynclessRecordingMenuItem.Name = "SynclessRecordingMenuItem"; - this.SynclessRecordingMenuItem.Size = new System.Drawing.Size(223, 22); + this.SynclessRecordingMenuItem.Size = new System.Drawing.Size(225, 22); this.SynclessRecordingMenuItem.Text = "S&yncless Recording Tools"; this.SynclessRecordingMenuItem.Click += new System.EventHandler(this.SynclessRecordingMenuItem_Click); // @@ -1814,8 +1817,8 @@ this.CoreSNESSubMenu, this.GbaCoreSubMenu, this.SGBCoreSubmenu, - this.GBCoreSubmenu, - this.GBInSGBMenuItem, + this.GBCoreSubmenu, + this.GBInSGBMenuItem, this.toolStripMenuItem16, this.allowGameDBCoreOverridesToolStripMenuItem, this.toolStripSeparator8, @@ -1898,12 +1901,6 @@ this.MgbaCoreMenuItem.Text = "mGBA"; this.MgbaCoreMenuItem.Click += new System.EventHandler(this.GbaCorePick_Click); // - // Atari7800HawkCoreMenuItem - // - this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem"; - this.Atari7800HawkCoreMenuItem.Size = new System.Drawing.Size(153, 22); - this.Atari7800HawkCoreMenuItem.Text = "Atari7800Hawk"; - // // SGBCoreSubmenu // this.SGBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -1913,48 +1910,48 @@ this.SGBCoreSubmenu.Size = new System.Drawing.Size(239, 22); this.SGBCoreSubmenu.Text = "SGB"; this.SGBCoreSubmenu.DropDownOpened += new System.EventHandler(this.SGBCoreSubmenu_DropDownOpened); - // - // GBCoreSubmenu - // - this.GBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.GBGambatteMenuItem, - this.GBGBHawkMenuItem}); - this.GBCoreSubmenu.Name = "GBCoreSubmenu"; - this.GBCoreSubmenu.Size = new System.Drawing.Size(239, 22); - this.GBCoreSubmenu.Text = "GB"; - this.GBCoreSubmenu.DropDownOpened += new System.EventHandler(this.GBCoreSubmenu_DropDownOpened); - // - // SgbBsnesMenuItem - // - this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; - this.SgbBsnesMenuItem.Size = new System.Drawing.Size(152, 22); + // + // SgbBsnesMenuItem + // + this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; + this.SgbBsnesMenuItem.Size = new System.Drawing.Size(123, 22); this.SgbBsnesMenuItem.Text = "BSNES"; this.SgbBsnesMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); // // SgbSameBoyMenuItem // this.SgbSameBoyMenuItem.Name = "SgbSameBoyMenuItem"; - this.SgbSameBoyMenuItem.Size = new System.Drawing.Size(152, 22); + this.SgbSameBoyMenuItem.Size = new System.Drawing.Size(123, 22); this.SgbSameBoyMenuItem.Text = "SameBoy"; this.SgbSameBoyMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); - // - // GBGambatteMenuItem - // - this.GBGambatteMenuItem.Name = "GBGambatteMenuItem"; - this.GBGambatteMenuItem.Size = new System.Drawing.Size(152, 22); - this.GBGambatteMenuItem.Text = "Gambatte"; - this.GBGambatteMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); - // - // GBGBHawkMenuItem - // - this.GBGBHawkMenuItem.Name = "GBGBHawkMenuItem"; - this.GBGBHawkMenuItem.Size = new System.Drawing.Size(152, 22); - this.GBGBHawkMenuItem.Text = "GBHawk"; - this.GBGBHawkMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); - // - // GBInSGBMenuItem - // - this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; + // + // GBCoreSubmenu + // + this.GBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.GBGambatteMenuItem, + this.GBGBHawkMenuItem}); + this.GBCoreSubmenu.Name = "GBCoreSubmenu"; + this.GBCoreSubmenu.Size = new System.Drawing.Size(239, 22); + this.GBCoreSubmenu.Text = "GB"; + this.GBCoreSubmenu.DropDownOpened += new System.EventHandler(this.GBCoreSubmenu_DropDownOpened); + // + // GBGambatteMenuItem + // + this.GBGambatteMenuItem.Name = "GBGambatteMenuItem"; + this.GBGambatteMenuItem.Size = new System.Drawing.Size(126, 22); + this.GBGambatteMenuItem.Text = "Gambatte"; + this.GBGambatteMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBGBHawkMenuItem + // + this.GBGBHawkMenuItem.Name = "GBGBHawkMenuItem"; + this.GBGBHawkMenuItem.Size = new System.Drawing.Size(126, 22); + this.GBGBHawkMenuItem.Text = "GBHawk"; + this.GBGBHawkMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBInSGBMenuItem + // + this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; this.GBInSGBMenuItem.Size = new System.Drawing.Size(239, 22); this.GBInSGBMenuItem.Text = "GB in SGB"; this.GBInSGBMenuItem.Click += new System.EventHandler(this.GbInSgbMenuItem_Click); @@ -2051,7 +2048,7 @@ this.batchRunnerToolStripMenuItem, this.ExperimentalToolsSubMenu}); this.ToolsSubMenu.Name = "ToolsSubMenu"; - this.ToolsSubMenu.Size = new System.Drawing.Size(47, 19); + this.ToolsSubMenu.Size = new System.Drawing.Size(48, 19); this.ToolsSubMenu.Text = "&Tools"; this.ToolsSubMenu.DropDownOpened += new System.EventHandler(this.ToolsSubMenu_DropDownOpened); // @@ -2440,7 +2437,7 @@ // this.PceControllerSettingsMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.GameController; this.PceControllerSettingsMenuItem.Name = "PceControllerSettingsMenuItem"; - this.PceControllerSettingsMenuItem.Size = new System.Drawing.Size(258, 22); + this.PceControllerSettingsMenuItem.Size = new System.Drawing.Size(259, 22); this.PceControllerSettingsMenuItem.Text = "Controller Settings"; this.PceControllerSettingsMenuItem.Click += new System.EventHandler(this.PceControllerSettingsMenuItem_Click); // @@ -2448,59 +2445,59 @@ // this.PCEGraphicsSettingsMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.tvIcon; this.PCEGraphicsSettingsMenuItem.Name = "PCEGraphicsSettingsMenuItem"; - this.PCEGraphicsSettingsMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEGraphicsSettingsMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEGraphicsSettingsMenuItem.Text = "Graphics Settings"; this.PCEGraphicsSettingsMenuItem.Click += new System.EventHandler(this.PceGraphicsSettingsMenuItem_Click); // // toolStripSeparator32 // this.toolStripSeparator32.Name = "toolStripSeparator32"; - this.toolStripSeparator32.Size = new System.Drawing.Size(255, 6); + this.toolStripSeparator32.Size = new System.Drawing.Size(256, 6); // // PCEBGViewerMenuItem // this.PCEBGViewerMenuItem.Name = "PCEBGViewerMenuItem"; - this.PCEBGViewerMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEBGViewerMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEBGViewerMenuItem.Text = "&BG Viewer"; this.PCEBGViewerMenuItem.Click += new System.EventHandler(this.PceBgViewerMenuItem_Click); // // PCEtileViewerToolStripMenuItem // this.PCEtileViewerToolStripMenuItem.Name = "PCEtileViewerToolStripMenuItem"; - this.PCEtileViewerToolStripMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEtileViewerToolStripMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEtileViewerToolStripMenuItem.Text = "&Tile Viewer"; this.PCEtileViewerToolStripMenuItem.Click += new System.EventHandler(this.PceTileViewerMenuItem_Click); // // PceSoundDebuggerToolStripMenuItem // this.PceSoundDebuggerToolStripMenuItem.Name = "PceSoundDebuggerToolStripMenuItem"; - this.PceSoundDebuggerToolStripMenuItem.Size = new System.Drawing.Size(258, 22); + this.PceSoundDebuggerToolStripMenuItem.Size = new System.Drawing.Size(259, 22); this.PceSoundDebuggerToolStripMenuItem.Text = "&Sound Debugger"; this.PceSoundDebuggerToolStripMenuItem.Click += new System.EventHandler(this.PceSoundDebuggerMenuItem_Click); // // toolStripSeparator25 // this.toolStripSeparator25.Name = "toolStripSeparator25"; - this.toolStripSeparator25.Size = new System.Drawing.Size(255, 6); + this.toolStripSeparator25.Size = new System.Drawing.Size(256, 6); // // PCEAlwaysPerformSpriteLimitMenuItem // this.PCEAlwaysPerformSpriteLimitMenuItem.Name = "PCEAlwaysPerformSpriteLimitMenuItem"; - this.PCEAlwaysPerformSpriteLimitMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEAlwaysPerformSpriteLimitMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEAlwaysPerformSpriteLimitMenuItem.Text = "Always Perform Sprite Limit"; this.PCEAlwaysPerformSpriteLimitMenuItem.Click += new System.EventHandler(this.PCEAlwaysPerformSpriteLimitMenuItem_Click); // // PCEAlwaysEqualizeVolumesMenuItem // this.PCEAlwaysEqualizeVolumesMenuItem.Name = "PCEAlwaysEqualizeVolumesMenuItem"; - this.PCEAlwaysEqualizeVolumesMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEAlwaysEqualizeVolumesMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEAlwaysEqualizeVolumesMenuItem.Text = "Always Equalize Volumes (PCE-CD)"; this.PCEAlwaysEqualizeVolumesMenuItem.Click += new System.EventHandler(this.PCEAlwaysEqualizeVolumesMenuItem_Click); // // PCEArcadeCardRewindEnableMenuItem // this.PCEArcadeCardRewindEnableMenuItem.Name = "PCEArcadeCardRewindEnableMenuItem"; - this.PCEArcadeCardRewindEnableMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEArcadeCardRewindEnableMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEArcadeCardRewindEnableMenuItem.Text = "Arcade Card Rewind-Enable Hack"; this.PCEArcadeCardRewindEnableMenuItem.Click += new System.EventHandler(this.PCEArcadeCardRewindEnableMenuItem_Click); // @@ -2510,7 +2507,7 @@ this.SMSregionToolStripMenuItem, this.SMSdisplayToolStripMenuItem, this.SMSControllerToolStripMenuItem, - this.SMStoolStripMenuItem2, + this.SMStoolStripMenuItem2, this.SMSenableBIOSToolStripMenuItem, this.SMSEnableFMChipMenuItem, this.SMSOverclockMenuItem, @@ -2536,7 +2533,7 @@ this.SMSregionJapanToolStripMenuItem, this.SMSregionAutoToolStripMenuItem}); this.SMSregionToolStripMenuItem.Name = "SMSregionToolStripMenuItem"; - this.SMSregionToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSregionToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSregionToolStripMenuItem.Text = "Region"; // // SMSregionExportToolStripMenuItem @@ -2567,7 +2564,7 @@ this.SMSdisplayPalToolStripMenuItem, this.SMSdisplayAutoToolStripMenuItem}); this.SMSdisplayToolStripMenuItem.Name = "SMSdisplayToolStripMenuItem"; - this.SMSdisplayToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSdisplayToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSdisplayToolStripMenuItem.Text = "Display Type"; // // SMSdisplayNtscToolStripMenuItem @@ -2577,51 +2574,9 @@ this.SMSdisplayNtscToolStripMenuItem.Text = "NTSC"; this.SMSdisplayNtscToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayNTSC_Click); // - // SMSControllerToolStripMenuItem + // SMSdisplayPalToolStripMenuItem // - this.SMSControllerToolStripMenuItem.Name = "SMSControllerToolStripMenuItem"; - this.SMSControllerToolStripMenuItem.Text = "&Controller Type"; - this.SMSControllerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.SMSControllerStandardToolStripMenuItem, - this.SMSControllerPaddleToolStripMenuItem, - this.SMSControllerLightPhaserToolStripMenuItem, - this.SMSControllerSportsPadToolStripMenuItem, - this.SMSControllerKeyboardToolStripMenuItem}); - // - // SMSControllerStandardToolStripMenuItem - // - this.SMSControllerStandardToolStripMenuItem.Name = "SMSControllerStandardToolStripMenuItem"; - this.SMSControllerStandardToolStripMenuItem.Text = "Standard"; - this.SMSControllerStandardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerStandardToolStripMenuItem_Click); - // - // SMSControllerPaddleToolStripMenuItem - // - this.SMSControllerPaddleToolStripMenuItem.Name = "SMSControllerPaddleToolStripMenuItem"; - this.SMSControllerPaddleToolStripMenuItem.Text = "Paddle"; - this.SMSControllerPaddleToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerPaddleToolStripMenuItem_Click); - // - // SMSControllerLightPhaserToolStripMenuItem - // - this.SMSControllerLightPhaserToolStripMenuItem.Name = "SMSControllerLightPhaserToolStripMenuItem"; - this.SMSControllerLightPhaserToolStripMenuItem.Text = "Light Phaser"; - this.SMSControllerLightPhaserToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerLightPhaserToolStripMenuItem_Click); - // - // SMSControllerSportsPadToolStripMenuItem - // - this.SMSControllerSportsPadToolStripMenuItem.Name = "SMSControllerSportsPadToolStripMenuItem"; - this.SMSControllerSportsPadToolStripMenuItem.Text = "Sports Pad"; - this.SMSControllerSportsPadToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerSportsPadToolStripMenuItem_Click); - // - // SMSControllerKeyboardToolStripMenuItem - // - this.SMSControllerKeyboardToolStripMenuItem.Name = "SMSControllerKeyboardToolStripMenuItem"; - this.SMSControllerKeyboardToolStripMenuItem.Text = "Keyboard"; - this.SMSControllerKeyboardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerKeyboardToolStripMenuItem_Click); - - // - // SMSdisplayPalToolStripMenuItem - // - this.SMSdisplayPalToolStripMenuItem.Name = "SMSdisplayPalToolStripMenuItem"; + this.SMSdisplayPalToolStripMenuItem.Name = "SMSdisplayPalToolStripMenuItem"; this.SMSdisplayPalToolStripMenuItem.Size = new System.Drawing.Size(104, 22); this.SMSdisplayPalToolStripMenuItem.Text = "PAL"; this.SMSdisplayPalToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayPAL_Click); @@ -2633,97 +2588,144 @@ this.SMSdisplayAutoToolStripMenuItem.Text = "Auto"; this.SMSdisplayAutoToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayAuto_Click); // + // SMSControllerToolStripMenuItem + // + this.SMSControllerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.SMSControllerStandardToolStripMenuItem, + this.SMSControllerPaddleToolStripMenuItem, + this.SMSControllerLightPhaserToolStripMenuItem, + this.SMSControllerSportsPadToolStripMenuItem, + this.SMSControllerKeyboardToolStripMenuItem}); + this.SMSControllerToolStripMenuItem.Name = "SMSControllerToolStripMenuItem"; + this.SMSControllerToolStripMenuItem.Size = new System.Drawing.Size(278, 22); + this.SMSControllerToolStripMenuItem.Text = "&Controller Type"; + // + // SMSControllerStandardToolStripMenuItem + // + this.SMSControllerStandardToolStripMenuItem.Name = "SMSControllerStandardToolStripMenuItem"; + this.SMSControllerStandardToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerStandardToolStripMenuItem.Text = "Standard"; + this.SMSControllerStandardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerStandardToolStripMenuItem_Click); + // + // SMSControllerPaddleToolStripMenuItem + // + this.SMSControllerPaddleToolStripMenuItem.Name = "SMSControllerPaddleToolStripMenuItem"; + this.SMSControllerPaddleToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerPaddleToolStripMenuItem.Text = "Paddle"; + this.SMSControllerPaddleToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerPaddleToolStripMenuItem_Click); + // + // SMSControllerLightPhaserToolStripMenuItem + // + this.SMSControllerLightPhaserToolStripMenuItem.Name = "SMSControllerLightPhaserToolStripMenuItem"; + this.SMSControllerLightPhaserToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerLightPhaserToolStripMenuItem.Text = "Light Phaser"; + this.SMSControllerLightPhaserToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerLightPhaserToolStripMenuItem_Click); + // + // SMSControllerSportsPadToolStripMenuItem + // + this.SMSControllerSportsPadToolStripMenuItem.Name = "SMSControllerSportsPadToolStripMenuItem"; + this.SMSControllerSportsPadToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerSportsPadToolStripMenuItem.Text = "Sports Pad"; + this.SMSControllerSportsPadToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerSportsPadToolStripMenuItem_Click); + // + // SMSControllerKeyboardToolStripMenuItem + // + this.SMSControllerKeyboardToolStripMenuItem.Name = "SMSControllerKeyboardToolStripMenuItem"; + this.SMSControllerKeyboardToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerKeyboardToolStripMenuItem.Text = "Keyboard"; + this.SMSControllerKeyboardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerKeyboardToolStripMenuItem_Click); + // // SMStoolStripMenuItem2 // this.SMStoolStripMenuItem2.Name = "SMStoolStripMenuItem2"; - this.SMStoolStripMenuItem2.Size = new System.Drawing.Size(274, 6); + this.SMStoolStripMenuItem2.Size = new System.Drawing.Size(275, 6); // // SMSenableBIOSToolStripMenuItem // this.SMSenableBIOSToolStripMenuItem.Name = "SMSenableBIOSToolStripMenuItem"; - this.SMSenableBIOSToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSenableBIOSToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSenableBIOSToolStripMenuItem.Text = "Enable BIOS (Must be Enabled for TAS)"; this.SMSenableBIOSToolStripMenuItem.Click += new System.EventHandler(this.SmsBiosMenuItem_Click); // // SMSEnableFMChipMenuItem // this.SMSEnableFMChipMenuItem.Name = "SMSEnableFMChipMenuItem"; - this.SMSEnableFMChipMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSEnableFMChipMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSEnableFMChipMenuItem.Text = "&Enable FM Chip"; this.SMSEnableFMChipMenuItem.Click += new System.EventHandler(this.SmsEnableFmChipMenuItem_Click); // // SMSOverclockMenuItem // this.SMSOverclockMenuItem.Name = "SMSOverclockMenuItem"; - this.SMSOverclockMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSOverclockMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSOverclockMenuItem.Text = "&Overclock when Known Safe"; this.SMSOverclockMenuItem.Click += new System.EventHandler(this.SMSOverclockMenuItem_Click); // // SMSForceStereoMenuItem // this.SMSForceStereoMenuItem.Name = "SMSForceStereoMenuItem"; - this.SMSForceStereoMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSForceStereoMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSForceStereoMenuItem.Text = "&Force Stereo Separation"; this.SMSForceStereoMenuItem.Click += new System.EventHandler(this.SMSForceStereoMenuItem_Click); // // SMSSpriteLimitMenuItem // this.SMSSpriteLimitMenuItem.Name = "SMSSpriteLimitMenuItem"; - this.SMSSpriteLimitMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSSpriteLimitMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSSpriteLimitMenuItem.Text = "Sprite &Limit"; this.SMSSpriteLimitMenuItem.Click += new System.EventHandler(this.SMSSpriteLimitMenuItem_Click); // // SMSDisplayOverscanMenuItem // this.SMSDisplayOverscanMenuItem.Name = "SMSDisplayOverscanMenuItem"; - this.SMSDisplayOverscanMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSDisplayOverscanMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSDisplayOverscanMenuItem.Text = "Display Overscan"; this.SMSDisplayOverscanMenuItem.Click += new System.EventHandler(this.SMSDisplayOverscanMenuItem_Click); // // SMSFix3DGameDisplayToolStripMenuItem // this.SMSFix3DGameDisplayToolStripMenuItem.Name = "SMSFix3DGameDisplayToolStripMenuItem"; - this.SMSFix3DGameDisplayToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSFix3DGameDisplayToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSFix3DGameDisplayToolStripMenuItem.Text = "Fix 3D Game Display"; this.SMSFix3DGameDisplayToolStripMenuItem.Click += new System.EventHandler(this.SMSFix3DDisplayMenuItem_Click); // // ShowClippedRegionsMenuItem // this.ShowClippedRegionsMenuItem.Name = "ShowClippedRegionsMenuItem"; - this.ShowClippedRegionsMenuItem.Size = new System.Drawing.Size(277, 22); + this.ShowClippedRegionsMenuItem.Size = new System.Drawing.Size(278, 22); this.ShowClippedRegionsMenuItem.Text = "&Show Clipped Regions"; this.ShowClippedRegionsMenuItem.Click += new System.EventHandler(this.ShowClippedRegionsMenuItem_Click); // // HighlightActiveDisplayRegionMenuItem // this.HighlightActiveDisplayRegionMenuItem.Name = "HighlightActiveDisplayRegionMenuItem"; - this.HighlightActiveDisplayRegionMenuItem.Size = new System.Drawing.Size(277, 22); + this.HighlightActiveDisplayRegionMenuItem.Size = new System.Drawing.Size(278, 22); this.HighlightActiveDisplayRegionMenuItem.Text = "&Highlight Active Display Region"; this.HighlightActiveDisplayRegionMenuItem.Click += new System.EventHandler(this.HighlightActiveDisplayRegionMenuItem_Click); // // SMSGraphicsSettingsMenuItem // this.SMSGraphicsSettingsMenuItem.Name = "SMSGraphicsSettingsMenuItem"; - this.SMSGraphicsSettingsMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSGraphicsSettingsMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSGraphicsSettingsMenuItem.Text = "&Graphics Settings..."; this.SMSGraphicsSettingsMenuItem.Click += new System.EventHandler(this.SMSGraphicsSettingsMenuItem_Click); // // toolStripSeparator24 // this.toolStripSeparator24.Name = "toolStripSeparator24"; - this.toolStripSeparator24.Size = new System.Drawing.Size(274, 6); + this.toolStripSeparator24.Size = new System.Drawing.Size(275, 6); // // SMSVDPViewerToolStripMenuItem // this.SMSVDPViewerToolStripMenuItem.Name = "SMSVDPViewerToolStripMenuItem"; - this.SMSVDPViewerToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSVDPViewerToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSVDPViewerToolStripMenuItem.Text = "&VDP Viewer"; this.SMSVDPViewerToolStripMenuItem.Click += new System.EventHandler(this.SmsVdpViewerMenuItem_Click); // // GGGameGenieMenuItem // this.GGGameGenieMenuItem.Name = "GGGameGenieMenuItem"; - this.GGGameGenieMenuItem.Size = new System.Drawing.Size(277, 22); + this.GGGameGenieMenuItem.Size = new System.Drawing.Size(278, 22); this.GGGameGenieMenuItem.Text = "&Game Genie Encoder/Decoder"; this.GGGameGenieMenuItem.Click += new System.EventHandler(this.GGGameGenieMenuItem_Click); // @@ -2795,7 +2797,7 @@ // this.A7800SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.A7800ControllerSettingsMenuItem, - this.A7800FilterSettingsMenuItem}); + this.A7800FilterSettingsMenuItem}); this.A7800SubMenu.Name = "A7800SubMenu"; this.A7800SubMenu.Size = new System.Drawing.Size(51, 19); this.A7800SubMenu.Text = "&A7800"; @@ -2804,26 +2806,26 @@ // A7800ControllerSettingsMenuItem // this.A7800ControllerSettingsMenuItem.Name = "A7800ControllerSettingsMenuItem"; - this.A7800ControllerSettingsMenuItem.Size = new System.Drawing.Size(125, 22); + this.A7800ControllerSettingsMenuItem.Size = new System.Drawing.Size(172, 22); this.A7800ControllerSettingsMenuItem.Text = "Controller Settings"; this.A7800ControllerSettingsMenuItem.Click += new System.EventHandler(this.A7800ControllerSettingsToolStripMenuItem_Click); - // - // A7800FilterSettingsMenuItem - // - this.A7800FilterSettingsMenuItem.Name = "A7800FilterSettingsMenuItem"; - this.A7800FilterSettingsMenuItem.Size = new System.Drawing.Size(125, 22); - this.A7800FilterSettingsMenuItem.Text = "Filter Settings"; - this.A7800FilterSettingsMenuItem.Click += new System.EventHandler(this.A7800FilterSettingsToolStripMenuItem_Click); - // - // GBSubMenu - // - this.GBSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + // + // A7800FilterSettingsMenuItem + // + this.A7800FilterSettingsMenuItem.Name = "A7800FilterSettingsMenuItem"; + this.A7800FilterSettingsMenuItem.Size = new System.Drawing.Size(172, 22); + this.A7800FilterSettingsMenuItem.Text = "Filter Settings"; + this.A7800FilterSettingsMenuItem.Click += new System.EventHandler(this.A7800FilterSettingsToolStripMenuItem_Click); + // + // GBSubMenu + // + this.GBSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.GBcoreSettingsToolStripMenuItem, this.LoadGBInSGBMenuItem, this.toolStripSeparator28, this.GBGPUViewerMenuItem, this.GBGameGenieMenuItem, - this.GBPrinterViewerMenuItem}); + this.GBPrinterViewerMenuItem}); this.GBSubMenu.Name = "GBSubMenu"; this.GBSubMenu.Size = new System.Drawing.Size(34, 19); this.GBSubMenu.Text = "&GB"; @@ -2861,17 +2863,17 @@ this.GBGameGenieMenuItem.Size = new System.Drawing.Size(233, 22); this.GBGameGenieMenuItem.Text = "&Game Genie Encoder/Decoder"; this.GBGameGenieMenuItem.Click += new System.EventHandler(this.GBGameGenieMenuItem_Click); - // - // GBPrinterViewerMenuItem - // - this.GBPrinterViewerMenuItem.Name = "GBPrinterViewerMenuItem"; - this.GBPrinterViewerMenuItem.Size = new System.Drawing.Size(233, 22); - this.GBPrinterViewerMenuItem.Text = "&Printer Viewer"; - this.GBPrinterViewerMenuItem.Click += new System.EventHandler(this.GBPrinterViewerMenuItem_Click); - // - // GBASubMenu - // - this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + // + // GBPrinterViewerMenuItem + // + this.GBPrinterViewerMenuItem.Name = "GBPrinterViewerMenuItem"; + this.GBPrinterViewerMenuItem.Size = new System.Drawing.Size(233, 22); + this.GBPrinterViewerMenuItem.Text = "&Printer Viewer"; + this.GBPrinterViewerMenuItem.Click += new System.EventHandler(this.GBPrinterViewerMenuItem_Click); + // + // GBASubMenu + // + this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.GBACoreSelectionSubMenu, this.GBAcoresettingsToolStripMenuItem1, this.toolStripSeparator33, @@ -3309,7 +3311,7 @@ // preferencesToolStripMenuItem1 // this.preferencesToolStripMenuItem1.Name = "preferencesToolStripMenuItem1"; - this.preferencesToolStripMenuItem1.Size = new System.Drawing.Size(144, 22); + this.preferencesToolStripMenuItem1.Size = new System.Drawing.Size(152, 22); this.preferencesToolStripMenuItem1.Text = "Preferences..."; this.preferencesToolStripMenuItem1.Click += new System.EventHandler(this.preferencesToolStripMenuItem1_Click); // @@ -3324,7 +3326,7 @@ // preferencesToolStripMenuItem2 // this.preferencesToolStripMenuItem2.Name = "preferencesToolStripMenuItem2"; - this.preferencesToolStripMenuItem2.Size = new System.Drawing.Size(144, 22); + this.preferencesToolStripMenuItem2.Size = new System.Drawing.Size(152, 22); this.preferencesToolStripMenuItem2.Text = "Preferences..."; this.preferencesToolStripMenuItem2.Click += new System.EventHandler(this.preferencesToolStripMenuItem2_Click); // @@ -3372,6 +3374,21 @@ this.AboutMenuItem.Text = "&About"; this.AboutMenuItem.Click += new System.EventHandler(this.AboutMenuItem_Click); // + // zXSpectrumToolStripMenuItem + // + this.zXSpectrumToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.preferencesToolStripMenuItem4}); + this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; + this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); + this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; + this.zXSpectrumToolStripMenuItem.DropDownOpened += new System.EventHandler(this.zXSpectrumToolStripMenuItem_DropDownOpened); + // + // Atari7800HawkCoreMenuItem + // + this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem"; + this.Atari7800HawkCoreMenuItem.Size = new System.Drawing.Size(153, 22); + this.Atari7800HawkCoreMenuItem.Text = "Atari7800Hawk"; + // // MainStatusBar // this.MainStatusBar.ClickThrough = true; @@ -3992,6 +4009,13 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // + // preferencesToolStripMenuItem4 + // + this.preferencesToolStripMenuItem4.Name = "preferencesToolStripMenuItem4"; + this.preferencesToolStripMenuItem4.Size = new System.Drawing.Size(152, 22); + this.preferencesToolStripMenuItem4.Text = "Preferences"; + this.preferencesToolStripMenuItem4.Click += new System.EventHandler(this.preferencesToolStripMenuItem4_Click); + // // MainForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -4454,5 +4478,7 @@ private System.Windows.Forms.ToolStripMenuItem SMSControllerLightPhaserToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem SMSControllerSportsPadToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem SMSControllerKeyboardToolStripMenuItem; - } + private System.Windows.Forms.ToolStripMenuItem zXSpectrumToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem4; + } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 3eb36838f1..7d8a4a3829 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2435,11 +2435,25 @@ namespace BizHawk.Client.EmuHawk new IntvControllerSettings().ShowDialog(); } - #endregion + #endregion - #region Help + #region ZXSpectrum - private void HelpSubMenu_DropDownOpened(object sender, EventArgs e) + private void zXSpectrumToolStripMenuItem_DropDownOpened(object sender, EventArgs e) + { + + } + + private void preferencesToolStripMenuItem4_Click(object sender, EventArgs e) + { + GenericCoreConfig.DoDialog(this, "ZXSpectrum Settings"); + } + + #endregion + + #region Help + + private void HelpSubMenu_DropDownOpened(object sender, EventArgs e) { FeaturesMenuItem.Visible = VersionInfo.DeveloperBuild; } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index f52e3a64d5..92dee71736 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -1719,6 +1719,7 @@ namespace BizHawk.Client.EmuHawk sNESToolStripMenuItem.Visible = false; neoGeoPocketToolStripMenuItem.Visible = false; pCFXToolStripMenuItem.Visible = false; + zXSpectrumToolStripMenuItem.Visible = false; switch (system) { @@ -1816,6 +1817,9 @@ namespace BizHawk.Client.EmuHawk case "PCFX": pCFXToolStripMenuItem.Visible = true; break; + case "ZXSpectrum": + zXSpectrumToolStripMenuItem.Visible = true; + break; } } @@ -4295,7 +4299,9 @@ namespace BizHawk.Client.EmuHawk GenericCoreConfig.DoDialog(this, "PC-FX Settings"); } - private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording) + + + private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording) { var isRewinding = false; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 33dddae230..16643c8b84 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -30,24 +30,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public bool PutSyncSettings(ZXSpectrumSyncSettings o) { + bool ret = ZXSpectrumSyncSettings.NeedsReboot(SyncSettings, o); SyncSettings = o; - return false; + return ret; } public class ZXSpectrumSettings { - [DisplayName("Spectrum model")] - [Description("The model of spectrum to be emulated")] - [DefaultValue(MachineType.ZXSpectrum48)] - public MachineType MachineType { get; set; } - - [DisplayName("Border type")] - [Description("Select how to show the border area")] - [DefaultValue(BorderType.Full)] - public BorderType BorderType { get; set; } - + [DisplayName("Auto-load tape")] + [Description("Auto or manual tape operation")] + [DefaultValue(true)] + public bool AutoLoadTape { get; set; } public ZXSpectrumSettings Clone() @@ -63,6 +58,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class ZXSpectrumSyncSettings { + [DisplayName("Spectrum model")] + [Description("The model of spectrum to be emulated")] + [DefaultValue(MachineType.ZXSpectrum48)] + public MachineType MachineType { get; set; } + + [DisplayName("Border type")] + [Description("Select how to show the border area")] + [DefaultValue(BorderType.Full)] + public BorderType BorderType { get; set; } + [DisplayName("Tape Load Speed")] [Description("Select how fast the spectrum loads the game from tape")] [DefaultValue(TapeLoadSpeed.Accurate)] @@ -72,6 +77,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { return (ZXSpectrumSyncSettings)MemberwiseClone(); } + + public ZXSpectrumSyncSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public static bool NeedsReboot(ZXSpectrumSyncSettings x, ZXSpectrumSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 9add8bdeec..083da9edf6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -34,11 +34,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _file = file; - switch (Settings.MachineType) + switch (SyncSettings.MachineType) { case MachineType.ZXSpectrum48: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum48, Settings.BorderType, SyncSettings.TapeLoadSpeed, _file); + Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); break; default: throw new InvalidOperationException("Machine not yet emulated"); From 30019d68fc99925ba5aab7b4e86e0bfe8e9ed3e5 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 5 Dec 2017 10:02:57 +0000 Subject: [PATCH 021/339] Started Spectrum128 implementation --- .../Database/FirmwareDatabase.cs | 7 +- .../BizHawk.Emulation.Cores.csproj | 5 + .../SinclairSpectrum/Machine/MachineType.cs | 7 +- .../Machine/SpectrumBase.Memory.cs | 100 ++---- .../Machine/SpectrumBase.Port.cs | 140 +-------- .../Machine/SpectrumBase.Screen.cs | 3 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 8 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 284 ++++++++++++++++++ .../Machine/ZXSpectrum128K/ZX128.Port.cs | 188 ++++++++++++ .../Machine/ZXSpectrum128K/ZX128.cs | 57 ++++ .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 149 +++++++++ .../Machine/ZXSpectrum48K/ZX48.Port.cs | 161 ++++++++++ .../Machine/ZXSpectrum48K/ZX48.cs | 156 +--------- .../Computers/SinclairSpectrum/RomData.cs | 8 + .../Computers/SinclairSpectrum/ZXSpectrum.cs | 10 + 15 files changed, 905 insertions(+), 378 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 7d8eb5fe63..789c01138f 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -52,10 +52,11 @@ namespace BizHawk.Emulation.Common // ZX Spectrum FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); + FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM"); - // for saturn, we think any bios region can pretty much run any iso - // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region - var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); + // for saturn, we think any bios region can pretty much run any iso + // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region + var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); var ss_100_ue = File("FAA8EA183A6D7BBE5D4E03BB1332519800D3FBC3", 524288, "saturn-1.00-(U+E).bin", "Bios v1.00 (U+E)"); var ss_100a_ue = File("3BB41FEB82838AB9A35601AC666DE5AACFD17A58", 524288, "saturn-1.00a-(U+E).bin", "Bios v1.00a (U+E)"); // ?? is this size correct? var ss_101_j = File("DF94C5B4D47EB3CC404D88B33A8FDA237EAF4720", 524288, "saturn-1.01-(J).bin", "Bios v1.01 (J)"); // ?? is this size correct? diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 13af57d15a..830c6523cf 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -276,7 +276,11 @@ + + + + @@ -1377,6 +1381,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs index 829f1a0e5b..e70dc3ea4a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -11,6 +11,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Sinclair Spectrum 48K model /// - ZXSpectrum48 + ZXSpectrum48, + + /// + /// Sinclair Spectrum 128K model + /// + ZXSpectrum128 } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 5b371bbc7f..d58ef1f6eb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -44,28 +44,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual byte ReadBus(ushort addr) + public abstract byte ReadBus(ushort addr); + + /// + /// Pushes a value onto the data bus that should be valid as long as the interrupt is true + /// + /// + /// + public virtual byte PushBus() { - throw new NotImplementedException("Must be overriden"); + return 0xFF; } - /// - /// Pushes a value onto the data bus that should be valid as long as the interrupt is true - /// - /// - /// - public virtual byte PushBus() - { - throw new NotImplementedException("Must be overriden"); - } - - /// - /// Simulates writing to the bus - /// Paging should be handled here - /// - /// - /// - public virtual void WriteBus(ushort addr, byte value) + /// + /// Simulates writing to the bus + /// Paging should be handled here + /// + /// + /// + public virtual void WriteBus(ushort addr, byte value) { throw new NotImplementedException("Must be overriden"); } @@ -76,23 +73,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual byte ReadMemory(ushort addr) - { - throw new NotImplementedException("Must be overriden"); - } - /* - /// - /// Reads a byte of data from a specified memory address - /// (with no memory contention) - /// - /// - /// - public virtual byte PeekMemory(ushort addr) - { - var data = ReadBus(addr); - return data; - } - */ + public abstract byte ReadMemory(ushort addr); /// /// Writes a byte of data to a specified memory address @@ -100,51 +81,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual void WriteMemory(ushort addr, byte value) - { - throw new NotImplementedException("Must be overriden"); - } - - /* - /// - /// Writes a byte of data to a specified memory address - /// (without contention) - /// - /// - /// - public virtual void PokeMemory(ushort addr, byte value) - { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - - WriteBus(addr, value); - } - */ - - /// - /// Fills memory from buffer - /// - /// - /// - public virtual void FillMemory(byte[] buffer, ushort startAddress) - { - //buffer?.CopyTo(RAM, startAddress); - } + public abstract void WriteMemory(ushort addr, byte value); /// /// Sets up the ROM /// /// /// - public virtual void InitROM(RomData romData) - { - RomData = romData; - // for 16/48k machines only ROM0 is used (no paging) - RomData.RomBytes?.CopyTo(ROM0, 0); - } + public abstract void InitROM(RomData romData); /// /// ULA reads the memory at the specified address @@ -154,7 +98,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual byte FetchScreenMemory(ushort addr) { - //var value = RAM0[(addr & 0x3FFF)];// + 0x4000]; var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); return value; } @@ -162,10 +105,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Helper function to refresh memory array (probably not the best way to do things) /// - public virtual void ReInitMemory() - { - throw new NotImplementedException("Must be overriden"); - } + public abstract void ReInitMemory(); /// /// Returns the memory contention value for the specified T-State (cycle) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index be31fead3f..c590901dac 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -22,150 +22,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public virtual byte ReadPort(ushort port) - { - int result = 0xFF; - - // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; - - ContendPort((ushort)port); - - // Kempston Joystick - if ((port & 0xe0) == 0 || (port & 0x20) == 0) - { - return (byte)KempstonDevice.JoyLine; - } - else if (lowBitReset) - { - // Even I/O address so get input - // The high byte indicates which half-row of keys is being polled - /* - IN: Reads keys (bit 0 to bit 4 inclusive) - 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 - 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y - 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H - 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B - */ - - if ((port & 0x8000) == 0) - result &= KeyboardDevice.KeyLine[7]; - - if ((port & 0x4000) == 0) - result &= KeyboardDevice.KeyLine[6]; - - if ((port & 0x2000) == 0) - result &= KeyboardDevice.KeyLine[5]; - - if ((port & 0x1000) == 0) - result &= KeyboardDevice.KeyLine[4]; - - if ((port & 0x800) == 0) - result &= KeyboardDevice.KeyLine[3]; - - if ((port & 0x400) == 0) - result &= KeyboardDevice.KeyLine[2]; - - if ((port & 0x200) == 0) - result &= KeyboardDevice.KeyLine[1]; - - if ((port & 0x100) == 0) - result &= KeyboardDevice.KeyLine[0]; - - result = result & 0x1f; //mask out lower 4 bits - result = result | 0xa0; //set bit 5 & 7 to 1 - - - if (TapeDevice.CurrentMode == TapeOperationMode.Load) - { - if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) - { - result &= ~(TAPE_BIT); // reset is EAR ON - } - else - { - result |= (TAPE_BIT); // set is EAR Off - } - } - else - { - if (KeyboardDevice.IsIssue2Keyboard) - { - if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - else - { - if ((LastULAOutByte & EAR_BIT) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - } - - } - else - { - // devices other than the ULA will respond here - // (e.g. the AY sound chip in a 128k spectrum - - // AY register activate - // Kemptson Mouse - - - // if unused port the floating memory bus should be returned (still todo) - } - - return (byte)result; - } + public abstract byte ReadPort(ushort port); /// /// Writes a byte of data to a specified port address /// /// /// - public virtual void WritePort(ushort port, byte value) - { - // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x01) == 0; - - ContendPort(port); - - // Only even addresses address the ULA - if (lowBitReset) - { - // store the last OUT byte - LastULAOutByte = value; - - /* - Bit 7 6 5 4 3 2 1 0 - +-------------------------------+ - | | | | E | M | Border | - +-------------------------------+ - */ - - // Border - LSB 3 bits hold the border colour - BorderColour = value & BORDER_BIT; - - // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); - - // Tape - TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); - } - } + public abstract void WritePort(ushort port, byte value); /// /// Apply I/O contention if necessary diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index 8a4bedf024..62bd4d98d4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -179,7 +179,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The time of displaying right part of the border. /// Given in Z80 clock cycles. /// - protected int BorderRightTime = 24; + protected int BorderRightTime = 24; /// /// The time used to render the nonvisible right part of the border. @@ -900,6 +900,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int VsyncNumerator { get { return 3500000; } + set { } } public int VsyncDenominator diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 7cd878d1cb..cb1c9064f9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -10,7 +10,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// * Main properties / fields / contruction* /// public abstract partial class SpectrumBase - { + { + + public bool ROMPaged { get; set; } + public bool SHADOWPaged { get; set; } + public int RAMPaged { get; set; } + public bool PagingDisabled { get; set; } + /// /// The calling ZXSpectrum class (piped in via constructor) /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs new file mode 100644 index 0000000000..4502a98a51 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128 : SpectrumBase + { + /* 128k paging controlled by writes to port 0x7ffd + * + * + + #7FFD (32765) - decoded as A15=0, A1=0 and /IORQ=0. Bits 0..5 are latched. Bits 0..2 select RAM bank in secton D. Bit 3 selects RAM bank to dispay screen (0 - RAM5, 1 - RAM7). Bit 4 selects ROM bank (0 - ROM0, 1 - ROM1). Bit 5, when set locks future writing to #7FFD port until reset. Reading #7FFD port is the same as writing #FF into it. + #BFFD (49149) - write data byte into AY-3-8912 chip. + #FFFD (65533) - select AY-3-8912 addres (D4..D7 ignored) and reading data byte. + + * 0xffff +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 0 | Bank 1 | Bank 2 | Bank 3 | Bank 4 | Bank 5 | Bank 6 | Bank 7 | + | | |(also at| | |(also at| | | + | | | 0x8000)| | | 0x4000)| | | + | | | | | | screen | | screen | + 0xc000 +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 2 | Any one of these pages may be switched in. + | | + | | + | | + 0x8000 +--------+ + | Bank 5 | + | | + | | + | screen | + 0x4000 +--------+--------+ + | ROM 0 | ROM 1 | Either ROM may be switched in. + | | | + | | | + | | | + 0x0000 +--------+--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + byte result = 0xff; + switch (divisor) + { + // ROM 0x000 + case 0: + if (!ROMPaged) + result = Memory[0][addr % 0x4000]; + else + result = Memory[1][addr % 0x4000]; + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + result = Memory[7][addr % 0x4000]; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = Memory[4][addr % 0x4000]; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = Memory[2][addr % 0x4000]; + break; + case 1: + result = Memory[3][addr % 0x4000]; + break; + case 2: + result = Memory[4][addr % 0x4000]; + break; + case 3: + result = Memory[5][addr % 0x4000]; + break; + case 4: + result = Memory[6][addr % 0x4000]; + break; + case 5: + result = Memory[7][addr % 0x4000]; + break; + case 6: + result = Memory[8][addr % 0x4000]; + break; + case 7: + result = Memory[9][addr % 0x4000]; + break; + } + break; + default: + break; + } + + return result; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + switch (divisor) + { + // ROM 0x000 + case 0: + if (!ROMPaged) + Memory[0][addr % 0x4000] = value; + else + Memory[1][addr % 0x4000] = value; + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + Memory[7][addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + Memory[4][addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + Memory[2][addr % 0x4000] = value; + break; + case 1: + Memory[3][addr % 0x4000] = value; + break; + case 2: + Memory[4][addr % 0x4000] = value; + break; + case 3: + Memory[5][addr % 0x4000] = value; + break; + case 4: + Memory[6][addr % 0x4000] = value; + break; + case 5: + Memory[7][addr % 0x4000] = value; + break; + case 6: + Memory[8][addr % 0x4000] = value; + break; + case 7: + Memory[9][addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = ROM1; + else + Memory.Add(1, ROM1); + + if (Memory.ContainsKey(2)) + Memory[2] = RAM0; + else + Memory.Add(2, RAM0); + + if (Memory.ContainsKey(3)) + Memory[3] = RAM1; + else + Memory.Add(3, RAM1); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM2; + else + Memory.Add(4, RAM2); + + if (Memory.ContainsKey(5)) + Memory[5] = RAM3; + else + Memory.Add(5, RAM3); + + if (Memory.ContainsKey(6)) + Memory[6] = RAM4; + else + Memory.Add(6, RAM4); + + if (Memory.ContainsKey(7)) + Memory[7] = RAM5; + else + Memory.Add(7, RAM5); + + if (Memory.ContainsKey(8)) + Memory[8] = RAM6; + else + Memory.Add(8, RAM6); + + if (Memory.ContainsKey(9)) + Memory[9] = RAM7; + else + Memory.Add(9, RAM7); + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // 128k uses ROM0 and ROM1 + // 128k loader is in ROM0, and fallback 48k rom is in ROM1 + for (int i = 0; i < 0x4000; i++) + { + ROM0[i] = RomData.RomBytes[i]; + ROM1[i] = RomData.RomBytes[i + 0x4000]; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs new file mode 100644 index 0000000000..d381bef130 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + int result = 0xFF; + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + + ContendPort((ushort)port); + + // Kempston Joystick + if ((port & 0xe0) == 0 || (port & 0x20) == 0) + { + return (byte)KempstonDevice.JoyLine; + } + else if (lowBitReset) + { + // Even I/O address so get input + // The high byte indicates which half-row of keys is being polled + /* + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + */ + + if ((port & 0x8000) == 0) + result &= KeyboardDevice.KeyLine[7]; + + if ((port & 0x4000) == 0) + result &= KeyboardDevice.KeyLine[6]; + + if ((port & 0x2000) == 0) + result &= KeyboardDevice.KeyLine[5]; + + if ((port & 0x1000) == 0) + result &= KeyboardDevice.KeyLine[4]; + + if ((port & 0x800) == 0) + result &= KeyboardDevice.KeyLine[3]; + + if ((port & 0x400) == 0) + result &= KeyboardDevice.KeyLine[2]; + + if ((port & 0x200) == 0) + result &= KeyboardDevice.KeyLine[1]; + + if ((port & 0x100) == 0) + result &= KeyboardDevice.KeyLine[0]; + + result = result & 0x1f; //mask out lower 4 bits + result = result | 0xa0; //set bit 5 & 7 to 1 + + + if (TapeDevice.CurrentMode == TapeOperationMode.Load) + { + if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (KeyboardDevice.IsIssue2Keyboard) + { + if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + else + { + if ((LastULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate + // Kemptson Mouse + + + // if unused port the floating memory bus should be returned (still todo) + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // paging + if (port == 0x7ffd) + { + // Bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // ROM page + if ((value & 0x10) != 0) + { + // 48k ROM + ROMPaged = true; + } + else + { + ROMPaged = false; + } + + // Bit 5 signifies that paging is disabled until next reboot + if ((value & 0x20) != 0) + PagingDisabled = true; + + + return; + } + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x01) == 0; + + ContendPort(port); + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + BorderColour = value & BORDER_BIT; + + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + + // Tape + TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs new file mode 100644 index 0000000000..93b7b58f35 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Emulation.Cores.Components.Z80A; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128 : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128(ZXSpectrum spectrum, Z80A cpu, byte[] file) + { + Spectrum = spectrum; + CPU = cpu; + + ROMPaged = false; + SHADOWPaged = false; + RAMPaged = 0; + PagingDisabled = false; + + // init addressable memory from ROM and RAM banks + ReInitMemory(); + + //RAM = new byte[0x4000 + 0xC000]; + + //DisplayLineTime = 132; + VsyncNumerator = 3546900; + + InitScreenConfig(); + InitScreen(); + + ResetULACycle(); + + BuzzerDevice = new Buzzer(this); + BuzzerDevice.Init(44100, UlaFrameCycleCount); + + KeyboardDevice = new Keyboard48(this); + KempstonDevice = new KempstonJoystick(this); + + TapeProvider = new DefaultTapeProvider(file); + + TapeDevice = new Tape(TapeProvider); + TapeDevice.Init(this); + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs new file mode 100644 index 0000000000..3cf3b09390 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX48 : SpectrumBase + { + /* 48K Spectrum has NO memory paging + * + * 0xffff +--------+ + | Bank 2 | + | | + | | + | | + 0xc000 +--------+ + | Bank 1 | + | | + | | + | | + 0x8000 +--------+ + | Bank 0 | + | | + | | + | screen | + 0x4000 +--------+ + | ROM 0 | + | | + | | + | | + 0x0000 +--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + return bank[index]; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + // paging logic goes here + + var bank = Memory[divisor]; + var index = addr % 0x4000; + bank[index] = value; + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = RAM0; + else + Memory.Add(1, RAM0); + + if (Memory.ContainsKey(2)) + Memory[2] = RAM1; + else + Memory.Add(2, RAM1); + + if (Memory.ContainsKey(3)) + Memory[3] = RAM2; + else + Memory.Add(3, RAM2); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM3; + else + Memory.Add(4, RAM3); + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // for 16/48k machines only ROM0 is used (no paging) + RomData.RomBytes?.CopyTo(ROM0, 0); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs new file mode 100644 index 0000000000..2a0080e1c8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX48 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + int result = 0xFF; + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + + ContendPort((ushort)port); + + // Kempston Joystick + if ((port & 0xe0) == 0 || (port & 0x20) == 0) + { + return (byte)KempstonDevice.JoyLine; + } + else if (lowBitReset) + { + // Even I/O address so get input + // The high byte indicates which half-row of keys is being polled + /* + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + */ + + if ((port & 0x8000) == 0) + result &= KeyboardDevice.KeyLine[7]; + + if ((port & 0x4000) == 0) + result &= KeyboardDevice.KeyLine[6]; + + if ((port & 0x2000) == 0) + result &= KeyboardDevice.KeyLine[5]; + + if ((port & 0x1000) == 0) + result &= KeyboardDevice.KeyLine[4]; + + if ((port & 0x800) == 0) + result &= KeyboardDevice.KeyLine[3]; + + if ((port & 0x400) == 0) + result &= KeyboardDevice.KeyLine[2]; + + if ((port & 0x200) == 0) + result &= KeyboardDevice.KeyLine[1]; + + if ((port & 0x100) == 0) + result &= KeyboardDevice.KeyLine[0]; + + result = result & 0x1f; //mask out lower 4 bits + result = result | 0xa0; //set bit 5 & 7 to 1 + + + if (TapeDevice.CurrentMode == TapeOperationMode.Load) + { + if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (KeyboardDevice.IsIssue2Keyboard) + { + if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + else + { + if ((LastULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate + // Kemptson Mouse + + + // if unused port the floating memory bus should be returned (still todo) + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x01) == 0; + + ContendPort(port); + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + BorderColour = value & BORDER_BIT; + + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + + // Tape + TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 3deae820b1..4979071d58 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -7,7 +7,7 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { - public class ZX48 : SpectrumBase + public partial class ZX48 : SpectrumBase { #region Construction @@ -21,17 +21,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; - // init addressable memory from ROM and RAM banks - /* - Memory.Add(0, ROM0); - Memory.Add(1, RAM0); - Memory.Add(2, RAM1); - Memory.Add(3, RAM2); - */ ReInitMemory(); - - //RAM = new byte[0x4000 + 0xC000]; - + InitScreenConfig(); InitScreen(); @@ -50,148 +41,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } #endregion - - #region MemoryMapping - - /* 48K Spectrum has NO memory paging - * - * 0xffff +--------+ - | Bank 2 | - | | - | | - | | - 0xc000 +--------+ - | Bank 1 | - | | - | | - | | - 0x8000 +--------+ - | Bank 0 | - | | - | | - | screen | - 0x4000 +--------+ - | ROM 0 | - | | - | | - | | - 0x0000 +--------+ - */ - - /// - /// Simulates reading from the bus (no contention) - /// Paging should be handled here - /// - /// - /// - public override byte ReadBus(ushort addr) - { - int divisor = addr / 0x4000; - // paging logic goes here - - var bank = Memory[divisor]; - var index = addr % 0x4000; - return bank[index]; - } - - /// - /// Pushes a value onto the data bus that should be valid as long as the interrupt is true - /// - /// - /// - public override byte PushBus() - { - return 0xFF; - } - - /// - /// Simulates writing to the bus (no contention) - /// Paging should be handled here - /// - /// - /// - public override void WriteBus(ushort addr, byte value) - { - int divisor = addr / 0x4000; - // paging logic goes here - - var bank = Memory[divisor]; - var index = addr % 0x4000; - bank[index] = value; - } - - /// - /// Reads a byte of data from a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public override byte ReadMemory(ushort addr) - { - var data = ReadBus(addr); - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - return data; - } - - /// - /// Writes a byte of data to a specified memory address - /// (with memory contention if appropriate) - /// - /// - /// - public override void WriteMemory(ushort addr, byte value) - { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - else if (addr < 0xC000) - { - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - - WriteBus(addr, value); - } - - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = RAM0; - else - Memory.Add(1, RAM0); - - if (Memory.ContainsKey(2)) - Memory[2] = RAM1; - else - Memory.Add(2, RAM1); - - if (Memory.ContainsKey(3)) - Memory[3] = RAM2; - else - Memory.Add(3, RAM2); - - if (Memory.ContainsKey(4)) - Memory[4] = RAM3; - else - Memory.Add(4, RAM3); - } - - - #endregion - - } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs index 97dffe206b..0f0f12110b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs @@ -71,6 +71,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RD.LoadBytesResumeAddress = 0x05E2; RD.LoadBytesInvalidHeaderAddress = 0x05B6; break; + + case MachineType.ZXSpectrum128: + RD.SaveBytesRoutineAddress = 0x04C2; + RD.SaveBytesResumeAddress = 0x0000; + RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C; + RD.LoadBytesResumeAddress = 0x05E2; + RD.LoadBytesInvalidHeaderAddress = 0x05B6; + break; } return RD; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 083da9edf6..538b9c4cbe 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -40,6 +40,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); break; + case MachineType.ZXSpectrum128: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + break; default: throw new InvalidOperationException("Machine not yet emulated"); } @@ -110,6 +114,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var romData = RomData.InitROM(machineType, _systemRom); _machine.InitROM(romData); break; + case MachineType.ZXSpectrum128: + _machine = new ZX128(this, _cpu, file); + var _systemRom128 = GetFirmware(0x8000, "128ROM"); + var romData128 = RomData.InitROM(machineType, _systemRom128); + _machine.InitROM(romData128); + break; } } From 27ba7e0008a9692fe5ffd9ba5e15eb45a63bfdcb Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 5 Dec 2017 10:26:06 +0000 Subject: [PATCH 022/339] Started +2 implementation --- .../Database/FirmwareDatabase.cs | 1 + .../BizHawk.Emulation.Cores.csproj | 2 ++ .../SinclairSpectrum/Machine/MachineType.cs | 7 ++++- .../Machine/SpectrumBase.Screen.cs | 2 +- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 24 ++++++++++++++ .../Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs | 31 +++++++++++++++++++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 10 ++++++ 7 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 789c01138f..3d40ef5de1 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -53,6 +53,7 @@ namespace BizHawk.Emulation.Common // ZX Spectrum FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM"); + FirmwareAndOption("8CAFB292AF58617907B9E6B9093D3588A75849B8", 32768, "ZXSpectrum", "PLUS2ROM", "PLUS2.ROM", "Spectrum 128K +2 ROM"); // for saturn, we think any bios region can pretty much run any iso // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 830c6523cf..2a18cd1e56 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -276,9 +276,11 @@ + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs index e70dc3ea4a..a29b745205 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -16,6 +16,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Sinclair Spectrum 128K model /// - ZXSpectrum128 + ZXSpectrum128, + + /// + /// Sinclair Spectrum 128 + 2 model + /// + ZXSpectrum128Plus2 } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index 62bd4d98d4..3ab09352db 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -765,7 +765,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Initialises the screen configuration calculations /// - protected virtual void InitScreenConfig() + public virtual void InitScreenConfig() { ScreenLines = BorderTopLines + DisplayLines + BorderBottomLines; FirstDisplayLine = VerticalSyncLines + NonVisibleBorderTopLines + BorderTopLines; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs new file mode 100644 index 0000000000..541a370341 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128 : SpectrumBase + { + public override void InitScreenConfig() + { + + ScreenLines = BorderTopLines + DisplayLines + BorderBottomLines; + FirstDisplayLine = VerticalSyncLines + NonVisibleBorderTopLines + BorderTopLines; + LastDisplayLine = FirstDisplayLine + DisplayLines - 1; + ScreenWidth = BorderLeftPixels + DisplayWidth + BorderRightPixels; + FirstPixelCycleInLine = HorizontalBlankingTime + BorderLeftTime; + ScreenLineTime = FirstPixelCycleInLine + DisplayLineTime + BorderRightTime + NonVisibleBorderRightTime; + UlaFrameCycleCount = (FirstDisplayLine + DisplayLines + BorderBottomLines + NonVisibleBorderTopLines) * ScreenLineTime; + FirstScreenPixelCycle = (VerticalSyncLines + NonVisibleBorderTopLines) * ScreenLineTime + HorizontalBlankingTime; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs new file mode 100644 index 0000000000..faaba1791f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs @@ -0,0 +1,31 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The +2 is almost identical to the 128k from an emulation point of view + /// There are just a few small changes in the ROMs + /// + public partial class ZX128Plus2 : ZX128 + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, byte[] file) + : base(spectrum, cpu, file) + { + + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 538b9c4cbe..64ddcde13a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -44,6 +44,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); break; + case MachineType.ZXSpectrum128Plus2: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + break; default: throw new InvalidOperationException("Machine not yet emulated"); } @@ -120,6 +124,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var romData128 = RomData.InitROM(machineType, _systemRom128); _machine.InitROM(romData128); break; + case MachineType.ZXSpectrum128Plus2: + _machine = new ZX128Plus2(this, _cpu, file); + var _systemRomP2 = GetFirmware(0x8000, "PLUS2ROM"); + var romDataP2 = RomData.InitROM(machineType, _systemRomP2); + _machine.InitROM(romDataP2); + break; } } From 85d38a3379c6f61f4398f01dc78580ce29bec06c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 5 Dec 2017 10:38:51 +0000 Subject: [PATCH 023/339] template for plus3 (but not implemented yet) --- .../Database/FirmwareDatabase.cs | 1 + .../BizHawk.Emulation.Cores.csproj | 3 + .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 284 ++++++++++++++++++ .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 188 ++++++++++++ .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 56 ++++ 5 files changed, 532 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 3d40ef5de1..9d9810b5e6 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -54,6 +54,7 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM"); FirmwareAndOption("8CAFB292AF58617907B9E6B9093D3588A75849B8", 32768, "ZXSpectrum", "PLUS2ROM", "PLUS2.ROM", "Spectrum 128K +2 ROM"); + FirmwareAndOption("929BF1A5E5687EBD8D7245F9B513A596C0EC21A4", 65563, "ZXSpectrum", "PLUS3ROM", "PLUS3.ROM", "Spectrum 128K +3 ROM"); // for saturn, we think any bios region can pretty much run any iso // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 2a18cd1e56..439b004828 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -277,6 +277,9 @@ + + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs new file mode 100644 index 0000000000..76dbb31891 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128Plus3 : SpectrumBase + { + /* 128k paging controlled by writes to port 0x7ffd + * + * + + #7FFD (32765) - decoded as A15=0, A1=0 and /IORQ=0. Bits 0..5 are latched. Bits 0..2 select RAM bank in secton D. Bit 3 selects RAM bank to dispay screen (0 - RAM5, 1 - RAM7). Bit 4 selects ROM bank (0 - ROM0, 1 - ROM1). Bit 5, when set locks future writing to #7FFD port until reset. Reading #7FFD port is the same as writing #FF into it. + #BFFD (49149) - write data byte into AY-3-8912 chip. + #FFFD (65533) - select AY-3-8912 addres (D4..D7 ignored) and reading data byte. + + * 0xffff +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 0 | Bank 1 | Bank 2 | Bank 3 | Bank 4 | Bank 5 | Bank 6 | Bank 7 | + | | |(also at| | |(also at| | | + | | | 0x8000)| | | 0x4000)| | | + | | | | | | screen | | screen | + 0xc000 +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 2 | Any one of these pages may be switched in. + | | + | | + | | + 0x8000 +--------+ + | Bank 5 | + | | + | | + | screen | + 0x4000 +--------+--------+ + | ROM 0 | ROM 1 | Either ROM may be switched in. + | | | + | | | + | | | + 0x0000 +--------+--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + byte result = 0xff; + switch (divisor) + { + // ROM 0x000 + case 0: + if (!ROMPaged) + result = Memory[0][addr % 0x4000]; + else + result = Memory[1][addr % 0x4000]; + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + result = Memory[7][addr % 0x4000]; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = Memory[4][addr % 0x4000]; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = Memory[2][addr % 0x4000]; + break; + case 1: + result = Memory[3][addr % 0x4000]; + break; + case 2: + result = Memory[4][addr % 0x4000]; + break; + case 3: + result = Memory[5][addr % 0x4000]; + break; + case 4: + result = Memory[6][addr % 0x4000]; + break; + case 5: + result = Memory[7][addr % 0x4000]; + break; + case 6: + result = Memory[8][addr % 0x4000]; + break; + case 7: + result = Memory[9][addr % 0x4000]; + break; + } + break; + default: + break; + } + + return result; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + switch (divisor) + { + // ROM 0x000 + case 0: + if (!ROMPaged) + Memory[0][addr % 0x4000] = value; + else + Memory[1][addr % 0x4000] = value; + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + Memory[7][addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + Memory[4][addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + Memory[2][addr % 0x4000] = value; + break; + case 1: + Memory[3][addr % 0x4000] = value; + break; + case 2: + Memory[4][addr % 0x4000] = value; + break; + case 3: + Memory[5][addr % 0x4000] = value; + break; + case 4: + Memory[6][addr % 0x4000] = value; + break; + case 5: + Memory[7][addr % 0x4000] = value; + break; + case 6: + Memory[8][addr % 0x4000] = value; + break; + case 7: + Memory[9][addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr < 0xC000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = ROM1; + else + Memory.Add(1, ROM1); + + if (Memory.ContainsKey(2)) + Memory[2] = RAM0; + else + Memory.Add(2, RAM0); + + if (Memory.ContainsKey(3)) + Memory[3] = RAM1; + else + Memory.Add(3, RAM1); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM2; + else + Memory.Add(4, RAM2); + + if (Memory.ContainsKey(5)) + Memory[5] = RAM3; + else + Memory.Add(5, RAM3); + + if (Memory.ContainsKey(6)) + Memory[6] = RAM4; + else + Memory.Add(6, RAM4); + + if (Memory.ContainsKey(7)) + Memory[7] = RAM5; + else + Memory.Add(7, RAM5); + + if (Memory.ContainsKey(8)) + Memory[8] = RAM6; + else + Memory.Add(8, RAM6); + + if (Memory.ContainsKey(9)) + Memory[9] = RAM7; + else + Memory.Add(9, RAM7); + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // 128k uses ROM0 and ROM1 + // 128k loader is in ROM0, and fallback 48k rom is in ROM1 + for (int i = 0; i < 0x4000; i++) + { + ROM0[i] = RomData.RomBytes[i]; + ROM1[i] = RomData.RomBytes[i + 0x4000]; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs new file mode 100644 index 0000000000..35fe0c3391 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128Plus3 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + int result = 0xFF; + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + + ContendPort((ushort)port); + + // Kempston Joystick + if ((port & 0xe0) == 0 || (port & 0x20) == 0) + { + return (byte)KempstonDevice.JoyLine; + } + else if (lowBitReset) + { + // Even I/O address so get input + // The high byte indicates which half-row of keys is being polled + /* + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + */ + + if ((port & 0x8000) == 0) + result &= KeyboardDevice.KeyLine[7]; + + if ((port & 0x4000) == 0) + result &= KeyboardDevice.KeyLine[6]; + + if ((port & 0x2000) == 0) + result &= KeyboardDevice.KeyLine[5]; + + if ((port & 0x1000) == 0) + result &= KeyboardDevice.KeyLine[4]; + + if ((port & 0x800) == 0) + result &= KeyboardDevice.KeyLine[3]; + + if ((port & 0x400) == 0) + result &= KeyboardDevice.KeyLine[2]; + + if ((port & 0x200) == 0) + result &= KeyboardDevice.KeyLine[1]; + + if ((port & 0x100) == 0) + result &= KeyboardDevice.KeyLine[0]; + + result = result & 0x1f; //mask out lower 4 bits + result = result | 0xa0; //set bit 5 & 7 to 1 + + + if (TapeDevice.CurrentMode == TapeOperationMode.Load) + { + if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (KeyboardDevice.IsIssue2Keyboard) + { + if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + else + { + if ((LastULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate + // Kemptson Mouse + + + // if unused port the floating memory bus should be returned (still todo) + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // paging + if (port == 0x7ffd) + { + // Bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // ROM page + if ((value & 0x10) != 0) + { + // 48k ROM + ROMPaged = true; + } + else + { + ROMPaged = false; + } + + // Bit 5 signifies that paging is disabled until next reboot + if ((value & 0x20) != 0) + PagingDisabled = true; + + + return; + } + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x01) == 0; + + ContendPort(port); + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + BorderColour = value & BORDER_BIT; + + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + + // Tape + TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs new file mode 100644 index 0000000000..2a75a600f7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -0,0 +1,56 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128Plus3 : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, byte[] file) + { + Spectrum = spectrum; + CPU = cpu; + + ROMPaged = false; + SHADOWPaged = false; + RAMPaged = 0; + PagingDisabled = false; + + // init addressable memory from ROM and RAM banks + ReInitMemory(); + + //RAM = new byte[0x4000 + 0xC000]; + + //DisplayLineTime = 132; + VsyncNumerator = 3546900; + + InitScreenConfig(); + InitScreen(); + + ResetULACycle(); + + BuzzerDevice = new Buzzer(this); + BuzzerDevice.Init(44100, UlaFrameCycleCount); + + KeyboardDevice = new Keyboard48(this); + KempstonDevice = new KempstonJoystick(this); + + TapeProvider = new DefaultTapeProvider(file); + + TapeDevice = new Tape(TapeProvider); + TapeDevice.Init(this); + } + + #endregion + } +} From f82b1b83362d68b5ba81788da3e48679c8548afc Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 5 Dec 2017 13:08:47 +0000 Subject: [PATCH 024/339] Custom SoundProviderMixer implementation --- .../BizHawk.Emulation.Cores.csproj | 3 + .../SinclairSpectrum/Hardware/AYSound.cs | 407 ++++++++++++++++++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 10 +- .../Machine/ZXSpectrum128K/ZX128.cs | 2 + .../SinclairSpectrum/SoundProviderMixer.cs | 196 +++++++++ .../ZXSpectrum.ISoundProvider.cs | 16 + .../Computers/SinclairSpectrum/ZXSpectrum.cs | 7 +- 7 files changed, 639 insertions(+), 2 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 439b004828..8db1b196d4 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -256,6 +256,7 @@ + @@ -270,6 +271,7 @@ + @@ -307,6 +309,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs new file mode 100644 index 0000000000..3cd2c513dd --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs @@ -0,0 +1,407 @@ +using System; + +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class AYSound : ISoundProvider + { + private readonly BlipBuffer _blip = new BlipBuffer(4096); + private short[] _sampleBuffer = new short[0]; + + public AYSound() + { + _blip.SetRates(894866 / 4.0, 44100); + } + + public ushort[] Register = new ushort[16]; + + public int total_clock; // TODO: what is this used for? + + public void Reset() + { + clock_A = clock_B = clock_C = 0x1000; + noise_clock = 0x20; + + for (int i = 0; i < 16; i++) + { + Register[i] = 0x0000; + } + sync_psg_state(); + DiscardSamples(); + } + + public void DiscardSamples() + { + _blip.Clear(); + _sampleClock = 0; + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + { + throw new InvalidOperationException("Only Sync mode is supported."); + } + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + _blip.EndFrame((uint)_sampleClock); + _sampleClock = 0; + + nsamp = _blip.SamplesAvailable(); + int targetLength = nsamp * 2; + if (_sampleBuffer.Length != targetLength) + { + _sampleBuffer = new short[targetLength]; + } + + _blip.ReadSamplesLeft(_sampleBuffer, nsamp); + for (int i = 0; i < _sampleBuffer.Length; i += 2) + { + _sampleBuffer[i + 1] = _sampleBuffer[i]; + } + + samples = _sampleBuffer; + } + + public void GetSamples(short[] samples) + { + throw new Exception(); + } + + private static readonly int[] VolumeTable = + { + 0x0000, 0x0055, 0x0079, 0x00AB, 0x00F1, 0x0155, 0x01E3, 0x02AA, + 0x03C5, 0x0555, 0x078B, 0x0AAB, 0x0F16, 0x1555, 0x1E2B, 0x2AAA + }; + + private int _sampleClock; + private int _latchedSample; + + private int TotalExecutedCycles; + private int PendingCycles; + private int psg_clock; + private int sq_per_A, sq_per_B, sq_per_C; + private int clock_A, clock_B, clock_C; + private int vol_A, vol_B, vol_C; + private bool A_on, B_on, C_on; + private bool A_up, B_up, C_up; + private bool A_noise, B_noise, C_noise; + + private int env_per; + private int env_clock; + private int env_shape; + private int env_E; + private int E_up_down; + private int env_vol_A, env_vol_B, env_vol_C; + + private int noise_clock; + private int noise_per; + private int noise = 0x1; + + public Func ReadMemory; + public Func WriteMemory; + + public void SyncState(Serializer ser) + { + ser.BeginSection("PSG"); + + ser.Sync("Register", ref Register, false); + ser.Sync("Toal_executed_cycles", ref TotalExecutedCycles); + ser.Sync("Pending_Cycles", ref PendingCycles); + + ser.Sync("psg_clock", ref psg_clock); + ser.Sync("clock_A", ref clock_A); + ser.Sync("clock_B", ref clock_B); + ser.Sync("clock_C", ref clock_C); + ser.Sync("noise_clock", ref noise_clock); + ser.Sync("env_clock", ref env_clock); + ser.Sync("A_up", ref A_up); + ser.Sync("B_up", ref B_up); + ser.Sync("C_up", ref C_up); + ser.Sync("noise", ref noise); + ser.Sync("env_E", ref env_E); + ser.Sync("E_up_down", ref E_up_down); + + sync_psg_state(); + + ser.EndSection(); + } + + public ushort? ReadPSG(ushort addr, bool peek) + { + if (addr >= 0x01F0 && addr <= 0x01FF) + { + return (ushort)(Register[addr - 0x01F0]); + } + + return null; + } + + private void sync_psg_state() + { + sq_per_A = (Register[0] & 0xFF) | (((Register[4] & 0xF) << 8)); + if (sq_per_A == 0) + { + sq_per_A = 0x1000; + } + + sq_per_B = (Register[1] & 0xFF) | (((Register[5] & 0xF) << 8)); + if (sq_per_B == 0) + { + sq_per_B = 0x1000; + } + + sq_per_C = (Register[2] & 0xFF) | (((Register[6] & 0xF) << 8)); + if (sq_per_C == 0) + { + sq_per_C = 0x1000; + } + + env_per = (Register[3] & 0xFF) | (((Register[7] & 0xFF) << 8)); + if (env_per == 0) + { + env_per = 0x10000; + } + + env_per *= 2; + + A_on = Register[8].Bit(0); + B_on = Register[8].Bit(1); + C_on = Register[8].Bit(2); + A_noise = Register[8].Bit(3); + B_noise = Register[8].Bit(4); + C_noise = Register[8].Bit(5); + + noise_per = Register[9] & 0x1F; + if (noise_per == 0) + { + noise_per = 0x20; + } + + var shape_select = Register[10] & 0xF; + + if (shape_select < 4) + env_shape = 0; + else if (shape_select < 8) + env_shape = 1; + else + env_shape = 2 + (shape_select - 8); + + vol_A = Register[11] & 0xF; + env_vol_A = (Register[11] >> 4) & 0x3; + + vol_B = Register[12] & 0xF; + env_vol_B = (Register[12] >> 4) & 0x3; + + vol_C = Register[13] & 0xF; + env_vol_C = (Register[13] >> 4) & 0x3; + } + + public bool WritePSG(ushort addr, ushort value, bool poke) + { + if (addr >= 0x01F0 && addr <= 0x01FF) + { + var reg = addr - 0x01F0; + + value &= 0xFF; + + if (reg == 4 || reg == 5 || reg == 6 || reg == 10) + value &= 0xF; + + if (reg == 9) + value &= 0x1F; + + if (reg == 11 || reg == 12 || reg == 13) + value &= 0x3F; + + Register[addr - 0x01F0] = value; + + sync_psg_state(); + + if (reg == 10) + { + env_clock = env_per; + + if (env_shape == 0 || env_shape == 2 || env_shape == 3 || env_shape == 4 || env_shape == 5) + { + env_E = 15; + E_up_down = -1; + } + else + { + env_E = 0; + E_up_down = 1; + } + } + + return true; + } + + return false; + } + + public void generate_sound(int cycles_to_do) + { + // there are 4 cpu cycles for every psg cycle + bool sound_out_A; + bool sound_out_B; + bool sound_out_C; + + for (int i = 0; i < cycles_to_do; i++) + { + psg_clock++; + + if (psg_clock == 4) + { + psg_clock = 0; + + total_clock++; + + clock_A--; + clock_B--; + clock_C--; + + noise_clock--; + env_clock--; + + // clock noise + if (noise_clock == 0) + { + noise = (noise >> 1) ^ (noise.Bit(0) ? 0x10004 : 0); + noise_clock = noise_per; + } + + if (env_clock == 0) + { + env_clock = env_per; + + env_E += E_up_down; + + if (env_E == 16 || env_E == -1) + { + + // we just completed a period of the envelope, determine what to do now based on the envelope shape + if (env_shape == 0 || env_shape == 1 || env_shape == 3 || env_shape == 9) + { + E_up_down = 0; + env_E = 0; + } + else if (env_shape == 5 || env_shape == 7) + { + E_up_down = 0; + env_E = 15; + } + else if (env_shape == 4 || env_shape == 8) + { + if (env_E == 16) + { + env_E = 15; + E_up_down = -1; + } + else + { + env_E = 0; + E_up_down = 1; + } + } + else if (env_shape == 2) + { + env_E = 15; + } + else + { + env_E = 0; + } + } + } + + if (clock_A == 0) + { + A_up = !A_up; + clock_A = sq_per_A; + } + + if (clock_B == 0) + { + B_up = !B_up; + clock_B = sq_per_B; + } + + if (clock_C == 0) + { + C_up = !C_up; + clock_C = sq_per_C; + } + + + sound_out_A = (noise.Bit(0) | A_noise) & (A_on | A_up); + sound_out_B = (noise.Bit(0) | B_noise) & (B_on | B_up); + sound_out_C = (noise.Bit(0) | C_noise) & (C_on | C_up); + + // now calculate the volume of each channel and add them together + int v; + + if (env_vol_A == 0) + { + v = (short)(sound_out_A ? VolumeTable[vol_A] : 0); + } + else + { + int shift_A = 3 - env_vol_A; + if (shift_A < 0) + shift_A = 0; + v = (short)(sound_out_A ? (VolumeTable[env_E] >> shift_A) : 0); + } + + if (env_vol_B == 0) + { + v += (short)(sound_out_B ? VolumeTable[vol_B] : 0); + + } + else + { + int shift_B = 3 - env_vol_B; + if (shift_B < 0) + shift_B = 0; + v += (short)(sound_out_B ? (VolumeTable[env_E] >> shift_B) : 0); + } + + if (env_vol_C == 0) + { + v += (short)(sound_out_C ? VolumeTable[vol_C] : 0); + } + else + { + int shift_C = 3 - env_vol_C; + if (shift_C < 0) + shift_C = 0; + v += (short)(sound_out_C ? (VolumeTable[env_E] >> shift_C) : 0); + } + + if (v != _latchedSample) + { + _blip.AddDelta((uint)_sampleClock, v - _latchedSample); + _latchedSample = v; + } + + _sampleClock++; + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index cb1c9064f9..f23173a259 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -37,6 +37,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public Buzzer BuzzerDevice { get; set; } + /// + /// Device representing the AY-3-8912 chip found in the 128k and up spectrums + /// + public AYSound AYDevice { get; set; } + /// /// The spectrum keyboard /// @@ -231,9 +236,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.SyncState(ser); TapeDevice.SyncState(ser); + if (AYDevice != null) + AYDevice.SyncState(ser); + ser.EndSection(); - ReInitMemory(); + //ReInitMemory(); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 93b7b58f35..fe3f08d651 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -42,6 +42,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, UlaFrameCycleCount); + AYDevice = new AYSound(); + KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs new file mode 100644 index 0000000000..57e37f3d77 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs @@ -0,0 +1,196 @@ +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider + /// Currently only supports SyncSoundMode.Sync + /// Attached ISoundProvider sources must already be stereo 44.1khz + /// + internal sealed class SoundProviderMixer : ISoundProvider + { + private class Provider + { + public ISoundProvider SoundProvider { get; set; } + public int MaxVolume { get; set; } + public short[] Buffer { get; set; } + public int NSamp { get; set; } + } + + private readonly List SoundProviders; + + private short[] _buffer; + private int _nSamp; + + public SoundProviderMixer(params ISoundProvider[] soundProviders) + { + SoundProviders = new List(); + + foreach (var s in soundProviders) + { + SoundProviders.Add(new Provider + { + SoundProvider = s, + MaxVolume = short.MaxValue, + }); + } + + EqualizeVolumes(); + } + + public void AddSource(ISoundProvider source) + { + SoundProviders.Add(new Provider + { + SoundProvider = source, + MaxVolume = short.MaxValue + }); + + EqualizeVolumes(); + } + + public void DisableSource(ISoundProvider source) + { + var sp = SoundProviders.Where(a => a.SoundProvider == source); + if (sp.Count() == 1) + SoundProviders.Remove(sp.First()); + else if (sp.Count() > 1) + foreach (var s in sp) + SoundProviders.Remove(s); + } + + public void EqualizeVolumes() + { + if (SoundProviders.Count < 1) + return; + + int eachVolume = short.MaxValue / SoundProviders.Count; + foreach (var source in SoundProviders) + { + source.MaxVolume = eachVolume; + } + } + + #region ISoundProvider + + public bool CanProvideAsync => false; + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + foreach (var soundSource in SoundProviders) + { + soundSource.SoundProvider.DiscardSamples(); + } + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + samples = null; + nsamp = 0; + + // get samples from all the providers + foreach (var sp in SoundProviders) + { + int sampCount; + short[] samp; + sp.SoundProvider.GetSamplesSync(out samp, out sampCount); + sp.NSamp = sampCount; + sp.Buffer = samp; + } + + // are all the sample lengths the same? + var firstEntry = SoundProviders.First(); + bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp); + + if (!sameCount) + { + int divisor = 1; + int highestCount = 0; + + // get the lowest divisor of all the soundprovider nsamps + for (int d = 2; d < 999; d++) + { + bool divFound = false; + foreach (var sp in SoundProviders) + { + if (sp.NSamp > highestCount) + highestCount = sp.NSamp; + + if (sp.NSamp % d == 0) + divFound = true; + else + divFound = false; + } + + if (divFound) + { + divisor = d; + break; + } + } + + // now we have the largest current number of samples among the providers + // along with a common divisor for all of them + nsamp = highestCount * divisor; + samples = new short[nsamp * 2]; + + // take a pass at populating the samples array for each provider + foreach (var sp in SoundProviders) + { + short sectorVal = 0; + int pos = 0; + for (int i = 0; i < sp.Buffer.Length; i++) + { + if (sp.Buffer[i] > sp.MaxVolume) + sectorVal = (short)sp.MaxVolume; + else + sectorVal = sp.Buffer[i]; + + for (int s = 0; s < divisor; s++) + { + samples[pos++] += sectorVal; + } + } + } + } + else + { + nsamp = firstEntry.NSamp; + samples = new short[nsamp * 2]; + + for (int i = 0; i < samples.Length; i++) + { + short sectorVal = 0; + foreach (var sp in SoundProviders) + { + if (sp.Buffer[i] > sp.MaxVolume) + sectorVal += (short)sp.MaxVolume; + else + sectorVal += sp.Buffer[i]; + } + + samples[i] = sectorVal; + } + } + } + + #endregion + + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs new file mode 100644 index 0000000000..02f144350c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs @@ -0,0 +1,16 @@ +using BizHawk.Emulation.Cores.Components; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZXSpectrum + { + private FakeSyncSound _fakeSyncSound; + private IAsyncSoundProvider ActiveSoundProvider; + private SoundProviderMixer SoundMixer; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 64ddcde13a..15052083da 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -69,7 +69,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Register(_tracer); ser.Register(_cpu); ser.Register(_machine); - ser.Register(_machine.BuzzerDevice); + + SoundMixer = new SoundProviderMixer(_machine.BuzzerDevice); + if (_machine.AYDevice != null) + SoundMixer.AddSource(_machine.AYDevice); + + ser.Register(SoundMixer); HardReset(); From f0cef1cf0ddbc0aeade40910471717f3e08118a2 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Dec 2017 10:49:43 +0000 Subject: [PATCH 025/339] AY-3-8912 Implementation --- .../BizHawk.Emulation.Cores.csproj | 2 +- .../SinclairSpectrum/Hardware/AY38912.cs | 732 ++++++++++++++++++ .../SinclairSpectrum/Hardware/AYSound.cs | 407 ---------- .../SinclairSpectrum/Hardware/Buzzer.cs | 13 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 31 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 4 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 32 +- .../Machine/ZXSpectrum128K/ZX128.cs | 7 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 4 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 4 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 5 +- .../SinclairSpectrum/SoundProviderMixer.cs | 47 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 13 +- 13 files changed, 862 insertions(+), 439 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 8db1b196d4..274f62c6a4 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -256,7 +256,7 @@ - + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs new file mode 100644 index 0000000000..9babd6fbd0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs @@ -0,0 +1,732 @@ + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class AY38912 : ISoundProvider + { + private int _tStatesPerFrame; + private int _sampleRate; + private int _samplesPerFrame; + private int _tStatesPerSample; + private int _sampleCounter; + const int AY_SAMPLE_RATE = 16; + private int _AYCyclesPerFrame; + private int _nsamp; + private int _AYCount; + + + /// + /// The final sample buffer + /// + private short[] _samples = new short[0]; + + /// + /// Number of samples in one frame + /// + public int SamplesPerFrame + { + get { return _samplesPerFrame; } + set { _samplesPerFrame = value; } + } + + /// + /// Number of TStates in each sample + /// + public int TStatesPerSample + { + get { return _tStatesPerSample; } + set { _tStatesPerSample = value; } + } + + #region Construction & Initialisation + + public AY38912() + { + Reset(); + } + + /// + /// Initialises the AY chip + /// + public void Init(int sampleRate, int tStatesPerFrame) + { + _sampleRate = sampleRate; + _tStatesPerFrame = tStatesPerFrame; + _tStatesPerSample = 79; + _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + _AYCyclesPerFrame = _tStatesPerFrame / AY_SAMPLE_RATE; + } + + #endregion + + public void UpdateSound(int currentFrameCycle) + { + //if (currentFrameCycle >= _tStatesPerFrame) + //currentFrameCycle = _tStatesPerFrame; + + for (int i = 0; i < (currentFrameCycle / AY_SAMPLE_RATE) - _AYCount; i++) + { + Update(); + SampleAY(); + _AYCount++; + } + + // calculate how many samples must be processed + int samplesToGenerate = (currentFrameCycle / _tStatesPerSample) - (_sampleCounter / 2); + + // begin generation + if (samplesToGenerate > 0) + { + // ensure the required resolution + while (soundSampleCounter < 4) + { + SampleAY(); + } + EndSampleAY(); + + // generate needed samples + for (int i = 0; i < samplesToGenerate; i++) + { + _samples[_sampleCounter++] = (short)(averagedChannelSamples[0]); + _samples[_sampleCounter++] = (short)(averagedChannelSamples[1]); + + samplesToGenerate--; + } + + averagedChannelSamples[0] = 0; + averagedChannelSamples[1] = 0; + averagedChannelSamples[2] = 0; + } + } + + public void StartFrame() + { + _AYCount = 0; + + // the stereo _samples buffer should already have been processed as a part of + // ISoundProvider at the end of the last frame + _samples = new short[_samplesPerFrame * 2]; + _nsamp = _samplesPerFrame; + _sampleCounter = 0; + + Init(44100, _tStatesPerFrame); + } + + public void EndFrame() + { + } + + + public void Reset() + { + // reset volumes + for (int i = 0; i < 16; i++) + AY_SpecVolumes[i] = (short)(AY_Volumes[i] * 8191); + + soundSampleCounter = 0; + regs[AY_NOISEPER] = 0xFF; + noiseOut = 0x01; + envelopeVolume = 0; + noiseCount = 0; + + // reset state of all channels + for (int f = 0; f < 3; f++) + { + channel_count[f] = 0; + channel_mix[f] = 0; + channel_out[f] = 0; + averagedChannelSamples[f] = 0; + } + + envelopeCount = 0; + randomSeed = 1; + selectedRegister = 0; + } + + #region IStatable + + public void SyncState(Serializer ser) + { + ser.BeginSection("AY38912"); + ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); + ser.Sync("_sampleRate", ref _sampleRate); + ser.Sync("_samplesPerFrame", ref _samplesPerFrame); + ser.Sync("_tStatesPerSample", ref _tStatesPerSample); + ser.Sync("_sampleCounter", ref _sampleCounter); + + ser.Sync("ChannelLeft", ref ChannelLeft); + ser.Sync("ChannelRight", ref ChannelRight); + ser.Sync("ChannelCenter", ref ChannelCenter); + ser.Sync("Regs", ref regs, false); + ser.Sync("NoiseOut", ref noiseOut); + ser.Sync("envelopeVolume", ref envelopeVolume); + ser.Sync("noiseCount", ref noiseCount); + ser.Sync("envelopeCount", ref envelopeCount); + ser.Sync("randomSeed", ref randomSeed); + ser.Sync("envelopeClock", ref envelopeClock); + ser.Sync("selectedRegister", ref selectedRegister); + ser.Sync("soundSampleCounter", ref soundSampleCounter); + ser.Sync("stereoSound", ref stereoSound); + ser.Sync("sustaining", ref sustaining); + ser.Sync("sustain", ref sustain); + ser.Sync("alternate", ref alternate); + ser.Sync("attack", ref attack); + ser.Sync("envelopeStep", ref envelopeStep); + + ser.Sync("channel_out", ref channel_out, false); + ser.Sync("channel_count", ref channel_count, false); + ser.Sync("averagedChannelSamples", ref averagedChannelSamples, false); + ser.Sync("channel_mix", ref channel_mix, false); + + ser.Sync("AY_SpecVolumes", ref AY_SpecVolumes, false); + + ser.Sync("_samples", ref _samples, false); + ser.Sync("_nsamp", ref _nsamp); + ser.EndSection(); + } + + #endregion + + #region AY Sound Implementation + + /* + Based on the AYSound class from ArjunNair's Zero-Emulator + https://github.com/ArjunNair/Zero-Emulator/ + *MIT LICENSED* + + The MIT License (MIT) + Copyright (c) 2009 Arjun Nair + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE + FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + /// + /// Register constants + /// + private const byte AY_A_FINE = 0; + private const byte AY_A_COARSE = 1; + private const byte AY_B_FINE = 2; + private const byte AY_B_COARSE = 3; + private const byte AY_C_FINE = 4; + private const byte AY_C_COARSE = 5; + private const byte AY_NOISEPER = 6; + private const byte AY_ENABLE = 7; + private const byte AY_A_VOL = 8; + private const byte AY_B_VOL = 9; + private const byte AY_C_VOL = 10; + private const byte AY_E_FINE = 11; + private const byte AY_E_COARSE = 12; + private const byte AY_E_SHAPE = 13; + private const byte AY_PORT_A = 14; + private const byte AY_PORT_B = 15; + + /// + /// Channels + /// + internal enum Channel + { + A, B, C + } + + /// + /// ACB configuration + /// + private int ChannelLeft = 0; + private int ChannelRight = 1; //2 if ABC + private int ChannelCenter = 2; //1 if ABC + + /// + /// Register storage + /// + private int[] regs = new int[16]; + + /// + /// State + /// + private int noiseOut; + private int envelopeVolume; + private int noiseCount; + private int envelopeCount; + private ulong randomSeed; + private byte envelopeClock = 0; + private int selectedRegister; + public ushort soundSampleCounter; + private bool stereoSound = true; + private bool sustaining; + private bool sustain; + private bool alternate; + private int attack; + private int envelopeStep; + + /// + /// Buffer arrays + /// + private int[] channel_out = new int[3]; + private int[] channel_count = new int[3]; + private int[] averagedChannelSamples = new int[3]; + private short[] channel_mix = new short[3]; + + /// + /// Measurements from comp.sys.sinclair (2001 Matthew Westcott) + /// + private float[] AY_Volumes = + { + 0.0000f, 0.0137f, 0.0205f, 0.0291f, + 0.0423f, 0.0618f, 0.0847f, 0.1369f, + 0.1691f, 0.2647f, 0.3527f, 0.4499f, + 0.5704f, 0.6873f, 0.8482f, 1.0000f + }; + + /// + /// Volume storage (short) + /// + private short[] AY_SpecVolumes = new short[16]; + + /// + /// Sets the ACB configuration + /// + /// + public void SetSpeakerACB(bool val) + { + // ACB + if (val) + { + ChannelCenter = 2; + ChannelRight = 1; + } + // ABC + else + { + ChannelCenter = 1; + ChannelRight = 2; + } + } + + /// + /// Set whether sound output is stereo or mono + /// + public bool StereoSound + { + get { return stereoSound; } + set { stereoSound = value; } + } + + /// + /// Utility method to set all registers externally + /// + /// + public void SetRegisters(byte[] _regs) + { + for (int f = 0; f < 16; f++) + regs[f] = _regs[f]; + } + + /// + /// Utility method to get all registers externally + /// + /// + public byte[] GetRegisters() + { + byte[] newArray = new byte[16]; + for (int f = 0; f < 16; f++) + newArray[f] = (byte)(regs[f] & 0xff); + return newArray; + } + + /// + /// Selected Register property + /// + public int SelectedRegister + { + get { return selectedRegister; } + set { if (value < 16) selectedRegister = value; } + } + + /// + /// Simulates a port write to the AY chip + /// + /// + public void PortWrite(int val) + { + switch (SelectedRegister) + { + // not implemented / necessary + case AY_A_FINE: + case AY_B_FINE: + case AY_C_FINE: + case AY_E_FINE: + case AY_E_COARSE: + break; + + case AY_A_COARSE: + case AY_B_COARSE: + case AY_C_COARSE: + val &= 0x0f; + break; + + case AY_NOISEPER: + case AY_A_VOL: + case AY_B_VOL: + case AY_C_VOL: + val &= 0x1f; + break; + + case AY_ENABLE: + /* + if ((lastEnable == -1) || ((lastEnable & 0x40) != (regs[AY_ENABLE] & 0x40))) { + SelectedRegister = ((regs[AY_ENABLE] & 0x40) > 0 ? regs[AY_PORT_B] : 0xff); + } + if ((lastEnable == -1) || ((lastEnable & 0x80) != (regs[AY_ENABLE] & 0x80))) { + PortWrite((regs[AY_ENABLE] & 0x80) > 0 ? regs[AY_PORT_B] : 0xff); + } + lastEnable = regs[AY_ENABLE];*/ + break; + + case AY_E_SHAPE: + val &= 0x0f; + attack = ((val & 0x04) != 0 ? 0x0f : 0x00); + // envelopeCount = 0; + if ((val & 0x08) == 0) + { + /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ + sustain = true; + alternate = (attack != 0); + } + else + { + sustain = (val & 0x01) != 0; + alternate = (val & 0x02) != 0; + } + envelopeStep = 0x0f; + sustaining = false; + envelopeVolume = (envelopeStep ^ attack); + break; + + case AY_PORT_A: + /* + if ((regs[AY_ENABLE] & 0x40) > 0) { + selectedRegister = regs[AY_PORT_A]; + }*/ + break; + + case AY_PORT_B: + /* + if ((regs[AY_ENABLE] & 0x80) > 0) { + PortWrite(regs[AY_PORT_A]); + }*/ + break; + } + + regs[SelectedRegister] = val; + } + + /// + /// Simulates port reads from the AY chip + /// + /// + public int PortRead() + { + if (SelectedRegister == AY_PORT_B) + { + if ((regs[AY_ENABLE] & 0x80) == 0) + return 0xff; + else + return regs[AY_PORT_B]; + } + + return regs[selectedRegister]; + } + + private void EndSampleAY() + { + if (stereoSound) + { + averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); + averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelRight] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); + averagedChannelSamples[2] = 0;// beeperSound; + } + else + { + averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] + averagedChannelSamples[ChannelRight]) / soundSampleCounter); + averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] + averagedChannelSamples[ChannelRight]) / soundSampleCounter); + averagedChannelSamples[2] = 0;// (averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] + averagedChannelSamples[ChannelRight]) / soundSampleCounter + beeperSound; + } + soundSampleCounter = 0; + } + + private void SampleAY() + { + int ah; + + ah = regs[AY_ENABLE]; + + channel_mix[(int)Channel.A] = MixChannel(ah, regs[AY_A_VOL], (int)Channel.A); + + ah >>= 1; + channel_mix[(int)Channel.B] = MixChannel(ah, regs[AY_B_VOL], (int)Channel.B); + + ah >>= 1; + channel_mix[(int)Channel.C] = MixChannel(ah, regs[AY_C_VOL], (int)Channel.C); + + averagedChannelSamples[0] += channel_mix[(int)Channel.A]; + averagedChannelSamples[1] += channel_mix[(int)Channel.B]; + averagedChannelSamples[2] += channel_mix[(int)Channel.C]; + soundSampleCounter++; + } + + private short MixChannel(int ah, int cl, int chan) + { + int al = channel_out[chan]; + int bl, bh; + bl = ah; + bh = ah; + bh &= 0x1; + bl >>= 3; + + al |= (bh); //Tone | AY_ENABLE + bl |= (noiseOut); //Noise | AY_ENABLE + al &= bl; + + if ((al != 0)) + { + if ((cl & 16) != 0) + cl = envelopeVolume; + + cl &= 15; + + //return (AY_Volumes[cl]); + return (AY_SpecVolumes[cl]); + } + return 0; + } + + /// + /// Gets the tone period for the specified channel + /// + /// + /// + private int TonePeriod(int channel) + { + return (regs[(channel) << 1] | ((regs[((channel) << 1) | 1] & 0x0f) << 8)); + } + + /// + /// Gets the noise period for the specified channel + /// + /// + private int NoisePeriod() + { + return (regs[AY_NOISEPER] & 0x1f); + } + + /// + /// Gets the envelope period for the specified channel + /// + /// + private int EnvelopePeriod() + { + return ((regs[AY_E_FINE] | (regs[AY_E_COARSE] << 8))); + } + + /// + /// Gets the noise enable value for the specified channel + /// + /// + /// + private int NoiseEnable(int channel) + { + return ((regs[AY_ENABLE] >> (3 + channel)) & 1); + } + + /// + /// Gets the tone enable value for the specified channel + /// + /// + /// + private int ToneEnable(int channel) + { + return ((regs[AY_ENABLE] >> (channel)) & 1); + } + + /// + /// Gets the tone envelope value for the specified channel + /// + /// + /// + private int ToneEnvelope(int channel) + { + //return ((regs[AY_A_VOL + channel] & 0x10) >> 4); + return ((regs[AY_A_VOL + channel] >> 4) & 0x1); + } + + /// + /// Updates noise + /// + private void UpdateNoise() + { + noiseCount++; + if (noiseCount >= NoisePeriod() && (noiseCount > 4)) + { + /* Is noise output going to change? */ + if (((randomSeed + 1) & 2) != 0) /* (bit0^bit1)? */ + { + noiseOut ^= 1; + } + + /* The Random Number Generator of the 8910 is a 17-bit shift */ + /* register. The input to the shift register is bit0 XOR bit3 */ + /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */ + + /* The following is a fast way to compute bit17 = bit0^bit3. */ + /* Instead of doing all the logic operations, we only check */ + /* bit0, relying on the fact that after three shifts of the */ + /* register, what now is bit3 will become bit0, and will */ + /* invert, if necessary, bit14, which previously was bit17. */ + if ((randomSeed & 1) != 0) + randomSeed ^= 0x24000; /* This version is called the "Galois configuration". */ + randomSeed >>= 1; + noiseCount = 0; + } + } + + /// + /// Updates envelope + /// + private void UpdateEnvelope() + { + /* update envelope */ + if (!sustaining) + { + envelopeCount++; + if ((envelopeCount >= EnvelopePeriod())) + { + envelopeStep--; + + /* check envelope current position */ + if (envelopeStep < 0) + { + if (sustain) + { + if (alternate) + attack ^= 0x0f; + sustaining = true; + envelopeStep = 0; + } + else + { + /* if CountEnv has looped an odd number of times (usually 1), */ + /* invert the output. */ + if (alternate && ((envelopeStep & (0x0f + 1)) != 0) && (envelopeCount > 4)) + attack ^= 0x0f; + + envelopeStep &= 0x0f; + } + } + envelopeCount = 0; + } + } + envelopeVolume = (envelopeStep ^ attack); + } + + + public void Update() + { + envelopeClock ^= 1; + + if (envelopeClock == 1) + { + envelopeCount++; + + //if ((((regs[AY_A_VOL + 0] & 0x10) >> 4) & (((regs[AY_A_VOL + 1] & 0x10) >> 4) & ((regs[AY_A_VOL + 2] & 0x10) >> 4))) != 1) + //if ((((regs[AY_A_VOL + 0] >> 4) & 0x1) & (((regs[AY_A_VOL + 1] >> 4) & 0x1) & ((regs[AY_A_VOL + 2] >> 4) & 0x1))) != 0) + if (((regs[AY_A_VOL + 0] & 0x10) & (regs[AY_A_VOL + 1] & 0x10) & (regs[AY_A_VOL + 2] & 0x10)) != 1) + { + // update envelope + if (!sustaining) + UpdateEnvelope(); + + envelopeVolume = (envelopeStep ^ attack); + } + } + + // update noise + if ((regs[AY_ENABLE] & 0x38) != 0x38) + { + UpdateNoise(); + } + + // update channels + channel_count[0]++; + int regs1 = (regs[1] & 0x0f) << 8; + if (((regs[0] | regs1) > 4) && (channel_count[0] >= (regs[0] | regs1))) + { + channel_out[0] ^= 1; + channel_count[0] = 0; + } + + int regs3 = (regs[3] & 0x0f) << 8; + channel_count[1]++; + if (((regs[2] | regs3) > 4) && (channel_count[1] >= (regs[2] | regs3))) + { + channel_out[1] ^= 1; + channel_count[1] = 0; + } + + int regs5 = (regs[5] & 0x0f) << 8; + channel_count[2]++; + if (((regs[4] | regs5) > 4) && (channel_count[2] >= (regs[4] | regs5))) + { + channel_out[2] ^= 1; + channel_count[2] = 0; + } + } + + + #endregion + + #region ISoundProvider + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + samples = _samples; + nsamp = _nsamp; + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs deleted file mode 100644 index 3cd2c513dd..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AYSound.cs +++ /dev/null @@ -1,407 +0,0 @@ -using System; - -using BizHawk.Common; -using BizHawk.Common.NumberExtensions; -using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Components; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public class AYSound : ISoundProvider - { - private readonly BlipBuffer _blip = new BlipBuffer(4096); - private short[] _sampleBuffer = new short[0]; - - public AYSound() - { - _blip.SetRates(894866 / 4.0, 44100); - } - - public ushort[] Register = new ushort[16]; - - public int total_clock; // TODO: what is this used for? - - public void Reset() - { - clock_A = clock_B = clock_C = 0x1000; - noise_clock = 0x20; - - for (int i = 0; i < 16; i++) - { - Register[i] = 0x0000; - } - sync_psg_state(); - DiscardSamples(); - } - - public void DiscardSamples() - { - _blip.Clear(); - _sampleClock = 0; - } - - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public bool CanProvideAsync => false; - - public SyncSoundMode SyncMode => SyncSoundMode.Sync; - - public void SetSyncMode(SyncSoundMode mode) - { - if (mode != SyncSoundMode.Sync) - { - throw new InvalidOperationException("Only Sync mode is supported."); - } - } - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - _blip.EndFrame((uint)_sampleClock); - _sampleClock = 0; - - nsamp = _blip.SamplesAvailable(); - int targetLength = nsamp * 2; - if (_sampleBuffer.Length != targetLength) - { - _sampleBuffer = new short[targetLength]; - } - - _blip.ReadSamplesLeft(_sampleBuffer, nsamp); - for (int i = 0; i < _sampleBuffer.Length; i += 2) - { - _sampleBuffer[i + 1] = _sampleBuffer[i]; - } - - samples = _sampleBuffer; - } - - public void GetSamples(short[] samples) - { - throw new Exception(); - } - - private static readonly int[] VolumeTable = - { - 0x0000, 0x0055, 0x0079, 0x00AB, 0x00F1, 0x0155, 0x01E3, 0x02AA, - 0x03C5, 0x0555, 0x078B, 0x0AAB, 0x0F16, 0x1555, 0x1E2B, 0x2AAA - }; - - private int _sampleClock; - private int _latchedSample; - - private int TotalExecutedCycles; - private int PendingCycles; - private int psg_clock; - private int sq_per_A, sq_per_B, sq_per_C; - private int clock_A, clock_B, clock_C; - private int vol_A, vol_B, vol_C; - private bool A_on, B_on, C_on; - private bool A_up, B_up, C_up; - private bool A_noise, B_noise, C_noise; - - private int env_per; - private int env_clock; - private int env_shape; - private int env_E; - private int E_up_down; - private int env_vol_A, env_vol_B, env_vol_C; - - private int noise_clock; - private int noise_per; - private int noise = 0x1; - - public Func ReadMemory; - public Func WriteMemory; - - public void SyncState(Serializer ser) - { - ser.BeginSection("PSG"); - - ser.Sync("Register", ref Register, false); - ser.Sync("Toal_executed_cycles", ref TotalExecutedCycles); - ser.Sync("Pending_Cycles", ref PendingCycles); - - ser.Sync("psg_clock", ref psg_clock); - ser.Sync("clock_A", ref clock_A); - ser.Sync("clock_B", ref clock_B); - ser.Sync("clock_C", ref clock_C); - ser.Sync("noise_clock", ref noise_clock); - ser.Sync("env_clock", ref env_clock); - ser.Sync("A_up", ref A_up); - ser.Sync("B_up", ref B_up); - ser.Sync("C_up", ref C_up); - ser.Sync("noise", ref noise); - ser.Sync("env_E", ref env_E); - ser.Sync("E_up_down", ref E_up_down); - - sync_psg_state(); - - ser.EndSection(); - } - - public ushort? ReadPSG(ushort addr, bool peek) - { - if (addr >= 0x01F0 && addr <= 0x01FF) - { - return (ushort)(Register[addr - 0x01F0]); - } - - return null; - } - - private void sync_psg_state() - { - sq_per_A = (Register[0] & 0xFF) | (((Register[4] & 0xF) << 8)); - if (sq_per_A == 0) - { - sq_per_A = 0x1000; - } - - sq_per_B = (Register[1] & 0xFF) | (((Register[5] & 0xF) << 8)); - if (sq_per_B == 0) - { - sq_per_B = 0x1000; - } - - sq_per_C = (Register[2] & 0xFF) | (((Register[6] & 0xF) << 8)); - if (sq_per_C == 0) - { - sq_per_C = 0x1000; - } - - env_per = (Register[3] & 0xFF) | (((Register[7] & 0xFF) << 8)); - if (env_per == 0) - { - env_per = 0x10000; - } - - env_per *= 2; - - A_on = Register[8].Bit(0); - B_on = Register[8].Bit(1); - C_on = Register[8].Bit(2); - A_noise = Register[8].Bit(3); - B_noise = Register[8].Bit(4); - C_noise = Register[8].Bit(5); - - noise_per = Register[9] & 0x1F; - if (noise_per == 0) - { - noise_per = 0x20; - } - - var shape_select = Register[10] & 0xF; - - if (shape_select < 4) - env_shape = 0; - else if (shape_select < 8) - env_shape = 1; - else - env_shape = 2 + (shape_select - 8); - - vol_A = Register[11] & 0xF; - env_vol_A = (Register[11] >> 4) & 0x3; - - vol_B = Register[12] & 0xF; - env_vol_B = (Register[12] >> 4) & 0x3; - - vol_C = Register[13] & 0xF; - env_vol_C = (Register[13] >> 4) & 0x3; - } - - public bool WritePSG(ushort addr, ushort value, bool poke) - { - if (addr >= 0x01F0 && addr <= 0x01FF) - { - var reg = addr - 0x01F0; - - value &= 0xFF; - - if (reg == 4 || reg == 5 || reg == 6 || reg == 10) - value &= 0xF; - - if (reg == 9) - value &= 0x1F; - - if (reg == 11 || reg == 12 || reg == 13) - value &= 0x3F; - - Register[addr - 0x01F0] = value; - - sync_psg_state(); - - if (reg == 10) - { - env_clock = env_per; - - if (env_shape == 0 || env_shape == 2 || env_shape == 3 || env_shape == 4 || env_shape == 5) - { - env_E = 15; - E_up_down = -1; - } - else - { - env_E = 0; - E_up_down = 1; - } - } - - return true; - } - - return false; - } - - public void generate_sound(int cycles_to_do) - { - // there are 4 cpu cycles for every psg cycle - bool sound_out_A; - bool sound_out_B; - bool sound_out_C; - - for (int i = 0; i < cycles_to_do; i++) - { - psg_clock++; - - if (psg_clock == 4) - { - psg_clock = 0; - - total_clock++; - - clock_A--; - clock_B--; - clock_C--; - - noise_clock--; - env_clock--; - - // clock noise - if (noise_clock == 0) - { - noise = (noise >> 1) ^ (noise.Bit(0) ? 0x10004 : 0); - noise_clock = noise_per; - } - - if (env_clock == 0) - { - env_clock = env_per; - - env_E += E_up_down; - - if (env_E == 16 || env_E == -1) - { - - // we just completed a period of the envelope, determine what to do now based on the envelope shape - if (env_shape == 0 || env_shape == 1 || env_shape == 3 || env_shape == 9) - { - E_up_down = 0; - env_E = 0; - } - else if (env_shape == 5 || env_shape == 7) - { - E_up_down = 0; - env_E = 15; - } - else if (env_shape == 4 || env_shape == 8) - { - if (env_E == 16) - { - env_E = 15; - E_up_down = -1; - } - else - { - env_E = 0; - E_up_down = 1; - } - } - else if (env_shape == 2) - { - env_E = 15; - } - else - { - env_E = 0; - } - } - } - - if (clock_A == 0) - { - A_up = !A_up; - clock_A = sq_per_A; - } - - if (clock_B == 0) - { - B_up = !B_up; - clock_B = sq_per_B; - } - - if (clock_C == 0) - { - C_up = !C_up; - clock_C = sq_per_C; - } - - - sound_out_A = (noise.Bit(0) | A_noise) & (A_on | A_up); - sound_out_B = (noise.Bit(0) | B_noise) & (B_on | B_up); - sound_out_C = (noise.Bit(0) | C_noise) & (C_on | C_up); - - // now calculate the volume of each channel and add them together - int v; - - if (env_vol_A == 0) - { - v = (short)(sound_out_A ? VolumeTable[vol_A] : 0); - } - else - { - int shift_A = 3 - env_vol_A; - if (shift_A < 0) - shift_A = 0; - v = (short)(sound_out_A ? (VolumeTable[env_E] >> shift_A) : 0); - } - - if (env_vol_B == 0) - { - v += (short)(sound_out_B ? VolumeTable[vol_B] : 0); - - } - else - { - int shift_B = 3 - env_vol_B; - if (shift_B < 0) - shift_B = 0; - v += (short)(sound_out_B ? (VolumeTable[env_E] >> shift_B) : 0); - } - - if (env_vol_C == 0) - { - v += (short)(sound_out_C ? VolumeTable[vol_C] : 0); - } - else - { - int shift_C = 3 - env_vol_C; - if (shift_C < 0) - shift_C = 0; - v += (short)(sound_out_C ? (VolumeTable[env_E] >> shift_C) : 0); - } - - if (v != _latchedSample) - { - _blip.AddDelta((uint)_sampleClock, v - _latchedSample); - _latchedSample = v; - } - - _sampleClock++; - } - } - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs index ace668868f..f815a80c1e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs @@ -94,6 +94,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _sampleRate = sampleRate; _tStatesPerFrame = tStatesPerFrame; + _tStatesPerSample = 79; + _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + + /* // get divisors var divs = from a in Enumerable.Range(2, _tStatesPerFrame / 2) @@ -105,6 +109,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // get _samplesPerFrame _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + */ Pulses = new List(1000); } @@ -265,13 +270,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum short[] stereoBuffer = new short[soundBufferContains * 2]; int index = 0; for (int i = 0; i < soundBufferContains; i++) - { - stereoBuffer[index++] = soundBuffer[i]; + { stereoBuffer[index++] = soundBuffer[i]; + stereoBuffer[index++] = soundBuffer[i]; } - + samples = stereoBuffer; - nsamp = soundBufferContains; + nsamp = _samplesPerFrame; // soundBufferContains; } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index f23173a259..a700bf98ac 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -12,10 +12,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public abstract partial class SpectrumBase { - public bool ROMPaged { get; set; } - public bool SHADOWPaged { get; set; } - public int RAMPaged { get; set; } - public bool PagingDisabled { get; set; } + protected int ROMPaged = 0; + protected bool SHADOWPaged; + protected int RAMPaged; + protected bool PagingDisabled; /// /// The calling ZXSpectrum class (piped in via constructor) @@ -40,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Device representing the AY-3-8912 chip found in the 128k and up spectrums /// - public AYSound AYDevice { get; set; } + public AY38912 AYDevice { get; set; } /// /// The spectrum keyboard @@ -100,6 +100,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum protected const int MIC_BIT = 0x08; protected const int TAPE_BIT = 0x40; + protected const int AY_SAMPLE_RATE = 16; + /// /// Executes a single frame /// @@ -107,9 +109,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { FrameCompleted = false; BuzzerDevice.StartFrame(); - + if (AYDevice != null) + AYDevice.StartFrame(); PollInput(); + var curr = CPU.TotalExecutedCycles; + while (CurrentFrameCycle <= UlaFrameCycleCount) { // check for interrupt @@ -122,14 +127,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var lastCycle = CurrentFrameCycle; RenderScreen(LastRenderedULACycle + 1, lastCycle); LastRenderedULACycle = lastCycle; - - } + // update AY + if (AYDevice != null) + AYDevice.UpdateSound(CurrentFrameCycle); + } + // we have reached the end of a frame LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; LastRenderedULACycle = OverFlow; - BuzzerDevice.EndFrame(); + BuzzerDevice.EndFrame(); TapeDevice.CPUFrameCompleted(); @@ -234,14 +242,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RomData.SyncState(ser); KeyboardDevice.SyncState(ser); BuzzerDevice.SyncState(ser); - TapeDevice.SyncState(ser); if (AYDevice != null) AYDevice.SyncState(ser); + TapeDevice.SyncState(ser); + ser.EndSection(); - //ReInitMemory(); + ReInitMemory(); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index 4502a98a51..5b8ef31e19 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -53,7 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - if (!ROMPaged) + if (ROMPaged == 0) result = Memory[0][addr % 0x4000]; else result = Memory[1][addr % 0x4000]; @@ -119,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - if (!ROMPaged) + if (ROMPaged == 0) Memory[0][addr % 0x4000] = value; else Memory[1][addr % 0x4000] = value; 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 d381bef130..67e667defb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -8,6 +8,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZX128 : SpectrumBase { + private int AYTStates = 0; + /// /// Reads a byte of data from a specified port address /// @@ -112,12 +114,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // (e.g. the AY sound chip in a 128k spectrum // AY register activate - // Kemptson Mouse + if ((port & 0xc002) == 0xc000) + { + result = (int)AYDevice.PortRead(); + } + + // Kempston Mouse // if unused port the floating memory bus should be returned (still todo) } + CPU.TotalExecutedCycles += 3; + return (byte)result; } @@ -128,6 +137,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + int currT = CPU.TotalExecutedCycles; + // paging if (port == 0x7ffd) { @@ -140,11 +151,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if ((value & 0x10) != 0) { // 48k ROM - ROMPaged = true; + ROMPaged = 1; } else { - ROMPaged = false; + ROMPaged = 0; } // Bit 5 signifies that paging is disabled until next reboot @@ -183,6 +194,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Tape TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } + + // Active AY Register + if ((port & 0xc002) == 0xc000) + { + var reg = value & 0x0f; + AYDevice.SelectedRegister = reg; + CPU.TotalExecutedCycles += 3; + } + + // AY Write + if ((port & 0xc002) == 0x8000) + { + AYDevice.PortWrite(value); + CPU.TotalExecutedCycles += 3; + } } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index fe3f08d651..b40df6e8a9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -21,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; - ROMPaged = false; + ROMPaged = 0; SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; @@ -29,8 +29,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // init addressable memory from ROM and RAM banks ReInitMemory(); - //RAM = new byte[0x4000 + 0xC000]; - //DisplayLineTime = 132; VsyncNumerator = 3546900; @@ -42,7 +40,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, UlaFrameCycleCount); - AYDevice = new AYSound(); + AYDevice = new AY38912(); + AYDevice.Init(44100, UlaFrameCycleCount); KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 76dbb31891..27d74882fb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -53,7 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - if (!ROMPaged) + if (ROMPaged == 0) result = Memory[0][addr % 0x4000]; else result = Memory[1][addr % 0x4000]; @@ -119,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - if (!ROMPaged) + if (ROMPaged == 0) Memory[0][addr % 0x4000] = value; else Memory[1][addr % 0x4000] = value; 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 35fe0c3391..9b6b458e4b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -140,11 +140,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if ((value & 0x10) != 0) { // 48k ROM - ROMPaged = true; + ROMPaged = 1; } else { - ROMPaged = false; + ROMPaged = 0; } // Bit 5 signifies that paging is disabled until next reboot diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 2a75a600f7..199beb7d8d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -21,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; - ROMPaged = false; + ROMPaged = 0; SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; @@ -42,6 +42,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, UlaFrameCycleCount); + AYDevice = new AY38912(); + AYDevice.Init(44100, UlaFrameCycleCount); + KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs index 57e37f3d77..16d9fa9c52 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs @@ -60,6 +60,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else if (sp.Count() > 1) foreach (var s in sp) SoundProviders.Remove(s); + + EqualizeVolumes(); } public void EqualizeVolumes() @@ -119,6 +121,38 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!sameCount) { + // get the highest number of samples + int max = SoundProviders.Aggregate((i, j) => i.Buffer.Length > j.Buffer.Length ? i : j).Buffer.Length; + + nsamp = max; + samples = new short[nsamp * 2]; + + // take a pass at populating the samples array for each provider + foreach (var sp in SoundProviders) + { + short sectorVal = 0; + int pos = 0; + for (int i = 0; i < sp.Buffer.Length; i++) + { + if (sp.Buffer[i] > sp.MaxVolume) + sectorVal = (short)sp.MaxVolume; + else + { + if (sp.SoundProvider is AY38912) + { + // boost audio + sectorVal += (short)(sp.Buffer[i] * 2); + } + else + { + sectorVal += sp.Buffer[i]; + } + } + + samples[pos++] += sectorVal; + } + } + /* int divisor = 1; int highestCount = 0; @@ -167,6 +201,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } } + */ } else { @@ -181,7 +216,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (sp.Buffer[i] > sp.MaxVolume) sectorVal += (short)sp.MaxVolume; else - sectorVal += sp.Buffer[i]; + { + if (sp.SoundProvider is AY38912) + { + // boost audio + sectorVal += (short)(sp.Buffer[i] * 2); + } + else + { + sectorVal += sp.Buffer[i]; + } + } } samples[i] = sectorVal; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 15052083da..81d2a965fb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -74,7 +74,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - ser.Register(SoundMixer); + //SoundMixer.DisableSource(_machine.BuzzerDevice); + + dcf = new DCFilter(SoundMixer, 1024); + + + + ser.Register(dcf); + //ser.Register(_machine.AYDevice); + + HardReset(); @@ -89,6 +98,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public IController _controller; private SpectrumBase _machine; + private DCFilter dcf; + private byte[] _file; From 43ed79cd645a285c154c97e3e1b418710b1797ec Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Dec 2017 13:09:53 +0000 Subject: [PATCH 026/339] Mixer balancing and stereo output toggle --- .../SinclairSpectrum/Hardware/AY38912.cs | 26 +---- .../SinclairSpectrum/Hardware/Buzzer.cs | 11 +-- .../SinclairSpectrum/SoundProviderMixer.cs | 96 +++++++++++-------- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 17 +++- 4 files changed, 80 insertions(+), 70 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs index 9babd6fbd0..8dcde1e0ee 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs @@ -265,7 +265,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private byte envelopeClock = 0; private int selectedRegister; public ushort soundSampleCounter; - private bool stereoSound = true; + private bool stereoSound = false; private bool sustaining; private bool sustain; private bool alternate; @@ -316,15 +316,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - /// - /// Set whether sound output is stereo or mono - /// - public bool StereoSound - { - get { return stereoSound; } - set { stereoSound = value; } - } - /// /// Utility method to set all registers externally /// @@ -453,18 +444,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private void EndSampleAY() { - if (stereoSound) - { - averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); - averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelRight] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); - averagedChannelSamples[2] = 0;// beeperSound; - } - else - { - averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] + averagedChannelSamples[ChannelRight]) / soundSampleCounter); - averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] + averagedChannelSamples[ChannelRight]) / soundSampleCounter); - averagedChannelSamples[2] = 0;// (averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] + averagedChannelSamples[ChannelRight]) / soundSampleCounter + beeperSound; - } + averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); + averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelRight] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); + soundSampleCounter = 0; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs index f815a80c1e..d751189fae 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs @@ -206,12 +206,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (var i = firstSample; i < currentEnd + pulse.Length; i += TStatesPerSample) { - samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 2) : (short)0; - - //resampler.EnqueueSample(samples[sampleIndex - 1], samples[sampleIndex - 1]); - - } - + if (_tapeMode) + samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 6) : (short)0; + else + samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 2) : (short)0; + } currentEnd += pulse.Length; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs index 16d9fa9c52..1b2e37b835 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs @@ -8,7 +8,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider /// Currently only supports SyncSoundMode.Sync - /// Attached ISoundProvider sources must already be stereo 44.1khz + /// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length /// internal sealed class SoundProviderMixer : ISoundProvider { @@ -20,11 +20,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int NSamp { get; set; } } + private bool _stereo = true; + public bool Stereo + { + get { return _stereo; } + set { _stereo = value; } + } + private readonly List SoundProviders; - - private short[] _buffer; - private int _nSamp; - + public SoundProviderMixer(params ISoundProvider[] soundProviders) { SoundProviders = new List(); @@ -119,40 +123,51 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var firstEntry = SoundProviders.First(); bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp); - if (!sameCount) + + if (sameCount) { - // get the highest number of samples - int max = SoundProviders.Aggregate((i, j) => i.Buffer.Length > j.Buffer.Length ? i : j).Buffer.Length; - - nsamp = max; + nsamp = firstEntry.NSamp; samples = new short[nsamp * 2]; - // take a pass at populating the samples array for each provider - foreach (var sp in SoundProviders) + if (_stereo) { - short sectorVal = 0; - int pos = 0; - for (int i = 0; i < sp.Buffer.Length; i++) + for (int i = 0; i < samples.Length; i++) { - if (sp.Buffer[i] > sp.MaxVolume) - sectorVal = (short)sp.MaxVolume; - else + short sectorVal = 0; + foreach (var sp in SoundProviders) { - if (sp.SoundProvider is AY38912) - { - // boost audio - sectorVal += (short)(sp.Buffer[i] * 2); - } + if (sp.Buffer[i] > sp.MaxVolume) + sectorVal += (short)sp.MaxVolume; else { sectorVal += sp.Buffer[i]; } } - samples[pos++] += sectorVal; + samples[i] = sectorVal; } } - /* + else + { + // convert to mono + for (int i = 0; i < samples.Length; i += 2) + { + short s = 0; + foreach (var sp in SoundProviders) + { + s += (short)((sp.Buffer[i] + sp.Buffer[i + 1]) / 2); + } + + samples[i] = s; + samples[i + 1] = s; + } + } + } + + else if (!sameCount) + { + // this is a pretty poor implementation that doesnt work very well + // ideally soundproviders should ensure that their number of samples is identical int divisor = 1; int highestCount = 0; @@ -194,27 +209,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum sectorVal = (short)sp.MaxVolume; else sectorVal = sp.Buffer[i]; - + for (int s = 0; s < divisor; s++) { samples[pos++] += sectorVal; } } } - */ - } - else - { - nsamp = firstEntry.NSamp; + + /* + // get the highest number of samples + int max = SoundProviders.Aggregate((i, j) => i.Buffer.Length > j.Buffer.Length ? i : j).Buffer.Length; + + nsamp = max; samples = new short[nsamp * 2]; - for (int i = 0; i < samples.Length; i++) + // take a pass at populating the samples array for each provider + foreach (var sp in SoundProviders) { short sectorVal = 0; - foreach (var sp in SoundProviders) + int pos = 0; + for (int i = 0; i < sp.Buffer.Length; i++) { if (sp.Buffer[i] > sp.MaxVolume) - sectorVal += (short)sp.MaxVolume; + sectorVal = (short)sp.MaxVolume; else { if (sp.SoundProvider is AY38912) @@ -225,12 +243,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else { sectorVal += sp.Buffer[i]; - } - } - } + } + } - samples[i] = sectorVal; + samples[pos++] += sectorVal; + } } + */ + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 16643c8b84..f68aa408e4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -24,7 +24,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public bool PutSettings(ZXSpectrumSettings o) { + if (SoundMixer != null) + SoundMixer.Stereo = o.StereoSound; + Settings = o; + return false; } @@ -39,11 +43,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class ZXSpectrumSettings { - [DisplayName("Auto-load tape")] - [Description("Auto or manual tape operation")] + [DisplayName("Stereo Sound")] + [Description("Turn stereo sound on or off")] [DefaultValue(true)] - public bool AutoLoadTape { get; set; } - + public bool StereoSound { get; set; } + public ZXSpectrumSettings Clone() { @@ -73,6 +77,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DefaultValue(TapeLoadSpeed.Accurate)] public TapeLoadSpeed TapeLoadSpeed { get; set; } + [DisplayName("Auto-load tape")] + [Description("Auto or manual tape operation")] + [DefaultValue(true)] + public bool AutoLoadTape { get; set; } + public ZXSpectrumSyncSettings Clone() { return (ZXSpectrumSyncSettings)MemberwiseClone(); From eff8ce69b44b9c451454c4881519aef17e4121b2 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Dec 2017 15:43:28 +0000 Subject: [PATCH 027/339] More +3 stuff (still not working) --- .../Database/FirmwareDatabase.cs | 2 +- .../BizHawk.Emulation.Cores.csproj | 1 + .../SinclairSpectrum/Machine/MachineType.cs | 9 +- .../Machine/SpectrumBase.Screen.cs | 43 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 12 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 455 ++++++++++++------ .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 209 ++++++-- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 2 - .../ZXSpectrum.ISoundProvider.cs | 2 - .../ZXSpectrum.IVideoProvider.cs | 13 + .../Computers/SinclairSpectrum/ZXSpectrum.cs | 10 + 11 files changed, 543 insertions(+), 215 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 9d9810b5e6..7755353463 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM"); FirmwareAndOption("8CAFB292AF58617907B9E6B9093D3588A75849B8", 32768, "ZXSpectrum", "PLUS2ROM", "PLUS2.ROM", "Spectrum 128K +2 ROM"); - FirmwareAndOption("929BF1A5E5687EBD8D7245F9B513A596C0EC21A4", 65563, "ZXSpectrum", "PLUS3ROM", "PLUS3.ROM", "Spectrum 128K +3 ROM"); + FirmwareAndOption("929BF1A5E5687EBD8D7245F9B513A596C0EC21A4", 65536, "ZXSpectrum", "PLUS3ROM", "PLUS3.ROM", "Spectrum 128K +3 ROM"); // for saturn, we think any bios region can pretty much run any iso // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 274f62c6a4..70231ad7e7 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1391,6 +1391,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs index a29b745205..cb895cf171 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -19,8 +19,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ZXSpectrum128, /// - /// Sinclair Spectrum 128 + 2 model + /// Sinclair Spectrum 128 +2 model /// - ZXSpectrum128Plus2 + ZXSpectrum128Plus2, + + /// + /// Sinclair Spectrum 128 +3 model + /// + ZXSpectrum128Plus3 } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index 3ab09352db..69bdc62382 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -913,13 +913,44 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ public int[] GetVideoBuffer() { - return _frameBuffer; + /* + switch(Spectrum.SyncSettings.BorderType) + { + case ZXSpectrum.BorderType.Full: + return _frameBuffer; - // convert the generated _framebuffer into ARGB colours via the ULAPalette - int[] trans = new int[_frameBuffer.Length]; - for (int i = 0; i < _frameBuffer.Length; i++) - trans[i] = ULAPalette[_frameBuffer[i]]; - return trans; //_frameBuffer; + case ZXSpectrum.BorderType.Small: + // leave only 10 border units all around + int[] smlBuff = new int[(ScreenWidth - 30) * (DisplayLines - 30)]; + int index = 0; + int brdCount = 0; + // skip top and bottom + for (int i = ScreenWidth * 30; i < smlBuff.Length - ScreenWidth * 30; i++) + { + if (brdCount < 30) + { + brdCount++; + continue; + } + if (brdCount > ScreenWidth - 30) + { + brdCount++; + continue; + } + + smlBuff[index] = _frameBuffer[i]; + index++; + brdCount++; + } + + return smlBuff; + + case ZXSpectrum.BorderType.Medium: + break; + } + */ + return _frameBuffer; + } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index a700bf98ac..8de871a5e1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -11,12 +11,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public abstract partial class SpectrumBase { - + // 128 and up only protected int ROMPaged = 0; protected bool SHADOWPaged; protected int RAMPaged; protected bool PagingDisabled; + // +3/+2A only + protected bool SpecialPagingMode; + protected int PagingConfiguration; + /// /// The calling ZXSpectrum class (piped in via constructor) /// @@ -238,6 +242,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("RAM5", ref RAM5, false); ser.Sync("RAM6", ref RAM6, false); ser.Sync("RAM7", ref RAM7, false); + ser.Sync("ROMPaged", ref ROMPaged); + ser.Sync("SHADOWPaged", ref SHADOWPaged); + ser.Sync("RAMPaged", ref RAMPaged); + ser.Sync("PagingDisabled", ref PagingDisabled); + ser.Sync("SpecialPagingMode", ref SpecialPagingMode); + ser.Sync("PagingConfiguration", ref PagingConfiguration); RomData.SyncState(ser); KeyboardDevice.SyncState(ser); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 27d74882fb..fca4961bd9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -8,35 +9,50 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZX128Plus3 : SpectrumBase { - /* 128k paging controlled by writes to port 0x7ffd - * - * - - #7FFD (32765) - decoded as A15=0, A1=0 and /IORQ=0. Bits 0..5 are latched. Bits 0..2 select RAM bank in secton D. Bit 3 selects RAM bank to dispay screen (0 - RAM5, 1 - RAM7). Bit 4 selects ROM bank (0 - ROM0, 1 - ROM1). Bit 5, when set locks future writing to #7FFD port until reset. Reading #7FFD port is the same as writing #FF into it. - #BFFD (49149) - write data byte into AY-3-8912 chip. - #FFFD (65533) - select AY-3-8912 addres (D4..D7 ignored) and reading data byte. + /* http://www.worldofspectrum.org/faq/reference/128kreference.htm + * + * Port 0x7ffd behaves in the almost exactly the same way as on the 128K/+2, with two exceptions: - * 0xffff +--------+--------+--------+--------+--------+--------+--------+--------+ - | Bank 0 | Bank 1 | Bank 2 | Bank 3 | Bank 4 | Bank 5 | Bank 6 | Bank 7 | - | | |(also at| | |(also at| | | - | | | 0x8000)| | | 0x4000)| | | - | | | | | | screen | | screen | - 0xc000 +--------+--------+--------+--------+--------+--------+--------+--------+ - | Bank 2 | Any one of these pages may be switched in. - | | - | | - | | - 0x8000 +--------+ - | Bank 5 | - | | - | | - | screen | - 0x4000 +--------+--------+ - | ROM 0 | ROM 1 | Either ROM may be switched in. - | | | - | | | - | | | - 0x0000 +--------+--------+ + Bit 4 is now the low bit of the ROM selection. + The partial decoding used is now slightly different: the hardware will respond only to those port addresses with bit 1 reset, bit 14 set and bit 15 reset (as opposed to just bits 1 and 15 reset on the 128K/+2). + The extra paging features of the +2A/+3 are controlled by port 0x1ffd (again, partial decoding applies here: the hardware will respond to all port addresses with bit 1 reset, bit 12 set and bits 13, 14 and 15 reset). This port is also write-only, and its last value should be saved at 0x5b67 (23399). + + Port 0x1ffd responds as follows: + + Bit 0: Paging mode. 0=normal, 1=special + Bit 1: In normal mode, ignored. + Bit 2: In normal mode, high bit of ROM selection. The four ROMs are: + ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + Bit 3: Disk motor; 1=on, 0=off + Bit 4: Printer port strobe. + When special mode is selected, the memory map changes to one of four configurations specified in bits 1 and 2 of port 0x1ffd: + Bit 2 =0 Bit 2 =0 Bit 2 =1 Bit 2 =1 + Bit 1 =0 Bit 1 =1 Bit 1 =0 Bit 1 =1 + 0xffff +--------+ +--------+ +--------+ +--------+ + | Bank 3 | | Bank 7 | | Bank 3 | | Bank 3 | + | | | | | | | | + | | | | | | | | + | | | screen | | | | | + 0xc000 +--------+ +--------+ +--------+ +--------+ + | Bank 2 | | Bank 6 | | Bank 6 | | Bank 6 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x8000 +--------+ +--------+ +--------+ +--------+ + | Bank 1 | | Bank 5 | | Bank 5 | | Bank 7 | + | | | | | | | | + | | | | | | | | + | | | screen | | screen | | screen | + 0x4000 +--------+ +--------+ +--------+ +--------+ + | Bank 0 | | Bank 4 | | Bank 4 | | Bank 4 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x0000 +--------+ +--------+ +--------+ +--------+ + RAM banks 1,3,4 and 6 are used for the disc cache and RAMdisc, while Bank 7 contains editor scratchpads and +3DOS workspace. */ /// @@ -49,58 +65,120 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { int divisor = addr / 0x4000; byte result = 0xff; - switch (divisor) + + // special paging + if (SpecialPagingMode) { - // ROM 0x000 - case 0: - if (ROMPaged == 0) - result = Memory[0][addr % 0x4000]; - else - result = Memory[1][addr % 0x4000]; - break; + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + result = Memory[4][addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = Memory[8][addr % 0x4000]; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + result = Memory[5][addr % 0x4000]; + break; + case 1: + case 2: + result = Memory[9][addr % 0x4000]; + break; + case 3: + result = Memory[11][addr % 0x4000]; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + result = Memory[6][addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = Memory[10][addr % 0x4000]; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + result = Memory[7][addr % 0x4000]; + break; + case 1: + result = Memory[11][addr % 0x4000]; + break; + } + break; + } + } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + result = Memory[ROMPaged][addr % 0x4000]; + break; - // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) - case 1: - result = Memory[7][addr % 0x4000]; - break; + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + result = Memory[9][addr % 0x4000]; + break; - // RAM 0x8000 (RAM2 - Bank2) - case 2: - result = Memory[4][addr % 0x4000]; - break; + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = Memory[6][addr % 0x4000]; + break; - // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) - case 3: - switch (RAMPaged) - { - case 0: - result = Memory[2][addr % 0x4000]; - break; - case 1: - result = Memory[3][addr % 0x4000]; - break; - case 2: - result = Memory[4][addr % 0x4000]; - break; - case 3: - result = Memory[5][addr % 0x4000]; - break; - case 4: - result = Memory[6][addr % 0x4000]; - break; - case 5: - result = Memory[7][addr % 0x4000]; - break; - case 6: - result = Memory[8][addr % 0x4000]; - break; - case 7: - result = Memory[9][addr % 0x4000]; - break; - } - break; - default: - break; + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = Memory[4][addr % 0x4000]; + break; + case 1: + result = Memory[5][addr % 0x4000]; + break; + case 2: + result = Memory[6][addr % 0x4000]; + break; + case 3: + result = Memory[7][addr % 0x4000]; + break; + case 4: + result = Memory[8][addr % 0x4000]; + break; + case 5: + result = Memory[9][addr % 0x4000]; + break; + case 6: + result = Memory[10][addr % 0x4000]; + break; + case 7: + result = Memory[11][addr % 0x4000]; + break; + } + break; + default: + break; + } } return result; @@ -115,59 +193,121 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WriteBus(ushort addr, byte value) { int divisor = addr / 0x4000; - switch (divisor) + + // special paging + if (SpecialPagingMode) { - // ROM 0x000 - case 0: - if (ROMPaged == 0) - Memory[0][addr % 0x4000] = value; - else - Memory[1][addr % 0x4000] = value; - break; - - // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) - case 1: - Memory[7][addr % 0x4000] = value; - break; - - // RAM 0x8000 (RAM2 - Bank2) - case 2: - Memory[4][addr % 0x4000] = value; - break; - - // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) - case 3: - switch (RAMPaged) - { - case 0: - Memory[2][addr % 0x4000] = value; - break; - case 1: - Memory[3][addr % 0x4000] = value; - break; - case 2: - Memory[4][addr % 0x4000] = value; - break; - case 3: - Memory[5][addr % 0x4000] = value; - break; - case 4: - Memory[6][addr % 0x4000] = value; - break; - case 5: - Memory[7][addr % 0x4000] = value; - break; - case 6: - Memory[8][addr % 0x4000] = value; - break; - case 7: - Memory[9][addr % 0x4000] = value; - break; - } - break; - default: - break; + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + Memory[4][addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + Memory[8][addr % 0x4000] = value; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + Memory[5][addr % 0x4000] = value; + break; + case 1: + case 2: + Memory[9][addr % 0x4000] = value; + break; + case 3: + Memory[11][addr % 0x4000] = value; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + Memory[6][addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + Memory[10][addr % 0x4000] = value; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + Memory[7][addr % 0x4000] = value; + break; + case 1: + Memory[11][addr % 0x4000] = value; + break; + } + break; + } } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + Memory[ROMPaged][addr % 0x4000] = value; + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + Memory[9][addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + Memory[6][addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + Memory[4][addr % 0x4000] = value; + break; + case 1: + Memory[5][addr % 0x4000] = value; + break; + case 2: + Memory[6][addr % 0x4000] = value; + break; + case 3: + Memory[7][addr % 0x4000] = value; + break; + case 4: + Memory[8][addr % 0x4000] = value; + break; + case 5: + Memory[9][addr % 0x4000] = value; + break; + case 6: + Memory[10][addr % 0x4000] = value; + break; + case 7: + Memory[11][addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } } /// @@ -224,44 +364,54 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Memory.Add(1, ROM1); if (Memory.ContainsKey(2)) - Memory[2] = RAM0; + Memory[2] = ROM2; else - Memory.Add(2, RAM0); + Memory.Add(2, ROM2); if (Memory.ContainsKey(3)) - Memory[3] = RAM1; + Memory[3] = ROM3; else - Memory.Add(3, RAM1); + Memory.Add(3, ROM3); if (Memory.ContainsKey(4)) - Memory[4] = RAM2; + Memory[4] = RAM0; else - Memory.Add(4, RAM2); + Memory.Add(4, RAM0); if (Memory.ContainsKey(5)) - Memory[5] = RAM3; + Memory[5] = RAM1; else - Memory.Add(5, RAM3); + Memory.Add(5, RAM1); if (Memory.ContainsKey(6)) - Memory[6] = RAM4; + Memory[6] = RAM2; else - Memory.Add(6, RAM4); + Memory.Add(6, RAM2); if (Memory.ContainsKey(7)) - Memory[7] = RAM5; + Memory[7] = RAM3; else - Memory.Add(7, RAM5); + Memory.Add(7, RAM3); if (Memory.ContainsKey(8)) - Memory[8] = RAM6; + Memory[8] = RAM4; else - Memory.Add(8, RAM6); + Memory.Add(8, RAM4); if (Memory.ContainsKey(9)) - Memory[9] = RAM7; + Memory[9] = RAM5; else - Memory.Add(9, RAM7); + Memory.Add(9, RAM5); + + if (Memory.ContainsKey(10)) + Memory[10] = RAM6; + else + Memory.Add(10, RAM6); + + if (Memory.ContainsKey(11)) + Memory[11] = RAM7; + else + Memory.Add(11, RAM7); } /// @@ -272,13 +422,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void InitROM(RomData romData) { RomData = romData; - // 128k uses ROM0 and ROM1 - // 128k loader is in ROM0, and fallback 48k rom is in ROM1 - for (int i = 0; i < 0x4000; i++) - { - ROM0[i] = RomData.RomBytes[i]; - ROM1[i] = RomData.RomBytes[i + 0x4000]; - } + // +3 uses ROM0, ROM1, ROM2 & ROM3 + /* ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + */ + Stream stream = new MemoryStream(RomData.RomBytes); + stream.Read(ROM0, 0, 16384); + stream.Read(ROM1, 0, 16384); + stream.Read(ROM2, 0, 16384); + stream.Read(ROM3, 0, 16384); + stream.Dispose(); } } } 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 9b6b458e4b..e7f2071fca 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -79,30 +79,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result |= (TAPE_BIT); // set is EAR Off } } + else if ((LastULAOutByte & 0x10) == 0) + { + result &= ~(0x40); + } else { - if (KeyboardDevice.IsIssue2Keyboard) - { - if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - else - { - if ((LastULAOutByte & EAR_BIT) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } + result |= 0x40; } } @@ -111,9 +94,35 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // devices other than the ULA will respond here // (e.g. the AY sound chip in a 128k spectrum - // AY register activate - // Kemptson Mouse + // AY register activate - on +3/2a both FFFD and BFFD active AY + if ((port & 0xc002) == 0xc000) + { + result = (int)AYDevice.PortRead(); + } + else if ((port & 0xc002) == 0x8000) + { + result = (int)AYDevice.PortRead(); + } + // Kempston Mouse + + + else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset? + { + //result = udpDrive.DiskStatusRead(); + } + else if ((port & 0xF002) == 0x3000) + { + //result = udpDrive.DiskReadByte(); + } + + else if ((port & 0xF002) == 0x0) + { + if (PagingDisabled) + result = 0x1; + else + result = 0xff; + } // if unused port the floating memory bus should be returned (still todo) } @@ -128,33 +137,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { - // paging - if (port == 0x7ffd) - { - // Bits 0, 1, 2 select the RAM page - var rp = value & 0x07; - if (rp < 8) - RAMPaged = rp; - - // ROM page - if ((value & 0x10) != 0) - { - // 48k ROM - ROMPaged = 1; - } - else - { - ROMPaged = 0; - } - - // Bit 5 signifies that paging is disabled until next reboot - if ((value & 0x20) != 0) - PagingDisabled = true; - - - return; - } - // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x01) == 0; @@ -183,6 +165,131 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Tape TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } + + else + { + // AY Register activation + if ((port & 0xc002) == 0xc000) + { + var reg = value & 0x0f; + AYDevice.SelectedRegister = reg; + CPU.TotalExecutedCycles += 3; + } + else + { + if ((port & 0xC002) == 0x8000) + { + AYDevice.PortWrite(value); + CPU.TotalExecutedCycles += 3; + } + else + { + if ((port & 0xC002) == 0x4000) //Are bits 1 and 15 reset and bit 14 set? + { + // memory paging activate + if (PagingDisabled) + return; + + // bit 5 handles paging disable (48k mode, persistent until next reboot) + if ((value & 0x20) != 0) + { + PagingDisabled = true; + } + + // shadow screen + if ((value & 0x08) != 0) + { + SHADOWPaged = true; + } + else + { + SHADOWPaged = false; + } + } + else + { + //Extra Memory Paging feature activate + if ((port & 0xF002) == 0x1000) //Is bit 12 set and bits 13,14,15 and 1 reset? + { + if (PagingDisabled) + return; + + // set disk motor state + //todo + + if ((value & 0x08) != 0) + { + //diskDriveState |= (1 << 4); + } + else + { + //diskDriveState &= ~(1 << 4); + } + + if ((value & 0x1) != 0) + { + // activate special paging mode + SpecialPagingMode = true; + PagingConfiguration = (value & 0x6 >> 1); + } + else + { + // normal paging mode + SpecialPagingMode = false; + } + } + else + { + // disk write port + if ((port & 0xF002) == 0x3000) //Is bit 12 set and bits 13,14,15 and 1 reset? + { + //udpDrive.DiskWriteByte((byte)(val & 0xff)); + } + } + } + } + } + } + + + // paging + if (port == 0x7ffd) + { + if (PagingDisabled) + return; + + LastULAOutByte = value; + + + + + + + // Bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // ROM page + if ((value & 0x10) != 0) + { + // 48k ROM + ROMPaged = 1; + } + else + { + ROMPaged = 0; + } + + // Bit 5 signifies that paging is disabled until next reboot + if ((value & 0x20) != 0) + PagingDisabled = true; + + + return; + } + + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 199beb7d8d..cd5e8f81b2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -29,8 +29,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // init addressable memory from ROM and RAM banks ReInitMemory(); - //RAM = new byte[0x4000 + 0xC000]; - //DisplayLineTime = 132; VsyncNumerator = 3546900; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs index 02f144350c..402b7f292d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs @@ -9,8 +9,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZXSpectrum { - private FakeSyncSound _fakeSyncSound; - private IAsyncSoundProvider ActiveSoundProvider; private SoundProviderMixer SoundMixer; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs new file mode 100644 index 0000000000..073236a69d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs @@ -0,0 +1,13 @@ +using System; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Main IVideoProvider implementation is inside the machine classes + /// This is just some helper functions + /// + public partial class ZXSpectrum + { + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 81d2a965fb..ac08f1bd3a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -48,6 +48,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); break; + case MachineType.ZXSpectrum128Plus3: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + break; default: throw new InvalidOperationException("Machine not yet emulated"); } @@ -146,6 +150,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var romDataP2 = RomData.InitROM(machineType, _systemRomP2); _machine.InitROM(romDataP2); break; + case MachineType.ZXSpectrum128Plus3: + _machine = new ZX128Plus3(this, _cpu, file); + var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); + var romDataP3 = RomData.InitROM(machineType, _systemRomP3); + _machine.InitROM(romDataP3); + break; } } From fc8b89c837dd767e45e98bdf0709451515d1c867 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Dec 2017 16:03:23 +0000 Subject: [PATCH 028/339] Added original 16k speccy (even though it sucks) --- .../BizHawk.Emulation.Cores.csproj | 1 + .../SinclairSpectrum/Machine/MachineType.cs | 5 + .../Machine/ZXSpectrum16K/ZX16.cs | 169 ++++++++++++++++++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 10 ++ 4 files changed, 185 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 70231ad7e7..244c3ae233 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -271,6 +271,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs index cb895cf171..478b821a1a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -8,6 +8,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public enum MachineType { + /// + /// Original Sinclair Spectrum 16K model + /// + ZXSpectrum16, + /// /// Sinclair Spectrum 48K model /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs new file mode 100644 index 0000000000..d810cf7255 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -0,0 +1,169 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class ZX16 : ZX48 + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX16(ZXSpectrum spectrum, Z80A cpu, byte[] file) + : base(spectrum, cpu, file) + { + + } + + #endregion + + + #region Memory + + /* 48K Spectrum has NO memory paging + * + * + | Bank 0 | + | | + | | + | screen | + 0x4000 +--------+ + | ROM 0 | + | | + | | + | | + 0x0000 +--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + // paging logic goes here + + if (divisor > 1) + { + // memory does not exist + return 0xff; + } + + var bank = Memory[divisor]; + var index = addr % 0x4000; + return bank[index]; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + // paging logic goes here + + if (divisor > 1) + { + // memory does not exist + return; + } + + var bank = Memory[divisor]; + var index = addr % 0x4000; + bank[index] = value; + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if ((addr & 0xC000) == 0x4000) + { + // addr is in RAM not ROM - apply memory contention if neccessary + if (addr >= 0x8000) + { + data = 0xFF; + } + else + { + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + } + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + if (addr < 0x4000) + { + // Do nothing - we cannot write to ROM + return; + } + else if (addr >= 0x8000) + { + // memory does not exist + return; + } + else if (addr < 0x8000) + { + // possible contended RAM + var delay = GetContentionValue(CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = RAM1; + else + Memory.Add(1, RAM1); + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // for 16/48k machines only ROM0 is used (no paging) + RomData.RomBytes?.CopyTo(ROM0, 0); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index ac08f1bd3a..036247f341 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -36,6 +36,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (SyncSettings.MachineType) { + case MachineType.ZXSpectrum16: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + break; case MachineType.ZXSpectrum48: ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); @@ -132,6 +136,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // setup the emulated model based on the MachineType switch (machineType) { + case MachineType.ZXSpectrum16: + _machine = new ZX16(this, _cpu, file); + var _systemRom16 = GetFirmware(0x4000, "48ROM"); + var romData16 = RomData.InitROM(machineType, _systemRom16); + _machine.InitROM(romData16); + break; case MachineType.ZXSpectrum48: _machine = new ZX48(this, _cpu, file); var _systemRom = GetFirmware(0x4000, "48ROM"); From 0cd8af59745910f6e1ac40b451226488aa00f986 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Dec 2017 17:24:30 +0000 Subject: [PATCH 029/339] SyncSettings option for widescreen mode (remove top and bottom borders) --- .../Machine/SpectrumBase.Screen.cs | 25 ++++++++++++++++--- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 12 --------- .../Machine/ZXSpectrum128K/ZX128.cs | 6 ++--- .../Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs | 4 +-- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 6 ++--- .../Machine/ZXSpectrum16K/ZX16.cs | 4 +-- .../Machine/ZXSpectrum48K/ZX48.cs | 4 +-- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 3 +-- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 11 ++++---- 9 files changed, 40 insertions(+), 35 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index 69bdc62382..65df1938cc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -151,7 +151,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The number of border pixels to the right of the display /// protected int BorderRightPixels = 48; - + /// /// The total width of the screen in pixels /// @@ -765,8 +765,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Initialises the screen configuration calculations /// - public virtual void InitScreenConfig() + public virtual void InitScreenConfig(ZXSpectrum.BorderType border_type) { + switch (border_type) + { + case ZXSpectrum.BorderType.Full: + BorderTopLines = 48; + BorderBottomLines = 48; + NonVisibleBorderTopLines = 8; + NonVisibleBorderBottomLines = 8; + break; + + case ZXSpectrum.BorderType.Widescreen: + BorderTopLines = 0; + BorderBottomLines = 0; + NonVisibleBorderTopLines = 8 + 48; + NonVisibleBorderBottomLines = 8 + 48; + break; + } + ScreenLines = BorderTopLines + DisplayLines + BorderBottomLines; FirstDisplayLine = VerticalSyncLines + NonVisibleBorderTopLines + BorderTopLines; LastDisplayLine = FirstDisplayLine + DisplayLines - 1; @@ -907,11 +924,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { get { return UlaFrameCycleCount; } } - /* + /* public int VsyncNumerator => NullVideo.DefaultVsyncNum; public int VsyncDenominator => NullVideo.DefaultVsyncDen; */ - public int[] GetVideoBuffer() + public int[] GetVideoBuffer() { /* switch(Spectrum.SyncSettings.BorderType) 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 541a370341..00b952614b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -8,17 +8,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZX128 : SpectrumBase { - public override void InitScreenConfig() - { - - ScreenLines = BorderTopLines + DisplayLines + BorderBottomLines; - FirstDisplayLine = VerticalSyncLines + NonVisibleBorderTopLines + BorderTopLines; - LastDisplayLine = FirstDisplayLine + DisplayLines - 1; - ScreenWidth = BorderLeftPixels + DisplayWidth + BorderRightPixels; - FirstPixelCycleInLine = HorizontalBlankingTime + BorderLeftTime; - ScreenLineTime = FirstPixelCycleInLine + DisplayLineTime + BorderRightTime + NonVisibleBorderRightTime; - UlaFrameCycleCount = (FirstDisplayLine + DisplayLines + BorderBottomLines + NonVisibleBorderTopLines) * ScreenLineTime; - FirstScreenPixelCycle = (VerticalSyncLines + NonVisibleBorderTopLines) * ScreenLineTime + HorizontalBlankingTime; - } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index b40df6e8a9..9a8dfbc55b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128(ZXSpectrum spectrum, Z80A cpu, byte[] file) + public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) { Spectrum = spectrum; CPU = cpu; @@ -30,9 +30,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ReInitMemory(); //DisplayLineTime = 132; - VsyncNumerator = 3546900; + //VsyncNumerator = 3546900 * 2; - InitScreenConfig(); + InitScreenConfig(borderType); InitScreen(); ResetULACycle(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs index faaba1791f..b88bcb3e58 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs @@ -20,8 +20,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, byte[] file) - : base(spectrum, cpu, file) + public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) + : base(spectrum, cpu, borderType, file) { } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index cd5e8f81b2..31f71db4c0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, byte[] file) + public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) { Spectrum = spectrum; CPU = cpu; @@ -30,9 +30,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ReInitMemory(); //DisplayLineTime = 132; - VsyncNumerator = 3546900; + //VsyncNumerator = 3546900; - InitScreenConfig(); + InitScreenConfig(borderType); InitScreen(); ResetULACycle(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index d810cf7255..5fc4d6badd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -16,8 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX16(ZXSpectrum spectrum, Z80A cpu, byte[] file) - : base(spectrum, cpu, file) + public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) + : base(spectrum, cpu, borderType, file) { } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 4979071d58..81e1b07258 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -16,14 +16,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX48(ZXSpectrum spectrum, Z80A cpu, byte[] file) + public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) { Spectrum = spectrum; CPU = cpu; ReInitMemory(); - InitScreenConfig(); + InitScreenConfig(borderType); InitScreen(); ResetULACycle(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index f68aa408e4..3d12898e0e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -104,8 +104,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public enum BorderType { Full, - Medium, - Small + Widescreen, } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 036247f341..5fff81c5be 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -137,34 +137,35 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (machineType) { case MachineType.ZXSpectrum16: - _machine = new ZX16(this, _cpu, file); + _machine = new ZX16(this, _cpu, borderType, file); var _systemRom16 = GetFirmware(0x4000, "48ROM"); var romData16 = RomData.InitROM(machineType, _systemRom16); _machine.InitROM(romData16); break; case MachineType.ZXSpectrum48: - _machine = new ZX48(this, _cpu, file); + _machine = new ZX48(this, _cpu, borderType, file); var _systemRom = GetFirmware(0x4000, "48ROM"); var romData = RomData.InitROM(machineType, _systemRom); _machine.InitROM(romData); break; case MachineType.ZXSpectrum128: - _machine = new ZX128(this, _cpu, file); + _machine = new ZX128(this, _cpu, borderType, file); var _systemRom128 = GetFirmware(0x8000, "128ROM"); var romData128 = RomData.InitROM(machineType, _systemRom128); _machine.InitROM(romData128); break; case MachineType.ZXSpectrum128Plus2: - _machine = new ZX128Plus2(this, _cpu, file); + _machine = new ZX128Plus2(this, _cpu, borderType, file); var _systemRomP2 = GetFirmware(0x8000, "PLUS2ROM"); var romDataP2 = RomData.InitROM(machineType, _systemRomP2); _machine.InitROM(romDataP2); break; case MachineType.ZXSpectrum128Plus3: - _machine = new ZX128Plus3(this, _cpu, file); + _machine = new ZX128Plus3(this, _cpu, borderType, file); var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); var romDataP3 = RomData.InitROM(machineType, _systemRomP3); _machine.InitROM(romDataP3); + System.Windows.Forms.MessageBox.Show("+3 is not working at all yet :/"); break; } } From b6ddf03c9666131def1eb084eb9c78e6d3e4946d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Dec 2017 17:34:02 +0000 Subject: [PATCH 030/339] Some comments --- .../Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 3d12898e0e..f64d476d6d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -103,7 +103,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public enum BorderType { + /// + /// How it was originally back in the day + /// Full, + + /// + /// Top and bottom border removed so that the result is *almost* 16:9 + /// Widescreen, } From e155bb05fce0ca50c1b553cb3e21fd21f1cb839a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 09:05:12 +0000 Subject: [PATCH 031/339] Embedded ZX Roms (allowed for distribution from AMSTRAD) --- BizHawk.Emulation.Common/Database/Database.cs | 1 + .../Database/FirmwareDatabase.cs | 2 + .../BizHawk.Emulation.Cores.csproj | 4 ++ .../SinclairSpectrum/ZXSpectrum.IEmulator.cs | 2 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 1 + .../Computers/SinclairSpectrum/ZXSpectrum.cs | 30 ++++++++++++- .../Properties/Resources.Designer.cs | 40 ++++++++++++++++++ .../Properties/Resources.resx | 12 ++++++ BizHawk.Emulation.Cores/Resources/128.ROM.gz | Bin 0 -> 24229 bytes BizHawk.Emulation.Cores/Resources/48.ROM.gz | Bin 0 -> 12246 bytes .../Resources/plus2.rom.gz | Bin 0 -> 24158 bytes .../Resources/plus3.rom.gz | Bin 0 -> 38413 bytes 12 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Resources/128.ROM.gz create mode 100644 BizHawk.Emulation.Cores/Resources/48.ROM.gz create mode 100644 BizHawk.Emulation.Cores/Resources/plus2.rom.gz create mode 100644 BizHawk.Emulation.Cores/Resources/plus3.rom.gz diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index d2d2bfccee..9364aac02d 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -308,6 +308,7 @@ namespace BizHawk.Emulation.Common break; case ".TAP": + byte[] head = File.ReadAllBytes(fileName).Take(8).ToArray(); if (System.Text.Encoding.Default.GetString(head).Contains("C64-TAPE")) game.System = "C64"; diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 7755353463..ba32833137 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -51,10 +51,12 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("D3B78C3DBAC55F5199F33F3FE0036439811F7FB3", 16384, "C64", "Drive1541II", "drive-1541ii.bin", "1541-II Disk Drive Rom"); // ZX Spectrum + /* These are now shipped with bizhawk FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM"); FirmwareAndOption("8CAFB292AF58617907B9E6B9093D3588A75849B8", 32768, "ZXSpectrum", "PLUS2ROM", "PLUS2.ROM", "Spectrum 128K +2 ROM"); FirmwareAndOption("929BF1A5E5687EBD8D7245F9B513A596C0EC21A4", 65536, "ZXSpectrum", "PLUS3ROM", "PLUS3.ROM", "Spectrum 128K +3 ROM"); + */ // for saturn, we think any bios region can pretty much run any iso // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 244c3ae233..638bf87f74 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1399,8 +1399,12 @@ + + + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs index 8b10763146..b7a1a07d82 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public string SystemId => "ZXSpectrum"; - public bool DeterministicEmulation => true; + public bool DeterministicEmulation => false; public void ResetCounters() { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index f64d476d6d..ba6a944346 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -116,6 +116,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The speed at which the tape is loaded + /// NOT IN USE YET /// public enum TapeLoadSpeed { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 5fff81c5be..75bb2fcca0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -1,6 +1,8 @@ -using BizHawk.Emulation.Common; +using BizHawk.Common; +using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Emulation.Cores.Properties; using System; using System.Collections.Generic; using System.IO; @@ -121,6 +123,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return rom; } + // Amstrad licensed ROMs are free to distribute and shipped with BizHawk + byte[] embeddedRom = new byte[length]; + bool embeddedFound = true; + switch (names.FirstOrDefault()) + { + case "48ROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_48_ROM)); + break; + case "128ROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_128_ROM)); + break; + case "PLUS2ROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus2_rom)); + break; + case "PLUS3ROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus3_rom)); + break; + default: + embeddedFound = false; + break; + } + + if (embeddedFound) + return embeddedRom; + + // Embedded ROM not found, maybe this is a peripheral ROM? var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("ZXSpectrum", n, false)).FirstOrDefault(b => b != null && b.Length == length); if (result == null) { diff --git a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs index 5a3bbcce9a..312ea05d92 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs +++ b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs @@ -89,5 +89,45 @@ namespace BizHawk.Emulation.Cores.Properties { return ((byte[])(obj)); } } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_128_ROM { + get { + object obj = ResourceManager.GetObject("ZX_128_ROM", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_48_ROM { + get { + object obj = ResourceManager.GetObject("ZX_48_ROM", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_plus2_rom { + get { + object obj = ResourceManager.GetObject("ZX_plus2_rom", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_plus3_rom { + get { + object obj = ResourceManager.GetObject("ZX_plus3_rom", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/BizHawk.Emulation.Cores/Properties/Resources.resx b/BizHawk.Emulation.Cores/Properties/Resources.resx index bdb07b9a9f..4699ab819d 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.resx +++ b/BizHawk.Emulation.Cores/Properties/Resources.resx @@ -127,4 +127,16 @@ ..\Resources\sgb-cart-present.spc.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\128.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\48.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\plus2.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\plus3.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Resources/128.ROM.gz b/BizHawk.Emulation.Cores/Resources/128.ROM.gz new file mode 100644 index 0000000000000000000000000000000000000000..697dc6e8723129a24edd92a430dbff2b9cec8690 GIT binary patch literal 24229 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 literal 0 HcmV?d00001 From 7532b4be8c3b1507f7843003b0744d491da3ad9a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 09:09:05 +0000 Subject: [PATCH 032/339] Updated readme with progress info --- .../Computers/SinclairSpectrum/readme.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index bbced33297..ce9e736487 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -4,19 +4,22 @@ At this moment this is still *very* experimental and needs a lot more work. ### Implemented and sorta working * IEmulator -* ZX Spectrum 48k model +* ZX Spectrum 48k, 128k & Plus2 models * ULA video output (implementing IVideoProvider) * ULA Mode 1 VBLANK interrupt generation * IM2 Interrupts and DataBus implementation (thanks Aloysha) * Beeper/Buzzer output (implementing ISoundProvider) +* AY-3-8912 sound chip implementation * Keyboard input (implementing IInputPollable) * Kempston joystick (mapped to J1 currently) * Tape device that will load spectrum games in realtime (*.tzx and *.tap) * IStatable (although this is not currently working/implemented properly during tape load operations) +* ISettable core settings * IMemoryDomains (I think) -### Some progress -* ISettable - There are some Settings and SyncSettings instantiated, although they are not really used and I haven't yet figured out how to wire these up to the front-end yet +### Work in progress +* Exact emulator timings +* Floating memory bus emulation ### Not working * IDebuggable From 2b988954eea486964169acfb0aa1faab5d8123f5 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 12:54:48 +0000 Subject: [PATCH 033/339] Started implementing new ULA implemetation (far more performant) --- .../BizHawk.Emulation.Cores.csproj | 2 + .../SinclairSpectrum/Machine/SpectrumBase.cs | 17 +- .../SinclairSpectrum/Machine/ULABase.cs | 571 ++++++++++++++++++ .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 10 + .../Machine/ZXSpectrum48K/ZX48.Port.cs | 10 +- .../Machine/ZXSpectrum48K/ZX48.ULA.cs | 184 ++++++ .../Machine/ZXSpectrum48K/ZX48.cs | 4 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- .../Computers/SinclairSpectrum/readme.md | 3 + 9 files changed, 795 insertions(+), 8 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 638bf87f74..da148d52e1 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -271,7 +271,9 @@ + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 8de871a5e1..220c6c1e38 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -36,6 +36,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public RomData RomData { get; set; } + /// + /// The emulated ULA device + /// + public ULABase ULADevice { get; set; } + /// /// The spectrum buzzer/beeper /// @@ -119,18 +124,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var curr = CPU.TotalExecutedCycles; - while (CurrentFrameCycle <= UlaFrameCycleCount) + while (CurrentFrameCycle <= ULADevice.FrameLength) // UlaFrameCycleCount) { // check for interrupt - CheckForInterrupt(CurrentFrameCycle); + ULADevice.CheckForInterrupt(CurrentFrameCycle); // run a single CPU instruction CPU.ExecuteOne(); // run a rendering cycle according to the current CPU cycle count + /* var lastCycle = CurrentFrameCycle; RenderScreen(LastRenderedULACycle + 1, lastCycle); LastRenderedULACycle = lastCycle; + */ // update AY if (AYDevice != null) @@ -141,6 +148,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; LastRenderedULACycle = OverFlow; + ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); + BuzzerDevice.EndFrame(); TapeDevice.CPUFrameCompleted(); @@ -149,7 +158,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // setup for next frame OverFlow = CurrentFrameCycle % UlaFrameCycleCount; - ResetInterrupt(); + ULADevice.ResetInterrupt(); FrameCompleted = true; if (FrameCount % FlashToggleFrames == 0) @@ -157,7 +166,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _flashPhase = !_flashPhase; } - RenderScreen(0, OverFlow); + //RenderScreen(0, OverFlow); } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs new file mode 100644 index 0000000000..015980b038 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -0,0 +1,571 @@ + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Another ULA implementation (maybe it will be more performant & accurate) + /// + public abstract class ULABase : IVideoProvider + { + #region ULA Configuration + + #region General + + /// + /// Length of the frame in T-States + /// + public int FrameLength; + + /// + /// Emulated clock speed + /// + public int ClockSpeed; + + /// + /// Whether machine is late or early timing model + /// + public bool LateTiming; //currently not implemented + + /// + /// The current cycle within the current frame + /// + public int CurrentTStateInFrame; + + + protected SpectrumBase _machine; + + #endregion + + #region Palettes + + /// + /// The standard ULA palette + /// + private static readonly int[] ULAPalette = + { + Colors.ARGB(0x00, 0x00, 0x00), // Black + Colors.ARGB(0x00, 0x00, 0xD7), // Blue + Colors.ARGB(0xD7, 0x00, 0x00), // Red + Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta + Colors.ARGB(0x00, 0xD7, 0x00), // Green + Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan + Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow + Colors.ARGB(0xD7, 0xD7, 0xD7), // White + Colors.ARGB(0x00, 0x00, 0x00), // Bright Black + Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue + Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red + Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta + Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green + Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan + Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow + Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White + }; + + #endregion + + #region Interrupts + + /// + /// The number of T-States that the INT pin is simulated to be held low + /// + public int InterruptPeriod; + + #endregion + + #region Contention + + /// + /// T-State at which to start applying contention + /// + protected int contentionStartPeriod; + /// + /// T-State at which to end applying contention + /// + protected int contentionEndPeriod; + /// + /// T-State memory contention delay mapping + /// + public byte[] contentionTable; + + #endregion + + #region Screen Rendering + + /// + /// Video output buffer + /// + public int[] ScreenBuffer; + /// + /// Display memory + /// + protected byte[] screen; + /// + /// Attribute memory lookup (mapped 1:1 to screen for convenience) + /// + protected short[] attr; + /// + /// T-State display mapping + /// + protected short[] tstateToDisp; + /// + /// Table that stores T-State to screen/attribute address values + /// + protected short[] floatingBusTable; + /// + /// Cycle at which the last render update took place + /// + protected int lastTState; + /// + /// T-States elapsed since last render update + /// + protected int elapsedTStates; + /// + /// T-State of top left raster pixel + /// + protected int actualULAStart; + /// + /// Offset into display memory based on current T-State + /// + protected int screenByteCtr; + /// + /// Offset into current pixel of rasterizer + /// + protected int ULAByteCtr; + /// + /// The current border colour + /// + public int borderColour; + /// + /// Signs whether the colour flash is ON or OFF + /// + protected bool flashOn = false; + + /// + /// Last 8-bit bitmap read from display memory + /// (Floating bus implementation) + /// + protected int lastPixelValue; + /// + /// Last 8-bit attr val read from attribute memory + /// (Floating bus implementation) + /// + protected int lastAttrValue; + /// + /// Last 8-bit bitmap read from display memory+1 + /// (Floating bus implementation) + /// + protected int lastPixelValuePlusOne; + /// + /// Last 8-bit attr val read from attribute memory+1 + /// (Floating bus implementation) + /// + protected int lastAttrValuePlusOne; + + /// + /// Used to create the non-border display area + /// + protected int TtateAtLeft; + protected int TstateWidth; + protected int TstateAtTop; + protected int TstateHeight; + protected int TstateAtRight; + protected int TstateAtBottom; + + /// + /// Total T-States in one scanline + /// + protected int TstatesPerScanline; + /// + /// Total pixels in one scanline + /// + protected int ScanLineWidth; + /// + /// Total chars in one PRINT row + /// + protected int CharRows; + /// + /// Total chars in one PRINT column + /// + protected int CharCols; + /// + /// Total pixels in one display row + /// + protected int ScreenWidth; + /// + /// Total pixels in one display column + /// + protected int ScreenHeight; + /// + /// Total pixels in top border + /// + protected int BorderTopHeight; + /// + /// Total pixels in bottom border + /// + protected int BorderBottomHeight; + /// + /// Total pixels in left border width + /// + protected int BorderLeftWidth; + /// + /// Total pixels in right border width + /// + protected int BorderRightWidth; + /// + /// Memory address of display start + /// + protected int DisplayStart; + /// + /// Total number of bytes of display memory + /// + protected int DisplayLength; + /// + /// Memory address of attribute start + /// + protected int AttributeStart; + /// + /// Total number of bytes of attribute memory + /// + protected int AttributeLength; + + #endregion + + #region Interrupt + + /// + /// The longest instruction cycle count + /// + protected const int LONGEST_OP_CYCLES = 23; + + /// + /// Signs that an interrupt has been raised in this frame. + /// + protected bool InterruptRaised; + + /// + /// Signs that the interrupt signal has been revoked + /// + protected bool InterruptRevoked; + + /// + /// Resets the interrupt - this should happen every frame in order to raise + /// the VBLANK interrupt in the proceding frame + /// + public virtual void ResetInterrupt() + { + InterruptRaised = false; + InterruptRevoked = false; + } + + /// + /// Generates an interrupt in the current phase if needed + /// + /// + public virtual void CheckForInterrupt(int currentCycle) + { + if (InterruptRevoked) + { + // interrupt has already been handled + return; + } + + if (currentCycle < InterruptPeriod) + { + // interrupt does not need to be raised yet + return; + } + + if (currentCycle > InterruptPeriod + LONGEST_OP_CYCLES) + { + // 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; + //CPU.IFF1 = true; + _machine.CPU.FlagI = false; + //CPU.NonMaskableInterruptPending = true; + + } + + if (InterruptRaised) + { + // INT is raised but not yet revoked + // CPU has NOT handled it yet + return; + } + + // if CPU is masking the interrupt do not raise it + //if (!CPU.IFF1) + //return; + + // Raise the interrupt + InterruptRaised = true; + //CPU.IFF1 = false; + //CPU.IFF2 = false; + _machine.CPU.FlagI = true; + //FrameCount++; + ULAUpdateStart(); + + } + + #endregion + + #endregion + + #region Construction & Initialisation + + public ULABase(SpectrumBase machine) + { + _machine = machine; + } + + public virtual void Init() + { + + } + + #endregion + + #region Methods + + /// + /// Resets the ULA chip + /// + public abstract void Reset(); + + /// + /// Builds the contention table for the emulated model + /// + public abstract void BuildContentionTable(); + + /// + /// Returns true if the given memory address should be contended + /// + /// + /// + public abstract bool IsContended(int addr); + + /// + /// Contends the machine for a given address + /// + /// + public virtual void Contend(ushort addr) + { + if (IsContended(addr) && !(_machine is ZX128Plus3)) + { + _machine.CPU.TotalExecutedCycles += contentionTable[CurrentTStateInFrame]; + } + } + + public virtual void Contend(int addr, int time, int count) + { + if (IsContended(addr) && !(_machine is ZX128Plus3)) + { + for (int f = 0; f < count; f++) + { + _machine.CPU.TotalExecutedCycles += contentionTable[CurrentTStateInFrame] + time; + } + } + else + _machine.CPU.TotalExecutedCycles += count * time; + } + + /// + /// Resets render state once interrupt is generated + /// + public void ULAUpdateStart() + { + ULAByteCtr = 0; + screenByteCtr = DisplayStart; + lastTState = actualULAStart; + } + + /// + /// Builds the T-State to attribute map used with the floating bus + /// + public void BuildAttributeMap() + { + int start = DisplayStart; + + for (int f = 0; f < DisplayLength; f++, start++) + { + int addrH = start >> 8; //div by 256 + int addrL = start % 256; + + int pixelY = (addrH & 0x07); + pixelY |= (addrL & (0xE0)) >> 2; + pixelY |= (addrH & (0x18)) << 3; + + int attrIndex_Y = AttributeStart + ((pixelY >> 3) << 5);// pixel/8 * 32 + + addrL = start % 256; + int pixelX = addrL & (0x1F); + + attr[f] = (short)(attrIndex_Y + pixelX); + } + } + + public virtual void UpdateScreenBuffer(int _tstates) + { + if (_tstates < actualULAStart) + { + return; + } + else if (_tstates >= FrameLength) + { + _tstates = FrameLength - 1; + } + + //the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna + elapsedTStates = (_tstates + 1 - lastTState); + + //It takes 4 tstates to write 1 byte. Or, 2 pixels per t-state. + + int numBytes = (elapsedTStates >> 2) + ((elapsedTStates % 4) > 0 ? 1 : 0); + + int pixelData; + int pixel2Data = 0xff; + int attrData; + int attr2Data; + int bright; + int ink; + int paper; + int flash; + + for (int i = 0; i < numBytes; i++) + { + if (tstateToDisp[lastTState] > 1) + { + screenByteCtr = tstateToDisp[lastTState] - 16384; //adjust for actual screen offset + + pixelData = _machine.FetchScreenMemory((ushort)screenByteCtr); //screen[screenByteCtr]; + attrData = _machine.FetchScreenMemory((ushort)(attr[screenByteCtr] - 16384)); //screen[attr[screenByteCtr] - 16384]; + + lastPixelValue = pixelData; + lastAttrValue = attrData; + + bright = (attrData & 0x40) >> 3; + flash = (attrData & 0x80) >> 7; + ink = (attrData & 0x07); + paper = ((attrData >> 3) & 0x7); + int paletteInk = ULAPalette[ink + bright]; + int palettePaper = ULAPalette[paper + bright]; + + if (flashOn && (flash != 0)) //swap paper and ink when flash is on + { + int temp = paletteInk; + paletteInk = palettePaper; + palettePaper = temp; + } + + for (int a = 0; a < 8; ++a) + { + if ((pixelData & 0x80) != 0) + { + ScreenBuffer[ULAByteCtr++] = paletteInk; + lastAttrValue = ink; + //pixelIsPaper = false; + } + else + { + ScreenBuffer[ULAByteCtr++] = palettePaper; + lastAttrValue = paper; + } + pixelData <<= 1; + } + } + else if (tstateToDisp[lastTState] == 1) + { + int bor = ULAPalette[borderColour]; + + for (int g = 0; g < 8; g++) + ScreenBuffer[ULAByteCtr++] = bor; + } + lastTState += 4; + } + } + + #endregion + + #region IVideoProvider + + private int _virtualWidth; + private int _virtualHeight; + private int _bufferWidth; + private int _bufferHeight; + + public int BackgroundColor + { + get { return ULAPalette[borderColour]; } + } + + public int VirtualWidth + { + get { return _virtualWidth; } + set { _virtualWidth = value; } + } + + public int VirtualHeight + { + get { return _virtualHeight; } + set { _virtualHeight = value; } + } + + public int BufferWidth + { + get { return _bufferWidth; } + set { _bufferWidth = value; } + } + + public int BufferHeight + { + get { return _bufferHeight; } + set { _bufferHeight = value; } + } + + public int VsyncNumerator + { + get { return ClockSpeed; } + set { } + } + + public int VsyncDenominator + { + get { return FrameLength; } + } + + public int[] GetVideoBuffer() + { + return ScreenBuffer; + } + + #endregion + + + #region Attribution + + /* + * Based on code from ArjunNair's Zero emulator (MIT Licensed) + * https://github.com/ArjunNair/Zero-Emulator + + The MIT License (MIT) + + Copyright (c) 2009 Arjun Nair + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + documentation files (the "Software"), to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + #endregion + } +} 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 3cf3b09390..8ec377b16a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -63,6 +63,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var bank = Memory[divisor]; var index = addr % 0x4000; bank[index] = value; + + // update ULA screen buffer if necessary + if ((addr & 49152) == 16384) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } /// @@ -96,13 +100,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Do nothing - we cannot write to ROM return; } + /* else if (addr < 0xC000) { // possible contended RAM var delay = GetContentionValue(CurrentFrameCycle); CPU.TotalExecutedCycles += delay; } + */ + // apply contention if necessry + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + 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 2a0080e1c8..aa92a35464 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -132,13 +132,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x01) == 0; - ContendPort(port); + ULADevice.Contend(port); // 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 @@ -148,13 +149,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - BorderColour = value & BORDER_BIT; + if (ULADevice.borderColour != (value & BORDER_BIT)) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + + ULADevice.borderColour = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); // Tape TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + + CPU.TotalExecutedCycles += 3; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs new file mode 100644 index 0000000000..d6ca8b4565 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs @@ -0,0 +1,184 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class ULA48 : ULABase + { + #region Construction + + public ULA48(SpectrumBase machine) + : base(machine) + { + InterruptPeriod = 32; + FrameLength = 69888; + ClockSpeed = 3500000; + + contentionTable = new byte[70930]; + floatingBusTable = new short[70930]; + for (int f = 0; f < 70930; f++) + floatingBusTable[f] = -1; + + CharRows = 24; + CharCols = 32; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + DisplayStart = 16384; + DisplayLength = 6144; + AttributeStart = 22528; + AttributeLength = 768; + borderColour = 7; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + TstatesPerScanline = 224; + TstateAtTop = BorderTopHeight * TstatesPerScanline; + TstateAtBottom = BorderBottomHeight * TstatesPerScanline; + tstateToDisp = new short[FrameLength]; + + ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border + + ScanLineWidth * ScreenHeight //border + main + border of 192 lines + + ScanLineWidth * BorderBottomHeight]; //56 lines of border + + attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped + + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + + + + Reset(); + } + + #endregion + + #region Misc Operations + + public override void Reset() + { + contentionStartPeriod = 14335; // + LateTiming; + contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); + screen = _machine.Memory[1]; + screenByteCtr = DisplayStart; + ULAByteCtr = 0; + actualULAStart = 14340 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; + lastTState = actualULAStart; + BuildAttributeMap(); + BuildContentionTable(); + } + + #endregion + + #region Contention Methods + + public override bool IsContended(int addr) + { + if ((addr & 49152) == 16384) + return true; + return false; + } + + public override void BuildContentionTable() + { + int t = contentionStartPeriod; + while (t < contentionEndPeriod) + { + //for 128 t-states + for (int i = 0; i < 128; i += 8) + { + contentionTable[t++] = 6; + contentionTable[t++] = 5; + contentionTable[t++] = 4; + contentionTable[t++] = 3; + contentionTable[t++] = 2; + contentionTable[t++] = 1; + contentionTable[t++] = 0; + contentionTable[t++] = 0; + } + t += (TstatesPerScanline - 128); //24 tstates of right border + left border + 48 tstates of retrace + } + + //build top half of tstateToDisp table + //vertical retrace period + for (t = 0; t < actualULAStart; t++) + tstateToDisp[t] = 0; + + //next 48 are actual border + while (t < actualULAStart + (TstateAtTop)) + { + //border(24t) + screen (128t) + border(24t) = 176 tstates + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + //horizontal retrace + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + + //build middle half of display + int _x = 0; + int _y = 0; + int scrval = 2; + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) + { + //left border + for (int g = 0; g < 24; g++) + tstateToDisp[t++] = 1; + + //screen + for (int g = 24; g < 24 + 128; g++) + { + //Map screenaddr to tstate + if (g % 4 == 0) + { + scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); + _x += 8; + } + tstateToDisp[t++] = (short)scrval; + } + _y++; + + //right border + for (int g = 24 + 128; g < 24 + 128 + 24; g++) + tstateToDisp[t++] = 1; + + //horizontal retrace + for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 48; g++) + tstateToDisp[t++] = 0; + } + + int h = contentionStartPeriod + 3; + while (h < contentionEndPeriod + 3) + { + for (int j = 0; j < 128; j += 8) + { + floatingBusTable[h] = tstateToDisp[h + 2]; //screen address + floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; //attr address + floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; //screen address + 1 + floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; //attr address + 1 + h += 8; + } + h += TstatesPerScanline - 128; + } + + //build bottom border + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) + { + //border(24t) + screen (128t) + border(24t) = 176 tstates + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + //horizontal retrace + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + } + + #endregion + + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 81e1b07258..42dbbeaf49 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -22,7 +22,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CPU = cpu; ReInitMemory(); - + + ULADevice = new ULA48(this); + InitScreenConfig(borderType); InitScreen(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 75bb2fcca0..ac8fd29229 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -78,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Register(_tracer); ser.Register(_cpu); - ser.Register(_machine); + ser.Register(_machine.ULADevice); SoundMixer = new SoundProviderMixer(_machine.BuzzerDevice); if (_machine.AYDevice != null) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index ce9e736487..c0147c1d3a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -23,9 +23,12 @@ At this moment this is still *very* experimental and needs a lot more work. ### Not working * IDebuggable +* ZX Spectrum Plus3 emulation * Default keyboard keymappings (you have to configure yourself in the core controller settings) * Manual tape device control (at the moment the tape device detects when the spectrum goes into 'loadbytes' mode and auto-plays the tape. This is not ideal and manual control should be implemented so the user can start/stop manually, return to zero etc..) * Only standard spectrum tape blocks currently work. Any fancy SpeedLock encoded (and similar) blocks do not ### Known bugs * Audible 'popping' from the emulated buzzer after a load state operation + +-Asnivor From a5b50fe5471e59d656421b9df0baaf3b321ecf42 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 14:08:00 +0000 Subject: [PATCH 034/339] 48k - new ULA implementation - 80% faster --- .../Machine/SpectrumBase.Memory.cs | 13 +-- .../Machine/SpectrumBase.Port.cs | 24 +---- .../Machine/SpectrumBase.Screen.cs | 7 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 78 +++------------- .../SinclairSpectrum/Machine/ULABase.cs | 90 +++++++++++++------ .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 27 +++--- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 18 ++-- .../Machine/ZXSpectrum128K/ZX128.cs | 12 +-- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 31 +++---- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 9 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 12 +-- .../Machine/ZXSpectrum16K/ZX16.cs | 39 +++----- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 28 ++---- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 5 +- .../Machine/ZXSpectrum48K/ZX48.cs | 7 +- 15 files changed, 141 insertions(+), 259 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index d58ef1f6eb..81ee1c819f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -106,17 +106,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Helper function to refresh memory array (probably not the best way to do things) /// public abstract void ReInitMemory(); - - /// - /// Returns the memory contention value for the specified T-State (cycle) - /// The ZX Spectrum memory access is contended when the ULA is accessing the lower 16k of RAM - /// - /// - /// - public virtual byte GetContentionValue(int cycle) - { - var val = _renderingCycleTable[cycle % UlaFrameCycleCount].ContentionDelay; - return val; - } + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index c590901dac..a508f9fa8e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -30,28 +30,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// public abstract void WritePort(ushort port, byte value); - - /// - /// Apply I/O contention if necessary - /// - /// - public virtual void ContendPort(ushort port) - { - var lowBit = (port & 0x0001) != 0; - var ulaHigh = (port & 0xc000) == 0x4000; - var cfc = CurrentFrameCycle; - if (cfc < 1) - cfc = 1; - - if (ulaHigh) - { - CPU.TotalExecutedCycles += GetContentionValue(cfc - 1); - } - else - { - if (!lowBit) - CPU.TotalExecutedCycles += GetContentionValue(cfc); - } - } + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs index 65df1938cc..1d3d1b0895 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs @@ -27,6 +27,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum copies or substantial portions of the Software. */ + /* + /// /// The abstract class that all emulated models will inherit from /// * Screen * @@ -927,7 +929,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /* public int VsyncNumerator => NullVideo.DefaultVsyncNum; public int VsyncDenominator => NullVideo.DefaultVsyncDen; - */ + public int[] GetVideoBuffer() { /* @@ -965,7 +967,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case ZXSpectrum.BorderType.Medium: break; } - */ + return _frameBuffer; } @@ -973,4 +975,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 220c6c1e38..a999933720 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -120,10 +120,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.StartFrame(); if (AYDevice != null) AYDevice.StartFrame(); + PollInput(); - - var curr = CPU.TotalExecutedCycles; - + while (CurrentFrameCycle <= ULADevice.FrameLength) // UlaFrameCycleCount) { // check for interrupt @@ -132,13 +131,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // run a single CPU instruction CPU.ExecuteOne(); - // run a rendering cycle according to the current CPU cycle count - /* - var lastCycle = CurrentFrameCycle; - RenderScreen(LastRenderedULACycle + 1, lastCycle); - LastRenderedULACycle = lastCycle; - */ - // update AY if (AYDevice != null) AYDevice.UpdateSound(CurrentFrameCycle); @@ -146,9 +138,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // we have reached the end of a frame LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; - LastRenderedULACycle = OverFlow; - ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); + // paint the buffer if needed + if (ULADevice.needsPaint) + ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); BuzzerDevice.EndFrame(); @@ -157,16 +150,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum FrameCount++; // setup for next frame - OverFlow = CurrentFrameCycle % UlaFrameCycleCount; ULADevice.ResetInterrupt(); FrameCompleted = true; - - if (FrameCount % FlashToggleFrames == 0) - { - _flashPhase = !_flashPhase; - } - - //RenderScreen(0, OverFlow); } /// @@ -174,8 +159,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void HardReset() { - ResetBorder(); - ResetInterrupt(); + //ResetBorder(); + ULADevice.ResetInterrupt(); } /// @@ -183,8 +168,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void SoftReset() { - ResetBorder(); - ResetInterrupt(); + //ResetBorder(); + ULADevice.ResetInterrupt(); } public void SyncState(Serializer ser) @@ -195,50 +180,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("FrameCount", ref FrameCount); ser.Sync("_frameCycles", ref _frameCycles); ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick); - ser.Sync("LastULAOutByte", ref LastULAOutByte); - ser.Sync("_flashPhase", ref _flashPhase); - ser.Sync("_frameBuffer", ref _frameBuffer, false); - ser.Sync("_flashOffColors", ref _flashOffColors, false); - ser.Sync("_flashOnColors", ref _flashOnColors, false); - ser.Sync("InterruptCycle", ref InterruptCycle); - ser.Sync("InterruptRaised", ref InterruptRaised); - ser.Sync("InterruptRevoked", ref InterruptRevoked); - ser.Sync("UlaFrameCycleCount", ref UlaFrameCycleCount); - ser.Sync("FirstScreenPixelCycle", ref FirstScreenPixelCycle); - ser.Sync("FirstDisplayPixelCycle", ref FirstDisplayPixelCycle); - ser.Sync("FirstPixelCycleInLine", ref FirstPixelCycleInLine); - ser.Sync("AttributeDataPrefetchTime", ref AttributeDataPrefetchTime); - ser.Sync("PixelDataPrefetchTime", ref PixelDataPrefetchTime); - ser.Sync("ScreenLineTime", ref ScreenLineTime); - ser.Sync("NonVisibleBorderRightTime", ref NonVisibleBorderRightTime); - ser.Sync("BorderRightTime", ref BorderRightTime); - ser.Sync("DisplayLineTime", ref DisplayLineTime); - ser.Sync("BorderLeftTime", ref BorderLeftTime); - ser.Sync("HorizontalBlankingTime", ref HorizontalBlankingTime); - ser.Sync("ScreenWidth", ref ScreenWidth); - ser.Sync("BorderRightPixels", ref BorderRightPixels); - ser.Sync("BorderLeftPixels", ref BorderLeftPixels); - ser.Sync("FirstDisplayLine", ref FirstDisplayLine); - ser.Sync("ScreenLines", ref ScreenLines); - ser.Sync("NonVisibleBorderBottomLines", ref NonVisibleBorderBottomLines); - ser.Sync("BorderBottomLines", ref BorderBottomLines); - ser.Sync("BorderTopLines", ref BorderTopLines); - ser.Sync("NonVisibleBorderTopLines", ref NonVisibleBorderTopLines); - ser.Sync("VerticalSyncLines", ref VerticalSyncLines); - ser.Sync("FlashToggleFrames", ref FlashToggleFrames); - ser.Sync("DisplayLines", ref DisplayLines); - ser.Sync("DisplayWidth", ref DisplayWidth); - ser.Sync("_pixelByte1", ref _pixelByte1); - ser.Sync("_pixelByte2", ref _pixelByte2); - ser.Sync("_attrByte1", ref _attrByte1); - ser.Sync("_attrByte2", ref _attrByte2); - ser.Sync("_xPos", ref _xPos); - ser.Sync("_yPos", ref _yPos); - ser.Sync("DisplayWidth", ref DisplayWidth); - ser.Sync("DisplayWidth", ref DisplayWidth); - ser.Sync("DisplayWidth", ref DisplayWidth); - ser.Sync("DisplayWidth", ref DisplayWidth); - ser.Sync("_borderColour", ref _borderColour); + ser.Sync("LastULAOutByte", ref LastULAOutByte); ser.Sync("ROM0", ref ROM0, false); ser.Sync("ROM1", ref ROM1, false); ser.Sync("ROM2", ref ROM2, false); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 015980b038..9bebc31eac 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -6,11 +6,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// /// Another ULA implementation (maybe it will be more performant & accurate) + /// -edit: it is :) /// public abstract class ULABase : IVideoProvider { - #region ULA Configuration - #region General /// @@ -65,15 +64,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - #region Interrupts - - /// - /// The number of T-States that the INT pin is simulated to be held low - /// - public int InterruptPeriod; - - #endregion - #region Contention /// @@ -142,6 +132,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// protected bool flashOn = false; + + protected int flashCounter; + public int FlashCounter + { + get { return flashCounter; } + set + { + flashCounter = value; + } + } + + + /// + /// Internal frame counter used for flasher operations + /// + protected int frameCounter = 0; + /// /// Last 8-bit bitmap read from display memory /// (Floating bus implementation) @@ -230,10 +237,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// protected int AttributeLength; + /// + /// Raised when ULA has finished painting the entire screen + /// + public bool needsPaint = false; + #endregion #region Interrupt + /// + /// The number of T-States that the INT pin is simulated to be held low + /// + public int InterruptPeriod; + /// /// The longest instruction cycle count /// @@ -282,9 +299,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // 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; - //CPU.IFF1 = true; _machine.CPU.FlagI = false; - //CPU.NonMaskableInterruptPending = true; } @@ -295,23 +310,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return; } - // if CPU is masking the interrupt do not raise it - //if (!CPU.IFF1) - //return; - // Raise the interrupt InterruptRaised = true; - //CPU.IFF1 = false; - //CPU.IFF2 = false; _machine.CPU.FlagI = true; - //FrameCount++; - ULAUpdateStart(); + // Signal the start of ULA processing + ULAUpdateStart(); } - #endregion - - #endregion + #endregion #region Construction & Initialisation @@ -320,11 +327,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine = machine; } - public virtual void Init() - { - - } - #endregion #region Methods @@ -379,6 +381,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULAByteCtr = 0; screenByteCtr = DisplayStart; lastTState = actualULAStart; + needsPaint = true; + + flashCounter++; + + if (flashCounter > 15) + { + flashOn = !flashOn; + flashCounter = 0; + } } /// @@ -406,6 +417,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + /// + /// Updates the screen buffer based on the number of T-States supplied + /// + /// public virtual void UpdateScreenBuffer(int _tstates) { if (_tstates < actualULAStart) @@ -415,6 +430,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else if (_tstates >= FrameLength) { _tstates = FrameLength - 1; + + needsPaint = true; } //the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna @@ -542,6 +559,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + #region IStatable + + public void SyncState(Serializer ser) + { + ser.BeginSection("ULA"); + ser.Sync("ScreenBuffer", ref ScreenBuffer, false); + ser.Sync("FrameLength", ref FrameLength); + ser.Sync("ClockSpeed", ref ClockSpeed); + ser.Sync("LateTiming", ref LateTiming); + ser.Sync("borderColour", ref borderColour); + ser.EndSection(); + } + + #endregion + #region Attribution diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index 5b8ef31e19..f1a63b0b24 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -168,6 +168,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum default: break; } + + // update ULA screen buffer if necessary + if ((addr & 49152) == 16384) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } /// @@ -178,13 +182,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + var data = ReadBus(addr); - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } return data; } @@ -196,17 +197,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - else if (addr < 0xC000) - { - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } + // apply contention if necessary + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; WriteBus(addr, value); } 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 67e667defb..0997bf3947 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -8,8 +8,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZX128 : SpectrumBase { - private int AYTStates = 0; - /// /// Reads a byte of data from a specified port address /// @@ -23,7 +21,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; - ContendPort((ushort)port); + ULADevice.Contend(port); + CPU.TotalExecutedCycles++; // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) @@ -124,9 +123,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // if unused port the floating memory bus should be returned (still todo) } - - CPU.TotalExecutedCycles += 3; - + return (byte)result; } @@ -170,13 +167,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x01) == 0; - ContendPort(port); + ULADevice.Contend(port); // 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 @@ -186,13 +184,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - BorderColour = value & BORDER_BIT; + if (ULADevice.borderColour != (value & BORDER_BIT)) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + + ULADevice.borderColour = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); // Tape TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } // Active AY Register diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 9a8dfbc55b..65117acdfe 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -29,19 +29,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // init addressable memory from ROM and RAM banks ReInitMemory(); - //DisplayLineTime = 132; - //VsyncNumerator = 3546900 * 2; - - InitScreenConfig(borderType); - InitScreen(); - - ResetULACycle(); + ULADevice = new ULA48(this); BuzzerDevice = new Buzzer(this); - BuzzerDevice.Init(44100, UlaFrameCycleCount); + BuzzerDevice.Init(44100, ULADevice.FrameLength); AYDevice = new AY38912(); - AYDevice.Init(44100, UlaFrameCycleCount); + AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index fca4961bd9..d236edc82e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -307,7 +307,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum default: break; } - } + } + + // update ULA screen buffer if necessary + if ((addr & 49152) == 16384) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } /// @@ -318,13 +322,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + var data = ReadBus(addr); - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } return data; } @@ -336,18 +337,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - else if (addr < 0xC000) - { - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - + // apply contention if necessary + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + WriteBus(addr, value); } 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 e7f2071fca..4a5df60bc8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -21,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; - ContendPort((ushort)port); + ULADevice.Contend(port); // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) @@ -141,7 +141,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x01) == 0; - ContendPort(port); + ULADevice.Contend(port); // Only even addresses address the ULA if (lowBitReset) @@ -157,7 +157,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - BorderColour = value & BORDER_BIT; + if (ULADevice.borderColour != (value & BORDER_BIT)) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + + ULADevice.borderColour = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 31f71db4c0..5904dc41de 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -29,19 +29,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // init addressable memory from ROM and RAM banks ReInitMemory(); - //DisplayLineTime = 132; - //VsyncNumerator = 3546900; - - InitScreenConfig(borderType); - InitScreen(); - - ResetULACycle(); + ULADevice = new ULA48(this); BuzzerDevice = new Buzzer(this); - BuzzerDevice.Init(44100, UlaFrameCycleCount); + BuzzerDevice.Init(44100, ULADevice.FrameLength); AYDevice = new AY38912(); - AYDevice.Init(44100, UlaFrameCycleCount); + AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index 5fc4d6badd..b828eb9b48 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -94,20 +94,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + + CPU.TotalExecutedCycles += 3; + var data = ReadBus(addr); - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - if (addr >= 0x8000) - { - data = 0xFF; - } - else - { - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - } return data; } @@ -119,22 +111,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - else if (addr >= 0x8000) - { - // memory does not exist - return; - } - else if (addr < 0x8000) - { - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } + // apply contention if necessary + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + + CPU.TotalExecutedCycles += 3; WriteBus(addr, value); } 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 8ec377b16a..0a1efe104e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -77,13 +77,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + + var data = ReadBus(addr); - if ((addr & 0xC000) == 0x4000) - { - // addr is in RAM not ROM - apply memory contention if neccessary - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } return data; } @@ -95,24 +93,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - if (addr < 0x4000) - { - // Do nothing - we cannot write to ROM - return; - } - /* - else if (addr < 0xC000) - { - // possible contended RAM - var delay = GetContentionValue(CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - */ - - // apply contention if necessry + // apply contention if necessary if (ULADevice.IsContended(addr)) CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + 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 aa92a35464..c5bfd6f4d8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -21,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; - ContendPort((ushort)port); + ULADevice.Contend(port); // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) @@ -159,8 +159,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Tape TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); - - CPU.TotalExecutedCycles += 3; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 42dbbeaf49..f7a447dc35 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -25,13 +25,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new ULA48(this); - InitScreenConfig(borderType); - InitScreen(); - - ResetULACycle(); - BuzzerDevice = new Buzzer(this); - BuzzerDevice.Init(44100, UlaFrameCycleCount); + BuzzerDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); From 12f5df2b058de04d0f44c4b02151a8d36a445c17 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 14:33:47 +0000 Subject: [PATCH 035/339] Added new ULA implementation for 128k and plus2 --- .../BizHawk.Emulation.Cores.csproj | 3 +- .../Machine/SpectrumBase.Screen.cs | 979 ------------------ .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 +- .../SinclairSpectrum/Machine/ULABase.cs | 2 +- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 12 - .../Machine/ZXSpectrum128K/ZX128.ULA.cs | 194 ++++ 6 files changed, 197 insertions(+), 995 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index da148d52e1..10ba63cfb3 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -272,6 +272,7 @@ + @@ -288,7 +289,6 @@ - @@ -1390,7 +1390,6 @@ - diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs deleted file mode 100644 index 1d3d1b0895..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Screen.cs +++ /dev/null @@ -1,979 +0,0 @@ -using BizHawk.Common; -using BizHawk.Emulation.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - * Much of the SCREEN implementation has been taken from: https://github.com/Dotneteer/spectnetide - * - * MIT License - - Copyright (c) 2017 Istvan Novak - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - */ - - /* - - /// - /// The abstract class that all emulated models will inherit from - /// * Screen * - /// - public abstract partial class SpectrumBase : IVideoProvider - { - #region State - - /// - /// The main screen buffer - /// - protected int[] _frameBuffer; - - /// - /// Pixel and attribute info stored while rendering the screen - /// - protected byte _pixelByte1; - protected byte _pixelByte2; - protected byte _attrByte1; - protected byte _attrByte2; - protected int _xPos; - protected int _yPos; - protected int[] _flashOffColors; - protected int[] _flashOnColors; - protected ScreenRenderingCycle[] _renderingCycleTable; - protected bool _flashPhase; - - #endregion - - #region Statics - - /// - /// The standard ULA palette - /// - private static readonly int[] ULAPalette = - { - Colors.ARGB(0x00, 0x00, 0x00), // Black - Colors.ARGB(0x00, 0x00, 0xD7), // Blue - Colors.ARGB(0xD7, 0x00, 0x00), // Red - Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta - Colors.ARGB(0x00, 0xD7, 0x00), // Green - Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan - Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow - Colors.ARGB(0xD7, 0xD7, 0xD7), // White - Colors.ARGB(0x00, 0x00, 0x00), // Bright Black - Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue - Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red - Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta - Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green - Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan - Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow - Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White - }; - - #endregion - - #region ScreenConfig - - /// - /// The number of displayed pixels in a display row - /// - protected int DisplayWidth = 256; - - /// - /// Number of display lines - /// - protected int DisplayLines = 192; - - /// - /// The number of frames after the flash is toggled - /// - protected int FlashToggleFrames = 25; - - /// - /// Number of lines used for vertical sync - /// - protected int VerticalSyncLines = 8; - - /// - /// The number of top border lines that are not visible - /// when rendering the screen - /// - protected int NonVisibleBorderTopLines = 8; - - /// - /// The number of border lines before the display - /// - protected int BorderTopLines = 48; - - /// - /// The number of border lines after the display - /// - protected int BorderBottomLines = 48; - - /// - /// The number of bottom border lines that are not visible - /// when rendering the screen - /// - protected int NonVisibleBorderBottomLines = 8; - - /// - /// The total number of lines in the screen - /// - protected int ScreenLines; - - /// - /// The first screen line that contains the top left display pixel - /// - protected int FirstDisplayLine; - - /// - /// The last screen line that contains the bottom right display pixel - /// - protected int LastDisplayLine; - - /// - /// The number of border pixels to the left of the display - /// - protected int BorderLeftPixels = 48; - - /// - /// The number of border pixels to the right of the display - /// - protected int BorderRightPixels = 48; - - /// - /// The total width of the screen in pixels - /// - protected int ScreenWidth; - - /// - /// Horizontal blanking time (HSync+blanking). - /// Given in Z80 clock cycles. - /// - protected int HorizontalBlankingTime = 40; - - /// - /// The time of displaying left part of the border. - /// Given in Z80 clock cycles. - /// - protected int BorderLeftTime = 24; - - /// - /// The time of displaying a pixel row. - /// Given in Z80 clock cycles. - /// - protected int DisplayLineTime = 128; - - /// - /// The time of displaying right part of the border. - /// Given in Z80 clock cycles. - /// - protected int BorderRightTime = 24; - - /// - /// The time used to render the nonvisible right part of the border. - /// Given in Z80 clock cycles. - /// - protected int NonVisibleBorderRightTime = 8; - - /// - /// The time of displaying a full screen line. - /// Given in Z80 clock cycles. - /// - protected int ScreenLineTime; - - /// - /// The time the data of a particular pixel should be prefetched - /// before displaying it. - /// Given in Z80 clock cycles. - /// - protected int PixelDataPrefetchTime = 2; - - /// - /// The time the data of a particular pixel attribute should be prefetched - /// before displaying it. - /// Given in Z80 clock cycles. - /// - protected int AttributeDataPrefetchTime = 1; - - /// - /// The tact within the line that should display the first pixel. - /// Given in Z80 clock cycles. - /// - protected int FirstPixelCycleInLine; - - /// - /// The tact in which the top left pixel should be displayed. - /// Given in Z80 clock cycles. - /// - protected int FirstDisplayPixelCycle; - - /// - /// The tact in which the top left screen pixel (border) should be displayed - /// - protected int FirstScreenPixelCycle; - - /// - /// Defines the number of Z80 clock cycles used for the full rendering - /// of the screen. - /// - public int UlaFrameCycleCount; - - /// - /// The last rendered ULA cycle - /// - public int LastRenderedULACycle; - - - /// - /// This structure defines information related to a particular T-State - /// (cycle) of ULA screen rendering - /// - [StructLayout(LayoutKind.Explicit)] - public struct ScreenRenderingCycle - { - /// - /// Tha rendering phase to be applied for the particular tact - /// - [FieldOffset(0)] - public ScreenRenderingPhase Phase; - - /// - /// Display memory contention delay - /// - [FieldOffset(1)] - public byte ContentionDelay; - - /// - /// Display memory address used in the particular tact - /// - [FieldOffset(2)] - public ushort PixelByteToFetchAddress; - - /// - /// Display memory address used in the particular tact - /// - [FieldOffset(4)] - public ushort AttributeToFetchAddress; - - /// - /// Pixel X coordinate - /// - [FieldOffset(6)] - public ushort XPos; - - /// - /// Pixel Y coordinate - /// - [FieldOffset(8)] - public ushort YPos; - } - - /// - /// This enumeration defines the particular phases of ULA rendering - /// - public enum ScreenRenderingPhase : byte - { - /// - /// The ULA does not do any rendering - /// - None, - - /// - /// The ULA simple sets the border color to display the current pixel. - /// - Border, - - /// - /// The ULA sets the border color to display the current pixel. It - /// prepares to display the fist pixel in the row with prefetching the - /// corresponding byte from the display memory. - /// - BorderAndFetchPixelByte, - - /// - /// The ULA sets the border color to display the current pixel. It has - /// already fetched the 8 pixel bits to display. It carries on - /// preparing to display the fist pixel in the row with prefetching the - /// corresponding attribute byte from the display memory. - /// - BorderAndFetchPixelAttribute, - - /// - /// The ULA displays the next two pixels of Byte1 sequentially during a - /// single Z80 clock cycle. - /// - DisplayByte1, - - /// - /// The ULA displays the next two pixels of Byte1 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding byte from the - /// display memory. - /// - DisplayByte1AndFetchByte2, - - /// - /// The ULA displays the next two pixels of Byte1 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding attribute from the - /// display memory. - /// - DisplayByte1AndFetchAttribute2, - - /// - /// The ULA displays the next two pixels of Byte2 sequentially during a - /// single Z80 clock cycle. - /// - DisplayByte2, - - /// - /// The ULA displays the next two pixels of Byte2 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding byte from the - /// display memory. - /// - DisplayByte2AndFetchByte1, - - /// - /// The ULA displays the next two pixels of Byte2 sequentially during a - /// single Z80 clock cycle. It prepares to display the pixels of the next - /// byte in the row with prefetching the corresponding attribute from the - /// display memory. - /// - DisplayByte2AndFetchAttribute1 - } - - #endregion - - #region Border - - private int _borderColour; - - /// - /// Gets or sets the ULA border color - /// - public int BorderColour - { - get { return _borderColour; } - set { _borderColour = value & 0x07; } - } - - protected virtual void ResetBorder() - { - BorderColour = 0; - } - - #endregion - - #region Screen Methods - - /// - /// ULA renders the screen between two specified T-States (cycles) - /// - /// - /// - public void RenderScreen(int fromCycle, int toCycle) - { - // Adjust cycle boundaries - fromCycle = fromCycle % UlaFrameCycleCount; - toCycle = toCycle % UlaFrameCycleCount; - - // Do rendering action for cycles based on the rendering phase - for (int curr = fromCycle; curr <= toCycle; curr++) - { - var ulaCycle = _renderingCycleTable[curr]; - _xPos = ulaCycle.XPos; - _yPos = ulaCycle.YPos; - - switch (ulaCycle.Phase) - { - case ScreenRenderingPhase.None: - // --- Invisible screen area, nothing to do - break; - - case ScreenRenderingPhase.Border: - // --- Fetch the border color from ULA and set the corresponding border pixels - SetPixels(BorderColour, BorderColour); - break; - - case ScreenRenderingPhase.BorderAndFetchPixelByte: - // --- Fetch the border color from ULA and set the corresponding border pixels - SetPixels(BorderColour, BorderColour); - // --- Obtain the future pixel byte - _pixelByte1 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); - break; - - case ScreenRenderingPhase.BorderAndFetchPixelAttribute: - // --- Fetch the border color from ULA and set the corresponding border pixels - SetPixels(BorderColour, BorderColour); - // --- Obtain the future attribute byte - _attrByte1 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte1: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte1 & 0x80, _attrByte1), - GetColor(_pixelByte1 & 0x40, _attrByte1)); - // --- Shift in the subsequent bits - _pixelByte1 <<= 2; - break; - - case ScreenRenderingPhase.DisplayByte1AndFetchByte2: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte1 & 0x80, _attrByte1), - GetColor(_pixelByte1 & 0x40, _attrByte1)); - // --- Shift in the subsequent bits - _pixelByte1 <<= 2; - // --- Obtain the next pixel byte - _pixelByte2 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte1AndFetchAttribute2: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte1 & 0x80, _attrByte1), - GetColor(_pixelByte1 & 0x40, _attrByte1)); - // --- Shift in the subsequent bits - _pixelByte1 <<= 2; - // --- Obtain the next attribute - _attrByte2 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte2: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte2 & 0x80, _attrByte2), - GetColor(_pixelByte2 & 0x40, _attrByte2)); - // --- Shift in the subsequent bits - _pixelByte2 <<= 2; - break; - - case ScreenRenderingPhase.DisplayByte2AndFetchByte1: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte2 & 0x80, _attrByte2), - GetColor(_pixelByte2 & 0x40, _attrByte2)); - // --- Shift in the subsequent bits - _pixelByte2 <<= 2; - // --- Obtain the next pixel byte - _pixelByte1 = FetchScreenMemory(ulaCycle.PixelByteToFetchAddress); - break; - - case ScreenRenderingPhase.DisplayByte2AndFetchAttribute1: - // --- Display bit 7 and 6 according to the corresponding color - SetPixels( - GetColor(_pixelByte2 & 0x80, _attrByte2), - GetColor(_pixelByte2 & 0x40, _attrByte2)); - // --- Shift in the subsequent bits - _pixelByte2 <<= 2; - // --- Obtain the next attribute - _attrByte1 = FetchScreenMemory(ulaCycle.AttributeToFetchAddress); - break; - } - } - } - - /// - /// Tests whether the specified cycle is in the visible area of the screen. - /// - /// Line index - /// Tacts index within the line - /// - /// True, if the tact is visible on the screen; otherwise, false - /// - public virtual bool IsCycleVisible(int line, int cycleInLine) - { - var firstVisibleLine = VerticalSyncLines + NonVisibleBorderTopLines; - var lastVisibleLine = firstVisibleLine + BorderTopLines + DisplayLines + BorderBottomLines; - return - line >= firstVisibleLine - && line < lastVisibleLine - && cycleInLine >= HorizontalBlankingTime - && cycleInLine < ScreenLineTime - NonVisibleBorderRightTime; - } - - /// - /// Tests whether the cycle is in the display area of the screen. - /// - /// Line index - /// Tacts index within the line - /// - /// True, if the tact is within the display area of the screen; otherwise, false. - /// - public virtual bool IsCycleInDisplayArea(int line, int cycleInLine) - { - return line >= FirstDisplayLine - && line <= LastDisplayLine - && cycleInLine >= FirstPixelCycleInLine - && cycleInLine < FirstPixelCycleInLine + DisplayLineTime; - } - - /// - /// Sets the two adjacent screen pixels belonging to the specified cycle to the given - /// color - /// - /// Color index of the first pixel - /// Color index of the second pixel - protected virtual void SetPixels(int colorIndex1, int colorIndex2) - { - var pos = _yPos * ScreenWidth + _xPos; - _frameBuffer[pos++] = ULAPalette[colorIndex1]; - _frameBuffer[pos] = ULAPalette[colorIndex2]; - } - - /// - /// Gets the color index for the specified pixel value according - /// to the given color attribute - /// - /// 0 for paper pixel, non-zero for ink pixel - /// Color attribute - /// - protected virtual int GetColor(int pixelValue, byte attr) - { - var offset = (pixelValue == 0 ? 0 : 0x100) + attr; - return _flashPhase - ? _flashOnColors[offset] - : _flashOffColors[offset]; - } - - /// - /// Resets the ULA cycle to start screen rendering from the beginning - /// - protected virtual void ResetULACycle() - { - LastRenderedULACycle = -1; - } - - /// - /// Initializes the ULA cycle table - /// - protected virtual void InitULACycleTable() - { - _renderingCycleTable = new ScreenRenderingCycle[UlaFrameCycleCount]; - - // loop through every cycle - for (var cycle = 0; cycle < UlaFrameCycleCount; cycle++) - { - var line = cycle / ScreenLineTime; - var cycleInLine = cycle % ScreenLineTime; - - var cycleItem = new ScreenRenderingCycle - { - Phase = ScreenRenderingPhase.None, - ContentionDelay = 0 - }; - - if (IsCycleVisible(line, cycleInLine)) - { - // calculate pixel positions - cycleItem.XPos = (ushort)((cycleInLine - HorizontalBlankingTime) * 2); - cycleItem.YPos = (ushort)(line - VerticalSyncLines - NonVisibleBorderTopLines); - - if (!IsCycleInDisplayArea(line, cycleInLine)) - { - // we are in the border - cycleItem.Phase = ScreenRenderingPhase.Border; - // set the border colour - if (line >= FirstDisplayLine && - line <= LastDisplayLine) - { - if (cycleInLine == FirstPixelCycleInLine - PixelDataPrefetchTime) - { - // left or right border beside the display area - // fetch the first pixel data byte of the current line (2 cycles away) - cycleItem.Phase = ScreenRenderingPhase.BorderAndFetchPixelByte; - cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); - cycleItem.ContentionDelay = 6; - } - else if (cycleInLine == FirstPixelCycleInLine - AttributeDataPrefetchTime) - { - // fetch the first attribute data byte of the current line (1 cycle away) - cycleItem.Phase = ScreenRenderingPhase.BorderAndFetchPixelAttribute; - cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); - cycleItem.ContentionDelay = 5; - } - } - } - else - { - var pixelCycle = cycleInLine - FirstPixelCycleInLine; - // the ULA will perform a different action based on the current cycle (T-State) - switch (pixelCycle & 7) - { - case 0: - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1; - cycleItem.ContentionDelay = 4; - break; - case 1: - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1; - cycleItem.ContentionDelay = 3; - break; - case 2: - // While displaying the current cycle pixels, we need to prefetch the - // pixel data byte 2 cycles away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1AndFetchByte2; - cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); - cycleItem.ContentionDelay = 2; - break; - case 3: - // While displaying the current cycle pixels, we need to prefetch the - // attribute data byte 1 cycle away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte1AndFetchAttribute2; - cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); - cycleItem.ContentionDelay = 1; - break; - case 4: - case 5: - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; - break; - case 6: - if (cycleInLine < FirstPixelCycleInLine + DisplayLineTime - 2) - { - // There are still more bytes to display in this line. - // While displaying the current cycle pixels, we need to prefetch the - // pixel data byte 2 cycles away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2AndFetchByte1; - cycleItem.PixelByteToFetchAddress = CalculatePixelByteAddress(line, cycleInLine + 2); - cycleItem.ContentionDelay = 6; - } - else - { - // Last byte in this line. - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; - } - break; - case 7: - if (cycleInLine < FirstPixelCycleInLine + DisplayLineTime - 1) - { - // There are still more bytes to display in this line. - // While displaying the current cycle pixels, we need to prefetch the - // attribute data byte 1 cycle away - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2AndFetchAttribute1; - cycleItem.AttributeToFetchAddress = CalculateAttributeAddress(line, cycleInLine + 1); - cycleItem.ContentionDelay = 5; - } - else - { - // Last byte in this line. - // Display the current cycle pixels - cycleItem.Phase = ScreenRenderingPhase.DisplayByte2; - } - break; - } - } - } - - // Store the calulated cycle item - _renderingCycleTable[cycle] = cycleItem; - } - } - - /// - /// Calculates the pixel address for the specified line and tact within - /// the line - /// - /// Line index - /// Tacts index within the line - /// ZX spectrum screen memory address - /// - /// Memory address bits: - /// C0-C2: pixel count within a byte -- not used in address calculation - /// C3-C7: pixel byte within a line - /// V0-V7: pixel line address - /// - /// Direct Pixel Address (da) - /// ================================================================= - /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | - /// ================================================================= - /// | 0 | 0 | 0 |V7 |V6 |V5 |V4 |V3 |V2 |V1 |V0 |C7 |C6 |C5 |C4 |C3 | - /// ================================================================= - /// | 1 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0xF81F - /// ================================================================= - /// | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0x0700 - /// ================================================================= - /// | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0x00E0 - /// ================================================================= - /// - /// Spectrum Pixel Address - /// ================================================================= - /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | - /// ================================================================= - /// | 0 | 0 | 0 |V7 |V6 |V2 |V1 |V0 |V5 |V4 |V3 |C7 |C6 |C5 |C4 |C3 | - /// ================================================================= - /// - protected virtual ushort CalculatePixelByteAddress(int line, int cycleInLine) - { - var row = line - FirstDisplayLine; - var column = 2 * (cycleInLine - (HorizontalBlankingTime + BorderLeftTime)); - var da = 0x4000 | (column >> 3) | (row << 5); - return (ushort)((da & 0xF81F) // --- Reset V5, V4, V3, V2, V1 - | ((da & 0x0700) >> 3) // --- Keep V5, V4, V3 only - | ((da & 0x00E0) << 3)); // --- Exchange the V2, V1, V0 bit - // --- group with V5, V4, V3 - } - - /// - /// Calculates the pixel attribute address for the specified line and - /// tact within the line - /// - /// Line index - /// Tacts index within the line - /// ZX spectrum screen memory address - /// - /// Memory address bits: - /// C0-C2: pixel count within a byte -- not used in address calculation - /// C3-C7: pixel byte within a line - /// V0-V7: pixel line address - /// - /// Spectrum Attribute Address - /// ================================================================= - /// |A15|A14|A13|A12|A11|A10|A9 |A8 |A7 |A6 |A5 |A4 |A3 |A2 |A1 |A0 | - /// ================================================================= - /// | 0 | 1 | 0 | 1 | 1 | 0 |V7 |V6 |V5 |V4 |V3 |C7 |C6 |C5 |C4 |C3 | - /// ================================================================= - /// - protected virtual ushort CalculateAttributeAddress(int line, int cycleInLine) - { - var row = line - FirstDisplayLine; - var column = 2 * (cycleInLine - (HorizontalBlankingTime + BorderLeftTime)); - var da = (column >> 3) | ((row >> 3) << 5); - return (ushort)(0x5800 + da); - } - - #endregion - - #region Initialisation - - /// - /// Initialises the screen configuration calculations - /// - public virtual void InitScreenConfig(ZXSpectrum.BorderType border_type) - { - switch (border_type) - { - case ZXSpectrum.BorderType.Full: - BorderTopLines = 48; - BorderBottomLines = 48; - NonVisibleBorderTopLines = 8; - NonVisibleBorderBottomLines = 8; - break; - - case ZXSpectrum.BorderType.Widescreen: - BorderTopLines = 0; - BorderBottomLines = 0; - NonVisibleBorderTopLines = 8 + 48; - NonVisibleBorderBottomLines = 8 + 48; - break; - } - - ScreenLines = BorderTopLines + DisplayLines + BorderBottomLines; - FirstDisplayLine = VerticalSyncLines + NonVisibleBorderTopLines + BorderTopLines; - LastDisplayLine = FirstDisplayLine + DisplayLines - 1; - ScreenWidth = BorderLeftPixels + DisplayWidth + BorderRightPixels; - FirstPixelCycleInLine = HorizontalBlankingTime + BorderLeftTime; - ScreenLineTime = FirstPixelCycleInLine + DisplayLineTime + BorderRightTime + NonVisibleBorderRightTime; - UlaFrameCycleCount = (FirstDisplayLine + DisplayLines + BorderBottomLines + NonVisibleBorderTopLines) * ScreenLineTime; - FirstScreenPixelCycle = (VerticalSyncLines + NonVisibleBorderTopLines) * ScreenLineTime + HorizontalBlankingTime; - } - - /// - /// Inits the screen - /// - protected virtual void InitScreen() - { - //BorderDevice.Reset(); - _flashPhase = false; - - _frameBuffer = new int[ScreenWidth * ScreenLines]; - - InitULACycleTable(); - - // --- Calculate color conversion table - _flashOffColors = new int[0x200]; - _flashOnColors = new int[0x200]; - - for (var attr = 0; attr < 0x100; attr++) - { - var ink = (attr & 0x07) | ((attr & 0x40) >> 3); - var paper = ((attr & 0x38) >> 3) | ((attr & 0x40) >> 3); - _flashOffColors[attr] = paper; - _flashOffColors[0x100 + attr] = ink; - _flashOnColors[attr] = (attr & 0x80) != 0 ? ink : paper; - _flashOnColors[0x100 + attr] = (attr & 0x80) != 0 ? paper : ink; - } - - FrameCount = 0; - } - - #endregion - - #region VBLANK Interrupt - - /// - /// The longest instruction cycle count - /// - protected const int LONGEST_OP_CYCLES = 23; - - /// - /// The ULA cycle to raise the interrupt at - /// - protected int InterruptCycle = 32; - - /// - /// Signs that an interrupt has been raised in this frame. - /// - protected bool InterruptRaised; - - /// - /// Signs that the interrupt signal has been revoked - /// - protected bool InterruptRevoked; - - /// - /// Resets the interrupt - this should happen every frame in order to raise - /// the VBLANK interrupt in the proceding frame - /// - public virtual void ResetInterrupt() - { - InterruptRaised = false; - InterruptRevoked = false; - } - - /// - /// Generates an interrupt in the current phase if needed - /// - /// - protected virtual void CheckForInterrupt(int currentCycle) - { - if (InterruptRevoked) - { - // interrupt has already been handled - return; - } - - if (currentCycle < InterruptCycle) - { - // interrupt does not need to be raised yet - return; - } - - if (currentCycle > InterruptCycle + LONGEST_OP_CYCLES) - { - // 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; - //CPU.IFF1 = true; - CPU.FlagI = false; - //CPU.NonMaskableInterruptPending = true; - } - - if (InterruptRaised) - { - // INT is raised but not yet revoked - // CPU has NOT handled it yet - return; - } - - // if CPU is masking the interrupt do not raise it - //if (!CPU.IFF1) - //return; - - // Raise the interrupt - InterruptRaised = true; - //CPU.IFF1 = false; - //CPU.IFF2 = false; - CPU.FlagI = true; - FrameCount++; - } - - #endregion - - #region IVideoProvider - - public int VirtualWidth => ScreenWidth; - public int VirtualHeight => ScreenLines; - public int BufferWidth => ScreenWidth; - public int BufferHeight => ScreenLines; - public int BackgroundColor => ULAPalette[BorderColour]; - - public int VsyncNumerator - { - get { return 3500000; } - set { } - } - - public int VsyncDenominator - { - get { return UlaFrameCycleCount; } - } - /* - public int VsyncNumerator => NullVideo.DefaultVsyncNum; - public int VsyncDenominator => NullVideo.DefaultVsyncDen; - - public int[] GetVideoBuffer() - { - /* - switch(Spectrum.SyncSettings.BorderType) - { - case ZXSpectrum.BorderType.Full: - return _frameBuffer; - - case ZXSpectrum.BorderType.Small: - // leave only 10 border units all around - int[] smlBuff = new int[(ScreenWidth - 30) * (DisplayLines - 30)]; - int index = 0; - int brdCount = 0; - // skip top and bottom - for (int i = ScreenWidth * 30; i < smlBuff.Length - ScreenWidth * 30; i++) - { - if (brdCount < 30) - { - brdCount++; - continue; - } - if (brdCount > ScreenWidth - 30) - { - brdCount++; - continue; - } - - smlBuff[index] = _frameBuffer[i]; - index++; - brdCount++; - } - - return smlBuff; - - case ZXSpectrum.BorderType.Medium: - break; - } - - return _frameBuffer; - - } - - #endregion - - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index a999933720..b9fad2fe5a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // 128 and up only protected int ROMPaged = 0; protected bool SHADOWPaged; - protected int RAMPaged; + public int RAMPaged; protected bool PagingDisabled; // +3/+2A only diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 9bebc31eac..9bdab42b2f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -514,7 +514,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int BackgroundColor { - get { return ULAPalette[borderColour]; } + get { return ULAPalette[7]; } //ULAPalette[borderColour]; } } public int VirtualWidth diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs deleted file mode 100644 index 00b952614b..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public partial class ZX128 : SpectrumBase - { - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs new file mode 100644 index 0000000000..090cd9c7be --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -0,0 +1,194 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class ULA128 : ULABase + { + #region Construction + + public ULA128(SpectrumBase machine) + : base(machine) + { + InterruptPeriod = 36; + FrameLength = 70908; + ClockSpeed = 3546900; + + contentionTable = new byte[70930]; + floatingBusTable = new short[70930]; + for (int f = 0; f < 70930; f++) + floatingBusTable[f] = -1; + + CharRows = 24; + CharCols = 32; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + DisplayStart = 16384; + DisplayLength = 6144; + AttributeStart = 22528; + AttributeLength = 768; + borderColour = 7; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + TstatesPerScanline = 228; + TstateAtTop = BorderTopHeight * TstatesPerScanline; + TstateAtBottom = BorderBottomHeight * TstatesPerScanline; + tstateToDisp = new short[FrameLength]; + + ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border + + ScanLineWidth * ScreenHeight //border + main + border of 192 lines + + ScanLineWidth * BorderBottomHeight]; //56 lines of border + + attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped + + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + + Reset(); + } + + #endregion + + #region Misc Operations + + public override void Reset() + { + contentionStartPeriod = 14361; // + LateTiming; + contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); + screen = _machine.Memory[1]; + screenByteCtr = DisplayStart; + ULAByteCtr = 0; + actualULAStart = 14366 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; + lastTState = actualULAStart; + BuildAttributeMap(); + BuildContentionTable(); + } + + #endregion + + #region Contention Methods + + public override bool IsContended(int addr) + { + addr = addr & 0xc000; + + if (addr == 0x4000) + { + // low port contention + return true; + } + + if (addr == 0xc000) + { + // high port contention - check for contended bank paged in + switch (_machine.RAMPaged) + { + case 1: + case 3: + case 5: + case 7: + return true; + } + } + + return false; + } + + public override void BuildContentionTable() + { + int t = contentionStartPeriod; + while (t < contentionEndPeriod) + { + //for 128 t-states + for (int i = 0; i < 128; i += 8) + { + contentionTable[t++] = 6; + contentionTable[t++] = 5; + contentionTable[t++] = 4; + contentionTable[t++] = 3; + contentionTable[t++] = 2; + contentionTable[t++] = 1; + contentionTable[t++] = 0; + contentionTable[t++] = 0; + } + t += (TstatesPerScanline - 128); + } + + //build top half of tstateToDisp table + //vertical retrace period + for (t = 0; t < actualULAStart; t++) + tstateToDisp[t] = 0; + + //next 48 are actual border + while (t < actualULAStart + (TstateAtTop)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + + //build middle half + int _x = 0; + int _y = 0; + int scrval = 2; + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) + { + for (int g = 0; g < 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24; g < 24 + 128; g++) + { + //Map screenaddr to tstate + if (g % 4 == 0) + { + scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); + _x += 8; + } + tstateToDisp[t++] = (short)scrval; + } + _y++; + + for (int g = 24 + 128; g < 24 + 128 + 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++) + tstateToDisp[t++] = 0; + } + + int h = contentionStartPeriod + 3; + while (h < contentionEndPeriod + 3) + { + for (int j = 0; j < 128; j += 8) + { + floatingBusTable[h] = tstateToDisp[h + 2]; + floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; + floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; + floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; + h += 8; + } + h += TstatesPerScanline - 128; + } + + //build bottom half + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + } + + + #endregion + + + } +} From a9d179d83a0decae226ab3eb1c222a2716e3ebf4 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 14:35:27 +0000 Subject: [PATCH 036/339] Added ULA state serialization --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index b9fad2fe5a..516b7d4b4d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -203,6 +203,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RomData.SyncState(ser); KeyboardDevice.SyncState(ser); BuzzerDevice.SyncState(ser); + ULADevice.SyncState(ser); if (AYDevice != null) AYDevice.SyncState(ser); From 2759f65b1af2b0c35b906db73da3bec6e94f15de Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 16:05:36 +0000 Subject: [PATCH 037/339] Added more border configuration options --- .../SinclairSpectrum/Machine/ULABase.cs | 150 ++++++++++++++++++ .../Machine/ZXSpectrum128K/ZX128.ULA.cs | 5 +- .../Machine/ZXSpectrum128K/ZX128.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.ULA.cs | 7 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 15 ++ 5 files changed, 168 insertions(+), 11 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 9bdab42b2f..657a255adf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -325,6 +325,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public ULABase(SpectrumBase machine) { _machine = machine; + borderType = _machine.Spectrum.SyncSettings.BorderType; } #endregion @@ -554,9 +555,158 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int[] GetVideoBuffer() { + switch (borderType) + { + // Full side borders, no top or bottom border (giving *almost* 16:9 output) + case ZXSpectrum.BorderType.Widescreen: + // we are cropping out the top and bottom borders + var startPixelsToCrop = ScanLineWidth * BorderTopHeight; + var endPixelsToCrop = ScanLineWidth * BorderBottomHeight; + int index = 0; + for (int i = startPixelsToCrop; i < ScreenBuffer.Length - endPixelsToCrop; i++) + { + croppedBuffer[index++] = ScreenBuffer[i]; + } + return croppedBuffer; + + // The full spectrum border + case ZXSpectrum.BorderType.Full: + return ScreenBuffer; + + case ZXSpectrum.BorderType.Medium: + // all border sizes now 24 + var lR = BorderLeftWidth - 24; + var rR = BorderRightWidth - 24; + var tR = BorderTopHeight - 24; + var bR = BorderBottomHeight - 24; + var startP = ScanLineWidth * tR; + var endP = ScanLineWidth * bR; + + int index2 = 0; + // line by line + for (int i = startP; i < ScreenBuffer.Length - endP; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR; p++) + { + if (index2 == croppedBuffer.Length) + break; + croppedBuffer[index2++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + + case ZXSpectrum.BorderType.Small: + // all border sizes now 24 + var lR_ = BorderLeftWidth - 10; + var rR_ = BorderRightWidth - 10; + var tR_ = BorderTopHeight - 10; + var bR_ = BorderBottomHeight - 10; + var startP_ = ScanLineWidth * tR_; + var endP_ = ScanLineWidth * bR_; + + int index2_ = 0; + // line by line + for (int i = startP_; i < ScreenBuffer.Length - endP_; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR_; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR_; p++) + { + if (index2_ == croppedBuffer.Length) + break; + croppedBuffer[index2_++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + + case ZXSpectrum.BorderType.None: + // all border sizes now 24 + var lR__ = BorderLeftWidth; + var rR__ = BorderRightWidth; + var tR__ = BorderTopHeight; + var bR__ = BorderBottomHeight; + var startP__ = ScanLineWidth * tR__; + var endP__ = ScanLineWidth * bR__; + + int index2__ = 0; + // line by line + for (int i = startP__; i < ScreenBuffer.Length - endP__; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR__; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR__; p++) + { + if (index2__ == croppedBuffer.Length) + break; + croppedBuffer[index2__++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + } + return ScreenBuffer; } + protected void SetupScreenSize() + { + switch (borderType) + { + case ZXSpectrum.BorderType.Full: + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + ScreenBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Widescreen: + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Medium: + BufferWidth = ScreenWidth + (24) + (24); + BufferHeight = ScreenHeight + (24) + (24); + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Small: + BufferWidth = ScreenWidth + (10) + (10); + BufferHeight = ScreenHeight + (10) + (10); + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.None: + BufferWidth = ScreenWidth; + BufferHeight = ScreenHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + } + } + + protected int[] croppedBuffer; + + private ZXSpectrum.BorderType _borderType; + + public ZXSpectrum.BorderType borderType + { + get { return _borderType; } + set { _borderType = value; } + } + + + #endregion #region IStatable diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs index 090cd9c7be..d7cc8f4941 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -43,10 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped - BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; - BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; + SetupScreenSize(); Reset(); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 65117acdfe..74df0bba39 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // init addressable memory from ROM and RAM banks ReInitMemory(); - ULADevice = new ULA48(this); + ULADevice = new ULA128(this); BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs index d6ca8b4565..a093a33aad 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs @@ -43,12 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped - BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; - BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; - - + SetupScreenSize(); Reset(); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index ba6a944346..6266d7e021 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -108,6 +108,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Full, + /// + /// All borders 24px + /// + Medium, + + /// + /// All borders 10px + /// + Small, + + /// + /// No border at all + /// + None, + /// /// Top and bottom border removed so that the result is *almost* 16:9 /// From 3d508455ec4fead9416e39d0db254eb1eaf16155 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Dec 2017 18:00:59 +0000 Subject: [PATCH 038/339] Some floating bus work (although still not working) --- .../SinclairSpectrum/Machine/ULABase.cs | 6 ++--- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 24 ++++++++++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 657a255adf..d6e3b1ef66 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -69,11 +69,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// T-State at which to start applying contention /// - protected int contentionStartPeriod; + public int contentionStartPeriod; /// /// T-State at which to end applying contention /// - protected int contentionEndPeriod; + public int contentionEndPeriod; /// /// T-State memory contention delay mapping /// @@ -102,7 +102,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Table that stores T-State to screen/attribute address values /// - protected short[] floatingBusTable; + public short[] floatingBusTable; /// /// Cycle at which the last render update took place /// 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 c5bfd6f4d8..c8ad708a67 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -30,6 +30,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else if (lowBitReset) { + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + // Even I/O address so get input // The high byte indicates which half-row of keys is being polled /* @@ -115,7 +117,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Kemptson Mouse - // if unused port the floating memory bus should be returned (still todo) + // if unused port the floating memory bus should be returned + + // Floating bus is read on the previous cycle + int _tStates = CurrentFrameCycle - 1; + + // if we are on the top or bottom border return 0xff + if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) + { + result = 0xff; + } + else + { + if (ULADevice.floatingBusTable[_tStates] < 0) + { + result = 0xff; + } + else + { + result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); + } + } } return (byte)result; From 1fb10f3d9c6ede9601d882ab1064a8576636edca Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 15 Jan 2018 12:50:07 +0000 Subject: [PATCH 039/339] Some TapeDevice serialization --- .../BizHawk.Emulation.Cores.csproj | 2 - .../Interfaces/ISupportsTapeBlockPlayback.cs | 6 +- .../SinclairSpectrum/Hardware/Tape.cs | 3 - .../Hardware/TapeBlockSetPlayer.cs | 53 ++++++++++-- .../Hardware/TapeDataBlockPlayer.cs | 81 ++++++++++++++++--- .../Hardware/TapeFilePlayer.cs | 13 ++- .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 +- .../Media/Tape/TAP/TapDataBlock.cs | 17 +++- .../Media/Tape/TAP/TapPlayer.cs | 11 +++ .../Media/Tape/TZX/DataBlocks.cs | 26 +++++- .../Media/Tape/TZX/TzxPlayer.cs | 13 ++- .../SinclairSpectrum/ZXSpectrum.IEmulator.cs | 2 +- .../ZXSpectrum.ISoundProvider.cs | 14 ---- .../ZXSpectrum.IVideoProvider.cs | 13 --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 15 +--- 15 files changed, 204 insertions(+), 67 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 10ba63cfb3..ff25860ed9 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -312,7 +312,6 @@ - @@ -1393,7 +1392,6 @@ - diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs index 969944fa92..8d1c47645b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs @@ -1,4 +1,5 @@ -using System; +using BizHawk.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -35,5 +36,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The EAR bit value to play back /// bool GetEarBit(long currentCycle); + + + void SyncState(Serializer ser); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs index 203fef91b2..fd29eb3855 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs @@ -526,9 +526,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.SyncEnum("_currentMode", ref _currentMode); ser.SyncEnum("_savePhase", ref _savePhase); ser.SyncEnum("_prevDataPulse", ref _prevDataPulse); - /* - private TapeFilePlayer _tapePlayer; - */ ser.EndSection(); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs index c08b488acf..6d5b149f9e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using BizHawk.Common; +using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -15,27 +16,56 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Signs that the player completed playing back the file /// - public bool Eof { get; private set; } + private bool eof; + public bool Eof + { + get { return eof; } + set { eof = value; } + } + /// /// Gets the currently playing block's index /// - public int CurrentBlockIndex { get; private set; } + private int currentBlockIndex; + public int CurrentBlockIndex + { + get { return currentBlockIndex; } + set { currentBlockIndex = value; } + } /// /// The current playable block /// - public ISupportsTapeBlockPlayback CurrentBlock => DataBlocks[CurrentBlockIndex]; + private ISupportsTapeBlockPlayback currentBlock; + public ISupportsTapeBlockPlayback CurrentBlock + { + get { return DataBlocks[CurrentBlockIndex]; } + //set { currentBlock = value; } + } + /// /// The current playing phase /// - public PlayPhase PlayPhase { get; private set; } + private PlayPhase playPhase; + public PlayPhase PlayPhase + { + get { return playPhase; } + set { playPhase = value; } + } + /// /// The cycle count of the CPU when playing starts /// - public long StartCycle { get; private set; } + private long startCycle; + public long StartCycle + { + get { return startCycle; } + set { startCycle = value; } + } + public TapeBlockSetPlayer(List dataBlocks) { @@ -98,5 +128,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CurrentBlockIndex++; CurrentBlock.InitPlay(currentCycle); } + + public void SyncState(Serializer ser) + { + ser.BeginSection("TapeBlockSetPlayer"); + ser.Sync("eof", ref eof); + ser.Sync("currentBlockIndex", ref currentBlockIndex); + ser.SyncEnum("playPhase", ref playPhase); + ser.Sync("startCycle", ref startCycle); + currentBlock.SyncState(ser); + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs index 547d7698b9..0c7dae4f14 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs @@ -1,4 +1,6 @@  +using BizHawk.Common; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// @@ -9,20 +11,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Pause after this block (default: 1000ms) /// - public ushort PauseAfter { get; } + private ushort pauseAfter; + public ushort PauseAfter + { + get { return pauseAfter; } + } /// /// Block Data /// - public byte[] Data { get; } + private byte[] data; + public byte[] Data + { + get { return data; } + } /// /// Initializes a new instance /// - public TapeDataBlockPlayer(byte[] data, ushort pauseAfter) + public TapeDataBlockPlayer(byte[] _data, ushort _pauseAfter) { - PauseAfter = pauseAfter; - Data = data; + pauseAfter = _pauseAfter; + data = _data; } /// @@ -82,27 +92,52 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The index of the currently playing byte /// - public int ByteIndex { get; private set; } + private int byteIndex; + public int ByteIndex + { + get { return byteIndex; } + set { byteIndex = value; } + } /// /// The mask of the currently playing bit in the current byte /// - public byte BitMask { get; private set; } + private byte bitMask; + public byte BitMask + { + get { return bitMask; } + set { bitMask = value; } + } /// /// The current playing phase /// - public PlayPhase PlayPhase { get; private set; } + private PlayPhase playPhase; + public PlayPhase PlayPhase + { + get { return playPhase; } + set { playPhase = value; } + } /// /// The cycle count of the CPU when playing starts /// - public long StartCycle { get; private set; } + private long startCycle; + public long StartCycle + { + get { return startCycle; } + set { startCycle = value; } + } /// /// Last cycle queried /// - public long LastCycle { get; private set; } + private long lastCycle; + public long LastCycle + { + get { return lastCycle; } + set { lastCycle = value; } + } /// /// Initializes the player @@ -214,5 +249,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } return true; } + + + public void SyncState(Serializer ser) + { + ser.BeginSection("TapeDataBlockPlayer"); + + ser.Sync("pauseAfter", ref pauseAfter); + ser.Sync("data", ref data, false); + + ser.Sync("_pilotEnds", ref _pilotEnds); + ser.Sync("_sync1Ends", ref _sync1Ends); + ser.Sync("_sync2Ends", ref _sync2Ends); + ser.Sync("_bitStarts", ref _bitStarts); + ser.Sync("_bitPulseLength", ref _bitPulseLength); + ser.Sync("_currentBit", ref _currentBit); + ser.Sync("_termSyncEnds", ref _termSyncEnds); + ser.Sync("_pauseEnds", ref _pauseEnds); + + ser.Sync("byteIndex", ref byteIndex); + ser.Sync("bitMask", ref bitMask); + ser.SyncEnum("playPhase", ref playPhase); + ser.Sync("startCycle", ref startCycle); + ser.Sync("lastCycle", ref lastCycle); + + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs index 25c75df0f8..92c32dbc95 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs @@ -1,4 +1,5 @@ -using System; +using BizHawk.Common; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -13,6 +14,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { private readonly BinaryReader _reader; private TapeBlockSetPlayer _player; + private int _numberOfDataBlocks; /// /// Data blocks to play back @@ -113,5 +115,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Tacts time to start the next block public void NextBlock(long currentCycle) => _player.NextBlock(currentCycle); + + public void SyncState(Serializer ser) + { + ser.BeginSection("TapeFilePlayer"); + ReadContent(); + ser.Sync("_numberOfDataBlocks", ref _numberOfDataBlocks); + _player.SyncState(ser); + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 516b7d4b4d..a67333aa03 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -142,7 +142,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // paint the buffer if needed if (ULADevice.needsPaint) ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); - + BuzzerDevice.EndFrame(); TapeDevice.CPUFrameCompleted(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs index 3c4858b533..0682b32752 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs @@ -1,4 +1,5 @@  +using BizHawk.Common; using System.IO; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum @@ -16,7 +17,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Block Data /// - public byte[] Data { get; private set; } + private byte[] data; + public byte[] Data + { + get { return data; } + set { data = value; } + } /// /// Pause after this block (given in milliseconds) @@ -86,5 +92,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The EAR bit value to play back /// public bool GetEarBit(long currentTact) => _player.GetEarBit(currentTact); + + public void SyncState(Serializer ser) + { + ser.BeginSection("TapDataBlock"); + + ser.Sync("data", ref data, false); + + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs index faa7d23c02..0c24566e37 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs @@ -1,4 +1,5 @@  +using BizHawk.Common; using System.Collections.Generic; using System.IO; using System.Linq; @@ -81,5 +82,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Tacts time to start the next block public void NextBlock(long currentCycle) => _player.NextBlock(currentCycle); + + + public void SyncState(Serializer ser) + { + ser.BeginSection("TapePlayer"); + + _player.SyncState(ser); + + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs index 18ce828a09..cfd694d544 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs @@ -1,4 +1,5 @@  +using BizHawk.Common; using System.IO; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum @@ -1144,12 +1145,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Lenght of block data /// - public ushort DataLength { get; set; } + private ushort dataLength; + public ushort DataLength + { + get { return dataLength; } + set { dataLength = value; } + } /// /// Block Data /// - public byte[] Data { get; set; } + private byte[] data; + public byte[] Data + { + get { return data; } + set { data = value; } + } + /// /// The ID of the block @@ -1222,6 +1234,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The EAR bit value to play back /// public bool GetEarBit(long currentCycle) => _player.GetEarBit(currentCycle); + + public void SyncState(Serializer ser) + { + ser.BeginSection("TzxStandardSpeedDataBlock"); + + ser.Sync("dataLength", ref dataLength); + ser.Sync("data", ref data, false); + + ser.EndSection(); + } } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs index 55797b80df..a9ac846467 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs @@ -1,4 +1,5 @@ -using System.IO; +using BizHawk.Common; +using System.IO; using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum @@ -79,5 +80,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Tacts time to start the next block public void NextBlock(long currentTact) => _player.NextBlock(currentTact); + + + public void SyncState(Serializer ser) + { + ser.BeginSection("TzxPlayer"); + + _player.SyncState(ser); + + ser.EndSection(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs index b7a1a07d82..8b10763146 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public string SystemId => "ZXSpectrum"; - public bool DeterministicEmulation => false; + public bool DeterministicEmulation => true; public void ResetCounters() { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs deleted file mode 100644 index 402b7f292d..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISoundProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using BizHawk.Emulation.Cores.Components; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public partial class ZXSpectrum - { - private SoundProviderMixer SoundMixer; - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs deleted file mode 100644 index 073236a69d..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IVideoProvider.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Main IVideoProvider implementation is inside the machine classes - /// This is just some helper functions - /// - public partial class ZXSpectrum - { - - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index ac8fd29229..6694a7e97d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -60,9 +60,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; default: throw new InvalidOperationException("Machine not yet emulated"); - } - - + } _cpu.MemoryCallbacks = MemoryCallbacks; @@ -84,14 +82,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - //SoundMixer.DisableSource(_machine.BuzzerDevice); - - dcf = new DCFilter(SoundMixer, 1024); - - - + dcf = new DCFilter(SoundMixer, 256); ser.Register(dcf); - //ser.Register(_machine.AYDevice); @@ -108,11 +100,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public IController _controller; private SpectrumBase _machine; + private SoundProviderMixer SoundMixer; + private DCFilter dcf; private byte[] _file; - public bool DiagRom = false; private byte[] GetFirmware(int length, params string[] names) From d534ee3f5fe7a3d64d97dad382714d4968d9f4de Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Feb 2018 17:22:03 +0000 Subject: [PATCH 040/339] Small settings change --- BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj | 8 ++++++++ .../Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs | 4 ++-- .../Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 537435d739..f8cad71c73 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -258,6 +258,14 @@ + + + + + + + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 6266d7e021..df56bd1b5b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -136,8 +136,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public enum TapeLoadSpeed { Accurate, - Fast, - Fastest + //Fast, + //Fastest } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index a9c407ca14..372cb8ed54 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -53,7 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.SyncState(ser); ser.BeginSection("ZXSpectrum"); - _cpu.SyncState(ser); + //_cpu.SyncState(ser); _machine.SyncState(ser); ser.Sync("Frame", ref _machine.FrameCount); ser.Sync("LagCount", ref _lagCount); From 181a6ba2ab93269e094598fbf85dee90fb9ffdd6 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Feb 2018 17:23:45 +0000 Subject: [PATCH 041/339] fix deleted files --- BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index f8cad71c73..537435d739 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -258,14 +258,6 @@ - - - - - - - - From f9e93cfa2a66762b405c5304e10f65c126c34a0e Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Feb 2018 15:29:21 +0000 Subject: [PATCH 042/339] Starting new tape implementation --- .../BizHawk.Emulation.Cores.csproj | 4 + .../Media/MediaSerializationType.cs | 18 +++ .../SinclairSpectrum/Media/MediaSerializer.cs | 114 ++++++++++++++++++ .../Media/Tape/TapeCommand.cs | 21 ++++ .../Media/Tape/TapeDataBlock.cs | 65 ++++++++++ 5 files changed, 222 insertions(+) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializationType.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 537435d739..8d0bceb934 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -275,6 +275,10 @@ + + + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializationType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializationType.cs new file mode 100644 index 0000000000..def321b245 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializationType.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the different types of media serializer avaiable + /// + public enum MediaSerializationType + { + NONE, + TZX, + TAP + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs new file mode 100644 index 0000000000..b20a6aa8b1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Abtract class that represents all Media Serializers + /// + public abstract class MediaSerializer + { + /// + /// The type of serializer + /// + public abstract MediaSerializationType FormatType { get; } + + /// + /// Signs whether this class can be used to serialize + /// + public virtual bool IsSerializer + { + get + { + return false; + } + } + + /// + /// Signs whether this class can be used to de-serialize + /// + public virtual bool IsDeSerializer + { + get + { + return false; + } + } + + /// + /// Serialization method + /// + /// + public virtual void Serialize(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Serialize operation is not implemented for this serializer"); + } + + /// + /// DeSerialization method + /// + /// + public virtual void DeSerialize(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "DeSerialize operation is not implemented for this serializer"); + } + + #region Static Tools + + /// + /// Converts an int32 value into a byte array + /// + /// + /// + protected static byte[] GetBytes(int value) + { + byte[] buf = new byte[4]; + buf[0] = (byte)value; + buf[1] = (byte)(value >> 8); + buf[2] = (byte)(value >> 16); + buf[3] = (byte)(value >> 24); + return buf; + } + + /// + /// Returns an int32 from a byte array based on offset + /// + /// + /// + /// + protected static int GetInt32(byte[] buf, int offsetIndex) + { + return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24; + } + + /// + /// Returns an uint16 from a byte array based on offset + /// + /// + /// + /// + protected static ushort GetUInt16(byte[] buf, int offsetIndex) + { + return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8); + } + + /// + /// Updates a byte array with a uint16 value based on offset + /// + /// + /// + /// + protected static void setUint16(byte[] buf, int offsetIndex, ushort value) + { + buf[offsetIndex] = (byte)value; + buf[offsetIndex + 1] = (byte)(value >> 8); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs new file mode 100644 index 0000000000..14fc54a460 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the possible commands that can be raised from each tape block + /// + public enum TapeCommand + { + NONE, + STOP_THE_TAPE, + STOP_THE_TAPE_48K, + BEGIN_GROUP, + END_GROUP, + SHOW_MESSAGE, + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs new file mode 100644 index 0000000000..796bee81d9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a tape block + /// + public class TapeDataBlock + { + /// + /// Either the TZX block ID, or -1 in the case of non-tzx blocks + /// + public int BlockID = -1; + + /// + /// Description of the block + /// + public string BlockDescription { get; set; } + + /// + /// Byte array containing the raw block data + /// + public byte[] BlockData = null; + + /// + /// List containing the pulse timing values + /// + public List DataPeriods = new List(); + + /// + /// Command that is raised by this data block + /// (that may or may not need to be acted on) + /// + public TapeCommand Command = TapeCommand.NONE; + + /// + /// Returns the data periods as an array + /// (primarily to aid in bizhawk state serialization) + /// + /// + public int[] GetDataPeriodsArray() + { + return DataPeriods.ToArray(); + } + + /// + /// Accepts an array of data periods and updates the DataPeriods list accordingly + /// (primarily to aid in bizhawk state serialization) + /// + /// + public void SetDataPeriodsArray(int[] periodArray) + { + DataPeriods = new List(); + + if (periodArray == null) + return; + + DataPeriods = periodArray.ToList(); + } + } +} From 42b5f5dc5d2419d392ad7c6508c6545df269580e Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 14 Feb 2018 12:21:02 +0000 Subject: [PATCH 043/339] Tape device re-write and TAP format reading done. Loading state is now fully serializable --- .../BizHawk.Emulation.Cores.csproj | 2 + .../Hardware/Datacorder/DatacorderDevice.cs | 393 ++++++++++++++++++ .../Machine/SpectrumBase.Input.cs | 6 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 6 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 4 +- .../Machine/ZXSpectrum128K/ZX128.cs | 5 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 4 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 5 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 4 +- .../Machine/ZXSpectrum48K/ZX48.cs | 5 +- .../Media/Tape/TapSerializer.cs | 301 ++++++++++++++ .../Media/Tape/TapeDataBlock.cs | 60 ++- 12 files changed, 772 insertions(+), 23 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 8d0bceb934..3b65b45bb8 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -258,6 +258,7 @@ + @@ -279,6 +280,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs new file mode 100644 index 0000000000..8018ac23cc --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -0,0 +1,393 @@ +using BizHawk.Common; +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the tape device (or build-in datacorder as it was called +2 and above) + /// + public class DatacorderDevice + { + #region Construction + + private SpectrumBase _machine { get; set; } + private Z80A _cpu { get; set; } + private Buzzer _buzzer { get; set; } + + /// + /// Default constructor + /// + public DatacorderDevice() + { + + } + + /// + /// Initializes the datacorder device + /// + /// + public void Init(SpectrumBase machine) + { + _machine = machine; + _cpu = _machine.CPU; + _buzzer = machine.BuzzerDevice; + } + + #endregion + + #region State Information + + /// + /// The index of the current tape data block that is loaded + /// + private int _currentDataBlockIndex = 0; + public int CurrentDataBlockIndex + { + get + { + if (_dataBlocks.Count() > 0) { return _currentDataBlockIndex; } + else { return -1; } + } + set + { + if (value == _currentDataBlockIndex) { return; } + if (value < _dataBlocks.Count() && value >= 0) + { + _currentDataBlockIndex = value; + _position = 0; + } + } + } + + /// + /// The current position within the current data block + /// + private int _position = 0; + public int Position + { + get + { + if (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count) { return 0; } + else { return _position; } + } + } + + /// + /// Signs whether the tape is currently playing or not + /// + private bool _tapeIsPlaying = false; + public bool TapeIsPlaying + { + get { return _tapeIsPlaying; } + } + + /// + /// A list of the currently loaded data blocks + /// + private List _dataBlocks = new List(); + public List DataBlocks + { + get { return _dataBlocks; } + set { _dataBlocks = value; } + } + + /// + /// Stores the last CPU t-state value + /// + private long _lastCycle = 0; + + /// + /// + /// + private int _waitEdge = 0; + + /// + /// + /// + private bool currentState = false; + + #endregion + + #region Datacorder Device Settings + + /// + /// Signs whether the device should autodetect when the Z80 has entered into + /// 'load' mode and auto-play the tape if neccesary + /// + public bool AutoPlay { get; set; } + + #endregion + + #region Tape Controls + + /// + /// Starts the tape playing from the beginning of the current block + /// + public void Play() + { + if (_tapeIsPlaying) + return; + + // update the lastCycle + _lastCycle = _cpu.TotalExecutedCycles; + + // reset waitEdge and position + _waitEdge = 0; + _position = 0; + + if ( + _dataBlocks.Count > 0 && // data blocks are present && + _currentDataBlockIndex >= 0 // the current data block index is 1 or greater + ) + { + while (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count) + { + // we are at the end of a data block - move to the next + _position = 0; + _currentDataBlockIndex++; + + // are we at the end of the tape? + if (_currentDataBlockIndex >= _dataBlocks.Count) + { + break; + } + } + + // check for end of tape + if (_currentDataBlockIndex >= _dataBlocks.Count) + { + // end of tape reached. Rewind to beginning + RTZ(); + return; + } + + // update waitEdge with the current position in the current block + _waitEdge = _dataBlocks[_currentDataBlockIndex].DataPeriods[_position]; + + // sign that the tape is now playing + _tapeIsPlaying = true; + } + } + + /// + /// Stops the tape + /// (should move to the beginning of the next block) + /// + public void Stop() + { + if (!_tapeIsPlaying) + return; + + // sign that the tape is no longer playing + _tapeIsPlaying = false; + + if ( + _currentDataBlockIndex >= 0 && // we are at datablock 1 or above + _position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count - 1 // the block is still playing back + ) + { + // move to the next block + _currentDataBlockIndex++; + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + { + _currentDataBlockIndex = -1; + } + + // reset waitEdge and position + _waitEdge = 0; + _position = 0; + + if ( + _currentDataBlockIndex < 0 && // block index is -1 + _dataBlocks.Count() > 0 // number of blocks is greater than 0 + ) + { + // move the index on to 0 + _currentDataBlockIndex = 0; + } + } + + // update the lastCycle + _lastCycle = _cpu.TotalExecutedCycles; + + } + + /// + /// Rewinds the tape to it's beginning (return to zero) + /// + public void RTZ() + { + Stop(); + _currentDataBlockIndex = 0; + } + + /// + /// Inserts a new tape and sets up the tape device accordingly + /// + /// + public void LoadTape(byte[] tapeData) + { + // attempt TAP deserialization + TapSerializer tapSer = new TapSerializer(this); + + try + { + tapSer.DeSerialize(tapeData); + } + catch (Exception ex) + { + // TAP format not detected + var e = ex; + } + } + + /// + /// Resets the tape + /// + public void Reset() + { + RTZ(); + } + + #endregion + + #region Tape Device Methods + + /// + /// Simulates the spectrum 'EAR' input reading data from the tape + /// + /// + /// + public bool GetEarBit(long cpuCycle) + { + // decide how many cycles worth of data we are capturing + int cycles = Convert.ToInt32(cpuCycle - _lastCycle); + + // check whether tape is actually playing + if (_tapeIsPlaying == false) + { + // it's not playing. Update lastCycle and return + _lastCycle = cpuCycle; + return false; + } + + // check for end of tape + if (_currentDataBlockIndex < 0) + { + // end of tape reached - RTZ (and stop) + RTZ(); + return currentState; + } + + // process the cycles based on the waitEdge + while (cycles >= _waitEdge) + { + // decrement cycles + cycles -= _waitEdge; + + // flip the current state + currentState = !currentState; + + // increment the current period position + _position++; + + if (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count()) + { + // we have reached the end of the current block + + // skip any empty blocks + while (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count()) + { + _position = 0; + _currentDataBlockIndex++; + if (_currentDataBlockIndex >= _dataBlocks.Count()) + { + break; + } + } + + // check for end of tape + if (_currentDataBlockIndex >= _dataBlocks.Count()) + { + _currentDataBlockIndex = -1; + RTZ(); + return currentState; + } + } + + // update waitEdge with current position within the current block + _waitEdge = _dataBlocks[_currentDataBlockIndex].DataPeriods[_position]; + + } + + // update lastCycle and return currentstate + _lastCycle = cpuCycle - (long)cycles; + + // play the buzzer + _buzzer.ProcessPulseValue(true, currentState); + + return currentState; + } + + #endregion + + #region Media Serialization + + + + #endregion + + #region State Serialization + + private int _tempBlockCount; + + /// + /// Bizhawk state serialization + /// + /// + public void SyncState(Serializer ser) + { + ser.BeginSection("DatacorderDevice"); + + ser.Sync("_currentDataBlockIndex", ref _currentDataBlockIndex); + ser.Sync("_position", ref _position); + ser.Sync("_tapeIsPlaying", ref _tapeIsPlaying); + ser.Sync("_lastCycle", ref _lastCycle); + ser.Sync("_waitEdge", ref _waitEdge); + ser.Sync("currentState", ref currentState); + + //_dataBlocks + ser.BeginSection("Datablocks"); + + if (ser.IsWriter) + { + _tempBlockCount = _dataBlocks.Count(); + ser.Sync("_tempBlockCount", ref _tempBlockCount); + + for (int i = 0; i < _tempBlockCount; i++) + { + _dataBlocks[i].SyncState(ser, i); + } + } + else + { + ser.Sync("_tempBlockCount", ref _tempBlockCount); + } + + + + ser.EndSection(); + + + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 9f0c736069..85bac061d8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -55,15 +55,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Tape control if (Spectrum._controller.IsPressed(Play)) { - + TapeDevice.Play(); } if (Spectrum._controller.IsPressed(Stop)) { - + TapeDevice.Stop(); } if (Spectrum._controller.IsPressed(RTZ)) { - + TapeDevice.RTZ(); } if (Spectrum._controller.IsPressed(Record)) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index a67333aa03..43c0ec8b7a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -59,12 +59,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The spectrum datacorder device /// - public virtual Tape TapeDevice { get; set; } + public virtual DatacorderDevice TapeDevice { get; set; } /// /// The tape provider /// - public virtual ITapeProvider TapeProvider { get; set; } + //public virtual ITapeProvider TapeProvider { get; set; } /// /// Kempston joystick @@ -145,7 +145,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.EndFrame(); - TapeDevice.CPUFrameCompleted(); + //TapeDevice.CPUFrameCompleted(); FrameCount++; 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 0997bf3947..64c2cff470 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -69,7 +69,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = result | 0xa0; //set bit 5 & 7 to 1 - if (TapeDevice.CurrentMode == TapeOperationMode.Load) + if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) { if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) { @@ -193,7 +193,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); // Tape - TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 74df0bba39..5188a658a5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -40,10 +40,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); - TapeProvider = new DefaultTapeProvider(file); + //TapeProvider = new DefaultTapeProvider(file); - TapeDevice = new Tape(TapeProvider); + TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); + TapeDevice.LoadTape(file); } #endregion 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 4a5df60bc8..556a5f1d23 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -68,7 +68,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = result | 0xa0; //set bit 5 & 7 to 1 - if (TapeDevice.CurrentMode == TapeOperationMode.Load) + if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) { if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) { @@ -166,7 +166,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); // Tape - TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } else diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 5904dc41de..e2723dab2d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -40,10 +40,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); - TapeProvider = new DefaultTapeProvider(file); + //TapeProvider = new DefaultTapeProvider(file); - TapeDevice = new Tape(TapeProvider); + TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); + TapeDevice.LoadTape(file); } #endregion 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 c8ad708a67..3d891fb9e7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = result | 0xa0; //set bit 5 & 7 to 1 - if (TapeDevice.CurrentMode == TapeOperationMode.Load) + if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) { if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) { @@ -180,7 +180,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); // Tape - TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index f7a447dc35..deddf9bda0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -31,10 +31,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); - TapeProvider = new DefaultTapeProvider(file); + //TapeProvider = new DefaultTapeProvider(file); - TapeDevice = new Tape(TapeProvider); + TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); + TapeDevice.LoadTape(file); } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs new file mode 100644 index 0000000000..39bc2853b5 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs @@ -0,0 +1,301 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for TAP format serializaton + /// + public class TapSerializer : MediaSerializer + { + /// + /// The type of serializer + /// + private MediaSerializationType _formatType = MediaSerializationType.TAP; + public override MediaSerializationType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to serialize + /// + public override bool IsSerializer { get { return false; } } + + /// + /// Signs whether this class can be used to de-serialize + /// + public override bool IsDeSerializer { get { return true; } } + + #region Construction + + private DatacorderDevice _datacorder; + + public TapSerializer(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + + #region TAP Format Constants + + /// + /// Pilot pulse length + /// + public const int PILOT_PL = 2168; + + /// + /// Pilot pulses in the ROM header block + /// + public const int HEADER_PILOT_COUNT = 8063; + + /// + /// Pilot pulses in the ROM data block + /// + public const int DATA_PILOT_COUNT = 3223; + + /// + /// Sync 1 pulse length + /// + public const int SYNC_1_PL = 667; + + /// + /// Sync 2 pulse lenth + /// + public const int SYNC_2_PL = 735; + + /// + /// Bit 0 pulse length + /// + public const int BIT_0_PL = 855; + + /// + /// Bit 1 pulse length + /// + public const int BIT_1_PL = 1710; + + /// + /// End sync pulse length + /// + public const int TERM_SYNC = 947; + + /// + /// 1 millisecond pause + /// + public const int PAUSE_MS = 3500; + + /// + /// Used bit count in last byte + /// + public const int BIT_COUNT_IN_LAST = 8; + + #endregion + + /// + /// DeSerialization method + /// + /// + public override void DeSerialize(byte[] data) + { + /* + The .TAP files contain blocks of tape-saved data. All blocks start with two bytes specifying how many bytes will follow (not counting the two length bytes). Then raw tape data follows, including the flag and checksum bytes. The checksum is the bitwise XOR of all bytes including the flag byte. For example, when you execute the line SAVE "ROM" CODE 0,2 this will result: + + |------ Spectrum-generated data -------| |---------| + + 13 00 00 03 52 4f 4d 7x20 02 00 00 00 00 80 f1 04 00 ff f3 af a3 + + ^^^^^...... first block is 19 bytes (17 bytes+flag+checksum) + ^^... flag byte (A reg, 00 for headers, ff for data blocks) + ^^ first byte of header, indicating a code block + + file name ..^^^^^^^^^^^^^ + header info ..............^^^^^^^^^^^^^^^^^ + checksum of header .........................^^ + length of second block ........................^^^^^ + flag byte ............................................^^ + first two bytes of rom .................................^^^^^ + checksum (checkbittoggle would be a better name!).............^^ + */ + + + // convert bytearray to memory stream + MemoryStream stream = new MemoryStream(data); + + // the first 2 bytes of the TAP file designate the length of the first data block + // this (I think) should always be 17 bytes (as this is the tape header) + byte[] blockLengthData = new byte[2]; + + // we are now going to stream through the entire file processing a block at a time + while (stream.Position < stream.Length) + { + // read and calculate the length of the block + stream.Read(blockLengthData, 0, 2); + int blockSize = BitConverter.ToUInt16(blockLengthData, 0); + if (blockSize == 0) + { + // block size is 0 - this is probably invalid (but I guess could be EoF in some situations) + break; + } + + // copy the entire block into a new bytearray + byte[] blockdata = new byte[blockSize]; + stream.Read(blockdata, 0, blockSize); + + // create and populate a new tapedatablock object + TapeDataBlock tdb = new TapeDataBlock(); + + // ascertain the block description + string description = string.Empty; + byte crc = 0; + byte crcValue = 0; + byte crcFile = 0; + byte[] programData = new byte[10]; + + // calculate block checksum value + for (int i = 0; i < blockSize; i++) + { + crc ^= blockdata[i]; + if (i < blockSize - 1) + { + crcValue = crc; + } + else + { + crcFile = blockdata[i]; + } + } + + // process the flag byte + /* (The type is 0,1,2 or 3 for a Program, Number array, Character array or Code file. + A SCREEN$ file is regarded as a Code file with start address 16384 and length 6912 decimal. + If the file is a Program file, parameter 1 holds the autostart line number (or a number >=32768 if no LINE parameter was given) + and parameter 2 holds the start of the variable area relative to the start of the program. If it's a Code file, parameter 1 holds + the start of the code block when saved, and parameter 2 holds 32768. For data files finally, the byte at position 14 decimal holds the variable name.) + */ + + if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || blockdata[1] == 3) + { + // This is the PROGRAM header + // take the 10 filename bytes (that start at offset 2) + programData = blockdata.Skip(2).Take(10).ToArray(); + + // get the filename as a string (with padding removed) + string fileName = Encoding.ASCII.GetString(programData).Trim(); + + // get the type + string type = ""; + if (blockdata[0] == 0x00) + { + type = "Program"; + } + else + { + type = "Bytes"; + } + + // now build the description string + StringBuilder sb = new StringBuilder(); + sb.Append(type + ": "); + sb.Append(fileName + " "); + sb.Append(GetUInt16(blockdata, 14)); + sb.Append(":"); + sb.Append(GetUInt16(blockdata, 12)); + description = sb.ToString(); + } + else if (blockdata[0] == 0xFF) + { + // this is a data block + description = "Data Block " + (blockSize - 2) + "bytes"; + } + else + { + // other type + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize - 2); + description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + } + + tdb.BlockDescription = description; + + // calculate the data periods for this block + int pilotLength = 0; + + // work out pilot length + if (blockdata[0] < 4) + { + pilotLength = 8064; + } + else + { + pilotLength = 3220; + } + + // create a list to hold the data periods + List dataPeriods = new List(); + + // generate pilot pulses + for (int i = 0; i < pilotLength; i++) + { + dataPeriods.Add(PILOT_PL); + } + + // add syncro pulses + dataPeriods.Add(SYNC_1_PL); + dataPeriods.Add(SYNC_2_PL); + + int pos = 0; + + // add bit0 and bit1 periods + for (int i = 0; i < blockSize - 1; i++, pos++) + { + for (byte b = 0x80; b != 0; b >>= 1) + { + if ((blockdata[i] & b) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + if ((blockdata[i] & b) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + } + } + + // add the last byte + for (byte c = 0x80; c != (byte)(0x80 >> BIT_COUNT_IN_LAST); c >>= 1) + { + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + } + + // add block pause + int actualPause = PAUSE_MS * 1000; + dataPeriods.Add(actualPause); + + // add to the tapedatablock object + tdb.DataPeriods = dataPeriods; + + // add the raw data + tdb.BlockData = blockdata; + + // add block to the tape + _datacorder.DataBlocks.Add(tdb); + + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index 796bee81d9..c75906b496 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -1,4 +1,5 @@ -using System; +using BizHawk.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -14,17 +15,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Either the TZX block ID, or -1 in the case of non-tzx blocks /// - public int BlockID = -1; + private int _blockID = -1; + public int BlockID + { + get { return _blockID; } + set { _blockID = value; } + } /// /// Description of the block /// - public string BlockDescription { get; set; } + private string _blockDescription; + public string BlockDescription + { + get { return _blockDescription; } + set { _blockDescription = value; } + } /// /// Byte array containing the raw block data /// - public byte[] BlockData = null; + private byte[] _blockData; + public byte[] BlockData + { + get { return _blockData; } + set { _blockData = value; } + } /// /// List containing the pulse timing values @@ -35,7 +51,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Command that is raised by this data block /// (that may or may not need to be acted on) /// - public TapeCommand Command = TapeCommand.NONE; + private TapeCommand _command = TapeCommand.NONE; + public TapeCommand Command + { + get { return _command; } + set { _command = value; } + } /// /// Returns the data periods as an array @@ -61,5 +82,34 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum DataPeriods = periodArray.ToList(); } + + /// + /// Bizhawk state serialization + /// + /// + public void SyncState(Serializer ser, int blockPosition) + { + ser.BeginSection("DataBlock" + blockPosition); + + ser.Sync("_blockID", ref _blockID); + ser.SyncFixedString("_blockDescription", ref _blockDescription, 50); + ser.Sync("_blockData", ref _blockData, true); + ser.SyncEnum("_command", ref _command); + + int[] tempArray = null; + + if (ser.IsWriter) + { + tempArray = GetDataPeriodsArray(); + ser.Sync("_periods", ref tempArray, true); + } + else + { + ser.Sync("_periods", ref tempArray, true); + SetDataPeriodsArray(tempArray); + } + + ser.EndSection(); + } } } From b9729d0dc26ccfc6cb67ee40f85a54344436f9e9 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 15 Feb 2018 14:37:22 +0000 Subject: [PATCH 044/339] TZX tape format handling re-write nearly complete (supporting advanced protection/loader schemes) --- .../BizHawk.Emulation.Cores.csproj | 1 + .../Hardware/Datacorder/DatacorderDevice.cs | 15 +- .../SinclairSpectrum/Media/MediaSerializer.cs | 4 +- .../Media/Tape/TapSerializer.cs | 10 +- .../Media/Tape/TapeDataBlock.cs | 163 +- .../Media/Tape/TzxSerializer.cs | 1630 +++++++++++++++++ 6 files changed, 1809 insertions(+), 14 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 3b65b45bb8..aefb1900ae 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -281,6 +281,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 8018ac23cc..ddc7598f03 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -233,12 +233,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void LoadTape(byte[] tapeData) { + // attempt TZX deserialization + TzxSerializer tzxSer = new TzxSerializer(this); + try + { + tzxSer.DeSerialize(tapeData); + return; + } + catch (Exception ex) + { + // TAP format not detected + var e = ex; + } + // attempt TAP deserialization TapSerializer tapSer = new TapSerializer(this); - try { tapSer.DeSerialize(tapeData); + return; } catch (Exception ex) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs index b20a6aa8b1..44c99c28ff 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs @@ -92,7 +92,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - protected static ushort GetUInt16(byte[] buf, int offsetIndex) + protected static ushort GetWordValue(byte[] buf, int offsetIndex) { return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8); } @@ -103,7 +103,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - protected static void setUint16(byte[] buf, int offsetIndex, ushort value) + protected static void SetWordValue(byte[] buf, int offsetIndex, ushort value) { buf[offsetIndex] = (byte)value; buf[offsetIndex + 1] = (byte)(value >> 8); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs index 39bc2853b5..01e9bee86c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs @@ -126,6 +126,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum checksum (checkbittoggle would be a better name!).............^^ */ + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); // convert bytearray to memory stream MemoryStream stream = new MemoryStream(data); @@ -174,7 +176,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - // process the flag byte + // process the type byte /* (The type is 0,1,2 or 3 for a Program, Number array, Character array or Code file. A SCREEN$ file is regarded as a Code file with start address 16384 and length 6912 decimal. If the file is a Program file, parameter 1 holds the autostart line number (or a number >=32768 if no LINE parameter was given) @@ -206,9 +208,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum StringBuilder sb = new StringBuilder(); sb.Append(type + ": "); sb.Append(fileName + " "); - sb.Append(GetUInt16(blockdata, 14)); + sb.Append(GetWordValue(blockdata, 14)); sb.Append(":"); - sb.Append(GetUInt16(blockdata, 12)); + sb.Append(GetWordValue(blockdata, 12)); description = sb.ToString(); } else if (blockdata[0] == 0xFF) @@ -223,7 +225,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); } - tdb.BlockDescription = description; + tdb.BlockDescription = BlockType.Standard_Speed_Data_Block; // calculate the data periods for this block int pilotLength = 0; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index c75906b496..f714d21385 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -19,17 +19,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int BlockID { get { return _blockID; } - set { _blockID = value; } + set { + _blockID = value; + + if (MetaData == null) + MetaData = new Dictionary(); + + AddMetaData(BlockDescriptorTitle.Block_ID, value.ToString()); + } } /// - /// Description of the block + /// The block type /// - private string _blockDescription; - public string BlockDescription + private BlockType _blockType; + public BlockType BlockDescription { - get { return _blockDescription; } - set { _blockDescription = value; } + get { return _blockType; } + set { + _blockType = value; + if (MetaData == null) + MetaData = new Dictionary(); + } } /// @@ -42,6 +53,63 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum set { _blockData = value; } } + /// + /// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization) + /// Its basically tape information + /// + private byte[][] _tapeDescriptionData; + + /// + /// Returns the Tape Description Data in a human readable format + /// + public List TapeDescriptionData + { + get + { + List data = new List(); + + foreach (byte[] b in _tapeDescriptionData) + { + data.Add(Encoding.ASCII.GetString(b)); + } + + return data; + } + } + + + #region Block Meta Data + + /// + /// Dictionary of block related data + /// + public Dictionary MetaData { get; set; } + + /// + /// Adds a single metadata item to the Dictionary + /// + /// + /// + public void AddMetaData(BlockDescriptorTitle descriptor, string data) + { + // check whether entry already exists + bool check = MetaData.ContainsKey(descriptor); + if (check) + { + // already exists - update + MetaData[descriptor] = data; + } + else + { + // create new + MetaData.Add(descriptor, data); + } + } + + #endregion + + + /// /// List containing the pulse timing values /// @@ -92,7 +160,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.BeginSection("DataBlock" + blockPosition); ser.Sync("_blockID", ref _blockID); - ser.SyncFixedString("_blockDescription", ref _blockDescription, 50); + //ser.SyncFixedString("_blockDescription", ref _blockDescription, 200); + ser.SyncEnum("_blockType", ref _blockType); ser.Sync("_blockData", ref _blockData, true); ser.SyncEnum("_command", ref _command); @@ -112,4 +181,84 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.EndSection(); } } + + /// + /// The types of TZX blocks + /// + public enum BlockType + { + Standard_Speed_Data_Block = 0x10, + Turbo_Speed_Data_Block = 0x11, + Pure_Tone = 0x12, + Pulse_Sequence = 0x13, + Pure_Data_Block = 0x14, + Direct_Recording = 0x15, + CSW_Recording = 0x18, + Generalized_Data_Block = 0x19, + Pause_or_Stop_the_Tape = 0x20, + Group_Start = 0x21, + Group_End = 0x22, + Jump_to_Block = 0x23, + Loop_Start = 0x24, + Loop_End = 0x25, + Call_Sequence = 0x26, + Return_From_Sequence = 0x27, + Select_Block = 0x28, + Stop_the_Tape_48K = 0x2A, + Set_Signal_Level = 0x2B, + Text_Description = 0x30, + Message_Block = 0x31, + Archive_Info = 0x32, + Hardware_Type = 0x33, + Custom_Info_Block = 0x35, + Glue_Block = 0x5A, + + // depreciated blocks + C64_ROM_Type_Data_Block = 0x16, + C64_Turbo_Tape_Data_Block = 0x17, + Emulation_Info = 0x34, + Snapshot_Block = 0x40, + + // unsupported / undetected + Unsupported + } + + /// + /// Different title possibilities + /// + public enum BlockDescriptorTitle + { + Undefined, + Block_ID, + Program, + Data_Bytes, + Bytes, + + Pilot_Pulse_Length, + Pilot_Pulse_Count, + First_Sync_Length, + Second_Sync_Length, + Zero_Bit_Length, + One_Bit_Length, + Data_Length, + Bits_In_Last_Byte, + Pause_After_Data, + + Pulse_Length, + Pulse_Count, + + Text_Description, + Title, + Publisher, + Author, + Year, + Language, + Type, + Price, + Protection, + Origin, + Comments, + + Needs_Parsing + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs new file mode 100644 index 0000000000..76c1b1fd97 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -0,0 +1,1630 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for TZX format serializaton + /// + public class TzxSerializer : MediaSerializer + { + /// + /// The type of serializer + /// + private MediaSerializationType _formatType = MediaSerializationType.TZX; + public override MediaSerializationType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to serialize + /// + public override bool IsSerializer { get { return false; } } + + /// + /// Signs whether this class can be used to de-serialize + /// + public override bool IsDeSerializer { get { return true; } } + + /// + /// Working list of generated tape data blocks + /// + private List _blocks = new List(); + + /// + /// Position counter + /// + private int _position = 0; + + /// + /// Object to keep track of loops - this assumes there is only one loop at a time + /// + private List> _loopCounter = new List>(); + + #region Construction + + private DatacorderDevice _datacorder; + + public TzxSerializer(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// DeSerialization method + /// + /// + public override void DeSerialize(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + +/* + // TZX Header + length: 10 bytes + Offset Value Type Description + 0x00 "ZXTape!" ASCII[7] TZX signature + 0x07 0x1A BYTE End of text file marker + 0x08 1 BYTE TZX major revision number + 0x09 20 BYTE TZX minor revision number +*/ + + // check whether this is a valid tzx format file by looking at the identifier in the header + // (first 7 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 7); + // and 'end of text' marker + byte eotm = data[7]; + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident != "ZXTape!" || eotm != 0x1A) + { + // this is not a valid TZX format file + throw new Exception(this.GetType().ToString() + + "This is not a valid TZX format file"); + } + + // iterate through each block + _position = 10; + while (_position < data.Length) + { + // block ID is the first byte in a new block + int ID = data[_position++]; + + // process the data + ProcessBlock(data, ID); + } + + } + + /// + /// Processes a TZX block + /// + /// + /// + private void ProcessBlock(byte[] data, int id) + { + // process based on detected block ID + switch (id) + { + // ID 10 - Standard Speed Data Block + case 0x10: + ProcessBlockID10(data); + break; + // ID 11 - Turbo Speed Data Block + case 0x11: + ProcessBlockID11(data); + break; + // ID 12 - Pure Tone + case 0x12: + ProcessBlockID12(data); + break; + // ID 13 - Pulse sequence + case 0x13: + ProcessBlockID13(data); + break; + // ID 14 - Pure Data Block + case 0x14: + ProcessBlockID14(data); + break; + // ID 15 - Direct Recording + case 0x15: + ProcessBlockID15(data); + break; + // ID 18 - CSW Recording + case 0x18: + ProcessBlockID18(data); + break; + // ID 19 - Generalized Data Block + case 0x19: + ProcessBlockID19(data); + break; + // ID 20 - Pause (silence) or 'Stop the Tape' command + case 0x20: + ProcessBlockID20(data); + break; + // ID 21 - Group start + case 0x21: + ProcessBlockID21(data); + break; + // ID 22 - Group end + case 0x22: + ProcessBlockID22(data); + break; + // ID 23 - Jump to block + case 0x23: + ProcessBlockID23(data); + break; + // ID 24 - Loop start + case 0x24: + ProcessBlockID24(data); + break; + // ID 25 - Loop end + case 0x25: + ProcessBlockID25(data); + break; + // ID 26 - Call sequence + case 0x26: + ProcessBlockID26(data); + break; + // ID 27 - Return from sequence + case 0x27: + ProcessBlockID27(data); + break; + // ID 28 - Select block + case 0x28: + ProcessBlockID28(data); + break; + // ID 2A - Stop the tape if in 48K mode + case 0x2A: + ProcessBlockID2A(data); + break; + // ID 2B - Set signal level + case 0x2B: + ProcessBlockID2B(data); + break; + // ID 30 - Text description + case 0x30: + ProcessBlockID30(data); + break; + // ID 31 - Message block + case 0x31: + ProcessBlockID31(data); + break; + // ID 32 - Archive info + case 0x32: + ProcessBlockID32(data); + break; + // ID 33 - Hardware type + case 0x33: + ProcessBlockID33(data); + break; + // ID 35 - Custom info block + case 0x35: + ProcessBlockID35(data); + break; + // ID 5A - "Glue" block + case 0x5A: + ProcessBlockID5A(data); + break; + + #region Depreciated Blocks + + // ID 16 - C64 ROM Type Data Block + case 0x16: + ProcessBlockID16(data); + break; + // ID 17 - C64 Turbo Tape Data Block + case 0x17: + ProcessBlockID17(data); + break; + // ID 34 - Emulation info + case 0x34: + ProcessBlockID34(data); + break; + // ID 40 - Snapshot block + case 0x40: + ProcessBlockID40(data); + break; + + #endregion + + default: + ProcessUnidentifiedBlock(data); + break; + } + } + + #region TZX Block Processors + + #region ID 10 - Standard Speed Data Block +/* length: [02,03]+04 + Offset Value Type Description + 0x00 - WORD Pause after this block (ms.) {1000} + 0x02 N WORD Length of data that follow + 0x04 - BYTE[N] Data as in .TAP files + + This block must be replayed with the standard Spectrum ROM timing values - see the values in + curly brackets in block ID 11. The pilot tone consists in 8063 pulses if the first data byte + (flag byte) is < 128, 3223 otherwise. This block can be used for the ROM loading routines AND + for custom loading routines that use the same timings as ROM ones do. */ + private void ProcessBlockID10(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x10; + t.BlockDescription = BlockType.Standard_Speed_Data_Block; + t.DataPeriods = new List(); + + int pauseLen = GetWordValue(data, _position); + int blockLen = GetWordValue(data, _position + 2); + + _position += 4; + + byte[] tmp = new byte[blockLen]; + tmp = data.Skip(_position).Take(blockLen).ToArray(); + + var t2 = DecodeDataBlock(t, tmp, DataBlockType.Standard, 1000); + + // add the block + _datacorder.DataBlocks.Add(t2); + + // advance the position to the next block + _position += blockLen; + } + #endregion + + #region ID 11 - Turbo Speed Data Block +/* length: [0F,10,11]+12 + Offset Value Type Description + 0x00 - WORD Length of PILOT pulse {2168} + 0x02 - WORD Length of SYNC first pulse {667} + 0x04 - WORD Length of SYNC second pulse {735} + 0x06 - WORD Length of ZERO bit pulse {855} + 0x08 - WORD Length of ONE bit pulse {1710} + 0x0A - WORD Length of PILOT tone (number of pulses) {8063 header (flag<128), 3223 data (flag>=128)} + 0x0C - BYTE Used bits in the last byte (other bits should be 0) {8} + (e.g. if this is 6, then the bits used (x) in the last byte are: xxxxxx00, + where MSb is the leftmost bit, LSb is the rightmost bit) + 0x0D - WORD Pause after this block (ms.) {1000} + 0x0F N BYTE[3] Length of data that follow + 0x12 - BYTE[N] Data as in .TAP files + + This block is very similar to the normal TAP block but with some additional info on the timings and other important + differences. The same tape encoding is used as for the standard speed data block. If a block should use some non-standard + sync or pilot tones (i.e. all sorts of protection schemes) then use the next three blocks to describe it.*/ + private void ProcessBlockID11(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x11; + t.BlockDescription = BlockType.Turbo_Speed_Data_Block; + t.DataPeriods = new List(); + + int pilotPL = GetWordValue(data, _position); + int sync1P = GetWordValue(data, _position + 2); + int sync2P = GetWordValue(data, _position + 4); + int bit0P = GetWordValue(data, _position + 6); + int bit1P = GetWordValue(data, _position + 8); + int pilotTL = GetWordValue(data, _position + 10); + int bitinbyte = data[_position + 12]; + int pause = GetWordValue(data, _position + 13); + + int blockLen = 0xFFFFFF & GetInt32(data, _position + 0x0F); + + _position += 0x12; + + byte[] tmp = new byte[blockLen]; + tmp = data.Skip(_position).Take(blockLen).ToArray(); + + var t2 = DecodeDataBlock(t, tmp, DataBlockType.Turbo, pause, pilotTL, pilotPL, sync1P, sync2P, bit0P, bit1P, bitinbyte); + + // add the block + _datacorder.DataBlocks.Add(t2); + + // advance the position to the next block + _position += blockLen; + } + #endregion + + #region ID 12 - Pure Tone +/* length: 04 + Offset Value Type Description + 0x00 - WORD Length of one pulse in T-states + 0x02 - WORD Number of pulses + + This will produce a tone which is basically the same as the pilot tone in the ID 10, ID 11 blocks. You can define how + long the pulse is and how many pulses are in the tone. */ + private void ProcessBlockID12(byte[] data) + { + int blockLen = 4; + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x12; + t.BlockDescription = BlockType.Pure_Tone; + t.DataPeriods = new List(); + + // get values + int pulseLength = GetWordValue(data, _position); + int pulseCount = GetWordValue(data, _position + 2); + + t.AddMetaData(BlockDescriptorTitle.Pulse_Length, pulseLength.ToString() + " T-States"); + t.AddMetaData(BlockDescriptorTitle.Pulse_Count, pulseCount.ToString()); + + // build period information + for (int p = 0; p < pulseCount; p++) + { + t.DataPeriods.Add(pulseLength); + } + + // add the block + _datacorder.DataBlocks.Add(t); + + // advance the position to the next block + _position += blockLen; + } + #endregion + + #region ID 13 - Pulse sequence +/* length: [00]*02+01 + Offset Value Type Description + 0x00 N BYTE Number of pulses + 0x01 - WORD[N] Pulses' lengths + + This will produce N pulses, each having its own timing. Up to 255 pulses can be stored in this block; this is useful for non-standard + sync tones used by some protection schemes. */ + private void ProcessBlockID13(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x13; + t.BlockDescription = BlockType.Pulse_Sequence; + t.DataPeriods = new List(); + + // get pulse count + int pulseCount = data[_position]; + t.AddMetaData(BlockDescriptorTitle.Pulse_Count, pulseCount.ToString()); + _position++; + + // build period information + for (int p = 0; p < pulseCount; p++, _position += 2) + { + // get pulse length + int pulseLength = GetWordValue(data, _position); + t.AddMetaData(BlockDescriptorTitle.Needs_Parsing, "Pulse " + p + " Length\t" + pulseLength.ToString() + " T-States"); + t.DataPeriods.Add(pulseLength); + } + + // add the block + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 14 - Pure Data Block +/* length: [07,08,09]+0A + Offset Value Type Description + 0x00 - WORD Length of ZERO bit pulse + 0x02 - WORD Length of ONE bit pulse + 0x04 - BYTE Used bits in last byte (other bits should be 0) + (e.g. if this is 6, then the bits used (x) in the last byte are: xxxxxx00, + where MSb is the leftmost bit, LSb is the rightmost bit) + 0x05 - WORD Pause after this block (ms.) + 0x07 N BYTE[3] Length of data that follow + 0x0A - BYTE[N] Data as in .TAP files + + This is the same as in the turbo loading data block, except that it has no pilot or sync pulses. */ + private void ProcessBlockID14(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x14; + t.BlockDescription = BlockType.Pure_Data_Block; + t.DataPeriods = new List(); + + int pilotPL = 0; + int sync1P = 0; + int sync2P = 0; + int bit0P = GetWordValue(data, _position + 0); + int bit1P = GetWordValue(data, _position + 2); + int pilotTL = 0; + int bitinbyte = data[_position + 4]; + int pause = GetWordValue(data, _position + 5); + + int blockLen = 0xFFFFFF & GetInt32(data, _position + 0x07); + + _position += 0x0A; + + byte[] tmp = new byte[blockLen]; + tmp = data.Skip(_position).Take(blockLen).ToArray(); + + var t2 = DecodeDataBlock(t, tmp, DataBlockType.Pure, pause, pilotTL, pilotPL, sync1P, sync2P, bit0P, bit1P, bitinbyte); + + // add the block + _datacorder.DataBlocks.Add(t2); + + // advance the position to the next block + _position += blockLen; + } + #endregion + + #region ID 15 - Direct Recording +/* length: [05,06,07]+08 + Offset Value Type Description + 0x00 - WORD Number of T-states per sample (bit of data) + 0x02 - WORD Pause after this block in milliseconds (ms.) + 0x04 - BYTE Used bits (samples) in last byte of data (1-8) + (e.g. if this is 2, only first two samples of the last byte will be played) + 0x05 N BYTE[3] Length of samples' data + 0x08 - BYTE[N] Samples data. Each bit represents a state on the EAR port (i.e. one sample). + MSb is played first. + + This block is used for tapes which have some parts in a format such that the turbo loader block cannot be used. + This is not like a VOC file, since the information is much more compact. Each sample value is represented by one bit only + (0 for low, 1 for high) which means that the block will be at most 1/8 the size of the equivalent VOC. + The preferred sampling frequencies are 22050 or 44100 Hz (158 or 79 T-states/sample). + Please, if you can, don't use other sampling frequencies. + Please use this block only if you cannot use any other block. */ + private void ProcessBlockID15(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x15; + t.BlockDescription = BlockType.Direct_Recording; + t.DataPeriods = new List(); + + // get values + int samLen = GetInt32(data, _position + 5); + int samSize = 0xFFFFFF & samLen; + + int tStatesPerSample = GetWordValue(data, _position); + int pauseAfterBlock = GetWordValue(data, _position + 2); + int usedBitsInLastByte = data[_position + 4]; + + // skip to samples data + _position += 8; + + int pulseLength = 0; + int pulseCount = 0; + + // ascertain the pulse count + for (int i = 0; i < samSize; i++) + { + for (int p = 0x80; p != 0; p >>= 1) + { + if (((data[_position + i] ^ pulseLength) & p) != 0) + { + pulseCount++; + pulseLength ^= -1; + } + } + } + + // get the pulses + t.DataPeriods = new List(pulseCount + 2); + int tStateCount = 0; + pulseLength = 0; + for (int i = 1; i < samSize; i++) + { + for (int p = 0x80; p != 0; p >>= 1) + { + tStateCount += tStatesPerSample; + if (((data[_position] ^ pulseLength) & p) != 0) + { + t.DataPeriods.Add(tStateCount); + pulseLength ^= -1; + tStateCount = 0; + } + } + + // incrememt position + _position++; + } + + // get the pulses in the last byte of data + for (int p = 0x80; p != (byte)(0x80 >> usedBitsInLastByte); p >>= 1) + { + tStateCount += tStatesPerSample; + if (((data[_position] ^ pulseLength) & p) != 0) + { + t.DataPeriods.Add(tStateCount); + pulseLength ^= -1; + tStateCount = 0; + } + } + + // add final pulse + t.DataPeriods.Add(tStateCount); + + // add end of block pause + if (pauseAfterBlock > 0) + { + t.DataPeriods.Add(3500 * pauseAfterBlock); + } + + // increment position + _position++; + + // add the block + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 18 - CSW Recording +/* length: [00,01,02,03]+04 + Offset Value Type Description + 0x00 10+N DWORD Block length (without these four bytes) + 0x04 - WORD Pause after this block (in ms). + 0x06 - BYTE[3] Sampling rate + 0x09 - BYTE Compression type + 0x01: RLE + 0x02: Z-RLE + 0x0A - DWORD Number of stored pulses (after decompression, for validation purposes) + 0x0E - BYTE[N] CSW data, encoded according to the CSW file format specification. + + This block contains a sequence of raw pulses encoded in CSW format v2 (Compressed Square Wave). */ + private void ProcessBlockID18(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x18; + t.BlockDescription = BlockType.CSW_Recording; + t.DataPeriods = new List(); + + // add the block + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 19 - Generalized Data Block +/* length: [00,01,02,03]+04 + Offset Value Type Description + 0x00 - DWORD Block length (without these four bytes) + 0x04 - WORD Pause after this block (ms) + 0x06 TOTP DWORD Total number of symbols in pilot/sync block (can be 0) + 0x0A NPP BYTE Maximum number of pulses per pilot/sync symbol + 0x0B ASP BYTE Number of pilot/sync symbols in the alphabet table (0=256) + 0x0C TOTD DWORD Total number of symbols in data stream (can be 0) + 0x10 NPD BYTE Maximum number of pulses per data symbol + 0x11 ASD BYTE Number of data symbols in the alphabet table (0=256) + 0x12 - SYMDEF[ASP] Pilot and sync symbols definition table + This field is present only if TOTP>0 + 0x12+ + (2*NPP+1)*ASP - PRLE[TOTP] Pilot and sync data stream + This field is present only if TOTP>0 + 0x12+ + (TOTP>0)*((2*NPP+1)*ASP)+ + TOTP*3 - SYMDEF[ASD] Data symbols definition table + This field is present only if TOTD>0 + 0x12+ + (TOTP>0)*((2*NPP+1)*ASP)+ + TOTP*3+ + (2*NPD+1)*ASD - BYTE[DS] Data stream + This field is present only if TOTD>0 + + This block has been specifically developed to represent an extremely wide range of data encoding techniques. + The basic idea is that each loading component (pilot tone, sync pulses, data) is associated to a specific sequence + of pulses, where each sequence (wave) can contain a different number of pulses from the others. + In this way we can have a situation where bit 0 is represented with 4 pulses and bit 1 with 8 pulses. + + ---- + SYMDEF structure format + Offset Value Type Description + 0x00 - BYTE Symbol flags + b0-b1: starting symbol polarity + 00: opposite to the current level (make an edge, as usual) - default + 01: same as the current level (no edge - prolongs the previous pulse) + 10: force low level + 11: force high level + 0x01 - WORD[MAXP] Array of pulse lengths. + + The alphabet is stored using a table where each symbol is a row of pulses. The number of columns (i.e. pulses) of the table is the + length of the longest sequence amongst all (MAXP=NPP or NPD, for pilot/sync or data blocks respectively); shorter waves are terminated by a + zero-length pulse in the sequence. + Any number of data symbols is allowed, so we can have more than two distinct waves; for example, imagine a loader which writes two bits at a + time by encoding them with four distinct pulse lengths: this loader would have an alphabet of four symbols, each associated to a specific + sequence of pulses (wave). + ---- + ---- + PRLE structure format + Offset Value Type Description + 0x00 - BYTE Symbol to be represented + 0x01 - WORD Number of repetitions + + Most commonly, pilot and sync are repetitions of the same pulse, thus they are represented using a very simple RLE encoding structure which stores + the symbol and the number of times it must be repeated. + Each symbol in the data stream is represented by a string of NB bits of the block data, where NB = ceiling(Log2(ASD)). + Thus the length of the whole data stream in bits is NB*TOTD, or in bytes DS=ceil(NB*TOTD/8). + ---- */ + private void ProcessBlockID19(byte[] data) + { + string test = "dgfg"; + } + #endregion + + #region ID 20 - Pause (silence) or 'Stop the Tape' command +/* length: 02 + Offset Value Type Description + 0x00 - WORD Pause duration (ms.) + + This will make a silence (low amplitude level (0)) for a given time in milliseconds. If the value is 0 then the + emulator or utility should (in effect) STOP THE TAPE, i.e. should not continue loading until the user or emulator requests it. */ + private void ProcessBlockID20(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x20; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Pause_or_Stop_the_Tape; + + int pauseDuration = GetWordValue(data, _position); + if (pauseDuration != 0) + { + //t.BlockDescription = "Pause: " + pauseDuration + " ms"; + } + else + { + //t.BlockDescription = "[STOP THE TAPE]"; + } + + if (pauseDuration == 0) + { + // issue stop the tape command + t.Command = TapeCommand.STOP_THE_TAPE; + // add 1ms period + t.DataPeriods.Add(3500); + pauseDuration = -1; + } + else + { + // this is actually just a pause + pauseDuration = 3500 * pauseDuration; + } + + // add end of block pause + t.DataPeriods.Add(pauseDuration); + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advanced position to next block + _position += 2; + } + #endregion + + #region ID 21 - Group start +/* length: [00]+01 + Offset Value Type Description + 0x00 L BYTE Length of the group name string + 0x01 - CHAR[L] Group name in ASCII format (please keep it under 30 characters long) + + This block marks the start of a group of blocks which are to be treated as one single (composite) block. + This is very handy for tapes that use lots of subblocks like Bleepload (which may well have over 160 custom loading blocks). + You can also give the group a name (example 'Bleepload Block 1'). + For each group start block, there must be a group end block. Nesting of groups is not allowed. */ + private void ProcessBlockID21(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x21; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Group_Start; + + int nameLength = data[_position]; + _position++; + + string name = Encoding.ASCII.GetString(data, _position, nameLength); + //t.BlockDescription = "[GROUP: " + name + "]"; + t.Command = TapeCommand.BEGIN_GROUP; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += nameLength; + } + #endregion + + #region ID 22 - Group end +/* length: 00 + + This indicates the end of a group. This block has no body. */ + private void ProcessBlockID22(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x22; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Group_End; + t.Command = TapeCommand.END_GROUP; + + // add to tape + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 23 - Jump to block +/* length: 02 + Offset Value Type Description + 0x00 - WORD Relative jump value + + This block will enable you to jump from one block to another within the file. The value is a signed short word + (usually 'signed short' in C); Some examples: + Jump 0 = 'Loop Forever' - this should never happen + Jump 1 = 'Go to the next block' - it is like NOP in assembler ;) + Jump 2 = 'Skip one block' + Jump -1 = 'Go to the previous block' + All blocks are included in the block count!. */ + private void ProcessBlockID23(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x23; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Jump_to_Block; + + int relativeJumpValue = GetWordValue(data, _position); + string result = string.Empty; + + switch(relativeJumpValue) + { + case 0: + result = "Loop Forever"; + break; + case 1: + result = "To Next Block"; + break; + case 2: + result = "Skip One Block"; + break; + case -1: + result = "Go to Previous Block"; + break; + } + + //t.BlockDescription = "[JUMP BLOCK - " + result +"]"; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 2; + } + #endregion + + #region ID 24 - Loop start +/* length: 02 + Offset Value Type Description + 0x00 - WORD Number of repetitions (greater than 1) + + If you have a sequence of identical blocks, or of identical groups of blocks, you can use this block to tell how many times they should + be repeated. This block is the same as the FOR statement in BASIC. + For simplicity reasons don't nest loop blocks! */ + private void ProcessBlockID24(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x24; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Loop_Start; + + // loop should start from the next block + int loopStart = _datacorder.DataBlocks.Count() + 1; + + int numberOfRepetitions = GetWordValue(data, _position); + + // update loop counter + _loopCounter.Add( + new KeyValuePair( + loopStart, + numberOfRepetitions)); + + // update description + //t.BlockDescription = "[LOOP START - " + numberOfRepetitions + " times]"; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 2; + } + #endregion + + #region ID 25 - Loop end +/* length: 00 + + This is the same as BASIC's NEXT statement. It means that the utility should jump back to the start of the loop if it hasn't + been run for the specified number of times. + This block has no body. */ + private void ProcessBlockID25(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x25; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Loop_End; + + // get the most recent loop info + var loop = _loopCounter.LastOrDefault(); + + int loopStart = loop.Key; + int numberOfRepetitions = loop.Value; + + if (numberOfRepetitions == 0) + { + return; + } + + // get the number of blocks to loop + int blockCnt = _datacorder.DataBlocks.Count() - loopStart; + + // loop through each group to repeat + for (int b = 0; b < numberOfRepetitions; b++) + { + TapeDataBlock repeater = new TapeDataBlock(); + //repeater.BlockDescription = "[LOOP REPEAT - " + (b + 1) + "]"; + repeater.DataPeriods = new List(); + + // add the repeat block + _datacorder.DataBlocks.Add(repeater); + + // now iterate through and add the blocks to be repeated + for (int i = 0; i < blockCnt; i++) + { + var block = _datacorder.DataBlocks[loopStart + i]; + _datacorder.DataBlocks.Add(block); + } + } + } + #endregion + + #region ID 26 - Call sequence +/* length: [00,01]*02+02 + Offset Value Type Description + 0x00 N WORD Number of calls to be made + 0x02 - WORD[N] Array of call block numbers (relative-signed offsets) + + This block is an analogue of the CALL Subroutine statement. It basically executes a sequence of blocks that are somewhere else and + then goes back to the next block. Because more than one call can be normally used you can include a list of sequences to be called. + The 'nesting' of call blocks is also not allowed for the simplicity reasons. You can, of course, use the CALL blocks in the LOOP + sequences and vice versa. The value is relative for the obvious reasons - so that you can add some blocks in the beginning of the + file without disturbing the call values. Please take a look at 'Jump To Block' for reference on the values. */ + private void ProcessBlockID26(byte[] data) + { + // block processing not implemented for this - just gets added for informational purposes only + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x26; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Call_Sequence; + + int blockSize = 2 + 2 * GetWordValue(data, _position); + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockSize; + } + #endregion + + #region ID 27 - Return from sequence +/* length: 00 + + This block indicates the end of the Called Sequence. The next block played will be the block after the last CALL block (or the next Call, + if the Call block had multiple calls). + Again, this block has no body. */ + private void ProcessBlockID27(byte[] data) + { + // block processing not implemented for this - just gets added for informational purposes only + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x27; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Return_From_Sequence; + + // add to tape + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 28 - Select block +/* length: [00,01]+02 + Offset Value Type Description + 0x00 - WORD Length of the whole block (without these two bytes) + 0x02 N BYTE Number of selections + 0x03 - SELECT[N] List of selections + + ---- + SELECT structure format + Offset Value Type Description + 0x00 - WORD Relative Offset + 0x02 L BYTE Length of description text + 0x03 - CHAR[L] Description text (please use single line and max. 30 chars) + ---- + + This block is useful when the tape consists of two or more separately-loadable parts. With this block, you are able to select + one of the parts and the utility/emulator will start loading from that block. For example you can use it when the game has a + separate Trainer or when it is a multiload. Of course, to make some use of it the emulator/utility has to show a menu with the + selections when it encounters such a block. All offsets are relative signed words. */ + private void ProcessBlockID28(byte[] data) + { + // block processing not implemented for this - just gets added for informational purposes only + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x28; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Select_Block; + + int blockSize = 2 + GetWordValue(data, _position); + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockSize; + } + #endregion + + #region ID 2A - Stop the tape if in 48K mode +/* length: 04 + Offset Value Type Description + 0x00 0 DWORD Length of the block without these four bytes (0) + + When this block is encountered, the tape will stop ONLY if the machine is an 48K Spectrum. This block is to be used for + multiloading games that load one level at a time in 48K mode, but load the entire tape at once if in 128K mode. + This block has no body of its own, but follows the extension rule. */ + private void ProcessBlockID2A(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x2A; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Stop_the_Tape_48K; + t.Command = TapeCommand.STOP_THE_TAPE_48K; + + int blockSize = 4 + GetWordValue(data, _position); + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockSize; + } + #endregion + + #region ID 2B - Set signal level +/* length: 05 + Offset Value Type Description + 0x00 1 DWORD Block length (without these four bytes) + 0x04 - BYTE Signal level (0=low, 1=high) + + This block sets the current signal level to the specified value (high or low). It should be used whenever it is necessary to avoid any + ambiguities, e.g. with custom loaders which are level-sensitive. */ + private void ProcessBlockID2B(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x2B; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Set_Signal_Level; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 5; + } + #endregion + + #region ID 30 - Text description +/* length: [00]+01 + Offset Value Type Description + 0x00 N BYTE Length of the text description + 0x01 - CHAR[N] Text description in ASCII format + + This is meant to identify parts of the tape, so you know where level 1 starts, where to rewind to when the game ends, etc. + This description is not guaranteed to be shown while the tape is playing, but can be read while browsing the tape or changing + the tape pointer. + The description can be up to 255 characters long but please keep it down to about 30 so the programs can show it in one line + (where this is appropriate). + Please use 'Archive Info' block for title, authors, publisher, etc. */ + private void ProcessBlockID30(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x30; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Text_Description; + + int textLen = data[_position]; + _position++; + + string desc = Encoding.ASCII.GetString(data, _position, textLen); + + //t.BlockDescription = "[" + desc + "]"; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += textLen; + } + #endregion + + #region ID 31 - Message block +/* length: [01]+02 + Offset Value Type Description + 0x00 - BYTE Time (in seconds) for which the message should be displayed + 0x01 N BYTE Length of the text message + 0x02 - CHAR[N] Message that should be displayed in ASCII format + + This will enable the emulators to display a message for a given time. This should not stop the tape and it should not make silence. + If the time is 0 then the emulator should wait for the user to press a key. + The text message should: + stick to a maximum of 30 chars per line; + use single 0x0D (13 decimal) to separate lines; + stick to a maximum of 8 lines. + If you do not obey these rules, emulators may display your message in any way they like. */ + private void ProcessBlockID31(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x31; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Message_Block; + + _position++; + + int msgLen = data[_position]; + _position++; + + string desc = Encoding.ASCII.GetString(data, _position, msgLen); + + t.Command = TapeCommand.SHOW_MESSAGE; + + //t.BlockDescription = "[MESSAGE: " + desc + "]"; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += msgLen; + } + #endregion + + #region ID 32 - Archive info +/* length: [00,01]+02 + Offset Value Type Description + 0x00 - WORD Length of the whole block (without these two bytes) + 0x02 N BYTE Number of text strings + 0x03 - TEXT[N] List of text strings + + ---- + TEXT structure format + Offset Value Type Description + 0x00 - BYTE Text identification byte: + 00 - Full title + 01 - Software house/publisher + 02 - Author(s) + 03 - Year of publication + 04 - Language + 05 - Game/utility type + 06 - Price + 07 - Protection scheme/loader + 08 - Origin + FF - Comment(s) + 0x01 L BYTE Length of text string + 0x02 - CHAR[L] Text string in ASCII format + ---- + + Use this block at the beginning of the tape to identify the title of the game, author, publisher, year of publication, price (including + the currency), type of software (arcade adventure, puzzle, word processor, ...), protection scheme it uses (Speedlock 1, Alkatraz, ...) + and its origin (Original, Budget re-release, ...), etc. This block is built in a way that allows easy future expansion. + The block consists of a series of text strings. Each text has its identification number (which tells us what the text means) and then + the ASCII text. To make it possible to skip this block, if needed, the length of the whole block is at the beginning of it. + If all texts on the tape are in English language then you don't have to supply the 'Language' field + The information about what hardware the tape uses is in the 'Hardware Type' block, so no need for it here. */ + private void ProcessBlockID32(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x32; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Archive_Info; + + int blockLen = GetWordValue(data, 0); + _position += 2; + int stringCount = data[_position++]; + + // iterate through each string + for (int s = 0; s < stringCount; s++) + { + // identify the type of text + int type = data[_position++]; + + // get text length + int strLen = data[_position++]; + + string title = "Info: "; + + switch (type) + { + case 0x00: + title = "Full Title: "; + break; + case 0x01: + title = "Software House/Publisher: "; + break; + case 0x02: + title = "Author(s): "; + break; + case 0x03: + title = "Year of Publication: "; + break; + case 0x04: + title = "Language: "; + break; + case 0x05: + title = "Game/Utility Type: "; + break; + case 0x06: + title = "Price: "; + break; + case 0x07: + title = "Protection Scheme/Loader: "; + break; + case 0x08: + title = "Origin: "; + break; + case 0xFF: + title = "Comment(s): "; + break; + default: + break; + } + + // add title to description + //t.BlockDescription += title; + + // get string data + string val = Encoding.ASCII.GetString(data, _position, strLen); + //t.BlockDescription += val + " \n"; + + // advance to next string block + _position += strLen; + } + + // add to tape + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 33 - Hardware type +/* length: [00]*03+01 + Offset Value Type Description + 0x00 N BYTE Number of machines and hardware types for which info is supplied + 0x01 - HWINFO[N] List of machines and hardware + + ---- + HWINFO structure format + Offset Value Type Description + 0x00 - BYTE Hardware type + 0x01 - BYTE Hardware ID + 0x02 - BYTE Hardware information: + 00 - The tape RUNS on this machine or with this hardware, + but may or may not use the hardware or special features of the machine. + 01 - The tape USES the hardware or special features of the machine, + such as extra memory or a sound chip. + 02 - The tape RUNS but it DOESN'T use the hardware + or special features of the machine. + 03 - The tape DOESN'T RUN on this machine or with this hardware. + ---- + + This blocks contains information about the hardware that the programs on this tape use. Please include only machines and hardware for + which you are 100% sure that it either runs (or doesn't run) on or with, or you know it uses (or doesn't use) the hardware or special + features of that machine. + If the tape runs only on the ZX81 (and TS1000, etc.) then it clearly won't work on any Spectrum or Spectrum variant, so there's no + need to list this information. + If you are not sure or you haven't tested a tape on some particular machine/hardware combination then do not include it in the list. + The list of hardware types and IDs is somewhat large, and may be found at the end of the format description. */ + private void ProcessBlockID33(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x33; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Hardware_Type; + + _position += 2; + int blockLen = GetWordValue(data, 0); + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockLen; + } + #endregion + + #region ID 35 - Custom info block +/* length: [10,11,12,13]+14 + Offset Value Type Description + 0x00 - CHAR[10] Identification string (in ASCII) + 0x10 L DWORD Length of the custom info + 0x14 - BYTE[L] Custom info + + This block can be used to save any information you want. For example, it might contain some information written by a utility, + extra settings required by a particular emulator, or even poke data. */ + private void ProcessBlockID35(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x35; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Custom_Info_Block; + + string info = Encoding.ASCII.GetString(data, _position, 0x10); + //t.BlockDescription = "[CUSTOM INFO: " + info + "]"; + _position += 0x10; + + int blockLen = BitConverter.ToInt32(data, _position); + _position += 4; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockLen; + } + #endregion + + #region ID 5A - "Glue" block +/* length: 09 + Offset Value Type Description + 0x00 - BYTE[9] Value: { "XTape!",0x1A,MajR,MinR } + Just skip these 9 bytes and you will end up on the next ID. + + This block is generated when you merge two ZX Tape files together. It is here so that you can easily copy the files together and use + them. Of course, this means that resulting file would be 10 bytes longer than if this block was not used. All you have to do + if you encounter this block ID is to skip next 9 bytes. + If you can avoid using this block for this purpose, then do so; it is preferable to use a utility to join the two files and + ensure that they are both of the higher version number. */ + private void ProcessBlockID5A(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x5A; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Glue_Block; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 9; + } + #endregion + + #region UnDetected Blocks + + private void ProcessUnidentifiedBlock(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = -2; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Unsupported; + //t.BlockDescription = "[UNSUPPORTED - 0x" + data[_position - 1] + "]"; + + _position += GetInt32(data, _position) & 0xFFFFFF; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 4; + } + + #endregion + + #region Depreciated Blocks + + // These mostly should be ignored by ZXHawk - here for completeness + + #region ID 16 - C64 ROM Type Data Block + private void ProcessBlockID16(byte[] data) + { + + } + #endregion + + #region ID 17 - C64 Turbo Tape Data Block + private void ProcessBlockID17(byte[] data) + { + + } + #endregion + + #region ID 34 - Emulation info + private void ProcessBlockID34(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x34; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Emulation_Info; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 8; + } + #endregion + + #region ID 40 - Snapshot block + /* length: [01,02,03]+04 + Offset Value Type Description + 0x00 - BYTE Snapshot type: + 00: .Z80 format + 01: .SNA format + 0x01 L BYTE[3] Snapshot length + 0x04 - BYTE[L] Snapshot itself + + This would enable one to snapshot the game at the start and still have all the tape blocks (level data, etc.) in the same file. + Only .Z80 and .SNA snapshots are supported for compatibility reasons! + The emulator should take care of that the snapshot is not taken while the actual Tape loading is taking place (which doesn't do much sense). + And when an emulator encounters the snapshot block it should load it and then continue with the next block. */ + private void ProcessBlockID40(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x40; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Snapshot_Block; + + _position++; + + int blockLen = data[_position] | + data[_position + 1] << 8 | + data[_position + 2] << 16; + _position += 3; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockLen; + } + #endregion + + #endregion + + #endregion + + #region DataBlockDecoder + + /// + /// Used to process either a standard or turbo data block + /// + /// + /// + /// + private TapeDataBlock DecodeDataBlock + ( + TapeDataBlock block, + byte[] blockdata, + DataBlockType dataBlockType, + int pauseAfterBlock, + int pilotCount, + + int pilotToneLength = 2168, + int sync1PulseLength = 667, + int sync2PulseLength = 735, + int bit0PulseLength = 855, + int bit1PulseLength = 1710, + int bitsInLastByte = 8 + ) + { + // first get the block description + string description = string.Empty; + + // process the type byte + /* (The type is 0,1,2 or 3 for a Program, Number array, Character array or Code file. + A SCREEN$ file is regarded as a Code file with start address 16384 and length 6912 decimal. + If the file is a Program file, parameter 1 holds the autostart line number (or a number >=32768 if no LINE parameter was given) + and parameter 2 holds the start of the variable area relative to the start of the program. If it's a Code file, parameter 1 holds + the start of the code block when saved, and parameter 2 holds 32768. For data files finally, the byte at position 14 decimal holds the variable name.) + */ + + int blockSize = blockdata.Length; + + // dont get description info for Pure Data Blocks + if (dataBlockType != DataBlockType.Pure) + { + if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || blockdata[1] == 3) + { + // This is the program header + string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); + + string type = ""; + if (blockdata[0] == 0x00) + { + type = "Program"; + block.AddMetaData(BlockDescriptorTitle.Program, fileName); + } + else + { + type = "Bytes"; + block.AddMetaData(BlockDescriptorTitle.Bytes, fileName); + } + + // now build the description string + StringBuilder sb = new StringBuilder(); + sb.Append(type + ": "); + sb.Append(fileName + " "); + sb.Append(GetWordValue(blockdata, 14)); + sb.Append(":"); + sb.Append(GetWordValue(blockdata, 12)); + description = sb.ToString(); + } + else if (blockdata[0] == 0xFF) + { + // this is a data block + description = "Data Block " + (blockSize - 2) + "bytes"; + block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); + } + else + { + // other type + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize - 2); + //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + block.AddMetaData(BlockDescriptorTitle.Undefined, description); + } + } + + // update metadata + switch (dataBlockType) + { + case DataBlockType.Standard: + case DataBlockType.Turbo: + + if (dataBlockType == DataBlockType.Standard) + block.BlockDescription = BlockType.Standard_Speed_Data_Block; + if (dataBlockType == DataBlockType.Turbo) + block.BlockDescription = BlockType.Turbo_Speed_Data_Block; + + block.AddMetaData(BlockDescriptorTitle.Pilot_Pulse_Length, pilotToneLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.Pilot_Pulse_Count, pilotCount.ToString() + " Pulses"); + block.AddMetaData(BlockDescriptorTitle.First_Sync_Length, sync1PulseLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.Second_Sync_Length, sync2PulseLength.ToString() + " T-States"); + break; + + case DataBlockType.Pure: + block.BlockDescription = BlockType.Pure_Data_Block; + break; + } + + block.AddMetaData(BlockDescriptorTitle.Zero_Bit_Length, bit0PulseLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.One_Bit_Length, bit1PulseLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.Data_Length, blockSize.ToString() + " Bytes"); + block.AddMetaData(BlockDescriptorTitle.Bits_In_Last_Byte, bitsInLastByte.ToString() + " Bits"); + block.AddMetaData(BlockDescriptorTitle.Pause_After_Data, pauseAfterBlock.ToString() + " ms"); + + // calculate period information + List dataPeriods = new List(); + + // generate pilot pulses + + if (pilotCount > 0) + { + for (int i = 0; i < pilotCount; i++) + { + dataPeriods.Add(pilotToneLength); + } + + // add syncro pulses + dataPeriods.Add(sync1PulseLength); + dataPeriods.Add(sync2PulseLength); + } + + int pos = 0; + + // add bit0 and bit1 periods + for (int i = 0; i < blockSize - 1; i++, pos++) + { + for (byte b = 0x80; b != 0; b >>= 1) + { + if ((blockdata[i] & b) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + if ((blockdata[i] & b) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + } + } + + // add the last byte + for (byte c = 0x80; c != (byte)(0x80 >> bitsInLastByte); c >>= 1) + { + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + } + + // add block pause if pause is not 0 + if (pauseAfterBlock != 0) + { + int actualPause = pauseAfterBlock * 3500; + dataPeriods.Add(actualPause); + } + + // add to the tapedatablock object + block.DataPeriods = dataPeriods; + + // add the raw data + block.BlockData = blockdata; + + return block; + } + + /// + /// Used to process either a standard or turbo data block + /// + /// + /// + /// + private TapeDataBlock DecodeDataBlock + ( + TapeDataBlock block, + byte[] blockData, + DataBlockType dataBlockType, + int pauseAfterBlock, + + int pilotToneLength = 2168, + int sync1PulseLength = 667, + int sync2PulseLength = 735, + int bit0PulseLength = 855, + int bit1PulseLength = 1710, + int bitsInLastByte = 8 + ) + { + + // pilot count needs to be ascertained from flag byte + int pilotCount; + if (blockData[0] < 128) + pilotCount = 8063; + else + pilotCount = 3223; + + // now we can decode + var nBlock = DecodeDataBlock + ( + block, + blockData, + dataBlockType, + pauseAfterBlock, + pilotCount, + pilotToneLength, + sync1PulseLength, + sync2PulseLength, + bit0PulseLength, + bit1PulseLength, + bitsInLastByte + ); + + + return nBlock; + } + + #endregion + } + + public enum DataBlockType + { + Standard, + Turbo, + Pure + } +} From ec7445669c75d391119e421ed90a923eb84e422a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 15 Feb 2018 18:16:12 +0000 Subject: [PATCH 045/339] Fixed integer overflow bug in the tape device --- .../Hardware/Datacorder/DatacorderDevice.cs | 27 ++++++++++++++++++- .../Computers/SinclairSpectrum/readme.md | 5 ++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index ddc7598f03..0080872421 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -280,7 +280,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public bool GetEarBit(long cpuCycle) { // decide how many cycles worth of data we are capturing - int cycles = Convert.ToInt32(cpuCycle - _lastCycle); + long cycles = cpuCycle - _lastCycle; // check whether tape is actually playing if (_tapeIsPlaying == false) @@ -314,6 +314,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // we have reached the end of the current block + // check for any commands + var command = _dataBlocks[_currentDataBlockIndex].Command; + var block = _dataBlocks[_currentDataBlockIndex]; + switch (command) + { + // Stop the tape command found - if this is the end of the tape RTZ + // otherwise just STOP and move to the next block + case TapeCommand.STOP_THE_TAPE: + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + break; + case TapeCommand.STOP_THE_TAPE_48K: + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + break; + } + // skip any empty blocks while (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count()) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index c0147c1d3a..afcf6b122f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -13,20 +13,19 @@ At this moment this is still *very* experimental and needs a lot more work. * Keyboard input (implementing IInputPollable) * Kempston joystick (mapped to J1 currently) * Tape device that will load spectrum games in realtime (*.tzx and *.tap) -* IStatable (although this is not currently working/implemented properly during tape load operations) +* IStatable * ISettable core settings * IMemoryDomains (I think) ### Work in progress * Exact emulator timings * Floating memory bus emulation +* Tape auto-loading routines (currently you have to manually start and stop the virtual tape device) ### Not working * IDebuggable * ZX Spectrum Plus3 emulation * Default keyboard keymappings (you have to configure yourself in the core controller settings) -* Manual tape device control (at the moment the tape device detects when the spectrum goes into 'loadbytes' mode and auto-plays the tape. This is not ideal and manual control should be implemented so the user can start/stop manually, return to zero etc..) -* Only standard spectrum tape blocks currently work. Any fancy SpeedLock encoded (and similar) blocks do not ### Known bugs * Audible 'popping' from the emulated buzzer after a load state operation From c8ea81bfd858224a7823a31505c05cbdd29244ea Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Feb 2018 08:49:41 +0000 Subject: [PATCH 046/339] Fixed off-by-one-tstate frame timing issue --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 2 +- .../Computers/SinclairSpectrum/Machine/ULABase.cs | 8 ++++---- .../SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs | 1 + .../SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs | 1 + 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 43c0ec8b7a..11ec6ae96b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -123,7 +123,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PollInput(); - while (CurrentFrameCycle <= ULADevice.FrameLength) // UlaFrameCycleCount) + while (CurrentFrameCycle < ULADevice.FrameLength) // UlaFrameCycleCount) { // check for interrupt ULADevice.CheckForInterrupt(CurrentFrameCycle); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index d6e3b1ef66..1604006525 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -254,7 +254,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The longest instruction cycle count /// - protected const int LONGEST_OP_CYCLES = 23; + protected int LongestOperationCycles = 23; /// /// Signs that an interrupt has been raised in this frame. @@ -288,19 +288,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return; } - if (currentCycle < InterruptPeriod) + if (currentCycle < LongestOperationCycles)// InterruptPeriod) { // interrupt does not need to be raised yet return; } - if (currentCycle > InterruptPeriod + LONGEST_OP_CYCLES) + if (currentCycle >= InterruptPeriod + LongestOperationCycles) { // 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) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs index d7cc8f4941..92789fa82c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -9,6 +9,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum : base(machine) { InterruptPeriod = 36; + LongestOperationCycles = 23; FrameLength = 70908; ClockSpeed = 3546900; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs index a093a33aad..f72bc8bde1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs @@ -9,6 +9,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum : base(machine) { InterruptPeriod = 32; + LongestOperationCycles = 23; FrameLength = 69888; ClockSpeed = 3500000; From a3dc506c06de18877ecbc288204012283b9db5d4 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Feb 2018 09:51:00 +0000 Subject: [PATCH 047/339] Another timing fix --- .../SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs | 2 +- .../SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 2 +- .../SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs index 92789fa82c..1ac1ccf326 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum : base(machine) { InterruptPeriod = 36; - LongestOperationCycles = 23; + LongestOperationCycles = 64 + 2; FrameLength = 70908; ClockSpeed = 3546900; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index e2723dab2d..36380c17b3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // init addressable memory from ROM and RAM banks ReInitMemory(); - ULADevice = new ULA48(this); + ULADevice = new ULA128(this); BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs index f72bc8bde1..61c22499ea 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs @@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum : base(machine) { InterruptPeriod = 32; - LongestOperationCycles = 23; + LongestOperationCycles = 64; FrameLength = 69888; ClockSpeed = 3500000; From 50d28c9627e80b0103bc91769d5eb7494129017f Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Feb 2018 10:14:02 +0000 Subject: [PATCH 048/339] file reorganisation and removal of obsolete stuff --- .../BizHawk.Emulation.Cores.csproj | 36 +- .../Hardware/DefaultTapeProvider.cs | 135 -- .../{Interfaces => Input}/IKeyboard.cs | 0 .../Hardware/{ => Input}/KempstonJoystick.cs | 0 .../Interfaces/ISaveToTapeProvider.cs | 35 - .../Interfaces/ISupportsTapeBlockPlayback.cs | 43 - .../ISupportsTapeBlockSetPlayback.cs | 21 - .../Interfaces/ITapeContentProvider.cs | 25 - .../Hardware/Interfaces/ITapeData.cs | 24 - .../Interfaces/ITapeDataSerialization.cs | 27 - .../Hardware/Interfaces/ITapeProvider.cs | 53 - .../{ => Hardware/Rom}/RomData.cs | 0 .../Hardware/{ => SoundOuput}/AY38912.cs | 0 .../Hardware/{ => SoundOuput}/Buzzer.cs | 0 .../{ => Hardware/SoundOuput}/Pulse.cs | 0 .../SinclairSpectrum/Hardware/Tape.cs | 814 ---------- .../Hardware/TapeBlockSetPlayer.cs | 143 -- .../Hardware/TapeDataBlockPlayer.cs | 279 ---- .../Hardware/TapeFilePlayer.cs | 128 -- .../Machine/SpectrumBase.Sound.cs | 17 - .../Media/Tape/TAP/TapDataBlock.cs | 105 -- .../Media/Tape/TAP/TapPlayer.cs | 96 -- .../Media/Tape/TAP/TapReader.cs | 52 - .../Media/Tape/TZX/BlockAbstraction.cs | 172 -- .../Media/Tape/TZX/DataBlocks.cs | 1433 ----------------- .../SinclairSpectrum/Media/Tape/TZX/Info.cs | 250 --- .../SinclairSpectrum/Media/Tape/TZX/Types.cs | 282 ---- .../Media/Tape/TZX/TzxException.cs | 28 - .../Media/Tape/TZX/TzxHeader.cs | 68 - .../Media/Tape/TZX/TzxPlayer.cs | 94 -- .../Media/Tape/TZX/TzxReader.cs | 125 -- .../Computers/SinclairSpectrum/readme.md | 10 +- 32 files changed, 12 insertions(+), 4483 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/{Interfaces => Input}/IKeyboard.cs (100%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/{ => Input}/KempstonJoystick.cs (100%) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/{ => Hardware/Rom}/RomData.cs (100%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/{ => SoundOuput}/AY38912.cs (100%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/{ => SoundOuput}/Buzzer.cs (100%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/{ => Hardware/SoundOuput}/Pulse.cs (100%) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index aefb1900ae..f108d03e54 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -256,22 +256,11 @@ - - + + - - - - - - - - - - - - - + + @@ -288,7 +277,6 @@ - @@ -298,20 +286,8 @@ - - - - - - - - - - - - - - + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs deleted file mode 100644 index 2ee8ef6f63..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/DefaultTapeProvider.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public class DefaultTapeProvider : ITapeProvider - { - public const string RESOURCE_FOLDER = "TzxResources"; - public const string DEFAULT_SAVE_FILE_DIR = @"C:\Temp\ZxSpectrumSavedFiles"; - public const string DEFAULT_NAME = "SavedFile"; - public const string DEFAULT_EXT = ".tzx"; - private string _suggestedName; - private string _fullFileName; - private int _dataBlockCount; - - private byte[] _file; - - /// - /// The directory files should be saved to - /// - public string SaveFileFolder { get; } - - - - public DefaultTapeProvider(byte[] file, string saveFolder = null) - { - SaveFileFolder = string.IsNullOrWhiteSpace(saveFolder) - ? DEFAULT_SAVE_FILE_DIR - : saveFolder; - - _file = file; - } - - /// - /// The component provider should be able to reset itself - /// - /// - - public void Reset() - { - _dataBlockCount = 0; - _suggestedName = null; - _fullFileName = null; - } - - - /// - /// Tha tape set to load the content from - /// - public string TapeSetName { get; set; } - - /// - /// Gets a binary reader that provider TZX content - /// - /// BinaryReader instance to obtain the content from - public BinaryReader GetTapeContent() - { - Stream stream = new MemoryStream(_file); - var reader = new BinaryReader(stream); - return reader; - } - - /// - /// Creates a tape file with the specified name - /// - /// - public void CreateTapeFile() - { - //Reset(); - } - - /// - /// This method sets the name of the file according to the - /// Spectrum SAVE HEADER information - /// - /// - public void SetName(string name) - { - _suggestedName = name; - } - - /// - /// Appends the TZX block to the tape file - /// - /// - public void SaveTapeBlock(ITapeDataSerialization block) - { - if (_dataBlockCount == 0) - { - if (!Directory.Exists(SaveFileFolder)) - { - Directory.CreateDirectory(SaveFileFolder); - } - var baseFileName = $"{_suggestedName ?? DEFAULT_NAME}_{DateTime.Now:yyyyMMdd_HHmmss}{DEFAULT_EXT}"; - _fullFileName = Path.Combine(SaveFileFolder, baseFileName); - using (var writer = new BinaryWriter(File.Create(_fullFileName))) - { - var header = new TzxHeader(); - header.WriteTo(writer); - } - } - _dataBlockCount++; - - var stream = File.Open(_fullFileName, FileMode.Append); - using (var writer = new BinaryWriter(stream)) - { - block.WriteTo(writer); - } - } - - /// - /// The tape provider can finalize the tape when all - /// TZX blocks are written. - /// - public void FinalizeTapeFile() - { - } - - /// - /// Obtains the specified resource stream ot the given assembly - /// - /// Assembly to get the resource stream from - /// Resource name - private static Stream GetFileResource(Assembly asm, string resourceName) - { - var resourceFullName = $"{asm.GetName().Name}.{RESOURCE_FOLDER}.{resourceName}"; - return asm.GetManifestResourceStream(resourceFullName); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IKeyboard.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/IKeyboard.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IKeyboard.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/KempstonJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/KempstonJoystick.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs deleted file mode 100644 index 05a4de9e06..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISaveToTapeProvider.cs +++ /dev/null @@ -1,35 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This interface describes the behavior of an object that - /// provides tape content - /// - public interface ISaveToTapeProvider - { - /// - /// Creates a tape file with the specified name - /// - /// - void CreateTapeFile(); - - /// - /// This method sets the name of the file according to the - /// Spectrum SAVE HEADER information - /// - /// - void SetName(string name); - - /// - /// Appends the tape block to the tape file - /// - /// - void SaveTapeBlock(ITapeDataSerialization block); - - /// - /// The tape provider can finalize the tape when all - /// tape blocks are written. - /// - void FinalizeTapeFile(); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs deleted file mode 100644 index 8d1c47645b..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockPlayback.cs +++ /dev/null @@ -1,43 +0,0 @@ -using BizHawk.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This interface represents that the implementing class supports - /// emulating tape playback of a single tape block - /// - public interface ISupportsTapeBlockPlayback - { - /// - /// The current playing phase - /// - PlayPhase PlayPhase { get; } - - /// - /// The tact count of the CPU when playing starts - /// - long StartCycle { get; } - - /// - /// Initializes the player - /// - void InitPlay(long startCycle); - - /// - /// Gets the EAR bit value for the specified tact - /// - /// Tacts to retrieve the EAR bit - /// - /// The EAR bit value to play back - /// - bool GetEarBit(long currentCycle); - - - void SyncState(Serializer ser); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs deleted file mode 100644 index 1312761e51..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ISupportsTapeBlockSetPlayback.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This interface represents that the implementing class supports - /// emulating tape playback of a set of subsequent tape blocks - /// - public interface ISupportsTapeBlockSetPlayback : ISupportsTapeBlockPlayback - { - /// - /// Moves the player to the next playable block - /// - /// Tacts time to start the next block - void NextBlock(long currentCycle); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs deleted file mode 100644 index 62f8c6f814..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeContentProvider.cs +++ /dev/null @@ -1,25 +0,0 @@ - -using System.IO; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This interface describes the behavior of an object that - /// provides tape content - /// - public interface ITapeContentProvider - { - /// - /// Tha tape set to load the content from - /// - string TapeSetName { get; set; } - - /// - /// Gets a binary reader that provides tape content - /// - /// BinaryReader instance to obtain the content from - BinaryReader GetTapeContent(); - - void Reset(); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs deleted file mode 100644 index 5879c57536..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeData.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represetns the data in the tape - /// - public interface ITapeData - { - /// - /// Block Data - /// - byte[] Data { get; } - - /// - /// Pause after this block (given in milliseconds) - /// - ushort PauseAfter { get; } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs deleted file mode 100644 index 9f48e9097e..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeDataSerialization.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Defines the serialization operations of a TZX record - /// - public interface ITapeDataSerialization - { - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - void ReadFrom(BinaryReader reader); - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - void WriteTo(BinaryWriter writer); - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs deleted file mode 100644 index 91a4457a31..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Interfaces/ITapeProvider.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.IO; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This interface describes the behavior of an object that - /// provides TZX tape content - /// - public interface ITapeProvider - { - /// - /// Tha tape set to load the content from - /// - string TapeSetName { get; set; } - - /// - /// Gets a binary reader that provider TZX content - /// - /// BinaryReader instance to obtain the content from - BinaryReader GetTapeContent(); - - /// - /// Creates a tape file with the specified name - /// - /// - void CreateTapeFile(); - - /// - /// This method sets the name of the file according to the - /// Spectrum SAVE HEADER information - /// - /// - void SetName(string name); - - /// - /// Appends the TZX block to the tape file - /// - /// - void SaveTapeBlock(ITapeDataSerialization block); - - /// - /// The tape provider can finalize the tape when all - /// TZX blocks are written. - /// - void FinalizeTapeFile(); - - /// - /// Provider can reset itself - /// - void Reset(); - - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/RomData.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/AY38912.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Buzzer.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Pulse.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Pulse.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs deleted file mode 100644 index fd29eb3855..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Tape.cs +++ /dev/null @@ -1,814 +0,0 @@ -using BizHawk.Common; -using BizHawk.Emulation.Cores.Components.Z80A; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - * Much of the TAPE implementation has been taken from: https://github.com/Dotneteer/spectnetide - * - * MIT License - - Copyright (c) 2017 Istvan Novak - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - */ - - /// - /// Represents the tape device (or DATACORDER as AMSTRAD liked to call it) - /// - public class Tape - { - private SpectrumBase _machine { get; set; } - private Z80A _cpu { get; set; } - private Buzzer _buzzer { get; set; } - - private TapeOperationMode _currentMode; - private TapeFilePlayer _tapePlayer; - private bool _micBitState; - private long _lastMicBitActivityCycle; - private SavePhase _savePhase; - private int _pilotPulseCount; - private int _bitOffset; - private byte _dataByte; - private int _dataLength; - private byte[] _dataBuffer; - private int _dataBlockCount; - private MicPulseType _prevDataPulse; - - /// - /// Number of tacts after save mod can be exited automatically - /// - public const int SAVE_STOP_SILENCE = 17500000; - - /// - /// The address of the ERROR routine in the Spectrum ROM - /// - public const ushort ERROR_ROM_ADDRESS = 0x0008; - - /// - /// The maximum distance between two scans of the EAR bit - /// - public const int MAX_TACT_JUMP = 10000; - - /// - /// The width tolerance of save pulses - /// - public const int SAVE_PULSE_TOLERANCE = 24; - - /// - /// Minimum number of pilot pulses before SYNC1 - /// - public const int MIN_PILOT_PULSE_COUNT = 3000; - - /// - /// Lenght of the data buffer to allocate for the SAVE operation - /// - public const int DATA_BUFFER_LENGTH = 0x10000; - - /// - /// Gets the tape content provider - /// - public ITapeProvider TapeProvider { get; } - - /// - /// The TapeFilePlayer that can playback tape content - /// - public TapeFilePlayer TapeFilePlayer => _tapePlayer; - - /// - /// The current operation mode of the tape - /// - public TapeOperationMode CurrentMode => _currentMode; - - - private bool _fastLoad = false; - - - public virtual void Init(SpectrumBase machine) - { - _machine = machine; - _cpu = _machine.CPU; - _buzzer = machine.BuzzerDevice; - Reset(); - } - - public Tape(ITapeProvider tapeProvider) - { - TapeProvider = tapeProvider; - } - - public virtual void Reset() - { - TapeProvider?.Reset(); - _tapePlayer = null; - _currentMode = TapeOperationMode.Passive; - _savePhase = SavePhase.None; - _micBitState = true; - } - - public void CPUFrameCompleted() - { - SetTapeMode(); - if (CurrentMode == TapeOperationMode.Load - && _fastLoad - && TapeFilePlayer != null - && TapeFilePlayer.PlayPhase != PlayPhase.Completed - && _cpu.RegPC == 1529) //_machine.RomData.LoadBytesRoutineAddress) - { - - if (FastLoadFromTzx()) - { - //FastLoadCompleted?.Invoke(this, EventArgs.Empty); - } - - } - } - - /// - /// Sets the current tape mode according to the current PC register - /// and the MIC bit state - /// - public void SetTapeMode() - { - switch (_currentMode) - { - case TapeOperationMode.Passive: - if (_cpu.RegPC == 1523) // _machine.RomData.LoadBytesRoutineAddress) //1529 - { - EnterLoadMode(); - } - else if (_cpu.RegPC == 2416) // _machine.RomData.SaveBytesRoutineAddress) - { - EnterSaveMode(); - } - - var res = _cpu.RegPC; - var res2 = _machine.Spectrum.RegPC; - - return; - case TapeOperationMode.Save: - if (_cpu.RegPC == ERROR_ROM_ADDRESS - || (int)(_cpu.TotalExecutedCycles - _lastMicBitActivityCycle) > SAVE_STOP_SILENCE) - { - LeaveSaveMode(); - } - return; - case TapeOperationMode.Load: - if ((_tapePlayer?.Eof ?? false) || _cpu.RegPC == ERROR_ROM_ADDRESS) - { - LeaveLoadMode(); - } - return; - } - } - - /// - /// Puts the device in save mode. From now on, every MIC pulse is recorded - /// - private void EnterSaveMode() - { - _currentMode = TapeOperationMode.Save; - _savePhase = SavePhase.None; - _micBitState = true; - _lastMicBitActivityCycle = _cpu.TotalExecutedCycles; - _pilotPulseCount = 0; - _prevDataPulse = MicPulseType.None; - _dataBlockCount = 0; - TapeProvider?.CreateTapeFile(); - } - - /// - /// Leaves the save mode. Stops recording MIC pulses - /// - private void LeaveSaveMode() - { - _currentMode = TapeOperationMode.Passive; - TapeProvider?.FinalizeTapeFile(); - } - - /// - /// Puts the device in load mode. From now on, EAR pulses are played by a device - /// - private void EnterLoadMode() - { - _currentMode = TapeOperationMode.Load; - - var contentReader = TapeProvider?.GetTapeContent(); - if (contentReader == null) return; - - // --- Play the content - _tapePlayer = new TapeFilePlayer(contentReader); - _tapePlayer.ReadContent(); - _tapePlayer.InitPlay(_cpu.TotalExecutedCycles); - _buzzer.SetTapeMode(true); - } - - /// - /// Leaves the load mode. Stops the device that playes EAR pulses - /// - private void LeaveLoadMode() - { - _currentMode = TapeOperationMode.Passive; - _tapePlayer = null; - TapeProvider?.Reset(); - _buzzer.SetTapeMode(false); - } - - /// - /// Loads the next TZX player block instantly without emulation - /// EAR bit processing - /// - /// True, if fast load is operative - private bool FastLoadFromTzx() - { - var c = _machine.Spectrum; - - var blockType = TapeFilePlayer.CurrentBlock.GetType(); - bool canFlash = TapeFilePlayer.CurrentBlock is ITapeData; - - // --- Check, if we can load the current block in a fast way - if (!(TapeFilePlayer.CurrentBlock is ITapeData) - || TapeFilePlayer.PlayPhase == PlayPhase.Completed) - { - // --- We cannot play this block - return false; - } - - var currentData = TapeFilePlayer.CurrentBlock as ITapeData; - - var regs = _cpu.Regs; - - //regs.AF = regs._AF_; - //c.Set16BitAF(c.Get16BitAF_()); - _cpu.A = _cpu.A_s; - _cpu.F = _cpu.F_s; - - // --- Check if the operation is LOAD or VERIFY - var isVerify = (c.RegAF & 0xFF01) == 0xFF00; - - // --- At this point IX contains the address to load the data, - // --- DE shows the #of bytes to load. A contains 0x00 for header, - // --- 0xFF for data block - var data = currentData.Data; - if (data[0] != regs[_cpu.A]) - { - // --- This block has a different type we're expecting - regs[_cpu.A] = (byte)(regs[_cpu.A] ^ regs[_cpu.L]); - regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.Z; - regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.C; - c.RegPC = _machine.RomData.LoadBytesInvalidHeaderAddress; - - // --- Get the next block - TapeFilePlayer.NextBlock(_cpu.TotalExecutedCycles); - return true; - } - - // --- It is time to load the block - var curIndex = 1; - //var memory = _machine.me MemoryDevice; - regs[_cpu.H] = regs[_cpu.A]; - while (c.RegDE > 0) - { - var de16 = c.RegDE; - var ix16 = c.RegIX; - if (curIndex > data.Length - 1) - { - // --- No more data to read - //break; - } - - regs[_cpu.L] = data[curIndex]; - if (isVerify && regs[_cpu.L] != _machine.ReadBus(c.RegIX)) - { - // --- Verify failed - regs[_cpu.A] = (byte)(_machine.ReadBus(c.RegIX) ^ regs[_cpu.L]); - regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.Z; - regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.C; - c.RegPC = _machine.RomData.LoadBytesInvalidHeaderAddress; - return true; - } - - // --- Store the loaded data byte - _machine.WriteBus(c.RegIX, (byte)regs[_cpu.L]); - regs[_cpu.H] ^= regs[_cpu.L]; - curIndex++; - //regs.IX++; - //c.Set16BitIX((ushort)((int)c.Get16BitIX() + 1)); - c.RegIX++; - //regs.DE--; - //c.Set16BitDE((ushort)((int)c.Get16BitDE() - 1)); - //_cpu.Regs[_cpu.E]--; - c.RegDE--; - var te = c.RegDE; - } - - // --- Check the parity byte at the end of the data stream - if (curIndex > data.Length - 1 || regs[_cpu.H] != data[curIndex]) - { - // --- Carry is reset to sign an error - regs[_cpu.F] &= (byte)ZXSpectrum.FlagsResetMask.C; - } - else - { - // --- Carry is set to sign success - regs[_cpu.F] |= (byte)ZXSpectrum.FlagsSetMask.C; - } - c.RegPC = _machine.RomData.LoadBytesResumeAddress; - - // --- Get the next block - TapeFilePlayer.NextBlock(_cpu.TotalExecutedCycles); - return true; - } - - - /// - /// the EAR bit read from tape - /// - /// - /// - public virtual bool GetEarBit(int cpuCycles) - { - if (_currentMode != TapeOperationMode.Load) - { - return true; - } - - var earBit = _tapePlayer?.GetEarBit(cpuCycles) ?? true; - _buzzer.ProcessPulseValue(true, earBit); - return earBit; - } - - /// - /// Processes the mic bit change - /// - /// - public virtual void ProcessMicBit(bool micBit) - { - if (_currentMode != TapeOperationMode.Save - || _micBitState == micBit) - { - return; - } - - var length = _cpu.TotalExecutedCycles - _lastMicBitActivityCycle; - - // --- Classify the pulse by its width - var pulse = MicPulseType.None; - if (length >= TapeDataBlockPlayer.BIT_0_PL - SAVE_PULSE_TOLERANCE - && length <= TapeDataBlockPlayer.BIT_0_PL + SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.Bit0; - } - else if (length >= TapeDataBlockPlayer.BIT_1_PL - SAVE_PULSE_TOLERANCE - && length <= TapeDataBlockPlayer.BIT_1_PL + SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.Bit1; - } - if (length >= TapeDataBlockPlayer.PILOT_PL - SAVE_PULSE_TOLERANCE - && length <= TapeDataBlockPlayer.PILOT_PL + SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.Pilot; - } - else if (length >= TapeDataBlockPlayer.SYNC_1_PL - SAVE_PULSE_TOLERANCE - && length <= TapeDataBlockPlayer.SYNC_1_PL + SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.Sync1; - } - else if (length >= TapeDataBlockPlayer.SYNC_2_PL - SAVE_PULSE_TOLERANCE - && length <= TapeDataBlockPlayer.SYNC_2_PL + SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.Sync2; - } - else if (length >= TapeDataBlockPlayer.TERM_SYNC - SAVE_PULSE_TOLERANCE - && length <= TapeDataBlockPlayer.TERM_SYNC + SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.TermSync; - } - else if (length < TapeDataBlockPlayer.SYNC_1_PL - SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.TooShort; - } - else if (length > TapeDataBlockPlayer.PILOT_PL + 2 * SAVE_PULSE_TOLERANCE) - { - pulse = MicPulseType.TooLong; - } - - _micBitState = micBit; - _lastMicBitActivityCycle = _cpu.TotalExecutedCycles; - - // --- Lets process the pulse according to the current SAVE phase and pulse width - var nextPhase = SavePhase.Error; - switch (_savePhase) - { - case SavePhase.None: - if (pulse == MicPulseType.TooShort || pulse == MicPulseType.TooLong) - { - nextPhase = SavePhase.None; - } - else if (pulse == MicPulseType.Pilot) - { - _pilotPulseCount = 1; - nextPhase = SavePhase.Pilot; - } - break; - case SavePhase.Pilot: - if (pulse == MicPulseType.Pilot) - { - _pilotPulseCount++; - nextPhase = SavePhase.Pilot; - } - else if (pulse == MicPulseType.Sync1 && _pilotPulseCount >= MIN_PILOT_PULSE_COUNT) - { - nextPhase = SavePhase.Sync1; - } - break; - case SavePhase.Sync1: - if (pulse == MicPulseType.Sync2) - { - nextPhase = SavePhase.Sync2; - } - break; - case SavePhase.Sync2: - if (pulse == MicPulseType.Bit0 || pulse == MicPulseType.Bit1) - { - // --- Next pulse starts data, prepare for receiving it - _prevDataPulse = pulse; - nextPhase = SavePhase.Data; - _bitOffset = 0; - _dataByte = 0; - _dataLength = 0; - _dataBuffer = new byte[DATA_BUFFER_LENGTH]; - } - break; - case SavePhase.Data: - if (pulse == MicPulseType.Bit0 || pulse == MicPulseType.Bit1) - { - if (_prevDataPulse == MicPulseType.None) - { - // --- We are waiting for the second half of the bit pulse - _prevDataPulse = pulse; - nextPhase = SavePhase.Data; - } - else if (_prevDataPulse == pulse) - { - // --- We received a full valid bit pulse - nextPhase = SavePhase.Data; - _prevDataPulse = MicPulseType.None; - - // --- Add this bit to the received data - _bitOffset++; - _dataByte = (byte)(_dataByte * 2 + (pulse == MicPulseType.Bit0 ? 0 : 1)); - if (_bitOffset == 8) - { - // --- We received a full byte - _dataBuffer[_dataLength++] = _dataByte; - _dataByte = 0; - _bitOffset = 0; - } - } - } - else if (pulse == MicPulseType.TermSync) - { - // --- We received the terminating pulse, the datablock has been completed - nextPhase = SavePhase.None; - _dataBlockCount++; - - // --- Create and save the data block - var dataBlock = new TzxStandardSpeedDataBlock - { - Data = _dataBuffer, - DataLength = (ushort)_dataLength - }; - - // --- If this is the first data block, extract the name from the header - if (_dataBlockCount == 1 && _dataLength == 0x13) - { - // --- It's a header! - var sb = new StringBuilder(16); - for (var i = 2; i <= 11; i++) - { - sb.Append((char)_dataBuffer[i]); - } - var name = sb.ToString().TrimEnd(); - TapeProvider?.SetName(name); - } - TapeProvider?.SaveTapeBlock(dataBlock); - } - break; - } - _savePhase = nextPhase; - } - - public void SyncState(Serializer ser) - { - ser.BeginSection("TapeDevice"); - ser.Sync("_micBitState", ref _micBitState); - ser.Sync("_lastMicBitActivityCycle", ref _lastMicBitActivityCycle); - ser.Sync("_pilotPulseCount", ref _pilotPulseCount); - ser.Sync("_bitOffset", ref _bitOffset); - ser.Sync("_dataByte", ref _dataByte); - ser.Sync("_dataLength", ref _dataLength); - ser.Sync("_dataBlockCount", ref _dataBlockCount); - ser.Sync("_dataBuffer", ref _dataBuffer, false); - ser.SyncEnum("_currentMode", ref _currentMode); - ser.SyncEnum("_savePhase", ref _savePhase); - ser.SyncEnum("_prevDataPulse", ref _prevDataPulse); - - ser.EndSection(); - } - } - - /// - /// This enum represents the operation mode of the tape - /// - public enum TapeOperationMode : byte - { - /// - /// The tape device is passive - /// - Passive = 0, - - /// - /// The tape device is saving information (MIC pulses) - /// - Save, - - /// - /// The tape device generates EAR pulses from a player - /// - Load - } - - /// - /// This class represents a spectrum tape header - /// - public class SpectrumTapeHeader - { - private const int HEADER_LEN = 19; - private const int TYPE_OFFS = 1; - private const int NAME_OFFS = 2; - private const int NAME_LEN = 10; - private const int DATA_LEN_OFFS = 12; - private const int PAR1_OFFS = 14; - private const int PAR2_OFFS = 16; - private const int CHK_OFFS = 18; - - /// - /// The bytes of the header - /// - public byte[] HeaderBytes { get; } - - /// - /// Initializes a new instance of the class. - /// - public SpectrumTapeHeader() - { - HeaderBytes = new byte[HEADER_LEN]; - for (var i = 0; i < HEADER_LEN; i++) HeaderBytes[i] = 0x00; - CalcChecksum(); - } - - /// - /// Initializes a new instance with the specified header data. - /// - /// Header data - public SpectrumTapeHeader(byte[] header) - { - if (header == null) throw new ArgumentNullException(nameof(header)); - if (header.Length != HEADER_LEN) - { - throw new ArgumentException($"Header must be exactly {HEADER_LEN} bytes long"); - } - HeaderBytes = new byte[HEADER_LEN]; - header.CopyTo(HeaderBytes, 0); - CalcChecksum(); - } - - /// - /// Gets or sets the type of the header - /// - public byte Type - { - get { return HeaderBytes[TYPE_OFFS]; } - set - { - HeaderBytes[TYPE_OFFS] = (byte)(value & 0x03); - CalcChecksum(); - } - } - - /// - /// Gets or sets the program name - /// - public string Name - { - get - { - var name = new StringBuilder(NAME_LEN + 4); - for (var i = NAME_OFFS; i < NAME_OFFS + NAME_LEN; i++) - { - name.Append((char)HeaderBytes[i]); - } - return name.ToString().TrimEnd(); - } - set - { - if (value == null) throw new ArgumentNullException(nameof(value)); - if (value.Length > NAME_LEN) value = value.Substring(0, NAME_LEN); - else if (value.Length < NAME_LEN) value = value.PadRight(NAME_LEN, ' '); - for (var i = NAME_OFFS; i < NAME_OFFS + NAME_LEN; i++) - { - HeaderBytes[i] = (byte)value[i - NAME_OFFS]; - } - CalcChecksum(); - } - } - - /// - /// Gets or sets the Data Length - /// - public ushort DataLength - { - get { return GetWord(DATA_LEN_OFFS); } - set { SetWord(DATA_LEN_OFFS, value); } - } - - /// - /// Gets or sets Parameter1 - /// - public ushort Parameter1 - { - get { return GetWord(PAR1_OFFS); } - set { SetWord(PAR1_OFFS, value); } - } - - /// - /// Gets or sets Parameter2 - /// - public ushort Parameter2 - { - get { return GetWord(PAR2_OFFS); } - set { SetWord(PAR2_OFFS, value); } - } - - /// - /// Gets the value of checksum - /// - public byte Checksum => HeaderBytes[CHK_OFFS]; - - /// - /// Calculate the checksum - /// - private void CalcChecksum() - { - var chk = 0x00; - for (var i = 0; i < HEADER_LEN - 1; i++) chk ^= HeaderBytes[i]; - HeaderBytes[CHK_OFFS] = (byte)chk; - } - - /// - /// Gets the word value from the specified offset - /// - private ushort GetWord(int offset) => - (ushort)(HeaderBytes[offset] + 256 * HeaderBytes[offset + 1]); - - /// - /// Sets the word value at the specified offset - /// - private void SetWord(int offset, ushort value) - { - HeaderBytes[offset] = (byte)(value & 0xff); - HeaderBytes[offset + 1] = (byte)(value >> 8); - CalcChecksum(); - } - } - - /// - /// This enum defines the MIC pulse types according to their widths - /// - public enum MicPulseType : byte - { - /// - /// No pulse information - /// - None = 0, - - /// - /// Too short to be a valid pulse - /// - TooShort, - - /// - /// Too long to be a valid pulse - /// - TooLong, - - /// - /// PILOT pulse (Length: 2168 cycles) - /// - Pilot, - - /// - /// SYNC1 pulse (Length: 667 cycles) - /// - Sync1, - - /// - /// SYNC2 pulse (Length: 735 cycles) - /// - Sync2, - - /// - /// BIT0 pulse (Length: 855 cycles) - /// - Bit0, - - /// - /// BIT1 pulse (Length: 1710 cycles) - /// - Bit1, - - /// - /// TERM_SYNC pulse (Length: 947 cycles) - /// - TermSync - } - - /// - /// Represents the playing phase of the current block - /// - public enum PlayPhase - { - /// - /// The player is passive - /// - None = 0, - - /// - /// Pilot signals - /// - Pilot, - - /// - /// Sync signals at the end of the pilot - /// - Sync, - - /// - /// Bits in the data block - /// - Data, - - /// - /// Short terminating sync signal before pause - /// - TermSync, - - /// - /// Pause after the data block - /// - Pause, - - /// - /// The entire block has been played back - /// - Completed - } - - /// - /// This enumeration defines the phases of the SAVE operation - /// - public enum SavePhase : byte - { - /// No SAVE operation is in progress - None = 0, - - /// Emitting PILOT impulses - Pilot, - - /// Emitting SYNC1 impulse - Sync1, - - /// Emitting SYNC2 impulse - Sync2, - - /// Emitting BIT0/BIT1 impulses - Data, - - /// Unexpected pulse detected - Error - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs deleted file mode 100644 index 6d5b149f9e..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeBlockSetPlayer.cs +++ /dev/null @@ -1,143 +0,0 @@ -using BizHawk.Common; -using System.Collections.Generic; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class is responsible to "play" a tape file. - /// - public class TapeBlockSetPlayer : ISupportsTapeBlockSetPlayback - { - /// - /// All data blocks that can be played back - /// - public List DataBlocks { get; } - - /// - /// Signs that the player completed playing back the file - /// - private bool eof; - public bool Eof - { - get { return eof; } - set { eof = value; } - } - - - /// - /// Gets the currently playing block's index - /// - private int currentBlockIndex; - public int CurrentBlockIndex - { - get { return currentBlockIndex; } - set { currentBlockIndex = value; } - } - - /// - /// The current playable block - /// - private ISupportsTapeBlockPlayback currentBlock; - public ISupportsTapeBlockPlayback CurrentBlock - { - get { return DataBlocks[CurrentBlockIndex]; } - //set { currentBlock = value; } - } - - - /// - /// The current playing phase - /// - private PlayPhase playPhase; - public PlayPhase PlayPhase - { - get { return playPhase; } - set { playPhase = value; } - } - - - /// - /// The cycle count of the CPU when playing starts - /// - private long startCycle; - public long StartCycle - { - get { return startCycle; } - set { startCycle = value; } - } - - - public TapeBlockSetPlayer(List dataBlocks) - { - DataBlocks = dataBlocks; - Eof = dataBlocks.Count == 0; - } - - /// - /// Initializes the player - /// - public void InitPlay(long startTact) - { - CurrentBlockIndex = -1; - NextBlock(startTact); - PlayPhase = PlayPhase.None; - StartCycle = startTact; - } - - /// - /// Gets the EAR bit value for the specified cycle - /// - /// Cycles to retrieve the EAR bit - /// - /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block - /// - public bool GetEarBit(long currentCycle) - { - // --- Check for EOF - if (CurrentBlockIndex == DataBlocks.Count - 1 - && (CurrentBlock.PlayPhase == PlayPhase.Pause || CurrentBlock.PlayPhase == PlayPhase.Completed)) - { - Eof = true; - } - if (CurrentBlockIndex >= DataBlocks.Count || CurrentBlock == null) - { - // --- After all playable block played back, there's nothing more to do - PlayPhase = PlayPhase.Completed; - return true; - } - var earbit = CurrentBlock.GetEarBit(currentCycle); - if (CurrentBlock.PlayPhase == PlayPhase.Completed) - { - NextBlock(currentCycle); - } - return earbit; - } - - /// - /// Moves the current block index to the next playable block - /// - /// Cycles time to start the next block - public void NextBlock(long currentCycle) - { - if (CurrentBlockIndex >= DataBlocks.Count - 1) - { - PlayPhase = PlayPhase.Completed; - Eof = true; - return; - } - CurrentBlockIndex++; - CurrentBlock.InitPlay(currentCycle); - } - - public void SyncState(Serializer ser) - { - ser.BeginSection("TapeBlockSetPlayer"); - ser.Sync("eof", ref eof); - ser.Sync("currentBlockIndex", ref currentBlockIndex); - ser.SyncEnum("playPhase", ref playPhase); - ser.Sync("startCycle", ref startCycle); - currentBlock.SyncState(ser); - ser.EndSection(); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs deleted file mode 100644 index 0c7dae4f14..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeDataBlockPlayer.cs +++ /dev/null @@ -1,279 +0,0 @@ - -using BizHawk.Common; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents the standard speed data block in a tape file - /// - public class TapeDataBlockPlayer : ISupportsTapeBlockPlayback, ITapeData - { - /// - /// Pause after this block (default: 1000ms) - /// - private ushort pauseAfter; - public ushort PauseAfter - { - get { return pauseAfter; } - } - - /// - /// Block Data - /// - private byte[] data; - public byte[] Data - { - get { return data; } - } - - /// - /// Initializes a new instance - /// - public TapeDataBlockPlayer(byte[] _data, ushort _pauseAfter) - { - pauseAfter = _pauseAfter; - data = _data; - } - - /// - /// Pilot pulse length - /// - public const int PILOT_PL = 2168; - - /// - /// Pilot pulses in the ROM header block - /// - public const int HEADER_PILOT_COUNT = 8063; - - /// - /// Pilot pulses in the ROM data block - /// - public const int DATA_PILOT_COUNT = 3223; - - /// - /// Sync 1 pulse length - /// - public const int SYNC_1_PL = 667; - - /// - /// Sync 2 pulse lenth - /// - public const int SYNC_2_PL = 735; - - /// - /// Bit 0 pulse length - /// - public const int BIT_0_PL = 855; - - /// - /// Bit 1 pulse length - /// - public const int BIT_1_PL = 1710; - - /// - /// End sync pulse length - /// - public const int TERM_SYNC = 947; - - /// - /// 1 millisecond pause - /// - public const int PAUSE_MS = 3500; - - private int _pilotEnds; - private int _sync1Ends; - private int _sync2Ends; - private int _bitStarts; - private int _bitPulseLength; - private bool _currentBit; - private long _termSyncEnds; - private long _pauseEnds; - - /// - /// The index of the currently playing byte - /// - private int byteIndex; - public int ByteIndex - { - get { return byteIndex; } - set { byteIndex = value; } - } - - /// - /// The mask of the currently playing bit in the current byte - /// - private byte bitMask; - public byte BitMask - { - get { return bitMask; } - set { bitMask = value; } - } - - /// - /// The current playing phase - /// - private PlayPhase playPhase; - public PlayPhase PlayPhase - { - get { return playPhase; } - set { playPhase = value; } - } - - /// - /// The cycle count of the CPU when playing starts - /// - private long startCycle; - public long StartCycle - { - get { return startCycle; } - set { startCycle = value; } - } - - /// - /// Last cycle queried - /// - private long lastCycle; - public long LastCycle - { - get { return lastCycle; } - set { lastCycle = value; } - } - - /// - /// Initializes the player - /// - public void InitPlay(long startTact) - { - PlayPhase = PlayPhase.Pilot; - StartCycle = LastCycle = startTact; - _pilotEnds = ((Data[0] & 0x80) == 0 ? HEADER_PILOT_COUNT : DATA_PILOT_COUNT) * PILOT_PL; - _sync1Ends = _pilotEnds + SYNC_1_PL; - _sync2Ends = _sync1Ends + SYNC_2_PL; - ByteIndex = 0; - BitMask = 0x80; - } - - /// - /// Gets the EAR bit value for the specified cycle - /// - /// Tacts to retrieve the EAR bit - /// - /// The EAR bit value to play back - /// - public bool GetEarBit(long currentCycle) - { - var pos = (int)(currentCycle - StartCycle); - LastCycle = currentCycle; - - if (PlayPhase == PlayPhase.Pilot || PlayPhase == PlayPhase.Sync) - { - // --- Generate the appropriate pilot or sync EAR bit - if (pos <= _pilotEnds) - { - // --- Alternating pilot pulses - return (pos / PILOT_PL) % 2 == 0; - } - if (pos <= _sync1Ends) - { - // --- 1st sync pulse - PlayPhase = PlayPhase.Sync; - return false; - } - if (pos <= _sync2Ends) - { - // --- 2nd sync pulse - PlayPhase = PlayPhase.Sync; - return true; - } - PlayPhase = PlayPhase.Data; - _bitStarts = _sync2Ends; - _currentBit = (Data[ByteIndex] & BitMask) != 0; - _bitPulseLength = _currentBit ? BIT_1_PL : BIT_0_PL; - } - if (PlayPhase == PlayPhase.Data) - { - // --- Data block playback - // --- Generate current bit pulse - var bitPos = pos - _bitStarts; - if (bitPos < _bitPulseLength) - { - // --- First pulse of the bit - return false; - } - if (bitPos < 2 * _bitPulseLength) - { - // --- Second pulse of the bit - return true; - } - - // --- Move to the next bit, or byte - if ((BitMask >>= 1) == 0) - { - BitMask = 0x80; - ByteIndex++; - } - - // --- Prepare the next bit - if (ByteIndex < Data.Length) - { - _bitStarts += 2 * _bitPulseLength; - _currentBit = (Data[ByteIndex] & BitMask) != 0; - _bitPulseLength = _currentBit ? BIT_1_PL : BIT_0_PL; - // --- We're in the first pulse of the next bit - return false; - } - - // --- We've played back all data bytes, send terminating pulse - PlayPhase = PlayPhase.TermSync; - _termSyncEnds = currentCycle + TERM_SYNC; - return false; - } - - if (PlayPhase == PlayPhase.TermSync) - { - if (currentCycle < _termSyncEnds) - { - return false; - } - - // --- We've played back all data, not, it's pause time - PlayPhase = PlayPhase.Pause; - _pauseEnds = currentCycle + PAUSE_MS * PauseAfter; - return true; - } - - // --- We need to produce pause signs - if (currentCycle > _pauseEnds) - { - PlayPhase = PlayPhase.Completed; - } - return true; - } - - - public void SyncState(Serializer ser) - { - ser.BeginSection("TapeDataBlockPlayer"); - - ser.Sync("pauseAfter", ref pauseAfter); - ser.Sync("data", ref data, false); - - ser.Sync("_pilotEnds", ref _pilotEnds); - ser.Sync("_sync1Ends", ref _sync1Ends); - ser.Sync("_sync2Ends", ref _sync2Ends); - ser.Sync("_bitStarts", ref _bitStarts); - ser.Sync("_bitPulseLength", ref _bitPulseLength); - ser.Sync("_currentBit", ref _currentBit); - ser.Sync("_termSyncEnds", ref _termSyncEnds); - ser.Sync("_pauseEnds", ref _pauseEnds); - - ser.Sync("byteIndex", ref byteIndex); - ser.Sync("bitMask", ref bitMask); - ser.SyncEnum("playPhase", ref playPhase); - ser.Sync("startCycle", ref startCycle); - ser.Sync("lastCycle", ref lastCycle); - - ser.EndSection(); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs deleted file mode 100644 index 92c32dbc95..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/TapeFilePlayer.cs +++ /dev/null @@ -1,128 +0,0 @@ -using BizHawk.Common; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class recognizes .TZX and .TAP files, and playes back - /// the content accordingly. - /// - public class TapeFilePlayer : ISupportsTapeBlockPlayback - { - private readonly BinaryReader _reader; - private TapeBlockSetPlayer _player; - private int _numberOfDataBlocks; - - /// - /// Data blocks to play back - /// - public List DataBlocks { get; private set; } - - /// - /// Signs that the player completed playing back the file - /// - public bool Eof => _player.Eof; - - /// - /// Initializes the player from the specified reader - /// - /// BinaryReader instance to get tape file data from - public TapeFilePlayer(BinaryReader reader) - { - _reader = reader; - } - - /// - /// Reads in the content of the tape file so that it can be played - /// - /// True, if read was successful; otherwise, false - public bool ReadContent() - { - // --- First try TzxReader - var tzxReader = new TzxReader(_reader); - var readerFound = false; - try - { - readerFound = tzxReader.ReadContent(); - } - catch (Exception) - { - // --- This exception is intentionally ingnored - } - - if (readerFound) - { - // --- This is a .TZX format - DataBlocks = tzxReader.DataBlocks.Where(b => b is ISupportsTapeBlockPlayback) - .Cast() - .ToList(); - _player = new TapeBlockSetPlayer(DataBlocks); - return true; - } - - // --- Let's assume .TAP tap format - _reader.BaseStream.Seek(0, SeekOrigin.Begin); - var tapReader = new TapReader(_reader); - readerFound = tapReader.ReadContent(); - DataBlocks = tapReader.DataBlocks.Cast() - .ToList(); - _player = new TapeBlockSetPlayer(DataBlocks); - return readerFound; - } - - /// - /// Gets the currently playing block's index - /// - public int CurrentBlockIndex => _player.CurrentBlockIndex; - - /// - /// The current playable block - /// - public ISupportsTapeBlockPlayback CurrentBlock => _player.CurrentBlock; - - /// - /// The current playing phase - /// - public PlayPhase PlayPhase => _player.PlayPhase; - - /// - /// The tact count of the CPU when playing starts - /// - public long StartCycle => _player.StartCycle; - - /// - /// Initializes the player - /// - public void InitPlay(long startCycle) - { - _player.InitPlay(startCycle); - } - - /// - /// Gets the EAR bit value for the specified cycle - /// - /// Tacts to retrieve the EAR bit - /// - /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block - /// - public bool GetEarBit(long currentCycle) => _player.GetEarBit(currentCycle); - - /// - /// Moves the current block index to the next playable block - /// - /// Tacts time to start the next block - public void NextBlock(long currentCycle) => _player.NextBlock(currentCycle); - - public void SyncState(Serializer ser) - { - ser.BeginSection("TapeFilePlayer"); - ReadContent(); - ser.Sync("_numberOfDataBlocks", ref _numberOfDataBlocks); - _player.SyncState(ser); - ser.EndSection(); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs deleted file mode 100644 index 994e32df80..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Sound.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// The abstract class that all emulated models will inherit from - /// * Sound * - /// - public abstract partial class SpectrumBase - { - - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs deleted file mode 100644 index 0682b32752..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapDataBlock.cs +++ /dev/null @@ -1,105 +0,0 @@ - -using BizHawk.Common; -using System.IO; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class describes a TAP Block - /// - public sealed class TapDataBlock : - ITapeData, - ITapeDataSerialization, - ISupportsTapeBlockPlayback - { - private TapeDataBlockPlayer _player; - - /// - /// Block Data - /// - private byte[] data; - public byte[] Data - { - get { return data; } - set { data = value; } - } - - /// - /// Pause after this block (given in milliseconds) - /// - public ushort PauseAfter => 1000; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public void ReadFrom(BinaryReader reader) - { - var length = reader.ReadUInt16(); - Data = reader.ReadBytes(length); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public void WriteTo(BinaryWriter writer) - { - writer.Write((ushort)Data.Length); - writer.Write(Data); - } - - /// - /// The index of the currently playing byte - /// - /// This proprty is made public for test purposes - public int ByteIndex => _player.ByteIndex; - - /// - /// The mask of the currently playing bit in the current byte - /// - public byte BitMask => _player.BitMask; - - /// - /// The current playing phase - /// - public PlayPhase PlayPhase => _player.PlayPhase; - - /// - /// The tact count of the CPU when playing starts - /// - public long StartCycle => _player.StartCycle; - - /// - /// Last tact queried - /// - public long LastCycle => _player.LastCycle; - - /// - /// Initializes the player - /// - public void InitPlay(long startTact) - { - _player = new TapeDataBlockPlayer(Data, PauseAfter); - _player.InitPlay(startTact); - } - - /// - /// Gets the EAR bit value for the specified tact - /// - /// Tacts to retrieve the EAR bit - /// - /// The EAR bit value to play back - /// - public bool GetEarBit(long currentTact) => _player.GetEarBit(currentTact); - - public void SyncState(Serializer ser) - { - ser.BeginSection("TapDataBlock"); - - ser.Sync("data", ref data, false); - - ser.EndSection(); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs deleted file mode 100644 index 0c24566e37..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapPlayer.cs +++ /dev/null @@ -1,96 +0,0 @@ - -using BizHawk.Common; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class is responsible to "play" a TAP file. - /// - public class TapPlayer : TapReader, ISupportsTapeBlockPlayback - { - private TapeBlockSetPlayer _player; - - /// - /// Signs that the player completed playing back the file - /// - public bool Eof => _player.Eof; - - /// - /// Initializes the player from the specified reader - /// - /// BinaryReader instance to get TZX file data from - public TapPlayer(BinaryReader reader) : base(reader) - { - } - - /// - /// Reads in the content of the TZX file so that it can be played - /// - /// True, if read was successful; otherwise, false - public override bool ReadContent() - { - var success = base.ReadContent(); - - var blocks = DataBlocks.Cast() - .ToList(); - _player = new TapeBlockSetPlayer(blocks); - return success; - } - - /// - /// Gets the currently playing block's index - /// - public int CurrentBlockIndex => _player.CurrentBlockIndex; - - /// - /// The current playable block - /// - public ISupportsTapeBlockPlayback CurrentBlock => _player.CurrentBlock; - - /// - /// The current playing phase - /// - public PlayPhase PlayPhase => _player.PlayPhase; - - /// - /// The tact count of the CPU when playing starts - /// - public long StartCycle => _player.StartCycle; - - /// - /// Initializes the player - /// - public void InitPlay(long startCycle) - { - _player.InitPlay(startCycle); - } - - /// - /// Gets the EAR bit value for the specified tact - /// - /// Tacts to retrieve the EAR bit - /// - /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block - /// - public bool GetEarBit(long currentCycle) => _player.GetEarBit(currentCycle); - - /// - /// Moves the current block index to the next playable block - /// - /// Tacts time to start the next block - public void NextBlock(long currentCycle) => _player.NextBlock(currentCycle); - - - public void SyncState(Serializer ser) - { - ser.BeginSection("TapePlayer"); - - _player.SyncState(ser); - - ser.EndSection(); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs deleted file mode 100644 index b915daf0fc..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapReader.cs +++ /dev/null @@ -1,52 +0,0 @@ - -using System.Collections.Generic; -using System.IO; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class reads a TAP file - /// - public class TapReader - { - private readonly BinaryReader _reader; - - /// - /// Data blocks of this TZX file - /// - public IList DataBlocks { get; } - - /// - /// Initializes the player from the specified reader - /// - /// - public TapReader(BinaryReader reader) - { - _reader = reader; - DataBlocks = new List(); - } - - /// - /// Reads in the content of the TZX file so that it can be played - /// - /// True, if read was successful; otherwise, false - public virtual bool ReadContent() - { - try - { - while (_reader.BaseStream.Position != _reader.BaseStream.Length) - { - var tapBlock = new TapDataBlock(); - tapBlock.ReadFrom(_reader); - DataBlocks.Add(tapBlock); - } - return true; - } - catch - { - // --- This exception is intentionally ignored - return false; - } - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs deleted file mode 100644 index a5145928e9..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/BlockAbstraction.cs +++ /dev/null @@ -1,172 +0,0 @@ - -using System; -using System.IO; -using System.Text; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class describes a TZX Block - /// - public abstract class TzxDataBlockBase : ITapeDataSerialization - { - /// - /// The ID of the block - /// - public abstract int BlockId { get; } - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public abstract void ReadFrom(BinaryReader reader); - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public abstract void WriteTo(BinaryWriter writer); - - /// - /// Override this method to check the content of the block - /// - public virtual bool IsValid => true; - - /// - /// Reads the specified number of words from the reader. - /// - /// Reader to obtain the input from - /// Number of words to get - /// Word array read from the input - public static ushort[] ReadWords(BinaryReader reader, int count) - { - var result = new ushort[count]; - var bytes = reader.ReadBytes(2 * count); - for (var i = 0; i < count; i++) - { - result[i] = (ushort)(bytes[i * 2] + bytes[i * 2 + 1] << 8); - } - return result; - } - - /// - /// Writes the specified array of words to the writer - /// - /// Output - /// Word array - public static void WriteWords(BinaryWriter writer, ushort[] words) - { - foreach (var word in words) - { - writer.Write(word); - } - } - - /// - /// Converts the provided bytes to an ASCII string - /// - /// Bytes to convert - /// First byte offset - /// Number of bytes - /// ASCII string representation - public static string ToAsciiString(byte[] bytes, int offset = 0, int count = -1) - { - if (count < 0) count = bytes.Length - offset; - var sb = new StringBuilder(); - for (var i = offset; i < count; i++) - { - sb.Append(Convert.ToChar(bytes[i])); - } - return sb.ToString(); - } - } - - /// - /// Base class for all TZX block type with data length of 3 bytes - /// - public abstract class Tzx3ByteDataBlockBase : TzxDataBlockBase - { - /// - /// Used bits in the last byte (other bits should be 0) - /// - /// - /// (e.g. if this is 6, then the bits used(x) in the last byte are: - /// xxxxxx00, where MSb is the leftmost bit, LSb is the rightmost bit) - /// - public byte LastByteUsedBits { get; set; } - - /// - /// Lenght of block data - /// - public byte[] DataLength { get; set; } - - /// - /// Block Data - /// - public byte[] Data { get; set; } - - /// - /// Override this method to check the content of the block - /// - public override bool IsValid => GetLength() == Data.Length; - - /// - /// Calculates data length - /// - protected int GetLength() - { - return DataLength[0] + DataLength[1] << 8 + DataLength[2] << 16; - } - } - - /// - /// This class represents a TZX data block with empty body - /// - public abstract class TzxBodylessDataBlockBase : TzxDataBlockBase - { - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - } - } - - /// - /// This class represents a deprecated block - /// - public abstract class TzxDeprecatedDataBlockBase : TzxDataBlockBase - { - /// - /// Reads through the block infromation, and does not store it - /// - /// Stream to read the block from - public abstract void ReadThrough(BinaryReader reader); - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - throw new InvalidOperationException("Deprecated TZX data blocks cannot be written."); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs deleted file mode 100644 index cfd694d544..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/DataBlocks.cs +++ /dev/null @@ -1,1433 +0,0 @@ - -using BizHawk.Common; -using System.IO; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxArchiveInfoDataBlock : Tzx3ByteDataBlockBase - { - /// - /// Length of the whole block (without these two bytes) - /// - public ushort Length { get; set; } - - /// - /// Number of text strings - /// - public byte StringCount { get; set; } - - /// - /// List of text strings - /// - public TzxText[] TextStrings { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x32; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Length = reader.ReadUInt16(); - StringCount = reader.ReadByte(); - TextStrings = new TzxText[StringCount]; - for (var i = 0; i < StringCount; i++) - { - var text = new TzxText(); - text.ReadFrom(reader); - TextStrings[i] = text; - } - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Length); - writer.Write(StringCount); - foreach (var text in TextStrings) - { - text.WriteTo(writer); - } - } - } - - /// - /// This block was created to support the Commodore 64 standard - /// ROM and similar tape blocks. - /// - public class TzxC64RomTypeDataBlock : TzxDeprecatedDataBlockBase - { - /// - /// The ID of the block - /// - public override int BlockId => 0x16; - - /// - /// Reads through the block infromation, and does not store it - /// - /// Stream to read the block from - public override void ReadThrough(BinaryReader reader) - { - var length = reader.ReadInt32(); - reader.ReadBytes(length - 4); - } - } - - /// - /// This block is made to support another type of encoding that is - /// commonly used by the C64. - /// - public class TzxC64TurboTapeDataBlock : TzxDeprecatedDataBlockBase - { - /// - /// The ID of the block - /// - public override int BlockId => 0x17; - - /// - /// Reads through the block infromation, and does not store it - /// - /// Stream to read the block from - public override void ReadThrough(BinaryReader reader) - { - var length = reader.ReadInt32(); - reader.ReadBytes(length - 4); - } - } - - /// - /// This block is an analogue of the CALL Subroutine statement. - /// - /// - /// It basically executes a sequence of blocks that are somewhere - /// else and then goes back to the next block. Because more than - /// one call can be normally used you can include a list of sequences - /// to be called. The 'nesting' of call blocks is also not allowed - /// for the simplicity reasons. You can, of course, use the CALL - /// blocks in the LOOP sequences and vice versa. - /// - public class TzxCallSequenceDataBlock : TzxDataBlockBase - { - /// - /// Number of group name - /// - public byte NumberOfCalls { get; set; } - - /// - /// Group name bytes - /// - public ushort[] BlockOffsets { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x26; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - NumberOfCalls = reader.ReadByte(); - BlockOffsets = ReadWords(reader, NumberOfCalls); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(NumberOfCalls); - WriteWords(writer, BlockOffsets); - } - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxCswRecordingDataBlock : TzxDataBlockBase - { - /// - /// Block length (without these four bytes) - /// - public uint BlockLength { get; set; } - - /// - /// Pause after this block - /// - public ushort PauseAfter { get; set; } - - /// - /// Sampling rate - /// - public byte[] SamplingRate { get; set; } - - /// - /// Compression type - /// - /// - /// 0x01=RLE, 0x02=Z-RLE - /// - public byte CompressionType { get; set; } - - /// - /// Number of stored pulses (after decompression, for validation purposes) - /// - public uint PulseCount { get; set; } - - /// - /// CSW data, encoded according to the CSW file format specification - /// - public byte[] Data { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x18; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - BlockLength = reader.ReadUInt32(); - PauseAfter = reader.ReadUInt16(); - SamplingRate = reader.ReadBytes(3); - CompressionType = reader.ReadByte(); - PulseCount = reader.ReadUInt32(); - var length = (int)BlockLength - 4 /* PauseAfter*/ - 3 /* SamplingRate */ - - 1 /* CompressionType */ - 4 /* PulseCount */; - Data = reader.ReadBytes(length); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(BlockLength); - writer.Write(PauseAfter); - writer.Write(SamplingRate); - writer.Write(CompressionType); - writer.Write(PulseCount); - writer.Write(Data); - } - - /// - /// Override this method to check the content of the block - /// - public override bool IsValid => BlockLength == 4 + 3 + 1 + 4 + Data.Length; - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxCustomInfoDataBlock : Tzx3ByteDataBlockBase - { - /// - /// Identification string (in ASCII) - /// - public byte[] Id { get; set; } - - /// - /// String representation of the ID - /// - public string IdText => ToAsciiString(Id); - - /// - /// Length of the custom info - /// - public uint Length { get; set; } - - /// - /// Custom information - /// - public byte[] CustomInfo { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x35; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Id = reader.ReadBytes(10); - Length = reader.ReadUInt32(); - CustomInfo = reader.ReadBytes((int)Length); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Id); - writer.Write(Length); - writer.Write(CustomInfo); - } - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxDirectRecordingDataBlock : Tzx3ByteDataBlockBase - { - /// - /// Number of T-states per sample (bit of data) - /// - public ushort TactsPerSample { get; set; } - - /// - /// Pause after this block - /// - public ushort PauseAfter { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x15; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - TactsPerSample = reader.ReadUInt16(); - PauseAfter = reader.ReadUInt16(); - LastByteUsedBits = reader.ReadByte(); - DataLength = reader.ReadBytes(3); - Data = reader.ReadBytes(GetLength()); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(TactsPerSample); - writer.Write(PauseAfter); - writer.Write(LastByteUsedBits); - writer.Write(DataLength); - writer.Write(Data); - } - } - - /// - /// This is a special block that would normally be generated only by emulators. - /// - public class TzxEmulationInfoDataBlock : TzxDeprecatedDataBlockBase - { - /// - /// The ID of the block - /// - public override int BlockId => 0x34; - - /// - /// Reads through the block infromation, and does not store it - /// - /// Stream to read the block from - public override void ReadThrough(BinaryReader reader) - { - reader.ReadBytes(8); - } - } - - /// - /// Represents a generalized data block in a TZX file - /// - public class TzxGeneralizedDataBlock : TzxDataBlockBase - { - /// - /// Block length (without these four bytes) - /// - public uint BlockLength { get; set; } - - /// - /// Pause after this block - /// - public ushort PauseAfter { get; set; } - - /// - /// Total number of symbols in pilot/sync block (can be 0) - /// - public uint Totp { get; set; } - - /// - /// Maximum number of pulses per pilot/sync symbol - /// - public byte Npp { get; set; } - - /// - /// Number of pilot/sync symbols in the alphabet table (0=256) - /// - public byte Asp { get; set; } - - /// - /// Total number of symbols in data stream (can be 0) - /// - public uint Totd { get; set; } - - /// - /// Maximum number of pulses per data symbol - /// - public byte Npd { get; set; } - - /// - /// Number of data symbols in the alphabet table (0=256) - /// - public byte Asd { get; set; } - - /// - /// Pilot and sync symbols definition table - /// - /// - /// This field is present only if Totp > 0 - /// - public TzxSymDef[] PilotSymDef { get; set; } - - /// - /// Pilot and sync data stream - /// - /// - /// This field is present only if Totd > 0 - /// - public TzxPrle[] PilotStream { get; set; } - - /// - /// Data symbols definition table - /// - /// - /// This field is present only if Totp > 0 - /// - public TzxSymDef[] DataSymDef { get; set; } - - /// - /// Data stream - /// - /// - /// This field is present only if Totd > 0 - /// - public TzxPrle[] DataStream { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x19; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - BlockLength = reader.ReadUInt32(); - PauseAfter = reader.ReadUInt16(); - Totp = reader.ReadUInt32(); - Npp = reader.ReadByte(); - Asp = reader.ReadByte(); - Totd = reader.ReadUInt32(); - Npd = reader.ReadByte(); - Asd = reader.ReadByte(); - - PilotSymDef = new TzxSymDef[Asp]; - for (var i = 0; i < Asp; i++) - { - var symDef = new TzxSymDef(Npp); - symDef.ReadFrom(reader); - PilotSymDef[i] = symDef; - } - - PilotStream = new TzxPrle[Totp]; - for (var i = 0; i < Totp; i++) - { - PilotStream[i].Symbol = reader.ReadByte(); - PilotStream[i].Repetitions = reader.ReadUInt16(); - } - - DataSymDef = new TzxSymDef[Asd]; - for (var i = 0; i < Asd; i++) - { - var symDef = new TzxSymDef(Npd); - symDef.ReadFrom(reader); - DataSymDef[i] = symDef; - } - - DataStream = new TzxPrle[Totd]; - for (var i = 0; i < Totd; i++) - { - DataStream[i].Symbol = reader.ReadByte(); - DataStream[i].Repetitions = reader.ReadUInt16(); - } - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(BlockLength); - writer.Write(PauseAfter); - writer.Write(Totp); - writer.Write(Npp); - writer.Write(Asp); - writer.Write(Totd); - writer.Write(Npd); - writer.Write(Asd); - for (var i = 0; i < Asp; i++) - { - PilotSymDef[i].WriteTo(writer); - } - for (var i = 0; i < Totp; i++) - { - writer.Write(PilotStream[i].Symbol); - writer.Write(PilotStream[i].Repetitions); - } - - for (var i = 0; i < Asd; i++) - { - DataSymDef[i].WriteTo(writer); - } - - for (var i = 0; i < Totd; i++) - { - writer.Write(DataStream[i].Symbol); - writer.Write(DataStream[i].Repetitions); - } - } - } - - /// - /// This block is generated when you merge two ZX Tape files together. - /// - /// - /// It is here so that you can easily copy the files together and use - /// them. Of course, this means that resulting file would be 10 bytes - /// longer than if this block was not used. All you have to do if - /// you encounter this block ID is to skip next 9 bytes. - /// - public class TzxGlueDataBlock : TzxDataBlockBase - { - /// - /// Value: { "XTape!", 0x1A, MajorVersion, MinorVersion } - /// - /// - /// Just skip these 9 bytes and you will end up on the next ID. - /// - public byte[] Glue { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x5A; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Glue = reader.ReadBytes(9); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Glue); - } - } - - /// - /// This indicates the end of a group. This block has no body. - /// - public class TzxGroupEndDataBlock : TzxBodylessDataBlockBase - { - /// - /// The ID of the block - /// - public override int BlockId => 0x22; - } - - /// - /// This block marks the start of a group of blocks which are - /// to be treated as one single (composite) block. - /// - public class TzxGroupStartDataBlock : TzxDataBlockBase - { - /// - /// Number of group name - /// - public byte Length { get; set; } - - /// - /// Group name bytes - /// - public byte[] Chars { get; set; } - - /// - /// Gets the group name - /// - public string GroupName => ToAsciiString(Chars); - - /// - /// The ID of the block - /// - public override int BlockId => 0x21; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Length = reader.ReadByte(); - Chars = reader.ReadBytes(Length); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Length); - writer.Write(Chars); - } - } - - /// - /// - /// - public class TzxHardwareInfoDataBlock : TzxDataBlockBase - { - /// - /// Number of machines and hardware types for which info is supplied - /// - public byte HwCount { get; set; } - - /// - /// List of machines and hardware - /// - public TzxHwInfo[] HwInfo { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x33; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - HwCount = reader.ReadByte(); - HwInfo = new TzxHwInfo[HwCount]; - for (var i = 0; i < HwCount; i++) - { - var hw = new TzxHwInfo(); - hw.ReadFrom(reader); - HwInfo[i] = hw; - } - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(HwCount); - foreach (var hw in HwInfo) - { - hw.WriteTo(writer); - } - } - } - - /// - /// This block will enable you to jump from one block to another within the file. - /// - /// - /// Jump 0 = 'Loop Forever' - this should never happen - /// Jump 1 = 'Go to the next block' - it is like NOP in assembler - /// Jump 2 = 'Skip one block' - /// Jump -1 = 'Go to the previous block' - /// - public class TzxJumpDataBlock : TzxDataBlockBase - { - /// - /// Relative jump value - /// - /// - /// - public short Jump { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x23; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Jump = reader.ReadInt16(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Jump); - } - } - - /// - /// It means that the utility should jump back to the start - /// of the loop if it hasn't been run for the specified number - /// of times. - /// - public class TzxLoopEndDataBlock : TzxBodylessDataBlockBase - { - /// - /// The ID of the block - /// - public override int BlockId => 0x25; - } - - /// - /// If you have a sequence of identical blocks, or of identical - /// groups of blocks, you can use this block to tell how many - /// times they should be repeated. - /// - public class TzxLoopStartDataBlock : TzxDataBlockBase - { - /// - /// Number of repetitions (greater than 1) - /// - public ushort Loops { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x24; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Loops = reader.ReadUInt16(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Loops); - } - } - - /// - /// This will enable the emulators to display a message for a given time. - /// - /// - /// This should not stop the tape and it should not make silence. If the - /// time is 0 then the emulator should wait for the user to press a key. - /// - public class TzxMessageDataBlock : TzxDataBlockBase - { - /// - /// Time (in seconds) for which the message should be displayed - /// - public byte Time { get; set; } - - /// - /// Length of the description - /// - public byte MessageLength { get; set; } - - /// - /// The description bytes - /// - public byte[] Message; - - /// - /// The string form of description - /// - public string MessageText => ToAsciiString(Message); - - /// - /// The ID of the block - /// - public override int BlockId => 0x31; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Time = reader.ReadByte(); - MessageLength = reader.ReadByte(); - Message = reader.ReadBytes(MessageLength); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Time); - writer.Write(MessageLength); - writer.Write(Message); - } - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxPulseSequenceDataBlock : TzxDataBlockBase - { - /// - /// Pause after this block - /// - public byte PulseCount { get; set; } - - /// - /// Lenght of block data - /// - public ushort[] PulseLengths { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x13; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - PulseCount = reader.ReadByte(); - PulseLengths = ReadWords(reader, PulseCount); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(PulseCount); - WriteWords(writer, PulseLengths); - } - - /// - /// Override this method to check the content of the block - /// - public override bool IsValid => PulseCount == PulseLengths.Length; - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxPureDataBlock : Tzx3ByteDataBlockBase - { - /// - /// Length of the zero bit - /// - public ushort ZeroBitPulseLength { get; set; } - - /// - /// Length of the one bit - /// - public ushort OneBitPulseLength { get; set; } - - /// - /// Pause after this block - /// - public ushort PauseAfter { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x14; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - ZeroBitPulseLength = reader.ReadUInt16(); - OneBitPulseLength = reader.ReadUInt16(); - LastByteUsedBits = reader.ReadByte(); - PauseAfter = reader.ReadUInt16(); - DataLength = reader.ReadBytes(3); - Data = reader.ReadBytes(GetLength()); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(ZeroBitPulseLength); - writer.Write(OneBitPulseLength); - writer.Write(LastByteUsedBits); - writer.Write(PauseAfter); - writer.Write(DataLength); - writer.Write(Data); - } - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxPureToneDataBlock : TzxDataBlockBase - { - /// - /// Pause after this block - /// - public ushort PulseLength { get; private set; } - - /// - /// Lenght of block data - /// - public ushort PulseCount { get; private set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x12; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - PulseLength = reader.ReadUInt16(); - PulseCount = reader.ReadUInt16(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(PulseLength); - writer.Write(PulseCount); - } - } - - /// - /// This block indicates the end of the Called Sequence. - /// The next block played will be the block after the last - /// CALL block - /// - public class TzxReturnFromSequenceDataBlock : TzxBodylessDataBlockBase - { - /// - /// The ID of the block - /// - public override int BlockId => 0x27; - } - - /// - /// Pause (silence) or 'Stop the Tape' block - /// - public class TzxSelectDataBlock : TzxDataBlockBase - { - /// - /// Length of the whole block (without these two bytes) - /// - public ushort Length { get; set; } - - /// - /// Number of selections - /// - public byte SelectionCount { get; set; } - - /// - /// List of selections - /// - public TzxSelect[] Selections { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x28; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Length = reader.ReadUInt16(); - SelectionCount = reader.ReadByte(); - Selections = new TzxSelect[SelectionCount]; - foreach (var selection in Selections) - { - selection.ReadFrom(reader); - } - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Length); - writer.Write(SelectionCount); - foreach (var selection in Selections) - { - selection.WriteTo(writer); - } - } - } - - /// - /// This block sets the current signal level to the specified value (high or low). - /// - public class TzxSetSignalLevelDataBlock : TzxDataBlockBase - { - /// - /// Length of the block without these four bytes - /// - public uint Lenght { get; } = 1; - - /// - /// Signal level (0=low, 1=high) - /// - public byte SignalLevel { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x2B; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - reader.ReadUInt32(); - SignalLevel = reader.ReadByte(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Lenght); - writer.Write(SignalLevel); - } - } - - /// - /// Pause (silence) or 'Stop the Tape' block - /// - public class TzxSilenceDataBlock : TzxDataBlockBase - { - /// - /// Duration of silence - /// - /// - /// This will make a silence (low amplitude level (0)) for a given time - /// in milliseconds. If the value is 0 then the emulator or utility should - /// (in effect) STOP THE TAPE, i.e. should not continue loading until - /// the user or emulator requests it. - /// - public ushort Duration { get; set; } - - /// - /// The ID of the block - /// - public override int BlockId => 0x20; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Duration = reader.ReadUInt16(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Duration); - } - } - - /// - /// This block was created to support the Commodore 64 standard - /// ROM and similar tape blocks. - /// - public class TzxSnapshotBlock : TzxDeprecatedDataBlockBase - { - /// - /// The ID of the block - /// - public override int BlockId => 0x40; - - /// - /// Reads through the block infromation, and does not store it - /// - /// Stream to read the block from - public override void ReadThrough(BinaryReader reader) - { - var length = reader.ReadInt32(); - length = length & 0x00FFFFFF; - reader.ReadBytes(length); - } - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxStandardSpeedDataBlock : TzxDataBlockBase, ISupportsTapeBlockPlayback, ITapeData - { - private TapeDataBlockPlayer _player; - - /// - /// Pause after this block (default: 1000ms) - /// - public ushort PauseAfter { get; set; } = 1000; - - /// - /// Lenght of block data - /// - private ushort dataLength; - public ushort DataLength - { - get { return dataLength; } - set { dataLength = value; } - } - - /// - /// Block Data - /// - private byte[] data; - public byte[] Data - { - get { return data; } - set { data = value; } - } - - - /// - /// The ID of the block - /// - public override int BlockId => 0x10; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - PauseAfter = reader.ReadUInt16(); - DataLength = reader.ReadUInt16(); - Data = reader.ReadBytes(DataLength); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write((byte)BlockId); - writer.Write(PauseAfter); - writer.Write(DataLength); - writer.Write(Data, 0, DataLength); - } - - /// - /// The index of the currently playing byte - /// - /// This proprty is made public for test purposes - public int ByteIndex => _player.ByteIndex; - - /// - /// The mask of the currently playing bit in the current byte - /// - public byte BitMask => _player.BitMask; - - /// - /// The current playing phase - /// - public PlayPhase PlayPhase => _player.PlayPhase; - - /// - /// The tact count of the CPU when playing starts - /// - public long StartCycle=> _player.StartCycle; - - /// - /// Last tact queried - /// - public long LastTact => _player.LastCycle; - - /// - /// Initializes the player - /// - public void InitPlay(long startCycle) - { - _player = new TapeDataBlockPlayer(Data, PauseAfter); - _player.InitPlay(startCycle); - } - - /// - /// Gets the EAR bit value for the specified tact - /// - /// Tacts to retrieve the EAR bit - /// - /// The EAR bit value to play back - /// - public bool GetEarBit(long currentCycle) => _player.GetEarBit(currentCycle); - - public void SyncState(Serializer ser) - { - ser.BeginSection("TzxStandardSpeedDataBlock"); - - ser.Sync("dataLength", ref dataLength); - ser.Sync("data", ref data, false); - - ser.EndSection(); - } - } - - /// - /// When this block is encountered, the tape will stop ONLY if - /// the machine is an 48K Spectrum. - /// - /// - /// This block is to be used for multiloading games that load one - /// level at a time in 48K mode, but load the entire tape at once - /// if in 128K mode. This block has no body of its own, but follows - /// the extension rule. - /// - public class TzxStopTheTape48DataBlock : TzxDataBlockBase - { - /// - /// Length of the block without these four bytes (0) - /// - public uint Lenght { get; } = 0; - - /// - /// The ID of the block - /// - public override int BlockId => 0x2A; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - reader.ReadUInt32(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Lenght); - } - } - - /// - /// This is meant to identify parts of the tape, so you know where level 1 starts, - /// where to rewind to when the game ends, etc. - /// - /// - /// This description is not guaranteed to be shown while the tape is playing, - /// but can be read while browsing the tape or changing the tape pointer. - /// - public class TzxTextDescriptionDataBlock : TzxDataBlockBase - { - /// - /// Length of the description - /// - public byte DescriptionLength { get; set; } - - /// - /// The description bytes - /// - public byte[] Description; - - /// - /// The string form of description - /// - public string DescriptionText => ToAsciiString(Description); - - /// - /// The ID of the block - /// - public override int BlockId => 0x30; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - DescriptionLength = reader.ReadByte(); - Description = reader.ReadBytes(DescriptionLength); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(DescriptionLength); - writer.Write(Description); - } - } - - /// - /// Represents the standard speed data block in a TZX file - /// - public class TzxTurboSpeedDataBlock : Tzx3ByteDataBlockBase - { - /// - /// Length of pilot pulse - /// - public ushort PilotPulseLength { get; set; } - - /// - /// Length of the first sync pulse - /// - public ushort Sync1PulseLength { get; set; } - - /// - /// Length of the second sync pulse - /// - public ushort Sync2PulseLength { get; set; } - - /// - /// Length of the zero bit - /// - public ushort ZeroBitPulseLength { get; set; } - - /// - /// Length of the one bit - /// - public ushort OneBitPulseLength { get; set; } - - /// - /// Length of the pilot tone - /// - public ushort PilotToneLength { get; set; } - - /// - /// Pause after this block - /// - public ushort PauseAfter { get; set; } - - public TzxTurboSpeedDataBlock() - { - PilotPulseLength = 2168; - Sync1PulseLength = 667; - Sync2PulseLength = 735; - ZeroBitPulseLength = 855; - OneBitPulseLength = 1710; - PilotToneLength = 8063; - LastByteUsedBits = 8; - } - - /// - /// The ID of the block - /// - public override int BlockId => 0x11; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - PilotPulseLength = reader.ReadUInt16(); - Sync1PulseLength = reader.ReadUInt16(); - Sync2PulseLength = reader.ReadUInt16(); - ZeroBitPulseLength = reader.ReadUInt16(); - OneBitPulseLength = reader.ReadUInt16(); - PilotToneLength = reader.ReadUInt16(); - LastByteUsedBits = reader.ReadByte(); - PauseAfter = reader.ReadUInt16(); - DataLength = reader.ReadBytes(3); - Data = reader.ReadBytes(GetLength()); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(PilotPulseLength); - writer.Write(Sync1PulseLength); - writer.Write(Sync2PulseLength); - writer.Write(ZeroBitPulseLength); - writer.Write(OneBitPulseLength); - writer.Write(PilotToneLength); - writer.Write(LastByteUsedBits); - writer.Write(PauseAfter); - writer.Write(DataLength); - writer.Write(Data); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs deleted file mode 100644 index 0a83aa3bfc..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Info.cs +++ /dev/null @@ -1,250 +0,0 @@ - -using System.IO; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This blocks contains information about the hardware that the programs on this tape use. - /// - public class TzxHwInfo : ITapeDataSerialization - { - /// - /// Hardware type - /// - public byte HwType { get; set; } - - /// - /// Hardwer Id - /// - public byte HwId { get; set; } - - /// - /// Information about the tape - /// - /// - /// 00 - The tape RUNS on this machine or with this hardware, - /// but may or may not use the hardware or special features of the machine. - /// 01 - The tape USES the hardware or special features of the machine, - /// such as extra memory or a sound chip. - /// 02 - The tape RUNS but it DOESN'T use the hardware - /// or special features of the machine. - /// 03 - The tape DOESN'T RUN on this machine or with this hardware. - /// - public byte TapeInfo; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public void ReadFrom(BinaryReader reader) - { - HwType = reader.ReadByte(); - HwId = reader.ReadByte(); - TapeInfo = reader.ReadByte(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public void WriteTo(BinaryWriter writer) - { - writer.Write(HwType); - writer.Write(HwId); - writer.Write(TapeInfo); - } - } - - /// - /// Symbol repetitions - /// - public struct TzxPrle - { - /// - /// Symbol represented - /// - public byte Symbol; - - /// - /// Number of repetitions - /// - public ushort Repetitions; - } - - /// - /// This block represents an extremely wide range of data encoding techniques. - /// - /// - /// The basic idea is that each loading component (pilot tone, sync pulses, data) - /// is associated to a specific sequence of pulses, where each sequence (wave) can - /// contain a different number of pulses from the others. In this way we can have - /// a situation where bit 0 is represented with 4 pulses and bit 1 with 8 pulses. - /// - public class TzxSymDef : ITapeDataSerialization - { - /// - /// Bit 0 - Bit 1: Starting symbol polarity - /// - /// - /// 00: opposite to the current level (make an edge, as usual) - default - /// 01: same as the current level(no edge - prolongs the previous pulse) - /// 10: force low level - /// 11: force high level - /// - public byte SymbolFlags; - - /// - /// The array of pulse lengths - /// - public ushort[] PulseLengths; - - public TzxSymDef(byte maxPulses) - { - PulseLengths = new ushort[maxPulses]; - } - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public void ReadFrom(BinaryReader reader) - { - SymbolFlags = reader.ReadByte(); - PulseLengths = TzxDataBlockBase.ReadWords(reader, PulseLengths.Length); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public void WriteTo(BinaryWriter writer) - { - writer.Write(SymbolFlags); - TzxDataBlockBase.WriteWords(writer, PulseLengths); - } - } - - /// - /// This is meant to identify parts of the tape, so you know where level 1 starts, - /// where to rewind to when the game ends, etc. - /// - /// - /// This description is not guaranteed to be shown while the tape is playing, - /// but can be read while browsing the tape or changing the tape pointer. - /// - public class TzxText : ITapeDataSerialization - { - /// - /// Text identification byte. - /// - /// - /// 00 - Full title - /// 01 - Software house/publisher - /// 02 - Author(s) - /// 03 - Year of publication - /// 04 - Language - /// 05 - Game/utility type - /// 06 - Price - /// 07 - Protection scheme/loader - /// 08 - Origin - /// FF - Comment(s) - /// - public byte Type { get; set; } - - /// - /// Length of the description - /// - public byte Length { get; set; } - - /// - /// The description bytes - /// - public byte[] TextBytes; - - /// - /// The string form of description - /// - public string Text => TzxDataBlockBase.ToAsciiString(TextBytes); - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public void ReadFrom(BinaryReader reader) - { - Type = reader.ReadByte(); - Length = reader.ReadByte(); - TextBytes = reader.ReadBytes(Length); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public void WriteTo(BinaryWriter writer) - { - writer.Write(Type); - writer.Write(Length); - writer.Write(TextBytes); - } - } - - /// - /// This block represents select structure - /// - public class TzxSelect : ITapeDataSerialization - { - /// - /// Bit 0 - Bit 1: Starting symbol polarity - /// - /// - /// 00: opposite to the current level (make an edge, as usual) - default - /// 01: same as the current level(no edge - prolongs the previous pulse) - /// 10: force low level - /// 11: force high level - /// - public ushort BlockOffset; - - /// - /// Length of the description - /// - public byte DescriptionLength { get; set; } - - /// - /// The description bytes - /// - public byte[] Description; - - /// - /// The string form of description - /// - public string DescriptionText => TzxDataBlockBase.ToAsciiString(Description); - - public TzxSelect(byte length) - { - DescriptionLength = length; - } - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public void ReadFrom(BinaryReader reader) - { - BlockOffset = reader.ReadUInt16(); - DescriptionLength = reader.ReadByte(); - Description = reader.ReadBytes(DescriptionLength); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public void WriteTo(BinaryWriter writer) - { - writer.Write(BlockOffset); - writer.Write(DescriptionLength); - writer.Write(Description); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs deleted file mode 100644 index 8742a35679..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/Types.cs +++ /dev/null @@ -1,282 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Identified AD or DA converter types - /// - public enum TzxAdOrDaConverterType : byte - { - HarleySystemsAdc8P2 = 0x00, - BlackboardElectronics = 0x01 - } - - /// - /// Identified computer types - /// - public enum TzxComputerType : byte - { - ZxSpectrum16 = 0x00, - ZxSpectrum48OrPlus = 0x01, - ZxSpectrum48Issue1 = 0x02, - ZxSpectrum128 = 0x03, - ZxSpectrum128P2 = 0x04, - ZxSpectrum128P2AOr3 = 0x05, - Tc2048 = 0x06, - Ts2068 = 0x07, - Pentagon128 = 0x08, - SamCoupe = 0x09, - DidaktikM = 0x0A, - DidaktikGama = 0x0B, - Zx80 = 0x0C, - Zx81 = 0x0D, - ZxSpectrum128Spanish = 0x0E, - ZxSpectrumArabic = 0x0F, - Tk90X = 0x10, - Tk95 = 0x11, - Byte = 0x12, - Elwro800D3 = 0x13, - ZsScorpion256 = 0x14, - AmstradCpc464 = 0x15, - AmstradCpc664 = 0x16, - AmstradCpc6128 = 0x17, - AmstradCpc464P = 0x18, - AmstradCpc6128P = 0x19, - JupiterAce = 0x1A, - Enterprise = 0x1B, - Commodore64 = 0x1C, - Commodore128 = 0x1D, - InvesSpectrumP = 0x1E, - Profi = 0x1F, - GrandRomMax = 0x20, - Kay1024 = 0x21, - IceFelixHc91 = 0x22, - IceFelixHc2000 = 0x23, - AmaterskeRadioMistrum = 0x24, - Quorum128 = 0x25, - MicroArtAtm = 0x26, - MicroArtAtmTurbo2 = 0x27, - Chrome = 0x28, - ZxBadaloc = 0x29, - Ts1500 = 0x2A, - Lambda = 0x2B, - Tk65 = 0x2C, - Zx97 = 0x2D - } - - /// - /// Identified digitizer types - /// - public enum TzxDigitizerType : byte - { - RdDigitalTracer = 0x00, - DkTronicsLightPen = 0x01, - MicrographPad = 0x02, - RomnticRobotVideoface = 0x03 - } - - /// - /// Identified EPROM programmer types - /// - public enum TzxEpromProgrammerType : byte - { - OrmeElectronics = 0x00 - } - - /// - /// Identified external storage types - /// - public enum TzxExternalStorageType : byte - { - ZxMicroDrive = 0x00, - OpusDiscovery = 0x01, - MgtDisciple = 0x02, - MgtPlusD = 0x03, - RobotronicsWafaDrive = 0x04, - TrDosBetaDisk = 0x05, - ByteDrive = 0x06, - Watsford = 0x07, - Fiz = 0x08, - Radofin = 0x09, - DidaktikDiskDrive = 0x0A, - BsDos = 0x0B, - ZxSpectrumP3DiskDrive = 0x0C, - JloDiskInterface = 0x0D, - TimexFdd3000 = 0x0E, - ZebraDiskDrive = 0x0F, - RamexMillenia = 0x10, - Larken = 0x11, - KempstonDiskInterface = 0x12, - Sandy = 0x13, - ZxSpectrumP3EHardDisk = 0x14, - ZxAtaSp = 0x15, - DivIde = 0x16, - ZxCf = 0x17 - } - - /// - /// Identified graphics types - /// - public enum TzxGraphicsType : byte - { - WrxHiRes = 0x00, - G007 = 0x01, - Memotech = 0x02, - LambdaColour = 0x03 - } - - /// - /// Represents the hardware types that can be defined - /// - public enum TzxHwType : byte - { - Computer = 0x00, - ExternalStorage = 0x01, - RomOrRamTypeAddOn = 0x02, - SoundDevice = 0x03, - JoyStick = 0x04, - Mouse = 0x05, - OtherController = 0x06, - SerialPort = 0x07, - ParallelPort = 0x08, - Printer = 0x09, - Modem = 0x0A, - Digitizer = 0x0B, - NetworkAdapter = 0x0C, - Keyboard = 0x0D, - AdOrDaConverter = 0x0E, - EpromProgrammer = 0x0F, - Graphics = 0x10 - } - - /// - /// Identified joystick types - /// - public enum TzxJoystickType - { - Kempston = 0x00, - ProtekCursor = 0x01, - Sinclair2Left = 0x02, - Sinclair1Right = 0x03, - Fuller = 0x04 - } - - /// - /// Identified keyboard and keypad types - /// - public enum TzxKeyboardType : byte - { - KeypadForZxSpectrum128K = 0x00 - } - - /// - /// Identified modem types - /// - public enum TzxModemTypes : byte - { - PrismVtx5000 = 0x00, - Westridge2050 = 0x01 - } - - /// - /// Identified mouse types - /// - public enum TzxMouseType : byte - { - AmxMouse = 0x00, - KempstonMouse = 0x01 - } - - /// - /// Identified network adapter types - /// - public enum TzxNetworkAdapterType : byte - { - ZxInterface1 = 0x00 - } - - /// - /// Identified other controller types - /// - public enum TzxOtherControllerType : byte - { - Trisckstick = 0x00, - ZxLightGun = 0x01, - ZebraGraphicTablet = 0x02, - DefnederLightGun = 0x03 - } - - /// - /// Identified parallel port types - /// - public enum TzxParallelPortType : byte - { - KempstonS = 0x00, - KempstonE = 0x01, - ZxSpectrum3P = 0x02, - Tasman = 0x03, - DkTronics = 0x04, - Hilderbay = 0x05, - InesPrinterface = 0x06, - ZxLprintInterface3 = 0x07, - MultiPrint = 0x08, - OpusDiscovery = 0x09, - Standard8255 = 0x0A - } - - /// - /// Identified printer types - /// - public enum TzxPrinterType : byte - { - ZxPrinter = 0x00, - GenericPrinter = 0x01, - EpsonCompatible = 0x02 - } - - /// - /// Identifier ROM or RAM add-on types - /// - public enum TzxRomRamAddOnType : byte - { - SamRam = 0x00, - MultifaceOne = 0x01, - Multiface128K = 0x02, - MultifaceP3 = 0x03, - MultiPrint = 0x04, - Mb02 = 0x05, - SoftRom = 0x06, - Ram1K = 0x07, - Ram16K = 0x08, - Ram48K = 0x09, - Mem8To16KUsed = 0x0A - } - - /// - /// Identified serial port types - /// - public enum TzxSerialPortType : byte - { - ZxInterface1 = 0x00, - ZxSpectrum128 = 0x01 - } - - /// - /// Identified sound device types - /// - public enum TzxSoundDeviceType : byte - { - ClassicAy = 0x00, - FullerBox = 0x01, - CurrahMicroSpeech = 0x02, - SpectDrum = 0x03, - MelodikAyAcbStereo = 0x04, - AyAbcStereo = 0x05, - RamMusinMachine = 0x06, - Covox = 0x07, - GeneralSound = 0x08, - IntecEdiB8001 = 0x09, - ZonXAy = 0x0A, - QuickSilvaAy = 0x0B, - JupiterAce = 0x0C - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs deleted file mode 100644 index 8ebe4921e5..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxException.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class represents a TZX-related exception - /// - public class TzxException : Exception - { - /// - /// Initializes the exception with the specified message - /// - /// Exception message - public TzxException(string message) : base(message) - { - } - - /// - /// Initializes the exception with the specified message - /// and inner exception - /// - /// Exception message - /// Inner exception - public TzxException(string message, Exception innerException) : base(message, innerException) - { - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs deleted file mode 100644 index e6901b4d7b..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxHeader.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents the header of the TZX file - /// - public class TzxHeader : TzxDataBlockBase - { - public static IReadOnlyList TzxSignature = - new ReadOnlyCollection(new byte[] { 0x5A, 0x58, 0x54, 0x61, 0x70, 0x65, 0x21 }); - public byte[] Signature { get; private set; } - public byte Eot { get; private set; } - public byte MajorVersion { get; private set; } - public byte MinorVersion { get; private set; } - - public TzxHeader(byte majorVersion = 1, byte minorVersion = 20) - { - Signature = TzxSignature.ToArray(); - Eot = 0x1A; - MajorVersion = majorVersion; - MinorVersion = minorVersion; - } - - /// - /// The ID of the block - /// - public override int BlockId => 0x00; - - /// - /// Reads the content of the block from the specified binary stream. - /// - /// Stream to read the block from - public override void ReadFrom(BinaryReader reader) - { - Signature = reader.ReadBytes(7); - Eot = reader.ReadByte(); - MajorVersion = reader.ReadByte(); - MinorVersion = reader.ReadByte(); - } - - /// - /// Writes the content of the block to the specified binary stream. - /// - /// Stream to write the block to - public override void WriteTo(BinaryWriter writer) - { - writer.Write(Signature); - writer.Write(Eot); - writer.Write(MajorVersion); - writer.Write(MinorVersion); - } - - #region Overrides of TzxDataBlockBase - - /// - /// Override this method to check the content of the block - /// - public override bool IsValid => Signature.SequenceEqual(TzxSignature) - && Eot == 0x1A - && MajorVersion == 1; - - #endregion - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs deleted file mode 100644 index a9ac846467..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxPlayer.cs +++ /dev/null @@ -1,94 +0,0 @@ -using BizHawk.Common; -using System.IO; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class is responsible to "play" a TZX file. - /// - public class TzxPlayer : TzxReader, ISupportsTapeBlockPlayback - { - private TapeBlockSetPlayer _player; - - /// - /// Signs that the player completed playing back the file - /// - public bool Eof => _player.Eof; - - /// - /// Initializes the player from the specified reader - /// - /// BinaryReader instance to get TZX file data from - public TzxPlayer(BinaryReader reader) : base(reader) - { - } - - /// - /// Reads in the content of the TZX file so that it can be played - /// - /// True, if read was successful; otherwise, false - public override bool ReadContent() - { - var success = base.ReadContent(); - var blocks = DataBlocks.Where(b => b is ISupportsTapeBlockPlayback) - .Cast() - .ToList(); - _player = new TapeBlockSetPlayer(blocks); - return success; - } - - /// - /// Gets the currently playing block's index - /// - public int CurrentBlockIndex => _player.CurrentBlockIndex; - - /// - /// The current playable block - /// - public ISupportsTapeBlockPlayback CurrentBlock => _player.CurrentBlock; - - /// - /// The current playing phase - /// - public PlayPhase PlayPhase => _player.PlayPhase; - - /// - /// The tact count of the CPU when playing starts - /// - public long StartCycle => _player.StartCycle; - - /// - /// Initializes the player - /// - public void InitPlay(long startTact) - { - _player.InitPlay(startTact); - } - - /// - /// Gets the EAR bit value for the specified tact - /// - /// Tacts to retrieve the EAR bit - /// - /// A tuple of the EAR bit and a flag that indicates it is time to move to the next block - /// - public bool GetEarBit(long currentTact) => _player.GetEarBit(currentTact); - - /// - /// Moves the current block index to the next playable block - /// - /// Tacts time to start the next block - public void NextBlock(long currentTact) => _player.NextBlock(currentTact); - - - public void SyncState(Serializer ser) - { - ser.BeginSection("TzxPlayer"); - - _player.SyncState(ser); - - ser.EndSection(); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs deleted file mode 100644 index ad7ea786ab..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxReader.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// This class reads a TZX file - /// - public class TzxReader - { - private readonly BinaryReader _reader; - - public static Dictionary DataBlockTypes = new Dictionary - { - {0x10, typeof(TzxStandardSpeedDataBlock)}, - {0x11, typeof(TzxTurboSpeedDataBlock)}, - {0x12, typeof(TzxPureToneDataBlock)}, - {0x13, typeof(TzxPulseSequenceDataBlock)}, - {0x14, typeof(TzxPureDataBlock)}, - {0x15, typeof(TzxDirectRecordingDataBlock)}, - {0x16, typeof(TzxC64RomTypeDataBlock)}, - {0x17, typeof(TzxC64TurboTapeDataBlock)}, - {0x18, typeof(TzxCswRecordingDataBlock)}, - {0x19, typeof(TzxGeneralizedDataBlock)}, - {0x20, typeof(TzxSilenceDataBlock)}, - {0x21, typeof(TzxGroupStartDataBlock)}, - {0x22, typeof(TzxGroupEndDataBlock)}, - {0x23, typeof(TzxJumpDataBlock)}, - {0x24, typeof(TzxLoopStartDataBlock)}, - {0x25, typeof(TzxLoopEndDataBlock)}, - {0x26, typeof(TzxCallSequenceDataBlock)}, - {0x27, typeof(TzxReturnFromSequenceDataBlock)}, - {0x28, typeof(TzxSelectDataBlock)}, - {0x2A, typeof(TzxStopTheTape48DataBlock)}, - {0x2B, typeof(TzxSetSignalLevelDataBlock)}, - {0x30, typeof(TzxTextDescriptionDataBlock)}, - {0x31, typeof(TzxMessageDataBlock)}, - {0x32, typeof(TzxArchiveInfoDataBlock)}, - {0x33, typeof(TzxHardwareInfoDataBlock)}, - {0x34, typeof(TzxEmulationInfoDataBlock)}, - {0x35, typeof(TzxCustomInfoDataBlock)}, - {0x40, typeof(TzxSnapshotBlock)}, - {0x5A, typeof(TzxGlueDataBlock)}, - }; - - /// - /// Data blocks of this TZX file - /// - public IList DataBlocks { get; } - - /// - /// Major version number of the file - /// - public byte MajorVersion { get; private set; } - - /// - /// Minor version number of the file - /// - public byte MinorVersion { get; private set; } - - /// - /// Initializes the player from the specified reader - /// - /// - public TzxReader(BinaryReader reader) - { - _reader = reader; - DataBlocks = new List(); - } - - /// - /// Reads in the content of the TZX file so that it can be played - /// - /// True, if read was successful; otherwise, false - public virtual bool ReadContent() - { - var header = new TzxHeader(); - try - { - header.ReadFrom(_reader); - if (!header.IsValid) - { - throw new TzxException("Invalid TZX header"); - } - MajorVersion = header.MajorVersion; - MinorVersion = header.MinorVersion; - - while (_reader.BaseStream.Position != _reader.BaseStream.Length) - { - var blockType = _reader.ReadByte(); - Type type; - if (!DataBlockTypes.TryGetValue(blockType, out type)) - { - throw new TzxException($"Unkonwn TZX block type: {blockType}"); - } - - try - { - var block = Activator.CreateInstance(type) as TzxDataBlockBase; - if (block.GetType() == typeof(TzxDeprecatedDataBlockBase)) - { - ((TzxDeprecatedDataBlockBase)block as TzxDeprecatedDataBlockBase).ReadThrough(_reader); - } - else - { - block?.ReadFrom(_reader); - } - DataBlocks.Add(block); - } - catch (Exception ex) - { - throw new TzxException($"Cannot read TZX data block {type}.", ex); - } - } - return true; - } - catch - { - // --- This exception is intentionally ignored - return false; - } - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index afcf6b122f..f4f78dd4fc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -1,6 +1,6 @@ ## ZXHawk -At this moment this is still *very* experimental and needs a lot more work. +At the moment this is very experimental and is still actively being worked on. ### Implemented and sorta working * IEmulator @@ -13,21 +13,23 @@ At this moment this is still *very* experimental and needs a lot more work. * Keyboard input (implementing IInputPollable) * Kempston joystick (mapped to J1 currently) * Tape device that will load spectrum games in realtime (*.tzx and *.tap) +* Most tape protection/loading schemes that I've tested are currently working (see caveat below) * IStatable * ISettable core settings -* IMemoryDomains (I think) ### Work in progress * Exact emulator timings * Floating memory bus emulation * Tape auto-loading routines (currently you have to manually start and stop the virtual tape device) +* TASStudio (need to verify that this works as it should) ### Not working -* IDebuggable +* IDebuggable (probably IMemoryDomains is setup incorrectly) * ZX Spectrum Plus3 emulation * Default keyboard keymappings (you have to configure yourself in the core controller settings) ### Known bugs -* Audible 'popping' from the emulated buzzer after a load state operation +* Audible 'popping' from the emulated buzzer after a load state operation (maybe this is a normal thing) +* Speedlock tape protection scheme doesn't appear to load correctly -Asnivor From 90c1e293bfc630c27dcd46ea8ea3951047ebd32c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 5 Mar 2018 11:17:22 +0000 Subject: [PATCH 049/339] Implemented multi bundler functionlity and multiple tape controls --- BizHawk.Client.Common/RomLoader.cs | 5 +- .../MultiDiskBundler.Designer.cs | 3 +- .../VirtualPads/schema/ZXSpectrumSchema.cs | 19 +- .../BizHawk.Emulation.Cores.csproj | 1 + .../Hardware/Datacorder/DatacorderDevice.cs | 5 +- .../Machine/SpectrumBase.Input.cs | 10 + .../Machine/SpectrumBase.Media.cs | 191 ++++++++++++++++++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 6 + .../Machine/ZXSpectrum128K/ZX128.cs | 7 +- .../Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs | 4 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 7 +- .../Machine/ZXSpectrum16K/ZX16.cs | 4 +- .../Machine/ZXSpectrum48K/ZX48.cs | 9 +- .../ZXSpectrum.Controllers.cs | 2 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 30 +-- 15 files changed, 266 insertions(+), 37 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index cd1ce95f22..3bc0b7059a 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -661,7 +661,8 @@ namespace BizHawk.Client.Common case "ZXSpectrum": nextEmulator = new ZXSpectrum( nextComm, - xmlGame.Assets.Select(a => a.Value).First(), + xmlGame.Assets.Select(a => a.Value), //.First(), + GameInfo.NullInstance, (ZXSpectrum.ZXSpectrumSettings)GetCoreSettings(), (ZXSpectrum.ZXSpectrumSyncSettings)GetCoreSyncSettings()); break; @@ -999,7 +1000,7 @@ namespace BizHawk.Client.Common nextEmulator = c64; break; case "ZXSpectrum": - var zx = new ZXSpectrum(nextComm, rom.FileData, GetCoreSettings(), GetCoreSyncSettings()); + var zx = new ZXSpectrum(nextComm, Enumerable.Repeat(rom.RomData, 1), rom.GameInfo, GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = zx; break; case "GBA": diff --git a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs index 3c1e2f2e9e..5616d71e02 100644 --- a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs +++ b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs @@ -143,7 +143,8 @@ "GB", "PCFX", "PSX", - "SAT"}); + "SAT", + "ZXSpectrum"}); this.SystemDropDown.Location = new System.Drawing.Point(425, 75); this.SystemDropDown.Name = "SystemDropDown"; this.SystemDropDown.Size = new System.Drawing.Size(69, 21); diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs index fbcb9b9e57..1995d75757 100644 --- a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs +++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs @@ -233,7 +233,24 @@ namespace BizHawk.Client.EmuHawk Icon = Properties.Resources.BackMore, Location = new Point(83, 22), Type = PadSchema.PadInputType.Boolean - } + }, + new PadSchema.ButtonSchema + { + Name = "Insert Next Tape", + DisplayName = "NEXT TAPE", + //Icon = Properties.Resources.MoveRight, + Location = new Point(23, 52), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "Insert Previous Tape", + DisplayName = "PREV TAPE", + //Icon = Properties.Resources.MoveLeft, + Location = new Point(100, 52), + Type = PadSchema.PadInputType.Boolean + }, + } }; } diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index f108d03e54..6149403b27 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1378,6 +1378,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 0080872421..be24dffbc9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -401,6 +401,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("currentState", ref currentState); //_dataBlocks + /* ser.BeginSection("Datablocks"); if (ser.IsWriter) @@ -417,11 +418,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { ser.Sync("_tempBlockCount", ref _tempBlockCount); } - + ser.EndSection(); - + */ ser.EndSection(); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 85bac061d8..8a4837ac9f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -10,6 +10,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string Stop = "Stop Tape"; string RTZ = "RTZ Tape"; string Record = "Record Tape"; + string NextTape = "Insert Next Tape"; + string PrevTape = "Insert Previous Tape"; public void PollInput() { @@ -69,6 +71,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { } + if (Spectrum._controller.IsPressed(NextTape)) + { + TapeMediaIndex++; + } + if (Spectrum._controller.IsPressed(PrevTape)) + { + TapeMediaIndex--; + } } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs new file mode 100644 index 0000000000..3c60ad48df --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -0,0 +1,191 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public abstract partial class SpectrumBase + { + // until +3 disk drive is emulated, we assume that incoming files are tape images + + /// + /// The tape or disk image(s) that are passed in from the main ZXSpectrum class + /// + protected List mediaImages { get; set; } + + /// + /// Tape images + /// + protected List tapeImages { get; set; } + + /// + /// Disk images + /// + protected List diskImages { get; set; } + + /// + /// The index of the currently 'loaded' tape or disk image + /// + protected int tapeMediaIndex; + public int TapeMediaIndex + { + get { return tapeMediaIndex; } + set + { + int tmp = value; + int result = value; + + if (tapeImages == null || tapeImages.Count() == 0) + { + // no tape images found + return; + } + + if (value >= tapeImages.Count()) + { + // media at this index does not exist - loop back to 0 + result = 0; + } + else if (value < 0) + { + // negative index not allowed - move to last item in the collection + result = tapeImages.Count() - 1; + } + + // load the media into the tape device + tapeMediaIndex = result; + LoadTapeMedia(); + } + } + + /// + /// The index of the currently 'loaded' tape or disk image + /// + protected int diskMediaIndex; + public int DiskMediaIndex + { + get { return diskMediaIndex; } + set + { + int tmp = value; + int result = value; + + if (diskImages == null || diskImages.Count() == 0) + { + // no tape images found + return; + } + + if (value >= diskImages.Count()) + { + // media at this index does not exist - loop back to 0 + result = 0; + } + else if (value < 0) + { + // negative index not allowed - move to last item in the collection + result = diskImages.Count() - 1; + } + + // load the media into the disk device + diskMediaIndex = result; + LoadDiskMedia(); + } + } + + /// + /// Called on first instantiation (and subsequent core reboots) + /// + /// + protected void InitializeMedia(List files) + { + mediaImages = files; + LoadAllMedia(); + } + + /// + /// Attempts to load all media into the relevant structures + /// + protected void LoadAllMedia() + { + tapeImages = new List(); + diskImages = new List(); + + foreach (var m in mediaImages) + { + switch (IdentifyMedia(m)) + { + case SpectrumMediaType.Tape: + tapeImages.Add(m); + break; + case SpectrumMediaType.Disk: + diskImages.Add(m); + break; + } + } + + if (tapeImages.Count > 0) + LoadTapeMedia(); + + if (diskImages.Count > 0) + LoadDiskMedia(); + } + + /// + /// Attempts to load a tape into the tape device based on tapeMediaIndex + /// + protected void LoadTapeMedia() + { + TapeDevice.LoadTape(tapeImages[tapeMediaIndex]); + } + + /// + /// Attempts to load a disk into the disk device based on diskMediaIndex + /// + protected void LoadDiskMedia() + { + throw new NotImplementedException("+3 disk drive device not yet implemented"); + } + + /// + /// Identifies and sorts the various media types + /// + /// + private SpectrumMediaType IdentifyMedia(byte[] data) + { + // get first 16 bytes as a string + string hdr = Encoding.ASCII.GetString(data.Take(16).ToArray()); + + // disk checking first + if (hdr.ToUpper().Contains("EXTENDED CPC DSK")) + { + // spectrum .dsk disk file + return SpectrumMediaType.Disk; + } + if (hdr.ToUpper().StartsWith("FDI")) + { + // spectrum .fdi disk file + return SpectrumMediaType.Disk; + } + + // tape checking + if (hdr.ToUpper().StartsWith("ZXTAPE!")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } + + // if we get this far, assume a .tap file + return SpectrumMediaType.Tape; + } + } + + public enum SpectrumMediaType + { + None, + Tape, + Disk + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 11ec6ae96b..38594c7670 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -208,6 +208,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (AYDevice != null) AYDevice.SyncState(ser); + ser.Sync("tapeMediaIndex", ref tapeMediaIndex); + TapeMediaIndex = tapeMediaIndex; + + ser.Sync("diskMediaIndex", ref diskMediaIndex); + DiskMediaIndex = diskMediaIndex; + TapeDevice.SyncState(ser); ser.EndSection(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 5188a658a5..1844d7ebe4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) + public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) { Spectrum = spectrum; CPU = cpu; @@ -40,11 +40,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); - //TapeProvider = new DefaultTapeProvider(file); - TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); - TapeDevice.LoadTape(file); + + InitializeMedia(files); } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs index b88bcb3e58..a1f3c8d019 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs @@ -20,8 +20,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) - : base(spectrum, cpu, borderType, file) + public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) + : base(spectrum, cpu, borderType, files) { } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 36380c17b3..403c8495a5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) + public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) { Spectrum = spectrum; CPU = cpu; @@ -40,11 +40,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); - //TapeProvider = new DefaultTapeProvider(file); - TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); - TapeDevice.LoadTape(file); + + InitializeMedia(files); } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index b828eb9b48..b89e07957c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -16,8 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) - : base(spectrum, cpu, borderType, file) + public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) + : base(spectrum, cpu, borderType, files) { } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index deddf9bda0..a9e2bc5959 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, byte[] file) + public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) { Spectrum = spectrum; CPU = cpu; @@ -31,11 +31,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new Keyboard48(this); KempstonDevice = new KempstonJoystick(this); - //TapeProvider = new DefaultTapeProvider(file); - TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); - TapeDevice.LoadTape(file); + + InitializeMedia(files); + + //TapeDevice.LoadTape(file); } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index 018c9b67b1..fdbc8c5e02 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Keyboard - row 5 "Key Symbol Shift", "Key Semi-Colon", "Key Quote", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", // Tape functions - "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape" + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", "Insert Previous Tape" } }; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 6694a7e97d..4c1a1be8d4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public partial class ZXSpectrum : IDebuggable, IInputPollable, IStatable, IRegionable { [CoreConstructor("ZXSpectrum")] - public ZXSpectrum(CoreComm comm, byte[] file, object settings, object syncSettings) + public ZXSpectrum(CoreComm comm, IEnumerable files, GameInfo game, object settings, object syncSettings) { PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); @@ -34,29 +34,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; - _file = file; + //_file = file; + _files = files?.ToList() ?? new List(); switch (SyncSettings.MachineType) { case MachineType.ZXSpectrum16: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); break; case MachineType.ZXSpectrum48: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); break; case MachineType.ZXSpectrum128: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); break; case MachineType.ZXSpectrum128Plus2: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); break; case MachineType.ZXSpectrum128Plus3: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _file); + Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); break; default: throw new InvalidOperationException("Machine not yet emulated"); @@ -104,7 +105,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private DCFilter dcf; - private byte[] _file; + //private byte[] _file; + private readonly List _files; public bool DiagRom = false; @@ -152,37 +154,37 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } - private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, byte[] file) + private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, List files) { // setup the emulated model based on the MachineType switch (machineType) { case MachineType.ZXSpectrum16: - _machine = new ZX16(this, _cpu, borderType, file); + _machine = new ZX16(this, _cpu, borderType, files); var _systemRom16 = GetFirmware(0x4000, "48ROM"); var romData16 = RomData.InitROM(machineType, _systemRom16); _machine.InitROM(romData16); break; case MachineType.ZXSpectrum48: - _machine = new ZX48(this, _cpu, borderType, file); + _machine = new ZX48(this, _cpu, borderType, files); var _systemRom = GetFirmware(0x4000, "48ROM"); var romData = RomData.InitROM(machineType, _systemRom); _machine.InitROM(romData); break; case MachineType.ZXSpectrum128: - _machine = new ZX128(this, _cpu, borderType, file); + _machine = new ZX128(this, _cpu, borderType, files); var _systemRom128 = GetFirmware(0x8000, "128ROM"); var romData128 = RomData.InitROM(machineType, _systemRom128); _machine.InitROM(romData128); break; case MachineType.ZXSpectrum128Plus2: - _machine = new ZX128Plus2(this, _cpu, borderType, file); + _machine = new ZX128Plus2(this, _cpu, borderType, files); var _systemRomP2 = GetFirmware(0x8000, "PLUS2ROM"); var romDataP2 = RomData.InitROM(machineType, _systemRomP2); _machine.InitROM(romDataP2); break; case MachineType.ZXSpectrum128Plus3: - _machine = new ZX128Plus3(this, _cpu, borderType, file); + _machine = new ZX128Plus3(this, _cpu, borderType, files); var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); var romDataP3 = RomData.InitROM(machineType, _systemRomP3); _machine.InitROM(romDataP3); From 23c07cdb678fc3b782140b32a1ee54e3b05e798a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 5 Mar 2018 13:29:34 +0000 Subject: [PATCH 050/339] OSD message handling implementation --- BizHawk.Client.Common/RomLoader.cs | 11 +- .../BizHawk.Emulation.Cores.csproj | 1 + .../Hardware/Datacorder/DatacorderDevice.cs | 5 + .../Machine/SpectrumBase.Input.cs | 57 ++++- .../Machine/SpectrumBase.Media.cs | 3 + .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 21 ++ .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 197 ++++++++++++++++++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 6 +- 8 files changed, 291 insertions(+), 10 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 3bc0b7059a..39ae27262e 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -659,10 +659,17 @@ namespace BizHawk.Client.Common (C64.C64SyncSettings)GetCoreSyncSettings()); break; case "ZXSpectrum": + + List zxGI = new List(); + foreach (var a in xmlGame.Assets) + { + zxGI.Add(new GameInfo { Name = Path.GetFileNameWithoutExtension(a.Key) }); + } + nextEmulator = new ZXSpectrum( nextComm, xmlGame.Assets.Select(a => a.Value), //.First(), - GameInfo.NullInstance, + zxGI, // GameInfo.NullInstance, (ZXSpectrum.ZXSpectrumSettings)GetCoreSettings(), (ZXSpectrum.ZXSpectrumSyncSettings)GetCoreSyncSettings()); break; @@ -1000,7 +1007,7 @@ namespace BizHawk.Client.Common nextEmulator = c64; break; case "ZXSpectrum": - var zx = new ZXSpectrum(nextComm, Enumerable.Repeat(rom.RomData, 1), rom.GameInfo, GetCoreSettings(), GetCoreSyncSettings()); + var zx = new ZXSpectrum(nextComm, Enumerable.Repeat(rom.RomData, 1), Enumerable.Repeat(rom.GameInfo, 1).ToList(), GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = zx; break; case "GBA": diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 6149403b27..d85d488b0a 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -296,6 +296,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index be24dffbc9..eb37c02cf4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -133,6 +133,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeIsPlaying) return; + _machine.Spectrum.OSD_TapePlaying(); + // update the lastCycle _lastCycle = _cpu.TotalExecutedCycles; @@ -183,6 +185,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!_tapeIsPlaying) return; + _machine.Spectrum.OSD_TapeStopped(); + // sign that the tape is no longer playing _tapeIsPlaying = false; @@ -224,6 +228,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void RTZ() { Stop(); + _machine.Spectrum.OSD_TapeRTZ(); _currentDataBlockIndex = 0; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 8a4837ac9f..97bd1c0e5f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -13,6 +13,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string NextTape = "Insert Next Tape"; string PrevTape = "Insert Previous Tape"; + bool pressed_Play = false; + bool pressed_Stop = false; + bool pressed_RTZ = false; + bool pressed_NextTape = false; + bool pressed_PrevTape = false; + public void PollInput() { Spectrum.InputCallbacks.Call(); @@ -22,8 +28,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // parse single keyboard matrix keys for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++) { - - string key = KeyboardDevice.KeyboardMatrix[i]; bool prevState = KeyboardDevice.GetKeyStatus(key); bool currState = Spectrum._controller.IsPressed(key); @@ -57,28 +61,67 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Tape control if (Spectrum._controller.IsPressed(Play)) { - TapeDevice.Play(); + if (!pressed_Play) + { + Spectrum.OSD_FireInputMessage(Play); + TapeDevice.Play(); + pressed_Play = true; + } } + else + pressed_Play = false; + if (Spectrum._controller.IsPressed(Stop)) { - TapeDevice.Stop(); + if (!pressed_Stop) + { + Spectrum.OSD_FireInputMessage(Stop); + TapeDevice.Stop(); + pressed_Stop = true; + } } + else + pressed_Stop = false; + if (Spectrum._controller.IsPressed(RTZ)) { - TapeDevice.RTZ(); + if (!pressed_RTZ) + { + Spectrum.OSD_FireInputMessage(RTZ); + TapeDevice.RTZ(); + pressed_RTZ = true; + } } + else + pressed_RTZ = false; + if (Spectrum._controller.IsPressed(Record)) { } if (Spectrum._controller.IsPressed(NextTape)) { - TapeMediaIndex++; + if (!pressed_NextTape) + { + Spectrum.OSD_FireInputMessage(NextTape); + TapeMediaIndex++; + pressed_NextTape = true; + } } + else + pressed_NextTape = false; + if (Spectrum._controller.IsPressed(PrevTape)) { - TapeMediaIndex--; + if (!pressed_PrevTape) + { + Spectrum.OSD_FireInputMessage(PrevTape); + TapeMediaIndex--; + pressed_PrevTape = true; + } } + else + pressed_PrevTape = false; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index 3c60ad48df..aa1b943823 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -56,6 +56,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // load the media into the tape device tapeMediaIndex = result; + // fire osd message + Spectrum.OSD_TapeInserted(); LoadTapeMedia(); } } @@ -103,6 +105,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { mediaImages = files; LoadAllMedia(); + Spectrum.OSD_TapeInit(); } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index df56bd1b5b..f964d6f43d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -48,6 +48,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DefaultValue(true)] public bool StereoSound { get; set; } + [DisplayName("Core OSD Message Verbosity")] + [Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")] + [DefaultValue(OSDVerbosity.Medium)] + public OSDVerbosity OSDMessageVerbosity { get; set; } + public ZXSpectrumSettings Clone() { @@ -98,6 +103,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + public enum OSDVerbosity + { + /// + /// Show all OSD messages + /// + Full, + /// + /// Only show machine/device generated messages + /// + Medium, + /// + /// No core-driven OSD messages + /// + None + } + /// /// The size of the Spectrum border /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs new file mode 100644 index 0000000000..2d83819f95 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -0,0 +1,197 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Handles all messaging (OSD) operations + /// + public partial class ZXSpectrum + { + /// + /// Writes a message to the OSD + /// + /// + /// + public void SendMessage(string message, MessageCategory category) + { + if (!CheckMessageSettings(category)) + return; + + StringBuilder sb = new StringBuilder(); + + switch (category) + { + case MessageCategory.Tape: + sb.Append("DATACORDER: "); + sb.Append(message); + break; + case MessageCategory.Input: + sb.Append("INPUT DETECTED: "); + sb.Append(message); + break; + case MessageCategory.Disk: + sb.Append("DISK DRIVE: "); + sb.Append(message); + break; + case MessageCategory.Emulator: + case MessageCategory.Misc: + sb.Append("ZXHAWK: "); + sb.Append(message); + break; + } + + CoreComm.Notify(sb.ToString()); + } + + #region Input Message Methods + + /// + /// Called when certain input presses are detected + /// + /// + public void OSD_FireInputMessage(string input) + { + StringBuilder sb = new StringBuilder(); + sb.Append(input); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Input); + } + + #endregion + + #region TapeDevice Message Methods + + /// + /// Tape message that is fired on core init + /// + public void OSD_TapeInit() + { + StringBuilder sb = new StringBuilder(); + sb.Append("Tape Media Imported (count: " + _gameInfo.Count() + ")"); + sb.Append("\n"); + for (int i = 0; i < _gameInfo.Count(); i++) + sb.Append(i.ToString() + ": " + _gameInfo[i].Name + "\n"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator); + } + + /// + /// Tape message that is fired when tape is playing + /// + public void OSD_TapePlaying() + { + StringBuilder sb = new StringBuilder(); + sb.Append("PLAYING (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when tape is stopped + /// + public void OSD_TapeStopped() + { + StringBuilder sb = new StringBuilder(); + sb.Append("STOPPED (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when tape is rewound + /// + public void OSD_TapeRTZ() + { + StringBuilder sb = new StringBuilder(); + sb.Append("REWOUND (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a new tape is inserted into the datacorder + /// + public void OSD_TapeInserted() + { + StringBuilder sb = new StringBuilder(); + sb.Append("TAPE INSERTED (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + + /// + /// Tape message that is fired when a tape is stopped automatically + /// + public void OSD_TapeStoppedAuto() + { + StringBuilder sb = new StringBuilder(); + sb.Append("STOPPED (Auto Tape Trap) (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + #endregion + + + + /// + /// Checks whether message category is allowed to be sent + /// + /// + /// + public bool CheckMessageSettings(MessageCategory category) + { + switch (Settings.OSDMessageVerbosity) + { + case OSDVerbosity.Full: + return true; + case OSDVerbosity.None: + return false; + case OSDVerbosity.Medium: + switch (category) + { + case MessageCategory.Disk: + case MessageCategory.Emulator: + case MessageCategory.Tape: + case MessageCategory.Misc: + return true; + default: + return false; + } + default: + return true; + } + } + + /// + /// Defines the different message categories + /// + public enum MessageCategory + { + /// + /// No defined category as such + /// + Misc, + /// + /// User generated input messages (at the moment only tape/disk controls) + /// + Input, + /// + /// Tape device generated messages + /// + Tape, + /// + /// Disk device generated messages + /// + Disk, + /// + /// Emulator generated messages + /// + Emulator + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 4c1a1be8d4..df56791f0c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public partial class ZXSpectrum : IDebuggable, IInputPollable, IStatable, IRegionable { [CoreConstructor("ZXSpectrum")] - public ZXSpectrum(CoreComm comm, IEnumerable files, GameInfo game, object settings, object syncSettings) + public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) { PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); @@ -30,6 +30,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CoreComm = comm; + _gameInfo = game; + _cpu = new Z80A(); _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; @@ -101,6 +103,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public IController _controller; private SpectrumBase _machine; + private List _gameInfo; + private SoundProviderMixer SoundMixer; private DCFilter dcf; From e2a212a0b8ec64832802a0be1b25c67f9a542c85 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 5 Mar 2018 16:12:19 +0000 Subject: [PATCH 051/339] Added tape trap auto-load option --- .../Hardware/Datacorder/DatacorderDevice.cs | 161 ++++++++++++++++-- .../Machine/SpectrumBase.Memory.cs | 2 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 15 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 12 +- .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 48 +++++- 5 files changed, 216 insertions(+), 22 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index eb37c02cf4..4bdc9aca30 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -2,6 +2,7 @@ using BizHawk.Emulation.Cores.Components.Z80A; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -123,6 +124,88 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + #region Emulator + + /// + /// This is the address the that ROM will jump to when the spectrum has quit tape playing + /// + public const ushort ERROR_ROM_ADDRESS = 0x0008; + + Stopwatch sw = new Stopwatch(); + + /// + /// Should be fired at the end of every frame + /// 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(); + } + } + */ + } + + /// + /// 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 /// @@ -277,6 +360,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region Tape Device Methods + private bool initialBlockPlayed = false; + /// /// Simulates the spectrum 'EAR' input reading data from the tape /// @@ -284,6 +369,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool GetEarBit(long cpuCycle) { + + // decide how many cycles worth of data we are capturing long cycles = cpuCycle - _lastCycle; @@ -312,6 +399,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // flip the current state currentState = !currentState; + if (_position == 0) + { + // start of block + // notify about the current block + + var bl = _dataBlocks[_currentDataBlockIndex]; + + StringBuilder sbd = new StringBuilder(); + sbd.Append("("); + sbd.Append((_currentDataBlockIndex + 1) + " of " + _dataBlocks.Count()); + sbd.Append(") : "); + //sbd.Append("ID" + bl.BlockID.ToString("X2") + " - "); + sbd.Append(bl.BlockDescription); + if (bl.MetaData.Count > 0) + { + sbd.Append(" - "); + sbd.Append(bl.MetaData.First().Key + ": " + bl.MetaData.First().Value); + } + _machine.Spectrum.OSD_TapePlayingBlockInfo(sbd.ToString()); + } + + // increment the current period position _position++; @@ -327,6 +436,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Stop the tape command found - if this is the end of the tape RTZ // otherwise just STOP and move to the next block case TapeCommand.STOP_THE_TAPE: + + _machine.Spectrum.OSD_TapeStoppedAuto(); + if (_currentDataBlockIndex >= _dataBlocks.Count()) RTZ(); else @@ -335,18 +447,50 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } break; case TapeCommand.STOP_THE_TAPE_48K: - if (_currentDataBlockIndex >= _dataBlocks.Count()) - RTZ(); - else + + if ((_machine.GetType() != typeof(ZX128) && + _machine.GetType() != typeof(ZX128Plus2) && + _machine.GetType() != typeof(ZX128Plus3)) || + (_machine.GetType() == typeof(ZX128) || + _machine.GetType() != typeof(ZX128Plus2) || + _machine.GetType() != typeof(ZX128Plus3)) && + _machine._ROMpaged == 1) { - Stop(); + _machine.Spectrum.OSD_TapeStoppedAuto(); + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + } break; } + if (_dataBlocks[_currentDataBlockIndex].DataPeriods.Count() == 0) + { + // notify about the current block (we are skipping it because its empty) + var bl = _dataBlocks[_currentDataBlockIndex]; + StringBuilder sbd = new StringBuilder(); + sbd.Append("("); + sbd.Append((_currentDataBlockIndex + 1) + " of " + _dataBlocks.Count()); + sbd.Append(") : "); + //sbd.Append("ID" + bl.BlockID.ToString("X2") + " - "); + sbd.Append(bl.BlockDescription); + if (bl.MetaData.Count > 0) + { + sbd.Append(" - "); + sbd.Append(bl.MetaData.First().Key + ": " + bl.MetaData.First().Value); + } + _machine.Spectrum.OSD_TapePlayingSkipBlockInfo(sbd.ToString()); + + } + // skip any empty blocks while (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count()) - { + { _position = 0; _currentDataBlockIndex++; if (_currentDataBlockIndex >= _dataBlocks.Count()) @@ -378,12 +522,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return currentState; } - #endregion - - #region Media Serialization - - - #endregion #region State Serialization @@ -403,6 +541,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("_tapeIsPlaying", ref _tapeIsPlaying); ser.Sync("_lastCycle", ref _lastCycle); ser.Sync("_waitEdge", ref _waitEdge); + //ser.Sync("_initialBlockPlayed", ref initialBlockPlayed); ser.Sync("currentState", ref currentState); //_dataBlocks diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 81ee1c819f..0d750bc64c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public byte[] ROM1 = new byte[0x4000]; public byte[] ROM2 = new byte[0x4000]; public byte[] ROM3 = new byte[0x4000]; - + /// /// RAM Banks /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 38594c7670..9d4ec84a10 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -12,7 +12,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public abstract partial class SpectrumBase { // 128 and up only - protected int ROMPaged = 0; + //protected int ROMPaged = 0; + + protected int ROMPaged; + + public int _ROMpaged + { + get { return ROMPaged; } + set { ROMPaged = value; } + } + + protected bool SHADOWPaged; public int RAMPaged; protected bool PagingDisabled; @@ -151,6 +161,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // setup for next frame ULADevice.ResetInterrupt(); + + TapeDevice.EndFrame(); + FrameCompleted = true; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index f964d6f43d..182cedd7a4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -43,6 +43,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class ZXSpectrumSettings { + [DisplayName("Auto-load/stop tape")] + [Description("Auto or manual tape operation. Auto will attempt to detect CPU tape traps and automatically Stop/Start the tape")] + [DefaultValue(true)] + public bool AutoLoadTape { get; set; } + [DisplayName("Stereo Sound")] [Description("Turn stereo sound on or off")] [DefaultValue(true)] @@ -80,12 +85,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DisplayName("Tape Load Speed")] [Description("Select how fast the spectrum loads the game from tape")] [DefaultValue(TapeLoadSpeed.Accurate)] - public TapeLoadSpeed TapeLoadSpeed { get; set; } - - [DisplayName("Auto-load tape")] - [Description("Auto or manual tape operation")] - [DefaultValue(true)] - public bool AutoLoadTape { get; set; } + public TapeLoadSpeed TapeLoadSpeed { get; set; } public ZXSpectrumSyncSettings Clone() { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 2d83819f95..5b7b29b3d6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -129,15 +129,57 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapeStoppedAuto() { StringBuilder sb = new StringBuilder(); - sb.Append("STOPPED (Auto Tape Trap) (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + sb.Append("STOPPED (Auto Tape Trap Detected)"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a tape is started automatically + /// + public void OSD_TapePlayingAuto() + { + StringBuilder sb = new StringBuilder(); + sb.Append("PLAYING (Auto Tape Trap Detected)"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a new block starts playing + /// + public void OSD_TapePlayingBlockInfo(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + sb.Append("...Starting Block "+ blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a tape block is skipped (because it is empty) + /// + public void OSD_TapePlayingSkipBlockInfo(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + sb.Append("...Skipping Empty Block " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a tape is started automatically + /// + public void OSD_TapeEndDetected(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + sb.Append("...Skipping Empty Block " + blockinfo); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } #endregion - - /// /// Checks whether message category is allowed to be sent /// From b409c88c50261797a86b76d0cc56cc16c8569fc9 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 5 Mar 2018 16:40:36 +0000 Subject: [PATCH 052/339] Fixed .tap system detection. was causing an exception due to shocking bit of anti-logic (on my part) when opening from zip files --- BizHawk.Emulation.Common/Database/Database.cs | 2 +- BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 9364aac02d..d106a5e91a 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -309,7 +309,7 @@ namespace BizHawk.Emulation.Common case ".TAP": - byte[] head = File.ReadAllBytes(fileName).Take(8).ToArray(); + byte[] head = romData.Take(8).ToArray(); if (System.Text.Encoding.Default.GetString(head).Contains("C64-TAPE")) game.System = "C64"; else diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index f4f78dd4fc..7b2345b74c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -16,11 +16,11 @@ At the moment this is very experimental and is still actively being worked on. * Most tape protection/loading schemes that I've tested are currently working (see caveat below) * IStatable * ISettable core settings +* Tape auto-loading routines (as a setting) ### Work in progress * Exact emulator timings * Floating memory bus emulation -* Tape auto-loading routines (currently you have to manually start and stop the virtual tape device) * TASStudio (need to verify that this works as it should) ### Not working From 3cc4b944061ba5f5ee7d459b2d2481c214b6a6c3 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 11:17:30 +0000 Subject: [PATCH 053/339] Added default control bindings and prettified the controller configuration panels --- Assets/defctrl.json | 70 ++++++++++++++++++ .../BizHawk.Client.EmuHawk.csproj | 2 + .../Properties/Resources.Designer.cs | 12 ++- .../Properties/Resources.resx | 5 +- .../Resources/ZXSpectrumKeyboard.bmp | Bin 0 -> 3382 bytes .../config/ControllerConfig.cs | 28 +++++-- .../ControllerImages/ZXSpectrumKeyboards.png | Bin 0 -> 435119 bytes .../ZXSpectrum.Controllers.cs | 70 +++++++++++++++++- 8 files changed, 178 insertions(+), 9 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/Resources/ZXSpectrumKeyboard.bmp create mode 100644 BizHawk.Client.EmuHawk/config/ControllerImages/ZXSpectrumKeyboards.png diff --git a/Assets/defctrl.json b/Assets/defctrl.json index 42f8b4cee1..631e0af74b 100644 --- a/Assets/defctrl.json +++ b/Assets/defctrl.json @@ -461,6 +461,76 @@ "Key Cursor Up/Down": "DownArrow", "Key Cursor Left/Right": "RightArrow", "Key Space": "Space" + }, + "ZXSpectrum Controller": { + "P1 Up": "NumberPad8, J1 POV1U, X1 DpadUp, X1 LStickUp", + "P1 Down": "NumberPad2, J1 POV1D, X1 DpadDown, X1 LStickDown", + "P1 Left": "NumberPad4, J1 POV1L, X1 DpadLeft, X1 LStickLeft", + "P1 Right": "NumberPad6, J1 POV1R, X1 DpadRight, X1 LStickRight", + "P1 Button": "NumberPad1, J1 B1, X1 X", + "Key True Video": "", + "Key Inv Video": "", + "Key 1": "D1", + "Key 2": "D2", + "Key 3": "D3", + "Key 4": "D4", + "Key 5": "D5", + "Key 6": "D6", + "Key 7": "D7", + "Key 8": "D8", + "Key 9": "D9", + "Key 0": "D0", + "Key Break": "Delete", + "Key Delete": "Backspace", + "Key Graph": "", + "Key Q": "Q", + "Key W": "W", + "Key E": "E", + "Key R": "R", + "Key T": "T", + "Key Y": "Y", + "Key U": "U", + "Key I": "I", + "Key O": "O", + "Key P": "P", + "Key Extend Mode": "", + "Key Edit": "", + "Key A": "A", + "Key S": "S", + "Key D": "D", + "Key F": "F", + "Key G": "G", + "Key H": "H", + "Key J": "J", + "Key K": "K", + "Key L": "L", + "Key Return": "Return", + "Key Caps Shift": "LeftShift, RightShift", + "Key Caps Lock": "", + "Key Z": "Z", + "Key X": "X", + "Key C": "C", + "Key V": "V", + "Key B": "B", + "Key N": "N", + "Key M": "M", + "Key Period": "Period", + "Key Symbol Shift": "LeftControl, RightControl", + "Key Semi-Colon": "Semicolon", + "Key Inverted-Comma": "", + "Key Left Cursor": "LeftArrow", + "Key Right Cursor": "RightArrow", + "Key Space": "Space", + "Key Up Cursor": "UpArrow", + "Key Down Cursor": "DownArrow", + "Key Comma": "Comma", + "Play Tape": "F1", + "Stop Tape": "F2", + "RTZ Tape": "F3", + "Record Tape": "", + "Key Quote": "Shift+D2", + "Insert Next Tape": "F6", + "Insert Previous Tape": "F5" }, "Intellivision Controller": { "P1 Up": "UpArrow, J1 POV1U, X1 DpadUp, X1 LStickUp", diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index fdce3477bf..ccca5cf3eb 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -1786,6 +1786,7 @@ + @@ -2110,6 +2111,7 @@ + diff --git a/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs b/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs index 72c965441f..7504818e28 100644 --- a/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs +++ b/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs @@ -489,7 +489,7 @@ namespace BizHawk.Client.EmuHawk.Properties { return ((System.Drawing.Bitmap)(obj)); } } - + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -3438,5 +3438,15 @@ namespace BizHawk.Client.EmuHawk.Properties { return ((System.Drawing.Bitmap)(obj)); } } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ZXSpectrumKeyboards { + get { + object obj = ResourceManager.GetObject("ZXSpectrumKeyboards", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } } } diff --git a/BizHawk.Client.EmuHawk/Properties/Resources.resx b/BizHawk.Client.EmuHawk/Properties/Resources.resx index 8d9fb1b714..c9a9623430 100644 --- a/BizHawk.Client.EmuHawk/Properties/Resources.resx +++ b/BizHawk.Client.EmuHawk/Properties/Resources.resx @@ -1557,4 +1557,7 @@ ..\images\ControllerImages\NGPController.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - + + ..\config\controllerimages\zxspectrumkeyboards.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/Resources/ZXSpectrumKeyboard.bmp b/BizHawk.Client.EmuHawk/Resources/ZXSpectrumKeyboard.bmp new file mode 100644 index 0000000000000000000000000000000000000000..55516c7aaa3591604865d4cd18379f8f650c3104 GIT binary patch literal 3382 zcmeIwA#&YN425B3CLjUbgJ57$uqM1+gO+lZkut%6E<(#F6wORPc)-i=r?4!c_Vwp; zZ3Fx2>gm_x54#?Zw`LE_etx;JyM1xL@%<_@PLnpcGPUluKQDjn-|L&12NJySLSxLr z;9wF7q0kt!G&lm>q)=$g*LR%^B!og^%$Qm^0tune7=F^=2qc6;W6U^h9D#&TXpDvN zo8~x1LMSw5m*!3e5<;OdyXkPr%u*^;D_frLmI06Zw(3st}a59h(3XRzqWFR3F8nf*xCj$wg(3oxIax#z*3XRz|Zzlr@q3r$RV$Z*g8j2NO iuBTlt#|(_)(Q$tsX9dm*oE11La8}@~z*&L+tOB3FDm>c& literal 0 HcmV?d00001 diff --git a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs index 9800e5ec13..f17a7e1afe 100644 --- a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs +++ b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs @@ -172,15 +172,24 @@ namespace BizHawk.Client.EmuHawk string tabname = cat.Key; tt.TabPages.Add(tabname); tt.TabPages[pageidx].Controls.Add(createpanel(settings, cat.Value, tt.Size)); - } + + // zxhawk hack - it uses multiple categoryLabels + if (Global.Emulator.SystemId == "ZXSpectrum") + pageidx++; + + } if (buckets[0].Count > 0) { - string tabname = (Global.Emulator.SystemId == "C64" || Global.Emulator.SystemId == "ZXSpectrum") ? "Keyboard" : "Console"; // hack - tt.TabPages.Add(tabname); - tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[0], tt.Size)); - } - } + // ZXHawk needs to skip this bit + if (Global.Emulator.SystemId == "ZXSpectrum") + return; + + string tabname = (Global.Emulator.SystemId == "C64") ? "Keyboard" : "Console"; // hack + tt.TabPages.Add(tabname); + tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[0], tt.Size)); + } + } } public ControllerConfig(ControllerDefinition def) @@ -256,6 +265,13 @@ namespace BizHawk.Client.EmuHawk pictureBox2.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom; } + + if (controlName == "ZXSpectrum Controller") + { + pictureBox1.Image = Properties.Resources.ZXSpectrumKeyboards; + pictureBox1.Size = Properties.Resources.ZXSpectrumKeyboards.Size; + tableLayoutPanel1.ColumnStyles[1].Width = Properties.Resources.ZXSpectrumKeyboards.Width; + } } // lazy methods, but they're not called often and actually diff --git a/BizHawk.Client.EmuHawk/config/ControllerImages/ZXSpectrumKeyboards.png b/BizHawk.Client.EmuHawk/config/ControllerImages/ZXSpectrumKeyboards.png new file mode 100644 index 0000000000000000000000000000000000000000..1af85ebea9d43492a61e560046e2a74126a54225 GIT binary patch literal 435119 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

ChMrh;k96x~J4C;&eVCv{U*5(cek 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 z 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

xWRU<^d;K;GOOgYoA=<~ba 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 literal 0 HcmV?d00001 diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index fdbc8c5e02..8712e2d425 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -1,11 +1,77 @@  using BizHawk.Emulation.Common; +using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZXSpectrum { + ///

+ /// The one ZX Hawk ControllerDefinition + /// + public static ControllerDefinition ZXSpectrumControllerDefinition + { + get + { + ControllerDefinition definition = new ControllerDefinition(); + definition.Name = "ZXSpectrum Controller"; + // joysticks + List joys = new List + { + // Kempston Joystick (P1) + "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", + }; + + foreach (var s in joys) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Kempton Joystick"; + } + + // keyboard + List keys = new List + { + /// Controller mapping includes all keyboard keys from the following models: + /// https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/ZXSpectrum48k.jpg/1200px-ZXSpectrum48k.jpg + /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg + + // Keyboard - row 1 + "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", + // Keyboard - row 2 + "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", + // Keyboard - row 3 + "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", + // Keyboard - row 4 + "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", + // Keyboard - row 5 + "Key Symbol Shift", "Key Semi-Colon", "Key Quote", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", + }; + + foreach (var s in keys) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Keyboard"; + } + + // Datacorder (tape device) + List tape = new List + { + // Tape functions + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", "Insert Previous Tape", "Next Tape Block", "Prev Tape Block" + }; + + foreach (var s in tape) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Datacorder"; + } + + return definition; + } + } + + /* /// /// Controller mapping includes all keyboard keys from the following models: /// https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/ZXSpectrum48k.jpg/1200px-ZXSpectrum48k.jpg @@ -29,8 +95,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Keyboard - row 5 "Key Symbol Shift", "Key Semi-Colon", "Key Quote", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", // Tape functions - "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", "Insert Previous Tape" + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", "Insert Previous Tape", "Next Tape Block", "Prev Tape Block" } }; + + */ } } From 93ae29c3a0a33cf3569c4bd818381ddd5b87a3ce Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 15:47:14 +0000 Subject: [PATCH 054/339] Added Cursor(Protek) and Sinclair (left and right) joystick emulation. Also user can now set J1, J2, and J3 emulated joystick type through syncsettings --- .../BizHawk.Emulation.Cores.csproj | 5 + .../Hardware/Input/CursorJoystick.cs | 116 ++++++++++++++++++ .../Hardware/Input/IJoystick.cs | 44 +++++++ .../Hardware/Input/KempstonJoystick.cs | 77 ++++++++---- .../Hardware/Input/NullJoystick.cs | 107 ++++++++++++++++ .../Hardware/Input/SinclairJoystick1.cs | 115 +++++++++++++++++ .../Hardware/Input/SinclairJoystick2.cs | 115 +++++++++++++++++ .../Machine/SpectrumBase.Input.cs | 101 +++++++++++++-- .../SinclairSpectrum/Machine/SpectrumBase.cs | 22 +++- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 3 +- .../Machine/ZXSpectrum128K/ZX128.cs | 6 +- .../Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs | 4 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 3 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 6 +- .../Machine/ZXSpectrum16K/ZX16.cs | 4 +- .../Machine/ZXSpectrum48K/ZX48.Keyboard.cs | 2 + .../Machine/ZXSpectrum48K/ZX48.Port.cs | 3 +- .../Machine/ZXSpectrum48K/ZX48.cs | 7 +- .../ZXSpectrum.Controllers.cs | 54 +++++++- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 18 ++- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 31 +++-- 21 files changed, 775 insertions(+), 68 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IJoystick.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index d85d488b0a..e9bc4d2bac 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -256,6 +256,11 @@ + + + + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs new file mode 100644 index 0000000000..bda58ec7eb --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs @@ -0,0 +1,116 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Cursor joystick + /// Maps to a combination of 0xf7fe and 0xeffe + /// + public class CursorJoystick : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public CursorJoystick(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + private List btnLookups = new List + { + "Key 5", // left + "Key 8", // right + "Key 6", // down + "Key 7", // up + "Key 0", // fire + }; + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.SinclairPORT1; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + + if (isPressed) + { + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true); + } + else + { + if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos])) + { + // key is already pressed elswhere - leave it as is + } + else + { + // key is safe to unpress + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false); + } + } + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + if (_machine == null) + return false; + + var l = _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]); + return l; + } + + #endregion + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IJoystick.cs new file mode 100644 index 0000000000..6421e99af4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IJoystick.cs @@ -0,0 +1,44 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a spectrum joystick + /// + public interface IJoystick + { + /// + /// The type of joystick + /// + JoystickType JoyType { get; } + + /// + /// Array of all the possibly button press names + /// + string[] ButtonCollection { get; set; } + + /// + /// The player number that this controller is currently assigned to + /// + int PlayerNumber { get; set; } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + void SetJoyInput(string key, bool isPressed); + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + bool GetJoyInput(string key); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs index 5da278df76..880e388f3a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs @@ -1,4 +1,5 @@ -using System; +using BizHawk.Common; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,34 +7,42 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { - public class KempstonJoystick + public class KempstonJoystick : IJoystick { private int _joyLine; private SpectrumBase _machine; - public readonly string[] _bitPos = new string[] - { - "P1 Right", - "P1 Left", - "P1 Down", - "P1 Up", - "P1 Button" - }; + #region Construction - /* - Active bits high - 0 0 0 F U D L R - */ - public int JoyLine - { - get { return _joyLine; } - set { _joyLine = value; } - } - - public KempstonJoystick(SpectrumBase machine) + public KempstonJoystick(SpectrumBase machine, int playerNumber) { _machine = machine; _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.Kempston; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } } /// @@ -60,6 +69,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var pos = GetBitPos(key); return (_joyLine & (1 << pos)) != 0; } + + #endregion + + /// + /// Active bits high + /// 0 0 0 F U D L R + /// + public int JoyLine + { + get { return _joyLine; } + set { _joyLine = value; } + } /// /// Gets the bit position of a particular joystick binding from the matrix @@ -68,8 +89,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int GetBitPos(string key) { - int index = Array.IndexOf(_bitPos, key); + int index = Array.IndexOf(ButtonCollection, key); return index; } + + + /* + public readonly string[] _bitPos = new string[] + { + "P1 Right", + "P1 Left", + "P1 Down", + "P1 Up", + "P1 Button" + }; + */ } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs new file mode 100644 index 0000000000..a8773d0625 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs @@ -0,0 +1,107 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// A null joystick object + /// + public class NullJoystick : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public NullJoystick(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + + }.ToArray(); + } + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.None; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + if (isPressed) + _joyLine |= (1 << pos); + else + _joyLine &= ~(1 << pos); + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + return (_joyLine & (1 << pos)) != 0; + } + + #endregion + + /// + /// Active bits high + /// 0 0 0 F U D L R + /// + public int JoyLine + { + get { return _joyLine; } + set { _joyLine = value; } + } + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + + + /* + public readonly string[] _bitPos = new string[] + { + "P1 Right", + "P1 Left", + "P1 Down", + "P1 Up", + "P1 Button" + }; + */ + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs new file mode 100644 index 0000000000..569f25b80d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs @@ -0,0 +1,115 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Sinclair Joystick LEFT + /// Just maps to the standard keyboard and is read the same (from port 0xf7fe) + /// + public class SinclairJoystick1 : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public SinclairJoystick1(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + private List btnLookups = new List + { + "Key 1", // left + "Key 2", // right + "Key 3", // down + "Key 4", // up + "Key 5", // fire + }; + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.SinclairPORT1; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + + if (isPressed) + { + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true); + } + else + { + if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos])) + { + // key is already pressed elswhere - leave it as is + } + else + { + // key is safe to unpress + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false); + } + } + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + if (_machine == null) + return false; + + return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]); + } + + #endregion + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs new file mode 100644 index 0000000000..160ca0ea39 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs @@ -0,0 +1,115 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Sinclair Joystick RIGHT + /// Just maps to the standard keyboard and is read the same (from port 0xeffe) + /// + public class SinclairJoystick2 : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public SinclairJoystick2(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + private List btnLookups = new List + { + "Key 6", // left + "Key 7", // right + "Key 8", // down + "Key 9", // up + "Key 0", // fire + }; + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.SinclairPORT1; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + + if (isPressed) + { + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true); + } + else + { + if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos])) + { + // key is already pressed elswhere - leave it as is + } + else + { + // key is safe to unpress + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false); + } + } + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + if (_machine == null) + return false; + + return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]); + } + + #endregion + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 97bd1c0e5f..74ea1f645c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -1,4 +1,7 @@  +using System.Collections.Generic; +using System.Linq; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// @@ -37,6 +40,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // non matrix keys + /* foreach (string k in KeyboardDevice.NonMatrixKeys) { if (!k.StartsWith("Key")) @@ -46,17 +50,40 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice.SetKeyStatus(k, currState); } + */ + + // J1 + foreach (string j in JoystickCollection[0].ButtonCollection) + { + bool prevState = JoystickCollection[0].GetJoyInput(j); + bool currState = Spectrum._controller.IsPressed(j); + + if (currState != prevState) + JoystickCollection[0].SetJoyInput(j, currState); + } + + // J2 + foreach (string j in JoystickCollection[1].ButtonCollection) + { + bool prevState = JoystickCollection[1].GetJoyInput(j); + bool currState = Spectrum._controller.IsPressed(j); + + if (currState != prevState) + JoystickCollection[1].SetJoyInput(j, currState); + } + + // J3 + foreach (string j in JoystickCollection[2].ButtonCollection) + { + bool prevState = JoystickCollection[2].GetJoyInput(j); + bool currState = Spectrum._controller.IsPressed(j); + + if (currState != prevState) + JoystickCollection[2].SetJoyInput(j, currState); + } } - // J1 - foreach (string j in KempstonDevice._bitPos) - { - bool prevState = KempstonDevice.GetJoyInput(j); - bool currState = Spectrum._controller.IsPressed(j); - - if (currState != prevState) - KempstonDevice.SetJoyInput(j, currState); - } + // Tape control if (Spectrum._controller.IsPressed(Play)) @@ -123,6 +150,62 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else pressed_PrevTape = false; } + + /// + /// Instantiates the joysticks array + /// + /// + protected void InitJoysticks(List joys) + { + List jCollection = new List(); + + for (int i = 0; i < joys.Count(); i++) + { + jCollection.Add(InstantiateJoystick(joys[i], i + 1)); + } + + JoystickCollection = jCollection.ToArray(); + + for (int i = 0; i < JoystickCollection.Length; i++) + { + Spectrum.OSD_FireInputMessage("Joystick " + (i + 1) + ": " + JoystickCollection[i].JoyType.ToString()); + } + } + + /// + /// Instantiates a new IJoystick object + /// + /// + /// + /// + public IJoystick InstantiateJoystick(JoystickType type, int playerNumber) + { + switch (type) + { + case JoystickType.Kempston: + return new KempstonJoystick(this, playerNumber); + case JoystickType.Cursor: + return new CursorJoystick(this, playerNumber); + case JoystickType.SinclairPORT1: + return new SinclairJoystick1(this, playerNumber); + case JoystickType.SinclairPORT2: + return new SinclairJoystick2(this, playerNumber); + case JoystickType.None: + return new NullJoystick(this, playerNumber); + } + + return null; + } + + /// + /// Returns a IJoystick object depending on the type (or null if not found) + /// + /// + /// + protected IJoystick LocateUniqueJoystick(JoystickType type) + { + return JoystickCollection.Where(a => a.JoyType == type).FirstOrDefault(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 9d4ec84a10..4e0080b41f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -2,6 +2,7 @@ using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components.Z80A; using System; +using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -72,14 +73,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public virtual DatacorderDevice TapeDevice { get; set; } /// - /// The tape provider + /// Holds the currently selected joysticks /// - //public virtual ITapeProvider TapeProvider { get; set; } + public virtual IJoystick[] JoystickCollection { get; set; } + + /* + /// + /// Joystick device 1 + /// + public virtual IJoystick Joystick1 { get; set; } /// - /// Kempston joystick + /// Joystick device 2 /// - public virtual KempstonJoystick KempstonDevice { get; set; } + public virtual IJoystick Joystick2 { get; set; } + + /// + /// Joystick device 3 + /// + public virtual IJoystick Joystick3 { get; set; } + + */ /// /// Signs whether the frame has ended 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 64c2cff470..2604d8187b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -27,7 +27,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) { - return (byte)KempstonDevice.JoyLine; + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; } else if (lowBitReset) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 1844d7ebe4..eaab39cb1d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) + public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) { Spectrum = spectrum; CPU = cpu; @@ -38,7 +38,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new Keyboard48(this); - KempstonDevice = new KempstonJoystick(this); + + InitJoysticks(joysticks); + //KempstonDevice = new KempstonJoystick(this); TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs index a1f3c8d019..72c546da51 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs @@ -20,8 +20,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) - : base(spectrum, cpu, borderType, files) + public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + : base(spectrum, cpu, borderType, files, joysticks) { } 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 556a5f1d23..18582daf1a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -26,7 +26,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) { - return (byte)KempstonDevice.JoyLine; + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; } else if (lowBitReset) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 403c8495a5..796f06e1db 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) + public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) { Spectrum = spectrum; CPU = cpu; @@ -38,7 +38,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new Keyboard48(this); - KempstonDevice = new KempstonJoystick(this); + + InitJoysticks(joysticks); + //KempstonDevice = new KempstonJoystick(this); TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index b89e07957c..49c6cd686b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -16,8 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) - : base(spectrum, cpu, borderType, files) + public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + : base(spectrum, cpu, borderType, files, joysticks) { } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs index e04eedc217..c51dbe60a3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs @@ -68,11 +68,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum }; var nonMatrix = new List(); + /* foreach (var key in ZXSpectrum.ZXSpectrumControllerDefinition.BoolButtons) { if (!KeyboardMatrix.Any(s => s == key)) nonMatrix.Add(key); } + */ NonMatrixKeys = nonMatrix.ToArray(); LineStatus = new byte[8]; 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 3d891fb9e7..f3f40faf8e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -26,7 +26,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) { - return (byte)KempstonDevice.JoyLine; + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; } else if (lowBitReset) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index a9e2bc5959..4aedb37344 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// /// - public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files) + public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) { Spectrum = spectrum; CPU = cpu; @@ -29,7 +29,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new Keyboard48(this); - KempstonDevice = new KempstonJoystick(this); + + InitJoysticks(joysticks); + + //KempstonDevice = new KempstonJoystick(this); TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index 8712e2d425..de59da486e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -1,6 +1,10 @@ - -using BizHawk.Emulation.Common; +using System; using System.Collections.Generic; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.ReflectionExtensions; +using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -17,16 +21,40 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum definition.Name = "ZXSpectrum Controller"; // joysticks - List joys = new List + List joys1 = new List { - // Kempston Joystick (P1) + // P1 Joystick "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", }; - foreach (var s in joys) + foreach (var s in joys1) { definition.BoolButtons.Add(s); - definition.CategoryLabels[s] = "Kempton Joystick"; + definition.CategoryLabels[s] = "Joystick 1"; + } + + List joys2 = new List + { + // P2 Joystick + "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button", + }; + + foreach (var s in joys2) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Joystick 2"; + } + + List joys3 = new List + { + // P3 Joystick + "P3 Up", "P3 Down", "P3 Left", "P3 Right", "P3 Button", + }; + + foreach (var s in joys3) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Joystick 3"; } // keyboard @@ -71,6 +99,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + + /* /// /// Controller mapping includes all keyboard keys from the following models: @@ -101,4 +131,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ } + + /// + /// The possible joystick types + /// + public enum JoystickType + { + None, + Kempston, + SinclairPORT1, + SinclairPORT2, + Cursor + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 182cedd7a4..a55807663e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -85,7 +85,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DisplayName("Tape Load Speed")] [Description("Select how fast the spectrum loads the game from tape")] [DefaultValue(TapeLoadSpeed.Accurate)] - public TapeLoadSpeed TapeLoadSpeed { get; set; } + public TapeLoadSpeed TapeLoadSpeed { get; set; } + + [DisplayName("Joystick 1")] + [Description("The emulated joystick assigned to P1 (SHOULD BE UNIQUE TYPE!)")] + [DefaultValue(JoystickType.Kempston)] + public JoystickType JoystickType1 { get; set; } + + [DisplayName("Joystick 2")] + [Description("The emulated joystick assigned to P2 (SHOULD BE UNIQUE TYPE!)")] + [DefaultValue(JoystickType.SinclairPORT1)] + public JoystickType JoystickType2 { get; set; } + + [DisplayName("Joystick 3")] + [Description("The emulated joystick assigned to P3 (SHOULD BE UNIQUE TYPE!)")] + [DefaultValue(JoystickType.SinclairPORT2)] + public JoystickType JoystickType3 { get; set; } + public ZXSpectrumSyncSettings Clone() { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index df56791f0c..840251b274 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -39,27 +39,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //_file = file; _files = files?.ToList() ?? new List(); + List joysticks = new List(); + joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType1); + joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType2); + joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType3); + switch (SyncSettings.MachineType) { case MachineType.ZXSpectrum16: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); + Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; case MachineType.ZXSpectrum48: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); + Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; case MachineType.ZXSpectrum128: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); + Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; case MachineType.ZXSpectrum128Plus2: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); + Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; case MachineType.ZXSpectrum128Plus3: ControllerDefinition = ZXSpectrumControllerDefinition; - Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files); + Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; default: throw new InvalidOperationException("Machine not yet emulated"); @@ -101,7 +106,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private readonly Z80A _cpu; private readonly TraceBuffer _tracer; public IController _controller; - private SpectrumBase _machine; + public SpectrumBase _machine; private List _gameInfo; @@ -112,7 +117,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //private byte[] _file; private readonly List _files; - public bool DiagRom = false; + public bool DiagRom = true; private byte[] GetFirmware(int length, params string[] names) { @@ -158,37 +163,37 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } - private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, List files) + private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, List files, List joys) { // setup the emulated model based on the MachineType switch (machineType) { case MachineType.ZXSpectrum16: - _machine = new ZX16(this, _cpu, borderType, files); + _machine = new ZX16(this, _cpu, borderType, files, joys); var _systemRom16 = GetFirmware(0x4000, "48ROM"); var romData16 = RomData.InitROM(machineType, _systemRom16); _machine.InitROM(romData16); break; case MachineType.ZXSpectrum48: - _machine = new ZX48(this, _cpu, borderType, files); + _machine = new ZX48(this, _cpu, borderType, files, joys); var _systemRom = GetFirmware(0x4000, "48ROM"); var romData = RomData.InitROM(machineType, _systemRom); _machine.InitROM(romData); break; case MachineType.ZXSpectrum128: - _machine = new ZX128(this, _cpu, borderType, files); + _machine = new ZX128(this, _cpu, borderType, files, joys); var _systemRom128 = GetFirmware(0x8000, "128ROM"); var romData128 = RomData.InitROM(machineType, _systemRom128); _machine.InitROM(romData128); break; case MachineType.ZXSpectrum128Plus2: - _machine = new ZX128Plus2(this, _cpu, borderType, files); + _machine = new ZX128Plus2(this, _cpu, borderType, files, joys); var _systemRomP2 = GetFirmware(0x8000, "PLUS2ROM"); var romDataP2 = RomData.InitROM(machineType, _systemRomP2); _machine.InitROM(romDataP2); break; case MachineType.ZXSpectrum128Plus3: - _machine = new ZX128Plus3(this, _cpu, borderType, files); + _machine = new ZX128Plus3(this, _cpu, borderType, files, joys); var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); var romDataP3 = RomData.InitROM(machineType, _systemRomP3); _machine.InitROM(romDataP3); From f8e1174aadeb911d8f0c039039bd10b4a2c8c0b5 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 16:01:40 +0000 Subject: [PATCH 055/339] ControllerConfiguration form now shows which Joystick type is assigned to each input --- .../Hardware/Input/CursorJoystick.cs | 2 +- .../Hardware/Input/NullJoystick.cs | 2 +- .../Hardware/Input/SinclairJoystick1.cs | 2 +- .../Hardware/Input/SinclairJoystick2.cs | 2 +- .../SinclairSpectrum/Machine/SpectrumBase.Input.cs | 6 +++--- .../SinclairSpectrum/ZXSpectrum.Controllers.cs | 14 +++++++------- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 4 ++-- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs index bda58ec7eb..edd54dd9b1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs @@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region IJoystick - public JoystickType JoyType => JoystickType.SinclairPORT1; + public JoystickType JoyType => JoystickType.Cursor; public string[] ButtonCollection { get; set; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs index a8773d0625..ba2d3ea467 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs @@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region IJoystick - public JoystickType JoyType => JoystickType.None; + public JoystickType JoyType => JoystickType.NULL; public string[] ButtonCollection { get; set; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs index 569f25b80d..a3789b9e5d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs @@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region IJoystick - public JoystickType JoyType => JoystickType.SinclairPORT1; + public JoystickType JoyType => JoystickType.SinclairLEFT; public string[] ButtonCollection { get; set; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs index 160ca0ea39..82d9fd9857 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs @@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region IJoystick - public JoystickType JoyType => JoystickType.SinclairPORT1; + public JoystickType JoyType => JoystickType.SinclairRIGHT; public string[] ButtonCollection { get; set; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 74ea1f645c..8a30b68040 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -186,11 +186,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return new KempstonJoystick(this, playerNumber); case JoystickType.Cursor: return new CursorJoystick(this, playerNumber); - case JoystickType.SinclairPORT1: + case JoystickType.SinclairLEFT: return new SinclairJoystick1(this, playerNumber); - case JoystickType.SinclairPORT2: + case JoystickType.SinclairRIGHT: return new SinclairJoystick2(this, playerNumber); - case JoystickType.None: + case JoystickType.NULL: return new NullJoystick(this, playerNumber); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index de59da486e..a59f03de9e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -13,7 +13,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The one ZX Hawk ControllerDefinition /// - public static ControllerDefinition ZXSpectrumControllerDefinition + public ControllerDefinition ZXSpectrumControllerDefinition { get { @@ -30,7 +30,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum foreach (var s in joys1) { definition.BoolButtons.Add(s); - definition.CategoryLabels[s] = "Joystick 1"; + definition.CategoryLabels[s] = "J1 (" + ((ZXSpectrumSyncSettings)SyncSettings as ZXSpectrumSyncSettings).JoystickType1.ToString() + ")"; } List joys2 = new List @@ -42,7 +42,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum foreach (var s in joys2) { definition.BoolButtons.Add(s); - definition.CategoryLabels[s] = "Joystick 2"; + definition.CategoryLabels[s] = "J2 (" + ((ZXSpectrumSyncSettings)SyncSettings as ZXSpectrumSyncSettings).JoystickType2.ToString() + ")"; } List joys3 = new List @@ -54,7 +54,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum foreach (var s in joys3) { definition.BoolButtons.Add(s); - definition.CategoryLabels[s] = "Joystick 3"; + definition.CategoryLabels[s] = "J3 (" + ((ZXSpectrumSyncSettings)SyncSettings as ZXSpectrumSyncSettings).JoystickType3.ToString() + ")"; } // keyboard @@ -137,10 +137,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public enum JoystickType { - None, + NULL, Kempston, - SinclairPORT1, - SinclairPORT2, + SinclairLEFT, + SinclairRIGHT, Cursor } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index a55807663e..8ad6ab5b1e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -94,12 +94,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DisplayName("Joystick 2")] [Description("The emulated joystick assigned to P2 (SHOULD BE UNIQUE TYPE!)")] - [DefaultValue(JoystickType.SinclairPORT1)] + [DefaultValue(JoystickType.SinclairLEFT)] public JoystickType JoystickType2 { get; set; } [DisplayName("Joystick 3")] [Description("The emulated joystick assigned to P3 (SHOULD BE UNIQUE TYPE!)")] - [DefaultValue(JoystickType.SinclairPORT2)] + [DefaultValue(JoystickType.SinclairRIGHT)] public JoystickType JoystickType3 { get; set; } From aa1cfde69b7256be5a53bccf5a1f142c2bcc7fe3 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 16:05:50 +0000 Subject: [PATCH 056/339] Disabled replacement DiagRom bios (vblank tests now appear to be working) --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 840251b274..fe9520b245 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -117,7 +117,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //private byte[] _file; private readonly List _files; - public bool DiagRom = true; + public bool DiagRom = false; private byte[] GetFirmware(int length, params string[] names) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 7b2345b74c..2f21a9ae56 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -11,7 +11,8 @@ At the moment this is very experimental and is still actively being worked on. * Beeper/Buzzer output (implementing ISoundProvider) * AY-3-8912 sound chip implementation * Keyboard input (implementing IInputPollable) -* Kempston joystick (mapped to J1 currently) +* Default keyboard keymappings +* Kempston, Cursor and Sinclair (Left & Right) joysticks emulated * Tape device that will load spectrum games in realtime (*.tzx and *.tap) * Most tape protection/loading schemes that I've tested are currently working (see caveat below) * IStatable @@ -26,7 +27,6 @@ At the moment this is very experimental and is still actively being worked on. ### Not working * IDebuggable (probably IMemoryDomains is setup incorrectly) * ZX Spectrum Plus3 emulation -* Default keyboard keymappings (you have to configure yourself in the core controller settings) ### Known bugs * Audible 'popping' from the emulated buzzer after a load state operation (maybe this is a normal thing) From 35bb1d0c93c34e530db288969371e513462acd05 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 16:40:25 +0000 Subject: [PATCH 057/339] Fixed default keys and also fixed a major syncsettings snafu --- Assets/defctrl.json | 4 +++- .../BizHawk.Emulation.Cores.csproj | 2 +- .../Input/StandardKeyboard.cs} | 10 +++++----- .../Machine/SpectrumBase.Input.cs | 2 -- .../Machine/ZXSpectrum128K/ZX128.cs | 2 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.cs | 2 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 15 ++++++++++----- 8 files changed, 22 insertions(+), 17 deletions(-) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/{Machine/ZXSpectrum48K/ZX48.Keyboard.cs => Hardware/Input/StandardKeyboard.cs} (98%) diff --git a/Assets/defctrl.json b/Assets/defctrl.json index 631e0af74b..c2bb94b42b 100644 --- a/Assets/defctrl.json +++ b/Assets/defctrl.json @@ -530,7 +530,9 @@ "Record Tape": "", "Key Quote": "Shift+D2", "Insert Next Tape": "F6", - "Insert Previous Tape": "F5" + "Insert Previous Tape": "F5", + "Next Tape Block": "F8", + "Prev Tape Block": "F7" }, "Intellivision Controller": { "P1 Up": "UpArrow, J1 POV1U, X1 DpadUp, X1 LStickUp", diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index e9bc4d2bac..b81298264b 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1381,7 +1381,7 @@
- + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs similarity index 98% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs index c51dbe60a3..2df804efec 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Keyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs @@ -10,7 +10,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The 48k keyboard device /// - public class Keyboard48 : IKeyboard + public class StandardKeyboard : IKeyboard { public SpectrumBase _machine { get; set; } private byte[] LineStatus; @@ -43,7 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum set { _nonMatrixKeys = value; } } - public Keyboard48(SpectrumBase machine) + public StandardKeyboard(SpectrumBase machine) { _machine = machine; @@ -68,13 +68,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum }; var nonMatrix = new List(); - /* - foreach (var key in ZXSpectrum.ZXSpectrumControllerDefinition.BoolButtons) + + foreach (var key in _machine.Spectrum.ZXSpectrumControllerDefinition.BoolButtons) { if (!KeyboardMatrix.Any(s => s == key)) nonMatrix.Add(key); } - */ + NonMatrixKeys = nonMatrix.ToArray(); LineStatus = new byte[8]; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 8a30b68040..7aee74e07a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -40,7 +40,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // non matrix keys - /* foreach (string k in KeyboardDevice.NonMatrixKeys) { if (!k.StartsWith("Key")) @@ -50,7 +49,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice.SetKeyStatus(k, currState); } - */ // J1 foreach (string j in JoystickCollection[0].ButtonCollection) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index eaab39cb1d..1eb5b96a14 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice = new AY38912(); AYDevice.Init(44100, ULADevice.FrameLength); - KeyboardDevice = new Keyboard48(this); + KeyboardDevice = new StandardKeyboard(this); InitJoysticks(joysticks); //KempstonDevice = new KempstonJoystick(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 796f06e1db..1bc487dd78 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice = new AY38912(); AYDevice.Init(44100, ULADevice.FrameLength); - KeyboardDevice = new Keyboard48(this); + KeyboardDevice = new StandardKeyboard(this); InitJoysticks(joysticks); //KempstonDevice = new KempstonJoystick(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 4aedb37344..3affcf59b1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - KeyboardDevice = new Keyboard48(this); + KeyboardDevice = new StandardKeyboard(this); InitJoysticks(joysticks); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index fe9520b245..3491c4a60c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -20,10 +20,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { [CoreConstructor("ZXSpectrum")] public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) - { - PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); - PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); - + { var ser = new BasicServiceProvider(this); ServiceProvider = ser; InputCallbacks = new InputCallbackSystem(); @@ -39,7 +36,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //_file = file; _files = files?.ToList() ?? new List(); - List joysticks = new List(); + if (settings == null) + settings = new ZXSpectrumSettings(); + if (syncSettings == null) + syncSettings = new ZXSpectrumSyncSettings(); + + PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); + PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); + + List joysticks = new List(); joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType1); joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType2); joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType3); From 7a7b84f35c3a0bcd77299ea8a21914b3fca04a9e Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 17:04:12 +0000 Subject: [PATCH 058/339] Fixed MemoryCallbacks (i think) - now debugger opens without throwing an exception --- .../Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs | 4 +--- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs index 85083f3fcc..716c29ec95 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs @@ -8,7 +8,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public partial class ZXSpectrum //: IMemoryDomains { - private MemoryDomainList memoryDomains; + internal IMemoryDomains memoryDomains; private readonly Dictionary _byteArrayDomains = new Dictionary(); private bool _memoryDomainsInit = false; @@ -42,8 +42,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum (ServiceProvider as BasicServiceProvider).Register(memoryDomains); _memoryDomainsInit = true; - - } private void SyncAllByteArrayDomains() diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 3491c4a60c..b8d3749514 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -24,6 +24,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var ser = new BasicServiceProvider(this); ServiceProvider = ser; InputCallbacks = new InputCallbackSystem(); + MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); CoreComm = comm; From 36485bba8ab6ba7f3a356ee63c76856ece2d529d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 17:04:58 +0000 Subject: [PATCH 059/339] Updated readme --- BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 2f21a9ae56..621411f812 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -17,6 +17,7 @@ At the moment this is very experimental and is still actively being worked on. * Most tape protection/loading schemes that I've tested are currently working (see caveat below) * IStatable * ISettable core settings +* IDebuggable (for what it's worth) * Tape auto-loading routines (as a setting) ### Work in progress @@ -25,7 +26,6 @@ At the moment this is very experimental and is still actively being worked on. * TASStudio (need to verify that this works as it should) ### Not working -* IDebuggable (probably IMemoryDomains is setup incorrectly) * ZX Spectrum Plus3 emulation ### Known bugs From 198008a573b14400e591b029660a742486d802ee Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 17:57:13 +0000 Subject: [PATCH 060/339] LagFrame implementation --- .../Machine/SpectrumBase.Input.cs | 5 ++++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 5 ++++ .../Machine/ZXSpectrum128K/ZX128.Port.cs | 23 ++++++++++++++++++- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 20 ++++++++++++++++ .../Machine/ZXSpectrum48K/ZX48.Port.cs | 20 ++++++++++++++++ .../SinclairSpectrum/ZXSpectrum.IEmulator.cs | 7 ++++++ .../ZXSpectrum.IInputPollable.cs | 2 +- 7 files changed, 80 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 7aee74e07a..1cac046ea8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -204,6 +204,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { return JoystickCollection.Where(a => a.JoyType == type).FirstOrDefault(); } + + /// + /// Signs whether input read has been requested + /// + protected bool InputRead { get; set; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 4e0080b41f..2dc4ee6d81 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -140,6 +140,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ///
public virtual void ExecuteFrame() { + InputRead = false; + FrameCompleted = false; BuzzerDevice.StartFrame(); if (AYDevice != null) @@ -179,6 +181,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice.EndFrame(); FrameCompleted = true; + + // is this a lag frame? + Spectrum.IsLagFrame = !InputRead; } /// 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 2604d8187b..4b9cb123c7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -15,6 +15,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { + InputRead = true; + int result = 0xFF; // Check whether the low bit is reset @@ -29,6 +31,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; } else if (lowBitReset) { @@ -43,28 +47,45 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ if ((port & 0x8000) == 0) + { result &= KeyboardDevice.KeyLine[7]; + } if ((port & 0x4000) == 0) + { result &= KeyboardDevice.KeyLine[6]; + } if ((port & 0x2000) == 0) + { result &= KeyboardDevice.KeyLine[5]; + } if ((port & 0x1000) == 0) + { result &= KeyboardDevice.KeyLine[4]; - + } + if ((port & 0x800) == 0) + { result &= KeyboardDevice.KeyLine[3]; + } if ((port & 0x400) == 0) + { result &= KeyboardDevice.KeyLine[2]; + } if ((port & 0x200) == 0) + { result &= KeyboardDevice.KeyLine[1]; + } if ((port & 0x100) == 0) + { result &= KeyboardDevice.KeyLine[0]; + } + result = result & 0x1f; //mask out lower 4 bits result = result | 0xa0; //set bit 5 & 7 to 1 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 18582daf1a..8218ce54df 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -15,6 +15,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { + InputRead = true; + int result = 0xFF; // Check whether the low bit is reset @@ -28,6 +30,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; } else if (lowBitReset) { @@ -42,28 +46,44 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ if ((port & 0x8000) == 0) + { result &= KeyboardDevice.KeyLine[7]; + } if ((port & 0x4000) == 0) + { result &= KeyboardDevice.KeyLine[6]; + } if ((port & 0x2000) == 0) + { result &= KeyboardDevice.KeyLine[5]; + } if ((port & 0x1000) == 0) + { result &= KeyboardDevice.KeyLine[4]; + } if ((port & 0x800) == 0) + { result &= KeyboardDevice.KeyLine[3]; + } if ((port & 0x400) == 0) + { result &= KeyboardDevice.KeyLine[2]; + } if ((port & 0x200) == 0) + { result &= KeyboardDevice.KeyLine[1]; + } if ((port & 0x100) == 0) + { result &= KeyboardDevice.KeyLine[0]; + } result = result & 0x1f; //mask out lower 4 bits result = result | 0xa0; //set bit 5 & 7 to 1 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 f3f40faf8e..873fa6a926 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -15,6 +15,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { + InputRead = true; + int result = 0xFF; // Check whether the low bit is reset @@ -28,6 +30,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; } else if (lowBitReset) { @@ -44,28 +48,44 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ if ((port & 0x8000) == 0) + { result &= KeyboardDevice.KeyLine[7]; + } if ((port & 0x4000) == 0) + { result &= KeyboardDevice.KeyLine[6]; + } if ((port & 0x2000) == 0) + { result &= KeyboardDevice.KeyLine[5]; + } if ((port & 0x1000) == 0) + { result &= KeyboardDevice.KeyLine[4]; + } if ((port & 0x800) == 0) + { result &= KeyboardDevice.KeyLine[3]; + } if ((port & 0x400) == 0) + { result &= KeyboardDevice.KeyLine[2]; + } if ((port & 0x200) == 0) + { result &= KeyboardDevice.KeyLine[1]; + } if ((port & 0x100) == 0) + { result &= KeyboardDevice.KeyLine[0]; + } result = result & 0x1f; //mask out lower 4 bits result = result | 0xa0; //set bit 5 & 7 to 1 diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs index 8b10763146..c318876694 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -13,6 +13,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _controller = controller; + _isLag = true; + if (_tracer.Enabled) { _cpu.TraceCallback = s => _tracer.Put(s); @@ -23,6 +25,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } _machine.ExecuteFrame(); + + if (_isLag) + { + _lagCount++; + } } public int Frame => _machine.FrameCount; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs index 70a3b15712..da34d34c66 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs @@ -21,6 +21,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public IInputCallbackSystem InputCallbacks { get; } private int _lagCount = 0; - private bool _isLag = true; + private bool _isLag = false; } } From 34663445f875af454a1fe9363e76ee5d2b5b1746 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 6 Mar 2018 18:03:55 +0000 Subject: [PATCH 061/339] LagFrame syncstate --- .../SinclairSpectrum/Machine/SpectrumBase.Input.cs | 8 +++++++- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 1cac046ea8..7b3e9a18b4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -208,7 +208,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Signs whether input read has been requested /// - protected bool InputRead { get; set; } + private bool inputRead; + public bool InputRead + { + get { return inputRead; } + set { inputRead = value; } + } + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 2dc4ee6d81..28df887d46 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -211,6 +211,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("OverFlow", ref OverFlow); ser.Sync("FrameCount", ref FrameCount); ser.Sync("_frameCycles", ref _frameCycles); + ser.Sync("inputRead", ref inputRead); ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick); ser.Sync("LastULAOutByte", ref LastULAOutByte); ser.Sync("ROM0", ref ROM0, false); From fbbd75b3ab7cb4d0b0e34074fe8cd30aa80aedac Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 7 Mar 2018 12:21:36 +0000 Subject: [PATCH 062/339] Implemented DeterministicEmulation as a syncsetting and if this is set to false, audio and video devices respect the render and renderSound IEmulator bools --- .../Hardware/SoundOuput/AY38912.cs | 9 ++-- .../Hardware/SoundOuput/Buzzer.cs | 3 ++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 52 +++++++++---------- .../SinclairSpectrum/Machine/ULABase.cs | 11 +++- .../SinclairSpectrum/ZXSpectrum.IEmulator.cs | 19 ++++++- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 5 ++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 + .../Computers/SinclairSpectrum/readme.md | 1 + 8 files changed, 69 insertions(+), 33 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs index 8dcde1e0ee..7443c43dd3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs @@ -60,6 +60,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _tStatesPerSample = 79; _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; _AYCyclesPerFrame = _tStatesPerFrame / AY_SAMPLE_RATE; + + _samples = new short[_samplesPerFrame * 2]; + _nsamp = _samplesPerFrame; } #endregion @@ -110,11 +113,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // the stereo _samples buffer should already have been processed as a part of // ISoundProvider at the end of the last frame - _samples = new short[_samplesPerFrame * 2]; - _nsamp = _samplesPerFrame; + //_samples = new short[_samplesPerFrame * 2]; + //_nsamp = _samplesPerFrame; _sampleCounter = 0; - Init(44100, _tStatesPerFrame); + //Init(44100, _tStatesPerFrame); } public void EndFrame() diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index d751189fae..04e5b53222 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -123,6 +123,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void ProcessPulseValue(bool fromTape, bool earPulse) { + if (!_machine._renderSound) + return; + if (!fromTape && _tapeMode) { // tape mode is active but the pulse value came from an OUT instruction diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 28df887d46..444a22a46c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -76,25 +76,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Holds the currently selected joysticks /// public virtual IJoystick[] JoystickCollection { get; set; } - - /* - /// - /// Joystick device 1 - /// - public virtual IJoystick Joystick1 { get; set; } - - /// - /// Joystick device 2 - /// - public virtual IJoystick Joystick2 { get; set; } - - /// - /// Joystick device 3 - /// - public virtual IJoystick Joystick3 { get; set; } - - */ - + /// /// Signs whether the frame has ended /// @@ -125,6 +107,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ///
public virtual int CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick; + /// + /// Non-Deterministic bools + /// + public bool _render; + public bool _renderSound; + /// /// Mask constants /// @@ -138,14 +126,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Executes a single frame /// - public virtual void ExecuteFrame() + public virtual void ExecuteFrame(bool render, bool renderSound) { InputRead = false; + _render = render; + _renderSound = renderSound; FrameCompleted = false; - BuzzerDevice.StartFrame(); - if (AYDevice != null) - AYDevice.StartFrame(); + + if (_renderSound) + { + BuzzerDevice.StartFrame(); + if (AYDevice != null) + AYDevice.StartFrame(); + } PollInput(); @@ -158,18 +152,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CPU.ExecuteOne(); // update AY - if (AYDevice != null) - AYDevice.UpdateSound(CurrentFrameCycle); + if (_renderSound) + { + if (AYDevice != null) + AYDevice.UpdateSound(CurrentFrameCycle); + } } // we have reached the end of a frame LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; // paint the buffer if needed - if (ULADevice.needsPaint) + if (ULADevice.needsPaint && _render) ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); - BuzzerDevice.EndFrame(); + if (_renderSound) + BuzzerDevice.EndFrame(); //TapeDevice.CPUFrameCompleted(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 1604006525..e10bdd16c0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -315,7 +315,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine.CPU.FlagI = true; // Signal the start of ULA processing - ULAUpdateStart(); + if (_machine._render) + ULAUpdateStart(); + + CalcFlashCounter(); } #endregion @@ -383,7 +386,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum screenByteCtr = DisplayStart; lastTState = actualULAStart; needsPaint = true; + } + /// + /// Flash processing + /// + public void CalcFlashCounter() + { flashCounter++; if (flashCounter > 15) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs index c318876694..33bcc68276 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -13,6 +13,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _controller = controller; + bool ren = render; + bool renSound = renderSound; + + if (DeterministicEmulation) + { + ren = true; + renSound = true; + } + _isLag = true; if (_tracer.Enabled) @@ -24,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.TraceCallback = null; } - _machine.ExecuteFrame(); + _machine.ExecuteFrame(ren, renSound); if (_isLag) { @@ -36,7 +45,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public string SystemId => "ZXSpectrum"; - public bool DeterministicEmulation => true; + private bool deterministicEmulation; + public bool DeterministicEmulation + { + get { return deterministicEmulation; } + } + + //public bool DeterministicEmulation => true; public void ResetCounters() { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 8ad6ab5b1e..059a2fcaa1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -72,6 +72,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class ZXSpectrumSyncSettings { + [DisplayName("Deterministic Emulation")] + [Description("If true, the core agrees to behave in a completely deterministic manner")] + [DefaultValue(true)] + public bool DeterministicEmulation { get; set; } + [DisplayName("Spectrum model")] [Description("The model of spectrum to be emulated")] [DefaultValue(MachineType.ZXSpectrum48)] diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index b8d3749514..4656ba6fa2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -50,6 +50,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType2); joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType3); + deterministicEmulation = ((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).DeterministicEmulation; + switch (SyncSettings.MachineType) { case MachineType.ZXSpectrum16: diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 621411f812..6cc05397c2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -18,6 +18,7 @@ At the moment this is very experimental and is still actively being worked on. * IStatable * ISettable core settings * IDebuggable (for what it's worth) +* DeterministicEmulation as a SyncSetting * Tape auto-loading routines (as a setting) ### Work in progress From 74423041f3269cce569707b18824d1299130de04 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 7 Mar 2018 17:40:15 +0000 Subject: [PATCH 063/339] Removed DCFilter --- .../BizHawk.Emulation.Cores.csproj | 1 + .../Hardware/Datacorder/DatacorderDevice.cs | 4 +-- .../Hardware/SoundOuput/Buzzer.cs | 30 +++++++++++++++++-- .../Hardware/SoundOuput/IBeeperDevice.cs | 24 +++++++++++++++ .../Machine/SpectrumBase.Port.cs | 9 ++++++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 +- .../SinclairSpectrum/Machine/ULABase.cs | 2 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 2 +- .../Machine/ZXSpectrum128K/ZX128.cs | 1 - .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 2 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 1 - .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 11 +++++++ .../Machine/ZXSpectrum48K/ZX48.cs | 4 --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 6 ++-- 15 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/IBeeperDevice.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index b81298264b..10d5eb3aab 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -266,6 +266,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 4bdc9aca30..0096c9af86 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -18,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private SpectrumBase _machine { get; set; } private Z80A _cpu { get; set; } - private Buzzer _buzzer { get; set; } + private IBeeperDevice _buzzer { get; set; } /// /// Default constructor @@ -369,8 +369,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool GetEarBit(long cpuCycle) { - - // decide how many cycles worth of data we are capturing long cycles = cpuCycle - _lastCycle; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index 04e5b53222..0e98c67c29 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -15,7 +15,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// For the purposes of emulation this devices is locked to a frame /// a list of Pulses is built up over the course of the frame and outputted at the end of the frame /// - public class Buzzer : ISoundProvider + public class Buzzer : ISoundProvider, IBeeperDevice { /// /// Supplied values are right for 48K spectrum @@ -99,6 +99,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /* + // set the tstatesperframe + _tStatesPerFrame = tStatesPerFrame; + + // calculate actual refresh rate + double refresh = (double)_machine.ULADevice.ClockSpeed / (double)_tStatesPerFrame; + + // how many samples per frame are expected by ISoundProvider (at 44.1KHz) + _samplesPerFrame = 880;// (int)((double)sampleRate / (double)refresh); + + // set the sample rate + _sampleRate = sampleRate; + + // calculate samples per frame (what ISoundProvider will be expecting at 44100) + //_samplesPerFrame = (int)((double)_tStatesPerFrame / (double)refresh); + + // calculate tstates per sameple + _tStatesPerSample = 79;// _tStatesPerFrame / _samplesPerFrame; + + /* + + + + + // get divisors var divs = from a in Enumerable.Range(2, _tStatesPerFrame / 2) where _tStatesPerFrame % a == 0 @@ -109,8 +133,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // get _samplesPerFrame _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; - */ - + + */ Pulses = new List(1000); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/IBeeperDevice.cs new file mode 100644 index 0000000000..7367e3948f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/IBeeperDevice.cs @@ -0,0 +1,24 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public interface IBeeperDevice + { + void Init(int sampleRate, int tStatesPerFrame); + + void ProcessPulseValue(bool fromTape, bool earPulse); + + void StartFrame(); + + void EndFrame(); + + void SetTapeMode(bool tapeMode); + + void SyncState(Serializer ser); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index a508f9fa8e..d52ac067a2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -30,6 +30,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// public abstract void WritePort(ushort port, byte value); + + /// + /// Increments the CPU totalCycles counter by the tStates value specified + /// + /// + public virtual void PortContention(int tStates) + { + CPU.TotalExecutedCycles += tStates; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 444a22a46c..67f26496c0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -55,7 +55,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The spectrum buzzer/beeper /// - public Buzzer BuzzerDevice { get; set; } + public IBeeperDevice BuzzerDevice { get; set; } /// /// Device representing the AY-3-8912 chip found in the 128k and up spectrums diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index e10bdd16c0..d4e5144b35 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -445,7 +445,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } //the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna - elapsedTStates = (_tstates + 1 - lastTState); + elapsedTStates = (_tstates + 1 - lastTState) - 1; //It takes 4 tstates to write 1 byte. Or, 2 pixels per t-state. diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index f1a63b0b24..bb3f42ac31 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -170,7 +170,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // update ULA screen buffer if necessary - if ((addr & 49152) == 16384) + if ((addr & 49152) == 16384 && _render) ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 1eb5b96a14..eb1b25aa19 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -40,7 +40,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new StandardKeyboard(this); InitJoysticks(joysticks); - //KempstonDevice = new KempstonJoystick(this); TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index d236edc82e..a7044ca032 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -310,7 +310,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // update ULA screen buffer if necessary - if ((addr & 49152) == 16384) + if ((addr & 49152) == 16384 && _render) ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 1bc487dd78..9fb6b4db95 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -40,7 +40,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice = new StandardKeyboard(this); InitJoysticks(joysticks); - //KempstonDevice = new KempstonJoystick(this); TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); 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 0a1efe104e..5812c898c4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -65,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bank[index] = value; // update ULA screen buffer if necessary - if ((addr & 49152) == 16384) + if ((addr & 49152) == 16384 && _render) ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } 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 873fa6a926..2c7db07cbd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -17,6 +17,11 @@ 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); + int result = 0xFF; // Check whether the low bit is reset @@ -171,6 +176,12 @@ 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); + + // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x01) == 0; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 3affcf59b1..aaf4ffdebd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -32,14 +32,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InitJoysticks(joysticks); - //KempstonDevice = new KempstonJoystick(this); - TapeDevice = new DatacorderDevice(); TapeDevice.Init(this); InitializeMedia(files); - - //TapeDevice.LoadTape(file); } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 4656ba6fa2..2af8fbe390 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -94,12 +94,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Register(_cpu); ser.Register(_machine.ULADevice); - SoundMixer = new SoundProviderMixer(_machine.BuzzerDevice); + SoundMixer = new SoundProviderMixer((ISoundProvider)_machine.BuzzerDevice); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - dcf = new DCFilter(SoundMixer, 256); - ser.Register(dcf); + //dcf = new DCFilter(SoundMixer, 256); + ser.Register(SoundMixer); From f121aedd6a26c1b958ea4767fbcf46abb59ebee0 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 8 Mar 2018 16:50:56 +0000 Subject: [PATCH 064/339] Added floating bus implementation to 128k/+2 and started looking at +3 emulation --- .../Machine/SpectrumBase.Memory.cs | 1 - .../SinclairSpectrum/Machine/SpectrumBase.cs | 46 ++++- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 30 ++- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 55 ++++- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 29 ++- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 193 ++++++++++++++---- .../SinclairSpectrum/ZXSpectrum.Util.cs | 16 ++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 17 +- 8 files changed, 325 insertions(+), 62 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 0d750bc64c..6ddbccedbc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -87,7 +87,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Sets up the ROM /// /// - /// public abstract void InitROM(RomData romData); /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 67f26496c0..9544610a3a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -12,26 +12,53 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public abstract partial class SpectrumBase { - // 128 and up only - //protected int ROMPaged = 0; - + /// + /// Index of the currently paged ROM + /// protected int ROMPaged; - - public int _ROMpaged + public virtual int _ROMpaged { get { return ROMPaged; } set { ROMPaged = value; } } - + /// + /// Signs that the shadow screen has been paged in + /// protected bool SHADOWPaged; + + /// + /// Index of the current RAM page + /// public int RAMPaged; + + /// + /// Signs that all paging is disabled + /// protected bool PagingDisabled; // +3/+2A only + + protected bool ROMhigh = false; + protected bool ROMlow = false; + + /// + /// Signs that the +2a/+3 special paging mode is activated + /// protected bool SpecialPagingMode; + + /// + /// Index of the current special paging config + /// protected int PagingConfiguration; + /// + /// Signs whether the disk motor is on or off + /// + protected bool DiskMotorState; + + protected bool PrinterPortStrobe; + /// /// The calling ZXSpectrum class (piped in via constructor) /// @@ -156,7 +183,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (AYDevice != null) AYDevice.UpdateSound(CurrentFrameCycle); - } + } + + if (SHADOWPaged) + { + + } } // we have reached the end of a frame diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index bb3f42ac31..c86a1e6ca4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -257,6 +257,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Memory.Add(9, RAM7); } + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + /// /// Sets up the ROM /// @@ -270,7 +297,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (int i = 0; i < 0x4000; i++) { ROM0[i] = RomData.RomBytes[i]; - ROM1[i] = RomData.RomBytes[i + 0x4000]; + if (RomData.RomBytes.Length > 0x4000) + ROM1[i] = RomData.RomBytes[i + 0x4000]; } } } 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 4b9cb123c7..434f4c7cfa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -17,6 +18,11 @@ 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); + int result = 0xFF; // Check whether the low bit is reset @@ -24,7 +30,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool lowBitReset = (port & 0x0001) == 0; ULADevice.Contend(port); - CPU.TotalExecutedCycles++; + //CPU.TotalExecutedCycles++; // Kempston Joystick if ((port & 0xe0) == 0 || (port & 0x20) == 0) @@ -143,7 +149,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Kempston Mouse - // if unused port the floating memory bus should be returned (still todo) + // if unused port the floating memory bus should be returned + + // Floating bus is read on the previous cycle + int _tStates = CurrentFrameCycle - 1; + + // if we are on the top or bottom border return 0xff + if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) + { + result = 0xff; + } + else + { + if (ULADevice.floatingBusTable[_tStates] < 0) + { + result = 0xff; + } + else + { + result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); + } + } } return (byte)result; @@ -156,38 +182,47 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + int currT = CPU.TotalExecutedCycles; // paging if (port == 0x7ffd) { + if (PagingDisabled) + return; + // Bits 0, 1, 2 select the RAM page var rp = value & 0x07; if (rp < 8) RAMPaged = rp; + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + // ROM page - if ((value & 0x10) != 0) + if (bits[4]) { - // 48k ROM + // 48k basic rom ROMPaged = 1; } else { + // 128k editor and menu system ROMPaged = 0; } - // Bit 5 signifies that paging is disabled until next reboot - if ((value & 0x20) != 0) - PagingDisabled = true; - - + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; return; } // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x01) == 0; + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; ULADevice.Contend(port); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index a7044ca032..15a091fccd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -133,7 +133,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - result = Memory[ROMPaged][addr % 0x4000]; + result = Memory[_ROMpaged][addr % 0x4000]; break; // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) @@ -407,6 +407,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Memory.Add(11, RAM7); } + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + /// /// Sets up the ROM /// 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 8218ce54df..199dda6515 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -158,12 +159,145 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x01) == 0; + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; ULADevice.Contend(port); + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (port == 0x7ffd) + { + if (!PagingDisabled) + { + // bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + + // portbit 4 is the LOW BIT of the ROM selection + ROMlow = bits[4]; + } + } + // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + else if (port == 0x1ffd) + { + if (!PagingDisabled) + { + if (bits[0]) + { + // special paging is not enabled - get the ROMpage high byte + ROMhigh = bits[2]; + + // set the special paging mode flag + SpecialPagingMode = false; + } + else + { + // special paging is enabled + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // set the special paging mode flag + SpecialPagingMode = true; + } + } + + // bit 3 controls the disk motor (1=on, 0=off) + DiskMotorState = bits[3]; + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + /* + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (!portBits[1] && !portBits[15] && portBits[14]) + { + // paging (skip if paging has been disabled - paging can then only happen after a machine hard reset) + if (!PagingDisabled) + { + // bit 0 specifies the paging mode + SpecialPagingMode = bits[0]; + + if (!SpecialPagingMode) + { + // we are in normal mode + // portbit 4 is the LOW BIT of the ROM selection + BitArray romHalfNibble = new BitArray(2); + romHalfNibble[0] = portBits[4]; + + // value bit 2 is the high bit of the ROM selection + romHalfNibble[1] = bits[2]; + + // value bit 1 is ignored in normal paging mode + + // set the ROMPage + ROMPaged = ZXSpectrum.GetIntFromBitArray(romHalfNibble); + + + + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + } + } + } + + // port 0x1ffd - special paging mode + // hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + if (!portBits[1] && portBits[12] && !portBits[13] && !portBits[14] && !portBits[15]) + { + if (!PagingDisabled && SpecialPagingMode) + { + // process special paging + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // last value should be saved at 0x5b67 (23399) - not sure if this is actually needed + WriteBus(0x5b67, value); + } + + // bit 3 controls the disk motor (1=on, 0=off) + DiskMotorState = bits[3]; + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + + */ + + // Only even addresses address the ULA if (lowBitReset) { @@ -206,6 +340,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.PortWrite(value); CPU.TotalExecutedCycles += 3; } + + /* + else { if ((port & 0xC002) == 0x4000) //Are bits 1 and 15 reset and bit 14 set? @@ -272,48 +409,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } } + */ } } + LastULAOutByte = value; - // paging - if (port == 0x7ffd) - { - if (PagingDisabled) - return; - - LastULAOutByte = value; - - - - - - - // Bits 0, 1, 2 select the RAM page - var rp = value & 0x07; - if (rp < 8) - RAMPaged = rp; - - // ROM page - if ((value & 0x10) != 0) - { - // 48k ROM - ROMPaged = 1; - } - else - { - ROMPaged = 0; - } - - // Bit 5 signifies that paging is disabled until next reboot - if ((value & 0x20) != 0) - PagingDisabled = true; - - - return; - } + + } + + /// + /// +3 and 2a overidden method + /// + public override int _ROMpaged + { + get + { + // calculate the ROMpage from the high and low bits + return ZXSpectrum.GetIntFromBitArray(new BitArray(new bool[] { ROMlow, ROMhigh })); + } + set { ROMPaged = value; } } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs index 893edbae8a..4cb9656858 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -248,5 +249,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// R3R5 = R3 | R5 } + + /// + /// Helper method that returns a single INT32 from a BitArray + /// + /// + /// + public static int GetIntFromBitArray(BitArray bitArray) + { + if (bitArray.Length > 32) + throw new ArgumentException("Argument length shall be at most 32 bits."); + + int[] array = new int[1]; + bitArray.CopyTo(array, 0); + return array[0]; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 2af8fbe390..c97a3c48d2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -77,6 +77,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum default: throw new InvalidOperationException("Machine not yet emulated"); } + + _cpu.MemoryCallbacks = MemoryCallbacks; @@ -125,13 +127,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //private byte[] _file; private readonly List _files; - public bool DiagRom = false; + public bool DiagRom = true; + + private List diagRoms = new List + { + @"\DiagROM.v28", + @"\zx-diagnostics\testrom.bin" + }; + private int diagIndex = 1; private byte[] GetFirmware(int length, params string[] names) { - if (DiagRom & File.Exists(Directory.GetCurrentDirectory() + @"\DiagROM.v28")) + if (DiagRom & File.Exists(Directory.GetCurrentDirectory() + diagRoms[diagIndex])) { - var rom = File.ReadAllBytes(Directory.GetCurrentDirectory() + @"\DiagROM.v28"); + var rom = File.ReadAllBytes(Directory.GetCurrentDirectory() + diagRoms[diagIndex]); return rom; } @@ -205,7 +214,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); var romDataP3 = RomData.InitROM(machineType, _systemRomP3); _machine.InitROM(romDataP3); - System.Windows.Forms.MessageBox.Show("+3 is not working at all yet :/"); + //System.Windows.Forms.MessageBox.Show("+3 is not working at all yet :/"); break; } } From cf8b632381eb915f238a90bf7b4ff09eca56a4fd Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 8 Mar 2018 16:51:25 +0000 Subject: [PATCH 065/339] Disabled new test DiagRom --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index c97a3c48d2..b07dfd6b9e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -127,7 +127,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //private byte[] _file; private readonly List _files; - public bool DiagRom = true; + public bool DiagRom = false; private List diagRoms = new List { From e6d43fa5d2b540ee155ddabb4beaeb59be2bc346 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 8 Mar 2018 19:33:14 +0000 Subject: [PATCH 066/339] Implemented +2a and +3 is now working (although disk drive not yet implemented so it just shows as +2a) --- .../BizHawk.Emulation.Cores.csproj | 11 +- .../SinclairSpectrum/Machine/MachineType.cs | 5 + .../SinclairSpectrum/Machine/SpectrumBase.cs | 5 - .../Machine/ZXSpectrum128K/ZX128.ULA.cs | 2 +- .../ZX128Plus2a.Memory.cs | 459 ++++++++++++++++++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 448 +++++++++++++++++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs | 196 ++++++++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 52 ++ .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 6 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 15 +- .../ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs | 196 ++++++++ .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.ULA.cs | 8 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 19 +- .../Properties/Resources.Designer.cs | 44 +- .../Properties/Resources.resx | 18 +- .../Resources/Spectrum3_V4-0_ROM0.bin.gz | Bin 0 -> 10000 bytes .../Resources/Spectrum3_V4-0_ROM1.bin.gz | Bin 0 -> 9963 bytes .../Resources/Spectrum3_V4-0_ROM2.bin.gz | Bin 0 -> 8472 bytes .../Resources/Spectrum3_V4-0_ROM3.bin.gz | Bin 0 -> 12477 bytes .../Resources/{plus3.rom.gz => plus2a.rom.gz} | Bin 21 files changed, 1463 insertions(+), 23 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs create mode 100644 BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM0.bin.gz create mode 100644 BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM1.bin.gz create mode 100644 BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM2.bin.gz create mode 100644 BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM3.bin.gz rename BizHawk.Emulation.Cores/Resources/{plus3.rom.gz => plus2a.rom.gz} (100%) diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 10d5eb3aab..c0fbe511fc 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -268,6 +268,11 @@ + + + + + @@ -1397,8 +1402,12 @@ - + + + + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs index 478b821a1a..cee9f524da 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -28,6 +28,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ///
ZXSpectrum128Plus2, + /// + /// Sinclair Spectrum 128 +2a model (same as the +3 just without disk drive) + /// + ZXSpectrum128Plus2a, + /// /// Sinclair Spectrum 128 +3 model /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 9544610a3a..553872b295 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -183,11 +183,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (AYDevice != null) AYDevice.UpdateSound(CurrentFrameCycle); - } - - if (SHADOWPaged) - { - } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs index 1ac1ccf326..6da4c3738f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { contentionStartPeriod = 14361; // + LateTiming; contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.Memory[1]; + screen = _machine.Memory[7]; screenByteCtr = DisplayStart; ULAByteCtr = 0; actualULAStart = 14366 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs new file mode 100644 index 0000000000..6505a5f81f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -0,0 +1,459 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128Plus2a : SpectrumBase + { + /* http://www.worldofspectrum.org/faq/reference/128kreference.htm + * + * Port 0x7ffd behaves in the almost exactly the same way as on the 128K/+2, with two exceptions: + + Bit 4 is now the low bit of the ROM selection. + The partial decoding used is now slightly different: the hardware will respond only to those port addresses with bit 1 reset, bit 14 set and bit 15 reset (as opposed to just bits 1 and 15 reset on the 128K/+2). + The extra paging features of the +2A/+3 are controlled by port 0x1ffd (again, partial decoding applies here: the hardware will respond to all port addresses with bit 1 reset, bit 12 set and bits 13, 14 and 15 reset). This port is also write-only, and its last value should be saved at 0x5b67 (23399). + + Port 0x1ffd responds as follows: + + Bit 0: Paging mode. 0=normal, 1=special + Bit 1: In normal mode, ignored. + Bit 2: In normal mode, high bit of ROM selection. The four ROMs are: + ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + Bit 3: Disk motor; 1=on, 0=off + Bit 4: Printer port strobe. + When special mode is selected, the memory map changes to one of four configurations specified in bits 1 and 2 of port 0x1ffd: + Bit 2 =0 Bit 2 =0 Bit 2 =1 Bit 2 =1 + Bit 1 =0 Bit 1 =1 Bit 1 =0 Bit 1 =1 + 0xffff +--------+ +--------+ +--------+ +--------+ + | Bank 3 | | Bank 7 | | Bank 3 | | Bank 3 | + | | | | | | | | + | | | | | | | | + | | | screen | | | | | + 0xc000 +--------+ +--------+ +--------+ +--------+ + | Bank 2 | | Bank 6 | | Bank 6 | | Bank 6 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x8000 +--------+ +--------+ +--------+ +--------+ + | Bank 1 | | Bank 5 | | Bank 5 | | Bank 7 | + | | | | | | | | + | | | | | | | | + | | | screen | | screen | | screen | + 0x4000 +--------+ +--------+ +--------+ +--------+ + | Bank 0 | | Bank 4 | | Bank 4 | | Bank 4 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x0000 +--------+ +--------+ +--------+ +--------+ + RAM banks 1,3,4 and 6 are used for the disc cache and RAMdisc, while Bank 7 contains editor scratchpads and +3DOS workspace. + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + byte result = 0xff; + + // special paging + if (SpecialPagingMode) + { + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + result = Memory[4][addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = Memory[8][addr % 0x4000]; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + result = Memory[5][addr % 0x4000]; + break; + case 1: + case 2: + result = Memory[9][addr % 0x4000]; + break; + case 3: + result = Memory[11][addr % 0x4000]; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + result = Memory[6][addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = Memory[10][addr % 0x4000]; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + result = Memory[7][addr % 0x4000]; + break; + case 1: + result = Memory[11][addr % 0x4000]; + break; + } + break; + } + } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + result = Memory[_ROMpaged][addr % 0x4000]; + break; + + // RAM 0x4000 (RAM5 - Bank5 always) + case 1: + result = Memory[9][addr % 0x4000]; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = Memory[6][addr % 0x4000]; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = Memory[4][addr % 0x4000]; + break; + case 1: + result = Memory[5][addr % 0x4000]; + break; + case 2: + result = Memory[6][addr % 0x4000]; + break; + case 3: + result = Memory[7][addr % 0x4000]; + break; + case 4: + result = Memory[8][addr % 0x4000]; + break; + case 5: + result = Memory[9][addr % 0x4000]; + break; + case 6: + result = Memory[10][addr % 0x4000]; + break; + case 7: + result = Memory[11][addr % 0x4000]; + break; + } + break; + default: + break; + } + } + + return result; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + + // special paging + if (SpecialPagingMode) + { + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + Memory[4][addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + Memory[8][addr % 0x4000] = value; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + Memory[5][addr % 0x4000] = value; + break; + case 1: + case 2: + Memory[9][addr % 0x4000] = value; + break; + case 3: + Memory[11][addr % 0x4000] = value; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + Memory[6][addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + Memory[10][addr % 0x4000] = value; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + Memory[7][addr % 0x4000] = value; + break; + case 1: + Memory[11][addr % 0x4000] = value; + break; + } + break; + } + } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + Memory[_ROMpaged][addr % 0x4000] = value; + break; + + // RAM 0x4000 (RAM5 - Bank5 only) + case 1: + Memory[9][addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + Memory[6][addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + Memory[4][addr % 0x4000] = value; + break; + case 1: + Memory[5][addr % 0x4000] = value; + break; + case 2: + Memory[6][addr % 0x4000] = value; + break; + case 3: + Memory[7][addr % 0x4000] = value; + break; + case 4: + Memory[8][addr % 0x4000] = value; + break; + case 5: + Memory[9][addr % 0x4000] = value; + break; + case 6: + Memory[10][addr % 0x4000] = value; + break; + case 7: + Memory[11][addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } + + // update ULA screen buffer if necessary + if ((addr & 49152) == 16384 && _render) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + + var data = ReadBus(addr); + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + // apply contention if necessary + if (ULADevice.IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + + WriteBus(addr, value); + } + + public override void ReInitMemory() + { + if (Memory.ContainsKey(0)) + Memory[0] = ROM0; + else + Memory.Add(0, ROM0); + + if (Memory.ContainsKey(1)) + Memory[1] = ROM1; + else + Memory.Add(1, ROM1); + + if (Memory.ContainsKey(2)) + Memory[2] = ROM2; + else + Memory.Add(2, ROM2); + + if (Memory.ContainsKey(3)) + Memory[3] = ROM3; + else + Memory.Add(3, ROM3); + + if (Memory.ContainsKey(4)) + Memory[4] = RAM0; + else + Memory.Add(4, RAM0); + + if (Memory.ContainsKey(5)) + Memory[5] = RAM1; + else + Memory.Add(5, RAM1); + + if (Memory.ContainsKey(6)) + Memory[6] = RAM2; + else + Memory.Add(6, RAM2); + + if (Memory.ContainsKey(7)) + Memory[7] = RAM3; + else + Memory.Add(7, RAM3); + + if (Memory.ContainsKey(8)) + Memory[8] = RAM4; + else + Memory.Add(8, RAM4); + + if (Memory.ContainsKey(9)) + Memory[9] = RAM5; + else + Memory.Add(9, RAM5); + + if (Memory.ContainsKey(10)) + Memory[10] = RAM6; + else + Memory.Add(10, RAM6); + + if (Memory.ContainsKey(11)) + Memory[11] = RAM7; + else + Memory.Add(11, RAM7); + } + + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // +3 uses ROM0, ROM1, ROM2 & ROM3 + /* ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + */ + Stream stream = new MemoryStream(RomData.RomBytes); + stream.Read(ROM0, 0, 16384); + stream.Read(ROM1, 0, 16384); + stream.Read(ROM2, 0, 16384); + stream.Read(ROM3, 0, 16384); + stream.Dispose(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs new file mode 100644 index 0000000000..07e22b4203 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -0,0 +1,448 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128Plus2a : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + InputRead = true; + + int result = 0xFF; + + // Check whether the low bit is reset + // 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) + { + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; + } + else if (lowBitReset) + { + // Even I/O address so get input + // The high byte indicates which half-row of keys is being polled + /* + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + */ + + if ((port & 0x8000) == 0) + { + result &= KeyboardDevice.KeyLine[7]; + } + + if ((port & 0x4000) == 0) + { + result &= KeyboardDevice.KeyLine[6]; + } + + if ((port & 0x2000) == 0) + { + result &= KeyboardDevice.KeyLine[5]; + } + + if ((port & 0x1000) == 0) + { + result &= KeyboardDevice.KeyLine[4]; + } + + if ((port & 0x800) == 0) + { + result &= KeyboardDevice.KeyLine[3]; + } + + if ((port & 0x400) == 0) + { + result &= KeyboardDevice.KeyLine[2]; + } + + if ((port & 0x200) == 0) + { + result &= KeyboardDevice.KeyLine[1]; + } + + if ((port & 0x100) == 0) + { + result &= KeyboardDevice.KeyLine[0]; + } + + result = result & 0x1f; //mask out lower 4 bits + result = result | 0xa0; //set bit 5 & 7 to 1 + + + if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) + { + if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else if ((LastULAOutByte & 0x10) == 0) + { + result &= ~(0x40); + } + else + { + result |= 0x40; + } + + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate - on +3/2a both FFFD and BFFD active AY + if ((port & 0xc002) == 0xc000) + { + result = (int)AYDevice.PortRead(); + } + else if ((port & 0xc002) == 0x8000) + { + result = (int)AYDevice.PortRead(); + } + + // Kempston Mouse + + /* + else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset? + { + //result = udpDrive.DiskStatusRead(); + + // disk drive is not yet implemented - return a max status byte for the menu to load + result = 255; + } + else if ((port & 0xF002) == 0x3000) + { + //result = udpDrive.DiskReadByte(); + result = 0; + } + + else if ((port & 0xF002) == 0x0) + { + if (PagingDisabled) + result = 0x1; + else + result = 0xff; + } + */ + + // if unused port the floating memory bus should be returned (still todo) + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + + // Check whether the low bit is reset + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; + + ULADevice.Contend(port); + + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (port == 0x7ffd) + { + if (!PagingDisabled) + { + // bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + + // portbit 4 is the LOW BIT of the ROM selection + ROMlow = bits[4]; + } + } + // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + else if (port == 0x1ffd) + { + if (!PagingDisabled) + { + if (!bits[0]) + { + // special paging is not enabled - get the ROMpage high byte + ROMhigh = bits[2]; + + // set the special paging mode flag + SpecialPagingMode = false; + } + else + { + // special paging is enabled + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // set the special paging mode flag + SpecialPagingMode = true; + } + } + + // bit 3 controls the disk motor (1=on, 0=off) + DiskMotorState = bits[3]; + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + /* + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (!portBits[1] && !portBits[15] && portBits[14]) + { + // paging (skip if paging has been disabled - paging can then only happen after a machine hard reset) + if (!PagingDisabled) + { + // bit 0 specifies the paging mode + SpecialPagingMode = bits[0]; + + if (!SpecialPagingMode) + { + // we are in normal mode + // portbit 4 is the LOW BIT of the ROM selection + BitArray romHalfNibble = new BitArray(2); + romHalfNibble[0] = portBits[4]; + + // value bit 2 is the high bit of the ROM selection + romHalfNibble[1] = bits[2]; + + // value bit 1 is ignored in normal paging mode + + // set the ROMPage + ROMPaged = ZXSpectrum.GetIntFromBitArray(romHalfNibble); + + + + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + } + } + } + + // port 0x1ffd - special paging mode + // hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + if (!portBits[1] && portBits[12] && !portBits[13] && !portBits[14] && !portBits[15]) + { + if (!PagingDisabled && SpecialPagingMode) + { + // process special paging + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // last value should be saved at 0x5b67 (23399) - not sure if this is actually needed + WriteBus(0x5b67, value); + } + + // bit 3 controls the disk motor (1=on, 0=off) + DiskMotorState = bits[3]; + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + + */ + + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + 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); + + ULADevice.borderColour = value & BORDER_BIT; + + // Buzzer + BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + + // Tape + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + + else + { + // AY Register activation + if ((port & 0xc002) == 0xc000) + { + var reg = value & 0x0f; + AYDevice.SelectedRegister = reg; + CPU.TotalExecutedCycles += 3; + } + else + { + if ((port & 0xC002) == 0x8000) + { + AYDevice.PortWrite(value); + CPU.TotalExecutedCycles += 3; + } + + /* + + else + { + if ((port & 0xC002) == 0x4000) //Are bits 1 and 15 reset and bit 14 set? + { + // memory paging activate + if (PagingDisabled) + return; + + // bit 5 handles paging disable (48k mode, persistent until next reboot) + if ((value & 0x20) != 0) + { + PagingDisabled = true; + } + + // shadow screen + if ((value & 0x08) != 0) + { + SHADOWPaged = true; + } + else + { + SHADOWPaged = false; + } + } + else + { + //Extra Memory Paging feature activate + if ((port & 0xF002) == 0x1000) //Is bit 12 set and bits 13,14,15 and 1 reset? + { + if (PagingDisabled) + return; + + // set disk motor state + //todo + + if ((value & 0x08) != 0) + { + //diskDriveState |= (1 << 4); + } + else + { + //diskDriveState &= ~(1 << 4); + } + + if ((value & 0x1) != 0) + { + // activate special paging mode + SpecialPagingMode = true; + PagingConfiguration = (value & 0x6 >> 1); + } + else + { + // normal paging mode + SpecialPagingMode = false; + } + } + else + { + // disk write port + if ((port & 0xF002) == 0x3000) //Is bit 12 set and bits 13,14,15 and 1 reset? + { + //udpDrive.DiskWriteByte((byte)(val & 0xff)); + } + } + } + } + */ + } + } + + LastULAOutByte = value; + + + + + } + + /// + /// +3 and 2a overidden method + /// + public override int _ROMpaged + { + get + { + // calculate the ROMpage from the high and low bits + var rp = ZXSpectrum.GetIntFromBitArray(new BitArray(new bool[] { ROMlow, ROMhigh })); + + if (rp != 0) + { + + } + + return rp; + } + set { ROMPaged = value; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs new file mode 100644 index 0000000000..35547ab931 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs @@ -0,0 +1,196 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class ULAPlus2a : ULABase + { + #region Construction + + public ULAPlus2a(SpectrumBase machine) + : base(machine) + { + InterruptPeriod = 36; + LongestOperationCycles = 64 + 2; + FrameLength = 70908; + ClockSpeed = 3546900; + + contentionTable = new byte[70930]; + floatingBusTable = new short[70930]; + for (int f = 0; f < 70930; f++) + floatingBusTable[f] = -1; + + CharRows = 24; + CharCols = 32; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + DisplayStart = 16384; + DisplayLength = 6144; + AttributeStart = 22528; + AttributeLength = 768; + borderColour = 7; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + TstatesPerScanline = 228; + TstateAtTop = BorderTopHeight * TstatesPerScanline; + TstateAtBottom = BorderBottomHeight * TstatesPerScanline; + tstateToDisp = new short[FrameLength]; + + ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border + + ScanLineWidth * ScreenHeight //border + main + border of 192 lines + + ScanLineWidth * BorderBottomHeight]; //56 lines of border + + attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped + + SetupScreenSize(); + + Reset(); + } + + #endregion + + #region Misc Operations + + public override void Reset() + { + contentionStartPeriod = 14361; // + LateTiming; + contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); + screen = _machine.Memory[9]; + screenByteCtr = DisplayStart; + ULAByteCtr = 0; + actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; + lastTState = actualULAStart; + BuildAttributeMap(); + BuildContentionTable(); + } + + #endregion + + #region Contention Methods + + public override bool IsContended(int addr) + { + addr = addr & 0xc000; + + if (addr == 0x4000) + { + // low port contention + return true; + } + + if (addr == 0xc000) + { + // high port contention - check for contended bank paged in + switch (_machine.RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + } + + return false; + } + + public override void BuildContentionTable() + { + int t = contentionStartPeriod; + while (t < contentionEndPeriod) + { + contentionTable[t++] = 1; + contentionTable[t++] = 0; + + //for 128 t-states + for (int i = 0; i < 128; i += 8) + { + contentionTable[t++] = 7; + contentionTable[t++] = 6; + contentionTable[t++] = 5; + contentionTable[t++] = 4; + contentionTable[t++] = 3; + contentionTable[t++] = 2; + contentionTable[t++] = 1; + contentionTable[t++] = 0; + } + t += (TstatesPerScanline - 128) - 2; + } + + //build top half of tstateToDisp table + //vertical retrace period + for (t = 0; t < actualULAStart; t++) + tstateToDisp[t] = 0; + + //next 48 are actual border + while (t < actualULAStart + (TstateAtTop)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + + //build middle half + int _x = 0; + int _y = 0; + int scrval = 2; + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) + { + for (int g = 0; g < 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24; g < 24 + 128; g++) + { + //Map screenaddr to tstate + if (g % 4 == 0) + { + scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); + _x += 8; + } + tstateToDisp[t++] = (short)scrval; + } + + _y++; + + for (int g = 24 + 128; g < 24 + 128 + 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++) + tstateToDisp[t++] = 0; + } + + int h = contentionStartPeriod + 3; + while (h < contentionEndPeriod + 3) + { + for (int j = 0; j < 128; j += 8) + { + floatingBusTable[h] = tstateToDisp[h + 2]; + floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; + floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; + floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; + h += 8; + } + h += TstatesPerScanline - 128; + } + + //build bottom half + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + } + + + #endregion + + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs new file mode 100644 index 0000000000..5cdfb1bbbf --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -0,0 +1,52 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public partial class ZX128Plus2a : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128Plus2a(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + { + Spectrum = spectrum; + CPU = cpu; + + ROMPaged = 0; + SHADOWPaged = false; + RAMPaged = 0; + PagingDisabled = false; + + // init addressable memory from ROM and RAM banks + ReInitMemory(); + + ULADevice = new ULAPlus2a(this); + + BuzzerDevice = new Buzzer(this); + BuzzerDevice.Init(44100, ULADevice.FrameLength); + + AYDevice = new AY38912(); + AYDevice.Init(44100, ULADevice.FrameLength); + + KeyboardDevice = new StandardKeyboard(this); + + InitJoysticks(joysticks); + + TapeDevice = new DatacorderDevice(); + TapeDevice.Init(this); + + InitializeMedia(files); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 15a091fccd..188581fd72 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -136,7 +136,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = Memory[_ROMpaged][addr % 0x4000]; break; - // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + // RAM 0x4000 (RAM5 - Bank5 always) case 1: result = Memory[9][addr % 0x4000]; break; @@ -261,10 +261,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - Memory[ROMPaged][addr % 0x4000] = value; + Memory[_ROMpaged][addr % 0x4000] = value; break; - // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + // RAM 0x4000 (RAM5 - Bank5 only) case 1: Memory[9][addr % 0x4000] = value; break; 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 199dda6515..ea53156118 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -132,10 +132,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset? { //result = udpDrive.DiskStatusRead(); + + // disk drive is not yet implemented - return a max status byte for the menu to load + result = 255; } else if ((port & 0xF002) == 0x3000) { //result = udpDrive.DiskReadByte(); + result = 0; } else if ((port & 0xF002) == 0x0) @@ -194,7 +198,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (!PagingDisabled) { - if (bits[0]) + if (!bits[0]) { // special paging is not enabled - get the ROMpage high byte ROMhigh = bits[2]; @@ -428,7 +432,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum get { // calculate the ROMpage from the high and low bits - return ZXSpectrum.GetIntFromBitArray(new BitArray(new bool[] { ROMlow, ROMhigh })); + var rp = ZXSpectrum.GetIntFromBitArray(new BitArray(new bool[] { ROMlow, ROMhigh })); + + if (rp != 0) + { + + } + + return rp; } set { ROMPaged = value; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs new file mode 100644 index 0000000000..e6d82474dc --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs @@ -0,0 +1,196 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class ULAPlus3 : ULABase + { + #region Construction + + public ULAPlus3(SpectrumBase machine) + : base(machine) + { + InterruptPeriod = 36; + LongestOperationCycles = 64 + 2; + FrameLength = 70908; + ClockSpeed = 3546900; + + contentionTable = new byte[70930]; + floatingBusTable = new short[70930]; + for (int f = 0; f < 70930; f++) + floatingBusTable[f] = -1; + + CharRows = 24; + CharCols = 32; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + DisplayStart = 16384; + DisplayLength = 6144; + AttributeStart = 22528; + AttributeLength = 768; + borderColour = 7; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + TstatesPerScanline = 228; + TstateAtTop = BorderTopHeight * TstatesPerScanline; + TstateAtBottom = BorderBottomHeight * TstatesPerScanline; + tstateToDisp = new short[FrameLength]; + + ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border + + ScanLineWidth * ScreenHeight //border + main + border of 192 lines + + ScanLineWidth * BorderBottomHeight]; //56 lines of border + + attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped + + SetupScreenSize(); + + Reset(); + } + + #endregion + + #region Misc Operations + + public override void Reset() + { + contentionStartPeriod = 14361; // + LateTiming; + contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); + screen = _machine.Memory[9]; + screenByteCtr = DisplayStart; + ULAByteCtr = 0; + actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; + lastTState = actualULAStart; + BuildAttributeMap(); + BuildContentionTable(); + } + + #endregion + + #region Contention Methods + + public override bool IsContended(int addr) + { + addr = addr & 0xc000; + + if (addr == 0x4000) + { + // low port contention + return true; + } + + if (addr == 0xc000) + { + // high port contention - check for contended bank paged in + switch (_machine.RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + } + + return false; + } + + public override void BuildContentionTable() + { + int t = contentionStartPeriod; + while (t < contentionEndPeriod) + { + contentionTable[t++] = 1; + contentionTable[t++] = 0; + + //for 128 t-states + for (int i = 0; i < 128; i += 8) + { + contentionTable[t++] = 7; + contentionTable[t++] = 6; + contentionTable[t++] = 5; + contentionTable[t++] = 4; + contentionTable[t++] = 3; + contentionTable[t++] = 2; + contentionTable[t++] = 1; + contentionTable[t++] = 0; + } + t += (TstatesPerScanline - 128) - 2; + } + + //build top half of tstateToDisp table + //vertical retrace period + for (t = 0; t < actualULAStart; t++) + tstateToDisp[t] = 0; + + //next 48 are actual border + while (t < actualULAStart + (TstateAtTop)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + + //build middle half + int _x = 0; + int _y = 0; + int scrval = 2; + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) + { + for (int g = 0; g < 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24; g < 24 + 128; g++) + { + //Map screenaddr to tstate + if (g % 4 == 0) + { + scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); + _x += 8; + } + tstateToDisp[t++] = (short)scrval; + } + + _y++; + + for (int g = 24 + 128; g < 24 + 128 + 24; g++) + tstateToDisp[t++] = 1; + + for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++) + tstateToDisp[t++] = 0; + } + + int h = contentionStartPeriod + 3; + while (h < contentionEndPeriod + 3) + { + for (int j = 0; j < 128; j += 8) + { + floatingBusTable[h] = tstateToDisp[h + 2]; + floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; + floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; + floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; + h += 8; + } + h += TstatesPerScanline - 128; + } + + //build bottom half + while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) + { + for (int g = 0; g < 176; g++) + tstateToDisp[t++] = 1; + + for (int g = 176; g < TstatesPerScanline; g++) + tstateToDisp[t++] = 0; + } + } + + + #endregion + + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 9fb6b4db95..4be628ba75 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // init addressable memory from ROM and RAM banks ReInitMemory(); - ULADevice = new ULA128(this); + ULADevice = new ULAPlus3(this); BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs index 61c22499ea..4c70608d7a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs @@ -151,10 +151,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { for (int j = 0; j < 128; j += 8) { - floatingBusTable[h] = tstateToDisp[h + 2]; //screen address - floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; //attr address - floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; //screen address + 1 - floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; //attr address + 1 + floatingBusTable[h] = tstateToDisp[h + 2]; //screen address + floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; //attr address + floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; //screen address + 1 + floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; //attr address + 1 h += 8; } h += TstatesPerScanline - 128; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index b07dfd6b9e..6381049957 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -70,6 +70,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; + case MachineType.ZXSpectrum128Plus2a: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128Plus2a, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); + break; case MachineType.ZXSpectrum128Plus3: ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); @@ -158,8 +162,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case "PLUS2ROM": embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus2_rom)); break; + case "PLUS2AROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus2a_rom)); + break; case "PLUS3ROM": - embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus3_rom)); + byte[] r0 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM0_bin)); + byte[] r1 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM1_bin)); + byte[] r2 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM2_bin)); + byte[] r3 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM3_bin)); + embeddedRom = r0.Concat(r1).Concat(r2).Concat(r3).ToArray(); break; default: embeddedFound = false; @@ -209,6 +220,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var romDataP2 = RomData.InitROM(machineType, _systemRomP2); _machine.InitROM(romDataP2); break; + case MachineType.ZXSpectrum128Plus2a: + _machine = new ZX128Plus2a(this, _cpu, borderType, files, joys); + var _systemRomP4 = GetFirmware(0x10000, "PLUS2AROM"); + var romDataP4 = RomData.InitROM(machineType, _systemRomP4); + _machine.InitROM(romDataP4); + break; case MachineType.ZXSpectrum128Plus3: _machine = new ZX128Plus3(this, _cpu, borderType, files, joys); var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); diff --git a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs index 312ea05d92..73259a9985 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs +++ b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs @@ -90,6 +90,46 @@ namespace BizHawk.Emulation.Cores.Properties { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM0_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM0_bin", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM1_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM1_bin", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM2_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM2_bin", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM3_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM3_bin", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Byte[]. /// @@ -123,9 +163,9 @@ namespace BizHawk.Emulation.Cores.Properties { /// /// Looks up a localized resource of type System.Byte[]. /// - internal static byte[] ZX_plus3_rom { + internal static byte[] ZX_plus2a_rom { get { - object obj = ResourceManager.GetObject("ZX_plus3_rom", resourceCulture); + object obj = ResourceManager.GetObject("ZX_plus2a_rom", resourceCulture); return ((byte[])(obj)); } } diff --git a/BizHawk.Emulation.Cores/Properties/Resources.resx b/BizHawk.Emulation.Cores/Properties/Resources.resx index 4699ab819d..b5e12deeae 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.resx +++ b/BizHawk.Emulation.Cores/Properties/Resources.resx @@ -127,16 +127,28 @@ ..\Resources\sgb-cart-present.spc.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\resources\spectrum3_v4-0_rom0.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\spectrum3_v4-0_rom1.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\spectrum3_v4-0_rom2.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\spectrum3_v4-0_rom3.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\128.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\48.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\resources\plus2a.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\plus2.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - ..\Resources\plus3.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM0.bin.gz b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM0.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..d70d805d620b4bdfd61e83385bbf8a019a6bd034 GIT binary patch literal 10000 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 z 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 H06+i$iGfWn literal 0 HcmV?d00001 diff --git a/BizHawk.Emulation.Cores/Resources/plus3.rom.gz b/BizHawk.Emulation.Cores/Resources/plus2a.rom.gz similarity index 100% rename from BizHawk.Emulation.Cores/Resources/plus3.rom.gz rename to BizHawk.Emulation.Cores/Resources/plus2a.rom.gz From c7fe4c2887dd87ca3117a9dc85b4a1749986989d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 8 Mar 2018 21:25:19 +0000 Subject: [PATCH 067/339] Datacorder - implement basic manual tape block navigation (i.e. NextBlock, PrevBlock) --- .../Hardware/Datacorder/DatacorderDevice.cs | 60 +++++++++++++++++++ .../Machine/SpectrumBase.Input.cs | 28 +++++++++ .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 22 +++++++ 3 files changed, 110 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 0096c9af86..0227fee185 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -315,6 +315,66 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _currentDataBlockIndex = 0; } + ///

+ /// Performs a block skip operation on the current tape + /// TRUE: skip forward + /// FALSE: skip backward + /// + /// + public void SkipBlock(bool skipForward) + { + int blockCount = _dataBlocks.Count; + int targetBlockId = _currentDataBlockIndex; + + if (skipForward) + { + if (_currentDataBlockIndex == blockCount - 1) + { + // last block, go back to beginning + targetBlockId = 0; + } + else + { + targetBlockId++; + } + } + else + { + if (_currentDataBlockIndex == 0) + { + // already first block, goto last block + targetBlockId = blockCount - 1; + } + else + { + targetBlockId--; + } + } + + var bl = _dataBlocks[targetBlockId]; + + StringBuilder sbd = new StringBuilder(); + sbd.Append("("); + sbd.Append((targetBlockId + 1) + " of " + _dataBlocks.Count()); + sbd.Append(") : "); + //sbd.Append("ID" + bl.BlockID.ToString("X2") + " - "); + sbd.Append(bl.BlockDescription); + if (bl.MetaData.Count > 0) + { + sbd.Append(" - "); + sbd.Append(bl.MetaData.First().Key + ": " + bl.MetaData.First().Value); + //sbd.Append("\n"); + //sbd.Append(bl.MetaData.Skip(1).First().Key + ": " + bl.MetaData.Skip(1).First().Value); + } + + if (skipForward) + _machine.Spectrum.OSD_TapeNextBlock(sbd.ToString()); + else + _machine.Spectrum.OSD_TapePrevBlock(sbd.ToString()); + + CurrentDataBlockIndex = targetBlockId; + } + /// /// Inserts a new tape and sets up the tape device accordingly /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 7b3e9a18b4..eb2bcd63a8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -15,12 +15,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string Record = "Record Tape"; string NextTape = "Insert Next Tape"; string PrevTape = "Insert Previous Tape"; + string NextBlock = "Next Tape Block"; + string PrevBlock = "Prev Tape Block"; bool pressed_Play = false; bool pressed_Stop = false; bool pressed_RTZ = false; bool pressed_NextTape = false; bool pressed_PrevTape = false; + bool pressed_NextBlock = false; + bool pressed_PrevBlock = false; public void PollInput() { @@ -147,6 +151,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else pressed_PrevTape = false; + + if (Spectrum._controller.IsPressed(NextBlock)) + { + if (!pressed_NextBlock) + { + Spectrum.OSD_FireInputMessage(NextBlock); + TapeDevice.SkipBlock(true); + pressed_NextBlock = true; + } + } + else + pressed_NextBlock = false; + + if (Spectrum._controller.IsPressed(PrevBlock)) + { + if (!pressed_PrevBlock) + { + Spectrum.OSD_FireInputMessage(PrevBlock); + TapeDevice.SkipBlock(false); + pressed_PrevBlock = true; + } + } + else + pressed_PrevBlock = false; } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 5b7b29b3d6..9a583013e8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -178,6 +178,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } + /// + /// Tape message that is fired when user has manually skipped to the next block + /// + public void OSD_TapeNextBlock(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + sb.Append("Manual Skip Next " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when user has manually skipped to the next block + /// + public void OSD_TapePrevBlock(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + sb.Append("Manual Skip Prev " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + #endregion /// From 31328dac2b755b76cfd52432e5e6072263202d5a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 8 Mar 2018 22:10:16 +0000 Subject: [PATCH 068/339] Readme progress update! --- .../Computers/SinclairSpectrum/readme.md | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 6cc05397c2..e5f12cfe06 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -1,15 +1,15 @@ ## ZXHawk -At the moment this is very experimental and is still actively being worked on. +At the moment this is experimental and is still being worked on. ### Implemented and sorta working * IEmulator -* ZX Spectrum 48k, 128k & Plus2 models +* ZX Spectrum 48k, 128k, +2 & +2A models * ULA video output (implementing IVideoProvider) * ULA Mode 1 VBLANK interrupt generation * IM2 Interrupts and DataBus implementation (thanks Aloysha) * Beeper/Buzzer output (implementing ISoundProvider) -* AY-3-8912 sound chip implementation +* AY-3-8912 sound chip implementation (stereo or mono options available as a setting) * Keyboard input (implementing IInputPollable) * Default keyboard keymappings * Kempston, Cursor and Sinclair (Left & Right) joysticks emulated @@ -18,19 +18,23 @@ At the moment this is very experimental and is still actively being worked on. * IStatable * ISettable core settings * IDebuggable (for what it's worth) -* DeterministicEmulation as a SyncSetting +* DeterministicEmulation as a SyncSetting, LagFrame detection and FrameAdvance render & renderSound bools respected (when DeterministicEmulation == false) * Tape auto-loading routines (as a setting) +* Basic tape block navigation (NextBlock, PrevBlock) +* Tape-related OSD messages (verbosity level configured in settings) ### Work in progress +* ZX Spectrum +3 emulation (partially working, see below) * Exact emulator timings * Floating memory bus emulation * TASStudio (need to verify that this works as it should) ### Not working -* ZX Spectrum Plus3 emulation - -### Known bugs -* Audible 'popping' from the emulated buzzer after a load state operation (maybe this is a normal thing) +* +3 disk drive - no implementation yet +* Hard & Soft Reset menu options in the client (they are greyed out for some reason) * Speedlock tape protection scheme doesn't appear to load correctly +### Help needed +* I'm not a TASer, i've never TASed before. It would be really useful if someone (anyone) can build this branch and test this core from a TAS-workflow / TAStudio perpective. There may still be some work to do an exact timings and memory contention, but otherwise this core is able to play the majority of speccy games out there. + -Asnivor From 8708e987f7e73c9f1620887fedd60d8d2aeabda9 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 9 Mar 2018 17:52:04 +0000 Subject: [PATCH 069/339] Improved tape auto-loading functions --- .../Hardware/Datacorder/DatacorderDevice.cs | 150 ++++++++++++++---- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 1 + .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 1 + .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 2 + .../Machine/ZXSpectrum48K/ZX48.Port.cs | 1 + 5 files changed, 121 insertions(+), 34 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 0227fee185..be338e7fb2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -120,12 +120,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Signs whether the device should autodetect when the Z80 has entered into /// 'load' mode and auto-play the tape if neccesary /// - public bool AutoPlay { get; set; } + private bool _autoPlay; + public bool AutoPlay + { + get { return _machine.Spectrum.Settings.AutoLoadTape; } + set { _autoPlay = value; MonitorReset(); } + } + #endregion #region Emulator - + /// /// This is the address the that ROM will jump to when the spectrum has quit tape playing /// @@ -138,7 +144,7 @@ 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) { @@ -172,6 +178,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } */ + + MonitorFrame(); } ///
@@ -302,7 +310,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // update the lastCycle _lastCycle = _cpu.TotalExecutedCycles; - } /// @@ -419,9 +426,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion #region Tape Device Methods - - private bool initialBlockPlayed = false; - + /// /// Simulates the spectrum 'EAR' input reading data from the tape /// @@ -582,6 +587,105 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + #region TapeMonitor + + private long _lastINCycle = 0; + private int _monitorCount; + private int _monitorTimeOut; + private ushort _monitorLastPC; + private ushort[] _monitorLastRegs = new ushort[7]; + + /// + /// Resets the TapeMonitor + /// + private void MonitorReset() + { + _lastINCycle = 0; + _monitorCount = 0; + _monitorLastPC = 0; + _monitorLastRegs = null; + } + + /// + /// An iteration of the monitor process + /// + public void MonitorRead() + { + long cpuCycle = _cpu.TotalExecutedCycles; + int delta = (int)(cpuCycle - _lastINCycle); + _lastINCycle = cpuCycle; + + var nRegs = new ushort[] + { + _cpu.Regs[_cpu.A], + _cpu.Regs[_cpu.B], + _cpu.Regs[_cpu.C], + _cpu.Regs[_cpu.D], + _cpu.Regs[_cpu.E], + _cpu.Regs[_cpu.H], + _cpu.Regs[_cpu.L] + }; + + if (delta > 0 && + delta < 96 && + _cpu.RegPC == _monitorLastPC && + _monitorLastRegs != null) + { + int dCnt = 0; + int dVal = 0; + + for (int i = 0; i < nRegs.Length; i++) + { + if (_monitorLastRegs[i] != nRegs[i]) + { + dVal = _monitorLastRegs[i] - nRegs[i]; + dCnt++; + } + } + + if (dCnt == 1 && + (dVal == 1 || dVal == -1)) + { + _monitorCount++; + + if (_monitorCount >= 8 && _machine.Spectrum.Settings.AutoLoadTape) + { + if (!_tapeIsPlaying) + { + Play(); + _machine.Spectrum.OSD_TapePlayingAuto(); + } + + _monitorTimeOut = 50; + } + } + else + { + _monitorCount = 0; + } + } + + _monitorLastRegs = nRegs; + _monitorLastPC = _cpu.RegPC; + } + + + private void MonitorFrame() + { + if (_tapeIsPlaying && _machine.Spectrum.Settings.AutoLoadTape) + { + _monitorTimeOut--; + + if (_monitorTimeOut < 0) + { + Stop(); + _machine.Spectrum.OSD_TapeStoppedAuto(); + } + } + } + + #endregion + #region State Serialization private int _tempBlockCount; @@ -593,39 +697,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void SyncState(Serializer ser) { ser.BeginSection("DatacorderDevice"); - ser.Sync("_currentDataBlockIndex", ref _currentDataBlockIndex); ser.Sync("_position", ref _position); ser.Sync("_tapeIsPlaying", ref _tapeIsPlaying); ser.Sync("_lastCycle", ref _lastCycle); ser.Sync("_waitEdge", ref _waitEdge); - //ser.Sync("_initialBlockPlayed", ref initialBlockPlayed); ser.Sync("currentState", ref currentState); - - //_dataBlocks - /* - ser.BeginSection("Datablocks"); - - if (ser.IsWriter) - { - _tempBlockCount = _dataBlocks.Count(); - ser.Sync("_tempBlockCount", ref _tempBlockCount); - - for (int i = 0; i < _tempBlockCount; i++) - { - _dataBlocks[i].SyncState(ser, i); - } - } - else - { - ser.Sync("_tempBlockCount", ref _tempBlockCount); - } - - - - ser.EndSection(); - */ - + ser.Sync("_lastINCycle", ref _lastINCycle); + ser.Sync("_monitorCount", ref _monitorCount); + ser.Sync("_monitorTimeOut", ref _monitorTimeOut); + ser.Sync("_monitorLastPC", ref _monitorLastPC); + ser.Sync("_monitorLastRegs", ref _monitorLastRegs, false); ser.EndSection(); } 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 434f4c7cfa..5ef01f9504 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -96,6 +96,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 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) { 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 07e22b4203..c29876cc3c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -89,6 +89,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 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) { 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 ea53156118..eb8ff18e36 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -90,6 +90,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = result | 0xa0; //set bit 5 & 7 to 1 + TapeDevice.MonitorRead(); + if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) { if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) 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 2c7db07cbd..5dc5c2a514 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -95,6 +95,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 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) { From ae8b030e57968f6fdfad9920113e459eb32bd3ba Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 10:19:42 +0000 Subject: [PATCH 070/339] 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; From 0bd433210e8a9079291f1e42012cee8a4cfe2b6b Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 10:20:56 +0000 Subject: [PATCH 071/339] Fixed tzx tape standard data block PauseAfter value. Now speedlock7 encoded games should work --- .../Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index 9d4188e351..28a761cd3d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -268,7 +268,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.DataPeriods = new List(); int pauseLen = GetWordValue(data, _position); - + if (pauseLen == 0) + pauseLen = 1000; int blockLen = GetWordValue(data, _position + 2); _position += 4; @@ -276,7 +277,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum byte[] tmp = new byte[blockLen]; tmp = data.Skip(_position).Take(blockLen).ToArray(); - var t2 = DecodeDataBlock(t, tmp, DataBlockType.Standard, 1000); + var t2 = DecodeDataBlock(t, tmp, DataBlockType.Standard, pauseLen); // add the block _datacorder.DataBlocks.Add(t2); From ccb5947adeb96ca7d288bf1f7322aa706293a8f0 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 12:00:17 +0000 Subject: [PATCH 072/339] Finished port IO contention rewrites --- .../BizHawk.Emulation.Cores.csproj | 7 +- .../IBeeperDevice.cs | 0 .../{Input => Abstraction}/IJoystick.cs | 0 .../{Input => Abstraction}/IKeyboard.cs | 2 +- .../Hardware/Abstraction/IPortIODevice.cs | 30 +++ .../Hardware/Datacorder/DatacorderDevice.cs | 73 ++++- .../Hardware/Input/StandardKeyboard.cs | 90 +++++++ .../Machine/SpectrumBase.Port.cs | 8 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 122 ++------- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 98 ++----- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 251 ++---------------- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 128 ++------- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 10 +- .../Computers/SinclairSpectrum/readme.md | 7 +- 14 files changed, 299 insertions(+), 527 deletions(-) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/{SoundOuput => Abstraction}/IBeeperDevice.cs (100%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/{Input => Abstraction}/IJoystick.cs (100%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/{Input => Abstraction}/IKeyboard.cs (98%) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index c0fbe511fc..272aae7483 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -256,7 +256,8 @@ - + + @@ -264,9 +265,9 @@ - + - + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/IBeeperDevice.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IJoystick.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs similarity index 98% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IKeyboard.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs index 83b8782226..77282f1dfa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/IKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs @@ -7,7 +7,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Represents a spectrum keyboard /// - public interface IKeyboard + public interface IKeyboard : IPortIODevice { /// /// The calling spectrumbase class diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs new file mode 100644 index 0000000000..2fbd89a0db --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a device that utilizes port IN & OUT + /// + public interface IPortIODevice + { + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + bool ReadPort(ushort port, ref int result); + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + bool WritePort(ushort port, int result); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index f0bba8ffff..29a746b880 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Represents the tape device (or build-in datacorder as it was called +2 and above) /// - public class DatacorderDevice + public class DatacorderDevice : IPortIODevice { #region Construction @@ -627,6 +627,77 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + #region IPortIODevice + + /// + /// Mask constants + /// + private const int TAPE_BIT = 0x40; + private const int EAR_BIT = 0x10; + private const int MIC_BIT = 0x08; + + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + public bool ReadPort(ushort port, ref int result) + { + if (TapeIsPlaying) + { + if (GetEarBit(_cpu.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (_machine.KeyboardDevice.IsIssue2Keyboard) + { + if ((_machine.LASTULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= (TAPE_BIT); + } + } + else + { + if ((_machine.LASTULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + + return true; + } + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + public bool WritePort(ushort port, int result) + { + // not implemented yet + return false; + } + + #endregion + #region State Serialization /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs index 2df804efec..f4289edf92 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs @@ -312,6 +312,96 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return (byte)index; } + + #region IPortIODevice + + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + public bool ReadPort(ushort port, ref int result) + { + /* + The high byte indicates which half-row of keys is being polled + A zero on one of these lines selects a particular half-row of five keys: + + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + + A zero in one of the five lowest bits means that the corresponding key is pressed. If more than one address line + is made low, the result is the logical AND of all single inputs, so a zero in a bit means that at least one of the + appropriate keys is pressed. For example, only if each of the five lowest bits of the result from reading from Port 00FE + (for instance by XOR A/IN A,(FE)) is one, no key is pressed + */ + + if ((port & 0x8000) == 0) + { + result &= KeyLine[7]; + } + + if ((port & 0x4000) == 0) + { + result &= KeyLine[6]; + } + + if ((port & 0x2000) == 0) + { + result &= KeyLine[5]; + } + + if ((port & 0x1000) == 0) + { + result &= KeyLine[4]; + } + + if ((port & 0x800) == 0) + { + result &= KeyLine[3]; + } + + if ((port & 0x400) == 0) + { + result &= KeyLine[2]; + } + + if ((port & 0x200) == 0) + { + result &= KeyLine[1]; + } + + if ((port & 0x100) == 0) + { + result &= KeyLine[0]; + } + + // mask out lower 4 bits + result = result & 0x1f; + + // set bit 5 & 7 to 1 + result = result | 0xa0; + + return true; + } + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + public bool WritePort(ushort port, int result) + { + // not implemented + return false; + } + + #endregion + public void SyncState(Serializer ser) { ser.BeginSection("Keyboard"); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 12b3e34f5a..874345ba9d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -16,6 +16,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The last OUT data that was sent to the ULA /// protected byte LastULAOutByte; + public byte LASTULAOutByte + { + get { return LastULAOutByte; } + set { LastULAOutByte = value; } + } + /// /// Reads a byte of data from a specified port address @@ -42,7 +48,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Simulates IO port contention based on the supplied address - /// This method is for 48k machines and should be overridden for other models + /// This method is for 48k and 128k/+2 machines only and should be overridden for other models /// /// public virtual void ContendPortAddress(ushort addr) 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 5ef01f9504..0fb70d9bca 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -16,12 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { - 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; @@ -29,10 +25,8 @@ 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); - //CPU.TotalExecutedCycles++; - - // Kempston Joystick + // Kempston joystick input takes priority over all other input + // if this is detected just return the kempston byte if ((port & 0xe0) == 0 || (port & 0x20) == 0) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) @@ -42,104 +36,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else if (lowBitReset) { - // Even I/O address so get input - // The high byte indicates which half-row of keys is being polled - /* - IN: Reads keys (bit 0 to bit 4 inclusive) - 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 - 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y - 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H - 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B - */ + // Even I/O address so get input from keyboard + KeyboardDevice.ReadPort(port, ref result); - if ((port & 0x8000) == 0) - { - result &= KeyboardDevice.KeyLine[7]; - } - - if ((port & 0x4000) == 0) - { - result &= KeyboardDevice.KeyLine[6]; - } - - if ((port & 0x2000) == 0) - { - result &= KeyboardDevice.KeyLine[5]; - } - - if ((port & 0x1000) == 0) - { - result &= KeyboardDevice.KeyLine[4]; - } - - if ((port & 0x800) == 0) - { - result &= KeyboardDevice.KeyLine[3]; - } - - if ((port & 0x400) == 0) - { - result &= KeyboardDevice.KeyLine[2]; - } - - if ((port & 0x200) == 0) - { - result &= KeyboardDevice.KeyLine[1]; - } - - if ((port & 0x100) == 0) - { - result &= KeyboardDevice.KeyLine[0]; - } - - - result = result & 0x1f; //mask out lower 4 bits - result = result | 0xa0; //set bit 5 & 7 to 1 + // not a lagframe + InputRead = true; + // tape loading monitor cycle TapeDevice.MonitorRead(); - if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) - { - if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) - { - result &= ~(TAPE_BIT); // reset is EAR ON - } - else - { - result |= (TAPE_BIT); // set is EAR Off - } - } - else - { - if (KeyboardDevice.IsIssue2Keyboard) - { - if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - else - { - if ((LastULAOutByte & EAR_BIT) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - } - + // process tape INs + TapeDevice.ReadPort(port, ref result); } else { // devices other than the ULA will respond here - // (e.g. the AY sound chip in a 128k spectrum + // (e.g. the AY sound chip in a 128k spectrum) // AY register activate if ((port & 0xc002) == 0xc000) @@ -147,11 +59,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = (int)AYDevice.PortRead(); } - // Kempston Mouse + // Kempston Mouse (not implemented yet) - // if unused port the floating memory bus should be returned - + // If this is an unused port the floating memory bus should be returned // Floating bus is read on the previous cycle int _tStates = CurrentFrameCycle - 1; @@ -183,6 +94,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + // process IO contention + ContendPortAddress(port); + // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); // get a BitArray of the value byte @@ -225,8 +139,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; - ULADevice.Contend(port); - // Only even addresses address the ULA if (lowBitReset) { 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 c29876cc3c..4f0a9ee47b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { - InputRead = true; + // process IO contention + ContendPortAddress(port); int result = 0xFF; @@ -24,9 +25,8 @@ 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 + // Kempston joystick input takes priority over all other input + // if this is detected just return the kempston byte if ((port & 0xe0) == 0 || (port & 0x20) == 0) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) @@ -36,81 +36,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else if (lowBitReset) { - // Even I/O address so get input - // The high byte indicates which half-row of keys is being polled - /* - IN: Reads keys (bit 0 to bit 4 inclusive) - 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 - 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y - 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H - 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B - */ - - if ((port & 0x8000) == 0) - { - result &= KeyboardDevice.KeyLine[7]; - } - - if ((port & 0x4000) == 0) - { - result &= KeyboardDevice.KeyLine[6]; - } - - if ((port & 0x2000) == 0) - { - result &= KeyboardDevice.KeyLine[5]; - } - - if ((port & 0x1000) == 0) - { - result &= KeyboardDevice.KeyLine[4]; - } - - if ((port & 0x800) == 0) - { - result &= KeyboardDevice.KeyLine[3]; - } - - if ((port & 0x400) == 0) - { - result &= KeyboardDevice.KeyLine[2]; - } - - if ((port & 0x200) == 0) - { - result &= KeyboardDevice.KeyLine[1]; - } - - if ((port & 0x100) == 0) - { - result &= KeyboardDevice.KeyLine[0]; - } - - result = result & 0x1f; //mask out lower 4 bits - result = result | 0xa0; //set bit 5 & 7 to 1 + // Even I/O address so get input from keyboard + KeyboardDevice.ReadPort(port, ref result); TapeDevice.MonitorRead(); - if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) - { - if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) - { - result &= ~(TAPE_BIT); // reset is EAR ON - } - else - { - result |= (TAPE_BIT); // set is EAR Off - } - } - else if ((LastULAOutByte & 0x10) == 0) - { - result &= ~(0x40); - } - else - { - result |= 0x40; - } + // not a lagframe + InputRead = true; + // tape loading monitor cycle + TapeDevice.MonitorRead(); + + // process tape INs + TapeDevice.ReadPort(port, ref result); } else { @@ -445,5 +383,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } set { ROMPaged = value; } } + + /// + /// Override port contention + /// +3/2a does not have the same ULA IO contention + /// + /// + public override void ContendPortAddress(ushort addr) + { + CPU.TotalExecutedCycles += 4; + } } } 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 eb8ff18e36..32bd26d842 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -16,7 +16,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { - InputRead = true; + // process IO contention + ContendPortAddress(port); int result = 0xFF; @@ -24,9 +25,8 @@ 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 + // Kempston joystick input takes priority over all other input + // if this is detected just return the kempston byte if ((port & 0xe0) == 0 || (port & 0x20) == 0) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) @@ -36,82 +36,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else if (lowBitReset) { - // Even I/O address so get input - // The high byte indicates which half-row of keys is being polled - /* - IN: Reads keys (bit 0 to bit 4 inclusive) - 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 - 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y - 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H - 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B - */ - - if ((port & 0x8000) == 0) - { - result &= KeyboardDevice.KeyLine[7]; - } - - if ((port & 0x4000) == 0) - { - result &= KeyboardDevice.KeyLine[6]; - } - - if ((port & 0x2000) == 0) - { - result &= KeyboardDevice.KeyLine[5]; - } - - if ((port & 0x1000) == 0) - { - result &= KeyboardDevice.KeyLine[4]; - } - - if ((port & 0x800) == 0) - { - result &= KeyboardDevice.KeyLine[3]; - } - - if ((port & 0x400) == 0) - { - result &= KeyboardDevice.KeyLine[2]; - } - - if ((port & 0x200) == 0) - { - result &= KeyboardDevice.KeyLine[1]; - } - - if ((port & 0x100) == 0) - { - result &= KeyboardDevice.KeyLine[0]; - } - - result = result & 0x1f; //mask out lower 4 bits - result = result | 0xa0; //set bit 5 & 7 to 1 - + // Even I/O address so get input from keyboard + KeyboardDevice.ReadPort(port, ref result); TapeDevice.MonitorRead(); - if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) - { - if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) - { - result &= ~(TAPE_BIT); // reset is EAR ON - } - else - { - result |= (TAPE_BIT); // set is EAR Off - } - } - else if ((LastULAOutByte & 0x10) == 0) - { - result &= ~(0x40); - } - else - { - result |= 0x40; - } + // not a lagframe + InputRead = true; + // tape loading monitor cycle + TapeDevice.MonitorRead(); + + // process tape INs + TapeDevice.ReadPort(port, ref result); } else { @@ -128,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = (int)AYDevice.PortRead(); } - // Kempston Mouse + // Kempston Mouse (not implemented yet) else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset? @@ -165,6 +102,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + // process IO contention + ContendPortAddress(port); + // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); // get a BitArray of the value byte @@ -173,8 +113,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Check whether the low bit is reset bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; - ULADevice.Contend(port); - // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set if (port == 0x7ffd) { @@ -234,76 +172,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // bit 4 is the printer port strobe PrinterPortStrobe = bits[4]; } - /* - // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set - if (!portBits[1] && !portBits[15] && portBits[14]) - { - // paging (skip if paging has been disabled - paging can then only happen after a machine hard reset) - if (!PagingDisabled) - { - // bit 0 specifies the paging mode - SpecialPagingMode = bits[0]; - - if (!SpecialPagingMode) - { - // we are in normal mode - // portbit 4 is the LOW BIT of the ROM selection - BitArray romHalfNibble = new BitArray(2); - romHalfNibble[0] = portBits[4]; - - // value bit 2 is the high bit of the ROM selection - romHalfNibble[1] = bits[2]; - - // value bit 1 is ignored in normal paging mode - - // set the ROMPage - ROMPaged = ZXSpectrum.GetIntFromBitArray(romHalfNibble); - - - - - // bit 3 controls shadow screen - SHADOWPaged = bits[3]; - - // Bit 5 set signifies that paging is disabled until next reboot - PagingDisabled = bits[5]; - } - } - } - - // port 0x1ffd - special paging mode - // hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set - if (!portBits[1] && portBits[12] && !portBits[13] && !portBits[14] && !portBits[15]) - { - if (!PagingDisabled && SpecialPagingMode) - { - // process special paging - // this is decided based on combinations of bits 1 & 2 - // Config 0 = Bit1-0 Bit2-0 - // Config 1 = Bit1-1 Bit2-0 - // Config 2 = Bit1-0 Bit2-1 - // Config 3 = Bit1-1 Bit2-1 - BitArray confHalfNibble = new BitArray(2); - confHalfNibble[0] = bits[1]; - confHalfNibble[1] = bits[2]; - - // set special paging configuration - PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); - - // last value should be saved at 0x5b67 (23399) - not sure if this is actually needed - WriteBus(0x5b67, value); - } - - // bit 3 controls the disk motor (1=on, 0=off) - DiskMotorState = bits[3]; - - // bit 4 is the printer port strobe - PrinterPortStrobe = bits[4]; - } - - */ - - + // Only even addresses address the ULA if (lowBitReset) { @@ -346,84 +215,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.PortWrite(value); CPU.TotalExecutedCycles += 3; } - - /* - - else - { - if ((port & 0xC002) == 0x4000) //Are bits 1 and 15 reset and bit 14 set? - { - // memory paging activate - if (PagingDisabled) - return; - - // bit 5 handles paging disable (48k mode, persistent until next reboot) - if ((value & 0x20) != 0) - { - PagingDisabled = true; - } - - // shadow screen - if ((value & 0x08) != 0) - { - SHADOWPaged = true; - } - else - { - SHADOWPaged = false; - } - } - else - { - //Extra Memory Paging feature activate - if ((port & 0xF002) == 0x1000) //Is bit 12 set and bits 13,14,15 and 1 reset? - { - if (PagingDisabled) - return; - - // set disk motor state - //todo - - if ((value & 0x08) != 0) - { - //diskDriveState |= (1 << 4); - } - else - { - //diskDriveState &= ~(1 << 4); - } - - if ((value & 0x1) != 0) - { - // activate special paging mode - SpecialPagingMode = true; - PagingConfiguration = (value & 0x6 >> 1); - } - else - { - // normal paging mode - SpecialPagingMode = false; - } - } - else - { - // disk write port - if ((port & 0xF002) == 0x3000) //Is bit 12 set and bits 13,14,15 and 1 reset? - { - //udpDrive.DiskWriteByte((byte)(val & 0xff)); - } - } - } - } - */ } } LastULAOutByte = value; - - - - } /// @@ -445,5 +240,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } set { ROMPaged = value; } } + + /// + /// Override port contention + /// +3/2a does not have the same ULA IO contention + /// + /// + public override void ContendPortAddress(ushort addr) + { + CPU.TotalExecutedCycles += 4; + } } } 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 7e0c4edd2e..0cc99cf239 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -15,8 +15,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { - InputRead = true; - // process IO contention ContendPortAddress(port); @@ -26,132 +24,41 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; - // Kempston Joystick + // Kempston joystick input takes priority over all other input + // if this is detected just return the kempston byte if ((port & 0xe0) == 0 || (port & 0x20) == 0) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + // not a lag frame InputRead = true; } else if (lowBitReset) { - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + // Even I/O address so get input from keyboard + KeyboardDevice.ReadPort(port, ref result); - // Even I/O address so get input - // The high byte indicates which half-row of keys is being polled - /* - IN: Reads keys (bit 0 to bit 4 inclusive) - 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 - 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y - 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H - 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B - */ + // not a lagframe + InputRead = true; - if ((port & 0x8000) == 0) - { - result &= KeyboardDevice.KeyLine[7]; - } + // tape loading monitor cycle + TapeDevice.MonitorRead(); - if ((port & 0x4000) == 0) - { - result &= KeyboardDevice.KeyLine[6]; - } - - if ((port & 0x2000) == 0) - { - result &= KeyboardDevice.KeyLine[5]; - } - - if ((port & 0x1000) == 0) - { - result &= KeyboardDevice.KeyLine[4]; - } - - if ((port & 0x800) == 0) - { - result &= KeyboardDevice.KeyLine[3]; - } - - if ((port & 0x400) == 0) - { - result &= KeyboardDevice.KeyLine[2]; - } - - if ((port & 0x200) == 0) - { - result &= KeyboardDevice.KeyLine[1]; - } - - 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 - - - if (TapeDevice.TapeIsPlaying)//.CurrentMode == TapeOperationMode.Load) - { - if (!TapeDevice.GetEarBit(CPU.TotalExecutedCycles)) - { - result &= ~(TAPE_BIT); // reset is EAR ON - } - else - { - result |= (TAPE_BIT); // set is EAR Off - } - } - else - { - if (KeyboardDevice.IsIssue2Keyboard) - { - if ((LastULAOutByte & (EAR_BIT + MIC_BIT)) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - else - { - if ((LastULAOutByte & EAR_BIT) == 0) - { - result &= ~(TAPE_BIT); - } - else - { - result |= TAPE_BIT; - } - } - } - */ + // process tape INs + TapeDevice.ReadPort(port, ref result); } else { // devices other than the ULA will respond here // (e.g. the AY sound chip in a 128k spectrum - // AY register activate - // Kemptson Mouse + // AY register activate - no AY chip in a 48k spectrum + + // Kemptson Mouse (not implemented yet) - // if unused port the floating memory bus should be returned - + // If this is an unused port the floating memory bus should be returned // Floating bus is read on the previous cycle int _tStates = CurrentFrameCycle - 1; @@ -203,14 +110,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Border - LSB 3 bits hold the border colour if (ULADevice.borderColour != (value & BORDER_BIT)) + { + // border value has changed - update the screen buffer ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + } ULADevice.borderColour = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); - // Tape + // Tape mic processing (not implemented yet) //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 6381049957..d2c7db7abe 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -86,8 +86,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.MemoryCallbacks = MemoryCallbacks; - HardReset = _machine.HardReset; - SoftReset = _machine.SoftReset; + //HardReset = _machine.HardReset; + //SoftReset = _machine.SoftReset; _cpu.FetchMemory = _machine.ReadMemory; _cpu.ReadMemory = _machine.ReadMemory; @@ -109,13 +109,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum - HardReset(); + //HardReset(); SetupMemoryDomains(); } - public Action HardReset; - public Action SoftReset; + //public Action HardReset; + //public Action SoftReset; private readonly Z80A _cpu; private readonly TraceBuffer _tracer; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index e5f12cfe06..af479f8536 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -2,7 +2,7 @@ At the moment this is experimental and is still being worked on. -### Implemented and sorta working +### Implemented and working (as far as I can tell) * IEmulator * ZX Spectrum 48k, 128k, +2 & +2A models * ULA video output (implementing IVideoProvider) @@ -14,12 +14,12 @@ At the moment this is experimental and is still being worked on. * Default keyboard keymappings * Kempston, Cursor and Sinclair (Left & Right) joysticks emulated * Tape device that will load spectrum games in realtime (*.tzx and *.tap) -* Most tape protection/loading schemes that I've tested are currently working (see caveat below) +* Most tape protection/loading schemes that I've tested are currently working * IStatable * ISettable core settings * IDebuggable (for what it's worth) * DeterministicEmulation as a SyncSetting, LagFrame detection and FrameAdvance render & renderSound bools respected (when DeterministicEmulation == false) -* Tape auto-loading routines (as a setting) +* Tape auto-loading routines (as a setting - default ON) * Basic tape block navigation (NextBlock, PrevBlock) * Tape-related OSD messages (verbosity level configured in settings) @@ -32,7 +32,6 @@ At the moment this is experimental and is still being worked on. ### Not working * +3 disk drive - no implementation yet * Hard & Soft Reset menu options in the client (they are greyed out for some reason) -* Speedlock tape protection scheme doesn't appear to load correctly ### Help needed * I'm not a TASer, i've never TASed before. It would be really useful if someone (anyone) can build this branch and test this core from a TAS-workflow / TAStudio perpective. There may still be some work to do an exact timings and memory contention, but otherwise this core is able to play the majority of speccy games out there. From deba6b18b8733e12e277f5d02a09729f8755eb1e Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 14:37:45 +0000 Subject: [PATCH 073/339] Added 'Get Tape Status' keybinding - fires an OSD message with state info about the current tape --- Assets/defctrl.json | 3 +- .../Hardware/Datacorder/DatacorderDevice.cs | 123 +++++++++++------- .../Machine/SpectrumBase.Input.cs | 16 ++- .../Machine/SpectrumBase.Memory.cs | 13 ++ .../ZXSpectrum.Controllers.cs | 3 +- .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 40 ++++++ 6 files changed, 145 insertions(+), 53 deletions(-) diff --git a/Assets/defctrl.json b/Assets/defctrl.json index c2bb94b42b..dace0123e0 100644 --- a/Assets/defctrl.json +++ b/Assets/defctrl.json @@ -532,7 +532,8 @@ "Insert Next Tape": "F6", "Insert Previous Tape": "F5", "Next Tape Block": "F8", - "Prev Tape Block": "F7" + "Prev Tape Block": "F7", + "Get Tape Status": "F10" }, "Intellivision Controller": { "P1 Up": "UpArrow, J1 POV1U, X1 DpadUp, X1 LStickUp", diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 29a746b880..88d5f844f3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -137,8 +137,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public const ushort ERROR_ROM_ADDRESS = 0x0008; - Stopwatch sw = new Stopwatch(); - /// /// Should be fired at the end of every frame /// Primary purpose is to detect tape traps and manage auto play (if/when this is ever implemented) @@ -373,6 +371,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // decide how many cycles worth of data we are capturing long cycles = cpuCycle - _lastCycle; + bool is48k = _machine.IsIn48kMode(); + // check whether tape is actually playing if (_tapeIsPlaying == false) { @@ -398,11 +398,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // flip the current state currentState = !currentState; - if (_position == 0) + if (_position == 0 && _tapeIsPlaying) { // start of block - // notify about the current block + // notify about the current block var bl = _dataBlocks[_currentDataBlockIndex]; StringBuilder sbd = new StringBuilder(); @@ -427,47 +427,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // we have reached the end of the current block - // check for any commands - var command = _dataBlocks[_currentDataBlockIndex].Command; - var block = _dataBlocks[_currentDataBlockIndex]; - switch (command) - { - // Stop the tape command found - if this is the end of the tape RTZ - // otherwise just STOP and move to the next block - case TapeCommand.STOP_THE_TAPE: - - _machine.Spectrum.OSD_TapeStoppedAuto(); - - if (_currentDataBlockIndex >= _dataBlocks.Count()) - RTZ(); - else - { - Stop(); - } - break; - case TapeCommand.STOP_THE_TAPE_48K: - - if ((_machine.GetType() != typeof(ZX128) && - _machine.GetType() != typeof(ZX128Plus2) && - _machine.GetType() != typeof(ZX128Plus3)) || - (_machine.GetType() == typeof(ZX128) || - _machine.GetType() != typeof(ZX128Plus2) || - _machine.GetType() != typeof(ZX128Plus3)) && - _machine._ROMpaged == 1) - { - _machine.Spectrum.OSD_TapeStoppedAuto(); - - if (_currentDataBlockIndex >= _dataBlocks.Count()) - RTZ(); - else - { - Stop(); - } - - } - break; - } - if (_dataBlocks[_currentDataBlockIndex].DataPeriods.Count() == 0) { // notify about the current block (we are skipping it because its empty) @@ -487,11 +446,77 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } - // skip any empty blocks + // skip any empty blocks (and process any command blocks) while (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count()) - { + { + // check for any commands + var command = _dataBlocks[_currentDataBlockIndex].Command; + var block = _dataBlocks[_currentDataBlockIndex]; + bool shouldStop = false; + switch (command) + { + // Stop the tape command found - if this is the end of the tape RTZ + // otherwise just STOP and move to the next block + case TapeCommand.STOP_THE_TAPE: + + _machine.Spectrum.OSD_TapeStoppedAuto(); + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + + _monitorTimeOut = 2000; + + break; + case TapeCommand.STOP_THE_TAPE_48K: + if (is48k) + { + _machine.Spectrum.OSD_TapeStoppedAuto(); + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + + _monitorTimeOut = 2000; + } + /* + if ((_machine.GetType() != typeof(ZX128) && + _machine.GetType() != typeof(ZX128Plus2) && + _machine.GetType() != typeof(ZX128Plus3)) || + (_machine.GetType() == typeof(ZX128) || + _machine.GetType() != typeof(ZX128Plus2) || + _machine.GetType() != typeof(ZX128Plus3)) && + _machine._ROMpaged == 1) + { + _machine.Spectrum.OSD_TapeStoppedAuto(); + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + + _monitorTimeOut = 2000; + } + */ + break; + } + + if (shouldStop) + break; + _position = 0; _currentDataBlockIndex++; + + + if (_currentDataBlockIndex >= _dataBlocks.Count()) { break; @@ -584,7 +609,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _monitorCount++; - if (_monitorCount >= 8 && _machine.Spectrum.Settings.AutoLoadTape) + if (_monitorCount >= 16 && _cpu.RegPC == 1523 && _machine.Spectrum.Settings.AutoLoadTape) { if (!_tapeIsPlaying) { @@ -592,7 +617,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine.Spectrum.OSD_TapePlayingAuto(); } - _monitorTimeOut = 500; + _monitorTimeOut = 90; } } else diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index eb2bcd63a8..bf6a679f15 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -17,6 +17,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string PrevTape = "Insert Previous Tape"; string NextBlock = "Next Tape Block"; string PrevBlock = "Prev Tape Block"; + string TapeStatus = "Get Tape Status"; bool pressed_Play = false; bool pressed_Stop = false; @@ -25,6 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool pressed_PrevTape = false; bool pressed_NextBlock = false; bool pressed_PrevBlock = false; + bool pressed_TapeStatus = false; public void PollInput() { @@ -85,8 +87,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - - // Tape control if (Spectrum._controller.IsPressed(Play)) { @@ -175,6 +175,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else pressed_PrevBlock = false; + + if (Spectrum._controller.IsPressed(TapeStatus)) + { + if (!pressed_TapeStatus) + { + //Spectrum.OSD_FireInputMessage(TapeStatus); + Spectrum.OSD_ShowTapeStatus(); + pressed_TapeStatus = true; + } + } + else + pressed_TapeStatus = false; } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 6ddbccedbc..0179c0c5f5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -105,6 +105,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Helper function to refresh memory array (probably not the best way to do things) /// public abstract void ReInitMemory(); + + /// + /// Detects whether the 48k rom is resident (or paged in) at 0x0001 + /// + /// + public virtual bool IsIn48kMode() + { + var data = ReadBus(0x0001); + if (data == 0xaf) + return true; + + return false; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index a59f03de9e..1ba7dd5b74 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -86,7 +86,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum List tape = new List { // Tape functions - "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", "Insert Previous Tape", "Next Tape Block", "Prev Tape Block" + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", + "Insert Previous Tape", "Next Tape Block", "Prev Tape Block", "Get Tape Status" }; foreach (var s in tape) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 9a583013e8..9ad4e3615a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -200,6 +200,46 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } + /// + /// Tape message that prints the current status of the tape device + /// + public void OSD_ShowTapeStatus() + { + StringBuilder sb = new StringBuilder(); + sb.Append("Status: "); + + if (_machine.TapeDevice.TapeIsPlaying) + sb.Append("PLAYING"); + else + sb.Append("STOPPED"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + + sb.Append("Tape: " + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + + sb.Append("Block: "); + sb.Append("(" + (_machine.TapeDevice.CurrentDataBlockIndex + 1) + + " of " + _machine.TapeDevice.DataBlocks.Count() + ") " + + _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].BlockDescription); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + + sb.Append("Block Pos: "); + + int pos = _machine.TapeDevice.Position; + int end = _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].DataPeriods.Count; + double p = 0; + if (end != 0) + p = ((double)pos / (double)end) * (double)100; + + sb.Append(p.ToString("N0") + "%"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + } + #endregion /// From 213437362db1c7a206e506c62c3eab161a329fe9 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 15:22:06 +0000 Subject: [PATCH 074/339] Memory overhaul for a nice performance benefit --- .../Machine/SpectrumBase.Memory.cs | 2 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 6 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 58 +++++---- .../Machine/ZXSpectrum128K/ZX128.ULA.cs | 2 +- .../Machine/ZXSpectrum128K/ZX128.cs | 3 - .../ZX128Plus2a.Memory.cs | 116 +++++++++++------- .../ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 3 - .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 114 ++++++++++------- .../ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs | 2 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 3 - .../Machine/ZXSpectrum16K/ZX16.cs | 3 +- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 35 +++++- .../Machine/ZXSpectrum48K/ZX48.ULA.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.cs | 2 - .../ZXSpectrum.IMemoryDomains.cs | 6 +- .../SinclairSpectrum/ZXSpectrum.IStatable.cs | 2 - 17 files changed, 219 insertions(+), 142 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 0179c0c5f5..49a7bbd561 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -104,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Helper function to refresh memory array (probably not the best way to do things) /// - public abstract void ReInitMemory(); + //public abstract void ReInitMemory(); /// /// Detects whether the 48k rom is resident (or paged in) at 0x0001 diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 553872b295..9650e8cfd0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -194,9 +194,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); if (_renderSound) - BuzzerDevice.EndFrame(); - - //TapeDevice.CPUFrameCompleted(); + BuzzerDevice.EndFrame(); FrameCount++; @@ -276,7 +274,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.EndSection(); - ReInitMemory(); + //ReInitMemory(); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index c86a1e6ca4..b9eb26cbb8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -49,24 +49,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { int divisor = addr / 0x4000; byte result = 0xff; + switch (divisor) { // ROM 0x000 case 0: if (ROMPaged == 0) - result = Memory[0][addr % 0x4000]; + result = ROM0[addr % 0x4000]; else - result = Memory[1][addr % 0x4000]; + result = ROM1[addr % 0x4000]; break; - // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + // RAM 0x4000 (RAM5 - Bank5) case 1: - result = Memory[7][addr % 0x4000]; + result = RAM5[addr % 0x4000]; break; // RAM 0x8000 (RAM2 - Bank2) case 2: - result = Memory[4][addr % 0x4000]; + result = RAM2[addr % 0x4000]; break; // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) @@ -74,28 +75,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (RAMPaged) { case 0: - result = Memory[2][addr % 0x4000]; + result = RAM0[addr % 0x4000]; break; case 1: - result = Memory[3][addr % 0x4000]; + result = RAM1[addr % 0x4000]; break; case 2: - result = Memory[4][addr % 0x4000]; + result = RAM2[addr % 0x4000]; break; case 3: - result = Memory[5][addr % 0x4000]; + result = RAM3[addr % 0x4000]; break; case 4: - result = Memory[6][addr % 0x4000]; + result = RAM4[addr % 0x4000]; break; case 5: - result = Memory[7][addr % 0x4000]; + result = RAM5[addr % 0x4000]; break; case 6: - result = Memory[8][addr % 0x4000]; + result = RAM6[addr % 0x4000]; break; case 7: - result = Memory[9][addr % 0x4000]; + result = RAM7[addr % 0x4000]; break; } break; @@ -115,24 +116,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WriteBus(ushort addr, byte value) { int divisor = addr / 0x4000; + switch (divisor) { // ROM 0x000 case 0: + // cannot write to ROMs + /* if (ROMPaged == 0) - Memory[0][addr % 0x4000] = value; + ROM0[addr % 0x4000] = value; else - Memory[1][addr % 0x4000] = value; + ROM1[addr % 0x4000] = value; + */ break; // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) case 1: - Memory[7][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; // RAM 0x8000 (RAM2 - Bank2) case 2: - Memory[4][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) @@ -140,28 +145,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (RAMPaged) { case 0: - Memory[2][addr % 0x4000] = value; + RAM0[addr % 0x4000] = value; break; case 1: - Memory[3][addr % 0x4000] = value; + RAM1[addr % 0x4000] = value; break; case 2: - Memory[4][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; case 3: - Memory[5][addr % 0x4000] = value; + RAM3[addr % 0x4000] = value; break; case 4: - Memory[6][addr % 0x4000] = value; + RAM4[addr % 0x4000] = value; break; case 5: - Memory[7][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; case 6: - Memory[8][addr % 0x4000] = value; + RAM6[addr % 0x4000] = value; break; case 7: - Memory[9][addr % 0x4000] = value; + RAM7[addr % 0x4000] = value; break; } break; @@ -203,7 +208,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - + /* public override void ReInitMemory() { if (Memory.ContainsKey(0)) @@ -256,6 +261,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else Memory.Add(9, RAM7); } + */ /// /// ULA reads the memory at the specified address diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs index 6da4c3738f..59d9108290 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { contentionStartPeriod = 14361; // + LateTiming; contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.Memory[7]; + screen = _machine.RAM5; screenByteCtr = DisplayStart; ULAByteCtr = 0; actualULAStart = 14366 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index eb1b25aa19..5e657b0863 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -26,9 +26,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAMPaged = 0; PagingDisabled = false; - // init addressable memory from ROM and RAM banks - ReInitMemory(); - ULADevice = new ULA128(this); BuzzerDevice = new Buzzer(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 6505a5f81f..3ce0adcc73 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -75,12 +75,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - result = Memory[4][addr % 0x4000]; + result = RAM0[addr % 0x4000]; break; case 1: case 2: case 3: - result = Memory[8][addr % 0x4000]; + result = RAM4[addr % 0x4000]; break; } break; @@ -88,14 +88,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - result = Memory[5][addr % 0x4000]; + result = RAM1[addr % 0x4000]; break; case 1: - case 2: - result = Memory[9][addr % 0x4000]; + case 2: + result = RAM5[addr % 0x4000]; break; case 3: - result = Memory[11][addr % 0x4000]; + result = RAM7[addr % 0x4000]; break; } break; @@ -103,12 +103,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - result = Memory[6][addr % 0x4000]; + result = RAM0[addr % 0x4000]; break; case 1: case 2: case 3: - result = Memory[10][addr % 0x4000]; + result = RAM6[addr % 0x4000]; break; } break; @@ -118,10 +118,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case 0: case 2: case 3: - result = Memory[7][addr % 0x4000]; + result = RAM3[addr % 0x4000]; break; case 1: - result = Memory[11][addr % 0x4000]; + result = RAM7[addr % 0x4000]; break; } break; @@ -133,17 +133,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - result = Memory[_ROMpaged][addr % 0x4000]; + switch (_ROMpaged) + { + case 0: + result = ROM0[addr % 0x4000]; + break; + case 1: + result = ROM1[addr % 0x4000]; + break; + case 2: + result = ROM2[addr % 0x4000]; + break; + case 3: + result = ROM3[addr % 0x4000]; + break; + } break; // RAM 0x4000 (RAM5 - Bank5 always) case 1: - result = Memory[9][addr % 0x4000]; + result = RAM5[addr % 0x4000]; break; // RAM 0x8000 (RAM2 - Bank2) case 2: - result = Memory[6][addr % 0x4000]; + result = RAM2[addr % 0x4000]; break; // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) @@ -151,28 +165,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (RAMPaged) { case 0: - result = Memory[4][addr % 0x4000]; + result = RAM0[addr % 0x4000]; break; case 1: - result = Memory[5][addr % 0x4000]; + result = RAM1[addr % 0x4000]; break; case 2: - result = Memory[6][addr % 0x4000]; + result = RAM2[addr % 0x4000]; break; case 3: - result = Memory[7][addr % 0x4000]; + result = RAM3[addr % 0x4000]; break; case 4: - result = Memory[8][addr % 0x4000]; + result = RAM4[addr % 0x4000]; break; case 5: - result = Memory[9][addr % 0x4000]; + result = RAM5[addr % 0x4000]; break; case 6: - result = Memory[10][addr % 0x4000]; + result = RAM6[addr % 0x4000]; break; case 7: - result = Memory[11][addr % 0x4000]; + result = RAM7[addr % 0x4000]; break; } break; @@ -203,12 +217,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - Memory[4][addr % 0x4000] = value; + RAM0[addr % 0x4000] = value; break; case 1: case 2: case 3: - Memory[8][addr % 0x4000] = value; + RAM4[addr % 0x4000] = value; break; } break; @@ -216,14 +230,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - Memory[5][addr % 0x4000] = value; + RAM1[addr % 0x4000] = value; break; case 1: case 2: - Memory[9][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; case 3: - Memory[11][addr % 0x4000] = value; + RAM7[addr % 0x4000] = value; break; } break; @@ -231,12 +245,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - Memory[6][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; case 1: case 2: case 3: - Memory[10][addr % 0x4000] = value; + RAM6[addr % 0x4000] = value; break; } break; @@ -246,10 +260,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case 0: case 2: case 3: - Memory[7][addr % 0x4000] = value; + RAM3[addr % 0x4000] = value; break; case 1: - Memory[11][addr % 0x4000] = value; + RAM7[addr % 0x4000] = value; break; } break; @@ -261,17 +275,34 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - Memory[_ROMpaged][addr % 0x4000] = value; + /* + switch (_ROMpaged) + { + // cannot write to ROMs + case 0: + ROM0[addr % 0x4000] = value; + break; + case 1: + ROM1[addr % 0x4000] = value; + break; + case 2: + ROM2[addr % 0x4000] = value; + break; + case 3: + ROM3[addr % 0x4000] = value; + break; + } + */ break; // RAM 0x4000 (RAM5 - Bank5 only) case 1: - Memory[9][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; // RAM 0x8000 (RAM2 - Bank2) case 2: - Memory[6][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) @@ -279,28 +310,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (RAMPaged) { case 0: - Memory[4][addr % 0x4000] = value; + RAM0[addr % 0x4000] = value; break; case 1: - Memory[5][addr % 0x4000] = value; + RAM1[addr % 0x4000] = value; break; case 2: - Memory[6][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; case 3: - Memory[7][addr % 0x4000] = value; + RAM3[addr % 0x4000] = value; break; case 4: - Memory[8][addr % 0x4000] = value; + RAM4[addr % 0x4000] = value; break; case 5: - Memory[9][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; case 6: - Memory[10][addr % 0x4000] = value; + RAM6[addr % 0x4000] = value; break; case 7: - Memory[11][addr % 0x4000] = value; + RAM7[addr % 0x4000] = value; break; } break; @@ -343,7 +374,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - + /* public override void ReInitMemory() { if (Memory.ContainsKey(0)) @@ -406,6 +437,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else Memory.Add(11, RAM7); } + */ /// /// ULA reads the memory at the specified address diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs index 35547ab931..c6547833ad 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { contentionStartPeriod = 14361; // + LateTiming; contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.Memory[9]; + screen = _machine.RAM5; screenByteCtr = DisplayStart; ULAByteCtr = 0; actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index 5cdfb1bbbf..88b672d174 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -26,9 +26,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAMPaged = 0; PagingDisabled = false; - // init addressable memory from ROM and RAM banks - ReInitMemory(); - ULADevice = new ULAPlus2a(this); BuzzerDevice = new Buzzer(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 188581fd72..7ca4147f9d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -75,12 +75,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - result = Memory[4][addr % 0x4000]; + result = RAM0[addr % 0x4000]; break; case 1: case 2: case 3: - result = Memory[8][addr % 0x4000]; + result = RAM4[addr % 0x4000]; break; } break; @@ -88,14 +88,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - result = Memory[5][addr % 0x4000]; + result = RAM1[addr % 0x4000]; break; case 1: case 2: - result = Memory[9][addr % 0x4000]; + result = RAM5[addr % 0x4000]; break; case 3: - result = Memory[11][addr % 0x4000]; + result = RAM7[addr % 0x4000]; break; } break; @@ -103,12 +103,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - result = Memory[6][addr % 0x4000]; + result = RAM0[addr % 0x4000]; break; case 1: case 2: case 3: - result = Memory[10][addr % 0x4000]; + result = RAM6[addr % 0x4000]; break; } break; @@ -118,10 +118,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case 0: case 2: case 3: - result = Memory[7][addr % 0x4000]; + result = RAM3[addr % 0x4000]; break; case 1: - result = Memory[11][addr % 0x4000]; + result = RAM7[addr % 0x4000]; break; } break; @@ -133,17 +133,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - result = Memory[_ROMpaged][addr % 0x4000]; + switch (_ROMpaged) + { + case 0: + result = ROM0[addr % 0x4000]; + break; + case 1: + result = ROM1[addr % 0x4000]; + break; + case 2: + result = ROM2[addr % 0x4000]; + break; + case 3: + result = ROM3[addr % 0x4000]; + break; + } break; // RAM 0x4000 (RAM5 - Bank5 always) case 1: - result = Memory[9][addr % 0x4000]; + result = RAM5[addr % 0x4000]; break; // RAM 0x8000 (RAM2 - Bank2) case 2: - result = Memory[6][addr % 0x4000]; + result = RAM2[addr % 0x4000]; break; // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) @@ -151,28 +165,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (RAMPaged) { case 0: - result = Memory[4][addr % 0x4000]; + result = RAM0[addr % 0x4000]; break; case 1: - result = Memory[5][addr % 0x4000]; + result = RAM1[addr % 0x4000]; break; case 2: - result = Memory[6][addr % 0x4000]; + result = RAM2[addr % 0x4000]; break; case 3: - result = Memory[7][addr % 0x4000]; + result = RAM3[addr % 0x4000]; break; case 4: - result = Memory[8][addr % 0x4000]; + result = RAM4[addr % 0x4000]; break; case 5: - result = Memory[9][addr % 0x4000]; + result = RAM5[addr % 0x4000]; break; case 6: - result = Memory[10][addr % 0x4000]; + result = RAM6[addr % 0x4000]; break; case 7: - result = Memory[11][addr % 0x4000]; + result = RAM7[addr % 0x4000]; break; } break; @@ -203,12 +217,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - Memory[4][addr % 0x4000] = value; + RAM0[addr % 0x4000] = value; break; case 1: case 2: case 3: - Memory[8][addr % 0x4000] = value; + RAM4[addr % 0x4000] = value; break; } break; @@ -216,14 +230,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - Memory[5][addr % 0x4000] = value; + RAM1[addr % 0x4000] = value; break; case 1: case 2: - Memory[9][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; case 3: - Memory[11][addr % 0x4000] = value; + RAM7[addr % 0x4000] = value; break; } break; @@ -231,12 +245,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (PagingConfiguration) { case 0: - Memory[6][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; case 1: case 2: case 3: - Memory[10][addr % 0x4000] = value; + RAM6[addr % 0x4000] = value; break; } break; @@ -246,10 +260,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case 0: case 2: case 3: - Memory[7][addr % 0x4000] = value; + RAM3[addr % 0x4000] = value; break; case 1: - Memory[11][addr % 0x4000] = value; + RAM7[addr % 0x4000] = value; break; } break; @@ -261,17 +275,34 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: - Memory[_ROMpaged][addr % 0x4000] = value; + /* + switch (_ROMpaged) + { + // cannot write to ROMs + case 0: + ROM0[addr % 0x4000] = value; + break; + case 1: + ROM1[addr % 0x4000] = value; + break; + case 2: + ROM2[addr % 0x4000] = value; + break; + case 3: + ROM3[addr % 0x4000] = value; + break; + } + */ break; // RAM 0x4000 (RAM5 - Bank5 only) case 1: - Memory[9][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; // RAM 0x8000 (RAM2 - Bank2) case 2: - Memory[6][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) @@ -279,28 +310,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (RAMPaged) { case 0: - Memory[4][addr % 0x4000] = value; + RAM0[addr % 0x4000] = value; break; case 1: - Memory[5][addr % 0x4000] = value; + RAM1[addr % 0x4000] = value; break; case 2: - Memory[6][addr % 0x4000] = value; + RAM2[addr % 0x4000] = value; break; case 3: - Memory[7][addr % 0x4000] = value; + RAM3[addr % 0x4000] = value; break; case 4: - Memory[8][addr % 0x4000] = value; + RAM4[addr % 0x4000] = value; break; case 5: - Memory[9][addr % 0x4000] = value; + RAM5[addr % 0x4000] = value; break; case 6: - Memory[10][addr % 0x4000] = value; + RAM6[addr % 0x4000] = value; break; case 7: - Memory[11][addr % 0x4000] = value; + RAM7[addr % 0x4000] = value; break; } break; @@ -343,7 +374,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - + /* public override void ReInitMemory() { if (Memory.ContainsKey(0)) @@ -406,6 +437,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else Memory.Add(11, RAM7); } + */ /// /// ULA reads the memory at the specified address diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs index e6d82474dc..3b2291a082 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { contentionStartPeriod = 14361; // + LateTiming; contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.Memory[9]; + screen = _machine.RAM5; screenByteCtr = DisplayStart; ULAByteCtr = 0; actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 4be628ba75..b9038e3af1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -26,9 +26,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAMPaged = 0; PagingDisabled = false; - // init addressable memory from ROM and RAM banks - ReInitMemory(); - ULADevice = new ULAPlus3(this); BuzzerDevice = new Buzzer(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index 49c6cd686b..7d79f80544 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -119,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - + /* public override void ReInitMemory() { if (Memory.ContainsKey(0)) @@ -132,6 +132,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else Memory.Add(1, RAM1); } + */ /// /// Sets up the ROM 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 5812c898c4..c6f319cec0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -42,11 +42,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override byte ReadBus(ushort addr) { int divisor = addr / 0x4000; + var index = addr % 0x4000; + // paging logic goes here - var bank = Memory[divisor]; - var index = addr % 0x4000; - return bank[index]; + switch (divisor) + { + case 0: return ROM0[index]; + case 1: return RAM0[index]; + case 2: return RAM1[index]; + case 3: return RAM2[index]; + default: return 0; + } } /// @@ -58,11 +65,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WriteBus(ushort addr, byte value) { int divisor = addr / 0x4000; + var index = addr % 0x4000; + // paging logic goes here - var bank = Memory[divisor]; - var index = addr % 0x4000; - bank[index] = value; + switch (divisor) + { + case 0: + // cannot write to ROM + break; + case 1: + RAM0[index] = value; + break; + case 2: + RAM1[index] = value; + break; + case 3: + RAM2[index] = value; + break; + } // update ULA screen buffer if necessary if ((addr & 49152) == 16384 && _render) @@ -100,6 +121,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } + /* public override void ReInitMemory() { if (Memory.ContainsKey(0)) @@ -127,6 +149,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else Memory.Add(4, RAM3); } + */ /// /// Sets up the ROM diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs index 4c70608d7a..3b4eca3dc3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { contentionStartPeriod = 14335; // + LateTiming; contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.Memory[1]; + screen = _machine.RAM0; screenByteCtr = DisplayStart; ULAByteCtr = 0; actualULAStart = 14340 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index aaf4ffdebd..d9737a40f1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -21,8 +21,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; - ReInitMemory(); - ULADevice = new ULA48(this); BuzzerDevice = new Buzzer(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs index 716c29ec95..5245886f7c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs @@ -45,8 +45,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } private void SyncAllByteArrayDomains() - { - + { SyncByteArrayDomain("ROM0", _machine.ROM0); SyncByteArrayDomain("ROM1", _machine.ROM1); SyncByteArrayDomain("ROM2", _machine.ROM2); @@ -58,8 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SyncByteArrayDomain("RAM4", _machine.RAM4); SyncByteArrayDomain("RAM5", _machine.RAM5); SyncByteArrayDomain("RAM6", _machine.RAM6); - SyncByteArrayDomain("RAM7", _machine.RAM7); - + SyncByteArrayDomain("RAM7", _machine.RAM7); } private void SyncByteArrayDomain(string name, byte[] data) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index 372cb8ed54..39bd7c984f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -53,12 +53,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.SyncState(ser); ser.BeginSection("ZXSpectrum"); - //_cpu.SyncState(ser); _machine.SyncState(ser); ser.Sync("Frame", ref _machine.FrameCount); ser.Sync("LagCount", ref _lagCount); ser.Sync("IsLag", ref _isLag); - //ser.Sync("_memoryDomainsInit", ref _memoryDomainsInit); ser.EndSection(); From baa46f3c99fa9a871542e8ae8d576933b9562eb7 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 15:48:00 +0000 Subject: [PATCH 075/339] Detection method for 48k (or 128k in 48k mode) - needed for 'stop the tape 48k' tzx block command detection --- .../Hardware/Datacorder/DatacorderDevice.cs | 25 +------ .../Machine/SpectrumBase.Memory.cs | 18 +++-- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 56 +--------------- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 12 ++-- .../ZX128Plus2a.Memory.cs | 66 +------------------ .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 66 +------------------ .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 30 --------- 7 files changed, 17 insertions(+), 256 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 88d5f844f3..e71a17d8f0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -484,28 +484,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } _monitorTimeOut = 2000; - } - /* - if ((_machine.GetType() != typeof(ZX128) && - _machine.GetType() != typeof(ZX128Plus2) && - _machine.GetType() != typeof(ZX128Plus3)) || - (_machine.GetType() == typeof(ZX128) || - _machine.GetType() != typeof(ZX128Plus2) || - _machine.GetType() != typeof(ZX128Plus3)) && - _machine._ROMpaged == 1) - { - _machine.Spectrum.OSD_TapeStoppedAuto(); - - if (_currentDataBlockIndex >= _dataBlocks.Count()) - RTZ(); - else - { - Stop(); - } - - _monitorTimeOut = 2000; - } - */ + } break; } @@ -515,8 +494,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _position = 0; _currentDataBlockIndex++; - - if (_currentDataBlockIndex >= _dataBlocks.Count()) { break; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 49a7bbd561..e625ecae77 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -102,21 +102,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Helper function to refresh memory array (probably not the best way to do things) - /// - //public abstract void ReInitMemory(); - - /// - /// Detects whether the 48k rom is resident (or paged in) at 0x0001 + /// Detects whether this is a 48k machine (or a 128k in 48k mode) /// /// public virtual bool IsIn48kMode() { - var data = ReadBus(0x0001); - if (data == 0xaf) + if (this.GetType() == typeof(ZX48) || + this.GetType() == typeof(ZX16) || + PagingDisabled) + { return true; - - return false; + } + else + return false; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index b9eb26cbb8..e76cffbaf7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -208,61 +208,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - /* - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = ROM1; - else - Memory.Add(1, ROM1); - - if (Memory.ContainsKey(2)) - Memory[2] = RAM0; - else - Memory.Add(2, RAM0); - - if (Memory.ContainsKey(3)) - Memory[3] = RAM1; - else - Memory.Add(3, RAM1); - - if (Memory.ContainsKey(4)) - Memory[4] = RAM2; - else - Memory.Add(4, RAM2); - - if (Memory.ContainsKey(5)) - Memory[5] = RAM3; - else - Memory.Add(5, RAM3); - - if (Memory.ContainsKey(6)) - Memory[6] = RAM4; - else - Memory.Add(6, RAM4); - - if (Memory.ContainsKey(7)) - Memory[7] = RAM5; - else - Memory.Add(7, RAM5); - - if (Memory.ContainsKey(8)) - Memory[8] = RAM6; - else - Memory.Add(8, RAM6); - - if (Memory.ContainsKey(9)) - Memory[9] = RAM7; - else - Memory.Add(9, RAM7); - } - */ - + /// /// ULA reads the memory at the specified address /// (No memory contention) 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 0fb70d9bca..130264a4c7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -48,16 +48,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // process tape INs TapeDevice.ReadPort(port, ref result); } + else if ((port & 0xc002) == 0xc000) + { + // AY sound chip + result = (int)AYDevice.PortRead(); + } else { // devices other than the ULA will respond here - // (e.g. the AY sound chip in a 128k spectrum) - - // AY register activate - if ((port & 0xc002) == 0xc000) - { - result = (int)AYDevice.PortRead(); - } // Kempston Mouse (not implemented yet) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 3ce0adcc73..33aa0c2bcf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -374,71 +374,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - /* - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = ROM1; - else - Memory.Add(1, ROM1); - - if (Memory.ContainsKey(2)) - Memory[2] = ROM2; - else - Memory.Add(2, ROM2); - - if (Memory.ContainsKey(3)) - Memory[3] = ROM3; - else - Memory.Add(3, ROM3); - - if (Memory.ContainsKey(4)) - Memory[4] = RAM0; - else - Memory.Add(4, RAM0); - - if (Memory.ContainsKey(5)) - Memory[5] = RAM1; - else - Memory.Add(5, RAM1); - - if (Memory.ContainsKey(6)) - Memory[6] = RAM2; - else - Memory.Add(6, RAM2); - - if (Memory.ContainsKey(7)) - Memory[7] = RAM3; - else - Memory.Add(7, RAM3); - - if (Memory.ContainsKey(8)) - Memory[8] = RAM4; - else - Memory.Add(8, RAM4); - - if (Memory.ContainsKey(9)) - Memory[9] = RAM5; - else - Memory.Add(9, RAM5); - - if (Memory.ContainsKey(10)) - Memory[10] = RAM6; - else - Memory.Add(10, RAM6); - - if (Memory.ContainsKey(11)) - Memory[11] = RAM7; - else - Memory.Add(11, RAM7); - } - */ - + /// /// ULA reads the memory at the specified address /// (No memory contention) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 7ca4147f9d..ad3d846ffd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -374,71 +374,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - /* - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = ROM1; - else - Memory.Add(1, ROM1); - - if (Memory.ContainsKey(2)) - Memory[2] = ROM2; - else - Memory.Add(2, ROM2); - - if (Memory.ContainsKey(3)) - Memory[3] = ROM3; - else - Memory.Add(3, ROM3); - - if (Memory.ContainsKey(4)) - Memory[4] = RAM0; - else - Memory.Add(4, RAM0); - - if (Memory.ContainsKey(5)) - Memory[5] = RAM1; - else - Memory.Add(5, RAM1); - - if (Memory.ContainsKey(6)) - Memory[6] = RAM2; - else - Memory.Add(6, RAM2); - - if (Memory.ContainsKey(7)) - Memory[7] = RAM3; - else - Memory.Add(7, RAM3); - - if (Memory.ContainsKey(8)) - Memory[8] = RAM4; - else - Memory.Add(8, RAM4); - - if (Memory.ContainsKey(9)) - Memory[9] = RAM5; - else - Memory.Add(9, RAM5); - - if (Memory.ContainsKey(10)) - Memory[10] = RAM6; - else - Memory.Add(10, RAM6); - - if (Memory.ContainsKey(11)) - Memory[11] = RAM7; - else - Memory.Add(11, RAM7); - } - */ - + /// /// ULA reads the memory at the specified address /// (No memory contention) 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 c6f319cec0..8b9427d66b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -121,36 +121,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteBus(addr, value); } - /* - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = RAM0; - else - Memory.Add(1, RAM0); - - if (Memory.ContainsKey(2)) - Memory[2] = RAM1; - else - Memory.Add(2, RAM1); - - if (Memory.ContainsKey(3)) - Memory[3] = RAM2; - else - Memory.Add(3, RAM2); - - if (Memory.ContainsKey(4)) - Memory[4] = RAM3; - else - Memory.Add(4, RAM3); - } - */ - /// /// Sets up the ROM /// From 33aa77d8e3547abca4d38c4c4ec644a3e86d9028 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 12 Mar 2018 18:17:06 +0000 Subject: [PATCH 076/339] Removed [CoreConstructor("ZXSpectrum")] identifier (as this was causing an exception when loading roms for other systems --- BizHawk.Emulation.Common/SystemLookup.cs | 3 ++- .../SinclairSpectrum/ZXSpectrum.IEmulator.cs | 11 ++++++++++- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 1 - BizHawk.Emulation.Cores/FileID.cs | 1 + 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Common/SystemLookup.cs b/BizHawk.Emulation.Common/SystemLookup.cs index f8e1a0de3c..4c6c692e46 100644 --- a/BizHawk.Emulation.Common/SystemLookup.cs +++ b/BizHawk.Emulation.Common/SystemLookup.cs @@ -32,7 +32,8 @@ namespace BizHawk.Emulation.Common new SystemInfo { SystemId = "C64", FullName = "Commodore 64" }, new SystemInfo { SystemId = "AppleII", FullName = "Apple II" }, - new SystemInfo { SystemId = "INTV", FullName = "Intellivision" } + new SystemInfo { SystemId = "INTV", FullName = "Intellivision" }, + new SystemInfo { SystemId = "ZXSpectrum", FullName = "Sinclair ZX Spectrum" } }; public SystemInfo this[string systemId] diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs index 33bcc68276..c87c9991d7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -41,7 +41,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - public int Frame => _machine.FrameCount; + public int Frame + { + get + { + if (_machine == null) + return 0; + else + return _machine.FrameCount; + } + } public string SystemId => "ZXSpectrum"; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index d2c7db7abe..bb116f8922 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -18,7 +18,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [ServiceNotApplicable(typeof(IDriveLight))] public partial class ZXSpectrum : IDebuggable, IInputPollable, IStatable, IRegionable { - [CoreConstructor("ZXSpectrum")] public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) { var ser = new BasicServiceProvider(this); diff --git a/BizHawk.Emulation.Cores/FileID.cs b/BizHawk.Emulation.Cores/FileID.cs index edc5252484..1314be8b6f 100644 --- a/BizHawk.Emulation.Cores/FileID.cs +++ b/BizHawk.Emulation.Cores/FileID.cs @@ -42,6 +42,7 @@ namespace BizHawk.Emulation.Cores WS, WSC, NGC, C64, + ZXSpectrum, INT, A26, A52, A78, LNX, From 97c453ae9157afae3d0e8f4fb6b5b10727f97d1b Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 12:48:08 +0000 Subject: [PATCH 077/339] Fixed zx16 machine after memory changes --- .../Machine/SpectrumBase.Memory.cs | 6 --- .../Machine/ZXSpectrum16K/ZX16.cs | 53 ++++++++----------- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 3 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 23 ++++---- 4 files changed, 31 insertions(+), 54 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index e625ecae77..5a21a8db42 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -32,12 +32,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public byte[] RAM6 = new byte[0x4000]; // Bank 6 public byte[] RAM7 = new byte[0x4000]; // Bank 7 - /// - /// Represents the addressable memory space of the spectrum - /// All banks for the emulated system should be added during initialisation - /// - public Dictionary Memory = new Dictionary(); - /// /// Simulates reading from the bus /// Paging should be handled here diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index 7d79f80544..f310b9f7de 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -51,17 +51,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override byte ReadBus(ushort addr) { int divisor = addr / 0x4000; + var index = addr % 0x4000; + // paging logic goes here - if (divisor > 1) + switch (divisor) { - // memory does not exist - return 0xff; + case 0: return ROM0[index]; + case 1: return RAM0[index]; + default: + // memory does not exist + return 0xff; } - - var bank = Memory[divisor]; - var index = addr % 0x4000; - return bank[index]; } /// @@ -73,17 +74,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WriteBus(ushort addr, byte value) { int divisor = addr / 0x4000; + var index = addr % 0x4000; + // paging logic goes here - if (divisor > 1) + switch (divisor) { - // memory does not exist - return; + case 0: + // cannot write to ROM + break; + case 1: + RAM0[index] = value; + break; } - var bank = Memory[divisor]; - var index = addr % 0x4000; - bank[index] = value; + // update ULA screen buffer if necessary + if ((addr & 49152) == 16384 && _render) + ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } /// @@ -97,8 +104,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (ULADevice.IsContended(addr)) CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - CPU.TotalExecutedCycles += 3; - var data = ReadBus(addr); return data; } @@ -115,25 +120,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (ULADevice.IsContended(addr)) CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - CPU.TotalExecutedCycles += 3; - WriteBus(addr, value); } - /* - public override void ReInitMemory() - { - if (Memory.ContainsKey(0)) - Memory[0] = ROM0; - else - Memory.Add(0, ROM0); - - if (Memory.ContainsKey(1)) - Memory[1] = RAM1; - else - Memory.Add(1, RAM1); - } - */ - + /// /// Sets up the ROM /// 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 8b9427d66b..bef6b97317 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -99,8 +99,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override byte ReadMemory(ushort addr) { if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; var data = ReadBus(addr); return data; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index bb116f8922..cb294a8146 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum isPorted: false, isReleased: false)] [ServiceNotApplicable(typeof(IDriveLight))] - public partial class ZXSpectrum : IDebuggable, IInputPollable, IStatable, IRegionable + public partial class ZXSpectrum : IRegionable { public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) { @@ -85,8 +85,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.MemoryCallbacks = MemoryCallbacks; - //HardReset = _machine.HardReset; - //SoftReset = _machine.SoftReset; + HardReset = _machine.HardReset; + SoftReset = _machine.SoftReset; _cpu.FetchMemory = _machine.ReadMemory; _cpu.ReadMemory = _machine.ReadMemory; @@ -102,19 +102,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SoundMixer = new SoundProviderMixer((ISoundProvider)_machine.BuzzerDevice); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - - //dcf = new DCFilter(SoundMixer, 256); + ser.Register(SoundMixer); - - //HardReset(); + HardReset(); SetupMemoryDomains(); } - //public Action HardReset; - //public Action SoftReset; + public Action HardReset; + public Action SoftReset; private readonly Z80A _cpu; private readonly TraceBuffer _tracer; @@ -123,11 +121,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private List _gameInfo; - private SoundProviderMixer SoundMixer; - - private DCFilter dcf; - - //private byte[] _file; + private SoundProviderMixer SoundMixer; + private readonly List _files; public bool DiagRom = false; From a55cf000e7ce90f56015454df3fbd1453c79bf27 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 13:09:44 +0000 Subject: [PATCH 078/339] Some code tidy --- .../Machine/SpectrumBase.Input.cs | 5 + .../Machine/SpectrumBase.Media.cs | 4 +- .../Machine/SpectrumBase.Memory.cs | 70 +++++++++++- .../Machine/SpectrumBase.Port.cs | 1 - .../SinclairSpectrum/Machine/SpectrumBase.cs | 104 ++++++++---------- 5 files changed, 119 insertions(+), 65 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index bf6a679f15..c14a7bcc62 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -28,6 +28,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool pressed_PrevBlock = false; bool pressed_TapeStatus = false; + /// + /// Cycles through all the input callbacks + /// This should be done once per frame + /// public void PollInput() { Spectrum.InputCallbacks.Call(); @@ -247,6 +251,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Signs whether input read has been requested + /// This forms part of the IEmulator LagFrame implementation /// private bool inputRead; public bool InputRead diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index aa1b943823..de2f5206d7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -26,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum protected List diskImages { get; set; } /// - /// The index of the currently 'loaded' tape or disk image + /// The index of the currently 'loaded' tape image /// protected int tapeMediaIndex; public int TapeMediaIndex @@ -63,7 +63,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// The index of the currently 'loaded' tape or disk image + /// The index of the currently 'loaded' disk image /// protected int diskMediaIndex; public int DiskMediaIndex diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 5a21a8db42..48058ee084 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -12,6 +12,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public abstract partial class SpectrumBase { + #region Memory Fields & Properties + /// /// ROM Banks /// @@ -19,7 +21,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public byte[] ROM1 = new byte[0x4000]; public byte[] ROM2 = new byte[0x4000]; public byte[] ROM3 = new byte[0x4000]; - + /// /// RAM Banks /// @@ -32,6 +34,65 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public byte[] RAM6 = new byte[0x4000]; // Bank 6 public byte[] RAM7 = new byte[0x4000]; // Bank 7 + /// + /// Signs that the shadow screen is now displaying + /// Note: normal screen memory in RAM5 is not altered, the ULA just outputs Screen1 instead (RAM7) + /// + protected bool SHADOWPaged; + + /// + /// Index of the current RAM page + /// /// 128k, +2/2a and +3 only + /// + public int RAMPaged; + + /// + /// Signs that all paging is disabled + /// If this is TRUE, then 128k and above machines need a hard reset before paging is allowed again + /// + protected bool PagingDisabled; + + /// + /// Index of the currently paged ROM + /// 128k, +2/2a and +3 only + /// + protected int ROMPaged; + public virtual int _ROMpaged + { + get { return ROMPaged; } + set { ROMPaged = value; } + } + + /* + * +3/+2A only + */ + + /// + /// High bit of the ROM selection (in normal paging mode) + /// + protected bool ROMhigh = false; + + /// + /// Low bit of the ROM selection (in normal paging mode) + /// + protected bool ROMlow = false; + + /// + /// Signs that the +2a/+3 special paging mode is activated + /// + protected bool SpecialPagingMode; + + /// + /// Index of the current special paging mode (0-3) + /// + protected int PagingConfiguration; + + #endregion + + + + #region Memory Related Methods + /// /// Simulates reading from the bus /// Paging should be handled here @@ -95,6 +156,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return value; } + #endregion + + #region Helper Methods + /// /// Detects whether this is a 48k machine (or a 128k in 48k mode) /// @@ -110,6 +175,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else return false; } - + + #endregion } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 874345ba9d..3b53dd28e3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -22,7 +22,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum set { LastULAOutByte = value; } } - /// /// Reads a byte of data from a specified port address /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 9650e8cfd0..c4b241b898 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -12,52 +12,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public abstract partial class SpectrumBase { - /// - /// Index of the currently paged ROM - /// - protected int ROMPaged; - public virtual int _ROMpaged - { - get { return ROMPaged; } - set { ROMPaged = value; } - } - - /// - /// Signs that the shadow screen has been paged in - /// - protected bool SHADOWPaged; - - /// - /// Index of the current RAM page - /// - public int RAMPaged; - - /// - /// Signs that all paging is disabled - /// - protected bool PagingDisabled; - - // +3/+2A only - - protected bool ROMhigh = false; - protected bool ROMlow = false; - - /// - /// Signs that the +2a/+3 special paging mode is activated - /// - protected bool SpecialPagingMode; - - /// - /// Index of the current special paging config - /// - protected int PagingConfiguration; - - /// - /// Signs whether the disk motor is on or off - /// - protected bool DiskMotorState; - - protected bool PrinterPortStrobe; + #region Devices /// /// The calling ZXSpectrum class (piped in via constructor) @@ -103,7 +58,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Holds the currently selected joysticks /// public virtual IJoystick[] JoystickCollection { get; set; } - + + /// + /// Signs whether the disk motor is on or off + /// + protected bool DiskMotorState; + + /// + /// +3/2a printer port strobe + /// + protected bool PrinterPortStrobe; + + #endregion + + #region Emulator State + /// /// Signs whether the frame has ended /// @@ -140,16 +109,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public bool _render; public bool _renderSound; + #endregion + + #region Constants + /// - /// Mask constants + /// Mask constants & misc /// protected const int BORDER_BIT = 0x07; protected const int EAR_BIT = 0x10; protected const int MIC_BIT = 0x08; protected const int TAPE_BIT = 0x40; - protected const int AY_SAMPLE_RATE = 16; + #endregion + + #region Emulation Loop + /// /// Executes a single frame /// @@ -169,7 +145,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } PollInput(); - + while (CurrentFrameCycle < ULADevice.FrameLength) // UlaFrameCycleCount) { // check for interrupt @@ -183,18 +159,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (AYDevice != null) AYDevice.UpdateSound(CurrentFrameCycle); - } + } } - + // we have reached the end of a frame LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; // paint the buffer if needed if (ULADevice.needsPaint && _render) ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); - + if (_renderSound) - BuzzerDevice.EndFrame(); + BuzzerDevice.EndFrame(); FrameCount++; @@ -208,14 +184,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // is this a lag frame? Spectrum.IsLagFrame = !InputRead; } - + + #endregion + + #region Reset Functions + /// /// Hard reset of the emulated machine /// public virtual void HardReset() - { + { //ResetBorder(); - ULADevice.ResetInterrupt(); + ULADevice.ResetInterrupt(); } /// @@ -227,6 +207,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.ResetInterrupt(); } + #endregion + + #region IStatable + public void SyncState(Serializer ser) { ser.BeginSection("ZXMachine"); @@ -236,7 +220,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("_frameCycles", ref _frameCycles); ser.Sync("inputRead", ref inputRead); ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick); - ser.Sync("LastULAOutByte", ref LastULAOutByte); + ser.Sync("LastULAOutByte", ref LastULAOutByte); ser.Sync("ROM0", ref ROM0, false); ser.Sync("ROM1", ref ROM1, false); ser.Sync("ROM2", ref ROM2, false); @@ -273,8 +257,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice.SyncState(ser); ser.EndSection(); - - //ReInitMemory(); } + + #endregion } } From 4e088574cf71307b32f3c2724f0c1ab62cfae2ed Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 13:20:09 +0000 Subject: [PATCH 079/339] more code tidy --- .../Computers/SinclairSpectrum/Machine/ULABase.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index d4e5144b35..216aa33ef9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -5,8 +5,7 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// - /// Another ULA implementation (maybe it will be more performant & accurate) - /// -edit: it is :) + /// ULA (Uncommitted Logic Array) implementation /// public abstract class ULABase : IVideoProvider { @@ -733,7 +732,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - #region Attribution /* From a7ed14cfe1a76dc032596a5eca7e114586ecbc46 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 14:00:29 +0000 Subject: [PATCH 080/339] new IPSG interface ahead of trying to A/B a version of the ColecoHawk AY-3-8910 --- .../BizHawk.Emulation.Cores.csproj | 1 + .../Hardware/Abstraction/IPSG.cs | 66 +++++++++++++++++++ .../Hardware/Datacorder/DatacorderDevice.cs | 2 +- .../Hardware/SoundOuput/AY38912.cs | 2 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 +- 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 272aae7483..ecdd02e979 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -258,6 +258,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs new file mode 100644 index 0000000000..8e8965d7b0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs @@ -0,0 +1,66 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a PSG device (in this case an AY-3-891x) + /// + public interface IPSG : ISoundProvider + { + /// + /// Initlization routine + /// + /// + /// + void Init(int sampleRate, int tStatesPerFrame); + + /// + /// Activates a register + /// + int SelectedRegister { get; set; } + + /// + /// Writes to the PSG + /// + /// + void PortWrite(int value); + + /// + /// Reads from the PSG + /// + int PortRead(); + + /// + /// Resets the PSG + /// + void Reset(); + + /// + /// Called at the start of a frame + /// + void StartFrame(); + + /// + /// called at the end of a frame + /// + void EndFrame(); + + /// + /// Updates the sound based on number of frame cycles + /// + /// + void UpdateSound(int frameCycle); + + /// + /// IStatable serialization + /// + /// + void SyncState(Serializer ser); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index e71a17d8f0..1bcbf2b022 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -594,7 +594,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine.Spectrum.OSD_TapePlayingAuto(); } - _monitorTimeOut = 90; + _monitorTimeOut = 50; } } else diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs index 7443c43dd3..f003ddd6f2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs @@ -7,7 +7,7 @@ using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { - public class AY38912 : ISoundProvider + public class AY38912 : IPSG { private int _tStatesPerFrame; private int _sampleRate; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index c4b241b898..258af9f579 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -42,7 +42,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Device representing the AY-3-8912 chip found in the 128k and up spectrums /// - public AY38912 AYDevice { get; set; } + public IPSG AYDevice { get; set; } /// /// The spectrum keyboard From f612ae043b829e92924cc6857cf4a3d6b47a1462 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 14:08:53 +0000 Subject: [PATCH 081/339] Disabled tape trap auto-stop (this is more trouble than its worth - tzx formats should include 'stopthetape' blocks anyway, and tap files are generally junk and should be discouraged) --- .../Hardware/Datacorder/DatacorderDevice.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 1bcbf2b022..a7db98253d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -614,15 +614,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _monitorTimeOut--; - if (_monitorTimeOut < 2) - { - - } - if (_monitorTimeOut < 0) { - Stop(); - _machine.Spectrum.OSD_TapeStoppedAuto(); + //Stop(); + //_machine.Spectrum.OSD_TapeStoppedAuto(); } } } From 9778cc2644c804be731c65e5cd074c6938d098b4 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 18:52:00 +0000 Subject: [PATCH 082/339] Reduced the AY center channel volume for better balance --- .../Hardware/SoundOuput/AY38912.cs | 25 ++++++++--------- .../Hardware/SoundOuput/Buzzer.cs | 2 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 8 +++--- .../SinclairSpectrum/SoundProviderMixer.cs | 27 +++++++++++++++++++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- 5 files changed, 47 insertions(+), 17 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs index f003ddd6f2..62c3d7829b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs @@ -2,8 +2,6 @@ using BizHawk.Common; using BizHawk.Emulation.Common; using System; -using System.Collections.Generic; -using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -69,9 +67,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void UpdateSound(int currentFrameCycle) { - //if (currentFrameCycle >= _tStatesPerFrame) - //currentFrameCycle = _tStatesPerFrame; - for (int i = 0; i < (currentFrameCycle / AY_SAMPLE_RATE) - _AYCount; i++) { Update(); @@ -113,11 +108,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // the stereo _samples buffer should already have been processed as a part of // ISoundProvider at the end of the last frame - //_samples = new short[_samplesPerFrame * 2]; - //_nsamp = _samplesPerFrame; _sampleCounter = 0; - - //Init(44100, _tStatesPerFrame); } public void EndFrame() @@ -447,15 +438,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private void EndSampleAY() { - averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); - averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelRight] + averagedChannelSamples[ChannelCenter]) / soundSampleCounter); + //averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + + // averagedChannelSamples[ChannelCenter] + + // averagedChannelSamples[ChannelRight]) + // / soundSampleCounter); + + // averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelLeft] + + // averagedChannelSamples[ChannelCenter] + + // averagedChannelSamples[ChannelRight]) + // / soundSampleCounter); + + averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] / 1.5) / soundSampleCounter); + averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelRight] + averagedChannelSamples[ChannelCenter] / 1.5) / soundSampleCounter); soundSampleCounter = 0; } private void SampleAY() { - int ah; + int ah; ah = regs[AY_ENABLE]; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index 0e98c67c29..667fe519be 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -236,7 +236,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeMode) samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 6) : (short)0; else - samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 2) : (short)0; + samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 3) : (short)0; } currentEnd += pulse.Length; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 258af9f579..bc4f9952bf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -146,7 +146,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PollInput(); - while (CurrentFrameCycle < ULADevice.FrameLength) // UlaFrameCycleCount) + while (CurrentFrameCycle < ULADevice.FrameLength) { // check for interrupt ULADevice.CheckForInterrupt(CurrentFrameCycle); @@ -157,8 +157,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // update AY if (_renderSound) { - if (AYDevice != null) - AYDevice.UpdateSound(CurrentFrameCycle); + if (AYDevice != null && CPU.RegPC != 1523) + { + AYDevice.UpdateSound(CurrentFrameCycle); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs index 1b2e37b835..303eda38b7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs @@ -45,6 +45,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum EqualizeVolumes(); } + public SoundProviderMixer(short maxVolume, params ISoundProvider[] soundProviders) + { + SoundProviders = new List(); + + foreach (var s in soundProviders) + { + SoundProviders.Add(new Provider + { + SoundProvider = s, + MaxVolume = maxVolume, + }); + } + + EqualizeVolumes(); + } + public void AddSource(ISoundProvider source) { SoundProviders.Add(new Provider @@ -56,6 +72,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum EqualizeVolumes(); } + public void AddSource(ISoundProvider source, short maxVolume) + { + SoundProviders.Add(new Provider + { + SoundProvider = source, + MaxVolume = maxVolume + }); + + EqualizeVolumes(); + } + public void DisableSource(ISoundProvider source) { var sp = SoundProviders.Where(a => a.SoundProvider == source); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index cb294a8146..edab502824 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -99,7 +99,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Register(_cpu); ser.Register(_machine.ULADevice); - SoundMixer = new SoundProviderMixer((ISoundProvider)_machine.BuzzerDevice); + SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); From 6c01ba3c6af0966ecf6df9f7871fa79ffc0e436a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 19:01:13 +0000 Subject: [PATCH 083/339] StereoSound core setting fixed (was not used after core initliazation --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index edab502824..f242fc10d4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -102,7 +102,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - + SoundMixer.Stereo = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).StereoSound; + + ser.Register(SoundMixer); From 5a2b0ae6a660c18383b5218d334ac09d3e73b046 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 19:16:12 +0000 Subject: [PATCH 084/339] Added joysticks 2 & 3 and removed TapeControls from VirtualPad schema (they are not needed there) --- .../tools/VirtualPads/schema/ZXSpectrumSchema.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs index 1995d75757..cdbf9228c0 100644 --- a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs +++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs @@ -11,19 +11,21 @@ namespace BizHawk.Client.EmuHawk { public IEnumerable GetPadSchemas(IEmulator core) { - yield return KempstonJoystick(1); + yield return Joystick(1); + yield return Joystick(2); + yield return Joystick(3); yield return Keyboard(); - yield return TapeDevice(); + //yield return TapeDevice(); } - private static PadSchema KempstonJoystick(int controller) + private static PadSchema Joystick(int controller) { return new PadSchema { - DisplayName = "Player " + controller + " (Kempton Joystick)", + DisplayName = "Joystick " + controller, IsConsole = false, DefaultSize = new Size(174, 74), - MaxSize = new Size(174, 74), + MaxSize = new Size(174, 74), Buttons = new[] { new PadSchema.ButtonSchema From d23dc0a296b9053cbcb61e2c61347fa204024397 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 20:31:13 +0000 Subject: [PATCH 085/339] Finally fixed tape trap auto loading/stopping routines --- .../Hardware/Datacorder/DatacorderDevice.cs | 22 ++++++++++++- .../Machine/SpectrumBase.Memory.cs | 33 +++++++++++++++++++ .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 2 ++ .../ZX128Plus2a.Memory.cs | 2 ++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 2 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 2 ++ .../Machine/ZXSpectrum16K/ZX16.cs | 4 ++- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 27 +++++++++++++-- 8 files changed, 89 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index a7db98253d..90bb8af1df 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -548,7 +548,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// An iteration of the monitor process /// public void MonitorRead() - { + { long cpuCycle = _cpu.TotalExecutedCycles; int delta = (int)(cpuCycle - _lastINCycle); _lastINCycle = cpuCycle; @@ -607,11 +607,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _monitorLastPC = _cpu.RegPC; } + public void AutoStopTape() + { + if (!_tapeIsPlaying) + return; + + Stop(); + _machine.Spectrum.OSD_TapeStoppedAuto(); + } + + public void AutoStartTape() + { + if (_tapeIsPlaying) + return; + + Play(); + _machine.Spectrum.OSD_TapePlayingAuto(); + } private void MonitorFrame() { + /* if (_tapeIsPlaying && _machine.Spectrum.Settings.AutoLoadTape) { + _monitorTimeOut--; if (_monitorTimeOut < 0) @@ -620,6 +639,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //_machine.Spectrum.OSD_TapeStoppedAuto(); } } + */ } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 48058ee084..a25bbc8186 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -176,6 +176,39 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; } + + public virtual void TestForTapeTraps(int addr) + { + if (!TapeDevice.TapeIsPlaying) + { + if (addr == 8) + { + TapeDevice?.AutoStopTape(); + return; + } + + if (addr == 4223) + { + TapeDevice?.AutoStopTape(); + return; + } + + if (addr == 83) + { + TapeDevice?.AutoStopTape(); + return; + } + } + else + { + if (addr == 1366) + { + TapeDevice?.AutoStartTape(); + return; + } + } + } + #endregion } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index e76cffbaf7..e769fc46ef 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -54,6 +54,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // ROM 0x000 case 0: + TestForTapeTraps(addr % 0x4000); + if (ROMPaged == 0) result = ROM0[addr % 0x4000]; else diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 33aa0c2bcf..265a413002 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -137,9 +137,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { case 0: result = ROM0[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); break; case 1: result = ROM1[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); break; case 2: result = ROM2[addr % 0x4000]; 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 4f0a9ee47b..24ac1f5f97 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -45,7 +45,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InputRead = true; // tape loading monitor cycle - TapeDevice.MonitorRead(); + //TapeDevice.MonitorRead(); // process tape INs TapeDevice.ReadPort(port, ref result); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index ad3d846ffd..bead71f975 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -137,9 +137,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { case 0: result = ROM0[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); break; case 1: result = ROM1[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); break; case 2: result = ROM2[addr % 0x4000]; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index f310b9f7de..3f6499e916 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -57,7 +57,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (divisor) { - case 0: return ROM0[index]; + case 0: + TestForTapeTraps(addr % 0x4000); + return ROM0[index]; case 1: return RAM0[index]; default: // memory does not exist diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index f242fc10d4..c09598667c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -15,8 +15,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum "Asnivor", isPorted: false, isReleased: false)] - [ServiceNotApplicable(typeof(IDriveLight))] - public partial class ZXSpectrum : IRegionable + public partial class ZXSpectrum : IRegionable, IDriveLight { public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) { @@ -238,6 +237,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + #region IDriveLight + + public bool DriveLightEnabled + { + get + { + return true; + } + } + + public bool DriveLightOn + { + get + { + if (_machine != null && + _machine.TapeDevice != null && + _machine.TapeDevice.TapeIsPlaying) + return true; + + return false; + } + } + + #endregion } } From 6d66eee45998a4f716db2128f5f9dae88eaa8c02 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 20:38:43 +0000 Subject: [PATCH 086/339] Disabled AY chip when memory paging is disabled (i.e. 48k mode) --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index bc4f9952bf..b7fcee7e93 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -140,7 +140,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_renderSound) { BuzzerDevice.StartFrame(); - if (AYDevice != null) + if (AYDevice != null && !PagingDisabled) AYDevice.StartFrame(); } @@ -157,7 +157,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // update AY if (_renderSound) { - if (AYDevice != null && CPU.RegPC != 1523) + if (AYDevice != null && !PagingDisabled) { AYDevice.UpdateSound(CurrentFrameCycle); } From 7739c0dee103ff5a076060fd901de251a5df166b Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 22:09:46 +0000 Subject: [PATCH 087/339] Handling loadstate issues for different machine configurations --- .../Machine/SpectrumBase.Input.cs | 27 +++++++++++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 1 + .../ZXSpectrum.Controllers.cs | 13 ++++++ .../SinclairSpectrum/ZXSpectrum.IStatable.cs | 46 +++++++++++++++---- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 3 ++ 5 files changed, 82 insertions(+), 8 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index c14a7bcc62..978d815c52 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -19,6 +19,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string PrevBlock = "Prev Tape Block"; string TapeStatus = "Get Tape Status"; + string HardResetStr = "Hard Reset"; + string SoftResetStr = "Soft Reset"; + bool pressed_Play = false; bool pressed_Stop = false; bool pressed_RTZ = false; @@ -27,6 +30,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool pressed_NextBlock = false; bool pressed_PrevBlock = false; bool pressed_TapeStatus = false; + bool pressed_HardReset = false; + bool pressed_SoftReset = false; /// /// Cycles through all the input callbacks @@ -191,6 +196,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else pressed_TapeStatus = false; + + if (Spectrum._controller.IsPressed(HardResetStr)) + { + if (!pressed_HardReset) + { + HardReset(); + pressed_HardReset = true; + } + } + else + pressed_HardReset = false; + + if (Spectrum._controller.IsPressed(SoftResetStr)) + { + if (!pressed_SoftReset) + { + SoftReset(); + pressed_SoftReset = true; + } + } + else + pressed_SoftReset = false; } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index b7fcee7e93..d5c1fff9a6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -207,6 +207,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { //ResetBorder(); ULADevice.ResetInterrupt(); + CPU.RegPC = 0; } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index 1ba7dd5b74..38a281ecca 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -82,6 +82,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum definition.CategoryLabels[s] = "Keyboard"; } + // Datacorder (tape device) + List power = new List + { + // Tape functions + "Soft Reset", "Hard Reset" + }; + + foreach (var s in power) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Power"; + } + // Datacorder (tape device) List tape = new List { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index 39bd7c984f..0cd1c37a8d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -50,19 +50,49 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ms.Close(); core = ms.ToArray(); } - _cpu.SyncState(ser); - ser.BeginSection("ZXSpectrum"); - _machine.SyncState(ser); - ser.Sync("Frame", ref _machine.FrameCount); - ser.Sync("LagCount", ref _lagCount); - ser.Sync("IsLag", ref _isLag); + - ser.EndSection(); + if (ser.IsWriter) + { + ser.SyncEnum("_machineType", ref _machineType); + _cpu.SyncState(ser); + ser.BeginSection("ZXSpectrum"); + _machine.SyncState(ser); + ser.Sync("Frame", ref _machine.FrameCount); + ser.Sync("LagCount", ref _lagCount); + ser.Sync("IsLag", ref _isLag); + ser.EndSection(); + } + if (ser.IsReader) { - SyncAllByteArrayDomains(); + var tmpM = _machineType; + ser.SyncEnum("_machineType", ref _machineType); + if (tmpM != _machineType) + { + string msg = "SAVESTATE FAILED TO LOAD!!\n\n"; + msg += "Current Configuration: " + _machineType.ToString(); + msg += "\n"; + msg += "Saved Configuration: " + tmpM.ToString(); + msg += "\n\n"; + msg += "If you with to load this SaveState ensure that you have to correct machine configuration selected, reboot the core, then try again."; + CoreComm.ShowMessage(msg); + _machineType = tmpM; + } + else + { + _cpu.SyncState(ser); + ser.BeginSection("ZXSpectrum"); + _machine.SyncState(ser); + ser.Sync("Frame", ref _machine.FrameCount); + ser.Sync("LagCount", ref _lagCount); + ser.Sync("IsLag", ref _isLag); + ser.EndSection(); + + SyncAllByteArrayDomains(); + } } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index c09598667c..27eb361a88 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -185,9 +185,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return result; } + private MachineType _machineType; private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, List files, List joys) { + _machineType = machineType; + // setup the emulated model based on the MachineType switch (machineType) { From df5cf0d85ffee2d5044874eecde3c3b486a17ccc Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 22:13:26 +0000 Subject: [PATCH 088/339] Type fix for sir feos --- .../Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index 0cd1c37a8d..8192f219e1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -77,7 +77,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum msg += "\n"; msg += "Saved Configuration: " + tmpM.ToString(); msg += "\n\n"; - msg += "If you with to load this SaveState ensure that you have to correct machine configuration selected, reboot the core, then try again."; + msg += "If you with to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again."; CoreComm.ShowMessage(msg); _machineType = tmpM; } From 8234b2acfa4c94e0cc2d70fb3f94141dc7be8b44 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 13 Mar 2018 22:15:53 +0000 Subject: [PATCH 089/339] more typos. FML --- .../Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index 8192f219e1..bbd45c713d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -77,7 +77,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum msg += "\n"; msg += "Saved Configuration: " + tmpM.ToString(); msg += "\n\n"; - msg += "If you with to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again."; + msg += "If you wish to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again."; CoreComm.ShowMessage(msg); _machineType = tmpM; } From 7a36f913ece9c6cbe522b759872f4c8323b32a4c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 15 Mar 2018 16:32:26 +0000 Subject: [PATCH 090/339] New AY-3-8912 implementation. Better sounding and more performant --- .../BizHawk.Emulation.Cores.csproj | 2 +- .../Hardware/Abstraction/IPSG.cs | 5 +- .../Hardware/Datacorder/DatacorderDevice.cs | 4 + .../Hardware/Input/StandardKeyboard.cs | 3 + .../Hardware/SoundOuput/AY38912.cs | 718 ---------------- .../Hardware/SoundOuput/AYChip.cs | 802 ++++++++++++++++++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 15 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 69 +- .../Machine/ZXSpectrum128K/ZX128.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 229 ++--- .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 2 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 109 +-- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 2 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 16 +- .../SinclairSpectrum/ZXSpectrum.IStatable.cs | 8 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 7 +- 16 files changed, 1020 insertions(+), 973 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index ecdd02e979..b9790199e8 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -263,7 +263,7 @@ - + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs index 8e8965d7b0..a6028aa2d6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs @@ -11,7 +11,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Represents a PSG device (in this case an AY-3-891x) /// - public interface IPSG : ISoundProvider + public interface IPSG : ISoundProvider, IPortIODevice { /// /// Initlization routine @@ -24,7 +24,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Activates a register /// int SelectedRegister { get; set; } - + /// /// Writes to the PSG /// @@ -35,6 +35,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Reads from the PSG /// int PortRead(); + /// /// Resets the PSG diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 90bb8af1df..066570f6c9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -158,6 +158,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeIsPlaying) return; + _machine.BuzzerDevice.SetTapeMode(true); + _machine.Spectrum.OSD_TapePlaying(); // update the lastCycle @@ -210,6 +212,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!_tapeIsPlaying) return; + _machine.BuzzerDevice.SetTapeMode(false); + _machine.Spectrum.OSD_TapeStopped(); // sign that the tape is no longer playing diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs index f4289edf92..0616003465 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs @@ -339,6 +339,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum (for instance by XOR A/IN A,(FE)) is one, no key is pressed */ + if ((port & 0x0001) != 0) + return false; + if ((port & 0x8000) == 0) { result &= KeyLine[7]; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs deleted file mode 100644 index 62c3d7829b..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs +++ /dev/null @@ -1,718 +0,0 @@ - -using BizHawk.Common; -using BizHawk.Emulation.Common; -using System; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - public class AY38912 : IPSG - { - private int _tStatesPerFrame; - private int _sampleRate; - private int _samplesPerFrame; - private int _tStatesPerSample; - private int _sampleCounter; - const int AY_SAMPLE_RATE = 16; - private int _AYCyclesPerFrame; - private int _nsamp; - private int _AYCount; - - - /// - /// The final sample buffer - /// - private short[] _samples = new short[0]; - - /// - /// Number of samples in one frame - /// - public int SamplesPerFrame - { - get { return _samplesPerFrame; } - set { _samplesPerFrame = value; } - } - - /// - /// Number of TStates in each sample - /// - public int TStatesPerSample - { - get { return _tStatesPerSample; } - set { _tStatesPerSample = value; } - } - - #region Construction & Initialisation - - public AY38912() - { - Reset(); - } - - /// - /// Initialises the AY chip - /// - public void Init(int sampleRate, int tStatesPerFrame) - { - _sampleRate = sampleRate; - _tStatesPerFrame = tStatesPerFrame; - _tStatesPerSample = 79; - _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; - _AYCyclesPerFrame = _tStatesPerFrame / AY_SAMPLE_RATE; - - _samples = new short[_samplesPerFrame * 2]; - _nsamp = _samplesPerFrame; - } - - #endregion - - public void UpdateSound(int currentFrameCycle) - { - for (int i = 0; i < (currentFrameCycle / AY_SAMPLE_RATE) - _AYCount; i++) - { - Update(); - SampleAY(); - _AYCount++; - } - - // calculate how many samples must be processed - int samplesToGenerate = (currentFrameCycle / _tStatesPerSample) - (_sampleCounter / 2); - - // begin generation - if (samplesToGenerate > 0) - { - // ensure the required resolution - while (soundSampleCounter < 4) - { - SampleAY(); - } - EndSampleAY(); - - // generate needed samples - for (int i = 0; i < samplesToGenerate; i++) - { - _samples[_sampleCounter++] = (short)(averagedChannelSamples[0]); - _samples[_sampleCounter++] = (short)(averagedChannelSamples[1]); - - samplesToGenerate--; - } - - averagedChannelSamples[0] = 0; - averagedChannelSamples[1] = 0; - averagedChannelSamples[2] = 0; - } - } - - public void StartFrame() - { - _AYCount = 0; - - // the stereo _samples buffer should already have been processed as a part of - // ISoundProvider at the end of the last frame - _sampleCounter = 0; - } - - public void EndFrame() - { - } - - - public void Reset() - { - // reset volumes - for (int i = 0; i < 16; i++) - AY_SpecVolumes[i] = (short)(AY_Volumes[i] * 8191); - - soundSampleCounter = 0; - regs[AY_NOISEPER] = 0xFF; - noiseOut = 0x01; - envelopeVolume = 0; - noiseCount = 0; - - // reset state of all channels - for (int f = 0; f < 3; f++) - { - channel_count[f] = 0; - channel_mix[f] = 0; - channel_out[f] = 0; - averagedChannelSamples[f] = 0; - } - - envelopeCount = 0; - randomSeed = 1; - selectedRegister = 0; - } - - #region IStatable - - public void SyncState(Serializer ser) - { - ser.BeginSection("AY38912"); - ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); - ser.Sync("_sampleRate", ref _sampleRate); - ser.Sync("_samplesPerFrame", ref _samplesPerFrame); - ser.Sync("_tStatesPerSample", ref _tStatesPerSample); - ser.Sync("_sampleCounter", ref _sampleCounter); - - ser.Sync("ChannelLeft", ref ChannelLeft); - ser.Sync("ChannelRight", ref ChannelRight); - ser.Sync("ChannelCenter", ref ChannelCenter); - ser.Sync("Regs", ref regs, false); - ser.Sync("NoiseOut", ref noiseOut); - ser.Sync("envelopeVolume", ref envelopeVolume); - ser.Sync("noiseCount", ref noiseCount); - ser.Sync("envelopeCount", ref envelopeCount); - ser.Sync("randomSeed", ref randomSeed); - ser.Sync("envelopeClock", ref envelopeClock); - ser.Sync("selectedRegister", ref selectedRegister); - ser.Sync("soundSampleCounter", ref soundSampleCounter); - ser.Sync("stereoSound", ref stereoSound); - ser.Sync("sustaining", ref sustaining); - ser.Sync("sustain", ref sustain); - ser.Sync("alternate", ref alternate); - ser.Sync("attack", ref attack); - ser.Sync("envelopeStep", ref envelopeStep); - - ser.Sync("channel_out", ref channel_out, false); - ser.Sync("channel_count", ref channel_count, false); - ser.Sync("averagedChannelSamples", ref averagedChannelSamples, false); - ser.Sync("channel_mix", ref channel_mix, false); - - ser.Sync("AY_SpecVolumes", ref AY_SpecVolumes, false); - - ser.Sync("_samples", ref _samples, false); - ser.Sync("_nsamp", ref _nsamp); - ser.EndSection(); - } - - #endregion - - #region AY Sound Implementation - - /* - Based on the AYSound class from ArjunNair's Zero-Emulator - https://github.com/ArjunNair/Zero-Emulator/ - *MIT LICENSED* - - The MIT License (MIT) - Copyright (c) 2009 Arjun Nair - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files - (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, - publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, - subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE - FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - - /// - /// Register constants - /// - private const byte AY_A_FINE = 0; - private const byte AY_A_COARSE = 1; - private const byte AY_B_FINE = 2; - private const byte AY_B_COARSE = 3; - private const byte AY_C_FINE = 4; - private const byte AY_C_COARSE = 5; - private const byte AY_NOISEPER = 6; - private const byte AY_ENABLE = 7; - private const byte AY_A_VOL = 8; - private const byte AY_B_VOL = 9; - private const byte AY_C_VOL = 10; - private const byte AY_E_FINE = 11; - private const byte AY_E_COARSE = 12; - private const byte AY_E_SHAPE = 13; - private const byte AY_PORT_A = 14; - private const byte AY_PORT_B = 15; - - /// - /// Channels - /// - internal enum Channel - { - A, B, C - } - - /// - /// ACB configuration - /// - private int ChannelLeft = 0; - private int ChannelRight = 1; //2 if ABC - private int ChannelCenter = 2; //1 if ABC - - /// - /// Register storage - /// - private int[] regs = new int[16]; - - /// - /// State - /// - private int noiseOut; - private int envelopeVolume; - private int noiseCount; - private int envelopeCount; - private ulong randomSeed; - private byte envelopeClock = 0; - private int selectedRegister; - public ushort soundSampleCounter; - private bool stereoSound = false; - private bool sustaining; - private bool sustain; - private bool alternate; - private int attack; - private int envelopeStep; - - /// - /// Buffer arrays - /// - private int[] channel_out = new int[3]; - private int[] channel_count = new int[3]; - private int[] averagedChannelSamples = new int[3]; - private short[] channel_mix = new short[3]; - - /// - /// Measurements from comp.sys.sinclair (2001 Matthew Westcott) - /// - private float[] AY_Volumes = - { - 0.0000f, 0.0137f, 0.0205f, 0.0291f, - 0.0423f, 0.0618f, 0.0847f, 0.1369f, - 0.1691f, 0.2647f, 0.3527f, 0.4499f, - 0.5704f, 0.6873f, 0.8482f, 1.0000f - }; - - /// - /// Volume storage (short) - /// - private short[] AY_SpecVolumes = new short[16]; - - /// - /// Sets the ACB configuration - /// - /// - public void SetSpeakerACB(bool val) - { - // ACB - if (val) - { - ChannelCenter = 2; - ChannelRight = 1; - } - // ABC - else - { - ChannelCenter = 1; - ChannelRight = 2; - } - } - - /// - /// Utility method to set all registers externally - /// - /// - public void SetRegisters(byte[] _regs) - { - for (int f = 0; f < 16; f++) - regs[f] = _regs[f]; - } - - /// - /// Utility method to get all registers externally - /// - /// - public byte[] GetRegisters() - { - byte[] newArray = new byte[16]; - for (int f = 0; f < 16; f++) - newArray[f] = (byte)(regs[f] & 0xff); - return newArray; - } - - /// - /// Selected Register property - /// - public int SelectedRegister - { - get { return selectedRegister; } - set { if (value < 16) selectedRegister = value; } - } - - /// - /// Simulates a port write to the AY chip - /// - /// - public void PortWrite(int val) - { - switch (SelectedRegister) - { - // not implemented / necessary - case AY_A_FINE: - case AY_B_FINE: - case AY_C_FINE: - case AY_E_FINE: - case AY_E_COARSE: - break; - - case AY_A_COARSE: - case AY_B_COARSE: - case AY_C_COARSE: - val &= 0x0f; - break; - - case AY_NOISEPER: - case AY_A_VOL: - case AY_B_VOL: - case AY_C_VOL: - val &= 0x1f; - break; - - case AY_ENABLE: - /* - if ((lastEnable == -1) || ((lastEnable & 0x40) != (regs[AY_ENABLE] & 0x40))) { - SelectedRegister = ((regs[AY_ENABLE] & 0x40) > 0 ? regs[AY_PORT_B] : 0xff); - } - if ((lastEnable == -1) || ((lastEnable & 0x80) != (regs[AY_ENABLE] & 0x80))) { - PortWrite((regs[AY_ENABLE] & 0x80) > 0 ? regs[AY_PORT_B] : 0xff); - } - lastEnable = regs[AY_ENABLE];*/ - break; - - case AY_E_SHAPE: - val &= 0x0f; - attack = ((val & 0x04) != 0 ? 0x0f : 0x00); - // envelopeCount = 0; - if ((val & 0x08) == 0) - { - /* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */ - sustain = true; - alternate = (attack != 0); - } - else - { - sustain = (val & 0x01) != 0; - alternate = (val & 0x02) != 0; - } - envelopeStep = 0x0f; - sustaining = false; - envelopeVolume = (envelopeStep ^ attack); - break; - - case AY_PORT_A: - /* - if ((regs[AY_ENABLE] & 0x40) > 0) { - selectedRegister = regs[AY_PORT_A]; - }*/ - break; - - case AY_PORT_B: - /* - if ((regs[AY_ENABLE] & 0x80) > 0) { - PortWrite(regs[AY_PORT_A]); - }*/ - break; - } - - regs[SelectedRegister] = val; - } - - /// - /// Simulates port reads from the AY chip - /// - /// - public int PortRead() - { - if (SelectedRegister == AY_PORT_B) - { - if ((regs[AY_ENABLE] & 0x80) == 0) - return 0xff; - else - return regs[AY_PORT_B]; - } - - return regs[selectedRegister]; - } - - private void EndSampleAY() - { - //averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + - // averagedChannelSamples[ChannelCenter] + - // averagedChannelSamples[ChannelRight]) - // / soundSampleCounter); - - // averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelLeft] + - // averagedChannelSamples[ChannelCenter] + - // averagedChannelSamples[ChannelRight]) - // / soundSampleCounter); - - averagedChannelSamples[0] = (short)((averagedChannelSamples[ChannelLeft] + averagedChannelSamples[ChannelCenter] / 1.5) / soundSampleCounter); - averagedChannelSamples[1] = (short)((averagedChannelSamples[ChannelRight] + averagedChannelSamples[ChannelCenter] / 1.5) / soundSampleCounter); - - soundSampleCounter = 0; - } - - private void SampleAY() - { - int ah; - - ah = regs[AY_ENABLE]; - - channel_mix[(int)Channel.A] = MixChannel(ah, regs[AY_A_VOL], (int)Channel.A); - - ah >>= 1; - channel_mix[(int)Channel.B] = MixChannel(ah, regs[AY_B_VOL], (int)Channel.B); - - ah >>= 1; - channel_mix[(int)Channel.C] = MixChannel(ah, regs[AY_C_VOL], (int)Channel.C); - - averagedChannelSamples[0] += channel_mix[(int)Channel.A]; - averagedChannelSamples[1] += channel_mix[(int)Channel.B]; - averagedChannelSamples[2] += channel_mix[(int)Channel.C]; - soundSampleCounter++; - } - - private short MixChannel(int ah, int cl, int chan) - { - int al = channel_out[chan]; - int bl, bh; - bl = ah; - bh = ah; - bh &= 0x1; - bl >>= 3; - - al |= (bh); //Tone | AY_ENABLE - bl |= (noiseOut); //Noise | AY_ENABLE - al &= bl; - - if ((al != 0)) - { - if ((cl & 16) != 0) - cl = envelopeVolume; - - cl &= 15; - - //return (AY_Volumes[cl]); - return (AY_SpecVolumes[cl]); - } - return 0; - } - - /// - /// Gets the tone period for the specified channel - /// - /// - /// - private int TonePeriod(int channel) - { - return (regs[(channel) << 1] | ((regs[((channel) << 1) | 1] & 0x0f) << 8)); - } - - /// - /// Gets the noise period for the specified channel - /// - /// - private int NoisePeriod() - { - return (regs[AY_NOISEPER] & 0x1f); - } - - /// - /// Gets the envelope period for the specified channel - /// - /// - private int EnvelopePeriod() - { - return ((regs[AY_E_FINE] | (regs[AY_E_COARSE] << 8))); - } - - /// - /// Gets the noise enable value for the specified channel - /// - /// - /// - private int NoiseEnable(int channel) - { - return ((regs[AY_ENABLE] >> (3 + channel)) & 1); - } - - /// - /// Gets the tone enable value for the specified channel - /// - /// - /// - private int ToneEnable(int channel) - { - return ((regs[AY_ENABLE] >> (channel)) & 1); - } - - /// - /// Gets the tone envelope value for the specified channel - /// - /// - /// - private int ToneEnvelope(int channel) - { - //return ((regs[AY_A_VOL + channel] & 0x10) >> 4); - return ((regs[AY_A_VOL + channel] >> 4) & 0x1); - } - - /// - /// Updates noise - /// - private void UpdateNoise() - { - noiseCount++; - if (noiseCount >= NoisePeriod() && (noiseCount > 4)) - { - /* Is noise output going to change? */ - if (((randomSeed + 1) & 2) != 0) /* (bit0^bit1)? */ - { - noiseOut ^= 1; - } - - /* The Random Number Generator of the 8910 is a 17-bit shift */ - /* register. The input to the shift register is bit0 XOR bit3 */ - /* (bit0 is the output). This was verified on AY-3-8910 and YM2149 chips. */ - - /* The following is a fast way to compute bit17 = bit0^bit3. */ - /* Instead of doing all the logic operations, we only check */ - /* bit0, relying on the fact that after three shifts of the */ - /* register, what now is bit3 will become bit0, and will */ - /* invert, if necessary, bit14, which previously was bit17. */ - if ((randomSeed & 1) != 0) - randomSeed ^= 0x24000; /* This version is called the "Galois configuration". */ - randomSeed >>= 1; - noiseCount = 0; - } - } - - /// - /// Updates envelope - /// - private void UpdateEnvelope() - { - /* update envelope */ - if (!sustaining) - { - envelopeCount++; - if ((envelopeCount >= EnvelopePeriod())) - { - envelopeStep--; - - /* check envelope current position */ - if (envelopeStep < 0) - { - if (sustain) - { - if (alternate) - attack ^= 0x0f; - sustaining = true; - envelopeStep = 0; - } - else - { - /* if CountEnv has looped an odd number of times (usually 1), */ - /* invert the output. */ - if (alternate && ((envelopeStep & (0x0f + 1)) != 0) && (envelopeCount > 4)) - attack ^= 0x0f; - - envelopeStep &= 0x0f; - } - } - envelopeCount = 0; - } - } - envelopeVolume = (envelopeStep ^ attack); - } - - - public void Update() - { - envelopeClock ^= 1; - - if (envelopeClock == 1) - { - envelopeCount++; - - //if ((((regs[AY_A_VOL + 0] & 0x10) >> 4) & (((regs[AY_A_VOL + 1] & 0x10) >> 4) & ((regs[AY_A_VOL + 2] & 0x10) >> 4))) != 1) - //if ((((regs[AY_A_VOL + 0] >> 4) & 0x1) & (((regs[AY_A_VOL + 1] >> 4) & 0x1) & ((regs[AY_A_VOL + 2] >> 4) & 0x1))) != 0) - if (((regs[AY_A_VOL + 0] & 0x10) & (regs[AY_A_VOL + 1] & 0x10) & (regs[AY_A_VOL + 2] & 0x10)) != 1) - { - // update envelope - if (!sustaining) - UpdateEnvelope(); - - envelopeVolume = (envelopeStep ^ attack); - } - } - - // update noise - if ((regs[AY_ENABLE] & 0x38) != 0x38) - { - UpdateNoise(); - } - - // update channels - channel_count[0]++; - int regs1 = (regs[1] & 0x0f) << 8; - if (((regs[0] | regs1) > 4) && (channel_count[0] >= (regs[0] | regs1))) - { - channel_out[0] ^= 1; - channel_count[0] = 0; - } - - int regs3 = (regs[3] & 0x0f) << 8; - channel_count[1]++; - if (((regs[2] | regs3) > 4) && (channel_count[1] >= (regs[2] | regs3))) - { - channel_out[1] ^= 1; - channel_count[1] = 0; - } - - int regs5 = (regs[5] & 0x0f) << 8; - channel_count[2]++; - if (((regs[4] | regs5) > 4) && (channel_count[2] >= (regs[4] | regs5))) - { - channel_out[2] ^= 1; - channel_count[2] = 0; - } - } - - - #endregion - - #region ISoundProvider - - public bool CanProvideAsync => false; - - public SyncSoundMode SyncMode => SyncSoundMode.Sync; - - public void SetSyncMode(SyncSoundMode mode) - { - if (mode != SyncSoundMode.Sync) - throw new InvalidOperationException("Only Sync mode is supported."); - } - - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public void DiscardSamples() - { - - } - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - samples = _samples; - nsamp = _nsamp; - } - - #endregion - - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs new file mode 100644 index 0000000000..06817afe19 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs @@ -0,0 +1,802 @@ + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// AY-3-8912 Emulated Device + /// + /// Based heavily on the YM-2149F / AY-3-8910 emulator used in Unreal Speccy + /// (Originally created under Public Domain license by SMT jan.2006) + /// + /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.cpp + /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.h + /// + public class AYChip : IPSG + { + #region Device Fields + + /// + /// The emulated machine (passed in via constructor) + /// + private SpectrumBase _machine; + + private int _tStatesPerFrame; + private int _sampleRate; + private int _samplesPerFrame; + private int _tStatesPerSample; + private short[] _audioBuffer; + private int _audioBufferIndex; + private int _lastStateRendered; + + #endregion + + #region Construction & Initialization + + /// + /// Main constructor + /// + public AYChip(SpectrumBase machine) + { + _machine = machine; + } + + /// + /// Initialises the AY chip + /// + public void Init(int sampleRate, int tStatesPerFrame) + { + InitTiming(sampleRate, tStatesPerFrame); + UpdateVolume(); + Reset(); + } + + #endregion + + #region IPortIODevice + + public bool ReadPort(ushort port, ref int value) + { + if (port != 0xfffd) + { + // port read is not addressing this device + return false; + } + + value = PortRead(); + + return true; + } + + public bool WritePort(ushort port, int value) + { + if (port == 0xfffd) + { + // register select + SelectedRegister = value & 0x0f; + return true; + } + else if (port == 0xbffd) + { + // Update the audiobuffer based on the current CPU cycle + // (this process the previous data BEFORE writing to the currently selected register) + int d = (int)(_machine.CurrentFrameCycle); + BufferUpdate(d); + + // write to register + PortWrite(value); + return true; + } + return false; + } + + #endregion + + #region AY Implementation + + #region Public Properties + + /// + /// AY mixer panning configuration + /// + [Flags] + public enum AYPanConfig + { + MONO = 0, + ABC = 1, + ACB = 2, + BAC = 3, + BCA = 4, + CAB = 5, + CBA = 6, + } + + /// + /// The AY panning configuration + /// + public AYPanConfig PanningConfiguration + { + get + { + return _currentPanTab; + } + set + { + if (value != _currentPanTab) + { + _currentPanTab = value; + UpdateVolume(); + } + } + } + + /// + /// The AY chip output volume + /// (0 - 100) + /// + public int Volume + { + get + { + return _volume; + } + set + { + value = Math.Max(0, value); + value = Math.Max(100, value); + if (_volume == value) + { + return; + } + _volume = value; + UpdateVolume(); + } + } + + /// + /// The currently selected register + /// + public int SelectedRegister + { + get { return _activeRegister; } + set + { + _activeRegister = (byte)value; + } + } + + #endregion + + #region Public Methods + + /// + /// Resets the PSG + /// + public void Reset() + { + /* + _noiseVal = 0x0FFFF; + _outABC = 0; + _outNoiseABC = 0; + _counterNoise = 0; + _counterA = 0; + _counterB = 0; + _counterC = 0; + _EnvelopeCounterBend = 0; + + // clear all the registers + for (int i = 0; i < 14; i++) + { + SelectedRegister = i; + PortWrite(0); + } + + randomSeed = 1; + + // number of frames to update + var fr = (_audioBufferIndex * _tStatesPerFrame) / _audioBuffer.Length; + + // update the audio buffer + BufferUpdate(fr); + */ + } + + /// + /// Reads the value from the currently selected register + /// + /// + public int PortRead() + { + return _registers[_activeRegister]; + } + + /// + /// Writes to the currently selected register + /// + /// + public void PortWrite(int value) + { + if (_activeRegister >= 0x10) + return; + + byte val = (byte)value; + + if (((1 << _activeRegister) & ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 13))) != 0) + val &= 0x0F; + + if (((1 << _activeRegister) & ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10))) != 0) + val &= 0x1F; + + if (_activeRegister != 13 && _registers[_activeRegister] == val) + return; + + _registers[_activeRegister] = val; + + switch (_activeRegister) + { + // Channel A (Combined Pitch) + // (not written to directly) + case 0: + case 1: + _dividerA = _registers[AY_A_FINE] | (_registers[AY_A_COARSE] << 8); + break; + // Channel B (Combined Pitch) + // (not written to directly) + case 2: + case 3: + _dividerB = _registers[AY_B_FINE] | (_registers[AY_B_COARSE] << 8); + break; + // Channel C (Combined Pitch) + // (not written to directly) + case 4: + case 5: + _dividerC = _registers[AY_C_FINE] | (_registers[AY_C_COARSE] << 8); + break; + // Noise Pitch + case 6: + _dividerN = val * 2; + break; + // Mixer + case 7: + _bit0 = 0 - ((val >> 0) & 1); + _bit1 = 0 - ((val >> 1) & 1); + _bit2 = 0 - ((val >> 2) & 1); + _bit3 = 0 - ((val >> 3) & 1); + _bit4 = 0 - ((val >> 4) & 1); + _bit5 = 0 - ((val >> 5) & 1); + break; + // Channel Volumes + case 8: + _eMaskA = (val & 0x10) != 0 ? -1 : 0; + _vA = ((val & 0x0F) * 2 + 1) & ~_eMaskA; + break; + case 9: + _eMaskB = (val & 0x10) != 0 ? -1 : 0; + _vB = ((val & 0x0F) * 2 + 1) & ~_eMaskB; + break; + case 10: + _eMaskC = (val & 0x10) != 0 ? -1 : 0; + _vC = ((val & 0x0F) * 2 + 1) & ~_eMaskC; + break; + // Envelope (Combined Duration) + // (not written to directly) + case 11: + case 12: + _dividerE = _registers[AY_E_FINE] | (_registers[AY_E_COARSE] << 8); + break; + // Envelope Shape + case 13: + // reset the envelope counter + _countE = 0; + + if ((_registers[AY_E_SHAPE] & 4) != 0) + { + // attack + _eState = 0; + _eDirection = 1; + } + else + { + // decay + _eState = 31; + _eDirection = -1; + } + break; + case 14: + // IO Port - not implemented + break; + } + } + + /// + /// Start of frame + /// + public void StartFrame() + { + _audioBufferIndex = 0; + BufferUpdate(0); + } + + /// + /// End of frame + /// + public void EndFrame() + { + BufferUpdate(_tStatesPerFrame); + } + + /// + /// Updates the audiobuffer based on the current frame t-state + /// + /// + public void UpdateSound(int frameCycle) + { + BufferUpdate(frameCycle); + } + + #endregion + + #region Private Fields + + /// + /// Register indicies + /// + private const int AY_A_FINE = 0; + private const int AY_A_COARSE = 1; + private const int AY_B_FINE = 2; + private const int AY_B_COARSE = 3; + private const int AY_C_FINE = 4; + private const int AY_C_COARSE = 5; + private const int AY_NOISEPITCH = 6; + private const int AY_MIXER = 7; + private const int AY_A_VOL = 8; + private const int AY_B_VOL = 9; + private const int AY_C_VOL = 10; + private const int AY_E_FINE = 11; + private const int AY_E_COARSE = 12; + private const int AY_E_SHAPE = 13; + private const int AY_PORT_A = 14; + private const int AY_PORT_B = 15; + + /// + /// The register array + /* + The AY-3-8910/8912 contains 16 internal registers as follows: + + Register Function Range + 0 Channel A fine pitch 8-bit (0-255) + 1 Channel A course pitch 4-bit (0-15) + 2 Channel B fine pitch 8-bit (0-255) + 3 Channel B course pitch 4-bit (0-15) + 4 Channel C fine pitch 8-bit (0-255) + 5 Channel C course pitch 4-bit (0-15) + 6 Noise pitch 5-bit (0-31) + 7 Mixer 8-bit (see below) + 8 Channel A volume 4-bit (0-15, see below) + 9 Channel B volume 4-bit (0-15, see below) + 10 Channel C volume 4-bit (0-15, see below) + 11 Envelope fine duration 8-bit (0-255) + 12 Envelope course duration 8-bit (0-255) + 13 Envelope shape 4-bit (0-15) + 14 I/O port A 8-bit (0-255) + 15 I/O port B 8-bit (0-255) (Not present on the AY-3-8912) + + * The volume registers (8, 9 and 10) contain a 4-bit setting but if bit 5 is set then that channel uses the + envelope defined by register 13 and ignores its volume setting. + * The mixer (register 7) is made up of the following bits (low=enabled): + + Bit: 7 6 5 4 3 2 1 0 + Register: I/O I/O Noise Noise Noise Tone Tone Tone + Channel: B A C B A C B A + + The AY-3-8912 ignores bit 7 of this register. + */ + /// + private int[] _registers = new int[16]; + + /// + /// The currently selected register + /// + private byte _activeRegister; + + /// + /// The frequency of the AY chip + /// + private static int _chipFrequency = 1773400; + + /// + /// The rendering resolution of the chip + /// + private double _resolution = 50D * 8D / _chipFrequency; + + /// + /// Channel generator state + /// + private int _bitA; + private int _bitB; + private int _bitC; + + /// + /// Envelope state + /// + private int _eState; + + /// + /// Envelope direction + /// + private int _eDirection; + + /// + /// Noise seed + /// + private int _noiseSeed; + + /// + /// Mixer state + /// + private int _bit0; + private int _bit1; + private int _bit2; + private int _bit3; + private int _bit4; + private int _bit5; + + /// + /// Noise generator state + /// + private int _bitN; + + /// + /// Envelope masks + /// + private int _eMaskA; + private int _eMaskB; + private int _eMaskC; + + /// + /// Amplitudes + /// + private int _vA; + private int _vB; + private int _vC; + + /// + /// Channel gen counters + /// + private int _countA; + private int _countB; + private int _countC; + + /// + /// Envelope gen counter + /// + private int _countE; + + /// + /// Noise gen counter + /// + private int _countN; + + /// + /// Channel gen dividers + /// + private int _dividerA; + private int _dividerB; + private int _dividerC; + + /// + /// Envelope gen divider + /// + private int _dividerE; + + /// + /// Noise gen divider + /// + private int _dividerN; + + /// + /// Panning table list + /// + private static List PanTabs = new List + { + // MONO + new uint[] { 100,100, 100,100, 100,100 }, + // ABC + new uint[] { 100,10, 66,66, 10,100 }, + // ACB + new uint[] { 100,10, 10,100, 66,66 }, + // BAC + new uint[] { 66,66, 100,10, 10,100 }, + // BCA + new uint[] { 10,100, 100,10, 66,66 }, + // CAB + new uint[] { 66,66, 10,100, 100,10 }, + // CBA + new uint[] { 10,100, 66,66, 100,10 } + }; + + /// + /// The currently selected panning configuration + /// + private AYPanConfig _currentPanTab = AYPanConfig.ABC; + + /// + /// The current volume + /// + private int _volume = 50; + + /// + /// Volume tables state + /// + private uint[][] _volumeTables; + + /// + /// Volume table to be used + /// + private static uint[] AYVolumes = new uint[] + { + 0x0000,0x0000,0x0340,0x0340,0x04C0,0x04C0,0x06F2,0x06F2, + 0x0A44,0x0A44,0x0F13,0x0F13,0x1510,0x1510,0x227E,0x227E, + 0x289F,0x289F,0x414E,0x414E,0x5B21,0x5B21,0x7258,0x7258, + 0x905E,0x905E,0xB550,0xB550,0xD7A0,0xD7A0,0xFFFF,0xFFFF, + }; + + #endregion + + #region Private Methods + + /// + /// Forces an update of the volume tables + /// + private void UpdateVolume() + { + var vol = (ulong)0xFFFF * (ulong)_volume / 100UL; + _volumeTables = new uint[6][]; + + // parent array + for (int j = 0; j < _volumeTables.Length; j++) + { + _volumeTables[j] = new uint[32]; + + // child array + for (int i = 0; i < _volumeTables[j].Length; i++) + { + _volumeTables[j][i] = (uint)( + (PanTabs[(int)_currentPanTab][j] * AYVolumes[i] * vol) / + (3 * 65535 * 100)); + } + } + } + + private int mult_const; + + /// + /// Initializes timing information for the frame + /// + /// + /// + private void InitTiming(int sampleRate, int frameTactCount) + { + _sampleRate = sampleRate; + _tStatesPerFrame = frameTactCount; + + _tStatesPerSample = 79; //(int)Math.Round(((double)_tStatesPerFrame * 50D) / + //(16D * (double)_sampleRate), + //MidpointRounding.AwayFromZero); + + _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + _audioBuffer = new short[_samplesPerFrame * 2]; //[_sampleRate / 50]; + _audioBufferIndex = 0; + + mult_const = ((_chipFrequency / 8) << 14) / _machine.ULADevice.ClockSpeed; + + var aytickspercputick = (double)_machine.ULADevice.ClockSpeed / (double)_chipFrequency; + int ayCyclesPerSample = (int)((double)_tStatesPerSample * (double)aytickspercputick); + } + + /// + /// Updates the audiobuffer based on the current frame t-state + /// + /// + private void BufferUpdate(int cycle) + { + if (cycle > _tStatesPerFrame) + { + // we are outside of the frame - just process the last value + cycle = _tStatesPerFrame; + } + + // get the current length of the audiobuffer + int bufferLength = _samplesPerFrame; // _audioBuffer.Length; + + int toEnd = ((bufferLength * cycle) / _tStatesPerFrame); + + // loop through the number of samples we need to render + while(_audioBufferIndex < toEnd) + { + // run the AY chip processing at the correct resolution + for (int i = 0; i < _tStatesPerSample / 14; i++) + { + if (++_countA >= _dividerA) + { + _countA = 0; + _bitA ^= -1; + } + + if (++_countB >= _dividerB) + { + _countB = 0; + _bitB ^= -1; + } + + if (++_countC >= _dividerC) + { + _countC = 0; + _bitC ^= -1; + } + + if (++_countN >= _dividerN) + { + _countN = 0; + _noiseSeed = (_noiseSeed * 2 + 1) ^ (((_noiseSeed >> 16) ^ (_noiseSeed >> 13)) & 1); + _bitN = 0 - ((_noiseSeed >> 16) & 1); + } + + if (++_countE >= _dividerE) + { + _countE = 0; + _eState += +_eDirection; + + var mask = (1 << _registers[AY_E_SHAPE]); + + if ((_eState & ~31) != 0) + { + if ((mask & ((1 << 0) | (1 << 1) | (1 << 2) | + (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | + (1 << 7) | (1 << 9) | (1 << 15))) != 0) + { + _eState = _eDirection = 0; + } + } + else if ((mask & ((1 << 8) | (1 << 12))) != 0) + { + _eState &= 31; + } + else if ((mask & ((1 << 10) | (1 << 14))) != 0) + { + _eDirection = -_eDirection; + _eState += _eDirection; + } + else + { + // 11,13 + _eState = 31; + _eDirection = 0; + } + } + } + + // mix the sample + var mixA = ((_eMaskA & _eState) | _vA) & ((_bitA | _bit0) & (_bitN | _bit3)); + var mixB = ((_eMaskB & _eState) | _vB) & ((_bitB | _bit1) & (_bitN | _bit4)); + var mixC = ((_eMaskC & _eState) | _vC) & ((_bitC | _bit2) & (_bitN | _bit5)); + + var l = _volumeTables[0][mixA]; + var r = _volumeTables[1][mixA]; + + l += _volumeTables[2][mixB]; + r += _volumeTables[3][mixB]; + l += _volumeTables[4][mixC]; + r += _volumeTables[5][mixC]; + + _audioBuffer[_audioBufferIndex * 2] = (short)l; + _audioBuffer[(_audioBufferIndex * 2) + 1] = (short)r; + + _audioBufferIndex++; + } + + _lastStateRendered = cycle; + } + + #endregion + + #endregion + + #region ISoundProvider + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + _audioBuffer = new short[_samplesPerFrame * 2]; + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + nsamp = _samplesPerFrame; + samples = _audioBuffer; + DiscardSamples(); + } + + #endregion + + #region State Serialization + + public int nullDump = 0; + + /// + /// State serialization + /// + /// + public void SyncState(Serializer ser) + { + ser.BeginSection("PSG-AY"); + + ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); + ser.Sync("_sampleRate", ref _sampleRate); + ser.Sync("_samplesPerFrame", ref _samplesPerFrame); + ser.Sync("_tStatesPerSample", ref _tStatesPerSample); + ser.Sync("_audioBufferIndex", ref _audioBufferIndex); + ser.Sync("_audioBuffer", ref _audioBuffer, false); + + ser.Sync("_registers", ref _registers, false); + ser.Sync("_activeRegister", ref _activeRegister); + ser.Sync("_bitA", ref _bitA); + ser.Sync("_bitB", ref _bitB); + ser.Sync("_bitC", ref _bitC); + ser.Sync("_eState", ref _eState); + ser.Sync("_eDirection", ref _eDirection); + ser.Sync("_noiseSeed", ref _noiseSeed); + ser.Sync("_bit0", ref _bit0); + ser.Sync("_bit1", ref _bit1); + ser.Sync("_bit2", ref _bit2); + ser.Sync("_bit3", ref _bit3); + ser.Sync("_bit4", ref _bit4); + ser.Sync("_bit5", ref _bit5); + ser.Sync("_bitN", ref _bitN); + ser.Sync("_eMaskA", ref _eMaskA); + ser.Sync("_eMaskB", ref _eMaskB); + ser.Sync("_eMaskC", ref _eMaskC); + ser.Sync("_vA", ref _vA); + ser.Sync("_vB", ref _vB); + ser.Sync("_vC", ref _vC); + ser.Sync("_countA", ref _countA); + ser.Sync("_countB", ref _countB); + ser.Sync("_countC", ref _countC); + ser.Sync("_countE", ref _countE); + ser.Sync("_countN", ref _countN); + ser.Sync("_dividerA", ref _dividerA); + ser.Sync("_dividerB", ref _dividerB); + ser.Sync("_dividerC", ref _dividerC); + ser.Sync("_dividerE", ref _dividerE); + ser.Sync("_dividerN", ref _dividerN); + ser.SyncEnum("_currentPanTab", ref _currentPanTab); + ser.Sync("_volume", ref nullDump); + + for (int i = 0; i < 6; i++) + { + ser.Sync("volTable" + i, ref _volumeTables[i], false); + } + + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index d5c1fff9a6..3e719e72e2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -140,7 +140,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_renderSound) { BuzzerDevice.StartFrame(); - if (AYDevice != null && !PagingDisabled) + if (AYDevice != null) AYDevice.StartFrame(); } @@ -155,11 +155,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CPU.ExecuteOne(); // update AY + int ayCnt = 0; if (_renderSound) { - if (AYDevice != null && !PagingDisabled) + if (AYDevice != null) { - AYDevice.UpdateSound(CurrentFrameCycle); + //AYDevice.UpdateSound(CurrentFrameCycle); + if (ayCnt > 10) + { + //AYDevice.UpdateSound(CurrentFrameCycle); + ayCnt = 0; + } } } } @@ -174,6 +180,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_renderSound) BuzzerDevice.EndFrame(); + if (AYDevice != null) + AYDevice.EndFrame(); + FrameCount++; // setup for next frame 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 130264a4c7..67e164dfe1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -16,11 +16,69 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { + bool deviceAddressed = true; + // process IO contention ContendPortAddress(port); int result = 0xFF; + // check AY + if (AYDevice.ReadPort(port, ref result)) + return (byte)result; + + // Kempston joystick input takes priority over all other input + // if this is detected just return the kempston byte + if ((port & 0xe0) == 0 || (port & 0x20) == 0) + { + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; + } + else + { + if (KeyboardDevice.ReadPort(port, ref result)) + { + // not a lagframe + InputRead = true; + + // tape loading monitor cycle + TapeDevice.MonitorRead(); + + // process tape INs + TapeDevice.ReadPort(port, ref result); + } + else + deviceAddressed = false; + } + + if (!deviceAddressed) + { + // If this is an unused port the floating memory bus should be returned + // Floating bus is read on the previous cycle + int _tStates = CurrentFrameCycle - 1; + + // if we are on the top or bottom border return 0xff + if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) + { + result = 0xff; + } + else + { + if (ULADevice.floatingBusTable[_tStates] < 0) + { + result = 0xff; + } + else + { + result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); + } + } + } + + /* + // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; @@ -81,7 +139,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } } - + + */ + return (byte)result; } @@ -102,6 +162,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int currT = CPU.TotalExecutedCycles; + AYDevice.WritePort(port, value); + // paging if (port == 0x7ffd) { @@ -164,7 +226,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } - + /* // Active AY Register if ((port & 0xc002) == 0xc000) { @@ -178,7 +240,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { AYDevice.PortWrite(value); CPU.TotalExecutedCycles += 3; - } + } + */ } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 5e657b0863..3ea955ddca 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -31,7 +31,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - AYDevice = new AY38912(); + AYDevice = new AYChip(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); 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 24ac1f5f97..3e8d97f36f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -16,11 +16,67 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { + bool deviceAddressed = true; + // process IO contention ContendPortAddress(port); int result = 0xFF; + // check AY + if (AYDevice.ReadPort(port, ref result)) + return (byte)result; + + // Kempston joystick input takes priority over all other input + // if this is detected just return the kempston byte + if ((port & 0xe0) == 0 || (port & 0x20) == 0) + { + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; + } + else + { + if (KeyboardDevice.ReadPort(port, ref result)) + { + // not a lagframe + InputRead = true; + + // tape loading monitor cycle + TapeDevice.MonitorRead(); + + // process tape INs + TapeDevice.ReadPort(port, ref result); + } + else + deviceAddressed = false; + } + + if (!deviceAddressed) + { + // If this is an unused port the floating memory bus should be returned + // Floating bus is read on the previous cycle + int _tStates = CurrentFrameCycle - 1; + + // if we are on the top or bottom border return 0xff + if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) + { + result = 0xff; + } + else + { + if (ULADevice.floatingBusTable[_tStates] < 0) + { + result = 0xff; + } + else + { + result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); + } + } + } + /* // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; @@ -88,10 +144,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else result = 0xff; } - */ + *//* // if unused port the floating memory bus should be returned (still todo) } + */ return (byte)result; } @@ -103,6 +160,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { + // process IO contention + ContendPortAddress(port); + // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); // get a BitArray of the value byte @@ -111,7 +171,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Check whether the low bit is reset bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; - ULADevice.Contend(port); + AYDevice.WritePort(port, value); // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set if (port == 0x7ffd) @@ -131,10 +191,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // portbit 4 is the LOW BIT of the ROM selection ROMlow = bits[4]; - } + } } // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set - else if (port == 0x1ffd) + if (port == 0x1ffd) { if (!PagingDisabled) { @@ -172,75 +232,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // bit 4 is the printer port strobe PrinterPortStrobe = bits[4]; } - /* - // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set - if (!portBits[1] && !portBits[15] && portBits[14]) - { - // paging (skip if paging has been disabled - paging can then only happen after a machine hard reset) - if (!PagingDisabled) - { - // bit 0 specifies the paging mode - SpecialPagingMode = bits[0]; - - if (!SpecialPagingMode) - { - // we are in normal mode - // portbit 4 is the LOW BIT of the ROM selection - BitArray romHalfNibble = new BitArray(2); - romHalfNibble[0] = portBits[4]; - - // value bit 2 is the high bit of the ROM selection - romHalfNibble[1] = bits[2]; - - // value bit 1 is ignored in normal paging mode - - // set the ROMPage - ROMPaged = ZXSpectrum.GetIntFromBitArray(romHalfNibble); - - - - - // bit 3 controls shadow screen - SHADOWPaged = bits[3]; - - // Bit 5 set signifies that paging is disabled until next reboot - PagingDisabled = bits[5]; - } - } - } - - // port 0x1ffd - special paging mode - // hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set - if (!portBits[1] && portBits[12] && !portBits[13] && !portBits[14] && !portBits[15]) - { - if (!PagingDisabled && SpecialPagingMode) - { - // process special paging - // this is decided based on combinations of bits 1 & 2 - // Config 0 = Bit1-0 Bit2-0 - // Config 1 = Bit1-1 Bit2-0 - // Config 2 = Bit1-0 Bit2-1 - // Config 3 = Bit1-1 Bit2-1 - BitArray confHalfNibble = new BitArray(2); - confHalfNibble[0] = bits[1]; - confHalfNibble[1] = bits[2]; - - // set special paging configuration - PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); - - // last value should be saved at 0x5b67 (23399) - not sure if this is actually needed - WriteBus(0x5b67, value); - } - - // bit 3 controls the disk motor (1=on, 0=off) - DiskMotorState = bits[3]; - - // bit 4 is the printer port strobe - PrinterPortStrobe = bits[4]; - } - - */ - // Only even addresses address the ULA if (lowBitReset) @@ -268,100 +259,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } - else - { - // AY Register activation - if ((port & 0xc002) == 0xc000) - { - var reg = value & 0x0f; - AYDevice.SelectedRegister = reg; - CPU.TotalExecutedCycles += 3; - } - else - { - if ((port & 0xC002) == 0x8000) - { - AYDevice.PortWrite(value); - CPU.TotalExecutedCycles += 3; - } - - /* - - else - { - if ((port & 0xC002) == 0x4000) //Are bits 1 and 15 reset and bit 14 set? - { - // memory paging activate - if (PagingDisabled) - return; - - // bit 5 handles paging disable (48k mode, persistent until next reboot) - if ((value & 0x20) != 0) - { - PagingDisabled = true; - } - - // shadow screen - if ((value & 0x08) != 0) - { - SHADOWPaged = true; - } - else - { - SHADOWPaged = false; - } - } - else - { - //Extra Memory Paging feature activate - if ((port & 0xF002) == 0x1000) //Is bit 12 set and bits 13,14,15 and 1 reset? - { - if (PagingDisabled) - return; - - // set disk motor state - //todo - - if ((value & 0x08) != 0) - { - //diskDriveState |= (1 << 4); - } - else - { - //diskDriveState &= ~(1 << 4); - } - - if ((value & 0x1) != 0) - { - // activate special paging mode - SpecialPagingMode = true; - PagingConfiguration = (value & 0x6 >> 1); - } - else - { - // normal paging mode - SpecialPagingMode = false; - } - } - else - { - // disk write port - if ((port & 0xF002) == 0x3000) //Is bit 12 set and bits 13,14,15 and 1 reset? - { - //udpDrive.DiskWriteByte((byte)(val & 0xff)); - } - } - } - } - */ - } - } LastULAOutByte = value; - - - - } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index 88b672d174..3cd1f695d5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -31,7 +31,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - AYDevice = new AY38912(); + AYDevice = new AYChip(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); 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 32bd26d842..bcbb31f35d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -16,14 +16,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { + bool deviceAddressed = true; + // process IO contention ContendPortAddress(port); int result = 0xFF; - // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; + // check AY + if (AYDevice.ReadPort(port, ref result)) + return (byte)result; // Kempston joystick input takes priority over all other input // if this is detected just return the kempston byte @@ -34,62 +36,45 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InputRead = true; } - else if (lowBitReset) - { - // Even I/O address so get input from keyboard - KeyboardDevice.ReadPort(port, ref result); - - TapeDevice.MonitorRead(); - - // not a lagframe - InputRead = true; - - // tape loading monitor cycle - TapeDevice.MonitorRead(); - - // process tape INs - TapeDevice.ReadPort(port, ref result); - } else { - // devices other than the ULA will respond here - // (e.g. the AY sound chip in a 128k spectrum - - // AY register activate - on +3/2a both FFFD and BFFD active AY - if ((port & 0xc002) == 0xc000) + if (KeyboardDevice.ReadPort(port, ref result)) { - result = (int)AYDevice.PortRead(); + // not a lagframe + InputRead = true; + + // tape loading monitor cycle + TapeDevice.MonitorRead(); + + // process tape INs + TapeDevice.ReadPort(port, ref result); } - else if ((port & 0xc002) == 0x8000) + else + deviceAddressed = false; + } + + if (!deviceAddressed) + { + // If this is an unused port the floating memory bus should be returned + // Floating bus is read on the previous cycle + int _tStates = CurrentFrameCycle - 1; + + // if we are on the top or bottom border return 0xff + if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) { - result = (int)AYDevice.PortRead(); + result = 0xff; } - - // Kempston Mouse (not implemented yet) - - - else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset? + else { - //result = udpDrive.DiskStatusRead(); - - // disk drive is not yet implemented - return a max status byte for the menu to load - result = 255; - } - else if ((port & 0xF002) == 0x3000) - { - //result = udpDrive.DiskReadByte(); - result = 0; - } - - else if ((port & 0xF002) == 0x0) - { - if (PagingDisabled) - result = 0x1; - else + if (ULADevice.floatingBusTable[_tStates] < 0) + { result = 0xff; + } + else + { + result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); + } } - - // if unused port the floating memory bus should be returned (still todo) } return (byte)result; @@ -113,6 +98,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Check whether the low bit is reset bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; + AYDevice.WritePort(port, value); + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set if (port == 0x7ffd) { @@ -134,7 +121,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set - else if (port == 0x1ffd) + if (port == 0x1ffd) { if (!PagingDisabled) { @@ -199,25 +186,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } - else - { - // AY Register activation - if ((port & 0xc002) == 0xc000) - { - var reg = value & 0x0f; - AYDevice.SelectedRegister = reg; - CPU.TotalExecutedCycles += 3; - } - else - { - if ((port & 0xC002) == 0x8000) - { - AYDevice.PortWrite(value); - CPU.TotalExecutedCycles += 3; - } - } - } - + LastULAOutByte = value; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index b9038e3af1..916232bcb6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -31,7 +31,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - AYDevice = new AY38912(); + AYDevice = new AYChip(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 059a2fcaa1..c3204f1c69 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -24,8 +24,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public bool PutSettings(ZXSpectrumSettings o) { - if (SoundMixer != null) - SoundMixer.Stereo = o.StereoSound; + //if (SoundMixer != null) + //SoundMixer.Stereo = o.StereoSound; + + if (_machine != null && _machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) + { + ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = o.AYPanConfig; + } Settings = o; @@ -48,10 +53,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DefaultValue(true)] public bool AutoLoadTape { get; set; } + /* [DisplayName("Stereo Sound")] [Description("Turn stereo sound on or off")] [DefaultValue(true)] public bool StereoSound { get; set; } + */ + + [DisplayName("AY-3-8912 Panning Config")] + [Description("Set the PSG panning configuration.\nThe chip has 3 audio channels that can be outputed in different configurations")] + [DefaultValue(AYChip.AYPanConfig.ABC)] + public AYChip.AYPanConfig AYPanConfig { get; set; } [DisplayName("Core OSD Message Verbosity")] [Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")] diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index bbd45c713d..01419fd093 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -51,8 +51,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum core = ms.ToArray(); } - - if (ser.IsWriter) { ser.SyncEnum("_machineType", ref _machineType); @@ -70,12 +68,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { var tmpM = _machineType; ser.SyncEnum("_machineType", ref _machineType); - if (tmpM != _machineType) + if (tmpM != _machineType && _machineType.ToString() != "72") { string msg = "SAVESTATE FAILED TO LOAD!!\n\n"; - msg += "Current Configuration: " + _machineType.ToString(); + msg += "Current Configuration: " + tmpM.ToString(); msg += "\n"; - msg += "Saved Configuration: " + tmpM.ToString(); + msg += "Saved Configuration: " + _machineType.ToString(); msg += "\n\n"; msg += "If you wish to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again."; CoreComm.ShowMessage(msg); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 27eb361a88..7f72bf4b4e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -101,7 +101,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - SoundMixer.Stereo = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).StereoSound; + //SoundMixer.Stereo = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).StereoSound; + + if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) + { + ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; + } ser.Register(SoundMixer); From 760ae8edf0ec6b9117d0068a98f7244778de64be Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 15 Mar 2018 17:19:13 +0000 Subject: [PATCH 091/339] Fixed 128k and +2 memory paging bug --- .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 ++ .../Machine/ZXSpectrum128K/ZX128.Port.cs | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 3e719e72e2..14ca115602 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -251,6 +251,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("PagingDisabled", ref PagingDisabled); ser.Sync("SpecialPagingMode", ref SpecialPagingMode); ser.Sync("PagingConfiguration", ref PagingConfiguration); + //ser.Sync("ROMhigh", ref ROMhigh); + //ser.Sync("ROMlow", ref ROMlow); RomData.SyncState(ser); KeyboardDevice.SyncState(ser); 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 67e164dfe1..a4a69e9877 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -167,8 +167,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // paging if (port == 0x7ffd) { - if (PagingDisabled) - return; + //if (PagingDisabled) + //return; // Bits 0, 1, 2 select the RAM page var rp = value & 0x07; @@ -178,6 +178,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // bit 3 controls shadow screen SHADOWPaged = bits[3]; + if (SHADOWPaged == false) + { + + } + else + { + + } + // ROM page if (bits[4]) { @@ -192,7 +201,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Bit 5 set signifies that paging is disabled until next reboot PagingDisabled = bits[5]; - return; } // Check whether the low bit is reset From 8ebcadbc583f412145fa716894f408e4506a1683 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 15 Mar 2018 20:47:47 -0400 Subject: [PATCH 092/339] z80: fix port addressing in some cases --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 4a1c46cc03..c53b013a86 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -419,7 +419,7 @@ private void IN_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {IN, ALU, C, + {IN, ALU, C, B, IDLE, WR, L, H, ALU, IDLE, @@ -438,7 +438,7 @@ cur_instr = new ushort[] {RD, ALU, L, H, IDLE, - OUT, C, ALU, + OUT, C, B, ALU, IDLE, IDLE, operation, L, H, From 396f875ac21077b40912a9221bf0d71502e56843 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 15 Mar 2018 20:58:08 -0400 Subject: [PATCH 093/339] z80 disassembler: handle address wrap --- BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs index a312975d78..effe7400df 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs @@ -435,6 +435,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A addr += extra_inc; size = addr - start_addr; + // handle case of addr wrapping around at 16 bit boundary + if (addr < start_addr) + { + size = addr + 1; + } + return temp; } From 4ecb247c170569b7046e2b0c686d0b63d5317203 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Mar 2018 08:08:35 +0000 Subject: [PATCH 094/339] Fixed AY-3-8912 overflow bug --- .../Hardware/SoundOuput/AYChip.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs index 06817afe19..34515cd5eb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs @@ -650,32 +650,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _countE = 0; _eState += +_eDirection; - var mask = (1 << _registers[AY_E_SHAPE]); - if ((_eState & ~31) != 0) { + var mask = (1 << _registers[AY_E_SHAPE]); + if ((mask & ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 9) | (1 << 15))) != 0) { _eState = _eDirection = 0; } - } - else if ((mask & ((1 << 8) | (1 << 12))) != 0) - { - _eState &= 31; - } - else if ((mask & ((1 << 10) | (1 << 14))) != 0) - { - _eDirection = -_eDirection; - _eState += _eDirection; - } - else - { - // 11,13 - _eState = 31; - _eDirection = 0; - } + else if ((mask & ((1 << 8) | (1 << 12))) != 0) + { + _eState &= 31; + } + else if ((mask & ((1 << 10) | (1 << 14))) != 0) + { + _eDirection = -_eDirection; + _eState += _eDirection; + } + else + { + // 11,13 + _eState = 31; + _eDirection = 0; + } + } } } From 5bb1d1f327f807c40b8c882197295c673f81d4a5 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Mar 2018 10:45:57 +0000 Subject: [PATCH 095/339] Fixed +2a/+3 ROM paging bytes missing from SaveState serialization --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 14ca115602..3c220d3157 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -251,8 +251,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("PagingDisabled", ref PagingDisabled); ser.Sync("SpecialPagingMode", ref SpecialPagingMode); ser.Sync("PagingConfiguration", ref PagingConfiguration); - //ser.Sync("ROMhigh", ref ROMhigh); - //ser.Sync("ROMlow", ref ROMlow); + ser.Sync("ROMhigh", ref ROMhigh); + ser.Sync("ROMlow", ref ROMlow); RomData.SyncState(ser); KeyboardDevice.SyncState(ser); From 7c9c39417b9d0b35338b4b1778a572992a6b5f10 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Mar 2018 11:58:02 +0000 Subject: [PATCH 096/339] UI - added joystick settings menu --- .../BizHawk.Client.EmuHawk.csproj | 9 + BizHawk.Client.EmuHawk/MainForm.Designer.cs | 40 +- BizHawk.Client.EmuHawk/MainForm.Events.cs | 5 + BizHawk.Client.EmuHawk/MainForm.cs | 4 +- .../ZXSpectrumJoystickSettings.Designer.cs | 184 +++++ .../ZXSpectrum/ZXSpectrumJoystickSettings.cs | 127 ++++ .../ZXSpectrumJoystickSettings.resx | 630 ++++++++++++++++++ 7 files changed, 981 insertions(+), 18 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.Designer.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.resx diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index ccca5cf3eb..6ed196a29b 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -497,6 +497,12 @@ TI83PaletteConfig.cs + + Form + + + ZXSpectrumJoystickSettings.cs + Form @@ -1395,6 +1401,9 @@ TI83PaletteConfig.cs + + ZXSpectrumJoystickSettings.cs + CoreFeatureAnalysis.cs diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index dcb8272627..6a3da605ba 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -380,6 +380,7 @@ this.FeaturesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.preferencesToolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton(); @@ -452,7 +453,7 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.preferencesToolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -3237,7 +3238,7 @@ this.C64DisksSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.toolStripSeparator36}); this.C64DisksSubMenu.Name = "C64DisksSubMenu"; - this.C64DisksSubMenu.Size = new System.Drawing.Size(125, 22); + this.C64DisksSubMenu.Size = new System.Drawing.Size(152, 22); this.C64DisksSubMenu.Text = "Disks"; this.C64DisksSubMenu.DropDownOpened += new System.EventHandler(this.C64DisksSubMenu_DropDownOpened); // @@ -3249,7 +3250,7 @@ // C64SettingsMenuItem // this.C64SettingsMenuItem.Name = "C64SettingsMenuItem"; - this.C64SettingsMenuItem.Size = new System.Drawing.Size(125, 22); + this.C64SettingsMenuItem.Size = new System.Drawing.Size(152, 22); this.C64SettingsMenuItem.Text = "&Settings..."; this.C64SettingsMenuItem.Click += new System.EventHandler(this.C64SettingsMenuItem_Click); // @@ -3281,7 +3282,7 @@ // preferencesToolStripMenuItem // this.preferencesToolStripMenuItem.Name = "preferencesToolStripMenuItem"; - this.preferencesToolStripMenuItem.Size = new System.Drawing.Size(144, 22); + this.preferencesToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.preferencesToolStripMenuItem.Text = "Preferences..."; this.preferencesToolStripMenuItem.Click += new System.EventHandler(this.preferencesToolStripMenuItem_Click); // @@ -3296,7 +3297,7 @@ // preferencesToolStripMenuItem3 // this.preferencesToolStripMenuItem3.Name = "preferencesToolStripMenuItem3"; - this.preferencesToolStripMenuItem3.Size = new System.Drawing.Size(144, 22); + this.preferencesToolStripMenuItem3.Size = new System.Drawing.Size(152, 22); this.preferencesToolStripMenuItem3.Text = "Preferences..."; this.preferencesToolStripMenuItem3.Click += new System.EventHandler(this.preferencesToolStripMenuItem3_Click); // @@ -3326,7 +3327,7 @@ // preferencesToolStripMenuItem2 // this.preferencesToolStripMenuItem2.Name = "preferencesToolStripMenuItem2"; - this.preferencesToolStripMenuItem2.Size = new System.Drawing.Size(152, 22); + this.preferencesToolStripMenuItem2.Size = new System.Drawing.Size(144, 22); this.preferencesToolStripMenuItem2.Text = "Preferences..."; this.preferencesToolStripMenuItem2.Click += new System.EventHandler(this.preferencesToolStripMenuItem2_Click); // @@ -3346,7 +3347,7 @@ // this.OnlineHelpMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Help; this.OnlineHelpMenuItem.Name = "OnlineHelpMenuItem"; - this.OnlineHelpMenuItem.Size = new System.Drawing.Size(146, 22); + this.OnlineHelpMenuItem.Size = new System.Drawing.Size(152, 22); this.OnlineHelpMenuItem.Text = "&Online Help..."; this.OnlineHelpMenuItem.Click += new System.EventHandler(this.OnlineHelpMenuItem_Click); // @@ -3354,7 +3355,7 @@ // this.ForumsMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.TAStudio; this.ForumsMenuItem.Name = "ForumsMenuItem"; - this.ForumsMenuItem.Size = new System.Drawing.Size(146, 22); + this.ForumsMenuItem.Size = new System.Drawing.Size(152, 22); this.ForumsMenuItem.Text = "Forums..."; this.ForumsMenuItem.Click += new System.EventHandler(this.ForumsMenuItem_Click); // @@ -3362,7 +3363,7 @@ // this.FeaturesMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.kitchensink; this.FeaturesMenuItem.Name = "FeaturesMenuItem"; - this.FeaturesMenuItem.Size = new System.Drawing.Size(146, 22); + this.FeaturesMenuItem.Size = new System.Drawing.Size(152, 22); this.FeaturesMenuItem.Text = "&Features"; this.FeaturesMenuItem.Click += new System.EventHandler(this.FeaturesMenuItem_Click); // @@ -3370,19 +3371,27 @@ // this.AboutMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.CorpHawkSmall; this.AboutMenuItem.Name = "AboutMenuItem"; - this.AboutMenuItem.Size = new System.Drawing.Size(146, 22); + this.AboutMenuItem.Size = new System.Drawing.Size(152, 22); this.AboutMenuItem.Text = "&About"; this.AboutMenuItem.Click += new System.EventHandler(this.AboutMenuItem_Click); // // zXSpectrumToolStripMenuItem // this.zXSpectrumToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ZXSpectrumControllerConfigurationMenuItem, this.preferencesToolStripMenuItem4}); this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; this.zXSpectrumToolStripMenuItem.DropDownOpened += new System.EventHandler(this.zXSpectrumToolStripMenuItem_DropDownOpened); // + // preferencesToolStripMenuItem4 + // + this.preferencesToolStripMenuItem4.Name = "preferencesToolStripMenuItem4"; + this.preferencesToolStripMenuItem4.Size = new System.Drawing.Size(192, 22); + this.preferencesToolStripMenuItem4.Text = "Preferences"; + this.preferencesToolStripMenuItem4.Click += new System.EventHandler(this.preferencesToolStripMenuItem4_Click); + // // Atari7800HawkCoreMenuItem // this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem"; @@ -4009,12 +4018,12 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // - // preferencesToolStripMenuItem4 + // ZXSpectrumControllerConfigurationMenuItem // - this.preferencesToolStripMenuItem4.Name = "preferencesToolStripMenuItem4"; - this.preferencesToolStripMenuItem4.Size = new System.Drawing.Size(152, 22); - this.preferencesToolStripMenuItem4.Text = "Preferences"; - this.preferencesToolStripMenuItem4.Click += new System.EventHandler(this.preferencesToolStripMenuItem4_Click); + this.ZXSpectrumControllerConfigurationMenuItem.Name = "ZXSpectrumControllerConfigurationMenuItem"; + this.ZXSpectrumControllerConfigurationMenuItem.Size = new System.Drawing.Size(192, 22); + this.ZXSpectrumControllerConfigurationMenuItem.Text = "Joystick Configuration"; + this.ZXSpectrumControllerConfigurationMenuItem.Click += new System.EventHandler(this.ZXSpectrumControllerConfigurationMenuItem_Click); // // MainForm // @@ -4480,5 +4489,6 @@ private System.Windows.Forms.ToolStripMenuItem SMSControllerKeyboardToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem zXSpectrumToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem4; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumControllerConfigurationMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 7d8a4a3829..858982ec2c 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2449,6 +2449,11 @@ namespace BizHawk.Client.EmuHawk GenericCoreConfig.DoDialog(this, "ZXSpectrum Settings"); } + private void ZXSpectrumControllerConfigurationMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumJoystickSettings().ShowDialog(); + } + #endregion #region Help diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index d87fb3522c..137e9d075a 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -4297,9 +4297,7 @@ namespace BizHawk.Client.EmuHawk private void preferencesToolStripMenuItem3_Click(object sender, EventArgs e) { GenericCoreConfig.DoDialog(this, "PC-FX Settings"); - } - - + } private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording) { diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.Designer.cs new file mode 100644 index 0000000000..342e833dc1 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.Designer.cs @@ -0,0 +1,184 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumJoystickSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumJoystickSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label5 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.Port2ComboBox = new System.Windows.Forms.ComboBox(); + this.Port1ComboBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.Port3ComboBox = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.lblDoubleSize = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(170, 312); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(236, 312); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(9, 207); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(57, 13); + this.label5.TabIndex = 16; + this.label5.Text = "Joystick 2:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 157); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(57, 13); + this.label4.TabIndex = 15; + this.label4.Text = "Joystick 1:"; + // + // Port2ComboBox + // + this.Port2ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.Port2ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Port2ComboBox.FormattingEnabled = true; + this.Port2ComboBox.Location = new System.Drawing.Point(12, 223); + this.Port2ComboBox.Name = "Port2ComboBox"; + this.Port2ComboBox.Size = new System.Drawing.Size(284, 21); + this.Port2ComboBox.TabIndex = 14; + // + // Port1ComboBox + // + this.Port1ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.Port1ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Port1ComboBox.FormattingEnabled = true; + this.Port1ComboBox.Location = new System.Drawing.Point(12, 173); + this.Port1ComboBox.Name = "Port1ComboBox"; + this.Port1ComboBox.Size = new System.Drawing.Size(284, 21); + this.Port1ComboBox.TabIndex = 13; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(151, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Joystick Settings"; + // + // Port3ComboBox + // + this.Port3ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.Port3ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Port3ComboBox.FormattingEnabled = true; + this.Port3ComboBox.Location = new System.Drawing.Point(12, 275); + this.Port3ComboBox.Name = "Port3ComboBox"; + this.Port3ComboBox.Size = new System.Drawing.Size(284, 21); + this.Port3ComboBox.TabIndex = 18; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 259); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(57, 13); + this.label2.TabIndex = 19; + this.label2.Text = "Joystick 3:"; + // + // lblDoubleSize + // + this.lblDoubleSize.Location = new System.Drawing.Point(26, 40); + this.lblDoubleSize.Name = "lblDoubleSize"; + this.lblDoubleSize.Size = new System.Drawing.Size(254, 117); + this.lblDoubleSize.TabIndex = 20; + this.lblDoubleSize.Text = resources.GetString("lblDoubleSize.Text"); + // + // ZXSpectrumJoystickSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(308, 347); + this.Controls.Add(this.lblDoubleSize); + this.Controls.Add(this.label2); + this.Controls.Add(this.Port3ComboBox); + this.Controls.Add(this.label1); + this.Controls.Add(this.label5); + this.Controls.Add(this.label4); + this.Controls.Add(this.Port2ComboBox); + this.Controls.Add(this.Port1ComboBox); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumJoystickSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Joystick Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox Port2ComboBox; + private System.Windows.Forms.ComboBox Port1ComboBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox Port3ComboBox; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lblDoubleSize; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.cs new file mode 100644 index 0000000000..fc151c843c --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.cs @@ -0,0 +1,127 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumJoystickSettings : Form + { + private ZXSpectrum.ZXSpectrumSyncSettings _syncSettings; + + public ZXSpectrumJoystickSettings() + { + InitializeComponent(); + } + + private string[] possibleControllers; + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _syncSettings = ((ZXSpectrum)Global.Emulator).GetSyncSettings().Clone(); + + possibleControllers = Enum.GetNames(typeof(JoystickType)); + + foreach (var val in possibleControllers) + { + Port1ComboBox.Items.Add(val); + Port2ComboBox.Items.Add(val); + Port3ComboBox.Items.Add(val); + } + + Port1ComboBox.SelectedItem = _syncSettings.JoystickType1.ToString(); + Port2ComboBox.SelectedItem = _syncSettings.JoystickType2.ToString(); + Port3ComboBox.SelectedItem = _syncSettings.JoystickType3.ToString(); + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _syncSettings.JoystickType1.ToString() != Port1ComboBox.SelectedItem.ToString() + || _syncSettings.JoystickType2.ToString() != Port2ComboBox.SelectedItem.ToString() + || _syncSettings.JoystickType3.ToString() != Port3ComboBox.SelectedItem.ToString(); + + if (changed) + { + // enforce unique joystick selection + + bool selectionValid = true; + + var j1 = Port1ComboBox.SelectedItem.ToString(); + if (j1 != possibleControllers.First()) + { + if (j1 == Port2ComboBox.SelectedItem.ToString()) + { + Port2ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + if (j1 == Port3ComboBox.SelectedItem.ToString()) + { + Port3ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + } + + var j2 = Port2ComboBox.SelectedItem.ToString(); + if (j2 != possibleControllers.First()) + { + if (j2 == Port1ComboBox.SelectedItem.ToString()) + { + Port1ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + if (j2 == Port3ComboBox.SelectedItem.ToString()) + { + Port3ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + } + + var j3 = Port3ComboBox.SelectedItem.ToString(); + if (j3 != possibleControllers.First()) + { + if (j3 == Port1ComboBox.SelectedItem.ToString()) + { + Port1ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + if (j3 == Port2ComboBox.SelectedItem.ToString()) + { + Port2ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + } + + if (selectionValid) + { + _syncSettings.JoystickType1 = (JoystickType)Enum.Parse(typeof(JoystickType), Port1ComboBox.SelectedItem.ToString()); + _syncSettings.JoystickType2 = (JoystickType)Enum.Parse(typeof(JoystickType), Port2ComboBox.SelectedItem.ToString()); + _syncSettings.JoystickType3 = (JoystickType)Enum.Parse(typeof(JoystickType), Port3ComboBox.SelectedItem.ToString()); + + GlobalWin.MainForm.PutCoreSyncSettings(_syncSettings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + MessageBox.Show("Invalid joystick configuration. \nDuplicates have automatically been changed to NULL.\n\nPlease review the configuration"); + } + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Joystick settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.resx new file mode 100644 index 0000000000..c45473925d --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.resx @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ZXHawk is set up to allow 3 different unique joysticks to be attached at one time. + +This is because the Kempston joystick had to be attached via a Kempton interface plugged into the single expansion port. The Sinclair and Cursor joysticks effectively mapped to different key presses on the keyboard. + + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAADLytDi9tOemQAAAAAAUy9EecLEsa1uPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPj11Mme + VakAAAAAAAAAAAAATS84M0akAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE + AAAAAAAAAAAAAAABAAAAAQAAAAAAAFFNTQBRUlIAU1RUAGJHRwBiT08Aa0lIAGJTUwBrVlYAYllZAGZc + XABpWloAb1xbAHNTUwB7V1YAc1hXAHFbWwBkZWUAaWFhAG5kZABpamkAcGFhAHlubgB2cHAAf3V1AH55 + eQB8fX0AgUpKAI1PTwCLWFcAhlhYAI9ZWQCKXFsAm1ZWAJJZWQCWWVgAmlpbAJtcWwCiXFwAl2BfAIBg + YACAZ2YAgG9vAI9oaACWZWQAmGBhAJ5kZACcaWoAmm9vAIV0dACNcHAAiXZ2AIB8fACac3IAm3V0AJ51 + dQCZfHwAnHx8AKNmZgCnZmYAqmJiAK5jYwCvb24AtWVmALBtbgC5bW0AvmxtAKx+fQCxcnIAtHBwALZz + dACydXQAtnd2ALlwcAC5dnYAt3p5ALh5eAC8fHsAun18ALx+fQDGb3AAxnBxAMdzdADAd3YAyHJzAMlz + dADJdXYAynd4AMd/fwDMe3wAzXx9AHunbwBhvHIAYsN4ANuLOwC2hn4A4Zt5APC3ZABte9sAX47+AHWM + 5QAl0foAY+P8AIeDgwCFhoYAioSEAJOIiACWi4sAmpKRAKGCgQCmhYUAqYGBAKuDhACniooApYyMAKiO + jQCyhYMAvoWEALeNjQCrj5AAr5eXALSVlAC9lJMAmbCEAK6RugDBgYAAwoSCAMWDhADChoQAxYeFAM6A + gQDFiIYAxoqIAMqIiQDMi4oAy4yKAMiPjQDPj44A0ISFANKJigDUi4wA04+NANWNjgDKkY8A0JCOANud + iQDWj5AAzJSTAM2XlgDGm5oA1pGSANOUkgDVl5EA1pOUANiVlgDYmJUA2ZeYANKenADbmpsA3pmYANuc + mgDbn5wA1aacAN6gngDqqZoA3Z+gAMyjowDCra0AxqysAMqpqQDboaAA3qKiAN6logDbp6UA3aWkANer + qgDWsbMA0rW0ANe0tADfs7IA4aSiAOGlpQDkp6UA46imAOWopgDsraIA6qimAOGoqADhrqwA6a2rAOqv + rADpsK4A7LGuAOGzswDlsbEA7bKxAO+1sgDotrYA5rm3AO+4twDot7sA6bq5AOu9uwDrv70A8bazAPG2 + tADxuLUA9Lm2APC9uwD2vboA9L+9APi+uwD4v7wA8sC+APXAvgD5wL0AkILJAKqXzACsu8cAqr/LALLV + 3QDawMIA48XFAOvDwQDswMAA7cTDAO/ExQDgxsgA8cbEAPTGxADwyskA9MvJAPLNzQD21dYA+NjZAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAMEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHCEcBQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAayU9PSYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdQlBSQiJpAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAM0pSUlJQPRcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnUlJSUlJGFQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFJSUlJSUkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzUlJSWVJZfxAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5XWYqKioqGDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASoqMkpqa + mqAsAAAAAAAAAAAAAAAAAABoNAAAAAAAAACMjJyuvLy2toYHAAAAAAAAAAAAABcOIDouBgAAAAAAc4yc + tsHKysPAriIKAAAAAAAAABYgRk1LTX+DEAAAAABukqXB4ejo4dHPQCIEChcXEwggTXV/k66unKMpAAAA + AG6Srsro6ero0dN/Rk1NRk2Dg4STrsbh4cHAt2sAAAAAbpKuOXPe6ajW15KGg4OGk528yuHo5eHPz882 + AAAAAAB4jCkDAxSoMabXt5yjt8ro3ePo5dbT09HTdAAAAAAAAABGcBFoGgFwdtfDwHxi2dpmZcrX09HP + z0MAAAAAAAAAAHh/qWwaOa6cz9PNZGPYsdzbzc3DwLk2AAAAAAAAAAAAAAAvhpKakoyg19HNyKS5wHtb + orZ/cwAAAAAAAAAAAAAAAAAANkaKWVm5zb1gYV6cXVxfNgAAAAAAAAAAAAAAAAAAALGvlTIuP1K5tqCR + l4xfLwAAAAAAAAAAAAAAAAAAsbPBenkAAAAAcCVYjE0scwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////+f///+D////A////wH + ///4B///+Af///gH///wB///8Af///AH/+fwA/8D4AH8AeAAAAHgAAAB4AAAA+AAAAfwAAAP8AAAH/wA + AD//AAD//gAD//B4D////////////////////////////ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA + AAAAAAAAAAEAAAABAAAAAAAAWlJSAHBJSQB1SEgAe1dXAHdYWAB5WlkAel1dAGBiYgB1bGwAfWtrAHh2 + dgB9fn4Ag01NAIRXVwCIV1cAhV9eAItbWgCgX14ApV1dAJhgXwCNYGAAnWtqAJhtbQCCdnYAh3x8AI15 + eACeensAqGBgAKhoZwCga2oArGpqALNqagCzb28AtG1tALltbQCxb3AApnVzAKlzcwCqdHMApnp6AKd+ + fgCpensAq3x7ALZ3dgC8dHQAvH59AMZvcADGcHEAxXN0AMhycwDJdncAynh5AMx5egDNfn8Ajo1wAOek + VgDGgH8A4p53AEZ2+gB8u4AAd8PaAIuEhACOh4cAjo6OAJ+DggCejo4Ao4SEAKSIiACsi4sAqo2MAK6P + jgC+gYAAvoaGAL+KiACskJAAtJeXALWenQC5np4At6iOAKmyjgC9nroAwYSDAMaGhADOhoYAxomHAMiK + iQDJjYwA0oeIANOOjwDUjY0A2ZiPANaPkADGkZEAx5eXAMySkADGnZwA1ZOSANeTlADWl5YA2JSVANGZ + mADan50A3J6dAOCcmwDVoJ8A7K2fAMOtrQDXo6IA3aCgAN+kpADVq6oA3ay3AMu0tADPtrYA3L+/AOCi + oQDhpqUA5KelAOinpgDlq6gA46usAOOvrQDqrqwA7LGuAOayswDjtrQA5re1AOqysQDts7EA57y6AO+8 + ugDrvL0A8LOwAPC1sgDwtrQA87q3APS6twD2vboA8b69APi/vAD2wb4A+cC9AJmTzwDHqMMAu8PMAIHf + 8QDByNAA7cLCAO3FwwDvxsQA5cjIAOzOzgDwxcQA9cbEAPPP0AD10tIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + BQMJAAAAAAAAAAAAAAAAAAAAAAAAAAAPHBMNAAAAAAAAAAAAAAAAAAAAAAAAABojLy8TAAAAAAAAAAAA + AAAAAAAAAAAAAB0wMDAiPgAAAAAAAAAAAAAAAAAAAAAAQjAwMDAtGAAAAAAAAAAAAAAAAAAAAAAAFzIy + NTU5CgAAAAAAAAAAAAAAAAAAAAAAIjZYWFxcBwAAAAAAAAAAAAAAAAAAAAAANlxtdW11JQAAAAAAAAAA + PgcRDgkAAAAAXG1/lISAZgMAAAAAABkVLC5SVhcAAABNY3WWnJuLfB8UBAcQHkhWaX91dSsAAABNY2BM + mJeCiVJSVl9laX+WloSJgEIAAAAAXAEIC0tGjnR0dJaRk5qNjIyJQwAAAAAAJkNADBtdjIaPO1GSPYuJ + hnVEAAAAAAAAAClISWRcd4xwkGp8UE90VwAAAAAAAAAAAAAAKSQ1NYZ7OjhbPDdGAAAAAAAAAAAAAHNv + YGsAKyJoXFYmRwAAAAAAAAAAAAAAcnIAAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////APx//wD4f/8A8H//APA//wDgP/8A4D//AOA//wDgP8EA4B8BAMAAAQDAAAEA4AADAOAA + BwDwAB8A/AA/APCA/wDn9/8A////AP///wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA + AAAAAQAAAAEAAAAAAABjZGQAdmRjAHtpaQB/eHgAgU9PAKBaWgCFbm0AlWtqAKptbgCwZ2cAsGhoAKxw + cACteHkAvnJyAMZvcADGcHEAy3l5AMx9fgCFmXQAwIB/ANeUfQDhoX8AlIqJAJWMjACYiIgAoIaGAK2K + igCxh4cAvoGAALKKigC4iYgAuJWVAL2cnACss50AuqKhAL+mpgDLgoIAxImHAMeNjADLkI8AxpWTANCS + kQDYlZUA1J6dANqZmgDdnp4A1J+oAMaiogDOr68AzLKyANi5uADhpaIA4qypAOWtqADrrqsA4bKwAOay + sgDtuLYA57++AOy4uADxtLIA8be0APa9ugDswL4A9sG+ALCcxwC5ncIA06zBALnH0QC2ytQA7sPDAPLS + 0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAZBgUAAAAAAAAAAAAAAAAACw8KAAAAAAAAAAAAAAAAGhAQDgAAAAAAAAAAAAAAAAkRESUYAAAA + AAAAAAAAAAAlKy4uBwAAAAAAAAcDAAAAKzlHPCYCAAAYCB0oKgAAAC0wSDs0FB0nLDlAOiwAAAANAQQb + Pi9DRkVBPzUAAAAAJB4cKz5EQjMiNSkAAAAAAAAAHwwRNxYVEyQAAAAAAAAxMgAAACEgAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD//wAA4/8AAOP/AADD/wAAwf8AAMH5 + AADAwQAAwAEAAMADAADABwAA8A8AAM5/AAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAkAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAUAAAAOAEBAVUAAABUAAAANQAAABAAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAkFBSUvGRl5TCkpwlYuLtxDJCTQFw0NmQAA + AEkAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGAwMKE8rK6V6RET2klJR/5ZS + U/+OT0//ZDc38B0QEJoAAAAyAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYDAwYVzAwoopP + T/ygXVz/oFtb/55ZWf+bWFf/k1NT/1UvL9wGAwNcAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AARNKipxhk5O+adkY/+uZWX/tWdo/7VmZ/+qYWH/nltb/3hERPcfERGCAAAAFgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADEZGS1zQ0LXqGdm/7ptbf/Fb3D/x3Bx/8hwcf/BbW7/q2Vl/4hPT/82HR2gAAAAIAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAB1gxMYyYXl3/vXFx/8Zwcf/HcHH/x3Bx/8dwcf/HcHH/uG1t/5NY + V/9EJia2AAAAKQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB8fNH1MS+K4cnH/x3Fy/8dwcf/HcHH/x3Bx/8dw + cf/HcHH/wHBx/51gX/9PLCzGAAAAMwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + cP/Gb3D/oF9e/hMKCmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4QECynZmX7xnBx/sdwcf/HcHH/tG1t/h8REYMAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAx + MIzFc3T+xm9w/sdwcf7HcHH+vHR0/jAcHJkAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ4OAYVSUtfIcnP/yXZ3/st5ef/LeHn/xoB//kQq + KrEAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJxYWGrNvb/7Nfn//0oeI/tSNjf/UjI3/1ZOS/mE+PtQAAAAXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAIAAAARAAAALQAAADUAAAARAAAAAAAAAAAAAAAAQyYmUM6Ghv/Wj5D/3J2e/uCl + pf/fpKT/4KOi/qRycPkHBARlAAAABQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAJh8REYBYNTXMhVJR8XxM + TO8gEhKeAAAAEAAAAAAAAAAAbUVEe9aPkP7doKD+5rKz/uu9vv7rvLz+6rKx/tqfnf5iNzfnCAQEcwAA + ACoAAAAbAAAAIQIBATorGBiQhFNT67Z3dv68fn3+wYSD/siKiP6aZmX2AQAAKQAAAAAAAAAAd05Ni9eT + lP/jq6z/7cLC/vXS0v/zz9D/8b69/uyxrv+samr/l15d+2tDQ+NkPz7bdkxL451nZve+gYD/yY2M/tWg + n//jtrT/46+t/uOmpP+mdHPwBQMDFAAAAAAAAAAAdkpJh9iUlf7Hl5f+tJeX/uzOzv7lyMj+57y6/vS6 + t/7HhoX+xYaE/saJh/7MkpD+0ZmY/tejov7mt7X+7cXD/vDFxP7vvLr+8Le0/u2zsf5PMzOMDQcHAQAA + AAAAAAAAYTg4X9OOj/9aUlL/YGJi/nh2dv+skJD/qo2M/vnAvf/dn53/4KKg/+Cnp/7vxsT/u8PM/sHI + 0P/1xsT/9sG+/ve+u//3vrv/87q3/ntVVLkkFhYIAAAAAAAAAAAAAAAAVC8wD6BkZOWjhIT/jo6O/n1+ + fv+eenv/xpGR/vi/vP/wtbL/mZPP/0Z2+v69nrr/gd/x/nfD2v/2vLr/9Lq3/vG2tP/lq6j/elJRrjQg + IAoAAAAAAAAAAAAAAAAAAAAAAAAAAGc7OyeOWVnGv4eH/r2Fhf7YlZb+1Y6P/uinpv74v7z+3ay3/seo + w/7srZ/+7LGv/qmyjv63qI7+5Kel/r2GhPZ1S0p1QCcmAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAd0pKOpReXtKxb3D/yXl6/sx5ev/ws7D/6q6s/+Ked/7npFb/2ZiP/ny7gP+OjW/9h1dWr2I7 + OiMAAAAAAAAAAAAAAAAAAAAAAAAAALSCggSqcXIbo2dnN61xcVS/h4eIzp2c2cKWle2OY2OGbz4+Y4xN + Tr6zaWn84Jyb/9aXlv7Ji4r/p25t9INTUqZlPDw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJg + YASjcnMorH9/a6h7e4yabm6Df1NTU3VKSgwAAAAAAAAAAAAAAABgNDQgcj8/bntHR4ZnPDxTVTExDQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////APx//wD4P/8A8D//AOA//wDgH/8A4B//AMAf + /wDAH8EAwA8AAMAAAADAAAAAwAAAAMAAAQDAAAMA4AAHAPgAHwAAAH8AAcH/AP///wD///8A////ACgA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQc + HA5LKSlUNBwcSAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsO + DgV/SkqHm1hY+X5HR90tGRkuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB4SEhCr2Zm7sZwcf+oYWL5UC8vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAACnl9fnMRwcf/IcXL/tmxs/mI8PGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa0NCGbRsbdbMenv/zn5//8R9ff9ySkmCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA + AAkAAAAAAAAAAItYWDvFfn/y2ZWW/92fn//anJv/jWFgvwAAAB0AAAAAAAAAAAAAAAIzHBwiYjs7a3pM + S6pqQkKjLBoaMwAAAACeZ2dZ05KS/em0tP/vxMT/77u6/8CHhfpmPDyvRysqYlExMV1ySEiGnWdn07qB + gPzLkI//w4iG/HJLS3YAAAAAomloXsyRkf/DoKD/48bG/+jAv//hpKL/vX17/7h/fPu/iYj7z5qZ/+Gw + rv/rvLr/77q3/9ScmuR9U1I+AAAAAJZbWz2ndnbxdG9v/4yCgv+4lJP/77Wy/86erP+6nsH/tsXR/8PH + 0P/4wsD/9b26/+Cppu2peXdiAAAAAQAAAABYKCgHn2lqe6eCguSsgoL90pKS//Cxrv/TrcP/s5y+/8i3 + s/+quab/26mh/82UktSgbm1TBAAAAwAAAACud3cEvYGBC7N6ehyyfHtyt39+3bNub9vLgYH05qak/+Kg + g//OlH39jZR04Zd0aYmDT1EiAAAAAAAAAAAAAAAAr3t7D7aCgki5h4Z8uImJgah+fUltPz8ajU1ORq1s + bI6vdHOgm2RkaYxJUiZgCygCAAAAAAAAAAAAAAAAAAAAAGo9PQF9UVEHcEdHCTodHQIAAAAAAAAAAAAA + AAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AADh/wAAwf8AAMH/ + AACB/wAAgfkAAIDAAACAAAAAgAAAAIAAAACAAQAAAAcAAAAPAAAOfwAA//8AAA== + + + \ No newline at end of file From 0ac17f2d1e68d9b67db135aff35e9dab7de6b0d6 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Mar 2018 14:33:18 +0000 Subject: [PATCH 097/339] UI - added Core Emulation Settings menu --- .../BizHawk.Client.EmuHawk.csproj | 9 + BizHawk.Client.EmuHawk/MainForm.Designer.cs | 42 +- BizHawk.Client.EmuHawk/MainForm.Events.cs | 5 + BizHawk.Client.EmuHawk/MainForm.cs | 2 +- ...XSpectrumCoreEmulationSettings.Designer.cs | 187 ++++++ .../ZXSpectrumCoreEmulationSettings.cs | 117 ++++ .../ZXSpectrumCoreEmulationSettings.resx | 624 ++++++++++++++++++ .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 152 +++++ 8 files changed, 1121 insertions(+), 17 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.resx diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 6ed196a29b..3de675a0f4 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -497,6 +497,12 @@ TI83PaletteConfig.cs + + Form + + + ZXSpectrumCoreEmulationSettings.cs + Form @@ -1401,6 +1407,9 @@ TI83PaletteConfig.cs + + ZXSpectrumCoreEmulationSettings.cs + ZXSpectrumJoystickSettings.cs diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 6a3da605ba..80c7e11780 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -380,6 +380,7 @@ this.FeaturesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.preferencesToolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); @@ -453,7 +454,7 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumCoreEmulationSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -3238,7 +3239,7 @@ this.C64DisksSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.toolStripSeparator36}); this.C64DisksSubMenu.Name = "C64DisksSubMenu"; - this.C64DisksSubMenu.Size = new System.Drawing.Size(152, 22); + this.C64DisksSubMenu.Size = new System.Drawing.Size(125, 22); this.C64DisksSubMenu.Text = "Disks"; this.C64DisksSubMenu.DropDownOpened += new System.EventHandler(this.C64DisksSubMenu_DropDownOpened); // @@ -3250,7 +3251,7 @@ // C64SettingsMenuItem // this.C64SettingsMenuItem.Name = "C64SettingsMenuItem"; - this.C64SettingsMenuItem.Size = new System.Drawing.Size(152, 22); + this.C64SettingsMenuItem.Size = new System.Drawing.Size(125, 22); this.C64SettingsMenuItem.Text = "&Settings..."; this.C64SettingsMenuItem.Click += new System.EventHandler(this.C64SettingsMenuItem_Click); // @@ -3282,7 +3283,7 @@ // preferencesToolStripMenuItem // this.preferencesToolStripMenuItem.Name = "preferencesToolStripMenuItem"; - this.preferencesToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.preferencesToolStripMenuItem.Size = new System.Drawing.Size(144, 22); this.preferencesToolStripMenuItem.Text = "Preferences..."; this.preferencesToolStripMenuItem.Click += new System.EventHandler(this.preferencesToolStripMenuItem_Click); // @@ -3297,7 +3298,7 @@ // preferencesToolStripMenuItem3 // this.preferencesToolStripMenuItem3.Name = "preferencesToolStripMenuItem3"; - this.preferencesToolStripMenuItem3.Size = new System.Drawing.Size(152, 22); + this.preferencesToolStripMenuItem3.Size = new System.Drawing.Size(144, 22); this.preferencesToolStripMenuItem3.Text = "Preferences..."; this.preferencesToolStripMenuItem3.Click += new System.EventHandler(this.preferencesToolStripMenuItem3_Click); // @@ -3312,7 +3313,7 @@ // preferencesToolStripMenuItem1 // this.preferencesToolStripMenuItem1.Name = "preferencesToolStripMenuItem1"; - this.preferencesToolStripMenuItem1.Size = new System.Drawing.Size(152, 22); + this.preferencesToolStripMenuItem1.Size = new System.Drawing.Size(144, 22); this.preferencesToolStripMenuItem1.Text = "Preferences..."; this.preferencesToolStripMenuItem1.Click += new System.EventHandler(this.preferencesToolStripMenuItem1_Click); // @@ -3347,7 +3348,7 @@ // this.OnlineHelpMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Help; this.OnlineHelpMenuItem.Name = "OnlineHelpMenuItem"; - this.OnlineHelpMenuItem.Size = new System.Drawing.Size(152, 22); + this.OnlineHelpMenuItem.Size = new System.Drawing.Size(146, 22); this.OnlineHelpMenuItem.Text = "&Online Help..."; this.OnlineHelpMenuItem.Click += new System.EventHandler(this.OnlineHelpMenuItem_Click); // @@ -3355,7 +3356,7 @@ // this.ForumsMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.TAStudio; this.ForumsMenuItem.Name = "ForumsMenuItem"; - this.ForumsMenuItem.Size = new System.Drawing.Size(152, 22); + this.ForumsMenuItem.Size = new System.Drawing.Size(146, 22); this.ForumsMenuItem.Text = "Forums..."; this.ForumsMenuItem.Click += new System.EventHandler(this.ForumsMenuItem_Click); // @@ -3363,7 +3364,7 @@ // this.FeaturesMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.kitchensink; this.FeaturesMenuItem.Name = "FeaturesMenuItem"; - this.FeaturesMenuItem.Size = new System.Drawing.Size(152, 22); + this.FeaturesMenuItem.Size = new System.Drawing.Size(146, 22); this.FeaturesMenuItem.Text = "&Features"; this.FeaturesMenuItem.Click += new System.EventHandler(this.FeaturesMenuItem_Click); // @@ -3371,13 +3372,14 @@ // this.AboutMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.CorpHawkSmall; this.AboutMenuItem.Name = "AboutMenuItem"; - this.AboutMenuItem.Size = new System.Drawing.Size(152, 22); + this.AboutMenuItem.Size = new System.Drawing.Size(146, 22); this.AboutMenuItem.Text = "&About"; this.AboutMenuItem.Click += new System.EventHandler(this.AboutMenuItem_Click); // // zXSpectrumToolStripMenuItem // this.zXSpectrumToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ZXSpectrumCoreEmulationSettingsMenuItem, this.ZXSpectrumControllerConfigurationMenuItem, this.preferencesToolStripMenuItem4}); this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; @@ -3385,10 +3387,17 @@ this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; this.zXSpectrumToolStripMenuItem.DropDownOpened += new System.EventHandler(this.zXSpectrumToolStripMenuItem_DropDownOpened); // + // ZXSpectrumControllerConfigurationMenuItem + // + this.ZXSpectrumControllerConfigurationMenuItem.Name = "ZXSpectrumControllerConfigurationMenuItem"; + this.ZXSpectrumControllerConfigurationMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumControllerConfigurationMenuItem.Text = "Joystick Configuration"; + this.ZXSpectrumControllerConfigurationMenuItem.Click += new System.EventHandler(this.ZXSpectrumControllerConfigurationMenuItem_Click); + // // preferencesToolStripMenuItem4 // this.preferencesToolStripMenuItem4.Name = "preferencesToolStripMenuItem4"; - this.preferencesToolStripMenuItem4.Size = new System.Drawing.Size(192, 22); + this.preferencesToolStripMenuItem4.Size = new System.Drawing.Size(201, 22); this.preferencesToolStripMenuItem4.Text = "Preferences"; this.preferencesToolStripMenuItem4.Click += new System.EventHandler(this.preferencesToolStripMenuItem4_Click); // @@ -4018,12 +4027,12 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // - // ZXSpectrumControllerConfigurationMenuItem + // ZXSpectrumCoreEmulationSettingsMenuItem // - this.ZXSpectrumControllerConfigurationMenuItem.Name = "ZXSpectrumControllerConfigurationMenuItem"; - this.ZXSpectrumControllerConfigurationMenuItem.Size = new System.Drawing.Size(192, 22); - this.ZXSpectrumControllerConfigurationMenuItem.Text = "Joystick Configuration"; - this.ZXSpectrumControllerConfigurationMenuItem.Click += new System.EventHandler(this.ZXSpectrumControllerConfigurationMenuItem_Click); + this.ZXSpectrumCoreEmulationSettingsMenuItem.Name = "ZXSpectrumCoreEmulationSettingsMenuItem"; + this.ZXSpectrumCoreEmulationSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumCoreEmulationSettingsMenuItem.Text = "Core Emulation Settings"; + this.ZXSpectrumCoreEmulationSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumCoreEmulationSettingsMenuItem_Click); // // MainForm // @@ -4490,5 +4499,6 @@ private System.Windows.Forms.ToolStripMenuItem zXSpectrumToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem4; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumControllerConfigurationMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumCoreEmulationSettingsMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 858982ec2c..d1b6674def 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2454,6 +2454,11 @@ namespace BizHawk.Client.EmuHawk new ZXSpectrumJoystickSettings().ShowDialog(); } + private void ZXSpectrumCoreEmulationSettingsMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumCoreEmulationSettings().ShowDialog(); + } + #endregion #region Help diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 137e9d075a..673769f728 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -4297,7 +4297,7 @@ namespace BizHawk.Client.EmuHawk private void preferencesToolStripMenuItem3_Click(object sender, EventArgs e) { GenericCoreConfig.DoDialog(this, "PC-FX Settings"); - } + } private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording) { diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs new file mode 100644 index 0000000000..c74ab81fab --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs @@ -0,0 +1,187 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumCoreEmulationSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumCoreEmulationSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.MachineSelectionComboBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.lblMachineNotes = new System.Windows.Forms.Label(); + this.determEmucheckBox1 = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.borderTypecomboBox1 = new System.Windows.Forms.ComboBox(); + this.lblBorderInfo = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(247, 485); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(313, 485); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 46); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(98, 13); + this.label4.TabIndex = 15; + this.label4.Text = "Emulated Machine:"; + // + // MachineSelectionComboBox + // + this.MachineSelectionComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.MachineSelectionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.MachineSelectionComboBox.FormattingEnabled = true; + this.MachineSelectionComboBox.Location = new System.Drawing.Point(12, 62); + this.MachineSelectionComboBox.Name = "MachineSelectionComboBox"; + this.MachineSelectionComboBox.Size = new System.Drawing.Size(361, 21); + this.MachineSelectionComboBox.TabIndex = 13; + this.MachineSelectionComboBox.SelectionChangeCommitted += new System.EventHandler(this.MachineSelectionComboBox_SelectionChangeCommitted); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(159, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Emulation Settings"; + // + // lblMachineNotes + // + this.lblMachineNotes.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblMachineNotes.Location = new System.Drawing.Point(15, 95); + this.lblMachineNotes.Name = "lblMachineNotes"; + this.lblMachineNotes.Size = new System.Drawing.Size(358, 204); + this.lblMachineNotes.TabIndex = 20; + this.lblMachineNotes.Text = "null\r\n"; + // + // determEmucheckBox1 + // + this.determEmucheckBox1.AutoSize = true; + this.determEmucheckBox1.Location = new System.Drawing.Point(15, 302); + this.determEmucheckBox1.Name = "determEmucheckBox1"; + this.determEmucheckBox1.Size = new System.Drawing.Size(135, 17); + this.determEmucheckBox1.TabIndex = 21; + this.determEmucheckBox1.Text = "Deterministic Emulation"; + this.determEmucheckBox1.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 335); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(118, 13); + this.label2.TabIndex = 23; + this.label2.Text = "Rendered Border Type:"; + // + // borderTypecomboBox1 + // + this.borderTypecomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.borderTypecomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.borderTypecomboBox1.FormattingEnabled = true; + this.borderTypecomboBox1.Location = new System.Drawing.Point(12, 351); + this.borderTypecomboBox1.Name = "borderTypecomboBox1"; + this.borderTypecomboBox1.Size = new System.Drawing.Size(157, 21); + this.borderTypecomboBox1.TabIndex = 22; + this.borderTypecomboBox1.SelectedIndexChanged += new System.EventHandler(this.borderTypecomboBox1_SelectedIndexChanged); + // + // lblBorderInfo + // + this.lblBorderInfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblBorderInfo.Location = new System.Drawing.Point(175, 351); + this.lblBorderInfo.Name = "lblBorderInfo"; + this.lblBorderInfo.Size = new System.Drawing.Size(196, 21); + this.lblBorderInfo.TabIndex = 24; + this.lblBorderInfo.Text = "null"; + this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // ZXSpectrumCoreEmulationSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(385, 520); + this.Controls.Add(this.lblBorderInfo); + this.Controls.Add(this.label2); + this.Controls.Add(this.borderTypecomboBox1); + this.Controls.Add(this.determEmucheckBox1); + this.Controls.Add(this.lblMachineNotes); + this.Controls.Add(this.label1); + this.Controls.Add(this.label4); + this.Controls.Add(this.MachineSelectionComboBox); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumCoreEmulationSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Core Emulation Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox MachineSelectionComboBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label lblMachineNotes; + private System.Windows.Forms.CheckBox determEmucheckBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox borderTypecomboBox1; + private System.Windows.Forms.Label lblBorderInfo; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs new file mode 100644 index 0000000000..bb8ba92d50 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs @@ -0,0 +1,117 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumCoreEmulationSettings : Form + { + private ZXSpectrum.ZXSpectrumSyncSettings _syncSettings; + + public ZXSpectrumCoreEmulationSettings() + { + InitializeComponent(); + } + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _syncSettings = ((ZXSpectrum)Global.Emulator).GetSyncSettings().Clone(); + + // machine selection + var machineTypes = Enum.GetNames(typeof(MachineType)); + foreach (var val in machineTypes) + { + MachineSelectionComboBox.Items.Add(val); + } + MachineSelectionComboBox.SelectedItem = _syncSettings.MachineType.ToString(); + UpdateMachineNotes((MachineType)Enum.Parse(typeof(MachineType), MachineSelectionComboBox.SelectedItem.ToString())); + + // border selecton + var borderTypes = Enum.GetNames(typeof(ZXSpectrum.BorderType)); + foreach (var val in borderTypes) + { + borderTypecomboBox1.Items.Add(val); + } + borderTypecomboBox1.SelectedItem = _syncSettings.BorderType.ToString(); + UpdateBorderNotes((ZXSpectrum.BorderType)Enum.Parse(typeof(ZXSpectrum.BorderType), borderTypecomboBox1.SelectedItem.ToString())); + + // deterministic emulation + determEmucheckBox1.Checked = _syncSettings.DeterministicEmulation; + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _syncSettings.MachineType.ToString() != MachineSelectionComboBox.SelectedItem.ToString() + || _syncSettings.BorderType.ToString() != borderTypecomboBox1.SelectedItem.ToString() + || _syncSettings.DeterministicEmulation != determEmucheckBox1.Checked; + + if (changed) + { + _syncSettings.MachineType = (MachineType)Enum.Parse(typeof(MachineType), MachineSelectionComboBox.SelectedItem.ToString()); + _syncSettings.BorderType = (ZXSpectrum.BorderType)Enum.Parse(typeof(ZXSpectrum.BorderType), borderTypecomboBox1.SelectedItem.ToString()); + _syncSettings.DeterministicEmulation = determEmucheckBox1.Checked; + + GlobalWin.MainForm.PutCoreSyncSettings(_syncSettings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Core emulator settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + + private void MachineSelectionComboBox_SelectionChangeCommitted(object sender, EventArgs e) + { + ComboBox cb = sender as ComboBox; + UpdateMachineNotes((MachineType)Enum.Parse(typeof(MachineType), cb.SelectedItem.ToString())); + } + + private void UpdateMachineNotes(MachineType type) + { + lblMachineNotes.Text = ZXMachineMetaData.GetMetaString(type); + } + + private void borderTypecomboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + ComboBox cb = sender as ComboBox; + UpdateBorderNotes((ZXSpectrum.BorderType)Enum.Parse(typeof(ZXSpectrum.BorderType), cb.SelectedItem.ToString())); + } + + private void UpdateBorderNotes(ZXSpectrum.BorderType type) + { + switch (type) + { + case ZXSpectrum.BorderType.Full: + lblBorderInfo.Text = "Original border sizes"; + break; + case ZXSpectrum.BorderType.Medium: + lblBorderInfo.Text = "All borders 24px"; + break; + case ZXSpectrum.BorderType.None: + lblBorderInfo.Text = "No border at all"; + break; + case ZXSpectrum.BorderType.Small: + lblBorderInfo.Text = "All borders 10px"; + break; + case ZXSpectrum.BorderType.Widescreen: + lblBorderInfo.Text = "No top and bottom border (almost 16:9)"; + break; + } + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAADLytDi9tOemQAAAAAAUy9EecLEsa1uPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPj11Mme + VakAAAAAAAAAAAAATS84M0akAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE + AAAAAAAAAAAAAAABAAAAAQAAAAAAAFFNTQBRUlIAU1RUAGJHRwBiT08Aa0lIAGJTUwBrVlYAYllZAGZc + XABpWloAb1xbAHNTUwB7V1YAc1hXAHFbWwBkZWUAaWFhAG5kZABpamkAcGFhAHlubgB2cHAAf3V1AH55 + eQB8fX0AgUpKAI1PTwCLWFcAhlhYAI9ZWQCKXFsAm1ZWAJJZWQCWWVgAmlpbAJtcWwCiXFwAl2BfAIBg + YACAZ2YAgG9vAI9oaACWZWQAmGBhAJ5kZACcaWoAmm9vAIV0dACNcHAAiXZ2AIB8fACac3IAm3V0AJ51 + dQCZfHwAnHx8AKNmZgCnZmYAqmJiAK5jYwCvb24AtWVmALBtbgC5bW0AvmxtAKx+fQCxcnIAtHBwALZz + dACydXQAtnd2ALlwcAC5dnYAt3p5ALh5eAC8fHsAun18ALx+fQDGb3AAxnBxAMdzdADAd3YAyHJzAMlz + dADJdXYAynd4AMd/fwDMe3wAzXx9AHunbwBhvHIAYsN4ANuLOwC2hn4A4Zt5APC3ZABte9sAX47+AHWM + 5QAl0foAY+P8AIeDgwCFhoYAioSEAJOIiACWi4sAmpKRAKGCgQCmhYUAqYGBAKuDhACniooApYyMAKiO + jQCyhYMAvoWEALeNjQCrj5AAr5eXALSVlAC9lJMAmbCEAK6RugDBgYAAwoSCAMWDhADChoQAxYeFAM6A + gQDFiIYAxoqIAMqIiQDMi4oAy4yKAMiPjQDPj44A0ISFANKJigDUi4wA04+NANWNjgDKkY8A0JCOANud + iQDWj5AAzJSTAM2XlgDGm5oA1pGSANOUkgDVl5EA1pOUANiVlgDYmJUA2ZeYANKenADbmpsA3pmYANuc + mgDbn5wA1aacAN6gngDqqZoA3Z+gAMyjowDCra0AxqysAMqpqQDboaAA3qKiAN6logDbp6UA3aWkANer + qgDWsbMA0rW0ANe0tADfs7IA4aSiAOGlpQDkp6UA46imAOWopgDsraIA6qimAOGoqADhrqwA6a2rAOqv + rADpsK4A7LGuAOGzswDlsbEA7bKxAO+1sgDotrYA5rm3AO+4twDot7sA6bq5AOu9uwDrv70A8bazAPG2 + tADxuLUA9Lm2APC9uwD2vboA9L+9APi+uwD4v7wA8sC+APXAvgD5wL0AkILJAKqXzACsu8cAqr/LALLV + 3QDawMIA48XFAOvDwQDswMAA7cTDAO/ExQDgxsgA8cbEAPTGxADwyskA9MvJAPLNzQD21dYA+NjZAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAMEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHCEcBQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAayU9PSYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdQlBSQiJpAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAM0pSUlJQPRcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnUlJSUlJGFQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFJSUlJSUkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzUlJSWVJZfxAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5XWYqKioqGDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASoqMkpqa + mqAsAAAAAAAAAAAAAAAAAABoNAAAAAAAAACMjJyuvLy2toYHAAAAAAAAAAAAABcOIDouBgAAAAAAc4yc + tsHKysPAriIKAAAAAAAAABYgRk1LTX+DEAAAAABukqXB4ejo4dHPQCIEChcXEwggTXV/k66unKMpAAAA + AG6Srsro6ero0dN/Rk1NRk2Dg4STrsbh4cHAt2sAAAAAbpKuOXPe6ajW15KGg4OGk528yuHo5eHPz882 + AAAAAAB4jCkDAxSoMabXt5yjt8ro3ePo5dbT09HTdAAAAAAAAABGcBFoGgFwdtfDwHxi2dpmZcrX09HP + z0MAAAAAAAAAAHh/qWwaOa6cz9PNZGPYsdzbzc3DwLk2AAAAAAAAAAAAAAAvhpKakoyg19HNyKS5wHtb + orZ/cwAAAAAAAAAAAAAAAAAANkaKWVm5zb1gYV6cXVxfNgAAAAAAAAAAAAAAAAAAALGvlTIuP1K5tqCR + l4xfLwAAAAAAAAAAAAAAAAAAsbPBenkAAAAAcCVYjE0scwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////+f///+D////A////wH + ///4B///+Af///gH///wB///8Af///AH/+fwA/8D4AH8AeAAAAHgAAAB4AAAA+AAAAfwAAAP8AAAH/wA + AD//AAD//gAD//B4D////////////////////////////ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA + AAAAAAAAAAEAAAABAAAAAAAAWlJSAHBJSQB1SEgAe1dXAHdYWAB5WlkAel1dAGBiYgB1bGwAfWtrAHh2 + dgB9fn4Ag01NAIRXVwCIV1cAhV9eAItbWgCgX14ApV1dAJhgXwCNYGAAnWtqAJhtbQCCdnYAh3x8AI15 + eACeensAqGBgAKhoZwCga2oArGpqALNqagCzb28AtG1tALltbQCxb3AApnVzAKlzcwCqdHMApnp6AKd+ + fgCpensAq3x7ALZ3dgC8dHQAvH59AMZvcADGcHEAxXN0AMhycwDJdncAynh5AMx5egDNfn8Ajo1wAOek + VgDGgH8A4p53AEZ2+gB8u4AAd8PaAIuEhACOh4cAjo6OAJ+DggCejo4Ao4SEAKSIiACsi4sAqo2MAK6P + jgC+gYAAvoaGAL+KiACskJAAtJeXALWenQC5np4At6iOAKmyjgC9nroAwYSDAMaGhADOhoYAxomHAMiK + iQDJjYwA0oeIANOOjwDUjY0A2ZiPANaPkADGkZEAx5eXAMySkADGnZwA1ZOSANeTlADWl5YA2JSVANGZ + mADan50A3J6dAOCcmwDVoJ8A7K2fAMOtrQDXo6IA3aCgAN+kpADVq6oA3ay3AMu0tADPtrYA3L+/AOCi + oQDhpqUA5KelAOinpgDlq6gA46usAOOvrQDqrqwA7LGuAOayswDjtrQA5re1AOqysQDts7EA57y6AO+8 + ugDrvL0A8LOwAPC1sgDwtrQA87q3APS6twD2vboA8b69APi/vAD2wb4A+cC9AJmTzwDHqMMAu8PMAIHf + 8QDByNAA7cLCAO3FwwDvxsQA5cjIAOzOzgDwxcQA9cbEAPPP0AD10tIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + BQMJAAAAAAAAAAAAAAAAAAAAAAAAAAAPHBMNAAAAAAAAAAAAAAAAAAAAAAAAABojLy8TAAAAAAAAAAAA + AAAAAAAAAAAAAB0wMDAiPgAAAAAAAAAAAAAAAAAAAAAAQjAwMDAtGAAAAAAAAAAAAAAAAAAAAAAAFzIy + NTU5CgAAAAAAAAAAAAAAAAAAAAAAIjZYWFxcBwAAAAAAAAAAAAAAAAAAAAAANlxtdW11JQAAAAAAAAAA + PgcRDgkAAAAAXG1/lISAZgMAAAAAABkVLC5SVhcAAABNY3WWnJuLfB8UBAcQHkhWaX91dSsAAABNY2BM + mJeCiVJSVl9laX+WloSJgEIAAAAAXAEIC0tGjnR0dJaRk5qNjIyJQwAAAAAAJkNADBtdjIaPO1GSPYuJ + hnVEAAAAAAAAAClISWRcd4xwkGp8UE90VwAAAAAAAAAAAAAAKSQ1NYZ7OjhbPDdGAAAAAAAAAAAAAHNv + YGsAKyJoXFYmRwAAAAAAAAAAAAAAcnIAAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////APx//wD4f/8A8H//APA//wDgP/8A4D//AOA//wDgP8EA4B8BAMAAAQDAAAEA4AADAOAA + BwDwAB8A/AA/APCA/wDn9/8A////AP///wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA + AAAAAQAAAAEAAAAAAABjZGQAdmRjAHtpaQB/eHgAgU9PAKBaWgCFbm0AlWtqAKptbgCwZ2cAsGhoAKxw + cACteHkAvnJyAMZvcADGcHEAy3l5AMx9fgCFmXQAwIB/ANeUfQDhoX8AlIqJAJWMjACYiIgAoIaGAK2K + igCxh4cAvoGAALKKigC4iYgAuJWVAL2cnACss50AuqKhAL+mpgDLgoIAxImHAMeNjADLkI8AxpWTANCS + kQDYlZUA1J6dANqZmgDdnp4A1J+oAMaiogDOr68AzLKyANi5uADhpaIA4qypAOWtqADrrqsA4bKwAOay + sgDtuLYA57++AOy4uADxtLIA8be0APa9ugDswL4A9sG+ALCcxwC5ncIA06zBALnH0QC2ytQA7sPDAPLS + 0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAZBgUAAAAAAAAAAAAAAAAACw8KAAAAAAAAAAAAAAAAGhAQDgAAAAAAAAAAAAAAAAkRESUYAAAA + AAAAAAAAAAAlKy4uBwAAAAAAAAcDAAAAKzlHPCYCAAAYCB0oKgAAAC0wSDs0FB0nLDlAOiwAAAANAQQb + Pi9DRkVBPzUAAAAAJB4cKz5EQjMiNSkAAAAAAAAAHwwRNxYVEyQAAAAAAAAxMgAAACEgAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD//wAA4/8AAOP/AADD/wAAwf8AAMH5 + AADAwQAAwAEAAMADAADABwAA8A8AAM5/AAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAkAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAUAAAAOAEBAVUAAABUAAAANQAAABAAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAkFBSUvGRl5TCkpwlYuLtxDJCTQFw0NmQAA + AEkAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGAwMKE8rK6V6RET2klJR/5ZS + U/+OT0//ZDc38B0QEJoAAAAyAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYDAwYVzAwoopP + T/ygXVz/oFtb/55ZWf+bWFf/k1NT/1UvL9wGAwNcAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AARNKipxhk5O+adkY/+uZWX/tWdo/7VmZ/+qYWH/nltb/3hERPcfERGCAAAAFgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADEZGS1zQ0LXqGdm/7ptbf/Fb3D/x3Bx/8hwcf/BbW7/q2Vl/4hPT/82HR2gAAAAIAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAB1gxMYyYXl3/vXFx/8Zwcf/HcHH/x3Bx/8dwcf/HcHH/uG1t/5NY + V/9EJia2AAAAKQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB8fNH1MS+K4cnH/x3Fy/8dwcf/HcHH/x3Bx/8dw + cf/HcHH/wHBx/51gX/9PLCzGAAAAMwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + 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 + + + \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index c3204f1c69..f1c5bdff80 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json; using BizHawk.Common; using BizHawk.Emulation.Common; using System.ComponentModel; +using System.Text; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -194,4 +195,155 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //Fastest } } + + /// + /// Provides information on each emulated machine + /// + public class ZXMachineMetaData + { + public MachineType MachineType { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string Released { get; set; } + public string CPU { get; set; } + public string Memory { get; set; } + public string Video { get; set; } + public string Audio { get; set; } + public string Media { get; set; } + public string OtherMisc { get; set; } + + + public static ZXMachineMetaData GetMetaObject(MachineType type) + { + ZXMachineMetaData m = new ZXMachineMetaData(); + m.MachineType = type; + + switch (type) + { + case MachineType.ZXSpectrum16: + m.Name = "Sinclair ZX Spectrum 16K"; + m.Description = "The original ZX Spectrum 16K RAM version. Aside from available RAM this machine is technically identical to the 48K machine that was released at the same time. "; + m.Description += "Due to the small amount of RAM, very few games were actually made to run on this model."; + m.Released = "1982"; + m.CPU = "Zilog Z80A @ 3.5MHz"; + m.Memory = "16KB ROM / 16KB RAM"; + m.Video = "ULA @ 7MHz - PAL (50.08Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) - Internal Speaker"; + m.Media = "Cassette Tape (via 3rd party external tape player)"; + break; + case MachineType.ZXSpectrum48: + m.Name = "Sinclair ZX Spectrum 48K / 48K+"; + m.Description = "The original ZX Spectrum 48K RAM version. 2 years later a 'plus' version was released that had a better keyboard. "; + m.Description += "Electronically both the 48K and + are identical, so ZXHawk treats them as the same emulated machine. "; + m.Description += "These machines dominated the UK 8-bit home computer market throughout the 1980's so most non-128k only games are compatible."; + m.Released = "1982 (48K) / 1984 (48K+)"; + m.CPU = "Zilog Z80A @ 3.5MHz"; + m.Memory = "16KB ROM / 48KB RAM"; + m.Video = "ULA @ 7MHz - PAL (50.08Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) - Internal Speaker"; + m.Media = "Cassette Tape (via 3rd party external tape player)"; + break; + case MachineType.ZXSpectrum128: + m.Name = "Sinclair ZX Spectrum 128"; + m.Description = "The first Spectrum 128K machine released in Spain in 1985 and later UK in 1986. "; + m.Description += "With an updated ROM and new memory paging system to work around the Z80's 16-bit address bus. "; + m.Description += "The 128 shipped with a copy of the 48k ROM (that is paged in when required) and a new startup menu with the option of dropping into a '48k mode'. "; + m.Description += "Even so, there were some compatibility issues with older Spectrum games that were written to utilise some of the previous model's intricacies. "; + m.Description += "Many games released after 1985 supported the new AY-3-8912 PSG chip making for far superior audio. The extra memory also enabled many games to be loaded in all at once (rather than loading each level from tape when needed)."; + m.Released = "1985 / 1986"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "32KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "Cassette Tape (via 3rd party external tape player)"; + break; + case MachineType.ZXSpectrum128Plus2: + m.Name = "Sinclair ZX Spectrum +2"; + m.Description = "The first Sinclair Spectrum 128K machine that was released after Amstrad purchased Sinclair in 1986. "; + m.Description += "Electronically it was almost identical to the 128, but with the addition of a built-in tape deck and 2 Sinclair Joystick ports."; + m.Released = "1986"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "32KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "Cassette Tape (via built-in Datacorder)"; + break; + case MachineType.ZXSpectrum128Plus2a: + m.Name = "Sinclair ZX Spectrum +2a"; + m.Description = "The +2a looks almost identical to the +2 but is a variant of the +3 machine that was released the same year (except with the same built-in datacorder that the +2 had rather than a floppy drive). "; + m.Description += "Memory paging again changed significantly and this (along with memory contention timing changes) caused more compatibility issues with some older games. "; + m.Description += "Although functionally identical to the +3, it does not contain floppy disk controller."; + m.Released = "1987"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "64KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "Cassette Tape (via built-in Datacorder)"; + break; + case MachineType.ZXSpectrum128Plus3: + m.Name = "Sinclair ZX Spectrum +3"; + m.Description = "Amstrad released the +3 the same year as the +2a, but it featured a built-in floppy drive rather than a datacorder. An external cassette player could still be connected though as in the older 48k models. "; + m.Description += "Memory paging again changed significantly and this (along with memory contention timing changes) caused more compatibility issues with some older games. "; + m.Description += "Currently ZXHawk does not emulate the floppy drive or floppy controller so the machine reports as a +2a on boot."; + m.Released = "1987"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "64KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "3\" Floppy Disk (via built-in Floppy Drive)"; + break; + } + return m; + } + + public static string GetMetaString(MachineType type) + { + var m = GetMetaObject(type); + + StringBuilder sb = new StringBuilder(); + + sb.Append(m.Name); + sb.Append("\n"); + sb.Append("-----------------------------------------------------------------\n"); + // Release + sb.Append("Released:"); + sb.Append(" "); + sb.Append(m.Released); + sb.Append("\n"); + // CPU + sb.Append("CPU:"); + sb.Append(" "); + sb.Append(m.CPU); + sb.Append("\n"); + // Memory + sb.Append("Memory:"); + sb.Append(" "); + sb.Append(m.Memory); + sb.Append("\n"); + // Video + sb.Append("Video:"); + sb.Append(" "); + sb.Append(m.Video); + sb.Append("\n"); + // Audio + sb.Append("Audio:"); + sb.Append(" "); + sb.Append(m.Audio); + sb.Append("\n"); + // Audio + sb.Append("Media:"); + sb.Append(" "); + sb.Append(m.Media); + sb.Append("\n"); + + sb.Append("-----------------------------------------------------------------\n"); + // description + sb.Append(m.Description); + if (m.OtherMisc != null) + sb.Append("\n" + m.OtherMisc); + + return sb.ToString(); + + } + } } From a0e2695811b7acfb5b5b3b18aefcd759ca4dcb87 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Mar 2018 15:12:55 +0000 Subject: [PATCH 098/339] UI - Added last non-sync settings menu --- .../BizHawk.Client.EmuHawk.csproj | 9 + BizHawk.Client.EmuHawk/MainForm.Designer.cs | 22 +- BizHawk.Client.EmuHawk/MainForm.Events.cs | 5 + ...XSpectrumCoreEmulationSettings.Designer.cs | 6 +- .../ZXSpectrumNonSyncSettings.Designer.cs | 162 +++++ .../ZXSpectrum/ZXSpectrumNonSyncSettings.cs | 66 ++ .../ZXSpectrum/ZXSpectrumNonSyncSettings.resx | 624 ++++++++++++++++++ 7 files changed, 885 insertions(+), 9 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.resx diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 3de675a0f4..b620d3b6eb 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -497,6 +497,12 @@ TI83PaletteConfig.cs + + Form + + + ZXSpectrumNonSyncSettings.cs + Form @@ -1407,6 +1413,9 @@ TI83PaletteConfig.cs + + ZXSpectrumNonSyncSettings.cs + ZXSpectrumCoreEmulationSettings.cs diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 80c7e11780..28e7f9368b 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -380,6 +380,7 @@ this.FeaturesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumCoreEmulationSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.preferencesToolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -454,7 +455,7 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.ZXSpectrumCoreEmulationSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumNonSyncSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -3381,12 +3382,20 @@ this.zXSpectrumToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.ZXSpectrumCoreEmulationSettingsMenuItem, this.ZXSpectrumControllerConfigurationMenuItem, + this.ZXSpectrumNonSyncSettingsMenuItem, this.preferencesToolStripMenuItem4}); this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; this.zXSpectrumToolStripMenuItem.DropDownOpened += new System.EventHandler(this.zXSpectrumToolStripMenuItem_DropDownOpened); // + // ZXSpectrumCoreEmulationSettingsMenuItem + // + this.ZXSpectrumCoreEmulationSettingsMenuItem.Name = "ZXSpectrumCoreEmulationSettingsMenuItem"; + this.ZXSpectrumCoreEmulationSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumCoreEmulationSettingsMenuItem.Text = "Core Emulation Settings"; + this.ZXSpectrumCoreEmulationSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumCoreEmulationSettingsMenuItem_Click); + // // ZXSpectrumControllerConfigurationMenuItem // this.ZXSpectrumControllerConfigurationMenuItem.Name = "ZXSpectrumControllerConfigurationMenuItem"; @@ -4027,12 +4036,12 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // - // ZXSpectrumCoreEmulationSettingsMenuItem + // ZXSpectrumNonSyncSettingsMenuItem // - this.ZXSpectrumCoreEmulationSettingsMenuItem.Name = "ZXSpectrumCoreEmulationSettingsMenuItem"; - this.ZXSpectrumCoreEmulationSettingsMenuItem.Size = new System.Drawing.Size(201, 22); - this.ZXSpectrumCoreEmulationSettingsMenuItem.Text = "Core Emulation Settings"; - this.ZXSpectrumCoreEmulationSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumCoreEmulationSettingsMenuItem_Click); + this.ZXSpectrumNonSyncSettingsMenuItem.Name = "ZXSpectrumNonSyncSettingsMenuItem"; + this.ZXSpectrumNonSyncSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumNonSyncSettingsMenuItem.Text = "Non-Sync Settings"; + this.ZXSpectrumNonSyncSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumNonSyncSettingsMenuItem_Click); // // MainForm // @@ -4500,5 +4509,6 @@ private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem4; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumControllerConfigurationMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumCoreEmulationSettingsMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumNonSyncSettingsMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index d1b6674def..6ce8d550d7 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2459,6 +2459,11 @@ namespace BizHawk.Client.EmuHawk new ZXSpectrumCoreEmulationSettings().ShowDialog(); } + private void ZXSpectrumNonSyncSettingsMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumNonSyncSettings().ShowDialog(); + } + #endregion #region Help diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs index c74ab81fab..d7e1cf1e63 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs @@ -44,7 +44,7 @@ // OkBtn // this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OkBtn.Location = new System.Drawing.Point(247, 485); + this.OkBtn.Location = new System.Drawing.Point(247, 393); this.OkBtn.Name = "OkBtn"; this.OkBtn.Size = new System.Drawing.Size(60, 23); this.OkBtn.TabIndex = 3; @@ -56,7 +56,7 @@ // this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBtn.Location = new System.Drawing.Point(313, 485); + this.CancelBtn.Location = new System.Drawing.Point(313, 393); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(60, 23); this.CancelBtn.TabIndex = 4; @@ -150,7 +150,7 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CancelBtn; - this.ClientSize = new System.Drawing.Size(385, 520); + this.ClientSize = new System.Drawing.Size(385, 428); this.Controls.Add(this.lblBorderInfo); this.Controls.Add(this.label2); this.Controls.Add(this.borderTypecomboBox1); diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs new file mode 100644 index 0000000000..511f9eeea4 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs @@ -0,0 +1,162 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumNonSyncSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumNonSyncSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.autoLoadcheckBox1 = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.panTypecomboBox1 = new System.Windows.Forms.ComboBox(); + this.lblBorderInfo = new System.Windows.Forms.Label(); + this.lblAutoLoadText = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(247, 158); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(313, 158); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(185, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Misc Non-Sync Settings"; + // + // autoLoadcheckBox1 + // + this.autoLoadcheckBox1.AutoSize = true; + this.autoLoadcheckBox1.Location = new System.Drawing.Point(15, 52); + this.autoLoadcheckBox1.Name = "autoLoadcheckBox1"; + this.autoLoadcheckBox1.Size = new System.Drawing.Size(103, 17); + this.autoLoadcheckBox1.TabIndex = 21; + this.autoLoadcheckBox1.Text = "Auto-Load Tape"; + this.autoLoadcheckBox1.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 97); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(135, 13); + this.label2.TabIndex = 23; + this.label2.Text = "AY-3-8912 Panning Config:"; + // + // panTypecomboBox1 + // + this.panTypecomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panTypecomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.panTypecomboBox1.FormattingEnabled = true; + this.panTypecomboBox1.Location = new System.Drawing.Point(12, 113); + this.panTypecomboBox1.Name = "panTypecomboBox1"; + this.panTypecomboBox1.Size = new System.Drawing.Size(157, 21); + this.panTypecomboBox1.TabIndex = 22; + // + // lblBorderInfo + // + this.lblBorderInfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblBorderInfo.Location = new System.Drawing.Point(175, 106); + this.lblBorderInfo.Name = "lblBorderInfo"; + this.lblBorderInfo.Size = new System.Drawing.Size(196, 37); + this.lblBorderInfo.TabIndex = 24; + this.lblBorderInfo.Text = "Selects a particular panning configuration for the 3ch AY-3-8912 Programmable Sou" + + "nd Generator (128K models only)"; + this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblAutoLoadText + // + this.lblAutoLoadText.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblAutoLoadText.Location = new System.Drawing.Point(175, 46); + this.lblAutoLoadText.Name = "lblAutoLoadText"; + this.lblAutoLoadText.Size = new System.Drawing.Size(196, 30); + this.lblAutoLoadText.TabIndex = 25; + this.lblAutoLoadText.Text = "When enabled ZXHawk will attempt to control the tape device automatically when th" + + "e correct traps are detected"; + this.lblAutoLoadText.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // ZXSpectrumNonSyncSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(385, 193); + this.Controls.Add(this.lblAutoLoadText); + this.Controls.Add(this.lblBorderInfo); + this.Controls.Add(this.label2); + this.Controls.Add(this.panTypecomboBox1); + this.Controls.Add(this.autoLoadcheckBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumNonSyncSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Other Non-Sync Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.CheckBox autoLoadcheckBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox panTypecomboBox1; + private System.Windows.Forms.Label lblBorderInfo; + private System.Windows.Forms.Label lblAutoLoadText; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs new file mode 100644 index 0000000000..0b8bfcbc0c --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumNonSyncSettings : Form + { + private ZXSpectrum.ZXSpectrumSettings _settings; + + public ZXSpectrumNonSyncSettings() + { + InitializeComponent(); + } + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _settings = ((ZXSpectrum)Global.Emulator).GetSettings().Clone(); + + // autoload tape + autoLoadcheckBox1.Checked = _settings.AutoLoadTape; + + // AY panning config + var panTypes = Enum.GetNames(typeof(AYChip.AYPanConfig)); + foreach (var val in panTypes) + { + panTypecomboBox1.Items.Add(val); + } + panTypecomboBox1.SelectedItem = _settings.AYPanConfig.ToString(); + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _settings.AutoLoadTape != autoLoadcheckBox1.Checked + || _settings.AYPanConfig.ToString() != panTypecomboBox1.SelectedItem.ToString(); + + if (changed) + { + _settings.AutoLoadTape = autoLoadcheckBox1.Checked; + _settings.AYPanConfig = (AYChip.AYPanConfig)Enum.Parse(typeof(AYChip.AYPanConfig), panTypecomboBox1.SelectedItem.ToString()); + + GlobalWin.MainForm.PutCoreSettings(_settings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Misc settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAADLytDi9tOemQAAAAAAUy9EecLEsa1uPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPj11Mme + VakAAAAAAAAAAAAATS84M0akAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE + AAAAAAAAAAAAAAABAAAAAQAAAAAAAFFNTQBRUlIAU1RUAGJHRwBiT08Aa0lIAGJTUwBrVlYAYllZAGZc + XABpWloAb1xbAHNTUwB7V1YAc1hXAHFbWwBkZWUAaWFhAG5kZABpamkAcGFhAHlubgB2cHAAf3V1AH55 + eQB8fX0AgUpKAI1PTwCLWFcAhlhYAI9ZWQCKXFsAm1ZWAJJZWQCWWVgAmlpbAJtcWwCiXFwAl2BfAIBg + YACAZ2YAgG9vAI9oaACWZWQAmGBhAJ5kZACcaWoAmm9vAIV0dACNcHAAiXZ2AIB8fACac3IAm3V0AJ51 + dQCZfHwAnHx8AKNmZgCnZmYAqmJiAK5jYwCvb24AtWVmALBtbgC5bW0AvmxtAKx+fQCxcnIAtHBwALZz + dACydXQAtnd2ALlwcAC5dnYAt3p5ALh5eAC8fHsAun18ALx+fQDGb3AAxnBxAMdzdADAd3YAyHJzAMlz + dADJdXYAynd4AMd/fwDMe3wAzXx9AHunbwBhvHIAYsN4ANuLOwC2hn4A4Zt5APC3ZABte9sAX47+AHWM + 5QAl0foAY+P8AIeDgwCFhoYAioSEAJOIiACWi4sAmpKRAKGCgQCmhYUAqYGBAKuDhACniooApYyMAKiO + jQCyhYMAvoWEALeNjQCrj5AAr5eXALSVlAC9lJMAmbCEAK6RugDBgYAAwoSCAMWDhADChoQAxYeFAM6A + gQDFiIYAxoqIAMqIiQDMi4oAy4yKAMiPjQDPj44A0ISFANKJigDUi4wA04+NANWNjgDKkY8A0JCOANud + iQDWj5AAzJSTAM2XlgDGm5oA1pGSANOUkgDVl5EA1pOUANiVlgDYmJUA2ZeYANKenADbmpsA3pmYANuc + mgDbn5wA1aacAN6gngDqqZoA3Z+gAMyjowDCra0AxqysAMqpqQDboaAA3qKiAN6logDbp6UA3aWkANer + qgDWsbMA0rW0ANe0tADfs7IA4aSiAOGlpQDkp6UA46imAOWopgDsraIA6qimAOGoqADhrqwA6a2rAOqv + rADpsK4A7LGuAOGzswDlsbEA7bKxAO+1sgDotrYA5rm3AO+4twDot7sA6bq5AOu9uwDrv70A8bazAPG2 + tADxuLUA9Lm2APC9uwD2vboA9L+9APi+uwD4v7wA8sC+APXAvgD5wL0AkILJAKqXzACsu8cAqr/LALLV + 3QDawMIA48XFAOvDwQDswMAA7cTDAO/ExQDgxsgA8cbEAPTGxADwyskA9MvJAPLNzQD21dYA+NjZAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAMEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHCEcBQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAayU9PSYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdQlBSQiJpAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAM0pSUlJQPRcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnUlJSUlJGFQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFJSUlJSUkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzUlJSWVJZfxAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5XWYqKioqGDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASoqMkpqa + mqAsAAAAAAAAAAAAAAAAAABoNAAAAAAAAACMjJyuvLy2toYHAAAAAAAAAAAAABcOIDouBgAAAAAAc4yc + tsHKysPAriIKAAAAAAAAABYgRk1LTX+DEAAAAABukqXB4ejo4dHPQCIEChcXEwggTXV/k66unKMpAAAA + AG6Srsro6ero0dN/Rk1NRk2Dg4STrsbh4cHAt2sAAAAAbpKuOXPe6ajW15KGg4OGk528yuHo5eHPz882 + AAAAAAB4jCkDAxSoMabXt5yjt8ro3ePo5dbT09HTdAAAAAAAAABGcBFoGgFwdtfDwHxi2dpmZcrX09HP + z0MAAAAAAAAAAHh/qWwaOa6cz9PNZGPYsdzbzc3DwLk2AAAAAAAAAAAAAAAvhpKakoyg19HNyKS5wHtb + orZ/cwAAAAAAAAAAAAAAAAAANkaKWVm5zb1gYV6cXVxfNgAAAAAAAAAAAAAAAAAAALGvlTIuP1K5tqCR + l4xfLwAAAAAAAAAAAAAAAAAAsbPBenkAAAAAcCVYjE0scwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////+f///+D////A////wH + ///4B///+Af///gH///wB///8Af///AH/+fwA/8D4AH8AeAAAAHgAAAB4AAAA+AAAAfwAAAP8AAAH/wA + AD//AAD//gAD//B4D////////////////////////////ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA + AAAAAAAAAAEAAAABAAAAAAAAWlJSAHBJSQB1SEgAe1dXAHdYWAB5WlkAel1dAGBiYgB1bGwAfWtrAHh2 + dgB9fn4Ag01NAIRXVwCIV1cAhV9eAItbWgCgX14ApV1dAJhgXwCNYGAAnWtqAJhtbQCCdnYAh3x8AI15 + eACeensAqGBgAKhoZwCga2oArGpqALNqagCzb28AtG1tALltbQCxb3AApnVzAKlzcwCqdHMApnp6AKd+ + fgCpensAq3x7ALZ3dgC8dHQAvH59AMZvcADGcHEAxXN0AMhycwDJdncAynh5AMx5egDNfn8Ajo1wAOek + VgDGgH8A4p53AEZ2+gB8u4AAd8PaAIuEhACOh4cAjo6OAJ+DggCejo4Ao4SEAKSIiACsi4sAqo2MAK6P + jgC+gYAAvoaGAL+KiACskJAAtJeXALWenQC5np4At6iOAKmyjgC9nroAwYSDAMaGhADOhoYAxomHAMiK + iQDJjYwA0oeIANOOjwDUjY0A2ZiPANaPkADGkZEAx5eXAMySkADGnZwA1ZOSANeTlADWl5YA2JSVANGZ + mADan50A3J6dAOCcmwDVoJ8A7K2fAMOtrQDXo6IA3aCgAN+kpADVq6oA3ay3AMu0tADPtrYA3L+/AOCi + oQDhpqUA5KelAOinpgDlq6gA46usAOOvrQDqrqwA7LGuAOayswDjtrQA5re1AOqysQDts7EA57y6AO+8 + ugDrvL0A8LOwAPC1sgDwtrQA87q3APS6twD2vboA8b69APi/vAD2wb4A+cC9AJmTzwDHqMMAu8PMAIHf + 8QDByNAA7cLCAO3FwwDvxsQA5cjIAOzOzgDwxcQA9cbEAPPP0AD10tIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + BQMJAAAAAAAAAAAAAAAAAAAAAAAAAAAPHBMNAAAAAAAAAAAAAAAAAAAAAAAAABojLy8TAAAAAAAAAAAA + AAAAAAAAAAAAAB0wMDAiPgAAAAAAAAAAAAAAAAAAAAAAQjAwMDAtGAAAAAAAAAAAAAAAAAAAAAAAFzIy + NTU5CgAAAAAAAAAAAAAAAAAAAAAAIjZYWFxcBwAAAAAAAAAAAAAAAAAAAAAANlxtdW11JQAAAAAAAAAA + PgcRDgkAAAAAXG1/lISAZgMAAAAAABkVLC5SVhcAAABNY3WWnJuLfB8UBAcQHkhWaX91dSsAAABNY2BM + mJeCiVJSVl9laX+WloSJgEIAAAAAXAEIC0tGjnR0dJaRk5qNjIyJQwAAAAAAJkNADBtdjIaPO1GSPYuJ + hnVEAAAAAAAAAClISWRcd4xwkGp8UE90VwAAAAAAAAAAAAAAKSQ1NYZ7OjhbPDdGAAAAAAAAAAAAAHNv + YGsAKyJoXFYmRwAAAAAAAAAAAAAAcnIAAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////APx//wD4f/8A8H//APA//wDgP/8A4D//AOA//wDgP8EA4B8BAMAAAQDAAAEA4AADAOAA + BwDwAB8A/AA/APCA/wDn9/8A////AP///wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA + AAAAAQAAAAEAAAAAAABjZGQAdmRjAHtpaQB/eHgAgU9PAKBaWgCFbm0AlWtqAKptbgCwZ2cAsGhoAKxw + cACteHkAvnJyAMZvcADGcHEAy3l5AMx9fgCFmXQAwIB/ANeUfQDhoX8AlIqJAJWMjACYiIgAoIaGAK2K + igCxh4cAvoGAALKKigC4iYgAuJWVAL2cnACss50AuqKhAL+mpgDLgoIAxImHAMeNjADLkI8AxpWTANCS + kQDYlZUA1J6dANqZmgDdnp4A1J+oAMaiogDOr68AzLKyANi5uADhpaIA4qypAOWtqADrrqsA4bKwAOay + sgDtuLYA57++AOy4uADxtLIA8be0APa9ugDswL4A9sG+ALCcxwC5ncIA06zBALnH0QC2ytQA7sPDAPLS + 0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAZBgUAAAAAAAAAAAAAAAAACw8KAAAAAAAAAAAAAAAAGhAQDgAAAAAAAAAAAAAAAAkRESUYAAAA + AAAAAAAAAAAlKy4uBwAAAAAAAAcDAAAAKzlHPCYCAAAYCB0oKgAAAC0wSDs0FB0nLDlAOiwAAAANAQQb + Pi9DRkVBPzUAAAAAJB4cKz5EQjMiNSkAAAAAAAAAHwwRNxYVEyQAAAAAAAAxMgAAACEgAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD//wAA4/8AAOP/AADD/wAAwf8AAMH5 + AADAwQAAwAEAAMADAADABwAA8A8AAM5/AAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAkAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAUAAAAOAEBAVUAAABUAAAANQAAABAAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAkFBSUvGRl5TCkpwlYuLtxDJCTQFw0NmQAA + AEkAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGAwMKE8rK6V6RET2klJR/5ZS + U/+OT0//ZDc38B0QEJoAAAAyAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYDAwYVzAwoopP + T/ygXVz/oFtb/55ZWf+bWFf/k1NT/1UvL9wGAwNcAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AARNKipxhk5O+adkY/+uZWX/tWdo/7VmZ/+qYWH/nltb/3hERPcfERGCAAAAFgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADEZGS1zQ0LXqGdm/7ptbf/Fb3D/x3Bx/8hwcf/BbW7/q2Vl/4hPT/82HR2gAAAAIAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAB1gxMYyYXl3/vXFx/8Zwcf/HcHH/x3Bx/8dwcf/HcHH/uG1t/5NY + V/9EJia2AAAAKQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB8fNH1MS+K4cnH/x3Fy/8dwcf/HcHH/x3Bx/8dw + cf/HcHH/wHBx/51gX/9PLCzGAAAAMwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + 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 + + + \ No newline at end of file From e198e7b8a6e7be7b3b4f67e725a2a37b03f80871 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 16 Mar 2018 15:16:33 +0000 Subject: [PATCH 099/339] Removed generic settings menu --- BizHawk.Client.EmuHawk/MainForm.Designer.cs | 24 ++++++--------------- BizHawk.Client.EmuHawk/MainForm.Events.cs | 2 ++ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 28e7f9368b..fbc83e208a 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -382,7 +382,7 @@ this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumCoreEmulationSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.preferencesToolStripMenuItem4 = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumNonSyncSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton(); @@ -455,7 +455,6 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.ZXSpectrumNonSyncSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -3382,8 +3381,7 @@ this.zXSpectrumToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.ZXSpectrumCoreEmulationSettingsMenuItem, this.ZXSpectrumControllerConfigurationMenuItem, - this.ZXSpectrumNonSyncSettingsMenuItem, - this.preferencesToolStripMenuItem4}); + this.ZXSpectrumNonSyncSettingsMenuItem}); this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; @@ -3403,12 +3401,12 @@ this.ZXSpectrumControllerConfigurationMenuItem.Text = "Joystick Configuration"; this.ZXSpectrumControllerConfigurationMenuItem.Click += new System.EventHandler(this.ZXSpectrumControllerConfigurationMenuItem_Click); // - // preferencesToolStripMenuItem4 + // ZXSpectrumNonSyncSettingsMenuItem // - this.preferencesToolStripMenuItem4.Name = "preferencesToolStripMenuItem4"; - this.preferencesToolStripMenuItem4.Size = new System.Drawing.Size(201, 22); - this.preferencesToolStripMenuItem4.Text = "Preferences"; - this.preferencesToolStripMenuItem4.Click += new System.EventHandler(this.preferencesToolStripMenuItem4_Click); + this.ZXSpectrumNonSyncSettingsMenuItem.Name = "ZXSpectrumNonSyncSettingsMenuItem"; + this.ZXSpectrumNonSyncSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumNonSyncSettingsMenuItem.Text = "Non-Sync Settings"; + this.ZXSpectrumNonSyncSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumNonSyncSettingsMenuItem_Click); // // Atari7800HawkCoreMenuItem // @@ -4036,13 +4034,6 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // - // ZXSpectrumNonSyncSettingsMenuItem - // - this.ZXSpectrumNonSyncSettingsMenuItem.Name = "ZXSpectrumNonSyncSettingsMenuItem"; - this.ZXSpectrumNonSyncSettingsMenuItem.Size = new System.Drawing.Size(201, 22); - this.ZXSpectrumNonSyncSettingsMenuItem.Text = "Non-Sync Settings"; - this.ZXSpectrumNonSyncSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumNonSyncSettingsMenuItem_Click); - // // MainForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -4506,7 +4497,6 @@ private System.Windows.Forms.ToolStripMenuItem SMSControllerSportsPadToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem SMSControllerKeyboardToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem zXSpectrumToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem4; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumControllerConfigurationMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumCoreEmulationSettingsMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumNonSyncSettingsMenuItem; diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 6ce8d550d7..ebef700b2b 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2444,10 +2444,12 @@ namespace BizHawk.Client.EmuHawk } + private void preferencesToolStripMenuItem4_Click(object sender, EventArgs e) { GenericCoreConfig.DoDialog(this, "ZXSpectrum Settings"); } + private void ZXSpectrumControllerConfigurationMenuItem_Click(object sender, EventArgs e) { From dbb90a996d4967e0299a76e1bda7f1891f320641 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 16 Mar 2018 17:50:51 -0400 Subject: [PATCH 100/339] z80: clean up --- BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs | 3 +-- .../CPUs/Z80A/NewDisassembler.cs | 2 +- BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt | 3 +-- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 16 +++++----------- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 88e0a264c2..2c4f5c5204 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -83,8 +83,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A } // Interrupt mode 2 uses the I vector combined with a byte on the data bus - // Again for now we assume only a 0 on the data bus and jump to (0xI00) - private void INTERRUPT_2(ushort src) + private void INTERRUPT_2() { cur_instr = new ushort[] {IDLE, diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs index effe7400df..8bc3938c7d 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs @@ -438,7 +438,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // handle case of addr wrapping around at 16 bit boundary if (addr < start_addr) { - size = addr + 1; + size = (0x10000 + addr) - start_addr; } return temp; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt b/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt index d11f79b637..10804fde94 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt @@ -1,8 +1,7 @@ TODO: -Mode 0 and 2 interrupts +Mode 0 Check T-cycle level memory access timing Check R register new tests for WZ Registers Memory refresh - IR is pushed onto the address bus at instruction start, does anything need this? -Data Bus - For mode zero and 2 interrupts, need a system that uses it to test diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 182056f026..25992d5b81 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -107,7 +107,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public Func ReadHardware; public Action WriteHardware; - // Data BUs + // Data Bus // Interrupting Devices are responsible for putting a value onto the data bus // for as long as the interrupt is valid public Func FetchDB; @@ -196,9 +196,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INTERRUPT_1(); break; case 2: - // Low byte of interrupt vector comes from data bus - // We'll assume it's zero for now - INTERRUPT_2(0); + INTERRUPT_2(); break; } IRQCallback(); @@ -321,9 +319,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INTERRUPT_1(); break; case 2: - // Low byte of interrupt vector comes from data bus - // We'll assume it's zero for now - INTERRUPT_2(0); + INTERRUPT_2(); break; } IRQCallback(); @@ -391,9 +387,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INTERRUPT_1(); break; case 2: - // Low byte of interrupt vector comes from data bus - // We'll assume it's zero for now - INTERRUPT_2(0); + INTERRUPT_2(); break; } IRQCallback(); @@ -663,8 +657,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagI ? "E" : "e") }; } - // State Save/Load + // State Save/Load public void SyncState(Serializer ser) { ser.BeginSection("Z80A"); From 75ae209f5c0c129c8bb71e877990cd10123368a4 Mon Sep 17 00:00:00 2001 From: adelikat Date: Fri, 16 Mar 2018 17:22:18 -0500 Subject: [PATCH 101/339] turn off a few resharper things we don't care about --- BizHawk.sln.DotSettings | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BizHawk.sln.DotSettings b/BizHawk.sln.DotSettings index e447905302..c6e4f57315 100644 --- a/BizHawk.sln.DotSettings +++ b/BizHawk.sln.DotSettings @@ -2,6 +2,7 @@ False True ExplicitlyExcluded + DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW @@ -96,5 +97,6 @@ UI VBA ROM - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> \ No newline at end of file From 4c98ee9ab9bccc1cbb11217ed5b55949e575a87c Mon Sep 17 00:00:00 2001 From: adelikat Date: Fri, 16 Mar 2018 17:35:55 -0500 Subject: [PATCH 102/339] NESHawk - Support a couple bad dumps of Super Donkey Kong (Unl) --- Assets/gamedb/gamedb_nes.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/gamedb/gamedb_nes.txt b/Assets/gamedb/gamedb_nes.txt index ea70a263de..256a27a55b 100644 --- a/Assets/gamedb/gamedb_nes.txt +++ b/Assets/gamedb/gamedb_nes.txt @@ -303,6 +303,8 @@ sha1:FFB4706E49B826C6CDD12E502E8AE94FC9810B7F Monty no Doki Doki Daisassou (FDS sha1:17473C223453D2D80FCB9DCFA317947287DC5C52 Xing He Zhan Shi (China) (Unl) NES board=WAIXINGMAPPER176 sha1:B1C74236FD17FAB4AB9AA6AB28E38864C66D6255 Pocahontus (UNL) NES board=MAPPER182;PRG=256;CHR=256;WRAM=8;PAD_H=1 sha1:5FA23F88432006DCF6874EA36E9E7DA8934427BE Super Donkey Kong (Unl) NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 +sha1:8A7DAB8B78DA1C5EA159BA9EEC00FF97742245F1 B Super Donkey Kong (Unl) [b1] NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 +sha1:8A7DAB8B78DA1C5EA159BA9EEC00FF97742245F1 O Super Donkey Kong (Unl) [o1] NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 ;wrong vram info sha1:32D71DD6C5A8D78A918FE1B9D6D6C4A570D9652D Oeka Kids Anpanman no Hiragana Daisuki (J) NES board=MAPPER096;VRAM=32 From 549b484c21a2dc52e4e69ae969f51b966b57effd Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 16 Mar 2018 18:48:19 -0400 Subject: [PATCH 103/339] Increase Subtitle Maker frame limit --- BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs b/BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs index df23542d0d..f9640b8f53 100644 --- a/BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs +++ b/BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs @@ -179,7 +179,7 @@ // this.FrameNumeric.Location = new System.Drawing.Point(78, 19); this.FrameNumeric.Maximum = new decimal(new int[] { - 999999, + 2147483647, 0, 0, 0}); From 81e80acf86da7d2d8cb076e3e61c0c988f917e72 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 18 Mar 2018 09:55:56 -0400 Subject: [PATCH 104/339] z80: make TotalExecutedCycles long and change related variables accordingly --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 3 +- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 4 +- .../Calculator/TI83.IDebuggable.cs | 218 +++++++++--------- .../Hardware/SoundOuput/Buzzer.cs | 6 +- .../Hardware/SoundOuput/Pulse.cs | 2 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 6 +- .../SinclairSpectrum/Machine/ULABase.cs | 10 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 4 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 2 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 2 +- .../ZXSpectrum.IDebuggable.cs | 2 +- .../Coleco/ColecoVision.IDebuggable.cs | 2 +- .../Consoles/Sega/SMS/SMS.IDebuggable.cs | 2 +- BizHawk.Emulation.Cores/Sound/SN76489.cs | 26 +-- 15 files changed, 145 insertions(+), 146 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index c70e4aa503..eddf74d0a4 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -4,8 +4,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { public partial class Z80A { - private int totalExecutedCycles; - public int TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } + public long TotalExecutedCycles; private int EI_pending; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 25992d5b81..f26582dd41 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -602,7 +602,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FTCH_DB_Func(); break; } - totalExecutedCycles++; + TotalExecutedCycles++; } // tracer stuff @@ -669,7 +669,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("IFF1", ref iff1); ser.Sync("IFF2", ref iff2); ser.Sync("Halted", ref halted); - ser.Sync("ExecutedCycles", ref totalExecutedCycles); + ser.Sync("ExecutedCycles", ref TotalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); ser.Sync("instruction_pointer", ref instr_pntr); diff --git a/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs b/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs index c5d795fcd5..d4e292ad18 100644 --- a/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs @@ -12,35 +12,35 @@ namespace BizHawk.Emulation.Cores.Calculators { return new Dictionary { - ["A"] = _cpu.Regs[_cpu.A], - ["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8), - ["B"] = _cpu.Regs[_cpu.B], - ["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8), - ["C"] = _cpu.Regs[_cpu.C], - ["D"] = _cpu.Regs[_cpu.D], - ["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8), - ["E"] = _cpu.Regs[_cpu.E], - ["F"] = _cpu.Regs[_cpu.F], - ["H"] = _cpu.Regs[_cpu.H], - ["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8), - ["I"] = _cpu.Regs[_cpu.I], - ["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8), - ["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), - ["L"] = _cpu.Regs[_cpu.L], - ["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8), - ["R"] = _cpu.Regs[_cpu.R], - ["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8), - ["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8), - ["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8), - ["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8), - ["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), - ["Flag C"] = _cpu.FlagC, - ["Flag N"] = _cpu.FlagN, - ["Flag P/V"] = _cpu.FlagP, - ["Flag 3rd"] = _cpu.Flag3, - ["Flag H"] = _cpu.FlagH, - ["Flag 5th"] = _cpu.Flag5, - ["Flag Z"] = _cpu.FlagZ, + ["A"] = _cpu.Regs[_cpu.A], + ["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8), + ["B"] = _cpu.Regs[_cpu.B], + ["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8), + ["C"] = _cpu.Regs[_cpu.C], + ["D"] = _cpu.Regs[_cpu.D], + ["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8), + ["E"] = _cpu.Regs[_cpu.E], + ["F"] = _cpu.Regs[_cpu.F], + ["H"] = _cpu.Regs[_cpu.H], + ["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8), + ["I"] = _cpu.Regs[_cpu.I], + ["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8), + ["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["L"] = _cpu.Regs[_cpu.L], + ["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8), + ["R"] = _cpu.Regs[_cpu.R], + ["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8), + ["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8), + ["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8), + ["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8), + ["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["Flag C"] = _cpu.FlagC, + ["Flag N"] = _cpu.FlagN, + ["Flag P/V"] = _cpu.FlagP, + ["Flag 3rd"] = _cpu.Flag3, + ["Flag H"] = _cpu.FlagH, + ["Flag 5th"] = _cpu.Flag5, + ["Flag Z"] = _cpu.FlagZ, ["Flag S"] = _cpu.FlagS }; } @@ -49,85 +49,85 @@ namespace BizHawk.Emulation.Cores.Calculators { switch (register) { - default: - throw new InvalidOperationException(); - case "A": - _cpu.Regs[_cpu.A] = (ushort)value; - break; - case "AF": - _cpu.Regs[_cpu.F] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00); - break; - case "B": - _cpu.Regs[_cpu.B] = (ushort)value; - break; - case "BC": - _cpu.Regs[_cpu.C] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00); - break; - case "C": - _cpu.Regs[_cpu.C] = (ushort)value; - break; - case "D": - _cpu.Regs[_cpu.D] = (ushort)value; - break; - case "DE": - _cpu.Regs[_cpu.E] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00); - break; - case "E": - _cpu.Regs[_cpu.E] = (ushort)value; - break; - case "F": - _cpu.Regs[_cpu.F] = (ushort)value; - break; - case "H": - _cpu.Regs[_cpu.H] = (ushort)value; - break; - case "HL": - _cpu.Regs[_cpu.L] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00); - break; - case "I": - _cpu.Regs[_cpu.I] = (ushort)value; - break; - case "IX": - _cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00); - break; - case "IY": - _cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00); - break; - case "L": - _cpu.Regs[_cpu.L] = (ushort)value; - break; - case "PC": - _cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00); - break; - case "R": - _cpu.Regs[_cpu.R] = (ushort)value; - break; - case "Shadow AF": - _cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00); - break; - case "Shadow BC": - _cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00); - break; - case "Shadow DE": - _cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00); - break; - case "Shadow HL": - _cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00); - break; - case "SP": - _cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00); + default: + throw new InvalidOperationException(); + case "A": + _cpu.Regs[_cpu.A] = (ushort)value; + break; + case "AF": + _cpu.Regs[_cpu.F] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00); + break; + case "B": + _cpu.Regs[_cpu.B] = (ushort)value; + break; + case "BC": + _cpu.Regs[_cpu.C] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00); + break; + case "C": + _cpu.Regs[_cpu.C] = (ushort)value; + break; + case "D": + _cpu.Regs[_cpu.D] = (ushort)value; + break; + case "DE": + _cpu.Regs[_cpu.E] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00); + break; + case "E": + _cpu.Regs[_cpu.E] = (ushort)value; + break; + case "F": + _cpu.Regs[_cpu.F] = (ushort)value; + break; + case "H": + _cpu.Regs[_cpu.H] = (ushort)value; + break; + case "HL": + _cpu.Regs[_cpu.L] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00); + break; + case "I": + _cpu.Regs[_cpu.I] = (ushort)value; + break; + case "IX": + _cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00); + break; + case "IY": + _cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00); + break; + case "L": + _cpu.Regs[_cpu.L] = (ushort)value; + break; + case "PC": + _cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00); + break; + case "R": + _cpu.Regs[_cpu.R] = (ushort)value; + break; + case "Shadow AF": + _cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00); + break; + case "Shadow BC": + _cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00); + break; + case "Shadow DE": + _cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00); + break; + case "Shadow HL": + _cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00); + break; + case "SP": + _cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00); break; } } @@ -145,6 +145,6 @@ namespace BizHawk.Emulation.Cores.Calculators return false; } - public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + public int TotalExecutedCycles => (int)_cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index 667fe519be..7fc0a71715 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -60,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// private long _frameStart; private bool _tapeMode; - private int _tStatesPerFrame; + private long _tStatesPerFrame; private int _sampleRate; private int _samplesPerFrame; private int _tStatesPerSample; @@ -78,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The last T-State (cpu cycle) that the last pulse was received /// - public int LastPulseTState { get; set; } + public long LastPulseTState { get; set; } #region Construction & Initialisation @@ -95,7 +95,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _sampleRate = sampleRate; _tStatesPerFrame = tStatesPerFrame; _tStatesPerSample = 79; - _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + _samplesPerFrame = (int)_tStatesPerFrame / _tStatesPerSample; /* diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs index af14d5cea7..140c1d136a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs @@ -21,6 +21,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Pulse length in Z80 T-States (cycles) /// - public int Length { get; set; } + public long Length { get; set; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 3c220d3157..c6b7567dc9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -91,17 +91,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The current cycle (T-State) that we are at in the frame /// - public int _frameCycles; + public long _frameCycles; /// /// Stores where we are in the frame after each CPU cycle /// - public int LastFrameStartCPUTick; + public long LastFrameStartCPUTick; /// /// Gets the current frame cycle according to the CPU tick count /// - public virtual int CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick; + public virtual long CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick; /// /// Non-Deterministic bools diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 216aa33ef9..79479b4789 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -105,11 +105,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Cycle at which the last render update took place /// - protected int lastTState; + protected long lastTState; /// /// T-States elapsed since last render update /// - protected int elapsedTStates; + protected long elapsedTStates; /// /// T-State of top left raster pixel /// @@ -279,7 +279,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Generates an interrupt in the current phase if needed /// /// - public virtual void CheckForInterrupt(int currentCycle) + public virtual void CheckForInterrupt(long currentCycle) { if (InterruptRevoked) { @@ -430,7 +430,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Updates the screen buffer based on the number of T-States supplied /// /// - public virtual void UpdateScreenBuffer(int _tstates) + public virtual void UpdateScreenBuffer(long _tstates) { if (_tstates < actualULAStart) { @@ -448,7 +448,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //It takes 4 tstates to write 1 byte. Or, 2 pixels per t-state. - int numBytes = (elapsedTStates >> 2) + ((elapsedTStates % 4) > 0 ? 1 : 0); + long numBytes = (elapsedTStates >> 2) + ((elapsedTStates % 4) > 0 ? 1 : 0); int pixelData; int pixel2Data = 0xff; 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 a4a69e9877..353d7bdacd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // If this is an unused port the floating memory bus should be returned // Floating bus is read on the previous cycle - int _tStates = CurrentFrameCycle - 1; + long _tStates = CurrentFrameCycle - 1; // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) @@ -160,7 +160,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // get a BitArray of the value byte BitArray bits = new BitArray(new byte[] { value }); - int currT = CPU.TotalExecutedCycles; + long currT = CPU.TotalExecutedCycles; AYDevice.WritePort(port, value); 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 3e8d97f36f..a99d2c53bc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // If this is an unused port the floating memory bus should be returned // Floating bus is read on the previous cycle - int _tStates = CurrentFrameCycle - 1; + long _tStates = CurrentFrameCycle - 1; // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) 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 bcbb31f35d..ce92c7c93f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // If this is an unused port the floating memory bus should be returned // Floating bus is read on the previous cycle - int _tStates = CurrentFrameCycle - 1; + long _tStates = CurrentFrameCycle - 1; // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) 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 0cc99cf239..09b1b8d073 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -60,7 +60,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // If this is an unused port the floating memory bus should be returned // Floating bus is read on the previous cycle - int _tStates = CurrentFrameCycle - 1; + long _tStates = CurrentFrameCycle - 1; // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs index b473bac3f8..e086ad09e3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs @@ -142,6 +142,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum throw new NotImplementedException(); } - public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + public int TotalExecutedCycles => (int)_cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs index c48bbabf8f..8739d2d616 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs @@ -142,6 +142,6 @@ namespace BizHawk.Emulation.Cores.ColecoVision throw new NotImplementedException(); } - public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + public int TotalExecutedCycles => (int)_cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs index d8b0d15402..3a9bf83be6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs @@ -144,7 +144,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem public int TotalExecutedCycles { - get { return Cpu.TotalExecutedCycles; } + get { return (int)Cpu.TotalExecutedCycles; } } } } diff --git a/BizHawk.Emulation.Cores/Sound/SN76489.cs b/BizHawk.Emulation.Cores/Sound/SN76489.cs index 1944edf267..c5e7e113c5 100644 --- a/BizHawk.Emulation.Cores/Sound/SN76489.cs +++ b/BizHawk.Emulation.Cores/Sound/SN76489.cs @@ -21,15 +21,15 @@ namespace BizHawk.Emulation.Cores.Components const int SampleRate = 44100; private static readonly byte[] LogScale = { 0, 10, 13, 16, 20, 26, 32, 40, 51, 64, 81, 102, 128, 161, 203, 255 }; - public void Mix(short[] samples, int start, int len, int maxVolume) + public void Mix(short[] samples, long start, long len, int maxVolume) { if (Volume == 0) return; float adjustedWaveLengthInSamples = SampleRate / (Noise ? (Frequency / (float)Wave.Length) : Frequency); float moveThroughWaveRate = Wave.Length / adjustedWaveLengthInSamples; - int end = start + len; - for (int i = start; i < end; ) + long end = start + len; + for (long i = start; i < end; ) { short value = Wave[(int)WaveOffset]; @@ -46,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Components public byte PsgLatch; private readonly Queue commands = new Queue(256); - int frameStartTime, frameStopTime; + long frameStartTime, frameStopTime; const int PsgBase = 111861; @@ -84,7 +84,7 @@ namespace BizHawk.Emulation.Cores.Components } } - public void BeginFrame(int cycles) + public void BeginFrame(long cycles) { while (commands.Count > 0) { @@ -94,12 +94,12 @@ namespace BizHawk.Emulation.Cores.Components frameStartTime = cycles; } - public void EndFrame(int cycles) + public void EndFrame(long cycles) { frameStopTime = cycles; } - public void WritePsgData(byte value, int cycles) + public void WritePsgData(byte value, long cycles) { commands.Enqueue(new QueuedCommand { Value = value, Time = cycles - frameStartTime }); } @@ -227,15 +227,15 @@ namespace BizHawk.Emulation.Cores.Components public void DiscardSamples() { commands.Clear(); } public void GetSamples(short[] samples) { - int elapsedCycles = frameStopTime - frameStartTime; + long elapsedCycles = frameStopTime - frameStartTime; if (elapsedCycles == 0) elapsedCycles = 1; // hey it's better than diving by zero - int start = 0; + long start = 0; while (commands.Count > 0) { var cmd = commands.Dequeue(); - int pos = ((cmd.Time * samples.Length) / elapsedCycles) & ~1; + long pos = ((cmd.Time * samples.Length) / elapsedCycles) & ~1; GetSamplesImmediate(samples, start, pos - start); start = pos; WritePsgDataImmediate(cmd.Value); @@ -243,16 +243,16 @@ namespace BizHawk.Emulation.Cores.Components GetSamplesImmediate(samples, start, samples.Length - start); } - public void GetSamplesImmediate(short[] samples, int start, int len) + public void GetSamplesImmediate(short[] samples, long start, long len) { - for (int i = 0; i < 4; i++) + for (long i = 0; i < 4; i++) Channels[i].Mix(samples, start, len, MaxVolume); } class QueuedCommand { public byte Value; - public int Time; + public long Time; } } } \ No newline at end of file From b28d159023048a8fe1a42dc0a9339e03a6082008 Mon Sep 17 00:00:00 2001 From: Isotarge Date: Mon, 19 Mar 2018 00:56:36 +1030 Subject: [PATCH 105/339] Hex Editor: Speedup FindPrev/FindNext --- .../tools/HexEditor/HexEditor.cs | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs index a509b57639..82b3f53de1 100644 --- a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs +++ b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs @@ -235,6 +235,28 @@ namespace BizHawk.Client.EmuHawk return str.Select(Convert.ToByte).ToArray(); } + public byte[] ConvertHexStringToByteArray(string str) + { + if (string.IsNullOrWhiteSpace(str)) { + return new byte[0]; + } + + // TODO: Better method of handling this? + if (str.Length % 2 == 1) + { + str += "0"; + } + + byte[] bytes = new byte[str.Length / 2]; + + for (int i = 0; i < str.Length; i += 2) + { + bytes[i / 2] = Convert.ToByte(str.Substring(i, 2), 16); + } + + return bytes; + } + public void FindNext(string value, bool wrap) { long found = -1; @@ -261,16 +283,20 @@ namespace BizHawk.Client.EmuHawk startByte = _addressHighlighted + DataSize; } + byte[] searchBytes = ConvertHexStringToByteArray(search); for (var i = startByte; i < (_domain.Size - numByte); i++) { - var ramblock = new StringBuilder(); + bool differenceFound = false; for (var j = 0; j < numByte; j++) { - ramblock.Append(string.Format("{0:X2}", (int)_domain.PeekByte(i + j))); + if (_domain.PeekByte(i + j) != searchBytes[j]) + { + differenceFound = true; + break; + } } - var block = ramblock.ToString().ToUpper(); - if (search == block) + if (!differenceFound) { found = i; break; @@ -313,16 +339,19 @@ namespace BizHawk.Client.EmuHawk startByte = _addressHighlighted - 1; } + byte[] searchBytes = ConvertHexStringToByteArray(search); for (var i = startByte; i >= 0; i--) { - var ramblock = new StringBuilder(); + bool differenceFound = false; for (var j = 0; j < numByte; j++) { - ramblock.Append(string.Format("{0:X2}", (int)_domain.PeekByte(i + j))); + if (_domain.PeekByte(i + j) != searchBytes[j]) { + differenceFound = true; + break; + } } - var block = ramblock.ToString().ToUpper(); - if (search == block) + if (!differenceFound) { found = i; break; From 014a6524220a7dfa4ffb96d6780371544facc51e Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 18 Mar 2018 16:06:48 -0400 Subject: [PATCH 106/339] GBHawk: Implement SaveRam fixes 1147 --- .../Nintendo/GBHawk/GBHawk.IMemoryDomains.cs | 8 +- .../Nintendo/GBHawk/GBHawk.ISaveRam.cs | 7 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 75 ++++++++----------- 3 files changed, 44 insertions(+), 46 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs index 375abbd592..b336e091fe 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs @@ -41,9 +41,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk MemoryDomain.Endian.Little, addr => _rom[addr], (addr, value) => ZP_RAM[addr] = value, - 1) + 1), }; + if (cart_RAM != null) + { + var CartRam = new MemoryDomainByteArray("Cart RAM", MemoryDomain.Endian.Little, cart_RAM, true, 1); + domains.Add(CartRam); + } + MemoryDomains = new MemoryDomainList(domains); (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs index d1fa270c30..8e9c0ddbe8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs @@ -7,19 +7,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public byte[] CloneSaveRam() { - return (byte[])_sram.Clone(); + return (byte[])cart_RAM.Clone(); } public void StoreSaveRam(byte[] data) { - Buffer.BlockCopy(data, 0, _sram, 0, data.Length); + Buffer.BlockCopy(data, 0, cart_RAM, 0, data.Length); + Console.WriteLine("loading SRAM here"); } public bool SaveRamModified { get { - return false; + return has_bat & _syncSettings.Use_SRAM; } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 02660d1f7f..1caa711336 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -44,11 +44,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte[] OAM = new byte[0xA0]; public readonly byte[] _rom; - public readonly byte[] _bios; - public readonly byte[] _sram = new byte[2048]; + public readonly byte[] _bios; public readonly byte[] header = new byte[0x50]; public byte[] cart_RAM; + public bool has_bat; private int _frame = 0; @@ -250,39 +250,39 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk switch (header[0x47]) { - case 0x0: mapper = new MapperDefault(); mppr = "NROM"; break; - case 0x1: mapper = new MapperMBC1(); mppr = "MBC1"; break; - case 0x2: mapper = new MapperMBC1(); mppr = "MBC1"; break; - case 0x3: mapper = new MapperMBC1(); mppr = "MBC1"; break; - case 0x5: mapper = new MapperMBC2(); mppr = "MBC2"; break; - case 0x6: mapper = new MapperMBC2(); mppr = "MBC2"; break; - case 0x8: mapper = new MapperDefault(); mppr = "NROM"; break; - case 0x9: mapper = new MapperDefault(); mppr = "NROM"; break; - case 0xB: mapper = new MapperMMM01(); mppr = "MMM01"; break; - case 0xC: mapper = new MapperMMM01(); mppr = "MMM01"; break; - case 0xD: mapper = new MapperMMM01(); mppr = "MMM01"; break; - case 0xF: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x10: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x11: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x12: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x13: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x19: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1A: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1B: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1C: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1D: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1E: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x20: mapper = new MapperMBC6(); mppr = "MBC6"; break; - case 0x22: mapper = new MapperMBC7(); mppr = "MBC7"; break; - case 0xFC: mapper = new MapperCamera(); mppr = "CAM"; break; - case 0xFD: mapper = new MapperTAMA5(); mppr = "TAMA5"; break; - case 0xFE: mapper = new MapperHuC3(); mppr = "HuC3"; break; - case 0xFF: mapper = new MapperHuC1(); mppr = "HuC1"; break; + case 0x0: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x1: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x2: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x3: mapper = new MapperMBC1(); mppr = "MBC1"; has_bat = true; break; + case 0x5: mapper = new MapperMBC2(); mppr = "MBC2"; break; + case 0x6: mapper = new MapperMBC2(); mppr = "MBC2"; has_bat = true; break; + case 0x8: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x9: mapper = new MapperDefault(); mppr = "NROM"; has_bat = true; break; + case 0xB: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xC: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xD: mapper = new MapperMMM01(); mppr = "MMM01"; has_bat = true; break; + case 0xF: mapper = new MapperMBC3(); mppr = "MBC3"; has_bat = true; break; + case 0x10: mapper = new MapperMBC3(); mppr = "MBC3"; has_bat = true; break; + case 0x11: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x12: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x13: mapper = new MapperMBC3(); mppr = "MBC3"; has_bat = true; break; + case 0x19: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1A: mapper = new MapperMBC5(); mppr = "MBC5"; has_bat = true; break; + case 0x1B: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1C: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1D: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1E: mapper = new MapperMBC5(); mppr = "MBC5"; has_bat = true; break; + case 0x20: mapper = new MapperMBC6(); mppr = "MBC6"; break; + case 0x22: mapper = new MapperMBC7(); mppr = "MBC7"; has_bat = true; break; + case 0xFC: mapper = new MapperCamera(); mppr = "CAM"; break; + case 0xFD: mapper = new MapperTAMA5(); mppr = "TAMA5"; break; + case 0xFE: mapper = new MapperHuC3(); mppr = "HuC3"; break; + case 0xFF: mapper = new MapperHuC1(); mppr = "HuC1"; break; // Bootleg mappers // NOTE: Sachen mapper selection does not account for scrambling, so if another bootleg mapper // identifies itself as 0x31, this will need to be modified - case 0x31: mapper = new MapperSachen2(); mppr = "Schn2"; break; + case 0x31: mapper = new MapperSachen2(); mppr = "Schn2"; break; case 0x4: case 0x7: @@ -299,8 +299,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // mapper not implemented Console.WriteLine(header[0x47]); throw new Exception("Mapper not implemented"); - break; - } // special case for multi cart mappers @@ -357,16 +355,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Console.Write("RAM: "); Console.WriteLine(cart_RAM.Length); - if (_syncSettings.Use_SRAM) + for (int i = 0; i < cart_RAM.Length; i++) { - // load cartRAM here - } - else - { - for (int i = 0; i < cart_RAM.Length; i++) - { - cart_RAM[i] = 0xFF; - } + cart_RAM[i] = 0xFF; } } From 22656fd373585f1f23b9700b0b55c7b567c36d53 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 19 Mar 2018 12:01:54 +0000 Subject: [PATCH 107/339] Some TapeDevice changes --- .../Hardware/Datacorder/DatacorderDevice.cs | 53 +++++++++++++------ .../Machine/SpectrumBase.Memory.cs | 26 ++++++++- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 4 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 6 +-- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 6 +-- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 10 ++-- 6 files changed, 74 insertions(+), 31 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 066570f6c9..48701e0f88 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -103,12 +103,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private long _lastCycle = 0; /// - /// + /// Edge /// private int _waitEdge = 0; /// - /// + /// Current tapebit state /// private bool currentState = false; @@ -132,11 +132,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region Emulator - /// - /// This is the address the that ROM will jump to when the spectrum has quit tape playing - /// - public const ushort ERROR_ROM_ADDRESS = 0x0008; - /// /// Should be fired at the end of every frame /// Primary purpose is to detect tape traps and manage auto play (if/when this is ever implemented) @@ -522,7 +517,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _lastCycle = cpuCycle - (long)cycles; // play the buzzer - _buzzer.ProcessPulseValue(true, currentState); + _buzzer.ProcessPulseValue(false, currentState); return currentState; } @@ -598,7 +593,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine.Spectrum.OSD_TapePlayingAuto(); } - _monitorTimeOut = 50; + _monitorTimeOut = 250; } } else @@ -613,7 +608,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void AutoStopTape() { - if (!_tapeIsPlaying) + if (_tapeIsPlaying) + return; + + if (!_machine.Spectrum.Settings.AutoLoadTape) return; Stop(); @@ -625,25 +623,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeIsPlaying) return; + if (!_machine.Spectrum.Settings.AutoLoadTape) + return; + Play(); _machine.Spectrum.OSD_TapePlayingAuto(); } private void MonitorFrame() { - /* if (_tapeIsPlaying && _machine.Spectrum.Settings.AutoLoadTape) { - _monitorTimeOut--; if (_monitorTimeOut < 0) { + // does not work properly - disabled for now (handled elsewhere) + //Stop(); //_machine.Spectrum.OSD_TapeStoppedAuto(); } } - */ } #endregion @@ -665,6 +665,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool ReadPort(ushort port, ref int result) { + if (TapeIsPlaying) + { + GetEarBit(_cpu.TotalExecutedCycles); + } + if (currentState) + { + result |= TAPE_BIT; + } + else + { + result &= ~TAPE_BIT; + } + + MonitorRead(); + + /* + if (TapeIsPlaying) { if (GetEarBit(_cpu.TotalExecutedCycles)) @@ -702,6 +719,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + */ + return true; } @@ -713,8 +732,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool WritePort(ushort port, int result) { - // not implemented yet - return false; + if (!TapeIsPlaying) + { + currentState = ((byte)result & 0x10) != 0; + } + + return true; } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index a25bbc8186..a23c2796ff 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -176,36 +176,58 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; } - + /// + /// Monitors ROM access + /// Used to auto start/stop the tape device when appropriate + /// + /// public virtual void TestForTapeTraps(int addr) { - if (!TapeDevice.TapeIsPlaying) + if (TapeDevice.TapeIsPlaying) { + // THE 'ERROR' RESTART if (addr == 8) { TapeDevice?.AutoStopTape(); return; } + // THE 'ED-ERROR' SUBROUTINE if (addr == 4223) { TapeDevice?.AutoStopTape(); return; } + // THE 'ERROR-2' ROUTINE if (addr == 83) { TapeDevice?.AutoStopTape(); return; } + + // THE 'MASKABLE INTERRUPT' ROUTINE + if (addr == 56) + { + //TapeDevice?.AutoStopTape(); + return; + } } else { + // THE 'LD-BYTES' SUBROUTINE if (addr == 1366) { TapeDevice?.AutoStartTape(); return; } + + // THE 'LD-EDGE-2' AND 'LD-EDGE-1' SUBROUTINES + if (addr == 1507) + { + TapeDevice?.AutoStartTape(); + return; + } } } 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 353d7bdacd..e01525017a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -43,9 +43,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // not a lagframe InputRead = true; - // tape loading monitor cycle - TapeDevice.MonitorRead(); - // process tape INs TapeDevice.ReadPort(port, ref result); } @@ -229,6 +226,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + TapeDevice.WritePort(port, value); // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); 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 a99d2c53bc..24e7a13760 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -43,9 +43,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // not a lagframe InputRead = true; - // tape loading monitor cycle - TapeDevice.MonitorRead(); - // process tape INs TapeDevice.ReadPort(port, ref result); } @@ -255,6 +252,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + // Tape + TapeDevice.WritePort(port, value); + // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } 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 ce92c7c93f..e56c23440b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -43,9 +43,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // not a lagframe InputRead = true; - // tape loading monitor cycle - TapeDevice.MonitorRead(); - // process tape INs TapeDevice.ReadPort(port, ref result); } @@ -182,6 +179,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + // Tape + TapeDevice.WritePort(port, value); + // Tape //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } 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 09b1b8d073..0cb5b9c7c1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -40,10 +40,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum KeyboardDevice.ReadPort(port, ref result); // not a lagframe - InputRead = true; - - // tape loading monitor cycle - TapeDevice.MonitorRead(); + InputRead = true; // process tape INs TapeDevice.ReadPort(port, ref result); @@ -120,9 +117,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Buzzer BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + // Tape + TapeDevice.WritePort(port, value); + // Tape mic processing (not implemented yet) //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); - + } } From 5ab7ecd4b0b38f8402288cbf85e5ceaaff83ebe3 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 19 Mar 2018 15:34:25 +0000 Subject: [PATCH 108/339] A few more small changes --- .../Hardware/Datacorder/DatacorderDevice.cs | 106 ++++++++++++++++++ .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 + 2 files changed, 108 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 48701e0f88..cd7d7a2cde 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -1,6 +1,7 @@ using BizHawk.Common; using BizHawk.Emulation.Cores.Components.Z80A; using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -141,6 +142,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum MonitorFrame(); } + public void StartFrame() + { + //if (TapeIsPlaying && AutoPlay) + //FlashLoad(); + } + #endregion #region Tape Controls @@ -522,6 +529,105 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return currentState; } + /// + /// Flash loading implementation + /// (Deterministic Emulation must be FALSE) + /// + private bool FlashLoad() + { + // deterministic emulation must = false + //if (_machine.Spectrum.SyncSettings.DeterministicEmulation) + //return; + + var util = _machine.Spectrum; + + if (_currentDataBlockIndex < 0) + _currentDataBlockIndex = 0; + + if (_currentDataBlockIndex >= DataBlocks.Count) + return false; + + //var val = GetEarBit(_cpu.TotalExecutedCycles); + //_buzzer.ProcessPulseValue(true, val); + + ushort addr = _cpu.RegPC; + + if (_machine.Spectrum.SyncSettings.DeterministicEmulation) + { + + } + + var tb = DataBlocks[_currentDataBlockIndex]; + var tData = tb.BlockData; + + if (tData == null || tData.Length < 2) + { + // skip this + return false; + } + + var toRead = tData.Length - 1; + + if (toRead < _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8)) + { + + } + else + { + toRead = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8); + } + + if (toRead <= 0) + return false; + + var parity = tData[0]; + + if (parity != _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8) >> 8) + return false; + + util.SetCpuRegister("Shadow AF", 0x0145); + + for (var i = 0; i < toRead; i++) + { + var v = tData[i + 1]; + _cpu.Regs[_cpu.L] = v; + parity ^= v; + var d = (ushort)(_cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8) + 1); + _machine.WriteBus(d, v); + } + var pc = (ushort)0x05DF; + + if (_cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8) == toRead && + toRead + 1 < tData.Length) + { + var v = tData[toRead + 1]; + _cpu.Regs[_cpu.L] = v; + parity ^= v; + _cpu.Regs[_cpu.B] = 0xB0; + } + else + { + _cpu.Regs[_cpu.L] = 1; + _cpu.Regs[_cpu.B] = 0; + _cpu.Regs[_cpu.F] = 0x50; + _cpu.Regs[_cpu.A] = parity; + pc = 0x05EE; + } + + _cpu.Regs[_cpu.H] = parity; + var de = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8); + util.SetCpuRegister("DE", de - toRead); + var ix = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8); + util.SetCpuRegister("IX", ix + toRead); + + util.SetCpuRegister("PC", pc); + + _currentDataBlockIndex++; + + return true; + + } + #endregion #region TapeMonitor diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index c6b7567dc9..92c48065bc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -137,6 +137,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum FrameCompleted = false; + TapeDevice.StartFrame(); + if (_renderSound) { BuzzerDevice.StartFrame(); From b939c47de6ecbf831762add6bf2beda6c3d5114f Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 19 Mar 2018 16:21:15 +0000 Subject: [PATCH 109/339] Added reset methods --- .../SinclairSpectrum/Machine/SpectrumBase.cs | 51 +++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 92c48065bc..ac557b4c99 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -207,8 +207,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void HardReset() { - //ResetBorder(); ULADevice.ResetInterrupt(); + ROMPaged = 0; + SpecialPagingMode = false; + RAMPaged = 0; + CPU.RegPC = 0; + + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("IY", 0xFFFF); + Spectrum.SetCpuRegister("IX", 0xFFFF); + Spectrum.SetCpuRegister("AF", 0xFFFF); + Spectrum.SetCpuRegister("BC", 0xFFFF); + Spectrum.SetCpuRegister("DE", 0xFFFF); + Spectrum.SetCpuRegister("HL", 0xFFFF); + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("Shadow AF", 0xFFFF); + Spectrum.SetCpuRegister("Shadow BC", 0xFFFF); + Spectrum.SetCpuRegister("Shadow DE", 0xFFFF); + Spectrum.SetCpuRegister("Shadow HL", 0xFFFF); + + CPU.Regs[CPU.I] = 0; + CPU.Regs[CPU.R] = 0; + + TapeDevice.Reset(); + if (AYDevice != null) + AYDevice.Reset(); } /// @@ -216,9 +239,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void SoftReset() { - //ResetBorder(); - ULADevice.ResetInterrupt(); + ULADevice.ResetInterrupt(); + ROMPaged = 0; + SpecialPagingMode = false; + RAMPaged = 0; CPU.RegPC = 0; + + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("IY", 0xFFFF); + Spectrum.SetCpuRegister("IX", 0xFFFF); + Spectrum.SetCpuRegister("AF", 0xFFFF); + Spectrum.SetCpuRegister("BC", 0xFFFF); + Spectrum.SetCpuRegister("DE", 0xFFFF); + Spectrum.SetCpuRegister("HL", 0xFFFF); + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("Shadow AF", 0xFFFF); + Spectrum.SetCpuRegister("Shadow BC", 0xFFFF); + Spectrum.SetCpuRegister("Shadow DE", 0xFFFF); + Spectrum.SetCpuRegister("Shadow HL", 0xFFFF); + + CPU.Regs[CPU.I] = 0; + CPU.Regs[CPU.R] = 0; + + TapeDevice.Reset(); + if (AYDevice != null) + AYDevice.Reset(); } #endregion From 683166da89cf9fba855c40b7dc9a46038567c6f7 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 19 Mar 2018 16:25:20 +0000 Subject: [PATCH 110/339] Readme update --- BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index af479f8536..36eb6a6f07 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -9,7 +9,7 @@ At the moment this is experimental and is still being worked on. * ULA Mode 1 VBLANK interrupt generation * IM2 Interrupts and DataBus implementation (thanks Aloysha) * Beeper/Buzzer output (implementing ISoundProvider) -* AY-3-8912 sound chip implementation (stereo or mono options available as a setting) +* AY-3-8912 sound chip implementation (multiple stereo or mono panning options available as a setting) * Keyboard input (implementing IInputPollable) * Default keyboard keymappings * Kempston, Cursor and Sinclair (Left & Right) joysticks emulated From bd319056af29a5b2cb28d335b24c894ee1a32b8b Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 19 Mar 2018 23:53:06 +0000 Subject: [PATCH 111/339] Better volume levels for AY-3-8912 MONO panning configuration --- .../Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs | 2 +- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs index 34515cd5eb..bd1597180d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs @@ -503,7 +503,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private static List PanTabs = new List { // MONO - new uint[] { 100,100, 100,100, 100,100 }, + new uint[] { 66,66, 66,66, 66,66 }, // ABC new uint[] { 100,10, 66,66, 10,100 }, // ACB diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index ac557b4c99..9b4d49030f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -307,7 +307,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.SyncState(ser); if (AYDevice != null) + { AYDevice.SyncState(ser); + ((AYChip)AYDevice as AYChip).PanningConfiguration = Spectrum.Settings.AYPanConfig; + } + ser.Sync("tapeMediaIndex", ref tapeMediaIndex); TapeMediaIndex = tapeMediaIndex; From 8f193eb68fa38f28c7f33d9316ee965cbd77fcda Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 20 Mar 2018 10:08:13 -0400 Subject: [PATCH 112/339] PCE: Increase read delay to fix Mugen Senshi Valis fixes #554 --- BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs index afa8a8a790..bf2d23f311 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs @@ -409,7 +409,10 @@ namespace BizHawk.Emulation.Cores.PCEngine if (CommandBuffer[4] == 0) SectorsLeftToRead = 256; - DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 5000; // figure out proper read delay later + // figure out proper read delay later + // 10000 fixes Mugen Senshi Valis, which runs code in a timed loop, expecting a certain number of VBlanks + // to happen before reading is complete + DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 10000; pce.CDAudio.Stop(); } From aa7cee1bb7d123f5ed9c136554bb8fae7d9d9b37 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 20 Mar 2018 10:40:14 -0400 Subject: [PATCH 113/339] PCE: update some graphics properties --- .../Consoles/PC Engine/VDC.Render.cs | 54 ++++++++++++++----- .../Consoles/PC Engine/VDC.cs | 10 ++-- .../Consoles/PC Engine/VPC.cs | 35 ++++++++---- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs index 0ddbd0030a..0cdd634880 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs @@ -22,6 +22,8 @@ namespace BizHawk.Emulation.Cores.PCEngine public int BackgroundY; public int RCRCounter; public int ActiveLine; + public bool latch_bgy; + public int ActiveDisplayStartLine; public int HBlankCycles = 79; public bool PerformSpriteLimit; @@ -31,10 +33,28 @@ namespace BizHawk.Emulation.Cores.PCEngine public void ExecFrame(bool render) { - if (MultiResHack > 0 && render) - Array.Clear(FrameBuffer, 0, FrameBuffer.Length); + Array.Clear(FrameBuffer, 0, FrameBuffer.Length); - int ActiveDisplayStartLine = DisplayStartLine; + ActiveDisplayStartLine = DisplayStartLine; + + /* + Console.Write("VDS: "); + Console.Write((Registers[VPR] >> 8)); + Console.Write(" VSW: "); + Console.Write((Registers[VPR] & 0xFF)); + Console.Write(" VDR: "); + Console.Write((Registers[VDW] & 0xFF)); + Console.Write(" VCR: "); + Console.Write((Registers[VCR] & 0xFF)); + Console.Write(" HDS: "); + Console.Write((Registers[HSR] >> 8)); + Console.Write(" HSW: "); + Console.Write((Registers[HSR] & 0xFF)); + Console.Write(" HDE: "); + Console.Write((Registers[HDR] >> 8)); + Console.Write(" HDW: "); + Console.WriteLine((Registers[HDR] & 0xFF)); + */ while (true) { @@ -67,6 +87,12 @@ namespace BizHawk.Emulation.Cores.PCEngine BackgroundY = Registers[BYR]; else { + if (latch_bgy) + { + BackgroundY = Registers[BYR]; + latch_bgy = false; + } + BackgroundY++; BackgroundY &= 0x01FF; } @@ -107,8 +133,8 @@ namespace BizHawk.Emulation.Cores.PCEngine public void RenderScanLine() { - if (((ActiveLine + ViewStartLine) >= pce.Settings.Bottom_Line) || - ((ActiveLine + ViewStartLine) < pce.Settings.Top_Line)) + if ((ScanLine >= pce.Settings.Bottom_Line) || + (ScanLine < pce.Settings.Top_Line)) return; RenderBackgroundScanline(pce.Settings.ShowBG1); @@ -126,7 +152,7 @@ namespace BizHawk.Emulation.Cores.PCEngine int p = vce.Palette[256]; fixed (int* FBptr = FrameBuffer) { - int* dst = FBptr + (ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch; + int* dst = FBptr + (ScanLine - pce.Settings.Top_Line) * FramePitch; for (int i = 0; i < FrameWidth; i++) *dst++ = p; } @@ -150,7 +176,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { // pointer to the BAT and the framebuffer for this line ushort* BatRow = VRAMptr + yTile * BatWidth; - int* dst = FBptr + (ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch; + int* dst = FBptr + (ScanLine - pce.Settings.Top_Line) * FramePitch; // parameters that change per tile ushort BatEnt; @@ -204,7 +230,7 @@ namespace BizHawk.Emulation.Cores.PCEngine if (BackgroundEnabled == false) { for (int i = 0; i < FrameWidth; i++) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + i] = vce.Palette[256]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + i] = vce.Palette[256]; return; } @@ -228,10 +254,10 @@ namespace BizHawk.Emulation.Cores.PCEngine byte c = PatternBuffer[(tileNo * 64) + (yOfs * 8) + xOfs]; if (c == 0) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + x] = vce.Palette[0]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + x] = vce.Palette[0]; else { - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + x] = show ? vce.Palette[paletteBase + c] : vce.Palette[0]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + x] = show ? vce.Palette[paletteBase + c] : vce.Palette[0]; PriorityBuffer[x] = 1; } } @@ -363,7 +389,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } } @@ -380,7 +406,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } @@ -401,7 +427,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } if (width == 32) @@ -417,7 +443,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } } diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs index 824f4c6a7d..eb4050f814 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs @@ -48,8 +48,7 @@ namespace BizHawk.Emulation.Cores.PCEngine public int RequestedFrameWidth => ((Registers[HDR] & 0x3F) + 1) * 8; public int RequestedFrameHeight => (Registers[VDW] & 0x1FF) + 1; - public int DisplayStartLine => (Registers[VPR] >> 8) + (Registers[VPR] & 0x1F); - public int ViewStartLine => (Registers[VPR] >> 8) + 2; + public int DisplayStartLine => (Registers[VPR] >> 8) + 3 + (Registers[VPR] & 0x1F); private const int MAWR = 0; // Memory Address Write Register private const int MARR = 1; // Memory Address Read Register @@ -118,8 +117,8 @@ namespace BizHawk.Emulation.Cores.PCEngine Registers[RegisterLatch] &= 0xFF00; Registers[RegisterLatch] |= value; - if (RegisterLatch == BYR) - BackgroundY = Registers[BYR] & 0x1FF; + if (RegisterLatch == BYR) { latch_bgy = true; } + //BackgroundY = Registers[BYR] & 0x1FF; RegisterCommit(RegisterLatch, msbComplete: false); } @@ -154,7 +153,8 @@ namespace BizHawk.Emulation.Cores.PCEngine break; case BYR: Registers[BYR] &= 0x1FF; - BackgroundY = Registers[BYR]; + latch_bgy = true; + //BackgroundY = Registers[BYR]; break; case HDR: // Horizontal Display Register - update framebuffer size FrameWidth = RequestedFrameWidth; diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs index 9d9e27b460..8ba32258b5 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs @@ -101,6 +101,7 @@ namespace BizHawk.Emulation.Cores.PCEngine const int DCR = 15; int EffectivePriorityMode = 0; + int ScanLine; int FrameHeight; int FrameWidth; @@ -122,12 +123,14 @@ namespace BizHawk.Emulation.Cores.PCEngine EffectivePriorityMode = 0; } + Array.Clear(FrameBuffer, 0, FrameBuffer.Length); + // Latch frame dimensions and framebuffer, for purely dumb reasons FrameWidth = VDC1.BufferWidth; FrameHeight = VDC1.BufferHeight; FrameBuffer = VDC1.GetVideoBuffer(); - int ScanLine = 0; + ScanLine = 0; int ActiveDisplayStartLine = VDC1.DisplayStartLine; while (true) @@ -183,6 +186,18 @@ namespace BizHawk.Emulation.Cores.PCEngine } else { + if (VDC1.latch_bgy) + { + VDC1.BackgroundY = VDC2.Registers[BYR]; + VDC1.latch_bgy = false; + } + + if (VDC2.latch_bgy) + { + VDC2.BackgroundY = VDC2.Registers[BYR]; + VDC2.latch_bgy = false; + } + VDC1.BackgroundY++; VDC1.BackgroundY &= 0x01FF; VDC2.BackgroundY++; @@ -239,12 +254,12 @@ namespace BizHawk.Emulation.Cores.PCEngine private void RenderScanLine() { - if (((VDC1.ActiveLine + VDC1.ViewStartLine) >= PCE.Settings.Bottom_Line) || - ((VDC1.ActiveLine + VDC1.ViewStartLine) < PCE.Settings.Top_Line)) + if ((ScanLine >= PCE.Settings.Bottom_Line) || + (ScanLine < PCE.Settings.Top_Line)) { return; } - InitializeScanLine(VDC1.ActiveLine); + InitializeScanLine(ScanLine); switch (EffectivePriorityMode) { @@ -269,7 +284,7 @@ namespace BizHawk.Emulation.Cores.PCEngine Array.Clear(PriorityBuffer, 0, FrameWidth); // Initialize scanline to background color for (int i = 0; i < FrameWidth; i++) - FrameBuffer[((scanline + VDC1.ViewStartLine) * FrameWidth) + i] = VCE.Palette[256]; + FrameBuffer[((scanline) * FrameWidth) + i] = VCE.Palette[256]; } private unsafe void RenderBackgroundScanline(VDC vdc, byte priority, bool show) @@ -293,7 +308,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { // pointer to the BAT and the framebuffer for this line ushort* BatRow = VRAMptr + yTile * vdc.BatWidth; - int* dst = FBptr + (vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth; + int* dst = FBptr + (ScanLine - PCE.Settings.Top_Line) * FrameWidth; // parameters that change per tile ushort BatEnt; @@ -449,7 +464,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } @@ -468,7 +483,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } @@ -490,7 +505,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } @@ -508,7 +523,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } From beee25619d0bc4ab06b20ffda6860e037b708037 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 20 Mar 2018 14:51:33 +0000 Subject: [PATCH 114/339] ZXHawk: Added missing OSD Message Verbosity setting menu option --- .../ZXSpectrumNonSyncSettings.Designer.cs | 46 +++++++++++++++++-- .../ZXSpectrum/ZXSpectrumNonSyncSettings.cs | 37 ++++++++++++++- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs index 511f9eeea4..02ffb098b4 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs @@ -37,12 +37,15 @@ this.panTypecomboBox1 = new System.Windows.Forms.ComboBox(); this.lblBorderInfo = new System.Windows.Forms.Label(); this.lblAutoLoadText = new System.Windows.Forms.Label(); + this.lblOSDVerbinfo = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.osdMessageVerbositycomboBox1 = new System.Windows.Forms.ComboBox(); this.SuspendLayout(); // // OkBtn // this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OkBtn.Location = new System.Drawing.Point(247, 158); + this.OkBtn.Location = new System.Drawing.Point(247, 215); this.OkBtn.Name = "OkBtn"; this.OkBtn.Size = new System.Drawing.Size(60, 23); this.OkBtn.TabIndex = 3; @@ -54,7 +57,7 @@ // this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBtn.Location = new System.Drawing.Point(313, 158); + this.CancelBtn.Location = new System.Drawing.Point(313, 215); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(60, 23); this.CancelBtn.TabIndex = 4; @@ -123,13 +126,47 @@ "e correct traps are detected"; this.lblAutoLoadText.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // + // lblOSDVerbinfo + // + this.lblOSDVerbinfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblOSDVerbinfo.Location = new System.Drawing.Point(175, 174); + this.lblOSDVerbinfo.Name = "lblOSDVerbinfo"; + this.lblOSDVerbinfo.Size = new System.Drawing.Size(196, 21); + this.lblOSDVerbinfo.TabIndex = 28; + this.lblOSDVerbinfo.Text = "null"; + this.lblOSDVerbinfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 158); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(125, 13); + this.label4.TabIndex = 27; + this.label4.Text = "OSD Message Verbosity:"; + // + // osdMessageVerbositycomboBox1 + // + this.osdMessageVerbositycomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.osdMessageVerbositycomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.osdMessageVerbositycomboBox1.FormattingEnabled = true; + this.osdMessageVerbositycomboBox1.Location = new System.Drawing.Point(12, 174); + this.osdMessageVerbositycomboBox1.Name = "osdMessageVerbositycomboBox1"; + this.osdMessageVerbositycomboBox1.Size = new System.Drawing.Size(157, 21); + this.osdMessageVerbositycomboBox1.TabIndex = 26; + this.osdMessageVerbositycomboBox1.SelectionChangeCommitted += new System.EventHandler(this.OSDComboBox_SelectionChangeCommitted); + // // ZXSpectrumNonSyncSettings // this.AcceptButton = this.OkBtn; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CancelBtn; - this.ClientSize = new System.Drawing.Size(385, 193); + this.ClientSize = new System.Drawing.Size(385, 250); + this.Controls.Add(this.lblOSDVerbinfo); + this.Controls.Add(this.label4); + this.Controls.Add(this.osdMessageVerbositycomboBox1); this.Controls.Add(this.lblAutoLoadText); this.Controls.Add(this.lblBorderInfo); this.Controls.Add(this.label2); @@ -158,5 +195,8 @@ private System.Windows.Forms.ComboBox panTypecomboBox1; private System.Windows.Forms.Label lblBorderInfo; private System.Windows.Forms.Label lblAutoLoadText; + private System.Windows.Forms.Label lblOSDVerbinfo; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox osdMessageVerbositycomboBox1; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs index 0b8bfcbc0c..eb6642824a 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs @@ -30,19 +30,30 @@ namespace BizHawk.Client.EmuHawk { panTypecomboBox1.Items.Add(val); } - panTypecomboBox1.SelectedItem = _settings.AYPanConfig.ToString(); + panTypecomboBox1.SelectedItem = _settings.AYPanConfig.ToString(); + + // OSD Message Verbosity + var osdTypes = Enum.GetNames(typeof(ZXSpectrum.OSDVerbosity)); + foreach (var val in osdTypes) + { + osdMessageVerbositycomboBox1.Items.Add(val); + } + osdMessageVerbositycomboBox1.SelectedItem = _settings.OSDMessageVerbosity.ToString(); + UpdateOSDNotes((ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), osdMessageVerbositycomboBox1.SelectedItem.ToString())); } private void OkBtn_Click(object sender, EventArgs e) { bool changed = _settings.AutoLoadTape != autoLoadcheckBox1.Checked - || _settings.AYPanConfig.ToString() != panTypecomboBox1.SelectedItem.ToString(); + || _settings.AYPanConfig.ToString() != panTypecomboBox1.SelectedItem.ToString() + || _settings.OSDMessageVerbosity.ToString() != osdMessageVerbositycomboBox1.SelectedItem.ToString(); if (changed) { _settings.AutoLoadTape = autoLoadcheckBox1.Checked; _settings.AYPanConfig = (AYChip.AYPanConfig)Enum.Parse(typeof(AYChip.AYPanConfig), panTypecomboBox1.SelectedItem.ToString()); + _settings.OSDMessageVerbosity = (ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), osdMessageVerbositycomboBox1.SelectedItem.ToString()); GlobalWin.MainForm.PutCoreSettings(_settings); @@ -62,5 +73,27 @@ namespace BizHawk.Client.EmuHawk DialogResult = DialogResult.Cancel; Close(); } + + private void UpdateOSDNotes(ZXSpectrum.OSDVerbosity type) + { + switch (type) + { + case ZXSpectrum.OSDVerbosity.Full: + lblOSDVerbinfo.Text = "Show all OSD messages"; + break; + case ZXSpectrum.OSDVerbosity.Medium: + lblOSDVerbinfo.Text = "Only show machine/device generated messages"; + break; + case ZXSpectrum.OSDVerbosity.None: + lblOSDVerbinfo.Text = "No core-driven OSD messages"; + break; + } + } + + private void OSDComboBox_SelectionChangeCommitted(object sender, EventArgs e) + { + ComboBox cb = sender as ComboBox; + UpdateOSDNotes((ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), cb.SelectedItem.ToString())); + } } } From 9a9b56c69b1edd0e5c83e4b905aefcbfd638cf3d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 20 Mar 2018 15:28:23 +0000 Subject: [PATCH 115/339] ZXHawk: Improvements to reset methods --- .../SinclairSpectrum/Machine/SpectrumBase.cs | 40 +++++++++++++++++++ .../Machine/ZXSpectrum48K/ZX48.cs | 15 +++++++ 2 files changed, 55 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 9b4d49030f..aa5548f645 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -232,6 +232,26 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice.Reset(); if (AYDevice != null) AYDevice.Reset(); + + byte[][] rams = new byte[][] + { + RAM0, + RAM1, + RAM2, + RAM3, + RAM4, + RAM5, + RAM6, + RAM7 + }; + + foreach (var r in rams) + { + for (int i = 0; i < r.Length; i++) + { + r[i] = 0x00; + } + } } /// @@ -264,6 +284,26 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice.Reset(); if (AYDevice != null) AYDevice.Reset(); + + byte[][] rams = new byte[][] + { + RAM0, + RAM1, + RAM2, + RAM3, + RAM4, + RAM5, + RAM6, + RAM7 + }; + + foreach (var r in rams) + { + for (int i = 0; i < r.Length; i++) + { + r[i] = 0x00; + } + } } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index d9737a40f1..9dacc597f3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -37,5 +37,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } #endregion + + #region Reset + + public override void HardReset() + { + base.HardReset(); + + Random rn = new Random(); + for (int d = 0; d < 6912; d++) + { + RAM0[d] = (byte)rn.Next(255); + } + } + + #endregion } } From f86e00f925cdd36f213b58743e82bb23cf301f8b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 20 Mar 2018 21:17:57 -0400 Subject: [PATCH 116/339] PCE: only latch Y-scroll once per scanline after an offset. The latching behaviour is correct, but I'm unsure of what the offset value should be. fixes #771 --- BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs | 9 +++++++-- BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs | 1 + BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs | 8 +++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs index 0cdd634880..4122f23bc8 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs @@ -79,7 +79,7 @@ namespace BizHawk.Emulation.Cores.PCEngine } } - cpu.Execute(HBlankCycles); + cpu.Execute(24); if (InActiveDisplay) { @@ -92,11 +92,16 @@ namespace BizHawk.Emulation.Cores.PCEngine BackgroundY = Registers[BYR]; latch_bgy = false; } - BackgroundY++; BackgroundY &= 0x01FF; + } + } + cpu.Execute(HBlankCycles - 24); + + if (InActiveDisplay) + { if (render) RenderScanLine(); } diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs index eb4050f814..e8fda4374b 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs @@ -323,6 +323,7 @@ namespace BizHawk.Emulation.Cores.PCEngine ser.Sync("ScanLine", ref ScanLine); ser.Sync("BackgroundY", ref BackgroundY); + ser.Sync("latch_bgy", ref latch_bgy); ser.Sync("RCRCounter", ref RCRCounter); ser.Sync("ActiveLine", ref ActiveLine); ser.EndSection(); diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs index 8ba32258b5..8c68463029 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs @@ -175,7 +175,7 @@ namespace BizHawk.Emulation.Cores.PCEngine } } - CPU.Execute(VDC1.HBlankCycles); + CPU.Execute(24); if (InActiveDisplay) { @@ -203,10 +203,16 @@ namespace BizHawk.Emulation.Cores.PCEngine VDC2.BackgroundY++; VDC2.BackgroundY &= 0x01FF; } + } + CPU.Execute(VDC1.HBlankCycles - 24); + + if (InActiveDisplay) + { if (render) RenderScanLine(); } + if (ScanLine == VBlankLine && VDC1.VBlankInterruptEnabled) VDC1.StatusByte |= VDC.StatusVerticalBlanking; From 752bd0e35b2056d150d5f415b221800225535457 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 21 Mar 2018 12:47:10 +0000 Subject: [PATCH 117/339] ZXHawk: Audio settings menu implimenting volume controls for AY, tape and buzzer --- .../BizHawk.Client.EmuHawk.csproj | 11 +- BizHawk.Client.EmuHawk/MainForm.Designer.cs | 68 +- BizHawk.Client.EmuHawk/MainForm.Events.cs | 5 + .../ZXSpectrumAudioSettings.Designer.cs | 210 ++++++ .../ZXSpectrum/ZXSpectrumAudioSettings.cs | 79 +++ .../ZXSpectrum/ZXSpectrumAudioSettings.resx | 624 ++++++++++++++++++ .../ZXSpectrumNonSyncSettings.Designer.cs | 52 +- .../ZXSpectrum/ZXSpectrumNonSyncSettings.cs | 10 - .../Hardware/Abstraction/IPSG.cs | 5 + .../Hardware/SoundOuput/Buzzer.cs | 67 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 36 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 7 + 12 files changed, 1073 insertions(+), 101 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.Designer.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.resx diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 7341cd207c..5b51df2137 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -51,7 +51,7 @@ AnyCPU false prompt - + AllRules.ruleset @@ -503,6 +503,12 @@ TI83PaletteConfig.cs + + Form + + + ZXSpectrumAudioSettings.cs + Form @@ -1422,6 +1428,9 @@ TI83PaletteConfig.cs + + ZXSpectrumAudioSettings.cs + ZXSpectrumNonSyncSettings.cs diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 0077501f4c..57d6c30767 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -272,8 +272,8 @@ this.SMSregionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSregionExportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSregionJapanToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSregionKoreaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSregionAutoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSregionKoreaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSregionAutoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayNtscToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayPalToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -339,8 +339,8 @@ this.ColecoControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator35 = new System.Windows.Forms.ToolStripSeparator(); this.ColecoSkipBiosMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.ColecoUseSGMMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.N64SubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.ColecoUseSGMMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.N64SubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.N64PluginSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.N64ControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator23 = new System.Windows.Forms.ToolStripSeparator(); @@ -457,6 +457,7 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); + this.ZXSpectrumAudioSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -2535,8 +2536,8 @@ this.SMSregionToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.SMSregionExportToolStripMenuItem, this.SMSregionJapanToolStripMenuItem, - this.SMSregionKoreaToolStripMenuItem, - this.SMSregionAutoToolStripMenuItem}); + this.SMSregionKoreaToolStripMenuItem, + this.SMSregionAutoToolStripMenuItem}); this.SMSregionToolStripMenuItem.Name = "SMSregionToolStripMenuItem"; this.SMSregionToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSregionToolStripMenuItem.Text = "Region"; @@ -2554,17 +2555,17 @@ this.SMSregionJapanToolStripMenuItem.Size = new System.Drawing.Size(107, 22); this.SMSregionJapanToolStripMenuItem.Text = "Japan"; this.SMSregionJapanToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionJapan_Click); - // - // SMSregionKoreaToolStripMenuItem - // - this.SMSregionKoreaToolStripMenuItem.Name = "SMSregionKoreaToolStripMenuItem"; - this.SMSregionKoreaToolStripMenuItem.Size = new System.Drawing.Size(107, 22); - this.SMSregionKoreaToolStripMenuItem.Text = "Korea"; - this.SMSregionKoreaToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionKorea_Click); - // - // SMSregionAutoToolStripMenuItem - // - this.SMSregionAutoToolStripMenuItem.Name = "SMSregionAutoToolStripMenuItem"; + // + // SMSregionKoreaToolStripMenuItem + // + this.SMSregionKoreaToolStripMenuItem.Name = "SMSregionKoreaToolStripMenuItem"; + this.SMSregionKoreaToolStripMenuItem.Size = new System.Drawing.Size(107, 22); + this.SMSregionKoreaToolStripMenuItem.Text = "Korea"; + this.SMSregionKoreaToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionKorea_Click); + // + // SMSregionAutoToolStripMenuItem + // + this.SMSregionAutoToolStripMenuItem.Name = "SMSregionAutoToolStripMenuItem"; this.SMSregionAutoToolStripMenuItem.Size = new System.Drawing.Size(107, 22); this.SMSregionAutoToolStripMenuItem.Text = "Auto"; this.SMSregionAutoToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionAuto_Click); @@ -3040,7 +3041,7 @@ this.ColecoControllerSettingsMenuItem, this.toolStripSeparator35, this.ColecoSkipBiosMenuItem, - this.ColecoUseSGMMenuItem}); + this.ColecoUseSGMMenuItem}); this.ColecoSubMenu.Name = "ColecoSubMenu"; this.ColecoSubMenu.Size = new System.Drawing.Size(56, 19); this.ColecoSubMenu.Text = "&Coleco"; @@ -3065,17 +3066,17 @@ this.ColecoSkipBiosMenuItem.Size = new System.Drawing.Size(253, 22); this.ColecoSkipBiosMenuItem.Text = "&Skip BIOS intro (When Applicable)"; this.ColecoSkipBiosMenuItem.Click += new System.EventHandler(this.ColecoSkipBiosMenuItem_Click); - // - // ColecoUseSGMMenuItem - // - this.ColecoUseSGMMenuItem.Name = "ColecoUseSGMMenuItem"; - this.ColecoUseSGMMenuItem.Size = new System.Drawing.Size(253, 22); - this.ColecoUseSGMMenuItem.Text = "&Use the Super Game Module"; - this.ColecoUseSGMMenuItem.Click += new System.EventHandler(this.ColecoUseSGMMenuItem_Click); - // - // N64SubMenu - // - this.N64SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + // + // ColecoUseSGMMenuItem + // + this.ColecoUseSGMMenuItem.Name = "ColecoUseSGMMenuItem"; + this.ColecoUseSGMMenuItem.Size = new System.Drawing.Size(253, 22); + this.ColecoUseSGMMenuItem.Text = "&Use the Super Game Module"; + this.ColecoUseSGMMenuItem.Click += new System.EventHandler(this.ColecoUseSGMMenuItem_Click); + // + // N64SubMenu + // + this.N64SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.N64PluginSettingsMenuItem, this.N64ControllerSettingsMenuItem, this.toolStripSeparator23, @@ -3399,6 +3400,7 @@ this.zXSpectrumToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.ZXSpectrumCoreEmulationSettingsMenuItem, this.ZXSpectrumControllerConfigurationMenuItem, + this.ZXSpectrumAudioSettingsMenuItem, this.ZXSpectrumNonSyncSettingsMenuItem}); this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); @@ -4052,6 +4054,13 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // + // ZXSpectrumAudioSettingsMenuItem + // + this.ZXSpectrumAudioSettingsMenuItem.Name = "ZXSpectrumAudioSettingsMenuItem"; + this.ZXSpectrumAudioSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumAudioSettingsMenuItem.Text = "Audio Settings"; + this.ZXSpectrumAudioSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumAudioSettingsMenuItem_Click); + // // MainForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -4520,5 +4529,6 @@ private System.Windows.Forms.ToolStripMenuItem ZXSpectrumControllerConfigurationMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumCoreEmulationSettingsMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumNonSyncSettingsMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumAudioSettingsMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 59450b68db..810dfb3567 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2482,6 +2482,11 @@ namespace BizHawk.Client.EmuHawk new ZXSpectrumNonSyncSettings().ShowDialog(); } + private void ZXSpectrumAudioSettingsMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumAudioSettings().ShowDialog(); + } + #endregion #region Help diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.Designer.cs new file mode 100644 index 0000000000..21b2122306 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.Designer.cs @@ -0,0 +1,210 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumAudioSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumAudioSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.panTypecomboBox1 = new System.Windows.Forms.ComboBox(); + this.lblBorderInfo = new System.Windows.Forms.Label(); + this.tapeVolumetrackBar = new System.Windows.Forms.TrackBar(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.earVolumetrackBar = new System.Windows.Forms.TrackBar(); + this.label5 = new System.Windows.Forms.Label(); + this.ayVolumetrackBar = new System.Windows.Forms.TrackBar(); + ((System.ComponentModel.ISupportInitialize)(this.tapeVolumetrackBar)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.earVolumetrackBar)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ayVolumetrackBar)).BeginInit(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(247, 298); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(313, 298); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(140, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Audio Settings"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 236); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(135, 13); + this.label2.TabIndex = 23; + this.label2.Text = "AY-3-8912 Panning Config:"; + // + // panTypecomboBox1 + // + this.panTypecomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panTypecomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.panTypecomboBox1.FormattingEnabled = true; + this.panTypecomboBox1.Location = new System.Drawing.Point(12, 252); + this.panTypecomboBox1.Name = "panTypecomboBox1"; + this.panTypecomboBox1.Size = new System.Drawing.Size(157, 21); + this.panTypecomboBox1.TabIndex = 22; + // + // lblBorderInfo + // + this.lblBorderInfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblBorderInfo.Location = new System.Drawing.Point(175, 245); + this.lblBorderInfo.Name = "lblBorderInfo"; + this.lblBorderInfo.Size = new System.Drawing.Size(196, 37); + this.lblBorderInfo.TabIndex = 24; + this.lblBorderInfo.Text = "Selects a particular panning configuration for the 3ch AY-3-8912 Programmable Sou" + + "nd Generator (128K models only)"; + this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // tapeVolumetrackBar + // + this.tapeVolumetrackBar.Location = new System.Drawing.Point(12, 60); + this.tapeVolumetrackBar.Maximum = 100; + this.tapeVolumetrackBar.Name = "tapeVolumetrackBar"; + this.tapeVolumetrackBar.Size = new System.Drawing.Size(359, 45); + this.tapeVolumetrackBar.TabIndex = 25; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 44); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(73, 13); + this.label3.TabIndex = 26; + this.label3.Text = "Tape Volume:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 108); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(80, 13); + this.label4.TabIndex = 28; + this.label4.Text = "Buzzer Volume:"; + // + // earVolumetrackBar + // + this.earVolumetrackBar.Location = new System.Drawing.Point(12, 124); + this.earVolumetrackBar.Maximum = 100; + this.earVolumetrackBar.Name = "earVolumetrackBar"; + this.earVolumetrackBar.Size = new System.Drawing.Size(359, 45); + this.earVolumetrackBar.TabIndex = 27; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(12, 172); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(98, 13); + this.label5.TabIndex = 30; + this.label5.Text = "AY-3-8912 Volume:"; + // + // ayVolumetrackBar + // + this.ayVolumetrackBar.Location = new System.Drawing.Point(12, 188); + this.ayVolumetrackBar.Maximum = 100; + this.ayVolumetrackBar.Name = "ayVolumetrackBar"; + this.ayVolumetrackBar.Size = new System.Drawing.Size(359, 45); + this.ayVolumetrackBar.TabIndex = 29; + // + // ZXSpectrumAudioSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(385, 333); + this.Controls.Add(this.label5); + this.Controls.Add(this.ayVolumetrackBar); + this.Controls.Add(this.label4); + this.Controls.Add(this.earVolumetrackBar); + this.Controls.Add(this.label3); + this.Controls.Add(this.tapeVolumetrackBar); + this.Controls.Add(this.lblBorderInfo); + this.Controls.Add(this.label2); + this.Controls.Add(this.panTypecomboBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumAudioSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Audio Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + ((System.ComponentModel.ISupportInitialize)(this.tapeVolumetrackBar)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.earVolumetrackBar)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ayVolumetrackBar)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox panTypecomboBox1; + private System.Windows.Forms.Label lblBorderInfo; + private System.Windows.Forms.TrackBar tapeVolumetrackBar; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TrackBar earVolumetrackBar; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TrackBar ayVolumetrackBar; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs new file mode 100644 index 0000000000..204d406d7f --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs @@ -0,0 +1,79 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumAudioSettings : Form + { + private ZXSpectrum.ZXSpectrumSettings _settings; + + public ZXSpectrumAudioSettings() + { + InitializeComponent(); + } + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _settings = ((ZXSpectrum)Global.Emulator).GetSettings().Clone(); + + // AY panning config + var panTypes = Enum.GetNames(typeof(AYChip.AYPanConfig)); + foreach (var val in panTypes) + { + panTypecomboBox1.Items.Add(val); + } + panTypecomboBox1.SelectedItem = _settings.AYPanConfig.ToString(); + + // tape volume + tapeVolumetrackBar.Value = _settings.TapeVolume; + + // ear volume + earVolumetrackBar.Value = _settings.EarVolume; + + // ay volume + ayVolumetrackBar.Value = _settings.AYVolume; + + + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _settings.AYPanConfig.ToString() != panTypecomboBox1.SelectedItem.ToString() + || _settings.TapeVolume != tapeVolumetrackBar.Value + || _settings.EarVolume != earVolumetrackBar.Value + || _settings.AYVolume != ayVolumetrackBar.Value; + + if (changed) + { + _settings.AYPanConfig = (AYChip.AYPanConfig)Enum.Parse(typeof(AYChip.AYPanConfig), panTypecomboBox1.SelectedItem.ToString()); + + _settings.TapeVolume = tapeVolumetrackBar.Value; + _settings.EarVolume = earVolumetrackBar.Value; + _settings.AYVolume = ayVolumetrackBar.Value; + + GlobalWin.MainForm.PutCoreSettings(_settings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Misc settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAADLytDi9tOemQAAAAAAUy9EecLEsa1uPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPj11Mme + VakAAAAAAAAAAAAATS84M0akAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE + AAAAAAAAAAAAAAABAAAAAQAAAAAAAFFNTQBRUlIAU1RUAGJHRwBiT08Aa0lIAGJTUwBrVlYAYllZAGZc + XABpWloAb1xbAHNTUwB7V1YAc1hXAHFbWwBkZWUAaWFhAG5kZABpamkAcGFhAHlubgB2cHAAf3V1AH55 + eQB8fX0AgUpKAI1PTwCLWFcAhlhYAI9ZWQCKXFsAm1ZWAJJZWQCWWVgAmlpbAJtcWwCiXFwAl2BfAIBg + YACAZ2YAgG9vAI9oaACWZWQAmGBhAJ5kZACcaWoAmm9vAIV0dACNcHAAiXZ2AIB8fACac3IAm3V0AJ51 + dQCZfHwAnHx8AKNmZgCnZmYAqmJiAK5jYwCvb24AtWVmALBtbgC5bW0AvmxtAKx+fQCxcnIAtHBwALZz + dACydXQAtnd2ALlwcAC5dnYAt3p5ALh5eAC8fHsAun18ALx+fQDGb3AAxnBxAMdzdADAd3YAyHJzAMlz + dADJdXYAynd4AMd/fwDMe3wAzXx9AHunbwBhvHIAYsN4ANuLOwC2hn4A4Zt5APC3ZABte9sAX47+AHWM + 5QAl0foAY+P8AIeDgwCFhoYAioSEAJOIiACWi4sAmpKRAKGCgQCmhYUAqYGBAKuDhACniooApYyMAKiO + jQCyhYMAvoWEALeNjQCrj5AAr5eXALSVlAC9lJMAmbCEAK6RugDBgYAAwoSCAMWDhADChoQAxYeFAM6A + gQDFiIYAxoqIAMqIiQDMi4oAy4yKAMiPjQDPj44A0ISFANKJigDUi4wA04+NANWNjgDKkY8A0JCOANud + iQDWj5AAzJSTAM2XlgDGm5oA1pGSANOUkgDVl5EA1pOUANiVlgDYmJUA2ZeYANKenADbmpsA3pmYANuc + mgDbn5wA1aacAN6gngDqqZoA3Z+gAMyjowDCra0AxqysAMqpqQDboaAA3qKiAN6logDbp6UA3aWkANer + qgDWsbMA0rW0ANe0tADfs7IA4aSiAOGlpQDkp6UA46imAOWopgDsraIA6qimAOGoqADhrqwA6a2rAOqv + rADpsK4A7LGuAOGzswDlsbEA7bKxAO+1sgDotrYA5rm3AO+4twDot7sA6bq5AOu9uwDrv70A8bazAPG2 + tADxuLUA9Lm2APC9uwD2vboA9L+9APi+uwD4v7wA8sC+APXAvgD5wL0AkILJAKqXzACsu8cAqr/LALLV + 3QDawMIA48XFAOvDwQDswMAA7cTDAO/ExQDgxsgA8cbEAPTGxADwyskA9MvJAPLNzQD21dYA+NjZAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAMEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHCEcBQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAayU9PSYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdQlBSQiJpAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAM0pSUlJQPRcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnUlJSUlJGFQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFJSUlJSUkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzUlJSWVJZfxAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5XWYqKioqGDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASoqMkpqa + mqAsAAAAAAAAAAAAAAAAAABoNAAAAAAAAACMjJyuvLy2toYHAAAAAAAAAAAAABcOIDouBgAAAAAAc4yc + tsHKysPAriIKAAAAAAAAABYgRk1LTX+DEAAAAABukqXB4ejo4dHPQCIEChcXEwggTXV/k66unKMpAAAA + AG6Srsro6ero0dN/Rk1NRk2Dg4STrsbh4cHAt2sAAAAAbpKuOXPe6ajW15KGg4OGk528yuHo5eHPz882 + AAAAAAB4jCkDAxSoMabXt5yjt8ro3ePo5dbT09HTdAAAAAAAAABGcBFoGgFwdtfDwHxi2dpmZcrX09HP + z0MAAAAAAAAAAHh/qWwaOa6cz9PNZGPYsdzbzc3DwLk2AAAAAAAAAAAAAAAvhpKakoyg19HNyKS5wHtb + orZ/cwAAAAAAAAAAAAAAAAAANkaKWVm5zb1gYV6cXVxfNgAAAAAAAAAAAAAAAAAAALGvlTIuP1K5tqCR + l4xfLwAAAAAAAAAAAAAAAAAAsbPBenkAAAAAcCVYjE0scwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////+f///+D////A////wH + ///4B///+Af///gH///wB///8Af///AH/+fwA/8D4AH8AeAAAAHgAAAB4AAAA+AAAAfwAAAP8AAAH/wA + AD//AAD//gAD//B4D////////////////////////////ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA + AAAAAAAAAAEAAAABAAAAAAAAWlJSAHBJSQB1SEgAe1dXAHdYWAB5WlkAel1dAGBiYgB1bGwAfWtrAHh2 + dgB9fn4Ag01NAIRXVwCIV1cAhV9eAItbWgCgX14ApV1dAJhgXwCNYGAAnWtqAJhtbQCCdnYAh3x8AI15 + eACeensAqGBgAKhoZwCga2oArGpqALNqagCzb28AtG1tALltbQCxb3AApnVzAKlzcwCqdHMApnp6AKd+ + fgCpensAq3x7ALZ3dgC8dHQAvH59AMZvcADGcHEAxXN0AMhycwDJdncAynh5AMx5egDNfn8Ajo1wAOek + VgDGgH8A4p53AEZ2+gB8u4AAd8PaAIuEhACOh4cAjo6OAJ+DggCejo4Ao4SEAKSIiACsi4sAqo2MAK6P + jgC+gYAAvoaGAL+KiACskJAAtJeXALWenQC5np4At6iOAKmyjgC9nroAwYSDAMaGhADOhoYAxomHAMiK + iQDJjYwA0oeIANOOjwDUjY0A2ZiPANaPkADGkZEAx5eXAMySkADGnZwA1ZOSANeTlADWl5YA2JSVANGZ + mADan50A3J6dAOCcmwDVoJ8A7K2fAMOtrQDXo6IA3aCgAN+kpADVq6oA3ay3AMu0tADPtrYA3L+/AOCi + oQDhpqUA5KelAOinpgDlq6gA46usAOOvrQDqrqwA7LGuAOayswDjtrQA5re1AOqysQDts7EA57y6AO+8 + ugDrvL0A8LOwAPC1sgDwtrQA87q3APS6twD2vboA8b69APi/vAD2wb4A+cC9AJmTzwDHqMMAu8PMAIHf + 8QDByNAA7cLCAO3FwwDvxsQA5cjIAOzOzgDwxcQA9cbEAPPP0AD10tIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + BQMJAAAAAAAAAAAAAAAAAAAAAAAAAAAPHBMNAAAAAAAAAAAAAAAAAAAAAAAAABojLy8TAAAAAAAAAAAA + AAAAAAAAAAAAAB0wMDAiPgAAAAAAAAAAAAAAAAAAAAAAQjAwMDAtGAAAAAAAAAAAAAAAAAAAAAAAFzIy + NTU5CgAAAAAAAAAAAAAAAAAAAAAAIjZYWFxcBwAAAAAAAAAAAAAAAAAAAAAANlxtdW11JQAAAAAAAAAA + PgcRDgkAAAAAXG1/lISAZgMAAAAAABkVLC5SVhcAAABNY3WWnJuLfB8UBAcQHkhWaX91dSsAAABNY2BM + mJeCiVJSVl9laX+WloSJgEIAAAAAXAEIC0tGjnR0dJaRk5qNjIyJQwAAAAAAJkNADBtdjIaPO1GSPYuJ + hnVEAAAAAAAAAClISWRcd4xwkGp8UE90VwAAAAAAAAAAAAAAKSQ1NYZ7OjhbPDdGAAAAAAAAAAAAAHNv + YGsAKyJoXFYmRwAAAAAAAAAAAAAAcnIAAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////APx//wD4f/8A8H//APA//wDgP/8A4D//AOA//wDgP8EA4B8BAMAAAQDAAAEA4AADAOAA + BwDwAB8A/AA/APCA/wDn9/8A////AP///wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA + AAAAAQAAAAEAAAAAAABjZGQAdmRjAHtpaQB/eHgAgU9PAKBaWgCFbm0AlWtqAKptbgCwZ2cAsGhoAKxw + cACteHkAvnJyAMZvcADGcHEAy3l5AMx9fgCFmXQAwIB/ANeUfQDhoX8AlIqJAJWMjACYiIgAoIaGAK2K + igCxh4cAvoGAALKKigC4iYgAuJWVAL2cnACss50AuqKhAL+mpgDLgoIAxImHAMeNjADLkI8AxpWTANCS + kQDYlZUA1J6dANqZmgDdnp4A1J+oAMaiogDOr68AzLKyANi5uADhpaIA4qypAOWtqADrrqsA4bKwAOay + sgDtuLYA57++AOy4uADxtLIA8be0APa9ugDswL4A9sG+ALCcxwC5ncIA06zBALnH0QC2ytQA7sPDAPLS + 0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAZBgUAAAAAAAAAAAAAAAAACw8KAAAAAAAAAAAAAAAAGhAQDgAAAAAAAAAAAAAAAAkRESUYAAAA + AAAAAAAAAAAlKy4uBwAAAAAAAAcDAAAAKzlHPCYCAAAYCB0oKgAAAC0wSDs0FB0nLDlAOiwAAAANAQQb + Pi9DRkVBPzUAAAAAJB4cKz5EQjMiNSkAAAAAAAAAHwwRNxYVEyQAAAAAAAAxMgAAACEgAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD//wAA4/8AAOP/AADD/wAAwf8AAMH5 + AADAwQAAwAEAAMADAADABwAA8A8AAM5/AAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAkAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAUAAAAOAEBAVUAAABUAAAANQAAABAAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAkFBSUvGRl5TCkpwlYuLtxDJCTQFw0NmQAA + AEkAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGAwMKE8rK6V6RET2klJR/5ZS + U/+OT0//ZDc38B0QEJoAAAAyAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYDAwYVzAwoopP + T/ygXVz/oFtb/55ZWf+bWFf/k1NT/1UvL9wGAwNcAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AARNKipxhk5O+adkY/+uZWX/tWdo/7VmZ/+qYWH/nltb/3hERPcfERGCAAAAFgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADEZGS1zQ0LXqGdm/7ptbf/Fb3D/x3Bx/8hwcf/BbW7/q2Vl/4hPT/82HR2gAAAAIAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAB1gxMYyYXl3/vXFx/8Zwcf/HcHH/x3Bx/8dwcf/HcHH/uG1t/5NY + V/9EJia2AAAAKQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB8fNH1MS+K4cnH/x3Fy/8dwcf/HcHH/x3Bx/8dw + cf/HcHH/wHBx/51gX/9PLCzGAAAAMwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + cP/Gb3D/oF9e/hMKCmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4QECynZmX7xnBx/sdwcf/HcHH/tG1t/h8REYMAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAx + MIzFc3T+xm9w/sdwcf7HcHH+vHR0/jAcHJkAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ4OAYVSUtfIcnP/yXZ3/st5ef/LeHn/xoB//kQq + KrEAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJxYWGrNvb/7Nfn//0oeI/tSNjf/UjI3/1ZOS/mE+PtQAAAAXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAIAAAARAAAALQAAADUAAAARAAAAAAAAAAAAAAAAQyYmUM6Ghv/Wj5D/3J2e/uCl + pf/fpKT/4KOi/qRycPkHBARlAAAABQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAJh8REYBYNTXMhVJR8XxM + TO8gEhKeAAAAEAAAAAAAAAAAbUVEe9aPkP7doKD+5rKz/uu9vv7rvLz+6rKx/tqfnf5iNzfnCAQEcwAA + ACoAAAAbAAAAIQIBATorGBiQhFNT67Z3dv68fn3+wYSD/siKiP6aZmX2AQAAKQAAAAAAAAAAd05Ni9eT + lP/jq6z/7cLC/vXS0v/zz9D/8b69/uyxrv+samr/l15d+2tDQ+NkPz7bdkxL451nZve+gYD/yY2M/tWg + n//jtrT/46+t/uOmpP+mdHPwBQMDFAAAAAAAAAAAdkpJh9iUlf7Hl5f+tJeX/uzOzv7lyMj+57y6/vS6 + t/7HhoX+xYaE/saJh/7MkpD+0ZmY/tejov7mt7X+7cXD/vDFxP7vvLr+8Le0/u2zsf5PMzOMDQcHAQAA + AAAAAAAAYTg4X9OOj/9aUlL/YGJi/nh2dv+skJD/qo2M/vnAvf/dn53/4KKg/+Cnp/7vxsT/u8PM/sHI + 0P/1xsT/9sG+/ve+u//3vrv/87q3/ntVVLkkFhYIAAAAAAAAAAAAAAAAVC8wD6BkZOWjhIT/jo6O/n1+ + fv+eenv/xpGR/vi/vP/wtbL/mZPP/0Z2+v69nrr/gd/x/nfD2v/2vLr/9Lq3/vG2tP/lq6j/elJRrjQg + IAoAAAAAAAAAAAAAAAAAAAAAAAAAAGc7OyeOWVnGv4eH/r2Fhf7YlZb+1Y6P/uinpv74v7z+3ay3/seo + w/7srZ/+7LGv/qmyjv63qI7+5Kel/r2GhPZ1S0p1QCcmAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAd0pKOpReXtKxb3D/yXl6/sx5ev/ws7D/6q6s/+Ked/7npFb/2ZiP/ny7gP+OjW/9h1dWr2I7 + OiMAAAAAAAAAAAAAAAAAAAAAAAAAALSCggSqcXIbo2dnN61xcVS/h4eIzp2c2cKWle2OY2OGbz4+Y4xN + Tr6zaWn84Jyb/9aXlv7Ji4r/p25t9INTUqZlPDw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJg + YASjcnMorH9/a6h7e4yabm6Df1NTU3VKSgwAAAAAAAAAAAAAAABgNDQgcj8/bntHR4ZnPDxTVTExDQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////APx//wD4P/8A8D//AOA//wDgH/8A4B//AMAf + /wDAH8EAwA8AAMAAAADAAAAAwAAAAMAAAQDAAAMA4AAHAPgAHwAAAH8AAcH/AP///wD///8A////ACgA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQc + HA5LKSlUNBwcSAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsO + DgV/SkqHm1hY+X5HR90tGRkuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB4SEhCr2Zm7sZwcf+oYWL5UC8vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAACnl9fnMRwcf/IcXL/tmxs/mI8PGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa0NCGbRsbdbMenv/zn5//8R9ff9ySkmCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA + AAkAAAAAAAAAAItYWDvFfn/y2ZWW/92fn//anJv/jWFgvwAAAB0AAAAAAAAAAAAAAAIzHBwiYjs7a3pM + S6pqQkKjLBoaMwAAAACeZ2dZ05KS/em0tP/vxMT/77u6/8CHhfpmPDyvRysqYlExMV1ySEiGnWdn07qB + gPzLkI//w4iG/HJLS3YAAAAAomloXsyRkf/DoKD/48bG/+jAv//hpKL/vX17/7h/fPu/iYj7z5qZ/+Gw + rv/rvLr/77q3/9ScmuR9U1I+AAAAAJZbWz2ndnbxdG9v/4yCgv+4lJP/77Wy/86erP+6nsH/tsXR/8PH + 0P/4wsD/9b26/+Cppu2peXdiAAAAAQAAAABYKCgHn2lqe6eCguSsgoL90pKS//Cxrv/TrcP/s5y+/8i3 + s/+quab/26mh/82UktSgbm1TBAAAAwAAAACud3cEvYGBC7N6ehyyfHtyt39+3bNub9vLgYH05qak/+Kg + g//OlH39jZR04Zd0aYmDT1EiAAAAAAAAAAAAAAAAr3t7D7aCgki5h4Z8uImJgah+fUltPz8ajU1ORq1s + bI6vdHOgm2RkaYxJUiZgCygCAAAAAAAAAAAAAAAAAAAAAGo9PQF9UVEHcEdHCTodHQIAAAAAAAAAAAAA + AAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AADh/wAAwf8AAMH/ + AACB/wAAgfkAAIDAAACAAAAAgAAAAIAAAACAAQAAAAcAAAAPAAAOfwAA//8AAA== + + + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs index 02ffb098b4..a519b00e94 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs @@ -33,9 +33,6 @@ this.CancelBtn = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.autoLoadcheckBox1 = new System.Windows.Forms.CheckBox(); - this.label2 = new System.Windows.Forms.Label(); - this.panTypecomboBox1 = new System.Windows.Forms.ComboBox(); - this.lblBorderInfo = new System.Windows.Forms.Label(); this.lblAutoLoadText = new System.Windows.Forms.Label(); this.lblOSDVerbinfo = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); @@ -45,7 +42,7 @@ // OkBtn // this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OkBtn.Location = new System.Drawing.Point(247, 215); + this.OkBtn.Location = new System.Drawing.Point(247, 145); this.OkBtn.Name = "OkBtn"; this.OkBtn.Size = new System.Drawing.Size(60, 23); this.OkBtn.TabIndex = 3; @@ -57,7 +54,7 @@ // this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBtn.Location = new System.Drawing.Point(313, 215); + this.CancelBtn.Location = new System.Drawing.Point(313, 145); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(60, 23); this.CancelBtn.TabIndex = 4; @@ -84,37 +81,6 @@ this.autoLoadcheckBox1.Text = "Auto-Load Tape"; this.autoLoadcheckBox1.UseVisualStyleBackColor = true; // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(12, 97); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(135, 13); - this.label2.TabIndex = 23; - this.label2.Text = "AY-3-8912 Panning Config:"; - // - // panTypecomboBox1 - // - this.panTypecomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.panTypecomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.panTypecomboBox1.FormattingEnabled = true; - this.panTypecomboBox1.Location = new System.Drawing.Point(12, 113); - this.panTypecomboBox1.Name = "panTypecomboBox1"; - this.panTypecomboBox1.Size = new System.Drawing.Size(157, 21); - this.panTypecomboBox1.TabIndex = 22; - // - // lblBorderInfo - // - this.lblBorderInfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblBorderInfo.Location = new System.Drawing.Point(175, 106); - this.lblBorderInfo.Name = "lblBorderInfo"; - this.lblBorderInfo.Size = new System.Drawing.Size(196, 37); - this.lblBorderInfo.TabIndex = 24; - this.lblBorderInfo.Text = "Selects a particular panning configuration for the 3ch AY-3-8912 Programmable Sou" + - "nd Generator (128K models only)"; - this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // // lblAutoLoadText // this.lblAutoLoadText.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); @@ -129,7 +95,7 @@ // lblOSDVerbinfo // this.lblOSDVerbinfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblOSDVerbinfo.Location = new System.Drawing.Point(175, 174); + this.lblOSDVerbinfo.Location = new System.Drawing.Point(175, 107); this.lblOSDVerbinfo.Name = "lblOSDVerbinfo"; this.lblOSDVerbinfo.Size = new System.Drawing.Size(196, 21); this.lblOSDVerbinfo.TabIndex = 28; @@ -139,7 +105,7 @@ // label4 // this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(12, 158); + this.label4.Location = new System.Drawing.Point(12, 91); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(125, 13); this.label4.TabIndex = 27; @@ -151,7 +117,7 @@ | System.Windows.Forms.AnchorStyles.Right))); this.osdMessageVerbositycomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.osdMessageVerbositycomboBox1.FormattingEnabled = true; - this.osdMessageVerbositycomboBox1.Location = new System.Drawing.Point(12, 174); + this.osdMessageVerbositycomboBox1.Location = new System.Drawing.Point(12, 107); this.osdMessageVerbositycomboBox1.Name = "osdMessageVerbositycomboBox1"; this.osdMessageVerbositycomboBox1.Size = new System.Drawing.Size(157, 21); this.osdMessageVerbositycomboBox1.TabIndex = 26; @@ -163,14 +129,11 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CancelBtn; - this.ClientSize = new System.Drawing.Size(385, 250); + this.ClientSize = new System.Drawing.Size(385, 180); this.Controls.Add(this.lblOSDVerbinfo); this.Controls.Add(this.label4); this.Controls.Add(this.osdMessageVerbositycomboBox1); this.Controls.Add(this.lblAutoLoadText); - this.Controls.Add(this.lblBorderInfo); - this.Controls.Add(this.label2); - this.Controls.Add(this.panTypecomboBox1); this.Controls.Add(this.autoLoadcheckBox1); this.Controls.Add(this.label1); this.Controls.Add(this.CancelBtn); @@ -191,9 +154,6 @@ private System.Windows.Forms.Button CancelBtn; private System.Windows.Forms.Label label1; private System.Windows.Forms.CheckBox autoLoadcheckBox1; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.ComboBox panTypecomboBox1; - private System.Windows.Forms.Label lblBorderInfo; private System.Windows.Forms.Label lblAutoLoadText; private System.Windows.Forms.Label lblOSDVerbinfo; private System.Windows.Forms.Label label4; diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs index eb6642824a..921cee6b8b 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs @@ -24,14 +24,6 @@ namespace BizHawk.Client.EmuHawk // autoload tape autoLoadcheckBox1.Checked = _settings.AutoLoadTape; - // AY panning config - var panTypes = Enum.GetNames(typeof(AYChip.AYPanConfig)); - foreach (var val in panTypes) - { - panTypecomboBox1.Items.Add(val); - } - panTypecomboBox1.SelectedItem = _settings.AYPanConfig.ToString(); - // OSD Message Verbosity var osdTypes = Enum.GetNames(typeof(ZXSpectrum.OSDVerbosity)); foreach (var val in osdTypes) @@ -46,13 +38,11 @@ namespace BizHawk.Client.EmuHawk { bool changed = _settings.AutoLoadTape != autoLoadcheckBox1.Checked - || _settings.AYPanConfig.ToString() != panTypecomboBox1.SelectedItem.ToString() || _settings.OSDMessageVerbosity.ToString() != osdMessageVerbositycomboBox1.SelectedItem.ToString(); if (changed) { _settings.AutoLoadTape = autoLoadcheckBox1.Checked; - _settings.AYPanConfig = (AYChip.AYPanConfig)Enum.Parse(typeof(AYChip.AYPanConfig), panTypecomboBox1.SelectedItem.ToString()); _settings.OSDMessageVerbosity = (ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), osdMessageVerbositycomboBox1.SelectedItem.ToString()); GlobalWin.MainForm.PutCoreSettings(_settings); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs index a6028aa2d6..d359103d27 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs @@ -42,6 +42,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// void Reset(); + /// + /// The volume of the AY chip + /// + int Volume { get; set; } + /// /// Called at the start of a frame /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index 7fc0a71715..3a9516a63b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -53,6 +53,69 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum set { _tStatesPerSample = value; } } + /// + /// The tape loading volume + /// Accepts an int 0-100 value + /// + private int _tapeVolume; + public int TapeVolume + { + get + { + return VolumeConverterOut(_tapeVolume); + } + set + { + _tapeVolume = VolumeConverterIn(value); + } + } + + /// + /// The EAR beeper volume + /// + private int _earVolume; + public int EarVolume + { + get + { + return VolumeConverterOut(_earVolume); + } + set + { + _earVolume = VolumeConverterIn(value); + } + } + + /// + /// Takes an int 0-100 and returns the relevant short volume to output + /// + /// + /// + private int VolumeConverterIn(int vol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + return vol * increment; + } + + /// + /// Takes an short volume and returns the relevant int value 0-100 + /// + /// + /// + private int VolumeConverterOut(int shortvol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + if (shortvol > maxLimit) + shortvol = maxLimit; + + return shortvol / increment; + } + + private SpectrumBase _machine; /// @@ -234,9 +297,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (var i = firstSample; i < currentEnd + pulse.Length; i += TStatesPerSample) { if (_tapeMode) - samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 6) : (short)0; + samples[sampleIndex++] = pulse.State ? (short)(_tapeVolume) : (short)0; else - samples[sampleIndex++] = pulse.State ? (short)(short.MaxValue / 3) : (short)0; + samples[sampleIndex++] = pulse.State ? (short)(_earVolume) : (short)0; } currentEnd += pulse.Length; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index f1c5bdff80..df8146fb7e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -25,12 +25,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public bool PutSettings(ZXSpectrumSettings o) { - //if (SoundMixer != null) - //SoundMixer.Stereo = o.StereoSound; - - if (_machine != null && _machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) + // restore user settings to devices + if (_machine != null && _machine.AYDevice != null) { ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = o.AYPanConfig; + _machine.AYDevice.Volume = o.AYVolume; + } + if (_machine != null && _machine.BuzzerDevice != null) + { + ((Buzzer)_machine.BuzzerDevice as Buzzer).TapeVolume = o.TapeVolume; + ((Buzzer)_machine.BuzzerDevice as Buzzer).EarVolume = o.EarVolume; } Settings = o; @@ -45,8 +49,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return ret; } - - public class ZXSpectrumSettings { [DisplayName("Auto-load/stop tape")] @@ -54,13 +56,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DefaultValue(true)] public bool AutoLoadTape { get; set; } - /* - [DisplayName("Stereo Sound")] - [Description("Turn stereo sound on or off")] - [DefaultValue(true)] - public bool StereoSound { get; set; } - */ - [DisplayName("AY-3-8912 Panning Config")] [Description("Set the PSG panning configuration.\nThe chip has 3 audio channels that can be outputed in different configurations")] [DefaultValue(AYChip.AYPanConfig.ABC)] @@ -71,6 +66,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DefaultValue(OSDVerbosity.Medium)] public OSDVerbosity OSDMessageVerbosity { get; set; } + [DisplayName("Tape Loading Volume")] + [Description("The buzzer volume when the tape is playing")] + [DefaultValue(50)] + public int TapeVolume { get; set; } + + [DisplayName("Ear (buzzer output) Volume")] + [Description("The buzzer volume when sound is being generated by the spectrum")] + [DefaultValue(100)] + public int EarVolume { get; set; } + + [DisplayName("AY-3-8912 Volume")] + [Description("The AY chip volume")] + [DefaultValue(100)] + public int AYVolume { get; set; } + public ZXSpectrumSettings Clone() { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 7f72bf4b4e..fab7424edf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -106,6 +106,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) { ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; + _machine.AYDevice.Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYVolume; + } + + if (_machine.BuzzerDevice != null) + { + ((Buzzer)_machine.BuzzerDevice as Buzzer).TapeVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; + ((Buzzer)_machine.BuzzerDevice as Buzzer).EarVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; } From c2d3a42a69a2759b109cb153ee8d6972d61a36ee Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 21 Mar 2018 14:23:41 +0000 Subject: [PATCH 118/339] ZXHawk: Fixed auto-tape traps (now working properly for all games tested so far) --- .../Hardware/Datacorder/DatacorderDevice.cs | 27 ++++++++++- .../Machine/SpectrumBase.Memory.cs | 10 ++-- .../SinclairSpectrum/Machine/SpectrumBase.cs | 17 +------ .../Media/Tape/TapSerializer.cs | 3 ++ .../Media/Tape/TapeDataBlock.cs | 11 +++++ .../Media/Tape/TzxSerializer.cs | 46 +++++++++++++++++++ 6 files changed, 91 insertions(+), 23 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index cd7d7a2cde..1f2a5f8d89 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -691,7 +691,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _monitorCount++; - if (_monitorCount >= 16 && _cpu.RegPC == 1523 && _machine.Spectrum.Settings.AutoLoadTape) + if (_monitorCount >= 16 && _machine.Spectrum.Settings.AutoLoadTape) { if (!_tapeIsPlaying) { @@ -714,7 +714,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void AutoStopTape() { - if (_tapeIsPlaying) + if (!_tapeIsPlaying) return; if (!_machine.Spectrum.Settings.AutoLoadTape) @@ -749,6 +749,29 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //Stop(); //_machine.Spectrum.OSD_TapeStoppedAuto(); } + + // number of t-states since last IN operation + long diff = _machine.CPU.TotalExecutedCycles - _lastINCycle; + + // pause in ms at the end of the current block + int blockPause = DataBlocks[_currentDataBlockIndex].PauseInMS; + + // timeout in t-states (equiv. to blockpause) + int timeout = ((_machine.ULADevice.FrameLength * 50) / 1000) * blockPause; + + // dont use autostop detection if block has no pause at the end + if (timeout == 0) + return; + + if (diff >= timeout * 2) + { + // There have been no attempted tape reads by the CPU within the double timeout period + // Autostop the tape + AutoStopTape(); + _lastCycle = _cpu.TotalExecutedCycles; + + MonitorReset(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index a23c2796ff..5ec3fec944 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -188,21 +188,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // THE 'ERROR' RESTART if (addr == 8) { - TapeDevice?.AutoStopTape(); + //TapeDevice?.AutoStopTape(); return; } // THE 'ED-ERROR' SUBROUTINE if (addr == 4223) { - TapeDevice?.AutoStopTape(); + //TapeDevice?.AutoStopTape(); return; } // THE 'ERROR-2' ROUTINE if (addr == 83) { - TapeDevice?.AutoStopTape(); + //TapeDevice?.AutoStopTape(); return; } @@ -218,14 +218,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // THE 'LD-BYTES' SUBROUTINE if (addr == 1366) { - TapeDevice?.AutoStartTape(); + //TapeDevice?.AutoStartTape(); return; } // THE 'LD-EDGE-2' AND 'LD-EDGE-1' SUBROUTINES if (addr == 1507) { - TapeDevice?.AutoStartTape(); + //TapeDevice?.AutoStartTape(); return; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index aa5548f645..a1e44356d1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -154,22 +154,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.CheckForInterrupt(CurrentFrameCycle); // run a single CPU instruction - CPU.ExecuteOne(); - - // update AY - int ayCnt = 0; - if (_renderSound) - { - if (AYDevice != null) - { - //AYDevice.UpdateSound(CurrentFrameCycle); - if (ayCnt > 10) - { - //AYDevice.UpdateSound(CurrentFrameCycle); - ayCnt = 0; - } - } - } + CPU.ExecuteOne(); } // we have reached the end of a frame diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs index 01e9bee86c..d0db311d27 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs @@ -288,6 +288,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int actualPause = PAUSE_MS * 1000; dataPeriods.Add(actualPause); + // default pause for tap files + tdb.PauseInMS = 1000; + // add to the tapedatablock object tdb.DataPeriods = dataPeriods; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index f714d21385..ee1a6bc4d8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -126,6 +126,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum set { _command = value; } } + /// + /// The defined post-block pause + /// + private int _pauseInMS; + public int PauseInMS + { + get { return _pauseInMS; } + set { _pauseInMS = value; } + } + + /// /// Returns the data periods as an array /// (primarily to aid in bizhawk state serialization) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index 28a761cd3d..8b3bc9e1ec 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -270,6 +270,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int pauseLen = GetWordValue(data, _position); if (pauseLen == 0) pauseLen = 1000; + + t.PauseInMS = pauseLen; + int blockLen = GetWordValue(data, _position + 2); _position += 4; @@ -331,6 +334,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var t2 = DecodeDataBlock(t, tmp, DataBlockType.Turbo, pause, pilotTL, pilotPL, sync1P, sync2P, bit0P, bit1P, bitinbyte); + t.PauseInMS = pause; + // add the block _datacorder.DataBlocks.Add(t2); @@ -355,6 +360,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.BlockID = 0x12; t.BlockDescription = BlockType.Pure_Tone; t.DataPeriods = new List(); + t.PauseInMS = 0; // get values int pulseLength = GetWordValue(data, _position); @@ -392,6 +398,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.BlockDescription = BlockType.Pulse_Sequence; t.DataPeriods = new List(); + t.PauseInMS = 0; + // get pulse count int pulseCount = data[_position]; t.AddMetaData(BlockDescriptorTitle.Pulse_Count, pulseCount.ToString()); @@ -449,6 +457,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var t2 = DecodeDataBlock(t, tmp, DataBlockType.Pure, pause, pilotTL, pilotPL, sync1P, sync2P, bit0P, bit1P, bitinbyte); + t.PauseInMS = pause; + // add the block _datacorder.DataBlocks.Add(t2); @@ -549,6 +559,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { t.DataPeriods.Add(3500 * pauseAfterBlock); } + + t.PauseInMS = pauseAfterBlock; // increment position _position++; @@ -673,6 +685,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //t.BlockDescription = "[STOP THE TAPE]"; } + t.PauseInMS = pauseDuration; + if (pauseDuration == 0) { // issue stop the tape command @@ -722,11 +736,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //t.BlockDescription = "[GROUP: " + name + "]"; t.Command = TapeCommand.BEGIN_GROUP; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); // advance to next block _position += nameLength; + + ; } #endregion @@ -742,6 +760,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.BlockDescription = BlockType.Group_End; t.Command = TapeCommand.END_GROUP; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); } @@ -787,6 +807,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //t.BlockDescription = "[JUMP BLOCK - " + result +"]"; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -824,6 +846,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // update description //t.BlockDescription = "[LOOP START - " + numberOfRepetitions + " times]"; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -899,6 +923,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.BlockDescription = BlockType.Call_Sequence; int blockSize = 2 + 2 * GetWordValue(data, _position); + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -921,6 +947,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.BlockID = 0x27; t.DataPeriods = new List(); t.BlockDescription = BlockType.Return_From_Sequence; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -956,6 +984,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int blockSize = 2 + GetWordValue(data, _position); + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -982,6 +1012,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int blockSize = 4 + GetWordValue(data, _position); + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -1005,6 +1037,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.DataPeriods = new List(); t.BlockDescription = BlockType.Set_Signal_Level; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -1037,6 +1071,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string desc = Encoding.ASCII.GetString(data, _position, textLen); + t.PauseInMS = 0; + //t.BlockDescription = "[" + desc + "]"; // add to tape @@ -1081,6 +1117,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //t.BlockDescription = "[MESSAGE: " + desc + "]"; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); @@ -1186,6 +1224,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string val = Encoding.ASCII.GetString(data, _position, strLen); //t.BlockDescription += val + " \n"; + t.PauseInMS = 0; + // advance to next string block _position += strLen; } @@ -1232,6 +1272,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.DataPeriods = new List(); t.BlockDescription = BlockType.Hardware_Type; + t.PauseInMS = 0; + _position += 2; int blockLen = GetWordValue(data, 0); @@ -1259,6 +1301,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.DataPeriods = new List(); t.BlockDescription = BlockType.Custom_Info_Block; + t.PauseInMS = 0; + string info = Encoding.ASCII.GetString(data, _position, 0x10); //t.BlockDescription = "[CUSTOM INFO: " + info + "]"; _position += 0x10; @@ -1292,6 +1336,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.DataPeriods = new List(); t.BlockDescription = BlockType.Glue_Block; + t.PauseInMS = 0; + // add to tape _datacorder.DataBlocks.Add(t); From 0ae191025912bad5a5d678476d7fa698658031e0 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 21 Mar 2018 15:02:12 +0000 Subject: [PATCH 119/339] ZXHawk: Tweaked some of the AY-3-8912 volumes --- .../SinclairSpectrum/Hardware/SoundOuput/AYChip.cs | 11 +++++++---- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs index bd1597180d..d2d015ff53 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs @@ -145,8 +145,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } set { - value = Math.Max(0, value); - value = Math.Max(100, value); + //value = Math.Max(0, value); + //value = Math.Max(100, value); if (_volume == value) { return; @@ -503,7 +503,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private static List PanTabs = new List { // MONO - new uint[] { 66,66, 66,66, 66,66 }, + new uint[] { 50,50, 50,50, 50,50 }, // ABC new uint[] { 100,10, 66,66, 10,100 }, // ACB @@ -553,7 +553,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// private void UpdateVolume() { - var vol = (ulong)0xFFFF * (ulong)_volume / 100UL; + var vol = ((ulong)0xFFFF * (ulong)_volume / 100UL) - 20000 ; _volumeTables = new uint[6][]; // parent array @@ -793,6 +793,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { ser.Sync("volTable" + i, ref _volumeTables[i], false); } + + if (ser.IsReader) + _volume = _machine.Spectrum.Settings.AYVolume; ser.EndSection(); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index df8146fb7e..5f721f233d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -73,12 +73,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DisplayName("Ear (buzzer output) Volume")] [Description("The buzzer volume when sound is being generated by the spectrum")] - [DefaultValue(100)] + [DefaultValue(90)] public int EarVolume { get; set; } [DisplayName("AY-3-8912 Volume")] [Description("The AY chip volume")] - [DefaultValue(100)] + [DefaultValue(75)] public int AYVolume { get; set; } From 76904df015e6b4e7692b1043050173770e69aef6 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 21 Mar 2018 16:48:14 +0000 Subject: [PATCH 120/339] ZXHawk: Better detect maskable interrupt used as a tape trap --- .../Hardware/Datacorder/DatacorderDevice.cs | 10 +++++++++- .../SinclairSpectrum/Machine/SpectrumBase.Memory.cs | 10 ++++++++++ .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 4 +++- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 1f2a5f8d89..750690462f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -193,6 +193,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_currentDataBlockIndex >= _dataBlocks.Count) { // end of tape reached. Rewind to beginning + AutoStopTape(); RTZ(); return; } @@ -736,6 +737,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine.Spectrum.OSD_TapePlayingAuto(); } + public int MaskableInterruptCount = 0; + private void MonitorFrame() { if (_tapeIsPlaying && _machine.Spectrum.Settings.AutoLoadTape) @@ -770,7 +773,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AutoStopTape(); _lastCycle = _cpu.TotalExecutedCycles; - MonitorReset(); + //MonitorReset(); } } } @@ -807,6 +810,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result &= ~TAPE_BIT; } + if (!TapeIsPlaying) + { + MonitorRead(); + } + MonitorRead(); /* diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 5ec3fec944..436e23e558 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -207,8 +207,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // THE 'MASKABLE INTERRUPT' ROUTINE + // This is sometimes used when the tape is to be stopped + // if (addr == 56) { + TapeDevice.MaskableInterruptCount++; + + if (TapeDevice.MaskableInterruptCount > 20) + { + TapeDevice.MaskableInterruptCount = 0; + TapeDevice.AutoStopTape(); + } + //TapeDevice?.AutoStopTape(); 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 bef6b97317..5f7958f4d9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -48,7 +48,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (divisor) { - case 0: return ROM0[index]; + case 0: + TestForTapeTraps(addr % 0x4000); + return ROM0[index]; case 1: return RAM0[index]; case 2: return RAM1[index]; case 3: return RAM2[index]; From be253911c64571063615263a231ad107cd4dc1f7 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 21 Mar 2018 18:15:51 -0400 Subject: [PATCH 121/339] PCE: Increase read access time even more to fix Gate of Thunder Loading fixes #70 --- BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs index bf2d23f311..1a8472594f 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs @@ -412,7 +412,9 @@ namespace BizHawk.Emulation.Cores.PCEngine // figure out proper read delay later // 10000 fixes Mugen Senshi Valis, which runs code in a timed loop, expecting a certain number of VBlanks // to happen before reading is complete - DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 10000; + // 175000 fixes 4 in 1 CD, loading Gate of Thunder + // which expects a certain number of timer interrupts to happen before loading is complete + DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 175000; pce.CDAudio.Stop(); } From bdbfc3fb4ecaf8e749d6c7c0bc871bf1ded4682f Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 22 Mar 2018 08:58:31 +0000 Subject: [PATCH 122/339] ZXHawk: Finally (hopefully) resolved all tape trap issues --- .../Hardware/Datacorder/DatacorderDevice.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 750690462f..93b91348ac 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -700,7 +700,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _machine.Spectrum.OSD_TapePlayingAuto(); } - _monitorTimeOut = 250; + _monitorTimeOut = 50; } } else @@ -747,17 +747,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_monitorTimeOut < 0) { - // does not work properly - disabled for now (handled elsewhere) - - //Stop(); - //_machine.Spectrum.OSD_TapeStoppedAuto(); + AutoStopTape(); + return; } + // fallback in case usual monitor detection methods do not work + // number of t-states since last IN operation long diff = _machine.CPU.TotalExecutedCycles - _lastINCycle; + // get current datablock + var block = DataBlocks[_currentDataBlockIndex]; + // pause in ms at the end of the current block - int blockPause = DataBlocks[_currentDataBlockIndex].PauseInMS; + int blockPause = block.PauseInMS; // timeout in t-states (equiv. to blockpause) int timeout = ((_machine.ULADevice.FrameLength * 50) / 1000) * blockPause; @@ -772,8 +775,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Autostop the tape AutoStopTape(); _lastCycle = _cpu.TotalExecutedCycles; - - //MonitorReset(); } } } From 2be76a405a19d459504d0602e6553b458fc068ce Mon Sep 17 00:00:00 2001 From: Asnvior Date: Thu, 22 Mar 2018 14:41:21 +0000 Subject: [PATCH 123/339] Fix: rom status icon --- BizHawk.Client.EmuHawk/MainForm.cs | 50 +++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 3f7a9ea1df..bb0e9a20af 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -1559,6 +1559,11 @@ namespace BizHawk.Client.EmuHawk if (!string.IsNullOrEmpty(Emulator.CoreComm.RomStatusAnnotation)) { annotation = Emulator.CoreComm.RomStatusAnnotation; + + if (annotation == "Multi-disk bundler") + { + DumpStatusButton.Image = Properties.Resources.RetroQuestion; + } } DumpStatusButton.ToolTipText = annotation; @@ -3620,6 +3625,38 @@ namespace BizHawk.Client.EmuHawk CoreFileProvider.SyncCoreCommInputSignals(nextComm); InputManager.SyncControls(); + if (Path.GetExtension(loaderName).ToLower() == ".xml") + { + // this is a multi-disk bundler file + // determine the xml assets and create RomStatusDetails for all of them + var xmlGame = XmlGame.Create(new HawkFile(loaderName.Replace("*OpenRom*", ""))); + + StringWriter xSw = new StringWriter(); + + for (int xg = 0; xg < xmlGame.Assets.Count; xg++) + { + var ext = Path.GetExtension(xmlGame.AssetFullPaths[xg]).ToLower(); + + if (ext == ".cue" || ext == ".ccd" || ext == ".toc" || ext == ".mds") + { + xSw.WriteLine(Path.GetFileNameWithoutExtension(xmlGame.Assets[xg].Key)); + xSw.WriteLine("SHA1:N/A"); + xSw.WriteLine("MD5:N/A"); + xSw.WriteLine(); + } + else + { + xSw.WriteLine(xmlGame.Assets[xg].Key); + xSw.WriteLine("SHA1:" + xmlGame.Assets[xg].Value.HashSHA1()); + xSw.WriteLine("MD5:" + xmlGame.Assets[xg].Value.HashMD5()); + xSw.WriteLine(); + } + } + + Emulator.CoreComm.RomStatusDetails = xSw.ToString(); + Emulator.CoreComm.RomStatusAnnotation = "Multi-disk bundler"; + } + if (Emulator is TI83 && Global.Config.TI83autoloadKeyPad) { GlobalWin.Tools.Load(); @@ -3649,10 +3686,15 @@ namespace BizHawk.Client.EmuHawk } } - if (Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null) - { - Emulator.CoreComm.RomStatusDetails = $"{loader.Game.Name}\r\nSHA1:{loader.Rom.RomData.HashSHA1()}\r\nMD5:{loader.Rom.RomData.HashMD5()}\r\n"; - } + if (Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null) + { + Emulator.CoreComm.RomStatusDetails = $"{loader.Game.Name}\r\nSHA1:{loader.Rom.RomData.HashSHA1()}\r\nMD5:{loader.Rom.RomData.HashMD5()}\r\n"; + } + else if (Emulator.CoreComm.RomStatusDetails == null && loader.Rom == null) + { + // single disc game + Emulator.CoreComm.RomStatusDetails = $"{loader.Game.Name}\r\nSHA1:N/A\r\nMD5:N/A\r\n"; + } if (Emulator.HasBoardInfo()) { From da71adddddcca3f5dd73744408a7bda39dd06b0c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 22 Mar 2018 17:06:52 +0000 Subject: [PATCH 124/339] ZXHawk: Fixed TZX block ID 0x33 deserialization (was causing some games to not be parsed properly) --- .../SinclairSpectrum/Media/Tape/TzxSerializer.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index 8b3bc9e1ec..91b5037426 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -1274,14 +1274,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.PauseInMS = 0; - _position += 2; - int blockLen = GetWordValue(data, 0); + // first byte contains number of HWINFOs + int infos = data[_position]; + + _position += 1; + + // now starts the HW infos (each block 3 bytes) + for (int i = 0; i < infos; i++) + { + _position += 3; + } // add to tape _datacorder.DataBlocks.Add(t); - - // advance to next block - _position += blockLen; } #endregion From 6e72e47c1c0b1f69b7c5d07c4275e9eaade32f96 Mon Sep 17 00:00:00 2001 From: zeromus Date: Thu, 22 Mar 2018 14:55:29 -0500 Subject: [PATCH 125/339] do a thing better than crash when configfiles are corrupt. fixes #1063 --- BizHawk.Client.EmuHawk/Program.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/BizHawk.Client.EmuHawk/Program.cs b/BizHawk.Client.EmuHawk/Program.cs index a6a5e5a538..47d97f0f1b 100644 --- a/BizHawk.Client.EmuHawk/Program.cs +++ b/BizHawk.Client.EmuHawk/Program.cs @@ -121,8 +121,19 @@ namespace BizHawk.Client.EmuHawk HawkFile.ArchiveHandlerFactory = new SevenZipSharpArchiveHandler(); string iniPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "config.ini"); - Global.Config = ConfigService.Load(iniPath); + + try + { + Global.Config = ConfigService.Load(iniPath); + } catch (Exception e) { + new ExceptionBox(e).ShowDialog(); + new ExceptionBox("Since your config file is corrupted, we're going to recreate it. Back it up before proceeding if you want to investigate further.").ShowDialog(); + File.Delete(iniPath); + Global.Config = ConfigService.Load(iniPath); + } + Global.Config.ResolveDefaults(); + BizHawk.Client.Common.StringLogUtil.DefaultToDisk = Global.Config.MoviesOnDisk; BizHawk.Client.Common.StringLogUtil.DefaultToAWE = Global.Config.MoviesInAWE; From dc38794dad9ebc920844cd2e497266c2170aadba Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 23 Mar 2018 19:49:54 -0400 Subject: [PATCH 126/339] GBHawk: Pass some new tests and fix some errors --- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 11 +++++------ .../Consoles/Nintendo/GBHawk/PPU.cs | 10 +++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 1caa711336..f766b1d246 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -221,12 +221,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk in_vblank = true; // we start off in vblank since the LCD is off in_vblank_old = true; - // Start off with RAM all 0xFF (the game 'X' (proto) expects this) - for (int i = 0; i < RAM.Length; i++) - { - RAM[i] = 0xFF; - } - Register_Reset(); timer.Reset(); ppu.Reset(); @@ -333,6 +327,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 5: cart_RAM = new byte[0x10000]; break; + case 0: + Console.WriteLine("Mapper Number indicates Battery Backed RAM but none present."); + Console.WriteLine("Disabling Battery Setting."); + has_bat = false; + break; } // Sachen maper not known to have RAM diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 6dc0c08613..b3f61be4bf 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -108,7 +108,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF43: ret = scroll_x; break; // SCX case 0xFF44: ret = LY; break; // LY case 0xFF45: ret = LYC; break; // LYC - case 0xFF46: ret = 0xFF; break; // DMA (not readable?) /*ret = DMA_addr; */ + case 0xFF46: ret = DMA_addr; break; // DMA case 0xFF47: ret = BGP; break; // BGP case 0xFF48: ret = obj_pal_0; break; // OBP0 case 0xFF49: ret = obj_pal_1; break; // OBP1 @@ -203,7 +203,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { // the cpu can't access memory during this time, but we still need the ppu to be able to. DMA_start = false; - DMA_byte = Core.ReadMemory((ushort)((DMA_addr << 8) + DMA_inc)); + // Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses + // So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF) + byte DMA_actual = DMA_addr; + if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; } + DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc)); DMA_start = true; } else if ((DMA_clock % 4) == 3) @@ -1075,7 +1079,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk scroll_x = 0; LY = 0; LYC = 0; - DMA_addr = 0; + DMA_addr = 0xFF; BGP = 0xFF; obj_pal_0 = 0xFF; obj_pal_1 = 0xFF; From ac66b258ba8bfc0f2a52b3e29cc926f112bc5caa Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 24 Mar 2018 09:11:23 -0400 Subject: [PATCH 127/339] GBHawk: Reorganize for pending GBC Support --- BizHawk.Client.Common/RomLoader.cs | 11 +- .../BizHawk.Emulation.Cores.csproj | 34 +- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 882 +++++++++++++++++ .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 16 +- .../Nintendo/GBHawk/GBHawk.ISettable.cs | 12 + .../Consoles/Nintendo/GBHawk/GBHawk.cs | 34 +- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 882 +++++++++++++++++ .../Consoles/Nintendo/GBHawk/PPU.cs | 898 +----------------- 8 files changed, 1876 insertions(+), 893 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 9458897687..84a91bbf7d 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -980,8 +980,15 @@ namespace BizHawk.Client.Common break; case "GBC": if (!Global.Config.GB_AsSGB) - { - core = CoreInventory.Instance["GBC", "Gambatte"]; + { + if (Global.Config.GB_UseGBHawk) + { + core = CoreInventory.Instance["GBC", "GBHawk"]; + } + else + { + core = CoreInventory.Instance["GBC", "Gambatte"]; + } } else { diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 0faaa3c4f6..7700b36452 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -645,13 +645,27 @@ - - - - - - - + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + @@ -671,6 +685,12 @@ + + PPU.cs + + + PPU.cs + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs new file mode 100644 index 0000000000..d308d004f3 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -0,0 +1,882 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class GBC_PPU : PPU + { + public override byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: ret = DMA_addr; break; // DMA + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX + } + + return ret; + } + + public override void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF40: // LCDC + if (LCDC.Bit(7) && !value.Bit(7)) + { + VRAM_access_read = true; + VRAM_access_write = true; + OAM_access_read = true; + OAM_access_write = true; + } + + if (!LCDC.Bit(7) && value.Bit(7)) + { + // don't draw for one frame after turning on + blank_frame = true; + } + + LCDC = value; + break; + case 0xFF41: // STAT + // writing to STAT during mode 0 or 2 causes a STAT IRQ + if (LCDC.Bit(7)) + { + if (((STAT & 3) == 0) || ((STAT & 3) == 1)) + { + LYC_INT = true; + } + } + STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); + break; + case 0xFF42: // SCY + scroll_y = value; + break; + case 0xFF43: // SCX + scroll_x = value; + // calculate the column number of the tile to start with + x_tile = (int)Math.Floor((float)(scroll_x) / 8); + break; + case 0xFF44: // LY + LY = 0; /*reset*/ + break; + case 0xFF45: // LYC + LYC = value; + if (LY != LYC) { STAT &= 0xFB; } + break; + case 0xFF46: // DMA + DMA_addr = value; + DMA_start = true; + DMA_OAM_access = true; + DMA_clock = 0; + DMA_inc = 0; + break; + case 0xFF47: // BGP + BGP = value; + break; + case 0xFF48: // OBP0 + obj_pal_0 = value; + break; + case 0xFF49: // OBP1 + obj_pal_1 = value; + break; + case 0xFF4A: // WY + window_y = value; + break; + case 0xFF4B: // WX + window_x = value; + break; + } + } + + public override void tick() + { + // the ppu only does anything if it is turned on via bit 7 of LCDC + if (LCDC.Bit(7)) + { + // start the next scanline + if (cycle == 456) + { + // scanline callback + if ((LY + LY_inc) == Core._scanlineCallbackLine) + { + if (Core._scanlineCallback != null) + { + Core.GetGPU(); + Core._scanlineCallback(LCDC); + } + } + + cycle = 0; + LY += LY_inc; + no_scan = false; + + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + if (LY == 0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; + } + + Core.cpu.LY = LY; + + // Automatically restore access to VRAM at this time (force end drawing) + // Who Framed Roger Rabbit seems to run into this. + VRAM_access_write = true; + VRAM_access_read = true; + + if (LY == 144) + { + Core.in_vblank = true; + } + } + + // exit vblank if LCD went from off to on + if (LCD_was_off) + { + //VBL_INT = false; + Core.in_vblank = false; + LCD_was_off = false; + + // we exit vblank into mode 0 for 4 cycles + // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 + STAT &= 0xFC; + + // also the LCD doesn't turn on right away + + // also, the LCD does not enter mode 2 on scanline 0 when first turned on + no_scan = true; + cycle = 8; + } + + // the VBL stat is continuously asserted + if ((LY >= 144)) + { + if (STAT.Bit(4)) + { + if ((cycle >= 4) && (LY == 144)) + { + VBL_INT = true; + } + else if (LY > 144) + { + VBL_INT = true; + } + } + + if ((cycle == 4) && (LY == 144)) { + + HBL_INT = false; + + // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled + STAT &= 0xFC; + STAT |= 0x01; + + if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x01; + } + + if ((LY >= 144) && (cycle == 4)) + { + // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if ((LY == 153) && (cycle == 8)) + { + LY = 0; + LY_inc = 0; + Core.cpu.LY = LY; + } + } + + if (!Core.in_vblank) + { + if (no_scan) + { + // timings are slightly different if we just turned on the LCD + // there is no mode 2 (presumably it missed the trigger) + // mode 3 is very short, probably in some self test mode before turning on? + + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + if (cycle == 84) + { + + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access_read = false; + OAM_access_write = false; + VRAM_access_read = false; + VRAM_access_write = false; + } + + if (cycle == 256) + { + STAT &= 0xFC; + OAM_INT = false; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + else + { + if (cycle < 80) + { + if (cycle == 4) + { + // apparently, writes can make it to OAM one cycle longer then reads + OAM_access_write = false; + + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } + + HBL_INT = false; + } + + // here OAM scanning is performed + OAM_scan(cycle); + } + else if (cycle >= 80 && LY < 144) + { + + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + OAM_access_write = false; + VRAM_access_write = false; + } + + // render the screen and handle hblank + render(cycle - 80); + } + } + } + + if ((LY_inc == 0)) + { + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + // Special case of LY = LYC + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + + // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if (cycle == 92) { OAM_INT = false; } + } + + // here LY=LYC will be asserted + if ((cycle == 4) && (LY != 0)) + { + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + cycle++; + } + else + { + STAT &= 0xF8; + + VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; + + Core.in_vblank = true; + + LCD_was_off = true; + + LY = 0; + Core.cpu.LY = LY; + + cycle = 0; + } + + // assert the STAT IRQ line if the line went from zero to 1 + stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; + + if (stat_line && !stat_line_old) + { + if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x02; + } + + stat_line_old = stat_line; + + // process latch delays + //latch_delay(); + } + + // might be needed, not sure yet + public override void latch_delay() + { + //BGP_l = BGP; + } + + public override void render(int render_cycle) + { + // we are now in STAT mode 3 + // NOTE: presumably the first necessary sprite is fetched at sprite evaulation + // i.e. just keeping track of the lowest x-value sprite + if (render_cycle == 0) + { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + + // window X is latched for the scanline, mid-line changes have no effect + window_x_latch = window_x; + + OAM_scan_index = 0; + read_case = 0; + internal_cycle = 0; + pre_render = true; + tile_inc = 0; + pixel_counter = -8; + sl_use_index = 0; + fetch_sprite = false; + fetch_sprite_01 = false; + fetch_sprite_4 = false; + going_to_fetch = false; + no_sprites = false; + evaled_sprites = 0; + + window_pre_render = false; + if (window_started && LCDC.Bit(5)) + { + window_y_tile_inc++; + if (window_y_tile_inc==8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + window_started = false; + + if (SL_sprites_index == 0) { no_sprites = true; } + // it is much easier to process sprites if we order them according to the rules of sprite priority first + if (!no_sprites) { reorder_and_assemble_sprites(); } + + } + + // before anything else, we have to check if windowing is in effect + if (LCDC.Bit(5) && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) + { + /* + Console.Write(LY); + Console.Write(" "); + Console.Write(cycle); + Console.Write(" "); + Console.Write(window_y_tile_inc); + Console.Write(" "); + Console.Write(window_x_latch); + Console.Write(" "); + Console.WriteLine(pixel_counter); + */ + if (pixel_counter == 0 && window_x_latch <= 7) + { + // if the window starts at zero, we still do the first access to the BG + // but then restart all over again at the window + window_pre_render = true; + } + else + { + // otherwise, just restart the whole process as if starting BG again + window_pre_render = true; + read_case = 4; + } + window_counter = 0; + + window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); + + window_tile_inc = 0; + window_started = true; + } + + if (!pre_render && !fetch_sprite && !window_pre_render) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + + int ref_pixel = pixel; + if (LCDC.Bit(0)) + { + pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + pixel = 0; + } + + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int s_pixel = 0; + int sprite_attr = 0; + + if (sprite_present_list[pixel_counter] == 1) + { + have_sprite = true; + s_pixel = sprite_pixel_list[pixel_counter]; + sprite_attr = sprite_attr_list[pixel_counter]; + } + + if (have_sprite) + { + bool use_sprite = false; + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + use_sprite = true; + } + else if (ref_pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + } + + if (use_sprite) + { + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel]; + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + hbl_countdown = 7; + } + } + else if ((render_counter >= render_offset) && (pixel_counter < 0)) + { + pixel_counter++; + } + render_counter++; + } + + if (!fetch_sprite) + { + if (!pre_render) + { + // before we go on to read case 3, we need to know if we stall there or not + // Gekkio's tests show that if sprites are at position 0 or 1 (mod 8) + // then it takes an extra cycle (1 or 2 more t-states) to process them + + if (!no_sprites && (pixel_counter < 160)) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + going_to_fetch = true; + fetch_sprite = true; + + if ((SL_sprites[i * 4 + 1] % 8) < 2) + { + fetch_sprite_01 = true; + } + if ((SL_sprites[i * 4 + 1] % 8) > 3) + { + fetch_sprite_4 = true; + } + } + } + } + } + + switch (read_case) + { + case 0: // read a background tile + if ((internal_cycle % 2) == 0) + { + // calculate the row number of the tiles to be fetched + y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; + + temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; + tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } + else + { + read_case = 1; + if (!pre_render) + { + tile_inc++; + if (window_pre_render) + { + read_case = 4; + } + } + } + break; + + case 1: // read from tile graphics (0) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + + } + else + { + read_case = 2; + } + break; + + case 2: // read from tile graphics (1) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (pre_render) + { + // here we set up rendering + pre_render = false; + render_offset = scroll_x % 8; + render_counter = 0; + latch_counter = 0; + read_case = 0; + } + else + { + read_case = 3; + } + } + break; + + case 3: // read from sprite data + if ((internal_cycle % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 0; + latch_new_data = true; + } + break; + + case 4: // read from window data + if ((window_counter % 2) == 0) + { + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } + else + { + if (!window_pre_render) + { + window_tile_inc++; + } + read_case = 5; + } + window_counter++; + break; + + case 5: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + + if (LCDC.Bit(4)) + { + + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + } + else + { + read_case = 6; + } + window_counter++; + break; + + case 6: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (window_pre_render) + { + // here we set up rendering + window_pre_render = false; + render_offset = 0; + render_counter = 0; + latch_counter = 0; + read_case = 4; + } + else + { + read_case = 7; + + } + } + window_counter++; + break; + + case 7: // read from sprite data + if ((window_counter % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 4; + latch_new_data = true; + } + window_counter++; + break; + + case 8: // done reading, we are now in phase 0 + + pre_render = true; + + // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here + if (hbl_countdown > 0) + { + hbl_countdown--; + if (hbl_countdown == 0) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + + break; + } + internal_cycle++; + + if (latch_new_data) + { + latch_new_data = false; + tile_data_latch[0] = tile_data[0]; + tile_data_latch[1] = tile_data[1]; + } + } + + // every in range sprite takes 6 cycles to process + // sprites located at x=0 still take 6 cycles to process even though they don't appear on screen + // sprites above x=168 do not take any cycles to process however + if (fetch_sprite) + { + if (going_to_fetch) + { + going_to_fetch = false; + sprite_fetch_counter = 0; + + if (fetch_sprite_01) + { + sprite_fetch_counter += 2; + fetch_sprite_01 = false; + } + + if (fetch_sprite_4) + { + sprite_fetch_counter -= 2; + fetch_sprite_4 = false; + } + + int last_eval = 0; + + // at this time it is unknown what each cycle does, but we only need to accurately keep track of cycles + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + sprite_fetch_counter += 6; + evaled_sprites |= (1 << i); + last_eval = SL_sprites[i * 4 + 1]; + } + } + + // if we didn't evaluate all the sprites immediately, 2 more cycles are added to restart it + if (evaled_sprites != (Math.Pow(2,SL_sprites_index) - 1)) + { + if ((last_eval % 8) == 0) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 1) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 2) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 3) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 4) { sprite_fetch_counter += 3; } + else { sprite_fetch_counter += 2; } + } + } + else + { + sprite_fetch_counter--; + if (sprite_fetch_counter == 0) + { + fetch_sprite = false; + } + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index bc094d10b3..39096e025f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -50,8 +50,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (controller.IsPressed("Power")) { - // it seems that theMachine.Reset() doesn't clear ram, etc - // this should leave hsram intact but clear most other things HardReset(); } @@ -114,15 +112,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk while (!vblank_rise) { + // These things do not change speed in GBC double spped mode audio.tick(); - timer.tick_1(); ppu.tick(); - serialport.serial_transfer_tick(); - if (Use_RTC) { mapper.RTC_Tick(); } + // These things all tick twice as fast in GBC double speed mode + ppu.DMA_tick(); + timer.tick_1(); + serialport.serial_transfer_tick(); cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); - timer.tick_2(); if (in_vblank && !in_vblank_old) @@ -139,11 +138,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk vblank_rise = false; } - public void RunCPUCycle() - { - - } - public void GetControllerState(IController controller) { InputCallbacks.Call(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 56cea73ae2..c43baa6d8c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -75,6 +75,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } + public enum ConsoleModeType + { + Auto, + GB, + GBC + } + + [DisplayName("Console Mode")] + [Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")] + [DefaultValue(ConsoleModeType.Auto)] + public ConsoleModeType ConsoleMode { get; set; } + [DisplayName("RTC Initial Time")] [Description("Set the initial RTC time in terms of elapsed seconds.")] [DefaultValue(0)] diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index f766b1d246..7b09b8a6b5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public Audio audio; public SerialPort serialport; - [CoreConstructor("GB")] + [CoreConstructor("GB", "GBC")] public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) { var ser = new BasicServiceProvider(this); @@ -77,7 +77,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk DummyReadMemory = ReadMemory, OnExecFetch = ExecFetch }; - ppu = new PPU(); + timer = new Timer(); audio = new Audio(); serialport = new SerialPort(); @@ -88,13 +88,39 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _syncSettings = (GBSyncSettings)syncSettings ?? new GBSyncSettings(); _controllerDeck = new GBHawkControllerDeck(_syncSettings.Port1); - byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); + byte[] Bios = null; + + // Load up a BIOS and initialize the correct PPU + if (_syncSettings.ConsoleMode == GBSyncSettings.ConsoleModeType.Auto) + { + if (game.System == "GB") + { + Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GB_PPU(); + } + else + { + Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GBC_PPU(); + } + + } + else if (_syncSettings.ConsoleMode == GBSyncSettings.ConsoleModeType.GB) + { + Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GB_PPU(); + } + else + { + Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GBC_PPU(); + } if (Bios == null) { throw new MissingFirmwareException("Missing Gamboy Bios"); } - + _bios = Bios; Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs new file mode 100644 index 0000000000..e0fad7a5bd --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -0,0 +1,882 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class GB_PPU : PPU + { + public override byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: ret = DMA_addr; break; // DMA + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX + } + + return ret; + } + + public override void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF40: // LCDC + if (LCDC.Bit(7) && !value.Bit(7)) + { + VRAM_access_read = true; + VRAM_access_write = true; + OAM_access_read = true; + OAM_access_write = true; + } + + if (!LCDC.Bit(7) && value.Bit(7)) + { + // don't draw for one frame after turning on + blank_frame = true; + } + + LCDC = value; + break; + case 0xFF41: // STAT + // writing to STAT during mode 0 or 2 causes a STAT IRQ + if (LCDC.Bit(7)) + { + if (((STAT & 3) == 0) || ((STAT & 3) == 1)) + { + LYC_INT = true; + } + } + STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); + break; + case 0xFF42: // SCY + scroll_y = value; + break; + case 0xFF43: // SCX + scroll_x = value; + // calculate the column number of the tile to start with + x_tile = (int)Math.Floor((float)(scroll_x) / 8); + break; + case 0xFF44: // LY + LY = 0; /*reset*/ + break; + case 0xFF45: // LYC + LYC = value; + if (LY != LYC) { STAT &= 0xFB; } + break; + case 0xFF46: // DMA + DMA_addr = value; + DMA_start = true; + DMA_OAM_access = true; + DMA_clock = 0; + DMA_inc = 0; + break; + case 0xFF47: // BGP + BGP = value; + break; + case 0xFF48: // OBP0 + obj_pal_0 = value; + break; + case 0xFF49: // OBP1 + obj_pal_1 = value; + break; + case 0xFF4A: // WY + window_y = value; + break; + case 0xFF4B: // WX + window_x = value; + break; + } + } + + public override void tick() + { + // the ppu only does anything if it is turned on via bit 7 of LCDC + if (LCDC.Bit(7)) + { + // start the next scanline + if (cycle == 456) + { + // scanline callback + if ((LY + LY_inc) == Core._scanlineCallbackLine) + { + if (Core._scanlineCallback != null) + { + Core.GetGPU(); + Core._scanlineCallback(LCDC); + } + } + + cycle = 0; + LY += LY_inc; + no_scan = false; + + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + if (LY == 0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; + } + + Core.cpu.LY = LY; + + // Automatically restore access to VRAM at this time (force end drawing) + // Who Framed Roger Rabbit seems to run into this. + VRAM_access_write = true; + VRAM_access_read = true; + + if (LY == 144) + { + Core.in_vblank = true; + } + } + + // exit vblank if LCD went from off to on + if (LCD_was_off) + { + //VBL_INT = false; + Core.in_vblank = false; + LCD_was_off = false; + + // we exit vblank into mode 0 for 4 cycles + // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 + STAT &= 0xFC; + + // also the LCD doesn't turn on right away + + // also, the LCD does not enter mode 2 on scanline 0 when first turned on + no_scan = true; + cycle = 8; + } + + // the VBL stat is continuously asserted + if ((LY >= 144)) + { + if (STAT.Bit(4)) + { + if ((cycle >= 4) && (LY == 144)) + { + VBL_INT = true; + } + else if (LY > 144) + { + VBL_INT = true; + } + } + + if ((cycle == 4) && (LY == 144)) { + + HBL_INT = false; + + // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled + STAT &= 0xFC; + STAT |= 0x01; + + if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x01; + } + + if ((LY >= 144) && (cycle == 4)) + { + // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if ((LY == 153) && (cycle == 8)) + { + LY = 0; + LY_inc = 0; + Core.cpu.LY = LY; + } + } + + if (!Core.in_vblank) + { + if (no_scan) + { + // timings are slightly different if we just turned on the LCD + // there is no mode 2 (presumably it missed the trigger) + // mode 3 is very short, probably in some self test mode before turning on? + + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + if (cycle == 84) + { + + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access_read = false; + OAM_access_write = false; + VRAM_access_read = false; + VRAM_access_write = false; + } + + if (cycle == 256) + { + STAT &= 0xFC; + OAM_INT = false; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + else + { + if (cycle < 80) + { + if (cycle == 4) + { + // apparently, writes can make it to OAM one cycle longer then reads + OAM_access_write = false; + + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } + + HBL_INT = false; + } + + // here OAM scanning is performed + OAM_scan(cycle); + } + else if (cycle >= 80 && LY < 144) + { + + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + OAM_access_write = false; + VRAM_access_write = false; + } + + // render the screen and handle hblank + render(cycle - 80); + } + } + } + + if ((LY_inc == 0)) + { + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + // Special case of LY = LYC + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + + // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if (cycle == 92) { OAM_INT = false; } + } + + // here LY=LYC will be asserted + if ((cycle == 4) && (LY != 0)) + { + if (LY == LYC) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + cycle++; + } + else + { + STAT &= 0xF8; + + VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; + + Core.in_vblank = true; + + LCD_was_off = true; + + LY = 0; + Core.cpu.LY = LY; + + cycle = 0; + } + + // assert the STAT IRQ line if the line went from zero to 1 + stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; + + if (stat_line && !stat_line_old) + { + if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x02; + } + + stat_line_old = stat_line; + + // process latch delays + //latch_delay(); + } + + // might be needed, not sure yet + public override void latch_delay() + { + //BGP_l = BGP; + } + + public override void render(int render_cycle) + { + // we are now in STAT mode 3 + // NOTE: presumably the first necessary sprite is fetched at sprite evaulation + // i.e. just keeping track of the lowest x-value sprite + if (render_cycle == 0) + { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + + // window X is latched for the scanline, mid-line changes have no effect + window_x_latch = window_x; + + OAM_scan_index = 0; + read_case = 0; + internal_cycle = 0; + pre_render = true; + tile_inc = 0; + pixel_counter = -8; + sl_use_index = 0; + fetch_sprite = false; + fetch_sprite_01 = false; + fetch_sprite_4 = false; + going_to_fetch = false; + no_sprites = false; + evaled_sprites = 0; + + window_pre_render = false; + if (window_started && LCDC.Bit(5)) + { + window_y_tile_inc++; + if (window_y_tile_inc==8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + window_started = false; + + if (SL_sprites_index == 0) { no_sprites = true; } + // it is much easier to process sprites if we order them according to the rules of sprite priority first + if (!no_sprites) { reorder_and_assemble_sprites(); } + + } + + // before anything else, we have to check if windowing is in effect + if (LCDC.Bit(5) && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) + { + /* + Console.Write(LY); + Console.Write(" "); + Console.Write(cycle); + Console.Write(" "); + Console.Write(window_y_tile_inc); + Console.Write(" "); + Console.Write(window_x_latch); + Console.Write(" "); + Console.WriteLine(pixel_counter); + */ + if (pixel_counter == 0 && window_x_latch <= 7) + { + // if the window starts at zero, we still do the first access to the BG + // but then restart all over again at the window + window_pre_render = true; + } + else + { + // otherwise, just restart the whole process as if starting BG again + window_pre_render = true; + read_case = 4; + } + window_counter = 0; + + window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); + + window_tile_inc = 0; + window_started = true; + } + + if (!pre_render && !fetch_sprite && !window_pre_render) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + + int ref_pixel = pixel; + if (LCDC.Bit(0)) + { + pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + pixel = 0; + } + + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int s_pixel = 0; + int sprite_attr = 0; + + if (sprite_present_list[pixel_counter] == 1) + { + have_sprite = true; + s_pixel = sprite_pixel_list[pixel_counter]; + sprite_attr = sprite_attr_list[pixel_counter]; + } + + if (have_sprite) + { + bool use_sprite = false; + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + use_sprite = true; + } + else if (ref_pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + } + + if (use_sprite) + { + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel]; + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + hbl_countdown = 7; + } + } + else if ((render_counter >= render_offset) && (pixel_counter < 0)) + { + pixel_counter++; + } + render_counter++; + } + + if (!fetch_sprite) + { + if (!pre_render) + { + // before we go on to read case 3, we need to know if we stall there or not + // Gekkio's tests show that if sprites are at position 0 or 1 (mod 8) + // then it takes an extra cycle (1 or 2 more t-states) to process them + + if (!no_sprites && (pixel_counter < 160)) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + going_to_fetch = true; + fetch_sprite = true; + + if ((SL_sprites[i * 4 + 1] % 8) < 2) + { + fetch_sprite_01 = true; + } + if ((SL_sprites[i * 4 + 1] % 8) > 3) + { + fetch_sprite_4 = true; + } + } + } + } + } + + switch (read_case) + { + case 0: // read a background tile + if ((internal_cycle % 2) == 0) + { + // calculate the row number of the tiles to be fetched + y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; + + temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; + tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } + else + { + read_case = 1; + if (!pre_render) + { + tile_inc++; + if (window_pre_render) + { + read_case = 4; + } + } + } + break; + + case 1: // read from tile graphics (0) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + + } + else + { + read_case = 2; + } + break; + + case 2: // read from tile graphics (1) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (pre_render) + { + // here we set up rendering + pre_render = false; + render_offset = scroll_x % 8; + render_counter = 0; + latch_counter = 0; + read_case = 0; + } + else + { + read_case = 3; + } + } + break; + + case 3: // read from sprite data + if ((internal_cycle % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 0; + latch_new_data = true; + } + break; + + case 4: // read from window data + if ((window_counter % 2) == 0) + { + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + } + else + { + if (!window_pre_render) + { + window_tile_inc++; + } + read_case = 5; + } + window_counter++; + break; + + case 5: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + + if (LCDC.Bit(4)) + { + + tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + + tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + } + else + { + read_case = 6; + } + window_counter++; + break; + + case 6: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (window_pre_render) + { + // here we set up rendering + window_pre_render = false; + render_offset = 0; + render_counter = 0; + latch_counter = 0; + read_case = 4; + } + else + { + read_case = 7; + + } + } + window_counter++; + break; + + case 7: // read from sprite data + if ((window_counter % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 4; + latch_new_data = true; + } + window_counter++; + break; + + case 8: // done reading, we are now in phase 0 + + pre_render = true; + + // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here + if (hbl_countdown > 0) + { + hbl_countdown--; + if (hbl_countdown == 0) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + + break; + } + internal_cycle++; + + if (latch_new_data) + { + latch_new_data = false; + tile_data_latch[0] = tile_data[0]; + tile_data_latch[1] = tile_data[1]; + } + } + + // every in range sprite takes 6 cycles to process + // sprites located at x=0 still take 6 cycles to process even though they don't appear on screen + // sprites above x=168 do not take any cycles to process however + if (fetch_sprite) + { + if (going_to_fetch) + { + going_to_fetch = false; + sprite_fetch_counter = 0; + + if (fetch_sprite_01) + { + sprite_fetch_counter += 2; + fetch_sprite_01 = false; + } + + if (fetch_sprite_4) + { + sprite_fetch_counter -= 2; + fetch_sprite_4 = false; + } + + int last_eval = 0; + + // at this time it is unknown what each cycle does, but we only need to accurately keep track of cycles + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + sprite_fetch_counter += 6; + evaled_sprites |= (1 << i); + last_eval = SL_sprites[i * 4 + 1]; + } + } + + // if we didn't evaluate all the sprites immediately, 2 more cycles are added to restart it + if (evaled_sprites != (Math.Pow(2,SL_sprites_index) - 1)) + { + if ((last_eval % 8) == 0) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 1) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 2) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 3) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 4) { sprite_fetch_counter += 3; } + else { sprite_fetch_counter += 2; } + } + } + else + { + sprite_fetch_counter--; + if (sprite_fetch_counter == 0) + { + fetch_sprite = false; + } + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index b3f61be4bf..bddb78d179 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -96,104 +96,37 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int window_y_tile_inc; public int window_x_latch; - public byte ReadReg(int addr) + public virtual byte ReadReg(int addr) { - byte ret = 0; - - switch (addr) - { - case 0xFF40: ret = LCDC; break; // LCDC - case 0xFF41: ret = STAT; break; // STAT - case 0xFF42: ret = scroll_y; break; // SCY - case 0xFF43: ret = scroll_x; break; // SCX - case 0xFF44: ret = LY; break; // LY - case 0xFF45: ret = LYC; break; // LYC - case 0xFF46: ret = DMA_addr; break; // DMA - case 0xFF47: ret = BGP; break; // BGP - case 0xFF48: ret = obj_pal_0; break; // OBP0 - case 0xFF49: ret = obj_pal_1; break; // OBP1 - case 0xFF4A: ret = window_y; break; // WY - case 0xFF4B: ret = window_x; break; // WX - } - - return ret; + return 0; } - public void WriteReg(int addr, byte value) + public virtual void WriteReg(int addr, byte value) { - switch (addr) - { - case 0xFF40: // LCDC - if (LCDC.Bit(7) && !value.Bit(7)) - { - VRAM_access_read = true; - VRAM_access_write = true; - OAM_access_read = true; - OAM_access_write = true; - } - if (!LCDC.Bit(7) && value.Bit(7)) - { - // don't draw for one frame after turning on - blank_frame = true; - } - - LCDC = value; - break; - case 0xFF41: // STAT - // writing to STAT during mode 0 or 2 causes a STAT IRQ - if (LCDC.Bit(7)) - { - if (((STAT & 3) == 0) || ((STAT & 3) == 1)) - { - LYC_INT = true; - } - } - STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); - break; - case 0xFF42: // SCY - scroll_y = value; - break; - case 0xFF43: // SCX - scroll_x = value; - // calculate the column number of the tile to start with - x_tile = (int)Math.Floor((float)(scroll_x) / 8); - break; - case 0xFF44: // LY - LY = 0; /*reset*/ - break; - case 0xFF45: // LYC - LYC = value; - if (LY != LYC) { STAT &= 0xFB; } - break; - case 0xFF46: // DMA - DMA_addr = value; - DMA_start = true; - DMA_OAM_access = true; - DMA_clock = 0; - DMA_inc = 0; - break; - case 0xFF47: // BGP - BGP = value; - break; - case 0xFF48: // OBP0 - obj_pal_0 = value; - break; - case 0xFF49: // OBP1 - obj_pal_1 = value; - break; - case 0xFF4A: // WY - window_y = value; - break; - case 0xFF4B: // WX - window_x = value; - break; - } } - public void tick() + public virtual void tick() { - // tick DMA, note that DMA is halted when the CPU is halted + + } + + // might be needed, not sure yet + public virtual void latch_delay() + { + + } + + public virtual void render(int render_cycle) + { + + } + + // normal DMA moves twice as fast in double speed mode on GBC + // So give it it's own function so we can seperate it from PPU tick + public virtual void DMA_tick() + { + // Note that DMA is halted when the CPU is halted if (DMA_start && !Core.cpu.halted) { if (DMA_clock >= 4) @@ -208,7 +141,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk byte DMA_actual = DMA_addr; if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; } DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc)); - DMA_start = true; + DMA_start = true; } else if ((DMA_clock % 4) == 3) { @@ -226,281 +159,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk DMA_OAM_access = true; } } - - // the ppu only does anything if it is turned on via bit 7 of LCDC - if (LCDC.Bit(7)) - { - // start the next scanline - if (cycle == 456) - { - // scanline callback - if ((LY + LY_inc) == Core._scanlineCallbackLine) - { - if (Core._scanlineCallback != null) - { - Core.GetGPU(); - Core._scanlineCallback(LCDC); - } - } - - cycle = 0; - LY += LY_inc; - no_scan = false; - - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } - - if (LY == 0 && LY_inc == 0) - { - LY_inc = 1; - Core.in_vblank = false; - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; - - // special note here, the y coordiate of the window is kept if the window is deactivated - // meaning it will pick up where it left off if re-enabled later - // so we don't reset it in the scanline loop - window_y_tile = 0; - window_y_tile_inc = 0; - window_started = false; - } - - Core.cpu.LY = LY; - - // Automatically restore access to VRAM at this time (force end drawing) - // Who Framed Roger Rabbit seems to run into this. - VRAM_access_write = true; - VRAM_access_read = true; - - if (LY == 144) - { - Core.in_vblank = true; - } - } - - // exit vblank if LCD went from off to on - if (LCD_was_off) - { - //VBL_INT = false; - Core.in_vblank = false; - LCD_was_off = false; - - // we exit vblank into mode 0 for 4 cycles - // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 - STAT &= 0xFC; - - // also the LCD doesn't turn on right away - - // also, the LCD does not enter mode 2 on scanline 0 when first turned on - no_scan = true; - cycle = 8; - } - - // the VBL stat is continuously asserted - if ((LY >= 144)) - { - if (STAT.Bit(4)) - { - if ((cycle >= 4) && (LY == 144)) - { - VBL_INT = true; - } - else if (LY > 144) - { - VBL_INT = true; - } - } - - if ((cycle == 4) && (LY == 144)) { - - HBL_INT = false; - - // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled - STAT &= 0xFC; - STAT |= 0x01; - - if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } - Core.REG_FF0F |= 0x01; - } - - if ((LY >= 144) && (cycle == 4)) - { - // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } - } - - if ((LY == 153) && (cycle == 8)) - { - LY = 0; - LY_inc = 0; - Core.cpu.LY = LY; - } - } - - if (!Core.in_vblank) - { - if (no_scan) - { - // timings are slightly different if we just turned on the LCD - // there is no mode 2 (presumably it missed the trigger) - // mode 3 is very short, probably in some self test mode before turning on? - - if (cycle == 12) - { - LYC_INT = false; - STAT &= 0xFB; - - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - } - - if (cycle == 84) - { - - STAT &= 0xFC; - STAT |= 0x03; - OAM_INT = false; - - OAM_access_read = false; - OAM_access_write = false; - VRAM_access_read = false; - VRAM_access_write = false; - } - - if (cycle == 256) - { - STAT &= 0xFC; - OAM_INT = false; - - if (STAT.Bit(3)) { HBL_INT = true; } - - OAM_access_read = true; - OAM_access_write = true; - VRAM_access_read = true; - VRAM_access_write = true; - } - } - else - { - if (cycle < 80) - { - if (cycle == 4) - { - // apparently, writes can make it to OAM one cycle longer then reads - OAM_access_write = false; - - // here mode 2 will be set to true and interrupts fired if enabled - STAT &= 0xFC; - STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } - - HBL_INT = false; - } - - // here OAM scanning is performed - OAM_scan(cycle); - } - else if (cycle >= 80 && LY < 144) - { - - if (cycle == 84) - { - STAT &= 0xFC; - STAT |= 0x03; - OAM_INT = false; - OAM_access_write = false; - VRAM_access_write = false; - } - - // render the screen and handle hblank - render(cycle - 80); - } - } - } - - if ((LY_inc == 0)) - { - if (cycle == 12) - { - LYC_INT = false; - STAT &= 0xFB; - - // Special case of LY = LYC - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - - // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } - } - - if (cycle == 92) { OAM_INT = false; } - } - - // here LY=LYC will be asserted - if ((cycle == 4) && (LY != 0)) - { - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - } - - cycle++; - } - else - { - STAT &= 0xF8; - - VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; - - Core.in_vblank = true; - - LCD_was_off = true; - - LY = 0; - Core.cpu.LY = LY; - - cycle = 0; - } - - // assert the STAT IRQ line if the line went from zero to 1 - stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; - - if (stat_line && !stat_line_old) - { - if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } - Core.REG_FF0F |= 0x02; - } - - stat_line_old = stat_line; - - // process latch delays - //latch_delay(); } - // might be needed, not sure yet - public void latch_delay() - { - //BGP_l = BGP; - } - - public void OAM_scan(int OAM_cycle) + public virtual void OAM_scan(int OAM_cycle) { // we are now in STAT mode 2 // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? @@ -569,509 +230,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - public void render(int render_cycle) - { - // we are now in STAT mode 3 - // NOTE: presumably the first necessary sprite is fetched at sprite evaulation - // i.e. just keeping track of the lowest x-value sprite - if (render_cycle == 0) - { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - // window X is latched for the scanline, mid-line changes have no effect - window_x_latch = window_x; - - OAM_scan_index = 0; - read_case = 0; - internal_cycle = 0; - pre_render = true; - tile_inc = 0; - pixel_counter = -8; - sl_use_index = 0; - fetch_sprite = false; - fetch_sprite_01 = false; - fetch_sprite_4 = false; - going_to_fetch = false; - no_sprites = false; - evaled_sprites = 0; - - window_pre_render = false; - if (window_started && LCDC.Bit(5)) - { - window_y_tile_inc++; - if (window_y_tile_inc==8) - { - window_y_tile_inc = 0; - window_y_tile++; - window_y_tile %= 32; - } - } - window_started = false; - - if (SL_sprites_index == 0) { no_sprites = true; } - // it is much easier to process sprites if we order them according to the rules of sprite priority first - if (!no_sprites) { reorder_and_assemble_sprites(); } - - } - - // before anything else, we have to check if windowing is in effect - if (LCDC.Bit(5) && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) - { - /* - Console.Write(LY); - Console.Write(" "); - Console.Write(cycle); - Console.Write(" "); - Console.Write(window_y_tile_inc); - Console.Write(" "); - Console.Write(window_x_latch); - Console.Write(" "); - Console.WriteLine(pixel_counter); - */ - if (pixel_counter == 0 && window_x_latch <= 7) - { - // if the window starts at zero, we still do the first access to the BG - // but then restart all over again at the window - window_pre_render = true; - } - else - { - // otherwise, just restart the whole process as if starting BG again - window_pre_render = true; - read_case = 4; - } - window_counter = 0; - - window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); - - window_tile_inc = 0; - window_started = true; - } - - if (!pre_render && !fetch_sprite && !window_pre_render) - { - // start shifting data into the LCD - if (render_counter >= (render_offset + 8)) - { - pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; - - int ref_pixel = pixel; - if (LCDC.Bit(0)) - { - pixel = (BGP >> (pixel * 2)) & 3; - } - else - { - pixel = 0; - } - - // now we have the BG pixel, we next need the sprite pixel - if (!no_sprites) - { - bool have_sprite = false; - int s_pixel = 0; - int sprite_attr = 0; - - if (sprite_present_list[pixel_counter] == 1) - { - have_sprite = true; - s_pixel = sprite_pixel_list[pixel_counter]; - sprite_attr = sprite_attr_list[pixel_counter]; - } - - if (have_sprite) - { - bool use_sprite = false; - if (LCDC.Bit(1)) - { - if (!sprite_attr.Bit(7)) - { - use_sprite = true; - } - else if (ref_pixel == 0) - { - use_sprite = true; - } - - if (!LCDC.Bit(0)) - { - use_sprite = true; - } - } - - if (use_sprite) - { - if (sprite_attr.Bit(4)) - { - pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; - } - else - { - pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; - } - } - } - } - - // based on sprite priority and pixel values, pick a final pixel color - Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel]; - pixel_counter++; - - if (pixel_counter == 160) - { - read_case = 8; - hbl_countdown = 7; - } - } - else if ((render_counter >= render_offset) && (pixel_counter < 0)) - { - pixel_counter++; - } - render_counter++; - } - - if (!fetch_sprite) - { - if (!pre_render) - { - // before we go on to read case 3, we need to know if we stall there or not - // Gekkio's tests show that if sprites are at position 0 or 1 (mod 8) - // then it takes an extra cycle (1 or 2 more t-states) to process them - - if (!no_sprites && (pixel_counter < 160)) - { - for (int i = 0; i < SL_sprites_index; i++) - { - if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && - (pixel_counter < (SL_sprites[i * 4 + 1])) && - !evaled_sprites.Bit(i)) - { - going_to_fetch = true; - fetch_sprite = true; - - if ((SL_sprites[i * 4 + 1] % 8) < 2) - { - fetch_sprite_01 = true; - } - if ((SL_sprites[i * 4 + 1] % 8) > 3) - { - fetch_sprite_4 = true; - } - } - } - } - } - - switch (read_case) - { - case 0: // read a background tile - if ((internal_cycle % 2) == 0) - { - // calculate the row number of the tiles to be fetched - y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; - - temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; - tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; - } - else - { - read_case = 1; - if (!pre_render) - { - tile_inc++; - if (window_pre_render) - { - read_case = 4; - } - } - } - break; - - case 1: // read from tile graphics (0) - if ((internal_cycle % 2) == 0) - { - y_scroll_offset = (scroll_y + LY) % 8; - - if (LCDC.Bit(4)) - { - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7)) - { - tile_byte -= 256; - } - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; - } - - } - else - { - read_case = 2; - } - break; - - case 2: // read from tile graphics (1) - if ((internal_cycle % 2) == 0) - { - y_scroll_offset = (scroll_y + LY) % 8; - - if (LCDC.Bit(4)) - { - // if LCDC somehow changed between the two reads, make sure we have a positive number - if (tile_byte < 0) - { - tile_byte += 256; - } - - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7) && tile_byte > 0) - { - tile_byte -= 256; - } - - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - - } - else - { - if (pre_render) - { - // here we set up rendering - pre_render = false; - render_offset = scroll_x % 8; - render_counter = 0; - latch_counter = 0; - read_case = 0; - } - else - { - read_case = 3; - } - } - break; - - case 3: // read from sprite data - if ((internal_cycle % 2) == 0) - { - // nothing to do if not fetching - } - else - { - read_case = 0; - latch_new_data = true; - } - break; - - case 4: // read from window data - if ((window_counter % 2) == 0) - { - temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; - tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; - } - else - { - if (!window_pre_render) - { - window_tile_inc++; - } - read_case = 5; - } - window_counter++; - break; - - case 5: // read from tile graphics (for the window) - if ((window_counter % 2) == 0) - { - y_scroll_offset = (window_y_tile_inc) % 8; - - if (LCDC.Bit(4)) - { - - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; - - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7)) - { - tile_byte -= 256; - } - - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; - } - } - else - { - read_case = 6; - } - window_counter++; - break; - - case 6: // read from tile graphics (for the window) - if ((window_counter % 2) == 0) - { - y_scroll_offset = (window_y_tile_inc) % 8; - if (LCDC.Bit(4)) - { - // if LCDC somehow changed between the two reads, make sure we have a positive number - if (tile_byte < 0) - { - tile_byte += 256; - } - - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7) && tile_byte > 0) - { - tile_byte -= 256; - } - - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - - } - else - { - if (window_pre_render) - { - // here we set up rendering - window_pre_render = false; - render_offset = 0; - render_counter = 0; - latch_counter = 0; - read_case = 4; - } - else - { - read_case = 7; - - } - } - window_counter++; - break; - - case 7: // read from sprite data - if ((window_counter % 2) == 0) - { - // nothing to do if not fetching - } - else - { - read_case = 4; - latch_new_data = true; - } - window_counter++; - break; - - case 8: // done reading, we are now in phase 0 - - pre_render = true; - - // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here - if (hbl_countdown > 0) - { - hbl_countdown--; - if (hbl_countdown == 0) - { - STAT &= 0xFC; - STAT |= 0x00; - - if (STAT.Bit(3)) { HBL_INT = true; } - - OAM_access_read = true; - OAM_access_write = true; - VRAM_access_read = true; - VRAM_access_write = true; - } - } - - break; - } - internal_cycle++; - - if (latch_new_data) - { - latch_new_data = false; - tile_data_latch[0] = tile_data[0]; - tile_data_latch[1] = tile_data[1]; - } - } - - // every in range sprite takes 6 cycles to process - // sprites located at x=0 still take 6 cycles to process even though they don't appear on screen - // sprites above x=168 do not take any cycles to process however - if (fetch_sprite) - { - if (going_to_fetch) - { - going_to_fetch = false; - sprite_fetch_counter = 0; - - if (fetch_sprite_01) - { - sprite_fetch_counter += 2; - fetch_sprite_01 = false; - } - - if (fetch_sprite_4) - { - sprite_fetch_counter -= 2; - fetch_sprite_4 = false; - } - - int last_eval = 0; - - // at this time it is unknown what each cycle does, but we only need to accurately keep track of cycles - for (int i = 0; i < SL_sprites_index; i++) - { - if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && - (pixel_counter < (SL_sprites[i * 4 + 1])) && - !evaled_sprites.Bit(i)) - { - sprite_fetch_counter += 6; - evaled_sprites |= (1 << i); - last_eval = SL_sprites[i * 4 + 1]; - } - } - - // if we didn't evaluate all the sprites immediately, 2 more cycles are added to restart it - if (evaled_sprites != (Math.Pow(2,SL_sprites_index) - 1)) - { - if ((last_eval % 8) == 0) { sprite_fetch_counter += 3; } - else if ((last_eval % 8) == 1) { sprite_fetch_counter += 2; } - else if ((last_eval % 8) == 2) { sprite_fetch_counter += 3; } - else if ((last_eval % 8) == 3) { sprite_fetch_counter += 2; } - else if ((last_eval % 8) == 4) { sprite_fetch_counter += 3; } - else { sprite_fetch_counter += 2; } - } - } - else - { - sprite_fetch_counter--; - if (sprite_fetch_counter == 0) - { - fetch_sprite = false; - } - } - } - } - - public void Reset() + public virtual void Reset() { LCDC = 0; STAT = 0x80; @@ -1112,7 +272,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_y_tile_inc = 0; } - public void process_sprite() + public virtual void process_sprite() { int y; @@ -1170,7 +330,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // order sprites according to x coordinate // note that for sprites of equal x coordinate, priority goes to first on the list - public void reorder_and_assemble_sprites() + public virtual void reorder_and_assemble_sprites() { sprite_ordered_index = 0; @@ -1236,7 +396,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - public void SyncState(Serializer ser) + public virtual void SyncState(Serializer ser) { ser.Sync("LCDC", ref LCDC); ser.Sync("STAT", ref STAT); From ca69e5222912cb6efe1c431f018e21d6dd0d2606 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 25 Mar 2018 10:07:12 -0400 Subject: [PATCH 128/339] GBHawk: GBC commits round 1 -RAM banks -Unify VRAM and add second Bank -Speed Switch and associated reorg. -Memory Map and Registers -PPU seperation --- .../CPUs/LR35902/Interrupts.cs | 2 + .../CPUs/LR35902/LR35902.cs | 48 ++- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 333 +++++++++++++++++- .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 33 +- .../Nintendo/GBHawk/GBHawk.IStatable.cs | 10 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 48 +-- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 293 ++++++++++++++- .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 97 +++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC5.cs | 79 ++++- .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 182 +++++++--- .../Consoles/Nintendo/GBHawk/PPU.cs | 254 +------------ 11 files changed, 1017 insertions(+), 362 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 0dba320f46..bde8ea4309 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -55,6 +55,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60, 0x00}; public ushort int_src; + public int stop_time; + public bool stop_check; private void ResetInterrupts() { diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 1b475ef337..bc5c2b3a06 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -68,6 +68,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ResetRegisters(); ResetInterrupts(); TotalExecutedCycles = 0; + stop_check = false; cur_instr = new ushort[] { OP }; } @@ -78,6 +79,9 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public Func PeekMemory; public Func DummyReadMemory; + // Special Function for Speed switching executed on a STOP + public Func SpeedFunc; + //this only calls when the first byte of an instruction is fetched. public Action OnExecFetch; @@ -306,7 +310,45 @@ namespace BizHawk.Emulation.Common.Components.LR35902 case STOP: stopped = true; - if (interrupt_src.Bit(4)) // button pressed, not actually an interrupt though + if (!stop_check) + { + stop_time = SpeedFunc(0); + stop_check = true; + } + + if (stop_time > 0) + { + stop_time--; + if (stop_time == 0) + { + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====un-stop====", + RegisterInfo = "" + }); + } + + stopped = false; + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + instr_pntr = 0; + + stop_check = false; + } + else + { + instr_pntr = 0; + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + STOP }; + } + } + else if (interrupt_src.Bit(4)) // button pressed, not actually an interrupt though { if (TraceCallback != null) { @@ -322,6 +364,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 if (TraceCallback != null && !CB_prefix) TraceCallback(State()); FetchInstruction(ReadMemory(RegPC++)); instr_pntr = 0; + + stop_check = false; } else { @@ -434,6 +478,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ser.Sync("ExecutedCycles", ref totalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); ser.Sync("int_src", ref int_src); + ser.Sync("stop_time", ref stop_time); + ser.Sync("stop_check", ref stop_check); ser.Sync("instruction_pointer", ref instr_pntr); ser.Sync("current instruction", ref cur_instr, false); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index d308d004f3..e24995e9b1 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -7,6 +7,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public class GBC_PPU : PPU { + // these are the uniquely GBC variables + public byte BG_pal_index; + public byte pal_transfer_byte; + public byte spr_pal_index; + public byte spr_transfer_byte; + public byte HDMA_src_hi; + public byte HDMA_src_lo; + public byte HDMA_dest_hi; + public byte HDMA_dest_lo; + public byte HDMA_ctrl; + + // controls for tile attributes + public byte tile_attr_byte; + public int VRAM_sel; + public override byte ReadReg(int addr) { byte ret = 0; @@ -25,6 +40,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF49: ret = obj_pal_1; break; // OBP1 case 0xFF4A: ret = window_y; break; // WY case 0xFF4B: ret = window_x; break; // WX + + // These are GBC specific Regs + case 0xFF51: ret = HDMA_src_hi; break; // HDMA1 + case 0xFF52: ret = HDMA_src_lo; break; // HDMA2 + case 0xFF53: ret = HDMA_dest_hi; break; // HDMA3 + case 0xFF54: ret = HDMA_dest_lo; break; // HDMA4 + case 0xFF55: ret = HDMA_ctrl; break; // HDMA5 + case 0xFF68: ret = BG_pal_index; break; // BGPI + case 0xFF69: ret = pal_transfer_byte; break; // BGPD + case 0xFF6A: ret = spr_pal_index; break; // OBPI + case 0xFF6B: ret = spr_transfer_byte; break; // OBPD } return ret; @@ -99,6 +125,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF4B: // WX window_x = value; break; + + // These are GBC specific Regs + case 0xFF51: // HDMA1 + HDMA_src_hi = value; + break; + case 0xFF52: // HDMA2 + HDMA_src_lo = value; + break; + case 0xFF53: // HDMA3 + HDMA_dest_hi = value; + break; + case 0xFF54: // HDMA4 + HDMA_dest_lo = value; + break; + case 0xFF55: // HDMA5 + HDMA_ctrl = value; + break; + case 0xFF68: // BGPI + BG_pal_index = value; + break; + case 0xFF69: // BGPD + pal_transfer_byte = value; + break; + case 0xFF6A: // OBPI + spr_pal_index = value; + break; + case 0xFF6B: // OBPD + spr_transfer_byte = value; + break; } } @@ -582,7 +637,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; - tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; + tile_attr_byte = Core.VRAM[0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; + VRAM_sel = tile_attr_byte.Bit(3) ? 1 : 0; } else { @@ -605,7 +662,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (LCDC.Bit(4)) { - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2]; } else { @@ -614,7 +671,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { tile_byte -= 256; } - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2]; } } @@ -637,7 +694,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte += 256; } - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1]; } else { @@ -647,7 +704,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte -= 256; } - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; } } @@ -685,7 +742,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if ((window_counter % 2) == 0) { temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; - tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; + tile_attr_byte = Core.VRAM[0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; + VRAM_sel = tile_attr_byte.Bit(3) ? 1 : 0; } else { @@ -705,8 +764,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (LCDC.Bit(4)) { - - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2]; } else @@ -716,8 +775,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { tile_byte -= 256; } - - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2]; } } else @@ -739,7 +798,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte += 256; } - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1]; } else { @@ -749,7 +808,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte -= 256; } - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; } } @@ -878,5 +937,255 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } } + + public override void process_sprite() + { + int y; + + if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 15 - y; + sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 7 - y; + sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + else + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + + if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) + { + int b0, b1, b2, b3, b4, b5, b6, b7 = 0; + for (int i = 0; i < 2; i++) + { + b0 = (sprite_sel[i] & 0x01) << 7; + b1 = (sprite_sel[i] & 0x02) << 5; + b2 = (sprite_sel[i] & 0x04) << 3; + b3 = (sprite_sel[i] & 0x08) << 1; + b4 = (sprite_sel[i] & 0x10) >> 1; + b5 = (sprite_sel[i] & 0x20) >> 3; + b6 = (sprite_sel[i] & 0x40) >> 5; + b7 = (sprite_sel[i] & 0x80) >> 7; + + sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); + } + } + } + + // normal DMA moves twice as fast in double speed mode on GBC + // So give it it's own function so we can seperate it from PPU tick + public override void DMA_tick() + { + // Note that DMA is halted when the CPU is halted + if (DMA_start && !Core.cpu.halted) + { + if (DMA_clock >= 4) + { + DMA_OAM_access = false; + if ((DMA_clock % 4) == 1) + { + // the cpu can't access memory during this time, but we still need the ppu to be able to. + DMA_start = false; + // Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses + // So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF) + byte DMA_actual = DMA_addr; + if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; } + DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc)); + DMA_start = true; + } + else if ((DMA_clock % 4) == 3) + { + Core.OAM[DMA_inc] = DMA_byte; + + if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } + } + } + + DMA_clock++; + + if (DMA_clock == 648) + { + DMA_start = false; + DMA_OAM_access = true; + } + } + } + + // order sprites according to x coordinate + // note that for sprites of equal x coordinate, priority goes to first on the list + public override void reorder_and_assemble_sprites() + { + sprite_ordered_index = 0; + + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < SL_sprites_index; j++) + { + if (SL_sprites[j * 4 + 1] == i) + { + sl_use_index = j; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; + sprite_ordered_index++; + } + } + } + + bool have_pixel = false; + byte s_pixel = 0; + byte sprite_attr = 0; + + for (int i = 0; i < 160; i++) + { + have_pixel = false; + for (int j = 0; j < SL_sprites_index; j++) + { + if ((i >= (SL_sprites_ordered[j * 4] - 8)) && + (i < SL_sprites_ordered[j * 4]) && + !have_pixel) + { + // we can use the current sprite, so pick out a pixel for it + int t_index = i - (SL_sprites_ordered[j * 4] - 8); + + t_index = 7 - t_index; + + sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1); + sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1); + + s_pixel = (byte)(sprite_data[0] + sprite_data[1]); + sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3]; + + // pixel color of 0 is transparent, so if this is the case we dont have a pixel + if (s_pixel != 0) + { + have_pixel = true; + } + } + } + + if (have_pixel) + { + sprite_present_list[i] = 1; + sprite_pixel_list[i] = s_pixel; + sprite_attr_list[i] = sprite_attr; + } + else + { + sprite_present_list[i] = 0; + } + } + } + + public override void OAM_scan(int OAM_cycle) + { + // we are now in STAT mode 2 + // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? + if (OAM_cycle == 0) + { + OAM_access_read = false; + + OAM_scan_index = 0; + SL_sprites_index = 0; + write_sprite = 0; + } + + // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw + // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close + if (OAM_cycle < 10) + { + // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) + SL_sprites[OAM_cycle * 4] = 0; + SL_sprites[OAM_cycle * 4 + 1] = 0; + SL_sprites[OAM_cycle * 4 + 2] = 0; + SL_sprites[OAM_cycle * 4 + 3] = 0; + } + else + { + if (write_sprite == 0) + { + if (OAM_scan_index < 40) + { + ushort temp = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4] : (ushort)0xFF; + // (sprite Y - 16) equals LY, we have a sprite + if ((temp - 16) <= LY && + ((temp - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) + { + // always pick the first 10 in range sprites + if (SL_sprites_index < 10) + { + SL_sprites[SL_sprites_index * 4] = temp; + + write_sprite = 1; + } + else + { + // if we already have 10 sprites, there's nothing to do, increment the index + OAM_scan_index++; + } + } + else + { + OAM_scan_index++; + } + } + } + else + { + ushort temp2 = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4 + write_sprite] : (ushort)0xFF; + SL_sprites[SL_sprites_index * 4 + write_sprite] = temp2; + write_sprite++; + + if (write_sprite == 4) + { + write_sprite = 0; + SL_sprites_index++; + OAM_scan_index++; + } + } + } + } + + public override void SyncState(Serializer ser) + { + ser.Sync("BG_pal_index", ref BG_pal_index); + ser.Sync("pal_transfer_byte", ref pal_transfer_byte); + ser.Sync("spr_pal_index", ref spr_pal_index); + ser.Sync("spr_transfer_byte", ref spr_transfer_byte); + ser.Sync("HDMA_src_hi", ref HDMA_src_hi); + ser.Sync("HDMA_src_lo", ref HDMA_src_lo); + ser.Sync("HDMA_dest_hi", ref HDMA_dest_hi); + ser.Sync("HDMA_dest_lo", ref HDMA_dest_lo); + ser.Sync("HDMA_ctrl", ref HDMA_ctrl); + + ser.Sync("tile_attr_byte", ref tile_attr_byte); + ser.Sync("VRAM_sel", ref VRAM_sel); + + base.SyncState(ser); + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 39096e025f..9e2a54d165 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -124,13 +124,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); timer.tick_2(); + if (double_speed) + { + ppu.DMA_tick(); + timer.tick_1(); + serialport.serial_transfer_tick(); + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); + timer.tick_2(); + } + if (in_vblank && !in_vblank_old) { vblank_rise = true; } ticker++; - if (ticker > 10000000) { throw new Exception("ERROR: Unable to Resolve Frame"); } + if (ticker > 10000000) { vblank_rise = true; }//throw new Exception("ERROR: Unable to Resolve Frame"); } in_vblank_old = in_vblank; } @@ -138,6 +147,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk vblank_rise = false; } + // Switch Speed (GBC only) + public int SpeedFunc(int temp) + { + if (is_GBC) + { + if (speed_switch) + { + speed_switch = false; + + int ret = double_speed ? 50000 : 25000; // actual time needs checking + double_speed = !double_speed; + return ret; + } + + // if we are not switching speed, return 0 + return 0; + } + + // if we are in GB mode, return 0 indicating not switching speed + return 0; + } + public void GetControllerState(IController controller) { InputCallbacks.Call(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index dbccea7f07..1cafb28dfc 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -79,11 +79,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // memory domains ser.Sync("RAM", ref RAM, false); ser.Sync("ZP_RAM", ref ZP_RAM, false); - ser.Sync("CHR_RAM", ref CHR_RAM, false); - ser.Sync("BG_map_1", ref BG_map_1, false); - ser.Sync("BG_map_2", ref BG_map_2, false); + ser.Sync("VRAM", ref VRAM, false); ser.Sync("OAM", ref OAM, false); + ser.Sync("RAM_Bank", ref RAM_Bank); + ser.Sync("VRAM_Bank", ref VRAM_Bank); + ser.Sync("is_GBC", ref is_GBC); + ser.Sync("double_speed", ref double_speed); + ser.Sync("speed_switch", ref speed_switch); + ser.Sync("Use_RTC", ref Use_RTC); // probably a better way to do this diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 7b09b8a6b5..70035ef974 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -36,13 +36,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // memory domains - public byte[] RAM = new byte[0x2000]; + public byte[] RAM = new byte[0x8000]; // only 0x2000 available to GB public byte[] ZP_RAM = new byte[0x80]; - public byte[] CHR_RAM = new byte[0x1800]; - public byte[] BG_map_1 = new byte[0x400]; - public byte[] BG_map_2 = new byte[0x400]; + /* + * VRAM is arranged as: + * 0x1800 Tiles + * 0x400 BG Map 1 + * 0x400 BG Map 2 + * 0x1800 Tiles + * 0x400 CA Map 1 + * 0x400 CA Map 2 + * Only the top set is available in GB (i.e. VRAM_Bank = 0) + */ + public byte[] VRAM = new byte[0x4000]; public byte[] OAM = new byte[0xA0]; + public int RAM_Bank; + public byte VRAM_Bank; + public bool is_GBC; + public bool double_speed; + public bool speed_switch; + public readonly byte[] _rom; public readonly byte[] _bios; public readonly byte[] header = new byte[0x50]; @@ -75,7 +89,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WriteMemory = WriteMemory, PeekMemory = PeekMemory, DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch + OnExecFetch = ExecFetch, + SpeedFunc = SpeedFunc, }; timer = new Timer(); @@ -102,6 +117,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); ppu = new GBC_PPU(); + is_GBC = true; } } @@ -114,6 +130,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); ppu = new GBC_PPU(); + is_GBC = true; } if (Bios == null) @@ -152,7 +169,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SetupMemoryDomains(); HardReset(); - iptr0 = Marshal.AllocHGlobal(CHR_RAM.Length + BG_map_1.Length + BG_map_2.Length + 1); + iptr0 = Marshal.AllocHGlobal(VRAM.Length + 1); iptr1 = Marshal.AllocHGlobal(OAM.Length + 1); iptr2 = Marshal.AllocHGlobal(color_palette.Length * 2 * 8 + 1); iptr3 = Marshal.AllocHGlobal(color_palette.Length * 8 + 1); @@ -173,22 +190,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { get { - byte[] temp = new byte[CHR_RAM.Length + BG_map_1.Length + BG_map_2.Length]; - - for (int i = 0; i < CHR_RAM.Length; i++) - { - temp[i] = CHR_RAM[i]; - } - for (int i = 0; i < BG_map_1.Length; i++) - { - temp[CHR_RAM.Length + i] = BG_map_1[i]; - } - for (int i = 0; i < BG_map_2.Length; i++) - { - temp[CHR_RAM.Length + BG_map_1.Length + i] = BG_map_2[i]; - } - - Marshal.Copy(temp, 0, iptr0, temp.Length); + Marshal.Copy(VRAM, 0, iptr0, VRAM.Length); Marshal.Copy(OAM, 0, iptr1, OAM.Length); int[] cp2 = new int[8]; @@ -247,6 +249,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk in_vblank = true; // we start off in vblank since the LCD is off in_vblank_old = true; + RAM_Bank = 1; // RAM bank always starts as 1 (even writing zero still sets 1) + Register_Reset(); timer.Reset(); ppu.Reset(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index e0fad7a5bd..e34e75480c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -582,7 +582,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; - tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; } else { @@ -605,7 +605,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (LCDC.Bit(4)) { - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + tile_data[0] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2]; } else { @@ -614,7 +614,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { tile_byte -= 256; } - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + tile_data[0] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; } } @@ -637,7 +637,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte += 256; } - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; } else { @@ -647,7 +647,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte -= 256; } - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; } } @@ -685,7 +685,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if ((window_counter % 2) == 0) { temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; - tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; ; } else { @@ -706,7 +706,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (LCDC.Bit(4)) { - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; + tile_data[0] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2]; } else @@ -717,7 +717,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte -= 256; } - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + tile_data[0] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; } } else @@ -739,7 +739,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte += 256; } - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; } else { @@ -749,7 +749,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte -= 256; } - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + tile_data[1] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; } } @@ -878,5 +878,278 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } } + + public override void process_sprite() + { + int y; + + if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 15 - y; + sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 7 - y; + sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + else + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + + if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) + { + int b0, b1, b2, b3, b4, b5, b6, b7 = 0; + for (int i = 0; i < 2; i++) + { + b0 = (sprite_sel[i] & 0x01) << 7; + b1 = (sprite_sel[i] & 0x02) << 5; + b2 = (sprite_sel[i] & 0x04) << 3; + b3 = (sprite_sel[i] & 0x08) << 1; + b4 = (sprite_sel[i] & 0x10) >> 1; + b5 = (sprite_sel[i] & 0x20) >> 3; + b6 = (sprite_sel[i] & 0x40) >> 5; + b7 = (sprite_sel[i] & 0x80) >> 7; + + sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); + } + } + } + + // normal DMA moves twice as fast in double speed mode on GBC + // So give it it's own function so we can seperate it from PPU tick + public override void DMA_tick() + { + // Note that DMA is halted when the CPU is halted + if (DMA_start && !Core.cpu.halted) + { + if (DMA_clock >= 4) + { + DMA_OAM_access = false; + if ((DMA_clock % 4) == 1) + { + // the cpu can't access memory during this time, but we still need the ppu to be able to. + DMA_start = false; + // Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses + // So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF) + byte DMA_actual = DMA_addr; + if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; } + DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc)); + DMA_start = true; + } + else if ((DMA_clock % 4) == 3) + { + Core.OAM[DMA_inc] = DMA_byte; + + if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } + } + } + + DMA_clock++; + + if (DMA_clock == 648) + { + DMA_start = false; + DMA_OAM_access = true; + } + } + } + + // order sprites according to x coordinate + // note that for sprites of equal x coordinate, priority goes to first on the list + public override void reorder_and_assemble_sprites() + { + sprite_ordered_index = 0; + + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < SL_sprites_index; j++) + { + if (SL_sprites[j * 4 + 1] == i) + { + sl_use_index = j; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; + sprite_ordered_index++; + } + } + } + + bool have_pixel = false; + byte s_pixel = 0; + byte sprite_attr = 0; + + for (int i = 0; i < 160; i++) + { + have_pixel = false; + for (int j = 0; j < SL_sprites_index; j++) + { + if ((i >= (SL_sprites_ordered[j * 4] - 8)) && + (i < SL_sprites_ordered[j * 4]) && + !have_pixel) + { + // we can use the current sprite, so pick out a pixel for it + int t_index = i - (SL_sprites_ordered[j * 4] - 8); + + t_index = 7 - t_index; + + sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1); + sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1); + + s_pixel = (byte)(sprite_data[0] + sprite_data[1]); + sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3]; + + // pixel color of 0 is transparent, so if this is the case we dont have a pixel + if (s_pixel != 0) + { + have_pixel = true; + } + } + } + + if (have_pixel) + { + sprite_present_list[i] = 1; + sprite_pixel_list[i] = s_pixel; + sprite_attr_list[i] = sprite_attr; + } + else + { + sprite_present_list[i] = 0; + } + } + } + + public override void OAM_scan(int OAM_cycle) + { + // we are now in STAT mode 2 + // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? + if (OAM_cycle == 0) + { + OAM_access_read = false; + + OAM_scan_index = 0; + SL_sprites_index = 0; + write_sprite = 0; + } + + // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw + // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close + if (OAM_cycle < 10) + { + // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) + SL_sprites[OAM_cycle * 4] = 0; + SL_sprites[OAM_cycle * 4 + 1] = 0; + SL_sprites[OAM_cycle * 4 + 2] = 0; + SL_sprites[OAM_cycle * 4 + 3] = 0; + } + else + { + if (write_sprite == 0) + { + if (OAM_scan_index < 40) + { + ushort temp = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4] : (ushort)0xFF; + // (sprite Y - 16) equals LY, we have a sprite + if ((temp - 16) <= LY && + ((temp - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) + { + // always pick the first 10 in range sprites + if (SL_sprites_index < 10) + { + SL_sprites[SL_sprites_index * 4] = temp; + + write_sprite = 1; + } + else + { + // if we already have 10 sprites, there's nothing to do, increment the index + OAM_scan_index++; + } + } + else + { + OAM_scan_index++; + } + } + } + else + { + ushort temp2 = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4 + write_sprite] : (ushort)0xFF; + SL_sprites[SL_sprites_index * 4 + write_sprite] = temp2; + write_sprite++; + + if (write_sprite == 4) + { + write_sprite = 0; + SL_sprites_index++; + OAM_scan_index++; + } + } + } + } + + public override void Reset() + { + LCDC = 0; + STAT = 0x80; + scroll_y = 0; + scroll_x = 0; + LY = 0; + LYC = 0; + DMA_addr = 0xFF; + BGP = 0xFF; + obj_pal_0 = 0xFF; + obj_pal_1 = 0xFF; + window_y = 0x0; + window_x = 0x0; + window_x_latch = 0xFF; + LY_inc = 1; + no_scan = false; + OAM_access_read = true; + VRAM_access_read = true; + OAM_access_write = true; + VRAM_access_write = true; + DMA_OAM_access = true; + + cycle = 0; + LYC_INT = false; + HBL_INT = false; + VBL_INT = false; + OAM_INT = false; + + stat_line = false; + stat_line_old = false; + + window_counter = 0; + window_pre_render = false; + window_started = false; + window_tile_inc = 0; + window_y_tile = 0; + window_x_tile = 0; + window_y_tile_inc = 0; + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index daf9161210..a82cdb820b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -99,11 +99,66 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ret = ppu.ReadReg(addr); break; + // Speed Control for GBC + case 0xFF4D: + if (is_GBC) + { + ret = (byte)(((double_speed ? 1 : 0) << 7) + ((speed_switch ? 1 : 0))); + } + else + { + ret = 0xFF; + } + break; + + case 0xFF4F: // VBK + if (is_GBC) + { + ret = VRAM_Bank; + } + else + { + ret = 0xFF; + } + break; + // Bios control register. Not sure if it is readable case 0xFF50: ret = 0xFF; break; + // PPU Regs for GBC + case 0xFF51: + case 0xFF52: + case 0xFF53: + case 0xFF54: + case 0xFF55: + case 0xFF68: + case 0xFF69: + case 0xFF6A: + case 0xFF6B: + if (is_GBC) + { + ret = ppu.ReadReg(addr); + } + else + { + ret = 0xFF; + } + break; + + // Speed Control for GBC + case 0xFF70: + if (is_GBC) + { + ret = (byte)RAM_Bank; + } + else + { + ret = 0xFF; + } + break; + // interrupt control register case 0xFFFF: ret = REG_FFFF; @@ -254,6 +309,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ppu.WriteReg(addr, value); break; + // Speed Control for GBC + case 0xFF4D: + if (is_GBC) + { + speed_switch = (value & 1) > 0; + } + break; + + // VBK + case 0xFF4F: + if (is_GBC) + { + VRAM_Bank = (byte)(value & 1); + } + break; + // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs case 0xFF50: //Console.WriteLine(value); @@ -263,6 +334,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } break; + // PPU Regs for GBC + case 0xFF51: + case 0xFF52: + case 0xFF53: + case 0xFF54: + case 0xFF55: + case 0xFF68: + case 0xFF69: + case 0xFF6A: + case 0xFF6B: + if (is_GBC) + { + ppu.WriteReg(addr, value); + } + break; + + // RAM Bank in GBC mode + case 0xFF70: + //Console.WriteLine(value); + if (is_GBC) + { + RAM_Bank = value & 7; + if (RAM_Bank == 0) { RAM_Bank = 1; } + } + break; + // interrupt control register case 0xFFFF: REG_FFFF = value; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs index 529f48ca4a..7c63b10f55 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs @@ -7,26 +7,57 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // Default mapper with no bank switching public class MapperMBC5 : MapperBase { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public int ROM_mask; + public int RAM_mask; + public override void Initialize() { - // nothing to initialize + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + + // some games have sizes that result in a degenerate ROM, account for it here + if (ROM_mask > 4) { ROM_mask |= 3; } + + RAM_mask = 0; + if (Core.cart_RAM != null) + { + RAM_mask = Core.cart_RAM.Length / 0x2000 - 1; + if (Core.cart_RAM.Length == 0x800) { RAM_mask = 0; } + } } public override byte ReadMemory(ushort addr) { - if (addr < 0x8000) + if (addr < 0x4000) { return Core._rom[addr]; } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } else { if (Core.cart_RAM != null) { - return Core.cart_RAM[addr - 0xA000]; + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0xFF; + } + } else { - return 0; + return 0xFF; } } } @@ -40,13 +71,40 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (addr < 0x8000) { - // no mapping hardware available + if (addr < 0x2000) + { + RAM_enable = (value & 0xF) == 0xA; + } + else if (addr < 0x3000) + { + value &= 0xFF; + + ROM_bank &= 0x100; + ROM_bank |= value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x4000) + { + value &= 1; + + ROM_bank &= 0xFF; + ROM_bank |= (value << 8); + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + RAM_bank = value & 0xF; + RAM_bank &= RAM_mask; + } } else { if (Core.cart_RAM != null) { - Core.cart_RAM[addr - 0xA000] = value; + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } } } } @@ -55,5 +113,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { WriteMemory(addr, value); } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_Mask", ref RAM_mask); + ser.Sync("RAM_enable", ref RAM_enable); + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index b1b241d39b..616b1c2413 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -33,13 +33,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (ppu.DMA_start) { + // some of gekkio's tests require these to be accessible during DMA if (addr < 0x4000) { - return mapper.ReadMemory(addr); // some of gekkio's tests require this to be accessible during DMA + return mapper.ReadMemory(addr); } - else if ((addr >= 0xE000) && (addr < 0xFE00)) + else if ((addr >= 0xE000) && (addr < 0xF000)) { - return RAM[addr - 0xE000]; // some of gekkio's tests require this to be accessible during DMA + return RAM[addr - 0xE000]; + } + else if ((addr >= 0xF000) && (addr < 0xFE00)) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; } else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access) { @@ -57,12 +62,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return 0xFF; } - if (addr < 0x100) + if (addr < 0x900) { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) + if (addr < 0x100) { - return _bios[addr]; // Return BIOS + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } + } + else if (addr >= 0x200) + { + // return Either BIOS ROM or Game ROM + if (((GB_bios_register & 0x1) == 0) && is_GBC) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } } else { @@ -73,33 +97,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { return mapper.ReadMemory(addr); } - else if (addr < 0x9800) - { - if (ppu.VRAM_access_read) { return CHR_RAM[addr - 0x8000]; } - else { return 0xFF; } - } - else if (addr < 0x9C00) - { - if (ppu.VRAM_access_read) { return BG_map_1[addr - 0x9800]; } - else { return 0xFF; } - } else if (addr < 0xA000) { - if (ppu.VRAM_access_read) { return BG_map_2[addr - 0x9C00]; } + if (ppu.VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; } else { return 0xFF; } } else if (addr < 0xC000) { return mapper.ReadMemory(addr); } - else if (addr < 0xE000) + else if (addr < 0xD000) { return RAM[addr - 0xC000]; } - else if (addr < 0xFE00) + else if (addr < 0xE000) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)]; + } + else if (addr < 0xF000) { return RAM[addr - 0xE000]; } + else if (addr < 0xFE00) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; + } else if (addr < 0xFEA0) { if (ppu.OAM_access_read) { return OAM[addr - 0xFE00]; } @@ -131,9 +153,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (ppu.DMA_start) { - if ((addr >= 0xE000) && (addr < 0xFE00)) + // some of gekkio's tests require this to be accessible during DMA + if ((addr >= 0xE000) && (addr < 0xF000)) { - RAM[addr - 0xE000] = value; // some of gekkio's tests require this to be accessible during DMA + RAM[addr - 0xE000] = value; + } + else if ((addr >= 0xF000) && (addr < 0xFE00)) + { + RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value; } else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access) { @@ -150,12 +177,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return; } - if (addr < 0x100) + if (addr < 0x900) { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) + if (addr < 0x100) { - // Can't write to BIOS region + if ((GB_bios_register & 0x1) == 0) + { + // No Writing to BIOS + } + else + { + mapper.WriteMemory(addr, value); + } + } + else if (addr >= 0x200) + { + if (((GB_bios_register & 0x1) == 0) && is_GBC) + { + // No Writing to BIOS + } + else + { + mapper.WriteMemory(addr, value); + } } else { @@ -166,30 +210,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { mapper.WriteMemory(addr, value); } - else if (addr < 0x9800) - { - if (ppu.VRAM_access_write) { CHR_RAM[addr - 0x8000] = value; } - } - else if (addr < 0x9C00) - { - if (ppu.VRAM_access_write) { BG_map_1[addr - 0x9800] = value; } - } else if (addr < 0xA000) { - if (ppu.VRAM_access_write) { BG_map_2[addr - 0x9C00] = value; } + if (ppu.VRAM_access_write) { VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)] = value; } } else if (addr < 0xC000) { mapper.WriteMemory(addr, value); } - else if (addr < 0xE000) + else if (addr < 0xD000) { RAM[addr - 0xC000] = value; } - else if (addr < 0xFE00) + else if (addr < 0xE000) + { + RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)] = value; + } + else if (addr < 0xF000) { RAM[addr - 0xE000] = value; } + else if (addr < 0xFE00) + { + RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value; + } else if (addr < 0xFEA0) { if (ppu.OAM_access_write) { OAM[addr - 0xFE00] = value; } @@ -216,13 +260,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (ppu.DMA_start) { + // some of gekkio's tests require these to be accessible during DMA if (addr < 0x4000) { - return mapper.ReadMemory(addr); // some of gekkio's tests require this to be accessible during DMA + return mapper.ReadMemory(addr); } - else if ((addr >= 0xE000) && (addr < 0xFE00)) + else if ((addr >= 0xE000) && (addr < 0xF000)) { - return RAM[addr - 0xE000]; // some of gekkio's tests require this to be accessible during DMA + return RAM[addr - 0xE000]; + } + else if ((addr >= 0xF000) && (addr < 0xFE00)) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; } else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access) { @@ -240,49 +289,66 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return 0xFF; } - if (addr < 0x100) + if (addr < 0x900) { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) + if (addr < 0x100) { - return _bios[addr]; // Return BIOS + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } + } + else if (addr >= 0x200) + { + // return Either BIOS ROM or Game ROM + if (((GB_bios_register & 0x1) == 0) && is_GBC) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } } else { - return mapper.PeekMemory(addr); + return mapper.ReadMemory(addr); } } else if (addr < 0x8000) { return mapper.PeekMemory(addr); } - else if (addr < 0x9800) - { - if (ppu.VRAM_access_read) { return CHR_RAM[addr - 0x8000]; } - else { return 0xFF; } - } - else if (addr < 0x9C00) - { - if (ppu.VRAM_access_read) { return BG_map_1[addr - 0x9800]; } - else { return 0xFF; } - } else if (addr < 0xA000) { - if (ppu.VRAM_access_read) { return BG_map_2[addr - 0x9C00]; } + if (ppu.VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; } else { return 0xFF; } } else if (addr < 0xC000) { return mapper.PeekMemory(addr); } - else if (addr < 0xE000) + else if (addr < 0xD000) { return RAM[addr - 0xC000]; } - else if (addr < 0xFE00) + else if (addr < 0xE000) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)]; + } + else if (addr < 0xF000) { return RAM[addr - 0xE000]; } + else if (addr < 0xFE00) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; + } else if (addr < 0xFEA0) { if (ppu.OAM_access_read) { return OAM[addr - 0xFE00]; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index bddb78d179..bda927e0c5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -122,278 +122,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } + public virtual void process_sprite() + { + + } + // normal DMA moves twice as fast in double speed mode on GBC // So give it it's own function so we can seperate it from PPU tick public virtual void DMA_tick() { - // Note that DMA is halted when the CPU is halted - if (DMA_start && !Core.cpu.halted) - { - if (DMA_clock >= 4) - { - DMA_OAM_access = false; - if ((DMA_clock % 4) == 1) - { - // the cpu can't access memory during this time, but we still need the ppu to be able to. - DMA_start = false; - // Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses - // So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF) - byte DMA_actual = DMA_addr; - if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; } - DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc)); - DMA_start = true; - } - else if ((DMA_clock % 4) == 3) - { - Core.OAM[DMA_inc] = DMA_byte; - if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } - } - } - - DMA_clock++; - - if (DMA_clock == 648) - { - DMA_start = false; - DMA_OAM_access = true; - } - } } public virtual void OAM_scan(int OAM_cycle) { - // we are now in STAT mode 2 - // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? - if (OAM_cycle == 0) - { - OAM_access_read = false; - OAM_scan_index = 0; - SL_sprites_index = 0; - write_sprite = 0; - } - - // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw - // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close - if (OAM_cycle < 10) - { - // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) - SL_sprites[OAM_cycle * 4] = 0; - SL_sprites[OAM_cycle * 4 + 1] = 0; - SL_sprites[OAM_cycle * 4 + 2] = 0; - SL_sprites[OAM_cycle * 4 + 3] = 0; - } - else - { - if (write_sprite == 0) - { - if (OAM_scan_index < 40) - { - ushort temp = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4] : (ushort)0xFF; - // (sprite Y - 16) equals LY, we have a sprite - if ((temp - 16) <= LY && - ((temp - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) - { - // always pick the first 10 in range sprites - if (SL_sprites_index < 10) - { - SL_sprites[SL_sprites_index * 4] = temp; - - write_sprite = 1; - } - else - { - // if we already have 10 sprites, there's nothing to do, increment the index - OAM_scan_index++; - } - } - else - { - OAM_scan_index++; - } - } - } - else - { - ushort temp2 = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4 + write_sprite] : (ushort)0xFF; - SL_sprites[SL_sprites_index * 4 + write_sprite] = temp2; - write_sprite++; - - if (write_sprite == 4) - { - write_sprite = 0; - SL_sprites_index++; - OAM_scan_index++; - } - } - } } public virtual void Reset() { - LCDC = 0; - STAT = 0x80; - scroll_y = 0; - scroll_x = 0; - LY = 0; - LYC = 0; - DMA_addr = 0xFF; - BGP = 0xFF; - obj_pal_0 = 0xFF; - obj_pal_1 = 0xFF; - window_y = 0x0; - window_x = 0x0; - window_x_latch = 0xFF; - LY_inc = 1; - no_scan = false; - OAM_access_read = true; - VRAM_access_read = true; - OAM_access_write = true; - VRAM_access_write = true; - DMA_OAM_access = true; - cycle = 0; - LYC_INT = false; - HBL_INT = false; - VBL_INT = false; - OAM_INT = false; - - stat_line = false; - stat_line_old = false; - - window_counter = 0; - window_pre_render = false; - window_started = false; - window_tile_inc = 0; - window_y_tile = 0; - window_x_tile = 0; - window_y_tile_inc = 0; - } - - public virtual void process_sprite() - { - int y; - - if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) - { - if (LCDC.Bit(2)) - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - y = 15 - y; - sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; - } - else - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - y = 7 - y; - sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; - } - } - else - { - if (LCDC.Bit(2)) - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; - } - else - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; - } - } - - if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) - { - int b0, b1, b2, b3, b4, b5, b6, b7 = 0; - for (int i = 0; i < 2; i++) - { - b0 = (sprite_sel[i] & 0x01) << 7; - b1 = (sprite_sel[i] & 0x02) << 5; - b2 = (sprite_sel[i] & 0x04) << 3; - b3 = (sprite_sel[i] & 0x08) << 1; - b4 = (sprite_sel[i] & 0x10) >> 1; - b5 = (sprite_sel[i] & 0x20) >> 3; - b6 = (sprite_sel[i] & 0x40) >> 5; - b7 = (sprite_sel[i] & 0x80) >> 7; - - sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); - } - } } // order sprites according to x coordinate // note that for sprites of equal x coordinate, priority goes to first on the list public virtual void reorder_and_assemble_sprites() { - sprite_ordered_index = 0; - - for (int i = 0; i < 256; i++) - { - for (int j = 0; j < SL_sprites_index; j++) - { - if (SL_sprites[j * 4 + 1] == i) - { - sl_use_index = j; - process_sprite(); - SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; - SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; - sprite_ordered_index++; - } - } - } - bool have_pixel = false; - byte s_pixel = 0; - byte sprite_attr = 0; - - for (int i = 0; i < 160; i++) - { - have_pixel = false; - for (int j = 0; j < SL_sprites_index; j++) - { - if ((i >= (SL_sprites_ordered[j * 4] - 8)) && - (i < SL_sprites_ordered[j * 4]) && - !have_pixel) - { - // we can use the current sprite, so pick out a pixel for it - int t_index = i - (SL_sprites_ordered[j * 4] - 8); - - t_index = 7 - t_index; - - sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1); - sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1); - - s_pixel = (byte)(sprite_data[0] + sprite_data[1]); - sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3]; - - // pixel color of 0 is transparent, so if this is the case we dont have a pixel - if (s_pixel != 0) - { - have_pixel = true; - } - } - } - - if (have_pixel) - { - sprite_present_list[i] = 1; - sprite_pixel_list[i] = s_pixel; - sprite_attr_list[i] = sprite_attr; - } - else - { - sprite_present_list[i] = 0; - } - } } public virtual void SyncState(Serializer ser) From 380b9f085e2e60b3396297c9db88a846caa16ba6 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 25 Mar 2018 17:58:21 -0400 Subject: [PATCH 129/339] GBHawk : GBC Commits round 2 -HDMA -GBA setting --- .../CPUs/LR35902/Interrupts.cs | 1 + .../CPUs/LR35902/LR35902.cs | 1 + .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 116 +++++++++++++++++- .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 19 +-- .../Nintendo/GBHawk/GBHawk.ISettable.cs | 5 + .../Nintendo/GBHawk/GBHawk.IStatable.cs | 3 + .../Consoles/Nintendo/GBHawk/GBHawk.cs | 19 ++- 7 files changed, 151 insertions(+), 13 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index bde8ea4309..f57d04eb4b 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -57,6 +57,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public ushort int_src; public int stop_time; public bool stop_check; + public bool is_GBC; // GBC automatically adds a NOP to avoid the HALT bug (according to Sinimas) private void ResetInterrupts() { diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index bc5c2b3a06..18312da82f 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -480,6 +480,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ser.Sync("int_src", ref int_src); ser.Sync("stop_time", ref stop_time); ser.Sync("stop_check", ref stop_check); + ser.Sync("is_GBC", ref is_GBC); ser.Sync("instruction_pointer", ref instr_pntr); ser.Sync("current instruction", ref cur_instr, false); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index e24995e9b1..56df77b4d9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -16,11 +16,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte HDMA_src_lo; public byte HDMA_dest_hi; public byte HDMA_dest_lo; - public byte HDMA_ctrl; + public byte HDMA_ctrl + { + get { return (byte)(((HDMA_active ? 0 : 1) << 7) | ((HDMA_length >> 16) - 1)); } + } + // controls for tile attributes public byte tile_attr_byte; public int VRAM_sel; + public bool HDMA_active; + public bool HDMA_mode; + public ushort cur_DMA_src; + public ushort cur_DMA_dest; + public int HDMA_length; + public int HDMA_countdown; public override byte ReadReg(int addr) { @@ -140,7 +150,37 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk HDMA_dest_lo = value; break; case 0xFF55: // HDMA5 - HDMA_ctrl = value; + if (!HDMA_active) + { + HDMA_mode = value.Bit(7); + HDMA_countdown = 4; + if (value.Bit(7)) + { + // HDMA during HBlank only + HDMA_active = true; + } + else + { + // HDMA immediately + HDMA_active = true; + Core.HDMA_transfer = true; + } + + // latch read locations + cur_DMA_dest = (ushort)(((HDMA_dest_hi & 0x1F) << 8) | (HDMA_dest_lo & 0xF0)); + cur_DMA_src = (ushort)(((HDMA_src_hi & 0xFF) << 8) | (HDMA_src_lo & 0xF0)); + + HDMA_length = ((HDMA_ctrl & 0x7F) + 1) * 16; + } + else + { + //terminate the transfer + if (!value.Bit(7)) + { + HDMA_active = false; + } + } + break; case 0xFF68: // BGPI BG_pal_index = value; @@ -159,6 +199,71 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void tick() { + // Do HDMA ticks + if (HDMA_active) + { + if (HDMA_countdown == 0) + { + + if (!HDMA_mode) + { + // immediately transfer bytes, 2 bytes per cycles + if (HDMA_length > 0) + { + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; + + if (HDMA_length == 0) + { + HDMA_active = false; + Core.HDMA_transfer = false; + } + } + } + else + { + // only transfer during mode 3, otherwise + if ((STAT & 3) == 3) + { + Core.HDMA_transfer = true; + + if (HDMA_length > 0) + { + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; + + if (HDMA_length == 0) + { + HDMA_active = false; + Core.HDMA_transfer = false; + } + } + } + else + { + Core.HDMA_transfer = false; + } + } + } + else + { + HDMA_countdown--; + } + } + + // the ppu only does anything if it is turned on via bit 7 of LCDC if (LCDC.Bit(7)) { @@ -1180,10 +1285,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("HDMA_src_lo", ref HDMA_src_lo); ser.Sync("HDMA_dest_hi", ref HDMA_dest_hi); ser.Sync("HDMA_dest_lo", ref HDMA_dest_lo); - ser.Sync("HDMA_ctrl", ref HDMA_ctrl); ser.Sync("tile_attr_byte", ref tile_attr_byte); ser.Sync("VRAM_sel", ref VRAM_sel); + ser.Sync("HDMA_active", ref HDMA_active); + ser.Sync("HDMA_mode", ref HDMA_mode); + ser.Sync("cur_DMA_src", ref cur_DMA_src); + ser.Sync("cur_DMA_dest", ref cur_DMA_dest); + ser.Sync("HDMA_length", ref HDMA_length); + ser.Sync("HDMA_countdown", ref HDMA_countdown); base.SyncState(ser); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 9e2a54d165..f7145514c4 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -117,20 +117,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ppu.tick(); if (Use_RTC) { mapper.RTC_Tick(); } - // These things all tick twice as fast in GBC double speed mode - ppu.DMA_tick(); - timer.tick_1(); - serialport.serial_transfer_tick(); - cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); - timer.tick_2(); - - if (double_speed) + if (!HDMA_transfer) { + // These things all tick twice as fast in GBC double speed mode ppu.DMA_tick(); timer.tick_1(); serialport.serial_transfer_tick(); cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); timer.tick_2(); + + if (double_speed) + { + ppu.DMA_tick(); + timer.tick_1(); + serialport.serial_transfer_tick(); + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); + timer.tick_2(); + } } if (in_vblank && !in_vblank_old) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index c43baa6d8c..6bc997b985 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -87,6 +87,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk [DefaultValue(ConsoleModeType.Auto)] public ConsoleModeType ConsoleMode { get; set; } + [DisplayName("CGB in GBA")] + [Description("Emulate GBA hardware running a CGB game, instead of CGB hardware. Relevant only for titles that detect the presense of a GBA, such as Shantae.")] + [DefaultValue(false)] + public bool GBACGB { get; set; } + [DisplayName("RTC Initial Time")] [Description("Set the initial RTC time in terms of elapsed seconds.")] [DefaultValue(0)] diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index 1cafb28dfc..d110c68a4c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -82,11 +82,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("VRAM", ref VRAM, false); ser.Sync("OAM", ref OAM, false); + ser.Sync("_bios", ref _bios, false); + ser.Sync("RAM_Bank", ref RAM_Bank); ser.Sync("VRAM_Bank", ref VRAM_Bank); ser.Sync("is_GBC", ref is_GBC); ser.Sync("double_speed", ref double_speed); ser.Sync("speed_switch", ref speed_switch); + ser.Sync("HDMA_transfer", ref HDMA_transfer); ser.Sync("Use_RTC", ref Use_RTC); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 70035ef974..000f215019 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -56,9 +56,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool is_GBC; public bool double_speed; public bool speed_switch; + public bool HDMA_transfer; // stalls CPU when in progress - public readonly byte[] _rom; - public readonly byte[] _bios; + public byte[] _bios; + public readonly byte[] _rom; public readonly byte[] header = new byte[0x50]; public byte[] cart_RAM; @@ -78,6 +79,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public Audio audio; public SerialPort serialport; + private static byte[] GBA_override = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 }; + [CoreConstructor("GB", "GBC")] public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) { @@ -140,6 +143,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _bios = Bios; + // Here we modify the BIOS if GBA mode is set (credit to ExtraTricky) + if (is_GBC && _syncSettings.GBACGB) + { + for (int i = 0; i < 13; i++) + { + _bios[i + 0xF3] = (byte)((GBA_override[i] + _bios[i + 0xF3]) & 0xFF); + } + } + + // CPU needs to know about GBC status too + cpu.is_GBC = is_GBC; + Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); string hash_md5 = null; From d4ee8f480bbb141ebe1bb38b5949bdeb194a92a2 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 25 Mar 2018 21:22:27 -0400 Subject: [PATCH 130/339] GBHawk: HDMA and GBC halt commits --- .../CPUs/LR35902/LR35902.cs | 37 +++++++++++++++-- .../CPUs/LR35902/Tables_Direct.cs | 41 +++++++++++++------ .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 31 +++++++++++++- 3 files changed, 92 insertions(+), 17 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 18312da82f..3ad5bdd7d4 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -57,6 +57,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public const ushort RD_F = 42; // special read case to pop value into F public const ushort EI_RETI = 43; // reti has no delay in interrupt enable public const ushort INT_GET = 44; + public const ushort GBC_INTERRUPT = 45; public LR35902() { @@ -278,8 +279,21 @@ namespace BizHawk.Emulation.Common.Components.LR35902 }); } halted = false; - // call interrupt processor - INTERRUPT_(); + + if (is_GBC) + { + // call the interrupt processor after 4 extra cycles + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + GBC_INTERRUPT }; + } + else + { + // call interrupt processor + INTERRUPT_(); + } } else if (FlagI) { @@ -295,7 +309,19 @@ namespace BizHawk.Emulation.Common.Components.LR35902 halted = false; if (OnExecFetch != null) OnExecFetch(RegPC); if (TraceCallback != null && !CB_prefix) TraceCallback(State()); - FetchInstruction(ReadMemory(RegPC++)); + if (is_GBC) + { + // extra 4 cycles for GBC + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP }; + } + else + { + FetchInstruction(ReadMemory(RegPC++)); + } } else { @@ -419,6 +445,11 @@ namespace BizHawk.Emulation.Common.Components.LR35902 Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src]; break; + case GBC_INTERRUPT: + // only used when exiting HALT from GBC, an extra NOP is added to avoid HALT bug + INTERRUPT_(); + instr_pntr = 0; + break; } totalExecutedCycles++; } diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index a611b780b9..9eb91d9031 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -76,25 +76,42 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private void HALT_() { + if (FlagI && (EI_pending == 0)) { - // if interrupts are disabled, - // a glitchy decrement to the program counter happens - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - OP_G}; + + if (is_GBC) + { + // in GBC mode, the HALT bug is worked around by simply adding a NOP + // so it just takes 4 cycles longer to reach the next instruction + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP}; + } + else + { // if interrupts are disabled, + // a glitchy decrement to the program counter happens + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP_G}; + } } else { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, - HALT }; - } - + IDLE, + IDLE, + HALT }; + } } private void JR_COND(bool cond) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 56df77b4d9..b8dbb31086 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -31,6 +31,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public ushort cur_DMA_dest; public int HDMA_length; public int HDMA_countdown; + public int HBL_HDMA_count; + public int last_HBL; + public bool HBL_HDMA_go; + public bool HBL_test; public override byte ReadReg(int addr) { @@ -158,6 +162,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { // HDMA during HBlank only HDMA_active = true; + HBL_HDMA_count = 0x10; + last_HBL = LY; + HBL_test = true; + HBL_HDMA_go = false; } else { @@ -228,8 +236,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - // only transfer during mode 3, otherwise - if ((STAT & 3) == 3) + // only transfer during mode 3, and only 16 bytes at a time + if (((STAT & 3) == 3) && (LY != last_HBL) && HBL_test) + { + HBL_HDMA_go = true; + HBL_test = false; + } + + if (HBL_HDMA_go && (HBL_HDMA_count > 0)) { Core.HDMA_transfer = true; @@ -239,16 +253,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); HDMA_length--; + HBL_HDMA_count--; Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); HDMA_length--; + HBL_HDMA_count--; if (HDMA_length == 0) { HDMA_active = false; Core.HDMA_transfer = false; } + + if ((HBL_HDMA_count == 0) && (HDMA_length != 0)) + { + HBL_test = true; + last_HBL = LY; + HBL_HDMA_count = 0x10; + } } } else @@ -1294,6 +1317,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("cur_DMA_dest", ref cur_DMA_dest); ser.Sync("HDMA_length", ref HDMA_length); ser.Sync("HDMA_countdown", ref HDMA_countdown); + ser.Sync("HBL_HDMA_count", ref HBL_HDMA_count); + ser.Sync("last_HBL", ref last_HBL); + ser.Sync("HBL_HDMA_go", ref HBL_HDMA_go); + ser.Sync("HBL_test", ref HBL_test); base.SyncState(ser); } From 7b711cb890b968e49bd289092ab77894b9cceb4d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 26 Mar 2018 07:42:55 +0100 Subject: [PATCH 131/339] Better error handling when parsing TZX files - #1158 --- .../Hardware/Datacorder/DatacorderDevice.cs | 49 +++++++++++-------- .../SinclairSpectrum/Media/MediaSerializer.cs | 10 ++++ .../Media/Tape/TzxSerializer.cs | 37 ++++++++++++++ 3 files changed, 76 insertions(+), 20 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 93b91348ac..37f82f5f52 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -329,30 +329,39 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void LoadTape(byte[] tapeData) { - // attempt TZX deserialization + // check TZX first TzxSerializer tzxSer = new TzxSerializer(this); - try + if (tzxSer.CheckType(tapeData)) { - tzxSer.DeSerialize(tapeData); - return; + // this file has a tzx header - attempt serialization + try + { + tzxSer.DeSerialize(tapeData); + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid TZX header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } } - catch (Exception ex) + else { - // TAP format not detected - var e = ex; - } - - // attempt TAP deserialization - TapSerializer tapSer = new TapSerializer(this); - try - { - tapSer.DeSerialize(tapeData); - return; - } - catch (Exception ex) - { - // TAP format not detected - var e = ex; + TapSerializer tapSer = new TapSerializer(this); + try + { + tapSer.DeSerialize(tapeData); + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nAn exception was thrown whilst data from this tape image was being parsed as TAP.\n\n" + e.ToString()); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs index 44c99c28ff..2f41ca7925 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs @@ -58,6 +58,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum "DeSerialize operation is not implemented for this serializer"); } + /// + /// Serializer does a quick check, returns TRUE if file is detected as this type + /// + /// + public virtual bool CheckType(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Check type operation is not implemented for this serializer"); + } + #region Static Tools /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index 91b5037426..2c3078f3f3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -60,6 +60,43 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + /// + /// Returns TRUE if tzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + /* + // TZX Header + length: 10 bytes + Offset Value Type Description + 0x00 "ZXTape!" ASCII[7] TZX signature + 0x07 0x1A BYTE End of text file marker + 0x08 1 BYTE TZX major revision number + 0x09 20 BYTE TZX minor revision number + */ + + // check whether this is a valid tzx format file by looking at the identifier in the header + // (first 7 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 7); + // and 'end of text' marker + byte eotm = data[7]; + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident != "ZXTape!" || eotm != 0x1A) + { + // this is not a valid TZX format file + return false; + } + else + { + return true; + } + } + /// /// DeSerialization method /// From 5f71e2428c7de2831a5ba7476974f70ab5ae85d9 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 26 Mar 2018 08:16:04 +0100 Subject: [PATCH 132/339] ZXHawk: fixed array out of bounds exception with certain tiny (3 byte) tzx turbo data blocks. Operations Wolf & Thunderbolt now load - #1158 --- .../SinclairSpectrum/Media/Tape/TzxSerializer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index 2c3078f3f3..d31b0eb2f6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -362,8 +362,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int bitinbyte = data[_position + 12]; int pause = GetWordValue(data, _position + 13); + int blockLen = 0xFFFFFF & GetInt32(data, _position + 0x0F); + byte[] bLenArr = data.Skip(_position + 0x0F).Take(3).ToArray(); + _position += 0x12; byte[] tmp = new byte[blockLen]; @@ -1526,7 +1529,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // dont get description info for Pure Data Blocks if (dataBlockType != DataBlockType.Pure) { - if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || blockdata[1] == 3) + if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || (blockdata[1] == 3 && blockdata.Length > 3)) { // This is the program header string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); @@ -1556,12 +1559,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // this is a data block description = "Data Block " + (blockSize - 2) + "bytes"; - block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); + block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize).ToString() + " Bytes"); } else { // other type - description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize - 2); + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize); //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); block.AddMetaData(BlockDescriptorTitle.Undefined, description); } From b0ae89f92490a7206ff8c377dad89de9472b4c88 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 26 Mar 2018 08:37:18 +0100 Subject: [PATCH 133/339] ZXHawk: TZX block 0x19 - added correct length placeholder - #1158 --- .../Media/Tape/TzxSerializer.cs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index d31b0eb2f6..1cccd7ec06 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -697,7 +697,38 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ---- */ private void ProcessBlockID19(byte[] data) { - string test = "dgfg"; + // not currently implemented properly + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x19; + t.BlockDescription = BlockType.Generalized_Data_Block; + t.DataPeriods = new List(); + + int blockLen = GetInt32(data, _position); + _position += 4; + + int pause = GetWordValue(data, _position); + _position += 2; + + int totp = GetInt32(data, _position); + _position += 4; + + int npp = data[_position++]; + + int asp = data[_position++]; + + int totd = GetInt32(data, _position); + _position += 4; + + int npd = data[_position++]; + + int asd = data[_position++]; + + // add the block + _datacorder.DataBlocks.Add(t); + + // advance the position to the next block + _position += blockLen; } #endregion From 426c3ddaea71b6ad793d8916da7288f6013cd745 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 26 Mar 2018 09:07:36 +0100 Subject: [PATCH 134/339] ZXHawk: All TZX blocks now handled correctly (in many cases these are not serialized correctly, but lengths are correct so the entire tape image should be loaded without throwing an exception) - #1158 --- .../Media/Tape/TzxSerializer.cs | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs index 1cccd7ec06..1bd5e6179b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs @@ -630,6 +630,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.BlockDescription = BlockType.CSW_Recording; t.DataPeriods = new List(); + int blockLen = GetInt32(data, _position); + _position += 4; + + _position += blockLen; + // add the block _datacorder.DataBlocks.Add(t); } @@ -814,8 +819,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // advance to next block _position += nameLength; - - ; } #endregion @@ -852,6 +855,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum All blocks are included in the block count!. */ private void ProcessBlockID23(byte[] data) { + // not implemented properly + TapeDataBlock t = new TapeDataBlock(); t.BlockID = 0x23; t.DataPeriods = new List(); @@ -1450,14 +1455,40 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region ID 16 - C64 ROM Type Data Block private void ProcessBlockID16(byte[] data) { + // zxhawk will not implement this block. it will however handle it so subsequent blocks can be parsed + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x16; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.C64_ROM_Type_Data_Block; + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + int blockLen = GetInt32(data, _position); + _position += blockLen; } #endregion #region ID 17 - C64 Turbo Tape Data Block private void ProcessBlockID17(byte[] data) { + // zxhawk will not implement this block. it will however handle it so subsequent blocks can be parsed + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x17; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.C64_Turbo_Tape_Data_Block; + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + int blockLen = GetInt32(data, _position); + _position += blockLen; } #endregion From f016c60b13a4b6e7bc2bded9a9f740279c742a9c Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 26 Mar 2018 11:46:02 -0400 Subject: [PATCH 135/339] GBHawk: GBC Audio Quirks --- .../Consoles/Nintendo/GBHawk/Audio.cs | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 2574978258..488278b4cd 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -155,7 +155,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF3F: if (WAVE_enable) { - if (WAVE_can_get) { ret = Wave_RAM[WAVE_wave_cntr >> 1]; } + if (WAVE_can_get || Core.is_GBC) { ret = Wave_RAM[WAVE_wave_cntr >> 1]; } else { ret = 0xFF; } } else { ret = Wave_RAM[addr & 0x0F]; } @@ -367,7 +367,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (WAVE_trigger) { // some corruption occurs if triggering while reading - if (WAVE_enable && WAVE_intl_cntr == 2) + if (WAVE_enable && (WAVE_intl_cntr == 2) && !Core.is_GBC) { // we want to use the previous wave cntr value since it was just incremented int t_wave_cntr = (WAVE_wave_cntr + 1) & 31; @@ -500,7 +500,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF3F: if (WAVE_enable) { - if (WAVE_can_get) { Wave_RAM[WAVE_wave_cntr >> 1] = value; } + if (WAVE_can_get || Core.is_GBC) { Wave_RAM[WAVE_wave_cntr >> 1] = value; } } else { Wave_RAM[addr & 0xF] = value; } @@ -508,25 +508,38 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } // when power is off, only length counters and waveRAM are effected by writes + // ON GBC, length counters cannot be written to either else { switch (addr) { case 0xFF11: // NR11 (sound length / wave pattern duty %) - SQ1_length = (ushort)(64 - (value & 0x3F)); - SQ1_len_cntr = SQ1_length; + if (!Core.is_GBC) + { + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; + } break; case 0xFF16: // NR21 (sound length / wave pattern duty %) - SQ2_length = (ushort)(64 - (value & 0x3F)); - SQ2_len_cntr = SQ2_length; + if (!Core.is_GBC) + { + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; + } break; case 0xFF1B: // NR31 (length) - WAVE_length = (ushort)(256 - value); - WAVE_len_cntr = WAVE_length; + if (!Core.is_GBC) + { + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; + } break; case 0xFF20: // NR41 (length) - NOISE_length = (ushort)(64 - (value & 0x3F)); - NOISE_len_cntr = NOISE_length; + if (!Core.is_GBC) + { + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; + } break; case 0xFF26: // NR52 (ctrl) AUD_CTRL_power = (value & 0x80) > 0; @@ -865,7 +878,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { for (int i = 0; i < 0x16; i++) { - WriteReg(0xFF10 + i, 0); + WriteReg(0xFF10 + i, 0); } // duty and length are reset @@ -878,6 +891,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ1_output = SQ2_output = WAVE_output = NOISE_output = 0; + // on GBC, lengths are also reset + if (Core.is_GBC) + { + SQ1_length = SQ2_length = WAVE_length = NOISE_length = 0; + SQ1_len_cntr = SQ2_len_cntr = WAVE_len_cntr = NOISE_len_cntr = 0; + } + sequencer_len = 0; sequencer_vol = 0; sequencer_swp = 0; From e8cd3b259c93e421e18c2395c00e0a02ca4b0801 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 26 Mar 2018 16:47:04 +0100 Subject: [PATCH 136/339] ZXHawk: Small change to 128 paging --- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 146 ++++-------------- 1 file changed, 31 insertions(+), 115 deletions(-) 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 e01525017a..bcebb55542 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -74,71 +74,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - /* - - // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; - - // Kempston joystick input takes priority over all other input - // if this is detected just return the kempston byte - if ((port & 0xe0) == 0 || (port & 0x20) == 0) - { - if (LocateUniqueJoystick(JoystickType.Kempston) != null) - return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; - - InputRead = true; - } - else if (lowBitReset) - { - // Even I/O address so get input from keyboard - KeyboardDevice.ReadPort(port, ref result); - - // not a lagframe - InputRead = true; - - // tape loading monitor cycle - TapeDevice.MonitorRead(); - - // process tape INs - TapeDevice.ReadPort(port, ref result); - } - else if ((port & 0xc002) == 0xc000) - { - // AY sound chip - result = (int)AYDevice.PortRead(); - } - else - { - // devices other than the ULA will respond here - - // Kempston Mouse (not implemented yet) - - - // If this is an unused port the floating memory bus should be returned - // Floating bus is read on the previous cycle - int _tStates = CurrentFrameCycle - 1; - - // if we are on the top or bottom border return 0xff - if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) - { - result = 0xff; - } - else - { - if (ULADevice.floatingBusTable[_tStates] < 0) - { - result = 0xff; - } - else - { - result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); - } - } - } - - */ - return (byte)result; } @@ -157,47 +92,45 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // get a BitArray of the value byte BitArray bits = new BitArray(new byte[] { value }); - long currT = CPU.TotalExecutedCycles; - + // handle AY port writes AYDevice.WritePort(port, value); - // paging - if (port == 0x7ffd) + // memory paging + // this is controlled by writes to port 0x7ffd + // but it is only partially decoded so it actually responds to any port with bits 1 and 15 reset + if (portBits[1] == false && portBits[15] == false) { - //if (PagingDisabled) - //return; - - // Bits 0, 1, 2 select the RAM page - var rp = value & 0x07; - if (rp < 8) - RAMPaged = rp; - - // bit 3 controls shadow screen - SHADOWPaged = bits[3]; - - if (SHADOWPaged == false) + // if paging is disabled then all writes to this port are ignored until the next reboot + if (!PagingDisabled) { + // Bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (RAMPaged != rp && rp < 8) + RAMPaged = rp; + // bit 3 controls shadow screen + if (SHADOWPaged != bits[3]) + SHADOWPaged = bits[3]; + + // ROM page + if (bits[4]) + { + // 48k basic rom + ROMPaged = 1; + } + else + { + // 128k editor and menu system + ROMPaged = 0; + } + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; } else { - + // no changes to paging } - - // ROM page - if (bits[4]) - { - // 48k basic rom - ROMPaged = 1; - } - else - { - // 128k editor and menu system - ROMPaged = 0; - } - - // Bit 5 set signifies that paging is disabled until next reboot - PagingDisabled = bits[5]; } // Check whether the low bit is reset @@ -229,25 +162,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice.WritePort(port, value); // Tape - //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); - - } - /* - // Active AY Register - if ((port & 0xc002) == 0xc000) - { - var reg = value & 0x0f; - AYDevice.SelectedRegister = reg; - CPU.TotalExecutedCycles += 3; - } - - // AY Write - if ((port & 0xc002) == 0x8000) - { - AYDevice.PortWrite(value); - CPU.TotalExecutedCycles += 3; + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } - */ } } } From 2f35bec26461ee389c6974a635ebb565b2754522 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 26 Mar 2018 17:29:31 +0100 Subject: [PATCH 137/339] ZXHawk: removed maskable interrupt tape trap. Does not work as it should. --- .../SinclairSpectrum/Machine/SpectrumBase.Memory.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 436e23e558..5c31caeb7b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -211,13 +211,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // if (addr == 56) { - TapeDevice.MaskableInterruptCount++; + //TapeDevice.MaskableInterruptCount++; - if (TapeDevice.MaskableInterruptCount > 20) - { - TapeDevice.MaskableInterruptCount = 0; - TapeDevice.AutoStopTape(); - } + //if (TapeDevice.MaskableInterruptCount > 50) + //{ + //TapeDevice.MaskableInterruptCount = 0; + //TapeDevice.AutoStopTape(); + //} //TapeDevice?.AutoStopTape(); return; From 1c40bbd49b3e80b5c1983f471492b2991d69de5b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 26 Mar 2018 14:34:47 -0400 Subject: [PATCH 138/339] SMSHawK: Add Port5 variable (link cable) -Fixes Pac Attack --- BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs | 1 + BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs index c01b5f50a3..f2b241bd3a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs @@ -64,6 +64,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem ser.Sync("Bios_bank", ref Bios_bank); ser.Sync("Port01", ref Port01); ser.Sync("Port02", ref Port02); + ser.Sync("Port05", ref Port05); ser.Sync("Port3E", ref Port3E); ser.Sync("Port3F", ref Port3F); ser.Sync("Controller1SelectHigh", ref Controller1SelectHigh); diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs index 0f393dff0c..885fde2dab 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs @@ -222,6 +222,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem private byte Port01 = 0xFF; private byte Port02 = 0xFF; + private byte Port05 = 0x00; private byte Port3E = 0xAF; private byte Port3F = 0xFF; private byte PortDE = 0x00; @@ -315,7 +316,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem case 0x02: return Port02; case 0x03: return 0x00; case 0x04: return 0xFF; - case 0x05: return 0x00; + case 0x05: return Port05; case 0x06: return 0xFF; case 0x3E: return Port3E; default: return 0xFF; @@ -356,6 +357,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem { case 0x01: Port01 = value; break; case 0x02: Port02 = value; break; + case 0x05: Port05 = value; break; case 0x06: PSG.StereoPanning = value; break; case 0x3E: Port3E = value; break; case 0x3F: Port3F = value; break; From dcf3673f5cd8a95a4c744c2e8c1c3e7af49e47d0 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 26 Mar 2018 14:56:10 -0400 Subject: [PATCH 139/339] RAM Watch: Update values when clear changes is pressed -fixes #1156 --- BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs index a208731d81..f480085983 100644 --- a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs +++ b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs @@ -855,6 +855,7 @@ namespace BizHawk.Client.EmuHawk private void ClearChangeCountsMenuItem_Click(object sender, EventArgs e) { _watches.ClearChangeCounts(); + UpdateValues(); } private void MoveUpMenuItem_Click(object sender, EventArgs e) From 75d86e995ef74373622e3db516eb33b665a7268a Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 27 Mar 2018 09:52:31 -0400 Subject: [PATCH 140/339] GBHawk: GBC commits round 3 -Color -BG tile properties --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 234 ++++++++++++++---- .../Consoles/Nintendo/GBHawk/PPU.cs | 4 +- 2 files changed, 191 insertions(+), 47 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index b8dbb31086..ff89659346 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -7,15 +7,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public class GBC_PPU : PPU { - // these are the uniquely GBC variables - public byte BG_pal_index; - public byte pal_transfer_byte; - public byte spr_pal_index; - public byte spr_transfer_byte; + public uint[] BG_palette = new uint[32]; + public uint[] OBJ_palette = new uint[32]; + + // individual byte used in palette colors + public byte[] BG_bytes = new byte[64]; + public byte[] OBJ_bytes = new byte[64]; + public bool BG_bytes_inc; + public bool OBJ_bytes_inc; + public byte BG_bytes_index; + public byte OBJ_bytes_index; + public byte BG_transfer_byte; + public byte OBJ_transfer_byte; + + // HDMA is unique to GBC, do it as part of the PPU tick public byte HDMA_src_hi; public byte HDMA_src_lo; public byte HDMA_dest_hi; public byte HDMA_dest_lo; + + // accessors for derived values + public byte BG_pal_ret + { + get { return (byte)(((BG_bytes_inc ? 1 : 0) << 7) | (BG_bytes_index & 0x3F)); } + } + + public byte OBJ_pal_ret + { + get { return (byte)(((OBJ_bytes_inc ? 1 : 0) << 7) | (BG_bytes_index & 0x1F)); } + } + public byte HDMA_ctrl { get { return (byte)(((HDMA_active ? 0 : 1) << 7) | ((HDMA_length >> 16) - 1)); } @@ -23,8 +44,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // controls for tile attributes - public byte tile_attr_byte; public int VRAM_sel; + public bool BG_V_flip; public bool HDMA_active; public bool HDMA_mode; public ushort cur_DMA_src; @@ -61,10 +82,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF53: ret = HDMA_dest_hi; break; // HDMA3 case 0xFF54: ret = HDMA_dest_lo; break; // HDMA4 case 0xFF55: ret = HDMA_ctrl; break; // HDMA5 - case 0xFF68: ret = BG_pal_index; break; // BGPI - case 0xFF69: ret = pal_transfer_byte; break; // BGPD - case 0xFF6A: ret = spr_pal_index; break; // OBPI - case 0xFF6B: ret = spr_transfer_byte; break; // OBPD + case 0xFF68: ret = BG_pal_ret; break; // BGPI + case 0xFF69: ret = BG_transfer_byte; break; // BGPD + case 0xFF6A: ret = OBJ_pal_ret; break; // OBPI + case 0xFF6B: ret = OBJ_transfer_byte; break; // OBPD } return ret; @@ -191,16 +212,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 0xFF68: // BGPI - BG_pal_index = value; + BG_bytes_index = (byte)(value & 0x3F); + BG_bytes_inc = ((value & 0x80) == 0x80); break; case 0xFF69: // BGPD - pal_transfer_byte = value; + BG_transfer_byte = value; + BG_bytes[BG_bytes_index] = value; + + // change the appropriate palette color + color_compute_BG(); + + if (BG_bytes_inc) { BG_bytes_index++; BG_bytes_index &= 0x3F; } break; case 0xFF6A: // OBPI - spr_pal_index = value; + OBJ_bytes_index = (byte)(value & 0x3F); + OBJ_bytes_inc = ((value & 0x80) == 0x80); break; case 0xFF6B: // OBPD - spr_transfer_byte = value; + OBJ_transfer_byte = value; + OBJ_bytes[OBJ_bytes_index] = value; + + // change the appropriate palette color + color_compute_OBJ(); + + if (OBJ_bytes_inc) { OBJ_bytes_index++; OBJ_bytes_index &= 0x3F; } break; } } @@ -237,7 +272,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk else { // only transfer during mode 3, and only 16 bytes at a time - if (((STAT & 3) == 3) && (LY != last_HBL) && HBL_test) + if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test) { HBL_HDMA_go = true; HBL_test = false; @@ -646,24 +681,38 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // start shifting data into the LCD if (render_counter >= (render_offset + 8)) { - pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; - - int ref_pixel = pixel; - if (LCDC.Bit(0)) + if (tile_data_latch[2].Bit(5)) { - pixel = (BGP >> (pixel * 2)) & 3; + pixel = tile_data_latch[0].Bit(render_counter % 8) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(render_counter % 8) ? 2 : 0; } else { - pixel = 0; + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; } - + + int ref_pixel = pixel; + + if (LCDC.Bit(0)) + { + //pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + //pixel = 0; + } + + int pal_num = tile_data_latch[2] & 0x7; + + bool use_sprite = false; + + int s_pixel = 0; + // now we have the BG pixel, we next need the sprite pixel if (!no_sprites) { - bool have_sprite = false; - int s_pixel = 0; + bool have_sprite = false; int sprite_attr = 0; if (sprite_present_list[pixel_counter] == 1) @@ -675,7 +724,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (have_sprite) { - bool use_sprite = false; if (LCDC.Bit(1)) { if (!sprite_attr.Bit(7)) @@ -695,6 +743,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (use_sprite) { + pal_num = sprite_attr & 7; + + /* if (sprite_attr.Bit(4)) { pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; @@ -702,13 +753,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk else { pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; - } + } + */ } } } // based on sprite priority and pixel values, pick a final pixel color - Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel]; + if (use_sprite) + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)OBJ_palette[pal_num * 4 + s_pixel]; + } + else + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)BG_palette[pal_num * 4 + pixel]; + } + pixel_counter++; if (pixel_counter == 160) @@ -766,8 +826,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; - tile_attr_byte = Core.VRAM[0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; - VRAM_sel = tile_attr_byte.Bit(3) ? 1 : 0; + tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; + VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; + BG_V_flip = tile_data[2].Bit(6); } else { @@ -788,6 +849,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { y_scroll_offset = (scroll_y + LY) % 8; + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + if (LCDC.Bit(4)) { tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2]; @@ -814,6 +880,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { y_scroll_offset = (scroll_y + LY) % 8; + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + if (LCDC.Bit(4)) { // if LCDC somehow changed between the two reads, make sure we have a positive number @@ -871,8 +942,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; - tile_attr_byte = Core.VRAM[0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; - VRAM_sel = tile_attr_byte.Bit(3) ? 1 : 0; + tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; + VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; } else { @@ -890,6 +961,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { y_scroll_offset = (window_y_tile_inc) % 8; + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + if (LCDC.Bit(4)) { @@ -918,6 +994,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if ((window_counter % 2) == 0) { y_scroll_offset = (window_y_tile_inc) % 8; + + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + if (LCDC.Bit(4)) { // if LCDC somehow changed between the two reads, make sure we have a positive number @@ -1004,6 +1086,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk latch_new_data = false; tile_data_latch[0] = tile_data[0]; tile_data_latch[1] = tile_data[1]; + tile_data_latch[2] = tile_data[2]; } } @@ -1069,6 +1152,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void process_sprite() { int y; + int VRAM_temp = SL_sprites[sl_use_index * 4 + 3].Bit(3) ? 1 : 0; if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) { @@ -1076,15 +1160,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { y = LY - (SL_sprites[sl_use_index * 4] - 16); y = 15 - y; - sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { y = LY - (SL_sprites[sl_use_index * 4] - 16); y = 7 - y; - sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; } } else @@ -1092,14 +1176,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (LCDC.Bit(2)) { y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; } } @@ -1298,19 +1382,69 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } + public void color_compute_BG() + { + uint R; + uint G; + uint B; + + if ((BG_bytes_index % 2) == 0) + { + R = (uint)(BG_bytes[BG_bytes_index] & 0x1F); + G = (uint)(((BG_bytes[BG_bytes_index] & 0xE0) | ((BG_bytes[BG_bytes_index + 1] & 0x03) << 8)) >> 5); + B = (uint)((BG_bytes[BG_bytes_index + 1] & 0x7C) >> 2); + } + else + { + R = (uint)(BG_bytes[BG_bytes_index - 1] & 0x1F); + G = (uint)(((BG_bytes[BG_bytes_index - 1] & 0xE0) | ((BG_bytes[BG_bytes_index] & 0x03) << 8)) >> 5); + B = (uint)((BG_bytes[BG_bytes_index] & 0x7C) >> 2); + } + + uint retR = ((R * 13 + G * 2 + B) >> 1) & 0xFF; + uint retG = ((G * 3 + B) << 1) & 0xFF; + uint retB = ((R * 3 + G * 2 + B * 11) >> 1) & 0xFF; + + BG_palette[BG_bytes_index >> 1] = (uint)(0xFF000000 | (retR << 16) | (retG << 8) | retB); + } + + public void color_compute_OBJ() + { + uint R; + uint G; + uint B; + + if ((OBJ_bytes_index % 2) == 0) + { + R = (uint)(OBJ_bytes[OBJ_bytes_index] & 0x1F); + G = (uint)(((OBJ_bytes[OBJ_bytes_index] & 0xE0) | ((OBJ_bytes[OBJ_bytes_index + 1] & 0x03) << 8)) >> 5); + B = (uint)((OBJ_bytes[OBJ_bytes_index + 1] & 0x7C) >> 2); + } + else + { + R = (uint)(OBJ_bytes[OBJ_bytes_index - 1] & 0x1F); + G = (uint)(((OBJ_bytes[OBJ_bytes_index - 1] & 0xE0) | ((OBJ_bytes[OBJ_bytes_index] & 0x03) << 8)) >> 5); + B = (uint)((OBJ_bytes[OBJ_bytes_index] & 0x7C) >> 2); + } + + uint retR = ((R * 13 + G * 2 + B) >> 1) & 0xFF; + uint retG = ((G * 3 + B) << 1) & 0xFF; + uint retB = ((R * 3 + G * 2 + B * 11) >> 1) & 0xFF; + + OBJ_palette[OBJ_bytes_index >> 1] = (uint)(0xFF000000 | (retR << 16) | (retG << 8) | retB); + } + public override void SyncState(Serializer ser) { - ser.Sync("BG_pal_index", ref BG_pal_index); - ser.Sync("pal_transfer_byte", ref pal_transfer_byte); - ser.Sync("spr_pal_index", ref spr_pal_index); - ser.Sync("spr_transfer_byte", ref spr_transfer_byte); + ser.Sync("pal_transfer_byte", ref BG_transfer_byte); + ser.Sync("spr_transfer_byte", ref OBJ_transfer_byte); ser.Sync("HDMA_src_hi", ref HDMA_src_hi); ser.Sync("HDMA_src_lo", ref HDMA_src_lo); ser.Sync("HDMA_dest_hi", ref HDMA_dest_hi); ser.Sync("HDMA_dest_lo", ref HDMA_dest_lo); - ser.Sync("tile_attr_byte", ref tile_attr_byte); ser.Sync("VRAM_sel", ref VRAM_sel); + ser.Sync("BG_V_flip", ref BG_V_flip); ser.Sync("HDMA_active", ref HDMA_active); ser.Sync("HDMA_mode", ref HDMA_mode); ser.Sync("cur_DMA_src", ref cur_DMA_src); @@ -1322,6 +1456,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("HBL_HDMA_go", ref HBL_HDMA_go); ser.Sync("HBL_test", ref HBL_test); + ser.Sync("BG_palette", ref BG_palette, false); + ser.Sync("OBJ_palette", ref OBJ_palette, false); + + ser.Sync("BG_bytes", ref BG_bytes, false); + ser.Sync("OBJ_bytes", ref OBJ_bytes, false); + ser.Sync("BG_bytes_inc", ref BG_bytes_inc); + ser.Sync("OBJ_bytes_inc", ref OBJ_bytes_inc); + ser.Sync("BG_bytes_index", ref BG_bytes_index); + ser.Sync("OBJ_bytes_index", ref OBJ_bytes_index); + base.SyncState(ser); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index bda927e0c5..826a80d531 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -69,8 +69,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int temp_fetch; public int tile_inc; public bool pre_render; - public byte[] tile_data = new byte[2]; - public byte[] tile_data_latch = new byte[2]; + public byte[] tile_data = new byte[3]; + public byte[] tile_data_latch = new byte[3]; public int latch_counter; public bool latch_new_data; public int render_counter; From 3b9835274ac26628eaad2d2541c95cfdbac834a9 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 27 Mar 2018 10:50:55 -0400 Subject: [PATCH 141/339] GBHawk: GBC initial Compatibility --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 78 +++++++++++++++++-- .../Nintendo/GBHawk/GBHawk.IMemoryDomains.cs | 9 ++- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 43 ++++++---- .../Consoles/Nintendo/GBHawk/PPU.cs | 7 ++ 4 files changed, 116 insertions(+), 21 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index ff89659346..c8956ec132 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -7,9 +7,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public class GBC_PPU : PPU { - public uint[] BG_palette = new uint[32]; - public uint[] OBJ_palette = new uint[32]; - // individual byte used in palette colors public byte[] BG_bytes = new byte[64]; public byte[] OBJ_bytes = new byte[64]; @@ -739,6 +736,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { use_sprite = true; } + + // There is another priority bit in GBC, that can still override sprite priority + if (LCDC.Bit(0) && tile_data_latch[2].Bit(7)) + { + use_sprite = false; + } } if (use_sprite) @@ -1456,9 +1459,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("HBL_HDMA_go", ref HBL_HDMA_go); ser.Sync("HBL_test", ref HBL_test); - ser.Sync("BG_palette", ref BG_palette, false); - ser.Sync("OBJ_palette", ref OBJ_palette, false); - ser.Sync("BG_bytes", ref BG_bytes, false); ser.Sync("OBJ_bytes", ref OBJ_bytes, false); ser.Sync("BG_bytes_inc", ref BG_bytes_inc); @@ -1468,5 +1468,71 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk base.SyncState(ser); } + + public override void Reset() + { + LCDC = 0; + STAT = 0x80; + scroll_y = 0; + scroll_x = 0; + LY = 0; + LYC = 0; + DMA_addr = 0xFF; + BGP = 0xFF; + obj_pal_0 = 0xFF; + obj_pal_1 = 0xFF; + window_y = 0x0; + window_x = 0x0; + window_x_latch = 0xFF; + LY_inc = 1; + no_scan = false; + OAM_access_read = true; + VRAM_access_read = true; + OAM_access_write = true; + VRAM_access_write = true; + DMA_OAM_access = true; + + cycle = 0; + LYC_INT = false; + HBL_INT = false; + VBL_INT = false; + OAM_INT = false; + + stat_line = false; + stat_line_old = false; + + window_counter = 0; + window_pre_render = false; + window_started = false; + window_tile_inc = 0; + window_y_tile = 0; + window_x_tile = 0; + window_y_tile_inc = 0; + + BG_bytes_inc = false; + OBJ_bytes_inc = false; + BG_bytes_index = 0; + OBJ_bytes_index = 0; + BG_transfer_byte = 0; + OBJ_transfer_byte = 0; + + HDMA_src_hi = 0; + HDMA_src_lo = 0; + HDMA_dest_hi = 0; + HDMA_dest_lo = 0; + + VRAM_sel = 0; + BG_V_flip = false; + HDMA_active = false; + HDMA_mode = false; + cur_DMA_src = 0; + cur_DMA_dest = 0; + HDMA_length = 0; + HDMA_countdown = 0; + HBL_HDMA_count = 0; + last_HBL = 0; + HBL_HDMA_go = false; + HBL_test = false; + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs index b336e091fe..ecb0940aa5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs @@ -40,8 +40,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _rom.Length, MemoryDomain.Endian.Little, addr => _rom[addr], - (addr, value) => ZP_RAM[addr] = value, + (addr, value) => _rom[addr] = value, 1), + new MemoryDomainDelegate( + "VRAM", + VRAM.Length, + MemoryDomain.Endian.Little, + addr => VRAM[addr], + (addr, value) => VRAM[addr] = value, + 1) }; if (cart_RAM != null) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 000f215019..7c7ae38b9b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -186,15 +186,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk iptr0 = Marshal.AllocHGlobal(VRAM.Length + 1); iptr1 = Marshal.AllocHGlobal(OAM.Length + 1); - iptr2 = Marshal.AllocHGlobal(color_palette.Length * 2 * 8 + 1); - iptr3 = Marshal.AllocHGlobal(color_palette.Length * 8 + 1); + iptr2 = Marshal.AllocHGlobal(color_palette.Length * 8 * 8 + 1); + iptr3 = Marshal.AllocHGlobal(color_palette.Length * 8 * 8 + 1); _scanlineCallback = null; } #region GPUViewer - public bool IsCGBMode() => false; + public bool IsCGBMode() => is_GBC; public IntPtr iptr0 = IntPtr.Zero; public IntPtr iptr1 = IntPtr.Zero; @@ -208,21 +208,36 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Marshal.Copy(VRAM, 0, iptr0, VRAM.Length); Marshal.Copy(OAM, 0, iptr1, OAM.Length); - int[] cp2 = new int[8]; - for (int i = 0; i < 4; i++) + if (is_GBC) { - cp2[i] = (int)color_palette[(ppu.obj_pal_0 >> (i * 2)) & 3]; - cp2[i + 4] = (int)color_palette[(ppu.obj_pal_1 >> (i * 2)) & 3]; - } - Marshal.Copy(cp2, 0, iptr2, cp2.Length); + int[] cp2 = new int[32]; + int[] cp = new int[32]; + for (int i = 0; i < 32; i++) + { + cp2[i] = (int)ppu.OBJ_palette[i]; + cp[i] = (int)ppu.BG_palette[i]; + } - int[] cp = new int[4]; - for (int i = 0; i < 4; i++) + Marshal.Copy(cp2, 0, iptr2, ppu.OBJ_palette.Length); + Marshal.Copy(cp, 0, iptr3, ppu.BG_palette.Length); + } + else { - cp[i] = (int)color_palette[(ppu.BGP >> (i * 2)) & 3]; - } - Marshal.Copy(cp, 0, iptr3, cp.Length); + int[] cp2 = new int[8]; + for (int i = 0; i < 4; i++) + { + cp2[i] = (int)color_palette[(ppu.obj_pal_0 >> (i * 2)) & 3]; + cp2[i + 4] = (int)color_palette[(ppu.obj_pal_1 >> (i * 2)) & 3]; + } + Marshal.Copy(cp2, 0, iptr2, cp2.Length); + int[] cp = new int[4]; + for (int i = 0; i < 4; i++) + { + cp[i] = (int)color_palette[(ppu.BGP >> (i * 2)) & 3]; + } + Marshal.Copy(cp, 0, iptr3, cp.Length); + } return new GPUMemoryAreas(iptr0, iptr1, iptr2, iptr3); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 826a80d531..90f3939443 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -9,6 +9,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public GBHawk Core { get; set; } + public uint[] BG_palette = new uint[32]; + public uint[] OBJ_palette = new uint[32]; + // register variables public byte LCDC; public byte STAT; @@ -154,6 +157,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public virtual void SyncState(Serializer ser) { + + ser.Sync("BG_palette", ref BG_palette, false); + ser.Sync("OBJ_palette", ref OBJ_palette, false); + ser.Sync("LCDC", ref LCDC); ser.Sync("STAT", ref STAT); ser.Sync("scroll_y", ref scroll_y); From 86dd0b4a063dd2e9da6e78c5e7cac23e520bb7d4 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 27 Mar 2018 14:12:51 +0100 Subject: [PATCH 142/339] ZXHawk: Started tapedevice independence implementation --- .../Hardware/Datacorder/DatacorderDevice.cs | 43 ++++++++++++++++--- .../SinclairSpectrum/Machine/SpectrumBase.cs | 16 ++++++- .../Machine/ZXSpectrum128K/ZX128.cs | 3 ++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 3 ++ .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 3 ++ .../Machine/ZXSpectrum48K/ZX48.cs | 3 ++ .../Computers/SinclairSpectrum/ZXSpectrum.cs | 8 ++++ 7 files changed, 71 insertions(+), 8 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 37f82f5f52..c67ce41ee4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -26,7 +26,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public DatacorderDevice() { - + } /// @@ -37,13 +37,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _machine = machine; _cpu = _machine.CPU; - _buzzer = machine.BuzzerDevice; + _buzzer = machine.TapeBuzzer; } + + #endregion #region State Information + /// + /// Internal counter used to trigger tape buzzer output + /// + private int counter = 0; + /// /// The index of the current tape data block that is loaded /// @@ -145,7 +152,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void StartFrame() { //if (TapeIsPlaying && AutoPlay) - //FlashLoad(); + //FlashLoad(); + + _buzzer.ProcessPulseValue(true, currentState); } #endregion @@ -160,7 +169,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeIsPlaying) return; - _machine.BuzzerDevice.SetTapeMode(true); + _buzzer.SetTapeMode(true); _machine.Spectrum.OSD_TapePlaying(); @@ -215,7 +224,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!_tapeIsPlaying) return; - _machine.BuzzerDevice.SetTapeMode(false); + _buzzer.SetTapeMode(false); _machine.Spectrum.OSD_TapeStopped(); @@ -376,6 +385,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion #region Tape Device Methods + + + + + /// + /// Runs every frame + /// + public void TapeCycle() + { + if (TapeIsPlaying) + { + counter++; + + if (counter > 50) + { + counter = 0; + bool state = GetEarBit(_machine.CPU.TotalExecutedCycles); + _buzzer.ProcessPulseValue(false, state); + } + } + } /// /// Simulates the spectrum 'EAR' input reading data from the tape @@ -534,7 +564,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _lastCycle = cpuCycle - (long)cycles; // play the buzzer - _buzzer.ProcessPulseValue(false, currentState); + //_buzzer.ProcessPulseValue(false, currentState); return currentState; } @@ -898,6 +928,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void SyncState(Serializer ser) { ser.BeginSection("DatacorderDevice"); + ser.Sync("counter", ref counter); ser.Sync("_currentDataBlockIndex", ref _currentDataBlockIndex); ser.Sync("_position", ref _position); ser.Sync("_tapeIsPlaying", ref _tapeIsPlaying); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index a1e44356d1..af68ff0587 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -39,6 +39,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public IBeeperDevice BuzzerDevice { get; set; } + /// + /// A second beeper for the tape + /// + public IBeeperDevice TapeBuzzer { get; set; } + /// /// Device representing the AY-3-8912 chip found in the 128k and up spectrums /// @@ -142,6 +147,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_renderSound) { BuzzerDevice.StartFrame(); + TapeBuzzer.StartFrame(); if (AYDevice != null) AYDevice.StartFrame(); } @@ -154,7 +160,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.CheckForInterrupt(CurrentFrameCycle); // run a single CPU instruction - CPU.ExecuteOne(); + CPU.ExecuteOne(); + + // cycle the tape device + TapeDevice.TapeCycle(); } // we have reached the end of a frame @@ -165,7 +174,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); if (_renderSound) + { BuzzerDevice.EndFrame(); + TapeBuzzer.EndFrame(); + } if (AYDevice != null) AYDevice.EndFrame(); @@ -329,6 +341,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RomData.SyncState(ser); KeyboardDevice.SyncState(ser); BuzzerDevice.SyncState(ser); + TapeBuzzer.SyncState(ser); ULADevice.SyncState(ser); if (AYDevice != null) @@ -336,7 +349,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.SyncState(ser); ((AYChip)AYDevice as AYChip).PanningConfiguration = Spectrum.Settings.AYPanConfig; } - ser.Sync("tapeMediaIndex", ref tapeMediaIndex); TapeMediaIndex = tapeMediaIndex; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 3ea955ddca..0494585323 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -31,6 +31,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new Buzzer(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + AYDevice = new AYChip(this); AYDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index 3cd1f695d5..c2eb1cb85b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -31,6 +31,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new Buzzer(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + AYDevice = new AYChip(this); AYDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 916232bcb6..d9cf36a106 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -31,6 +31,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new Buzzer(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + AYDevice = new AYChip(this); AYDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 9dacc597f3..20618d7f1b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -26,6 +26,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum BuzzerDevice = new Buzzer(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); + TapeBuzzer = new Buzzer(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + KeyboardDevice = new StandardKeyboard(this); InitJoysticks(joysticks); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index fab7424edf..cd4eabe7ba 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -101,6 +101,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); + if (_machine.TapeBuzzer != null) + SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); //SoundMixer.Stereo = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).StereoSound; if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) @@ -115,6 +117,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ((Buzzer)_machine.BuzzerDevice as Buzzer).EarVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; } + if (_machine.TapeBuzzer != null) + { + ((Buzzer)_machine.TapeBuzzer as Buzzer).TapeVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; + //((Buzzer)_machine.TapeBuzzer as Buzzer).EarVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + } + ser.Register(SoundMixer); From 2cd641701ea789e57478594a2f3ca231a419b681 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 27 Mar 2018 14:14:04 +0100 Subject: [PATCH 143/339] ZXHawk: Removed RomData object from SynState (this should always be deterministic anyway after a core reboot) --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index af68ff0587..bfcccda887 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -338,7 +338,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("ROMhigh", ref ROMhigh); ser.Sync("ROMlow", ref ROMlow); - RomData.SyncState(ser); KeyboardDevice.SyncState(ser); BuzzerDevice.SyncState(ser); TapeBuzzer.SyncState(ser); From 8485a526628880ec6ce5c84ea95a371496891328 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 27 Mar 2018 15:40:13 +0100 Subject: [PATCH 144/339] ZXHawk: Finished separation of emulated TapeDevice - #1158 --- .../Hardware/Abstraction/IBeeperDevice.cs | 4 +- .../Hardware/Datacorder/DatacorderDevice.cs | 22 +- .../Hardware/SoundOuput/Buzzer.cs | 189 ++++++------------ .../Machine/ZXSpectrum128K/ZX128.Port.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 2 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 2 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 7 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 20 +- 9 files changed, 83 insertions(+), 167 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs index 7367e3948f..2e4d2dea50 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs @@ -11,14 +11,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { void Init(int sampleRate, int tStatesPerFrame); - void ProcessPulseValue(bool fromTape, bool earPulse); + void ProcessPulseValue(bool pulse); void StartFrame(); void EndFrame(); - void SetTapeMode(bool tapeMode); - void SyncState(Serializer ser); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index c67ce41ee4..3ca036d3bc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -151,10 +151,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void StartFrame() { - //if (TapeIsPlaying && AutoPlay) - //FlashLoad(); - - _buzzer.ProcessPulseValue(true, currentState); + //_buzzer.ProcessPulseValue(currentState); } #endregion @@ -169,8 +166,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeIsPlaying) return; - _buzzer.SetTapeMode(true); - _machine.Spectrum.OSD_TapePlaying(); // update the lastCycle @@ -224,8 +219,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!_tapeIsPlaying) return; - _buzzer.SetTapeMode(false); - _machine.Spectrum.OSD_TapeStopped(); // sign that the tape is no longer playing @@ -384,16 +377,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - #region Tape Device Methods - - - + #region Tape Device Methods /// - /// Runs every frame + /// Is called every cpu cycle but runs every 50 t-states + /// This enables the tape devices to play out even if the spectrum itself is not + /// requesting tape data /// public void TapeCycle() - { + { if (TapeIsPlaying) { counter++; @@ -402,7 +394,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { counter = 0; bool state = GetEarBit(_machine.CPU.TotalExecutedCycles); - _buzzer.ProcessPulseValue(false, state); + _buzzer.ProcessPulseValue(state); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index 3a9516a63b..1f6b9ad20c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -1,10 +1,8 @@  using BizHawk.Common; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Components; using System; using System.Collections.Generic; -using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -14,30 +12,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// For the purposes of emulation this devices is locked to a frame /// a list of Pulses is built up over the course of the frame and outputted at the end of the frame + /// + /// ZXHawk instantiates multiples of these buzzers to achieve separation between tape and spectrum audio output /// public class Buzzer : ISoundProvider, IBeeperDevice { - /// - /// Supplied values are right for 48K spectrum - /// These will deviate for 128k and up (as there are more T-States per frame) - /// - //public int SampleRate = 44100; //35000; - //public int SamplesPerFrame = 882; //699; - //public int TStatesPerSample = 79; //100; - + #region Fields and Properties + /// /// Sample Rate /// This usually has to be 44100 for ISoundProvider /// + private int _sampleRate; public int SampleRate { get { return _sampleRate; } set { _sampleRate = value; } } - + /// /// Number of samples in one frame /// + private int _samplesPerFrame; public int SamplesPerFrame { get { return _samplesPerFrame; } @@ -47,6 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Number of TStates in each sample /// + private int _tStatesPerSample; public int TStatesPerSample { get { return _tStatesPerSample; } @@ -54,37 +51,55 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// The tape loading volume + /// Buzzer volume /// Accepts an int 0-100 value /// - private int _tapeVolume; - public int TapeVolume + private int _volume; + public int Volume { get { - return VolumeConverterOut(_tapeVolume); + return VolumeConverterOut(_volume); } set { - _tapeVolume = VolumeConverterIn(value); + _volume = VolumeConverterIn(value); } } /// - /// The EAR beeper volume + /// The number of cpu cycles per frame /// - private int _earVolume; - public int EarVolume - { - get - { - return VolumeConverterOut(_earVolume); - } - set - { - _earVolume = VolumeConverterIn(value); - } - } + private long _tStatesPerFrame; + + /// + /// The cycle at which the frame starts + /// + private long _frameStart; + + /// + /// The parent emulated machine + /// + private SpectrumBase _machine; + + /// + /// Pulses collected during the last frame + /// + public List Pulses { get; private set; } + + /// + /// The last pulse + /// + public bool LastPulse { get; set; } + + /// + /// The last T-State (cpu cycle) that the last pulse was received + /// + public long LastPulseTState { get; set; } + + #endregion + + #region Private Methods /// /// Takes an int 0-100 and returns the relevant short volume to output @@ -115,33 +130,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return shortvol / increment; } - - private SpectrumBase _machine; - - /// - /// State fields - /// - private long _frameStart; - private bool _tapeMode; - private long _tStatesPerFrame; - private int _sampleRate; - private int _samplesPerFrame; - private int _tStatesPerSample; - - /// - /// Pulses collected during the last frame - /// - public List Pulses { get; private set; } - - /// - /// The last pulse - /// - public bool LastPulse { get; set; } - - /// - /// The last T-State (cpu cycle) that the last pulse was received - /// - public long LastPulseTState { get; set; } + #endregion #region Construction & Initialisation @@ -159,75 +148,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _tStatesPerFrame = tStatesPerFrame; _tStatesPerSample = 79; _samplesPerFrame = (int)_tStatesPerFrame / _tStatesPerSample; - - /* - - // set the tstatesperframe - _tStatesPerFrame = tStatesPerFrame; - - // calculate actual refresh rate - double refresh = (double)_machine.ULADevice.ClockSpeed / (double)_tStatesPerFrame; - - // how many samples per frame are expected by ISoundProvider (at 44.1KHz) - _samplesPerFrame = 880;// (int)((double)sampleRate / (double)refresh); - - // set the sample rate - _sampleRate = sampleRate; - - // calculate samples per frame (what ISoundProvider will be expecting at 44100) - //_samplesPerFrame = (int)((double)_tStatesPerFrame / (double)refresh); - - // calculate tstates per sameple - _tStatesPerSample = 79;// _tStatesPerFrame / _samplesPerFrame; - - /* - - - - - // get divisors - var divs = from a in Enumerable.Range(2, _tStatesPerFrame / 2) - where _tStatesPerFrame % a == 0 - select a; - - // get the highest int value under 120 (this will be TStatesPerSample) - _tStatesPerSample = divs.Where(a => a < 100).Last(); - - // get _samplesPerFrame - _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; - - */ Pulses = new List(1000); } #endregion + #region IBeeperDevice + /// - /// When the pulse value from the EAR output changes it is processed here + /// When the pulse value changes it is processed here /// - /// - /// - public void ProcessPulseValue(bool fromTape, bool earPulse) + /// + public void ProcessPulseValue(bool pulse) { if (!_machine._renderSound) return; - if (!fromTape && _tapeMode) - { - // tape mode is active but the pulse value came from an OUT instruction - // do not process the value - //return; - } - - if (earPulse == LastPulse) + if (pulse == LastPulse) { // no change detected return; } // set the lastpulse - LastPulse = earPulse; + LastPulse = pulse; // get where we are in the frame var currentULACycle = _machine.CurrentFrameCycle; @@ -244,7 +189,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // add the pulse Pulse p = new Pulse { - State = !earPulse, + State = !pulse, Length = length }; Pulses.Add(p); @@ -259,7 +204,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void StartFrame() { - //DiscardSamples(); Pulses.Clear(); LastPulseTState = 0; } @@ -296,11 +240,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (var i = firstSample; i < currentEnd + pulse.Length; i += TStatesPerSample) { - if (_tapeMode) - samples[sampleIndex++] = pulse.State ? (short)(_tapeVolume) : (short)0; - else - samples[sampleIndex++] = pulse.State ? (short)(_earVolume) : (short)0; - } + samples[sampleIndex++] = pulse.State ? (short)(_volume) : (short)0; + } currentEnd += pulse.Length; } @@ -316,16 +257,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _frameStart += _tStatesPerFrame; } - /// - /// When the spectrum is set to receive tape input, the EAR output on the ULA is disabled - /// (so no buzzer sound is emitted) - /// - /// - public void SetTapeMode(bool tapeMode) - { - _tapeMode = tapeMode; - } - + #endregion #region ISoundProvider @@ -370,24 +302,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - + #region State Serialization + public void SyncState(Serializer ser) { ser.BeginSection("Buzzer"); ser.Sync("_frameStart", ref _frameStart); - ser.Sync("_tapeMode", ref _tapeMode); ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); ser.Sync("_sampleRate", ref _sampleRate); ser.Sync("_samplesPerFrame", ref _samplesPerFrame); ser.Sync("_tStatesPerSample", ref _tStatesPerSample); - ser.Sync("soundBuffer", ref soundBuffer, false); ser.Sync("soundBufferContains", ref soundBufferContains); ser.EndSection(); } - + #endregion } - - } 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 bcebb55542..f88922132c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -158,7 +158,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); TapeDevice.WritePort(port, value); // Tape 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 24e7a13760..425d33f275 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -250,7 +250,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, value); 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 e56c23440b..8226326b11 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -177,7 +177,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, 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 0cb5b9c7c1..12f8850957 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -115,7 +115,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice.borderColour = value & BORDER_BIT; // Buzzer - BuzzerDevice.ProcessPulseValue(false, (value & EAR_BIT) != 0); + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); // Tape TapeDevice.WritePort(port, value); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 5f721f233d..deb5d63eaa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -33,8 +33,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } if (_machine != null && _machine.BuzzerDevice != null) { - ((Buzzer)_machine.BuzzerDevice as Buzzer).TapeVolume = o.TapeVolume; - ((Buzzer)_machine.BuzzerDevice as Buzzer).EarVolume = o.EarVolume; + ((Buzzer)_machine.BuzzerDevice as Buzzer).Volume = o.EarVolume; + } + if (_machine != null && _machine.TapeBuzzer != null) + { + ((Buzzer)_machine.TapeBuzzer as Buzzer).Volume = o.TapeVolume; } Settings = o; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index cd4eabe7ba..f6c97c692e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -79,8 +79,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum default: throw new InvalidOperationException("Machine not yet emulated"); } - - _cpu.MemoryCallbacks = MemoryCallbacks; @@ -98,37 +96,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Register(_cpu); ser.Register(_machine.ULADevice); + // initialize sound mixer and attach the various ISoundProvider devices SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); + SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - if (_machine.TapeBuzzer != null) - SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); - //SoundMixer.Stereo = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).StereoSound; - + + // set audio device settings if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) { ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; _machine.AYDevice.Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYVolume; } - + if (_machine.BuzzerDevice != null) { - ((Buzzer)_machine.BuzzerDevice as Buzzer).TapeVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; - ((Buzzer)_machine.BuzzerDevice as Buzzer).EarVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + ((Buzzer)_machine.BuzzerDevice as Buzzer).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; } if (_machine.TapeBuzzer != null) { - ((Buzzer)_machine.TapeBuzzer as Buzzer).TapeVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; - //((Buzzer)_machine.TapeBuzzer as Buzzer).EarVolume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + ((Buzzer)_machine.TapeBuzzer as Buzzer).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; } - ser.Register(SoundMixer); HardReset(); - SetupMemoryDomains(); } From f1fbd2b64ecfe1742f59ab1d2efbf875ef06abce Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 27 Mar 2018 16:06:59 +0100 Subject: [PATCH 145/339] ZXHawk: AutoTapeLoad has been changed to a SyncSetting - #1158 --- ...XSpectrumCoreEmulationSettings.Designer.cs | 33 +++++++++++++++++-- .../ZXSpectrumCoreEmulationSettings.cs | 7 +++- .../ZXSpectrumNonSyncSettings.Designer.cs | 33 ++----------------- .../ZXSpectrum/ZXSpectrumNonSyncSettings.cs | 9 ++--- .../Hardware/Datacorder/DatacorderDevice.cs | 22 ++++--------- .../Machine/ZXSpectrum128K/ZX128.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 2 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.cs | 2 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 10 +++--- 10 files changed, 58 insertions(+), 64 deletions(-) diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs index d7e1cf1e63..b7f58ec911 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs @@ -39,12 +39,14 @@ this.label2 = new System.Windows.Forms.Label(); this.borderTypecomboBox1 = new System.Windows.Forms.ComboBox(); this.lblBorderInfo = new System.Windows.Forms.Label(); + this.lblAutoLoadText = new System.Windows.Forms.Label(); + this.autoLoadcheckBox1 = new System.Windows.Forms.CheckBox(); this.SuspendLayout(); // // OkBtn // this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OkBtn.Location = new System.Drawing.Point(247, 393); + this.OkBtn.Location = new System.Drawing.Point(247, 434); this.OkBtn.Name = "OkBtn"; this.OkBtn.Size = new System.Drawing.Size(60, 23); this.OkBtn.TabIndex = 3; @@ -56,7 +58,7 @@ // this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBtn.Location = new System.Drawing.Point(313, 393); + this.CancelBtn.Location = new System.Drawing.Point(313, 434); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(60, 23); this.CancelBtn.TabIndex = 4; @@ -144,13 +146,36 @@ this.lblBorderInfo.Text = "null"; this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; // + // lblAutoLoadText + // + this.lblAutoLoadText.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblAutoLoadText.Location = new System.Drawing.Point(175, 388); + this.lblAutoLoadText.Name = "lblAutoLoadText"; + this.lblAutoLoadText.Size = new System.Drawing.Size(196, 30); + this.lblAutoLoadText.TabIndex = 27; + this.lblAutoLoadText.Text = "When enabled ZXHawk will attempt to control the tape device automatically when th" + + "e correct traps are detected"; + this.lblAutoLoadText.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // autoLoadcheckBox1 + // + this.autoLoadcheckBox1.AutoSize = true; + this.autoLoadcheckBox1.Location = new System.Drawing.Point(15, 394); + this.autoLoadcheckBox1.Name = "autoLoadcheckBox1"; + this.autoLoadcheckBox1.Size = new System.Drawing.Size(103, 17); + this.autoLoadcheckBox1.TabIndex = 26; + this.autoLoadcheckBox1.Text = "Auto-Load Tape"; + this.autoLoadcheckBox1.UseVisualStyleBackColor = true; + // // ZXSpectrumCoreEmulationSettings // this.AcceptButton = this.OkBtn; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CancelBtn; - this.ClientSize = new System.Drawing.Size(385, 428); + this.ClientSize = new System.Drawing.Size(385, 469); + this.Controls.Add(this.lblAutoLoadText); + this.Controls.Add(this.autoLoadcheckBox1); this.Controls.Add(this.lblBorderInfo); this.Controls.Add(this.label2); this.Controls.Add(this.borderTypecomboBox1); @@ -183,5 +208,7 @@ private System.Windows.Forms.Label label2; private System.Windows.Forms.ComboBox borderTypecomboBox1; private System.Windows.Forms.Label lblBorderInfo; + private System.Windows.Forms.Label lblAutoLoadText; + private System.Windows.Forms.CheckBox autoLoadcheckBox1; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs index bb8ba92d50..1f6929cac3 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs @@ -41,6 +41,9 @@ namespace BizHawk.Client.EmuHawk // deterministic emulation determEmucheckBox1.Checked = _syncSettings.DeterministicEmulation; + + // autoload tape + autoLoadcheckBox1.Checked = _syncSettings.AutoLoadTape; } private void OkBtn_Click(object sender, EventArgs e) @@ -48,13 +51,15 @@ namespace BizHawk.Client.EmuHawk bool changed = _syncSettings.MachineType.ToString() != MachineSelectionComboBox.SelectedItem.ToString() || _syncSettings.BorderType.ToString() != borderTypecomboBox1.SelectedItem.ToString() - || _syncSettings.DeterministicEmulation != determEmucheckBox1.Checked; + || _syncSettings.DeterministicEmulation != determEmucheckBox1.Checked + || _syncSettings.AutoLoadTape != autoLoadcheckBox1.Checked; if (changed) { _syncSettings.MachineType = (MachineType)Enum.Parse(typeof(MachineType), MachineSelectionComboBox.SelectedItem.ToString()); _syncSettings.BorderType = (ZXSpectrum.BorderType)Enum.Parse(typeof(ZXSpectrum.BorderType), borderTypecomboBox1.SelectedItem.ToString()); _syncSettings.DeterministicEmulation = determEmucheckBox1.Checked; + _syncSettings.AutoLoadTape = autoLoadcheckBox1.Checked; GlobalWin.MainForm.PutCoreSyncSettings(_syncSettings); diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs index a519b00e94..6e2c603520 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs @@ -32,8 +32,6 @@ this.OkBtn = new System.Windows.Forms.Button(); this.CancelBtn = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); - this.autoLoadcheckBox1 = new System.Windows.Forms.CheckBox(); - this.lblAutoLoadText = new System.Windows.Forms.Label(); this.lblOSDVerbinfo = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.osdMessageVerbositycomboBox1 = new System.Windows.Forms.ComboBox(); @@ -42,7 +40,7 @@ // OkBtn // this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.OkBtn.Location = new System.Drawing.Point(247, 145); + this.OkBtn.Location = new System.Drawing.Point(247, 142); this.OkBtn.Name = "OkBtn"; this.OkBtn.Size = new System.Drawing.Size(60, 23); this.OkBtn.TabIndex = 3; @@ -54,7 +52,7 @@ // this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBtn.Location = new System.Drawing.Point(313, 145); + this.CancelBtn.Location = new System.Drawing.Point(313, 142); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(60, 23); this.CancelBtn.TabIndex = 4; @@ -71,27 +69,6 @@ this.label1.TabIndex = 17; this.label1.Text = "ZX Spectrum Misc Non-Sync Settings"; // - // autoLoadcheckBox1 - // - this.autoLoadcheckBox1.AutoSize = true; - this.autoLoadcheckBox1.Location = new System.Drawing.Point(15, 52); - this.autoLoadcheckBox1.Name = "autoLoadcheckBox1"; - this.autoLoadcheckBox1.Size = new System.Drawing.Size(103, 17); - this.autoLoadcheckBox1.TabIndex = 21; - this.autoLoadcheckBox1.Text = "Auto-Load Tape"; - this.autoLoadcheckBox1.UseVisualStyleBackColor = true; - // - // lblAutoLoadText - // - this.lblAutoLoadText.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.lblAutoLoadText.Location = new System.Drawing.Point(175, 46); - this.lblAutoLoadText.Name = "lblAutoLoadText"; - this.lblAutoLoadText.Size = new System.Drawing.Size(196, 30); - this.lblAutoLoadText.TabIndex = 25; - this.lblAutoLoadText.Text = "When enabled ZXHawk will attempt to control the tape device automatically when th" + - "e correct traps are detected"; - this.lblAutoLoadText.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // // lblOSDVerbinfo // this.lblOSDVerbinfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); @@ -129,12 +106,10 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.CancelBtn; - this.ClientSize = new System.Drawing.Size(385, 180); + this.ClientSize = new System.Drawing.Size(385, 177); this.Controls.Add(this.lblOSDVerbinfo); this.Controls.Add(this.label4); this.Controls.Add(this.osdMessageVerbositycomboBox1); - this.Controls.Add(this.lblAutoLoadText); - this.Controls.Add(this.autoLoadcheckBox1); this.Controls.Add(this.label1); this.Controls.Add(this.CancelBtn); this.Controls.Add(this.OkBtn); @@ -153,8 +128,6 @@ private System.Windows.Forms.Button OkBtn; private System.Windows.Forms.Button CancelBtn; private System.Windows.Forms.Label label1; - private System.Windows.Forms.CheckBox autoLoadcheckBox1; - private System.Windows.Forms.Label lblAutoLoadText; private System.Windows.Forms.Label lblOSDVerbinfo; private System.Windows.Forms.Label label4; private System.Windows.Forms.ComboBox osdMessageVerbositycomboBox1; diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs index 921cee6b8b..983e896359 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs @@ -21,8 +21,7 @@ namespace BizHawk.Client.EmuHawk { _settings = ((ZXSpectrum)Global.Emulator).GetSettings().Clone(); - // autoload tape - autoLoadcheckBox1.Checked = _settings.AutoLoadTape; + // OSD Message Verbosity var osdTypes = Enum.GetNames(typeof(ZXSpectrum.OSDVerbosity)); @@ -36,13 +35,11 @@ namespace BizHawk.Client.EmuHawk private void OkBtn_Click(object sender, EventArgs e) { - bool changed = - _settings.AutoLoadTape != autoLoadcheckBox1.Checked - || _settings.OSDMessageVerbosity.ToString() != osdMessageVerbositycomboBox1.SelectedItem.ToString(); + bool changed = + _settings.OSDMessageVerbosity.ToString() != osdMessageVerbositycomboBox1.SelectedItem.ToString(); if (changed) { - _settings.AutoLoadTape = autoLoadcheckBox1.Checked; _settings.OSDMessageVerbosity = (ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), osdMessageVerbositycomboBox1.SelectedItem.ToString()); GlobalWin.MainForm.PutCoreSettings(_settings); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 3ca036d3bc..59ff27d0f8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -24,9 +24,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Default constructor /// - public DatacorderDevice() + public DatacorderDevice(bool autoplay) { - + _autoPlay = autoplay; } /// @@ -40,8 +40,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _buzzer = machine.TapeBuzzer; } - - #endregion #region State Information @@ -129,12 +127,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// 'load' mode and auto-play the tape if neccesary /// private bool _autoPlay; - public bool AutoPlay - { - get { return _machine.Spectrum.Settings.AutoLoadTape; } - set { _autoPlay = value; MonitorReset(); } - } - #endregion @@ -428,7 +420,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // process the cycles based on the waitEdge - while (cycles >= _waitEdge) + while (cycles >= _waitEdge) { // decrement cycles cycles -= _waitEdge; @@ -723,7 +715,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _monitorCount++; - if (_monitorCount >= 16 && _machine.Spectrum.Settings.AutoLoadTape) + if (_monitorCount >= 16 && _autoPlay) { if (!_tapeIsPlaying) { @@ -749,7 +741,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!_tapeIsPlaying) return; - if (!_machine.Spectrum.Settings.AutoLoadTape) + if (!_autoPlay) return; Stop(); @@ -761,7 +753,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_tapeIsPlaying) return; - if (!_machine.Spectrum.Settings.AutoLoadTape) + if (!_autoPlay) return; Play(); @@ -772,7 +764,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private void MonitorFrame() { - if (_tapeIsPlaying && _machine.Spectrum.Settings.AutoLoadTape) + if (_tapeIsPlaying && _autoPlay) { _monitorTimeOut--; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 0494585323..3ef13c4559 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -41,7 +41,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InitJoysticks(joysticks); - TapeDevice = new DatacorderDevice(); + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); TapeDevice.Init(this); InitializeMedia(files); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index c2eb1cb85b..ed9d65282c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -41,7 +41,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InitJoysticks(joysticks); - TapeDevice = new DatacorderDevice(); + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); TapeDevice.Init(this); InitializeMedia(files); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index d9cf36a106..ddf4def7c2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -41,7 +41,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InitJoysticks(joysticks); - TapeDevice = new DatacorderDevice(); + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); TapeDevice.Init(this); InitializeMedia(files); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 20618d7f1b..98968ff315 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InitJoysticks(joysticks); - TapeDevice = new DatacorderDevice(); + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); TapeDevice.Init(this); InitializeMedia(files); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index deb5d63eaa..6e954e3430 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -54,11 +54,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public class ZXSpectrumSettings { - [DisplayName("Auto-load/stop tape")] - [Description("Auto or manual tape operation. Auto will attempt to detect CPU tape traps and automatically Stop/Start the tape")] - [DefaultValue(true)] - public bool AutoLoadTape { get; set; } - [DisplayName("AY-3-8912 Panning Config")] [Description("Set the PSG panning configuration.\nThe chip has 3 audio channels that can be outputed in different configurations")] [DefaultValue(AYChip.AYPanConfig.ABC)] @@ -133,6 +128,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum [DefaultValue(JoystickType.SinclairRIGHT)] public JoystickType JoystickType3 { get; set; } + [DisplayName("Auto-load/stop tape")] + [Description("Auto or manual tape operation. Auto will attempt to detect CPU tape traps and automatically Stop/Start the tape")] + [DefaultValue(true)] + public bool AutoLoadTape { get; set; } + public ZXSpectrumSyncSettings Clone() { From 35c87c5aeabdb56d99a0b866813a9eab31b87580 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 27 Mar 2018 16:38:08 +0100 Subject: [PATCH 146/339] ZXHawk: Fixed AY-3-8912 volume control (values under 50 were registering as maxvolume) - #1158 --- .../SinclairSpectrum/Hardware/SoundOuput/AYChip.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs index d2d015ff53..161b93176b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs @@ -526,7 +526,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The current volume /// - private int _volume = 50; + private int _volume = 75; /// /// Volume tables state @@ -553,7 +553,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// private void UpdateVolume() { - var vol = ((ulong)0xFFFF * (ulong)_volume / 100UL) - 20000 ; + int upperFloor = 40000; + var inc = (0xFFFF - upperFloor) / 100; + + var vol = inc * _volume; // ((ulong)0xFFFF * (ulong)_volume / 100UL) - 20000 ; _volumeTables = new uint[6][]; // parent array From ae6ce00d7cbc248cdebea2c0bed8f7d3089a53e3 Mon Sep 17 00:00:00 2001 From: feos Date: Tue, 27 Mar 2018 19:00:59 +0300 Subject: [PATCH 147/339] I've heard bad things about ElementAt() --- .../movie/tasproj/TasMovie.Editing.cs | 12 ++++++------ .../movie/tasproj/TasStateManager.cs | 11 +++++------ .../tools/Lua/Libraries/EmuLuaLibrary.Forms.cs | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs b/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs index bd70407858..c7eb292890 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs @@ -101,7 +101,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; if (m.Frame == frame) { Markers.Remove(m); @@ -152,7 +152,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; if (m.Frame == frame) { Markers.Remove(m); @@ -198,7 +198,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; if (m.Frame < removeUpTo) { Markers.Remove(m); @@ -241,7 +241,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; Markers.Move(m.Frame, m.Frame + 1); } } @@ -274,7 +274,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; Markers.Move(m.Frame, m.Frame + inputLog.Count()); } } @@ -363,7 +363,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; Markers.Move(m.Frame, m.Frame + count, fromHistory); } } diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index cd1ff55c1c..705325ee6c 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -271,7 +271,7 @@ namespace BizHawk.Client.Common return; } - StateManagerState state = _states.ElementAt(index).Value; + StateManagerState state = _states.Values[index]; if (state.IsOnDisk) { @@ -407,11 +407,10 @@ namespace BizHawk.Client.Common { continue; } - - KeyValuePair kvp = _states.ElementAt(i); - bw.Write(kvp.Key); - bw.Write(kvp.Value.Length); - bw.Write(kvp.Value.State); + + bw.Write(_states.Keys[i]); + bw.Write(_states.Values[i].Length); + bw.Write(_states.Values[i].State); } } diff --git a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs index e73c85da03..2fe833b845 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs @@ -183,7 +183,7 @@ namespace BizHawk.Client.EmuHawk { for (var i = _luaForms.Count - 1; i >= 0; i--) { - _luaForms.ElementAt(i).Close(); + _luaForms[i].Close(); } } From 220b41cc16d0f24fc9aca753245587123f9538a2 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 27 Mar 2018 17:24:55 -0400 Subject: [PATCH 148/339] GBHawk: GBC Bug fixes --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 14 +++++++------- .../Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs | 8 ++++++++ .../Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs | 11 +++++++++++ .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 4 ++-- .../Consoles/Nintendo/GBHawk/PPU.cs | 4 ++++ 5 files changed, 32 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index c8956ec132..de47032864 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -43,7 +43,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // controls for tile attributes public int VRAM_sel; public bool BG_V_flip; - public bool HDMA_active; public bool HDMA_mode; public ushort cur_DMA_src; public ushort cur_DMA_dest; @@ -196,7 +195,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cur_DMA_dest = (ushort)(((HDMA_dest_hi & 0x1F) << 8) | (HDMA_dest_lo & 0xF0)); cur_DMA_src = (ushort)(((HDMA_src_hi & 0xFF) << 8) | (HDMA_src_lo & 0xF0)); - HDMA_length = ((HDMA_ctrl & 0x7F) + 1) * 16; + HDMA_length = ((value & 0x7F) + 1) * 16; } else { @@ -244,17 +243,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (HDMA_countdown == 0) { - if (!HDMA_mode) { // immediately transfer bytes, 2 bytes per cycles if (HDMA_length > 0) { - Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); HDMA_length--; - Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); + + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); HDMA_length--; @@ -268,7 +267,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - // only transfer during mode 3, and only 16 bytes at a time + // only transfer during mode 0, and only 16 bytes at a time if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test) { HBL_HDMA_go = true; @@ -286,6 +285,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); HDMA_length--; HBL_HDMA_count--; + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); @@ -303,6 +303,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk HBL_test = true; last_HBL = LY; HBL_HDMA_count = 0x10; + HBL_HDMA_go = false; } } } @@ -1448,7 +1449,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("VRAM_sel", ref VRAM_sel); ser.Sync("BG_V_flip", ref BG_V_flip); - ser.Sync("HDMA_active", ref HDMA_active); ser.Sync("HDMA_mode", ref HDMA_mode); ser.Sync("cur_DMA_src", ref cur_DMA_src); ser.Sync("cur_DMA_dest", ref cur_DMA_dest); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index f7145514c4..7a5354a093 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -135,6 +135,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk timer.tick_2(); } } + else + { + cpu.TotalExecutedCycles++; + if (double_speed) + { + cpu.TotalExecutedCycles++; + } + } if (in_vblank && !in_vblank_old) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 6bc997b985..58d1261388 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -101,6 +101,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk set { _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } } + [DisplayName("Timer Div Initial Time")] + [Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] + [DefaultValue(0)] + public int DivInitialTime + { + get { return _DivInitialTime; } + set { _DivInitialTime = Math.Min((ushort)65535, (ushort)value); } + } + [DisplayName("Use Existing SaveRAM")] [Description("When true, existing SaveRAM will be loaded at boot up")] [DefaultValue(false)] @@ -109,6 +118,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk [JsonIgnore] private int _RTCInitialTime; + [JsonIgnore] + private ushort _DivInitialTime; public GBSyncSettings Clone() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index a82cdb820b..396fc7ec32 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -319,7 +319,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // VBK case 0xFF4F: - if (is_GBC) + if (is_GBC && !ppu.HDMA_active) { VRAM_Bank = (byte)(value & 1); } @@ -353,7 +353,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // RAM Bank in GBC mode case 0xFF70: //Console.WriteLine(value); - if (is_GBC) + if (is_GBC && !ppu.HDMA_active) { RAM_Bank = value & 7; if (RAM_Bank == 0) { RAM_Bank = 1; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 90f3939443..9a4f94c409 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -12,6 +12,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public uint[] BG_palette = new uint[32]; public uint[] OBJ_palette = new uint[32]; + + public bool HDMA_active; + // register variables public byte LCDC; public byte STAT; @@ -160,6 +163,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("BG_palette", ref BG_palette, false); ser.Sync("OBJ_palette", ref OBJ_palette, false); + ser.Sync("HDMA_active", ref HDMA_active); ser.Sync("LCDC", ref LCDC); ser.Sync("STAT", ref STAT); From 3e50881ab98b51e3da659eaf7673e0aa7e95f01c Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 28 Mar 2018 10:15:05 -0400 Subject: [PATCH 149/339] GBHawk: Fix GBC compatibility mode for GB games --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 63 ++++++++++++------- .../Nintendo/GBHawk/GBHawk.IStatable.cs | 1 + .../Consoles/Nintendo/GBHawk/GBHawk.cs | 2 + .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 14 +++++ 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index de47032864..e0ae904368 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -679,7 +679,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // start shifting data into the LCD if (render_counter >= (render_offset + 8)) { - if (tile_data_latch[2].Bit(5)) + if (tile_data_latch[2].Bit(5) && Core.GBC_compat) { pixel = tile_data_latch[0].Bit(render_counter % 8) ? 1 : 0; pixel |= tile_data_latch[1].Bit(render_counter % 8) ? 2 : 0; @@ -692,13 +692,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk int ref_pixel = pixel; - if (LCDC.Bit(0)) + if (!Core.GBC_compat) { - //pixel = (BGP >> (pixel * 2)) & 3; - } - else - { - //pixel = 0; + if (LCDC.Bit(0)) + { + pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + pixel = 0; + } } int pal_num = tile_data_latch[2] & 0x7; @@ -739,7 +742,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // There is another priority bit in GBC, that can still override sprite priority - if (LCDC.Bit(0) && tile_data_latch[2].Bit(7)) + if (LCDC.Bit(0) && tile_data_latch[2].Bit(7) && Core.GBC_compat) { use_sprite = false; } @@ -749,28 +752,45 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { pal_num = sprite_attr & 7; - /* - if (sprite_attr.Bit(4)) + if (!Core.GBC_compat) { - pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; - } - else - { - pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; - } - */ + pal_num = sprite_attr.Bit(4) ? 1 : 0; + + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } } } } // based on sprite priority and pixel values, pick a final pixel color - if (use_sprite) + if (Core.GBC_compat) { - Core._vidbuffer[LY * 160 + pixel_counter] = (int)OBJ_palette[pal_num * 4 + s_pixel]; + if (use_sprite) + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)OBJ_palette[pal_num * 4 + s_pixel]; + } + else + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)BG_palette[pal_num * 4 + pixel]; + } } else { - Core._vidbuffer[LY * 160 + pixel_counter] = (int)BG_palette[pal_num * 4 + pixel]; + if (use_sprite) + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)OBJ_palette[pal_num * 4 + pixel]; + } + else + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)BG_palette[pixel]; + } } pixel_counter++; @@ -832,7 +852,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; - BG_V_flip = tile_data[2].Bit(6); + BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; } else { @@ -948,6 +968,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; + BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; } else { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index d110c68a4c..749164d070 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -87,6 +87,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("RAM_Bank", ref RAM_Bank); ser.Sync("VRAM_Bank", ref VRAM_Bank); ser.Sync("is_GBC", ref is_GBC); + ser.Sync("GBC_compat", ref GBC_compat); ser.Sync("double_speed", ref double_speed); ser.Sync("speed_switch", ref speed_switch); ser.Sync("HDMA_transfer", ref HDMA_transfer); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 7c7ae38b9b..3b48926029 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -54,6 +54,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int RAM_Bank; public byte VRAM_Bank; public bool is_GBC; + public bool GBC_compat; // compatibility mode for GB games played on GBC public bool double_speed; public bool speed_switch; public bool HDMA_transfer; // stalls CPU when in progress @@ -276,6 +277,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk private void HardReset() { GB_bios_register = 0; // bios enable + GBC_compat = true; in_vblank = true; // we start off in vblank since the LCD is off in_vblank_old = true; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index 396fc7ec32..84389700b9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -309,6 +309,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ppu.WriteReg(addr, value); break; + // GBC compatibility register (I think) + case 0xFF4C: + if (value != 0xC0) + { + GBC_compat = false; + } + break; + // Speed Control for GBC case 0xFF4D: if (is_GBC) @@ -382,6 +390,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } break; + + default: + Console.Write(addr); + Console.Write(" "); + Console.WriteLine(value); + break; } } From 75f380428ffc58b25616286a6eba6f77b8719e5b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 28 Mar 2018 19:02:02 -0400 Subject: [PATCH 150/339] GBHawk: Fix GBC HDMA behaviour (Wacky Racers) --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index e0ae904368..933dfd8154 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -160,15 +160,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // These are GBC specific Regs case 0xFF51: // HDMA1 HDMA_src_hi = value; + cur_DMA_src = (ushort)(((HDMA_src_hi & 0xFF) << 8) | (cur_DMA_src & 0xF0)); break; case 0xFF52: // HDMA2 HDMA_src_lo = value; + cur_DMA_src = (ushort)((cur_DMA_src & 0xFF00) | (HDMA_src_lo & 0xF0)); break; case 0xFF53: // HDMA3 HDMA_dest_hi = value; + cur_DMA_dest = (ushort)(((HDMA_dest_hi & 0x1F) << 8) | (cur_DMA_dest & 0xF0)); break; case 0xFF54: // HDMA4 HDMA_dest_lo = value; + cur_DMA_dest = (ushort)((cur_DMA_dest & 0xFF00) | (HDMA_dest_lo & 0xF0)); break; case 0xFF55: // HDMA5 if (!HDMA_active) @@ -180,7 +184,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // HDMA during HBlank only HDMA_active = true; HBL_HDMA_count = 0x10; - last_HBL = LY; + + // TODO: DOES HDMA start if triggered in mode 0 immediately? (for now assume no) + if ((STAT & 3) == 0) + { + last_HBL = LY; + } + else + { + last_HBL = LY - 1; + } + HBL_test = true; HBL_HDMA_go = false; } @@ -191,10 +205,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Core.HDMA_transfer = true; } - // latch read locations - cur_DMA_dest = (ushort)(((HDMA_dest_hi & 0x1F) << 8) | (HDMA_dest_lo & 0xF0)); - cur_DMA_src = (ushort)(((HDMA_src_hi & 0xFF) << 8) | (HDMA_src_lo & 0xF0)); - HDMA_length = ((value & 0x7F) + 1) * 16; } else From c829534170d140f5784f997115b5c99f5352ddaf Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 29 Mar 2018 09:26:55 -0400 Subject: [PATCH 151/339] GBHawk: Bug Fixes, fix to Beast fighter --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 6 +++++- .../Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs | 2 +- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs | 7 ++++++- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs | 2 ++ BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs | 2 +- 5 files changed, 15 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 933dfd8154..304d1aa276 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -372,6 +372,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_y_tile = 0; window_y_tile_inc = 0; window_started = false; + window_is_reset = true; } Core.cpu.LY = LY; @@ -632,7 +633,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk evaled_sprites = 0; window_pre_render = false; - if (window_started && LCDC.Bit(5)) + + // TODO: If Window is turned on midscanline what happens? When is this check done exactly? + if ((window_started && LCDC.Bit(5)) || (window_is_reset && !LCDC.Bit(5) && (LY > window_y))) { window_y_tile_inc++; if (window_y_tile_inc==8) @@ -682,6 +685,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_tile_inc = 0; window_started = true; + window_is_reset = false; } if (!pre_render && !fetch_sprite && !window_pre_render) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 58d1261388..f1d1cf846c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -119,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk [JsonIgnore] private int _RTCInitialTime; [JsonIgnore] - private ushort _DivInitialTime; + public ushort _DivInitialTime; public GBSyncSettings Clone() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index e34e75480c..209af304ed 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -146,6 +146,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_y_tile = 0; window_y_tile_inc = 0; window_started = false; + window_is_reset = true; } Core.cpu.LY = LY; @@ -406,7 +407,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk evaled_sprites = 0; window_pre_render = false; - if (window_started && LCDC.Bit(5)) + + // TODO: If Window is turned on midscanline what happens? When is this check done exactly? + if ((window_started && LCDC.Bit(5)) || (window_is_reset && !LCDC.Bit(5) && (LY > window_y))) { window_y_tile_inc++; if (window_y_tile_inc==8) @@ -456,6 +459,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_tile_inc = 0; window_started = true; + window_is_reset = false; } if (!pre_render && !fetch_sprite && !window_pre_render) @@ -1146,6 +1150,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_counter = 0; window_pre_render = false; window_started = false; + window_is_reset = true; window_tile_inc = 0; window_y_tile = 0; window_x_tile = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 9a4f94c409..1dc152fd00 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -96,6 +96,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int window_counter; public bool window_pre_render; public bool window_started; + public bool window_is_reset; public int window_tile_inc; public int window_y_tile; public int window_x_tile; @@ -243,6 +244,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("window_counter", ref window_counter); ser.Sync("window_pre_render", ref window_pre_render); ser.Sync("window_started", ref window_started); + ser.Sync("window_is_reset", ref window_is_reset); ser.Sync("window_tile_inc", ref window_tile_inc); ser.Sync("window_y_tile", ref window_y_tile); ser.Sync("window_x_tile", ref window_x_tile); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs index 817c28201e..1a6aa88e0a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs @@ -144,7 +144,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void Reset() { - divider_reg = 0; + divider_reg = Core._syncSettings._DivInitialTime; timer_reload = 0; timer = 0; timer_old = 0; From 7a21f55c438d3dcde61da50bb43b2973990eeda1 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 30 Mar 2018 21:56:32 -0400 Subject: [PATCH 152/339] GBHawk: Add RockMan 8 mapper --- .../BizHawk.Emulation.Cores.csproj | 7 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 9 ++- .../GBHawk/Mappers/Mapper_RockMan8.cs | 69 +++++++++++++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_RockMan8.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 7700b36452..ddf0709624 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -641,6 +641,7 @@ + @@ -685,11 +686,11 @@ - - PPU.cs + + PPU.cs - PPU.cs + PPU.cs diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 3b48926029..6becfb67af 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -367,6 +367,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk mapper = new MapperMBC1Multi(); } + // special case for bootlegs + if ((_rom.HashMD5(0, _rom.Length) == "CAE0998A899DF2EE6ABA8E7695C2A096")) + { + Console.WriteLine("Using RockMan 8 (Unlicensed) Mapper"); + mapper = new MapperRM8(); + } + Console.Write("Mapper: "); Console.WriteLine(mppr); @@ -413,7 +420,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (cart_RAM != null) { - Console.Write("RAM: "); Console.WriteLine(cart_RAM.Length); for (int i = 0; i < cart_RAM.Length; i++) @@ -422,7 +428,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - // Extra RTC initialization for mbc3 if (mppr == "MBC3") { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_RockMan8.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_RockMan8.cs new file mode 100644 index 0000000000..a45175f84e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_RockMan8.cs @@ -0,0 +1,69 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // RockMan 8, just some simple bankswitching + public class MapperRM8 : MapperBase + { + public int ROM_bank; + public int ROM_mask; + + public override void Initialize() + { + ROM_bank = 1; + ROM_mask = Core._rom.Length / 0x4000 - 1; + + // some games have sizes that result in a degenerate ROM, account for it here + if (ROM_mask > 4) { ROM_mask |= 3; } + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + // lowest bank is fixed + return Core._rom[addr]; + + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else + { + return 0xFF; + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if ((addr >= 0x2000) && (addr < 0x4000)) + { + value &= 0x1F; + + if (value == 0) { value = 1; } + + // in hhugboy they just subtract 8, but to me looks like bits 4 and 5 are just swapped (and bit 4 is unused?) + ROM_bank = ((value & 0xF) | ((value & 0x10) >> 1)) & ROM_mask; + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + } + } +} From b2ed6eb737c056750af8af756f1df78dcbe83539 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 31 Mar 2018 11:23:55 -0400 Subject: [PATCH 153/339] Always initialize independent instances of IController in cores to NullController - Fixes #1168 --- BizHawk.Emulation.Cores/Calculator/TI83.cs | 2 +- .../Computers/Commodore64/C64.Motherboard.cs | 2 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs | 2 +- .../Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs | 2 +- BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs | 2 +- BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs | 2 +- BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs | 2 +- BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/Calculator/TI83.cs b/BizHawk.Emulation.Cores/Calculator/TI83.cs index 403f0fa7b8..2cc63e9b7c 100644 --- a/BizHawk.Emulation.Cores/Calculator/TI83.cs +++ b/BizHawk.Emulation.Cores/Calculator/TI83.cs @@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Cores.Calculators private readonly byte[] _rom; // configuration - private IController _controller; + private IController _controller = NullController.Instance; private byte[] _ram; private byte[] _vram = new byte[0x300]; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs index e0f51a0b5d..b4b5a7b9b1 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 // ports public readonly CartridgePort CartPort; public readonly CassettePort Cassette; - public IController Controller; + public IController Controller = NullController.Instance; public readonly SerialPort Serial; public readonly TapeDrive TapeDrive; public readonly UserPort User; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index f6c97c692e..7fee26c1f0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -131,7 +131,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private readonly Z80A _cpu; private readonly TraceBuffer _tracer; - public IController _controller; + public IController _controller = NullController.Instance; public SpectrumBase _machine; private List _gameInfo; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs index 6d5e8b0f27..4cc9af86d3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs @@ -18,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private MapperBase _mapper; private byte[] _ram; - private IController _controller; + private IController _controller = NullController.Instance; private int _frame; private int _lastAddress; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs index 27f51380c7..a4aa5a5b3d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs @@ -76,7 +76,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk GetControllerState(controller); GetConsoleState(controller); - + maria.RunFrame(); if (_islag) diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs index e9bcba4f8f..1a5b88475c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs @@ -75,7 +75,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision public bool temp_1_prev, temp_2_prev; private int _frame; - private IController _controller; + private IController _controller = NullController.Instance; private enum InputPortMode { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs index 7828f82e1d..17380d3aea 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs @@ -309,7 +309,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES private int VsyncNum { get; set; } private int VsyncDen { get; set; } - private IController _controller; + private IController _controller = NullController.Instance; bool resetSignal; bool hardResetSignal; diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs index 808a751862..918bb5699b 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs @@ -124,7 +124,7 @@ namespace BizHawk.Emulation.Cores.PCEngine private VPC VPC; private ScsiCDBus SCSI; private ADPCM ADPCM; - private IController _controller; + private IController _controller = NullController.Instance; public HuC6280PSG PSG; internal CDAudio CDAudio; diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs index 885fde2dab..b0bfe21403 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs @@ -216,7 +216,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem private bool HasYM2413 = false; private bool PortDEEnabled = false; - private IController _controller; + private IController _controller = NullController.Instance; private int _frame = 0; From fde2035f310f49028af6811cd81fd92c496a5ac6 Mon Sep 17 00:00:00 2001 From: "Dan B (Narry)" Date: Sat, 31 Mar 2018 10:49:39 -0400 Subject: [PATCH 154/339] Fixes #1167 by moving certain settings into the SyncSettings --- .../Consoles/Sega/gpgx64/GPGX.ISettable.cs | 110 +++++++++--------- .../Consoles/Sega/gpgx64/GPGX.cs | 2 +- 2 files changed, 58 insertions(+), 54 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs index 261a4418a3..e5ef0463c2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs @@ -198,6 +198,63 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx [DefaultValue(false)] public bool PadScreen320 { get { return _PadScreen320; } set { _PadScreen320 = value; } } + + [DeepEqualsIgnore] + [JsonIgnore] + private bool _Backdrop; + + [DisplayName("Use custom backdrop color")] + [Description("Filler when layers are off")] + [DefaultValue((bool)false)] + public bool Backdrop { get { return _Backdrop; } set { _Backdrop = value; } } + + + public GPGXSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public GPGXSettings Clone() + { + return (GPGXSettings)MemberwiseClone(); + } + + public LibGPGX.DrawMask GetDrawMask() + { + LibGPGX.DrawMask ret = 0; + if (DrawBGA) ret |= LibGPGX.DrawMask.BGA; + if (DrawBGB) ret |= LibGPGX.DrawMask.BGB; + if (DrawBGW) ret |= LibGPGX.DrawMask.BGW; + if (DrawObj) ret |= LibGPGX.DrawMask.Obj; + if (Backdrop) ret |= LibGPGX.DrawMask.Backdrop; + return ret; + } + + public static bool NeedsReboot(GPGXSettings x, GPGXSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + + + } + + public class GPGXSyncSettings + { + [DisplayName("Use Six Button Controllers")] + [Description("Controls the type of any attached normal controllers; six button controllers are used if true, otherwise three button controllers. Some games don't work correctly with six button controllers. Not relevant if other controller types are connected.")] + [DefaultValue(true)] + public bool UseSixButton { get; set; } + + [DisplayName("Control Type")] + [Description("Sets the type of controls that are plugged into the console. Some games will automatically load with a different control type.")] + [DefaultValue(ControlType.Normal)] + public ControlType ControlType { get; set; } + + [DisplayName("Autodetect Region")] + [Description("Sets the region of the emulated console. Many games can run on multiple regions and will behave differently on different ones. Some games may require a particular region.")] + [DefaultValue(LibGPGX.Region.Autodetect)] + public LibGPGX.Region Region { get; set; } + [DisplayName("Audio Filter")] [DefaultValue(LibGPGX.InitSettings.FilterType.LowPass)] public LibGPGX.InitSettings.FilterType Filter { get; set; } @@ -233,46 +290,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx [DefaultValue((short)1)] public short HighGain { get; set; } - [DeepEqualsIgnore] - [JsonIgnore] - private bool _Backdrop; - - [DisplayName("Use custom backdrop color")] - [Description("Filler when layers are off")] - [DefaultValue((bool)false)] - public bool Backdrop { get { return _Backdrop; } set { _Backdrop = value; } } - [Description("Magic pink by default. Requires core reboot")] [TypeConverter(typeof(UintToHexConverter))] [DefaultValue((uint)0xffff00ff)] public uint BackdropColor { get; set; } - public GPGXSettings() - { - SettingsUtil.SetDefaultValues(this); - } - - public GPGXSettings Clone() - { - return (GPGXSettings)MemberwiseClone(); - } - - public LibGPGX.DrawMask GetDrawMask() - { - LibGPGX.DrawMask ret = 0; - if (DrawBGA) ret |= LibGPGX.DrawMask.BGA; - if (DrawBGB) ret |= LibGPGX.DrawMask.BGB; - if (DrawBGW) ret |= LibGPGX.DrawMask.BGW; - if (DrawObj) ret |= LibGPGX.DrawMask.Obj; - if (Backdrop) ret |= LibGPGX.DrawMask.Backdrop; - return ret; - } - - public static bool NeedsReboot(GPGXSettings x, GPGXSettings y) - { - return !DeepEquality.DeepEquals(x, y); - } - public LibGPGX.InitSettings GetNativeSettings() { return new LibGPGX.InitSettings @@ -287,24 +309,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx BackdropColor = BackdropColor }; } - } - - public class GPGXSyncSettings - { - [DisplayName("Use Six Button Controllers")] - [Description("Controls the type of any attached normal controllers; six button controllers are used if true, otherwise three button controllers. Some games don't work correctly with six button controllers. Not relevant if other controller types are connected.")] - [DefaultValue(true)] - public bool UseSixButton { get; set; } - - [DisplayName("Control Type")] - [Description("Sets the type of controls that are plugged into the console. Some games will automatically load with a different control type.")] - [DefaultValue(ControlType.Normal)] - public ControlType ControlType { get; set; } - - [DisplayName("Autodetect Region")] - [Description("Sets the region of the emulated console. Many games can run on multiple regions and will behave differently on different ones. Some games may require a particular region.")] - [DefaultValue(LibGPGX.Region.Autodetect)] - public LibGPGX.Region Region { get; set; } public GPGXSyncSettings() { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs index fafb34dde3..c28bfe7067 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs @@ -113,7 +113,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx break; } - if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings())) + if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _syncSettings.GetNativeSettings())) throw new Exception("gpgx_init() failed"); { From f968cbdd73c2da51f47bc9a132d512ea50b3c4f4 Mon Sep 17 00:00:00 2001 From: gifvex Date: Mon, 2 Apr 2018 18:19:10 -0400 Subject: [PATCH 155/339] Gambatte: implement TotalExecutedCycles --- .../Nintendo/Gameboy/Gambatte.IDebuggable.cs | 28 ++++++++++++++---- .../Consoles/Nintendo/Gameboy/Gambatte.cs | 1 + .../Consoles/Nintendo/Gameboy/LibGambatte.cs | 2 +- libgambatte/include/gambatte.h | 7 +++-- libgambatte/src/cinterface.cpp | 6 ++-- libgambatte/src/cpu.cpp | 1 + libgambatte/src/cpu.h | 6 ++-- libgambatte/src/gambatte.cpp | 6 ++-- libgambatte/src/memory.h | 24 +++++++++------ output/dll/libgambatte.dll | Bin 185856 -> 177664 bytes 10 files changed, 53 insertions(+), 28 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs index 8423fd437b..f9d8d42534 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs @@ -44,19 +44,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy throw new NotImplementedException(); } - [FeatureNotImplemented] public int TotalExecutedCycles { - get { throw new NotImplementedException(); } + get { return (int)Math.Max(_cycleCount, callbackCycleCount); } } + private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks; private LibGambatte.MemoryCallback _readcb; private LibGambatte.MemoryCallback _writecb; private LibGambatte.MemoryCallback _execcb; - private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); + private void ReadCallback(uint address, ulong cycleOffset) + { + callbackCycleCount = _cycleCount + cycleOffset; + MemoryCallbacks.CallReads(address, "System Bus"); + } + + private void WriteCallback(uint address, ulong cycleOffset) + { + callbackCycleCount = _cycleCount + cycleOffset; + MemoryCallbacks.CallWrites(address, "System Bus"); + } + + private void ExecCallback(uint address, ulong cycleOffset) + { + callbackCycleCount = _cycleCount + cycleOffset; + MemoryCallbacks.CallExecutes(address, "System Bus"); + } /// /// for use in dual core @@ -68,9 +84,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy private void InitMemoryCallbacks() { - _readcb = addr => MemoryCallbacks.CallReads(addr, "System Bus"); - _writecb = addr => MemoryCallbacks.CallWrites(addr, "System Bus"); - _execcb = addr => MemoryCallbacks.CallExecutes(addr, "System Bus"); + _readcb = new LibGambatte.MemoryCallback(ReadCallback); + _writecb = new LibGambatte.MemoryCallback(WriteCallback); + _execcb = new LibGambatte.MemoryCallback(ExecCallback); _memorycallbacks.ActiveChanged += RefreshMemoryCallbacks; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 4fa2a67ec4..1f1891e1a5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -244,6 +244,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// total cycles actually executed /// private ulong _cycleCount = 0; + private ulong callbackCycleCount = 0; /// /// number of extra cycles we overran in the last frame diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index ecb9b432ca..1a05daea20 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -190,7 +190,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// /// the address which the cpu is read\writing [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void MemoryCallback(uint address); + public delegate void MemoryCallback(uint address, ulong cycleOffset); /// /// type of the CDLogger callback diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 2b953984a9..94bab7befb 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -28,6 +28,7 @@ namespace gambatte { enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 }; +typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags); enum eCDLog_AddrType @@ -105,9 +106,9 @@ public: /** Sets the callback used for getting input state. */ void setInputGetter(unsigned (*getInput)()); - void setReadCallback(void (*callback)(unsigned)); - void setWriteCallback(void (*callback)(unsigned)); - void setExecCallback(void (*callback)(unsigned)); + void setReadCallback(MemoryCallback); + void setWriteCallback(MemoryCallback); + void setExecCallback(MemoryCallback); void setCDCallback(CDCallback); void setTraceCallback(void (*callback)(void *)); void setScanlineCallback(void (*callback)(), int sl); diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index fc4d793a75..ad8247f17c 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -85,17 +85,17 @@ GBEXPORT void gambatte_setinputgetter(GB *g, unsigned (*getinput)(void)) g->setInputGetter(getinput); } -GBEXPORT void gambatte_setreadcallback(GB *g, void (*callback)(unsigned)) +GBEXPORT void gambatte_setreadcallback(GB *g, MemoryCallback callback) { g->setReadCallback(callback); } -GBEXPORT void gambatte_setwritecallback(GB *g, void (*callback)(unsigned)) +GBEXPORT void gambatte_setwritecallback(GB *g, MemoryCallback callback) { g->setWriteCallback(callback); } -GBEXPORT void gambatte_setexeccallback(GB *g, void (*callback)(unsigned)) +GBEXPORT void gambatte_setexeccallback(GB *g, MemoryCallback callback) { g->setExecCallback(callback); } diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 0fcebe0ca6..3db9a3c7c0 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -45,6 +45,7 @@ CPU::CPU() } long CPU::runFor(const unsigned long cycles) { + memory.setBasetime(cycleCounter_); process(cycles/* << memory.isDoubleSpeed()*/); const long csb = memory.cyclesSinceBlit(cycleCounter_); diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 46dc592015..427847e95d 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -72,15 +72,15 @@ public: memory.setInputGetter(getInput); } - void setReadCallback(void (*callback)(unsigned)) { + void setReadCallback(MemoryCallback callback) { memory.setReadCallback(callback); } - void setWriteCallback(void (*callback)(unsigned)) { + void setWriteCallback(MemoryCallback callback) { memory.setWriteCallback(callback); } - void setExecCallback(void (*callback)(unsigned)) { + void setExecCallback(MemoryCallback callback) { memory.setExecCallback(callback); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 167e1093e1..a1e3dfa65c 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -108,15 +108,15 @@ void GB::setInputGetter(unsigned (*getInput)()) { p_->cpu.setInputGetter(getInput); } -void GB::setReadCallback(void (*callback)(unsigned)) { +void GB::setReadCallback(MemoryCallback callback) { p_->cpu.setReadCallback(callback); } -void GB::setWriteCallback(void (*callback)(unsigned)) { +void GB::setWriteCallback(MemoryCallback callback) { p_->cpu.setWriteCallback(callback); } -void GB::setExecCallback(void (*callback)(unsigned)) { +void GB::setExecCallback(MemoryCallback callback) { p_->cpu.setExecCallback(callback); } diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 95a2e18f16..ac2b5d2d17 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -44,10 +44,11 @@ class Memory { bool gbIsCgb_; unsigned short &SP; unsigned short &PC; + unsigned long basetime; - void (*readCallback)(unsigned); - void (*writeCallback)(unsigned); - void (*execCallback)(unsigned); + MemoryCallback readCallback; + MemoryCallback writeCallback; + MemoryCallback execCallback; CDCallback cdCallback; void(*linkCallback)(); @@ -134,6 +135,8 @@ public: void di() { intreq.di(); } unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { + if (readCallback) + readCallback(P, (cycleCounter - basetime) >> 1); return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00]; } @@ -206,7 +209,7 @@ public: unsigned read(const unsigned P, const unsigned long cycleCounter) { if (readCallback) - readCallback(P); + readCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -221,7 +224,7 @@ public: unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) { if (execCallback) - execCallback(P); + execCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -254,7 +257,7 @@ public: } else nontrivial_write(P, data, cycleCounter); if (writeCallback) - writeCallback(P); + writeCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -268,6 +271,8 @@ public: ioamhram[P - 0xFE00] = data; } else nontrivial_ff_write(P, data, cycleCounter); + if (writeCallback) + writeCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -285,13 +290,13 @@ public: this->getInput = getInput; } - void setReadCallback(void (*callback)(unsigned)) { + void setReadCallback(MemoryCallback callback) { this->readCallback = callback; } - void setWriteCallback(void (*callback)(unsigned)) { + void setWriteCallback(MemoryCallback callback) { this->writeCallback = callback; } - void setExecCallback(void (*callback)(unsigned)) { + void setExecCallback(MemoryCallback callback) { this->execCallback = callback; } void setCDCallback(CDCallback cdc) { @@ -310,6 +315,7 @@ public: this->linkCallback = callback; } + void setBasetime(unsigned long cc) { basetime = cc; } void setEndtime(unsigned long cc, unsigned long inc); void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 20e91644161b286b91edcc243f6fa79ba422aa58..4c02731caf11359d434846f5270e96c7848fd240 100644 GIT binary patch delta 67616 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%0A_1s?oHZ?O13?> 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 cse9H``>Ly}S&dltA{QcAMzpp3w`i%YybcN delta 73779 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 X?vmZCOg{dSNDeA5u*tVx68-)UBI{HM From f48a79509075be86ba48fa4bca6092b6a00b2a7a Mon Sep 17 00:00:00 2001 From: gifvex Date: Mon, 2 Apr 2018 18:19:40 -0400 Subject: [PATCH 156/339] MemoryCallbackSystem: fix #1159 --- .../MemoryCallbackSystem.cs | 37 ++++++------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs b/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs index 084a110174..c28c1f9c8d 100644 --- a/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs +++ b/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs @@ -33,8 +33,6 @@ namespace BizHawk.Emulation.Common private readonly ObservableCollection _writes = new ObservableCollection(); private readonly ObservableCollection _execs = new ObservableCollection(); - private bool _empty = true; - private bool _hasReads; private bool _hasWrites; private bool _hasExecutes; @@ -54,24 +52,19 @@ namespace BizHawk.Emulation.Common { case MemoryCallbackType.Execute: _execs.Add(callback); - _hasExecutes = true; break; case MemoryCallbackType.Read: _reads.Add(callback); - _hasReads = true; break; case MemoryCallbackType.Write: _writes.Add(callback); - _hasWrites = true; break; } - if (_empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = false; } private static void Call(ObservableCollection cbs, uint addr, string scope) @@ -130,11 +123,17 @@ namespace BizHawk.Emulation.Common return _execs.Where(e => e.Scope == scope).Any(); } - private void UpdateHasVariables() + private bool UpdateHasVariables() { + bool hadReads = _hasReads; + bool hadWrites = _hasWrites; + bool hadExecutes = _hasExecutes; + _hasReads = _reads.Count > 0; _hasWrites = _writes.Count > 0; _hasExecutes = _execs.Count > 0; + + return (_hasReads != hadReads || _hasWrites != hadWrites || _hasExecutes != hadExecutes); } private int RemoveInternal(Action action) @@ -158,8 +157,6 @@ namespace BizHawk.Emulation.Common _execs.Remove(exec); } - UpdateHasVariables(); - return readsToRemove.Count + writesToRemove.Count + execsToRemove.Count; } @@ -167,13 +164,10 @@ namespace BizHawk.Emulation.Common { if (RemoveInternal(action) > 0) { - bool newEmpty = !HasReads && !HasWrites && !HasExecutes; - if (newEmpty != _empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = newEmpty; } } @@ -187,16 +181,11 @@ namespace BizHawk.Emulation.Common if (changed) { - bool newEmpty = !HasReads && !HasWrites && !HasExecutes; - if (newEmpty != _empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = newEmpty; } - - UpdateHasVariables(); } public void Clear() @@ -217,14 +206,10 @@ namespace BizHawk.Emulation.Common _execs.RemoveAt(i); } - if (!_empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = true; - - UpdateHasVariables(); } public delegate void ActiveChangedEventHandler(); From 6019073157d45fef97caa3ec570c55aa2945bc4a Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 3 Apr 2018 18:18:41 -0400 Subject: [PATCH 157/339] GBHawk: Fix some GBC Behaviours, start MBC7 --- .../CPUs/LR35902/Interrupts.cs | 29 ++++ .../CPUs/LR35902/LR35902.cs | 11 +- .../CPUs/LR35902/Tables_Direct.cs | 1 - .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 94 +++++------ .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 4 + .../Nintendo/GBHawk/GBHawkControllers.cs | 96 +++++++++++ .../Nintendo/GBHawk/Mappers/Mapper_MBC5.cs | 2 +- .../Nintendo/GBHawk/Mappers/Mapper_MBC7.cs | 157 ++++++++++++++++-- 8 files changed, 330 insertions(+), 64 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index f57d04eb4b..04e9eb38fb 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -52,6 +52,35 @@ namespace BizHawk.Emulation.Common.Components.LR35902 OP }; } + private void INTERRUPT_GBC_NOP() + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, PCh, + IDLE, + INT_GET, W,// NOTE: here is where we check for a cancelled IRQ + DEC16, SPl, SPh, + WR, SPl, SPh, PCl, + IDLE, + IDLE, + IDLE, + IDLE, + TR, PCl, W, + ASGN, PCh, 0, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + OP }; + } + private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60, 0x00}; public ushort int_src; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 3ad5bdd7d4..02205e5c09 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -279,18 +279,21 @@ namespace BizHawk.Emulation.Common.Components.LR35902 }); } halted = false; - + if (is_GBC) { // call the interrupt processor after 4 extra cycles + /* cur_instr = new ushort[] {IDLE, IDLE, IDLE, GBC_INTERRUPT }; + */ + INTERRUPT_GBC_NOP(); } else - { + { // call interrupt processor INTERRUPT_(); } @@ -309,6 +312,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 halted = false; if (OnExecFetch != null) OnExecFetch(RegPC); if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + if (is_GBC) { // extra 4 cycles for GBC @@ -319,7 +323,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 OP }; } else - { + { FetchInstruction(ReadMemory(RegPC++)); } } @@ -335,7 +339,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 break; case STOP: stopped = true; - if (!stop_check) { stop_time = SpeedFunc(0); diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index 9eb91d9031..78e64d7846 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -79,7 +79,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 if (FlagI && (EI_pending == 0)) { - if (is_GBC) { // in GBC mode, the HALT bug is worked around by simply adding a NOP diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 304d1aa276..0a39d9269c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -22,6 +22,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte HDMA_src_lo; public byte HDMA_dest_hi; public byte HDMA_dest_lo; + public int HDMA_tick; + public byte HDMA_byte; // accessors for derived values public byte BG_pal_ret @@ -179,6 +181,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { HDMA_mode = value.Bit(7); HDMA_countdown = 4; + HDMA_tick = 0; if (value.Bit(7)) { // HDMA during HBlank only @@ -253,59 +256,47 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (HDMA_countdown == 0) { - if (!HDMA_mode) + if (HDMA_length > 0) { - // immediately transfer bytes, 2 bytes per cycles - if (HDMA_length > 0) + if (!HDMA_mode) { - Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); - cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); - cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); - HDMA_length--; - - Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); - cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); - cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); - HDMA_length--; - - if (HDMA_length == 0) + // immediately transfer bytes, 2 bytes per cycles + if ((HDMA_tick % 2) == 0) { - HDMA_active = false; - Core.HDMA_transfer = false; + HDMA_byte = Core.ReadMemory(cur_DMA_src); + } + else + { + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = HDMA_byte; + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; } } - } - else - { - // only transfer during mode 0, and only 16 bytes at a time - if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test) + else { - HBL_HDMA_go = true; - HBL_test = false; - } - - if (HBL_HDMA_go && (HBL_HDMA_count > 0)) - { - Core.HDMA_transfer = true; - - if (HDMA_length > 0) + // only transfer during mode 0, and only 16 bytes at a time + if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test) { - Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); - cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); - cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); - HDMA_length--; - HBL_HDMA_count--; + HBL_HDMA_go = true; + HBL_test = false; + } - Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = Core.ReadMemory(cur_DMA_src); - cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); - cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); - HDMA_length--; - HBL_HDMA_count--; + if (HBL_HDMA_go && (HBL_HDMA_count > 0)) + { + Core.HDMA_transfer = true; - if (HDMA_length == 0) + if ((HDMA_tick % 2) == 0) { - HDMA_active = false; - Core.HDMA_transfer = false; + HDMA_byte = Core.ReadMemory(cur_DMA_src); + } + else + { + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = HDMA_byte; + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; + HBL_HDMA_count--; } if ((HBL_HDMA_count == 0) && (HDMA_length != 0)) @@ -316,11 +307,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk HBL_HDMA_go = false; } } + else + { + Core.HDMA_transfer = false; + } } - else - { - Core.HDMA_transfer = false; - } + + HDMA_tick++; + } + else + { + HDMA_active = false; + Core.HDMA_transfer = false; } } else @@ -1481,6 +1479,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("HDMA_src_lo", ref HDMA_src_lo); ser.Sync("HDMA_dest_hi", ref HDMA_dest_hi); ser.Sync("HDMA_dest_lo", ref HDMA_dest_lo); + ser.Sync("HDMA_tick", ref HDMA_tick); + ser.Sync("HDMA_byte", ref HDMA_byte); ser.Sync("VRAM_sel", ref VRAM_sel); ser.Sync("BG_V_flip", ref BG_V_flip); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 7a5354a093..a57581e6ae 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -137,9 +137,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { + timer.tick_1(); + timer.tick_2(); cpu.TotalExecutedCycles++; if (double_speed) { + timer.tick_1(); + timer.tick_2(); cpu.TotalExecutedCycles++; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs index 87006a5539..cd7000330c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs @@ -15,6 +15,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { byte Read(IController c); + ushort ReadAccX(IController c); + + ushort ReadAccY(IController c); + ControllerDefinition Definition { get; } void SyncState(Serializer ser); @@ -81,6 +85,98 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return result; } + public ushort ReadAccX(IController c) + { + return 0; + } + + public ushort ReadAccY(IController c) + { + return 0; + } + + private static readonly string[] BaseDefinition = + { + "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" + }; + + public void SyncState(Serializer ser) + { + //nothing + } + } + + [DisplayName("Gameboy Controller + Kirby")] + public class StandardKirby : IPort + { + public StandardKirby(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Gameboy Controller + Kirby", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList(), + FloatControls = { "P" + PortNum + " Tilt X", "P" + PortNum + " Tilt Y" }, + FloatRanges = { new[] { -127.0f, 0, 127.0f }, new[] { -127.0f, 0, 127.0f } } + }; + } + + public int PortNum { get; } + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + + if (c.IsPressed(Definition.BoolButtons[0])) + { + result -= 4; + } + if (c.IsPressed(Definition.BoolButtons[1])) + { + result -= 8; + } + if (c.IsPressed(Definition.BoolButtons[2])) + { + result -= 2; + } + if (c.IsPressed(Definition.BoolButtons[3])) + { + result -= 1; + } + if (c.IsPressed(Definition.BoolButtons[4])) + { + result -= 128; + } + if (c.IsPressed(Definition.BoolButtons[5])) + { + result -= 64; + } + if (c.IsPressed(Definition.BoolButtons[6])) + { + result -= 32; + } + if (c.IsPressed(Definition.BoolButtons[7])) + { + result -= 16; + } + + return result; + } + + public ushort ReadAccX(IController c) + { + return 0; + } + + public ushort ReadAccY(IController c) + { + return 0; + } + private static readonly string[] BaseDefinition = { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs index 7c63b10f55..3c1ea0c60f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs @@ -4,7 +4,7 @@ using System; namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { - // Default mapper with no bank switching + // MBC5, common mapper for GBC games public class MapperMBC5 : MapperBase { public int ROM_bank; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs index 24d957c653..28114486dc 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -4,31 +4,63 @@ using System; namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { - // Default mapper with no bank switching + // Mapper with built in EEPROM, also used with Kirby's tilt 'n tumble + // The EEPROM contains 256 bytes of read/write memory public class MapperMBC7 : MapperBase { + public int ROM_bank; + public bool RAM_enable_1, RAM_enable_2; + public int ROM_mask; + public byte acc_x_low; + public byte acc_x_high; + public byte acc_y_low; + public byte acc_y_high; + public bool is_erased; + public override void Initialize() { - // nothing to initialize + ROM_bank = 1; + RAM_enable_1 = RAM_enable_2 = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + + // some games have sizes that result in a degenerate ROM, account for it here + if (ROM_mask > 4) { ROM_mask |= 3; } + + acc_x_low = 0; + acc_x_high = 0x80; + acc_y_low = 0; + acc_y_high = 0x80; } public override byte ReadMemory(ushort addr) { - if (addr < 0x8000) + if (addr < 0x4000) { return Core._rom[addr]; } - else + else if (addr < 0x8000) { - if (Core.cart_RAM != null) + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else if (addr < 0xA000) + { + return 0xFF; + } + else if (addr < 0xB000) + { + if (RAM_enable_2) { - return Core.cart_RAM[addr - 0xA000]; + return Register_Access_Read(addr); } else { - return 0; + return 0xFF; } } + else + { + return 0xFF; + } } public override byte PeekMemory(ushort addr) @@ -38,15 +70,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void WriteMemory(ushort addr, byte value) { - if (addr < 0x8000) + if (addr < 0xA000) { - // no mapping hardware available + if (addr < 0x2000) + { + RAM_enable_1 = (value & 0xF) == 0xA; + } + else if (addr < 0x4000) + { + value &= 0xFF; + + ROM_bank &= 0x100; + ROM_bank |= value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + if (RAM_enable_1) + { + RAM_enable_2 = (value & 0xF0) == 0x40; + } + } } else { - if (Core.cart_RAM != null) + if (RAM_enable_2) { - Core.cart_RAM[addr - 0xA000] = value; + Register_Access_Write(addr, value); } } } @@ -55,5 +105,90 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { WriteMemory(addr, value); } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_enable_1", ref RAM_enable_1); + ser.Sync("RAM_enable_2", ref RAM_enable_2); + ser.Sync("acc_x_low", ref acc_x_low); + ser.Sync("acc_x_high", ref acc_x_high); + ser.Sync("acc_y_low", ref acc_y_low); + ser.Sync("acc_y_high", ref acc_y_high); + ser.Sync("is_erased", ref is_erased); + } + + public void Register_Access_Write(ushort addr, byte value) + { + if ((addr & 0xA0F0) == 0xA000) + { + if (value == 0x55) + { + is_erased = true; + acc_x_low = 0; + acc_x_high = 0x80; + acc_y_low = 0; + acc_y_high = 0x80; + } + } + else if ((addr & 0xA0F0) == 0xA010) + { + if ((value == 0xAA) && is_erased) + { + // latch new accelerometer values + } + } + else if ((addr & 0xA0F0) == 0xA080) + { + + } + } + + public byte Register_Access_Read(ushort addr) + { + if ((addr & 0xA0F0) == 0xA000) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA010) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA020) + { + return acc_x_low; + } + else if ((addr & 0xA0F0) == 0xA030) + { + return acc_x_high; + } + else if ((addr & 0xA0F0) == 0xA040) + { + return acc_y_low; + } + else if ((addr & 0xA0F0) == 0xA050) + { + return acc_y_high; + } + else if ((addr & 0xA0F0) == 0xA060) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA070) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA080) + { + return 0xFF; + } + else + { + return 0xFF; + } + } + + } } From a5eca362c8a72f46f4693155496080d55425ee14 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 6 Apr 2018 19:11:21 -0400 Subject: [PATCH 158/339] GBHawk: MBC7 (Kirby tilt n tumble) - Implement controller - Fix some GBC bugs - Start eeprom (WIP) --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 11 +- .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 5 + .../Nintendo/GBHawk/GBHawk.ISettable.cs | 26 +- .../Nintendo/GBHawk/GBHawk.IStatable.cs | 2 + .../Consoles/Nintendo/GBHawk/GBHawk.cs | 9 +- .../Nintendo/GBHawk/GBHawkControllerDeck.cs | 10 + .../Nintendo/GBHawk/GBHawkControllers.cs | 15 +- .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 2 +- .../Nintendo/GBHawk/Mappers/Mapper_MBC7.cs | 300 ++++++++++++++++-- 9 files changed, 336 insertions(+), 44 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 0a39d9269c..14c436a3ac 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -230,7 +230,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // change the appropriate palette color color_compute_BG(); - if (BG_bytes_inc) { BG_bytes_index++; BG_bytes_index &= 0x3F; } break; case 0xFF6A: // OBPI @@ -272,11 +271,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); HDMA_length--; } + + HDMA_tick++; } else { // only transfer during mode 0, and only 16 bytes at a time - if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test) + if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test && (LY_inc == 1)) { HBL_HDMA_go = true; HBL_test = false; @@ -306,14 +307,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk HBL_HDMA_count = 0x10; HBL_HDMA_go = false; } + + HDMA_tick++; } else { Core.HDMA_transfer = false; } - } - - HDMA_tick++; + } } else { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index a57581e6ae..b5e3dc33e9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -13,6 +13,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; public byte controller_state; + public ushort Acc_X_state; + public ushort Acc_Y_state; public bool in_vblank_old; public bool in_vblank; public bool vblank_rise; @@ -188,6 +190,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { InputCallbacks.Call(); controller_state = _controllerDeck.ReadPort1(controller); + + Acc_X_state = _controllerDeck.ReadAccX1(controller); + Acc_Y_state = _controllerDeck.ReadAccY1(controller); } public int Frame => _frame; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index f1d1cf846c..5de963bc1f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -58,20 +58,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public class GBSyncSettings { - private string _port1 = GBHawkControllerDeck.DefaultControllerName; + [JsonIgnore] + public string Port1 = GBHawkControllerDeck.DefaultControllerName; + + public enum ControllerType + { + Default, + Kirby + } [JsonIgnore] - public string Port1 + private ControllerType _GBController; + + [DisplayName("Controller")] + [Description("Select Controller Type")] + [DefaultValue(ControllerType.Default)] + public ControllerType GBController { - get { return _port1; } + get { return _GBController; } set { - if (!GBHawkControllerDeck.ValidControllerTypes.ContainsKey(value)) - { - throw new InvalidOperationException("Invalid controller type: " + value); - } + if (value == ControllerType.Default) { Port1 = GBHawkControllerDeck.DefaultControllerName; } + else { Port1 = "Gameboy Controller + Kirby"; } - _port1 = value; + _GBController = value; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index 749164d070..ac8bfc95b8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -62,6 +62,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _controllerDeck.SyncState(ser); ser.Sync("controller_state", ref controller_state); + ser.Sync("Acc_X_state", ref Acc_X_state); + ser.Sync("Acc_Y_state", ref Acc_Y_state); ser.Sync("in_vblank", ref in_vblank); ser.Sync("in_vblank_old", ref in_vblank_old); ser.Sync("vblank_rise", ref vblank_rise); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 6becfb67af..8ebc54ba0f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -415,10 +415,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cart_RAM = new byte[0x200]; } + // mbc7 has 256 bytes of RAM, regardless of any header info + if (mppr == "MBC7") + { + cart_RAM = new byte[0x100]; + has_bat = true; + } + mapper.Core = this; mapper.Initialize(); - if (cart_RAM != null) + if (cart_RAM != null && (mppr != "MBC7")) { Console.Write("RAM: "); Console.WriteLine(cart_RAM.Length); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs index 684b022ab8..5f21be47e0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs @@ -36,6 +36,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return Port1.Read(c); } + public ushort ReadAccX1(IController c) + { + return Port1.ReadAccX(c); + } + + public ushort ReadAccY1(IController c) + { + return Port1.ReadAccY(c); + } + public ControllerDefinition Definition { get; } public void SyncState(Serializer ser) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs index cd7000330c..2a224ba58e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs @@ -119,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk .Select(b => "P" + PortNum + " " + b) .ToList(), FloatControls = { "P" + PortNum + " Tilt X", "P" + PortNum + " Tilt Y" }, - FloatRanges = { new[] { -127.0f, 0, 127.0f }, new[] { -127.0f, 0, 127.0f } } + FloatRanges = { new[] { -45.0f, 0, 45.0f }, new[] { -45.0f, 0, 45.0f } } }; } @@ -167,14 +167,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return result; } + // acc x is the result of rotating around body y AFTER rotating around body x + // therefore this control scheme gives decreasing sensitivity in X as Y rotation inscreases public ushort ReadAccX(IController c) { - return 0; + double theta = c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0; + double phi = c.GetFloat(Definition.FloatControls[0]) * Math.PI / 180.0; + + float temp = (float)(Math.Cos(theta) * Math.Sin(phi)); + + return (ushort)(0x81D0 - Math.Floor(temp * 125)); } + // acc y is just the sine of the angle public ushort ReadAccY(IController c) { - return 0; + float temp = (float)Math.Sin(c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0); + return (ushort)(0x81D0 - Math.Floor(temp * 125)); } private static readonly string[] BaseDefinition = diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index 84389700b9..3875ec6482 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -361,7 +361,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // RAM Bank in GBC mode case 0xFF70: //Console.WriteLine(value); - if (is_GBC && !ppu.HDMA_active) + if (is_GBC) { RAM_Bank = value & 7; if (RAM_Bank == 0) { RAM_Bank = 1; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs index 28114486dc..db108521ee 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -17,6 +17,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte acc_y_high; public bool is_erased; + // EEPROM related + public bool CS_prev; + public bool CLK_prev; + public bool DI_prev; + public bool DO; + public bool instr_read; + public bool perf_instr; + public int instr_bit_counter; + public int instr; + public bool WR_EN; + public int EE_addr; + public int instr_case; + public int instr_clocks; + public int EE_value; + public int countdown; + public bool countdown_start; + + public override void Initialize() { ROM_bank = 1; @@ -80,6 +98,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { value &= 0xFF; + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + //Console.WriteLine(value); + ROM_bank &= 0x100; ROM_bank |= value; ROM_bank &= ROM_mask; @@ -117,32 +138,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("acc_y_low", ref acc_y_low); ser.Sync("acc_y_high", ref acc_y_high); ser.Sync("is_erased", ref is_erased); - } - public void Register_Access_Write(ushort addr, byte value) - { - if ((addr & 0xA0F0) == 0xA000) - { - if (value == 0x55) - { - is_erased = true; - acc_x_low = 0; - acc_x_high = 0x80; - acc_y_low = 0; - acc_y_high = 0x80; - } - } - else if ((addr & 0xA0F0) == 0xA010) - { - if ((value == 0xAA) && is_erased) - { - // latch new accelerometer values - } - } - else if ((addr & 0xA0F0) == 0xA080) - { - - } + ser.Sync("CS_prev", ref CS_prev); + ser.Sync("CLK_prev", ref CLK_prev); + ser.Sync("DI_prev", ref DI_prev); + ser.Sync("DO", ref DO); + ser.Sync("instr_read", ref instr_read); + ser.Sync("perf_instr", ref perf_instr); + ser.Sync("instr_bit_counter", ref instr_bit_counter); + ser.Sync("instr", ref instr); + ser.Sync("WR_EN", ref WR_EN); + ser.Sync("EE_addr", ref EE_addr); + ser.Sync("instr_case", ref instr_case); + ser.Sync("instr_clocks", ref instr_clocks); + ser.Sync("EE_value", ref EE_value); + ser.Sync("countdown", ref countdown); + ser.Sync("countdown_start", ref countdown_start); } public byte Register_Access_Read(ushort addr) @@ -181,7 +192,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if ((addr & 0xA0F0) == 0xA080) { - return 0xFF; + return (byte)((CS_prev ? 0x80 : 0) | + (CLK_prev ? 0x40 : 0) | + (DI_prev ? 2 : 0) | + (DO ? 1 : 0)); } else { @@ -189,6 +203,240 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } + public void Register_Access_Write(ushort addr, byte value) + { + if ((addr & 0xA0F0) == 0xA000) + { + if (value == 0x55) + { + is_erased = true; + acc_x_low = 0; + acc_x_high = 0x80; + acc_y_low = 0; + acc_y_high = 0x80; + } + } + else if ((addr & 0xA0F0) == 0xA010) + { + if ((value == 0xAA) && is_erased) + { + // latch new accelerometer values + acc_x_low = (byte)(Core.Acc_X_state & 0xFF); + acc_x_high = (byte)((Core.Acc_X_state & 0xFF00) >> 8); + acc_y_low = (byte)(Core.Acc_Y_state & 0xFF); + acc_y_high = (byte)((Core.Acc_Y_state & 0xFF00) >> 8); + } + } + else if ((addr & 0xA0F0) == 0xA080) + { + // EEPROM writes + EEPROM_write(value); + } + } + private void EEPROM_write(byte value) + { + bool CS = value.Bit(7); + bool CLK = value.Bit(6); + bool DI = value.Bit(1); + + // if we deselect the chip, complete instructions or countdown and stop + if (!CS) + { + CS_prev = CS; + CLK_prev = CLK; + DI_prev = DI; + + DO = true; + countdown_start = false; + } + + if (!instr_read && !perf_instr) + { + // if we aren't performing an operation or reading an incoming instruction, we are waiting for one + // this is signalled by CS and DI both being 1 while CLK goes from 0 to 1 + if (CLK && !CLK_prev && DI && CS) + { + instr_read = true; + instr_bit_counter = 0; + instr = 0; + DO = false; + Console.Write("Initiating command: "); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + else if (instr_read && CLK && !CLK_prev) + { + // all instructions are 10 bits long + instr = (instr << 1) | ((value & 2) >> 1); + + instr_bit_counter++; + if (instr_bit_counter == 10) + { + instr_read = false; + perf_instr = true; + instr_clocks = 0; + EE_addr = instr & 0x7F; + EE_value = 0; + + + + switch (instr & 0x300) + { + case 0x0: + switch (instr & 0xC0) + { + case 0x0: // disable writes + instr_case = 0; + break; + case 0x40: // fill mem with value + instr_case = 1; + break; + case 0x80: // fill mem with FF + instr_case = 2; + break; + case 0xC0: // enable writes + instr_case = 3; + break; + } + break; + case 0x100: // write to address + instr_case = 4; + break; + case 0x200: // read from address + instr_case = 5; + break; + case 0x300: // set address to FF + instr_case = 6; + break; + + } + + Console.Write("Selected Command: "); + Console.Write(instr_case); + Console.Write(" "); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + else if (perf_instr && CLK && !CLK_prev) + { + switch (instr_case) + { + case 0: + WR_EN = false; + instr_case = 7; + countdown = 8; + break; + case 1: + if (instr_clocks < 16) + { + EE_value = (EE_value << 1) | ((value & 2) >> 1); + } + else + { + if (WR_EN) + { + for (int i = 0; i < 128; i++) + { + Core.cart_RAM[i * 2] = (byte)(EE_value & 0xFF); + Core.cart_RAM[i * 2 + 1] = (byte)((EE_value & 0xFF00) >> 8); + } + } + instr_case = 7; + countdown = 8; + } + break; + case 2: + if (WR_EN) + { + for (int i = 0; i < 256; i++) + { + Core.cart_RAM[i] = 0xFF; + } + } + instr_case = 7; + countdown = 8; + break; + case 3: + WR_EN = true; + instr_case = 7; + countdown = 8; + break; + case 4: + if (instr_clocks < 16) + { + EE_value = (EE_value << 1) | ((value & 2) >> 1); + } + else + { + if (WR_EN) + { + Core.cart_RAM[EE_addr * 2] = (byte)(EE_value & 0xFF); + Core.cart_RAM[EE_addr * 2 + 1] = (byte)((EE_value & 0xFF00) >> 8); + } + instr_case = 7; + countdown = 8; + } + break; + case 5: + if (instr_clocks < 16) + { + if ((instr_clocks >= 0) && (instr_clocks <= 7)) + { + DO = (Core.cart_RAM[EE_addr * 2 + 1] >> (8 - instr_clocks)) == 1 ? true : false; + } + else if ((instr_clocks >= 8) && (instr_clocks <= 15)) + { + DO = (Core.cart_RAM[EE_addr * 2] >> (8 - instr_clocks)) == 1 ? true : false; + } + } + else + { + instr_case = 7; + countdown = 8; + } + + break; + case 6: + if (WR_EN) + { + Core.cart_RAM[EE_addr * 2] = 0xFF; + Core.cart_RAM[EE_addr * 2 + 1] = 0xFF; + } + instr_case = 7; + countdown = 8; + break; + case 7: + // completed operations take time, so countdown a bit here. + // not cycle accurate for operations like writing to all of the EEPROM, but good enough + + break; + } + + if (instr_case == 7) + { + perf_instr = false; + countdown_start = true; + } + + instr_clocks++; + } + else if (countdown_start) + { + countdown--; + if (countdown == 0) + { + countdown_start = false; + DO = true; + + Console.Write("Command Complete: "); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + + CS_prev = CS; + CLK_prev = CLK; + DI_prev = DI; + } } } From cdc1f0c21fc3a702dab26ead3c8a0bf1085d226a Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 7 Apr 2018 16:47:43 -0400 Subject: [PATCH 159/339] GBHawk: MBC7: playable, needs testing --- .../Nintendo/GBHawk/Mappers/Mapper_MBC7.cs | 127 +++++++++--------- 1 file changed, 65 insertions(+), 62 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs index db108521ee..db01d14f6d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -209,10 +209,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (value == 0x55) { + //Console.WriteLine("Erasing ACC"); + is_erased = true; - acc_x_low = 0; + acc_x_low = 0x00; acc_x_high = 0x80; - acc_y_low = 0; + acc_y_low = 0x00; acc_y_high = 0x80; } } @@ -221,6 +223,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if ((value == 0xAA) && is_erased) { // latch new accelerometer values + //Console.WriteLine("Latching ACC"); acc_x_low = (byte)(Core.Acc_X_state & 0xFF); acc_x_high = (byte)((Core.Acc_X_state & 0xFF00) >> 8); acc_y_low = (byte)(Core.Acc_Y_state & 0xFF); @@ -249,6 +252,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk DO = true; countdown_start = false; + perf_instr = false; + instr_read = false; + + //Console.Write("Chip De-selected: "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); } if (!instr_read && !perf_instr) @@ -261,8 +269,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk instr_bit_counter = 0; instr = 0; DO = false; - Console.Write("Initiating command: "); - Console.WriteLine(Core.cpu.TotalExecutedCycles); + //Console.Write("Initiating command: "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); } } else if (instr_read && CLK && !CLK_prev) @@ -274,13 +282,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (instr_bit_counter == 10) { instr_read = false; - perf_instr = true; instr_clocks = 0; EE_addr = instr & 0x7F; EE_value = 0; - - switch (instr & 0x300) { case 0x0: @@ -288,51 +293,70 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { case 0x0: // disable writes instr_case = 0; + WR_EN = false; + DO = true; break; case 0x40: // fill mem with value instr_case = 1; + perf_instr = true; break; case 0x80: // fill mem with FF instr_case = 2; + if (WR_EN) + { + for (int i = 0; i < 256; i++) + { + Core.cart_RAM[i] = 0xFF; + } + } + DO = true; break; case 0xC0: // enable writes instr_case = 3; + WR_EN = true; + DO = true; break; } break; case 0x100: // write to address instr_case = 4; + perf_instr = true; break; case 0x200: // read from address instr_case = 5; + perf_instr = true; break; case 0x300: // set address to FF instr_case = 6; + if (WR_EN) + { + Core.cart_RAM[EE_addr * 2] = 0xFF; + Core.cart_RAM[EE_addr * 2 + 1] = 0xFF; + } + DO = true; break; - } - Console.Write("Selected Command: "); - Console.Write(instr_case); - Console.Write(" "); - Console.WriteLine(Core.cpu.TotalExecutedCycles); + //Console.Write("Selected Command: "); + //Console.Write(instr_case); + //Console.Write(" "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); } } else if (perf_instr && CLK && !CLK_prev) { + //Console.Write("Command In progress, Cycle: "); + //Console.Write(instr_clocks); + //Console.Write(" "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + + // for commands that require additional clocking switch (instr_case) { - case 0: - WR_EN = false; - instr_case = 7; - countdown = 8; - break; case 1: - if (instr_clocks < 16) - { - EE_value = (EE_value << 1) | ((value & 2) >> 1); - } - else + EE_value = (EE_value << 1) | ((value & 2) >> 1); + + if (instr_clocks == 15) { if (WR_EN) { @@ -346,28 +370,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk countdown = 8; } break; - case 2: - if (WR_EN) - { - for (int i = 0; i < 256; i++) - { - Core.cart_RAM[i] = 0xFF; - } - } - instr_case = 7; - countdown = 8; - break; - case 3: - WR_EN = true; - instr_case = 7; - countdown = 8; - break; + case 4: - if (instr_clocks < 16) - { - EE_value = (EE_value << 1) | ((value & 2) >> 1); - } - else + EE_value = (EE_value << 1) | ((value & 2) >> 1); + + if (instr_clocks == 15) { if (WR_EN) { @@ -378,34 +385,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk countdown = 8; } break; + case 5: - if (instr_clocks < 16) + if ((instr_clocks >= 0) && (instr_clocks <= 7)) { - if ((instr_clocks >= 0) && (instr_clocks <= 7)) - { - DO = (Core.cart_RAM[EE_addr * 2 + 1] >> (8 - instr_clocks)) == 1 ? true : false; - } - else if ((instr_clocks >= 8) && (instr_clocks <= 15)) - { - DO = (Core.cart_RAM[EE_addr * 2] >> (8 - instr_clocks)) == 1 ? true : false; - } + DO = ((Core.cart_RAM[EE_addr * 2 + 1] >> (7 - instr_clocks)) & 1) == 1 ? true : false; } - else + else if ((instr_clocks >= 8) && (instr_clocks <= 15)) + { + DO = ((Core.cart_RAM[EE_addr * 2] >> (15 - instr_clocks)) & 1) == 1 ? true : false; + } + + if (instr_clocks == 15) { instr_case = 7; countdown = 8; - } - + } break; + case 6: - if (WR_EN) - { - Core.cart_RAM[EE_addr * 2] = 0xFF; - Core.cart_RAM[EE_addr * 2 + 1] = 0xFF; - } + instr_case = 7; countdown = 8; break; + case 7: // completed operations take time, so countdown a bit here. // not cycle accurate for operations like writing to all of the EEPROM, but good enough @@ -429,8 +432,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk countdown_start = false; DO = true; - Console.Write("Command Complete: "); - Console.WriteLine(Core.cpu.TotalExecutedCycles); + //Console.Write("Command Complete: "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); } } From a7197b348eaad4d3a74a482a5604d39b2e4b2977 Mon Sep 17 00:00:00 2001 From: zeromus Date: Sun, 8 Apr 2018 01:48:47 -0500 Subject: [PATCH 160/339] fix bug where autodump (and maybe lua-instructed client exits) would vanish the window but linger the process --- BizHawk.Client.EmuHawk/MainForm.Events.cs | 6 +++--- BizHawk.Client.EmuHawk/MainForm.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 810dfb3567..ba02157085 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -695,12 +695,12 @@ namespace BizHawk.Client.EmuHawk public void CloseEmulator() { - _exit = true; + _exitRequestPending = true; } public void CloseEmulator(int exitCode) { - _exit = true; + _exitRequestPending = true; _exitCode = exitCode; } @@ -2947,7 +2947,7 @@ namespace BizHawk.Client.EmuHawk protected override void OnClosed(EventArgs e) { - _exit = true; + _windowClosedAndSafeToExitProcess = true; base.OnClosed(e); } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 3f7a9ea1df..79ff357164 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -493,7 +493,7 @@ namespace BizHawk.Client.EmuHawk Close(); } - if (_exit) + if (_windowClosedAndSafeToExitProcess) { break; } @@ -1370,7 +1370,7 @@ namespace BizHawk.Client.EmuHawk private int _avwriterResizeh; private bool _avwriterpad; - private bool _exit; + private bool _windowClosedAndSafeToExitProcess; private int _exitCode; private bool _exitRequestPending; private bool _runloopFrameProgress; @@ -3427,7 +3427,7 @@ namespace BizHawk.Client.EmuHawk StopAv(); if (argParse._autoCloseOnDump) { - _exit = true; + _exitRequestPending = true; } } } From 9f7ce12f443139b71b2fe8fbf277abc7b7dc3faa Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 9 Apr 2018 09:07:08 -0400 Subject: [PATCH 161/339] GBHawk: GBC sprite priority fixes --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 14c436a3ac..700a11e0f9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -1289,19 +1289,37 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { sprite_ordered_index = 0; - for (int i = 0; i < 256; i++) + // In CGB mode, sprites are ordered solely based on their position in OAM, so they are already ordered + + if (Core.GBC_compat) { for (int j = 0; j < SL_sprites_index; j++) { - if (SL_sprites[j * 4 + 1] == i) + sl_use_index = j; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; + sprite_ordered_index++; + } + } + else + { + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < SL_sprites_index; j++) { - sl_use_index = j; - process_sprite(); - SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; - SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; - sprite_ordered_index++; + if (SL_sprites[j * 4 + 1] == i) + { + sl_use_index = j; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; + sprite_ordered_index++; + } } } } From 0fc046bea930c90e3a5e8b0cafbe8589e67cad39 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 9 Apr 2018 10:35:23 -0400 Subject: [PATCH 162/339] GBHawk: MBC7: Fix Command Master --- .../Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs index db01d14f6d..41b9dd1506 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -66,7 +66,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if (addr < 0xB000) { - if (RAM_enable_2) + if (RAM_enable_1 && RAM_enable_2) { return Register_Access_Read(addr); } @@ -107,15 +107,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else if (addr < 0x6000) { - if (RAM_enable_1) - { - RAM_enable_2 = (value & 0xF0) == 0x40; - } + RAM_enable_2 = (value & 0xF0) == 0x40; } } else { - if (RAM_enable_2) + if (RAM_enable_1 && RAM_enable_2) { Register_Access_Write(addr, value); } From f2529fa0e0ac82e0d7e599b07fac8d1958bf1d6e Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 9 Apr 2018 12:48:47 -0400 Subject: [PATCH 163/339] NESHawk: Fix mapper 219 --- Assets/gamedb/gamedb_nes.txt | 1 + .../NES/Boards/MMC3_family/Mapper219.cs | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/Assets/gamedb/gamedb_nes.txt b/Assets/gamedb/gamedb_nes.txt index 256a27a55b..5ccc1128a5 100644 --- a/Assets/gamedb/gamedb_nes.txt +++ b/Assets/gamedb/gamedb_nes.txt @@ -276,6 +276,7 @@ sha1:4EBC1ED9665C36913D0F05129E6A54787BAD3165 Dragon Ball 3 - Gokuu Den (Japan) sha1:5A6DFDD8A2D62EBE313A6FDB986C3585077BB348 Final Combat (Asia) (NTSC) (Unl) NES board=MAPPER139 sha1:DFAF6D81280ADBEB2ADF3DAB38E536B0F2FDFC76 Final Combat (Asia) (PAL) (Unl) NES board=MAPPER139;system=NES-PAL sha1:433CEC30E71DCA31E32B8A44A0D534DBFE7039CA BoogerMan II (RexSoft) [!] NES board=UNIF_UNL-KOF97 +sha1:1903F3C614E54C0AA4EDE740909D7B3C791FA514 Toy Story NES board=MAPPER219 ;;;;;;;;;;;;;;;;;;;----------------------------------------------------------------------- diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs index 477ffd3ef9..53c335f07f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs @@ -1,4 +1,6 @@ -namespace BizHawk.Emulation.Cores.Nintendo.NES +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.NES { public sealed class Mapper219 : MMC3Board_Base { @@ -15,14 +17,15 @@ case "MAPPER219": break; default: - return false; + return false; } BaseSetup(); - prgregs[1] = 1; - prgregs[2] = 2; - prgregs[3] = 3; + prgregs[0] = 0xFC; + prgregs[1] = 0xFD; + prgregs[2] = 0xFE; + prgregs[3] = 0xFF; byte r0_0 = (byte)(0 & ~1); byte r0_1 = (byte)(0 | 1); @@ -108,16 +111,21 @@ public override byte ReadPRG(int addr) { int bank_prg = addr >> 13; - bank_prg = prgregs[bank_prg]; + bank_prg = prgregs[bank_prg] & prg_mask; return ROM[((bank_prg << 13) + (addr & 0x1FFF))]; } public override byte ReadPPU(int addr) { + if (addr<0x2000) { int bank_chr = addr >> 10; - bank_chr = chrregs[bank_chr]; + bank_chr = chrregs[bank_chr] & chr_mask; + if (VROM != null) + { + return VROM[((bank_chr << 10) + (addr & 0x3FF))]; + } return VRAM[((bank_chr << 10) + (addr & 0x3FF))]; } else From 0e4887b2f7b74bb1104c6bb2d2aead93c6ffbf3b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 10 Apr 2018 09:19:29 -0400 Subject: [PATCH 164/339] GBHawk - Reorganize Sachen Mappers - Add Sachen MMC1 support (Captain Knick Knack) --- .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 2 +- .../Nintendo/GBHawk/GBHawk.IStatable.cs | 3 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 12 +- .../Nintendo/GBHawk/Mappers/MapperBase.cs | 2 +- .../Nintendo/GBHawk/Mappers/Mapper_MBC3.cs | 2 +- .../GBHawk/Mappers/Mapper_Sachen_MMC1.cs | 101 ++++++++-------- .../GBHawk/Mappers/Mapper_Sachen_MMC2.cs | 110 +++++++++++------- .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 4 +- 8 files changed, 131 insertions(+), 105 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index b5e3dc33e9..1628fb2944 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -117,7 +117,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // These things do not change speed in GBC double spped mode audio.tick(); ppu.tick(); - if (Use_RTC) { mapper.RTC_Tick(); } + if (Use_MT) { mapper.Mapper_Tick(); } if (!HDMA_transfer) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index ac8bfc95b8..9268eaee52 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -94,7 +94,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("speed_switch", ref speed_switch); ser.Sync("HDMA_transfer", ref HDMA_transfer); - ser.Sync("Use_RTC", ref Use_RTC); + ser.Sync("Use_MT", ref Use_MT); + ser.Sync("addr_access", ref addr_access); // probably a better way to do this if (cart_RAM != null) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 8ebc54ba0f..3a0e26ba7c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -68,7 +68,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk private int _frame = 0; - public bool Use_RTC; + public bool Use_MT; + public ushort addr_access; public MapperBase mapper; @@ -373,6 +374,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Console.WriteLine("Using RockMan 8 (Unlicensed) Mapper"); mapper = new MapperRM8(); } + if ((_rom.HashMD5(0, _rom.Length) == "D3C1924D847BC5D125BF54C2076BE27A")) + { + Console.WriteLine("Using Sachen 1 (Unlicensed) Mapper"); + mapper = new MapperSachen1(); + mppr = "Schn1"; + } Console.Write("Mapper: "); Console.WriteLine(mppr); @@ -407,6 +414,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if ((mppr == "Schn1") || (mppr == "Schn2")) { cart_RAM = null; + Use_MT = true; } // mbc2 carts have built in RAM @@ -438,7 +446,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // Extra RTC initialization for mbc3 if (mppr == "MBC3") { - Use_RTC = true; + Use_MT = true; int days = (int)Math.Floor(_syncSettings.RTCInitialTime / 86400.0); int days_upper = ((days & 0x100) >> 8) | ((days & 0x200) >> 2); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs index ce4ed125bb..89b8ce1034 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { } - public virtual void RTC_Tick() + public virtual void Mapper_Tick() { } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs index f1ed2a807d..e0649675ab 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs @@ -158,7 +158,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk RTC_regs[index] = value; } - public override void RTC_Tick() + public override void Mapper_Tick() { if (!halt) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs index b178fe1037..c96600a6fd 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs @@ -17,6 +17,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int ROM_bank_mask; public int BASE_ROM_Bank; public bool reg_access; + public ushort addr_last; + public int counter; public override void Initialize() { @@ -26,37 +28,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ROM_bank_mask = 0xFF; locked = true; reg_access = false; + addr_last = 0; + counter = 0; } public override byte ReadMemory(ushort addr) { if (addr < 0x4000) { - ushort t_addr = addr; - - // header is scrambled - if ((addr >= 0x100) && (addr < 0x200)) + if (locked) { - int temp0 = (addr & 1); - int temp1 = (addr & 2); - int temp4 = (addr & 0x10); - int temp6 = (addr & 0x40); + // header is scrambled + if ((addr >= 0x100) && (addr < 0x200)) + { + int temp0 = (addr & 1); + int temp1 = (addr & 2); + int temp4 = (addr & 0x10); + int temp6 = (addr & 0x40); - temp0 = temp0 << 6; - temp1 = temp1 << 3; - temp4 = temp4 >> 3; - temp6 = temp6 >> 6; + temp0 = temp0 << 6; + temp1 = temp1 << 3; + temp4 = temp4 >> 3; + temp6 = temp6 >> 6; - addr &= 0x1AC; - addr |= (ushort)(temp0 | temp1 | temp4 | temp6); - } - - if (locked) { addr |= 0x80; } - - if (t_addr == 0x133) - { - locked = false; - Console.WriteLine("cleared"); + addr &= 0x1AC; + addr |= (ushort)(temp0 | temp1 | temp4 | temp6); + } + addr |= 0x80; } return Core._rom[addr + BASE_ROM_Bank * 0x4000]; @@ -73,39 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override byte PeekMemory(ushort addr) { - if (addr < 0x4000) - { - ushort t_addr = addr; - - // header is scrambled - if ((addr >= 0x100) && (addr < 0x200)) - { - int temp0 = (addr & 1); - int temp1 = (addr & 2); - int temp4 = (addr & 0x10); - int temp6 = (addr & 0x40); - - temp0 = temp0 << 6; - temp1 = temp1 << 3; - temp4 = temp4 >> 3; - temp6 = temp6 >> 6; - - addr &= 0x1AC; - addr |= (ushort)(temp0 | temp1 | temp4 | temp6); - } - - if (locked) { addr |= 0x80; } - - return Core._rom[addr + BASE_ROM_Bank * 0x4000]; - } - else if (addr < 0x8000) - { - return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; - } - else - { - return 0xFF; - } + return ReadMemory(addr); } public override void WriteMemory(ushort addr, byte value) @@ -144,6 +110,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WriteMemory(addr, value); } + public override void Mapper_Tick() + { + if (locked) + { + if (((Core.addr_access & 0x8000) == 0) && ((addr_last & 0x8000) > 0) && (Core.addr_access >= 0x100)) + { + counter++; + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + + if (Core.addr_access >= 0x100) + { + addr_last = Core.addr_access; + } + + if (counter == 0x30) + { + locked = false; + Console.WriteLine("Unlocked"); + } + } + } + public override void SyncState(Serializer ser) { ser.Sync("ROM_Bank", ref ROM_bank); @@ -152,6 +141,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("ROM_bank_mask", ref ROM_bank_mask); ser.Sync("BASE_ROM_Bank", ref BASE_ROM_Bank); ser.Sync("reg_access", ref reg_access); + ser.Sync("addr_last", ref addr_last); + ser.Sync("counter", ref counter); } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs index d43e912cf8..29f16a8212 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs @@ -12,11 +12,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk class MapperSachen2 : MapperBase { public int ROM_bank; - public bool locked; + public bool locked, locked_GBC, finished; public int ROM_mask; public int ROM_bank_mask; public int BASE_ROM_Bank; public bool reg_access; + public ushort addr_last; + public int counter; public override void Initialize() { @@ -24,16 +26,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ROM_mask = Core._rom.Length / 0x4000 - 1; BASE_ROM_Bank = 0; ROM_bank_mask = 0; - locked = false; + locked = true; + locked_GBC = false; + finished = false; reg_access = false; + addr_last = 0; + counter = 0; } public override byte ReadMemory(ushort addr) { if (addr < 0x4000) { - ushort t_addr = addr; - // header is scrambled if ((addr >= 0x100) && (addr < 0x200)) { @@ -51,12 +55,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk addr |= (ushort)(temp0 | temp1 | temp4 | temp6); } - if (locked) { addr |= 0x80; } - - if (t_addr == 0x133) - { - if ((Core.GB_bios_register & 0x1) == 0) { locked ^= true; } - } + if (locked_GBC) { addr |= 0x80; } return Core._rom[addr + BASE_ROM_Bank * 0x4000]; } @@ -75,39 +74,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override byte PeekMemory(ushort addr) { - if (addr < 0x4000) - { - ushort t_addr = addr; - - // header is scrambled - if ((addr >= 0x100) && (addr < 0x200)) - { - int temp0 = (addr & 1); - int temp1 = (addr & 2); - int temp4 = (addr & 0x10); - int temp6 = (addr & 0x40); - - temp0 = temp0 << 6; - temp1 = temp1 << 3; - temp4 = temp4 >> 3; - temp6 = temp6 >> 6; - - addr &= 0x1AC; - addr |= (ushort)(temp0 | temp1 | temp4 | temp6); - } - - if (locked) { addr |= 0x80; } - - return Core._rom[addr + BASE_ROM_Bank * 0x4000]; - } - else if (addr < 0x8000) - { - return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; - } - else - { - return 0xFF; - } + return ReadMemory(addr); } public override void WriteMemory(ushort addr, byte value) @@ -146,14 +113,71 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WriteMemory(addr, value); } + public override void Mapper_Tick() + { + if (locked) + { + if (((Core.addr_access & 0x8000) == 0) && ((addr_last & 0x8000) > 0) && (Core.addr_access >= 0x100)) + { + counter++; + } + + if (Core.addr_access >= 0x100) + { + addr_last = Core.addr_access; + } + + if (counter == 0x30) + { + locked = false; + locked_GBC = true; + counter = 0; + } + } + else if (locked_GBC) + { + if (((Core.addr_access & 0x8000) == 0) && ((addr_last & 0x8000) > 0) && (Core.addr_access >= 0x100)) + { + counter++; + } + + if (Core.addr_access >= 0x100) + { + addr_last = Core.addr_access; + } + + if (counter == 0x30) + { + locked_GBC = false; + finished = true; + Console.WriteLine("Finished"); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + + // The above condition seems to never be reached as described in the mapper notes + // so for now add this one + + if ((Core.addr_access == 0x133) && (counter == 1)) + { + locked_GBC = false; + finished = true; + Console.WriteLine("Unlocked"); + } + } + } + public override void SyncState(Serializer ser) { ser.Sync("ROM_Bank", ref ROM_bank); ser.Sync("ROM_Mask", ref ROM_mask); ser.Sync("locked", ref locked); + ser.Sync("locked_GBC", ref locked_GBC); + ser.Sync("finished", ref finished); ser.Sync("ROM_bank_mask", ref ROM_bank_mask); ser.Sync("BASE_ROM_Bank", ref BASE_ROM_Bank); ser.Sync("reg_access", ref reg_access); + ser.Sync("addr_last", ref addr_last); + ser.Sync("counter", ref counter); } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index 616b1c2413..26a3e6e20d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -30,6 +30,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte ReadMemory(ushort addr) { MemoryCallbacks.CallReads(addr, "System Bus"); + addr_access = addr; if (ppu.DMA_start) { @@ -150,7 +151,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void WriteMemory(ushort addr, byte value) { MemoryCallbacks.CallWrites(addr, "System Bus"); - + addr_access = addr; + if (ppu.DMA_start) { // some of gekkio's tests require this to be accessible during DMA From 8e007490b5ba4239ff6266daf90fe01323537a54 Mon Sep 17 00:00:00 2001 From: nattthebear Date: Thu, 12 Apr 2018 17:38:59 -0400 Subject: [PATCH 165/339] use 0xff as the gba default saveram filler, just like mgba standalone. Fixes #1144 --- output/dll/mgba.dll | Bin 837120 -> 837120 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/output/dll/mgba.dll b/output/dll/mgba.dll index ab39aaeb38c60f18d35353de6a4b25812efab274..7592cbb0476e48365a414522c0205cc4b1c7f501 100644 GIT binary patch delta 139515 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 delta 139568 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 Date: Fri, 13 Apr 2018 16:21:53 -0400 Subject: [PATCH 166/339] PCE: Don't always make a new frame buffer when settings change Fixes #1175 --- BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs index e8fda4374b..7574ce7f93 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs @@ -1,6 +1,7 @@ using BizHawk.Common; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components.H6280; +using System; namespace BizHawk.Emulation.Cores.PCEngine { @@ -161,7 +162,6 @@ namespace BizHawk.Emulation.Cores.PCEngine FramePitch = MultiResHack == 0 ? FrameWidth : MultiResHack; //if (FrameBuffer.Length != FramePitch * FrameHeight) //FrameBuffer = new int[FramePitch * FrameHeight]; - FrameBuffer = new int[320 * 262]; break; case VDW: // Vertical Display Word? - update framebuffer size //FrameHeight = RequestedFrameHeight; @@ -172,7 +172,6 @@ namespace BizHawk.Emulation.Cores.PCEngine FramePitch = MultiResHack; //if (FrameBuffer.Length != FramePitch * FrameHeight) //FrameBuffer = new int[FramePitch * FrameHeight]; - FrameBuffer = new int[320 * 262]; break; case LENR: // Initiate DMA transfer if (!msbComplete) break; From 71499e531086979fa95df178f6295be9cac6dd2b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 13 Apr 2018 18:09:36 -0400 Subject: [PATCH 167/339] NESHawk: Mapper 219 savestates --- .../Nintendo/NES/Boards/MMC3_family/Mapper219.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs index 53c335f07f..13d30b938d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs @@ -1,4 +1,5 @@ using System; +using BizHawk.Common; namespace BizHawk.Emulation.Cores.Nintendo.NES { @@ -143,5 +144,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES else base.WritePPU(addr, value); } + + public override void SyncState(Serializer ser) + { + ser.Sync("exregs", ref exregs, false); + ser.Sync("prgregs", ref prgregs, false); + ser.Sync("chrregs", ref chrregs, false); + ser.Sync("bits_rev", ref bits_rev); + ser.Sync("reg_value", ref reg_value); + + base.SyncState(ser); + } } } From 9c06ac02a21b1aeb74f74ad7a5b5ccc02a7427b8 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 18 Apr 2018 19:00:59 -0400 Subject: [PATCH 168/339] Z80: Fix Flags for IN operations --- BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs index adf7452c29..c26a7a6aff 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs @@ -39,6 +39,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { Regs[dest] = ReadHardware((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); Regs[DB] = Regs[dest]; + + FlagZ = Regs[dest] == 0; + FlagP = TableParity[Regs[dest]]; + FlagH = false; + FlagN = false; + FlagS = Regs[dest].Bit(7); + Flag5 = Regs[dest].Bit(5); + Flag3 = Regs[dest].Bit(3); } public void TR_Func(ushort dest, ushort src) From 0d3f8f16ed3ae258653baa4e24e9919eb8400075 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 25 Apr 2018 20:38:36 -0400 Subject: [PATCH 169/339] GBCHawk: Fix some palette bugs --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 44 +++++++++---------- .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 4 +- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 700a11e0f9..d3f179e2f4 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte OBJ_pal_ret { - get { return (byte)(((OBJ_bytes_inc ? 1 : 0) << 7) | (BG_bytes_index & 0x1F)); } + get { return (byte)(((OBJ_bytes_inc ? 1 : 0) << 7) | (OBJ_bytes_index & 0x3F)); } } public byte HDMA_ctrl @@ -61,29 +61,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk switch (addr) { - case 0xFF40: ret = LCDC; break; // LCDC - case 0xFF41: ret = STAT; break; // STAT - case 0xFF42: ret = scroll_y; break; // SCY - case 0xFF43: ret = scroll_x; break; // SCX - case 0xFF44: ret = LY; break; // LY - case 0xFF45: ret = LYC; break; // LYC - case 0xFF46: ret = DMA_addr; break; // DMA - case 0xFF47: ret = BGP; break; // BGP - case 0xFF48: ret = obj_pal_0; break; // OBP0 - case 0xFF49: ret = obj_pal_1; break; // OBP1 - case 0xFF4A: ret = window_y; break; // WY - case 0xFF4B: ret = window_x; break; // WX + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: ret = DMA_addr; break; // DMA + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX // These are GBC specific Regs - case 0xFF51: ret = HDMA_src_hi; break; // HDMA1 - case 0xFF52: ret = HDMA_src_lo; break; // HDMA2 - case 0xFF53: ret = HDMA_dest_hi; break; // HDMA3 - case 0xFF54: ret = HDMA_dest_lo; break; // HDMA4 - case 0xFF55: ret = HDMA_ctrl; break; // HDMA5 - case 0xFF68: ret = BG_pal_ret; break; // BGPI - case 0xFF69: ret = BG_transfer_byte; break; // BGPD - case 0xFF6A: ret = OBJ_pal_ret; break; // OBPI - case 0xFF6B: ret = OBJ_transfer_byte; break; // OBPD + case 0xFF51: ret = HDMA_src_hi; break; // HDMA1 + case 0xFF52: ret = HDMA_src_lo; break; // HDMA2 + case 0xFF53: ret = HDMA_dest_hi; break; // HDMA3 + case 0xFF54: ret = HDMA_dest_lo; break; // HDMA4 + case 0xFF55: ret = HDMA_ctrl; break; // HDMA5 + case 0xFF68: ret = BG_pal_ret; break; // BGPI + case 0xFF69: ret = BG_bytes[BG_bytes_index]; break; // BGPD + case 0xFF6A: ret = OBJ_pal_ret; break; // OBPI + case 0xFF6B: ret = OBJ_bytes[OBJ_bytes_index]; break; // OBPD } return ret; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index 3875ec6482..b4bdde86d6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -311,8 +311,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // GBC compatibility register (I think) case 0xFF4C: - if (value != 0xC0) + if ((value != 0xC0) && (value != 0x80)) { + Console.Write("GBC Compatibility? "); + Console.WriteLine(value); GBC_compat = false; } break; From 7aaa5e8a52bb2250c5a54f7456568f6a77ce30b6 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 26 Apr 2018 12:54:10 +0100 Subject: [PATCH 170/339] ZXHawk: +3 disk ready for testing --- BizHawk.Client.EmuHawk/MainForm.cs | 2 +- BizHawk.Emulation.Common/Database/Database.cs | 8 + .../BizHawk.Emulation.Cores.csproj | 25 +- .../Hardware/Abstraction/IFDDHost.cs | 35 + .../Hardware/Datacorder/DatacorderDevice.cs | 8 +- .../SinclairSpectrum/Hardware/Disk/CHRN.cs | 185 + .../Hardware/Disk/NECUPD765.Definitions.cs | 830 ++++ .../Hardware/Disk/NECUPD765.FDC.cs | 3742 +++++++++++++++++ .../Hardware/Disk/NECUPD765.FDD.cs | 909 ++++ .../Hardware/Disk/NECUPD765.IPortIODevice.cs | 92 + .../Hardware/Disk/NECUPD765.Registers.cs | 554 +++ .../Hardware/Disk/NECUPD765.Timing.cs | 151 + .../Hardware/Disk/NECUPD765.cs | 251 ++ .../Hardware/Disk/NECUPS765.Static.cs | 112 + .../Machine/SpectrumBase.Media.cs | 32 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 12 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 3 - .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 9 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 3 + .../Media/Disk/CPCExtendedFloppyDisk.cs | 167 + .../Media/Disk/CPCFloppyDisk.cs | 169 + .../SinclairSpectrum/Media/Disk/DiskImage.cs | 145 + .../SinclairSpectrum/Media/Disk/DiskType.cs | 24 + .../Media/Disk/DskConverter.cs | 511 +++ .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 396 ++ .../SinclairSpectrum/Media/Disk/Sector.cs | 228 + .../SinclairSpectrum/Media/Disk/Track.cs | 95 + .../SinclairSpectrum/Media/MediaConverter.cs | 124 + ...alizationType.cs => MediaConverterType.cs} | 5 +- .../SinclairSpectrum/Media/MediaSerializer.cs | 2 + .../{TapSerializer.cs => TapConverter.cs} | 18 +- .../{TzxSerializer.cs => TzxConverter.cs} | 18 +- .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 139 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 39 +- 34 files changed, 8967 insertions(+), 76 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/{MediaSerializationType.cs => MediaConverterType.cs} (84%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/{TapSerializer.cs => TapConverter.cs} (94%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/{TzxSerializer.cs => TzxConverter.cs} (99%) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 79ff357164..25a6409e01 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -2109,7 +2109,7 @@ namespace BizHawk.Client.EmuHawk "Apple II", "*.dsk;*.do;*.po;%ARCH%", "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", - "Sinclair ZX Spectrum", "*.tzx;*.tap;%ARCH%", + "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;%ARCH%", "All Files", "*.*"); } diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index d106a5e91a..8f0af8194d 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -340,6 +340,14 @@ namespace BizHawk.Emulation.Common break; case ".DSK": + byte[] head2 = romData.Take(20).ToArray(); + if (System.Text.Encoding.Default.GetString(head2).ToUpper().Contains("EXTENDED CPC DSK") || + System.Text.Encoding.Default.GetString(head2).ToUpper().Contains("MV - CPCEMU")) + game.System = "ZXSpectrum"; + else + game.System = "AppleII"; + break; + case ".PO": case ".DO": game.System = "AppleII"; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index ddf0709624..68b09cf8d3 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -258,9 +258,20 @@ + + + + + + + + + + + @@ -280,12 +291,20 @@ - + + + + + + + + + - - + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs new file mode 100644 index 0000000000..fd2a3016db --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Defines an object that can load a floppy disk image + /// + public interface IFDDHost + { + /// + /// The currently inserted diskimage + /// + FloppyDisk Disk { get; set; } + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + /// + void FDD_LoadDisk(byte[] diskData); + + /// + /// Ejects the current disk + /// + void FDD_EjectDisk(); + + /// + /// Signs whether the current active drive has a disk inserted + /// + bool FDD_IsDiskLoaded { get; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 59ff27d0f8..8fcc6f11ee 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -324,13 +324,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void LoadTape(byte[] tapeData) { // check TZX first - TzxSerializer tzxSer = new TzxSerializer(this); + TzxConverter tzxSer = new TzxConverter(this); if (tzxSer.CheckType(tapeData)) { // this file has a tzx header - attempt serialization try { - tzxSer.DeSerialize(tapeData); + tzxSer.Read(tapeData); return; } catch (Exception ex) @@ -343,10 +343,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else { - TapSerializer tapSer = new TapSerializer(this); + TapConverter tapSer = new TapConverter(this); try { - tapSer.DeSerialize(tapeData); + tapSer.Read(tapeData); return; } catch (Exception ex) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs new file mode 100644 index 0000000000..b5bba9600d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs @@ -0,0 +1,185 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Used for the sector CHRN structure + /// + public class CHRN + { + /// + /// Track + /// + public byte C { get; set; } + + /// + /// Side + /// + public byte H { get; set; } + + /// + /// Sector ID + /// + public byte R { get; set; } + + /// + /// Sector Size + /// + public byte N { get; set; } + + /// + /// Status register 1 + /// + private byte _flag1; + public byte Flag1 + { + get { return _flag1; } + set { _flag1 = value; } + } + + /// + /// Status register 2 + /// + private byte _flag2; + public byte Flag2 + { + get { return _flag2; } + set { _flag2 = value; } + } + + /// + /// Used to store the last transmitted/received data bytes + /// + public byte[] DataBytes { get; set; } + + /// + /// ID for the read/write data command + /// + public int DataID { get; set; } + + #region Helper Methods + + /// + /// Missing Address Mark (Sector_ID or DAM not found) + /// + public bool ST1MA + { + get { return NECUPD765.GetBit(0, _flag1); } + set + { + if (value) { NECUPD765.SetBit(0, ref _flag1); } + else { NECUPD765.UnSetBit(0, ref _flag1); } + } + } + + /// + /// No Data (Sector_ID not found, CRC fail in ID_field) + /// + public bool ST1ND + { + get { return NECUPD765.GetBit(2, _flag1); } + set + { + if (value) { NECUPD765.SetBit(2, ref _flag1); } + else { NECUPD765.UnSetBit(2, ref _flag1); } + } + } + + /// + /// Data Error (CRC-fail in ID- or Data-Field) + /// + public bool ST1DE + { + get { return NECUPD765.GetBit(5, _flag1); } + set + { + if (value) { NECUPD765.SetBit(5, ref _flag1); } + else { NECUPD765.UnSetBit(5, ref _flag1); } + } + } + + /// + /// End of Track (set past most read/write commands) (see IC) + /// + public bool ST1EN + { + get { return NECUPD765.GetBit(7, _flag1); } + set + { + if (value) { NECUPD765.SetBit(7, ref _flag1); } + else { NECUPD765.UnSetBit(7, ref _flag1); } + } + } + + /// + /// Missing Address Mark in Data Field (DAM not found) + /// + public bool ST2MD + { + get { return NECUPD765.GetBit(0, _flag2); } + set + { + if (value) { NECUPD765.SetBit(0, ref _flag2); } + else { NECUPD765.UnSetBit(0, ref _flag2); } + } + } + + /// + /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) + /// + public bool ST2BC + { + get { return NECUPD765.GetBit(1, _flag2); } + set + { + if (value) { NECUPD765.SetBit(1, ref _flag2); } + else { NECUPD765.UnSetBit(1, ref _flag2); } + } + } + + /// + /// Wrong Cylinder (read/programmed track-ID different) (see b1) + /// + public bool ST2WC + { + get { return NECUPD765.GetBit(4, _flag2); } + set + { + if (value) { NECUPD765.SetBit(4, ref _flag2); } + else { NECUPD765.UnSetBit(4, ref _flag2); } + } + } + + /// + /// Data Error in Data Field (CRC-fail in data-field) + /// + public bool ST2DD + { + get { return NECUPD765.GetBit(5, _flag2); } + set + { + if (value) { NECUPD765.SetBit(5, ref _flag2); } + else { NECUPD765.UnSetBit(5, ref _flag2); } + } + } + + /// + /// Control Mark (read/scan command found sector with deleted DAM) + /// + public bool ST2CM + { + get { return NECUPD765.GetBit(6, _flag2); } + set + { + if (value) { NECUPD765.SetBit(6, ref _flag2); } + else { NECUPD765.UnSetBit(6, ref _flag2); } + } + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs new file mode 100644 index 0000000000..f1e89de394 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs @@ -0,0 +1,830 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Definitions + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + #region Enums + + /// + /// Defines the current phase of the controller + /// + private enum Phase + { + /// + /// FDC is in an idle state, awaiting the next initial command byte + /// + Idle, + + /// + /// FDC is in a state waiting for the next command instruction + /// A command consists of a command byte (eventually including the MF, MK, SK bits), and up to eight parameter bytes + /// + Command, + + /// + /// During this phase, the actual data is transferred (if any). Usually that are the data bytes for the read/written sector(s), except for the Format Track Command, + /// in that case four bytes for each sector are transferred + /// + Execution, + + /// + /// Returns up to seven result bytes (depending on the command) that are containing status information. The Recalibrate and Seek Track commands do not return result bytes directly, + /// instead the program must wait until the Main Status Register signalizes that the command has been completed, and then it must (!) send a + /// Sense Interrupt State command to 'terminate' the Seek/Recalibrate command. + /// + Result + } + + /// + /// The lifecycle of an instruction + /// Similar to phase, this describes the current 'sub-phase' we are in when dealing with an instruction + /// + private enum InstructionState + { + /// + /// FDC has received a command byte and is currently reading parameter bytes from the data bus + /// + ReceivingParameters, + + /// + /// All parameter bytes have been received. This phase allows any neccessary setup before instruction execution starts + /// + PreExecution, + + /// + /// The start of instruction execution. This may end up with the FDC moving into result phase, + /// but also may also prepare the way for further processing to occur later in execution phase + /// + StartExecute, + + /// + /// Data is read or written in execution phase + /// + ExecutionReadWrite, + + /// + /// Execution phase is well under way. This state primarily deals with data transfer between CPU and FDC + /// + ExecutionWrite, + + /// + /// Execution phase is well under way. This state primarily deals with data transfer between FDC and CPU + /// + ExecutionRead, + + /// + /// Execution has finished and results bytes are ready to be read by the CPU + /// Initial result setup + /// + StartResult, + + /// + /// Result processing + /// + ProcessResult, + + /// + /// Results are being sent + /// + SendingResults, + + /// + /// Final cleanup tasks when the instruction has fully completed + /// + Completed + + } + + /// + /// Represents internal interrupt state of the FDC + /// + public enum InterruptState + { + /// + /// There is no interrupt + /// + None, + /// + /// Execution interrupt + /// + Execution, + /// + /// Result interrupt + /// + Result, + /// + /// Ready interrupt + /// + Ready, + /// + /// Seek interrupt + /// + Seek + } + + /// + /// Possible main states that each drive can be in + /// + public enum DriveMainState + { + /// + /// Drive is not doing anything + /// + None, + /// + /// Seek operation is in progress + /// + Seek, + /// + /// Recalibrate operation is in progress + /// + Recalibrate, + /// + /// A scan data operation is in progress + /// + Scan, + /// + /// A read ID operation is in progress + /// + ReadID, + /// + /// A read data operation is in progress + /// + ReadData, + /// + /// A read diagnostic (read track) operation is in progress + /// + ReadDiagnostic, + /// + /// A write id (format track) operation is in progress + /// + WriteID, + /// + /// A write data operation is in progress + /// + WriteData, + } + + /// + /// State information during a seek/recalibration operation + /// + public enum SeekSubState + { + /// + /// Seek hasnt started yet + /// + Idle, + /// + /// Delayed + /// + Wait, + /// + /// Setup for head move + /// + MoveInit, + /// + /// Seek is currently happening + /// + HeadMove, + /// + /// Head move with no delay + /// + MoveImmediate, + /// + /// Ready to complete + /// + PerformCompletion, + /// + /// Seek operation has completed + /// + SeekCompleted + } + + /// + /// Seek int code + /// + public enum SeekIntStatus + { + Normal, + Abnormal, + DriveNotReady, + } + + /// + /// The direction of a specific command + /// + private enum CommandDirection + { + /// + /// Data flows from UPD765A to Z80 + /// + OUT, + /// + /// Data flows from Z80 to UPD765A + /// + IN + } + + /// + /// Enum defining the different types of result that can be returned + /// + private enum ResultType + { + /// + /// Standard 7 result bytes are returned + /// + Standard, + /// + /// 1 byte returned - ST3 + /// (used for SenseDriveStatus) + /// + ST3, + /// + /// 1 byte returned - ST0 + /// (used for version & invalid) + /// + ST0, + /// + /// 2 bytes returned for sense interrupt status command + /// ST0 + /// CurrentCylinder + /// + Interrupt + } + + /// + /// Possible list of encountered drive status errors + /// + public enum Status + { + /// + /// No error detected + /// + None, + /// + /// An undefined error has been detected + /// + Undefined, + /// + /// Drive is not ready + /// + DriveNotReady, + /// + /// Invalid command received + /// + Invalid, + /// + /// The disk has its write protection tab enabled + /// + WriteProtected, + /// + /// The requested sector has not been found + /// + SectorNotFound + } + + /// + /// Represents the direction that the head is moving over the cylinders + /// Increment: Track number increasing (head moving from outside of disk inwards) + /// Decrement: Track number decreasing (head moving from inside of disk outwards) + /// + public enum SkipDirection + { + Increment, + Decrement + } + + #endregion + + #region Constants + + // Command Instruction Constants + // Designates the default postitions within the cmdbuffer array + + public const int CM_HEAD = 0; + /// + /// C - Track + /// + public const int CM_C = 1; + /// + /// H - Side + /// + public const int CM_H = 2; + /// + /// R - Sector ID + /// + public const int CM_R = 3; + /// + /// N - Sector size + /// + public const int CM_N = 4; + /// + /// EOT - End of track + /// + public const int CM_EOT = 5; + /// + /// GPL - Gap length + /// + public const int CM_GPL = 6; + /// + /// DTL - Data length + /// + public const int CM_DTL = 7; + /// + /// STP - Step + /// + public const int CM_STP = 7; + + // Result Instruction Constants + // Designates the default postitions within the cmdbuffer array + + /// + /// Status register 0 + /// + public const int RS_ST0 = 0; + /// + /// Status register 1 + /// + public const int RS_ST1 = 1; + /// + /// Status register 2 + /// + public const int RS_ST2 = 2; + /// + /// C - Track + /// + public const int RS_C = 3; + /// + /// H - Side + /// + public const int RS_H = 4; + /// + /// R - Sector ID + /// + public const int RS_R = 5; + /// + /// N - Sector size + /// + public const int RS_N = 6; + + // Main Status Register Constants + // Designates the bit positions within the Main status register + + /// + /// FDD0 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 0 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D0B = 0; + /// + /// FDD1 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 1 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D1B = 1; + /// + /// FDD2 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 2 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D2B = 2; + /// + /// FDD3 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 3 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D3B = 3; + /// + /// FDC Busy (still in command-, execution- or result-phase) + /// A Read or Write command is in orocess. (FDC Busy) FDC will not accept any other command + /// + public const int MSR_CB = 4; + /// + /// Execution Mode (still in execution-phase, non_DMA_only) + /// This bit is set only during execution ohase (Execution Mode) in non-DMA mode When DB5 goes low, execution phase has ended and result phase has started.It operates only during + /// non-DMA mode of operation + /// + public const int MSR_EXM = 5; + /// + /// Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) + /// Indicates direction of data transfer between FDC and data regrster If DIO = 1, then transfer is from data register to the + /// processor.If DIO = 0, then transfer is from the processor to data register + /// + public const int MSR_DIO = 6; + /// + /// Request For Master (1=ready for next byte) (see b6 for direction) + /// ndicates data register IS ready to send or receive data to or from the processor Both bits DIO and RQM should be + /// used to perform the hand-shaking functions of “ready” and “directron” to the processor + /// + public const int MSR_RQM = 7; + + // Status Register 0 Constants + // Designates the bit positions within the status register 0 + + /// + /// Unit Select (driveno during interrupt) + /// This flag IS used to indicate a drive unit number at interrupt + /// + public const int SR0_US0 = 0; + + /// + /// Unit Select (driveno during interrupt) + /// This flag IS used to indicate a drive unit number at interrupt + /// + public const int SR0_US1 = 1; + + /// + /// Head Address (head during interrupt) + /// State of the head at interrupt + /// + public const int SR0_HD = 2; + + /// + /// Not Ready (drive not ready or non-existing 2nd head selected) + /// Not Ready - When the FDD IS in the not-ready state and a Read or Write command IS Issued, this + /// flag IS set If a Read or Write command is issued to side 1 of a single-sided drive, then this flag IS set + /// + public const int SR0_NR = 3; + + /// + /// Equipment Check (drive failure or recalibrate failed (retry)) + /// Equipment check - If a fault srgnal IS received from the FDD, or if the track 0 srgnal fails to occur after 77 + /// step pulses(Recalibrate Command) then this flag is set + /// + public const int SR0_EC = 4; + + /// + /// Seek End (Set if seek-command completed) + /// Seek end - When the FDC completes the Seek command, this flag IS set lo 1 (high) + /// + public const int SR0_SE = 5; + + /// + /// Interrupt Code (low byte) + /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + /// or senseint with no int occured, 3=aborted:disc removed etc.) + /// + public const int SR0_IC0 = 6; + + /// + /// Interrupt Code (high byte) + /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + /// or senseint with no int occured, 3=aborted:disc removed etc.) + /// + public const int SR0_IC1 = 7; + + // Status Register 1 Constants + // Designates the bit positions within the status register 1 + + /// + /// Missing Address Mark (Sector_ID or DAM not found) + /// Missing address mark - This bit is set i f the FDC does not detect the IDAM before 2 index pulses It is also set if + /// the FDC cannot find the DAM or DDAM after the IDAM i s found.MD bit of ST2 is also set at this time + /// + public const int SR1_MA = 0; + + /// + /// Not Writeable (tried to write/format disc with wprot_tab=on) + /// Not writable (write protect) - During execution of Write Data, Write Deleted Data or Write ID command. if the FDC + /// detect: a write protect srgnal from the FDD.then this flag is Set + /// + public const int SR1_NW = 1; + + /// + /// No Data + /// No Data (Sector_ID not found, CRC fail in ID_field) + /// + /// During execution of Read Data. Read Deleted Data Write Data.Write Deleted Data or Scan command, if the FDC cannot find + /// the sector specified in the IDR(2)Register, this flag i s set. + /// + /// During execution of the Read ID command. if the FDC cannot read the ID field without an error, then this flag IS set + /// + /// During execution of the Read Diagnostic command. if the starting sector cannot be found, then this flag is set + /// + public const int SR1_ND = 2; + + /// + /// Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) + /// Overrun - If the FDC i s not serviced by the host system during data transfers within a certain time interval.this flaa i s set + /// + public const int SR1_OR = 4; + + /// + /// Data Error (CRC-fail in ID- or Data-Field) + /// Data error - When the FDC detects a CRC(1) error in either the ID field or the data field, this flag is set + /// + public const int SR1_DE = 5; + + /// + /// End of Track (set past most read/write commands) (see IC) + /// End of cylinder - When the FDC tries to access a sector beyond the final sector of a cylinder, this flag I S set + /// + public const int SR1_EN = 7; + + // Status Register 2 Constants + // Designates the bit positions within the status register 2 + + /// + /// Missing Address Mark in Data Field (DAM not found) + /// Missing address mark - When data IS read from the medium, i f the FDC cannot find a data address mark or deleted + /// data address mark, then this flag is set + /// + public const int SR2_MD = 0; + + /// + /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) + /// Bad cylinder - This bit i s related to the ND bit. and when the contents of C on the medium is different + /// from that stored i n the IDR and the contents of C IS FFH.then this flag IS set + /// + public const int SR2_BC = 1; + + /// + /// Scan Not Satisfied (no fitting sector found) + /// Scan not satisfied - During execution of the Scan command, i f the F D cannot find a sector on the cylinder + /// which meets the condition.then this flag i s set + /// + public const int SR2_SN = 2; + + /// + /// Scan Equal Hit (equal) + /// Scan equal hit - During execution of the Scan command. i f the condition of “equal” is satisfied, this flag i s set + /// + public const int SR2_SH = 3; + + /// + /// Wrong Cylinder (read/programmed track-ID different) (see b1) + /// Wrong cylinder - This bit IS related to the ND bit, and when the contents of C(3) on the medium is different + /// from that stored i n the IDR.this flag is set + /// + public const int SR2_WC = 4; + + /// + /// Data Error in Data Field (CRC-fail in data-field) + /// Data error in data field - If the FDC detects a CRC error i n the data field then this flag is set + /// + public const int SR2_DD = 5; + + /// + /// Control Mark (read/scan command found sector with deleted DAM) + /// Control mark - During execution of the Read Data or Scan command, if the FDC encounters a sector + /// which contains a deleted data address mark, this flag is set Also set if DAM is + /// found during Read Deleted Data + /// + public const int SR2_CM = 6; + + // Status Register 3 Constants + // Designates the bit positions within the status register 3 + + /// + /// Unit select 0 + /// Unit Select (pin 28,29 of FDC) + /// + public const int SR3_US0 = 0; + + /// + /// Unit select 1 + /// Unit Select (pin 28,29 of FDC) + /// + public const int SR3_US1 = 1; + + /// + /// Head address (side select) + /// Head Address (pin 27 of FDC) + /// + public const int SR3_HD = 2; + + /// + /// Two Side (0=yes, 1=no (!)) + /// Two-side - This bit IS used to indicate the status of the two-side signal from the FDD + /// + public const int SR3_TS = 3; + + /// + /// Track 0 (on track 0 we are) + /// Track 0 - This bit IS used to indicate the status of the track 0 signal from the FDD + /// + public const int SR3_T0 = 4; + + /// + /// Ready - status of the ready signal from the fdd + /// Ready (drive ready signal) + /// + public const int SR3_RY = 5; + + /// + /// Write Protected (write protected) + /// Write protect - status of the wp signal from the fdd + /// + public const int SR3_WP = 6; + + /// + /// Fault - This bit is used to indicate the status of the fault signal from the FDD + /// Fault (if supported: 1=Drive failure) + /// + public const int SR3_FT = 7; + + // Interrupt Code Masks + + /// + /// 1 = aborted:readfail / OK if EN (end of track) + /// + public const byte IC_OK = 0x00; + + /// + /// 1 = aborted:readfail / OK if EN (end of track) + /// + public const byte IC_ABORTED_RF_OKEN = 0x40; + + /// + /// 2 = unknown cmd or senseint with no int occured + /// + public const byte IC_NO_INT_OCCURED = 0x80; + + /// + /// 3 = aborted:disc removed etc + /// + public const byte IC_ABORTED_DISCREMOVED = 0xC0; + + // command code constants + public const int CC_READ_DATA = 0x06; + public const int CC_READ_ID = 0x0a; + public const int CC_SPECIFY = 0x03; + public const int CC_READ_DIAGNOSTIC = 0x02; + public const int CC_SCAN_EQUAL = 0x11; + public const int CC_SCAN_HIGHOREQUAL = 0x1d; + public const int CC_SCAN_LOWOREQUAL = 0x19; + public const int CC_READ_DELETEDDATA = 0x0c; + public const int CC_WRITE_DATA = 0x05; + public const int CC_WRITE_ID = 0x0d; + public const int CC_WRITE_DELETEDDATA = 0x09; + public const int CC_SEEK = 0x0f; + public const int CC_RECALIBRATE = 0x07; + public const int CC_SENSE_INTSTATUS = 0x08; + public const int CC_SENSE_DRIVESTATUS = 0x04; + public const int CC_VERSION = 0x10; + public const int CC_INVALID = 0x00; + + // drive seek state constants + public const int SEEK_IDLE = 0; + public const int SEEK_SEEK = 1; + public const int SEEK_RECALIBRATE = 2; + // seek interrupt + public const int SEEK_INTACKNOWLEDGED = 3; + public const int SEEK_NORMALTERM = 4; + public const int SEEK_ABNORMALTERM = 5; + public const int SEEK_DRIVENOTREADY = 6; + + #endregion + + #region Classes & Structs + + /// + /// Class that holds information about a specific command + /// + private class Command + { + /// + /// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command + /// + //public int BitMask { get; set; } + /// + /// The command code after bitmask has been applied + /// + public int CommandCode { get; set; } + /// + /// The number of bytes that make up the full command + /// + public int ParameterByteCount { get; set; } + /// + /// The number of result bytes that will be generated from the command + /// + public int ResultByteCount { get; set; } + /// + /// The command direction + /// IN - Z80 to UPD765A + /// OUT - UPD765A to Z80 + /// + public CommandDirection Direction { get; set; } + /// + /// Command makes use of the MT bit + /// + public bool MT; + /// + /// Command makes use of the MF bit + /// + public bool MF; + /// + /// Command makes use of the SK bit + /// + public bool SK; + /// + /// Read/Write command that is READ + /// + public bool IsRead; + /// + /// Read/Write command that is WRITE + /// + public bool IsWrite; + + /// + /// Delegate function that is called by this command + /// bool 1: EXECUTE - if TRUE the command will be executed. if FALSE the method will instead parse commmand parameter bytes + /// bool 2: RESULT - if TRUE + /// + public Action CommandDelegate { get; set; } + } + + /// + /// Storage for command parameters + /// + public class CommandParameters + { + /// + /// The requested drive + /// + public byte UnitSelect; + /// + /// The requested physical side + /// + public byte Side; + /// + /// The requested track (C) + /// + public byte Cylinder; + /// + /// The requested head (H) + /// + public byte Head; + /// + /// The requested sector (R) + /// + public byte Sector; + /// + /// The specified sector size (N) + /// + public byte SectorSize; + /// + /// The end of track or last sector value (EOT) + /// + public byte EOT; + /// + /// Gap3 length (GPL) + /// + public byte Gap3Length; + /// + /// Data length (DTL) - When N is defined as 00, DTL stands for the data length + /// which users are going to read out or write into the sector + /// + public byte DTL; + + /// + /// Clear down + /// + public void Reset() + { + UnitSelect = 0; + Side = 0; + Cylinder = 0; + Head = 0; + Sector = 0; + SectorSize = 0; + EOT = 0; + Gap3Length = 0; + DTL = 0; + } + + public void SyncState(Serializer ser) + { + ser.BeginSection("ActiveCmdParams"); + + ser.Sync("UnitSelect", ref UnitSelect); + ser.Sync("Side", ref Side); + ser.Sync("Cylinder", ref Cylinder); + ser.Sync("Head", ref Head); + ser.Sync("Sector", ref Sector); + ser.Sync("SectorSize", ref SectorSize); + ser.Sync("EOT", ref EOT); + ser.Sync("Gap3Length", ref Gap3Length); + ser.Sync("DTL", ref DTL); + + ser.EndSection(); + } + } + + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs new file mode 100644 index 0000000000..2ee2b3467e --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -0,0 +1,3742 @@ +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// FDC State and Methods + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + #region Controller State + + /// + /// Signs whether the drive is active + /// + public bool DriveLight; + + /// + /// Collection of possible commands + /// + private List CommandList; + + /// + /// State parameters relating to the Active command + /// + public CommandParameters ActiveCommandParams = new CommandParameters(); + + /// + /// The current active phase of the controller + /// + private Phase ActivePhase = Phase.Command; + + /// + /// The currently active interrupt + /// + private InterruptState ActiveInterrupt = InterruptState.None; + + /// + /// Stores the current data flow direction + /// + //private CommandDirection ActiveDirection = CommandDirection.IN; + /* + /// + /// Current raised status/error message + /// + private Status ActiveStatus + { + get { return _activeStatus; } + set + { + if (value == Status.None) + { + // clear the active status flag + _statusRaised = false; + } + else + _statusRaised = true; + _activeStatus = value; + } + } + private Status _activeStatus; + + /// + /// Signs whether there is an active status code raised + /// + private bool StatusRaised + { + get { return _statusRaised; } + set + { + if (value == false) + { + // return to none status + _activeStatus = Status.None; + } + + // dont set true here + } + } + private bool _statusRaised; + */ + + + /// + /// Command buffer + /// This does not contain the initial command byte (only parameter bytes) + /// + private byte[] CommBuffer = new byte[9]; + + /// + /// Current index within the command buffer + /// + private int CommCounter = 0; + + /// + /// Initial command byte flag + /// Bit7 Multi Track (continue multi-sector-function on other head) + /// + private bool CMD_FLAG_MT; + + /// + /// Initial command byte flag + /// Bit6 MFM-Mode-Bit (Default 1=Double Density) + /// + private bool CMD_FLAG_MF; + + /// + /// Initial command byte flag + /// Bit5 Skip-Bit (set if secs with deleted DAM shall be skipped) + /// + private bool CMD_FLAG_SK; + + /// + /// Step Rate Time (supplied via the specify command) + /// SRT stands for the steooino rate for the FDD ( 1 to 16 ms in 1 ms increments). + /// Stepping rate applies to all drives(FH= 1ms, EH= 2ms, etc.). + /// + private int SRT; + + /// + /// Keeps track of the current SRT state + /// + private int SRT_Counter; + + /// + /// Head Unload Time (supplied via the specify command) + /// HUT stands for the head unload time after a Read or Write operation has occurred + /// (16 to 240 ms in 16 ms Increments) + /// + private int HUT; + + /// + /// Keeps track of the current HUT state + /// + private int HUT_Counter; + + /// + /// Head load Time (supplied via the specify command) + /// HLT stands for the head load time in the FDD (2 to 254 ms in 2 ms Increments) + /// + private int HLT; + + /// + /// Keeps track of the current HLT state + /// + private int HLT_Counter; + + /// + /// Non-DMA Mode (supplied via the specify command) + /// ND stands for operation in the non-DMA mode + /// + private bool ND; + + /// + /// Signs that the the controller is ready + /// + //public bool FDC_FLAG_RQM; + + /// + /// When TRUE, a SCAN command is currently active + /// + //private bool FDC_FLAG_SCANNING; + + /// + /// Set when a seek operation has completed on drive 0 + /// + //private bool FDC_FLAG_SEEKCOMPLETED_0; + + /// + /// Set when a seek operation is active on drive 0 + /// + //private bool FDC_FLAG_SEEKACTIVE_0; + + /// + /// Contains result bytes in result phase + /// + private byte[] ResBuffer = new byte[7]; + + /// + /// Contains sector data to be written/read in execution phase + /// + private byte[] ExecBuffer = new byte[0x8000]; + + /// + /// Interrupt result buffer + /// Persists (and returns when needed) the last result data when a sense interrupt status command happens + /// + private byte[] InterruptResultBuffer = new byte[2]; + + /// + /// Current index within the result buffer + /// + private int ResCounter = 0; + + /// + /// The byte length of the currently active command + /// This may or may not be the same as the actual command resultbytes value + /// + private int ResLength = 0; + + /// + /// Index for sector data within the result buffer + /// + private int ExecCounter = 0; + + /// + /// The length of the current exec command + /// + private int ExecLength = 0; + + /// + /// The last write byte that was received during execution phase + /// + private byte LastSectorDataWriteByte = 0; + + /// + /// The last read byte to be sent during execution phase + /// + private byte LastSectorDataReadByte = 0; + + /// + /// The last parameter byte that was written to the FDC + /// + private byte LastByteReceived = 0; + + /// + /// Delay for reading sector + /// + private int SectorDelayCounter = 0; + + /// + /// The phyical sector ID + /// + private int SectorID = 0; + + /// + /// Counter for index pulses + /// + private int IndexPulseCounter; + + /// + /// Specifies the index of the currently selected command (in the CommandList) + /// + public int CMDIndex + { + get { return _cmdIndex; } + set + { + _cmdIndex = value; + ActiveCommand = CommandList[_cmdIndex]; + + // clear command params + //ActiveCommandParams.Reset(); + } + } + private int _cmdIndex; + + /// + /// The currently active command + /// + private Command ActiveCommand; + + /// + /// Main status register (accessed via reads to port 0x2ffd) + /// + /* + b0..3 DB FDD0..3 Busy (seek/recalib active, until succesful sense intstat) + b4 CB FDC Busy (still in command-, execution- or result-phase) + b5 EXM Execution Mode (still in execution-phase, non_DMA_only) + b6 DIO Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) + b7 RQM Request For Master (1=ready for next byte) (see b6 for direction) + */ + private byte StatusMain; + + /// + /// Status Register 0 + /// + /* + b0,1 US Unit Select (driveno during interrupt) + b2 HD Head Address (head during interrupt) + b3 NR Not Ready (drive not ready or non-existing 2nd head selected) + b4 EC Equipment Check (drive failure or recalibrate failed (retry)) + b5 SE Seek End (Set if seek-command completed) + b6,7 IC Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + or senseint with no int occured, 3=aborted:disc removed etc.) + */ + private byte Status0; + + /// + /// Status Register 1 + /// + /* + b0 MA Missing Address Mark (Sector_ID or DAM not found) + b1 NW Not Writeable (tried to write/format disc with wprot_tab=on) + b2 ND No Data (Sector_ID not found, CRC fail in ID_field) + b3,6 0 Not used + b4 OR Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) + b5 DE Data Error (CRC-fail in ID- or Data-Field) + b7 EN End of Track (set past most read/write commands) (see IC) + */ + private byte Status1; + + /// + /// Status Register 2 + /// + /* + b0 MD Missing Address Mark in Data Field (DAM not found) + b1 BC Bad Cylinder (read/programmed track-ID different and read-ID = FF) + b2 SN Scan Not Satisfied (no fitting sector found) + b3 SH Scan Equal Hit (equal) + b4 WC Wrong Cylinder (read/programmed track-ID different) (see b1) + b5 DD Data Error in Data Field (CRC-fail in data-field) + b6 CM Control Mark (read/scan command found sector with deleted DAM) + b7 0 Not Used + */ + private byte Status2; + + /// + /// Status Register 3 + /// + /* + b0,1 US Unit Select (pin 28,29 of FDC) + b2 HD Head Address (pin 27 of FDC) + b3 TS Two Side (0=yes, 1=no (!)) + b4 T0 Track 0 (on track 0 we are) + b5 RY Ready (drive ready signal) + b6 WP Write Protected (write protected) + b7 FT Fault (if supported: 1=Drive failure) + */ + private byte Status3; + + + #endregion + + #region UPD Internal Functions + + #region READ Commands + + /// + /// Read Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ReadData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + int buffPos = 0; + int sectorSize = 0; + int maxTransferCap = 0; + + // calculate requested size of data required + if (ActiveCommandParams.SectorSize == 0) + { + // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual + // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) + // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform + // a Multi-Sector Read Operation. + sectorSize = ActiveCommandParams.DTL; + + // calculate maximum transfer capacity + if (!CMD_FLAG_MF) + maxTransferCap = 3328; + } + else + { + // When N is non - zero, then DTL has no meaning and should be set to ffh + ActiveCommandParams.DTL = 0xFF; + + // calculate maximum transfer capacity + switch (ActiveCommandParams.SectorSize) + { + case 1: + if (CMD_FLAG_MF) + maxTransferCap = 6656; + else + maxTransferCap = 3840; + break; + case 2: + if (CMD_FLAG_MF) + maxTransferCap = 7680; + else + maxTransferCap = 4096; + break; + case 3: + if (CMD_FLAG_MF) + maxTransferCap = 8192; + else + maxTransferCap = 4096; + break; + } + + sectorSize = 0x80 << ActiveCommandParams.SectorSize; + } + + // get the current track + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track == null || track.NumberOfSectors <= 0) + { + // track could not be found + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + FloppyDisk.Sector sector = null; + + // sector read loop + for (;;) + { + bool terminate = false; + + // lookup the sector + sector = GetSector(); + + if (sector == null) + { + // sector was not found after two passes of the disk index hole + SetBit(SR1_ND, ref Status1); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + + // sector ID was found on this track + + // get status regs from sector + Status1 = sector.Status1; + Status2 = sector.Status2; + + // we dont need EN + UnSetBit(SR1_EN, ref Status1); + + // If SK=1, the FDC skips the sector with the Deleted Data Address Mark and reads the next sector. + // The CRC bits in the deleted data field are not checked when SK=1 + if (CMD_FLAG_SK && Status2.Bit(SR2_CM)) + { + if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) + { + // increment the sector ID and search again + ActiveCommandParams.Sector++; + continue; + } + else + { + // no execution phase + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + } + + // read the sector + for (int i = 0; i < sectorSize; i++) + { + ExecBuffer[buffPos++] = sector.ActualData[i]; + } + + // any CRC errors? + if (Status1.Bit(SR1_DE) || Status2.Bit(SR2_DD)) + { + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + if (!CMD_FLAG_SK && Status2.Bit(SR2_CM)) + { + // deleted address mark was detected with NO skip flag set + ActiveCommandParams.EOT = ActiveCommandParams.Sector; + SetBit(SR2_CM, ref Status2); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + if (sector.SectorID == ActiveCommandParams.EOT || terminate) + { + // this was the last sector to read + // or termination requested + + //SetBit(SR1_EN, ref Status1); + + int keyIndex = 0; + for (int i = 0; i < track.Sectors.Length; i++) + { + if (track.Sectors[i].SectorID == sector.SectorID) + { + keyIndex = i; + break; + } + } + + if (keyIndex == track.Sectors.Length - 1) + { + // last sector on the cylinder, set EN + SetBit(SR1_EN, ref Status1); + + // increment cylinder + ActiveCommandParams.Cylinder++; + + // reset sector + ActiveCommandParams.Sector = 1; + ActiveDrive.SectorIndex = 0; + } + else + { + ActiveDrive.SectorIndex++; + } + + UnSetBit(SR0_IC1, ref Status0); + if (terminate) + SetBit(SR0_IC0, ref Status0); + else + UnSetBit(SR0_IC0, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Execution; + break; + } + else + { + // continue with multi-sector read operation + ActiveCommandParams.Sector++; + //ActiveDrive.SectorIndex++; + } + } + + if (ActivePhase == Phase.Execution) + { + ExecLength = buffPos; + ExecCounter = buffPos; + + DriveLight = true; + } + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + + var index = ExecLength - ExecCounter; + + LastSectorDataReadByte = ExecBuffer[index]; + + ExecCounter--; + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Read Deleted Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ReadDeletedData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + int buffPos = 0; + int sectorSize = 0; + int maxTransferCap = 0; + + // calculate requested size of data required + if (ActiveCommandParams.SectorSize == 0) + { + // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual + // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) + // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform + // a Multi-Sector Read Operation. + sectorSize = ActiveCommandParams.DTL; + + // calculate maximum transfer capacity + if (!CMD_FLAG_MF) + maxTransferCap = 3328; + } + else + { + // When N is non - zero, then DTL has no meaning and should be set to ffh + ActiveCommandParams.DTL = 0xFF; + + // calculate maximum transfer capacity + switch (ActiveCommandParams.SectorSize) + { + case 1: + if (CMD_FLAG_MF) + maxTransferCap = 6656; + else + maxTransferCap = 3840; + break; + case 2: + if (CMD_FLAG_MF) + maxTransferCap = 7680; + else + maxTransferCap = 4096; + break; + case 3: + if (CMD_FLAG_MF) + maxTransferCap = 8192; + else + maxTransferCap = 4096; + break; + } + + sectorSize = 0x80 << ActiveCommandParams.SectorSize; + } + + // get the current track + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track == null || track.NumberOfSectors <= 0) + { + // track could not be found + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + FloppyDisk.Sector sector = null; + + // sector read loop + for (;;) + { + bool terminate = false; + + // lookup the sector + sector = GetSector(); + + if (sector == null) + { + // sector was not found after two passes of the disk index hole + SetBit(SR1_ND, ref Status1); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + + // sector ID was found on this track + + // get status regs from sector + Status1 = sector.Status1; + Status2 = sector.Status2; + + // we dont need EN + UnSetBit(SR1_EN, ref Status1); + + // If SK=1, the FDC skips the sector with the Deleted Data Address Mark and reads the next sector. + // The CRC bits in the deleted data field are not checked when SK=1 + if (CMD_FLAG_SK && !Status2.Bit(SR2_CM)) + { + if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) + { + // increment the sector ID and search again + ActiveCommandParams.Sector++; + continue; + } + else + { + // no execution phase + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + } + + // read the sector + for (int i = 0; i < sectorSize; i++) + { + ExecBuffer[buffPos++] = sector.ActualData[i]; + } + + // any CRC errors? + if (Status1.Bit(SR1_DE) || Status2.Bit(SR2_DD)) + { + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + if (!CMD_FLAG_SK && !Status2.Bit(SR2_CM)) + { + // deleted address mark was detected with NO skip flag set + ActiveCommandParams.EOT = ActiveCommandParams.Sector; + SetBit(SR2_CM, ref Status2); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + if (sector.SectorID == ActiveCommandParams.EOT || terminate) + { + // this was the last sector to read + // or termination requested + + //SetBit(SR1_EN, ref Status1); + + int keyIndex = 0; + for (int i = 0; i < track.Sectors.Length; i++) + { + if (track.Sectors[i].SectorID == sector.SectorID) + { + keyIndex = i; + break; + } + } + + if (keyIndex == track.Sectors.Length - 1) + { + // last sector on the cylinder, set EN + SetBit(SR1_EN, ref Status1); + + // increment cylinder + ActiveCommandParams.Cylinder++; + + // reset sector + ActiveCommandParams.Sector = 1; + ActiveDrive.SectorIndex = 0; + } + else + { + ActiveDrive.SectorIndex++; + } + + UnSetBit(SR0_IC1, ref Status0); + if (terminate) + SetBit(SR0_IC0, ref Status0); + else + UnSetBit(SR0_IC0, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Execution; + break; + } + else + { + // continue with multi-sector read operation + ActiveCommandParams.Sector++; + //ActiveDrive.SectorIndex++; + } + } + + if (ActivePhase == Phase.Execution) + { + ExecLength = buffPos; + ExecCounter = buffPos; + DriveLight = true; + } + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + var index = ExecLength - ExecCounter; + + LastSectorDataReadByte = ExecBuffer[index]; + + ExecCounter--; + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + // get the correct position in the sector buffer + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + // write the byte + ResBuffer[dataIndex] = LastSectorDataWriteByte; + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + /// + /// Read Diagnostic (read track) + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDD and FDC. FDC reads all data fields from index hole to EDT + /// RESULT: 7 result bytes + /// + private void UPD_ReadDiagnostic() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + // get the correct position in the sector buffer + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + // write the byte + ResBuffer[dataIndex] = LastSectorDataWriteByte; + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + /// + /// Read ID + /// COMMAND: 1 parameter byte + /// EXECUTION: The first correct ID information on the cylinder is stored in the data register + /// RESULT: 7 result bytes + /// + private void UPD_ReadID() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + ClearResultBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // set unit select + //SetUnitSelect(ActiveDrive.ID, ref Status0); + + // HD should always be 0 + UnSetBit(SR0_HD, ref Status0); + + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + // it is at this point the +3 detects whether a disk is present + // if not (and after another readid and SIS) it will eventually proceed to loading from tape + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + // setup the result buffer + ResBuffer[RS_ST0] = Status0; + for (int i = 1; i < 7; i++) + ResBuffer[i] = 0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track != null && track.NumberOfSectors > 0 && track.TrackNumber != 0xff) + { + // formatted track + + // is the index out of bounds? + if (ActiveDrive.SectorIndex >= track.NumberOfSectors) + { + // reset the index + ActiveDrive.SectorIndex = 0; + } + + // read the sector data + var data = track.Sectors[ActiveDrive.SectorIndex]; //.GetCHRN(); + ResBuffer[RS_C] = data.TrackNumber; + ResBuffer[RS_H] = data.SideNumber; + ResBuffer[RS_R] = data.SectorID; + ResBuffer[RS_N] = data.SectorSize; + + ResBuffer[RS_ST0] = Status0; + + // increment the current sector + ActiveDrive.SectorIndex++; + + // is the index out of bounds? + if (ActiveDrive.SectorIndex >= track.NumberOfSectors) + { + // reset the index + ActiveDrive.SectorIndex = 0; + } + } + else + { + // unformatted track? + CommitResultCHRN(); + + SetBit(SR0_IC0, ref Status0); + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = 0x01; + } + + ActivePhase = Phase.Result; + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + #endregion + + #region WRITE Commands + + /// + /// Write Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDC and FDD + /// RESULT: 7 result bytes + /// + private void UPD_WriteData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + // get the correct position in the sector buffer + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + // write the byte + ResBuffer[dataIndex] = LastSectorDataWriteByte; + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + /// + /// Write ID (format write) + /// COMMAND: 5 parameter bytes + /// EXECUTION: Entire track is formatted + /// RESULT: 7 result bytes + /// + private void UPD_WriteID() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + byte currByte = CommBuffer[CommCounter]; + switch (CommCounter) + { + case CM_HEAD: + ParseParamByteStandard(CommCounter); + break; + // N + case 1: + ActiveCommandParams.SectorSize = currByte; + break; + // SC (sectors per cylinder) + case 2: + ActiveCommandParams.SectorCount = currByte; + break; + // GPL + case 3: + ActiveCommandParams.Gap3Length = currByte; + break; + // filler + case 4: + ActiveCommandParams.Filler = currByte; + break; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + // get the correct position in the sector buffer + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + // write the byte + ResBuffer[dataIndex] = LastSectorDataWriteByte; + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + /// + /// Write Deleted Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDC and FDD + /// RESULT: 7 result bytes + /// + private void UPD_WriteDeletedData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + // get the correct position in the sector buffer + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + // write the byte + ResBuffer[dataIndex] = LastSectorDataWriteByte; + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + #endregion + + #region SCAN Commands + + /// + /// Scan Equal + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data compared between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ScanEqual() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // use STP instead of DTL for scan commands + if (CommCounter == CM_STP) + { + //ActiveCommandParams.SectorLength = 0; + ActiveCommandParams.STP = LastByteReceived; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + byte oldData = ResBuffer[dataIndex]; + + if (LastSectorDataWriteByte != oldData) + { + if (LastSectorDataWriteByte != 255 && + oldData != 255) + { + // scan not equal + UnSetBit(SR2_SH, ref Status2); + + // scan not satified + SetBit(SR2_SN, ref Status2); + } + } + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + /// + /// Scan Low or Equal + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data compared between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ScanLowOrEqual() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // use STP instead of DTL for scan commands + if (CommCounter == CM_STP) + { + //ActiveCommandParams.SectorLength = 0; + ActiveCommandParams.STP = LastByteReceived; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + byte oldData = ResBuffer[dataIndex]; + + if (LastSectorDataWriteByte != oldData) + { + if (LastSectorDataWriteByte != 255 && + oldData != 255) + { + // scan not equal + UnSetBit(SR2_SH, ref Status2); + + if (oldData > LastSectorDataWriteByte) + { + // scan not satified + SetBit(SR2_SN, ref Status2); + } + } + } + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + /// + /// Scan High or Equal + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data compared between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ScanHighOrEqual() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + /* + switch (iState) + { + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case InstructionState.ReceivingParameters: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // use STP instead of DTL for scan commands + if (CommCounter == CM_STP) + { + //ActiveCommandParams.SectorLength = 0; + ActiveCommandParams.STP = LastByteReceived; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + // move to pre-execution + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + break; + + //---------------------------------------- + // Pre-execution + //---------------------------------------- + case InstructionState.PreExecution: + // instruction is read/write + SetupReadWriteCommand(); + break; + + //---------------------------------------- + // Execution begins + //---------------------------------------- + case InstructionState.StartExecute: + StartReadWriteExecution(); + break; + + //---------------------------------------- + // Write commands during execution + //---------------------------------------- + case InstructionState.ExecutionWrite: + int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; + byte oldData = ResBuffer[dataIndex]; + + if (LastSectorDataWriteByte != oldData) + { + if (LastSectorDataWriteByte != 255 && + oldData != 255) + { + // scan not equal + UnSetBit(SR2_SH, ref Status2); + + if (oldData < LastSectorDataWriteByte) + { + // scan not satified + SetBit(SR2_SN, ref Status2); + } + } + } + break; + + //---------------------------------------- + // Read commands during execution + //---------------------------------------- + case InstructionState.ExecutionRead: + break; + + //---------------------------------------- + // Setup for result phase + //---------------------------------------- + case InstructionState.StartResult: + CheckUnloadHead(); + if (ActivePhase != Phase.Idle) + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + SetPhase_Result(); + } + else + { + // all result bytes have been sent + // move to idle + SetPhase_Idle(); + } + + // set RQM flag + SetBit(MSR_RQM, ref StatusMain); + break; + + //---------------------------------------- + // Result processing + //---------------------------------------- + case InstructionState.ProcessResult: + // instruction is read/write + ReadWriteCommandResult(); + break; + + //---------------------------------------- + // Results sending + //---------------------------------------- + case InstructionState.SendingResults: + break; + + //---------------------------------------- + // Instruction lifecycle completed + //---------------------------------------- + case InstructionState.Completed: + break; + }*/ + } + + #endregion + + #region OTHER Commands + + /// + /// Specify + /// COMMAND: 2 parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: NO result phase + /// + /// Looks like specify command returns status 0x80 throughout its lifecycle + /// so CB is NOT set + /// + private void UPD_Specify() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + byte currByte = CommBuffer[CommCounter]; + BitArray bi = new BitArray(new byte[] { currByte }); + + switch (CommCounter) + { + // SRT & HUT + case 0: + SRT = 16 - (currByte >> 4) & 0x0f; + HUT = (currByte & 0x0f) << 4; + if (HUT == 0) + { + HUT = 255; + } + break; + // HLT & ND + case 1: + if (bi[0]) + ND = true; + else + ND = false; + + HLT = currByte & 0xfe; + if (HLT == 0) + { + HLT = 255; + } + break; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + ActivePhase = Phase.Idle; + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Seek + /// COMMAND: 2 parameter bytes + /// EXECUTION: Head is positioned over proper cylinder on disk + /// RESULT: NO result phase + /// + private void UPD_Seek() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + byte currByte = CommBuffer[CommCounter]; + switch (CommCounter) + { + case 0: + ParseParamByteStandard(CommCounter); + break; + case 1: + ActiveDrive.SeekingTrack = currByte; + break; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + ActivePhase = Phase.Execution; + ActiveCommand.CommandDelegate(); + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // set seek flag + ActiveDrive.SeekStatus = SEEK_SEEK; + + // immediate seek + ActiveDrive.CurrentTrackID = CommBuffer[CM_C]; + + // skip execution mode and go directly to idle + // result is determined by SIS command + ActivePhase = Phase.Idle; + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Recalibrate (seek track 0) + /// COMMAND: 1 parameter byte + /// EXECUTION: Head retracted to track 0 + /// RESULT: NO result phase + /// + private void UPD_Recalibrate() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + ActivePhase = Phase.Execution; + ActiveCommand.CommandDelegate(); + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + + // immediate recalibration + ActiveDrive.TrackIndex = 0; + ActiveDrive.SectorIndex = 0; + + // set seek flag + ActiveDrive.SeekStatus = SEEK_RECALIBRATE; + + // skip execution mode and go directly to idle + // result is determined by SIS command + ActivePhase = Phase.Idle; + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Sense Interrupt Status + /// COMMAND: NO parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: 2 result bytes + /// + private void UPD_SenseInterruptStatus() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // SIS should return 2 bytes if sucessfully sensed an interrupt + // 1 byte otherwise + + // it seems like the +3 ROM makes 3 SIS calls for each seek/recalibrate call for some reason + // possibly one for each drive??? + // 1 - the interrupt is acknowleged with ST0 = 32 and track number + // 2 - second sis returns 1 ST0 byte with 192 + // 3 - third SIS call returns standard 1 byte 0x80 (unknown cmd or SIS with no interrupt occured) + // for now I will assume that the first call is aimed at DriveA, the second at DriveB (which we are NOT implementing) + + // check active drive first + if (ActiveDrive.SeekStatus == SEEK_RECALIBRATE || + ActiveDrive.SeekStatus == SEEK_SEEK) + { + // interrupt has been raised for this drive + // acknowledge + ActiveDrive.SeekStatus = SEEK_INTACKNOWLEDGED; + + // result length 2 + ResLength = 2; + + // first byte ST0 0x20 + Status0 = 0x20; + ResBuffer[0] = Status0; + // second byte is the current track id + ResBuffer[1] = ActiveDrive.CurrentTrackID; + } + else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) + { + // DriveA interrupt has already been acknowledged + ActiveDrive.SeekStatus = SEEK_IDLE; + + ResLength = 1; + Status0 = 192; + ResBuffer[0] = Status0; + } + else if (ActiveDrive.SeekStatus == SEEK_IDLE) + { + // SIS with no interrupt + ResLength = 1; + Status0 = 0x80; + ResBuffer[0] = Status0; + } + + ActivePhase = Phase.Result; + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Sense Drive Status + /// COMMAND: 1 parameter byte + /// EXECUTION: NO execution phase + /// RESULT: 1 result byte + /// + /// The ZX spectrum appears to only specify drive 1 as the parameter byte, NOT drive 0 + /// After the final param byte is received main status changes to 0xd0 + /// Data register (ST3) result is 0x51 if drive/disk not available + /// 0x71 if disk is present in 2nd drive + /// + private void UPD_SenseDriveStatus() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + ActivePhase = Phase.Execution; + UPD_SenseDriveStatus(); + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // one ST3 byte required + + // set US + Status3 = (byte)ActiveDrive.ID; + + if (Status3 != 0) + { + // we only support 1 drive + SetBit(SR3_FT, ref Status3); + } + else + { + // HD - only one side + UnSetBit(SR3_HD, ref Status3); + + // write protect + if (ActiveDrive.FLAG_WRITEPROTECT) + SetBit(SR3_WP, ref Status3); + + // track 0 + if (ActiveDrive.FLAG_TRACK0) + SetBit(SR3_T0, ref Status3); + + // rdy + if (ActiveDrive.Disk != null) + SetBit(SR3_RY, ref Status3); + } + + ResBuffer[0] = Status3; + ActivePhase = Phase.Result; + + + + /* + + + if (ActiveCommandParams.UnitSelect != 0) + { + // we only support 1 drive + SetBit(SR3_FT, ref Status3); + } + else + { + // HD - only one side + UnSetBit(SR3_HD, ref Status3); + + // write protect + if (ActiveDrive.FLAG_WRITEPROTECT) + SetBit(SR3_WP, ref Status3); + + // track 0 + if (ActiveDrive.FLAG_TRACK0) + SetBit(SR3_T0, ref Status3); + + // rdy + if (ActiveDrive.Disk != null) + SetBit(SR3_RY, ref Status3); + } + + ResBuffer[0] = Status3; + ActivePhase = Phase.Result; + + */ + + + /* + + // ready + if (ActiveDrive.FLAG_READY) + SetBit(SR3_RY, ref Status3); + else + { + // set WR if not ready + SetBit(SR3_WP, ref Status3); + } + + */ + + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Version + /// COMMAND: NO parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: 1 result byte + /// + private void UPD_Version() + { + switch (ActivePhase) + { + case Phase.Idle: + case Phase.Command: + case Phase.Execution: + case Phase.Result: + UPD_Invalid(); + break; + } + } + + /// + /// Invalid + /// COMMAND: NO parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: 1 result byte + /// + private void UPD_Invalid() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // no execution phase + ActivePhase = Phase.Result; + UPD_Invalid(); + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + ResBuffer[0] = 0x80; + break; + } + } + + #endregion + + #endregion + + #region Controller Methods + + /// + /// Called when a status register read is required + /// This can be called at any time + /// The main status register appears to be queried nearly all the time + /// so needs to be kept updated. It keeps the CPU informed of the current state + /// + private byte ReadMainStatus() + { + SetBit(MSR_RQM, ref StatusMain); + + switch (ActivePhase) + { + case Phase.Idle: + UnSetBit(MSR_DIO, ref StatusMain); + UnSetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + break; + case Phase.Command: + UnSetBit(MSR_DIO, ref StatusMain); + SetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + break; + case Phase.Execution: + if (ActiveCommand.Direction == CommandDirection.OUT) + SetBit(MSR_DIO, ref StatusMain); + else + UnSetBit(MSR_DIO, ref StatusMain); + + SetBit(MSR_EXM, ref StatusMain); + SetBit(MSR_CB, ref StatusMain); + + break; + case Phase.Result: + SetBit(MSR_DIO, ref StatusMain); + SetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + break; + } + + return StatusMain; + } + + /// + /// Handles CPU reading from the data register + /// + /// + private byte ReadDataRegister() + { + // default return value + byte res = 0xff; + + // check RQM flag status + if (!GetBit(MSR_RQM, StatusMain)) + { + // FDC is not ready to return data + return res; + } + + // check active direction + if (!GetBit(MSR_DIO, StatusMain)) + { + // FDC is expecting to receive, not send data + return res; + } + + switch (ActivePhase) + { + case Phase.Execution: + + // execute read + ActiveCommand.CommandDelegate(); + + res = LastSectorDataReadByte; + + if (ExecCounter <= 0) + { + // end of execution phase + ActivePhase = Phase.Result; + } + + return res; + + case Phase.Result: + + DriveLight = false; + + ActiveCommand.CommandDelegate(); + + // result byte reading + res = ResBuffer[ResCounter]; + + // increment result counter + ResCounter++; + + if (ResCounter >= ResLength) + { + ActivePhase = Phase.Idle; + } + + break; + } + + return res; + } + + /// + /// Handles CPU writing to the data register + /// + /// + private void WriteDataRegister(byte data) + { + if (!GetBit(MSR_RQM, StatusMain) || GetBit(MSR_DIO, StatusMain)) + { + // FDC will not receive and process any bytes + return; + } + + // store the incoming byte + LastByteReceived = data; + + // process incoming bytes + switch (ActivePhase) + { + //// controller is idle awaiting the first command byte of a new instruction + case Phase.Idle: + ParseCommandByte(data); + break; + //// we are in command phase + case Phase.Command: + // attempt to process this parameter byte + //ProcessCommand(data); + ActiveCommand.CommandDelegate(); + break; + //// we are in execution phase + case Phase.Execution: + // CPU is going to be sending data bytes to the FDC to be written to disk + + // store the byte + LastSectorDataWriteByte = data; + ActiveCommand.CommandDelegate(); + break; + //// result phase + case Phase.Result: + // data register will not receive bytes during result phase + break; + } + } + + /// + /// Processes the first command byte (within a command instruction) + /// Returns TRUE if successful. FALSE if otherwise + /// Called only in idle phase + /// + /// + /// + /// + private bool ParseCommandByte(byte cmdByte) + { + // clear counters + CommCounter = 0; + ResCounter = 0; + + // controller is expecting the first command byte + BitArray bi = new BitArray(new byte[] { cmdByte }); + + // save command flags + // skip + CMD_FLAG_SK = bi[5]; + // multitrack + CMD_FLAG_MT = bi[7]; + // MFM mode + CMD_FLAG_MF = bi[6]; + + // remove flags from command byte + bi[5] = false; + bi[6] = false; + bi[7] = false; + + byte[] bytes = new byte[1]; + bi.CopyTo(bytes, 0); + cmdByte = bytes[0]; + + // lookup the command + var cmd = CommandList.Where(a => a.CommandCode == cmdByte).FirstOrDefault(); + + if (cmd == null) + { + // no command found - use invalid + CMDIndex = CommandList.Count() - 1; + } + else + { + // valid command found + CMDIndex = CommandList.FindIndex(a => a.CommandCode == cmdByte); + + // check validity of command byte flags + // if a flag is set but not valid for this command then it is invalid + if ((CMD_FLAG_MF && !ActiveCommand.MF) || + (CMD_FLAG_MT && !ActiveCommand.MT) || + (CMD_FLAG_SK && !ActiveCommand.SK)) + { + // command byte included spurious bit 5,6 or 7 flags + CMDIndex = CommandList.Count() - 1; + } + } + + CommCounter = 0; + ResCounter = 0; + + // there will now be an active command set + // move to command phase + ActivePhase = Phase.Command; + + /* + // check for invalid SIS + if (ActiveInterrupt == InterruptState.None && CMDIndex == CC_SENSE_INTSTATUS) + { + CMDIndex = CC_INVALID; + //ActiveCommand.CommandDelegate(InstructionState.StartResult); + } + */ + + // set reslength + ResLength = ActiveCommand.ResultByteCount; + + // if there are no expected param bytes to receive - go ahead and run the command + if (ActiveCommand.ParameterByteCount == 0) + { + ActivePhase = Phase.Execution; + ActiveCommand.CommandDelegate(); + } + + return true; + } + /* + /// + /// Processes written data bytes whilst in command phase + /// + /// + private void ProcessCommand(byte data) + { + // capture the parameter byte + CommBuffer[CommCounter] = data; + + // process parameter byte + ActiveCommand.CommandDelegate(InstructionState.ReceivingParameters); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // clear down the counters + CommCounter = 0; + ResCounter = 0; + + // all bytes received - execute the UPD command + //ActivePhase = Phase.Execution; + ActiveCommand.CommandDelegate(InstructionState.PreExecution); + } + } + */ + + /// + /// Parses the first 5 command argument bytes that are of the standard format + /// + /// + private void ParseParamByteStandard(int index) + { + byte currByte = CommBuffer[index]; + BitArray bi = new BitArray(new byte[] { currByte }); + + switch (index) + { + // HD & US + case CM_HEAD: + if (bi[2]) + ActiveCommandParams.Side = 1; + else + ActiveCommandParams.Side = 0; + + ActiveCommandParams.UnitSelect = (byte)(GetUnitSelect(currByte)); + DiskDriveIndex = ActiveCommandParams.UnitSelect; + break; + + // C + case CM_C: + ActiveCommandParams.Cylinder = currByte; + break; + + // H + case CM_H: + ActiveCommandParams.Head = currByte; + break; + + // R + case CM_R: + ActiveCommandParams.Sector = currByte; + break; + + // N + case CM_N: + ActiveCommandParams.SectorSize = currByte; + break; + + // EOT + case CM_EOT: + ActiveCommandParams.EOT = currByte; + break; + + // GPL + case CM_GPL: + ActiveCommandParams.Gap3Length = currByte; + break; + + // DTL + case CM_DTL: + ActiveCommandParams.DTL = currByte; + break; + + default: + break; + } + } + /* + /// + /// Initializes a read or write command in execution phase + /// Data bytes are going to be read to the CPU or written to the FDC + /// + private void SetupReadWriteCommand() + { + // set the active drive that this command is referring to + int US = ActiveCommandParams.UnitSelect; + DiskDriveIndex = US; + + // clear everything in interrupt ST0 except for IC and US + UnSetBit(SR0_HD, ref ActiveDrive.IntStatus); + UnSetBit(SR0_NR, ref ActiveDrive.IntStatus); + UnSetBit(SR0_EC, ref ActiveDrive.IntStatus); + UnSetBit(SR0_SE, ref ActiveDrive.IntStatus); + + IndexPulseCounter = 0; + CMD_FLAG_MF = false; + + // clear status registers + Status1 = 0; + Status2 = 0; + + SectorID = 0; + SectorDelayCounter = -1; + + // is the active drive ready? + if (!ActiveDrive.FLAG_READY) + { + ActiveStatus = Status.DriveNotReady; + ActiveCommand.CommandDelegate(InstructionState.StartResult); + return; + } + + // is this a write command? + if (ActiveCommand.IsWrite) + { + // check write protection status + if (ActiveDrive.FLAG_WRITEPROTECT) + { + ActiveStatus = Status.WriteProtected; + ActiveCommand.CommandDelegate(InstructionState.StartResult); + return; + } + } + + // StartExecute + ActiveCommand.CommandDelegate(InstructionState.StartExecute); + } + + /// + /// Starts the read/write execution phase + /// + private void StartReadWriteExecution() + { + // set execution phase + ActivePhase = Phase.Execution; + + // set direction + if (ActiveCommand.Direction == CommandDirection.IN) + UnSetBit(MSR_DIO, ref StatusMain); + else + SetBit(MSR_DIO, ref StatusMain); + + // clear RQM flag + UnSetBit(MSR_RQM, ref StatusMain); + + // setup HLT & HUT + if (HUT_Counter > 0) + { + HLT_Counter = 0; + HUT_Counter = 0; + } + else + { + HLT_Counter = HLT; + HUT_Counter = 0; + } + + // we require 2 index pulses as standard + IndexPulseCounter = 2; + + switch (ActiveCommand.CommandCode) + { + case CC_WRITE_ID: + case CC_READ_DIAGNOSTIC: + CMD_FLAG_MT = false; + break; + default: + CMD_FLAG_MT = true; + break; + } + } + + /// + /// Process standard result data for read/write commands (at the start of result phase) + /// + private void ReadWriteCommandResult() + { + // clear st0 + Status0 = 0; + + // HD + if (ActiveCommandParams.Side == 1) + SetBit(SR0_HD, ref Status0); + // US + switch (ActiveCommandParams.UnitSelect) + { + case 1: + SetBit(SR0_US0, ref Status0); + break; + case 2: + SetBit(SR0_US1, ref Status0); + break; + case 3: + SetBit(SR0_US0, ref Status0); + SetBit(SR0_US1, ref Status0); + break; + } + + if (ActiveStatus != Status.None) + { + Status0 |= 0x40; + + // check for errors + switch (ActiveStatus) + { + case Status.WriteProtected: + SetBit(SR1_NW, ref Status1); + break; + case Status.SectorNotFound: + SetBit(SR1_MA, ref Status1); + SetBit(SR1_ND, ref Status1); + break; + case Status.DriveNotReady: + SetBit(SR0_NR, ref Status0); + break; + } + } + + // populate result buffer + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = Status1; + ResBuffer[RS_ST2] = Status2; + ResBuffer[RS_C] = ActiveCommandParams.Cylinder; + ResBuffer[RS_H] = ActiveCommandParams.Head; + ResBuffer[RS_R] = ActiveCommandParams.Sector; + ResBuffer[RS_N] = ActiveCommandParams.SectorSize; + } + + */ + + /// + /// Clears the result buffer + /// + public void ClearResultBuffer() + { + for (int i = 0; i < ResBuffer.Length; i++) + { + ResBuffer[i] = 0; + } + } + + /// + /// Clears the result buffer + /// + public void ClearExecBuffer() + { + for (int i = 0; i < ExecBuffer.Length; i++) + { + ExecBuffer[i] = 0; + } + } + + + /* + /// + /// Called when a write operation is asked for and we are in Execution phase + /// + /// + private void ProcessExecutionWrite(byte data) + { + // check command direction + if (ActiveCommand.Direction == CommandDirection.IN) + { + if (FDC_FLAG_SCANNING) + { + // scan command is being processed + if (data != 255) + { + switch (ActiveCommand.CommandCode) + { + // scan equal + case 0x11: + break; + // scan high or equal + case 0x1d: + break; + // scan low or equal + case 0x19: + break; + } + } + } + + } + } + + /// + /// Called when a read operation is asked for and we are in Execution phase + /// + /// + private byte ProcessExecutionRead() + { + byte result = 0xFF; + + return result; + } + */ + /// + /// Populates the result status registers + /// + private void CommitResultStatus() + { + // check for read diag + if (ActiveCommand.CommandCode == 0x02) + { + // commit to result buffer + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = Status1; + return; + } + + // check for error bits + if (GetBit(SR1_DE, Status1) || + GetBit(SR1_MA, Status1) || + GetBit(SR1_ND, Status1) || + GetBit(SR1_NW, Status1) || + GetBit(SR1_OR, Status1) || + GetBit(SR2_BC, Status2) || + GetBit(SR2_CM, Status2) || + GetBit(SR2_DD, Status2) || + GetBit(SR2_MD, Status2) || + GetBit(SR2_SN, Status2) || + GetBit(SR2_WC, Status2)) + { + // error bits set - unset end of track + UnSetBit(SR1_EN, ref Status1); + } + + // check for data errors + if (GetBit(SR1_DE, Status1) || + GetBit(SR2_DD, Status2)) + { + // unset control mark + UnSetBit(SR2_CM, ref Status2); + } + else if (GetBit(SR2_CM, Status2)) + { + // DAM found - unset IC and US0 + UnSetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_US0, ref Status0); + } + + /* + // initial defaults + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_EN, ref Status1); + + // check for read diag + if (ActiveCommand.CommandCode == 0x02) + { + // commit to result buffer + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = Status1; + return; + } + + // check for error bits + if (GetBit(SR1_DE, Status1) || + GetBit(SR1_MA, Status1) || + GetBit(SR1_ND, Status1) || + GetBit(SR1_NW, Status1) || + GetBit(SR1_OR, Status1) || + GetBit(SR2_BC, Status2) || + GetBit(SR2_CM, Status2) || + GetBit(SR2_DD, Status2) || + GetBit(SR2_MD, Status2) || + GetBit(SR2_SN, Status2) || + GetBit(SR2_WC, Status2)) + { + // error bits set - unset end of track + UnSetBit(SR1_EN, ref Status1); + } + + // check for data errors + if (GetBit(SR1_DE, Status1) || + GetBit(SR2_DD, Status2)) + { + // unset control mark + UnSetBit(SR2_CM, ref Status2); + } + else if (GetBit(SR2_CM, Status2)) + { + // DAM found - unset IC and US0 + UnSetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_US0, ref Status0); + } + */ + + // commit to result buffer + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = Status1; + ResBuffer[RS_ST2] = Status2; + + } + + /// + /// Populates the result CHRN values + /// + private void CommitResultCHRN() + { + ResBuffer[RS_C] = ActiveCommandParams.Cylinder; + ResBuffer[RS_H] = ActiveCommandParams.Head; + ResBuffer[RS_R] = ActiveCommandParams.Sector; + ResBuffer[RS_N] = ActiveCommandParams.SectorSize; + } + /* + /// + /// Sets everything up ready to return the specified result byte format + /// + private void GetResult(ResultType resType) + { + switch (resType) + { + // return the standard 7 byte format + case ResultType.Standard: + + // status registers + CommitResultStatus(); + + // CHRN + CommitResultCHRN(); + + // make main status register ready + // set FDC busy + SetBit(MSR_CB, ref StatusMain); + // set direction + SetBit(MSR_DIO, ref StatusMain); + // RQM ready + SetBit(MSR_RQM, ref StatusMain); + + // update buffer counters + CommCounter = 0; + ResCounter = 0; + + // clear down status registers + Status0 = 0; + Status1 = 0; + Status2 = 0; + + // move to result phase + ActivePhase = Phase.Result; + + break; + + // return 1 byte ST3 + case ResultType.ST3: + + // commit ST3 to result buffer + ResBuffer[0] = Status3; + + // make main status register ready + // set FDC busy + SetBit(MSR_CB, ref StatusMain); + // set direction + SetBit(MSR_DIO, ref StatusMain); + // RQM ready + SetBit(MSR_RQM, ref StatusMain); + + // update buffer counters + CommCounter = 0; + ResCounter = 0; + + // move to result phase + ActivePhase = Phase.Result; + + break; + + // return 1 byte ST0 + case ResultType.ST0: + + // commit st0 to result buffer + ResBuffer[0] = Status0; + + // make main status register ready + // set FDC busy + SetBit(MSR_CB, ref StatusMain); + // set direction + SetBit(MSR_DIO, ref StatusMain); + // RQM ready + SetBit(MSR_RQM, ref StatusMain); + + // update buffer counters + CommCounter = 0; + ResCounter = 0; + + // move to result phase + ActivePhase = Phase.Result; + + break; + + case ResultType.Interrupt: + + // commit st0 to result buffer + ResBuffer[0] = Status0; + + // commit current track to result buffer + ResBuffer[1] = (byte)FDD_CurrentCylinder; + + // make main status register ready + // set FDC busy + SetBit(MSR_CB, ref StatusMain); + // set direction + SetBit(MSR_DIO, ref StatusMain); + // RQM ready + SetBit(MSR_RQM, ref StatusMain); + + // move to result phase + ActivePhase = Phase.Result; + + break; + } + } + + /// + /// Sets everything up ready to return the standard 7 byte result format + /// + private void GetResult() + { + GetResult(ResultType.Standard); + } + + */ + + /// + /// Moves active phase into idle + /// + public void SetPhase_Idle() + { + ActivePhase = Phase.Idle; + + // active direction + UnSetBit(MSR_DIO, ref StatusMain); + // CB + UnSetBit(MSR_CB, ref StatusMain); + // RQM + SetBit(MSR_RQM, ref StatusMain); + + CommCounter = 0; + ResCounter = 0; + } + + /// + /// Moves to result phase + /// + public void SetPhase_Result() + { + ActivePhase = Phase.Result; + + // active direction + SetBit(MSR_DIO, ref StatusMain); + // CB + SetBit(MSR_CB, ref StatusMain); + // RQM + SetBit(MSR_RQM, ref StatusMain); + // EXM + UnSetBit(MSR_EXM, ref StatusMain); + + CommCounter = 0; + ResCounter = 0; + } + + /// + /// Moves to command phase + /// + public void SetPhase_Command() + { + ActivePhase = Phase.Command; + + // default 0x80 - just RQM + SetBit(MSR_RQM, ref StatusMain); + UnSetBit(MSR_DIO, ref StatusMain); + UnSetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + + // active direction + //UnSetBit(MSR_DIO, ref StatusMain); + // CB + //SetBit(MSR_CB, ref StatusMain); + // RQM + //SetBit(MSR_RQM, ref StatusMain); + + CommCounter = 0; + ResCounter = 0; + } + + /// + /// Moves to execution phase + /// + public void SetPhase_Execution() + { + ActivePhase = Phase.Execution; + + // EXM + SetBit(MSR_EXM, ref StatusMain); + // CB + SetBit(MSR_CB, ref StatusMain); + // RQM + UnSetBit(MSR_RQM, ref StatusMain); + + CommCounter = 0; + ResCounter = 0; + } + /* + /// + /// Runs the execution phase + /// This is called from the Drive Cycle routine + /// + private void ExecutionPhase() + { + // are we currently searching for a sector? + if (IndexPulseCounter > 0) + { + // if the drive ready? + if (!ActiveDrive.FLAG_READY) + { + ActiveStatus = Status.DriveNotReady; + ActiveCommand.CommandDelegate(InstructionState.StartResult); + return; + } + + // is the head loaded? + if (HLT_Counter > 0) + { + return; + } + + SectorDelayCounter--; + + if (SectorDelayCounter < 0) + { + // get next sector + var ns = FDD_CurrentSector + 1; + + if (ns < 0) + { + ActiveStatus = Status.SectorNotFound; + } + } + + + return; + } + + // still bytes to process? + if (ExecCounter > 0) + { + if (SectorDelayCounter > 0) + { + if (--SectorDelayCounter <= 0) + { + // we are at the sector data + SetBit(MSR_RQM, ref StatusMain); + } + + return; + } + + // IO sector transfer + if (GetBit(MSR_RQM, StatusMain)); + { + // overrun + SetBit(SR1_OR, ref Status1); + + if ((ActiveCommand.CommandCode & 19) == 1) + { + ResBuffer[ActiveCommandParams.SectorLength - ExecCounter] = 0; + } + } + + // decrement ExecCounter + ExecCounter--; + + // has transfer completed? + if (ExecCounter <= 0) + { + SectorDelayCounter = 2; + } + else + { + // next byte + SetBit(MSR_RQM, ref StatusMain); + } + return; + } + + if (SectorDelayCounter > 0) + { + if (--SectorDelayCounter > 0) + { + // data crc delay + return; + } + } + + // check interrupt status + if (ActiveDrive.IntStatus >= 0xc0) + { + ActiveStatus = Status.DriveNotReady; + return; + } + + // is this a write command? + if ((ActiveCommand.CommandCode & 19) == 1) + { + byte sr1 = 0; + byte sr2 = (byte)((ActiveCommand.CommandCode & 8) << 3); + + // write sector !!! + // assume no error for now !!! + + sr1 |= (byte)(sr1 & 0x25); + sr2 |= (byte)(sr2 & 0x21); + } + + // increment sector id + } + + /// + /// Unloads head in execution + /// + private void CheckUnloadHead() + { + if (ActivePhase != Phase.Idle && ActivePhase == Phase.Execution) + { + // unload head + HUT_Counter = HUT; + HLT_Counter = 0; + } + } + /* + /// + /// Starts processing the result phase + /// + private void ResultPhase() + { + if (ActivePhase != Phase.Idle) + { + if (ActivePhase == Phase.Execution) + { + // unload head + HUT_Counter = HUT; + HLT_Counter = 0; + } + + if (ActiveStatus == Status.Invalid) + { + // default error st0 + ResBuffer[0] = 0x80; + ResLength = 1; + } + else + { + ActiveCommand.CommandDelegate(InstructionState.ProcessResult); + } + } + + // are there still result bytes to return? + if (ResCounter < ResLength) + { + // set result phase + ActivePhase = Phase.Result; + // active direction + ActiveDirection = CommandDirection.OUT; + } + else + { + // all result bytes have been sent + // move to idle + ActivePhase = Phase.Idle; + // active direction + ActiveDirection = CommandDirection.IN; + } + + // set RQM flag + FDC_FLAG_RQM = true; + } + */ + + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs new file mode 100644 index 0000000000..2b60c5bc52 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs @@ -0,0 +1,909 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Floppy drive related stuff + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 : IFDDHost + { + #region Drive State + + /// + /// FDD Flag - motor on/off + /// + public bool FDD_FLAG_MOTOR; + + /// + /// The index of the currently active disk drive + /// + public int DiskDriveIndex + { + get { return _diskDriveIndex; } + set + { + // when index is changed update the ActiveDrive + _diskDriveIndex = value; + ActiveDrive = DriveStates[_diskDriveIndex]; + } + } + private int _diskDriveIndex = 0; + + /// + /// The currently active drive + /// + private DriveState ActiveDrive; + + /// + /// Array that holds state information for each possible drive + /// + private DriveState[] DriveStates = new DriveState[4]; + + #endregion + + #region FDD Methods + + /// + /// Initialization / reset of the floppy drive subsystem + /// + private void FDD_Init() + { + for (int i = 0; i < 4; i++) + { + DriveState ds = new DriveState(i, this); + DriveStates[i] = ds; + } + } + + /// + /// Searches for the requested sector + /// + /// + private FloppyDisk.Sector GetSector() + { + FloppyDisk.Sector sector = null; + + // get the current track + var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex]; + + // get the current sector index + int index = ActiveDrive.SectorIndex; + + // make sure this index exists + if (index > trk.Sectors.Length) + { + index = 0; + } + + // index hole count + int iHole = 0; + + // loop through the sectors in a track + // the loop ends with either the sector being found + // or the index hole being passed twice + while (iHole <= 2) + { + // does the requested sector match the current sector + if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder && + trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head && + trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector && + trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize) + { + // sector has been found + sector = trk.Sectors[index]; + + UnSetBit(SR2_BC, ref Status2); + UnSetBit(SR2_WC, ref Status2); + break; + } + + // check for bad cylinder + if (trk.Sectors[index].SectorIDInfo.C == 255) + { + SetBit(SR2_BC, ref Status2); + } + // check for no cylinder + else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder) + { + SetBit(SR2_WC, ref Status2); + } + + // incrememnt sector index + index++; + + // have we reached the index hole? + if (trk.Sectors.Length <= index) + { + // wrap around + index = 0; + iHole++; + } + } + + // search loop has completed and the sector may or may not have been found + + // bad cylinder detected? + if (Status2.Bit(SR2_BC)) + { + // remove WC + UnSetBit(SR2_WC, ref Status2); + } + + // update sectorindex on drive + ActiveDrive.SectorIndex = index; + + return sector; + } + + #endregion + + #region IFDDHost + + // IFDDHost methods that fall through to the currently active drive + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + /// + public void FDD_LoadDisk(byte[] diskData) + { + // we are only going to load into the first drive + DriveStates[0].FDD_LoadDisk(diskData); + } + + /// + /// Ejects the current disk + /// + public void FDD_EjectDisk() + { + DriveStates[DiskDriveIndex].FDD_EjectDisk(); + } + + /// + /// Signs whether the current active drive has a disk inserted + /// + public bool FDD_IsDiskLoaded + { + get { return DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; } + } + + public FloppyDisk Disk { get; set; } + + #endregion + + #region Drive Status Class + + /// + /// Holds specfic state information about a drive + /// + private class DriveState : IFDDHost + { + #region State + + /// + /// The drive ID from an FDC perspective + /// + public int ID; + + /// + /// Signs whether this drive ready + /// TRUE if both drive exists and has a disk inserted + /// + public bool FLAG_READY + { + get + { + if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR) + return false; + else + return true; + } + } + + /// + /// Disk is write protected (TRUE BY DEFAULT) + /// + public bool FLAG_WRITEPROTECT = true; + + /// + /// Storage for recal steps + /// One step for each indexpulse (track index) until track 0 + /// + //public int RecalibrationCounter; + + /// + /// Storage for seek steps + /// One step for each indexpulse (track index) until seeked track + /// + public int SeekCounter; + + /// + /// Seek status + /// + public int SeekStatus; + + /// + /// Age counter + /// + public int SeekAge; + + /// + /// The current side + /// + public byte CurrentSide; + + /// + /// The current track index in the DiskTracks array + /// + public byte TrackIndex; + + /// + /// The track ID of the current cylinder + /// + public byte CurrentTrackID + { + get + { + // default invalid track + int id = 0xff; + + if (Disk == null) + return (byte)id; + + if (Disk.DiskTracks.Count() == 0) + return (byte)id; + + if (TrackIndex >= Disk.GetTrackCount()) + TrackIndex = 0; + else if (TrackIndex < 0) + TrackIndex = 0; + + var track = Disk.DiskTracks[TrackIndex]; + + id = track.TrackNumber; + + return (byte)id; + } + set + { + for (int i = 0; i < Disk.GetTrackCount(); i++) + { + if (Disk.DiskTracks[i].TrackNumber == value) + { + TrackIndex = (byte)i; + break; + } + } + } + } + + + /// + /// The new track that the drive is seeking to + /// (used in seek operations) + /// + public int SeekingTrack; + + /// + /// The current sector index in the Sectors array + /// + public int SectorIndex; + + /// + /// The current command that the drive is processing + /// + //public DriveMainState CurrentState = DriveMainState.None; + + /// + /// Current seek state + /// + //public SeekSubState SeekState = SeekSubState.Idle; + + /// + /// Seek int state + /// + //public SeekIntStatus SeekIntState = SeekIntStatus.Normal; + + /// + /// The currently loaded floppy disk + /// + public FloppyDisk Disk { get; set; } + + /// + /// The parent controller + /// + private NECUPD765 FDC; + + #endregion + + #region Lookups + + /// + /// TRUE if we are on track 0 + /// + public bool FLAG_TRACK0 + { + get + { + if (TrackIndex == 0) { return true; } + else { return false; } + } + } + + #endregion + + #region Public Methods + /* + /// + /// Moves the head across the disk cylinders + /// + /// + /// + public void MoveHead(SkipDirection direction, int cylinderCount) + { + // get total tracks + int trackCount = Disk.DiskTracks.Count(); + + int trk = 0; + + switch (direction) + { + case SkipDirection.Increment: + trk = (int)CurrentTrack + cylinderCount; + if (trk >= trackCount) + { + // past the last track + trk = trackCount - 1; + } + else if (trk < 0) + trk = 0; + break; + case SkipDirection.Decrement: + trk = (int)CurrentTrack - cylinderCount; + if (trk < 0) + { + // before the first track + trk = 0; + } + else if (trk >= trackCount) + trk = trackCount - 1; + break; + } + + // move the head + CurrentTrack = (byte)trk; + } + */ + + /* + + /// + /// Finds a supplied sector + /// + /// + /// + /// + /// + public FloppyDisk.Sector FindSector(ref byte[] resBuffer, CommandParameters prms) + { + int index =CurrentSector; + int lc = 0; + FloppyDisk.Sector sector = null; + + bool found = false; + + do + { + sector = Disk.DiskTracks[CurrentTrack].Sectors[index]; + if (sector != null && sector.SectorID == prms.Sector) + { + // sector found + // check for data errors + if ((sector.Status1 & 0x20) != 0 || (sector.Status2 & 0x20) != 0) + { + // data errors found + } + found = true; + break; + } + + // sector doesnt match + var c = Disk.DiskTracks[CurrentTrack].Sectors[index].TrackNumber; + if (c == 255) + { + // bad cylinder + resBuffer[RS_ST2] |= 0x02; + } + else if (prms.Cylinder != c) + { + // cylinder mismatch + resBuffer[RS_ST2] |= 0x10; + } + + // increment index + index++; + + if (index >= Disk.DiskTracks[CurrentTrack].NumberOfSectors) + { + // out of bounds + index = 0; + lc++; + } + + } while (lc < 2); + + if ((resBuffer[RS_ST2] & 0x02) != 0) + { + // bad cylinder set - remove no cylinder + UnSetBit(SR2_WC, ref resBuffer[RS_ST2]); + } + + // update current sector + CurrentSector = index; + + if (found) + return sector; + else + return null; + } + + + /// + /// Populates a result buffer + /// + /// + /// + public void FillResult(ref byte[] resBuffer, CHRN chrn) + { + // clear results + resBuffer[RS_ST0] = 0; + resBuffer[RS_ST1] = 0; + resBuffer[RS_ST2] = 0; + resBuffer[RS_C] = 0; + resBuffer[RS_H] = 0; + resBuffer[RS_R] = 0; + resBuffer[RS_N] = 0; + + if (chrn == null) + { + // no chrn supplied + resBuffer[RS_ST0] = ST0; + resBuffer[RS_ST1] = 0; + resBuffer[RS_ST2] = 0; + resBuffer[RS_C] = 0; + resBuffer[RS_H] = 0; + resBuffer[RS_R] = 0; + resBuffer[RS_N] = 0; + } + } + + + + /// + /// Populates the result buffer with ReadID data + /// + /// + public void ReadID(ref byte[] resBuffer) + { + if (CheckDriveStatus() == false) + { + // drive not ready + resBuffer[RS_ST0] = ST0; + return; + } + + var track = Disk.DiskTracks.Where(a => a.TrackNumber == CurrentTrack).FirstOrDefault(); + + if (track != null && track.NumberOfSectors > 0) + { + // formatted track + + // get the current sector + int index = CurrentSector; + + // is the index out of bounds? + if (index >= track.NumberOfSectors) + { + // reset the index + index = 0; + } + + // read the sector data + var data = track.Sectors[index]; + resBuffer[RS_C] = data.TrackNumber; + resBuffer[RS_H] = data.SideNumber; + resBuffer[RS_R] = data.SectorID; + resBuffer[RS_N] = data.SectorSize; + + resBuffer[RS_ST0] = ST0; + + // increment the current sector + CurrentSector = index + 1; + return; + } + else + { + // unformatted track? + resBuffer[RS_C] = FDC.CommBuffer[CM_C]; + resBuffer[RS_H] = FDC.CommBuffer[CM_H]; + resBuffer[RS_R] = FDC.CommBuffer[CM_R]; + resBuffer[RS_N] = FDC.CommBuffer[CM_N]; + + SetBit(SR0_IC0, ref ST0); + resBuffer[RS_ST0] = ST0; + resBuffer[RS_ST1] = 0x01; + return; + } + } + */ + + /* + + /// + /// The drive performs a seek operation if necessary + /// Return value TRUE indicates seek complete + /// + public void DoSeek() + { + if (CurrentState != DriveMainState.Recalibrate && + CurrentState != DriveMainState.Seek) + { + // no seek/recalibrate has been asked for + return; + } + + if (GetBit(ID, FDC.StatusMain)) + { + // drive is already seeking + return; + } + + RunSeekCycle(); + } + + /// + /// Runs a seek cycle + /// + public void RunSeekCycle() + { + for (;;) + { + switch (SeekState) + { + // seek or recalibrate has been requested + case SeekSubState.Idle: + + if (CurrentState == DriveMainState.Recalibrate) + { + // recalibrate always seeks to track 0 + SeekingTrack = 0; + } + SeekState = SeekSubState.MoveInit; + + // mark drive as busy + // this should be cleared by SIS command + SetBit(ID, ref FDC.StatusMain); + + break; + + // setup for the head move + case SeekSubState.MoveInit: + + if (CurrentTrack == SeekingTrack) + { + // we are already at the required track + if (CurrentState == DriveMainState.Recalibrate && + !FLAG_TRACK0) + { + // recalibration fail + SeekIntState = SeekIntStatus.Abnormal; + + // raise seek interrupt + FDC.ActiveInterrupt = InterruptState.Seek; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + // equipment check + SetBit(SR0_EC, ref FDC.Status0); + + SeekState = SeekSubState.PerformCompletion; + break; + } + + if (CurrentState == DriveMainState.Recalibrate && + FLAG_TRACK0) + { + // recalibration success + SeekIntState = SeekIntStatus.Normal; + + // raise seek interrupt + FDC.ActiveInterrupt = InterruptState.Seek; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + SeekState = SeekSubState.PerformCompletion; + break; + } + } + + // check for error + if (IntStatus >= IC_ABORTED_DISCREMOVED || Disk == null) + { + // drive not ready + FLAG_READY = false; + + // drive not ready + SeekIntState = SeekIntStatus.DriveNotReady; + + // cancel any interrupt + FDC.ActiveInterrupt = InterruptState.None; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + SeekState = SeekSubState.PerformCompletion; + break; + } + + if (SeekCounter > 1) + { + // not ready to seek yet + SeekCounter--; + return; + } + + if (FDC.SRT < 1 && CurrentTrack != SeekingTrack) + { + SeekState = SeekSubState.MoveImmediate; + break; + } + + // head move + SeekState = SeekSubState.HeadMove; + + break; + + case SeekSubState.HeadMove: + + // do the seek + SeekCounter = FDC.SRT; + + if (CurrentTrack < SeekingTrack) + { + // we are seeking forward + var delta = SeekingTrack - CurrentTrack; + MoveHead(SkipDirection.Increment, 1); + } + else if (CurrentTrack > SeekingTrack) + { + // we are seeking backward + var delta = CurrentTrack - SeekingTrack; + MoveHead(SkipDirection.Decrement, 1); + } + + // should the seek be completed now? + if (CurrentTrack == SeekingTrack) + { + SeekState = SeekSubState.PerformCompletion; + break; + } + + // seek not finished yet + return; + + // seek emulation processed immediately + case SeekSubState.MoveImmediate: + + if (CurrentTrack < SeekingTrack) + { + // we are seeking forward + var delta = SeekingTrack - CurrentTrack; + MoveHead(SkipDirection.Increment, delta); + + } + else if (CurrentTrack > SeekingTrack) + { + // we are seeking backward + var delta = CurrentTrack - SeekingTrack; + MoveHead(SkipDirection.Decrement, delta); + } + + SeekState = SeekSubState.PerformCompletion; + break; + + case SeekSubState.PerformCompletion: + SeekDone(); + SeekState = SeekSubState.SeekCompleted; + break; + + case SeekSubState.SeekCompleted: + // seek has already completed + return; + } + } + } + + /// + /// Called when a seek operation has completed + /// + public void SeekDone() + { + SeekCounter = 0; + SeekingTrack = CurrentTrack; + + // generate ST0 register data + + // get only the IC bits + IntStatus &= IC_ABORTED_DISCREMOVED; + + // drive ready? + if (!FLAG_READY) + { + SetBit(SR0_NR, ref IntStatus); + SetBit(SR0_EC, ref IntStatus); + + // are we recalibrating? + if (CurrentState == DriveMainState.Recalibrate) + { + SetBit(SR0_EC, ref IntStatus); + } + } + + // set seek end + SetBit(SR0_SE, ref IntStatus); + /* + // head address + if (CurrentSide > 0) + { + SetBit(SR0_HD, ref IntStatus); + + // drive only supports 1 head + // set the EC bit + SetBit(SR0_EC, ref IntStatus); + } + */ + /* + // UnitSelect + SetUnitSelect(ID, ref IntStatus); + + // move to none state + //CurrentState = DriveMainState.None; + + //SeekState = SeekSubState.SeekCompleted; + + // set the seek interrupt flag for this drive + // this will be cleared at the next successful senseint + FLAG_SEEK_INTERRUPT = true; + + //CurrentState = DriveMainState.None; + + } + */ + + #endregion + + #region Construction + + public DriveState(int driveID, NECUPD765 fdc) + { + ID = driveID; + FDC = fdc; + } + + #endregion + + #region IFDDHost + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + /// + public void FDD_LoadDisk(byte[] diskData) + { + // try dsk first + FloppyDisk fdd = null; + bool found = false; + + foreach (DiskType type in Enum.GetValues(typeof(DiskType))) + { + switch (type) + { + case DiskType.CPCExtended: + fdd = new CPCExtendedFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + case DiskType.CPC: + fdd = new CPCFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + } + + if (found) + { + Disk = fdd; + break; + } + } + + if (!found) + { + throw new Exception(this.GetType().ToString() + + "\n\nDisk image file could not be parsed. Potentially an unknown format."); + } + } + + /// + /// Ejects the current disk + /// + public void FDD_EjectDisk() + { + Disk = null; + //FLAG_READY = false; + } + + /// + /// Signs whether the current active drive has a disk inserted + /// + public bool FDD_IsDiskLoaded + { + get + { + if (Disk != null) + return true; + else + return false; + } + } + + #endregion + + #region StateSerialization + + public void SyncState(Serializer ser) + { + ser.Sync("ID", ref ID); + ser.Sync("FLAG_WRITEPROTECT", ref FLAG_WRITEPROTECT); + //ser.Sync("FLAG_DISKCHANGED", ref FLAG_DISKCHANGED); + //ser.Sync("FLAG_RECALIBRATING", ref FLAG_RECALIBRATING); + //ser.Sync("FLAG_SEEK_INTERRUPT", ref FLAG_SEEK_INTERRUPT); + //ser.Sync("IntStatus", ref IntStatus); + //ser.Sync("ST0", ref ST0); + //ser.Sync("RecalibrationCounter", ref RecalibrationCounter); + ser.Sync("SeekCounter", ref SeekCounter); + ser.Sync("SeekStatus", ref SeekStatus); + ser.Sync("SeekAge", ref SeekAge); + ser.Sync("CurrentSide", ref CurrentSide); + //ser.Sync("CurrentTrack", ref CurrentTrack); + ser.Sync("TrackIndex", ref TrackIndex); + ser.Sync("SeekingTrack", ref SeekingTrack); + //ser.Sync("CurrentSector", ref CurrentSector); + ser.Sync("SectorIndex", ref SectorIndex); + //ser.Sync("RAngles", ref RAngles); + //ser.Sync("DataPointer", ref DataPointer); + //ser.SyncEnum("CurrentState", ref CurrentState); + //ser.SyncEnum("SeekState", ref SeekState); + //ser.SyncEnum("SeekIntState", ref SeekIntState); + } + + #endregion + } + +#endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs new file mode 100644 index 0000000000..d3ff9519cb --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// IPortIODevice + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 : IPortIODevice + { + + public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv"; + public string outputString = "STATUS,WRITE,READ\r\n"; + public bool writeDebug = false; + + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + public bool ReadPort(ushort port, ref int data) + { + BitArray bits = new BitArray(new byte[] { (byte)data }); + + if (port == 0x3ffd) + { + // Z80 is trying to read from the data register + data = ReadDataRegister(); + if (writeDebug) + outputString += ",," + data + "\r\n"; + return true; + } + + if (port == 0x2ffd) + { + // read main status register + // this can happen at any time + data = ReadMainStatus(); + if (writeDebug) + outputString += data + ",,\r\n"; + return true; + } + + return false; + } + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + public bool WritePort(ushort port, int data) + { + BitArray bits = new BitArray(new byte[] { (byte)data }); + + if (port == 0x3ffd) + { + // Z80 is attempting to write to the data register + WriteDataRegister((byte)data); + if (writeDebug) + { + outputString += "," + data + ",\r\n"; + System.IO.File.WriteAllText(outputfile, outputString); + } + + return true; + } + + if (port == 0x1ffd) + { + // set disk motor on/off + FDD_FLAG_MOTOR = bits[3]; + return true; + } + return false; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs new file mode 100644 index 0000000000..9ce1f467c8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs @@ -0,0 +1,554 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Registers + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + /* + + #region Main Status Register + + /// + /// Main status register (accessed via reads to port 0x2ffd) + /// + private byte _RegMain; + + /// + /// FDD0 Busy (seek/recalib active, until succesful sense intstat) + /// + private bool MainDB0 + { + get { return GetBit(0, _RegMain); } + set + { + if (value) { SetBit(0, ref _RegMain); } + else { UnSetBit(0, ref _RegMain); } + } + } + + /// + /// FDD1 Busy (seek/recalib active, until succesful sense intstat) + /// + private bool MainDB1 + { + get { return GetBit(1, _RegMain); } + set + { + if (value) { SetBit(1, ref _RegMain); } + else { UnSetBit(1, ref _RegMain); } + } + } + + /// + /// FDD2 Busy (seek/recalib active, until succesful sense intstat) + /// + private bool MainDB2 + { + get { return GetBit(2, _RegMain); } + set + { + if (value) { SetBit(2, ref _RegMain); } + else { UnSetBit(2, ref _RegMain); } + } + } + + /// + /// FDD3 Busy (seek/recalib active, until succesful sense intstat) + /// + private bool MainDB3 + { + get { return GetBit(3, _RegMain); } + set + { + if (value) { SetBit(3, ref _RegMain); } + else { UnSetBit(3, ref _RegMain); } + } + } + + /// + /// FDC Busy (still in command-, execution- or result-phase) + /// + private bool MainCB + { + get { return GetBit(4, _RegMain); } + set + { + if (value) { SetBit(4, ref _RegMain); } + else { UnSetBit(4, ref _RegMain); } + } + } + + /// + /// Execution Mode (still in execution-phase, non_DMA_only) + /// + private bool MainEXM + { + get { return GetBit(5, _RegMain); } + set + { + if (value) { SetBit(5, ref _RegMain); } + else { UnSetBit(5, ref _RegMain); } + } + } + + /// + /// Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) + /// + private bool MainDIO + { + get { return GetBit(6, _RegMain); } + set + { + if (value) { SetBit(6, ref _RegMain); } + else { UnSetBit(6, ref _RegMain); } + } + } + + /// + /// Request For Master (1=ready for next byte) (see b6 for direction) + /// + private bool MainRQM + { + get { return GetBit(7, _RegMain); } + set + { + if (value) { SetBit(7, ref _RegMain); } + else { UnSetBit(7, ref _RegMain); } + } + } + + #endregion + + #region Status Register 0 + + /// + /// Status Register 0 + /// + private byte _Reg0; + + /// + /// Unit Select (driveno during interrupt) + /// + private bool ST0US0 + { + get { return GetBit(0, _Reg0); } + set + { + if (value) { SetBit(0, ref _Reg0); } + else { UnSetBit(0, ref _Reg0); } + } + } + + /// + /// Unit Select (driveno during interrupt) + /// + private bool ST0US1 + { + get { return GetBit(1, _Reg0); } + set + { + if (value) { SetBit(1, ref _Reg0); } + else { UnSetBit(1, ref _Reg0); } + } + } + + /// + /// Head Address (head during interrupt) + /// + private bool ST0HD + { + get { return GetBit(2, _Reg0); } + set + { + if (value) { SetBit(2, ref _Reg0); } + else { UnSetBit(2, ref _Reg0); } + } + } + + /// + /// Not Ready (drive not ready or non-existing 2nd head selected) + /// + private bool ST0NR + { + get { return GetBit(3, _Reg0); } + set + { + if (value) { SetBit(3, ref _Reg0); } + else { UnSetBit(3, ref _Reg0); } + } + } + + /// + /// Equipment Check (drive failure or recalibrate failed (retry)) + /// + private bool ST0EC + { + get { return GetBit(4, _Reg0); } + set + { + if (value) { SetBit(4, ref _Reg0); } + else { UnSetBit(4, ref _Reg0); } + } + } + + /// + /// Seek End (Set if seek-command completed) + /// + private bool ST0SE + { + get { return GetBit(5, _Reg0); } + set + { + if (value) { SetBit(5, ref _Reg0); } + else { UnSetBit(5, ref _Reg0); } + } + } + + /// + /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + /// or senseint with no int occured, 3=aborted:disc removed etc.) + /// + private bool ST0IC0 + { + get { return GetBit(6, _Reg0); } + set + { + if (value) { SetBit(6, ref _Reg0); } + else { UnSetBit(6, ref _Reg0); } + } + } + + /// + /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + /// or senseint with no int occured, 3=aborted:disc removed etc.) + /// + private bool ST0IC1 + { + get { return GetBit(7, _Reg0); } + set + { + if (value) { SetBit(7, ref _Reg0); } + else { UnSetBit(7, ref _Reg0); } + } + } + + #endregion + + #region Status Register 1 + + /// + /// Status Register 1 + /// + private byte _Reg1; + + /// + /// Missing Address Mark (Sector_ID or DAM not found) + /// + private bool ST1MA + { + get { return GetBit(0, _Reg1); } + set + { + if (value) { SetBit(0, ref _Reg1); } + else { UnSetBit(0, ref _Reg1); } + } + } + + /// + /// Not Writeable (tried to write/format disc with wprot_tab=on) + /// + private bool ST1NW + { + get { return GetBit(1, _Reg1); } + set + { + if (value) { SetBit(1, ref _Reg1); } + else { UnSetBit(1, ref _Reg1); } + } + } + + /// + /// No Data (Sector_ID not found, CRC fail in ID_field) + /// + private bool ST1ND + { + get { return GetBit(2, _Reg1); } + set + { + if (value) { SetBit(2, ref _Reg1); } + else { UnSetBit(2, ref _Reg1); } + } + } + + /// + /// Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) + /// + private bool ST1OR + { + get { return GetBit(4, _Reg1); } + set + { + if (value) { SetBit(4, ref _Reg1); } + else { UnSetBit(4, ref _Reg1); } + } + } + + /// + /// Data Error (CRC-fail in ID- or Data-Field) + /// + private bool ST1DE + { + get { return GetBit(5, _Reg1); } + set + { + if (value) { SetBit(5, ref _Reg1); } + else { UnSetBit(5, ref _Reg1); } + } + } + + /// + /// End of Track (set past most read/write commands) (see IC) + /// + private bool ST1EN + { + get { return GetBit(7, _Reg1); } + set + { + if (value) { SetBit(7, ref _Reg1); } + else { UnSetBit(7, ref _Reg1); } + } + } + + #endregion + + #region Status Register 2 + + /// + /// Status Register 2 + /// + private byte _Reg2; + + /// + /// Missing Address Mark in Data Field (DAM not found) + /// + private bool ST2MD + { + get { return GetBit(0, _Reg2); } + set + { + if (value) { SetBit(0, ref _Reg2); } + else { UnSetBit(0, ref _Reg2); } + } + } + + /// + /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) + /// + private bool ST2BC + { + get { return GetBit(1, _Reg2); } + set + { + if (value) { SetBit(1, ref _Reg2); } + else { UnSetBit(1, ref _Reg2); } + } + } + + /// + /// Scan Not Satisfied (no fitting sector found) + /// + private bool ST2SN + { + get { return GetBit(2, _Reg2); } + set + { + if (value) { SetBit(2, ref _Reg2); } + else { UnSetBit(2, ref _Reg2); } + } + } + + /// + /// Scan Equal Hit (equal) + /// + private bool ST2SH + { + get { return GetBit(3, _Reg2); } + set + { + if (value) { SetBit(3, ref _Reg2); } + else { UnSetBit(3, ref _Reg2); } + } + } + + /// + /// Wrong Cylinder (read/programmed track-ID different) (see b1) + /// + private bool ST2WC + { + get { return GetBit(4, _Reg2); } + set + { + if (value) { SetBit(4, ref _Reg2); } + else { UnSetBit(4, ref _Reg2); } + } + } + + /// + /// Data Error in Data Field (CRC-fail in data-field) + /// + private bool ST2DD + { + get { return GetBit(5, _Reg2); } + set + { + if (value) { SetBit(5, ref _Reg2); } + else { UnSetBit(5, ref _Reg2); } + } + } + + /// + /// Control Mark (read/scan command found sector with deleted DAM) + /// + private bool ST2CM + { + get { return GetBit(6, _Reg2); } + set + { + if (value) { SetBit(6, ref _Reg2); } + else { UnSetBit(6, ref _Reg2); } + } + } + + #endregion + + #region Status Register 3 + + /// + /// Status Register 3 + /// + private byte _Reg3; + + /// + /// Unit Select (pin 28,29 of FDC) + /// + private bool ST3US0 + { + get { return GetBit(0, _Reg3); } + set + { + if (value) { SetBit(0, ref _Reg3); } + else { UnSetBit(0, ref _Reg3); } + } + } + + /// + /// Unit Select (pin 28,29 of FDC) + /// + private bool ST3US1 + { + get { return GetBit(1, _Reg3); } + set + { + if (value) { SetBit(1, ref _Reg3); } + else { UnSetBit(1, ref _Reg3); } + } + } + + /// + /// Head Address (pin 27 of FDC) + /// + private bool ST3HD + { + get { return GetBit(2, _Reg3); } + set + { + if (value) { SetBit(2, ref _Reg3); } + else { UnSetBit(2, ref _Reg3); } + } + } + + /// + /// Two Side (0=yes, 1=no (!)) + /// + private bool ST3TS + { + get { return GetBit(3, _Reg3); } + set + { + if (value) { SetBit(3, ref _Reg3); } + else { UnSetBit(3, ref _Reg3); } + } + } + + /// + /// Track 0 (on track 0 we are) + /// + private bool ST3T0 + { + get { return GetBit(4, _Reg3); } + set + { + if (value) { SetBit(4, ref _Reg3); } + else { UnSetBit(4, ref _Reg3); } + } + } + + /// + /// Ready (drive ready signal) + /// + private bool ST3RY + { + get { return GetBit(5, _Reg3); } + set + { + if (value) { SetBit(5, ref _Reg3); } + else { UnSetBit(5, ref _Reg3); } + } + } + + /// + /// Write Protected (write protected) + /// + private bool ST3WP + { + get { return GetBit(6, _Reg3); } + set + { + if (value) { SetBit(6, ref _Reg3); } + else { UnSetBit(6, ref _Reg3); } + } + } + + /// + /// Fault (if supported: 1=Drive failure) + /// + private bool ST3FT + { + get { return GetBit(7, _Reg3); } + set + { + if (value) { SetBit(7, ref _Reg3); } + else { UnSetBit(7, ref _Reg3); } + } + } + + #endregion + + */ + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs new file mode 100644 index 0000000000..12f64af0f9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Timimng + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + /// + /// The current Z80 cycle + /// + private long CurrentCPUCycle + { + get + { + if (_machine == null) + return 0; + else + return _machine.CPU.TotalExecutedCycles; + } + } + + /// + /// The last CPU cycle when the FDC accepted an IO read/write + /// + private long LastCPUCycle; + + /// + /// The current delay figure (in Z80 t-states) + /// This implementation only introduces delay upon main status register reads + /// All timing calculations should be done during the other read/write operations + /// + private long StatusDelay; + + /// + /// Defines the numbers of Z80 cycles per MS + /// + private long CPUCyclesPerMs; + + /// + /// The floppy drive emulated clock speed + /// + public const double DriveClock = 31250; + + /// + /// The number of floppy drive cycles per MS + /// + public long DriveCyclesPerMs; + + /// + /// The number of T-States in one floppy drive clock tick + /// + public long StatesPerDriveTick; + + /// + /// Responsible for measuring when the floppy drive is ready to run a cycle + /// + private long TickCounter; + + /// + /// Internal drive cycle counter + /// + private int DriveCycleCounter = 1; + + /// + /// Initializes the timing routines + /// + private void TimingInit() + { + // z80 timing + double frameSize = _machine.ULADevice.FrameLength; + double rRate = _machine.ULADevice.ClockSpeed / frameSize; + long tPerSecond = (long)(frameSize * rRate); + CPUCyclesPerMs = tPerSecond / 1000; + + // drive timing + double dRate = DriveClock / frameSize; + long dPerSecond = (long)(frameSize * dRate); + DriveCyclesPerMs = dPerSecond / 1000; + + long TStatesPerDriveCycle = (long)((double)_machine.ULADevice.ClockSpeed / DriveClock); + StatesPerDriveTick = TStatesPerDriveCycle; + + } + + /// + /// Called every cycle by the emulated machine + /// Simulates the floppy drive and updates execution phase bits + /// + public void RunCPUCycle() + { + // decrement tick counter + TickCounter--; + + if (TickCounter > 0) + { + // not ready to emulate a floppy drive cycle yet + return; + } + + // time to emulate a floppy drive cycle + // reset the tick counter + TickCounter = StatesPerDriveTick; + + //RunDriveCycle(); + } + + /// + /// Called by reads to the main status register + /// Returns true if there is no delay + /// Returns false if read is to be deferred + /// + /// + private bool CheckTiming() + { + // get delta + long delta = CurrentCPUCycle - LastCPUCycle; + + if (StatusDelay >= delta) + { + // there is still delay remaining + StatusDelay -= delta; + LastCPUCycle = CurrentCPUCycle; + return false; + } + else + { + // no delay remaining + StatusDelay = 0; + LastCPUCycle = CurrentCPUCycle; + return true; + } + + } + + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs new file mode 100644 index 0000000000..45f35b412a --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs @@ -0,0 +1,251 @@ +using BizHawk.Common; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The NEC floppy disk controller (and floppy drive) found in the +3 + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + #region Devices + + /// + /// The emulated spectrum machine + /// + private SpectrumBase _machine; + + #endregion + + #region Construction & Initialization + + /// + /// Main constructor + /// + /// + public NECUPD765() + { + InitCommandList(); + } + + /// + /// Initialization routine + /// + public void Init(SpectrumBase machine) + { + _machine = machine; + FDD_Init(); + TimingInit(); + Reset(); + } + + /// + /// Resets the FDC + /// + public void Reset() + { + // setup main status + StatusMain = 0; + + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + SetBit(MSR_RQM, ref StatusMain); + + SetPhase_Idle(); + + //FDC_FLAG_RQM = true; + //ActiveDirection = CommandDirection.IN; + SRT = 6; + HUT = 16; + HLT = 2; + HLT_Counter = 0; + HUT_Counter = 0; + IndexPulseCounter = 0; + CMD_FLAG_MF = false; + + foreach (var d in DriveStates) + { + //d.SeekingTrack = d.CurrentTrack; + ////d.SeekCounter = 0; + //d.FLAG_SEEK_INTERRUPT = false; + //d.IntStatus = 0; + //d.SeekState = SeekSubState.Idle; + //d.SeekIntState = SeekIntStatus.Normal; + + } + + } + + /// + /// Setup the command structure + /// Each command represents one of the internal UPD765 commands + /// + private void InitCommandList() + { + CommandList = new List + { + // read data + new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, + // read id + new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 }, + // specify + new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, + Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, + // read diagnostic + new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, + // scan equal + new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // scan high or equal + new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // scan low or equal + new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // read deleted data + new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, + // write data + new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // write id + new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, + Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 }, + // write deleted data + new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // seek + new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, + Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, + // recalibrate (seek track00) + new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, + Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 }, + // sense interrupt status + new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, + Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 }, + // sense drive status + new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, + Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 }, + // version + new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, + Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, + // invalid + new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, + Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, + }; + } + + #endregion + + #region State Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("NEC-UPD765"); + + #region FDD + + ser.Sync("FDD_FLAG_MOTOR", ref FDD_FLAG_MOTOR); + + for (int i = 0; i < 4; i++) + { + ser.BeginSection("HITDrive_" + i); + DriveStates[i].SyncState(ser); + ser.EndSection(); + } + + ser.Sync("DiskDriveIndex", ref _diskDriveIndex); + // set active drive + DiskDriveIndex = _diskDriveIndex; + + #endregion + + #region Registers + + ser.Sync("_RegMain", ref StatusMain); + ser.Sync("_Reg0", ref Status0); + ser.Sync("_Reg1", ref Status1); + ser.Sync("_Reg2", ref Status2); + ser.Sync("_Reg3", ref Status3); + + #endregion + + #region Controller state + + ser.Sync("DriveLight", ref DriveLight); + ser.SyncEnum("ActivePhase", ref ActivePhase); + //ser.SyncEnum("ActiveDirection", ref ActiveDirection); + ser.SyncEnum("ActiveInterrupt", ref ActiveInterrupt); + ser.Sync("CommBuffer", ref CommBuffer, false); + ser.Sync("CommCounter", ref CommCounter); + ser.Sync("ResBuffer", ref ResBuffer, false); + ser.Sync("ExecBuffer", ref ExecBuffer, false); + ser.Sync("ExecCounter", ref ExecCounter); + ser.Sync("ExecLength", ref ExecLength); + ser.Sync("InterruptResultBuffer", ref InterruptResultBuffer, false); + ser.Sync("ResCounter", ref ResCounter); + ser.Sync("ResLength", ref ResLength); + ser.Sync("LastSectorDataWriteByte", ref LastSectorDataWriteByte); + ser.Sync("LastSectorDataReadByte", ref LastSectorDataReadByte); + ser.Sync("LastByteReceived", ref LastByteReceived); + + ser.Sync("_cmdIndex", ref _cmdIndex); + // resync the ActiveCommand + CMDIndex = _cmdIndex; + + ActiveCommandParams.SyncState(ser); + + ser.Sync("IndexPulseCounter", ref IndexPulseCounter); + //ser.SyncEnum("_activeStatus", ref _activeStatus); + //ser.SyncEnum("_statusRaised", ref _statusRaised); + + ser.Sync("CMD_FLAG_MT", ref CMD_FLAG_MT); + ser.Sync("CMD_FLAG_MF", ref CMD_FLAG_MF); + ser.Sync("CMD_FLAG_SK", ref CMD_FLAG_SK); + ser.Sync("SRT", ref SRT); + ser.Sync("HUT", ref HUT); + ser.Sync("HLT", ref HLT); + ser.Sync("ND", ref ND); + ser.Sync("SRT_Counter", ref SRT_Counter); + ser.Sync("HUT_Counter", ref HUT_Counter); + ser.Sync("HLT_Counter", ref HLT_Counter); + + ser.Sync("SectorDelayCounter", ref SectorDelayCounter); + ser.Sync("SectorID", ref SectorID); + + #endregion + + #region Timing + + ser.Sync("LastCPUCycle", ref LastCPUCycle); + ser.Sync("StatusDelay", ref StatusDelay); + ser.Sync("TickCounter", ref TickCounter); + ser.Sync("DriveCycleCounter", ref DriveCycleCounter); + + #endregion + + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs new file mode 100644 index 0000000000..7cbb83c13b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Static helper methods + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + /// + /// Returns the specified bit value from supplied byte + /// + /// + /// + /// + public static bool GetBit(int bitNumber, byte dataByte) + { + if (bitNumber < 0 || bitNumber > 7) + return false; + + BitArray bi = new BitArray(new byte[] { dataByte }); + + return bi[bitNumber]; + } + + /// + /// Sets the specified bit of the supplied byte to 1 + /// + /// + /// + public static void SetBit(int bitNumber, ref byte dataByte) + { + if (bitNumber < 0 || bitNumber > 7) + return; + + int db = (int)dataByte; + + db |= 1 << bitNumber; + + dataByte = (byte)db; + } + + /// + /// Sets the specified bit of the supplied byte to 0 + /// + /// + /// + public static void UnSetBit(int bitNumber, ref byte dataByte) + { + if (bitNumber < 0 || bitNumber > 7) + return; + + int db = (int)dataByte; + + db &= ~(1 << bitNumber); + + dataByte = (byte)db; + } + + /// + /// Returns a drive number (0-3) based on the first two bits of the supplied byte + /// + /// + /// + public static int GetUnitSelect(byte dataByte) + { + int driveNumber = dataByte & 0x03; + return driveNumber; + } + + /// + /// Sets the first two bits of a byte based on the supplied drive number (0-3) + /// + /// + /// + public static void SetUnitSelect(int driveNumber, ref byte dataByte) + { + switch (driveNumber) + { + case 0: + UnSetBit(SR0_US0, ref dataByte); + UnSetBit(SR0_US1, ref dataByte); + break; + case 1: + SetBit(SR0_US0, ref dataByte); + UnSetBit(SR0_US1, ref dataByte); + break; + case 2: + SetBit(SR0_US1, ref dataByte); + UnSetBit(SR0_US0, ref dataByte); + break; + case 3: + SetBit(SR0_US0, ref dataByte); + SetBit(SR0_US1, ref dataByte); + break; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index de2f5206d7..a227d8a64a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -8,8 +8,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public abstract partial class SpectrumBase { - // until +3 disk drive is emulated, we assume that incoming files are tape images - /// /// The tape or disk image(s) that are passed in from the main ZXSpectrum class /// @@ -18,12 +16,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Tape images /// - protected List tapeImages { get; set; } + public List tapeImages { get; set; } /// /// Disk images /// - protected List diskImages { get; set; } + public List diskImages { get; set; } /// /// The index of the currently 'loaded' tape image @@ -93,6 +91,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // load the media into the disk device diskMediaIndex = result; + + // fire osd message + Spectrum.OSD_DiskInserted(); + LoadDiskMedia(); } } @@ -105,7 +107,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { mediaImages = files; LoadAllMedia(); - Spectrum.OSD_TapeInit(); } /// @@ -116,17 +117,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum tapeImages = new List(); diskImages = new List(); - foreach (var m in mediaImages) + int cnt = 0; + foreach (var m in mediaImages) { - switch (IdentifyMedia(m)) + switch (IdentifyMedia(m)) { case SpectrumMediaType.Tape: tapeImages.Add(m); + Spectrum._tapeInfo.Add(Spectrum._gameInfo[cnt]); break; case SpectrumMediaType.Disk: diskImages.Add(m); + Spectrum._diskInfo.Add(Spectrum._gameInfo[cnt]); break; } + + cnt++; } if (tapeImages.Count > 0) @@ -149,7 +155,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// protected void LoadDiskMedia() { - throw new NotImplementedException("+3 disk drive device not yet implemented"); + if (this.GetType() != typeof(ZX128Plus3)) + { + Spectrum.CoreComm.ShowMessage("You are trying to load one of more disk images.\n\n Please select ZX Spectrum +3 emulation immediately and reboot the core"); + return; + } + else + { + //Spectrum.CoreComm.ShowMessage("You are attempting to load a disk into the +3 disk drive.\n\nThis DOES NOT currently work properly but IS under active development."); + } + + UPDDiskDevice.FDD_LoadDisk(diskImages[diskMediaIndex]); } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index bfcccda887..d2ef106fa1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -59,6 +59,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual DatacorderDevice TapeDevice { get; set; } + /// + /// The +3 built-in disk drive + /// + public virtual NECUPD765 UPDDiskDevice { get; set; } + /// /// Holds the currently selected joysticks /// @@ -67,7 +72,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Signs whether the disk motor is on or off /// - protected bool DiskMotorState; + //protected bool DiskMotorState; /// /// +3/2a printer port strobe @@ -349,6 +354,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ((AYChip)AYDevice as AYChip).PanningConfiguration = Spectrum.Settings.AYPanConfig; } + if (UPDDiskDevice != null) + { + UPDDiskDevice.SyncState(ser); + } + ser.Sync("tapeMediaIndex", ref tapeMediaIndex); TapeMediaIndex = tapeMediaIndex; 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 425d33f275..4568de663d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -223,9 +223,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - // bit 3 controls the disk motor (1=on, 0=off) - DiskMotorState = bits[3]; - // bit 4 is the printer port strobe PrinterPortStrobe = bits[4]; } 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 8226326b11..ec718c8343 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -36,6 +36,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InputRead = true; } + else if (UPDDiskDevice.ReadPort(port, ref result)) + { + return (byte)result; + } else { if (KeyboardDevice.ReadPort(port, ref result)) @@ -97,6 +101,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum AYDevice.WritePort(port, value); + UPDDiskDevice.WritePort(port, value); + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set if (port == 0x7ffd) { @@ -150,9 +156,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - // bit 3 controls the disk motor (1=on, 0=off) - DiskMotorState = bits[3]; - // bit 4 is the printer port strobe PrinterPortStrobe = bits[4]; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index ddf4def7c2..9cb24e8e5a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -44,6 +44,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); TapeDevice.Init(this); + UPDDiskDevice = new NECUPD765(); + UPDDiskDevice.Init(this); + InitializeMedia(files); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs new file mode 100644 index 0000000000..8cb77126a6 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs @@ -0,0 +1,167 @@ +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Logical object representing a standard +3 disk image + /// + public class CPCExtendedFloppyDisk : FloppyDisk + { + /// + /// The format type + /// + public override DiskType DiskFormatType => DiskType.CPCExtended; + + /// + /// Attempts to parse incoming disk data + /// + /// + /// + /// TRUE: disk parsed + /// FALSE: unable to parse disk + /// + public override bool ParseDisk(byte[] data) + { + // look for standard magic string + string ident = Encoding.ASCII.GetString(data, 0, 16); + + if (!ident.ToUpper().Contains("EXTENDED CPC DSK")) + { + // incorrect format + return false; + } + + // read the disk information block + DiskHeader.DiskIdent = ident; + DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14); + DiskHeader.NumberOfTracks = data[0x30]; + DiskHeader.NumberOfSides = data[0x31]; + DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskData = data; + int pos = 0x34; + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + DiskHeader.TrackSizes[i] = data[pos++] * 256; + } + + // move to first track information block + pos = 0x100; + + // parse each track + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + // check for unformatted track + if (DiskHeader.TrackSizes[i] == 0) + { + DiskTracks[i] = new Track(); + DiskTracks[i].Sectors = new Sector[0]; + continue; + } + + int p = pos; + DiskTracks[i] = new Track(); + + // track info block + DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12); + p += 16; + DiskTracks[i].TrackNumber = data[p++]; + DiskTracks[i].SideNumber = data[p++]; + DiskTracks[i].DataRate = data[p++]; + DiskTracks[i].RecordingMode = data[p++]; + DiskTracks[i].SectorSize = data[p++]; + DiskTracks[i].NumberOfSectors = data[p++]; + DiskTracks[i].GAP3Length = data[p++]; + DiskTracks[i].FillerByte = data[p++]; + + int dpos = pos + 0x100; + + // sector info list + DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors]; + for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++) + { + DiskTracks[i].Sectors[s] = new Sector(); + + DiskTracks[i].Sectors[s].TrackNumber = data[p++]; + DiskTracks[i].Sectors[s].SideNumber = data[p++]; + DiskTracks[i].Sectors[s].SectorID = data[p++]; + DiskTracks[i].Sectors[s].SectorSize = data[p++]; + DiskTracks[i].Sectors[s].Status1 = data[p++]; + DiskTracks[i].Sectors[s].Status2 = data[p++]; + DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p); + p += 2; + + // sector data - begins at 0x100 offset from the start of the track info block (in this case dpos) + DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength]; + + // copy the data + for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++) + { + DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b]; + } + + // check for multiple weak/random sectors stored + if (DiskTracks[i].Sectors[s].SectorSize <= 7) + { + // sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length + int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize; + + if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength) + { + // more data stored than sectorsize defines + // check for multiple weak/random copies + if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0) + { + DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true; + } + } + } + + // move dpos to the next sector data postion + dpos += DiskTracks[i].Sectors[s].ActualDataByteLength; + } + + // move to the next track info block + pos += DiskHeader.TrackSizes[i]; + } + + // run speedlock detector + SpeedlockDetection(); + + return true; + } + + /// + /// State serlialization + /// + /// + public override void SyncState(Serializer ser) + { + ser.BeginSection("Plus3FloppyDisk"); + + ser.Sync("CylinderCount", ref CylinderCount); + ser.Sync("SideCount", ref SideCount); + ser.Sync("BytesPerTrack", ref BytesPerTrack); + ser.Sync("WriteProtected", ref WriteProtected); + ser.SyncEnum("Protection", ref Protection); + + ser.Sync("DirtyData", ref DirtyData); + if (DirtyData) + { + + } + + // sync deterministic track and sector counters + ser.Sync(" _randomCounter", ref _randomCounter); + RandomCounter = _randomCounter; + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs new file mode 100644 index 0000000000..88d6b1a91f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs @@ -0,0 +1,169 @@ +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Logical object representing a standard +3 disk image + /// + public class CPCFloppyDisk : FloppyDisk + { + /// + /// The format type + /// + public override DiskType DiskFormatType => DiskType.CPC; + + /// + /// Attempts to parse incoming disk data + /// + /// + /// + /// TRUE: disk parsed + /// FALSE: unable to parse disk + /// + public override bool ParseDisk(byte[] data) + { + // look for standard magic string + string ident = Encoding.ASCII.GetString(data, 0, 16); + + if (!ident.ToUpper().Contains("MV - CPCEMU")) + { + // incorrect format + return false; + } + + // read the disk information block + DiskHeader.DiskIdent = ident; + DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14); + DiskHeader.NumberOfTracks = data[0x30]; + DiskHeader.NumberOfSides = data[0x31]; + DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskData = data; + int pos = 0x32; + + // standard CPC format all track sizes are the same in the image + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + DiskHeader.TrackSizes[i] = MediaConverter.GetWordValue(data, pos); + } + + // move to first track information block + pos = 0x100; + + // parse each track + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + // check for unformatted track + if (DiskHeader.TrackSizes[i] == 0) + { + DiskTracks[i] = new Track(); + DiskTracks[i].Sectors = new Sector[0]; + continue; + } + + int p = pos; + DiskTracks[i] = new Track(); + + // track info block + DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12); + p += 16; + DiskTracks[i].TrackNumber = data[p++]; + DiskTracks[i].SideNumber = data[p++]; + p += 2; + DiskTracks[i].SectorSize = data[p++]; + DiskTracks[i].NumberOfSectors = data[p++]; + DiskTracks[i].GAP3Length = data[p++]; + DiskTracks[i].FillerByte = data[p++]; + + int dpos = pos + 0x100; + + // sector info list + DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors]; + for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++) + { + DiskTracks[i].Sectors[s] = new Sector(); + + DiskTracks[i].Sectors[s].TrackNumber = data[p++]; + DiskTracks[i].Sectors[s].SideNumber = data[p++]; + DiskTracks[i].Sectors[s].SectorID = data[p++]; + DiskTracks[i].Sectors[s].SectorSize = data[p++]; + DiskTracks[i].Sectors[s].Status1 = data[p++]; + DiskTracks[i].Sectors[s].Status2 = data[p++]; + DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p); + p += 2; + + // actualdatabytelength value is calculated now + if (DiskTracks[i].Sectors[s].SectorSize == 0) + { + // no sectorsize specified - DTL will be used at runtime + DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i]; + } + else if (DiskTracks[i].Sectors[s].SectorSize > 6) + { + // invalid - wrap around to 0 + DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i]; + } + else if (DiskTracks[i].Sectors[s].SectorSize == 6) + { + // only 0x1800 bytes are stored + DiskTracks[i].Sectors[s].ActualDataByteLength = 0x1800; + } + else + { + // valid sector size for this format + DiskTracks[i].Sectors[s].ActualDataByteLength = 0x80 << DiskTracks[i].Sectors[s].SectorSize; + } + + // sector data - begins at 0x100 offset from the start of the track info block (in this case dpos) + DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength]; + + // copy the data + for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++) + { + DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b]; + } + + // move dpos to the next sector data postion + dpos += DiskTracks[i].Sectors[s].ActualDataByteLength; + } + + // move to the next track info block + pos += DiskHeader.TrackSizes[i]; + } + + // run speedlock detector + SpeedlockDetection(); + + return true; + } + + /// + /// State serlialization + /// + /// + public override void SyncState(Serializer ser) + { + ser.BeginSection("Plus3FloppyDisk"); + + ser.Sync("CylinderCount", ref CylinderCount); + ser.Sync("SideCount", ref SideCount); + ser.Sync("BytesPerTrack", ref BytesPerTrack); + ser.Sync("WriteProtected", ref WriteProtected); + ser.SyncEnum("Protection", ref Protection); + + ser.Sync("DirtyData", ref DirtyData); + if (DirtyData) + { + + } + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs new file mode 100644 index 0000000000..6c70fa8f4a --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Object that represents logical disk media once imported + /// + public class DiskImage + { + #region File Header Info + + /// + /// The identified format + /// (in the case of .dsk files, this is probably "EXTENDED CPC DSK") + /// + public string Header_SystemIdent { get; set; } + + /// + /// The software originally used to create thsi image + /// + public string Header_CreatorSoftware { get; set; } + + /// + /// The number of tracks in the disk image (header specified) + /// + public int Header_TrackCount { get; set; } + + /// + /// The number of sides on the disk (header specified) + /// + public int Header_SideCount { get; set; } + + /// + /// Size of a track including 0x100 byte track information block + /// (in a .dsk all tracks will be the same size) + /// + public int Header_TrackSize { get; set; } + + #endregion + + public List Tracks = new List(); + + /// + /// Reads an entire sector + /// + /// + /// + /// + /// + public CHRN ReadSector(int side, int track, int sector) + { + var t = Tracks.Where(a => a.TrackNumber == track).FirstOrDefault(); + + if (t == null) + return null; + + var s = t.Sectors.Where(a => a.SectorID == sector).FirstOrDefault(); + + if (s == null) + return null; + + var chrn = s.GetCHRN(); + chrn.DataBytes = s.SectorData; + + return chrn; + } + + /// + /// Reads data from a sector based on offset and length + /// + /// + /// + /// + /// + public CHRN ReadSector(int side, int track, int sector, int offset, int length) + { + var result = ReadSector(side, track, sector); + + if (result == null) + return null; + + if (offset > result.DataBytes.Length) + return null; + + if (length + offset > result.DataBytes.Length) + return null; + + // data is safe to read + var data = result.DataBytes.Skip(offset).Take(length).ToArray(); + + result.DataBytes = data; + + return result; + } + + /// + /// Reads an entire track + /// + /// + /// + /// + public Track ReadTrack(int side, int track) + { + return Tracks.Where(a => a.TrackNumber == track).FirstOrDefault(); + } + + /// + /// Reads a track based on offset and length + /// + /// + /// + /// + /// + /// + public Track ReadTrack(int side, int track, int offset, int length) + { + var t = ReadTrack(side, track); + + if (t == null) + return null; + + if (offset >= t.AllSectorBytes.Count() || offset + length >= t.AllSectorBytes.Count()) + return null; + + var data = t.AllSectorBytes.Skip(offset).Take(length).ToArray(); + + t.ResultBytes = data; + + return t; + } + + /// + /// Returns the number of tracks preset on the disk + /// + /// + public int GetTrackCount() + { + return Tracks.Count(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs new file mode 100644 index 0000000000..fe185248ee --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The different disk formats ZXHawk currently supports + /// + public enum DiskType + { + /// + /// Standard CPCEMU disk format (used in the built-in +3 disk drive) + /// + CPC, + + /// + /// Extended CPCEMU disk format (used in the built-in +3 disk drive) + /// + CPCExtended + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs new file mode 100644 index 0000000000..47a7d6b1ff --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs @@ -0,0 +1,511 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for DSK format serializaton + /// File format info taken from: http://www.cpcwiki.eu/index.php/Format:DSK_disk_image_file_format + /// + public class DskConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.DSK; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to read data + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write data + /// + public override bool IsWriter { get { return false; } } + + /// + /// The disk image that we will be populating + /// + private DiskImage _diskImage = new DiskImage(); + + + /// + /// The current position whilst parsing the incoming data + /// + private int _position = 0; + + #region Construction + + private IFDDHost _diskDrive; + + public DskConverter(IFDDHost diskDrive) + { + _diskDrive = diskDrive; + } + + #endregion + + /// + /// Returns TRUE if dsk header is detected + /// + /// + public override bool CheckType(byte[] data) + { + // check whether this is a valid dsk format file by looking at the identifier in the header + // (first 16 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 16); + + if (!ident.ToUpper().Contains("EXTENDED CPC DSK") && !ident.ToUpper().Contains("MV - CPCEMU")) + { + // this is not a valid DSK format file + return false; + } + else + { + return true; + } + } + + /// + /// Read method + /// + /// + public override void Read(byte[] data) + { + // populate dsk header info + string ident = Encoding.ASCII.GetString(data, 0, 34); + _diskImage.Header_SystemIdent = ident; + + if (ident.ToUpper().Contains("EXTENDED CPC DSK")) + ReadExtendedDsk(data); + else if (ident.ToUpper().Contains("MV - CPCEMU")) + ReadDsk(data); + + // load the disk 'into' the controller + //_diskDrive.Disk = _diskImage; + } + + /// + /// Parses the extended disk format + /// The extended DSK image is a file designed to describe copy-protected floppy disk software + /// + /// + private void ReadExtendedDsk(byte[] data) + { + /* DISK INFORMATION BLOCK + + offset description bytes + 00 - 21 "EXTENDED CPC DSK File\r\nDisk-Info\r\n" 34 + 22 - 2f name of creator (utility/emulator) 14 + 30 number of tracks 1 + 31 number of sides 1 + 32 - 33 unused 2 + 34 - xx track size table number of tracks*number of sides + */ + + // name of creator + _position = 0x22; + _diskImage.Header_CreatorSoftware = Encoding.ASCII.GetString(data, _position, 14); + + // number of tracks + _position = 0x30; + _diskImage.Header_TrackCount = data[_position++]; + + // number of sides + _diskImage.Header_SideCount = data[_position++]; + + _position += 2; + + /* TRACK OFFSET TABLE + + offset description bytes + 01 high byte of track 0 length (equivalent to track length/256) 1 + ... ... ... + + track lengths are stored in the same order as the tracks in the image e.g. In the case of a double sided disk: Track 0 side 0, Track 0 side 1, Track 1 side 0 etc... + A size of "0" indicates an unformatted track. In this case there is no data, and no track information block for this track in the image file! + Actual length of track data = (high byte of track length) * 256 + Track length includes the size of the TRACK INFORMATION BLOCK (256 bytes) + The location of a Track Information Block for a chosen track is found by summing the sizes of all tracks up to the chosen track plus the size of the Disc Information Block (&100 bytes). The first track is at offset &100 in the disc image. + */ + + // iterate through the track size table and do an initial setup of all the tracks + // (this is just new tracks with the offset value saved) + int trkPos = 0x100; + for (int i = 0; i < _diskImage.Header_TrackCount * _diskImage.Header_SideCount; i++) + { + Track track = new Track(); + + // calc actual track length (including track info block) + int ts = data[_position++]; + int tLen = ts * 256; + + track.TrackStartOffset = trkPos; + track.TrackByteLength = tLen; + _diskImage.Tracks.Add(track); + trkPos += tLen; + } + + // iterate through each newly created track and parse it based on the offset + List tracks = new List(); + for (int tr = 0; tr < _diskImage.Tracks.Count(); tr++) + { + // get the track we are interested in + var t = _diskImage.Tracks[tr]; + + // validity check + if (t.TrackByteLength == 0) + { + // unformatted track that is not present in the disk image + Track trU = new Track(); + trU.TrackNumber = (byte)tr; + trU.TrackByteLength = 0; + tracks.Add(trU); + continue; + } + + Track track = new Track(); + track.TrackStartOffset = t.TrackStartOffset; + track.TrackByteLength = t.TrackByteLength; + + // get data for this track block + byte[] tData = data.Skip(t.TrackStartOffset).Take(t.TrackByteLength).ToArray(); + + // start of data block + int pos = 0; + + // seek past track info ident + pos += 0x10; + + /* TRACK INFORMATION BLOCK + + offset description bytes + 00 - 0b "Track-Info\r\n" 12 + 0c - 0f unused 4 + 10 track number 1 + 11 side number 1 + 12 data rate 1 + 13 recording mode 1 + 14 sector size 1 + 15 number of sectors 1 + 16 GAP#3 length 1 + 17 filler byte 1 + 18 - xx Sector Information List xx + */ + + /* Format extensions + ----------------- + + Date rate description + 0 Unknown. + 1 Single or double density + 2 High Density + 3 Extended density + + Data rate defines the rate at which data was written to the track. This value applies to the entire track. + + Recording mode description + 0 Unknown. + 1 FM + 2 MFM + + Recording mode is used to define how the data was written. It defines the encoding used to write the data to the disc and the structure of the data on the disc including the layout of the sectors. + This value applies to the entire track + + The NEC765 floppy disc controller is supplied with a single clock. When reading from and writing to a disc using the NEC765 you can choose + FM or MFM recording modes. Use of these modes and the clock into the NEC765 define the final rate at which the data is written to the disc. + When FM recording mode is used, data is read from or written to at a rate which is double that of when MFM is used. + The time for each bit will be twice the time for MFM + + NEC765 Clock FM/MFM Actual rate + 4Mhz FM 4us per bit + 4Mhz MFM 2us per bit + */ + + // track number + track.TrackNumber = tData[pos++]; + // side number + track.SideNumber = tData[pos++]; + // data rate + track.DataRate = tData[pos++]; + // recording mode + track.RecordingMode = tData[pos++]; + // sector size + track.SectorSize = tData[pos++]; + // number of sectors + track.SectorCount = tData[pos++]; + // GAP#3 Length + track.GAP3Length = tData[pos++]; + // filler byte + track.FillerByte = tData[pos++]; + + /* SECTOR INFORMATION LIST + + offset description bytes + 00 track (equivalent to C parameter in NEC765 commands) 1 + 01 side (equivalent to H parameter in NEC765 commands) 1 + 02 sector ID (equivalent to R parameter in NEC765 commands) 1 + 03 sector size (equivalent to N parameter in NEC765 commands) 1 + 04 FDC status register 1 (equivalent to NEC765 ST1 status register) 1 + 05 FDC status register 2 (equivalent to NEC765 ST2 status register) 1 + 06 - 07 actual data length in bytes 2 + */ + + // parse sector information list + for (int s = 0; s < track.SectorCount; s++) + { + Sector sector = new Sector(); + sector.Track = tData[pos++]; + sector.Side = tData[pos++]; + sector.SectorID = tData[pos++]; + sector.SectorSize = tData[pos++]; + sector.ST1 = tData[pos++]; + sector.ST2 = tData[pos++]; + sector.TotalDataLength = GetWordValue(tData, pos); + pos += 2; + + // get the sector data - lives directly after the 256byte track info block + int secDataPos = 0x100; + + /* Format extension + ---------------_ + It has been found that many protections using 8K Sectors (N="6") do store more than &1800 bytes of useable data. + It was thought that &1800 was the maximum useable limit, but this has proved wrong. So you should support 8K of data to ensure + this data is read correctly. The size of the sector will be reported in the SECTOR INFORMATION LIST as described above. + For sector size N="7" the full 16K will be stored. It is assumed that sector sizes are defined as 3 bits only, + so that a sector size of N="8" is equivalent to N="0". + */ + int bps = 0x80 << sector.SectorSize; + + if (sector.SectorSize == 8 || sector.SectorSize == 0) + { + // no sector data + bps = 0x80 << 0; + //continue; + } + + + /* Format extension + ---------------- + Storing Multiple Versions of Weak/Random Sectors. + Some copy protections have what is described as 'weak/random' data. Each time the sector is read one or more bytes will change, + the value may be random between consecutive reads of the same sector. + To support these formats the following extension has been proposed. + Where a sector has weak/random data, there are multiple copies stored. The actual sector size field in the SECTOR INFORMATION LIST + describes the size of all the copies. To determine if a sector has multiple copies then compare the actual sector size field to the + size defined by the N parameter. For multiple copies the actual sector size field will have a value which is a multiple of the size + defined by the N parameter. The emulator should then choose which copy of the sector it should return on each read. + */ + + int sizeOfAllCopies = sector.TotalDataLength; + + if (sizeOfAllCopies % bps == 0) + { + // sector size is the same as total data length (indicating 1 sector data) + // or a factor of the total data length (indicating multiple copies) + for (int sd = 0; sd < sizeOfAllCopies / bps; sd++) + { + byte[] sData = new byte[bps]; + for (int c = 0; c < bps; c++) + { + sData[c] = data[secDataPos++]; + } + + sector.AddSectorData(sd, sData); + } + } + else + { + // assume that there is one sector copy, but the total data length does not match + if (sector.TotalDataLength <= sizeOfAllCopies) + { + byte[] sData = new byte[bps]; + for (int c = 0; c < bps; c++) + { + sData[c] = data[secDataPos++]; + } + + sector.AddSectorData(0, sData); + } + else + { + byte[] sData = new byte[sizeOfAllCopies]; + for (int c = 0; c < sizeOfAllCopies; c++) + { + sData[c] = data[secDataPos++]; + } + + sector.AddSectorData(0, sData); + } + } + + track.Sectors.Add(sector); + + } + + // add track to working list + tracks.Add(track); + } + + // replace the tracks collection + _diskImage.Tracks = tracks; + } + + + /// + /// Parses the standard disk image format + /// + /// + private void ReadDsk(byte[] data) + { + /* DISK INFORMATION BLOCK + + offset description bytes + 00-21 "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" 34 + 22-2f name of creator 14 + 30 number of tracks 1 + 31 number of sides 1 + 32-33 size of a track (little endian; low byte followed by high byte) 2 + 34-ff not used (0) 204 + */ + + // name of creator + _position = 0x22; + _diskImage.Header_CreatorSoftware = Encoding.ASCII.GetString(data, _position, 14); + + // number of tracks + _position = 0x30; + _diskImage.Header_TrackCount = data[_position++]; + + // number of sides + _diskImage.Header_SideCount = data[_position++]; + + // size of a track (little endian) + _diskImage.Header_TrackSize = GetWordValue(data, _position); + + // 34-ff not used + + // move to start of first track info block + _position = 0x100; + + int tmpPos = _position; + + // iterate through each track + for (int t = 0; t < _diskImage.Header_TrackCount; t++) + { + /* TRACK INFORMATION BLOCK + + offset description bytes + 00 - 0b "Track-Info\r\n" 12 + 0c - 0f unused 4 + 10 track number 1 + 11 side number 1 + 12 - 13 unused 2 + 14 sector size 1 + 15 number of sectors 1 + 16 GAP#3 length 1 + 17 filler byte 1 + 18 - xx Sector Information List xx + */ + + Track track = new Track(); + _position += 0x10; + + track.TrackNumber = data[_position++]; + track.SideNumber = data[_position++]; + _position += 2; + + /* + BPS (bytes per sector) + 0 = 128 0x80 + 1 = 256 0x100 + 2 = 512 0x200 + 3 = 1024 0x400 + 4 = 2048 0x800 + 5 = 4096 0x1000 + 6 = 8192 0x2000 + */ + + track.SectorSize = data[_position++]; + + track.SectorCount = data[_position++]; + track.GAP3Length = data[_position++]; + track.FillerByte = data[_position++]; + + /* SECTOR INFORMATION LIST + + offset description bytes + 00 track (equivalent to C parameter in NEC765 commands) 1 + 01 side (equivalent to H parameter in NEC765 commands) 1 + 02 sector ID (equivalent to R parameter in NEC765 commands) 1 + 03 sector size (equivalent to N parameter in NEC765 commands) 1 + 04 FDC status register 1 (equivalent to NEC765 ST1 status register) 1 + 05 FDC status register 2 (equivalent to NEC765 ST2 status register) 1 + 06 - 07 notused (0) 2 + */ + + // get the sector info first + for (int s = 0; s < track.SectorCount; s++) + { + Sector sector = new Sector(); + sector.Track = data[_position++]; + sector.Side = data[_position++]; + sector.SectorID = data[_position++]; + sector.SectorSize = data[_position++]; + sector.ST1 = data[_position++]; + sector.ST2 = data[_position++]; + track.Sectors.Add(sector); + _position += 2; + } + + // now process the sector data - always offset 0x100 from the start of the track information block + tmpPos += 0x100; + _position = tmpPos; + + // sectorsize in the track information list implies the boundaries of the sector data + // sectorsize in the sector info list gives us the actual size of each sector to read + for (int s = 0; s < track.SectorCount; s++) + { + var sec = track.Sectors[s]; + + List tmpData = new List(); + + // read the sector data bytes + int bps = 0x80 << sec.SectorSize; + if (bps > 0x1800) + bps = 0x1800; + + for (int d = 0; d < bps; d++) + { + tmpData.Add(data[_position + d]); + } + + sec.AddSectorData(0, tmpData.ToArray()); + track.Sectors[s] = sec; + + // sector data parsed - move to next sector data block + int trackBps = 0x80 << track.SectorSize; + if (trackBps > 0x1800) + trackBps = 0x1800; + + _position += track.SectorSize; + } + + // add the track to the disk image + _diskImage.Tracks.Add(track); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs new file mode 100644 index 0000000000..65a46b50b7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -0,0 +1,396 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This interface defines a logical floppy disk + /// + public abstract class FloppyDisk + { + /// + /// The disk format type + /// + public abstract DiskType DiskFormatType { get; } + + /// + /// Disk information header + /// + public Header DiskHeader = new Header(); + + /// + /// Track array + /// + public Track[] DiskTracks = null; + + /// + /// No. of tracks per side + /// + public int CylinderCount; + + /// + /// The number of physical sides + /// + public int SideCount; + + /// + /// The number of bytes per track + /// + public int BytesPerTrack; + + /// + /// The write-protect tab on the disk + /// + public bool WriteProtected; + + /// + /// The detected protection scheme (if any) + /// + public ProtectionType Protection; + + /// + /// The actual disk image data + /// + public byte[] DiskData; + + /// + /// If TRUE then data on the disk has changed (been written to) + /// This will be used to determine whether the disk data needs to be included + /// in any SyncState operations + /// + protected bool DirtyData = false; + + /// + /// Used to deterministically choose a 'random' sector when dealing with weak reads + /// + public int RandomCounter + { + get { return _randomCounter; } + set + { + _randomCounter = value; + + foreach (var trk in DiskTracks) + { + foreach (var sec in trk.Sectors) + { + sec.RandSecCounter = _randomCounter; + } + } + } + } + protected int _randomCounter; + + + /// + /// Attempts to parse incoming disk data + /// + /// + /// + /// TRUE: disk parsed + /// FALSE: unable to parse disk + /// + public virtual bool ParseDisk(byte[] diskData) + { + // default result + // override in inheriting class + return false; + } + + /// + /// Should be run at the end of the ParseDisk process + /// If speedlock is detected the flag is set in the disk image + /// + /// + protected virtual void SpeedlockDetection() + { + /* + Based on the information here: http://simonowen.com/samdisk/plus3/ + */ + + if (DiskTracks.Length == 0) + return; + + // check for speedlock copyright notice + string ident = Encoding.ASCII.GetString(DiskData, 0x100, 0x1400); + if (!ident.ToUpper().Contains("SPEEDLOCK")) + { + // speedlock not found + return; + } + + // get cylinder 0 + var cyl = DiskTracks[0]; + + // get sector with ID=2 + var sec = cyl.Sectors.Where(a => a.SectorID == 2).FirstOrDefault(); + + if (sec == null) + return; + + // check for already multiple weak copies + if (sec.ContainsMultipleWeakSectors || sec.SectorData.Length != 0x80 << sec.SectorSize) + return; + + // check for invalid crcs in sector 2 + if (sec.Status1.Bit(5) || sec.Status2.Bit(5)) + { + Protection = ProtectionType.Speedlock; + } + else + { + return; + } + + // we are going to create a total of 5 weak sector copies + // keeping the original copy + byte[] origData = sec.SectorData.ToArray(); + List data = new List(); + //Random rnd = new Random(); + + for (int i = 0; i < 6; i++) + { + for (int s = 0; s < origData.Length; s++) + { + if (i == 0) + { + data.Add(origData[s]); + continue; + } + + // deterministic 'random' implementation + int n = origData[s] + i + 1; + if (n > 0xff) + n = n - 0xff; + else if (n < 0) + n = 0xff + n; + + byte nByte = (byte)n; + + if (s < 336) + { + // non weak data + data.Add(origData[s]); + } + else if (s < 511) + { + // weak data + data.Add(nByte); + } + else if (s == 511) + { + // final sector byte + data.Add(nByte); + } + else + { + // speedlock sector should not be more than 512 bytes + // but in case it is just do non weak + data.Add(origData[i]); + } + } + } + + // commit the sector data + sec.SectorData = data.ToArray(); + sec.ContainsMultipleWeakSectors = true; + sec.ActualDataByteLength = data.Count(); + + } + + /// + /// Returns the track count for the disk + /// + /// + public virtual int GetTrackCount() + { + return DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; + } + + /// + /// Reads the current sector ID info + /// + /// + /// + public virtual CHRN ReadID(byte trackIndex, byte side, int sectorIndex) + { + if (side != 0) + return null; + + if (DiskTracks.Length <= trackIndex || trackIndex < 0) + { + // invalid track - wrap around + trackIndex = 0; + } + + var track = DiskTracks[trackIndex]; + + if (track.NumberOfSectors <= sectorIndex) + { + // invalid sector - wrap around + sectorIndex = 0; + } + + var sector = track.Sectors[sectorIndex]; + + CHRN chrn = new CHRN(); + + chrn.C = sector.TrackNumber; + chrn.H = sector.SideNumber; + chrn.R = sector.SectorID; + + // wrap around for N > 7 + if (sector.SectorSize > 7) + { + chrn.N = (byte)(sector.SectorSize - 7); + } + else if (sector.SectorSize < 0) + { + chrn.N = 0; + } + else + { + chrn.N = sector.SectorSize; + } + + chrn.Flag1 = (byte)(sector.Status1 & 0x25); + chrn.Flag2 = (byte)(sector.Status2 & 0x61); + + chrn.DataBytes = sector.ActualData; + + return chrn; + } + + /// + /// State serialization routines + /// + /// + public abstract void SyncState(Serializer ser); + + + public class Header + { + public string DiskIdent { get; set; } + public string DiskCreatorString { get; set; } + public byte NumberOfTracks { get; set; } + public byte NumberOfSides { get; set; } + public int[] TrackSizes { get; set; } + } + + public class Track + { + public string TrackIdent { get; set; } + public byte TrackNumber { get; set; } + public byte SideNumber { get; set; } + public byte DataRate { get; set; } + public byte RecordingMode { get; set; } + public byte SectorSize { get; set; } + public byte NumberOfSectors { get; set; } + public byte GAP3Length { get; set; } + public byte FillerByte { get; set; } + public Sector[] Sectors { get; set; } + + /// + /// Presents a contiguous byte array of all sector data for this track + /// (including any multiple weak/random data) + /// + public byte[] TrackSectorData + { + get + { + List list = new List(); + + foreach (var sec in Sectors) + { + list.AddRange(sec.ActualData); + } + + return list.ToArray(); + } + } + } + + public class Sector + { + public byte TrackNumber { get; set; } + public byte SideNumber { get; set; } + public byte SectorID { get; set; } + public byte SectorSize { get; set; } + public byte Status1 { get; set; } + public byte Status2 { get; set; } + public int ActualDataByteLength { get; set; } + public byte[] SectorData { get; set; } + public bool ContainsMultipleWeakSectors { get; set; } + + public int RandSecCounter = 0; + + public byte[] ActualData + { + get + { + if (!ContainsMultipleWeakSectors) + { + // check whether filler bytes are needed + int size = 0x80 << SectorSize; + if (size > ActualDataByteLength) + { + List l = new List(); + l.AddRange(SectorData); + for (int i = 0; i < size - ActualDataByteLength; i++) + { + l.Add(0xe5); + } + + return l.ToArray(); + } + else + { + return SectorData; + } + } + else + { + int copies = ActualDataByteLength / (0x80 << SectorSize); + Random rnd = new Random(); + int r = rnd.Next(0, copies - 1); + int step = r * (0x80 << SectorSize); + byte[] res = new byte[(0x80 << SectorSize)]; + Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); + return res; + } + } + } + + public CHRN SectorIDInfo + { + get + { + return new CHRN + { + C = TrackNumber, + H = SideNumber, + R = SectorID, + N = SectorSize, + Flag1 = Status1, + Flag2 = Status2, + }; + } + } + } + } + + /// + /// Defines the type of speedlock detection found + /// + public enum ProtectionType + { + None, + Speedlock + } + +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs new file mode 100644 index 0000000000..208b674c4c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs @@ -0,0 +1,228 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a disk sector + /// + public class Sector + { + /// + /// Used when the FDD returns the sector + /// False means the sector does not exist + /// + public bool IsValid = true; + + /// + /// track (equivalent to C parameter in NEC765 commands) + /// + private byte _track; + public byte Track + { + get { return _track; } + set { _track = value; } + } + + /// + /// side (equivalent to H parameter in NEC765 commands) + /// + private byte _side; + public byte Side + { + get { return _side; } + set { _side = value; } + } + + /// + /// sector ID (equivalent to R parameter in NEC765 commands) + /// + private byte _sectorID; + public byte SectorID + { + get { return _sectorID; } + set { _sectorID = value; } + } + + /// + /// sector size (equivalent to N parameter in NEC765 commands) + /// + private byte _sectorSize; + public byte SectorSize + { + get { return _sectorSize; } + set { _sectorSize = value; } + } + + /// + /// FDC status register 1 (equivalent to NEC765 ST1 status register) + /// + private byte _sT1; + public byte ST1 + { + get { return _sT1; } + set { _sT1 = value; } + } + + /// + /// FDC status register 2 (equivalent to NEC765 ST2 status register) + /// + private byte _sT2; + public byte ST2 + { + get { return _sT2; } + set { _sT2 = value; } + } + + /// + /// Actual total data length in bytes + /// If this is greater than the sector size specified by N, then multiple copies + /// of the sector data are present (representing weak/random copy protection data) + /// + private int _totalDataLength; + public int TotalDataLength + { + get { return _totalDataLength; } + set { _totalDataLength = value; } + } + + /// + /// The sector data entry point + /// If multiple sector data is found this should return a randomly chosen data for this sector + /// + public byte[] SectorData + { + get + { + // number of sector data copies found + int count = _sectorDatas.Count(); + + if (count <= 0) + { + // no sectors present + return new byte[0]; + } + + if (count == 1) + { + // only one copy of data found - return this + return _sectorDatas.First(); + } + + if (count > 1) + { + // there is more than one copy of sector data stored + // return at random to simulate weak/random copy protection sectors + Random rnd = new Random(); + int pnt = rnd.Next(0, count - 1); + return _sectorDatas[pnt]; + } + + // probably shouldnt ever get this far + return null; + } + } + + /// + /// Adds sector data based on the copy index + /// + /// + public void AddSectorData(int position, byte[] data) + { + if (_sectorDatas.ElementAtOrDefault(position) == null) + { + // create byte arrays for uninstantiated indexes + for (int i = 0; i < position + 1; i++) + { + if (_sectorDatas.ElementAtOrDefault(i) == null) + { + _sectorDatas.Add(new byte[0]); + } + } + } + + _sectorDatas[position] = data; + } + + /// + /// Internal storage for sector data + /// + private List _sectorDatas = new List(); + + + /// + /// Returns a CHRN object for this sector + /// + /// + public CHRN GetCHRN() + { + return new CHRN + { + C = Track, + H = Side, + R = SectorID, + N = SectorSize, + Flag1 = ST1, + Flag2 = ST2, + DataBytes = SectorData + }; + } + + + + /// + /// State serialization + /// Should only be called for the ActiveSector object in the floppy drive + /// + /// + public void SyncState(Serializer ser) + { + ser.BeginSection("ActiveSector"); + + ser.Sync("IsValid", ref IsValid); + ser.Sync("_track", ref _track); + ser.Sync("_side", ref _side); + ser.Sync("_sectorID", ref _sectorID); + ser.Sync("_sectorSize", ref _sectorSize); + ser.Sync("_sT1", ref _sT1); + ser.Sync("_sT2", ref _sT2); + ser.Sync("_totalDataLength", ref _totalDataLength); + + if (ser.IsReader) + { + ser.Sync("SecCopySize", ref SecCopySize); + + byte[][] sds = new byte[SecCopySize][]; + + for (int i = 0; i < SecCopySize; i++) + { + ser.Sync("sec" + i, ref sds[i], false); + } + + _sectorDatas = sds.ToList(); + } + + if (ser.IsWriter) + { + SecCopySize = _sectorDatas.Count(); + ser.Sync("SecCopySize", ref SecCopySize); + + byte[][] sds = _sectorDatas.ToArray(); + + for (int i = 0; i < SecCopySize; i++) + { + ser.Sync("sec" + i, ref sds[i], false); + } + } + + ser.EndSection(); + } + + // Sector array size (for state serialization) + private int SecCopySize; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs new file mode 100644 index 0000000000..d14b010170 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a disk track + /// + public class Track + { + /// + /// The track number + /// + public byte TrackNumber { get; set; } + /// + /// The side number + /// + public byte SideNumber { get; set; } + /// + /// Data rate defines the rate at which data was written to the track. This value applies to the entire track + /// + public byte DataRate { get; set; } + /// + /// Recording mode is used to define how the data was written. It defines the encoding used to write the data to the disc and the structure of the data on the + /// disc including the layout of the sectors. This value applies to the entire track. + /// + public byte RecordingMode { get; set; } + /// + /// Used to calculate the location of each sector's data. Therefore, The data allocated for each sector must be the same + /// + public int SectorSize { get; set; } + /// + /// Identifies the number of valid entries in the sector information list + /// + public byte SectorCount { get; set; } + /// + /// The length of GAP3 data + /// + public byte GAP3Length { get; set; } + /// + /// Byte used as filler + /// + public byte FillerByte { get; set; } + + /// + /// List containing all the sectors for this track + /// + public List Sectors = new List(); + + /// + /// Defines the offset in the disk image file at which the track information block starts + /// + public int TrackStartOffset { get; set; } + + /// + /// The actual length of the track (including info block) in the disk image file + /// + public int TrackByteLength { get; set; } + + /// + /// Returns all sector data for this track in one byte array + /// + public byte[] AllSectorBytes + { + get + { + int byteSize = 0; + foreach (var s in Sectors) + { + byteSize += s.SectorData.Count(); + } + + byte[] all = new byte[byteSize]; + + int _pos = 0; + + foreach (var s in Sectors) + { + foreach (var d in s.SectorData) + { + all[_pos] = d; + _pos++; + } + } + + return all; + } + } + + public byte[] ResultBytes { get; set; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs new file mode 100644 index 0000000000..f3e56b38fd --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Abtract class that represents all Media Serializers + /// + public abstract class MediaConverter + { + /// + /// The type of serializer + /// + public abstract MediaConverterType FormatType { get; } + + /// + /// Signs whether this class can be used to read the data format + /// + public virtual bool IsReader + { + get + { + return false; + } + } + + /// + /// Signs whether this class can be used to write the data format + /// + public virtual bool IsWriter + { + get + { + return false; + } + } + + /// + /// Serialization method + /// + /// + public virtual void Read(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Read operation is not implemented for this converter"); + } + + /// + /// DeSerialization method + /// + /// + public virtual void Write(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Write operation is not implemented for this converter"); + } + + /// + /// Serializer does a quick check, returns TRUE if file is detected as this type + /// + /// + public virtual bool CheckType(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Check type operation is not implemented for this converter"); + } + + #region Static Tools + + /// + /// Converts an int32 value into a byte array + /// + /// + /// + public static byte[] GetBytes(int value) + { + byte[] buf = new byte[4]; + buf[0] = (byte)value; + buf[1] = (byte)(value >> 8); + buf[2] = (byte)(value >> 16); + buf[3] = (byte)(value >> 24); + return buf; + } + + /// + /// Returns an int32 from a byte array based on offset + /// + /// + /// + /// + public static int GetInt32(byte[] buf, int offsetIndex) + { + return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24; + } + + /// + /// Returns an uint16 from a byte array based on offset + /// + /// + /// + /// + public static ushort GetWordValue(byte[] buf, int offsetIndex) + { + return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8); + } + + /// + /// Updates a byte array with a uint16 value based on offset + /// + /// + /// + /// + public static void SetWordValue(byte[] buf, int offsetIndex, ushort value) + { + buf[offsetIndex] = (byte)value; + buf[offsetIndex + 1] = (byte)(value >> 8); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializationType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs similarity index 84% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializationType.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs index def321b245..d7b456919f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializationType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs @@ -9,10 +9,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Represents the different types of media serializer avaiable /// - public enum MediaSerializationType + public enum MediaConverterType { NONE, TZX, - TAP + TAP, + DSK } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs index 2f41ca7925..bfba0bf66e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* /// /// Abtract class that represents all Media Serializers /// @@ -121,4 +122,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs similarity index 94% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs index d0db311d27..df4ec5a3d5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs @@ -10,13 +10,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Reponsible for TAP format serializaton /// - public class TapSerializer : MediaSerializer + public class TapConverter : MediaConverter { /// /// The type of serializer /// - private MediaSerializationType _formatType = MediaSerializationType.TAP; - public override MediaSerializationType FormatType + private MediaConverterType _formatType = MediaConverterType.TAP; + public override MediaConverterType FormatType { get { @@ -25,20 +25,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Signs whether this class can be used to serialize + /// Signs whether this class can be used to read the data format /// - public override bool IsSerializer { get { return false; } } + public override bool IsReader { get { return true; } } /// - /// Signs whether this class can be used to de-serialize + /// Signs whether this class can be used to write the data format /// - public override bool IsDeSerializer { get { return true; } } + public override bool IsWriter { get { return false; } } #region Construction private DatacorderDevice _datacorder; - public TapSerializer(DatacorderDevice _tapeDevice) + public TapConverter(DatacorderDevice _tapeDevice) { _datacorder = _tapeDevice; } @@ -104,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// DeSerialization method /// /// - public override void DeSerialize(byte[] data) + public override void Read(byte[] data) { /* The .TAP files contain blocks of tape-saved data. All blocks start with two bytes specifying how many bytes will follow (not counting the two length bytes). Then raw tape data follows, including the flag and checksum bytes. The checksum is the bitwise XOR of all bytes including the flag byte. For example, when you execute the line SAVE "ROM" CODE 0,2 this will result: diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs similarity index 99% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs index 1bd5e6179b..ac877f15d2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxSerializer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs @@ -10,13 +10,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Reponsible for TZX format serializaton /// - public class TzxSerializer : MediaSerializer + public class TzxConverter : MediaConverter { /// /// The type of serializer /// - private MediaSerializationType _formatType = MediaSerializationType.TZX; - public override MediaSerializationType FormatType + private MediaConverterType _formatType = MediaConverterType.TZX; + public override MediaConverterType FormatType { get { @@ -25,14 +25,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Signs whether this class can be used to serialize + /// Signs whether this class can be used to read the data format /// - public override bool IsSerializer { get { return false; } } + public override bool IsReader { get { return true; } } /// - /// Signs whether this class can be used to de-serialize + /// Signs whether this class can be used to write the data format /// - public override bool IsDeSerializer { get { return true; } } + public override bool IsWriter { get { return false; } } /// /// Working list of generated tape data blocks @@ -53,7 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum private DatacorderDevice _datacorder; - public TzxSerializer(DatacorderDevice _tapeDevice) + public TzxConverter(DatacorderDevice _tapeDevice) { _datacorder = _tapeDevice; } @@ -101,7 +101,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// DeSerialization method /// /// - public override void DeSerialize(byte[] data) + public override void Read(byte[] data) { // clear existing tape blocks _datacorder.DataBlocks.Clear(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 9ad4e3615a..43724f8ee2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -62,6 +62,33 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + #region DiskDevice Message Methods + + /// + /// Disk message that is fired on core init + /// + public void OSD_DiskInit() + { + StringBuilder sb = new StringBuilder(); + if (_machine.diskImages != null) + { + sb.Append("Disk Media Imported (count: " + _machine.diskImages.Count() + ")"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator); + } + } + + /// + /// Disk message that is fired when a new disk is inserted into the drive + /// + public void OSD_DiskInserted() + { + StringBuilder sb = new StringBuilder(); + sb.Append("DISK INSERTED (" + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name + ")"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + #endregion + #region TapeDevice Message Methods /// @@ -69,12 +96,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void OSD_TapeInit() { - StringBuilder sb = new StringBuilder(); - sb.Append("Tape Media Imported (count: " + _gameInfo.Count() + ")"); - sb.Append("\n"); - for (int i = 0; i < _gameInfo.Count(); i++) - sb.Append(i.ToString() + ": " + _gameInfo[i].Name + "\n"); + if (_tapeInfo.Count == 0) + return; + StringBuilder sb = new StringBuilder(); + sb.Append("Tape Media Imported (count: " + _tapeInfo.Count() + ")"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator); } @@ -83,8 +109,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void OSD_TapePlaying() { + if (_tapeInfo.Count == 0) + return; + StringBuilder sb = new StringBuilder(); - sb.Append("PLAYING (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + sb.Append("PLAYING (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } @@ -94,8 +123,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void OSD_TapeStopped() { + if (_tapeInfo.Count == 0) + return; + StringBuilder sb = new StringBuilder(); - sb.Append("STOPPED (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + sb.Append("STOPPED (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } @@ -105,8 +137,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void OSD_TapeRTZ() { + if (_tapeInfo.Count == 0) + return; + StringBuilder sb = new StringBuilder(); - sb.Append("REWOUND (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + sb.Append("REWOUND (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } @@ -116,8 +151,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void OSD_TapeInserted() { + if (_tapeInfo.Count == 0) + return; + StringBuilder sb = new StringBuilder(); - sb.Append("TAPE INSERTED (" + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name + ")"); + sb.Append("TAPE INSERTED (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } @@ -129,6 +167,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapeStoppedAuto() { StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + sb.Append("STOPPED (Auto Tape Trap Detected)"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); @@ -140,6 +187,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapePlayingAuto() { StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + sb.Append("PLAYING (Auto Tape Trap Detected)"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); @@ -151,7 +207,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapePlayingBlockInfo(string blockinfo) { StringBuilder sb = new StringBuilder(); - sb.Append("...Starting Block "+ blockinfo); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("...Starting Block " + blockinfo); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } @@ -162,6 +227,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapePlayingSkipBlockInfo(string blockinfo) { StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + sb.Append("...Skipping Empty Block " + blockinfo); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); @@ -173,6 +247,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapeEndDetected(string blockinfo) { StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + sb.Append("...Skipping Empty Block " + blockinfo); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); @@ -184,6 +267,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapeNextBlock(string blockinfo) { StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + sb.Append("Manual Skip Next " + blockinfo); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); @@ -195,6 +287,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_TapePrevBlock(string blockinfo) { StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + sb.Append("Manual Skip Prev " + blockinfo); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); @@ -206,6 +307,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_ShowTapeStatus() { StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + sb.Append("Status: "); if (_machine.TapeDevice.TapeIsPlaying) @@ -216,22 +325,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); sb.Clear(); - sb.Append("Tape: " + _machine.TapeMediaIndex + ": " + _gameInfo[_machine.TapeMediaIndex].Name); + sb.Append("Tape: " + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); sb.Clear(); sb.Append("Block: "); - sb.Append("(" + (_machine.TapeDevice.CurrentDataBlockIndex + 1) + - " of " + _machine.TapeDevice.DataBlocks.Count() + ") " + + sb.Append("(" + (_machine.TapeDevice.CurrentDataBlockIndex + 1) + + " of " + _machine.TapeDevice.DataBlocks.Count() + ") " + _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].BlockDescription); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); sb.Clear(); - + sb.Append("Block Pos: "); int pos = _machine.TapeDevice.Position; int end = _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].DataPeriods.Count; - double p = 0; + double p = 0; if (end != 0) p = ((double)pos / (double)end) * (double)100; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 7fee26c1f0..ea4c7df76d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -18,9 +18,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public partial class ZXSpectrum : IRegionable, IDriveLight { public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) - { + { var ser = new BasicServiceProvider(this); - ServiceProvider = ser; + ServiceProvider = ser; InputCallbacks = new InputCallbackSystem(); MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); @@ -43,7 +43,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); - List joysticks = new List(); + List joysticks = new List(); joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType1); joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType2); joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType3); @@ -57,7 +57,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; case MachineType.ZXSpectrum48: - ControllerDefinition = ZXSpectrumControllerDefinition; + ControllerDefinition = ZXSpectrumControllerDefinition; Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); break; case MachineType.ZXSpectrum128: @@ -78,8 +78,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; default: throw new InvalidOperationException("Machine not yet emulated"); - } - + } + _cpu.MemoryCallbacks = MemoryCallbacks; HardReset = _machine.HardReset; @@ -90,7 +90,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.WriteMemory = _machine.WriteMemory; _cpu.ReadHardware = _machine.ReadPort; _cpu.WriteHardware = _machine.WritePort; - _cpu.FetchDB = _machine.PushBus; + _cpu.FetchDB = _machine.PushBus; ser.Register(_tracer); ser.Register(_cpu); @@ -101,14 +101,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); if (_machine.AYDevice != null) SoundMixer.AddSource(_machine.AYDevice); - + // set audio device settings if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) { ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; _machine.AYDevice.Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYVolume; } - + if (_machine.BuzzerDevice != null) { ((Buzzer)_machine.BuzzerDevice as Buzzer).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; @@ -120,24 +120,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } ser.Register(SoundMixer); - + HardReset(); - SetupMemoryDomains(); + SetupMemoryDomains(); } - + public Action HardReset; public Action SoftReset; private readonly Z80A _cpu; private readonly TraceBuffer _tracer; - public IController _controller = NullController.Instance; + public IController _controller; public SpectrumBase _machine; - private List _gameInfo; + public List _gameInfo; + + public List _tapeInfo = new List(); + public List _diskInfo = new List(); + + private SoundProviderMixer SoundMixer; - private SoundProviderMixer SoundMixer; - private readonly List _files; public bool DiagRom = false; @@ -269,8 +272,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum get { if (_machine != null && - _machine.TapeDevice != null && - _machine.TapeDevice.TapeIsPlaying) + (_machine.TapeDevice != null && _machine.TapeDevice.TapeIsPlaying) || + (_machine.UPDDiskDevice != null && _machine.UPDDiskDevice.DriveLight)) return true; return false; From 00f46b0e7dc212f46c0b3836d3384a9ba1f9ea39 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 26 Apr 2018 15:00:54 +0100 Subject: [PATCH 171/339] Fix edge-case issue where non-rom games have a header offset detected in RomGame.cs (leading to system detection issues further down the line) --- BizHawk.Client.Common/RomGame.cs | 7 +++++++ BizHawk.Emulation.Common/Database/Database.cs | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/BizHawk.Client.Common/RomGame.cs b/BizHawk.Client.Common/RomGame.cs index a30e3b64c4..13eaf5b94f 100644 --- a/BizHawk.Client.Common/RomGame.cs +++ b/BizHawk.Client.Common/RomGame.cs @@ -66,6 +66,13 @@ namespace BizHawk.Client.Common { RomData = FileData; } + else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX") + { + // these are not roms. unforunately if treated as such there are certain edge-cases + // where a header offset is detected. This should mitigate this issue until a cleaner solution is found + // (-Asnivor) + RomData = FileData; + } else { // if there was a header offset, read the whole file into FileData and then copy it into RomData (this is unfortunate, in case RomData isnt needed) diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 8f0af8194d..5f37e91711 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -341,8 +341,9 @@ namespace BizHawk.Emulation.Common case ".DSK": byte[] head2 = romData.Take(20).ToArray(); - if (System.Text.Encoding.Default.GetString(head2).ToUpper().Contains("EXTENDED CPC DSK") || - System.Text.Encoding.Default.GetString(head2).ToUpper().Contains("MV - CPCEMU")) + string ident = System.Text.Encoding.Default.GetString(head2); + if (ident.ToUpper().Contains("EXTENDED CPC DSK") || + ident.ToUpper().Contains("MV - CPCEMU")) game.System = "ZXSpectrum"; else game.System = "AppleII"; From 85fb40ed027cc9e16b68fe22512dfc77d6d64d26 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 26 Apr 2018 16:39:40 +0100 Subject: [PATCH 172/339] ZXHawk: Implemented UPD765A ReadTrack command (platoon now loads) --- .../Hardware/Disk/NECUPD765.FDC.cs | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 2ee2b3467e..e2d3752ce6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -1046,12 +1046,208 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Receiving command parameter bytes //---------------------------------------- case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + int buffPos = 0; + int sectorSize = 0; + int maxTransferCap = 0; + + // calculate requested size of data required + if (ActiveCommandParams.SectorSize == 0) + { + // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual + // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) + // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform + // a Multi-Sector Read Operation. + sectorSize = ActiveCommandParams.DTL; + + // calculate maximum transfer capacity + if (!CMD_FLAG_MF) + maxTransferCap = 3328; + } + else + { + // When N is non - zero, then DTL has no meaning and should be set to ffh + ActiveCommandParams.DTL = 0xFF; + + // calculate maximum transfer capacity + switch (ActiveCommandParams.SectorSize) + { + case 1: + if (CMD_FLAG_MF) + maxTransferCap = 6656; + else + maxTransferCap = 3840; + break; + case 2: + if (CMD_FLAG_MF) + maxTransferCap = 7680; + else + maxTransferCap = 4096; + break; + case 3: + if (CMD_FLAG_MF) + maxTransferCap = 8192; + else + maxTransferCap = 4096; + break; + } + + sectorSize = 0x80 << ActiveCommandParams.SectorSize; + } + + // get the current track + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track == null || track.NumberOfSectors <= 0) + { + // track could not be found + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + FloppyDisk.Sector sector = null; + ActiveDrive.SectorIndex = 0; + + int secCount = 0; + + // read the whole track + for (int i = 0; i < track.Sectors.Length; i++) + { + if (secCount > ActiveCommandParams.EOT) + { + break; + } + + var sec = track.Sectors[i]; + for (int b = 0; b < sec.ActualData.Length; b++) + { + ExecBuffer[buffPos++] = sec.ActualData[b]; + } + + // end of sector - compare IDs + if (sec.TrackNumber != ActiveCommandParams.Cylinder || + sec.SideNumber != ActiveCommandParams.Head || + sec.SectorID != ActiveCommandParams.Sector || + sec.SectorSize != ActiveCommandParams.SectorSize) + { + SetBit(SR1_ND, ref Status1); + } + + secCount++; + ActiveDrive.SectorIndex = i; + } + + if (secCount == ActiveCommandParams.EOT) + { + // this was the last sector to read + // or termination requested + + int keyIndex = 0; + for (int i = 0; i < track.Sectors.Length; i++) + { + if (track.Sectors[i].SectorID == track.Sectors[ActiveDrive.SectorIndex].SectorID) + { + keyIndex = i; + break; + } + } + + if (keyIndex == track.Sectors.Length - 1) + { + // last sector on the cylinder, set EN + SetBit(SR1_EN, ref Status1); + + // increment cylinder + ActiveCommandParams.Cylinder++; + + // reset sector + ActiveCommandParams.Sector = 1; + ActiveDrive.SectorIndex = 0; + } + else + { + ActiveDrive.SectorIndex++; + } + + UnSetBit(SR0_IC1, ref Status0); + UnSetBit(SR0_IC0, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Execution; + } + + if (ActivePhase == Phase.Execution) + { + ExecLength = buffPos; + ExecCounter = buffPos; + + DriveLight = true; + } + } + break; //---------------------------------------- // FDC in execution phase reading/writing bytes //---------------------------------------- case Phase.Execution: + + var index = ExecLength - ExecCounter; + + LastSectorDataReadByte = ExecBuffer[index]; + + ExecCounter--; + break; //---------------------------------------- From 23a255044472c018f920d415ee439ff0de2da7f9 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 27 Apr 2018 10:57:54 +0100 Subject: [PATCH 173/339] ZXHawk: disabled auto-tape detection routines when a disk is in the drive --- .../BizHawk.Emulation.Cores.csproj | 4 - .../Hardware/Datacorder/DatacorderDevice.cs | 7 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 10 +- .../SinclairSpectrum/Media/Disk/DiskImage.cs | 145 ----- .../Media/Disk/DskConverter.cs | 511 ------------------ .../SinclairSpectrum/Media/Disk/Sector.cs | 228 -------- .../SinclairSpectrum/Media/Disk/Track.cs | 95 ---- 7 files changed, 11 insertions(+), 989 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 68b09cf8d3..980cdafec2 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -293,11 +293,7 @@ - - - - diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 8fcc6f11ee..ebe6733f2d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -836,10 +836,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!TapeIsPlaying) { - MonitorRead(); + if (_machine.UPDDiskDevice == null || !_machine.UPDDiskDevice.FDD_IsDiskLoaded) + MonitorRead(); } - - MonitorRead(); + if (_machine.UPDDiskDevice == null || !_machine.UPDDiskDevice.FDD_IsDiskLoaded) + MonitorRead(); /* diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index d2ef106fa1..07c64c9f45 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -147,12 +147,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum FrameCompleted = false; - TapeDevice.StartFrame(); + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) + TapeDevice.StartFrame(); if (_renderSound) { BuzzerDevice.StartFrame(); TapeBuzzer.StartFrame(); + if (AYDevice != null) AYDevice.StartFrame(); } @@ -168,7 +170,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CPU.ExecuteOne(); // cycle the tape device - TapeDevice.TapeCycle(); + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) + TapeDevice.TapeCycle(); } // we have reached the end of a frame @@ -192,7 +195,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // setup for next frame ULADevice.ResetInterrupt(); - TapeDevice.EndFrame(); + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) + TapeDevice.EndFrame(); FrameCompleted = true; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs deleted file mode 100644 index 6c70fa8f4a..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskImage.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Object that represents logical disk media once imported - /// - public class DiskImage - { - #region File Header Info - - /// - /// The identified format - /// (in the case of .dsk files, this is probably "EXTENDED CPC DSK") - /// - public string Header_SystemIdent { get; set; } - - /// - /// The software originally used to create thsi image - /// - public string Header_CreatorSoftware { get; set; } - - /// - /// The number of tracks in the disk image (header specified) - /// - public int Header_TrackCount { get; set; } - - /// - /// The number of sides on the disk (header specified) - /// - public int Header_SideCount { get; set; } - - /// - /// Size of a track including 0x100 byte track information block - /// (in a .dsk all tracks will be the same size) - /// - public int Header_TrackSize { get; set; } - - #endregion - - public List Tracks = new List(); - - /// - /// Reads an entire sector - /// - /// - /// - /// - /// - public CHRN ReadSector(int side, int track, int sector) - { - var t = Tracks.Where(a => a.TrackNumber == track).FirstOrDefault(); - - if (t == null) - return null; - - var s = t.Sectors.Where(a => a.SectorID == sector).FirstOrDefault(); - - if (s == null) - return null; - - var chrn = s.GetCHRN(); - chrn.DataBytes = s.SectorData; - - return chrn; - } - - /// - /// Reads data from a sector based on offset and length - /// - /// - /// - /// - /// - public CHRN ReadSector(int side, int track, int sector, int offset, int length) - { - var result = ReadSector(side, track, sector); - - if (result == null) - return null; - - if (offset > result.DataBytes.Length) - return null; - - if (length + offset > result.DataBytes.Length) - return null; - - // data is safe to read - var data = result.DataBytes.Skip(offset).Take(length).ToArray(); - - result.DataBytes = data; - - return result; - } - - /// - /// Reads an entire track - /// - /// - /// - /// - public Track ReadTrack(int side, int track) - { - return Tracks.Where(a => a.TrackNumber == track).FirstOrDefault(); - } - - /// - /// Reads a track based on offset and length - /// - /// - /// - /// - /// - /// - public Track ReadTrack(int side, int track, int offset, int length) - { - var t = ReadTrack(side, track); - - if (t == null) - return null; - - if (offset >= t.AllSectorBytes.Count() || offset + length >= t.AllSectorBytes.Count()) - return null; - - var data = t.AllSectorBytes.Skip(offset).Take(length).ToArray(); - - t.ResultBytes = data; - - return t; - } - - /// - /// Returns the number of tracks preset on the disk - /// - /// - public int GetTrackCount() - { - return Tracks.Count(); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs deleted file mode 100644 index 47a7d6b1ff..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DskConverter.cs +++ /dev/null @@ -1,511 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Reponsible for DSK format serializaton - /// File format info taken from: http://www.cpcwiki.eu/index.php/Format:DSK_disk_image_file_format - /// - public class DskConverter : MediaConverter - { - /// - /// The type of serializer - /// - private MediaConverterType _formatType = MediaConverterType.DSK; - public override MediaConverterType FormatType - { - get - { - return _formatType; - } - } - - /// - /// Signs whether this class can be used to read data - /// - public override bool IsReader { get { return true; } } - - /// - /// Signs whether this class can be used to write data - /// - public override bool IsWriter { get { return false; } } - - /// - /// The disk image that we will be populating - /// - private DiskImage _diskImage = new DiskImage(); - - - /// - /// The current position whilst parsing the incoming data - /// - private int _position = 0; - - #region Construction - - private IFDDHost _diskDrive; - - public DskConverter(IFDDHost diskDrive) - { - _diskDrive = diskDrive; - } - - #endregion - - /// - /// Returns TRUE if dsk header is detected - /// - /// - public override bool CheckType(byte[] data) - { - // check whether this is a valid dsk format file by looking at the identifier in the header - // (first 16 bytes of the file) - string ident = Encoding.ASCII.GetString(data, 0, 16); - - if (!ident.ToUpper().Contains("EXTENDED CPC DSK") && !ident.ToUpper().Contains("MV - CPCEMU")) - { - // this is not a valid DSK format file - return false; - } - else - { - return true; - } - } - - /// - /// Read method - /// - /// - public override void Read(byte[] data) - { - // populate dsk header info - string ident = Encoding.ASCII.GetString(data, 0, 34); - _diskImage.Header_SystemIdent = ident; - - if (ident.ToUpper().Contains("EXTENDED CPC DSK")) - ReadExtendedDsk(data); - else if (ident.ToUpper().Contains("MV - CPCEMU")) - ReadDsk(data); - - // load the disk 'into' the controller - //_diskDrive.Disk = _diskImage; - } - - /// - /// Parses the extended disk format - /// The extended DSK image is a file designed to describe copy-protected floppy disk software - /// - /// - private void ReadExtendedDsk(byte[] data) - { - /* DISK INFORMATION BLOCK - - offset description bytes - 00 - 21 "EXTENDED CPC DSK File\r\nDisk-Info\r\n" 34 - 22 - 2f name of creator (utility/emulator) 14 - 30 number of tracks 1 - 31 number of sides 1 - 32 - 33 unused 2 - 34 - xx track size table number of tracks*number of sides - */ - - // name of creator - _position = 0x22; - _diskImage.Header_CreatorSoftware = Encoding.ASCII.GetString(data, _position, 14); - - // number of tracks - _position = 0x30; - _diskImage.Header_TrackCount = data[_position++]; - - // number of sides - _diskImage.Header_SideCount = data[_position++]; - - _position += 2; - - /* TRACK OFFSET TABLE - - offset description bytes - 01 high byte of track 0 length (equivalent to track length/256) 1 - ... ... ... - - track lengths are stored in the same order as the tracks in the image e.g. In the case of a double sided disk: Track 0 side 0, Track 0 side 1, Track 1 side 0 etc... - A size of "0" indicates an unformatted track. In this case there is no data, and no track information block for this track in the image file! - Actual length of track data = (high byte of track length) * 256 - Track length includes the size of the TRACK INFORMATION BLOCK (256 bytes) - The location of a Track Information Block for a chosen track is found by summing the sizes of all tracks up to the chosen track plus the size of the Disc Information Block (&100 bytes). The first track is at offset &100 in the disc image. - */ - - // iterate through the track size table and do an initial setup of all the tracks - // (this is just new tracks with the offset value saved) - int trkPos = 0x100; - for (int i = 0; i < _diskImage.Header_TrackCount * _diskImage.Header_SideCount; i++) - { - Track track = new Track(); - - // calc actual track length (including track info block) - int ts = data[_position++]; - int tLen = ts * 256; - - track.TrackStartOffset = trkPos; - track.TrackByteLength = tLen; - _diskImage.Tracks.Add(track); - trkPos += tLen; - } - - // iterate through each newly created track and parse it based on the offset - List tracks = new List(); - for (int tr = 0; tr < _diskImage.Tracks.Count(); tr++) - { - // get the track we are interested in - var t = _diskImage.Tracks[tr]; - - // validity check - if (t.TrackByteLength == 0) - { - // unformatted track that is not present in the disk image - Track trU = new Track(); - trU.TrackNumber = (byte)tr; - trU.TrackByteLength = 0; - tracks.Add(trU); - continue; - } - - Track track = new Track(); - track.TrackStartOffset = t.TrackStartOffset; - track.TrackByteLength = t.TrackByteLength; - - // get data for this track block - byte[] tData = data.Skip(t.TrackStartOffset).Take(t.TrackByteLength).ToArray(); - - // start of data block - int pos = 0; - - // seek past track info ident - pos += 0x10; - - /* TRACK INFORMATION BLOCK - - offset description bytes - 00 - 0b "Track-Info\r\n" 12 - 0c - 0f unused 4 - 10 track number 1 - 11 side number 1 - 12 data rate 1 - 13 recording mode 1 - 14 sector size 1 - 15 number of sectors 1 - 16 GAP#3 length 1 - 17 filler byte 1 - 18 - xx Sector Information List xx - */ - - /* Format extensions - ----------------- - - Date rate description - 0 Unknown. - 1 Single or double density - 2 High Density - 3 Extended density - - Data rate defines the rate at which data was written to the track. This value applies to the entire track. - - Recording mode description - 0 Unknown. - 1 FM - 2 MFM - - Recording mode is used to define how the data was written. It defines the encoding used to write the data to the disc and the structure of the data on the disc including the layout of the sectors. - This value applies to the entire track - - The NEC765 floppy disc controller is supplied with a single clock. When reading from and writing to a disc using the NEC765 you can choose - FM or MFM recording modes. Use of these modes and the clock into the NEC765 define the final rate at which the data is written to the disc. - When FM recording mode is used, data is read from or written to at a rate which is double that of when MFM is used. - The time for each bit will be twice the time for MFM - - NEC765 Clock FM/MFM Actual rate - 4Mhz FM 4us per bit - 4Mhz MFM 2us per bit - */ - - // track number - track.TrackNumber = tData[pos++]; - // side number - track.SideNumber = tData[pos++]; - // data rate - track.DataRate = tData[pos++]; - // recording mode - track.RecordingMode = tData[pos++]; - // sector size - track.SectorSize = tData[pos++]; - // number of sectors - track.SectorCount = tData[pos++]; - // GAP#3 Length - track.GAP3Length = tData[pos++]; - // filler byte - track.FillerByte = tData[pos++]; - - /* SECTOR INFORMATION LIST - - offset description bytes - 00 track (equivalent to C parameter in NEC765 commands) 1 - 01 side (equivalent to H parameter in NEC765 commands) 1 - 02 sector ID (equivalent to R parameter in NEC765 commands) 1 - 03 sector size (equivalent to N parameter in NEC765 commands) 1 - 04 FDC status register 1 (equivalent to NEC765 ST1 status register) 1 - 05 FDC status register 2 (equivalent to NEC765 ST2 status register) 1 - 06 - 07 actual data length in bytes 2 - */ - - // parse sector information list - for (int s = 0; s < track.SectorCount; s++) - { - Sector sector = new Sector(); - sector.Track = tData[pos++]; - sector.Side = tData[pos++]; - sector.SectorID = tData[pos++]; - sector.SectorSize = tData[pos++]; - sector.ST1 = tData[pos++]; - sector.ST2 = tData[pos++]; - sector.TotalDataLength = GetWordValue(tData, pos); - pos += 2; - - // get the sector data - lives directly after the 256byte track info block - int secDataPos = 0x100; - - /* Format extension - ---------------_ - It has been found that many protections using 8K Sectors (N="6") do store more than &1800 bytes of useable data. - It was thought that &1800 was the maximum useable limit, but this has proved wrong. So you should support 8K of data to ensure - this data is read correctly. The size of the sector will be reported in the SECTOR INFORMATION LIST as described above. - For sector size N="7" the full 16K will be stored. It is assumed that sector sizes are defined as 3 bits only, - so that a sector size of N="8" is equivalent to N="0". - */ - int bps = 0x80 << sector.SectorSize; - - if (sector.SectorSize == 8 || sector.SectorSize == 0) - { - // no sector data - bps = 0x80 << 0; - //continue; - } - - - /* Format extension - ---------------- - Storing Multiple Versions of Weak/Random Sectors. - Some copy protections have what is described as 'weak/random' data. Each time the sector is read one or more bytes will change, - the value may be random between consecutive reads of the same sector. - To support these formats the following extension has been proposed. - Where a sector has weak/random data, there are multiple copies stored. The actual sector size field in the SECTOR INFORMATION LIST - describes the size of all the copies. To determine if a sector has multiple copies then compare the actual sector size field to the - size defined by the N parameter. For multiple copies the actual sector size field will have a value which is a multiple of the size - defined by the N parameter. The emulator should then choose which copy of the sector it should return on each read. - */ - - int sizeOfAllCopies = sector.TotalDataLength; - - if (sizeOfAllCopies % bps == 0) - { - // sector size is the same as total data length (indicating 1 sector data) - // or a factor of the total data length (indicating multiple copies) - for (int sd = 0; sd < sizeOfAllCopies / bps; sd++) - { - byte[] sData = new byte[bps]; - for (int c = 0; c < bps; c++) - { - sData[c] = data[secDataPos++]; - } - - sector.AddSectorData(sd, sData); - } - } - else - { - // assume that there is one sector copy, but the total data length does not match - if (sector.TotalDataLength <= sizeOfAllCopies) - { - byte[] sData = new byte[bps]; - for (int c = 0; c < bps; c++) - { - sData[c] = data[secDataPos++]; - } - - sector.AddSectorData(0, sData); - } - else - { - byte[] sData = new byte[sizeOfAllCopies]; - for (int c = 0; c < sizeOfAllCopies; c++) - { - sData[c] = data[secDataPos++]; - } - - sector.AddSectorData(0, sData); - } - } - - track.Sectors.Add(sector); - - } - - // add track to working list - tracks.Add(track); - } - - // replace the tracks collection - _diskImage.Tracks = tracks; - } - - - /// - /// Parses the standard disk image format - /// - /// - private void ReadDsk(byte[] data) - { - /* DISK INFORMATION BLOCK - - offset description bytes - 00-21 "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" 34 - 22-2f name of creator 14 - 30 number of tracks 1 - 31 number of sides 1 - 32-33 size of a track (little endian; low byte followed by high byte) 2 - 34-ff not used (0) 204 - */ - - // name of creator - _position = 0x22; - _diskImage.Header_CreatorSoftware = Encoding.ASCII.GetString(data, _position, 14); - - // number of tracks - _position = 0x30; - _diskImage.Header_TrackCount = data[_position++]; - - // number of sides - _diskImage.Header_SideCount = data[_position++]; - - // size of a track (little endian) - _diskImage.Header_TrackSize = GetWordValue(data, _position); - - // 34-ff not used - - // move to start of first track info block - _position = 0x100; - - int tmpPos = _position; - - // iterate through each track - for (int t = 0; t < _diskImage.Header_TrackCount; t++) - { - /* TRACK INFORMATION BLOCK - - offset description bytes - 00 - 0b "Track-Info\r\n" 12 - 0c - 0f unused 4 - 10 track number 1 - 11 side number 1 - 12 - 13 unused 2 - 14 sector size 1 - 15 number of sectors 1 - 16 GAP#3 length 1 - 17 filler byte 1 - 18 - xx Sector Information List xx - */ - - Track track = new Track(); - _position += 0x10; - - track.TrackNumber = data[_position++]; - track.SideNumber = data[_position++]; - _position += 2; - - /* - BPS (bytes per sector) - 0 = 128 0x80 - 1 = 256 0x100 - 2 = 512 0x200 - 3 = 1024 0x400 - 4 = 2048 0x800 - 5 = 4096 0x1000 - 6 = 8192 0x2000 - */ - - track.SectorSize = data[_position++]; - - track.SectorCount = data[_position++]; - track.GAP3Length = data[_position++]; - track.FillerByte = data[_position++]; - - /* SECTOR INFORMATION LIST - - offset description bytes - 00 track (equivalent to C parameter in NEC765 commands) 1 - 01 side (equivalent to H parameter in NEC765 commands) 1 - 02 sector ID (equivalent to R parameter in NEC765 commands) 1 - 03 sector size (equivalent to N parameter in NEC765 commands) 1 - 04 FDC status register 1 (equivalent to NEC765 ST1 status register) 1 - 05 FDC status register 2 (equivalent to NEC765 ST2 status register) 1 - 06 - 07 notused (0) 2 - */ - - // get the sector info first - for (int s = 0; s < track.SectorCount; s++) - { - Sector sector = new Sector(); - sector.Track = data[_position++]; - sector.Side = data[_position++]; - sector.SectorID = data[_position++]; - sector.SectorSize = data[_position++]; - sector.ST1 = data[_position++]; - sector.ST2 = data[_position++]; - track.Sectors.Add(sector); - _position += 2; - } - - // now process the sector data - always offset 0x100 from the start of the track information block - tmpPos += 0x100; - _position = tmpPos; - - // sectorsize in the track information list implies the boundaries of the sector data - // sectorsize in the sector info list gives us the actual size of each sector to read - for (int s = 0; s < track.SectorCount; s++) - { - var sec = track.Sectors[s]; - - List tmpData = new List(); - - // read the sector data bytes - int bps = 0x80 << sec.SectorSize; - if (bps > 0x1800) - bps = 0x1800; - - for (int d = 0; d < bps; d++) - { - tmpData.Add(data[_position + d]); - } - - sec.AddSectorData(0, tmpData.ToArray()); - track.Sectors[s] = sec; - - // sector data parsed - move to next sector data block - int trackBps = 0x80 << track.SectorSize; - if (trackBps > 0x1800) - trackBps = 0x1800; - - _position += track.SectorSize; - } - - // add the track to the disk image - _diskImage.Tracks.Add(track); - } - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs deleted file mode 100644 index 208b674c4c..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Sector.cs +++ /dev/null @@ -1,228 +0,0 @@ -using BizHawk.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents a disk sector - /// - public class Sector - { - /// - /// Used when the FDD returns the sector - /// False means the sector does not exist - /// - public bool IsValid = true; - - /// - /// track (equivalent to C parameter in NEC765 commands) - /// - private byte _track; - public byte Track - { - get { return _track; } - set { _track = value; } - } - - /// - /// side (equivalent to H parameter in NEC765 commands) - /// - private byte _side; - public byte Side - { - get { return _side; } - set { _side = value; } - } - - /// - /// sector ID (equivalent to R parameter in NEC765 commands) - /// - private byte _sectorID; - public byte SectorID - { - get { return _sectorID; } - set { _sectorID = value; } - } - - /// - /// sector size (equivalent to N parameter in NEC765 commands) - /// - private byte _sectorSize; - public byte SectorSize - { - get { return _sectorSize; } - set { _sectorSize = value; } - } - - /// - /// FDC status register 1 (equivalent to NEC765 ST1 status register) - /// - private byte _sT1; - public byte ST1 - { - get { return _sT1; } - set { _sT1 = value; } - } - - /// - /// FDC status register 2 (equivalent to NEC765 ST2 status register) - /// - private byte _sT2; - public byte ST2 - { - get { return _sT2; } - set { _sT2 = value; } - } - - /// - /// Actual total data length in bytes - /// If this is greater than the sector size specified by N, then multiple copies - /// of the sector data are present (representing weak/random copy protection data) - /// - private int _totalDataLength; - public int TotalDataLength - { - get { return _totalDataLength; } - set { _totalDataLength = value; } - } - - /// - /// The sector data entry point - /// If multiple sector data is found this should return a randomly chosen data for this sector - /// - public byte[] SectorData - { - get - { - // number of sector data copies found - int count = _sectorDatas.Count(); - - if (count <= 0) - { - // no sectors present - return new byte[0]; - } - - if (count == 1) - { - // only one copy of data found - return this - return _sectorDatas.First(); - } - - if (count > 1) - { - // there is more than one copy of sector data stored - // return at random to simulate weak/random copy protection sectors - Random rnd = new Random(); - int pnt = rnd.Next(0, count - 1); - return _sectorDatas[pnt]; - } - - // probably shouldnt ever get this far - return null; - } - } - - /// - /// Adds sector data based on the copy index - /// - /// - public void AddSectorData(int position, byte[] data) - { - if (_sectorDatas.ElementAtOrDefault(position) == null) - { - // create byte arrays for uninstantiated indexes - for (int i = 0; i < position + 1; i++) - { - if (_sectorDatas.ElementAtOrDefault(i) == null) - { - _sectorDatas.Add(new byte[0]); - } - } - } - - _sectorDatas[position] = data; - } - - /// - /// Internal storage for sector data - /// - private List _sectorDatas = new List(); - - - /// - /// Returns a CHRN object for this sector - /// - /// - public CHRN GetCHRN() - { - return new CHRN - { - C = Track, - H = Side, - R = SectorID, - N = SectorSize, - Flag1 = ST1, - Flag2 = ST2, - DataBytes = SectorData - }; - } - - - - /// - /// State serialization - /// Should only be called for the ActiveSector object in the floppy drive - /// - /// - public void SyncState(Serializer ser) - { - ser.BeginSection("ActiveSector"); - - ser.Sync("IsValid", ref IsValid); - ser.Sync("_track", ref _track); - ser.Sync("_side", ref _side); - ser.Sync("_sectorID", ref _sectorID); - ser.Sync("_sectorSize", ref _sectorSize); - ser.Sync("_sT1", ref _sT1); - ser.Sync("_sT2", ref _sT2); - ser.Sync("_totalDataLength", ref _totalDataLength); - - if (ser.IsReader) - { - ser.Sync("SecCopySize", ref SecCopySize); - - byte[][] sds = new byte[SecCopySize][]; - - for (int i = 0; i < SecCopySize; i++) - { - ser.Sync("sec" + i, ref sds[i], false); - } - - _sectorDatas = sds.ToList(); - } - - if (ser.IsWriter) - { - SecCopySize = _sectorDatas.Count(); - ser.Sync("SecCopySize", ref SecCopySize); - - byte[][] sds = _sectorDatas.ToArray(); - - for (int i = 0; i < SecCopySize; i++) - { - ser.Sync("sec" + i, ref sds[i], false); - } - } - - ser.EndSection(); - } - - // Sector array size (for state serialization) - private int SecCopySize; - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs deleted file mode 100644 index d14b010170..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/Track.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Represents a disk track - /// - public class Track - { - /// - /// The track number - /// - public byte TrackNumber { get; set; } - /// - /// The side number - /// - public byte SideNumber { get; set; } - /// - /// Data rate defines the rate at which data was written to the track. This value applies to the entire track - /// - public byte DataRate { get; set; } - /// - /// Recording mode is used to define how the data was written. It defines the encoding used to write the data to the disc and the structure of the data on the - /// disc including the layout of the sectors. This value applies to the entire track. - /// - public byte RecordingMode { get; set; } - /// - /// Used to calculate the location of each sector's data. Therefore, The data allocated for each sector must be the same - /// - public int SectorSize { get; set; } - /// - /// Identifies the number of valid entries in the sector information list - /// - public byte SectorCount { get; set; } - /// - /// The length of GAP3 data - /// - public byte GAP3Length { get; set; } - /// - /// Byte used as filler - /// - public byte FillerByte { get; set; } - - /// - /// List containing all the sectors for this track - /// - public List Sectors = new List(); - - /// - /// Defines the offset in the disk image file at which the track information block starts - /// - public int TrackStartOffset { get; set; } - - /// - /// The actual length of the track (including info block) in the disk image file - /// - public int TrackByteLength { get; set; } - - /// - /// Returns all sector data for this track in one byte array - /// - public byte[] AllSectorBytes - { - get - { - int byteSize = 0; - foreach (var s in Sectors) - { - byteSize += s.SectorData.Count(); - } - - byte[] all = new byte[byteSize]; - - int _pos = 0; - - foreach (var s in Sectors) - { - foreach (var d in s.SectorData) - { - all[_pos] = d; - _pos++; - } - } - - return all; - } - } - - public byte[] ResultBytes { get; set; } - } -} From ca0ae3d971eba3bcad6989b5d0339fe0c0359dce Mon Sep 17 00:00:00 2001 From: adelikat Date: Sat, 28 Apr 2018 09:48:24 -0500 Subject: [PATCH 174/339] Make the bk2 SetControllersAsMnemonic implementation less bad. Fixes #1181 --- BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs b/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs index 026bfbcfb7..0fd295a010 100644 --- a/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs +++ b/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs @@ -172,11 +172,12 @@ namespace BizHawk.Client.Common } else if (def.FloatControls.Contains(key)) { - var temp = trimmed.Substring(iterator, 5); + var commaIndex = trimmed.Substring(iterator).IndexOf(','); + var temp = trimmed.Substring(iterator, commaIndex); var val = int.Parse(temp.Trim()); _myFloatControls[key] = val; - iterator += 6; + iterator += commaIndex + 1; } } } From 0c12707bb70c1877084cb3a4ca029b35e92a3f27 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Sat, 28 Apr 2018 17:06:46 +0100 Subject: [PATCH 175/339] ZXHawk: some updates to speedlock disk detection --- .../Hardware/Disk/NECUPD765.FDC.cs | 4 +- .../Media/Disk/CPCExtendedFloppyDisk.cs | 4 +- .../Media/Disk/CPCFloppyDisk.cs | 4 +- .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 140 +++++++++++++++++- 4 files changed, 145 insertions(+), 7 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index e2d3752ce6..ec52e5cb1e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -531,7 +531,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // read the sector - for (int i = 0; i < sectorSize; i++) + for (int i = 0; i < sector.DataLen; i++) { ExecBuffer[buffPos++] = sector.ActualData[i]; } @@ -2947,7 +2947,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return StatusMain; } - + private int testCount = 0; /// /// Handles CPU reading from the data register /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs index 8cb77126a6..b8cbf0968e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs @@ -131,8 +131,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum pos += DiskHeader.TrackSizes[i]; } - // run speedlock detector - SpeedlockDetection(); + // run protection scheme detector + ParseProtection(); return true; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs index 88d6b1a91f..4835540657 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs @@ -137,8 +137,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum pos += DiskHeader.TrackSizes[i]; } - // run speedlock detector - SpeedlockDetection(); + // run protection scheme detector + ParseProtection(); return true; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index 65a46b50b7..f9a6d947ca 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -102,6 +102,127 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; } + /// + /// Examines the floppydisk data to work out what protection (if any) is present + /// If possible it will also fix the disk data for this protection + /// This should be run at the end of the ParseDisk() method + /// + public virtual void ParseProtection() + { + int[] weakArr = new int[2]; + + // speedlock + if (DetectSpeedlock(ref weakArr)) + { + Protection = ProtectionType.Speedlock; + + Sector sec = DiskTracks[0].Sectors[1]; + if (!sec.ContainsMultipleWeakSectors) + { + byte[] origData = sec.SectorData.ToArray(); + List data = new List(); + for (int m = 0; m < 3; m++) + { + for (int i = 0; i < 512; i++) + { + // deterministic 'random' implementation + int n = origData[i] + m + 1; + if (n > 0xff) + n = n - 0xff; + else if (n < 0) + n = 0xff + n; + + byte nByte = (byte)n; + + if (m == 0) + { + data.Add(origData[i]); + continue; + } + + if (i < weakArr[0]) + { + data.Add(origData[i]); + } + + else if (weakArr[1] > 0) + { + data.Add(nByte); + weakArr[1]--; + } + + else + { + data.Add(origData[i]); + } + } + } + + sec.SectorData = data.ToArray(); + sec.ActualDataByteLength = data.Count(); + sec.ContainsMultipleWeakSectors = true; + } + } + } + + /// + /// Detect speedlock weak sector + /// + /// + /// + public bool DetectSpeedlock(ref int[] weak) + { + // always must have track 0 containing 9 sectors + if (DiskTracks[0].Sectors.Length != 9) + return false; + + // check for SPEEDLOCK ident in sector 0 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[0].SectorData, 0, DiskTracks[0].Sectors[0].SectorData.Length); + if (!ident.ToUpper().Contains("SPEEDLOCK")) + return false; + + // check for correct sector 0 lengths + if (DiskTracks[0].Sectors[0].SectorSize != 2 || + DiskTracks[0].Sectors[0].SectorData.Length < 0x200) + return false; + + // sector[1] (SectorID 2) contains the weak sectors + Sector sec = DiskTracks[0].Sectors[1]; + + // check for correct sector 1 lengths + if (sec.SectorSize != 2 || + sec.SectorData.Length < 0x200) + return false; + + // secID 2 needs a CRC error + if (!(sec.Status1.Bit(5) || sec.Status2.Bit(5))) + return false; + + // check for filler + bool startFillerFound = true; + for (int i = 0; i < 250; i++) + { + if (sec.SectorData[i] != sec.SectorData[i + 1]) + { + startFillerFound = false; + break; + } + } + + if (!startFillerFound) + { + weak[0] = 0; + weak[1] = 0x200; + } + else + { + weak[0] = 0x150; + weak[1] = 0x20; + } + + return true; + } + /// /// Should be run at the end of the ParseDisk process /// If speedlock is detected the flag is set in the disk image @@ -327,6 +448,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public byte[] SectorData { get; set; } public bool ContainsMultipleWeakSectors { get; set; } + public int DataLen + { + get + { + if (!ContainsMultipleWeakSectors) + { + return ActualDataByteLength; + } + else + { + return ActualDataByteLength / (ActualDataByteLength / (0x80 << SectorSize)); + } + } + } + + public int RandSecCounter = 0; public byte[] ActualData @@ -343,7 +480,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum l.AddRange(SectorData); for (int i = 0; i < size - ActualDataByteLength; i++) { - l.Add(0xe5); + //l.Add(SectorData[i]); + l.Add(SectorData.Last()); } return l.ToArray(); From e642e0216cd60c4899d1b15b8f76d3ad81811942 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Sat, 28 Apr 2018 17:07:59 +0100 Subject: [PATCH 176/339] ZXHawk: improved detection of cpcemu dsk files --- .../Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs index 4835540657..99818cc932 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs @@ -31,7 +31,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // look for standard magic string string ident = Encoding.ASCII.GetString(data, 0, 16); - if (!ident.ToUpper().Contains("MV - CPCEMU")) + if (!ident.ToUpper().Contains("MV - CPC")) { // incorrect format return false; From 2fbdc00183d2f37b7f8c4aa1364ef6df471bf643 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 28 Apr 2018 14:50:58 -0400 Subject: [PATCH 177/339] GBHawk: upgrade audio to use blip buffer (avoids some high frequency aliasing) --- .../Consoles/Nintendo/GBHawk/Audio.cs | 97 +++++++++++++------ .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 4 +- 2 files changed, 71 insertions(+), 30 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 488278b4cd..27dd958043 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -12,6 +12,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public GBHawk Core { get; set; } + private readonly BlipBuffer _blip_L = new BlipBuffer(15000); + private readonly BlipBuffer _blip_R = new BlipBuffer(15000); + public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, @@ -106,8 +109,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + public byte sample; + public int master_audio_clock; + public int latched_sample_L, latched_sample_R; + public byte ReadReg(int addr) { byte ret = 0; @@ -502,7 +509,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (WAVE_can_get || Core.is_GBC) { Wave_RAM[WAVE_wave_cntr >> 1] = value; } } - else { Wave_RAM[addr & 0xF] = value; } + else + { + Wave_RAM[addr & 0xF] = value; + } break; } @@ -620,13 +630,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (WAVE_intl_cntr == 0) { - WAVE_intl_cntr = (2048 - WAVE_frq) * 2; - WAVE_wave_cntr++; - WAVE_wave_cntr &= 0x1F; - WAVE_can_get = true; - byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; + WAVE_intl_cntr = (2048 - WAVE_frq) * 2; if ((WAVE_wave_cntr & 1) == 0) { @@ -652,6 +658,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WAVE_output = sample; + // NOTE: The sample buffer is only reloaded after the current sample is played, even if just triggered + WAVE_wave_cntr++; + WAVE_wave_cntr &= 0x1F; + sample = Wave_RAM[WAVE_wave_cntr >> 1]; + if (!WAVE_DAC_pow) { WAVE_output = 0; } // avoid aliasing at high frequenices @@ -697,23 +708,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } - L_final *= (AUD_CTRL_vol_L + 1); - R_final *= (AUD_CTRL_vol_R + 1); + L_final *= (AUD_CTRL_vol_L + 1) * 40; + R_final *= (AUD_CTRL_vol_R + 1) * 40; - // send out an actual sample every 94 cycles - master_audio_clock++; - if (master_audio_clock == 94) + if (L_final != latched_sample_L) { - master_audio_clock = 0; - if (AudioClocks < 1500) - { - AudioSamples[AudioClocks] = (short)(L_final * 20); - AudioClocks++; - AudioSamples[AudioClocks] = (short)(R_final * 20); - AudioClocks++; - } + _blip_L.AddDelta((uint)master_audio_clock, L_final - latched_sample_L); + latched_sample_L = L_final; } + if (R_final != latched_sample_R) + { + _blip_R.AddDelta((uint)master_audio_clock, R_final - latched_sample_R); + latched_sample_R = R_final; + } + + master_audio_clock++; + // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) sequencer_tick++; @@ -905,9 +916,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void Reset() { - Wave_RAM = new byte[] { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, + if (Core.is_GBC) + { + Wave_RAM = new byte[] { 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF }; + } + else + { + Wave_RAM = new byte[] { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, 0x60, 0x59, 0x59, 0xB0, 0x34, 0xB8, 0x2E, 0xDA }; - + } + Audio_Regs = new byte[21]; AudioClocks = 0; @@ -917,6 +936,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk sequencer_swp = 0; sequencer_vol = 0; sequencer_tick = 0; + + sample = 0; + + _blip_L.SetRates(4194304, 44100); + _blip_R.SetRates(4194304, 44100); } public void SyncState(Serializer ser) @@ -1007,6 +1031,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("master_audio_clock", ref master_audio_clock); + ser.Sync("sample", ref sample); + ser.Sync("latched_sample_L", ref latched_sample_L); + ser.Sync("latched_sample_R", ref latched_sample_R); + ser.Sync("AUD_CTRL_vin_L_en", ref AUD_CTRL_vin_L_en); ser.Sync("AUD_CTRL_vin_R_en", ref AUD_CTRL_vin_R_en); ser.Sync("AUD_CTRL_sq1_L_en", ref AUD_CTRL_sq1_L_en); @@ -1037,7 +1065,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool CanProvideAsync => false; public int AudioClocks; - public short[] AudioSamples = new short[1500]; + public short[] AudioSamples = new short[150000]; public void SetSyncMode(SyncSoundMode mode) { @@ -1051,17 +1079,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void GetSamplesSync(out short[] samples, out int nsamp) { - nsamp = AudioClocks / 2; - short[] temp_samp = new short[AudioClocks]; + _blip_L.EndFrame((uint)master_audio_clock); + _blip_R.EndFrame((uint)master_audio_clock); + + nsamp = _blip_R.SamplesAvailable(); - for (int i = 0; i < AudioClocks; i++) - { - temp_samp[i] = AudioSamples[i]; - } + samples = new short[nsamp * 2]; - samples = temp_samp; + _blip_L.ReadSamplesLeft(samples, nsamp); + _blip_R.ReadSamplesRight(samples, nsamp); AudioClocks = 0; + master_audio_clock = 0; + } public void GetSamplesAsync(short[] samples) @@ -1072,6 +1102,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void DiscardSamples() { AudioClocks = 0; + _blip_L.Clear(); + _blip_R.Clear(); + master_audio_clock = 0; } private void GetSamples(short[] samples) @@ -1080,5 +1113,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } #endregion + + public void DisposeSound() + { + _blip_L.Dispose(); + _blip_R.Dispose(); + } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 1628fb2944..7370214a3b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -156,7 +156,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } ticker++; - if (ticker > 10000000) { vblank_rise = true; }//throw new Exception("ERROR: Unable to Resolve Frame"); } + //if (ticker > 10000000) { vblank_rise = true; }//throw new Exception("ERROR: Unable to Resolve Frame"); } in_vblank_old = in_vblank; } @@ -216,6 +216,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Marshal.FreeHGlobal(iptr1); Marshal.FreeHGlobal(iptr2); Marshal.FreeHGlobal(iptr3); + + audio.DisposeSound(); } #region Video provider From bc023c11bd424b8a2f08ed540848acea5559e658 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Sun, 29 Apr 2018 09:34:23 +0100 Subject: [PATCH 178/339] ZXHawk: Original CPC DSK format images now detected properly --- BizHawk.Emulation.Common/Database/Database.cs | 2 +- .../Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 5f37e91711..052b1957cc 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -343,7 +343,7 @@ namespace BizHawk.Emulation.Common byte[] head2 = romData.Take(20).ToArray(); string ident = System.Text.Encoding.Default.GetString(head2); if (ident.ToUpper().Contains("EXTENDED CPC DSK") || - ident.ToUpper().Contains("MV - CPCEMU")) + ident.ToUpper().Contains("MV - CPC")) game.System = "ZXSpectrum"; else game.System = "AppleII"; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index a227d8a64a..f49c84989c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -178,7 +178,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string hdr = Encoding.ASCII.GetString(data.Take(16).ToArray()); // disk checking first - if (hdr.ToUpper().Contains("EXTENDED CPC DSK")) + if (hdr.ToUpper().Contains("EXTENDED CPC DSK") || hdr.ToUpper().Contains("MV - CPC")) { // spectrum .dsk disk file return SpectrumMediaType.Disk; From a7bf8e4ca380b9025ec72ed8224568e109d3074d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 30 Apr 2018 17:36:16 +0100 Subject: [PATCH 179/339] ZXHawk: +3 Paul Owens Disk Protection games now loading --- .../Hardware/Disk/NECUPD765.FDC.cs | 4 + .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 80 ++++++++++++++++++- 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index ec52e5cb1e..83269135d3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -394,6 +394,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // temp sector index byte secIdx = ActiveCommandParams.Sector; + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + // do we have a valid disk inserted? if (!ActiveDrive.FLAG_READY) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index f9a6d947ca..8f14e4bb82 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -163,6 +163,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum sec.ContainsMultipleWeakSectors = true; } } + else if (DetectAlkatraz(ref weakArr)) + { + Protection = ProtectionType.Alkatraz; + } + else if (DetectPaulOwens(ref weakArr)) + { + Protection = ProtectionType.PaulOwens; + } } /// @@ -223,6 +231,72 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return true; } + /// + /// Detect speedlock weak sector + /// + /// + /// + public bool DetectAlkatraz(ref int[] weak) + { + // check for ALKATRAZ ident in sector 0 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[0].SectorData, 0, DiskTracks[0].Sectors[0].SectorData.Length); + if (!ident.ToUpper().Contains("ALKATRAZ PROTECTION SYSTEM")) + return false; + + // Alkatraz protection appears to revolve around a track on the disk with 18 sectors, + // (track position is not consistent) with the sector ID info being incorrect: + // TrackID is consistent between the sectors although is usually high (233, 237 etc) + // SideID is fairly random looking but with all IDs being even + // SectorID is also fairly random looking but contains both odd and even numbers + // + // There doesnt appear to be any CRC errors in this track, but the sector size is always 1 (256 bytes) + // Each sector contains different filler byte + // Presumably all of this weird data needs to be read correctly for decryption to succeed and the game to load + + // Immediately following this track are a number of tracks and sectors with a DAM set. + // This presumably has something to do with the protection scheme + + // Finally, when the 18 sector track is read, the CPU supplies a strange GAP3 value (10). + // Perhaps the alkatraz protection scheme is reading the ID marks as sector data because of this? + + + foreach (var t in DiskTracks) + { + foreach (var s in t.Sectors) + { + if (s.Status1 > 0 || s.Status2 > 0) + { + string tvcv = ""; + } + } + } + + return true; + } + + /// + /// Detect speedlock weak sector + /// + /// + /// + public bool DetectPaulOwens(ref int[] weak) + { + // check for PAUL OWENS ident in sector 2 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[2].SectorData, 0, DiskTracks[0].Sectors[2].SectorData.Length); + if (!ident.ToUpper().Contains("PAUL OWENS")) + return false; + + /* Paul Owens protection appears to exibit the following things: + * + * * There are more than 10 cylinders + * * Cylinder 1 has no sector data + * * The sector ID infomation in most cases contains incorrect track IDs + * * Track 0 is pretty much the only track that appears to be standard (assume this is the boot track) + * */ + + return true; + } + /// /// Should be run at the end of the ParseDisk process /// If speedlock is detected the flag is set in the disk image @@ -528,7 +602,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public enum ProtectionType { None, - Speedlock + Speedlock, + Alkatraz, + Hexagon, + Frontier, + PaulOwens } } From 451d5654ce5e409544fd65d35047bb04a93bacb6 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 1 May 2018 08:03:14 +0100 Subject: [PATCH 180/339] ZXHawk: modifications to UPD sense interrupt status instruction --- .../SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 83269135d3..38a74ca56a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -2669,7 +2669,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // interrupt has been raised for this drive // acknowledge - ActiveDrive.SeekStatus = SEEK_INTACKNOWLEDGED; + ActiveDrive.SeekStatus = SEEK_IDLE;// SEEK_INTACKNOWLEDGED; // result length 2 ResLength = 2; @@ -2680,6 +2680,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // second byte is the current track id ResBuffer[1] = ActiveDrive.CurrentTrackID; } + /* else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) { // DriveA interrupt has already been acknowledged @@ -2689,7 +2690,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Status0 = 192; ResBuffer[0] = Status0; } - else if (ActiveDrive.SeekStatus == SEEK_IDLE) + */ + else //if (ActiveDrive.SeekStatus == SEEK_IDLE) { // SIS with no interrupt ResLength = 1; From b5eef04b42024da2ee545e16c5bc3fc2806c3b6d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 1 May 2018 09:41:07 +0100 Subject: [PATCH 181/339] ZXHawk: FDC ReadDeletedData command now fixed. Alkatraz games and previously not loading Paul Owens protection (eg Red Heat) should now work --- .../Hardware/Disk/NECUPD765.FDC.cs | 39 +++++++++++++-- .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 49 ++++++++----------- 2 files changed, 55 insertions(+), 33 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 38a74ca56a..11550dfb35 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -497,6 +497,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SetBit(SR1_ND, ref Status1); SetBit(SR0_IC0, ref Status0); UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + CommitResultCHRN(); CommitResultStatus(); ActivePhase = Phase.Result; @@ -527,6 +531,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // no execution phase SetBit(SR0_IC0, ref Status0); UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + CommitResultCHRN(); CommitResultStatus(); ActivePhase = Phase.Result; @@ -563,7 +571,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // this was the last sector to read // or termination requested - //SetBit(SR1_EN, ref Status1); + SetBit(SR1_EN, ref Status1); int keyIndex = 0; for (int i = 0; i < track.Sectors.Length; i++) @@ -598,6 +606,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else UnSetBit(SR0_IC0, ref Status0); + SetBit(SR0_IC0, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + CommitResultCHRN(); CommitResultStatus(); ActivePhase = Phase.Execution; @@ -786,6 +799,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SetBit(SR1_ND, ref Status1); SetBit(SR0_IC0, ref Status0); UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + CommitResultCHRN(); CommitResultStatus(); ActivePhase = Phase.Result; @@ -816,6 +833,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // no execution phase SetBit(SR0_IC0, ref Status0); UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + CommitResultCHRN(); CommitResultStatus(); ActivePhase = Phase.Result; @@ -837,7 +858,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum terminate = true; } - if (!CMD_FLAG_SK && !Status2.Bit(SR2_CM)) + if (!CMD_FLAG_SK && Status2.Bit(SR2_CM)) { // deleted address mark was detected with NO skip flag set ActiveCommandParams.EOT = ActiveCommandParams.Sector; @@ -852,7 +873,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // this was the last sector to read // or termination requested - //SetBit(SR1_EN, ref Status1); + SetBit(SR1_EN, ref Status1); int keyIndex = 0; for (int i = 0; i < track.Sectors.Length; i++) @@ -887,6 +908,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else UnSetBit(SR0_IC0, ref Status0); + SetBit(SR0_IC0, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + // remove CM (appears to be required to defeat Alkatraz copy protection) + UnSetBit(SR2_CM, ref Status2); + CommitResultCHRN(); CommitResultStatus(); ActivePhase = Phase.Execution; @@ -3478,7 +3507,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum GetBit(SR1_NW, Status1) || GetBit(SR1_OR, Status1) || GetBit(SR2_BC, Status2) || - GetBit(SR2_CM, Status2) || + //GetBit(SR2_CM, Status2) || GetBit(SR2_DD, Status2) || GetBit(SR2_MD, Status2) || GetBit(SR2_SN, Status2) || @@ -3498,7 +3527,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else if (GetBit(SR2_CM, Status2)) { // DAM found - unset IC and US0 - UnSetBit(SR0_IC0, ref Status0); + //UnSetBit(SR0_IC0, ref Status0); UnSetBit(SR0_US0, ref Status0); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index 8f14e4bb82..5059671fea 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -243,6 +243,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!ident.ToUpper().Contains("ALKATRAZ PROTECTION SYSTEM")) return false; + // ALKATRAZ NOTES (-asni 2018-05-01) + // --------------------------------- // Alkatraz protection appears to revolve around a track on the disk with 18 sectors, // (track position is not consistent) with the sector ID info being incorrect: // TrackID is consistent between the sectors although is usually high (233, 237 etc) @@ -251,25 +253,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // // There doesnt appear to be any CRC errors in this track, but the sector size is always 1 (256 bytes) // Each sector contains different filler byte - // Presumably all of this weird data needs to be read correctly for decryption to succeed and the game to load + // Once track 0 is loaded the CPU completely reads all the sectors in this track one-by-one. + // Data transferred during execution must be correct, also result ST0, ST1 and ST2 must be 64, 128 and 0 respectively // Immediately following this track are a number of tracks and sectors with a DAM set. - // This presumably has something to do with the protection scheme - - // Finally, when the 18 sector track is read, the CPU supplies a strange GAP3 value (10). - // Perhaps the alkatraz protection scheme is reading the ID marks as sector data because of this? - - - foreach (var t in DiskTracks) - { - foreach (var s in t.Sectors) - { - if (s.Status1 > 0 || s.Status2 > 0) - { - string tvcv = ""; - } - } - } + // These are all read in sector by sector + // Again, Alkatraz appears to require that ST0, ST1, and ST2 result bytes are set to 64, 128 and 0 respectively + // (so the CM in ST2 needs to be reset) return true; } @@ -286,17 +276,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!ident.ToUpper().Contains("PAUL OWENS")) return false; - /* Paul Owens protection appears to exibit the following things: - * - * * There are more than 10 cylinders - * * Cylinder 1 has no sector data - * * The sector ID infomation in most cases contains incorrect track IDs - * * Track 0 is pretty much the only track that appears to be standard (assume this is the boot track) - * */ + // Paul Owens Disk Protection Notes (-asni 2018-05-01) + // --------------------------------------------------- + // + // This scheme looks a little similar to Alkatraz with incorrect sector ID info in many places + // and deleted address marks (although these do not seem to show the strict relience on removing the CM mark from ST2 result that Alkatraz does) + // There are also data CRC errors but these dont look to be read more than once/checked for changes during load + // Main identifiers: + // + // * There are more than 10 cylinders + // * Cylinder 1 has no sector data + // * The sector ID infomation in most cases contains incorrect track IDs + // * Tracks 0 (boot) and 5 appear to be pretty much the only tracks that do not have incorrect sector ID marks return true; } - + /* /// /// Should be run at the end of the ParseDisk process /// If speedlock is detected the flag is set in the disk image @@ -304,9 +299,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// protected virtual void SpeedlockDetection() { - /* - Based on the information here: http://simonowen.com/samdisk/plus3/ - */ if (DiskTracks.Length == 0) return; @@ -397,6 +389,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum sec.ActualDataByteLength = data.Count(); } + */ /// /// Returns the track count for the disk From d891938d89fb8ace2256edd33861def92f09bed7 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 1 May 2018 12:45:53 +0100 Subject: [PATCH 182/339] ZXHawk: fix speedlock +3 regression --- .../Hardware/Disk/NECUPD765.FDC.cs | 139 ++++++++++++++++-- .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 4 +- 2 files changed, 132 insertions(+), 11 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 11550dfb35..197d761907 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -818,8 +818,125 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // we dont need EN UnSetBit(SR1_EN, ref Status1); - // If SK=1, the FDC skips the sector with the Deleted Data Address Mark and reads the next sector. - // The CRC bits in the deleted data field are not checked when SK=1 + // invert CM for read deleted data command + if (Status2.Bit(SR2_CM)) + UnSetBit(SR2_CM, ref Status2); + else + SetBit(SR2_CM, ref Status2); + + // skip flag is set and no DAM found + if (CMD_FLAG_SK && Status2.Bit(SR2_CM)) + { + if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) + { + // increment the sector ID and search again + ActiveCommandParams.Sector++; + continue; + } + else + { + // no execution phase + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + } + // we can read this sector + else + { + // if DAM is not set this will be the last sector to read + if (Status2.Bit(SR2_CM)) + { + ActiveCommandParams.EOT = ActiveCommandParams.Sector; + } + + if (!CMD_FLAG_SK && !Status2.Bit(SR2_CM) && + ActiveDrive.Disk.Protection == ProtectionType.PaulOwens) + { + ActiveCommandParams.EOT = ActiveCommandParams.Sector; + SetBit(SR2_CM, ref Status2); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + // read the sector + for (int i = 0; i < sectorSize; i++) + { + ExecBuffer[buffPos++] = sector.ActualData[i]; + } + + if (sector.SectorID == ActiveCommandParams.EOT) + { + // this was the last sector to read + + SetBit(SR1_EN, ref Status1); + + int keyIndex = 0; + for (int i = 0; i < track.Sectors.Length; i++) + { + if (track.Sectors[i].SectorID == sector.SectorID) + { + keyIndex = i; + break; + } + } + + if (keyIndex == track.Sectors.Length - 1) + { + // last sector on the cylinder, set EN + SetBit(SR1_EN, ref Status1); + + // increment cylinder + ActiveCommandParams.Cylinder++; + + // reset sector + ActiveCommandParams.Sector = 1; + ActiveDrive.SectorIndex = 0; + } + else + { + ActiveDrive.SectorIndex++; + } + + UnSetBit(SR0_IC1, ref Status0); + if (terminate) + SetBit(SR0_IC0, ref Status0); + else + UnSetBit(SR0_IC0, ref Status0); + + SetBit(SR0_IC0, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + // remove CM (appears to be required to defeat Alkatraz copy protection) + UnSetBit(SR2_CM, ref Status2); + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Execution; + break; + } + else + { + // continue with multi-sector read operation + ActiveCommandParams.Sector++; + //ActiveDrive.SectorIndex++; + } + } + + + /* + // If SK=1, the FDC skips the sector with the Data Address Mark and reads the next sector. + // The CRC bits in the data field are not checked when SK=1 if (CMD_FLAG_SK && !Status2.Bit(SR2_CM)) { if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) @@ -858,7 +975,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum terminate = true; } - if (!CMD_FLAG_SK && Status2.Bit(SR2_CM)) + if (!CMD_FLAG_SK && !Status2.Bit(SR2_CM)) { // deleted address mark was detected with NO skip flag set ActiveCommandParams.EOT = ActiveCommandParams.Sector; @@ -927,6 +1044,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ActiveCommandParams.Sector++; //ActiveDrive.SectorIndex++; } + */ } if (ActivePhase == Phase.Execution) @@ -1479,6 +1597,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ResBuffer[RS_ST0] = Status0; + // check for DAM & CRC + //if (data.Status2.Bit(SR2_CM)) + //SetBit(SR2_CM, ref ResBuffer[RS_ST2]); + + // increment the current sector ActiveDrive.SectorIndex++; @@ -2698,7 +2821,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // interrupt has been raised for this drive // acknowledge - ActiveDrive.SeekStatus = SEEK_IDLE;// SEEK_INTACKNOWLEDGED; + ActiveDrive.SeekStatus = SEEK_INTACKNOWLEDGED; // result length 2 ResLength = 2; @@ -2709,7 +2832,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // second byte is the current track id ResBuffer[1] = ActiveDrive.CurrentTrackID; } - /* else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) { // DriveA interrupt has already been acknowledged @@ -2719,8 +2841,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Status0 = 192; ResBuffer[0] = Status0; } - */ - else //if (ActiveDrive.SeekStatus == SEEK_IDLE) + else if (ActiveDrive.SeekStatus == SEEK_IDLE) { // SIS with no interrupt ResLength = 1; @@ -3507,7 +3628,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum GetBit(SR1_NW, Status1) || GetBit(SR1_OR, Status1) || GetBit(SR2_BC, Status2) || - //GetBit(SR2_CM, Status2) || + GetBit(SR2_CM, Status2) || GetBit(SR2_DD, Status2) || GetBit(SR2_MD, Status2) || GetBit(SR2_SN, Status2) || @@ -3527,7 +3648,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else if (GetBit(SR2_CM, Status2)) { // DAM found - unset IC and US0 - //UnSetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC0, ref Status0); UnSetBit(SR0_US0, ref Status0); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index 5059671fea..8e0ebc642b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -203,8 +203,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; // secID 2 needs a CRC error - if (!(sec.Status1.Bit(5) || sec.Status2.Bit(5))) - return false; + //if (!(sec.Status1.Bit(5) || sec.Status2.Bit(5))) + //return false; // check for filler bool startFillerFound = true; From 462d66c5497826cdc37853de9e45e4c99c877a04 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 1 May 2018 13:42:21 +0100 Subject: [PATCH 183/339] ZXHawk: UPD ReadDiagnostic fix (more Alkatraz protected games now load) --- .../SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 197d761907..2446067b72 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -1313,7 +1313,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // read the whole track for (int i = 0; i < track.Sectors.Length; i++) { - if (secCount > ActiveCommandParams.EOT) + if (secCount >= ActiveCommandParams.EOT) { break; } @@ -2821,7 +2821,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // interrupt has been raised for this drive // acknowledge - ActiveDrive.SeekStatus = SEEK_INTACKNOWLEDGED; + ActiveDrive.SeekStatus = SEEK_IDLE;// SEEK_INTACKNOWLEDGED; // result length 2 ResLength = 2; @@ -2832,6 +2832,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // second byte is the current track id ResBuffer[1] = ActiveDrive.CurrentTrackID; } + /* else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) { // DriveA interrupt has already been acknowledged @@ -2841,6 +2842,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Status0 = 192; ResBuffer[0] = Status0; } + */ else if (ActiveDrive.SeekStatus == SEEK_IDLE) { // SIS with no interrupt From e977826c5e73f8de823f80530bb3aae9414f0d04 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 1 May 2018 17:32:35 +0100 Subject: [PATCH 184/339] ZXHawk: Implemented CPU overrun detection within the FDC. This appears to have sorted more Alkatraz protected games AND N=6 (hexagon protection) sectors --- .../Hardware/Disk/NECUPD765.FDC.cs | 70 ++++++++++++++----- .../Hardware/Disk/NECUPD765.IPortIODevice.cs | 61 ++++++++++++++-- .../SinclairSpectrum/Machine/SpectrumBase.cs | 10 +++ .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 50 ++++++++++++- 4 files changed, 165 insertions(+), 26 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 2446067b72..60582674a8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -164,6 +164,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// private bool ND; + /// + /// In lieu of actual timing, this will count status reads in execution phase + /// where the CPU hasnt actually read any bytes + /// + private int OverrunCounter; + /// /// Signs that the the controller is ready /// @@ -644,7 +650,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LastSectorDataReadByte = ExecBuffer[index]; - ExecCounter--; + OverrunCounter--; + ExecCounter--; break; @@ -1064,7 +1071,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LastSectorDataReadByte = ExecBuffer[index]; + OverrunCounter--; ExecCounter--; + break; //---------------------------------------- @@ -1397,7 +1406,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LastSectorDataReadByte = ExecBuffer[index]; - ExecCounter--; + OverrunCounter--; + ExecCounter--; break; @@ -3095,6 +3105,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SetBit(MSR_EXM, ref StatusMain); SetBit(MSR_CB, ref StatusMain); + // overrun detection + OverrunCounter++; + if (OverrunCounter >= 64) + { + // CPU has read the status register 64 times without reading the data register + // switch the current command into result phase + ActivePhase = Phase.Result; + + // reset the overun counter + OverrunCounter = 0; + } + break; case Phase.Result: SetBit(MSR_DIO, ref StatusMain); @@ -3226,25 +3248,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CommCounter = 0; ResCounter = 0; - // controller is expecting the first command byte - BitArray bi = new BitArray(new byte[] { cmdByte }); + // get the first 4 bytes + byte cByte = (byte)(cmdByte & 0x0f); - // save command flags - // skip - CMD_FLAG_SK = bi[5]; - // multitrack - CMD_FLAG_MT = bi[7]; - // MFM mode - CMD_FLAG_MF = bi[6]; + // get MT, MD and SK states + CMD_FLAG_MT = cmdByte.Bit(7); + CMD_FLAG_MF = cmdByte.Bit(6); + CMD_FLAG_SK = cmdByte.Bit(5); - // remove flags from command byte - bi[5] = false; - bi[6] = false; - bi[7] = false; - - byte[] bytes = new byte[1]; - bi.CopyTo(bytes, 0); - cmdByte = bytes[0]; + cmdByte = cByte; // lookup the command var cmd = CommandList.Where(a => a.CommandCode == cmdByte).FirstOrDefault(); @@ -3261,6 +3273,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // check validity of command byte flags // if a flag is set but not valid for this command then it is invalid + bool invalid = false; + + if (!ActiveCommand.MT) + if (CMD_FLAG_MT) + invalid = true; + if (!ActiveCommand.MF) + if (CMD_FLAG_MF) + invalid = true; + if (!ActiveCommand.SK) + if (CMD_FLAG_SK) + invalid = true; + + if (invalid) + { + // command byte included spurious bit 5,6 or 7 flags + CMDIndex = CommandList.Count() - 1; + } + + /* if ((CMD_FLAG_MF && !ActiveCommand.MF) || (CMD_FLAG_MT && !ActiveCommand.MT) || (CMD_FLAG_SK && !ActiveCommand.SK)) @@ -3268,6 +3299,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // command byte included spurious bit 5,6 or 7 flags CMDIndex = CommandList.Count() - 1; } + */ } CommCounter = 0; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs index d3ff9519cb..f2e6d3e67d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs @@ -22,9 +22,47 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv"; - public string outputString = "STATUS,WRITE,READ\r\n"; + public string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n"; public bool writeDebug = false; + /* + * Status read + * Data write + * Data read + * CMD code + * CMD string + * MT flag + * MK flag + * SK flag + * */ + private string[] workingArr = new string[3]; + + private void BuildCSVLine() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 3; i++) + { + sb.Append(workingArr[i]); + sb.Append(","); + workingArr[i] = ""; + } + + sb.Append(ActiveCommand.CommandCode).Append(","); + + sb.Append(CMD_FLAG_MT).Append(","); + sb.Append(CMD_FLAG_MF).Append(","); + sb.Append(CMD_FLAG_SK).Append(","); + + sb.Append(CommCounter).Append(","); + sb.Append(ResCounter).Append(","); + sb.Append(ExecCounter).Append(","); + sb.Append(ExecLength); + + sb.Append("\r\n"); + + outputString += sb.ToString(); + } + /// /// Device responds to an IN instruction /// @@ -40,7 +78,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Z80 is trying to read from the data register data = ReadDataRegister(); if (writeDebug) - outputString += ",," + data + "\r\n"; + { + workingArr[2] = data.ToString(); + //outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n"; + BuildCSVLine(); + } + return true; } @@ -50,7 +93,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // this can happen at any time data = ReadMainStatus(); if (writeDebug) - outputString += data + ",,\r\n"; + { + //outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n"; + workingArr[0] = data.ToString(); + BuildCSVLine(); + //System.IO.File.WriteAllText(outputfile, outputString); + } + return true; } @@ -73,8 +122,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum WriteDataRegister((byte)data); if (writeDebug) { - outputString += "," + data + ",\r\n"; - System.IO.File.WriteAllText(outputfile, outputString); + //outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n"; + workingArr[1] = data.ToString(); + BuildCSVLine(); + //System.IO.File.WriteAllText(outputfile, outputString); } return true; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 07c64c9f45..9d52641d6f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -202,6 +202,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // is this a lag frame? Spectrum.IsLagFrame = !InputRead; + + // FDC debug + /* + if (UPDDiskDevice != null && UPDDiskDevice.writeDebug) + { + // only write UPD log every second + if (FrameCount % 10 == 0) + System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString); + } + */ } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index 8e0ebc642b..60627fc01b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -171,6 +171,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { Protection = ProtectionType.PaulOwens; } + else if (DetectHexagon(ref weakArr)) + { + Protection = ProtectionType.Hexagon; + } } /// @@ -180,6 +184,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool DetectSpeedlock(ref int[] weak) { + // SPEEDLOCK NOTES (-asni 2018-05-01) + // --------------------------------- + // Speedlock is one of the more common +3 disk protections and there are a few different versions + // Usually, track 0 sector 1 (ID 2) has data CRC errors that result in certain bytes returning a different value every time they are read + // Speedlock will generally read this track a number of times during the load process + // and if the correct bytes are not different between reads, the load fails + // always must have track 0 containing 9 sectors if (DiskTracks[0].Sectors.Length != 9) return false; @@ -232,7 +243,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Detect speedlock weak sector + /// Detect Alkatraz /// /// /// @@ -265,7 +276,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Detect speedlock weak sector + /// Detect Paul Owens /// /// /// @@ -291,6 +302,41 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return true; } + + /// + /// Detect Hexagon copy protection + /// + /// + /// + public bool DetectHexagon(ref int[] weak) + { + if (DiskTracks[0].Sectors.Length != 10 || DiskTracks[0].Sectors[8].ActualDataByteLength != 512) + return false; + + // check for Hexagon ident in sector 8 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[8].SectorData, 0, DiskTracks[0].Sectors[8].SectorData.Length); + if (ident.ToUpper().Contains("GON DISK PROT")) + return true; + + // hexagon protection may not be labelled as such + var track = DiskTracks[1]; + var sector = track.Sectors[0]; + + if (sector.SectorSize == 6 && sector.Status1 == 0x20 && sector.Status2 == 0x60) + { + if (track.Sectors.Length == 1) + return true; + } + + + // Hexagon Copy Protection Notes (-asni 2018-05-01) + // --------------------------------------------------- + // + // + + return false; + } + /* /// /// Should be run at the end of the ParseDisk process From 19c509e9c2f62c3191a9f123284b871cc561e521 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 1 May 2018 21:53:20 -0400 Subject: [PATCH 185/339] GBHawk: Attempt at some halt bugs --- .../CPUs/LR35902/Interrupts.cs | 16 ++-- .../CPUs/LR35902/LR35902.cs | 76 ++++++++++++++++--- .../CPUs/LR35902/Tables_Direct.cs | 25 ++++-- .../Nintendo/GBHawk/GBHawk.ISettable.cs | 2 +- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 30 +++++--- 5 files changed, 109 insertions(+), 40 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 04e9eb38fb..e20cbcd90e 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -4,12 +4,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { public partial class LR35902 { - private bool iff1; - public bool IFF1 { get { return iff1; } set { iff1 = value; } } - - private bool iff2; - public bool IFF2 { get { return iff2; } set { iff2 = value; } } - private bool nonMaskableInterrupt; public bool NonMaskableInterrupt { @@ -87,11 +81,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public int stop_time; public bool stop_check; public bool is_GBC; // GBC automatically adds a NOP to avoid the HALT bug (according to Sinimas) + public bool I_use; // in halt mode, the I flag is checked earlier then when deicision to IRQ is taken + public bool skip_once; + public bool Halt_bug_2; + public bool Halt_bug_3; private void ResetInterrupts() { - IFF1 = false; - IFF2 = false; + I_use = false; + skip_once = false; + Halt_bug_2 = false; + Halt_bug_3 = false; NonMaskableInterrupt = false; NonMaskableInterruptPending = false; InterruptMode = 1; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 02205e5c09..cf4576b297 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -58,6 +58,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public const ushort EI_RETI = 43; // reti has no delay in interrupt enable public const ushort INT_GET = 44; public const ushort GBC_INTERRUPT = 45; + public const ushort HALT_CHK = 46; // when in halt mode, actually check I Flag here public LR35902() { @@ -68,7 +69,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { ResetRegisters(); ResetInterrupts(); - TotalExecutedCycles = 0; + TotalExecutedCycles = 8; stop_check = false; cur_instr = new ushort[] { OP }; } @@ -255,6 +256,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902 case HALT: halted = true; + bool temp = false; + + if (cur_instr[instr_pntr++] == 1) + { + temp = FlagI; + } + else + { + temp = I_use; + } + if (EI_pending > 0 && !CB_prefix) { EI_pending--; @@ -265,8 +277,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } // if the I flag is asserted at the time of halt, don't halt - - if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + if (temp && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; @@ -298,7 +309,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 INTERRUPT_(); } } - else if (FlagI) + else if (temp) { // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution if (TraceCallback != null) @@ -324,17 +335,44 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } else { - FetchInstruction(ReadMemory(RegPC++)); + if (Halt_bug_3) + { + //special variant of halt bug where RegPC also isn't incremented post fetch + RegPC++; + FetchInstruction(ReadMemory(RegPC)); + Halt_bug_3 = false; + } + else + { + FetchInstruction(ReadMemory(RegPC++)); + } } } else { - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - HALT }; + if (skip_once) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + HALT, 0 }; + + skip_once = false; + } + else + { + cur_instr = new ushort[] + { + IDLE, + HALT_CHK, + IDLE, + + HALT, 0 }; + } + } + I_use = false; instr_pntr = 0; break; case STOP: @@ -453,6 +491,18 @@ namespace BizHawk.Emulation.Common.Components.LR35902 INTERRUPT_(); instr_pntr = 0; break; + case HALT_CHK: + // only used when exiting HALT from GBC, an extra NOP is added to avoid HALT bug + I_use = FlagI; + if (Halt_bug_2 && I_use) + { + RegPC--; + + if (!interrupts_enabled) { Halt_bug_3 = true; } + } + + Halt_bug_2 = false; + break; } totalExecutedCycles++; } @@ -506,8 +556,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ser.Sync("NMI", ref nonMaskableInterrupt); ser.Sync("NMIPending", ref nonMaskableInterruptPending); ser.Sync("IM", ref interruptMode); - ser.Sync("IFF1", ref iff1); - ser.Sync("IFF2", ref iff2); + ser.Sync("I_use", ref I_use); + ser.Sync("skip_once", ref skip_once); + ser.Sync("Halt_bug_2", ref Halt_bug_2); + ser.Sync("Halt_bug_3", ref Halt_bug_3); ser.Sync("Halted", ref halted); ser.Sync("ExecutedCycles", ref totalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index 78e64d7846..3325ffcc1c 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -76,8 +76,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private void HALT_() { - - if (FlagI && (EI_pending == 0)) + if (FlagI && (EI_pending == 0) && !interrupts_enabled) { if (is_GBC) { @@ -96,21 +95,31 @@ namespace BizHawk.Emulation.Common.Components.LR35902 else { // if interrupts are disabled, // a glitchy decrement to the program counter happens - cur_instr = new ushort[] + { + cur_instr = new ushort[] {IDLE, IDLE, IDLE, OP_G}; + } } } else { cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - HALT }; - } + { + IDLE, + HALT_CHK, + IDLE, + HALT, 1 }; + skip_once = true; + // If the interrupt flag is not currently set, but it does get set in the first check + // then a bug is triggered + // With interrupts enabled, this runs the halt command twice + // when they are disabled, it reads the next byte twice + if (!FlagI) { Halt_bug_2 = true; } + + } } private void JR_COND(bool cond) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 5de963bc1f..695c1a1481 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -113,7 +113,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk [DisplayName("Timer Div Initial Time")] [Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] - [DefaultValue(0)] + [DefaultValue(8)] public int DivInitialTime { get { return _DivInitialTime; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 209af304ed..d819b614d7 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -75,7 +75,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 0xFF45: // LYC LYC = value; - if (LY != LYC) { STAT &= 0xFB; } + if (LCDC.Bit(7)) + { + if (LY != LYC) { STAT &= 0xFB; } + else { STAT |= 0x4; /*if (STAT.Bit(6)) { LYC_INT = true; } */} + } break; case 0xFF46: // DMA DMA_addr = value; @@ -124,7 +128,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY += LY_inc; no_scan = false; - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) if (LY_inc == 1) { LYC_INT = false; @@ -177,7 +181,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // also, the LCD does not enter mode 2 on scanline 0 when first turned on no_scan = true; - cycle = 8; + cycle = 4; } // the VBL stat is continuously asserted @@ -195,7 +199,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if ((cycle == 4) && (LY == 144)) { + if ((cycle == 0) && (LY == 144)) { HBL_INT = false; @@ -205,6 +209,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } Core.REG_FF0F |= 0x01; + //Console.WriteLine(Core.cpu.TotalExecutedCycles); } if ((LY >= 144) && (cycle == 4)) @@ -229,12 +234,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 12) + if (cycle == 8) { - LYC_INT = false; - STAT &= 0xFB; + if (LY != LYC) + { + LYC_INT = false; + STAT &= 0xFB; + } - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -314,7 +322,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk STAT &= 0xFB; // Special case of LY = LYC - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -331,7 +339,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // here LY=LYC will be asserted if ((cycle == 4) && (LY != 0)) { - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -343,7 +351,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - STAT &= 0xF8; + STAT &= 0xFC; VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; From 282c3533b43a79474b31aea1191609087ada1544 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 2 May 2018 11:11:40 +0100 Subject: [PATCH 186/339] ZXHawk: Multi-disks are now supported for +3 disk drive --- .../Hardware/Disk/NECUPD765.FDD.cs | 10 ++- .../Machine/SpectrumBase.Input.cs | 58 +++++++++++++++++ .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 33 ++++++++++ .../ZXSpectrum.Controllers.cs | 15 ++++- .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 62 ++++++++++++++++++- 5 files changed, 174 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs index 2b60c5bc52..44cd6dd9ab 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs @@ -170,7 +170,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void FDD_EjectDisk() { - DriveStates[DiskDriveIndex].FDD_EjectDisk(); + DriveStates[0].FDD_EjectDisk(); } /// @@ -181,6 +181,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum get { return DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; } } + /// + /// Returns the disk object from drive 0 + /// + public FloppyDisk DiskPointer + { + get { return DriveStates[0].Disk; } + } + public FloppyDisk Disk { get; set; } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 978d815c52..cb80c28bd4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -19,6 +19,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string PrevBlock = "Prev Tape Block"; string TapeStatus = "Get Tape Status"; + string NextDisk = "Insert Next Disk"; + string PrevDisk = "Insert Previous Disk"; + string EjectDisk = "Eject Current Disk"; + string DiskStatus = "Get Disk Status"; + string HardResetStr = "Hard Reset"; string SoftResetStr = "Soft Reset"; @@ -30,6 +35,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool pressed_NextBlock = false; bool pressed_PrevBlock = false; bool pressed_TapeStatus = false; + bool pressed_NextDisk = false; + bool pressed_PrevDisk = false; + bool pressed_EjectDisk = false; + bool pressed_DiskStatus = false; bool pressed_HardReset = false; bool pressed_SoftReset = false; @@ -218,6 +227,55 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else pressed_SoftReset = false; + + // disk control + if (Spectrum._controller.IsPressed(NextDisk)) + { + if (!pressed_NextDisk) + { + Spectrum.OSD_FireInputMessage(NextDisk); + DiskMediaIndex++; + pressed_NextDisk = true; + } + } + else + pressed_NextDisk = false; + + if (Spectrum._controller.IsPressed(PrevDisk)) + { + if (!pressed_PrevDisk) + { + Spectrum.OSD_FireInputMessage(PrevDisk); + DiskMediaIndex--; + pressed_PrevDisk = true; + } + } + else + pressed_PrevDisk = false; + + if (Spectrum._controller.IsPressed(EjectDisk)) + { + if (!pressed_EjectDisk) + { + Spectrum.OSD_FireInputMessage(EjectDisk); + if (UPDDiskDevice != null) + UPDDiskDevice.FDD_EjectDisk(); + } + } + else + pressed_EjectDisk = false; + + if (Spectrum._controller.IsPressed(DiskStatus)) + { + if (!pressed_DiskStatus) + { + //Spectrum.OSD_FireInputMessage(TapeStatus); + Spectrum.OSD_ShowDiskStatus(); + pressed_DiskStatus = true; + } + } + else + pressed_DiskStatus = false; } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index 60627fc01b..e9b5252a9d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -249,6 +249,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool DetectAlkatraz(ref int[] weak) { + try + { + var data1 = DiskTracks[0].Sectors[0].SectorData; + var data2 = DiskTracks[0].Sectors[0].SectorData.Length; + } + catch (Exception) + { + return false; + } + // check for ALKATRAZ ident in sector 0 string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[0].SectorData, 0, DiskTracks[0].Sectors[0].SectorData.Length); if (!ident.ToUpper().Contains("ALKATRAZ PROTECTION SYSTEM")) @@ -282,6 +292,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool DetectPaulOwens(ref int[] weak) { + try + { + var data1 = DiskTracks[0].Sectors[2].SectorData; + var data2 = DiskTracks[0].Sectors[2].SectorData.Length; + } + catch (Exception) + { + return false; + } + // check for PAUL OWENS ident in sector 2 string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[2].SectorData, 0, DiskTracks[0].Sectors[2].SectorData.Length); if (!ident.ToUpper().Contains("PAUL OWENS")) @@ -310,6 +330,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool DetectHexagon(ref int[] weak) { + try + { + var data1 = DiskTracks[0].Sectors.Length; + var data2 = DiskTracks[0].Sectors[8].ActualDataByteLength; + var data3 = DiskTracks[0].Sectors[8].SectorData; + var data4 = DiskTracks[0].Sectors[8].SectorData.Length; + var data5 = DiskTracks[1].Sectors[0]; + } + catch (Exception) + { + return false; + } + if (DiskTracks[0].Sectors.Length != 10 || DiskTracks[0].Sectors[8].ActualDataByteLength != 512) return false; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index 38a281ecca..fa69c5164f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -82,7 +82,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum definition.CategoryLabels[s] = "Keyboard"; } - // Datacorder (tape device) + // Power functions List power = new List { // Tape functions @@ -109,6 +109,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum definition.CategoryLabels[s] = "Datacorder"; } + // Datacorder (tape device) + List disk = new List + { + // Tape functions + "Insert Next Disk", "Insert Previous Disk", /*"Eject Current Disk",*/ "Get Disk Status" + }; + + foreach (var s in disk) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "+3 Disk Drive"; + } + return definition; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 43724f8ee2..737829434a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_DiskInit() { StringBuilder sb = new StringBuilder(); - if (_machine.diskImages != null) + if (_machine.diskImages != null && _machine.UPDDiskDevice != null) { sb.Append("Disk Media Imported (count: " + _machine.diskImages.Count() + ")"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator); @@ -83,8 +83,66 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void OSD_DiskInserted() { StringBuilder sb = new StringBuilder(); + + if (_machine.UPDDiskDevice == null) + { + sb.Append("No Drive Present"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + sb.Append("DISK INSERTED (" + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name + ")"); - SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + } + + /// + /// Tape message that prints the current status of the tape device + /// + public void OSD_ShowDiskStatus() + { + StringBuilder sb = new StringBuilder(); + + if (_machine.UPDDiskDevice == null) + { + sb.Append("No Drive Present"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + + if (_diskInfo.Count == 0) + { + sb.Append("No Disk Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + + if (_machine.UPDDiskDevice != null) + { + if (_machine.UPDDiskDevice.DiskPointer == null) + { + sb.Append("No Disk Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + + sb.Append("Disk: " + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + sb.Clear(); + + sb.Append("Detected Protection: " + Enum.GetName(typeof(ProtectionType), _machine.UPDDiskDevice.DiskPointer.Protection)); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + sb.Clear(); + + sb.Append("Status: "); + + if (_machine.UPDDiskDevice.DriveLight) + sb.Append("READING/WRITING DATA"); + else + sb.Append("UNKNOWN"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + sb.Clear(); + } } #endregion From a374f9f4b8138cbbf976d7f0fa81c5888a25e72d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 3 May 2018 15:33:30 +0100 Subject: [PATCH 187/339] ZXHawk: Some UPD ReadID improvements --- .../Hardware/Disk/NECUPD765.FDC.cs | 32 +++++++++++++++++-- .../SinclairSpectrum/Machine/SpectrumBase.cs | 4 +-- .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 7 +++- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 60582674a8..4ab2c2d133 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -598,7 +598,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ActiveCommandParams.Cylinder++; // reset sector - ActiveCommandParams.Sector = 1; + ActiveCommandParams.Sector = sector.SectorID; // 1; ActiveDrive.SectorIndex = 0; } else @@ -1598,6 +1598,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ActiveDrive.SectorIndex = 0; } + if (ActiveDrive.SectorIndex == 0 && ActiveDrive.Disk.DiskTracks[ActiveDrive.CurrentTrackID].Sectors.Length > 1) + { + // looks like readid always skips the first sector on a track + ActiveDrive.SectorIndex++; + } + // read the sector data var data = track.Sectors[ActiveDrive.SectorIndex]; //.GetCHRN(); ResBuffer[RS_C] = data.TrackNumber; @@ -2710,8 +2716,24 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // set seek flag ActiveDrive.SeekStatus = SEEK_SEEK; - // immediate seek - ActiveDrive.CurrentTrackID = CommBuffer[CM_C]; + if (ActiveDrive.CurrentTrackID == CommBuffer[CM_C]) + { + // we are already on the correct track + ActiveDrive.SectorIndex = 0; + } + else + { + // immediate seek + ActiveDrive.CurrentTrackID = CommBuffer[CM_C]; + + ActiveDrive.SectorIndex = 0; + + if (ActiveDrive.Disk.DiskTracks[ActiveDrive.CurrentTrackID].Sectors.Length > 1) + { + // always read the first sector + //ActiveDrive.SectorIndex++; + } + } // skip execution mode and go directly to idle // result is determined by SIS command @@ -2773,6 +2795,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ActiveDrive.TrackIndex = 0; ActiveDrive.SectorIndex = 0; + // recalibrate appears to always skip the first sector + //if (ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex].Sectors.Length > 1) + //ActiveDrive.SectorIndex++; + // set seek flag ActiveDrive.SeekStatus = SEEK_RECALIBRATE; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 9d52641d6f..f2766502a7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -204,14 +204,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum.IsLagFrame = !InputRead; // FDC debug - /* + if (UPDDiskDevice != null && UPDDiskDevice.writeDebug) { // only write UPD log every second if (FrameCount % 10 == 0) System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString); } - */ + } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 737829434a..8c7f6baf14 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -129,7 +129,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); sb.Clear(); - sb.Append("Detected Protection: " + Enum.GetName(typeof(ProtectionType), _machine.UPDDiskDevice.DiskPointer.Protection)); + string protection = "None"; + protection = Enum.GetName(typeof(ProtectionType), _machine.UPDDiskDevice.DiskPointer.Protection); + if (protection == "None") + protection += " (OR UNKNOWN)"; + + sb.Append("Detected Protection: " + protection); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); sb.Clear(); From 2b53f9b9541bf96f4c908cf797118d7cb971dc18 Mon Sep 17 00:00:00 2001 From: zeromus Date: Thu, 3 May 2018 12:55:11 -0500 Subject: [PATCH 188/339] fix hangs when sound throttle is enabled and cores go a long time without giving samples. i mean, --- BizHawk.Client.EmuHawk/Sound/Sound.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/BizHawk.Client.EmuHawk/Sound/Sound.cs b/BizHawk.Client.EmuHawk/Sound/Sound.cs index 84362490e7..88a94e44c9 100644 --- a/BizHawk.Client.EmuHawk/Sound/Sound.cs +++ b/BizHawk.Client.EmuHawk/Sound/Sound.cs @@ -15,6 +15,8 @@ namespace BizHawk.Client.EmuHawk public const int BlockAlign = BytesPerSample * ChannelCount; private bool _disposed; + private bool _unjamSoundThrottle; + private readonly ISoundOutput _outputDevice; private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(); // Buffer for Sync sources private readonly BufferedAsync _bufferedAsync = new BufferedAsync(); // Buffer for Async sources @@ -117,6 +119,7 @@ namespace BizHawk.Client.EmuHawk int silenceSamples = Math.Max(samplesNeeded - samplesPerFrame, 0); _outputDevice.WriteSamples(new short[silenceSamples * 2], silenceSamples); samplesNeeded -= silenceSamples; + _unjamSoundThrottle = isUnderrun; if (isUnderrun) { @@ -163,7 +166,14 @@ namespace BizHawk.Client.EmuHawk { Thread.Sleep((samplesProvided - samplesNeeded) / (SampleRate / 1000)); // Let the audio clock control sleep time samplesNeeded = _outputDevice.CalculateSamplesNeeded(); + if (_unjamSoundThrottle) + { + //may be garbage, but what can we do? + samplesProvided = samplesNeeded; + break; + } } + _unjamSoundThrottle = false; } else { From 20cdb02de8fc4d91ed1c571c66e271153bcdf8a2 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 3 May 2018 19:29:47 +0100 Subject: [PATCH 189/339] ZXHawk: Started UPD write commands implementation --- .../Hardware/Disk/NECUPD765.FDC.cs | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 4ab2c2d133..742f9cf70f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -1681,6 +1681,81 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Receiving command parameter bytes //---------------------------------------- case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + // check write protect tab + if (ActiveDrive.FLAG_WRITEPROTECT) + { + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + else + { + // not implemented yet + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + } + break; //---------------------------------------- @@ -1819,6 +1894,81 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Receiving command parameter bytes //---------------------------------------- case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + // check write protect tab + if (ActiveDrive.FLAG_WRITEPROTECT) + { + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + else + { + // not implemented yet + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + } + break; //---------------------------------------- @@ -1979,6 +2129,81 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Receiving command parameter bytes //---------------------------------------- case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + // check write protect tab + if (ActiveDrive.FLAG_WRITEPROTECT) + { + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + else + { + // not implemented yet + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + } + break; //---------------------------------------- From 99b7adaf192c6fa92a7ecfd2257175a9e10677e2 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 3 May 2018 19:41:33 +0100 Subject: [PATCH 190/339] ZXHawk: Tape and Disk devices are now only re-initialized when a save state is loaded, NOT saved (TLDR: schoolboy error fixed) --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index f2766502a7..f7fd233969 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -374,10 +374,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } ser.Sync("tapeMediaIndex", ref tapeMediaIndex); - TapeMediaIndex = tapeMediaIndex; + if (ser.IsReader) + TapeMediaIndex = tapeMediaIndex; ser.Sync("diskMediaIndex", ref diskMediaIndex); - DiskMediaIndex = diskMediaIndex; + if (ser.IsReader) + DiskMediaIndex = diskMediaIndex; TapeDevice.SyncState(ser); From 5df212817c8c54dd16748a119b2fd202541267d0 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 3 May 2018 19:53:42 +0100 Subject: [PATCH 191/339] ZXHawk: state should be populated in the disk AFTER the disk is loaded (maybe) --- .../SinclairSpectrum/Machine/SpectrumBase.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index f7fd233969..f3ff00fd09 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -368,20 +368,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ((AYChip)AYDevice as AYChip).PanningConfiguration = Spectrum.Settings.AYPanConfig; } - if (UPDDiskDevice != null) - { - UPDDiskDevice.SyncState(ser); - } - ser.Sync("tapeMediaIndex", ref tapeMediaIndex); if (ser.IsReader) TapeMediaIndex = tapeMediaIndex; + TapeDevice.SyncState(ser); + ser.Sync("diskMediaIndex", ref diskMediaIndex); if (ser.IsReader) DiskMediaIndex = diskMediaIndex; - TapeDevice.SyncState(ser); + if (UPDDiskDevice != null) + { + UPDDiskDevice.SyncState(ser); + } ser.EndSection(); } From c1a6a16bb1b0ad46e591c7106da083a441815ee3 Mon Sep 17 00:00:00 2001 From: zeromus Date: Thu, 3 May 2018 15:02:09 -0500 Subject: [PATCH 192/339] fix xpad triggers to actually be useful? I dont know if they ever were before. Triggers are like halfs of axes, but our whole input system isnt built to handle halfs of axes; so with this, I make them appear to be whole axes in a -1..1 range. --- BizHawk.Client.EmuHawk/Input/GamePad360.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/BizHawk.Client.EmuHawk/Input/GamePad360.cs b/BizHawk.Client.EmuHawk/Input/GamePad360.cs index 00078cdeac..2d77a44485 100644 --- a/BizHawk.Client.EmuHawk/Input/GamePad360.cs +++ b/BizHawk.Client.EmuHawk/Input/GamePad360.cs @@ -169,15 +169,18 @@ namespace BizHawk.Client.EmuHawk //constant for adapting a +/- 32768 range to a +/-10000-based range const float f = 32768 / 10000.0f; - //constant for adapting a 0-255 range to a 0-10000-based range - const float f255 = 255 / 10000.0f; + //since our whole input framework really only understands whole axes, let's make the triggers look like an axis + float ltrig = g.bLeftTrigger / 255.0f * 2 - 1; + float rtrig = g.bRightTrigger / 255.0f * 2 - 1; + ltrig *= 10000; + rtrig *= 10000; yield return new Tuple("LeftThumbX", g.sThumbLX / f); yield return new Tuple("LeftThumbY", g.sThumbLY / f); yield return new Tuple("RightThumbX", g.sThumbRX / f); yield return new Tuple("RightThumbY", g.sThumbRY / f); - yield return new Tuple("LeftTrigger", g.bLeftTrigger / f255); - yield return new Tuple("RightTrigger", g.bRightTrigger / f255); + yield return new Tuple("LeftTrigger", ltrig); + yield return new Tuple("RightTrigger", rtrig); yield break; } From e4275ec7777827ae42492a47ad02d39a2fa8bb7b Mon Sep 17 00:00:00 2001 From: nattthebear Date: Thu, 3 May 2018 17:16:10 -0400 Subject: [PATCH 193/339] update 7z.dll to a newer one to fix some security stuff --- output/dll/7z.dll | Bin 1422336 -> 1677824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/output/dll/7z.dll b/output/dll/7z.dll index cea996e4f53f70579f56e5d528eaa9a9778c42bb..be29515b795a94ffa0ec09d0613d5eeecb7c7c71 100644 GIT binary patch literal 1677824 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 z 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Ï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%0DI5AGv4O|#43kWX<^)-E)~MYi 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

-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

7P1TynZUD#jFmN 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

$i8mPUQk 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 TUJiIU;N^gq16~enF9-e~Jze@o literal 1422336 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 Hc{(DeW5Aq}c`x3aYL|}Y z$Fi;R*P{H-9Cx^gO}_p!G#t!4?MRKrw0VJSaFJL&+L{Ryf0UAyru1IV*!^`Fky8Fl z2-ZgT;Yb7u4V@P`_yc$IFYx6n{o5!UrLYu@ z53Ig3*=vaa*MNGJxK)O7hE}0ZpvIRw)_Aa({l-=#-tYkco}9nkfCY#=Lxpc2s|qO+ z>tuMO3t84Q*!liw$Nyf%SECx0^)5TA#ms6)O|hf?!^{QfqH-15QJ0&!5VhuPyTUIn zvKn-eivQBiy3vk0NkyFkI;Sf2Pj=K{Y2So{eNaJDJZ+CdEqUs$>r$nQeUN1(GaOcnMxpq8X9()s_x* z8Xve+>;U^Ob*5+RZ@G?cJK0)CjN`w)c)??R@PcE=(kZR8Wf*2MC3scjwF{J6)ASIW zaJGOTO+pyP1ET-`MTD&)dQ&xMjZ%=V*hN7)X?yXD1ivwHx(f z@b(tFu-E}7JKfKh4QlHi4Avm;OnR)$F%i~OR%NxGfGYt4lA|YP^Sj|h^Lyw-XTa^> zBqKFr@fWDj{Y;t5=t7}SvUYFRJhxuF7En)zu#-$@)o_@42Wda;*v}1k z_ftM}5O5OOGg?LTOZ<>!;R*tCK&Bm}XI7Iu-A!S=52+JZU*-KU1nxYPhYTnSxxjF3 zl@eMniTYIsdX@jo{UAj{DU^kbfN3V&$8t~lC7YBNgun)`QLw3`NkzaU)8vGCrk{<{T-hsTte+=T60AYvrX}U3LSi{^#e(yeagqoRw+V-@)6s*NnTkWMuMo!y)-|%dA z)0fD`?5f#$xRUxoV}&RtOw0ctw6{HR9NHV80H>^`6xCn>aoW_-aN$@(^Zz!F?TIG{ zmpR@j)ZJ7hzc}WJ)9~sy=3txM41*0ueNJLAhQVLFFHpPHgeO5GrxSuLJ_3|rtzgP( zE#@OcIZpcrL5d7Yo&k`;0KgO#P%zQClCUo5_M5qk`w3Vy_cJ<>1QV`w60Wobyc-}h zrIN|ER2cOxTP77qIi!TM!q8UjP27mXO*a(32P=p3^nW->b7XB8IcTwSvBG*i1`ex- zi`SS28S)*WVHD~!u(S(gFwC|U;7|y_7?nz0K%iGj7ZU!Kc^>?o!L7n&3b)A+192F? z;J5_b&%B1K1K<+w$^VzD;X!POU=&UdqYKfRg+&0Sgpftt;U2UAcqxx*!dGY;z;=c)Jql`crFVP6dD6mR(g|=w&Y0SgS^Hlf zz2mDLk$1Bt?~5CTO{@Cou>wDZ=N(YRNvf(IZTAWUH(=Z z)I54@K&y_h44YWXP+*E(V@su)_)Ho;G*?`D|IzUw1G*en&OCZN?P3iYAb&jGfQE?o zs7PXyQBwCQJsw}m_;p8(NBw`wj7-PJfF{sxR-T2AnRc^q0h2TqsZ~|Q`_Xojnr1Zq z9UBBV$auXLCV;v@dTS(6xeroAKyK;fKZyqV4WG-@MfV6T2 z(po8t;?ZK|3Z$)6APqq#NL#5u8UqTTjc|U}L?=L6o62Rx_MjaAX_Eoc?p?+<0e4d% zt(9V+X0pj}TKWqnB>^;GOezqU{uW4cilzm6NMC#v9)SALv6N9v*h27d3z^bo#7e7- zcR3P*&eG5YUm`V)o;Z-d}v)OuTVD^7ic zx!j9?iZ}Q3mnvMci|s3kT`F6Z{Y*~CA_}^38gBREHy_3;cx=a~z;TvxK_zP7OH`Wxmf0E^V>+giO*n_6 z_Gbj|uLN$>{2RQ{dA*E#1yrnrfsojY;awj$ybA&gNB&i`IuSx}pMh579whOIq38|h z7>2rqN4n*Nj_BNpE68so^gDY^5gGa97|0ck{0iJkb}*pI9DpO{{P?bCsor zb(<)WOgE6taP&}|(4)hg=nOf{gzFxx-9v|W09 ziIgc3-LeVavc0>nwA;I+kN`xw4t>rkcr0#fQ_;Ymi8=jF-qeu;?oLnzL5G_V~mC z&{E_a3Z`C()cE4q{x@EwwF##B!%rD5Gn|Z9kVQ^}%CwZyX+XY2A)x)rVOe<%U_|o} zQ1-JFqF~cIK@ahXC&122JG^=Rrr{4BQRX^=UXqHBFJ~jiWG2bXpPd<*S-#5uc3wsm zY5C(jS^htd9xs>uZ;u`?SH{Al$IB_1b@X^S2|wx+PkMN2400dDSSS1ABegg70Q6-+ zGIc4QBIM=F;}(UWj>v30_)lD`f|oPNRQ0|S@9h!z??lig2c0$^)#>Lhe;3-4umNkx>}eM)acu{J>a(0ezF0wf2$Iv+%b z@E)X=Iv(+n=a`KmKSY;pzh@@e&asjFCJ%_`#%IPM*0=@B!`K$-aDl0|vrc>Yy*h2^ z?nrOJ{CDdlKSIeY*lmTF!puVbpTGq$xUh4A+^P1CZRJ?%^p-o>Z**xfmo{N=D6f4H z7aADt+cjg87Fp|p=1=d&KyT~NwP5KM%zrJ_&m-G+etP@RJlQ(W{P$Ch>)WsR-C+1_ z6<>f<4540j3wb!6iGK4?iCgxGi-H+nK*qE_?Xdea1+9;P*69e3aj$4qU4VVnHrLRu z$N|s%_rPH^Ck*T7kDJu`NzK85tIau}Z9oW5z!1#-<{i``PtXkN76CxpJF(=poQdOa zAB0&wZFxyN|JSIgx83_ye5T$tf0yilvI&VQ%FHTiFwK;Z%MuT%D(rcNF3M^(Rj-RC ztQBdw8dN=K{6zD_Z3rPVWOGYpw)ko4!zpS~A30)j>LXWNlKL1VF2sk-o+lQ8`rZQE z#9FajYsy|F82*wSA@yPsLE}TYg13n2%)zA|=zR%GJwD6BF}TDE1yfdr`u@2J(in_7 zD-~t#4Vy2cc=b41F_^E5gGyPlB~R=#-zpsff`6*5*#iFA)T>i$vtDz=dh0b;tio$c zCA6&MhLY<`&KL>@qCWf_AnMUOOCWvV6Dz8SCBR^9^lmu6X8OeM5!5%&+eOT?YNcFa z(N50Qe3{c&&Y^LLw=_5C!ttTScF1IuN7 z?`B($$I@Tmnv?>unZqWX90;G_Dkz#Urc(&-Xbo@muw1rJtYwZ6^;D ze~E$+v{$99i&oAN@7nJ-idXITo5kaDdN)Ta=Zf!IuOV@&^;$0i*6VyZ=O{P2xB;60 zHqKc8!!3B%mY>P`t_EEE2jlek^IdoZjSm<+oGlv^aDK@i0)RYE4y76Q6(rX);<{3C zyZgpfOX}!mvDS(R6n_%lD_N6z{FyGqnnU+HCP*LDXUL;8Bb$zhB$tv6z0rIO+UibIW3B2=FZg=#3pWAl(`Yp|Z~X z;9R!k1)N{A%Dt=ODmY_N{TbQP;gmF6y(?I|xJzPAl3y1zPQJ_-fn$9NsMf8ZHeG%* zNIx&r8KH?M{bOCu7h5)-2Z4-O~WdQdgCy1H}zs!!?t40{?;RSw2u_ntv&4+AMy9 zP{#fZqQp=O44n$txT=k7Kwa6RPZc(>0YlM%mw3~@aLp)uGs{ny)%mrK6}gY!x1b0< zCn0>|6|$sT`zfhysyu2oi!+(-FEU+aKj|K|(jA2Hy@@TMAx-Q*>YmtwTHFs}CdAu_ z!Us6sXNXT#PwGH7JyW#X5rtwk-XZkKp1s|AWX~?N>Ulac-v=`eG%2Z0CT;kE~@p! z$_}<@2V-T2TC_v4vcoOf;aJ(%E!x+J{|52jh-w|z-}tl=5ba7tIX?`d7QAYh`p;Jo zIV$NRd=<-} zeREod7B`JN}oaMPay_kUQO7_JTtqlG6rB34!&B#mK8Nq-5@Uh%L@ARAjUOPLVP zfC;Qa{N>qtkJ`>A;J}t#O>#&Wl z>+$Opl!VSZZjpy;cl>oWQ;zQZLu z08AY6lhoL#dt+*0DXgP+t=wDLxL-VEFVI(&11J{aYpXl2MKua8#N z8|Cw(mGg~A19F^l9pI8BEQ^-NP_&|jcA3&%=3);mt zGcAuvq{V|JtU{=X2Ha0HfEmoF$5jTN{|HrRK)YyBq4MC5Kfnizxs9PlL>~Il#eBG* zkLY;RMHlmdIEev77xRI*%T*j7h?D3*bTJ=@o2laXK%B%4qKo-JTpa`B_Y)C7(fo=< zh#{bb3kak~7ZDr+LK(D3d|Biafta^nHRf|?^dx5-cM>UQr_O4g+U}-%F}=p+IbsY^BUmNTq2EYi zoo|JMbnxOX7+L^C1y?C@iagl1i1nZJ_D)#mZu(rVqT|k&S1d>w5jin26zta7{G7v| zxu*FpJeDwvDd|Ff)+6{<8}qn_j^oBuN25R1O44cxfPq*9eNW$ne}_EdA1mxe@-X23 zFG)rt)wD#c14E&unZz^-4OBbA;LhWcUpgujXef1r{A$RL`w@q{Nz!R)0$yrvqWDhn zdwHdTwl*9-U=LP{KYbTTYp2X4wxUPJb@o=y^u-Xx%6*KO6`K#RYPUX_r`2FX6Mw9*3&pMh!2x??oA?`wgxIO`jX?1i;k^)Lj*nbD z)Rh%$%HNJ;r8&pDo5!J=fD_(T59H48hAG}x5s~d)d^Un(s&qP&m!y#tM5&a08EJ2DiU6t)dVt@1x za?CNbFRWI$7Nu%yX;^wch)uDb#s=B z!i?*am*|aaTTm&6hUct!9Q#D7u0G69c4scPS=|0?lSt$?F%^g*r1%oNlil`FZ*R(&-ovIsOOde>yh{BM|rj2NmYYEtCKb2IM^(VGt8^?xw7{7Z@KTS zbi-2VYOQqHD&74{kxqOmL(Lsg1QqV31iEPpPF}~!h%x_Vj?a}h*$cYAcwA!TR@;_R z#m>lYF{^H!VGVNzo=}Z=_D1}Zc!#-fY+e}e`QJ}v?5BR{Kictfr{{iWxC>Cvp+_4_ zu;+u(#!~h05duUTb@f%P9yNIQqm6Ub1HQVMp&k#Z3K`Fh=5pE_=O9Fn{=7jYdj#!8 zCK3aXnuG$qqmkkz_z%#N`-jay)S`_Xd?Z_`9ts#Yw&DvVb@CC%i^m1w+p6jW&hEyp{8mN96N41)awJe8vEOW_yO6m>l* zvp|WbPv@3a{Jd2jNH&O5nM>W1FX-^0>9XHvvftFm1L-K9yGIT({Mz7l!~|k2jvIA< zrk0UpOv*A-Xf9xAKeobu4?u^-t#s@xaEkr24n%i=MM52GnWwK&pS)MzGRIoy>9)eb zG8mN@`jCWFD1z%s1Blkpbgc+~0I-UO*)Qx9cV6nmbq;>c#54P0H6L$9;*aJVbXCna z$e9u#F2QGidywrb5Vt>UwU24~wjc6F1I8DL?++MXB2Kj9)%YwXrc4C zxAG&>#=dvsGfU2$Cd4xod+3OC-NG5fBzHu*Q?L6{uRW>PtXs{rhCR({UCaBtWrW}# zTr@y7?vs8Gwg!z2;^iNaR2I&K);7#_#z&s@Q))78AS-aCj0$~#(9^EBL!VF?AmBgE zl_YQAomx9?k&5#mu7C<%(4`x_Q9lbh3BXJVesMyltAv}(Jld0=0!kp~B9bUbl$-DU zoK*_8b7U0E#)|y0-&z+Be_^eQxcTN05%aA;+^ODXVYsEmo6W+lY<6U>@e~A4Ul-Mw zCy)4eb-FM_dOQS6u&z%3<(EeO02c$iX~NCja*Oc}*g!*x?!cp->fBgdl9 zBn&-|JQpv~3xeYp7PywywTm%j;eb%hQjzeg*X%lalEu`I2ot-3l10-NbCou zxM~zKh{GT~^Fqj8P%Q)aSf_aQdpTIoJE=qRb|79}6!>blK6eb{q<;VPT-Hr8TocTkfFAuuk!VHYaXl zr+JoOLq$geBJ4Lf;(+GyTrUD8*(;vgszNb(xxZAeQnlu7Jznn7zg_qIr7ucZICs+% zXc{$YXqbZoXNt=~3`BiBAna&JX_(nrs0xNn1)0MOm@imGq)oIB3SOM z%v7V8>V;fRRscz3N*H{i<94PUo0)dcN%q8^hP2v*4j8U&8RRd$#NG63RDkv2ayP$* zMpM$g#@_<;TjKZHN~gn#*?1)Y$n`W-AXTH!r+iiomz|}Dfja-WBBK}etTXX6uy!kP zm@RuWGu7p(mMQ<^xIBPV5Fh#3th}sVNWL$0g)g!SNUv>bW;sJr<#^Mow5}}d)@&?D z>>%k)dp2tL?RBhyWzR-WWaAD$m(hjiPPB*ZLlWf-+;nEZw5&OGauk?1yFJxIEIahsi z{V_AwbDVE6WR_QAw8wSRSa4grsVSgG+qC-RGt{|}~Z ze3tnso90)dbftEq2}xY|)?2z#(jE|RgS;D@eiDKBG*??wb%MpALeM0 ztfo)O(1enLbJriJZxfyvHa-;#G9p| z6iIhb?bg$IAjk-G9ffLzfXGu4e*SDGSCLPI&XMgh5T6o;e3g@+b zJmT5^NFIkzU}NFjwvUHAdlOHVA~73nUFwgmBhL(ZfAbLh3G#R<5^DnpqF}UF$8<;vg7_zcA_?mh{IC6hD=$)ZiWB2Rhi28n;*4`>EU(28aBd4YDM9_YV;*=6VAF3F>yac_dkwHpr`De1^!OcctIRBh z6V^+h%XB#Bp5q_}`c(H&8bSXES2yE_594EiYUd7AZRD^se3w~yWoG3Q`c&TV4|X12 z2mHo=x8lV#V_PIXm3-D7)9=TKgsi$o77 zyJ}7OfHz#5V!oPZvRuCw{nTh~K$Veh2UtkMZJS1MZZz@2tJZX<(UNEvXJn**ERZ%*1atJmfwCTy={?ebbbD;tRR)vNjaHbZ`TJC^Pb(kl99SXg* zn>1J^Ga}3lskBb5bW$)hSTo9MkT6-Ml^U{u^I$R_dis&&NcN;Fl(XEC%xe?b`@0Gq z`*$LICBjE6HkKG z3Rsbc41c7jSDDi&rw;stj?n3VRq!5BE$s#TLFy5}zC^ihJXlx+6syAfk+iTeZU}Fn zZ_`=SBPdZb;Ab3n1I>Hb9a+i%6a|y`Z^LI*hu=8$UVrHt*;9?8>2MMZp|Y*wF*GDl zyID6DEkhRF$nP|!RvU|`Ar<#$=YT(adkNSzHM$db@5f|C$ua2#Jd6U!>(QONtOUIs zY)5*N6F)b$!`H)cvBc%A7S4j}TpXGXr4gSO_!{ngeg@f(Hra-{u+G(n-m9km>Bn4l2Yit=V5 zt$qOz*`B#hw<;egE8mdl0#m!Hc8|F{;4@H2^}`0alCnZ4m@DC5TuM_x+DR6qhun=2 z5IET0tnf(6BD^a6AZdH z4pM`zape6FlBN&tnf{jR3oH0j<}dvd7r(oyLSEG6YG-$hhq%-YtvVje&LM9Dq9{RH z&Uh5Tpe+T8x58+R13tg0M+k{=B*3livbpRhu6nPD>!69M#`F?+WH`#eZaB?s_=|X< zb&)&JUhR2@TqhLLn9Um;O9V~Xof&b0?a+$p64!Anp@q$L7*;NfIXP^q-6T+;8W14b z!6#Mgs{gsZ$ZFC#vPtU`?}H%7IhvD0iFC@a0CBOYKUkVsR*tVuJ!$D44 z63pc#63Q7ljWprYA6(*0ZJ!*^87C!i?m>2LoS&dk5YEA#(zsn!kPkBk4 zfHa^YVn2`Lo0*pTnQ77F3J1WJ77G@ZnT<}2Me%=gv>-%_t+xVCmvs_GnG0!z@Ec{- z@W~u>w!$OxoS<=hAU-_{oN(H-(0VU7w|M_k@#oRz>UbP4T-H}%O678BsGpP-nvaHQ zAZqyKKD~ zD)*(e#%FWeee}My?Is?5+g)~wIj|LY;kL_S_sngVsFI)ndy1Rxq*5$cchmU@vlE<$ z4?tM>DHs9Q@7^FKmtGUE7vkvPR`eHl%aO76XVBSWbRk zQwHDCKp(Yt86=N_ql4##q~7yvxE(L-&V^{81z_FHf51^p$9Wtdxq-#gcS(ClkYZ|D zjL7s|ZNvO?twPT*3w;B#g;}_q`}JJB2O*io?IDHp02V}zEEQ1N-84jQgcPi>OZ^vV z*KF!$yl_b`;u_$DlLk#I2@W7K-Eg?J-aPC{NNeD*IMh_J#yoiAFLv zW^#x%f5}_z88TX}NlFdZEm-9CNHyJQkJObiyq`X0S@nJeQZ$F3djI|yPWoi7eAwel zc`}O8SjVcVM|CqEdcwvzTJ#z7#JhD|3zOyV39z$wfb&E2{HR3;)69GV2B}`%73+y|sPu*!b0rFn&GyR2A)`xdKb=yl_Qni?n^oP$ne5I%t zrykZlkf}14dQd1nOWA6`Q2iJLd0^QM2*bL7SCmh{X^US6_jGT&n;t<(N2SaM38%Ua zeA2`MvYm@;L1PSLlKS)LVU*K|LUN`QZIW(}ibNN3kS7Hm&jhb9_MtskEM1JQQD-%a4xs2Y(odq|0TeO50Ql@uP^W_*5tzBe&O<%WVRk==OFQ$7 zL>@>S&NM0ZGRt$o2?y@(M<6MGyY4=Rlola?YASMt*y}6(&X{2bH`bZHrtI|62I4Il zaT#||8GqR^;=gx{_+fUur0vb-<~FHgaQn9%vjDMlpy~2=Qsm%qkSE6KiX1%A-5kYB zMZCu02M4{-51hzT0l$+-4Ko(8#}IkX1+cS9BRY83aY^+LDGSJS{_$7O_PZn3w z8!wqf$<7L6Gif}faE2(laJRv{7jPBm;x33{;tMvq#1s(xlLaw(LspTWP)-v!KbdkI zs4-Xr%=BN7SW5wj$okn9Oz1m~y14X$^f~AoUn)LqKO#OuhA*D`u(j{gX<2hQo|3kc z@>^a5iFAZsjWbC4E$8T@{Fd*-7krfGxSRgTH@GNoqJwNT>zg-{3HfB@f*a`{T2@;I(fVmvT4MHn(+-xiNPv$8B!;2OHb;ONki9V zBpc0SlPh8of-H7J7cW5$7k%hrIYr{SRn%-jA9OL}cmNZaCInf8a+rV;?85;mkSDkI z_mRL{1~uY*C8)!EYVRt8bI zK>P?gwg?41dPWHXz*A5Gq;E?@48}<~g2cWrWwMWv%!6dmK|?_DHB+vQRA2N~+~Vdu zBKD{1r9jM-FZ4s}xh63MAK0LZ7oq6obD^)=!#u7`dbv6gnak48Izqo_7vx>U@`F8C zWgV3T7*ZUhqtxS6K8$IQte8?D-U0PB2%a`7fj3G=ofdTl;CU3pdg&2x51ea7;Q=x} z&4c6~ah+LWi7HAS@Ew&CRf(lYQX=kGCCc&Yzr@mLWhqpreb7Jl#me=TN?l+n z43emA%i^hXhO)YY)*gL*BOcu>5bw67r^(1Kc@}H!cqN}r|C#B$gx5u3563w^xd4hY z(F>#h>5Beme*8+OXabRz;U1A~rYOmXzaH_vmU%k<0`29K9C%K~Ul4y)(RtH5%4c&k zDC%t~{fgLomwh|eZWrM?|7FC>z75Jq(>LBDK5Vu6F4vup>#lYaA(jVw5g;9V@sdxm z1{uPO``_e(B#Xjl>Z_XibZH2n$$+9$QQnjIPeHxI*gZ>iZ@B2T)0Bno#n^#M6%tdO z&)w0HAgqlw{{g>1ymv?5%W+;?>U;|Vso;xvD~je->GW?8g&$8v?7SSBmRL_80$yc` zTmsh*u=|3waP=XRR+Fl^2FykfFi=zZbTkW7!b|9CLhkP*G$iGU<*gu zvjdU!*>%wVtsA;M1mz!=IuQA!TO@7SE&$@E>Iuo9Wt*HS8+K?x88%hlwt8&e)(@~0L4)v zuLWAKPzMS)|3#)!I;$l<7^uWjY}rWf2zP||uc z_+6-{7mvB(r5CJKrC@6&e3kqEh!l2t@(vR_M*NL-ysh_duUNen$i!!W8{FIchgJK; z8|b=R6z5^s;7N4iJr@S!xzDd33e(p)Lc`Juc}!RaUQwY9TE85`&DoylK{*`6)8?}z zV_k2D?Tx`)_kZ1ILe2noe=z_6ECDa(sEki7>m!20s_dBm~$s*LfX9ij)TeV{MQCc-Jy~tv)ziwu$wy;$v z_i2^g{pYYA0Lr(Bo?O{^fD&7UKJA?3JifcC?@Xu9W2snc$E)){rAHy*eYHJl!E`@6 z!i`pI(f|$pdp^Rk->*SWb?hg&y5C1z3l;G?h_h8?#ZEm(bMCKjei(4>Z8&t;z34bZ z`n}!m`@xLPij6oY^6o-AfqO+xDz(uW>0a2@oj0_r;m|kXN}$zp4xUmfxfWexFE=%0 zEuElr$+-wPw+5WAaxOqAaX`LP-ZQr50A_rKb~YUPI$Xh$O2uE4_MlnPl5bT~sc2$J zf0iYI+w(ZP4p$zz=kJ0iHRoFaXS;%e@_;E#D>FWF2$CoOYqb!5K$5dCD&g;Y_uX?P zhf1LiSynzHujN#SAMQNTJdW6-l>|crkZqgT{)ssV{U*n>fJw9DR!MdxI$534QBmX$ z2?XqQVKj3-Z9X|a05z1Y;Rl|#my?}d!h^d~5hh4Uk1rKh+wt}~Q?#{udxen{XB-&2 zJ6Z!o^5p1MuIN?y@o7#m+9|1MPof=IF!^ zUdWEr{V+Sry_ol6?=E*!63@CaW5l4wcY7CtvJL#MJ-e>)Kz4VytnHxO*|5_LO-O7h z^9GyspHfhyi-Y?Jj%&p5yozR552y}Lri=;8>FS+|KrIQvu& zl0)5Vq(sa-1aVMyxtHv<=eXWhTDIrGXyy>w^U4p->oPpW3(xgCuchLNR75}XS|sir zFuq3oWWab`l-cnnjt)#l(q14@V3%zBWjFk!o?J78?VP2)oq|+ zbgLHW&hb05HSl(*NreS9Tm1QBKzhlrf|NXkDqwbbKFlu1XGp)>&`0TI0^`$)@e6X? zi}ryUEe{Z2{vN3ki`#9maGZGgI4&&k^g+-8u@9xsFC3@62-dF*ZenmA^x1M~^5YXy zY=*jO!Df`VDnT=5EYwU@>s1b{6~g)8x(gum4Cpomk8EKywvKz}UdNvf;| zh-qkQ5Y9*%WRk6Yug{Q)2q&XY$r5O278ma66e56q9l5G|^mtR@GUoO6gf3%UT*11r zL5dB+FUtY_qSRTAEFm_q{aEp>DZ~^Dy#D=}#8J6)HYtp(o?#&3E5N!nS7Y4H&&v z=RpRO5}CpH&s>v?5ohQ|{&?NEyhIYS&VCQIcsn!*LnXlt>n|bt9Po&Ly=x~lX9gtm zQg}1*7<%>iN!8(#Dj@oF0_Z_=;dzLAVe(uk(80}+6OB3Sy=lIC$&G*o{_%IuUvQOX zY^^dn%z_}A2U_uzYAw>1<2PRSPM&|yIhT=<4`(h$b+zBS>2`Mo@Quh?m*0EM{P`j| z#NTp9l~%kxI%B2=94ON2ifg^z^7-@s8=WSUaW|ib$*nLxVs-BO#^3{PWz6~l#?84H z{>d)m3Q(hbK#b999h(87SC;$}n8HSnlwJB8=L*1#OBcdllD^D$bI(q8i4U&9siYF$ z&&1$%G6ANIq;Y`MYWM1Q#>~6a-x8kWUj5ma$P1h#XV_<@9DLh6wEIs8gjXlHx0y~g zqKZJk=rDuvZTNGph*r!_4)wR(Gbvg%9feJfRzasI93;jW*?_UB#Tbvm7$>1F0{`En z@g>Ld^#%Egi~m+dOVo^Spbd$yCm6vm*m>D19#mi0Uj)dD@P#>o|6z0{#kiz>Gn;-D zhrO2@pSI2$>r6_I_lQXY#mBh`#dkB4q~(vRy>Qfc-0-gZ*j6C#=iIIqoBn%%_Qks~ z>f+CtNmBJ2an7W;`v8Cr=(J#&T6a7Q<^k~oNO}-exxjpW*L;pKpC_45w|e3@suoWB zeY*GxVob=d7!2j#(csq6z2>~*d)K=#x3|G@J((WgZA|O}oQRu*Q1Z8^0m4I{$(4xc{00b60Hh z!z#^hTU!2mc8t_*D;S@wkKF-Sf~^QY6Y!DX zZkE=XuyHHmmkwb~KJiKoqyiY#%VV1qb^|7?a9*ji4$_QtZX;8?NsJ*_=z~9;iT;*n zId)#-LHzn#{>r`Qk3PE$kMK$UmYa~^uT0>N{*`+*aft8W>SzSv75FRDo`p5~2bLjR zo3J@Ns$#+`;o6pTTC4$#W|vyo_v@`XPQ(9*r=0_-rQgR7}_s9<3qQiD(O} z?}$jKfH$du2>~C!U**sytP7vQVn;H4fxq1KbDk7^AE8p6jamb^SeQNX_u=tjvB(SW3zaGoOUwYF-Bf`-JJ#~*RsV*@N|QNwpatkm#E1jK4SXlJP*zJ z208KZ$RAm1d~&rCPK|I>p&bs+Z;>Gf7dmIyf*!F1`W|vgkh#cu$Ot4tp)$Xykc)%= zG*fXQSSW`MwJ?aEwPne77pYq3-5F%wEv=L*Y{7rG?)?RYqZHqrX=$d6I}zB#R-vOsAc{1T z_~4KG01QTM|C?e7juR)LV~{b;hxt?5HVb$U-wvpB`1T=E`ys(AEd4?mo0WYVl!x`h z(tfXEU?}fY)x1;-El;NCm7s`j_9Qf415W%SDdJeBea3B-cmc8-F z3x9kr;>COK@uC&a?chO82Bc!Bs<{FWL2w7JA0FthFIj)*T1b#}aJOQx=kWp}4!Bd%94>9cPaFbAG}_wVjW*~7VtuCQeHRNn zbtuqWn(w_PH^XLeFFK1Ac)zSOeV>x)ew?~aPmw#?(oWCZDV+7py~}kqN^CmgX!Fi^ zkN6*6Vf$TIw6nz4h{dTQ($_WgYmc+m9hb_geN22Esj@~VgxtKHpUYwLP5upekr&?uRtbSSx?&1*#;Pg{&Zx+A`tZW2$>=Wk5AK;ycd&ZY|`7_frGV9 zLvv{`H3Q&X)A2`Aj>zXsI!n@{FXkh`XV!2fegwiOo%S zuPBE}4`|Z_+PZOM;kpKSas&>-H#l^B&EczlLufgxbZlpNY4_mHIL zWtu#sA{#~(T+DRn5nOzp!nocp{th~@guyXiy2u51s9z8FY~;L=WfqDqG0c4 zPmyJPDNod)QF6MuENBY{c5xC8>AQsQGd3PFZREHTrhe_W)ZdibHw_p^e=9@c532)x zvuJ^SWtu0uZ?C^8`O4@;#ux&2{2R-VhuY|WseO!5hgqiQ@MR5+e5UUkpLl49y>FB+ zl=M;|F7Vl^0!)Y)p5&B5kqfH?GVh_${#c&B1@nayC#uU+XNWwyOv}+*#UEcrj@rv` zCBL+QmZObJ^W(ZBiU2f=`nu`us1|R5aLQ$JZNG5KI=2hVE|#Kjd2YXk7u$~AuUN)0 z&h0-9?$cjSWECd{U14uS6P4&jV;k3@D8k?cYZu9xGZrxjDw4%5TgC#A7%gCO$YF+8$V~i z2w6(RREYd0-oq#Dz`)b5R==7=B|U!JYpoDw_z8I}>k(i~yG6DDz$ioSr?=$bMc1LO*>LCB5%Y#a;j+F>IG=QcMpk&&;Xu2pjwV90aJ+p-{DnuG;%7wYH`03veLS+O`t( zg;CG;g;AykC~iCZ&~j4lI2B|=R2d0~v{FH}w+>|468>B?Ii7!cz$k}MPLQKFO`l|( z3eQJ9$GI0riO%z_=@Ptw58*Ff>u!1zrOB5U@FE3Zm#LS~0?NP6=FcVkIhB#kkLGZ8 zVeyWaBykH^T8Tvn@|E8GeRtD*6$#D-Bd0R*1{Ha+yJ?n+#F50vaf}Qy2q*0YH)4f! zsS*7VMmjMT5t;$V&Y&qvYPK19H6u@yxrVFcIzw{IayH)pzO4uuBG-{8AYEE~#r1gRz=!9e;Oq=tezGf_P*dNT+_hcy}s26K3Th?x_P$#PX!5 zk*W{si-)*Xz3K0+CXJ3RgH3;THFY4Uzenl+OI?X3j4tFBOD$VZCX1z07PwQj!K^=Y znW`Jr)PYB0)xF_^w7Tb-b7MDm70P)kf6#T{H za8(ai0%vAi6;cDla^TAFns^ASN3Zp_01oA^l^b(}1}o|2#`Jq%^SOW9k<9nS?kw@O zewyQJ_{ialNlA_pDW{jXLM}ux%-tk!m=wA`g+Y9wTM7B{3SS}u7PWteHz|pRWg5Cn zEyYOB5O?!&$P}A4R<-$Iv^jAINgM9IXwbalf#qC;`}(RsP`GHsTB^#Ae1jjdgBL;K zFR>Mg`&k@y@1wi1@^KSO-1m~MGx1{gVs4U*b?_Cw0E5rI;Q^eqwe5A<_=$eFPHl%f z4jryjrE-Tyj7CxRDw#tc(baHdl2cd*v=EZ4Tsw0D5QdX6oTzu`&I3&!7ZD}YTm)BV zIjR<~Zs3HO&Q#F@@Gm|9B*FIBS%=Lxp(8vSoqZ5)f!IHfSb^ABc}bhdjdbV5bxf;S zQ2dC33?xmS>0Xhl?1m%K{7hCR*9(`&7Z6c17sQ3&w(rHXBQlox7Mjdtz?Ua6`tS+%#sFsKaKpsj?h)pZ8;cg>CiV)KJakd?JGXsaq zz!Zd@fk67Ek@Nfbzu7|hIvlSl2;I1zP&CajawdF!ntVN+!TtGaiR0pU*+WOb?-cJK z-oU9U&Pdp=y;_k8FV2W;0ITwyMqdPG3kHJ5s% zl$^xEcjOSk{^HToTw|sw8X(ONWE#oFCkmeGk73ejPRZy~-}q7D;Dc75?K?ir9q3Yu zDJ5ePlvo)k=lK0s0pmrj?4r8WC2}w6;_~+? zYASdr6a~E^R1Vy#gu_|aQGs6#0vudvMdEzaZ5FyEU|a#W2-(&~Cs!2$`=4mWwJzde z1PZ$N;_V(-_5OsdYUVblsXm;c<_r#9}If_>1{ylBAq zQgOn7@ipS}r2~~;Ds~JQUnBnK81cV2M*MAdJkMp<5w@9NBk6~~kOnhXJtn z=mB+$ba4VYf!>tzz`G`qgD+{{MjmQvlvDO?aKotvXyy&ahbM116*Q5%ody{|7!Sg0 zEQ~GeBZP(*5WMcht7Z8H0!GL*VOopW%@N3x8T9XL3nj%hkMuJg+(QwwaYy0zCX@}*LI}t+480+ z@7bL}=Y=#az>(E)H)ust@j)9Vv37|XHKR$6T6AJdZ<7i^vTi0@gJiTD^x#@1mLrD5 z9I=)W4kTs>too?V1q~Q0z=S0zL!&{Y&4CEn=5|!iA&jXR)sgTKCj2i+m4*L#e1N_L zz7{Q9_Q3ZHE&yf|UPcpu_L=S8AioTNI^DaO_CCqyf-295@1O)s)KYd5&qwgY0-J-s zo4L~Fl6peh`51!SCr$I0vsYm$!8RQT;^6qw<;|JV8sOEr-yO;Y1Bz?z*F*RQv1Pgk zjLw2xV4X`AwrkjA?;)CK#x(>^FkDMlaV3fl(AgPLlE47bi3%7Mb=0U+L5&1;;-Jn+CdnC@ND##+R?-Gj6tAL86idX! zNhFiQD85>)FKoS5TkE4*9zGKuNf3F6wW3zV_Z|loUw~-M|M%PboHJ)a5^e4MZttJ( zBeVC}@3q(7d+oK>UTZD8$q2alSg(1~w)b}Zu+MY=OGoXj8Uuug(X}XSyvywQY!ttR z<|ti=%OwOS%KTnqe&2#$b3F+Jm+S1m4agykc?EIs@?t$TvK&G)0O+G3|(2bJ_c3Zdx-SL+XL&n%yg`rp_o*}Oj zhoArk-iSmnc$#S+HNAD$bgl52KOj# zK{Twvg%mOZUOk@OO0;RmcfD4}&JxhW#)$4P%JHm%{uz^rZ6VC(?Q~m&_%a!PUR+fz z_bXVC0?mmf1BXSUWl?7Ry-giNr7xA7jL6(8Z7mab0oN0y_8#-HpElUvgi~ z2$<}Te9Ubl>Q$R^iQ-LSQ_hjAKt~mv2yy7y1hu=FJ~H{y+|&Tb#Ylz$tN%?v{^j8` zK>qD18<2CJcCLsEtXMmTbheE31<3n6X@jI`VqOpa*AV69Kc$Ut`;+$gZdOAz5v9e7 z$?;t&V||Tp72Yja$nI`__8`V6x=%cgAeJY0x}I*mRa+TEFgmgr9r3SS z&OOE*?C8DC{D)!Tu@9xqQq^*MmOfTNbC%w+V(l1~cu~gsnx%sP5p{-7-pI?8Jj3^i zRpXwNb4Sh)oI*oF@IAoO?Hkf!a&vW_EiQR&@^Rz64NaLe1tuDrez2sjNDR(9XO}rb zP{}}*`C4ZM?Rvt(WHxG2yKyq#ie{m2FwT?}wC_GWX zO)KyBoN)>k22i9Mv?yqof09_)|G~6H;a_4eip?r$ zE{f-^SUYj*X&LKlQ4BqR^P{K(IKZVmWLN=I78DS|#?bAX)$U`+fu#k=dL;prNACb)%JK)Aj(F4?8fw$C_la z307oNu0PBj0H2vF@oW5$tF2K%p zqYMTMT->+bB|d=`Hi=&>^84g9px0+kuk4l)lihOeTq-3TyO$|Rxz3zfm0OG|EFaf5 zgW)5=!iM*dn``0Ux=b)&Q11WoTKVL^)2843xIO(3sh~OicUv(ISuB#VzNY^TX(L?S zChLzR5KXF$G;lRQ^8VKZ3BkY%r@Q%t!JK+!?-o-{84QmFTw5)$Sv!vWJo1gL^q}+~ z2E_;NOM~K?$80Fp{aFT0C{|lB3B}W7tS>13dK585og3J#aYhJk?eUxwWkVoR%pIF& z1)=vSe^G!i;$C0Kj{$tdkFI{g_%U8WoDa0XF{vvatwn}Q?gzglVP#;UEF#*vxt73S zLWz*EF^mJv%jVHj)QepY5ls zF3!(FiC+A+5yivoUKrhDuAo&Otr13MKde!~G$b*T34};10-!jLg!L3k9(4`;CT#L9 ztu+|Rln`PLvyLR}KX47>(GXrt7%w_7^)TdOp9nZH0eu3Zs%FG7R!m{>z9034#TYpk zGZkO^xU{ob-ZC1nWl5I9n4FWYY>$g20`P_{gp>s{?1$J8!cF7s|*Q6qbDU;r|gVdElNjELroA zL=Ccs>i#5y*8XS3B$k{eV|`)Cuc121r6a-m#mY3G{QZN$8Ho%DOmi6)wM$1n)_L)t zc+4)iI}Jb&{LuzbnSzxGpcAc_1kg`ptS*X`0?=*)b2q=oyPLd|hT&IPPke>=?w3XhWx$Zj zm?zCL{Az6)wPl*0ab;wTJ#Ip_dx1X0Jby+5_F>S>v;AilTzvKZIGiEwXh(q}_>xyg zG_xePFx}nK#`_{_=j<_4Yco91gDO7Ay&#HTNM5}7$;iqEp*&~LWtj-@2*p+6jX@Wvmy*VA2W`iCjPgEKNeh-SQazlHESS zUQMPKwdVISXu|ajD<PdhC>{%gxDh@6ac>@rGJ3yI=5-Q$YU7r>&!Yc3%gy+88d^MbpN$rw z|8}B<--=1JI9kT~LW^JW=3lF%NI#w}%zcX6^Y%a?)S+78X9Of-($$GcL?F|5UO^x- z-29BKF+fz16K7q9D6BD zcJ-oB)H+eTVH?&sW-LyyOt<(Qa#bZ_TvGqSj{7NB>3{AqHRYBxxJT}_;l7ue0&_df zvSJeMU&(BJ!M%i>mVauv?&hox$e6Nl$pNMx^-<(&@CI?Q@w|E$g56l|KS zQKNS^?*KrZDDeg2yh~*AG)r5Z|H**ez9_6Zd<7uWq(tQ~kGZj-UzfK>>b zct--pRxEJowE;eGBz94F1Oh?Fw%5H=Gb%hgHu^QM`<9MYWz&ee*8zRei*05)FrR$X zkde9l)dM!|fN5EnL|F5G7Om&rkOuwR@3f(R!d*_Z_F6Fs{rzREH}pw?|KdmE9tbq< za>>KwK9@X1{p_!P_EA5%>L*A3U{?0Wm%jY)v9Rei& zf(ylX;-J67_(Gau(7#=4XRqtiz&!2_8_XM2&_wbMD<;AGxQsc#Ou#rF2UGzQQ!21f zXLsmI(r*3U?}O>q-}`{LY-7@kyPR24x|Z?y7C==T+iyB-4;|I%eD|ELs~L||k4)Ua zHzlbqr0O@~GXZ>Y4>!3a>Co}rJ3TQ?moy$1Z2!}D`)S|o zU(cicq{rihZ2yt|%F{N}=Nr<1o8Eqpq1`k5ax6~rU`ro*=SP3ehg<1^xga{j@f?x+4drcX+*B85MM?@j9mf^Utu{Cm>R z_@4A}1Es6?F)yu3a}S>0zt7hV(7*U%j&I6APE}>+`i%#eZu~q=T{^w~n+B|ZF3V5s zw|u;t>0|m$2Ykm3R6o-99w=SG=LWtq>1+MR@zetCe+VqHl~f0 zDjKjb+Ey~IRU6km3^my*6;?Lk;>$IH)SHZ9^ zfXmX9Jg4ETb(LA|;^-yhEWs7Zqs$$f1<=idTTa?k{ zAV%vy)5pB3zxYiu=P7EG(d(_WEn7(AoUjBM##^*|ALP8^vI?c@kTGhvS4lANsR8OJ zP^ia`Hm)0j=LT=N7rMcJhXV)GV;3~C*NdztBxyBrI`WV_#K#TdlN+R`ua=iEi?EWT z$Kgtdng0gu3CvvxFRAfvt25&7DtF15Hug-pG((-TcL%|sJs!#R;oPN+FQv&y(Zxpa zrkyR^DO=dA$AfHPbL15Jd7IW%hNjF{(q3^73bG0>9!U2ZK40P)^VHyFBeR${espuo zkc`B)*u=oPd~_e^*kGjD7-`WOxU*{rW&b$rj}9M>vnOwg=`H7?B>mmGrvo_YZ86|2Bk z{-Hx##JusNA8f|Z4#Ci1k*EMXhEZyim*X=>dC>Ko&qy%_-gS)nG+cc;LdSTJvyDUjh(C&j5Vvm;dQZ}NGtGb#lxyL)10l-8whKI zL5>)G^bw~T$iC;G=BU6raS;NKW+q+(0m|>wWK~!_SfXVxUuFKGA?6 z$Kv5w@=+apmcOMNt7)Cw?)6K=;(Sq!yu{*s_tG-F@3h=7tyca1NQ1Hps#zzz$Oj1W z{-^9+YQ}aLyX0^%CT>Yf9)pPi&3Kb;2ik!$z@!hGxOiORQbQW!jhtwf&{c_MSt5S^ zAV>tw5|5cBPBu&AXl4U!~{e!6kwXciL0n_3C zZNPM0T-SfP(Lz*?!{z0ky|3H3S9NKq&nKn2rtvCGP z{|=TX6`gMSm2=u+g7NvUm(NDO!~$@{owk^c)@kjrTP%Pf{IGF;O*l3Uc0o>`Sr?l) z)4jC(qPke=MfklKzZWM5;aw&6x*=_-qjnOkGr@o$tus|f>rD6%O?fY~u)v82p!dW$ zc0gW6NI@HZx{ zA?xuQ9zY}<>!Yu~Vu~2U&KP&M%Ut*H$GEN=e`5>yx9vKznx# zTOfb1<3(5bH2Ab4&nq1t4cyE2-(=0Rg$XwPgNUmE;p`Ew?diOD_fGeTCkITg62BQR zeWv*30O?H~1h1I$qTS7O`nzfC%t06}^hbDy=hNbl@pYxK4c3`q>dW-f02ND|=+v{E zjZBrWjEl_^4^6?0#&qEfs}|Qk4P&Q#9m3{dfj#8!pN$dnE? zkMoi1)ir@BubC^(#A?aoPy@rR-jLM=3<_o z3uP-}5wzigM)_Q=Lu5-B1P@)!6E>iL`r_xNtB!zp2kdgoR*gy&xZ_~0q=rP;>4ths zMZwu-Sj%oM!6@YDY7waPf9P(0S&j>`1&m7=s5W^NzDEvBX~*jZwD3?_2onvZGARj+ z-6ua0=2grY#;jK<1HSS-DVmebe2&_gZrK-3OQbZCh%rXVj4~l4K_?`Zs37eTcU6x#tR%yi7*-*e0U@yqRmW`MbsZ z-Ddvo#4k#Bn5BjJIgilA3h?5WjgckGU$UMnLCyj2asn{5?5=nt5BrLOi3h_VQo}82 zeCk|IzRYnjR9%9tPZu1I?EF1JHoQ!4YDeT z7B?4Eqd)EizcuT+jgx!=>(YVrj8R`j%4Q$s0D#ii`Jp3g=9eAYG?HB>7O()ZAm38a z$4hGQ;Uj#kr4s#bxSMC8EbquIlPGM)=vbL3I}l%$qX?B%9E^S@wxh;C^F#6x5-cPZ z&o79KG|CIctk!P>k0XH7&zpI1t1CI#D5qnQRmdKDNLnW_%4gt1J3i>~)APlJ*J44` zmgA|%2$VYA%SFvZ&B%yl--vgo*|Fe_+4yzg*Ohq*GM;V^RXhhn^~A)VE<3m-IeEt= z!H!)y#tN7_&)PU^(~RP&!TM5E9wc`0Ea7l#Fse}2I^uwkpRl(zs~`ZVashX(h47}i z%;XFd+|C_=+CbI}U3WfT?Zbc8X6%cA*)~)ua+>8t-J+~10Mrc+%QbE$zj+%L>wZDs z(^4)N83yPD-OVRs7ZWRy6wbQ1plbORR@929)pvSf8OizM+>!>GCH>b68d(1nK&ci7 zH4&7j&j9de>anriawp2X1aT9?E?{+t_&BlEhya^RB>qV4LiUE?Za!XmAmgTyI10hU z0u)MI%eGNwBYXE7-2dgy6%fN^WvJTPZ>;BHY_5@+6JXWp2A#dUWrk|R%Vghi`f!O+ zDhad(jq40?HB|$)M#mOrtW3NIP}#j0k?KW@WCx~t;ciyPg&af|dcod2%HB-dEvK4z zne#4Z&f|LJbo9#{nHxFO$a2h3jritQ|Julgab#8}m#`AX`UUDO$f25UGeb3^@n31W zs6BB5IAa4gcBI|dfp%m2{40%N1`qwd#-6;@8rh>}s7Bn6(7%E%f58qp;K(ZN#!BtR zirLsdhnAIW$68RgFEf{XzmxaME!Gg%nV}l75}|)(h?}^guVY~jlxK~YD=Re@`z)Dc z_3a-^zprn8S(vN!I5Siuj$|DJtX67P%H?aVR;g;aI>GZ$e59DpX5apf7&|IAq75?1 z>f6u0w{Q2z!tC3vW~fFOtYd(_ab2hNjURxAzcH7(+}+D%M=bdHWaVZb$H*kBkIyXm zKKOlelQp-WnV}l-AwvHgM!`eDOyE@CEREFtgLYTXU5y6iJ$3i{3-9(P4V12Y=IooE z#aVx~82sM@$?b{b;+0itU2v8Ui0x(8eZPCAo9t zU(&khOeZBH1xA;q>gb&=4#TYW)BZM|8AYkG>G#LDl3VW6U%C@M z`0J2)WrH{gDC$`hHa3KfPWV9~J9#MX5z@y6RQUGcL$fnH@Me*_45P!dx@CK23~9sw z(r&p5+UYhHd7qWFca?cq6jbtZTo}`QTbx;tXWKrr^A~L@S|hH%gAbe1P;ECGj+doZ z_X3z88kr!2sBZ$B;YiU`_z0fy4KJT;iq z*4pqKqD>Zs%!|IAq}uq@Fu@l;k@G`EoRu6&wgVt*JKd#QvS_9c_ zE23Mn^xAEB(KCNveGFV#t`pg408Vpt|C_DBgNHP1MPD5Ll>ilwv?(@}EEHHH;s&L6 z5emRu*cOA`Yx?&9!+s4ch6Q~wGCU%IN+Z8A2A=uyc0?=vuNt>^BXGqbnsF@`m4D@& zQEjkGF*Ixb>N#MDJ{3moTY-$>-sC2v#Emh6IC2(mf5Zna*KaxfCh+#C)GV z95)wG;?d14hvsH$%b;M4^!4(3BVM@TTj`%>iY#Qs%LXvxFxS&YSe2RtKOD`@sJ~lD0BQdnKbj0?K%*c5Er|#?C zM^Z3e@g=K(E$#K<2($c)NPvf@ll30i+2{5|90gJ4+Q$%VgkkDA?w0!zjGql|YPevW z*s*AEMqM#b+1+wIzK{9T_=oPFR~;#B`8YC`jLAPN1l*+@xSDcc_;;I9lO_ii-rMkA zb`X4j)3V<0nLReE27+M7si)M?tq^+H{4qyZZw)Nl+vtMAugd}AEU{bP z`qJOP-iAet)hpm12i#X+n)FlehOKgW0^!ECR+J%4GFF`m6TQ!xDgk|^9(i<3L1*lv zBb|R41xr1Ct`4)Qm9<;S{O?1O8k7RnU14z7fmB{_?7Z&lsi>=Ar=am)k6yb`FP`k0 zGgKcl*(G}5t)2w>E4=x@LSFGFH#eEBB2p z#Kp6xok^=2wuNgyCO;i!_ewj1#{=g#fc*!{xzh{lw2tIZ4Xzj5&6luw|5kUr7=h<# zQZa-wLH~!EJN#|D0y+-P9_4^HP-&cvQLPD%dB14Wr1-_hXO_ja%(Awh!Up&YVW`%4 z%KTfXRtnS%w!&l|e%;q!h1J>iTF}429lsF4mQD4e;5Xn&t~*%%FKHX?HH)~HtnEk) zRtnrbYw*MoIrvMna`jrD4<-&@wQ z$$kARC>Zv?>5lWdT-ugf*~+X6bivt|h0VPbKapVQ(h> zW_zWl!>sO5Z3i=JvD^|30^Fpga^-q8_mY+DR$W$i(q(;AxiMy0AUVSB6`&n#Q!s`f zi1v2}mHAg$fLwUJG`|Q$i&yjj@|e($qV4)^Fi4zT0w5YNRcU0I`aE@nPF?YdWF1RE zcu!|t)RzHKxLhSU3#2&Hy>tjXEkTZ~WXe)4A~wgBgpK}5KKNP?dqKYd-Z4h^s+hKc zN+5W@C=hJ*a?eY<0NCq>)PQ?wQ?rCEg-~^|l0tEawzTdGU?v{f;9IF0q-Oy@>@Oj zR4X#WQDEBfF3ksr{4ZF72xMcOMrj@#R0Nt=M}{}f8&Pn1j%K`&90Z!sh_aT}R+_ic zmPvE~N#^CLGYwGIPFm$|mToelk{-ipZ?FJ-XOa1Vu{sJt5TW^3!OJ07xwIJ6j*NJ) zL0+l~N>Rm5nP&~guN#m-AP=1uKrl6<0D`F*1!2wj65^sG<(kj>%2Xur$TbgoFCg56 z3pCOKp!VUy5UU>=FvV&I@UN=>l%5knG!>ptaRzwEtiBo$<8MtcKK6t%>!2b~-{SX-L2gqc3u1mX|CjR*1?uT=3NYrCpaHk#*~3~hY5j!icJRLt z>RD6&Q3^DaJrE6y)w=Nt#WUOFm_HgM=lD?!x&Bh^nY=tk|Hx?MG%8mNlHCBIVelI! z@rwQEchSEN1Q0Bd-MkE6i3Y!ngB&yrM#V1jqGOLU9FEDlFr_kMwB4;ow`>E|-!983 z!_vI(SpAue{#ZtfGuRpM=Qqo|H>)DU#bNKj)v@Y51tq$na>SzDsXAfYOTLhog<#ps zqf$Gg#QDr%>~|Yw-VNe(^mP%hXk{v(USjo(Jm$W^8Ip(21ynp|_MAL!TVtlo*5ear zibX&}9dimH430q%vBcn3^D5os*kF3`GTK|!`T_y5Kjjm1%N7eYr#t&JH z?a10Qvi7%w;rKK0shoxl;=F6j8K1%cylHcz7v5nz#fsgqtj`HM85 z<7AW&3dSUKLHT@b&jYdK41gIkP!H|6kw-cEGE((Lz3PK7o*hvNiIkU-riGG>tXdnh z5*}zswHgKylbeP(iZyqg0{=V7Fr8UXB+yU&+59)a7d_)@>{HQx{Gr6# zF=}zx&HcXD2#AA@8-T9~{h|A}TKfErSB)EV&XoLChby{M-gA&6eVo>Dd{!JaDjvC_`+@$ePNsuXWc@~QM%4cF&a@l^8a zE5o%~LAW;L!Mz}y)vg;_eq|PJ-c@<08`&#hwJUHD{#0Ge?FEsU_(dLUwt8MDtE#3F zvftB;+%;nAr(7OVZyl#1dm(uRU8S-Y;2HROO2=YUnO&uYAcag{hjcb*(#!-B)M7Lm z-G2w2E7}e}V#TFn8wLk_bH+!iaqgZeL#MDAsTvXFSqT-Hzv6fTm*o^Xpsa%T0(=pJ zq1O;AE#wy@<#S0s4jPP1wYB0agz$Bb4pN(Q6ID+5alB>$dd%_ubA2vz-E~os|gK?OgW* z+PSG$JG;GG0aC;+%fmEOtF{n2Mus$3>@rX6GWe;!4BmpY!dzfJ5`VS_XG_bET^5L4 z21SF*^f=yn#ICAB+VN-0G+*qpn%HGCkOr~*RiGYn!YZK}$#UVSD{_Gsj97o$gHwXPN)MhC-Rcsj$jS4||@Pl&0M)@~Cr`4GLs5qN9pkF8!9y!t&;r zB=0I6SE!8{zg>a|Y@=#Gt*(erqaiJkoUU+RwAIMK4Fq{gX^}F*8{~|fbS2Z{)A1@u zRdV}Tb|v>)m6XX!;?sRrreG>lla`lW2oU>m6aXpL5Gdztq(!USwllu0dy+2%M>mq>K5b%i; zWni5+R#xBT3iZ5DzcFa6B=jRybDxKFYpBDY^Py+{P`}L4U4tCue))sT+5ViHwg9`N zR_eyE@meUR%?`(7JGuUhSHWQpDQ}qnbprUE?e(LKin+K0u!sr?gyeWArZVXr>4T?A zTtA1XGch1qRG?5a$iQ$S#k9nwh;XmqSsSolsKIY+QF|S*4G*1y1xH0c8XGwkeqlY) zUBl`p6*p009I1deRoOw@@4cb|>UYWUzNT&H2}J|S`?yTH?>cI9nMr#giT$P&$SYz< z0o%~gv16ZY;%K_$;1D3-J3+q55#h!yCrEzpY9hG#uS=`uPp5Vs?*?A+ zhjS&Y;TYLL29{Pa52ir&!KMyNb$+&xP>)`vXTV{iHXb`V$+H-UTFQf!yip3V(IM+q zn7Hk7wj@Qi5<>mbHOHR8ojFsu(KYPfCcX3^V&-n4ep2uyz0|hI+GqPZLo6T6f1RwV ze&QTi^#(Bowc7N?sLR+xsaW1GU2|xv9p!I}PX1mko>zVOu*P}kf%SGfK*UgF7OvZ~ zaSe0Ht0A18>p654N`&QwTP$k8ckSM(`(5UK)0^O(!W)K}Cz z^~d*=R(F=3myv8_HR1Tg8nP~`xm%o#T?4DG*oS!JNMy=PR7(mvc?51;haX45yg}p< z%c0w-6b^Dbqg)D^%g#3S4ipe5>(b8D&VskAMeH{E?@^q|gxnH8Q_g(n(wXls_2R2^ z@DOXEFRb}=xPwUg{gbQbjNo#aD~cP@7Hdyr;QlF|L^VNU3-loOR_Xg8T{BiC-jN_X zy(XH>ayQo_A_FHP&;#Q(dzioPp*PWFw!4|6RW!LrWM0qO$i!&!fXH}cYiW>`OfFO_ zwGOlokSR|!^d1^QjO+H0OQHQrMw&4>UnO38Exw$hN>}ldPk7Tx-*1)niFYqSUTK9f zJ~_a=y7*Qea#l;fwDRg=Df51Sz8awdNP?PQCp1l-rFf?mxMxOGH>%l*zXjKPr|I){u6PEW2Fa&9QO^`dsS#~tjn!i7v~L}d z{0*5Xgk-gN<-g3}Oz{GOrfbu=V&!~(m%Z^JC@Dax+S0U&2<32XDi?NYUB@fuiuEzT zMbZM+;&-Oc6qg)g6F+7KXL_|beZX{G95Y}#-arnR4q1z5xAv{~^;p~=fm+SOx=YH7 zx$@B}{mBJ!M&(P+zuK-+PUXVJ-yJJ$CRbXSu_bIgAI3%AIHw8=i&E7P9udG~5mafl zlh*0(;ARcykNR>PzC>JQM&0UkL09)oQ@55H9n$_lGeC_=LxBgKgo0<7iuT zGv+h-a2Wh{?j+KP(ng4Fv6rz)aLCcF^4B$g7UmTd)Ubb2=&)@Cci(LkI($Vw?A(z5 zD=HF0)*-!wVNVQ^a9S^(?AY0()EIoM1y2!C$B1BTCX`Te&bf;^3`4@POW_$M1QiWz zOS9pyG^La5f@0wt>1Qoe1lBpu+C1fU<+Y1g`(|hDcl=3eNQCizpS8b1)31#jDr!Vu z&C`341Z*aZTGLQGTcrC-FoXkR8ZQ!x;LI$9aQ;v;ty?C{qPfoVru|{Nk$p2QV zwmfuKYEq4V7Gg0ek{q1erQoKA9VHJxC;Mc<7~5WxMbG3}JtuhH3Uk%@d3hCC*28 zirRzzEfL+U4d}YgRa>eeopd4LKa; zcTG1iJFlb`5&(_I_ZZoB4MqyAk*#;YRK6Tm_(zz2M+8d2bbBwDJ_TkrmS+(h)}v~l zM7)1Ty|q$G0t^cI*SnjKkuhq%B@RO{*a~4~ zJG3jSg0+hf?yQRT1vxGkMLXy$V8O-+t_0xaK96>+lyIyTDOLeC&+XAyc9P&KuL-%! z+I6UE1zSr!ou!9lDy+Jwvba32enw?&In-39Vn0|bKJSv-fSPD`^BE*V*f`+%FHbhz z#s?*oFAp0J$+sT78EeMWxuLA7vn9PrT`o$HjV)^BR+$6)Q7iW>G@NP?i$zhuYJJ2i z@-fPA>`G6t^-6EZi08?%W^(WMhAD`HcpF+-O0;QWXyuOvMh6hf>Qm4cDJaBI*;;>| z)>8(;c_QX;Jy#A*1Jur$9CqU9Z%4}hJ&L0BzXYPppB*v=$Rv}?fb$u@(KHwWR9 zS{@)_;|V!gLF2xHE$DXQ57?=dR%l?qOZ=`XQD&b5B)Q`MXcy(jK#>Q)A zj&rwAB}tE7lZRVZD}st7IGn84-837A-z(2;=pV|vSHGH#wN&J(CL{Y~_s zOg?G{SFx9Zqc`n>KF=G9jV=IjdysMVTs`aT*-%pq^M$$*@`*5Pn)Ij8?Sxm2`1!57 z%||bKa)|{m=tq*|?d!fPxe8y4uPY@*&T&-a0h6)hjMCAuiqS@?2d~zR(t=n;0YoOT z3a?Q*HdX;erYRWvq(bqWEE%7IJI$nkc+8AdAyy@RhgjH{R)YuaHEpFcTPtR^m0s9d zaba8OMXePV?RJ4qjgRl|pQW$Ze(+7%V=5HCNz0Q~9z2VH_@$lR_t?Mg(mZm;;yIfR3(4o z=hP@}F*ffDCd!-6#6mI63*1=o@QSIQ48)(- z#h)RRP0eTTxq|(_e$ye3*k>SIsK#6uitE$zIO}J6KwQ#qdKJ=Z#Bb>`*2Xty`6>u& z|#DS@_pQqCDl6+M0V%)B+T%~zXE}xvWe?s0;giVBd2l>cjwtsDg^AZQx-}4gt z*x&OLS@!q*#5Z55{8Co4n_SX}SqO*w0gtprU|~r(F9lbfkw<~m;tz-*yqMv;Rd^A@ zH_0%x<6|oKM9G;j7L;CSf06(1AY^-yJbg{ow*o`W-E`fS*DnQcs(8=E*+Zdthg6r)x= zat2)v`jFOK%B3|Ie1Mo->K{1ftx8G9Y8?m zh7YKpFWaYqfuQd`nd)wy$UO1Wy?oeGCCDbyjWY{sdhrR--EjQ+EoegO2-+PE$QGLA z!&1HYI%aS$7>AD`|KB4Q1dA^jFO`_t8LD4$u<(MWv@V0Vr>{G5z+(=@hbnl1UyOFeV({V_kzl#mj5IRkIvF7(8yJ!>9Rze)q+2lk zveb;p!XH>MS?^}X`huzdeG8_|4*_n8dU@wFd$YH*H#yGU?5t)#*_%%gO2Qv+!q1ZN z>m^DVBRA|fJVRXcU+j@JwpFr>Iku%{M2_tbc5RCh3KrLmcei{1P*b0k^N)$BcWKZp zJ5}}$ur%=^KGNL^asc4WV>2r63o7qE$ctwcGYSgj-*MQ0$r&kp2Kbpl?g(cvas(k+ zC?1+=_SCGx;(xKx=h1_@DA-mmEBE{Xa#Jx;hs8BmqL~4WeEflR#~B4Ct^ z?aE*D542kWWM)!}42!ASz`Hm~4u@xed z9THg>00Zsf!BbtzdNHX>aDYLecGM3L)21$Z4s9Mel+@i97|*-!OE`d>vH?kt9fqbj zu4p1Jv9BG@PYgH1N;go-%6;PJXA*TLdc`>^K&=$W$R{GkI{K#3aOpmg#8RgC;{=J% zCZ$vctXCa1|E6G1;^BxnHccYyP1n=0WUL!cP*qo{syB#F5wK;ff1bkr_P%>K+rb_2 za=RV%{;$RsI%0n6Ab`Xbj6bJw!C{L7>lOS_V>1YjoW$dpWA{?zsXN&P&Hx2u+?=Xx z!7BgCTXI_mX`q3CcJQ zwt^Ix0vc$NSL|WN@azyq^m>4E!M167uywlLRvK!p2(^`-)>?6z1NBm{QFq;ZacGVc ze!bJZVvwC~$;7p(+S!oS@zp(P%5A_Z(v+1jmCSelc+u;8M%BcdxQQd`nFwEL2wcO4tC^UA!%v3u*YX0r+=;o(I0ArgO3;RE;$IW1&w1Xs$uj1k}%|3a}h^*)UL>BQTRYxxv_O+Puw(e$6p zh-`W>A{ax?ZR5CaU~Q@%?3L=l!wAB*N0SS+tHvNczqPqwIr@ONfNa(O3O_94hi2I~ zR5Tkr<({*FQHSvg_|7 z!mht@Tw2$so@fK_3k1>iPRL!kCsoz?yXuDpU{vf$G@ZPx?&RSak@HofqthB~b2fUl zv(edRqchVReftEv(fMdpsac$kcI#lQubR+dld7r5&fkgGDSZIs_ChS zqcJ0+X$^HAfMgBHi|bC_$A;ABKXvw|#@U-G&fb)py*VYlH%pJVA$2Btqaam)4jE9E z0p2tyAT2~IeFBM!!X}odVa>F(ZmkJBfb_H(kwAI`5d!J=$EE?vlj_zp2m(lpAo#R_ zG#K+WH+fCn$pIS-q#uAg!klgZF9U&%I_ug60;*BF$L{lirTKAScz+ z-y#Um)Z<2@KqC+N?P!-5iRvQh^1K^Sn1ds=ho8T)Q?DYhnYn6L))nVgz?17ri0=6X zKB}S32cujFiv!HwFhYxRTjcN4QeY_`0Lo{0b_!kt)7P3nF1`kS168t7URY!es8+HN zXP*e28Iw>jHnO#y$R_TR2&z7`^20`J50J^%oo3FjtYfYfW6k0s#EN>7wb)Rs4dnH6 zo#+rk1{=r|%!q8{SXog#=g6befV_B|jSeRlS?I6_W;6i9cHBE6Wm0BsA3j~krPr!k z3#GKIyfZuVSOga#D7`T;0A&&@InX*GQ?0IJ&{-ZGw;_YJY(M;!OG*sG%iLZ^$2B3x zNG>-cawHcZ!j<&Xv1udu+i~_t=3r1jmvUT;+1dlLwf*hZ?!=qFUajrrY%SZ_+74Z{ z_UV|k)@G(!%TcXu<}qlWOU*rKuFuti&AHFjLLNkFw{1kT_C(GxBXan4W{1vC@6aoS z_V6R<8-`zj4)Ho)HPXVBN5FJQHF2P`i9BZ$S!NU89F;caNUDjw(1hAL-o|h!?5?JT z!$wBrVH!kJ%c~F-Z50>^(xjLIcnhsRn1CUBa2wxV8k>{g-oC|Bh2l`Opx1^$7oo-g z8G}7RM$98<7OO&p=p)WIBN8Z+5$Wl)Ew&%1q2~#{a<7a-JDUs&-WTxR=M_+~h2r_W zF$Z8KGau>cwMp!1F-M0 zDOxjL$#k#yRsZQ?7hY;t|e3^&d1~`9kp> zIW+dzdiM`+gMKz}{m%|qzEIpfU^?tFe^34A4_LlXoHAf~l{o2p>i@2Gfbqw>iLy&~ zrh&jQ|56P?{JQ^ic{E(vZ#r;#{y^;`{rvAqKV`sljGt1zcQ0?r$O{!;Wim8YC11tXDIl*xFM2&(3yr6pVgE~EzEM~^|6s{R z43F7-T0EHR(b{sePFmaQia-hit6I-DBuZM(8{vU5D38ZjTklppM_8EYX!Lm^+x6NG zwS&dO*&Z<-VkRgaa=)6*wCUM$3%hu(+bNjc{qLBQiogP|us_YDnO-I2dL0S_qY6{?2S4ioz;N zuB@;X;LPd_WM*`ga#oOJTU-Ngp=m2^ETe^%O@wCcraqwAyv7MlDG3M}-6fsD5uOZj z%2YJmw2HMrlfmXmXlE-)jWQbmOuQO?t8ha^CR%+eh4tO~bAp-I%q?nfY{;ISQU7y2Yb8E5 zd=_X+(!Miu@=hyD@SLInhXz?=GwACQ6K)emKrX@Omyii&Ce=RyGkIxtIC% z9QE~Y_=*CNQAk=}pbDI#lAdBqM%R-w2w|K}v&Fp_aX4O)uRYC4)?h3Uv$Q(0I8=QfQVJ=dkVuBV5O$aS4;0plJfJRshwDx6)gY8oz z#-j{!2DGO==%v{njW3DQksB#h=C|AV?Ie6_e7ayc!Wr&c+o8gO99|8RE!Ycw7pJBA zxewyV-eG6I2-(Lzd!L}pNB-i*k6 z+*^#15BChqXv^Nq7c8!+$Kz=*Wxvj8_yoF6;mA+_d@Sp#5})He;G(e6E|(HN1;p>h zvU>1Td66LPbQ3=?2EO%turCqj=Mzscg8?zt3Ra0j5JVHl)eq6T$ParDmK%12rd3?2poo5IJ%yHJhjY*Nh|bT_ zHgx20rsG}xfaw8o-GJ$ZqRvhalPjf|i|Iyns>xmRsLkY6xtUOI4Q1i7*vz?F+s9>X zc+W~s?cGNbPzZX(%?#q8knh-W)4`q)TP$Pd&gA#^Yd~qmcPOD+m z-57a?`Ry(B&D~OkGI|qq-2%YX1yUAlT$-mfttN{^YkHr2M<_s3`n?jo&#~6xD|K6fLfVvY)1W8NX&>$ zgp0a`lCNe#;E968KoV9v5mv+%B$~IQb>f6qZ1MwO33^#m@fD0~#v6%td?Ji~5jkeU z+{jTAsv}2C2u2Q_aN;#XClp@2KeePssv9)E7_c%faf)o~bW*nZQ!to`fch zHyQDXPZ5y^VRh;j;P6f4#A}~e|H(jj?-dW*>DIf-76{uZfud%zQZjbY;G$E5>tGND z(z++hG;Q@_`8B;G-v=3A*Jnex=zWT(;h5J5CaebV;EOg1zfQR^zCaw0FJi$R^7v(G zMVnZ4$rslsR@hmsrIAVppDt3Wm z3FVJGJ$^Q(Y+@mlK=O==e5gYk6{BONqv3-)R_ZY-3Sy-NMuj(4>NP6H#!APE4irS8 z015@f6Dm@LNR{|KBT$f!Ppi?#ytJIhWJz20{#t7mjL+$?lJpsHt+jM!Tg8Q~r5CnU zT+~_$%|uc!Cfq;{rld+R8i{^lcbJWOz>Z*bCdPG(#rv}pwo|aL4XB<}tyuC0 z_y|)-@`{grVjDG*XtOR~z4khf__3L&i`_ zUAt5Fe=~m%tieB{;q_v6Ij53p74h5?+XmqkPHU##({a+u`p+K+or9W02)&VnrzM0` zbAm+#n-s{P8@r@Ui)B3fK`BBP-#h=P&a6hLsAu8-ApA#FT_6?pjt^aWhw#zVW=U%> zE34td^EkPH58d*qa{Zps})~F+1QKPF&+$SM-}1R}T~0QDb5U zGXNo>n+9q4qF}r>GgP}(=X0@6<>UkMtC7*|1UPdI1@{v5%|$Ul7E2aa04~n}*TsFP zQHp^FB`+@{Nr`*Up5pFbai_xjw#~`&bfs6d5@(nBDMS@DS#>#%c5PX$?2uGh+8|y( z2(GnE!YUw+lbln7$ovub&~~#&quE8&BQsSv)-e&0MpPx*WrSq?5;GJK4?)($NMkP!dWCndTIuwPns`8Sf^jaKDrMMVm zcf*|vo{|LeXz{0KMJU?g3i{8=ZGah(r=^1&`4SKhjidviRT0bzS#ZjFp!6!RGBE4jtG6+zig$KY* z!c!C5dLmydRvKRhWB18!66EgdmqOf1bui*cICcfJtF9;{l0VW4#9utMRksF4D*{W7P*AHwIbB0r-FlPK&tB1$#X4s4BUa8AYedpxO!PrvW#)!GamV z*82(`L{v#ah;&gPPX=X> z#j^m9q-+_!3lvcHfSAAn3ht`mtgrZzt58DLM8-3~x7Q65*~?^vi@ELc4 zB~%O$xwy(g(N)-3RG5`;T|vp(z3^(>)UiwwloInDkiD>}-EKa7T}0I|d-q`i_BaRG z4z?848F62%*w{tu-Z*d5(}YF~oj8X!qa}Dz=z4F4%q(vM>`;JyNnP5H<%&Ve?jP9h5*}t&ap!J7{9aq{p|MLcKbO$w*6BF zZ2x}nX>44*T}I^i%@D?q&;a8%54zn^O{wdwkb8^T9(Eu6Pn?a9wj1BuZrt@RHcq%# z4A}I;V;tkYS4KGQUz?$Th$A$>xW9Lu(S41L?{KRHpC zr|ci_cFqtcAqbk+wB8`8Ow)S9qBN4#T*(f?5X$dOt4Ol<$!bdDp%#iGreS_0U!jkC zg7V3fm7XPak1*mq2*p0$v?@~RaV580d2YYy?dYZWUBBtjA71f#TH#)N5x%K{Y%eX3 zqy0*z2gLmT)5Vg<2f8Qu#9+H~;Ql!5)#Y>KA0L6v8IX4bQ}*iaT1}%tU|GnAwiZy+ z`c1v)H(6Bib9S%y);{Vjll8{+eEDRrT|cbDY$P>a)_Yy5Udd0>!MkxU3E?dWe6qNg zfJNh8GGz$$OR{9Srnh_C85uh|GN3~d8Qhe}$b;mpX)#XmlLNS)xyxUiwP}K03vL zLCF*_ppZ1WYp}yE-JlPLWf;%v9dBo&9z%0!F)*btB=^!#Ze7e3>cUWHZDi#O(Op@N za!38(a-f+lL%VuUjq3%Y={?k_jG4)|>OdARUU9of%fXPPd{P5TDJ1}HC9>>;YjQZQ;rcWgBs*L&d+cn`=}%QLVPF42G9vu2RHoo+sCYUacAU6-MB13m9DHc zVgH%yU2}3^ls%ahG`dUU*)Ew!^LNz05vs*YicY;_3ns2K5FS(I3TNdt&htP8I$S&@ z54Q91YN4~#gR3q0ZI&+s7!3r#z*epbLg&5){Dpv?wVfI}2#2@96!J=PaK=o`*Zmzq zcLm(4O$2(&tS|(#k}L+IW+v*)1Va$i^WfON)&xjHy#NColSB$JsL0{`Gk95MMpolW zy*4xfr85xVf zNA_hz0AG600u%!x0d|PGn}{PITuL4ty{aLz9<$oS%3*gVx{R#L%m!FQZ*1o$#awfV zD{ug@SrO7NT0A&(d-iQH_oW}+a!37^$7gV&bB4R+kq9_$S zrw7XeLzSG$8X3TXlR6r-3w;Vi@R$Yl=93Ihm4VmtHRi_s zkl?5a`*z0MBlDWpN3i7qlau#Amk#}FQ$q{FWtt~33A51C#X%-UAynD&9$8!21yb=J z=MEO90ui5olTpVjs0B8Db7ITv6lK#}YLbp0x_A~`i(dHC3qO9}2Yz5nlz0(P+)k+d zyvo&y`R32LoS+q9>8x5B06ndC!cFF4fcdkXr`1Mo0wbu&oQ-X#*PeM3h8XakRvX@k zgs4gYJE?YZKB9{OVjXr5wWxsY!Z>0KMe4cHOyi&9WqdS(?v|&RIobj7!kN&u-WP{% z;sNyurw2b}^Aq0{HWtIqAw?LP+A4JSBg(>Gwb*$aR&fLwBA3GKA6Syq+kTfHr?b>8 zE7X?+v>20>T6DQYKVYk-u(mw}RBy%h0Ay5z0vwM7jV^XT7aM+pO~f>tSS|hp-ih2h zbn(%FKrUqlwOYI_`2@1zsR*V`uYAX3kxtI_UM)UXhkif%fi7-(vETh5p!B?cWzBcs z2iewxJc6tjF88^_25DIDRb@G^9pf*A-81?|cL$AoxopUf-GbjTyrLNb)t(E$zo~@O z5vX4St7EiJ6)5w+;EpOebyo!LZaSbNVRRVT*Xp;0<{?YL7jO{XGZZ}9m z?9$gRM)WizyPZ>6)#%H1w>*z891QfPJ95bO>&(dAg52}u;mm3-Nd+3r*f)Tp$$n-xl+@q{+L4T2xPxoC?4#b{P z;@q-{M88t_06O6_P0BGV?;IEacP$@g0~-h?2S*SFQJPN*8V2R$o~El*_3#Df3U(BE zS;Ib$m(;%-0LqLv0Sc9w8_RVDnyr=E>s}_9^aG5B9k-8Y+ zYMMS*CJIgY(Y;f&p}pc(C1>|vrFo0fAd}`MWSMFU8G=XLR`-P0|4 ztx%ocZtMKE^|V2)6%x~>o*r(mQOyPWF0OhnS1egTh2oy?c9060m}Y+s=f~M2%{y_S zO-QEt3w{a;;|;Ifr!e+7h{?ln+TxUuLrvUnKV&*Q{!F-6%bUiM3}sH`|T9H{0kk6a;N3Nkv=_`#l5jpLN71co~!xSG$ew)OA2YLV`_lnxR+trg{2OHFCUcZ$Tl($2T)Iy zlK6IffbHLohL1vcAltHrdr*90K2&)%<0Y|R7|2N)9(@eoS*yD_ffkh+y*Kyap$y_6 zWm>vLp^5tM>9RhGpHSr6jADaa|?Ezgw2aVZNHgg|y7O1Lmrf5dR;aFa*_s*enni zvm|jLP3bO1`)TH3;cPfQ6*roRdE(1aUxTojhGVMK1Ih%2r+d&B#7%iWWa{2@JHFm< z>>=4jJ$5x9ukz(B0wuQ+dyION^wr+>K#R0RyUQ;f7!8)mHP^h|v!bpH z+)7xNXxfzl+`D|m%F32@rbUjf!+jwfAGr)1OYlQo@DkRu!o8#$uCqGo%8cC8q(9tX zQFo}PyZ$9+rvHzvUTEV%_vSU2nS`PC%V&&hFIrPFD0@F>Zb5H6yWxxF;M!)SzT`E0 zp`BEkUGLW#=jCM7k4N=cb+JMCDIEkyvxCYI7#4~L^1}Y^%SVOcQ}W=VvKo_Ir{MvK z)G7tcQ6)g=B|qHO@hkxWLxoj6rh(!p31HK~zN+oiqbqbxsuzko_s4pPPdO7)U7$X( z7C6s|B0<)L5qYSZ$PIqfPT(Q6Joiw6*|-Pui}WlhcJ9~#S^VozA_Y!N!xEpC7mQ7V z*`a9#oL%$1jN*m*^xAWC4cds z?w1`a$;J=Hu_0FC5_h7laP4%IpOi;h-ZL>iVj7qyFaVMp%f1n6PvEcLn2lc-eqB8k zxq8p~`Ufp569f4Li+0QwFQa;Fa2V-Y70P)xA$LYVbfS*Yz)x7yo&(ur0la)-@A=O6 zRw=wzXI_=K+Rc4OwE_7YOkt#gXluQwiDB~2e;*c?Zo^l)sD&g)_0rl`CmHb8&;yv z!hA!2Pg|BxRL*)yWagtAP%MA!D{HDN@+QV}VF`llSCuWE+bv$*cW?&hH@Y&PbNm<8 zIo{P|zDcauhdf0e(eh{~dJgKj&8)LVLE5XOddnBm4afhq~ ztX-gdp=!NBcpM$ldpb{xjoys*L;f!to>pz!{))(lHhKRVtxA#WuluiZMS|ck)3eP6 z;buGZEf2X6zU?os9R4mS6kAU3;}{>B-cCYW4v2GMr zt*PS)#~@I&Orx4=@lNX<^g;3{)d3ui&D?_1Rp9?tE&g9ws4T8;5f2RqLrKHocKu=4 zTC~Oq1+IE6es-q+%!2U^!#Go3(TY4e$Z0Ql1!@EGcU@f%Z{j07b(;U>$VGLry@1Qu zHQ{BdtKoy>IjkqIzQO2{(ukgRKMegeELub*S$o$Mq73b7nt!Hth3XRh_N>44!7Iop6gvs zlHg@z25c@Xt~zi2RiLhGbmKyZnwCF`nktRz8W?~EOLk+(cx*Aglo|In;wOZci)F?{ zFf(#HHOMbX`_|G^<_6qbSA*bzXsRSTqGTc$6C-jWq^t(QAsqmibm?822^dYpJ0VlI6M$w_|MrS4J)!Ylq*~39xj}#%bo} zcY;|=z&HuVZ@4UTOSy3xBxY<`yL6x`8>dP&u8_?`aeiZsqQWy_xdU>ukNM@4vx(*1+h)KHiaJi#BS!0H5F z$8bj$U4ZX?In$8)lQYc|{Yk3gh%?PPG2|pU)9jKn&0m-tw`p*u!L8W4ie^^LEQhKc z_BZJMajsHh^1y6xK?6$--#~|MTL)Vw_Ax{nIu@4#=xFlL13v#C&_%0Hf-XtC3yOR> zx=nayRy8h`N3`yAE-}%5&eyo>BDDV;v-Q%z)E_Q#&Uy%KSLGK+0_UEid-hK8>yhGf z{3s5@3WtURv7fbxQXFUmGKcQ7*@7owd*XGEE~tmLJ+hV=PnrM9tM#&`!{3egky1=OHZ59*qq{zk%p!fH5YIcA>7u{>JhsF#?@QC>i)8 zmATMNOS)@s(o75Q{G|*`~Dk3b8s7uXyXE!J;&r*czJd$fz&Y zJ)4{@Y)+Rl0Y^{PcHz~y!Xc_~V1&g4wi!Ja0bp*YQRWDjIAV9zYD;nZ8)(rj{&@lx zV0p=cxjOD}arnaAXhm=otiDk$rnF;gsX<^+ZGElSZ##M&l$RBV)kryyLAQ7jLExm4 zhGuDaqhGqeEs>E&I0q^a?TPrbajVxH=}yKAdcTtqb60t;sNVTCEF0E|0tu43lyNJ) z)L}VJzm)OX7<-x1j6?BvJs4#0SqE@@dnYcE&B0}`kkD$m#285D^oL10nPMD@&}**G zF(&5Nq#w!8qF^(P|LUG?Gk>BR*JF|%`~?}b;Yg@<8f7k%)dSNBt&3x&EJb6>m(_3K~x0FE0%=O zRh+}O7a*Skk*pQlGNoxw9hVHu9jm;L@uEZu)xW*^w5B6M>}VlqmpfZbXS~I_n7i9tqc|_D<>4(KZ}~>?u&_3) zy|}PVE99OGB#`@_BmoeG8bw{2i`W7$iTmWTX;u`up@LIQr8&;vbwMWRDp3i$N^UNpT-PQtjNNhN+C7))I zNI`Q5JOWIJX9b^trxH(`laf&L%?z~ESvHTzo&O!$h9VX*F&34R^oZ%q8E&7N(bK>@ z*`hj8xC8StJU%u0VV=>%^3dO}AX|o(8KhIbInn?~EI>L?{TvCE_>Fuso2eUfWaC5t zwWgP4Ozz%K)1T_gjL~~EV@8J2^&)~qhy#8!B%GfY@vuU#*1nC4vl^QJOJi1*B}HI8(<4 z4x=Kk)F{vPL550}PF}|Lx|sWgX~AfZc0+&%-rWzHLu-jZk-bxzE%rCIME{1^+UA$! zcY7wF-xhrspK#;Y>20NuK`t3{c5S#ct7%3?cJMg-+GUa;lCVImf1^>BX2`ZnF_@GJ zZ}8(DGnZz}e~QgDUU?dieame!uN!&w$o&;S8+edutntVA6q!XdwU=jwKD6mI;OC&Y z!lU}3-vm5ynJNzI2pzE1-{NhnFVsEPdxQQ`wOv>%rXA$q+YfUsuM|v>7xAo6LW!$% z;`Ymaa=U<3IMSNRaQPL>e=m_{UN@Li)8mRa$JVb1rZ;# zs{;8WX=>Mv9L^;a_u;}h9I^V~B#>RBg;qJRR6)Xv?!|-`(==2t`!QKk4lE|u7$+E- zEXuhASI;9m5H%7-Kzf-d!WHUrp$MF9aL%MZ?$iE@2G^8UDGjdj>OR!BGV7~+7yyK( z)PttVQ(~Zq472W>bq4y)A~tvqJRVr7#mbEbjnGIZ&Q|5_Z-zWyy2=|n7pAdL1uB|R z#Z#&BYRvvA=Sw~t)|yW2noFzTNXT=QL!B_eAlRhkbN0mJxOUXp@+`Op46A-`aaaMF z)FW|Ahqu)1EKKIO2`ifP$A@JW$9&xB8BmVn!8U%X z#a4o{pBtn?l)4Wy2imts7IVfWvY1EJ(Guo|M_2{^I9Du?A@E4*P!LGRdOZO(Vhqni zoEY{kIc$~5pNTWbasZp_oXxT|<56jMj+WMmvp!_kmpAH0YZKzagWEFe8#N4hp4H>e z<-Gq>Jp5+CoLFTAPMGeZ{EYw`$c7r@oct!F^Wj?uz8P@lf(P9hodC{+=ytYlMgq>B z3-A=jQ`!zlh)yhTaotu$wMq2>SiJO2)sK_dNe$uyNP~4x6%an_cUYICumGuY(^D-& zs(IwQ7cpRb5an58qRKz;BbgzyOv6e&OQzlym8x=Lohbh*cOAlxJpAK)8!F|^I8#Qukq?9AfHe|Wj2Lxh_H7= z4a)2_UggL5bc-*N?)sAX6Y&*d{2+^+OWBV`B8x~0OOkohcy16V?&(U8M1qv^%d5qj zU5SFt2ByTTGr5V$BIMpqm$KxZ8{ZMTu)w#mGsBZMh9_+aPkIl#T}Y5OieFPoPWSA+ z?0F$Vm3v;!I2eLp%DF3jZ^|K1_eS+{ihSV?^;8s;l4@~ks`@*bg{i*4aY7LeF6*z< z!$Tq9g?v|vcMbTMPKtLS5B^F?W!EBoFVbB?mJsg#Ngh$Gh-t|pCpySx&Q+QFXc?_M z*rqB~1Sp|W9Z2-(ivls`DQib0Z5q^m)?_V#^X8qL!%}vA+}rZ*+Hgaqzh)1GvCv-L zcxLjH`@4D|UK^WSCXtw>1o-SuCh z_aw0#iz+0F54)bEtA00&A@o?O*$H2K2^6cwtP`UwhzrE4pMM=ENDc|oY4}x{FeNPd zPcI;Sfb#{YsN<5+2$pcTv*Dctz9K>eN5!nE9wpKU2o;-)d;yGcb?gkQBw732$sjNiCz>Px-+?-1^+{-;rZ)k9fY*ff&2RDRqN5F)a zW1v)wIUNT=XQrC&ocgYT2FaEMO(VY(?s7r%4(qiuuWGY=7ayeF8 z|C2&bkmj)aj|e=)#3w9rPn(kp#HGoI!{;Q(qR#9yzCavWm?*fH`~_lLpYd+-N+Q0O zy)(U|IG5l!zPIZSBG%5;5GJ;%5OSMHX)%B1pFF*;caKu1;QoY7d>V_eH)r{LP6$D{$W0X{bhRYpL zs@fBXwB$D-79!yV@chP`&9+O!TQ^f?T?od7QQG_95HSma;h_ z>wq1c;hz}xg&*U?nB9Wk@O}I>W^>gI-^X8L_8b}JuO4}ftfrJ+^`u9KjJN;jZKrU=WEIvhyhY!7Rw2m0gqde<~nj}9XH`W+na zp$^m_i{A>-4euNxE6;TT-dXF*=@k%FzuY4ZVD3S2V$ZwS$Maf=+9GKE<4f=izwJ9c z6t}_CAX-P`>SpBSb2^@(7m4wl`T<=b29A}jd@g?>>N6hX7pOHUkU?p-fPqoA8E1>i z>jx}kwfGeZSU6n=u<~{;3)t>}39PF}oCR!&0@m5Oo01lcDFQ4n>S=Lc7g~FX9rkhm z01%;jMthx+a-@Q2aWt9%R-_TTcKigmh*ywTsWj)lf80x2gSnD(NlX5dXh>N!mNr4%kl$g<{Yz6xighz>)IZVD; zM$lWNXp2d?H{f%!nWfIm(vZyZlT9j1H9o0POnUj434vxgoQ8eHQT=E>)!%*CxD_&K z^FeNdowIHNbbP@nuXFZ9yZ}3GhyfHMkq&Qj)SDY`#OOS{)*^N^-l^dHDBiWux5&K& zVwG1p36>hWVIJG*Z82`@P5{ zKHdZdsm2slvh(qAY7D>Xq`!VxYh-u6onDSqk7umk*eME76>19N$>_#UV}pG~4LLI|^@U5b!xcHuKQL7aFiqL^Fir4W&Gwx?wA`V2WKE5~ z(`;C9I~`DvaL(bR5`&CzRrFkd%IDd5qCN-e!>kr}$@z-1q*CJ()rcmLOu4Ze=Rlb& zK)tdpkVSGFRcnS^Yr<%nTxf9b6GC#a;jzo){A%N?X551r|9SHvMZw3TG*f*QavsdP zoDsZAF~INH9Xtm=(;%2#Cr+VM-q=s-h(j8E`$~13Fhyi`*8Zii2700_jPMSQAj^oC&BC3dnxQS$&=w475Q@Ixy?bS%ht3 zOiHveqQ6J(*fE&A?eg4!(&J(P2El$4VxTqL2x^Vp#h)pOk=l*w`K3DXWih^RQp<=1 z65sxk_y#e8ImYKEzDWm>a^}4|@y(S;SBr0`0iHa_fgEvTlg7tUzH%AJO!4Hs@H-Js zRifr&5Oiao3j+h_yzi{P|`7H`4UwA96@WbH0JCR@Up$R=uX)LdF z8Uj38h3ylxAC>JBM*OiU=EHDG&+#|{Iu*u#P-I>VDY5f~3Ra$(5(AyK*m@Tx94ucv zET@lA3uk;vnMdM|4(@l_;cT+2G>^RudjD&@hh4cm^Y3xo55KAQp|5jbWQwV4<=`^) zVUK%Vse>e{Oyyf)E1zlMQQ@JNa&2b0r>B+sJl+yz%66o*!7Jt;z1(-#DtK3{Nrm_N zzRSI|N4bf9lSjNt*E?d*GmD<2iryglqtt}{?ivcO59^Cmyi|zI8R>ZGy&kbfH6g>Q z=T8VE>iOmOsOR?YRnH=`o=09Y=go^#>-lE;(c9DSd)3om)>CNJvjKrbdw%mh>Uk^< z6$-UiKcc=9S0K4P1JU-AHA>OIi`|M2#I>^a$c;DzQA$z&dW~q7iRN30E;JKWSc$5V ziSD!#xy(d`R-*pNL;)*NOa;>1SRX!2)Aw`YYMSOGCpV7c6#AzwL!r2WN63jZJ>xOX z`;4y>%a0geDK0u>;n^C5spVISlaCU=@4LS9Z%)KhwKHPB2#E7h#RRNS1j<$s`?uZf z6b(m;8jKyZJj7PwzY5`M_#?rglKpq^+K(g^_}qa1#z45aN%s8%inFjkr4rQn!p&qu z@Q~7+aPtC{>?uCrA44W;X;gV2@#|Ip!biAPr}9j=?+=knK=4%hruQiscu zclTW1NK+{6WL$-ZMT+fte# zBJZm1ZN<~NNESXR60Jxm!$y~^kgZ^~%l3o5#5v4jbYnbux`TG{xEkIxSd#j0lA$#< zm0(-yf^Dg*{z5LxUWkuKy}}E`0JCto^cePi5K5)FGa)nb**mc3=m2JthNeB{CNJ=l zE9TRNrPsNM@p?km+i9*R1m*MSK^IbB)D0StNCaYSAhKMibF9U4=RNyQKUUfsECmh4{@}9eP4Np5#YynM%5m z;bMflLYxNpC$R@pEuYcMeV#9z)wJwOM%3pR&F|r_ZeN6qaFnjaEaP6~2nJb2Otbi0cH(CafHv5IsjG>hCq0(#` zYy3bhWd+^Wm zIdp6@c`CVILZkcKXvvYf^NI6cFzX9733wsJMo%9gBAqB*~Q`U4ic%lk2=-gQu9uBC*@h3 z=P5`nr-jjh5%1Q5r_lK`7Q=;#1M`r;XWY-8^+oQL76;0(_gbpP*_@A^DZ9>YoCsQ)@!I82IgXdS!&Vjh)cdIyOL=Vm;W%fmgIdDJV= zY+ab#u}pT$&uxPtV7YJ%3N#L)Ee$X_at|&H9;4albE+Y>+bV5nqBI-eAx=38>FIns zp6&hGGX1s<)@Q#B9$HikZjkth^{Q%Pu!Y2N>8O~q<F9#jhdh!Q9Qx z_&E+#qG3=H$j;f?gn-%g0VD5KP%YtTLn+1=pwt!wG!Hyp%_yOV>q5O|wGQ8F?rSoG z=iu<=RDiD=12IsRLj|=nCqC$!ukAq0%wvp00F%XNwyzOlIkCl_gU&flR#*c;9mOeh zRnDvu#%Th8?ExW6z&=5sCt&b11gNU=8x=_KPnQDHslg%X5M=|R?D(LreS~O6KR^_O zbPeD@eKPhkVF|I0bXcUrc=&SyzM?lV8c66SHaJ*a%v+nD&8U_Jf|Lf=3^t{5@K!(@v+Z#w7F zTTC3DE=h1jkg`RG;DZlP&pizR3n0S*jlW#aZ02%Cqy#g4zQKGu{Y>Y>(ovT#y#GT% z?_jx+1#Lp0_kxxnrpLtPTyptf1aF6ZJ`|4I-R?~ynX4?l+y zY@N^NiM*GApu}|a>5ClRQbk`*M_K2kO-CEe=_pGUfE8yf3X^lvBo6SZY_~NjO^SVH z4e$A~Oippu@Lmdd6bRvm@;mUt#nhac80W`1ff-|sanBd(qJjjZ-X8?%da>Z&CZsUo zOM|p73F*!ktjW!S^pR9ZiP98E8D~Pu7z@&r+33BG60_02Hj=l6N-v4#DM&BCYchBS zf=S0|%`5kyq@<|O6lW#(pAl$4J_}10#Xo}0k$v z7KqE1A*~^qZrtuvIAa) zl;do9TXhv2riN80Cs>P^te}sb^%L~$an4p=C#lN%4?=!y0aRHhq0T68U}T4YdAl4E zdDihb&=viw`tEF5AoD06i4Eda#>wlYJYXnW5C^)t-0b0BGqTRg_$x;4({i(eXTs$J z7(&^1EuiW_%jSW*bCwSJE1t3b_^^l{WaT@yp^Rb7D(UNNgwT4NDG;*htiXv+Lp#wl zTW}J7`vouOuk${-{z1qwEf2pZJ6k`&JBq$4QMAFLH}NYu!_amaPZb%faUT&OTwh1L zq3Vk=$Da<%Ay*L4&m__Ij3OQgl}J1%EMcc^0R`OpXGH;S@!ewdY|>g#7X_FnakHg; z4x|t!DOHMFqu-H~%%1rWA{vvh7DGTRgSz;;6?BUQR`;mk^FaoQuzkqQ_&PAN@(&xkCd;6LX3&h!d##f50KI12ek7o8={_;NK-Qq8O z#_M8MpYfIAmxssqn6Ig3lQv)Xe$LU`6~mI<5(kyA7nZ$8PV=@*BX_`Q0+O`;pnvmc zXIS{^Z6A)Eq#n5jdlx02&YTMpQ}XG3|B`gHR{Zu^Nh0=tEMy!8-*^}Lp9Z?)0E1fc z7$sJot1B~{W^ zjr@=(V<9Ovb;BU*!idq9a;S?7-^7u+p-zOA@)1wc2H;p8w%(SZS~nMo?{89uLtTyda7)Qcuwzd;bXuoM+_DVBYQ^gY~6c>=nWaZy{Ci|8>)*%<`nKd& zpJ#sYn)j_B1!LzKQ|F?_Gw*;A~!eb%RiIX=oYs;g=!%f zE8tf=3(e<9J|O~vctckz%(|(<9CAadWT`Me3;8uwn8B(*p+L4oA4;EBwpyq#pDIQanh)7w3KdS7^f zU!_F+k>;nagl%swn+?LnK58%F=_;$_m#(sEKJZ$np0k_y#2@6{`~JjfYj!SF@bs?P zdDO&XMAz&BD`@m~nB8N(sz9v{aV|_&l51bGotW=9CdGgw#utdWwcnX}RXY!}mBmDC z_e)@#C9p=A5Q-fTzT;|eOwn+532SDUbV*12YsCb3|3jZB$vNg_`Y*-%da-u5?5xYA zhwx;?_@3|32}l*yS_aKM+vO0$(+psG#^;I?j~HJm=HG0!`|xq0tle_O8#D`%GJ2*- zEx%IY_1Q$cc?JS;b8_mIevl4GhftS8y;tn{D?LH!MhxdYmr)`FA|AX)c}$$V95q4M zS4}%ee22S$Zt<`bVr;SI?wr44)JT71sa^tf&sUAlDq>G~2}p;vT8v`pvK{6a_oj?- zt}hyI_?E-FK>Y9T?7qmLbhIar^Z(`IYan8AFX1z6LWjhSIo|4m*sZ)ZcZ0+*8S+1XZ8@)r6Jx!VeB!xV@ zc}p&T5b?wCP~@W}&`NL11q|4BVjdd3A9GnAv*Vf1C-86v@c^dOD-NOq=3zSIK3~F( z*~`nat0{K3lQDS_+c=s@GYMnz*pIWpc%j8tpsO^mIBau zFFMl;gS!>j$*98Y-X)nan|C;r2OPoUL;wAnZK@Cz9muzDMK(VjRL#6Zeahme@A#=& zoU1-%k~?6-PQ$ymB_2EhZ+YS4@tYsMEIZL1sgtNVSW^3?Q0$<)@5DGs8!JXDQK61I zT%jlUatQWg;Q!L{B+9eWXK@^;^zfhsz4lpsr_WBNKPc_fuIM|xBbgqWqu>kAPNYBF z{|dyDm<3^c5V22unxkT^xCfeNZSa*oF$W*dH9ul1jklHIZudbhevw1K>T{j5pj=r zw-}pG)#+Jgx!H z@q3}T6LW((x@5X#Xbil{)}O{U6pH1PDl&-spAn2^VU}UWI^jk}Us$<^<5THMKHZzH zWcfs(5*m+f0F_C}?DF{>o^jVacUCK;|b_BoDw2~{_;0=HsPJ{II@BzG_(F9nRVMsi8j88VDwC6h1WM9^zjLy zbM7wu`i-xs?=C@wUIB{!Ig5^{lNs@s|C4C6d?TzAnV7ZOF=3kVccWl!)ceksxhTum zHW{v_@uTsln0*Gd5Gcy>=ca^Uyix=dWN@~}F@6qcQ>`@__)hG&0V!SdO7(XyY4Myd z(Xp7oA=G-Gng)5|k%id5)CBT}sS^x1Wzgcjd~7`AJrlqu?dDcHJTIxn&N&&hq_BIb zoOqG0P`rqACK)WEyta8I$Pab9DIAtcHcxTV9B**d2ni=f z9Yj2F&G}Fl4-&nln1h|hGM<#$mQh9;Zm4LD%iAYj#JMJpZ3E^&81V)+_Z6PEr%q>{ zY-3Ffa<7~R{58NPoS?7%wR$mtGEpODdrSpNi`>+o|$z~;+@ z+W|GkVrL>J*l}jwjMrQ&a(vw2$NGF=2Ar6k{@lkb5pXSYy3s1(zKh4c|Zkm^O;f<*cja`@5QNrzYpFEq2n9wg;>psbyh}< zlp5n=eX@@~jM+jC&SK;_j!{B0{I%oMobhx&F^_kZe z=oz@SAQy?$wFR4HU4=ieu54A;H7v%YA6{FON&y4Av5m*+JTRMR!i@p=Q-lmwj6T)G z=l5^k19WuK72N@DDN!DD0IZu15?HkEhmhU%{fi&2il3cQJhTY7_{b`vI~c6lkQ^QZ zt>FQO>@_ndz(I*Ke0(alAwXzE=hhGKwPTz{pFjcWDBo}0G9ugKr{@uK=VM+wa#$Bf zK9@N7NP-2jU>=&FVYkFl1DXDMAD(rg0|TcHGCFbVNMU|3`(K$cXS@@$O#kUpI)JYZD7s22Yl?=TpEf?7YnjTTtaCzGTH z+#Au7-vpXW&}I*8-6iwFFOBj4N1!$I#<4m|t$aD!D}A2E{0a1NsU{9}(nAwuygQdn z%=2TUb^svUda{XbH?_{_iCWW#xYB(dxd)n;F5naNj9G#H0}#rie;8{9sJamgEyq#) zP%vu4d=W2PRGA(!UhlBu@{D@ySO7)r^jAP-8R)3Wm@Bj&W*~Hz8L7+Ump*CRM8*&8 zt*c>^ilOs&)RHKEU`kKLa%6|a?D9dBRO>eO6Pj4fn<*Lsw&(JjkX=sDu+H^zPQx4; zeL?Owzvm3K9tP!_u{!qSwAndj68#4fq{{8N1Ri##X|c5_Yj30ju!%d6J7xJz#?$-D zuW^c-5{W{R@sS9Z3~|biv?M9<$p6j%(juAPl9hYa-4tb5uB5Eoyj$uBvJuM1z;B)_ z#nYKRy}gM`7h@~!TvC=>3rl0jNFXff)8m^1kfXSh=ubjPyB&W3q>+hv+%`M{l+5eZ znme`qlyM*WQQtD|Yd0li+-!F$x6-_G5ho<@(=H4*vMS}+`%u0LSA>|GU<~^)HB;Rj z4ieMP!%NA$a?iKHfw-fdYaXHoP5RJ|>0(3N^(sn%Sq8Lh<{{vV+;zwY1)2XaZ0yF@ zr3`t5b9T`&q{xN&dr-X(ewbS5F+sXZRd-t@>boi?(SYp|0OC+)4&6|x0HCo4_OR!2 zvEG8R#iiVq^Nq=Hxh~vi0?lf`M4v!V^Q;U`NF;amNS>I+nc^uHxQj9byvW9H=RTd| z&~4t^D=!9I9UaHk6sgvv0P`=EJO!BIBrq4tpb7SDvlc=f8wOX9!NG~^vr<}<&}cN) zQ&UQ!BJz;(jy8H6Fb^}fmAK?XVtIG{nAEJ+0+ndlNvSRCcZ8PR2`GVJK$Fsn>mk7~ zQF?_8CgKjq-#`ADg1-fLNy8tPKg{*VN?f!k=X0T_0RANz12Qw5^E=u?Pvfl=zA-2G zYt|?907iDhg?WNmn&smyZ*@-j~ybtG^O3&-nNVBow+eyWk$ge;JSf61g!Am`l@vIX?-^>ndpfxGfEsR#kdRSA7i*)ughW45qgAgFDmO zy79M(w%%qI-}Q>IhV|l$xXTn82Q+1y-u5BcCmSy_p2p5%`-yTDTvhriLL{=%L>=yrnm)3x>ox=UwA3}Fk=ZqCO&DDb5_(klALgNcg9nic&ry&~IT6+I+NmlcH(w5ApCzw>}4XiP7Z>d-Pk z_iW}0B@lc86q~^tiq(w#Fk>-KF-n{%1th8D>`I3#JO!a?-8Kf~ zZjNUUf&l~TCD1YAHR>jJAcRM^dc2N@u@~bHwYg9PhZFZt@!L?;lc0n%Ri%eff?7== zk_6I>aRD){9WqD*myIt}+T=74C|c-aH2PtKsbn7eBy6w`i(ce(Q-6%l{rD3I1aZp# zt+(yPFA+|Fu@F+v1Q-uX5vVzEEv_bY`>+U2K79L-%GbNa+3%o4C6F^W7Af&+UvRHK z?bt6hUM{R3z-}a^h}8Hk0A3utHchOU5)Zz6`1@&_mh^oI;%iUti!b+z<*O{d+yp;$ zUxWPT93}s(qvYR_boO@`eyEdE+*kgHT*LnUdRh~c?PvZso=8L9;q5<4{mWM#rTs_A zKdZ0&$~V|OIP~kie}UiC1N-iuv->I^`R_YQ{*PAl(LS~B{X=CR^mWcr%Aa+3`PRO+ zQ0;5!WJK<36L&7s?mG3-!vk8E#-%DlQ%YXEC%XWP0v_jb@M6n_p|J%b+pO3S zC;Vs>)L!C+aE#Ih2vqvVaxjes`@c9bqkoe;y=E5Seonf;3_)8ZVW6L+AD(4xqX~!{ zzlrW%g)7hg1$)c=x`a1G)f=_Dl2R+@wSy+ zj5jo-3|madjKgJKJas(v_LAzhvhjGUz*_~Lm3TtVQv&%)S#^7;3FWqx{R$tzg_l8C zTGD`L9k{MG#30z%9$LUOvqOA0^`67h>lWU)%0MY(4jzWw^@PhBfDm4mcK0GXE=wc+ zVW}0)YS(hxim`)(1BEaov#?~u(*;|DJlyA&YiRl5;?r*}VJq=h5 zPyw>Ms4(;5JoQEtAZ`KQWXo41&Mmd80lOClTEpOq+js}MUT8(|=74caVE_hkS|q!0 z#B0zLmj-cx8r;Fz_**n#;H%}4{P`3m@AMCi!D{fQnme=pxcG9K4aPe|-wd4cDSn(S z^HFDUd-jit-Pxy3wU-*pai|iRlBs$2)IaVw)KRD&h7=J*2D@wxp6=wG0BgmaVGwQ& z?>d2UWMp6y#UPMEqIKC1;KpSn#|3e?FvW$Z%eV(P9hVm=%QQBJOLD|yW(K_lYPJ=J zM?WIH)S>BK?S{k=nb8&7rEH4Xdr}tThhcKm9F8EwZ@^mmF#IZ5bRe@N7bg$K9#MD5 zhK<55xWNXqA1=?3z5wMtUgo`FbTQ8|jp>dN?=A&9AGbl`BOvjK+>zxuh!ZNOZ+B?b zWPOOxVfE5-KX4SFUo`UkJ3vtZPfQP=hifZ2^2a#3#_*~Za0)mI3y&$_b8vXfFh29b zV+#4q508QGO-vf$F>pSK)5P$Y(cDv9=JiENAVhjg{2KnvJqK&UGjE!CHZ_hzt84v6 zPCh(af_)n(Z&IucpN!90o(~%Tr^CAnC?$a`C96ZDlBzgz60zSCA-k0+{ykfAqNe|j z^0qW>(R5jwSDg2XHD{}Gd-qe3XBJ(liryf;Mj+wW;f2T8!#&lxl5-zrcB@}b)N>_Z z&+E!09tk)!yws^~Z(6sa7G+})qFXQ)^Z9^Z$O7|O=-_+A-aLmb2T?@qGl%y>La$7K z1F^0WpP5#dPhh*Nq=E|oaGHLfA`B6)$>+?mw`h7k@0{I@YiB`>IX@L!2`k#S3TCle zJTvpzDKvsLDGfL^?-oHBclvl|!hxb|WErq}pU5)KRb?O(%h=<&QIfw;{ zSE`rA1Hg}4wB09}YiN;D!ir|zZ?MHMre7;owyN$HH8PPQOs4bIewrt9Syc4~a3*!QBa@gwJNp_;iOU;#T1yCF*4Q1pT8~V3_&xiBV=?QMhAE|h zVKMIenCK8IA;Y$6fjU^Zk&7nx)bvG(^)c8_)0Cu83wxzcw~fYF2ljD<$7+Y|HO;do zqGdUKotVHws~r)2vYfUoxQBG>na6sr%6HD844>OTg`YRLBen&?17QYjbFj5mQ=N0q z!4d|%2hcr*eh<2%6eKwk2FLVh=PNoHH^;H{?nZbxRWduh26vHr(e~`_*fn}&rfmDT z!`t5ddyf1$`AsMY=GFn@RJeGhEq1z!Guy2I#kxW+3XBH?Hxy*l(LD$*?8VVIJTd^0 z@)%$Bx^A504^MEo0yeCKn+PIor&NC@5U!55q$V0E0JHXFP%To)UN&pBQuF#)T)9)8 zkL{eI0-85VN^#El2&Vy{_Jpo+`nsHldk)(nVjH2Bk5ArA5H$&(|A6UcffhwGctle?W(j)9K}~7U!dkXU}s&xc6vK=X>GQ0 z+cxCgi@brzk8ka?qk6xw^03UOnwcSI>ov1xT!n(yioLT`CGbg+ad<-9;sNgR!uN3g z2Qz=pgsTs$>7Ql^%+2Pd!Pr;(rln+rLWS+KM9w{E9U6s}r6Q8x7ANz7t;sL9?MP(W zjZB=HQkV$E`%Oxye)2v92)4yp-#Lv)@rr2gHPorsE_x;VJQ;j zNqxQFC*l*UManfm-Bf9yvKugE9?pSVGtcBOUto7eMl3RDVwXK5?lQLUbPIR>K@BVQ zfNDiF1G=SwB8YtJadD70qba)kc+P?UDK%D>7TugPGgrE0flEhQg-gTbs#p$7J*7E~ zJ8{UzgFOf`k689X6s1(nehV=?@Av@_xCp))OrHHo+-$>zIbv^=p%N$-)xf_|jl2l{ zJz9qIpx^I#kJ_nRz#;RVw~XA07{lTqY;}P@VVP24==q(jLwb4q*18A8kK;(>#*7b0 z9rLtBVA^rR+Xm4Xqdm?}O8dAf(moD4cBoH$1T6>({3r!Ve5{A&f%eKqq%2pCd1WlI zDaP!>ZkDp#;Du&wK=THw5v75S1Mw8(n}-G=IC-HU?I{$@Cr`8p(jq+ByG!xpmJLYS zP(VIL=_@d4H$L-8fhe;OHfGs^r{vlq_D9Vu9r(lpCwPatDSv`IiQ?F=b}&oCKBI;u zLt}z^0Pb?*OBTnqlEo9lrA75Q&bfs+;9$&t<_h28#y{w6gE;3?wx9Z52Z1r@pt#D7 zk^MCHw2{u%fAdRdrVCE^pF(gMQ7ys{T*G%OE`Nm1;Ys01m`d%hH4}z@diZu%3|;*d zjS1?Y(az2@k2~hkTc_7NTK2-Pq@+H#A*gK8vKd`R82-mz1ERfPrFbx+6lpN0%THIk z=w%Sg3e;@ZK$@yPqf#bK(L3@2PGECPu6#x=%M%q*TSnIy^&P~B1Gxe@e=-4_B6Bqc zHs~;%BSH`qAwjt{&(HERm5M6~s{){6lanWgeStk$;)AJFatbJ|wWFb2!=^}@b^35_ z2<}3Q^Oj8z9LBc(_4^dQxHS$CH1{o>b|>P%H@V{#1$5EN&Wn(%h6y}SQm#(V&K<2n$5;w44KCpb`~=hf$06 z0~E5eU~rhc%!}bt&&@f`Ir}A3{hS;r(UaKM5&Q6Jb113SU5Pbjo(k?!LGxhZBkOGe ziWC2lZ{{RL=-y4vc;>W`a>j>4@c@(xdTmr;o=z7Q9g$$_mvSk;h#=9gCe9jt$X z^(LVi^TVK76@3Ns$l=gf*d-vRrvoxN3CLCzG|{rwdXs>>EZ@F2T2@6b>=h97bvjxi zH>9EEnY$8b8E4fm(Q=9PCZRe1heGpz(0`EY@j_e|tR_>kH6*M?jtB4|Sp4S5Nb%RX z^2U^>5WyWZzc*!|YyODixd8Tud4bTb-?K9a zEtH+gvGOYsIhAvx`6d1T@)d(brp7G80;Hfe+|L)Tg%v1)NO?m-JWNh>iPmsp7vJu}N-CtD zPP@eSdJ#qi^K`?cl6&ME^f|6equ`yv1OU7N9tB8{g6xqNB4YtD@Vn zhH$eOH^?wa-hka}(T5Jm-Sp%FYGVk_VlbC4*Kh3@&gEo3agV2i^CR|>8F*rOap94r z89O!4f1Go-%C=(qx0VXrbe<N;F;}(Zrl)y)8g#VuXCdWrYb= z@~E>kJUvIBAdemyq|wJWR=YZ}f<3@K=lEID5aV;Z>%YR)f~qz@ zI#92fOnJsS|IpW|)>=n_05-w6n?1)LU#X4is^6p8c7b_pi1>b%6{y)>?)fA*3_sJ3 zqY=|L5c^tJJ%DabA;jh!W)XR9SHJin&9i$Zd{l0R(Q%fs&iM)@HyQBv=ZCjEJ^ot# zu5x3gu{LO#h#Ji`c)G)=l8T*)Uz?-kG4x z=T*?8%>T3A7O)F`$2Y$xjWYe74-VrWa2cI-mJY(qku&0YF%)&vF})hlFUk3uZO+B; zK>-I)-vpX zahsY^hVvPoo0jF_nTKZqo&`o(VYsBQy=-J#$w;HjEp5ou(H19*bQyT^z!CO+dd;Ue zJuQy-aF!`uJx|XW`dw>$<}HDmuPN$)pK0j?fR3hV#0g)|yMiTtDI>g8RZU92{|T=e?$eIJQeZB*Vq3UUc}^$WtbKUlXBo_ z6S4!n@-Nv3kY36&uhT9{?c|>Ex#BV0DVLmQQsN^7RKyn~`dG<*5A!EMn+YQ;oUoSY z(p<<0jRQ)&GU(KcM{oBxv+Hg^_l$=J#Z2qCM0O&WKwRzV3S<8txdAx(J~kEiN`*>^ zzUOWMj->VVTG4_k!5~fqXLgxeOe;S~#PZb9aynrl3eS|bO1~No@!7~D;R@$}e@Y<2 zETd=sTyaIh#?+*vv_tEj{v#Iss#pIJm1u$()`T`;)vTuc1+)x7eYd!W2R1P0H7}`u zeG<9J8=<7|L;Xj5bFVe`r_j4L5q7PTII;Vb&i*tb3jfIg9Hbe0!IB$y=3^6n9ekai zV`^jwKMst?8iS)DPX^8pYfkbTaN1-H+!Uk!4*L2;x{L7E(&0pKU~9*8yJmb9%Px)F zoayuIsDD0oeCw`;tng4AE=T`|W|KuI5U=A(IW%*bM8pt+3bsWuASt-GgGaTv942eT zKFSaym*t8tflq(Udk}$PppV?b!m7p3fg}(F73i%}BKG3j6i?!18n2j&j7Y`UDHcIoeo^u7aY}9;sCVZscf}fc$%K1pGF2bJDuWOG5`dA zEdAcdv%%KaK;YvYVVshI7@JoT%e`|cNt)uB%R9ux5qJQ@w0TCQDM`}utMNa)cbZ1| zh{yQfmRd(Lzgd3#(FED|ZVuhNb0gVMNH4naG|w;2LpIe4RNSCYvG697oEwCupvJad^i#R* z`z}twyj$F5C3cH}NW9eU#o5s_02m@zqGBa#=EM)X6-c)6LIG3wI+-N0si`$SG^ThzbDE0C?fDIW~j(|K@ zP&*23Da=+9U_!l7N@Ql?8{5@^r2q3HXyLw|)La zY4|&I{8Zz0FGO76XuYDV05=3n8MWUbsA`a{hKj z+oKZ91Y9El*HL-_C8YGi{=<7=54P&bw%wqhGd1*^6i~gi?Ui37+IF2xBzqyxY?~{+ zZ4X$9;V&AAVQ;$@6H!_(oV4%V(>p@jU05Z&?1(?gjyS1jN0g)i`!7qjEQjQqs+L}W zJ;AEKK|CXa64M5u7OUszj4Ls@iQXR_5#k#yAR5HCGAJSLRJrZ{8j%L^a4WG}Y(Nl1J{eMT!c-_> z!ca9yKwQ@gOt+@N6j6osg2lBrCc1o55~iP-FrASO(_>a*=nE*AwqQOWOl}ENP70~nZ4W~^kJGbU<}j|T#BT9_2m((&z;u-H z15BojpI&CMMG!+I$IqE*fO}Pqy#PM*h6FNCPXfMOiJI*jFGvIYax1Z0e1;$ZH*kzh z!=esAH>k&CbygZc5leb4$H+fbNxcBH>-q#h?J|+XycQE5oJ%P z1_`-69u6c%NqZ|s2^QZi0GNyLcJui$p3=(~cx(^{qs2!Q6w7X@LlH7C1p#w$;Wzkz zv=%^XWtoSZEP>HtFlv*1BFk%+f@f>?Zj^6GDG~CRYDq5$d9Nygkm)j!M98HoxBa}r zv>y7WmDnu;NQ@rBNw=KgWD_oNWH?)?53S?8sDTYIQ9$m6FrtxpngP+XsTokS0@Hqe zS@78S3hY_;BLz&dk{IfMz?NsK%nBJGnWsf4mHHa<@N_Qek9*I#x#MYpj0tritYYP= z_a2Jfj@(h6X!DB_u_iG1PAC@iR{?4@H(>0bVEHLD6Dnh9l0XCS*rOg_;t`oBj|3SZ=Cd|L zXAnKW7(aRr&X^2;^;CWyQycoGDtHEd&xc+Qh(c!Cie=179J@$cMrM$V7bB%A7bV_a z$nAMT4@xI6;2Q}7{~+iF14$!&)*1s-qmIvTdpS-EB_KGw!Lv5(HX!ds7FNwKVh2Rg z7Nfz=TPl-pW|P|xR5#jNJu4%56AiRA8Cwf8;rGd16Hw^MLms2-O{-Wc0DXj|;fiK? zB`@pazBW$lRJy6sjStAey2ajW$jt}xeH2^kI{A2CzR&A^Npc^-(@NDrXc{^$z;Q(E z48P3(FXZ>*49iD}4DOj8Hcw*#6x$!N3CrAoaIFPBsNyW>`RXaqlQi8Mzklnse~{qxtF61 z>enTSjoe}K3;N@`9gCoTguP}YB1vOhdXi-MMtPxl@(y%apZVo>)EJW3jwbV~^5u5a zxiJAoGM?p6o7)Hc3cmA`WwVLN{Ai!7{P?s4J>#MOy&+L~T7K5QFVaW-!0+Zh<5m50 z&rUQxt$YO^{G0Ys|Fk~KcZ;k0ijQ=Fip6*^>nUB-v;B~N{itvFrmR0Qem2kP8$XNt zj92)%{V4T+zV68NU)^W?rlW zk!H-9>|fIFMSYg<7AN-^uj*fa)=}EuNBxW2kJA1=a%>bzt4EH z{bwGf{e9HGIDC}$_Z6?`^M3HvebVQ7M}hCzHAlwJ$$iz2`E6fYU+^pZJab0h_!-_u z{qy@QAF}+u;v?ik#rbK~r_TTCyl*_1@;>qR+2Fq6pYqy~@wa()U-)s0#eK#r{M~+( z`akbJa{X8LSw7~!zT%ZF_3qPCo1cO|BSOAStbm-nPy9WTRzPz8vDUZYM}l9K|KNRT zC``?d{!fqO`q{%4`U^Nth-Co4G+XF`(Dacm93D=cNR3;M=9n@-{PH3AM{eIAJI34Y zhAs+h2qDVXJtdAQ&_G`+N)YX9-x52<*Pbh*ZN|7l&AE6*=ZB7CyS>iET`~|V8R-g- zD@28`=e5obapF^e8xo8u5cO{jXBuN&F9UVhFLuJFGdwO^+a!D~tAZ})3m=|1WdMFp zaE4Ar3Zr=7B^gcFNR&8&4yqkz<72ajPLl3ACv>RV+4=>FfiY-~FEVD)h&RNjAPk`+ zH!Q6U-#QroXU6ok<{^EYgi{VM+}t)L`aRXA zL&ETlG6Rehds$e%XrBUOrq^J_3sML~fJ~u7E;wEDIep8EjR_HNv2DVf!CL6s{$s+vNJepZLd1ukIdzRc#?6@hOFet0 z4lW(K+8>#7ew!l$7jJIHZ5m?3ogg*a7XjPB_-ZZwx~y<|1a@rB#r=Vw6OBL%E~IXe zZK5yBmO5IP#Jeo;w=Bw}Y7U@D?oZGm!#iRk<@^yNr&`{od_beU@@DN7FLKD9&^tfs z2fem(*aFzz7K3lMXG8{H6uWN5S+sNOV5*uonFg_XWUN<`{N` z$GCFr6PU%wB-78(Yu4k1Ds#LN(Y8T!+-Aqsk+yd|@6Y@*B4wASME@DlAgJo(>AM{F z(gt4|$gROB7a^QZ855yhP#77z=S_i8*WvS&8|r)T+xBj0N3Nkbo9{uNiQ1`-VxvV305uD# zJAa#|J21+&25~fe3^qP)@z%4Lxv4lYu7WD)u$-U*JuQP#Ka#4u9g?4OgSE3P^40$q%H zyU0faL#yJ^y#V|Yj+v>?M;7r64W=!|w$Kh|XDnO94#c5zI)0=vvCubkt+7^MnR9vqAg{0B8dRXCgmucm6>obXoF}5d1MzsdWLY zMB*=%=O_^Lu7cqX9YG*ao?pw0Da4aNW9$Tomhxi9%3r&rBqY@_AMh{1o8-|?i&0u= zIKt(5IQZ^D2S_F*Me(qg^lmHtAHPp}*xvO>hB-&ej5_ZwsQ95D0Gk}Lr~6dM6Y z!3a!goyf#rWYKF*)N78Bb14U(EM-30;rt6qeE=N}p$x_r}qV^2A}RjF&^aoO=?tk17vJVP()6W#a% z+E#%BFZxYLqb)dE$;X*8jcnH$ugw|Rwicb#ke4u<68VgSja_WUc7QPv{ zQUad9#1*oH>s-7Ot#{?ATxhyM(M{>O<%5rckr{m8giD4QWh28$O}Dbq_%<4GK0F~0 zEeqfoFv=!`OC}g)mEhBivZ`=NReM>rN!$Cpd+s`<=_+w6yi7n}$5)qBgH@M6ab-&WrNV`~5fX6u+OX^&Zg?Bo z*}&$x4t!uJj`B(^G#QoQD=GyP~ ztPT$Jh3mjH+850Mv4#5w!gs;&(Qh20Wns17b0}$Q;FyPL0F;d)QSf*d-Gjq&O|HY? zJ6wUHX^#3TWo3K5EFV_3t7&Ch=R*132+PN*g8gI}qLuAbSlLdMR<_&p@R@W=EO4kzn#8%_XfX1>V-;SHOZ8E)kT`EY6>PkAljJg88ocLFx63=!nil zoK%+7BW>|}QEF$@$)Tl7uZq6N*lhn&c01W#zo$cUmTkfb!$!Or%L9>H;sNJBMZoh6 zZ`qnQ6_@168?5pn5D$bGGmHo0$~~V2C*Wt=aOzTSf$SW|47zb2bn=5$(fKS$yIUM;&H@zXeF-NUe z4-UcM5Rs1Cvg-$E#_=*D2ImDT6tI0B>u)CU8^iMbLw9K~`}gdhdK_~=+3!3KhVyh! z)HzFegnUS9F!+tEv2bi+4KrgU&LnmVn58siCd)D$$Is9*d2-U$Dnb*@W zXc9**h0<`HS+kH;TgEN4jsp~&$22o634NNQ6rWtap|5F}lb~Me)jhHL3rX3O2>=lV ztcNRumvcoA(nb}m;Bh30-O%)sJoC&5ViFzWTHUmh6E#YLp7HlAss0x+%<7lradWa= zW`0}z5f`su3p!5E#GUA}H-JT27*#lDO(wJ9HM=x*sKz|$A8{D;)7SuzPCt?l3EfPM z2+)xv3G)j$#n8%zwB4AROOx{`&xHO0%@Q@gTQhAkp7Z~+S6T7t`#8yK7D`^TJ&MUS zU_fdru3#Pj2Rc2y%ZptQ5pU1kt>!m#8I&rn=qPuZ5iO_Cm+}`dM`~-(iC9KJ z8HrJG4>?J>kTSy}m4Ti#)m8}}wM5z9VF+I0MHG4u2dI2D>#+O5%xUZ`aQ59Z%H>p1hP>Ug+84 z)~0=Qs7{zQ1FEeXdGUyGdG*Vj3~}`~{AMjTF0a%)n`aCtbw4LBTB>(|iPLPa+4kad zqs#M^GeosLZ5e(tVrw*G8&lGWXz;vcTt>c$z*o-zQF#>KwyT@L)`05IelK}-7;^gq;*g;(Sto4-LkaV6QEvv~QV z7PcuJ*cH&!_pGjup#XT_h&BK)t|0}y5TlSkBTYR>>i^z^0_J(}tucpAVEO5Ce1ex) znyylQ6~uikR*SHQU!g|^W0J)Kjlv2mzPa9xGw|%8Rlwn9f6Y2hebv(Ql5tCAspqp4 z)C~j5f;sW^^g-L@E6qTbp?5mUYcxl>t%FrY0`T5qoFJY=naJAMUowiZzXGdhb$tf0 zuqqXAfJcD{N-9jj8g;lgh}l<2>;TH1jGRKyjX=;kaSc)dQL!t4{0mS10mv^<#rJ`H z&}zE)!W!E0nM$|t-)K+pcj_M7yjED{V`AAGyj8FHrZzO%nPG{?s*L4QD_rwX0enp>+59#h(K4R$=8^){b@FU8eC zM`_EodB&#tEMuc@)be1iOxBOd@TnUYtw8H9E8Y!#9{&X7k=q>2Gj;;@OC5aroPp&& zPY1V49ZXb(k5wwJO750?m^WaxV7UTo-bwPNLbMl8PJ;5fg#-JR_YcB13r1n|1E^=b z%z+5~0)B!2tWsl*k4sPF`Yd1MdazsL3+Mw35}-23GO_8^{~8C=R6hjk3_VDJ)Uozq zrFsLNww$dIl<31Emsc9O)&&=AprSSU(Dl0Ku3YD=+YzaIK6lQ(nSsy&hjSJzk1_A{ zhiA5c0c#bMUyfp0UWHbdwy&eVvlUvbSUkI(vwns^`)sNXX``0c5AcWf7dU4vM!G;I z3~h=gx}3A0W-g%i3`BaT4Dc0Q4d;uiD2FiC(v}q}^ebY60!3FloUPB`^U{2jW^|+0 zg3xzyXA8uv&?^~M=xvEPTd^U_h?Hh&5McUNMSKp&GCDEThjfAubj~hk#lVxB$bxP; zR6G(hhg+OGo~jfzoW>)8!Ys)p!2RIQDR>Yg-XNk}D1dt6L_4-W)w(gPy9hoxX_ z=tBA3yyoo_DCo?q)s~JzO%RjJ$*)ALIEI=?ih;o>!B?GMJ=p801qffrbLpPW;ME|a zEeP|EED*mZE-p6Hi3=~|jPj_;gl$WKZVc!-w@3?{Vh0p(SR9Lzm404MeBn(3_A%aY z4f1H()hpA|QtT32@&~d_Mk#iRE%{VgZ+exe$OxWAT(n3p+#}wA*Hw9=4%MH;W7mO$ zhBi|YKXdYB#4jr0=ryUQ#2sskyxJk&fjgnG4lhFa_*8($vPmpz2!JA9HIWkU*mIL9P3?oW3jczb&CvwZ^IdXmHlM_^3zdY`-5Zpos3RRI8OzMzlT&X2f;^FkUGeZs^CBb7a&-Q`$h+& z2E4Z6)xvimKPWD^7RUzful8C#UHJ9ut0A+eYc=TRnfma~CH`;0*%v_V?9M|iYn`p5 z&=7A6goqa)03Up~C(73uxh$)7pVPPARQmZ~w>t$VODaL#@oxFH2s@Xn*x z#0TJz$RTgTXCPS)Et{&8sj9hZV;C+)GO+v_Bu1^gj;bBq%~l5T>wIR{mwGy9oTYi9 z^(Sebz4gayqdGC6M=p11EoV4h^nD*=&*-~*&8D(W7`2fji;B>(SQ`^+mO!QS7WADMH`ey+Xt+Iz3P_F8N6 z8i-$#z>G+B5!TVfEX>4L<8KE3rs1y+e{l3boxAq~p!;=+@d&A8$PBd|A-cv5j9Zg9 z6B~;?_}h)Yf9o_)rqcXLr|E}ux!}bXubmHs9xh?+&KuwUEh_0%Z)_`ISHDU0W+xr1 zLmle;>>@IFn3zm+$MzO)!cO&5qmTNr$2mE>@+{uo)OOouA=2SI2D>JBxRk|}Y8Nt5T#DjBiD_Lb;Wn@ABP8kM{q&j z*iKvcmmr!4UwWz8;%>Shb7Jr8A&cW{fltx>!k7$ifXff?d8O*;k8mcjiwpq%AdCb* zEK&yu(zxeayNi212+_?ieX%=oDyENHDA%z%)feB#N(4U{XL0=*wAv`)lp{ z-1EjQ*j4_Bti{I$W7VZNu&rf%n2(+6*AE`-0;8dWuS*3-5bQG(Sk|b)QRv071q06F zCSzj_CElOajQX^y#W&b?m~-qnrpC}n_||ZE%kzfNG4|R-zBd+f(K9w{2|wt4mfWd7 z`eSg!e-*5G<>D{DU9PPl8o8W0`HZZBHh_Njxo?@~xcf;EgspdU*;etR$uv;K9jT-!U-P&eul@Bvq_x03W!i!_ z%rArHdjP<350_|7bmb>H!*ld9KLV7r6r1utBC`1*nd0@*@lzk>4lEsl4|IHb>39W~ zs0G2C*n?(5xRVipI?0^`!JYVW7J)tCEPE3Rk$HV4-az9u_76ekj=MwTdj)^DBeOYW z8VZ`NZXboof9#jA#d1%dcBf0iR-Z+bOkZG_OBw)UW%NW0m_-Y`_P3E{!N27q#Nn&J zjY{g!&kyRO!u3eogA9lp!HhW+clRE+DoZivu=qFnjIUL1yq`6JXXS@44N#G~wkkCZ zr({XAN2W|RImrafo&FZkJ%mYd!<++$>47434l`O}BRo0JVj1rU`S^ngWV6~`Nd$Mo zC3^vv;^Tuj?svCop)V#yEXh}13>GSva!?$$yvgKIh>)Ul3$#4`5vu@XKDs?6vijc! z(r$mr2etb3gcL%efl09&rB!f4fJ+RLf=nYi50XEw;J4jLdsi?$yO|4fpiGP<0y~C= z)87IgIO}*Tt|b30eEncGk@wRUO4gKY7Yi+slscix>bn@BQoCR>lCi{Q_HC_t4WQ`A zv|npF_r^?rxtN6SCPKlqFU6BnebC(IYr*yD`FSU4)$~#@;5d(i8qbX`51iuXtx`w1 zasshh2-9w);V5`UUrQ@CSn3oMVD6xM9}k>H=Eu)K*Lz#qJ9Zc+bVWXdv2^?Bp!vR0 zf!V=vR}LZrv0HKa@DH&gYZ>3wEMCLLcN!}>KjEJZ8$;r+mTmy?tOFa4=?F}xZ|t|B zQM~11c8-mVGnDVm@x$;4fmMcT)G#&+pe;}@O+fFv9LvWS(JSq2bNo?Os$()!{Wns9 za~^+UeFYLhR2$Qt9=Q{*wY(zUg)|1XV&$9{V-rf%8D{|VGbfDG>Ln9AbfNPoDz?&3 zMEb6(aZpL&*(GMC^1!IIFpqFViqJMeQ_y|2tK8%G`8wR-4O;ei$cUlOce9k~5IUw0 zu`~O%RK56Qa?Ga2lXR$c^d-_+ZQU=5E0!!e>Mi*fbi9DQt7QhPJO?0uOcJbx)&fEPEtPY`&nd6$3SimOx4~*rW4U_% zB2oG}Zt)LA_ikQUYr}=M@N6nHwM9C?19XX!8EA4h@^@{G zd8gMi_R~-i6b3($!xy8XdbY#&sh;+4y%3}k8~nm?{cNx_fnQ3G&wazVJ5P@HL*o+z zMB@vL^j@9XKx(Kz%wvJG`)u=+S<*XJleefR4xqiAIGf5%9Oo4sq70|g@{Vu zOv0IabU^n7BK~|MhM%ZKG2%Fhz>UGt5e76j<;YVmm&%3tI%l)#S593+G!^jf1({r( zoV%oweEat~apkBxz9-@j>)wdrr={JfbZZyyLh6{BY?m&#=X)a_6PvOe<k3lm*XeP~GnIS3Q$4e-dRDlk30u^Z%zBRh4)vVYXFb&LimaNg zY|@U{bPO@;9)?%B>JjB?oUVJL+9fZsOQ;IVdl)w2JK6bJ^nv!t#^5hMPU4cuvf7XM zTJ=^*=7N?&-`6XM}OfMvi-dXs&5lJxYE}f64*een>!xvrL zj0|{?X$(debAdG47gLC1?Qq`R%%qDO5Q1df@Z<3u9!TAahY`JxJQxa{EoNSem+7yc z;R`8uNRq`{@xa%=@j$-a41_ju^ge|*wyP(46b-#!F=w*lE|eOi<07YOx>u);!Nozjve7SiFkRsqd#$v9jo;PF-1-NlV@7sZL$kXS}Az+L&2E|+`O*+ML`1HpI7?eTpKEj z=&+2FOdJJWT+X`N;Q?w>c#G&i!S<6Ro#go94Nj!6ShmaLPNnpO*N5M#>-+Ls<%72> zXNI?`+JmXy^`koQt{c@Jd5Ps8JPLjtqdX<6DN>J|uN_zY63?XLsz-ZqT-81zHBV&X ze6||$=Oj+qpZu2*ul-V8dwD8}%mbYyk`A0_LU4y65B0WuHAvG82fx;fjd%;XOzMyB z3_H|1#-bUYAd^fIRCblFgVHSeU?94_7^f|C$}KYp*Q*sy59)cb8rT?xod`5G=>AG} z%ZXFL&wCJ!iKvWgtP?&pQU0A{k=Wc!X;449A7B!R_O7tuRkRC2YZLwUZ7*xZa<+Ia ztC008mjAn<9E@Io5h#iK*gEUpopL#b`v~`WIWg3|E9hhcjR}~at2=)w>g?y>2nqQb zMgAG&A@Wf2#W)>kbTuHgY>r=L5cJL`Lg<63Th!z&TnSegs8gFMRyT3zy-r@ZSURW1 z)yW9$ZGD}IGaNu&P_`k8gZWtg4QJ>nC6@1%H|)b=`E5lE zF{mlOkt6m@Rj&*hsPV9K)EijS2@mu$HT|ReGHE}h=ZeMbHmKbgdOx>i41vcUfoq1` zIdEKghSJUS!<24-$c#Z5onEZVS;BJAui5*;pF`0f6DU@mU7UMSNroSAYzkx5F0{J{ zdsQdeixs1)7-v%Gy-SMFC=h3kkG+N6-z!vZp~7r#qQZ(U5}Mnti6#&JuRxo3IP*jO zpU(&9lQ^oa+Bu-D`jla9)u-mSRgbiRSGV{h+`IAX^6UC!e0qQGaha{3C}>K-&J=o2Cz%? z!A})P?Sub17%QY5^Os}=rqjbEl$Cxa(hHFA?2tof)L5H0)6+>pyImB^&7h{xZ67hU`~Vb@XAO`aO;pHS{+Z zuw6aV-?fP2m55mTq~QciefFzl!>^F6ck2$&OE(gxFg8h|;c_3j$k=dra955QHRv)t zE#q?JZgtPtZyk-BFd>sf8<5t~bPT>c4P8SVO^cAn3%}>h2>+o*Wdx#a3zgO#C?}n& zQ3(ykUPzV2b|X~=ahTWEuRIzic@S6B15Fb_gLMopWzZ}JU5}vI4D!`O=+12Q80&Z8 ziE%=>7+20X?n4;&YaRC}9;6|s0Ul%-K2&ifoO8pwzK4$U*CT{A|JC&(-h4pNRy+|= zj5cF(;VTzrOc;EncSJa9!==wmz}!RG5; zJV=LJ#xMgU8<1259{w71PP$|;W7ggjI*oi(tj3-~F-zrSAlS8`JskIv6brLlCj)e! zUEKq^Z#*C99%{9*wC4dj=I{Y3gRlX$_Ao#_y$n#avj7!YR6`{ktMxo1u?88{aX7MG zppZdERjj(p;lqN}Gh+S){g4dAd`}omaax0&`~y;73NK?&W>0t-K0ZAnkpm#wi3fcezX$jX?`?1H&N_0$9Inz&lug+WqiyE9ZMeSF(B78iX-pXV}&uy-nK;k zN>VQbRQ6n>qQB#=`%qn?kXLo_YXQDw!Npz8+&>zI`)*-J9d|E)G}_aG`zhl$F%fo6 zgK&m|{Q!61OXL6UDX_h#z`ykr_;62w%{>Lq?J4leo&p0s1&%==W|H@?xOQ8^TxI#t z8h5{$)vna_RZxr0(^((O$cn>b&iL`sN65N?W28IE9%o>;`t)|;UdHq^{~ccS%}1*G z;$F&n$D780HD{c9q_Swlk#&fwFKEdkgzX@&Bc&lXe;7K*2nm5D9Qu(OHkzQX$z-Va zHEX^SXZqdHnz)R90IewthWZ8Fhgc6;PEC%ovQOwf0#~{Z>)tVGLRbeGP#qA_)6sld z1`>D2Hb_?4eAI^x(%Fp<}^$}0I zhpYOkA7>rU|FHM+V>PqXZ~KamHL(46WGBgJe~hbsmly40*16RjR&R+UGq%Ui0(HCKtinV^;a1 z)jqQdyKcj*3P!7gW>sypy0)cia&z@$_?xdoRds4xA?@m?&hj-+pVd-zU32wy`ot~M ziZugokbl*x<2@;1GKF9Gm!>0qX601dUPv&hW|DHt+j}EQGfdl3I`Y zH6`*8{r(KklxR6ES5;3yZs{-E6^u@ujlcQ9=x-W;EIed$YOcYXuu|x1kx9o3;!7Zg zB3wLLNXw@wEzFQlQ>Lenc+H({i|LoZu1}Hl>5)W57n75YE~bbe3jZSfC7T^xOhzdx z=S$JWd+=V?+P0XCcuwyVpg zQ;`Eh=JXR+ny}8vU;X|7n@J70Jj1>of_N_-??5IE&E&%RXhEPI`E*X$4`$@M{)6wD z&-9Lo;>Zv3U@hO=36W}v`dNSUM{I(j{&_u%*I>R>9~p}ayf=gg8IjwIuohGn&$W++ ziHGB9>{cC5+v?%vr+(0SSTPM?m>oZ!+;m_dm!bI#&C3YQV?aMf^|wM{hVPA3u~{PpDf%5j-=xCXyvVN44=ZkX&EW69BhOK7^n&XZiE z-fGD1q5DBd8}0gl0yzkQgNPihMBw2e4BOFzo`T`e)Kh3FN$Dxv8_d*G_=!Y>A#>qv z7;B7uP;}a0jC|3E!rqBztk?q_uD1}$81IMmC#PKAehd5FT)F)g1>egdoh4seXLY&Rv?_O(gG~3xp;wOjw_S?fqV7PcDIJOnPXL`9L5Jd^?^MrU zt$ISqdXCPl=e6UHT+h1|$@XC62F*8u=F7ec+zn*ZnCk;3H&nxN>KBjA`BnG~&N2Px zfDfEl?0EX39Jqc;&l!UnZqN0G`!(Ed_lI~nHvzU%rF3MY$<87Xhi){Ir zKqlnrGk+>%%X;jgexzu($Y{ZN6A+dS3qvAWP?tq(ohTl)8hH0}#}dq9fBkBeFY(=XQ~6 zX7G;oS*m{DLJfvylTT{I9EGI-J9?2t;oh9g@tiqirI9Io2>)_` zrXezSCNYp1Ungg!ucgvxSTEz?OCOd3SV-ot!%UU`l0z>OvHV}l8xG84`M2m-o)!4? zYjO0t(&%+#jOcacSW!GA5=5Rpgqgiy`+j8TCgOUZ*n|@GS`}c7-cX0X8A#Xu1j1f(fB@~HKW||L!hNVd!PyD>4 z<}=N^m8kP@dXvUa7H-t@G+^l!chvtd)XAxHX5u$9KlM`xw3?SLrh7aARf%}glosm= z)NA$nZz;b0t4NSH{EG{LJURST=by?YC7cfpx5PZ0lD7NvaZ(2xqRr|=i3+`s(EHM% z{d5K#kxeQ{oQJq;(s3W1B84%oC~-XE_NU`E>o_h^SS63VL}_Pw2G^*vBu^SIil3f( zFOHvJy^DT;ZYxN{rza`xXa!%h&}QskyE-@t%>s_l^eYOWAmfWo0$aFQtsjn_ft+sU zF40xE3~bVRHNu&1Ys+(sE4^dN2lDuSHAG4JrqvEu4Y%bL*`jppz~k(f*qyyJ`%z|gdy2-~3PrO$N{yb}SLz{F^%637}8p_co$BBXI!=z1M`=IIE%(lB*GEanq+_yLBGH)8j#unH(w z%RzcesksK@F_~&l23A1s7>F!iiGH5Uo8KEI5kZ)89gkPu874`FY85`F{;3N|gx?UK z;gPj7$Ook9H3oaAGkN?ON$O%{(>wQ!FShp;U!wkUcs%*t)R>IrF9LaF@fcXgQc48r z>!cb$5@}UxZ~vGyB9W zTwfol7#JQ(Z~VOe*xas`-3bZjAp3SSb9Q1Q)Y)-Yt)p=ZB=?lBGZ8zYTup`+S$qY8 zrH<#s@RRV`ta{3NsApKRo@cm{D?h6mf~p>n+=+F`+WzaX+CD(H$1h?q=E$cO08$|! z+?T%Oq9?*7^sN*_4 z4kdwCg~MQp?qakQIS$t+@F^tuOKs>Uw;|e~U6L%shm)W@4ta2h5~;0KQPq zLGvuMFl%DHg%-K-!s^jF3(MzpD{FDZWs`Mx(sMrwyM~47?yQ6J4C&6$G0Cn(1H{9B z*O?fqW4YUK;$}BBAD`%UsIO#nxwC6{DOeYVui$YRVx`5 zLzv@fn2~ev{FxzlAjF9MlHifbnn^pqfX(YtxxE_76bK->zlDn#l5=FaS_?*2D38(V z&=BiOa__}g+=XHr7F{}Q?r_J`)t4HvAKbpX1M}wtZI6C&lq~_4Z205b2UvsnZ4?&& zvOhY~vHX@i%ttq3y9o%L>IeHfx|RynHcp4J!c{n?!M^|7nT{sEfHnLps}QF;3Ydf% z!BLwpKyeXO;Cc%0ehD}OGJQK3QhFQKi((1g;Q zy;D88FIlG@jZ7N)B}?PIAGOHZlG~hPz-@wM2WY?o+loOW!tdn3)l0GwSEdTI8sY4@ z5%sh@t6GN_9iI==)JLmaY$MUV2h=d=*Uki^GHge~mojxaI>ym{zRJb9ye3RMzee=ijW_}(U}nqep#{{pF{ zYir{(unPLecEP}&C7IuQ_T>Q>u@ zi5pO*Cs`$rIjz=fT+UB?mTY?Du*$YtmDP2xOd@n;5|^wjNhRf}qK-f%7-56v-VVd8 zbr`p+|Bcp~|7ffI@+csviyb@}_w4w}$CZW(?BDPc(87lk$Lp(Kbw|x^Kucc`<$$)O zU#goV*#2cQMpN_xS4n&-#wKds6p0r)8s7$)MdBFvpCET6KGD&5D_*)bI2vu%vjxR7 zxxS+?qNwft&8gaW`B6rv z+?cM-b69P%%|mUa*|kZqu8lG2+S>4i+HOhL7Rs(Il&)>t7v0z;FQ?gQ<=Wy9XlLhB&Vx{kn1V3GR=pLcJoVn$%L6VHscO0aG#V^VER+yb-~ zNJFLGyO}+!LWZO(3?Eiu0lNkj7UP*wp#k7N+d|74}bH zWP06SX@3&eK9iLsV}GXaNvQ5Uz8MHqr}rNJ7~}i*8V_6Vk5*pdLA8X}JrKgqDyuAyH$x`wb0s?2^#O=0ZtadHI_$F<^26Jz8OL+$)A%_qV*&C4Gm zwo_1jq!g)~hw#t~wnXN@{ge`Fc9lx5)2|1Qw1eb)Ta(Aa%63< z55w=p*~RrCFIr8TE-f$W564!JiejHK{A1{uSfZlOQ62E9g+eDG?$I%+xL0-DNF#QC zt<2E>k?iMM07=?d9B1Zu9X^xqf87nKXb)#5!vJmCg!6K#K6D&_as%LW#06=%3?TTs z$fU&F6KA84Nkvj{F>_peC@hZ4`>eMEs{`IWjh~ z&pAXcoRp5n^1rvAl2B5%`sgEUA@~wa$P2txXUT*w5&BNch%YJW7Vqr#Eiz@xiU3rX zKKHRmKSTDTWu?XHi7}buKRx#(&%y8MGrmOK+-H2by137Hk2yK>&Xz3DY_8D^MirEB`b{dH8qOfsMoXg|IP*fWa zE5{Spo8t?iv>ZhM4z{d=z$DU0_bGW6@S^_03T05I?C?as+p7&AWLG`f0K%(7jX;;| zGZ+BYNa5N5a9fWCfQwXzjKyAQP3BOjN8~?#Z;I9yB9qYCNoYA9zWLb{C|`bPf$}pA)4c^8_nDN>wc%GE za8qxcoYbN*&wZUL;VG*Gs|gorgh(s0;iNpjM4k3tZ|4D8-hU3N(*4=Johf<$5?ZY2 zc|02rn@>vO!KywBPqpfaRD4p8BxRF$9F#}0Ml5E6OAd8psF1vLMEfgpj2)+;ie@G&Oa}P^ZNkhOcoRwl&Sj z$^CceVpF!;O&tqil=cFH>1i6w&}{XWw|J9Bc(B+07Bx`aZ!Or97{=mL#iF{hU5P>d z=olB=fV&))j3j)vXo)A;xXHK({x9!#b&XvE2kXdQ3Qs`TwR;>7w0Q`u*5fVswgb)E zZNTx)@Ms+msU~8=4n*PJ94v#_W69q7F@P9!zZV)Lp3lvfFzP|?4`~A_@D(^oxA+RG z;bwXgmU05^K36e{uz686Y_uSH0bb#A1|CHi3};V84Uk6ILMP%1rh#*hN>x(?S$o_A zWf&v!DO@tX@SLR9`mUy!gB$iv!+u*GJ<{;d^={cysYoqLmH7)2p}g}sk=?MnNnM}m@oxd=g-iKrw#s|5{$+m~~Z zqX>s>npMg117m30xl{tDGmplpGG&PS9ZaHI!2d1H;u*7AYqrH7hpgY%a-(asZ|tXz z#{WUjpvCYhLV8}g427x34`jDGz*d)5+R5<^c#W-IGM|-3mpAA~Gas@1vr`!PMvi_3 zBU|Nl3HgvDMhdbUi}pFQG2#S95Of$u61M@=;_xJGfRt;2iGDd=-xctc)J0pa7cBs+ zDu0+=7-}ret`UZ3ix6~pjr|2xAVKga+hg)l>o#tIM>$6h%)im8Oe zEiwohvI*!KKJ-Mg+5bmq?!iHX5z{6JYWjOvo60tn!-tWh>368oH#Y8QdQj>PVZVX3 zt`>iF_?w1nx#}y1(3NqU-~K7R8P``7gulY|J)9nEl?nN65C&HeI`&KZ#%{~42aEnO zCCPObSz~q6_FKxp&_hGErr^0_uqD1G2CfjSI9*{f+2IjIM!ci7MaDAi8 zNc7WBZ)DMA+rr~PzIBMkI#v?G^4I}3q`>Q*H+jKF{B<-PvZ&evtZ0dMdA+~slkiNv z)SX13vbon^_6e@5;H4m9v^k2SFN)1wevZ72$E6@QtHKjkdQL*APf^U8 z0c(-B1xE_&)ipn$TI6>JcYsxJVYoos;3Y1u_b&dn%XTX~fHVq6=8Km0V9C(y3JreO z=65{3&a&+TE`7~6x}0?N7N>|4vF1bTXE=3uD4$1%9*|u5f0#UJY*Ss-2+Hc1s2!D;2 z+hO{u690wp(Y8=9$;}2~M&>g;*nl%#ff_$k($UzxADTkqArDxf9-fhk_yiR~_4nNH zFkfs|P687!@;cP0Du7(CeE0WlN02br(x2P2p zQx3GA!ywd*sp7H_-N0ek|6&_Ko^o7KrKkY27s>J2DIY zn6?*7JlaS8&DoJX<^P}TB$@S7ckZyWg)BMZRX7Unt$oB_? zS?KEDX5uol{5tjGfMkB_93)dt&C@W=zg9!spDCw47eY5|8z85SK}H?R|HC?>f%ZDs zXkaF3_ZG=Bc6ovN`zNw^LzK5gt=WchW?q7&6TXHiyRH`5H3ANkU1!2stG=MnTgGcr z7MZeXgJbFZky8I+k#d+(iZ@g%bD&#R;F&a}iaQ`^B#y1KpnuCP?>LM3bM{%xgOr+y zT~@^FgzcP@wsQ`(iGJEqa(0JR{jX|i`S&yi{s0!ckHbhFLxU4IB zTS4@;LL+)x5$&CbK(y%b`GQfk_+?`3ibC`9Lg+_etLWN_0QcJqu?+h$-w*3#;1AI* zTw`sxzgeLA(kB)T;@A48`dr}Ze&$2F9mjH9ew5=_UWtb{7tcIA^UTVEsJ8%@#z(#A z!b)@@%?c}v@q}Bp)sV}ta;k5H(&=L}HmOwclJKR6Tfu08&+22ze9S{hTGivSC)kvq zWx@^wo@c5dp{SaO=R~t=aumjuW>uXLC1+kW1J45=HUVQcuO*MbB41MEk7AKS~k*Dy{yoA3kk-y11e|u|cw{d+SOKyMI zxCbugs{^S8Qm)RB!`kF*mC`~6+m%*CE&$=9UGSCD3CH8S9jVy9T*TMC zc!jEG7t9Hp8(mKDrMLto&u`v2#^>H29%jTW!PQ+j;{bE_D4a6*OkBzi;gY+3ZVeKH zIcZJw!;hJF7qkQ6^`RUTzdmTc2(kmKF8joyAQXzEvaVL%a8=i$Ya`Dpnz9=bPw-@+ zR5)YE+|8Han3Bx(4(uh;a2$XfA1>UA0S6qcL!vw15+htZUL3ytqZ6Zx#7Tw_vOL+v){eX5xECDO-=-}fg^dDB`?vrSTycHBR47! zvY4F!aHLhL#o~czExcVsc-!N%tfFdJ0(aVQ2PIc)!;Lkrc_H3y#AFA3TWFo&N^{Ip z6sh~oP2?2iavUI}MCu#E-CC_B8`Z)rtA z1o0E_3K~f_?(EE@X(O#(da%E)?0cD&nL#4f(exg%jV(b5pP>BN{BT+yvy$iz zf8Xi=+<9-|_b(k;(9Rc{Ail(1N?$!QJ2U4IfxqZ&P5&sL;ttYYO{~VKi8ZmC4G^~6 zM`w7=CYpSs2_D(b2BTL)9S0k(TD^mfK{Ly0mNQ`foRcWq(f~42=V5N@)}0&S{RJ!9 zckIqTy(K`dVWG)9J&_OgcGG&Ko@xJ4b?52oCR=R~dOtq11V^JnyDaK?6R&;s$8N2i z{vDh8%$w5G9a4TDBZa;iG27^2^1~F_%>9oltC7kQH>a|X$;xgCcD!9}GUpm5wSnz) zj>LK0z_kuDc%d421H8qrgXj6Ixf^1MBn9mGH&>^mW>$}Ca~W&9wYPf`nKIb+EIvn_ z_BNK1NH2CvF|H$OJIqWnlBDx_nau3Vxtv;EeBk zGec*jnVD>iTZ=bcf&CFtvMe$P2tC_f6ObfDAAV|M0hA9tk1$zTnLFj8z{11V8+ooz;)>+M)1a>r_sF%)8Eu zsR6E3Ny=THs268O3F6a~$xxcL@a zb$D^X{s4?i6&8tVkM37qSr){)fej7@kapbch2wI$ZRSDFvCv;3S0jUF=1#3Zs=md! zj1O7}u_wiKi61?3$zIhTzPITSE*^wf)xFbk&rTRXU1Xm4DJjs2zY_eNWqw{?F^C6r z$@_|NF8g&-FZ-)E2=Ub}EK@HTqc-yH+-Tl$Mg*4@<6w3#<}&Ceywo-Pm2dNGxEitX zAo%nNG2cy?@4=4CnlNS+4yMMr>cX=CdMAPa1XKq#wLQQqXaFs>m&btN#pRzm5zXfz zho7A|z}tOL7}KdM08Y~`N7F;72D|}Fi!ND@@&JVLATmA!F*pxVP(onXvQ}gOYcUro z>mwK9lGSrqpLuHmr=B|G)xvvgb@ zOJPKmSN7@?>b*FQnE@WUYxu^4d8~gLl_0=VmrDKNLD3~Ip{n@9_(AMBR_swawy<4d z0l6))5YhWy)tSG1fxw5YXre7K9ntHp=vT6$Ih+$7M4z!x=l*9_G~Lc5PC@kTR_u?%aWe@Ez$KaO)6QnT7^L zI^hDg7(tGwTdlTk#;yj3dn^>3^@H+cH;1V;06^le5Y7TMSOfCF8ul`isd)gPY{S@{ zj>f;Iy6q#BW|&t(MKS0q#a}u8#!;uhB3<2OLb{?+P^v1}3@p1CX`6c@T^ykCd|>!- z-@pZ^xTZ?(07#CdYSX(K&I$|8Yn~TU(r^l!v*q=UuEt%CMzJuoN`QKn&R&n~6kvfV zEH&IaLO%q(m7{f7*w7SSE-1C!k1|qFnn!CX2xb}1FEYkHF>ff~VIO(9S!x=S$A&+= zow(bqUjCO*$bhybpP}TO#D$2v&5HYHR$Ol4RK#6o#r;a+LIpep;tj((zrAhAr!1yz z$tO}84x@Vn82k3Nf=_EH1B zmIDxNcdDv)tO3tN)__0uqvU{RI&~vT=79eKrDDJ{VRXq>Ko`FSA?a>okS_EX77D!MX?zXA}vjU_86=jELpt5^y;2QZ9!M5%Sj9JgkSv~wRM;w>*IJ`^Bf3Pf z_&f0FU4beA?PM^Og2nH#EHwC1=!SDV{p;COgW)(+or$8X*49FKCzbyJSF@BuaGrVF zYAv`mnRfTTp9F`A)CZ6tvlr%QaB8DVb|5(ZF$Q;fgg=%l?tT^rh+aQNLo_N4(cLa4 zQ$I(67EqHL5RfjV6<=1nl0ZSkCO}=C0*Z;$r4~>$E(4AxN0j)44sV+y~zzOy(xP z*=_-~S|x#EDs{RA6z_wNpO2q3NzO=>@)k=$r=2-m!*FXR3{0kWLzHfHnh)O@r8J~U znJusYKRyeTC*Mng!bIvJltKJ(5TVn2(IvdNEdG1^pzc#|)2sHGQg;}32HJa*`I_2? z0m6lRKT;-cMcV6O1H%>m&)raT8PTrOrl~|ZW_FnORiAAW+{c$?SgbX(6z3{{VAnS3e8xFbT-G`e}p6?n}pVAriT}mAXVHJ0&q9aZ=)h#BqsZ6T=fnCx+r` z;lcPDfWLf6LWlw=hxZEO$YJr?2LVv`qtG;89RnA9T2qpzKAi84#b&dU?Ab{&^Vg{l z8vDvG>-U=MB$@e3)qiJ40*H)wy_bw+C&`G%Ict4(WVigaguf~~Nk;ydEC3^WkC(~a z-g~^v&hJICP>@kR&WYQyBfG&T)9D|wlVs%A=g^Vf<6A*%YG&{8yBS~Ad%TVK8QFWh zQ~JO6cn{;>yQ{bM$7V2oP4DpyjQ>MAUUc?*K4)0`u?LeV_d67+Yazi-KW*mrm$mxL z-4Nvd5v>L5UWY#syv^X*AQ(fiV1xg*0rME%Yke(yq5d?-+=~qJQN1rVAD8)l?TFMv zv$na>^>-A4$3O5$o-9xlBb0I{rU)&=U)Pp^$48LpWE4}2icdr*akd_c2`I*R9SxK}~N zOJEw1hUxZ=nN7b@zTo#y6a!LZ@aErBO}`!Bq3NdpAZdENdsTQS=pJhfxDn|usA>g51DFh78s;uipDK&pyc4f2El?_H^8Uargu|J=4HK6d!+RU9GuLYn*W9FLr zio)tpA#PemKQIX9`r>!VqaH?0(gi%V+s)Ll$$kt^8l&MJ@{rISf<%@4WAP9(0WzhF=b;iJ;kJR}7ttGUOjreAh7T~DQ|c?dOX zgnMWY1Z(XE6weVYT#GdSC5R^aqPY^}uxKvb^j{*A)%1cam=3JhO`o$itLZnSVJZMj zX!?^7Db(XAZ2r;Vqi{AuxNMFG^n98a+_Iz--`4c1yUB$rIs5HKgm(qy6`cEYN5Kai zMEN`&)f%~Ku7Ht5)HN1RZ?4G#>a}GlM9l?!0P1)E#X6MKfeAjdjwRjdaAehyW7Tn6 zb{%ulbvSe#PY>30?3FsWr^;x@NU5W&6+3>bC67bsm!8XhBVV!nqqA!%OxN-_UeS_T z)PkvHDFy`|58zSJ=;9_bJOD4d@e;a{0t+VK0>hB5;nU)f!?lLNeRF<0C#!0wRrQ0S zd61e*?t3zY#B)%!rnYV99+tiqrLuI^qPAnF$&IInAq5zIYVqF>B!wIv3OTOfrWaW- z08zj8aj7L89`!gR*%Hi={V1V^4k6Fc@tPs_NJc{Zk&KmLZ}Mh56bf|^5{*=UF^crH z=ppBWh2BqsGf_WR!8KGXCJ2?01>Qs74^Qxo-4*_!Itdxlec_O=SpJe#Sya{VL<%7% z0XG^Ut3XS>mR+m_MTL&8f9Cr@43QkbO3ka646co{%OBwmC0tnk$N@YZP0te|ts^4j+SmE^HmGdVse+1!UyQfjsQLyLd`fUYdV+L9)x zxQwXL5)uzT9nwBB#W)iM^Gxozh$9324Ozzfau%*7#jq@LG~NZCip4J#u)$$kSG%Dl z94Vr80|UUh1eIyJhCANRqXpdEVrQZT5C`~hviO6m@R6)*gdi4C53*=jF+rVQtEa^B zC;cm{4}yP5^+6o3=**>{j0!*n5Wrt!A>;`;EKMDa&mzcEzWi$x8gjHO;mtgVo`vW{ zfv@==^479spANnb!Q^(NbvSPE;EzYhLGvxNc{XFoXpKoRS`11vT4T_jAbQLbz>Qky zXdCJ#H`R(%*}Rs-SiBZv>v2c>o91h%q#0k{e>nkEBKmbQc)egWCg2iBlc^_SKT7}&lQx;2ged*92%e-sH)_+P+}B>o?H+@k&- z@V{M57Kk^0K;s`<#QQRqZ>Q%%`jsfG6vzA1Ef#VKDbNc=euSt#@%5g#p>E6VR1#mFw0pXIu zb*%ZwR$yE1w{ySc2=1s@PBGt|gUvPjcKDCq<89bekbvFnpBb7$aPgl}4fd$K+KZY$ zMR@v}T{*s-=OoNG_JhzFsnj3+_u;8M&0|ujy$uHoLWA%-Ae8STRr*`n@ihcS4y%1* zUkeZP!Ri|^n;EmkymvQ10cOXrQu7fmFudrGf==@VvR%M-xc~<9ObjCPr;34%VvyVN z)35M>K}=%~^B0suV^J)XqMoMc*N0(bFz)HSjQfWxC`Dld$)Xw#GO$rhp!|(cx7vzW ze`9OtTCZG7;dQSMpXN2Utl(nf-rV=vO7T}2il0*`?J0Y(} zIqn@RJjmvA)F@M21+{!40piCguu4q&ViF>oJN9~oC|dz5aoCd_y&NYim_N>)4BK1fVW`2PU3ZGQAjfi@9 zwM7TrQZyVa43B5G9$(fPGy3Cf{^3@yy*-6LQla0yBYfwIM}L}QTj~5=e{f%K5Y0SG z(_LVp88m%W_%g5iZLcHvw%m1%{5-4uc_V7l9eLHGS*bp4~Vld`)!Vzg;@iWsMi}o_;Rpc$Ir%R(jBMf@gq6y{`wD@i$Ip+ll;y9 z%vyxof1g?eu0}2h#fPANuE1$!Di6NW0`hL&uCip&(0e+tD@&=Vn=E%3P^&g(C&`fI zBL7=Ede?EGUe)t`#(UK5H)RQ0v+IYf`PVqxGa|j{b)oz#`z*g!UD#(l_}C+qf9UnT z+xJ$V@$=OiSaJG-|F?bSuT=~CjGwP=KSKEz_L;v{oqUA&L$4j7{YQv@zOVS0Y;ot^ zkVOL7^tG_h{7#U%I=`3vX44-*z7e@D;^@eq_;JXH#ny8$+S7Tm>l@NneW15bU+oJY z^VLT<{p+j0pXf7xt-7a=cr&(xu)84@&!{2;e#?eU^%^hvH})Pc^VTSLJ?bL(8PBLZd%n zL#miOnzvF%%{gIu!dw1fq{H`yXb521Qhq1OpH`bSdlbl>fvD z&*k)tRj|CX%L-2%&s}ih6tQK%ontCEj2;EJH-;TUS11?Ph38Zi!x0ZuLo+?3$x&C@ zuF+otWY)|8CjSb?OEBpaOp1#%9+w1)^JG^pD&XETioQ+Z8+Tr;C@De#zgvS{{;1tl zr8McEu2tC|gKhpxD7KQF-`%g1i&LZ`$G~I`KAc>sN&ma z4o=)Q0C!)&qxW81J6RDN<_r&r<(*OC8&(p!nD=0;2FG>l2llwXY@2)I+*3#GWcSfU z#4xAN+-~A}f}B9tDsQtb(A8>mZ3us+q41h-Toju;%;_Js8t3%!o6$1UH_SC^W3;*u zFC~#xMNr@@!s}VaOxPh+7b96>5IIb^cE_zoMe1w-F6Z-X`SdG!6H8LRbQaDtK|#1l z1N5mKfNI~LZrtHjE*PL4<0zDX9YLX)KNPL30NkPex&|1@pdSr-NhI|}t8i-;usutZ zos)fY9F@&8?d++aivf^~A3#zmmAf?H2jC4Fzw>x1Z**&-zyM{GrYHk&<5G?@MSmOo z9W`SKq+#il0MyoRF-3N53@cPYeO60%mxI*HF@me0-Z9*B3PL_VX!$JaKKuxRXM;z( z5b_Fro0H%as|~)tf{w~osOPHJpT#aS7k)5skD0oh$)h&_?Q;uY>JRINNku-hjoJ`Z ztQ<}AP7^ds-6ZBFm;iLr+iE-vT-&_S+JfdHx(`^d7N9ibrV=oA!x$}H2fMo3@=nIY zaV)$W>3dXgi%Qq3hH_gtb&m?%C9-|GGgJD>RMicd|3;N*<@O+_&N#0oq~oRuhFpr8 zE4}P!x*lYL@xQ%Av$7=|VWprPoP1jfgU_Ccm52HyWspY003WVWFXJ1eK+qsY-=w4p z?r$8GZ%4j4X6``o+Z)eA**^1|z$j#^K@pfwYKo)koy8$5(-wR~ zD_Zn-HmSdVBXfWi$ARaI&HN}Z^Ptq=Z`$K%{w)H{{9A7YS>~M|uJ%R_4x2O9%)f~N zzVkm0pA$4g=YIuHw)7Ec9Z%9SV!>i$b$3pY@y?Qquw({ zeRJD?Co16-??R?EqW(I(j}FXy#q^CX3`G4lGdQ{^5UnW)jC$R$L*55Sg0Z}ufO~7G ze_+&BBYGKgpyiv@sgUZ6R=Sa9|MfV;P|Vapfjz)qGW-x?zS5?T=u!{+MC=vdyEjN` z+6e{vI@F5Z^3ITF?(1_%XR)2+{*=Ub6Fbe;S_Nw{vG^;?oRhZlJ7z$Bk1D^0@ zp>DyY3!LI^AX!AJiu168VH}3FJHKCefGvdQKs>|!AzYQ)6AmL7kW*u`MNZFf@6L6i zL_K)Qp)T)pT>WZb!-+Z<7;#kPdK8veITh2y6?xbI^cE`sA@c}}k7bAIBF8Q+v`@MA5$04Bo? z<+b5aW>IuffqQsG2n`xo4>bcEA+Amoxrd*?kbVvG^BU$Dqqj9~3s=fbMqc-ZIc;9v zpxx;owaRbq1_43of3sQ+@Z(jOn)z`a<>~7ZXY&0_io}6TTjB)C25ZL|MyxuI=x6*u z#LI-~@m{H2Ag8IKJ6-(jgp?igVYbzB!5qfh>zE0|1E*RxyI%7cSd{vpJ>Cu)oTAe<}@y%pa1e!1Q7Hli?&tmZqO}5QHXiDlA5^ zP8Qo%Rf4YM44=B=DU;8ep+1171t-ZXQu9CW0(Gi;ehysVMw7u0kIlD5CzVGhjWeQ? zJVtavX{-{OHC0$3-|(t}K&_$j9?J;%8C$ zsK$)fn7d^K;i571XB}OULwSz7UIlFhAt~C6cJZotg(q5WmzHxuMgesQllgoGUy?eU zlU*)Ohxeh4j^)!_E*>*s83kvJ0>Jn%G9-};T+T?_LH8j?^`ZL6H?T_`h)P8?7^v{u z!ex=J0>@ovBFwvjl*Zgx6gH{zkeklyN^7E5*jB&@wadQO-YFS*LbID`E9~JhlK)G5 zrSp53zXL{H%b%qW}G3M+ScO>~4E8NG`y zBx@f&JJJQb@q{rs{Y|E%Xp>rk3_es>;*XxZ0-Y()g7C+gZ>Kd@*Ar+Y{xK4h%XmL= zl24zf)-w2BSfdI*LT2FC|LW=*wa+l&hEAOdK|DJwG9A5{I2DU%5keBj;iW%citus> z66i#KynKxpS*$Qh=4Y-K@h+g03x@SCb6go}^&?sHgLg94j2Yx^(IFo1isJ?J&($M1 z5`*hbY-OgoF88$nzs=R~eBg7zsP>j3-W>(^0&sx<{aAB5Uh}}%;p}uXu7SkcMZV?{ z!g;zb#)?QS+KD88CaQ(fPjG7sld{)Xu0LP8_M%!H+>*9nGjfW8* z`t5V|d)(f`#!I^$%NMw=bS$6js>4n&5bdvC11-U&U`J>)F+mDL>2PU;dnYN%L`Q*f*&Ku+X9-PtaX$*UB8+vW1bsTO zL0aSD2I+1I0;U*)^B$Wz?kR4{V&c>RZCrBTZm4}I1=vRR!2j?LVQ3rvUVWMh#G?(g0woO-U=s7~EW zN4o9wvcQs#O_lTlQlTeD%l+9&fc z#rFukt_?Tr6F?8-9k6|}zs!X$YftHDx*N%p^ng)E4r3@f&K&&Y^&kc~!bt8a z>(i+)gKbfX&u9>#xm7FU4g`UFtm_uMioqz!tSZDdbw;?*sF+)XYZfXd6ekNGAm+`2 z*$nwewrnJp56SwG@I|@7%2u>V9Shk%`7MlBNMKrVP)M`R^|>D)`C z)kg()!h&H){H6u@`}T34?r-sv^t&_+ncDdI$n#zEb0Q`AlHzs5sKNSJb*cuRu)Fo;yl#QTCGf-~ri03o4 zIn1TwzCxUSyBTj@c)NDy1VHY^#;CS{yUh`4L$m3(Uc0#}QS}SOa`Y)`hL3bi_?uzX-R~32p-BP&f zKtFT!DmB8s^}1~edk$~Kl)^CFfBS8LnNtd3h6_dvMVzK9i2s8O)-+dfaMW8hI5EEK z3ikbRdgPy)$fap30a=A~W_to2kAXUvVFsW8^7A#|Bdq~C{YX4~sk;8@A<8`&A^gX$17{O_x~lX z?P+ZFI(&z~>_paPZdSxzIE!yhE+JY^klbs4+^d4MutkOsEdi&G0n;eN5G}8XEpQc> z`G2a%T}>7J9ZeJh5amww2G(nMLkAfJM)}|YKBBO7g03xb=4Hvlv@agXC7T-(Pv zN>+S{8Ummt9ap^zz%vBhpM}m^F`_AP>AssmBKa1H+_3HHipwqXFlJt!B#-v+IRW!^ z*72)^EH0sQHB8U^O-Oa$H3Mw^iV=>+iKLbhAd?+B{?~|y3eH<1q-GZ~%y7O=5 zd(%4@IU0`NeqoIp!3OM61yPPB-oxc{?+Y8|+Z7877nU35XYgTz?aWyvqOvFE>YF0a z*=r9C?!rcHU4i?`!Uek$LjXsefqs}DKp)g0(9w7Y3p3k&Xpi5$$`MGukm{_7mwLl>k zOw}~k9C(Jif?QoF>Yt2btOq3-?q9`I{qYgH(t61CGCq9G50fbsa<^#f0%+`laS8oc z_t}-pFk-tP4b(%U%Lcsq+@Eu_*8*tA-CHn56EpPGvX)(5(P(%RsE@nw^rp+sx8H?4; z51L&nGaqI445fUi1SqoHQZ-}k0OcBqlR5j~?lD9WwN82d(N&BmT557E&`3KfibF*yvC(Xc;%Di!`JrI}72*CM# zfAFk3`EAXqRhk;Z3|}ST8Gd=#00J&@%U3uBKLFTS90UooMe z0=s@P2fmgLPxIdym)vb@uT=!F8VZ}<6=xxi(mfdi2t5~}7$i{0LUd6c{3}B<{uZZBsEPjZJFFt1TLl|&;%r{I6mdI*B*mmGI ziR;&)x5@1RaeWyk%L?2#8Fu5hF8qVQ%yv#jGr}%DW`$3pPwm-ow1vWnPCa3b@gj9$ z=yVjot4~3TT#vcQ!uHE|tloYxNd4&#vgevmkl-Ew%i6os!QAGA=cSn*_6|`t3}B@Z zxs-(=^w1uLg6ag3$+QZgLC~y_wHtcah!>h_Vqq3deTGEIcHSi61mH&kfF!O%pm)Vl zQnrX`HmZA($2WFgsE8A9X+8ENFyaXvl^7te7!IY5<*h*QWF{(PqR~t+B$3ZMNk;3) zx>V#CMmlul7(_}Jree>_h&|7WoyFKeD61UK^Fo6X+*Fq{+w5e-xO4NwDt^yn4Rqf>CCi#w3R6XZE7T;-Dv#bpAzLuCQ#FZ~%lT?TP%0_4+B#Pz$; z$8_HpiArB!NJOhj2N= z@|MBrLc_CMQ*2?LmYwT(dcviV{rwjiq0b?qJub38Cp0p$KR5JVWWRmkTao?w3wK8L z_Y1!Q?~$ui9uzP*IZs_vZ&qFYJzJs8c7Y9a7y9P~wgN0R540wBN8*D*+i-ho=yzQk zLL0g^Bz}brrareT+g1LfOGEt{Zo70)cu@JT2t~+l{%EqQzzhxO?2(PQ>Q7hj(o2oo zLjCF^fn1!kFaEFGLtT`+BCLP?;y|vgKmxn=A<)~-*4Vu59*zKLq*n~|nSr7jb89%@ zjqG>MsS29jV(+t*&;=r`d4BuG#F>zh7(AS};fsL~)O#J(8|p8}v4!&^`-|reHCF}g zt-etkP(K!}SDz8~s`>S(xPyM zH*z3<4n(_wV#m`t22N=stFc?z;dlIDhaB>U|P-eN@q zmgRV(1tMq8xzxia8G}xEjSRY59rw33duuy(oX{HCJq+rXQExERFVZ>)Pp?@SbS!@{ z7R)&rU>V3L!cWP!e(nHdp36SZU6zwB0dCGqY<-qt?K?n+&NAF?i_PJ?h8xrW&Rfr{ zY{@&FN1x_|g66z|T~$RmCvsN}3*(CF2?c8E9&lpxxq4{MYevpo4?8v`e-nSgPRDY{ z&Od(v^CKu5my#q;WjDj@13sqFWA&?WqT974yqmd2o?+=gI4O4j`~^Mh9hS8kS*@J! zq;irDb-h|j^#W+Nr+Nn>v(an4TI`_@s^E!U)x8TF6s>mfFw7XZNO~X*!(~zZ8`Oq} zInKKsW;?9I5Yg|5;b0bahS3q~J?bsvQ*C|+FYVPHK!-y$tJzSM_!#W=faE!WK3E7H zgVp3TtJ+e`bfxNYsWKvZ73QS|gl?XAD_fL213!);-fWPfBTx?yzh$h7d_ecN-hx%! zUbqlvV8Zode`A;@mN=FltAp{uFPaoo*4(L1M)HO`YHiQs*%cmYRLm=yI|#qUbMrl;KS|g<|I9Im|FaJG#r(Y= zg?k!5@wJQy9kudoV9D!vdW{brcq&)GFkDR?)hpm#Q^W_g8s(2eR&yrORNwXofsg63{iH|P+2&nu=Bexv#k zLQo>QYoi*9ckp<=it2IPc1-d3%uBGc&|wZ-dTlUFqo^k4E658cJT2=wcGRD+p+54? zuzIh3y<_<*cUACK>a+M9ny_j?She{cTZ5YY8xR(d4~H)s)&F8{^O@DCZ;F%O0m{2O z0A=V0^>Dk1py!7HOR5Rs_0JRu4sXLd&z+e5ef=)2*~$0Vlv0L&4%o}KRZIcoL9<#Q zhcW`@%OvDL^*}&w|H3^tIClg@IJMd1X`^}`t5sJUqUU@dlfrly1&3s4zp$0r?b&UPwZZI-_Dvu@`sEfR}dMzYbqTUdR1yc-G;$&fnM(cA(Yafqwkt z`$zAgviyhU@TSWL*wk7;;%k|TT_SwZ(7eFhLfV+GPS6pT9u|>{2!G=qU&{%-ky%zb z+re<70->?2h-VOit^$E=Q7jYhAh0c}1L5?ngxAv%Y)%4!ZBZvmtsO#OoLjf3fe1;G z+$40^7S*XsM2Bxt%kb`RdDGj{=Iz*3f5Nu<$h!p{J3vgDlnUzYFL&(lwye$k)DD^J z{U>aTT?EI~KuEoBB$6Rb847sERqwOETycLR5WR3@(2S6W^qD5Vdn5pm>o&sT61eoK zDn5tf69=?-8>9iA$oH=OY%AR4Y*D3z9ergt=hkuPY*8m81oH@5HVLG><^NIlF5ppB zSKoM&Nnp6h1O!c$YSh@q#@5tO4G!uYG6QGG1R|iuD^ZjNwHhIVQBe~oflLkqw9;x@ zt9WU()wj}Wk&D)ZOA(f83ah?K%7F%@lxP^iBPyxHAqAS zKRJj`>Q-|@H?3ywj8y|M7`B=>Aacg4TzRj=y8`-Gc(bdWAUSd*$9Z@+rd0C7o8Tm< z%Pu|NKhPe9$Z&}L#t1R1q5VbCqEFb*Z+X z4n0h{IhjXSFsWa$w)4xa%KvCWwmJx@wj7c}e8i(qz#hN>ML2en= zKK+Bn*xX<=y$DviN}~jboY)sp#Hs4(3ebh8}u*hC~<*7-+-0A=!DQl~{p3tQY4lX zhm6wl5N$bm!oLQpT*%S+?5bQqUSaN-%>9&T#0|NF`@p4g)z>j%pBSMA5UdHuc2x#^ zk!$`MH6dS)Int`KW(i={mGLF%+?)ocVUc%YP13ksYu=~i92!qST-Ye)KPfkM>CtJ$ zI*%V?xW-1jxttZ@=&`Tq78r0Q23dZ}U~3;Q!yoC{60ZiHL)O>W4aEs-LD^C=L(`!z zg|X}<<+SN6bSW$*g(A6cLH6w@yTBE)U6yRDb>}6(j;c}=wY(gMyLe&4? z99|U#9a^6O}@5^v}*jQ7vz4LhB8N`JCheCjHG8c4aBD$9`9%-$Jk9hG< zFDZw`K7Zq|dE+CAab)w(M$CxV zC8nLG#W1y8_E`!+B&_KO1ZGa!)A1tL(p(yxL26JYWK%$bNJcz5FC9tBhL@NBp#;cR z`~oRhov05^#$d?BWZ`3tcOtn=tmoe2S)uGhzrPbq(hVwzgpXo4H_qOVuu^qa1GaetoDj-Kb zH`>e^s1jflcM{YH1O}TeCB?I86Im>Zh0|v1dcodsy&6%rjkD|ubmvXr;ZNNyj8@0u z*gTtrU`)Yn#Z!t&l|&4%3WP!Y^r8g_OI*TY1>qd?(}Nj2lAqEMsnpPox1qnY%@g2{ z3Ax0MQRXwzFP)kA!964=2~-)7!KRtx3ZJi@os~OQl~xxr5k`$`yB;%*h`@;yyrI!D@$T zEd|HXZNid)>?1{_IZ!1ZMbFGreAi8L2MR=gE_>QSHnSW1*04{5e2f2&d5U%!e5Sahs>9_-u3Ad6{W+ zP2T4A-ZXgbQ((ONSyy#M>SR_cI@WBO>Y`0m1FV=e`Fw@WKJ10m`~vK;eimeF_xm0$w+_6X8Q? z9yU}mXTJmclAiHgZa(n)1e}Sv5eD|PK)y(JyUpCW=%vOM@R6F+lV^0JczS zu1*{a(XB!Gny|i)an{Gv@Nh#g z!jup9@IyFWS7&gTY-UxG!9rRA0YO2R55@^yN3OXN4G9@n_z(_7FRL*B42Baj;1UdA z@IYQ6u+7sXe}K+~R#yuVuOpp092$lcQ@fvzF|^st0MqHZ3An4UKP3KWuUk*tYA!)F zj!mkLz-C+Pu1wlV8UY`$jiW>O(yE$B$BoKc%u}UaoO;A&rK)o(>)foTXPeJsyIAA` zT^X9m;V(7z50Q%?_nIT1Xk3g@3VBrTv18m#i7FHd{s_AcCBLjtnr=c07O{r8jZq4PXMbMz;)4c~~4bfc? zj@xG+t9!NL*+a~FSY$(Ba(7-TUmOsz8=EG)qr+R03z-n}eH0gUcyG_eNr&t-4OY#* zw3=HvI3Q)|K?8~=$d8ow3tBNVYxUUFKR+#4@D=LQJ>w7X1kZ#ZWQBq?U8l-duYg&e z3RPE{Ua-xKgAJ0}UjyArE}XUT9=HX0N?=6NjvI_(gEP&m8ZjxP->!GfXQgP87z*YR zG`}%Dm(Tzbt8M_4jrw8Srg_GG4g1?%2;T*zxqtz7Vq>=&2fNMz=>-rg zUNLI~l<;IsQuVlR4UO==IOiy%Jy`If{UM4-2Y`SLnl>EkBXGv&<^%Nr4=Yty^bUYI zoABgfWsxr{i+m+eJN0n3UdZ0Raw>;-lnRbtZgxj+#!ZzsXJY|p-&Fb7Kv{2?uU-kV zqh%#w7qI$z(NFWMSOndR8D^7x3q{zhaSk%=(WxC#d@m0HN2 zjZyM6yX2&jpnE%t&4CFZ9dfiRrw^Kcy4)fd^uMtcW{jqO8Gzm2_y(xv&L1d}+1fm^ zpSb`zZHfV@XiXzsAR1B(PPtFZ)nj3-W85a?LTLe4v*K3 zA)aSp-_e>eLyNkC)}=Wg;1(60RtaDhy|4nx+zQ+=Zv;j~$B)8oleiJ6IWVSa{1{A8 zxE06<_@m?fMgTflKo^RR4;g`S+(FtLnA$Xcsu_h6)8BkWmA`3PRdZlw)A*T+o)eOG z5B>DBeX5^^0!T+c$ru#9RlYvGxBHa}d%j-@8`hi6&#*DT=gL_mBz3*g>pfnt)OD!{ zgb7{_68VmG{%xMBGjP~CZ#lHi3dgGTV4&G-hTh<#X6+cQDIH!auw%^oXEhk6@aoxg zF1|2*xbe(}Y*uJqf)detwPrb90&rOXx1hJofQiy#P(ubRO`pvB72Z70jvLNPCzs%T zM*H~8>2L-HP87>}8zPm6Ds~ zMvr!5cJ9T^^P7;+B%-En%qJqO1EcsxR(4TC9tkPijciL*HhCEHMdXS`l;ow(uyTLS zq1P@q=bQsvBzR$JJJq_P*35thsAQg$@9p*5|MJX;Dn{}5?m8agLpKKMQE?!mI1}4H zp85rVjul;$78=+up}xTw&zU&4d3=5o_gi_mj;cOrU!#hbIZE&$4U)qo9cd0}kfmdH zC+d_K5f{Um9%{}5;ITN(Ld7{wv+x1@iRL{TQ409^qCbyXYV`TEt3?;^(L6sNzfBST z8uL%bZ#2SRT`a_$n;_lh2i*UcIO}55Rw_OzL!JW-Sa0~I4(qr+({s%)w)ORWv3X_+ z4BhM3&0&3}<38`DcKMEZ0e**NYWT>ydK$bs7uQa|!|SJc!5iLFZgqnV{XQM{x+QLO zbHm^nm&X>Gr5E94`W0i0abv_h9#J1m1%s8Fv%jYs7h-Tv1ThT%0!DwJUYc=zIrqQn z>g)u&?sZSH9Am^Rg2vo=#_4?7!tF4j8OL;UWiFOexy=*vo2Z@^T@UILT@RKoq8rjvvbGDcvp}8MtF_7aoc?0CY}O#U>UO87i!O=!K8Je z_BqqYDKr^Vps9T8HeL_-u^a{OY@zd^4)b#u zxMCr}J!?88)H(RDDu9+8i~)NL?#0?N?EO5N$1~t!Fu&D&ww0p9lX;Iz6js;^)J=Gs zCV)%RcnJWf;W?F0PG}YOOQoAJv8=ycrREtuP{jG35aF+y*VimXp`*tQ_AG43f_+5M z{!sKT@GRa*xYaxp71JR*%3dRPMva`g1^a8Zz%XWc04 zQy8j~W@9o=UB+9e8hj^;e-_0PnSVysLm?f|3#1nkQgmt+Fas1#rS<)0v*dp<-edR6 zc@)YG=rb6dQa6GT91K1QmRYw;;o(-*M}*K^is15&$7NkEG90ZCN~QV1F&U|dZxzbhy> zZ^AYVQO=L@*&1x6C3;gglqY94y#Xi66!7yskZqlgMw7xJ8VWPCm>1g*X+pNsl1TH>_7uMR@2 zwa1Nj9Hc_D*R!Av)<2zN1;ttZE3Lt%s`weI!0gT1wyRgG6TX7dYjG#UMCq40LjK8VA0JSkDcqLd<5vWV59SELG0w=t`P3@o7RHY~6Te*B6Q*nXQV_^gzID+RV= z`Yf~9DsyUTnSo@P@2FR+y@RF9HKby|*)vHT0{D*G6h=P0JO%Jw6B8J@Bc+nZaNcBb zSLL^;;byCbSZWP-CTlo8rG@}&u-P?C>pLG}i)=)nVwHA&YH5YZ(l)DC3n`z!pITbi zMI5xtgec%qi}ZfYYYGJ)T$X}@x5^SIxGkl~N2N$iDo|^yNhKRRzkVHOsZ`C2Ey<9yt;*I)qkB3s8|oU_cw?oG*(l*O$3!%hScjv^fCWzc=-DTyhvW(?_n!ATRx$EoQt z87Yqe&yFmqGKQ!9fL_V*iNyeR#eDuV%(>Wgl^W-ah0W zJq40Pny?K>fLs?1?pHh%I!D*7IU(1~oHEz0PB}$3JqRueZw{;>RajVz%FG&cuR~aX z#g9{12q7r@3?h{*$is!%IQ{ca2n%pn7yd4!9@h+|SS8A&iBw z?xp8>w0fTiZE=WvQpt&tOYP~NorHK>WW<#qjF&w@NLZ+Iqe8*1%3Hu2$U z_)u-}JavMqAPtTcb?=%QY&X=p;iL*O)q*@XBRTKo&}G{JY5%J%qy=D^2XO1;;-U>Y z{8n?A88?-$NJG5W^o%!gm>hyM?m)meHcx2~z9C;q#IQZEG?jy>nzzeyZ;Kzd@RoW1 z6NpWT2R!4}1kPFGY50Ti-tpMZatF@Y7#`p+zIbZ*u0Zn|t?uC9FkB)A;h^pFPmM`_ z&ASoj%r~K*KmTZDEC&@ph_sU{F)k3uWncsYHad%gV5Xh_9mzE{u5sqi zH9W&wwOS--)LR{joqF9xS%Bu6yb5g5nt!HZ*fIC4Ih^WvY zmu@hd+se+P;LM(FWzSLBkF&A^5-By`dQxS@YsE=6{^W2GvpstuX%>kaGDt*8WgNM<#Yo5~JgD zs>#t>;Vs88U+0GJVU!zhadApkAHaD|FnhwvbrV)J=MMV_rn+WDdEVf; zP4v5ldlJ(Oa7oR($1{(+XbLdaf>FrY$n*Jp+>rh&Clh>{VCU7a1oOJ zq>4ff{NDTn_TX@E0C(nM?qLf9#(UDp-PT;jffF=pveMuw*ZWxw+?)0T{}W5sAu#>3 z?HNMRvqI53!Kx<0y8E>7AhYs>%rrReSWlNt6R}TsT1}?$R#$&hSMhIo8gE3&a<<=G zL1j0%DOz^1;&&TGB84Len-O5bk6w?Z==H_X>&v6p!{OHT(6E=-YKV{ba)8o<;Pq82 zP;Ociw9C=!YxU?8bqICA&fqOQdZmxH1BB0GwtOG(nKG>pgT4pbWsL7{KCa^5z*N`x zFO-C9;eDbHO0ngbi2c(<+flHtDxF6@64OsK-@hc$B9CfOQw2G=%Z3EEw!f8HBKCrv zC1Og4mJQv+p{GZ6*jiQQ0F|(KXwzdOuH6xjn{$s>IQBG-0@Wy!iGXWYFtXmwuJn7~ z^W0qw@hVWXr!!aR%VIK*jI}s`FqvQ5Az?C$-f%LR(Qi7L%zq!Wz7r<%hvQP1%(&Z~ zOlFClRWX^0xm*2a~ z6C2G@W{jmavp+^6Z@^<|!AQHCO2*lkqgcDJ-hclE1V)wPsl``jcp4}LCDP8i4%`@3 zt1;bqKEJcHpuH_@UWF3mTP|#N!aIx7f(~>7cF7B(^AtUmK>GMh1r3~^r*}RL+hB@D zb%d8kLnCX;DL&U?k2*f$s$1CQz_zs-p@$acgt{|cUTm1cC$Z>gq#!6dPvq5sA|2=8 zkF!@Nx&ilcld{c=H*sofkitSn4i|^;QV;FmzM?(oiN+fVHqxtzVl_ObRu8S5*D;{%F4$V7COrqL|Aloy(SGkM7QjBPl6l(nlrSAQ> z24=D_O5g;`)?%m5yOF0csLn)0bapvGQkgem^{WzOStpS&t1(}LD2PuBjSH|63}&1E zc$_qkg~gL2GSdRF%k$#c-cy@%m*tY=asi$BxmQUHDxpC{U`h;df+KR*QRkjJGUt0%LtVeqcM_^M#dkbr9+Co~{z zwB+I7x&b2}ptNvA-}@Mbk-zxb^qPM7O$%SAPcQ3D10Wy#6=rshfl`Yh>)0-s%MlDY zj9J+T(EvR4S1f_O>pYDQf+kptr{Q(vlUlQy1J;Itls*z?GPQHo)m%g!Gh@UI{C;aa zzJoF@m-jRhzj-?VX)eVM0-R4li(n!*1FRvX z{AaB4UvZW{RLY-tNcmTw{4S7=`pEtwIgX%UEZSZ?MfWt&#DI=XYPma5OIS{A=WZZ1 z@nDK-3~w^iekTN0XoKW$9yhc3qAK%)e{(Crfd+WQf6=_{rPSvA4(x`_<$QuMW&Y;3 zWaI{}xc z5M{CChcm!hti^DrEQXt9po_4vu#R7PI}#(VMUOt9;<{&@Sj&92{ZRBuUBZiHtPB$N?4~kv$9R;OV;vGzZnh>6s7l zosFDejB3@5iC_tvxac1mpA4b+%5qUfH7z8IF&=Q^m-hpE8a?2RexsF+)X@dIP?ovv zY~!e${oD+@IA3AY^CmQeH&L61>d z%UG+0q3Co_%tGlHtNW?=qDso&Tr#tHN|pH%`^-3zA3+rbp}D*e26s@j5UN*C%9;i3WXjRrdzL;gSe3 z7&Jhy;e*4ueSWYZ3MRH_<5^J1^U+u*jO45b?8wCb6bh47@h;%R`~{$tkdVzZJCufU z%uCQ?l2*=;Ynv4k8S!uTSl?}9DL)@#35Aa%1bV;83QE>1?W{XdOnk&AH#%g*y+5;x zX<`+-B!nKQLnvi(cdi#3kzSRC+Rt;B&NIaH|cp(EoK^pqYtYtp65q7c& z1Q2p+02VluJ;OQVQyXz}DxHWMrK+_i+8@{2!ODEP2 z3`)V;LpM0F_TgeHtHN3X5))Xv%dSw!?Pduju;u{XiM2<3DOg)_W&&$JwZ9jveFCL$ zg52I`z{c7D=ELG_xhV62HManqz?!4GY+4(5r4wr%s!j`QtL%_q?HP#E$w7LhjYGlO zU>gP`%+%< zy2%V@dGx=|Zcq zf|tJxNWsgL>zsIb$j+*$=`18B@bXi;Lcz-^60-2ZiL*zQ$A#ym;ALE40x$oOR~s*X zgvvNUP4_Wi<7GATDdu%8^?5dGUcJnTnqRAeE!51nLxP(64NlY)*oYF;ycTn!W|2Du zHN`WXsF`PHRj4^0i3!vkV^=7saZAWT4P~^Bo{>|0ZVGZvJ0pRdHhHy?(*zB30y)%Y zCy?_1^L3Vqyl;`r3zs_aa)&C@!plu|NboYV!-S{Zg`lSS*QZMn#LDmN?*%KDLusDC$|MGCtjuD*&XX{k+Ixpw5mhj= zEG>>(;Y5+&hB24Z?^$+8P*jLTND@V_+aW(A`03Pl@{ zm>`_>s(mnZWA)EUD1oA0crX{&lvXYE>gxpw6y0KfFDM#a;y}^q4A>}|#C+Jr5wZcZ zG3P)!HRwFO7F_ud^ALw!MNDMPGeBiM+g-$D1N(<4u9tKX)Lt0eTY6cA`PUz$B&OPB z#JH1co)Jc4lBOw6`09wq+qo+5IbC|>!BxGci%k4sAMmsMV|z~*GWxJG#k9i--!4ws zTYFCz_*@{-KH$fSHOIW{;)J^vXZidd=|1ym>>OHUIMW3`AI?ss0lf~J`dEzV_}*<6 zBw=)XkNz2!OyI}(=SYvqp4^AidTqbZ&rQA8&vewEZGQ4_0!C-~R(g)PqtEnQ^XWd* zi_NZCeU-19oBB*IH=pP|-TY*IFa2l0S^C4!WIH;y8~#-PFYo31Sj_5Q^#6s2>3^sN zdhP$46KUQ1FWLWBCi>8UfA)Vefgj_aBVFJ#Oa5_q_l%&=z0v2Q8xv{0w;#Uz4*eeE>+i{g9@;PP|0m(9 zUvhMBpY)&iF!0}lExtqR7yfT<{Y4_JSNI1d!F1FQ_&>fR(Xig&KjjGEzrDO~{O`Cw z0i&aS1;6JA;4e*r>G&S-pP5YP0e)Goo>AT>{g@@o`b?Mp`TeB6+xN-udvD(vHzd+} z?Vsx_65Wv6zRAgiL)(Y(v;F#%!jZ@MeXaTTzS1rJkLxQA$3K5R@i6V@Mn!M@KNSQA zd`bD``ipyOe=_0F_AB@!*h4xT{BM-?4S!J|@ZWS@qHVqQ-xC-00{^d)3BAF;_J0l! z|M7=`e>S#udc*%yGZSgO!rygaFYs?qCL97k<)gcytaczDd7S;L_}`ODgO$I5a}hSX7u32J*y zjz3yU6VxC6j4MQ#px%XDpS#2iH6HXj&H}h;NsA@%R`aVTxCh!EK20~UEgtl4O{j2E zKFC)0jL8KT2AqKN(}%$2xRuggg9G!)kGa>iIQ3(kk0_NES-I@3IBaX`h!1){ulaFk z^qm!94}L3YlKjsonikGUEk?DRCYVH2HGgV(6HFcQ(wdO8!RJyud1Fp2lviy&_z-dZ zzlDAADtMs8MhfUVX%F~X%g z`kRnDaZh|!J0nsN0kg9Db)=@$rFtqI4xEuf#Hr(`tlZpvZC~lt=ElRM?@gWLQp=AK zr{?_BBAoD5o7bf#rhIS6_cBkGB(uRiK+>g_GPOJnT5Ignu}^$T1KH2ij-%QJ?ynLK zBvCg1CF|$;h~xOF4DuezeF<9M?&WrVPt-xWTZp7ZKc&^GB(>Z%{4~itRMqsso4f~} zGy_8%T$#vmSV=mnqM^AjggE#fdd+X__ACcm*j^SL8jAhQJo~AsKz1C2-e*w1w4IqL8D4 zhR#4O0yS|Gomme#N#+`o82UiieWes#CyrPUk@r@lB2S(8O? zZ#WE-9v;lM{wp%TpSrI0HSDi}(_`Eg4UdccR}4+htm`U^Z=+r1aJpbk#-1&)bVZIG ze*DK~wY%$kEXxtbF(xaisHH1QxDibQz=()N+zD05yZB%~K`bJ!7+t`7I_!TO!r2Mm z0kJG-fKu<2r0WTEOCd)#wxM6A18Sx&%?-%tS|o443ppYsfoa@QZvUZUl(m?)gXuKJ-T$(xrRt%+kHdLUD`{e!(|{ z2LQ9m5Z%a{cO^WxdX^vLrBGjhA9$Y4x&=`)Mr)b4@k%0s{fySgu5Zp={6i!+zKl2= zyD0JajiZ7Y1JY)AJMewU5Y6-JHNnFBYjC*SP&wi@-b~_!2X4I%WArv-vABcy^(( zBav=p2S<2;hm3jjHyt~m1Ilrgx6Fyg$GGz`KH{zN%(TY+p;*?E_-B|t8g_C7kl7e5#GHPd88rd-q|Y(r4s zY4{Ky^QH>Ts9Su+5GaOmnPM~pp(O}K$7bT~K6!KF?S6TK&k%SAkqf?O%$TuQ{cjqm z19h#PxYDH~{PB#qKko;e|8j9B1$XDakM>~5;Y_!?<}QHchULj202QazO#kk%q(abD zkCNb<`u<7qve(GdU8r>gCeQIO1hNqU7;H9eU_`ppl#g(ZSq@j-ScglHbAU)aT5%v0 zt=t)kR&9g#Wbj40E`BW(t?t0VGoK!7x&h<4Z2Cv0eh%#B8sEM1BzT~Kaw7C zLqi?#p~p9d_v+Cn<>-kV5748}n znGJ{QSuA;*z z3ON)D{06M%UJev|n zckIHoDba@EbtoeIZ@>4|;Pf@O6lg18LkswDI&Mh=lL7^6W-Ly8zAMs>>m56yW4{V_ zjWwn|GYTc|*j*291bENVj4j6c9lP}D8@<~AJ&tGifaQWUu(vc?;FoL>m_>$pPcevO zn4L<`)heG>upj<(0>+Cz&+;v0#=myFhpzQ3>>v=HgU-$JDK6@B`e+1D9c^w^mBnz6QE<0nsaT?TM>B8A&e7v79Jj`#%t-$O;H_(0-*yk`h$48t zdv=?}W8pACaaJ|svOYhs!pY69Qj29~uXLj_Rm#Cmj++qjspc$RmxzT+PBQWPP8vWW~DUxMdD zLKdzsyq*v#|5tUl0yoBMyt8Bg5Rh%YT}wF+4!Ynf1@jf|;iah@POIbtE@Um06Y9z6 zDv5@4m-J4q+H3PG-gpmYrAMpw>CtLakJj$88JESdw0{x>e&hO3C|cWMdR|vIhlTsDXo-$U+#Nhi6PAy2wJKm>GAZ;s2+@Czrc`F_` z3);9RWW48T_#r}yg&R|F3EP%}Qhmqn*4=L0#b$iey7#EaH~qs$dse+Yctsk9;q2ir znen#9*6;zu-_d`C3w3t6V&lGsi$b=17H)@1X~kVrz6I9e>&G@da|&S(#@A`y55srB z^CHkkur|U4{EodqU+dmuBJaD{P2KA{`rFh6cHP)gJ2Ku9j4c?c(4|kuHFfP~RZ(V| z>%)-izdZSM_xsQ!fH4rc-o5(t^|MF#LHV%-IqDmK!Fuy-e4xi7#BwMWVLavox$y;a zT)warnLr@*fEnR@Ev09O&^Pex?KKpzt=YO#M_d($STzQEtwXk!@rCsb9cOd}OsLdIkwsRglOgLy8*NiHpd z#s~1I>sh!#NZS(MFrTK-H~b~731Wh5HQmN@(sz;g(Q|*ivJP!j<3Vfe02iCy&SaT^ z)H^f0)QQY@{2LLT<%$y0OM9XJ@^69TNIb){h*MExJnW8*Pw(7__eeZFd~M1&$h5}6 z@W}ff_Ul*$Z?}<`^=)d7jdOKXrc^S(s-zG4d1TVJXj^RD@OVdUA?CT*f&$gIkWY*Q z=D{;?nAP=P-SuJDFkjt=8NA#nzTVgLT*1Y;RN3tDEEtP1$l-#C2fZDfzD_@vRKdaH zyCX-D2#2I9HdN4JK8iv)Btb72l8j%)FSM0|fl+9SWMHCGm1OrZj<0h}7IGwF{MGOp zUaovo7P$8Oj$ByKa0Z-$L3d4FuJ#ak z$=JFA_)gog%e9`JR*Vbd+u2{amMlY= z1t_+biZa2gwa9(=iW=NPYH)j-=ea83)UKTKbJm9p*Mcw0FmeX2xHzqUUFSqt-K~EAh(~dzJ*bhmE>?h3!_H*HnajppxnxF8$f{(Jz zkMR){7<_%?6kbz^Q~r<=SeCq8gqQXbJr_XrJilKX>H(dlAqaky{~-J+*}~$76xeV@ zp)b8?zu(v!guy5-_RaM~3NZsyuL68Wf2YE6#M_0B}JouDq_) zU6`NI5V+RrM}=yxEOq4&bB=L`Mq99V&-+ zqbD*D1sY$YV{&khZNa|y7R}o->)5*6eK>yg0KPOz^WbVaat~fz9~mVhwSpG!`Z=0W zl2_1%7w}G8ak~YMSSG}Vr(vCt4>y|7Y;by1e-QnfG6+n(75pK*Ppa-3hn*9J67TFh z_$t9A1yi!oEYI@HW12FDs#f8W%tX6RLc93=Hu>Hjm{{wZ` zx#eSYbA_P01-HUzFBwv3Zg|E9{-<(Xl z8)@*nSZ3^{q)a}}?+cKJ{&=>UZ+$<>#Yc!`G}!s#K!S@e?#2)Ph&)Hk??mqn{#~3N zjEu=k^UQl5eA_YhI0lRM_+!^v+9RqQF<&c&w-M4kMfqbR@2BIAtX}vpVLoDbFhLv@ zaD5tZy&GR2`MUqCd*Dg_#%>j=HL6k{Y~HaqmiZ&A=APxvUjtP~T)*G7Kfo0Qk5u+B z*JOK^e~hWdZB^Lgdi>OHQ=bN12PREl)2MVUJ|2o*{CUR@4kHrxpkgl?3L~x^ayz$S zENPLpjDOOM7DvFe*e?bs{+C1#{Qrlv9Uo||9~`Si-tD7L>R~l~#5jTCt!A*_wHG^4 z=#!&nEkd7Mm#kEXpw~V*R6fl5Hg%&~UQg#0nc{tPie`KhAd-xITES=WEdj4N>o~3M zu0m_F0Ro5nad18IOXPL!LvS$W8!SfVc$rkt#@9k%JTc>x#4-ytsJPT@L;CJM-*o9k zZ`gAYpPKiU;U51dLE~8Vo;Baty$3(>iT-LoQP~mHd;8B;?K4 zaHRxxY3^Qxt&1)%dtbxX_6%TYq3ARAcFsOb5cO1IYPelFROvdKgBjnr{*XB& zgxSmStvVdanTsIS0RjrewOnab%bJ6;2j&c}Wf51-*+a+dLB5&b>CXA1&|LVzcbz}3 z`F~~p=%!D7&CGx&jP?@QHE1s>#B>vX0mk$;AJq*P)OBuLI0EUZzh;fR0k(G4Wt5Jh zWG$LiJ-#(sIx764wHoQBSyeN#0R?kIeGY2it*S4+=#SRtCjy)=6Yv?|v-Gc}g&-Ly zITOOl?U+jw6eN1j=oL)}yN>#xV9Bk{~fc4_Gfb6~ISae`P92m8gI5 zA=OBXVYD2QATfrKhNh=NEHOO+rqBo6a&KuT3#4yDJdbSl->JM>YuJN?+X*b#BhSf! zZ|t{@Mo5p%%vnXpV)WU0-&LIAYc5d{xUAQlr((v3V$X8)NCFK;ZdCDSAcD)jMTE<| zL`5E_NAAi_3x9tlFB34If%_~d<9V&0yrj9lsiDm=?K1na*-o^+K-^Nf2@zQx{=NJ7S0T;pTp-*0U=z<_15x%6BzqRr-l zNqEZjY8G3OwX&#P5jj@_IqC9`VUIHtAF?;x!B*FJw6emP|P(Z^cw z+BSb3et`+)(|#2YD&E>wX5*>!-_dM6TJFYQwjOEm;kgrkyYL5{dpUIP^jfrEkY2E<=4!w3pTt^( z{6B=n5zKuuu;6xGj2pZD#TyQ9z#;x1@0QKsb*-Mp&q0N?gVW;yTsq?R1_txa-Oc7v z=nQ|e%H;>R*tbFPehlc-!IE0@z8PcIoUhe=;|ULx@V+s1HD|=z2p!a6QmYnUs{o0E zEfKbOi=WGQ?9zSI*$CixNjk3n21I#yuJtTGok18J3^H5a!$iIYpk@#1l=l|(9`pY! z4Q5dOSje4)9)v~qydd*GD0^e~HAO{KmJu4pdE zp}*+x@G|4;kc;yh_Q;WfLc2!Mj%Cof>Ati&PvZhP1g+|#*B1Qmfc=)zuoQzZg6%lo z0iB$8@2ubUf;W!N@x{=H)Z}2=1n7`nFnxM{)q)8$xAQ5$OM90QTHRN;sQk!G3JkPm$>JQ>4J`0nQlm^{+Zq0R`<-#eb54o3_^w!d=9$um9GFn^BoF*yS?%}xP!2FE%L9O(Uy2rospE-z76e{bdmyInub{n{o-f$7Z5;rV_ zyd@C&DInYi$BU7@?pt3AG=ECE5Pp$5H2o0DMs=PByc;hWf~Hn;kmsp}McIh5@TLMQ z)7;Rtb+mS7xjLc??q$0Az{2&@0PF~GeWQ(Q#ecsmz8{2h#P5dhZoElxAVI@n*--OG z#Gn_#t7qOjkTBD0^87k}bQ)jg03j3}a!Btje*(91UiOFExDmFccwRbLC_dLI!D=10#b~l{MbaDR1JCkN`5NYd zAm#zJ*bIR6;*V1SVsVcmygT~pzELB+t?Ng4?}YIlM|s!xIR$tcKS6ES0BFm@G~j7` z51|#b$ThY(iBO2lCPE1ujtIFOeFQ{!l|_W#eY~FtcaMG1-=X8&-@L!eg&%DE1miDy z8vh1Ku`83lZbr=!B}-x%6!#3kE^tr{GB=6 zk5TcrYq3sKHn_EeaHp8$DmW#Ct+a!t#mZaf4f76~hU|0;X&QRbZZv&Mk2L-4QsSov zntoPjdTnQyq#tG^{hzM23qr&H?JBcmTZl3@Cs68eJLLIHUW+`R%Ugjbje?fR^U1u& z<%z3Xl-r6|57l zg;ZVKXT|3;u-FRZn)gG4EE-^6QUhF-&=xrKxjv=;EvG)$2mLSHGblx^uV?+Qjs^bqE>1%;lnzN3AR1=OU@9os7g_40DGD zw`zIJgT>CzQnH*OSs-IF3zy{-vxlW*8H_AUh0PX9gZCfQY<_r#v!YHUF-xJ9#e^Jf z`e8j#|E(g$uSS02+U1x0`-A<&t~nr)O${%uB{TWMCeI3hB00$coy-h zsGmxXEhhbSu!xf+#RB!Ky*O53e{;)Z*r&(t&)bDY=qv5T+KOxhZJ9h69Tbd~;?lg* zY}|k7!*eJ8cHz&&-#+|dz19&%N@rExBXxzP)s&MX+)#AhU`9EmDr$3Zvx z%T6I(FyiVYYnzas0#OKeht#@&9_Mm{*p|$hg|-q?{m>kWu2&)}Q?Y534@t)Gw5++Y9nIz05(@u9z~ zX!0_64G}Hz5T&aMq5>um9@d=LLPhBkp|w6L5V$+L0C+(M;|aLGpe2BB`3=4mK-d{bymYY&E-1SETVe8UXt8 zEQeKfiyrOY`o4S6N7R33&fS{m^j}TdA1~M!*$Y$c?nFa<{$Yt`AxvS6S6X*N6ifXc z%Yq~Le3JD!*3;kR^K5+Hy0-^{KO)$-qwN~ZVLK;6V-5Q597m)3SST;w{IUx>#O738=%{zCe zO!i72{Qqx0hk2mv>-u`0)f4-(zV9-h8-WUY{(rFB#BS?r?fqgYKbCc8w`^)Z*LgrJ zVTxEm{db{UMhN-hkvOz}(RA^)JAXEmsj&yiotPW5=qA&Er3h9RFpU7YC47CU^?V}J zjGi-1D^KL`_mU%rInahp=E=?+)`>{p{E80ky0k7IK}CnUT&V*M|yp06L4U9l3ASJ=S%skz;u%`;-TsE&(v4?ZM~)|_%BUO zhsw{5O@!ouKOzqJ6>HSjisI2*v(cMxeWQ9ami2_ZY4`>b+rl4+Poz@aY_Q&H&D+$Q zA+y>qBoJLqkte#E(>ZoIgk3~eXM;twM<{h+e#Bd@yk+B!(jJA9_6UVK^+o|swD!hY ztDguVyU3zk^>E@#wRhnCU-p78rt9YJ-}jYXYi{f#y}ngi`QO2?8c=2&%KklM92Ycp z;#kx38$W|1*)s2a`!4ceS+S8P*)GK?w)+ksj1y|xaHMd1IOBGshvr|Blc59{!H?ojv|n{kAW9(aqoWnU3+%N4iB%_rlD} zrl-VNN~28ihu@ucnDKv@^h*zueon9HJ{j}JrKSu0WXExDci5tz4K(W_QZ>-UbZGJ-=9Et@Yh<8B$kSjy~1h^GT#d)v!(atn;?@itFuSrWw<;S3p5N`C2 zWj#C+_j!5U;UOVo0@#-h_QfN{)`{vcx&e#n1lP-7YcnlUQSx8Amd%m+mD zC%`58_!#tV+kwoqcAuL$brefdv?wx}{~|~JEq&$(SnV2Z#?#{E`3cHaFe!eI9I!u* zmlvj&3?@A$Pc7lhn}usloRS0EomJOoB#@L$58x&`B$hf7QAIL6`F)2YOETU1{s~87 zFW*<(&`14VM`ADEA8=$jr2aP?iM@R929+U6{vGmt7f8xZ#8di<{9BG7 z{|_AbUpa#O`#(&!_uswdS5`I24hMsd9LSiJ?~j#dgklrYu@ibh96tlJ$eIkTDHzw9 zhQbj*Mx-Sp;N3Q7S9g9R-)GJ`#~-V}*1=?cKOqC#;}clOFs(_RC^oV-Q)`->9%!0? zQU?ZcKsVBo8Sw6$vn91e(w{9K^*1M?O{okZuDKzNCD@UJUia@!w*RbN+n=QGHb?%S z^qzkTzR2a_6JvmR#tg9EcIef><}9v5E&$;O^3PmsrM(_9UeaQ>`U1!wy8vc)i;W3i z`His$a3VQ7;Mxfj0l(`t80|&Jns_g0H74wfj{SgfD)eb|>^?h0bE&bPhM%XoRBkZ# zE9xI%zn8(&&6+XzZVfjp#9Ohsqz!0jW};{L*srvB=G}hprdh92XwO;uV#s(C4bX$p z%b=sY9!&rC+_LEO17*?c_mxFw1BjNg=_IF^wh>re@C%T|M3K*x)4^M=h>p0E$K=xCOwZ<*JJO^ODwD0|<>x`tijDX!E-;9+#l!20HeiC z`i=KJfld@;Y+_OI^?K13M`7IcOBObmg?Ug|(FO1c*S)MeWr-eYFMb^d93*n2=0~0l4FX=A@|C(`s(^5Bl zHzYd2i8twlSI6KUh*(z5^Pk1p2`|9T1ec&}0F+%5aPy0?$!OEi73>ASv0pFhFxJM` zJIX~LN&lvHi4;8-MO(#jC#pKKhBXaDO;Cc=dK$C)=o_7VQ!Yo*?7peQ7_3Ti0>#rJ zU!+HWSze7}AOqX$`J|jx)Dl_A_sls~BnC^G;yac+5m}jy^fZj~aAFmx7yL2}krcwI z1u8b{&H;TI26Iw=E`$_96+Nu`k;*%7QzQ`uPka4=n@}+RDbB0*b7X5iT8*9gkLi!4 z7A9=fCLCtr>`gh^v>maS+Dl^#&RmA>!1ln) znz7TY{M?0g`_#N#zw678Yexu(&jo$OUlc%Yt@AAJ2bmKG+dJSAfyHUj=jIVRxWK+a z%8$3hY@t3bNO)-(5vYYo_~+p~mb?>(tmr4JQ?Fgw&;uE%3nDIXFN8hXWF~7$BlZwmcjcf+v$oJk9eybMc6P9)qW{Km=#w!>r>mzUXhfSEP^=+39HzWi6|MF?^HbS?q{%ex4X8~>%isQHk zFY3w*571)c(mNkULMn(Zaszr*-?29kyEB87Ga~YSfAlQSO|mdo<-z&sQh|GtRyARjy~l#0fZgJ}H+D zlfx}$7zf|*SgT{;E?+Q;ZM^eX&+>**4>B>n1@TP{qaH%wu1*LKTGNnVyv+~$&HSbX zdCcv(`x{tAluiH8kD7KO0w=eOqjByg*=1QcffIG%49?LwgL7g}48S$?h`Xz3PZ>OR zEF`DK$WZ?^OsMh5fulY1O~8^$tVb}O8nchMc8sSRzjgVi9(nn(%(FxT0lPZa+nsqB zjsB)bPxqm*1E{f^Zbfc&cmIOY?asi`LGdB3K zf6ZPGVfj*swL}@jRHK!#()GsMWyVJ{7Q^6ov^SKO?}=2P3LRz}EAUOcb;X$&T9`=A zu}UeM87E93S6oiks%)4y;4?^_ryGPhE|&-DGC6rbWcA*XH+!hj2FVsSwacF@rWDY1 z9HR4svGMuO4@CL11}%mWd}e&Tw{Z6U^a9LEvnF?Api71vafkac*|2Fu17tUBry{`h z+w_-L4kx21sJZv60vx*2Ts8Mjt^bDoF{_jg^}t8p^)y6~hwh4qSIFd>{ONfMC&U_- zbLo|!UuwTvGBLa`wkVE=_u3fkf<(ULS&Ax=FEVET4}DX`TR30u!ig6F%qGM!o7b_N~>9xPl!~}ByjABybZI~c;-U8^N0;tRtwTC ztPs;!3l(kXoCFe$>FM5=JdJmEWPiyr0&Os zyX^hAQhDR1(XBbrTXXR!#GlWW0Y4bHKOS8@Se6K@^ZAGwg?N~cJdCj%U0tP8a`1?* z&c$zgqyoRdP+D8W%{Sd%d194%=!Tn9AMKOsu-Uv6u!oxyvT#10}hQY&TYeFAf^TUuaEabgMIpPc%lacKn+Yx6nTX-mx_*nNi zHp@^EO58>dkaqDq8AvJK+HHgW+g;YZ09)}aT3p#LI~!eY$=OPNUP*ibeLSFN9P~7_ z5nc7y2)f9=fPKL=J#C105Yzu4rNnprqaP!Bp^ca`IS5q3$Ttk@a1^F+UBTjSMGg;X*u4z9f7a9;f^ud@>$+Q^LpN zaq1=E;l_M^_)<$zTU6e}CoGT(T1oL>MXG@{FORczb3kD4k3e4;t;Uxm%b_x0;r%Vb z82*!0sv2*y-zDiAB1En`AtNW|J6M!p8!5`sXIB26 zgpP21D_LHA#H(LY=(x3qd89 zL?Qm@I8PjaYXd(nSceX09LqOv!*)Dm6(7_L6B!Y$uf~TIJKE}!@V(3Goo)Tk7pd?~ zvEf^Vm&Kj`0#eb*|ScDTd80F)@t=NQtF)|5Af9D-EWRZ>tfAK z?DJSNMyiC=@So(Te{WE^-?+P08h5lg`$LEt2M~FfhIbJHXTx_N zOl|n3Hi)fwS={-Ch>cU+D|0~r1p*j!hpv$Ea4lghcso?^a24NN4|8CXLsWLn?dk^# z_jS4X3;Wm#u5p&40OBsXq9@c%=?>TAZg8!+&qCd4$O#(m)BqWtcG1)?a@1F*v~Dm; z@q-P(9p1}u)oc#N759sAICAFpVn8dxsL`Y*fzZH0^9ocF0(KxD{%r;@>ubV^9J&L( z+E-kY7pVC@geYkJ24RR$Y~;}&9VMH8?1UNG@x3G#qfR&pm>M1)#7)X5-5f_FMCq)q z0WSa!D)W3@KXSGP5x!=EZ_zNG0CxRYqZjtTt85CK4dWLl44Ve^0w-9~0D$Oey~bLxLy%&yjYg_AS~G~d2`H(C+&~d-9Erx#ds-I`f9$9HN+uu z+I(A1>vj-LZy#R>(9kw6gk;gc`b?a1vNs+Omirj2lR{!zYMBmJa&E1X`xvlvu$N5q z27Q$I@~^F^%Y32r*H6w7*Jbo)yLl8H>^jd0u5`{a3YKSpe2Y&mNMCW1& zwGHonW38;BcW770(D+o3%9;&oO`FfG25e=<2ay+8Km0vCjNc%x1D*MX zRO@<>(8J4&yY2--l~!{(Wld@5AoEN#)Yu26Rl?cJiu}GE`4dFKLK(;!WV-Fh?;$c9 ze)+MxTmuGg-n3X10eAlU!y|Bd7Y>g6URi;9?w(CbIU0^22pmy$5>BoeZCwKnfAIsl zTDLYjo$LQ%^Cw$?#KhX4Gem+up_JJTd_H2+r{#7G&Ou~!fKe<421pIHp=doh5AN&% z!J4ZuHIfvDz?P4Dh_WoeZopDx*TGZBU&uhKAy@cEKh4%)t9j0CLE{_n2i z$ohAr4j;G(n`6HCOzo2$tDIMIsba5$zQ7tlAM0ssMsauzUg~MQOTA90@-)`q)w4X5R~pl{j&XbDUxTpL7Qkty<(>ZM z)0|ZzdkSanSvoJ>Mxv)dxJS(3kVPIYa{NtCQlHk>_%g3vNIA`5?_f$VG*4zY zX5=FreWI2bqfb*9#Dns+nSpdi|K{8Mt4c6Jo?P>$nCp<<__tN2`7sjlPPokFcz2X_ zx++WQ9}?x|nAI#9sjNsK2=l1Z_m#!QCf$kAK1DwejI?CCUNY7cv?BC@ z7WJ8l&_0AdNQ6E`XrBt=nRL8jXgz$5w}T zN)2Rilp>bOQ!zlw4iF&3eVFBsvYVqYC7xUUmxRSaKF>uRRHYn%Dy2vHyo|S{?Zb&( za9*t~ot~4xEG>J26|h!Lo{2q&G>$eZS$ueoQx-pgX%L`bW`pa&6bEagtuv&MV(r;< ze-p$vWPmec=c-TS%b{m&?12_;fyR@@$6op`$rJVdh&r74VC#)dkjpWAYLv5T5tmz zN80l^E=D1trZj+ioCZj?@Ui>j^eqEX&Zy?F>rv&V8;~E6g7uyFJ8ds1M6qzJ-gOPM zP(JBe^G7JkIPl#IYwtQ1@GeHVT|?}bELkj5Jg!4Lm4>yHA3@EO20_x5B<>0d#^v3; z-$9;8*vHd<>It8LoO_lxa@F~`e9F%R0J6u;w(Q~bw~A#z^1dn~Q0T}%;jsB*S!Z<1 z19`WY&W@_+r^?qRi(A~zLH>0!G!$IR(qEBhNeke{*#_npo0~GDBV^#d7IOj>8ImUQ zQ*TGJ4n{ONb>V4L0+I05tSer!_l1h&O>>(Q{wvq1i41Li-3T2R1nRO&uUUxhg5 zn(wHj2PtlFoMNikWQJk%3mFQW45X4gr8f=JgRCbunQLKxk;q}4uc0(~$~rNX#iF8| z-D~e0CHv1lPm}TgQTI0RQB~L4_)I2&0f7l3HD1w9YqYVWZ4}!k4(gm_2F}O?V?m2o zYiY4GR$J2~gLp+vm;`d%3}F52)hfMOYp-5wtA!wn2_H!S*x28x!>vUG6iHdO@9R+j4}-` zaJQyeZeWwUT!emqjP8=p(;NC3`_C{g=GplNvMeH)1b~e)QYpxWpPVuM=1nUCj`E5L zqGpSwkJrfST?OWK!;^2p?qsh5$%uEFS))7}7j8LDk|3jK+0GEmCQ|-PL zCP;F`Wt{nR!wHF0{`}F1y`L{}c0#S!r&`CP)=4usI|<+Ta^DwIUC4C92&N;c@5rW< z2h$TD**OfS?47A~h$ayHbKPQO66?tMrD`)POjf!pS|^>x1zsw+z*SztRgOhUh*0tr zl!wO*qPXXz%hAAq6AIJb3gsC&^4lsBaJdaFX?Zm4#lqc2Q zFgwuIa2o8&eJu|m!yF?~HqO`b3+7cJFM^U8)`FcFN&OW@m%QNJ)px1ni(kh)T(^32 zE(mt{rgZ5tcOV~RN~5)+sIOG#J`XI1w{7W6b>90g2R3c*^&m!9A%Ymz4;SN$zJlNu z-;^zeb?#BHl?J0ygeVR9Fp%~w_$DX`28*=pHz^6YEO{99Po#o1wSq_4;qzjxy^t zrVKysj%wb4t$dw!QOV&y4OI=^X?}o~s96SOo zJ`*l`XcSX%c_8fhTMKb)+F|EMQ|+DM{8?@Z43A64kfH96)b5Gl52WyJH2PE_rpZj= zaB|>0jsW0H2=<4|jwPDtuiCEdFyjYxd8w^&3tE<^*h%0xOiXOChn&^$lR>??YXV2A zyM$Tdf1gQzd@Vc=3@3jK69~)~m}AbD7rFzQJGKIJRqA@2!ChHAY2thK?oA#b()Eaw zg3y;(TZoo9u{{klH5`<0;>OAN=*NY^JABP|0_X$q#>|4Sgv(}(s*V56OziW{zm&3C zEv8$>hgfPOY16<@t>KX)aJW2jI9_l9A09ajFNnes9$A2wVSLHQO95Xzc**BW9$q|r zapNWLy5E2?xc=CI_VH!ZRfq_R{<9pJ`LvU{9g>2TI z*Ea?!ZmC7eM`w4Sa03bh8xS=k_-EgO_1Kcx@WiRfQDvF#V!ow)LQUFlH4be3Plh&O zy{^zmR;4OhN1ZJp@y_TQThW<+HhMp~^%N&5I(q}=Ya?2R7aa826L)y-l2LeWk^XoV zH_yWdm}Gg(vuMvX|D|O9NKg&G6L8eG;v%nSXB)7EXTH_S%`l&_aB|cJlEAjlxAdKl z4RA&{5Y10cJuTb{>y7tcR{w~owH272bb|J4=TnGEAa>n00P`l#A6jZI@-{hg)ZQ`_5|mpc`fDy~Y#hH5BMgAI!uH ze?t?5P<2fo&`OTK@o@ExZu(%>2vF){Uo{fzhv~)kCu0@!X6F0w?gwlmtX?u!`J6Tm z^ZJd>hSv|*AcCJk?V;fQ^PzcpJE!j4`s(K)c9iN3?Vb9`Q z1dT~K*M@`LbqFS3kqUNPf4mX=u;Cb7?9_0SgrgXPC5p>{!vS3YYaXi}7ksaqKf(;I zy)GZlLj^)bp@V20#mfRC!430YG6KCI(M^5qQfe!9Fz`dZmLCYcxhj9z#23xk>(bN) zJtxsQH@Uc!k#FrpC-h}-XZq9p7Z8*s`S2}Y2hxJtY|IQ`8B8HZ1?j@Du4!KiFs>Vg zlzgOkx{+34#TM5_$;j+G{kmbn`Ne0{ZCOL)1;{l1MQ5*LtVa#9i94nxXOxztae+@2 zz6G>tEt@F}dpe@Q?epIZ2RrNGkM(WrVtYU_aEIgkHeN$X#ibs>#Es`BZ;>&;@dM>M z;AIvKZk)fDUGOb9lhc9$-OW+r1bGr&Yu9P{9GBk?>k=M`H%zwY2RF^{GiPr#RuODL zC@1d5(G6w*m&lg_-oJA*ruIqDD+FJvAHz#Qw%A4Zg^g>p{x%Z7uxSKeiubTzm^lqb zjqiuKcLFJx#82`qo{3K2{3hD8o|qa#;w<0dKNH^&#_d%AZ{my260TA&>CO;;d@B$* z6%*g|4_UqlX|}?XTIbn@BpeET*;!X>Xg&=mCI{UeX$)1~1|2jbflidfWOwH^(r%^JXe{GGBJVfPq_0 zJ79oPef2Taw3c!AYM8|RG6MREXAIY=@&f~G$7n4x39{4-NK$6_M(mjMQA_mWmRV%+ zEqMVdG`c#F4WswPGI>JHD$o0hm*Yt_EIC}NVaZ<8X{$}14hP({{sH`iU?%z;bv`5$ z3bqu(MS_P^Vye9mOpLbE0BK`2w{zlnM4bzH1f=}=h%dGold~}rc;49YJzDI169UQR z9$U*t?U9p_v-O)^9ONmm3>AeU9_1Nb6^=Oa;tjuxae7u8Y(e(i^hPou_nz*xZ zZ7Tj(RMi&|E-(VYkLqp`?auo>8YKu| z5r|cf5!kG;rRNRbihsywXMa18ehh}kq0tX8^IjwIfq%ZGWpWr73NIFYXS3LUxS&<9C4*56WOKEG(Rh+Hz_$?h2Et#upQbv#1-#8322$JK zR$Ur?IDXAbm5s`47p+6YwUqw~d(3g&(XK&0yhZWEpn znYcsu$57KjufWKzh5lrl=ug%fJ7!aV0xLt+P5ntX^(Whmlcz&}GSwSD(ojc4aqk?j zL@-hw!@43Lb>8rM69NecJFY>{{e%ml+TcLT9q6zs@^1u`S_wpS-^4zEvy|D#Bq!-z z;2J0yx7c)=kcvR@cS25jhCmGm&dqNGMaQeF;WBOo#oP)?!T~VI-9}(jIJmL?I!vTG zx6qu8dlUleU^UTjg6Y{53#?V!GRs5u&wNCsq`CyMA@9Sx&|jGxH5P)?%flf?1$wO? z=39Aa;uLEk5hZNhwa}iA`H^2#-$B2LZGcEOQ!KZkJgZrEBY~~355Y&q_TV~dsm8== zv1;;LO>f@@jg&d@66%Wv;w4C|1Qcbs1n|j@&dav~-Uv96b1|SoSlNuRfH^*;_$j^N zfPOBd)-u>%cRA8;y9@7ueoVC5YV_Fb5O*6xE#c{Kz>cbgv+yE2%|a%jxIBUjuf(tT zsAzHtGuWqn^ll2rd0kRDn=(2VvTj#=Nneq1@*1{@TKNd=c+K!W%#Uym6{h)O!AH1C z2#nzF$@Lln`_8JV4lYGkfw72^F!wT5Sr*(=R~^`+o|DM_Ye!C>)UyjlsDb_=L;TPHbDsw1tV&9QS+JyG?;@;Dd0Di-X2XYLQxu$cT#~_<%!ekRT1Dppa^2>nPJyYZ(D@ylN z;~V*x0ZlQ8UbSp4IHX`GKv+c;oZFT@V)DxyT#u3k@2+H`QP^cnfC2z!#_&X#Ee)Snzi7K^j}7&WG>P?c#%!6Y%fQDniJ=MTOsi{}L)<*uOJ8`n;?{pcr$q$fA=hQuZ^B zyx|LK(VJv2lqEgCyq#SH7`&tk_FXj|SNT1xPxZ_#k0lpzZ^_!9oVrqLp$N@aOj$h?CpC4D=A_2|7L+)q?qoLDr4Y$+7y?Hj{Z-*B z43#^OjIX=pYYeVJe1mD05@LPf5Y>JY1=CF_izLl{Y7UZ;N@)EFDAIc=c0%xpe!vR1 zfl0UQ$6@wFlX<7#Mzt9*LZ%0Y$X0@227ijWtc1*vygPYS6DO-&lHxcUWH$bT%fxh; z(Qtg7jXB-Ehy57@jXR+uOs6^B_GWeaO1v-Yy&nW4ohc2*@1Y>aLjDuj@hDawIm$Q)X%3$vbLIT!H{XI7biHAN;XveAFQClwGC}fM4)F1rL<}FT2ilO;}Dwe!(Y+o zM%72=-*<=JQL@|~h7 z&*VYKX-|Dm8qL!7)Hokcyg4t6g`=@w&7SO7Jvc4jhNJRsIOMcf;r=4pSwlCKHlb#{2KPBQ;0y}96W`MqSB3IS6p;Ve zNi1^`1?m?$i6utwK>QmD>E@aSmcTywaWG9pWr^mqrn7N;UEl=+Og{-CIwyb zlUF@*(PWoBLO$gO`Q;R|fDSaw=`{-~u0B>?3&||3Ba#GV9QD55FyN*wsUHHvs?8je z(@O0>Y|gfETsH7c%Q>ft0Zke!e}d06#ACtNQBYpKhZp+AIg-&|5K2_Ra{1d(#embA3{Gn@IIZU5Mz-#(>EV6v zrXJ^sbKtf+*fauPnVz-G^#Bra;dLyjUl}2%QIZC(%n?79oIdS*P8+t@f+jtAG_t3a zHSFKvUf+w*_ks28(4dc9-*p2$%IeR*jLQ4p7`h_|Mdc?W>5d$Bpwu0tF9%)UPnI3f zmwy=`yL&$=K&lVu#NpZnzI%|q92_9a4(Q9j43IzBpAaBn^)R>4v~GbuMzAjaCq$YG$_$b2`^qQFPob@r3ZAqImsZkH z+*I@2$)vhCB+*Hr9he9E`i%W*vZ}=10#|CJXNhoNmS>V#E(4Dbc~GC#P)sS|k4B70 zWET5A>=1G3!kVc!=Aj^{_npG?K*(nlxkAsC~D8jkv2lcZfxH?r0ak{ zY+yc>+{aUDfBEY<`rXWY^LtYVyJWw=BQx!}<+a+vQSbLgFz~;u$HX;7Nat#%_k(yoKu$GPFGSV&DRRHNJr3D1V7M<7#g7q5&haWh z2~r0DfBj4ZAdnLb(F{9&_|U|brJeE3u81G2VL+j}j8187bb1Q|qO*8c@o+N`@kbGd zZd6ze593|F@NQpIT~I_fpII=Q7GKEtxlmfo!YqWUT3&4czlrY%P^X@@cVZUONQ7mgqq&TnefIyW{qT6D<=X82_`i9D#{G^Vb#n zm*2Fip&!QS2M+1v=`1woq0iGY_cwhsi=^l<+~33_oX_U|Cf?_?*^Q+m|D^u|z8sQ zyqOk)H)pQ3p|{Au{D9utUtvSJ^hC}l9bYw^BF4uF(JKg(kJxN~loV2rBF;IaoJVzG z4IE@V%SU58ccN7Gcubg>{ti0xZu>geXrk9AWqLh{!S2}WWI>aDC2|kbuRP9t(UuHY zf$7_Na-3jt$2W})=Z?=gZx|q&8IprVH-F$hydG(XRPWdFCOhq0`juU(*RSl7sb4`D zE~;g1U`00DeQjBrWtJ!2z;;@Wcz#%cIX*oD7v0^wm@qr|MR#d<=j@+6!u{T`jB_0F zWW%FGrgG#{*)YleuJ^Y$aFf6Q%s`(?d@F9M!!&j^HQ+D#uFj@iiy=sW`f-J_ zOWNOu=ZzBm?ZEi-ljtyhhTVoBT3PYw8&SaVcR+l4pKs|8$h90KKD{5`bbNXn#Td3> zn>-(q`*7)^fgNE`eEMBTL45kqf$`}Z@!^Rv_$=)Y8K0ifZxSE*vf|TEO2?-kmX1#! zs<37(u9e{Q!?J_ZyP%+e#jpgYUnkMM5Rv}C;PmLlH3`T+IzBzlT)I`bxt77{@rA+Z zVK?nWrkCMArz6wv!bkd^696=F6oBB_E$QhnSp&Ca_e;LViuds8+=`tOnf_4cuOGp= zK5qUlJLvo+`u%m!Gcd^Eojkyf^5c;6_aa97spk&^Nzb1LgFeXR0*!|{e~Fa8?uE~2 z@;<@(E4%H}&)>NJm*($j%zb+PDsMd;^Ed7@oxheHG1UsxR= zD&yvb{fV|1$x|!7;{{1H)s#ZBe2TYjw3$55Yxn++$1M#}q_ zGUK;6BpeRguMVffh~Bkgd(SH)zJ&=GRidWk!|?P7Mw9JF2wCgAd~109J!C4u-=kr5 zbQoF0_!d5=ux{(HM;J}J7~4M_oQz=KFn5D*ApG1Gmy)XX9gk#$9GP6Q3MWynIv>}+ z#QW|D2VM2=_FNT$vz9q~y9F^IeA|a&DAFcs;11u4ZnO~ggl`MMmQyIIuMImJwp;>+ zyIF;57TP4E*7Sl)>NHz7eTK%Ta@% z@RXV%aQOBI@L8krmAPnqr8T9d{`M?9maOipYt$CO9jYW;J-brfC#*$yV)-~=vxN0n z#$|`aNEsL_Qil-G;R7J^B~M*-u@@^-Oj2RgS2cTA;@TQ-U9D_*Sf7;`U!{z1YONjp zO?rHlS>vnZ_!61%RSq@2n>jwZb(N^f+yPF#(yFYfzfDcA3FFg2)@R}mScBP1v}pbj zKrtN5CwAhCtWd1Vx+Ag|=!0z2S)-LT;Xw1v4`KzJ9C#h`@Mi}vxluV$ zS(B$aI(qXGyF8YpBux*Z6fXHbumV#U4SwiLeh=^Px#hd%rM3}=6aCwASC?#~gh zct1v#gTV8;&!xIM^S=uB!!DSuzeNZy-Uw@^N8EjRr?+P-X0X_}LaVuUu@P!((1F+$ zLhlvh7udugp*k}`dD2m>=k1a>M6u*q3#&=K_SId9`I(YUZ&p2+hR2kw^*CiHdV{NZ z;Ml7-Od%3jjEl>b4aaS3Q*syNc|vh+h3O=wAvL8&b!_j2{f{NT5$OWKYFZaHLPAU2 z0HnWSP?t{nEW5ObT}n&=&Ubg%d5M=jmQLB1H+T5y?Y(ON$Cizh*mmJ9KC5$469q{% zrRMQM6ja^sKMyngp5L$*PY0AwAg%TYxC}Ht)ryQRYtei>8iqgO?!5vPNFn61{|ZB= zIqz@tt?Vve${c_|@41|_eQw_Sv<!usUX#FK}~Gx=;QaQ^X2wew|`^Q>{6wa#-M zo^4QpxO@>rY;Q(my_X{Cad0;f=eF8oFs}5EaPkp1zS;kPsnX+cK7mk307)tPH6+5I z)ah`%F!2;CJ6?cZj#98!p~%$_K=4AWop{nNWgPA_h|P8`3>5h(8vHZx?y+F)IX>5Q zpL?9H;Os~X>NjU^L5KjT6T_{Soq_oLh}`f@d@GI{9szx3&;q}G{om-R;#@d}C);C) z9G-e8>8YHsqeG}PlFT1we~5~iV+L~IDm%=Afv>p7?(_Do7UC+*Mm#{A$+HCK3~nF9 z9>SAP!jn(JlTX6aE?)>UiA-w~blJsH#y$?;*kcY0Jr3D1xEry3##iTu;rwum&b((2 zN2Ae)LN;-hW>>rmgc@ltphFKsmptz(-ox~!^&}RVGp4kC>rB>S*eo)a+E+nHKr%^-wT8p#TzCuqx^caY*d3mSCd=gmxtV$7VPIa)donSW zlIt%4jh35m#v)&82`f;O%hZM8z`|vCXA`OJ;M%%8&stoB;SIyR6&^%t8;BwzxwPgN zrVKCAgT|qb^(A7}fDBZ^Te^X{_ zi~U8A70x`Acg{Fq6-RR9^5IS%(2g~vB(Pm1m5|yO;YCfZ0lynR1Lh{6^OJ?|0vNad zGEL_vJ9It-xoC22-{(^cm!p{RNUHCs@QrfQV4~gI@h~CmwI1T z@WoYsau|SzYm~G*7_#HhE+O0dT_CdL_cbr!>+vY+OSIv&Acf`9xJIaB!%+xs@=$5* z**>z550yTGm%bwi9w{CFkh$x1Cx(&cTi zJUKnYy|GmG#>!lwa%#TissB$kJ>(9kXL2}i0s^Z$lAK@Qv8H3S^+eR$k>H0B-;_7) z69JmwLeflM;yDZxomDn`!-cQ&6kL~`Qqy)26Whm^P zh#UJPC{oWWsZA$r>#=d)z06&*;7IgGH9xQopv63w!V58+JOdFzb{m2HsjU$>T@^K6 zTMA?zj`66{-qPIa~Bk4;s2anC}q za#Y+)RrvZ&Ao7M(yhp|r3A`2wd=xjaPS2Zy+l6p7R?B1?Kg@6YVSc*@=C^RL+bi)lpBI9AuUJllZG_#t>26$2dI_Hx zY_?VE!wB|Qf8SS)C;do-IZI%A4Gqu2YV~p?!~*|_1y(om&9j;mCg!;o!Gm7KX-=vA zEyUszGln_Kp2DrORArx1g%ijVSETssmr4gt04-E15(^gcwCUoCL*wJ=;k<_BF$8du-Sf%%#j z;IFEzao9A((X6cL5g86qR>C-^L@WrzlDqCw*Au@IE7u@Mf!eDU<2hbpdcW@QO3)ZRDkh* z1vi+516S09gSPLd?Pv{AHMLG5V}dx_6g22vi6*l?W`X19{DNB$n|VJ9?tctS#nknB zbR#+%ZzHpK5}dhcpv&|?+{OVk=qb8&2Twf%VN4B@KxT1jEM7=(T-E2sV!GJ;1yr6j zJ3n4`kl7J47QdLWfccWXX9ugNf{@(Eay-eUaAzS8)+kRz>*PABJuot-UI1q1{3wa@ zz)Rl(`VTrU%Z;~LO?wwV4!vr)pj^3`QDw$H#;@&ZW~TF>eJJ^p1%ur%blRX2rHZj=xTXUg{owkd2yg+qGn}n~L2#YlaiG}u8 zk;w?G=czD$o!arPHhCJ1E8;Z6P7o1DMgf#7X@7-IV6-^~QG@4C{AoDf_Z`aosfhT~ zF#mrXO8#WQMT4?YKb@t9R=rwN4GrIlTgiuZ2^%eB8Z58y$be&1_1in@iT9i70>b%z zM(fxx#?XLj4x=sUr9A0Sf8uMF|CtkR`4Y~TqaQB=`>_+@55TBnP@T(p_$)`k*Yc_* zc+evC5uFOmEBK{;^-tVrO@nH>Ac@#;rZo(2v*m5F7q1WKR~9{0zp`kae)XHyv{Gvt zl!7qIv!>0&)YjV1{^z@?*1IVJgZ(Dg)wz(sz=K9jFZVq%vOojhwF10++o9mhH&Nw5*(7^T|oYbyvWHz09X7=s|xYezFFsFT2|!`GN+ZWk)v^SndHBIVtZNQT_oUk zYHUAUm9xMpLts>UkfsuTWJ0(ak*;95LF6iQ+5i0ncfItt<8ZkEq&e(V9;9Q#28df=re&AZV>^8YF?VS9be58-FR&hs^MgF--62v)up9)&rv zR6Fo$l!v@flr*=uHvOI8fGp8Oxb!9m^8JkYB%UhnctBU_m&hjxm6A8;o8n%pX%rIs zzLn@7dBdnK`2i`x)i*iO-meQlMc{IBou(tN8m!l9A~gDr*GXSt60R1qnm8AApAR;1 zHm*L}5O>)kGqE;5g3CD<91S4Ag6e?as=)6UE@^Cf)AVA#R|!r@mRCJDtUg2!6w^df$d?PFYKG5@jbY$9x)-I09vLtLA1&rTTvDs zk%|vbtu=z%>iV1^C8wq4SO6GQ9vn}4(T+qbZg+xEb6^}FmP%YEP`;~p7ILNmN%}J# zdi$`|RD;i9-^#l>!k`9xPR0}MN7Uws>hY~y7irlsr$XKJJkuv5!}H8IXR_72QC=$W z!cT~#7~kw$*~E7%4=?;`HC>77Wg*G>AyObZjDrfw;@5J;E<~;Rep9{E@D1O}=FKdM zJ~eMjSYE2`@I1v?T53xcWVu3p`hU~T-@m0%|Rvuh#M#56Ww z1`Atu#J#w$xA!X08%$$fvSL5?ubE^9LJ{A}GeY6uqnyaOGZ1mmNM4U{`z!N9*03~G zzXs#I)6&RTq#6CiWAj|@)dV*0mKceub$a;N?DTiA8qL|xoE)JeJVJhm1oy)D>Y6ED zXnEWaNsDSx&z!DA4CCiWs`129^I6Ko8$Qgy!|kfODwe#tb{Ln>&6o0nppLe`ApYjf z>8jXI)d5Klc_Td%x=!NInghZc{ig-Gt{w$A+raU9_mkIdp`Bny^$t&_AxJ-S#k&Nv3t(+fm1y^$xe2e~!B8FNI zjX|+HI{Rt$*t!r#>%fsWTGdhpQ;4oQ3edad7(&nUY(?;v516lhY=x!YfVKC1K;%L* zzUHr?){5k)FNK49>p|2(Ka9W@Guhn3LSfHd)3cpO>(}ei#Ck_~)-IWc_+H=23-S;^ z3K4w)+xoFMdRQItg6)x2duAV^imXN2uXLIN@BFevd!fuOU~OoXZgMr}&$D+KPVk3A z+W%tM(-{sn%K+<-!G+TD1{!wXI$3VkE$g@_d@CcEB+wZ|Zpt$g8$gc#-0(f8H^VDI zIm-J+MRA##yk<4aeh2nC8Ap=DoQY2N!H!@po`qp1vwc4%mF7=G67sfC7Kqg_R~MNfGQHKJeaPHnpjR&f6-h0d?b)b; z@nZ4Qw=z}*-{U#BCt60$*-CA-gkVS_L6TmSXJAp5rWfVWdhxwKI=%R?CND$(b@C%{ zeAR0e$yG=uJp{ii5y4=98?%aw5f{j};>gu045VcT2^_ATL&oPqn?f_u!_o37(JoAD)gU%}(72|+o-!JXG3c4osxk>Cs0 z4U4!ZSbblF%rPdoxdE#!-$ESSP+uW}agDrT7~aEuNHt)m`C`is7=a$qb|b1`5BiFN zoA49J10{X+M|~sOfK>8|xk6ym;77j2^DwlSDlR4m?j_U<$$^W6<|hZ9QI4m_-_q`| z{0`F%ifkr-whW69TCIk8BKAr2E$mNI%YysrLV>!J#WEvZgrnuZC6`GlHRc^@W%dbRP{{gi=8`mc`GLcp0lQx zIUuctzlYwc5py*q2G=69TE$2JX%jSRGP&0myee7vl?te{f zj`u333m``&G5p{{VjM1i?{j#A>ghrw_&3q?W|a<64BHGX>EuE~T@Yf0ry?mi{Ud`) zVrgLl5)N8<-*v>dvseR+doBpGhCXbXQUkeiCcJ=zgVzvbVMHvu0OIu1_QO*6T|8I}$;3uFRdg_XQ%8-zLdz-$A{WUTUI;_$_9Uj)bUSTgYlQnBGy zAIXQRMponjj3m$3kjLo=JrEMSszm-lJ|OadXrL;rCK#6?=5QjF7f~-5>YXaJY5wHo z6fh#Vynx)O+L>WS1z$+2N8&=Iyh`71!ma&b&o-olN2cWGhn{W|*aMaqrGxLY1AaoH zsD3o|_x!O_)P&Zs?>9RulKEo|&pLJqTaARs| zDfVgzwSGBLW%OkP!O;+2O3)(a#ee-IAE&{DF!SNzLi1-i&Ck@0WK*kCX;KMbq6@?iv2T>5UxN?d|o zTM2&UxN69+3*%WXOlLft&G0m1yy0@G%9pqrKhita@sJIIl@JoYheVYqLY?JOvrn!? zYFtfe(A5Cbj?MP};9SJvmcXEm0}ES_taJB$*QpC>r%Wfa3huQMw9pTxH6F%}7)tMm zN9wKbwPES4ukS>iaxVlXy&3k6%Schv=+}*E)SkqCXv(EmF{O5KCrFwQ%?Nm$G`9uT)~& zD9xLcxrGmRjx#WeLOY1bGdmL$QE}v{*D%$PjjLofh*DneHrPtv0BZSKzKIdb%J!}J z+7%~JD}lxLR3m0Cmb~dFs3w;DcQ@oI-wKF;z7<=zTSl7OBfdy?r1f0P*oq^XaRKu7 zXku+1?lj56H4KPWyFb1K6eC=I^90l|_Y%}yh`M9RJ2kt1R@Ir_-23XtKeBow*~mFS zfK#l@1Xow{j<7GXiK06~g6EB*IS#^0rous~yRH+ZMn85UI-a2#i23moki=NB;7ho< z(U8l0&87`sK)sq7#$VrxD)xQ5^xep-PPI-?A&r;Nrqc>r$05a?l~V0)o$f|jUM9`N zEiU9=Q3FQbj{7n;wNBq3{{^Q3AO^70^s}WsreKo0v7s)ab~65{TrUU7M0KBd!3E%< z+qd^Nz09{Gx>ZNh%M&NMTrhpneWUASqS04dA130 z1z){KY%^}GNCmok-azba!#e-N_|0oE#ndNb@@EOF!3nR$Mxn{ud}@=yNW`+q8;F-Z z1ym}k=~*30xJGd7`LceL?&;?E68vM^NqayX*?u+7fB4sCYyATU8-mgE zCZ=o2LC0Wx@ef0cfeeHnD0gLPo+kG7M`0H8PUiX922lIar1q4BHz2vM1j(}47JHcv z^3EA7=Vl*K_sQxAt6mQNzRzOtU5HsYG^(ZtUwXgXWQF8Cqzx_a2^-!}r?(8si+dq2 zW|N(dl^2hOK~!2^ytYinw;DW42h20|Z5}&bMs)oB8$rajhQIiXvE${icj}}-r&S4| z5fqubc`dgQ?CT`kgU{EXvk(@MHU_*5t7Z>x!GdkUuC!q(2+lqn3@Z5h?&W1!!iT4I zY(RKu;EX3y5c$Hsir0+5n{tz6%WiOH7r?@mtR$!uM5itGJuqOCD-mE%muKIEUq)gL ziJ`ZClA&%B3hP_3kn}UEmJoWa`6G?gLQQ)0Be*b4dL@JX{(Z!}m47hWxNo5q1QC`h ziFln-m&-m0O+mNzr%a|f#``@e)nkvsm#8`$S4>3%AHepbp~B#G4#S=99C9~m(G74E zW@<~kBNBKMinW%PMIwV}FknYA6qA+BT|pkfd^Ei&XXs+~Q3K72w(m7(zeDKDO#u7M z%ioIus;1|+a(bnnRSU!TZ8edL46FNT0%}(yN*-m(hI-mqwgMou7ut#|yQzk>UxsmH zSlz>|*J%2ndNLTS;g5qUboG56zh@pRzhT(!>ht0EHteHNgx|h4v33Mcuh-$WZ^$UJ z^W*2MI1Hx!zYP3rGEpzr#zLwopz7!3_3zJ6b zEbeNS_-{BevqFD$p0HGwfGq1$BB<6xvqYN7MqAs3iOKEfBlY_U9xsM z*_UQIwADTu_Y1=!lNC8)md=53eE6meYa4hJ1WTO&XiAO7D!hr{gH)5zz|u3N%F)MPmhbczifk~Jx0JD_2*T%&4c^1-bt7;_VM$4Fqpf3M(1hss zi4VrrZGouw+-$f?CvP8Uy)E53aojcudbJhe-`#jqOIaZ;F52*shar#P>Die?%+c^T zL|D12csV~bttVDFY@`+rwftKWo4wW%e?`Bv?{{7ZiJVwOQ6-keJWUS(58(WzacsH% zK^)UI%U07o=r!yH3E#EUPa2puTb{+Vwv4^9gD}=25=zrrbccQ{=ObgwjIrU<1Co}u zUPc0UNIcr~qg_9`@vwgYOeRkHG+^vSSkxqbU>zZd^RldrmXAuSA}F>B(~RsqlcR_E z0SUWFsU?I^1`O-97l9%5B3mVkjI3?vq6&-}D@@K5?P)OK0A*arRc9?CbQ;$jh1qba zg~e z%85IcHSHsPZ6kU0RdfWplQY(q#NP=1v2`gImG3qlmo74-&>M(>TI=>LnSfux<6bJl zDawLEyn{PIJJ0E5n4Ewmw+`2|=#eed2_Q7SWO(2TExk= zn)p|=B?vds9aiuHE}muonvy8m` z;>5y(Nra(O@Q)g{tpc&g8%PsXFy{G^;(3;oL0X9=HFLitr-mJq+LRn)6s(tUEAMx3 zlqxkuoDdNQS|j-Co3n9jw4&WqZxeeIhO}D`>DL@mrT!;2O@W0rtrgAh2vXq1SlGx2c6zPeeLP`49h@;q3jAbA+0~&WhB3eeQ1jvM2w-{0*p4XrVvZa zeS^2`ZZ5%wLd|#RLH!aid7L_66hTC7d%TU|-p0MbXu}^@(;M7$SlAD@9l|%#UVuvIzuH3D?6=3E$H<}gtlLNn2j|#}#@MNgP?u^U zMvw+#b-)BiuM8q}hzLGlh(qJ9-YNf^u|P`OmqA)lJ2BGS!2e9)KYV=PfR9X(kBLk) z4)8OyXQGegKui&+i|l89c3`XtthSuc8L~%Z+Xscv*&_Re`8oDMj=j_Y@iRmT4l6mU zAKkJe9d3pPu*8}Ny>|i7whM_!;yaRdG2iZXN#Z+_xbW;3bQ0eY2IiJ=)DUg9n{gP6 z9BB|`0%|m^BipLCQ)GMXjv>a03907P%*2S(dhzszoI74@!|c^cQdmIs+k@hz2s}37 z_5tT3_jB=Q#a#pjxq-%5xu+hAOAo9CaX%<%rNihTf?5$twrRluGHbf1ubxNis*Js0 zEomGG9n`+0aj*`MV4v4sq*`+Td7+wkEhBVRLlk zK{Je(IoNC;_ks>(`6kw?O0c4iU8sBlHXl(8laX!2z!*0O%O7B8+@P$9NQVKlR?E~R z6Wcf)$27wQ{a(X4Y51IbTRM2@%>+^= zcquk~$F$flE>fH&iAtqxn4*o=4K|$JdolO9ft7=pt2&(DgEC&vlUCOEh1<~ukJ3>gn`dPy)r$=_f?i<{}u zw_@}I*wHB!0Kde(&JN-u4~sHnArnycp9~HrqCA$2^=p((X^SUhi=u&$c zOh*USK52-swOGIZEFR%e;9N722n#~4MpQ#yN6wSk2KEgo_9L12gWEA}VqfhT`Z)wE zH$QAo2W3M|&_j>|1??u6S;Ai+B|sPZJfcD_4b=3D91+eTn>Zs@lT44*#5qJ-n||!n z4^nDvFNjb5I*kOqwW*AY!fGNqYpcF4vEuUs1vdQ+vX(<{DD|z2dc{1Z|sNJ5ITj8COyBod-tm4VBhSE2la>XvmkmS ze%4t%(&|Zfr(69n-DaC$Te>(4V?@zObo zdHv(!a8iRo%0Jw7ena12PD4=4VLR%hA?WjUJHpoag}H{H!2c3n4Ua<`#IxJq9l=f0 zQMCzD1Qr;s^PW*$N@sygx0xT^JIekGVzM3Lcp%H7z}g|lgE$551}Az8OxzH-$?#@^ z{Q=6UCW?zn{&LdUenNhwbYgtCeMe{@bc??4r{0x`7sq&$E3uB#_;TRy!d}KA9r&K} zJJocQw9mf-?eiDf=L78*4Q~Gjdp<$?ZG~4s`m!o< zV8I9H2d8=UnJGFFWM`XQ>~3wU#SRRMZ_G{yOvBsX$V;>jYkUQQT!&#bkrOalZ~6%y zFou}eZdlelyszUO-u@VqG1hcDKjRfRC~o30Tqco0<89U><49Dts9PL6L*8bjI_LGNp9bg zMSwCy(!>qb-n#j)F+2gC$L~SC-}u9SsrNjVDjT}{x6$*uF}wTuYA;Sy6MyyTE~Yx! zH{Sx>w**n`tifg!#fAud7Y=AjGjMLs;TsFDO=_zcmVRGp=f*r7ZLhOpqPffSV*%zto zk373(=;u;4DgGzlat&|ny%WhmV9GCr_!3hf{}4Y={4LZ?y)|C%IS1@Lul}f-X+MW# zdHE(@x+2wn3^r{2XhzujA1)d^xN77`DqQwI*PzaZFPO<&spBs=W`gV2o}LkqFc7blG!<5n>Oth|R>GHLP0prOMAI;ce_#%zl!*Sk>WV~Zh>a&EwYn(UU)UBrC$?WDU_~Xi_(aE)&&9VOozCx|3nuheW4*4vD;u9A7P=bz=s(erL$RNRWiC8ZwA;S8d-sjihBSoBSJ3_N zjXRybhFd4N2lusT=)Mx27PRBg0c)!F1vcj3rBCCfEVUn%_IdC!1b)KBcqCw&E;L3L zLNw=6RK?_6ni8pHvPSA1I)f~NPg3qbv&tP>C#Ai$mI$)7^g%uQ{$l)ml3rId*t~G>noN`4kO6-~8Fgr$g!|A(vbMdmEF?@Jjlbsa8QK!HTA9+L=8LR=# zk83q#Cz3Eu#(oUTu$g@?i0HLpauB$81KFaH_~H-_*fw+AFiZQ+~%@o^eGL z-f<=vMkgk7{2HowSvALF&&MAOcd68KmGeUy{ht2u9nkShcrv%yo;fQ^IhxU+CK)B1 z_Or-we3ExWjr;IA&&|L6dn+)|+ zmV>>c8(T&yzZn-=$KNp#Q|b{U(;=!&o;tKIE|fMA_P85K5vw8`u@)@GD{BkGYB8Gp zZ*K2UQo~+&{)7;YsJd|Fw`V8J45)><`ZP6TLfB07<%g|rw|OqehaX2`H|!%nXiw&! zXK6hN%Pu~i8N3K1DYsDO_xl@c}{+HD=;lMLJ zSB0$M$%s1~$X^H$Zd4m|VA6$VI4Gfq>W;x_G&z46wK$^?929SHLmZ*(d_ro4%5NlF zACk|4k4`)S@!pQ%Vq37vL&iOj0}m73bW#ihtl!Iho|F;hT={e|VQ5?bnA~f3IQ!{)P3|>>ZfDU1f)5{+dz(gzcZj z66Cg!`N#b=tSa(lAEkB!8?ERnxufE~84L}p`HjFx9gy1#BhG#!h|8br;k(-f!)iKr z-{mI>>N}1fN?-+7T(X++9_mIxKM3avcqW$Bzvuop?A>*fWgmy6>@|at!a5&hC$H`M z*1Pmk+1WQTd}9%IXMgh{`Sl6}+Jt+{z}%QRH;6Rqgmpyb0152D1Uhezsy!wnAypFTNA z4ikQm#C>{ryglP^!y}Oz`zi4Q{0BlwsPj!HOdN!#d47mzwBlA*$BZ>xG06&;V{Eae zm|zCq>Ue8h;;)}Kf}M3|i@vkc7UrK93zuJQ)~|1Q zj|&t&KbOHJ5O1&z0b0l7YKn=L!+|}C_5!RIwwrDhO0&92V2QKa{KfRa&>XVO#0@4^ zTVnq>YvoMn5fMWfWVsFhyLw^AgB!)i-LMzho|gAukc;o&(^6IHwb<QX3J$Gr8Xk%L!{c>q;Hq>3?w;LjVE569*W7I2 zEJ*n|WB=th4{7WUymvQ@Q=K?h=r6qf4n6WN48Nik9tqLZdLy+Vn)uKYpPw_vJZFrb zGfwzByD%8~Cw!6~Bqt-{*Supv5y8}+0Il`xorq^@O9tf+abS3 zXTx=(f82HB9V547EZ;wIM=a28S0Ohv1kLEWsBrBzcv(_DC7tIP0Bs(``e7DDGg3$L zZ;kQNY6?Imf+gT9 zIIe<8P|sWHnWn!2V_ZFN5l9ii7Vgi84G3JVl=U&S+I|;-wR3ld%4#(=mZ;2LA5*bD z>ScXot%_?^rHSrfRT!T2vMO?$<;sxtnA2X%p|zK|R1 zYa_w$*=yrX07DA{;@6?3o9c=*`g(xAl|bL|iPr|!M^67`Jtc0FpkP-7+yW zG^pj*st&bfBXK=&6hU6!HLMkn6cR}v$sp+i9Axm_8}^cZ!-0q2IfthDNC^1F_^y|v zp%#+5x3phV7)@SiMw3fOM{GnAg&&B=mmH6ra_WYstG5?_aKIj)Yp4q`vZo_zPK<;< zAHHrJn$hv*QAXlbABPqZd3@0L3O@1pz-@Z;Jof!~P^%*bTJ=Rvcx@Go3H^G?M54IlZI{tEQBGd#&pALrDDRQEN#eMg*<2VXWs!wpaRd^DKyEf)JAkkx9; zzBzkCG`QiqBVz&ESr1iegZ;}>5br#>b#*85!? zv*Oqm)>W`;wx9Y@_Q`BSe=Edjux0}1W`8>w-y##=qrm|T)Si?0l@}lf4`va{a@ZF+ z9-N1v;Mqu)msL@QDmVS;fn4@3>ruXJMV8!MVsbT>Bjg5(50D+E#kewMMXVPAL<062 z>aE0@MpQRCVnaT7Dt|=nkHKQDq$(Ie)Tk(MKY}|%TAoKX21e<|lfgRATII)E z05KYIe2VTM6jTSUpPz;6=b^EOJ~yh|Johc(RN}|R%&F;qtQ3TBHbu0;cBn3l^}M0A zh5x`&XCameF||Ukzd<8?4KO(Lk-|6~_>EP)#eNm%I{+x&&V*^cKEM4PdLm;ra1@E~ z*u*^5)C|!L%N-F$FEs7l68{0i&~d{{)rHWxO)G?(U}|!anp_l|Jb{Otn6j6yWWZA7 zj3UAu08rBFLG@>mocaOnKNf%2_O zhLd$E5D|e=U{hHAIA-+hHkw{T9WLjR@GVF|Xpz$$h=GY9{{Z+8V8-vwA>egktm74p z8QZK2voJ%$A`@`i@cc22@oFtDl=UUxdkMqXsHZ&&Fwh6NZazaY|rm2!6aUN6;QlII54`2dt9JlxNmPhvsLE#V>Rd-zn7bM8O428XEc zq)Qv0ru1X!(w;*utq(Eh)(H38H&=ST;fDh~&z#6kEd8> zRKdeiJ$xLpI_-cK95`$_EL|qGJFboy?vxP_ppCC32U|m4b{4r>#9Ybv-VK~jF~0Gb zz5BDyhR8E$`&U|p;moJu2pYS}(bL825QYnpC?5L@>}wJoru~<{x*4^z9KkfdjQ@|t z?uYCJ+L9N?K|&9qV&Ll-SfDtsKzye@GjVO1D?SbuW(W%U+ES3r7AQI@%Nn+cshe}6 zWU3}Xk@OY8x_@R1tUaHHRT_hJ?nITe2g!rA?JlVvQCuHG_h3hf?ZUc~np!+;Ji^w+ zr{I5>I6C$@T$%`M`DUvWye5Q$fV+wt@k3ks9?tK@3O=b8^b>AL}?(= z0agk=&I4j7NE;VQ8_H05R5Nk+8Ysk2BV-Fz87r?e<0qO3$!}c|v#x*}!|+&gYK;C8 zg*cOquZ0SEQ>|uKV9+hy3v4xmTTI`SEupdp36ut0izK=5ev0 zjGfek1JMv$J9PydV`z@z4PB$=OfkkXYVK&G%v zWWhlAgEoOWwOX3PV>yW|NOc+b2R4Pp#2%#i2RR?DQafX`radnC`rpZ-1QUBM^kFaA z6dxN?6<+-A$M1N)+SXTa?2g0>DFE)@NY3)jq~VR?Sf5S^FpSlBS*6Ctf;YQx<#K#% zl{z2gZ}#%XCo8|}(ya0}e+*r|@F49!l3l*-;N^=BQhr%>`8y6?-akZnH$b-B89$7d zgNX+YKK_ye%D3fZ$WY2CRYs{ZS)^~YFE&b3NQ}cf!Y$q>scJ@+O3-v$?2B{mt3L$$ z;`gzcIRY2T7xliL^BXbaa(25o%r|CC{7Cf$`SZ3>~^aE?PNq6S7KCQo)rEl7?u0OjQ<_SNh}Fs9>d{rWb{+D~jhxLP$%;u|~I49M~80ylgVe-OdJ&yNaS( zrBE?K*h|JX0EUV}hHRkNN-0j*Td2&daR0>81_ z=?L!6vfkwQt{1V{TLv4wk12@`Y$L`t{EX{{ciK-Mkq3}W4cQZ03HYgHS*RL%uM1oR znrQISm}Yw+`6O^J?1n5hKtQj@BCN-vgRDoa*M0d7rzQ4{ZWv=Hadr%@qg2YzNt`*Z zK?`2;8 z@hi?%W7vl;u@B>jbbef@V7sL+@yfpA<|KmS;@?q~UZCJ5ePiS&c6zViEY6P+!UX}g zih>{2t%w3Q0Z$xA+it`tgv-i1WfT--)A?9sqeiAb=Auu=J~@ij_kw*X!yZz=G6dNG zbvk<3D{BFH&GDpJ9f!*y$(bkC_c7YGBAy_6L***gPmev3KY-_LL@FefG4c3sF!4N4 zOmY#J?-w;h&}?}BPE%L zG;$yrkm_d3IlC|Mr?;^8IU~y-L-75zVlIqqe1B9@2Jk(B-wwVzl(N8n=?%AQ5YkQ< z!_f{x+N?Qn_)*Z?WI@|GRN#&O#ZFl@T)_7;1xsootYr(%M~g7o(?)#7(j774^W)pQ zCvd@yo-0Kw3+2aKt52A4~}P1AWoJ)R<=FfsuK3(nNfr5e;usW zA=STzMRV#$D0FIsGcH2-y(jy32Hpr$()b*JLRsW?+-Qoi!Kz5hBZ&0h@Nf8WFnPqC zVGo8DO*#|Yn?SoXIU9p`^7vhz_&feKoM@W~EL_DO_NjPH%Po&8$zVxlfi3n~(l-WJ zfEa&kd(3L;Myar6oYLHmza_0rbg)SaFYS0o7PgIt?1Kr2G9z%Af>cM(GA*xIbgdDk zu53*Sky~a$ojb3+3P*2Bq@(70p;BmEwG-93e9gDvhq_s$1;ki_SS9tVU$^2bjP@G& z0Fq#dZ=mW1aWcT0o-&fVJ-<IgN@gw+-^ur*ZnD3wX@9#Dy0c!4lOXq&-~tHm1{U?QVxEXA6Q za36mR!b{}zSJT}aV+Mth7!Yd^y_|B*hK4bb=2zn*AhBpo4VW1UOgOozfehx30h(&D z2#oeuMA7gdVGa%i17^p};0Du5ut5w4VoQ-=*7#K${>0Qm4G}d6BZwzE{I9% z9iNt-vNO7!*>l26KZ*o(zF4_u9md(V7KpFh~@2DKnCgY1w=R`T?07W8i&95bEIE z%%JGpk_MINeHdQIS;U<5Q{=gr0?|FL{dq0S-I*oK{X2w_I}Z@%8cwQG$KXS$BeGn? zTxAD{EMuc^@f#%Ru~=HtlVTq)>A8j~-{?qMQoi-zk`^SrAGV%5#OiuOm)V_V?W1MZJUN zxs^Hji+Xc^`*Y>FUVrbRocx1-m-ODAE&uhF^nNQRf9`Kkd5NyBt^z&7SMvw_YW4(+F7GpvEqL&pndt%3TUTj zDJ{{L*D#K+j+FL$%~*pB0hRiGrG>TSnkfBDz7YX~uQ|Vg32m zCfUG7Misy%DoAeEl$Dy6b7Y7afQN1DU1cQ>6mY!1<;nw6=QGVm@xn-zc|{OhHFIJhyBX+vGVK9_$g3K zmmwSw+cVXLp|Ev*4F6*^5c{Xa^ae5onc>WJHV$_X7ctP!t80TF8NMkWnI05TUFs$K zuF)Qs8|B=0b3+OT-i~?xCjBtn(vSEOz!H?ZhT6E9lpUd=8_I!`+fM>F)#>@$m4>0n z*KZ$_C10<_5Aft<&zI^~%Ga0ZSBko)=~uGl!}Ke*ptO9w6TNJ`i4g3<%_{s?h&QrT zRPnanBYZLhDTOcZZ`yLW+)s=D^aGnokt z5IF&Zjh2?F#2OuJqrn;+v~$Qwasq=yr8T|9N-ve-MJ*;7h*dD-NhHV32)*K?t*vOa z+ACIDQ9*oU0%QWn%lJTTE#jjmjwo7$fSUP#*V<=Zc_660zw6(RoMCnqz9{&$InNkpY$xGmnqG+q7CG^jCYE|lRhB{coVg%O*ns0aN%l#?;0{>EjO!2 zH^bVz+M^Oj7+HtflY%q)ZFPE3gULY^ERDO0)v(%QbZ0L(10@Ib`aXoQUr&93TKA7G zH}%99|%|W0PZJAo^Cd7_mF-N(SdIZ=L#kvCBh`Kc2Yqbx;@Xr==oRFPOVvb?tcNjaj zAt_tOmQFZ?l_;1tZQ^{#@(dCz_*tGoCSd*?ECTDda9V_3jik#-I}fs@7cP+p$;@DJ z)L!b8#Mk-_IQqcXfPOm(JHCH1Y5_DeNv=^rKftpxc|#&_S*LaSw<$3p?9gptM={Ac zunCoo*pJ|$XY#QF3BxG?(D7LJTu5>;DbRuZgBm7gm?azjq#WdSB&|!xLFOgpAa=5p zgVZGDAe;G;UW zDJgu^nV-Z*w!8!@CFht9g21Tb@US8ivwKTVUi)NUFBFrQUy_%!e21kYgozi*S1BmD zAD>DJO0K~Vb{j*cj^UgD{R|&-Z(lIz)P=fu@TRw6O zbrO^2M>|qJ5``zKBa&bAe2l6Y@{7#&i)h6@NQE-mKY*QeMD72+zxIooaO-Ayr(h8z zq^NSP>90^n!<`4>4k|Z1fR97;fUraeiu7ES*se6u3L-V5)IVseYeP?hO`0BWn`i~E z!tcP)Z*yu*UC;Jp1(|XNL-TZAEwnBC(5g2X^{8V1$E~$vczVG}eAlUYqp;_~#cJ>j zI{agz&vkbDt{GjsO_Yr`>qa}u3jC#Yh4sTj*AxIo{<@r}iRAcO>VUj$7LE2ovM0UL z5*Oc@&VP9S5&xE8qf$&9b8paVzzqu+I5<@fkh`mcbi;DGR(e%o5IPWq1IbZd*Tv`y z9Anr#88{tPz_mGr6kR4Ztj%f!JNKgZlF{CF-#TT|PEk3!$_ZVeKElDRa;^SrVnL}IQXNWkScY$!+)?@wbmnWfC3lfe$&WVFAb z>^oi}OH3@*1a4HIWI=OVEat*+6_-)!Q@9*}CM9HCn`o*%FOUhnsD*|Jxs5BZu#x>*f>h2U}7y+px)D>%9tK7Q} z%88wCp~4n|LJ_o(o$Cn{#Yoyp(D9IZvIFYLb{8quj?^zo>*+=4%3*q1`gwXvbHRMS zKkWJr?Ye3@36Sv$ORiJ&gapB7SAIk+OD^3fjt1)eGX&j)pHplB;TO!GCh<%7M|q4# z00z>`UPF_H3BY7^zPE;^%DX9T+_iHV+a#dRlk>B&haGY`vROYZZE`M5OwQ)WZuhVq zn4E3=Vj_blhlVUs4ILBHtyh`9gYpV=3(q<+%QAbj*K$_I$3+`uy{G z5Ub^@G@qBdQuEmw&&CA>u7kgreR3$yK={8ipH>ept-$-g2rYgDd56`neex;~4LRNZ^yv)y@pai&H6aHcx4dow?MInxc9oGEd4IQY@%X1$nZ&b*a5p`fgagAVPHFGBnt+vC^s)KIn!Q5DI zeHjoR1|Xsf&$(TFGM|e#>`I-@3FhUZTvJ8R&l5|Bh_K%U?%XWbBJUjoUyoqNYv)4W zH4iHI1+~Mg!c+6C?HCNP;X~ztp~^&Vq67@KP~y)hk#06YMN}w!s7ATg&#^%@DQlsY zS9goQB?zmY4OQ`HmFAhmNJ9MOsS1bZ9S8RBPIl6#G?C*y(ZrX|CGj&hHn(bsQ{6;% z4pY57xDgmt){=X!+A_Q<-mb>i!`PzhEaXwlTzi;Pwa#~TyrVpRNcBCn94)6C*RL$n zzlOz&k2c?qtfy$@`bul_qzHiR>o?H)RUKDo-042>dH8DtpS=X1Vl3N!;Bym791(ol zdGV&fN1ycq2ZB<)Pu_Aly5~gzwhmggPu{W$KaF_4Vzhw&gx&p9-IUNhLf+t0yx-(R zvyE8qY5oPRhvY=csebFY&zkCWC~-tXktf8oq1ZDRUgSDsW7n+^HW=L_mFD{Z?mm6Z zN%S=rmff@@>k{~)SAWm^%>Ewx#rvC&{=SWF!u+@XSoB6&d>nfJ4E|+xJ=9 zkr4-8x927Dj%)67BVY>T+ofE89~2oLh9h4b9Q&9s^W|AWpFN?Im(ZhwCz_3E$4Bzl z)q&-SFsnEz0)4^V9)7|ZX+3=8sBlFb$|y)Ug6yO2j|vm``Ge1B?P4%K^$~&bnLLIid;+1D;RPSmk0Xtg zE!HpIN93jUU~aV8qGF9eYa9)Di}eHKn4B6r_K5 z$m@Bq3Jf>mHCi^_gNM;fKXA>kp$Sj9n<4O7R84i5@6#hH?`=!nD4@BPR=`uS}cUkGOhRq-uK z^DmeHhCATmpXs3W8k+~oodWOM3}d-wFT9_I>9(;!+z}rpXL3JmallVHc}RFq@?`PX^e`KT$G^&>EoI zt47N;4;oZUA0!D`{SdtHMO|(GyhFY6Sp?t$l=yrE!0v<6d^b7-Nt8VX`fr>``J{2=95a$WLiO_p$Wn`YqP%ocoxjBf)iNj4^~hX+-5^G3iXvJ8DD*i zBVbW_>>Wg=BADWb*co{v&w3dt!**7QW`e)JrJ>NQ@tAW8%sHd0%m*7$5}hF4R-N!5 zfzrOyAOSkW;6;mOP}tQNOlMX2GzOuMryrXjGlw14X*0if*WgYdp%yNKo<#r*~n|k_!Nt~8Xd}H zac3aJVeA`Vai2t1g2g=sLr(Fxj=AGWT-*!gJr9j);%#vxBi@#j&C@Rn9!2(Nqddvg z!mq3?kDm2{eGq$+ol?U|BQvyghz!M9koXgool7C=Ir&XceT=IV<3&iiG_lLrH5hc* zX><>UGyQC(`Bdz~K7ja8^*!1M>{A!eA@-Jzv{n{%Knfq5UJh=Vq9x#-h)`eMZCF=4 z2Ly2rnUl73F8!{YfG!nZ2MC+cs03kg?NA$j#qht?FZ`AdJ1ofvBKx!9*Na8LnZSnM zs#|K%8Nd(sE30mySSFz!+PjLek`F*j9(|M24$8FKk)}C6AYxxPRu#O1%EXd;8&RXo zY~rUXsdiT*;RxfCZ`U1PF4lwLanRY{cs5&&mAd2<9d0#Vy6 zTmk~3F!*&MKqB5GfmMyxPhm=)o#LhJ3lSVa({DsF>Fq^B4-33fWFIx~66>TqT)5Ah z*fg|O-UZdAO_N$H^KgWYoq!qNi>xD_@w!Pb0=_eK{ zxC923Xqglbz8|Y7c{MFx^<6QeO8h{Fbfa3)Vz|8?l@;I6;u_Hp<{sz$D2`@=n+a8y zdq2UoXz-it?V^IbMSx;}{50PP8WS5X@0N#@n~xHSB7?#HloI(K&%UyR;uFQ#%5)#g z`bn7~?*E| zB2wK{W~Y+Iqzj>AaPuw=GFLxrPSEN)EJ2y;jf?+rh_Qg4rQ(Y z!c;gd;sozUZzRZPFMEbU5p$WlQ3fkNCZ8dARdtaxtYq0;`p;x9kpj~wbT{T&&^N6EgOf%2105@5!2iA* zR=e)trPBYtFcSj+s-aD=Ob=fI)u$Px%COm*0o@M}E0{lGh-oFT)mZCrv$ZKJtwaI{ z@U{P&=gA7k1_FKB*8H&b9g^_~A-TbQ#zIdY6Cy{opK!bB3QuTBayd0wFtV zR!F^yRE=ZVr{D=1As+%VX57+)i{~kqwa~0;0SniY%*@-qC zEj5QhA44CgPW?z>(4bOdh#l9w@#;L|VBD|V(QdS7n~yNqzzjfhDot0(o(@u20=zNV zR~G{`Y+!LMRL)3Y40m=wLsNwn9Jhwo!M}9dVxzJcCU2xP!`-8P@%30W=CWoT+<6x0 zVtHXB`UMB71tbN*rn{3EHJ&=q!-Q2HYdY!TBJ}J|8kY3UKymBI-YFm!QzGRDyz599 zu-Cc{K^&k?bqP3)MaDBUw(R<9BG+Iw7Sm)0!m<+UKzl12K!9~;C8nH0Y=SyVJCHGr z&(IuZ)nAWlJc$#ynsPnSXWH9DjwE3M;QAXU>;zxtbt?^No8b26YBZU!oNdHA+A(>D znW1a(({}j*coz|oFe)Th5H+1d$*d|X{MRyo0zJdI`B&_(j{$Z^6`t$d4H2zxp`g>WOEfv*3x8rJ& zP}8(%S-*WTWRpDmw11%A{t}i=>UUn(&olD#dc}h*=&H=fFX$By($jTvM!u(OL3;aG zKcC^BWht-e4ARXh<|Wt=2?WlJ2|`q)_!l!#4M8ZFYm6F{LjQ4L=3=;fDjq9VuAvrz z?iK|R>Vx9mgmbR?r{r0}IhFjzrcL?sH4ph+Z&N;74LvPiJz$=St-l~dyNw#5!Y^)z z3ja;tA$lV2r#+HEr>D%cCUp~Mv)LNDh3T<!xR1*PR{sU z(Dk*9-=40)>A&ACB5Pbpq6nDbbBcaXJ%9m6z;|aa)7(4znC1q@(nxGSOBfs=O@5Y= z^TH&N9WLFLXIt;m2Dq)DkM-`6HH4k8c&}}bOP-4sclWW!O*RDP2mD{ZJxX;ju{q?X zh^qxqy|M`9T;J?+Bj5#!uf|aHiTK>M^&m z`yMGB3ctUgROmgL#t5!v2vDAXJNqqSCw$uXL|+JDw?i1-k9?a4>GV8L2SNYOOEL z)CU2<_vqn^=GdQuILGC*9!ED2H5H`#Dyon80z07(>A-=9wB1iZof^7+D&mVB-COBL z6hP4FVxrSURl-#$>Gb%1`z^cMGz)t&Hj;o~b(GUB)3hS%wtf4+H?YDy8)t%fB;pMv z`o1%jSU-&a43$0p0GkD;0YiQFjRXeE;Wx%$uk2>Dl}C=OtQ0(d8CXsZr`rx%?#CBR;-Lm3=NkTe zr5mr+{0c8z;Y+|vRH8?lEfoKgTzX*Q;`kd}vG(!Zet^CHo`mKB=!ZT04!VWN}T)uj+fr@4AOJ))n zU-)2OynQiqhkA4|(H)sPt<<{vT$(b3YYQ)3fkf->Z)ayMLyQgfTLY+KtM)k!0>7l6 z2d$sb05Zla{il$`4Pl9SN#IP+Vv;Vf;K7q6^F99@8~K!1Cr@HrNJ{V;fOn>nR(l?p zZ1CgO*?Sr9B=~e@(KE}N7ngz|E-HCdH!t?LT#UZ~MD@U0EB7G10Kq>z^|{H=parpE z^ablXX!pjBmL;Pb0Rf$YeSe&N?Y72+dRG1MEP*7kBO1>5mh!lL9^jMFE8>vxh7x%{ zH@*&)mFu?v(HZe#~!cIm)Ru_9ML5-P|M;6|lgg>hZJL{HzI_XxfEL?$-@Q4KV#S_aR zUjRT;d+LW)`KIC`WDhc>H^;oJ`r#1&;>ZVgH0|(gxi=s#tp)_?v#xpBSp)~s9n8Z) zE_YIkYb*iC_ge5DIxdHFv4jmSb;k@+j9h0u0xKGH|Hq;{fXDRGay>Y?Io<#cZ3zo7 z!6hh7e4&q^T((Cm8;;)cBE)~iTs0LoqIXQO$&?I-1oXY@``CT_p z&5{w12RHu35z9VfqwG?ow7K*fIMR5VHU_`RWAp&Da1$f5@1`E#4)=2L;C~xWtw%qy zf+pOl+$bf{x7k=F#SdJT#t+O)@dIA6@gC@1bsjD>;!$UPSSVQ z{InV7Bz>3rjA^p<+Maa!{&y@naU1mrOZM2CXvt2iiR{jo^!*GyeZA8C3a|$MPY_ zpH^f2u8ued{#YYQC%K268QcT!ha~p^j)Axb?1v2Qp{)O&+U&y|KrZH?Z$805EX0So z2b*j8CC{4tuFXu?+=G2>g3Z%+S)@oP(f6q8M&B8k`$21d`YOm0TXs2k{Sl=Hsks2{ zgZxx;QS>9qQ<#UJQJywj|KpA3ll}t@-;Rg%q;_~3F3)qeER^_}v(STQUZEb0Y7|(K z5bWXbb-z_dOp};WZJm7`jX8)Gnnr3C@Q3Eta>ij0KB?L?|QxwT8qn&Umy8Aa_8`%tepzf;`g~x`vZ#PlG5C z%mQS@q+vMBa%09a?<*aoQKmd$xncS(%JQ(|I-ISMuEF?eyzUrwFupbTSMHB_4H&#+ zClj5+vO)h~zd0G&Cl|Qj9O6fATVh75Eqob%5lIu?8&6gVIuzLSM<<>8s}Ny_8e zYqOq}ChDFRuch^nch|9KBa8x7H66Nd{h#V{d#_?dz1KNI?UFr*!Vq8{sY=)Fi4 zK6WURQsFB_gTM!HWEq~~CRY2$dVHoQ13wplC!u{+8f8C7gSo=NXy{42011G2hbCcZ zsN%Um*la~Y`hHqX#jA`6z~4IC+lGap_NXn@a2H@48eIE2Chu2Uxeb#p!PT|Ypc)O4 zLjk{h{Uu&GRcwMc^Qqy`A|xc&%W5h`%MAVxW_u%O-`~?RISze-gZ|pxv&+r%d#XZ_ zXQ@zpbMRiV_+h*f&M3m%<%H+JNnm&!62N5=swkJ-`1KSTu-;-iSMK2119_}0FE1remP6A^-v zxUuGbROpd>n0Q%()aO!v>6%qY44x=V0x|0?cJ9Wi@>mVfTWM;oKY@tlN?cVSah|*^ zd;86db62|5L26o{XpJ=o8XM+X`XsrRKaP|&`j(u)1!!g)n18f6ERtrR(?yFeksv_} zO4IXLYH>jgin1bT>v)Zoc&EM|xq{;}7?cWa6Zzfi?&F59LBow}>5?f+Y5oy@Kt8*g zo4&9H{=>*1z$Nz9O+nleTx`|omu=)}4lg;r2|LIdgA#E4k4c8#YxKt7`t~4yc6)E# zO3nUs*Smy~+dnaSvbFFcr_yZlrvxnyYTnw03rZM)*Is3`_6W<^<&5mfHac8lOXn`* z(?R9oYX|o%tO;E+hbTEy?|C#eHl3M_xfzJgl+A&2%rxpfM^f)C!M|!w@CY%d=IjP% z!9#4dhgh%*%ha04U2?&TXYFjUcgK6-E8_!oVB;H~-GS|3AKn4`Fvr;K766-m#znRl6_-WsYKil<<$E?!YDnohCHboiohF@VKcp+beML-Y4iiiXx1He4>D00U zAB3nfq0e+B)@79#4aY8!(-{#r&Ja}y))SuQvHY)tJH;^yhj2K4M+Tvkgk}=*$GN0t zQg~beHB5UngFjm<^^;IC8zmF^Nt>ac^fImqW{Xa`53Ph9o7hPwuol^|`VRZTQ}-@z zL=uiaz-5rHa-UW^Gt`nupqinUg!2+QN(~18tLqw|XQ`4z!!gkbXp0Dlm+^U!RSNuq zQ;JPiF9KPe=0+fL_)eX{->N}{wBqvKC{dgQa3~Hfr**N@xcDhYSezEhJX?-*6#7D2 z52Wi_5Y@-zACbP$^Rx;_;JjJfpVm+s|3I#eNu~$#1qV6he&IH!7UPT+0UVDYzPg?$ zf7_9i59biYO>Z{+zK&-e)0DceqZIltbzfJ~S{fqy6@&-pXU9PgBD9_VcJSZz{I`|= zK8e5Ji10V-OnS5M=J3$ln|N!?qPJc4n;H5W-ptS{{`)-t-HE>xuY&HkK~>c!KrX-l zWf9fDt_qv0pj*1EJUpqV-29arxD&h#So7`WW{3!Wx#>9Y9KGZ2n;x+C2v2fQQBL@k z8woI7#MuN*#NqHqHO(wH)eE^$T}2fri_rtl#88t;P~)oWq^x*W7Psv}Q&a*to7)b# zWTR-&p@t1s6-RTj9apLy({$lWUc3g>ee(gpTbdH)4+wtWC&!Km2)Zq%{ z=C9po3_Z=2J#{B4st~clgqGveP`2z}uIxZ=dIxglP;=2?Zc`1dBdCEB%|lNv!9T6S?2$DF734#owfiX&Zrr)5kmh7_BG1~)$xBxirOI26BMr>qDtwnQ zD>J^cmr2F(&{&4m5T_cN<&v*ut}0`lTZbmj>j<#7GtoBjNSC9jXjuVzgGaufA9*wR zk=w`*_X>WvTIu23g}?HiBJH4TZ$aobIqC*U*MyB{6IEyl-H-B+`Ed#6@90h^D3Fpk+M3)8d7?Y#&8@s+QAg6AyGr&JTvudBdg8Kl2HV}FQad#gJ z>%CKOUGg^1_$m!CFl(yNamK#-M$a&-geBbHm<35 z*Pch)u_i&&aWtxdE2o(h-x#dunXlSBNiwo`S&g`Gx=~gw;~VdG58H0yHyWbI-h3W} z^g#2HZ>DGYlt{5+Sy@JP)`ubU15+=BFr*R0XT%itwDB=m@0rBZGXLE`fT%7ndDB=! zC?uO@^MH?L-bJXYJbZZ}9C`i;z!2xdKh$(Mu_AM14wYJ+2$i}a6^}1td_TbaG1Hem zzA35kapdwT)3ph6t0{hS{kiZ-yaJzsceCJd^-0iuZ2R?HSrEpFn~4t&oMO;&1Mmv0 zf_v#4peoDPQ<=Zv7^RtipfLb~)XOQ=od_J5XRIXzdlI_wO5KUtgg+1(#1M=TP?bO+UaE{jh1fW9Qo0tK{P`#JniWiRp2+0{h zuP_C7{F0|24^yCqug%F0#T{5lLBv6d4-F1C9-+$7%>-MjT*0s@MzTmaVGKs#UboSvo=*m5uXRb!E=IqO1_7R9|#H%8i2y$>@9^Hc5fl zY8&0aFJ)fvi>A2kn&O;_a}uiI;iM3tw3w zU4j9XM-J@c6YBPd9Z8IC-UruNN)3e7gQ!|8gS#zG{NMnNRh4XJrs!q##cn3Fu}7S@n*Jrm0Ru< zRJzPi#ev#m317hjjA#{GJ9qIF#|KaNd*PT#=) zZDIcJ5c7ZiPTr)W>9XeCBXG;~7%XftdEXCW@}6Ye(KQ$N@tipLBISlhX}+k}Z~P+F z>rTA)>9rTVegYYi8zp!iegCiDeiR_j?EiK3-2c_UeO(VEI=%nORHxs@d!J6zZrOqi zp8uoo?{nMw>2F_bO>bSp+=&um8Se)Bv?YO-iaPJm1IZ3QE=!_N+7SguF zH~5?Oh~+TJgJX|L4infLZ&JBWuHP^hUqd2tnguqsPfo||Wg!n8=Z3gLGx=OMM0Zs= z+x7US;M?(yS0gqcw140kJF zxnSu%0tU#}^~-OaxwW6k>AQa=za?%asmYE#p|={rb*aPNHG1IU1_YtL*ywT9dm+p} z!S?`h5(`F#Cp+w^G6mI&9n<_={SZhlOoM(J?M_(|+(*0QLl!uVc6aCyoi!JX39Vg? zQdzjAsU2(}PLRJgH_AMU!-KYl%J(evLXuu%Lp+ldGe?+CiuX%W9CVYJ%wN%Tk~fs( zxZC?_e49;6vza(&)+E#z>AWpiij_KbpA!!f zQ6-Tv9fkSe&3DEi#p=^oG{9lFzmYgBIyZ$59;f&4m4_H5l!s@#(lPLNffjB>0Xc5D zh5Er~@;rVReEiqV{Vc(O@xxn;kDq0Htl;y`iJ{nq=fp22b^>&C{eOXuE{3-r(H)z3 zP8^avP7YG?JehpbIdK^EbMm(-`A0Gt0p&-%b{T$G@o!@-iS=-g@CV|Dw?REjX>KK+ z_(FLI-`e*O-rn~-^TMWn2G@6gN#uv~_pva1e1zqRj}V8{wGM1LeQ_>x1AOe?gFPMD z^EF(HsJ`|-$Jd`7%?DvnhBL#c8jM!x;Cg5aojOB;38ho61d0}bblyJ@i zGasIu1AhKkb4oStX2!r;zE3CqvGIsTEwKu0#waQ?#3tfEfq#peJG)^nPu=v6jTNS;1l4vIML! z2(uUlF`jwAw=acrvu#WjT1$z2KR3zY>)CgC}d=@VX+8`hyfl0M=63(zNOnnj-0zWbRz zAp!preZs@fZ|m_kliynL*2r&9;%x=Lt;Sm`zfnJT;q3^9u$br*TwBoSP=lxvJwS@_ zkXO7OmY3O5v<$6^l%x#!t4NG`ZZJg>&efx>nszgtlwemLEzNS7DlHyR>lCV{MCU>-lb6v%3HC@|{{vHbU|a(O|B zwm6Q`?xyF=8|W$lOXDUILd&JWuS)atfS>FQ^WWhcsP0j$4|rICSb9_NEif=U^sxMZ zTp*o)!~(hDS$xrhk3?_iz?+}n*5hp^zqR76k>8%g+X{YLjki{Qqu%bq+a$64k4HYH zrte{`&|>hgXq}{|rMz@*qBhJX1`nMri_PVnl+tO#`he93j(+kolLe`49b!{AX&*xP z3o5QwK{k5R~cj;wuMSl5DB1qP}U=<^*sm_4>Mm|oBAg*FOTG$HCEpG z^t?&j5CYvxK`7mW;s9NWGBBJa?uCE9`CAHm|2)?pjXbn$nTOn778ieHBR8eLr zH+>gW18bdD%>bWMjk}3xif#~W_%B~?3=AI z$%Z$g%^0j+H(P?KH4SPvrJazdO?gXUdM^9-FXSb_v(&=c=^6$Q=y1A6_q<785_QE$ znwOb0Pei-UJDS4ZI-0`!sAEUkjp)%7{>@Poj?J@yvk*F;M*T^lP9_DoG9`@QC(2$5dN9Yn&q~^lgu}EstdB3sxZ);x6XAo; z@`te=Dg)5na()0bCe`;nv z*`ub}mujM_5HXXe<8D*+2$)y?$YZLbHSr@PUXC!!IL8z~5m*%wubQu!RWL8W0TK4J z2udurgAGHD^3ZuM<)J~&4zW&os3Mo*Bhd?jS)et>N4aSXWt* zPo3(imWTwx72|$g(&lY*R1}Gb>W%OZ7(c3E*AP3Sm@?YzALEfR)P6@ab=AAh{xK05 z!|ZoIdYeeskW9qQZRa62ho+fd}2J^f^Q;Anh-(N9( z49%6tL{{?*ARF;RgtUf&G}HyeCe8d|kt*E2O?5e9hbB7Wt>tDO-Gmw4!)o7F;~P~9 z5xBUJkW_fo75}*}K1nROvGw=EphUAN{<;`sbnjJ`5?Im_?ckkNB&RcjD)$Eg%i<;N| zkiMDQyB4zY7W$}GpR7Ep1qDH~BCl7i^WZ`vZ(&Y6VxE`Bqvw1A!0cr!em5-ZDg1i{ z{|@3`0RVRv{#}57SKwa@{`~>}67ul=?(1uc)9&kW$O*l>6Qdp=zRE=CEEkTqihh^j z|GyCx_`JaJ8x&>f&uPN6ref&1t5FBot2k66plnKFV{-2&<8jc;k$2wh{9MVg^#bq> zyc)l;i;7hf7r@M->_UoaF7j*QeO9&)z*7|MvnPldRG%D=7NpzHP7>OZ;?7-~ z)567;vY7PAjZYOQ=xd7BRwkz3G;xx1`jT;Qh&QOxblp^DPQS@N$(+7q9^#ZmtU=|z z?z)r9z0oP*C1(`nuo&zG74ZN|?ikSdKYDSs0scmLY!Hlc;f zGA|z0AzX^{@Lzfu)&qAG?m{N%p%DPc4+jGT&kjzPfCxaw*UI@%7IyPqD$O6Q^u+u*qXzcTo<$(5SFf8F}U=T9mS)c)J>C+kb%q%Z!%#ljwM!os={P)7slfG*FI9vX9O45b%Ukza*phGG%d4k!G^{$-=S0{E9b8xfIf zD~NyeLvhs&6Yo6I0-Nf#zWZozsZP0DOm!LMSyBs(wWRQSVHRtFaRYQpne~z#A7jSy zPrTb$meCngoimpELm%^^$zM&B*p_sVIExhe5NhI%Ms%=Ovg8;@{>757F9Hvup`dYlm{G~|O zX9*g!9yVHWp{R$Kks}&??#3B#VQYyG*ha@gS~4Wg=xv;H-}BH$mN4VLKBE+E1z`^Q z2sK*XyazE@AW))cANAzg02dRqvy0_+f@E^7#z?!ApvY5*&bTcu|eun^JxL2mo41OdkW{_sXhtE z)>z%pzLOiC`TJgdMXW0BYd(jn>f6_oj;61v*i*sVr7NSb*n2X;ZB-%EC$cQ1y9Yvb zrRhe9%%{%UQza1XcaL(@g6zbN2JETl%G(^$bV})l`X0zHf;aIIVgsQaETDS|==LEt z8hf|^FY1#-;eAE5!wBC%ZOd0w_YtXU^{Y=={oqkdK-H}08RK|Y-uy1cqI%cEM4@w! zbLGv&_^|Qmv5K)4{TE9fn1|nD14IR|1b?h)3@xMVNTH(RLSLKs{jL_w5i%8y2t(75 z7%49qd5e3T@fI(ca^5|BzmvItZdrL0T@O!n;-=#!1s|^ReX4}%1rD|+em}kuooe1l z-Ma3ruJ-{tl>H7J%Ja9DQFCW(2y69Mpec{*Q9a_t^6a6&q*6a2VIF|ehCQiwH z{AU0`o3NHP*dk38FhEYw$0(Mv%U?AsY7<30yo5=j^{ao$U>0O2t;VCFDD6hg$8B7T zT8v0&ANXxtQr(iBMPxx@5fUrVt{{&)j9nmxpnXf1Qn&MSRpByz%6-j|q3CyLJ)vWm z_p=f*f56&+&_gi69goC^aa*H@mlysQdShLI+YqE;6|ZZrR!hx>SfKFT@|sMQ@tGl@8p81Va+i(;0uxPYQbfV zYX}`?)t|1<3K4Ef!OBhSgLM*!CL80_pLh;|srIZhT8YT~c%2@Kw4kPiSWfYCjzL!N z7>KYruj@M4%bvrGqB~I>Cx5P%Aehpgkv%~6&;JnfgNS~+G;t^(K2UD@BY0s5%i*u( zq{gYZsHk&E#61jJp(!Tkv6bi(@J*7*G}aSd2du`4IdHyhJqRK0H~;5?wYWvm+Z6;Pa+am2oBF9AVr|)+P4- z$Qq>kq$!@N?buWGzJ;jQ#7?g}AqZqvs?Z!bQ)gy_4xoir6NdQK4 zHjdSSG6-01IuR~{fZ7YB-rv#$#S=WGT>QBjo0fRjrbkgq6*mO%JKh1M^>T5~4HNOBi!9hf z2k9a}G2W)`%R;*7^hT<}HxYVCD{ftgP+La#XvO#$QAYv;uHVC_QjELt*WN83Cw5B*O>knj;Bp5>YCN%hc1!O;Y`f=(2LUkMGp-dMQdz%+qJ7-R zlcS0~1a4E#jvD90a#Kzu?S8RW_3n#rE%)uJJwa_;q=Q*~nWwE{9EBt_p2ZL6ULXoG zFHn6O=AVUk7#Ps&I6@^!3j>7pC{oP|W+PMjj2mqG_wo$;chyIge^{W3^E`_7ucU!N zKS+uEB;u?Ye6}k5lj?Hsr@+YXRqlfkd|X+2>7hitL>!*r41#Zg=@{jwW7HhFg_&vJ z7SmOwv0kTR)HZvzmbb>%+XO<&b6YT0x)m=G?m8Of4zz#0>0`qEIV5U?>Dw27mD)JWmPp9%u<%URAgs3Kt5p<%spIQ}HWU&Q3 zG)H<+1)3vtX5;Jh|GAnpM_$BxjkOzjc&WBNnnIc*GYU(=z)(r0`D83mPmY3kMw8x=%3e$BdAlNa81g<54hEgJLeuy;6fB<&$O}TDSen7XVo%>ZA%_K%C5) z7pY{f2V12#fKlk9Gv!C=3+W#XnAapfYHM;JX&$R6blpdFNkQii>i|l25deOR=KwsR z@dm)AKI6gXi9K75r%mS)^zLbLDydW0lx@0iE0Eep-UHm;g=h~-=Z$i#FJmKRS{1Zb=oIuE(zt2?b%iu0wpV#y(9PxC9kEsS?WoJ6aW+7$hAPnZ)xEfM zqwt;7<*CpRtx&}_9G2|3ESRN;?uAo-CuRwwgD<#2C35lyPZ+)>!bBP5?UzPjP-JO;>#TDPAj@@n^ODZp<*wxoR8oYHRhXWcM zg)?d<+h{jD&jsZlQUod+2llY8f%^y(3Phyxm6fs;f3fuo%x^C#W3jlfC~bsP*}Sl* zuC(9!-|esd6j@(d$iu5tk(e@$(Vh|MMs(NCUFMW=YNr+N7~5u+6^XLZFHO>T8`}s(Lpeh}-;PRcr#o=9nsW=*}H*6-&(pygO_$6W_jG^l+m#jnWz6me1MbpnZI8AFOEudA!Rj^OEfSmfagP*ptf zd$7$~xX~d`66vUwuHwm9fQd`w?R=Phd~_xZNMzOUtsK9p4mII}V=#wqinZFA_~J`e z2Mms6cVMUzyO*{}^1uG{_x6oCdi}jQ!OyRM;&pt5^iRe?ZVISM{jd5jL;r+_|8LVj zIa2*E_{T<+O7S9`=uZvb)QDBg^1Foo<>B=ACVrLlzvyl}F@KN17kEG*kf-MSIN(I+ z0KCG-Qv6xg0zESQOCvt<|AhY6RiA(UMtqg#Z$05xd;k3lK^eaI{Ehg(Fn>iD%IDpG z`Cp~^+nzRmB?vh2#pf^o-#UM`zM7wzFE~0BuUPk2?ThbU2YqW8pVY%JcAGVOCP}=a zl4xff0hxcBly;sqx)qMS_(9#GBsYqD0iSWcNqoyT*=;Qf_vVBq^*Yg_nE{Wq{yw$) zJJLe>kBho41!q05KpHOB>PF&_VJjpoDc*~Tij77(0x?t+scUc_l2w~4^${YPFRN~! zcFGhN+ORo5zUu=U<=r^8t!mZ|)2Vs2TR-80A+_(+ZuNuN7+tHd&LE$mnW32^nr>)B z+o_Z!bAp%82fuTdd=dC%^{M@N;75e!%ZJ~@{}uT4-1tS{*V?D{=Yby~`Y#`TBaRk+ z{p+t8ks|A^a+bQ$-G1?pyKcrWTCZvHFu(N^Nc8Ejnik)>mo8lRM>y8mMr@@mX=1#D zkd7KpIKSxKK3~^7aZ`r84fsm+ZKpQTFiB?U(1;VGD!Lb(uZz$MfHff4F+U^>0jJrB z1n;4e{Z+o!x)TH5Rs>Pn7VzQLXospyg2`D#M}S6@v0i@?Lf^f>at>n!eoJ#$GDJo@ zCHw7`bYW8FQy|h=UO3Rb`F8w*+5zmfzNve6*lj`Y$+ul2^O;F)_>gr2HNA!Hq;-d& z2AW$@lwN`p0@4Rcm9L|&5DhjWG2o3*C!@W3_2#d|h1-(-GRa{+l7&;Q`G^aDMVU76Xqs;54PAieC`P08SO2I-!+Mi% zLf??C5|?AKSS_$J8-5!5WfY>X85~%_cBI4*n$0M} zi)6sSclF`H(vU@O9<0o-d7EzjBoEbz1#7z5Pc0eU`HJx#z*HgbF&a7Llf1gGL$72E zt$Db}%U++-^IQY4!LTu3S-cii)cxxnwzpo|#?ApC_|)}B072)#5M0<_{r(`xlWIgh z?S+(ZA>hPH@4ANw0d;in03FTm??b)u&0!>_&xiUM=wUi~uy03)p`!rs$hs2t+wlX) z7J*Y2X)J#U^$_~0D`P!Z6_J6Y_HDc!MXuW&8$%^eunTShgE(U}A^$4fr6ZtN2~;Re}7!K@W^{D(B*xR)lphm8SJFFEC8z-A8$s zvL2?=bid5Y8!q!`HY_tL+gu{^Xi3>xql`&U_z6zBtz@K1IFz(SB?gtamG18gfMa#2 zE9CL>j>0T@hSqnZcru>#@ir$xzARGy@rh5N|8y~BrXAowMl2|s$;lb2mVC^}l&^zd z0%@{YCeoLYtELYR@Y;2~XT1*(fOZC6-oNitzYi0TA!m=XjWU)FoP)8rx^oxZ%VCPK zcF6h3i%flKPP44zp^wpZAKuyr(c1TIkc>=OZ^q&H{yLGl^nDu!9>Xh(dc&aa82GZo zh{VPA&bK<>Au6MyPeC&LEljp$HAkS^y+y7m@d&h=5Mv&civUeT(!1ruuHrh(k77IL)GUk2)jXf3}CbRnW_mZtx;$CbdlHns3k z?KucYI{}e10|>tsew-Foz`3{)bK`vt8cqmUSMr?Ys3CO8shZd#I+0=RxrkVVR4tUW zE0#P*`CE{04~YVLmQEIfb|ZC=l_ zF2s_{->!!z>8xgEefT3+g~9YqRxJsGkSKodiBLJfqMt&72tX-AQ0p!n^TB zTOZhlZvZak$Zu5#awc2_p7a^KAU?~)396D2+Hc_qCe9jDBWlINy62hp(h z|1deM_`0h220HEb8Xt^izVHy0#1J1lQx0)aAUQ<3kV_lklCjW-pzh;30k?HaXF+|ClKCLl;?5Qn^To_NX^AU~ ze~rJGj1_ob74Wh81>JdDBYu1TPKS379p2Yr0r*;z=VI=MbuxOOiEfx7IF?fVu3h+J ze+>3O3&-n`-Oh~luZ!Gl+RsC266#%A%hdq_sL|%Ot6lq|`4IIB>WjDHff293qj$$N z??*Vc!?$PM7#!IVTacW(_UT>=jBb!&caBc}+*o}M`W%B_ZXPTh|4+gqZKTiO_>W;i z)x#E0$YJ`?4yx!9z_v$GcwJ(xGS)AHrWr991XyA)s4WsYG!RCe2Lv+*AwYVO8b4ii zW69h8*Q9q*NqN?EV{MD~C#<1KTXd!_4nSWYuIBM8APNT|z*POnD(pIsHNRvZbY7pe z&V{yke1i^0u)YVV(zV!6f-t?c@{D~5&9+WZKwOra& zgg&Ixha#&7>|u&_Fg;F_ZxvaKUhAEYE3`aD&?3}8A4H#L67XtD_BX+%S1a^DH+}=) z4ubqMGK9ajuo$K+FeyNxairmS^|#0)jK})x@gxP(#I7KeUbk4YaQKKF1LvLb*kDcE z#h`B{ctQDl{$SYrQ0$$A^Espu6ITsJgIwkLHS=Do0N3x#Na^8kxc;7<)zVbhh>j>t zw1S~OpM@5{sIrOLD|RsoT&9+b3w7W zV7zWFC=Hmu@FVbZk-4DSTreFeSTVnti4?rfF&A8JE|?c{>H_iI!Zah+x?+4`R+vVj zi)Nl{b7DuFhZX~8%SlzU;K&21mGdS%O6!Ka+X5ltvUWe=ezA2?fL9Eyms@E4Rf&$t z!qrygMc8u}^oW`%aq@)mt-(QRydAMa;4^TMYqB}Mm4a)>TQ%P;)b6#d6tVDR^lc}8 zStrZBQI6e&ZjMLaD4|?@v<3tz%R0Bx2GP#?ubcCV%z5JgqGFh8mjXP$dzHi{WgZ&x zYZxZgteVwMy4SK#x;eT-GiOx`1URERP`JR{*AG@h>$Ac53-*qthVyf6 zMXvo(4|vo1W8`9KMisO2a#4A?S*D3*!c4M;FH%3k8(~7N`qnRi)71^JY|-9Q7AR@O zH}Xsz6J2H*1^6|~@`)gpY0d1$;H18p`;xnxMgEyxOpZSW&& z{p>vI?=>D`MykKFFaDGt9l#Iqg7wijOc;`@kQ|tGNiITk=cz5@o$<ghkX&qcGAX1lkv6fP6p3MH0VG4Xu7%2xv!ML z17*}e!)V}h-`iL9KJ)Z*?t>!cKpIMa>{GuCaL!N@OPu?Fo}q}zj*210gA9))9XLWk zpeH_pfSL|~u!ZkIG)ZtzcVpOx)2$lU--(d$!=w zXs2GrMjAct;4$GJ0>X%oz7sso-cS#11jf2u7y?B*?ZlIbE<5su$M!zQ zp5I^Bj$&vd`m3KgDbT_st+&M3$_3;w8%RS|Q#-qG=Q*~uD*h}+2x{GcWv{gair`MW z0xY4Vu0(NhHc;tsu-V$#P3c6;`3xs1G2H?6InD9?C-k6`97y>50)`5F$H35(G{>YX z>rv1sBw}e@O{B1$A&3^mPUDTi@pZ<>gM%kl!Ca!C3bu1p%X$-Hz2;UfeKSgjYX-+k zjgQ^IlFVXHaWS#8;ib&tlIa9@6?C1L9dG3f8^w^+J06Y!4)PDxA#J*&IDFoA!n+dJJk6{~Al5jL`{NfCLi$44h91|eDYi>iLt#8P~>}usw zuKFCKJ12Pc)8K2MT;gjF`p8)dhL!r4{^jkDQ{NgMjr`NCJk}a-cXk^+&TIc3k3Q`| z1L-CIC`$@N^NH#{gJdOj&2;#EiMr)}4u(xO`!jVR!6ndlI#y{*T&M!Z#$ z<@gq<)J^7^<`R-Iw-0hiu@JC!V%B*P^f!MfofX?o7G?PKHz3oAZAhVa$d8C26(lsM zfjb$G_|q;Tqh`Zi$OtSjl7GWbFe|$>@u%(lI&Y^Y{_;G(HsFqO%}y>~Q=oxgoe2p8 zxSQ^@^|6%K$87_zk8PF?VJWST7icZW(~LfW4|2-lmoYb@5&~-bE}|0HtrD8Wm8@Aj zO}Fai_C~ywy0|D!F_|ly7uh7IPyLeq>Q74}In(@TPE6C%?jjbGPwtGnh*FAvM|Tm6 zcOq-RyNGET8sa6`xvTeGL?zut+_sXc^IgPkd+9aQ?FCe7fA9owWju6&pqBe{Iab|t*(uz z1+U$Ui_s!f_VKswyYdu{DQLu}Tk;)(`4W2_>hZ<2j!jPMD#YhxHKj?zaUgVv%>09s zh+FB925-$2*`5bWtVVx!oy>WFD@i+H)~pL1B7QbF1iVdK*H{|cp}|N{j2`}^6Yurj z&>=!GL9nasv4YScvQnlW(U@Bm>(ihEjE_K|Qv80A-@55l+erCRKoi_0IPkg0;BXD- zxE>C`y0<>1s2Js_ZgdYNm%1y%t;(@kSWTyFM~&JmjYBRtAIB0i+CH(aKx2_z zE*O}L#dLR#%g7OMz2SU_ud_KRrlWOvIf5Bj@;7QYuWlHsn2V`YtO&oA=1$;hyIz#* zk!yrlE`7)vjIW^lQ$2BK)|wu$%n0|bdbdROjDdN(Gv@5l#Zp?EIK1d2Zd^=l$;BIx z5)ouLAzuN$+M<%pB`@XGBnfTmw#!uyvzmlG zO(Z@^+S7EM0MF7XdK*Q*C7m>Q_|X= zoz?X+T?Wxl+CYn86;B9}Zk9CjA;P%!(tH9I64D5yh(b!J8xO+cNR&k2F4>E_m{^1& z8L9vaE3(1i{edo9qD;fFhZE!%u>`yE301s{FrReTU35-W;xNZ_*Om|AG(QNpeYm=! zsN5Gp%1&?@Q`$>Bgm?y0JQVhjh-@DkT~CcxSBZBeiD1b{p6E5TDVu4NSv+)07m^bkq@CAeLxLynPSCj{WX5KeX>YN5Py z5fF~@mLd;4zHOR4N1A5W=*}q+yajBdUuLMNRomtl8UQ~oMPtDw)Y6;(q?UqJ1GiGl zro+ir@RKZuQ3uX-IcTOnffbHa<-ZZr4#H!Ky#UmW6>FQSHV_X216A;##E1~To{USB zT%Q^8v8P|gs>i)Wn*LWt{<+xrz4C2))@1yLYLcD-4A&Bi`!j?o#9;t^--}4X;A=Vb zgG$R8_oj(1U9JEbDI|*p!8&v}&*8Cnk~ON9fk}pQbwn*g!??%dtO$yGgkVpcNp!0_c{7 zI=DU1tyy>}FWIA+b2a?)>o5Tyq6=uhVRqb@nfv+68|xGLa3+U`AeO&@0^g%=w^IqR z+?MKYwq6FpBJ1;HRXE_GTi}5C05@je4MVC(j{F1W+?k-OBiUVZ6#?xeD-M>8f_x|)7GVE-Ullhp&%2h76ace*Y}vpvR= zr4tsm=1(tS^dr6Z+tcXZ-|m3y4!Fq6GV5pz0w?HC+YkrekD&mPy>2&q=~wdo=Ks8y z4)fG}h^c-Yk!i-Q+b+doliRR&J`dw83}b+N9`kL`Xc!=$0kmMCe2TnsLh9o*ztGQ2 zL^{Z2GeGMU@FHwxNtVMp*+ajCNw*OSLD5Y|)I7oi1toiEl0D*Tw|FHWc7%g)i9ab< z^F`}MREg{=#0QAH;{-E2Iy>epKjT&CbF1u~#@IDj^LD7dmlvI=ijAuGWio6OFLv%0 zo8z0tZij)byK}c{PRbqjPV7O|@gEQh;xp=v8&BO#B|9zL9bt0Zoo~mtj%}w+Q7{ai z^T}s?0`tjdul|aw(0R1<4Dd{r_y@XNf-qOq<955p9pveOdh9ro9=~+B9-~wJ^!T6g zZDUz8-_u`@b*Kj5Kg1}I1CjUNx-kJsF9(lV;{v6!9={JIOeZF3f)f$xX(=Rha&waV>pwI+ET_EF)^=E@sJQZj6`XKvu<=b5_vaj zAv$=kL(Q>N6AZuK)qgGFa>xu+$(RBA`#bpiW6(rSrSS-8g`&@Hus>9i-4r^wv{I+IdEN!WwGYDvXBbEg(SqNql3dlsd zJkMa8!As1W0TH2P0?Y1q!wcf&LeDMtZLZ6!5}*znX*uv}$fo5RQCarQ%Xho#JGAIq zFb7w?hZ3uIo4tBR(CYQm>U~&sG~}u`Zc-lW^q>0wvG*?UQB~Ih_e^FI1~NENgT{(D z%AnDpjSSl0P|ZnZ-~=WJLMg4)(&DwWwJnB>Vzu7HNg(5KEWM>ZdcD;?Zm+kst-aSG zqP3X>NC0`p2al?Nww@4Bd|^OjzW>_i%w!UX&wG2n@Av!oF*%RD&)#dVz4qE`t-bcz zn~XJGdyP%XTV`|Nv%aRe^mclkuZhWvYyFr1;unxU4g|itzE2q+2t~MXPtR}vQbftu zE&Z5fkNRk3i$TYDU1YA0Q~q8V{9tcy274R&V^84zgXWy?^)25C1}o3s(A8sXP(%Jj zt?;k05y_d*Zl^h)33j-UOLs!xcwXMk;$7-xcVTF4~1XXd8y0&5Bhak)T0z)}US+ zJ0iy4!p2KPu6l#>yjcZN%u6?1wA*H0#fo{!9{pT)8-d+2hSo+DyKOUIURo}{O;8jl ze>m*C3W{CJNZhKOzuxHR;!OMX7R_pf&-j{}!Dl$}9fBHrw3XN*(2S=7#Kwgz4+4Z5 zGPxWyrLFLgOAVUVNNBSF(HSY*Oa<*05bNxYcXsVjKk`)a zyjJ*Z;tTHX??Rb^1;NGt8VbO1n?eC5A{z(c(XElfRk5*=rh|ze5Qj)hideYbd>OSO z5pj1vM@!{74gtzD7GOt51c?{QT699f@bXInruI&ybFYTRKhNtw427L_B@QPG$QP%C5*#HZ(4>QjR2Q&%g0~B_b}A zCaZch10PN|tJloHe0j*%bP=5>tQe{H`hX|h$ualBiTwn!3-uOotcY-I*5h|s$|ZmA z7!u!gX51|DwW#n;_oDxMOPlc9X$M$sC5v&6;7&$Ht+7M7Eckx;qEA_OYm(I^AZMNE zbPcsbNE3ni!;=6~`!1JT7O&UvEzU7*eKQr>QcQ>c@ z5WR&5XZA)ryA@2!3CYjOH0E=3?HSgV+UwC;@)H2k~G?FkLQ4RxAbdfm6_3(r>mU zz6tuN-!d-C(u81I9X%_K4RnO`kxYWd;o5lz5Z;pR32K2sf}yZfTvN72ty_p=#9W2H z$hg+f>sWCX-V`-93x8*r7ceYZdusT5HotGV<(?aZ79pap#u@$wAGRGU0OWls)@I`CbxvgyV8DUf`(i~bQ$yTrm-atuM*T-Kt+y3gtJW(5|-JC25)Us#3%QK0Pb zir@a`H^0G6-?i@DSGVj9bAprf5@Nf{p}kL7mYQ^DY`;a=HYJ0~`GO&xFnod*2!66N=ucYph`-Sb+8uA5S2A^wiXQ% zdRU!F97%QOWgh$%$*42HXevq0Oj7B=_A`CWO9W2{yLdIgZY!OFj3UJmSPwf; z56($<^qd_(df?VG(i?kDiytleL2-IbPf`5+1Gg5ksbfg|{h}Y_;VMPD7f9pX*bWy( zT+h_H{+e!&gr4)=emiPY*ci(S925YxXyoQFu+YA6f|r2o@!c*KdLb{R#-mgUVPt&- zlzRZgC1YM`_p-7$pEIyd2jT#~3;12a?{a=RKjE+<-zwo%m@Hi3TS_m3N+H>>@pZ4z ztUqetl(GG(%0)kL!dbiDc`IErk2u(u-1H4%t2Q@$*6XNoZAkCd@S;H09wV}Tfkv32 z9^^ATP@-{WIG5&{I?_@R48#lU{-n%`%hh9l6eB8SwYM*Ok(E7B?1atv!D~X&t@{L@ zcHhWg=9N@>j#N4Xra5S>cVue4qpwy>*?F;FWJ>L;`G8e(dLxS}-9@UFsYsD+Qg`FE zhSDvDh7C`8W4FZSrGQKuRKvZh;m5Wl({Qid@LX(79TZ#M47R|d(N>mVL2ty!^U>+ z$D$u>HP$um7W|WGK>7yK?fTADgvyjRcmq+P?FQb_*TBBs8?1$y-YdxvxR$p$qxxQO z+y+A3Jtu1j-}G88SvOg2z4W+kb*i=^3B`9eq;{8Rmkw;~E+kjlH`N_!T3b)7c7^5( zrP&gOj&76Lf8KEcqU9=*)~sPL|#Q`dNo{q_pn-+ZB&~zUO!K2Zlh%4 zU_2hbDjAN#IkE(4w<|j$2PK8fml+~DrfnZ@SRSXD^{88eH*+?r|Nls2fGajCbXBH? z;Q!H3yy+tk#eZWRF^6jHp}2&%Hq~ah(9FSzcA%)?Nw;Nq&he>y;AD4lmOohBVOT$q ztak@F6Jp>n8Wf_;^pp`UJV&z-eY|17>@;w+e+NKp@eJVt!NMBmv-{xt1PELA=@7P^ zHR--Sv=FwMcb@`bS%^M0!X^U9aS%3U0DPZZ4jWVq&k$x-_}7A2Ddm$<<^nbZ4n*`< zcxNN}A1QE+%Qodx_s~z-ad(^^it{>`g8m^rG&laVQYlI@hYyC{bSe)F^bv%}R|rR*g|=)MlkdDcMFtZ6#Fw?$-J%?Qf-WU$aR# z2VN+9P@j>YpXFw@$}*Vcrot*9(;Q#(-5*>c5>mH2Ckq`XMyNN~Q}~G*B_F-JWotQsFSuuO@is053vWoZ;=5rQ}D`FL0#qGaKo0*gax$(pnb5?S6!nP-LzQ7XgI zq*wQ5oAjv}=10>hF^_Z#5>a2#nb&U3w(DbeoZZBs7%Ad=8d7^oq?Ht_*g@B>F%78D zDRYRlRhoJP*Q@`F-jh~#?fswWJyVPCa`yG!d3{aF_TJP%jgVsWw2Y8q^#m24fCtli z%F+RviA}eB-PUZwK6dwcG-dzj5#t(Rv350RC_!3sige)?A=a+F7{auSsps?N|6%tX z{$J_7Z{aX&Q{?|u_hlRQzukSe;csrYH6y?bYP}L`xw2jhKFMfL=zCs(>nRwT& z+=Y#8HK~{Lqplaa&!yOd)nnP7b?2w5;H+!~r}wX5&EM<_n#(>_1+BzxwOX)}yPOvM z6BYPY&KrBCFTR%BPmZH&vNg=^fy@Y2hw1ukE6_)}G|9S0Jto4dHnLD6ki1q5z; znY&Nkf<@U1>iSo32F1p!14pE2V`cz`6_s^^)5RvwQl{l{Xd9codEKw!yFpg9mEYx% z_s0=P5k?Z^Z5u2v6D!HTB-yAAL=D#%acitHvI|w>d>K=mB~t+_sU{2UI_g((?rL(;(VswCv4s`Jf~9}Ww@MmJ{}OHKxtFYQVbP${mfEdXpQQe$ zGRs1?{+CnuafX}JKRYK2!%MWaYF_=nVo7AsMM+DWK31E_(WdS7BukqXRA!`rk7jlK zhp$35+~RiB&$7wx0;Rp5|Dqy|5;fA0prLu#*^vy*hFjdu?t5O4JX6K(PN^Y{{XH)h zsbodUR+-OPF9t0$L7CM#c|>hvWvKhJ);lo-WOT~G7w13F`{1MDUl=XkQ)1_`Ncjv4 zfDal<20PXGF4y~Ik@YGI_9r(PeVA*<)RH@*hPZYd?500FY>XKjKZFV9pA-aBEx2bH zipRTV+6)=z8TYX6uAQn6u@W7x3;!gkiaNwkKq$G-PoPB9BjqP>+_9ii#ZN#D2K6L< z0$Ij_VVvF=%rL+LyKNYNDX=!~Yr0QB!F|YGo0Q3Fy_vU(rBzOKTd%$Qrb-}SoB17H zTHLJr&<@3K*;MUtI~L+viT|t3bHzm^5?ZzJbdg?eJFV8(1_Kr;8xAaj_EUWNh_fee zT&wtM;a-KaRn`+0CoJ|7;JQ`Xj*r^dVP40WLa*anUaup+$m=*T+UuAR@H&>B>~*Ay zy^i~MeshG^vFucQyH4{uwtdFy_%G6n$9Nq#@_U|N#X02TcbH%Ox%hqY8->$W!AP&; z>QP?D>3*+c$5~#-IcInsRi)IyFL|EV@q2#H@SAl$SAOPLujA_%dL5rTfx1uhIu--l z&-twazA3{g1NhDYrYnKxiBl-M#Op`{)Agr&9V;oj{7kRoHR}4^*N{{5|VIC+H=Z)q*`!ouN%n8Y`6lKAY(rkt$==ji8A-YJgyc$9al4NQ2oixO?D z6{qs9J>kL~nrmCna&abi$vz{T+Se*C3O(^?paEFKS$Zw5v12nn?p?m7gS@xm2Cdux zosbx&s|jWQLQ;2fJnSQ+<01JF7o)=$-@_gQ@k#Z|cIe;m@+kyTB&k<(;Kx;YEbv#3 z+i$f0MUrudFY67xd)p{Zn0?m=nrb2usS1rMx0VPOx;bP-t{ygE$<=FA70I>8 zs4A9gu~Ahb*Ak;@q+Ca)BVHroWxu=el0NT%p89*f?ur%VpcoSIDiT>ckVH1yB(k}W zMA)CM_8s&m5$r7r$80*#Tswp`)@8{ec5^s+rRx}~{IpfNc0iR|s8Cg`2(e$aiBo!R z)mG+%;(vJ6@itH56n)-hRR^0sx8TT04!5tV0XVF##L0E(|19!93=?d2YnkNkaj)SN z@TxnWR3OD)b@}f4FF}rP`80PlImx58dQ0q{2c8yZ@x0hMnWW-uQWHtpQLRR9&kgb0 zJkHqF`n&>KTQ03FslURQ=50JA(K>GRGf_U#}-xAsM4>2?qbY9}c!|~3=EnI6{ z8`HZx3Q9@q`EEzSd2+q3qu_kGUei%fCfC^=1!Ltpt)t)qxlZaRxKOT_brgJ7u9tKa zl*{$Pj)IHidQL~dIJu7MC8`oIh=VepU<7|D;VM#>g)SAtk65$emcSShsMobw#wu9JYT%- z7YI{Y#O6IBV2fMy^cv$0-Pp?n#1SMn5&+;zXE^y)LQ1H32->*M)JO!v^|5!@$t2#> zy7}NsZ>2jX`C*u{GyKx4VVP7An@(ths4gLfHkj`mcG6wX>dE0{s|y?xGfwsvu+q!b%e9DW5!Yg_ z#av6c;;>yglIuuj37ttqXa3MryYoB3&Ry)jW=^2}(#_NDZdQ>81hJWoKGyzJ?FkX0 zH1_|q#URur=b|uqZ>>ps^P)sN91$+tA?LS*c5=YV&WO=&PH+;3G2XXr)!Hm%`?E*q z5mk&6Vi3&;d+3V?8D{#twrFzrg&nj!SzK8=ujxk*F+l0l0^gF8Nv}5UY+P|EV?|=z zuEm#~uqidh$q~-W^(}srrbzc|zGdyfwOT_vO9yR7d`&xeZv`0PXx?;cqRAH5OWR`? zhncJzdr5RI^hD%b*}R@p*y?ffiE{8S5vhi1$p%SHr22^3o+Biy z*UUQvsgBzk33Wvbj<9N5c#7_7Kfn02h%3c5kCr0c)e#A`*X<`{CZ{0Bh~?I|*N`d{ zESZYm1Sk(UaNUTI-~zP1>b=0K0Bt4uLneRQfLzr8Y314r=gj1~9~1}IB7a8+-eR=X zm(?cSCxOv40Jcrciw(acb6cP1-leq=D3So5DM8hCDu|S=3nyAef(0IIfE3i%E$yxF zI^2qRAjA4jV5MF_OEI4lN+W+@T4-{CLg5ecu$_Ujts%5t_g>8_3DU*2~T;%K~9_SaCmuWB2C=&&e7U>{#3SZX`5@7(gC zd9P&qqF(qnMEY;8EQ;RsG#MD|wN(@i- zvct;k*mztb|@9e+C)|&rc9F$7npZnu0m(rY31%0V1tQE8adah z;r-K9#;9~!Hdcx{3b^}S1X-P!s|u1EDBim3;S1(Ezn}VnDZp<|7LEmGJvH~ial%K1 zBQ`i3-&1frZ#Ar&eAZppW!zEu{J%(mHSH;uMjVlGuY?UYOHT93`Ad#UI@QCN)suI5 zxmWM}ynFkIlZU9$AqfqqkDlh*Q}{>B^MN@kWd#s8*m{X3fm1=(o}se> z0WIFzi;0YpO$7^oMNIQ2Xp^Js<#>DA=%J{?AFIbht11C)q2NUeosRr%8{Pm%qnZ z9Lb+aAJwp)lDzu_KC*gC`ln%nBUb2pEKfdvP(F{hgX3%46=+HAT=sIjGKly@#8EZy#VY!=Fk z6>4x>%d*UCLCC2fq-9yT@eD^OoG@4PKSjf<2t41;Gs>3TxZsc(rcK+*z< zQ%H_Z$I;@s0yj)LOs8{seP(h5YTvWW)Gs@0* z3e7how{1P4k5a;M+DZLCchVBYux#N$;y9k65OGjWb^cuZ`96J={p8VPu4C_=_?0G&wT&WLDDnde}9nl%=cd& zBt7%}rGunrzW>Z1>6z~j!>^CuezZRDvty9-%=aq?Nzc^()Bfppe7OoG#$dE;k$z^- zmg+n-*bjcwO^nl?6B)bZJ%#G8wGj?LmDre739|9ZkT&-sY*$KrvvQ)2r4W-;e6#XC zVNmpkOguC@zn;l|&qv9h%$F_9xy2&CqHlbK&G7_OxIR#l>M4T5));0KmCN@Sfhtze zrf4F7rh>>Vs!6H#^hF*!@3{{TOQR8TA8~Eh)9YfpLhmjtl(?8DO1f)1N~_DY&FF}_HtS{PiW?e+ zG-&VcPVM!n+i!05IglcVi10G6tS8qFj`i?0FQ9Ev14xnE=?(!GQN1MfauJ#Z2cFSN z@ZUe)lgJV!AVC)-R0;|zq@m@ip%Mwss42oYfXQFZ$C)eY7^Uuj&8qOK#uaEtUA4w4 z6^njL{mAvqtZiH!_)Kc$ybMR=pB=bBs;cZz)B< zx{Gffe{eX$%Gd4UL2`xE5wZ2UPBR=k%A}NkL8Y8;F@!spx|@tKG+(!Fi@4ozxuk>4 zWd1L#TfYsBf6bDgP=!-BIHX}9V1)6^O7Q?zYI!Tl9JxS@S3TjkRoG047Uf&cL~XD~ z+=ob5Zy$1*PeN+9>;?`8ZqQtvE1HX{Jn!x%Y((1@)73`Umv6V88S2bre~t!g2V4D_ zXNjQ$Tp+A}FvctTb!_4$D66`Nvrwpw{8LM#1`&yxluDCjDN7{{{P}B*4N+DfYm8^K z^pzzNm7NOEtfi?ZK%z{3nGWL&lFw6~-?#incY`Cszxb9n-oss?#DwAg-kkgU)V)9w znqE?$pPa-}B~g=YekGPe8A@}6oQd5j6vd#bgo5V_1?SE;73R*CN=dBCn!9PzY+b-^O&PsuTcghhr2%a*YB#x09PgekT4%vRyuPhFN_`ZYc za`9bhUMOH?=YDAs?OD{1A7DEM)vEJf3#HVimVvn#ppnx&x13PQdtZM6R)FTg0`IT6bjl z!7>Fz4aTdmX0e1#0u(2v5C^t?m;^|LUs%j8ckD(J^B8-Rvtu{0fWErIZ;#)Hqzovf zR85(+#>QHMKrCM@k#S3GD0Lu zdx2i=J>1{UeaoGJI2UaN>z{D$zV_5tzMak)!wX~#&lggfD`S{N5upO!Nz9f-IRa`9 zeJPucu_sqN7@faiG;5#of8EKn0s^$8g)Cl?GdLz<*j-_NG!es@R*hQGhiu zewe*!8`e=B8>zDLoI4D4z_K^ZVYti{v4C;(aG-(mvPPa#^HlO-t@RENN>kVwU>1p~q1$Ec?=-d_jk@>y(-&QlbTe!57)8=9mEr@2l!54*wafs&Ru? zb(gA2nX=}Xzb2mr_)kGj)X;6?kyzrll{l~^$8YmHFnGpq6Cps3-q4L{CFZ)B+$6q>JT%R3{@Hr)^H6F& zVm*}VN$=_AkJZB@7)TbO+sX!SWz;h$LLX920_#o9DiNldEphP&lz4c^pe4?>OMLxG zY04z{O}51GA5h{$gO}*0#Kdjm!*__1ZyQFzt3!KI+3yJQ+C)6VpRCpzSBnA~8JFgj zu-!=|*CifuVDjPCU)l1Iq}I$Mc2-m1LB3xa4!z@RE>^gY;>3`WDWtTlowK{@N`=F) z!n8_RGw%$vlHYv#Z?X`G^`E(ooSJc@XL6A9lV7R-yqkz<%jDJImu`(AyS*?YR)Da}?NH{tEeX4Ls*ftp-+W8P1aIWB;7(?a&CB~5Y ztCb<-PUZe#+d}H5OU6gYC?*^|Yx{tsTH=vy`H-T7>)p#W*ZA>kidMK~gNmS-L(b)5 zJWF(6&3IZ!Re8SUPr6$vhQnJ_n6)PLRM@KybGfrvk_rzQ8;G{o^*TYl z(xZDd*FG)uns3QXRHlXA_r?FjjrcmAhk=OO*jcL2R)}3Toct%^H<_7kqCdN@gfWGU zj^Iu!cimWJZ^BnudRE2<8c|-0yHIdTx1)@?r)#Y8QQE@oyeY+J(M4jUDP4c6miz+p z$s|AT@G&`kuSBlPg3QUf!qmkASV5}|7)0Viz!hN6LMEsNM3bLV;3hvXBn zh=oHBtFgZD3hgnZY(^%1_8@4uE-_J8!NQ0_YrT=I(gh01zQv2M{)I!oQe4EB{1#6N z5gdToeKij>US;f_&Vxl7ubn&iNdLO~IO7ugQ;G_$uiq}sn6>7ZzZzJQZPOC!EjfNk zJ>R#A`R2e+bNKfQt^q(~Emu7gt*^^I;Ohqlek~;{8WanYpB@zplc)je*lu-wG9acS zu`o%G%Q4@*G&pKoprR~|nSy}ERWjH0&47LLx14Nw+s%gt%7;1jqdDfm>AAKOB|qk- zEBnb$WuC~&nElFE>E9nT9jO)j%a;dDXAzC`O9xH2zW>ai>DKp$nF)>$pY{EYLDQ}8 zR}PwPegD%z)2;8nKWMt${z232_79qFw|~%dyZu8xr2T`Y+wC7T-ERM&>2~}3r+-BI z*et@k{C~tgR@$MN9>r{CU^^c^?w19~+w#J^oCO(WmnqE-K+z|o^Rxpp9qJFj zZs0IOt0Y>&X9)De1aAcDG`7cgU4AgtN=W^sv6Cc|81Jal&S`8(?HwYzfR?y&*-ayq zJ}@wkZ}}C@R8N8SL>_p>!t=5-zT1cC#%QzwWywYbx=8jLe+&Z;=5jfZ$ z`#~^0;byn-{EegFqUG83FW+))o4D7cs5>;%UH7^^;TC^%UfYf5T3Of89%Dz<0?P|2tJ`YE_l_t)x6$)`m%=?M0`f`)MIlB)W3( zM{ec8JU8LQj#l^-Gm%5@`uiHJRSy1~g?fLG>)G-!TK)v&&lvf0j{G^-QGdOtRU*a( zmE$h!%hhfO4wC~Fn9~dvJMm7^jhSc(@@67$)WA@W_BcFOWqV1wq3UROq(3&4b4WX|fhU(rnMS_@Pcq1o@sgEa6u$N!v%WTy%FD$r$;)&FV}c0DcFI>3mZBeu5BFp}o#p$_M47tYJ@i*BRdqz2l z^`EFJ!^Udspj73>CAP3~F%PQ=tULHMh(o~#us@(YC>h3OxsjH zR&z6%g{^A3Z8H5@UCC9qIx|tt4Ju}46HtcItt;)<`$mh!Pp-!vO^3~uCeN3Cu9SYR zh|2iw=6sTfvCp`nST_8(!6L)P6mWHOi8cS|#+l*JRCnwvICV~N#lD~!&)`a0e@0Dm zswZl+*J7$b@AXtTW0$IF41bZj{WZo+zaEF+@)Jo=hXz$dzCPi=`Bp6g#Yb95Rbh6&Vxfe&o!*Zj! z@{dyGHAOWEASl3);Izymq+$5BwvE}7l5{)-HCG5{zKoqp8ATOvNqaT3KmRQ?@9LD{ z*w26ABheG%Dw-Vg&~FM@Ik{F2ta~~=;*P!QNG)mPdh?$!9bkyMk$y!m7(VTu0*5(6 zz4J9!(&I*ch*uNxYxsLBheQq^FF8*A!&~Xcr_-h8T5-%~+AbHS$RUDKYNzQ_UOByR zL)nR!9Gt@ zFH|15XLOVg`}96;!lW(2iogw7w8ZgMt00YnmU!f$UqRp#iHM})Eji^m1Zntlbw6?y1{3ecz1T9KjU z913ThXNRdcBabw8OR3$f>{oi`nRRu^Vy(Zi12~G*l04&g%LJ9tP&@~ZRX`m-7LT9F z!DAoDbmmnS`hJ{?z8@cpz7PHWKo0tU+)ZMpWT`=51P)%-7zM zyMEpR=4u(Z9dX5{D1lrHU$I7DGQURMAzLK#N2%MmqAR1Fd8!qvDZk264HSj+ACD&p zApOVV0C-yk-X~i7!+RZ7U9~R*?~mR*7TyuZz&kSw?|FUj9(`FFVZocs-)ubzijX*u z2Yv2*BKq4j0jxj#gYo-AnNuOJmEGO2HA^q-~{Hp*~O}44iP4h1UxZh*D zv)pgiLl*Zl-{M9Tswc>Nd{#UF|M4GGI>a$20t;%T;z(tWNAHMK(t4Utbd7J#XZP_IiwGKE7^>kA)a!W^zak-N&v^Q zD7!UMFMGsEu5g>AoGp*`{pg(gF@>Fyd`?&-3$j~}=VtHDW3*Bz>vP#x=V$NAvUeAd zO_ywAv#&19-hDQEcM;j5lC3=ZYFzd%sP4!yy(b7MqFdFMapWMUzCOXn0v=lx>S_SA5c5qk~+P^2p!D`liZD_{Uxeh_wiND=O8iCkkd~ z1u88uk5`HB`put%Lm}3F_T{R^`ui)t#XrkpSycBL+ji!g|DGv9o~%+5zgM(+6{Ym8 z5b6*<1==6)K zJ=~4Gh%-_s`XcoB^rqOueFox;!Pqic3)Z>2uBl&i3~8YF(sujnfO#fs(DvtbLA)hS|GP#+|2tebilsT>`}m~$id6op-PeR9_lNv zwy(S^A3k1r7Cn`)qys(m%Kkw)b5uoZku%-fbYqL2Z0ThJ#OK+3N~TzPX*&6>)rsbv zz9!`!gO^jUa6$&PPHHsqtH1%C!z{R0s?r%+T($Trw%YfNS(&{m#pR@k%-V|0GFnobOo~kO8At;G>^q4;(CpIKD-L1AtF*entcIBD-Q*6~;onE6SC;mY%+vaPYsC?wD zwo2PA99V4?=Ym}{OPmc*?iHJ{T#fDz6bVimR|(?#5m@f{o?ny|9K?>#6!RjRp@aKn za!QZ59XoS48G+<&z{_d7-~3l5%=C!w96#|k5}z_*&T=-P47H`w;?nep2>DT*M@V51 zC;Z-C3W}=YE+ko!0(O#kM^anByny5BHPODGxUU_n~h@;&SZnUy7*VdO9hd%#)vQzEgN-x0jlDb6!{5Y=C5YOl0JSzy$(U`G z=f+hEHR&-;7_r;2fxe?dITZkKRrAKzr_?GOVhG;pcW$J zXhGw>;HBZ<&%IjAz4zrm{uL6ZhfZ>#4RA*;EsJMl<$vq*qRn?&B00E=*Bs zUy-{gq}47^-A97$>^kMN@V`ckUA`ZVSA3xFP||Ddbr3E1f?65T5}(nGMuldvP-sjO zU}&WbB-Z4K*U>15jwbVsjZqP1L7~>UzPt~K;$K(6UX#x#9NaNtxO(?6geyD2#2262u|@?$FdBmTRsQJ7t$| zhH%DdE$(t&wzYW2g*sx-YOG#}$`cN4sJjFWA$Kv&oM1C?@ZTqS*A z`!f(j#+(O>8X#NP;}<{FRvEi2PV9(z$peAOj;K-yw6|!X&k~ZdFJ)X;Xz|tqYE@G$ zvuVjis8Kf{ioWhPUQhkaZDANeaH*WTYQeu%Y}4wnm%dJf8pi9H!}p#~-9etYKkn_ci@Sb=Qv;aS>{d8b4nkZ;~yh5|&n( zsmGaOo|r2i;q+s~rN3*~FE<4zS?OGvXy%I;z&M#Jz8?$Z0eWA=)qD%qz`CfV(Hlo~ z<28j3Fl78()w9eZ6#2LoiDe$!jxvcx2Rj8&$SylPTCCul8Ar!NH@<*1C~ zt?awLeU6&T?YV5O`6a%QK_Wt<(7iNI<|}D?G#S60&->^tIf-+iTivL*?oxb&4kst& zW$xc&D^=$H2sh(P<7?cU)JwMa>pIrNt?pq!DLgXc&6SE8S4ro!uW7WC`@gpByOmvH!P7w)lKn!;neD6)_~@ zLLMz~=`jmHCrLsG80;9L{Ypjn2KBK_6kGVSy{1gdTeNjt8$2Xg6AOWxt(a;XBl5tr<_-A6+sIRV zR~W7^-+r@#r7sITSE;o~q1&nae%t_-EW?Mat5qZ9|9B~{U_SA&=5_yRE3U^7q*b16 z3l_AX0_1lyf@AoSA^<&VE5K`Jb_B+MO$rByJn*3A|+}kRZb+* zhXlG0h>L?>Z45OQxs3&$FoB`M#vJCr>jQyk@>*1&vc1@~*u)(*-i(S69!*a1M9cPQ ze-ve_ z_uYK^myM0VO`6d@dOhC+H>&*m$kaZHFZM-}O&*nLQ;=;Rt(uj~D)oS?Q&ZVyowqOf z=n0ZdGu8*!8*8-Dol@9(RoL5-t#wp&VwQJb@=2ea4Ty}6(E=x#$k~}%GpZ)N!G_7# zTuvWEjM)?AeDhMyHxHP(-zf2L5V7jcFhLPBf;6PSQv?b4OMZlD+eJ83qUK`M_O3W)2w<&AUMhG zYZm?-MP7K2u@Fv-4+t%k2rWo7U{AzYlN}&SBgU7^h%sZIX573>3r^6CFTKK+94&Zr zu@?Lik=JPGm)zQDdf~k-9jUf_a9D#R3M#b3&3>(L6TMm3>DuJlAh;4#o%q)$PyeCx zo3TkN?9@iL8qd;y@@e8rfrwFaps-VR<#=4{HP_QJ3c|@>sc*swH}=WOk>OyahvIRR z3Z~HF!p6veR=8f<(kf+)-V{z$mIBu3cDo$5tMk>{eC5?#8!WQ3$_sW-OySd!U|WGr6eh074U&T*Cru9NKbthktny2gRv?Stm@SYH)x|7CGDeu=y@P2>?9(j z1&qv69twygs?bx`3pq+hl!TK_sw7|Y&*%s}X$4Wyo{}EO&?jWM3(ABlBZ?}+!BG$? zgjp=KX>`g+Oo8p~zyZ-gl=MUgBh$4ygJ{olsjsi2hZ5HWNTR91Rz7jHqiNY_RXHrQ zPC6iJI=+C=Ii0yRY!vfV8??kH5K^fz`|aRtFu2;npj9|@N3{_07CKzWG?RU-F}nzw z)?6Jn>g@bdMQ{}()_^Mjvt2sqX;bK7RDm6pxkk)N`vpP9vcMxSFo>c^Olk4GtOn|&o zVT_Yrx5&C`woT{~(O7bcxO=aatEiq}I(G*AJn6*2NEGOXcv?)t4yEBUHVF_lJccG= zx6=Gv{aj1AmU1oUik7EUaIFyd7Dy==!MNB~M3EnrhL@w=8NFh&VRgWazv^Rw5%19s zH+x|n4H#R}ClIQs{#>jPx3hm1Hovv3LkUc5rjVnBFnW#zd#?ACtWg6yMVX_tBoV1x{{5w z$(aER2Wl~np?SpINE8`8UCpN0gRx($W*}nqxZpI$6RecDngKi`zZkN;RV0Az7#q5MO=r(G=-;6FcmRde9XL*lYSOE+E%G(1 zNEoyVO^?MQR)=ZUQS)7K6QWsIiF{2D2p>+eApx-^r9Cc8Al39e3#kP=^ zDX+`k_o@R%D%D*gTjHm@*;4fenRwGO%?c3b`&? zr4S!X(-{@nP>zSt>jXtAgXX?&9z=WJ>{J-LLB?QF5_$!d%}vhgJ# z<3LefM`Xf%cdRiiREwwJ=3b#Cws;;Ew z+o=UDwL^?6Ja($Ok~-8*9oAA?Xk6j7Q`ME!;dbf?Eww(voY<-AO6rMr>PaoN*eV#% z_|wuh+-zmAcMMTd`D9BfpKM9xlP#%yvL%%(B`LCw(W+b2{w-Gf>uXh~ zS+;f|Q-kh8NlI_3N`#F-y4sJRY59{Y9y0zl#Rv{fo0ZM;l&e~y_qv9!DTfvEJ*tcGw>98oj^(?w$krWm0 z?|+2}lMvZNaj`;FbToG2fhsx}d)ty)nzLwbIt95uk&yR)bT+M3`FxSK_TbiW)4&h~Mw7R2Sx=DOk-r z|0+StcM{r?!Nq?CaYfi$9y?1dD5Y3O8dlf+h_l6Y4A`m_<62u_l+>=Y`hIPZ_N*Xo zdAbZPWi4#uw4chLT9PyKy}|wtnozFKJ1vJx3oEcjS@QZu0!%acvy*YRuj!Ydga!Wk z^Jv5w_Vi_~8M6?kr#sT^XWa^8sMv|#;b+*D+wZzDP z;*B3HIb``wU$bR}#A1Sn^v2Xk2qq$%MBg+wD{1};U9FBhXKs;M-5Ph<$>JD0`-haz zc(V(SD@XbtN_BSmUsJMiKZbd%9p73HGWqHlkq0hg9AW?^$FKH))o{|Q*h_NkCCRJv z!pW;#8q=+o>dvd5B2hJW3FebCMn#M*S@IWptm4q>DM!WV%Ss-iiCa9Hs*c!RMpf^t zn;Le1CiEk*%@RACT^W^FPpth0l)RLZvo&A}DwTy+S>-lIy(HA%%J98ZB3@NS#6-|) zNsZ}NU}tY>StpUpiTN+0CG)tC4JZZ!oSu&nm`6u@i?fZ@;f^;D~D=Xsvk}z*fKifP-ArZd_QAjM` z00I+`C@Z!HxH9S*R|)#EiPn_4s;4Ojq#$wAK#hL|WqzNZhbS7Rwp_|JG?qoaV zZei;TId-C^ofXd61y)u9~86uFY@qc!(vRs3}5r9k-7BzU7?h;%*4io$JY09Q&hY zkQ;v*t{g2R0p3{*N@)Jm)_F8ISv&GH#LynRh7sw>bkzq5veYyZ{vvS-EEo*ht2Q8$RlQZ*9y zw|Hls(HVck>H9-xsvBH(YAsVagX~JMD9GXk_%ggpf++b{a=^|M64q+2DJ6ZVol=s6 zTEo30$WCZ5TQxFvk8mcxObp;QhvjGH`!;OFs=aE0k|vw)zpW-H9L;huk*F>W!dm@2r?#;t{;385M8 z#afVE>6vdDwfpJbZp-!;D}uz$t= zhl&kU9|dqFe|$`M*XO|d0;fbXSy%Rh8DFLc1=0s!w;)ws78yu)ipw|5v{Jf&mj}p+ z;YBA%CWaUG?kU3y*CLS%xzc%0T5ItGW5FnS2K~P=bIO_JpOx`1(pxO&BC`S@Z8rx5 z=;xV@Dyh_AIuv}fS&fM@KkbpNR8*xLa^U`tD8Q_EV2bPC%L~4j`nE|Lw6i9UiKg*k zn!+4VBnAc86iXBfiL+VhXy`7MnS?cCtC4>c3)gk=%UlviQ zzU%Veyzjct=Q(cL<9lqH+xOV)0%9U{YDq0GwW}bMimgA`=EPlMgUnosC1Pr-UvZsp z<(?Dj{upKHTHKBJ;JY5};A)?*8Gj-UoQQB5SF3Lc&gdX}_AnKwkG;6}tJz$ zp6ROmE9gEv>nKsh-N4$%tz;y}{dNv($xGHS1kdml*{Y`8yzIEEr~+q3MD7tkdo&+- zlX!g9b9QsK7+zTA(3Nv3#$jH(AHgCSIiNC&_g8=yHc`sw`8az-jj#5B%;bOw;Stpe zh2rw#LaCr*|5XlT=o$nlwIPeY=U!N=P$iZ~i*Bs;3xV$fE-^g{hQ%r={UcI^(1O?s zTh%ijigImSB_!1zZ$F}TCU%+Q|HbL3n&2KC8?LP>z>2}>ASf-)usuIkHTFEL*ssX* zYO1V|jS4d5$xMVSaL5h;iKEL4uL+;`HNQg*h)Mxao>(H*PhZmuyn=9j&CduBRxiPd z#XaUf?63Yp3hImP)=0u#cEYboz=Z4P`-CD2-|ctt>hsFyU`d?Yq+X8TQlkJ)+P55^uzCXU2_{#^# zC<6)VlbpJ7k30#;Z|}DzaGGCl@7h(kl6fHKorO62ugDv=J)}}p0$c6?iE}iLK{O7l zods`*vl8OabxSQ*YdK_!VFZs&p91;d2nixYY8$P)dhtVzZG&S_D-(kfC8S(wf|4hU z)jcm~VaTaJr)sNGYOnvCU2jGQ`jGlZ9l!pJD3c2l?^YDhOZ{aW;9+eQr(G0BSw<=c zx9k%YD+B)KaS-WLU-`l*K18DZi1;fX41cqw>|+u5{6|Ef)Xzjw-X}AHJ8_aK!>L<( zQO1RFC{`Yw;JUGloXm>i?meTz#$>0o((kC0Rw~k$o|K*CA=g5eE?yH?luF+bhN6`L zHi6otR8HYjT{}=f6#Ql2sZfTV{_sEt3Z9_AbD@HzF(kF$I7IJo$&J9M;1g?4rwtU4 z91BuKE=W+10!e7B&%FP%?wrvtf~bNI!lfAm*{cCZN`1Ih2oxQnj3cga8(kOUoFl|S zqf=T-lCAK{R`^mqjXef#G3&Gn>@s?&SNBcsMe{J{E<^2b-cl;)qV1RUeM^7XP;r-# z-l7krom7yX>&cD=>=ViS$HfSp%s-n;^Hv-z`9i$t$6?suJ0_F-&sn$Fz49NnZc8#o zj=u7(H`L;>YAI5+@JYA&#A&BDuZ>}8A@I?d1nlH#u0!RT`G@n4mCkW#%65-eBoi=W zAw8xAmotS=d`eD0lU?CxfQm+ZQ@yr3L8T{$|J(2LNV!~6k|P%JAYD7+bMg$=@TWJ+ zh4m4g2B3B? z$uabl2TN!tFu%JwoUC!HCiwNyo6{p!<7mQHu~Ty!r=;2Ri2szd_yC_Mopan|tf6IW z&niL-)sAFdDh1bH6Yp_r@suaH^)D`9nZ_ApRSgq|^u(s(s}nxS$=~Qn==SWDV&%c1 zZkl%{-AOV#>W8p&=t}kEm$hlij)g(3#gXbRs%qgQLsTcZlY?K}HKDE>e=bx8A&C-( zfG_wt%z`SwZG(={(iXadtyd8(sT-Ku-NCIEAJ6bp0H84Rhd}Sd1F;|A&>TKVTvl6o z?u~s1P~9gH6Vn3rj#dm3oDy5usTyK6p?POG=^k3|3A@@NuJ)b+B1uJv^0CDfUlF5o zQcIY&nkNHyU`WH-Uhe?`-=*=|Nsg3BK-spuDjtu@O-*QvZ}B&TLX9@r_nO7G(lGIf zQu}U1`&`%;TTC!7NDppZ3SeGNd`kt6Ow?nyDKK3uPIYPxyIoqtA!qDc;X6{iR*gpI z!nb?3M@#K0vD~SFl}?H85_1nO*TkTac7^SBg=rVv0o2_^;`1&25J0_^PVwrIVKkPe zRkhIH+(@Vq>7q<~a=R%rygpw0Siz|^Qq|({)o`dXR><9~BIm)j*7{Q$4*j#Qc?tAW zeFv{FR|Hxow#IHogBH_;kQ$l8MOK%oTWNS9apaQ4Hr;8iEv~Mfe64ItSr_Ye37f`c(9s^O}+5A_T~XA){#42T&(&#d#yh&VoHdAOm7&!bQA5#xFCSGsGB zmkCyK)x>oD7Xj84ac$M&>*VaEo(JJ*8V5krg!*Sdd(*JiSw70(WT4K;6;BRZ!4v+- z?s^XexF@D#e~xVU8;zX}6YM8xF4P`Hdicy3wY!Om{*9pmGer1e`}Z4{pZ$)vfk2aO0924{|HM z&XsxHOJ!oCQ(#oL;?l+)v#_n&kLl+7q%JEWYbZWG^;_6lM6UfeT7!m-&#>*K`qR#7rD9dQv9q zy?-0RR@;Jn0>(2i3*4*;&_5d@#yiAn7QS^3yH#KIWFGQzAMkX@q`1$q?}>a=)5R$u zDeT5$0=|j)zUEdSs(Cl%5FhF0|CR@1t;*2NdayY>zS}tl<1zD3aUm8oH|x9vax^xoe2sg=YK&urOE284uSlQiaJ&X-Mn~iBuxI!RA(EQHEjrY0 zY>*?W^?UWv?UEHA)s|x5G?}@==b+4qZJYTld=4viat|k!=Bj(n#M)b0)iUdxe0k~C z@Mw*y^@_HHYY2ha3n9W7vQ(z1M)f-O z^(^Ux@euQe#a|O2yCO^*Ei^+61Pxr_^ijNUR;X9?oGp?Z*-$g@jrdW_H7uxcqO7o& z>Bga(h6G!?U%+A(Hmo?;>QwLA-;fW?g$iZ&FpIZ%J()$67wB^kUzV zN*;)bk{rK6#~oj43>({sp~Z={LX~quo9gn)HqpCA6yxOBM#{9trGbMvSn?PV6$qC}Q6QMrw>Ke-3xB?v~nBJk-TuXW+p&x$Y$2%Dtl! zCpYePV0%dI9yWBUThU1OBjgur=|=N=m$7@Xzy?A5G%K+aJP6=MFP)z5dfk@FjP?5H zl#J{-hC7}p2(~7tbDZyHVwvL4H}WF80LE6A8S1FN(&ziy|(`10f1^l z-Q^ajSm?{^PO-rBtu#jgDrdr%YeS>uZhDvrx2F*MqjZTPaRU!M%PPhi&1l#G9<>~{ z+hus;S|WPU5m6kalM-VaeYaoD0MW{}s*~O8FEZNtBg=gk$KeUK+;`O#GrT~Qf?&_E zg!`_cv+iO@?yMilGGUFxAXWOe;UykuyBn)%V(O9D8*-eCK-PT~^kb2W)wL1BYbYWN zy?G-FZ8ckH)c2fCD>P+in&35J!*Cm?_e0Osy&QTjc5!kV{pxF)!FU>gvTWbFe7BDy zSwKy=x=)v!cPcU_j$Qna)3g2fk>)R=gp+vw5R8^(3@I9PW-E^O1R?-ho#pD{zGrp z{Rci&0lQWk9;|T!L)mIqCvJllj6G)<%hXs2y&3yjCi{l6PCFU+^?8q4gXwX;#ZPU2DpoQ@uflEXDE*_;hE4 z*2LZb@K*9=@*Ar;?HLCMV=E|Qva~vzfq(`X4wUlz2)Xg*L1{NH!ofcK{hg`_*>bE9 z`ehr!WgDsAWud~@MoE-WBX&y`QmJ-t!tC*6nk>)csSM!wIJx%grbVvrJ0J z284TKgG|1%k>77_#a9#i!tii%Z-#fliAbao#wT z!&R-vs$}uGcr{1MT9Z>Utb?htYN&g$#s1-Vi*M{d1Hs!CR}GCWW(L~hdPxhtAZ_>E z{uI1c<^)f~mG1tLdVs0YBO>rHDxXx-er9ym*UJ8Y;@?BeED{zA#stYMnT%R^MiSS) z;>Ae)-NS?wE_Z}oYenG$xy6e$>n@xq{(V*Q`r%G3w7>38no%Ia)qLfv48BArpDUik zN@TK+8Dj8D#0szjGvRn`-!Bqw<+kkLbaJCB%mV2z!fbRhTFK&;wN5~DBM^DD#$CPx zAg^(^k0pZOPWX@5y4oWJ4!)Z(y*D;&!lEO1M@}fIFJc_VTfOnauDZXigg0X-6UnZk z{>t>a`b+rvl&B%{il~Mq?Z}S>byMVTtVW9;@-3`Zn{>s*n3E;H?p(6u$Ic;3LEY*6 z9oBQQ`qZaBJwapvPaF4D@a9;S$|g@?tHJ%42@MRFrgEj$j28$(Bw;dO;LKj; z+J6=s7x6My*I&A{nZ4~V)L)>MN@Q4w?km2&-cllJr%_#4LT)?1Djj;UWl9<&O=-IB zuc8KbNJr~{5qgomJ-#rz$RAeQjmz5gdGD#HM-%<@D$6(K94drJF~^Fp3gR_rF56V& zdfL2<6j>7ztGZC@9Cdxv-g11uwJ4g|YMh-IY_}`iO3&$k6*4>xv#2IX2x{Wovl2p^Q2{Y zdKC{VB}Zm{aVE|aiN$5g!u=bRNO+QZJ!))H8I~HS5VA2p(sEYMm=&tv&AzI()Sh8% zNo#77lC{FkiQ+h?HCq+QY==iJeov9m>bjIjIM4(l+LJ;Wh)ZRgP5uAj?rq?ss?Nm! zBr`~a=md!xmDaSREjYAI8@Ip2vD@6_4!J`n3I&yJX-gMsu~Nlk2BYAba1-G6Fup9b zwp-iUwcWL=ySD%KhrC#ugqI0WAwaF5eTgsaPKa8xRiYsC|32s3$qRzo{%e2x`}>jH zJNKS@UY_%u=REJ{qa2~|dO`F1v?3zXuey@sR6w)Rm|<*hvRCA;E~;7`>W>#dmawh% z@Uf--MAzc#XdDA{FAt*X=ZHpe^&|oGiTB%)?TNE3HO^82vlX(*z_0b(SR||k;oSgZ zfVZ9W5Di99Xx1yTuXp`^94Gzg)~)f|t+wCEcU0*G)n8C+ukf$wpekCcx(t2R{jkQv zr)9jNwN~2~j|tU|o__r?xWVnXpN3XNpm@UCRmgBzMlxJ6smdtb?c}pSd(pU{x2^og z@@dB_@<1!bP^>K!4~~W6!LhI$4~~W6!Lg7t+xa62LCUETPtxtOaTdAapC2P+us=w( z2sWmNNbKWOiQ9Du(E$xl-#9c+5sq9qz3E-ks~4`DG$UoNBbU+4Z1Rjd(53|8-tFqL z#GhPyU0IgHmt4EOtN?EPUYuv37m4c2Qt@_HqwvF)nt*cZUFHXWfjA3OLtCm@9jr<| zXLQ+}*(7A|=bNC%!wkk93I|xhPlAF1#OLqy>>_x(zT|%ct|^R-9h?==J5)bRl#!>> zD&D>XnJTm%Ri)j&M9}+bA#w*aa5)H?tI({BzGBDL*J1CtJZgs@mQ^}}AI8rsY!wba zm^!Oy>a4=4vvMgwjXlG?X(n|aOjf%aWi!o7R=77U%;VIGS;ZS=dkdp+<@kn0O9QH5 z5w}z87NvsYyt?A#*fgQNGG(q9DmYqJ)y)YLU>g|kgu9$TU5)liO!Vc}J7z71&A~Tu zVqU`-?n;}~?;kv>|-MkyW&ryaatu*;#(`at%GxS>WPeSoyb8Uw1 zs0lX1H@3E#XrL<=-kW*!CK3gyPtT19J`EOLFuIb;&T4C6a-y0w#|g};al#W+Y@UNn z;A@@Td3m%T_9TH;QqrhWb+t9GX-sB4B#(Nq`{m%dP{Xr6t|qG4b1?MPsJeNv@IJAO z2KG8&D>Z_CiTB~&Pgm=TZHC(z-Yb1n`!Z8U_t9NOi`DMt+~h3NlQa9zoSaj@F*iAy zKR)&RY#N%MAB>uxfBm!Or*dD~y0nb>PFqU^Z_?I$6cA{N+SYL4L;_AKSjc6HH!C4H zqjsw9EQjL4t%wYi-Em}Z+f!xkhUbah?b_RqWgy#gr1yRLNtf!;1^-rs;GP+R$YG%s zb66#^$^<{O&R=j21{7pR+?+K<+()h|beXu^!IP2}Xm*Wx&a)bZ56L?5iNN!K@* zcJlHxPdih@0hSc3rcN4!*B{%f4jx(hQQ?aQ zuVG$bFBybrYkKN?1Tz%oB~G)w1#<>S_|6ESr>EJ~3+5y)AU+~{1zKhtco1xs)kvZ2 z70h~F4UV(bbv2wsgr`hn1hks ziCQyaCsp#l`7{rH6xA>D!46j7M7k1$2&M;!cmynB1_lsQ5@(x6om6l&zbk|t$s3p~ z4VcPKm{NhLnNrb_zz8uTI%-S{BtxD9GeM}iAgJJ{5FsQ0aPOd9t;d0lVCf|CRFdBd zpHt2`fcZcvU2rU-0npTMSJy36vvEaj(AeiV-gs|zu{XQW#!VGq2;Ol!^JlVKB;K#~ zoO;J-_IHedFG1DyCV^@d%AfcLS?{N&KOmF8KU_45{!BhK{TayZPRwATs+&Z~FkZE@ z=UL%F>}fzC0uT5+v*{~eU z5}RUFvH?9s3Br#$MPC=`_S@;kEdx$NSmpkyvx}zAE}S~MSnm`Q&F)!UH!n%myE0#V z70q1Tr0Tj-*{@4!B@Z}gps*+WHNm0%d$miPe<1Dxb8@33Qp91i5B^0^h1k#o$d0{| zS26Cj;L<%9XLgsUgJ`Z>qUyJH_=%2eOucA%C9C7=}0l_-5wE!@H?Z63bNuCy>m_aaO7>3tL_o5tr{{s=rYkPE3;y z>%McUdG3}0M8m)4V6_g>zYi4VIy`JN9%&`gw#ZSYq7eonZ5k5!OU}K^311N8R>pe@ zpxV6F))#==$iX;I;3vY8uSC(<(wPc=!Vd33Y;sbG4j_6^!qw3OEQ+U=21D&OQX0`Fpqm9R9c5Z3r7XLhfN637q$Y>br0h38Z0nQ_DM;1Q?j-oN&gDU^lfw zwY?Y?8&F5O)S@`4XN#KRdf{l;foLq&?A@AMlbGS<0C@POd17}(MzR*_PUP(ED+Kb}q zh~)BE;*ZcrM%mw58I6HUsN+1Dvl12Z&LrT}^3EJMI|qWa`0M3a<|31cp)Vmyj>Y9u zG~pddbM?-Ny~<3c&|dj(#C$?Thfj^p?cJ=Ak&8o)yqQzJL}rd->zOXIK6>zYA#@t& z1QKtqpyS9`sV_2rJu(h|JbJ#6o1&3NL+*pTkm=&o-}-GbUtNLDc&cQD2%GBhxO$Xj z6O9~UzJAGcT~u2Y9G9#anx1!Ig+dsf5)Y$iHq&$7@bp-!z(Wg7QUGvi;7Xa}=WLcU zYH?E&^Kzl3{vjAk-Hbx z6~q@4oa}O{u9OQ~d7U<)lw~eJde5>uH55u5el{Qn0ELe`UD<+2Ly;lhJ&^C=9>FPr zc`d{H`5VVaB{(^rbbQOH>m=hlUB-swXvTy`NI_^lt}wLI^>pEQ^#5|IW5blnb*`}K zp@+!u(aA_LGpG>1SyMc}Q5JPs4 z2b$mY##voSrSQD?bc09dahlN-$tx#25gUWJn>FMUDAZ7(%=om2izp;Wq|;CsOD)40 z3BMCtJ!MaBsM5-S%6-vrc`HU>E7c}Eq_E?2^-kjzwF;aC9{ zUsB4MUlQ}-9uG=t2_5M1^vNds8$@<8*{|!##(8)cThvrNO_D1kw7xN4ty?A7T*=AR zjRlAjPX*X2Sk71iL~kV_b_a;)0Q;l7xmy>uQY&P-ZE`sgz0mc{7+c8l-e|a_tv)=h z4Ia&|e8QgmxhgB=<`H;1!=7DJBkfj4Q6Ja}Y)uiV4VILdyT?w^WsZWx*s*7ZuQFRC& zsvVW^QgA4?pohcUh2JTeN=r`QQTQF&5^Q6$MfEP&Ho%`$-EkUhPu8e<2wBJ`ibNwo zJ%nt(#sQ-Id>q3wnkQwMGQolRLI6Y9zmpvsxx8rLbJ0kb*s}J=Qbq056%EyPNqu|( zo)MZdgj)3mwcc3mtIX%fYB~8N7XFpfb+mv6ghKAg{F`Jx0GHszKq%DxknSa;Fl`9Q zTn=3@sP&iPS2c}+UkCp$!moyt;@3u5_9O7C2>d!I__YD-Cx7c(;aATn{Q9IF*&SQ3 z<5c(savA*EE%>!t<5y98a+12Ll(kV|dz0=QImtfmkVp>P0cm;~Q$fKWu}3%{kfgY) z8G)6jK*=u?Q#i8Pli<#m2;KY3a7WXplkpdCvi}|<{OXLYr`W=qqUoeTH^&UDX)PyyDrElilu&OA)mTzuY^)wKvMGC&jgo5xM;aToVqKj;T$l9_E$|m^y9cmfQ1;LQ} zOMvkP2wm!9+l;q>(0g*^eQ5vNcFO(l+LP>m&x`=nwPyeO>)_k66Y%YBjc*7;pr4VT zM0_HLXy_qad1|qk=mJ(-r{JBz$F4roY;G~QD27=48gj_kCRPrR5$R}!G4*@FuRsKa zUcMP2H2$et&!F}gS!CUFC&AE%eGGkv;XjCv;Lv*iYr-F8UZ~?=PC!&GL5p9!-ekc~ zE%8=q<&+?%Hb4&XXYR5RuyfDJu=7kGJKw4G`&8cuu=s4*Uk5GcoPd@_i(`vmKN2mw z-x4jmb7-l0ZB-=dEz~zcM4^tN=rZ`Zb2NT7Ai&fiz!a^1!-D_Y@N=tw`wjtC6ULoB zzB$6r)d)YQqmX?e`fcDFd+$l{?Ga|}o!l>O_{|%^^4AdqbV3z19u=u*N{3iDL6l)0 z4lf_<7CsKC9Fhdzh$QQMs${>#u5ban!e1m0E&BC41PF~E=Zo))7)Vc!AE_}Oc3UwQD!g^`uSMm{d z>&yc4b#Z1KH}0|;p3+xI!`MJi{_|StNsZr=(#-G1>9)!;zcM#dGW*RGy1w2!PDTK|WFU4WplQd1yG~X{8ia`CsQ4IL3$Jdmh-zd>6}n7vj*a z^L@OU__B3H#*!^nl=taBl;t6q{-p_9QOP3 zIln*h8N(UfpZWkG(H$ys#aWSf%*vh8qpqjMRLY~V9P}Vv+Yt`lCLp3~x8=IlDfLmQ z?wanT^ulyaPtl0}od_=&Y{`(jFisUCfpy}1)zQH^I=DqlbK*K{OGqpU&9qD;u`U6g zsP5?GE@p8R`O0J#SIH~_JaToP5_t5`w?~KtLefoPnC*LmW4wCG`q}Xd#U>e+TJj#8>w&aAj#PZwqAr?+} ze=NVGJbQNgsJfQ&tN-(G(9-=MQhnyx(@gEhs8W5VuLix%V+% zT(O+N`UXdh=EW)4`RZ zj8={FBL|zH6kip?3@IufDbz~2aF=RaE(e-as4EHs2c(g>_(!QmtY^Q)r2Sh~7aqp( zax`O&RaSPLP8yj%>5=@;6cXJH$fO=8Kbn`h1i<%qKsV&+eLRhN4`^rF%o4`f-@qHO zHKSxE#$4MIPHspLbAw>>63}@6%+nzqXi^v*h${^qMo)BY9=y zX1cbZ(OcGMhkrDeQgP9tz8Vd@f^7xlwj(cFUVSjMl7maBp}DltTQJz@U3(aL@FAP> zYL(F0$o5djQm*dL+WDK8nCYZ$zlXc@D)9}LN8WejjrScvDU;A%ryoiM0uHd%>Ty!MchNgokpUX#4jcnb7-r(Wa@17?@RRM!TDUmhm~&5 zj`Z50x?U^XZG*LQ2*06=EO}8MsERYLQoBw$<&D)lHN+)G(9h z=o`i79$`^TcVC4alWhuzJkAQ<>U0fqzUDS!tO)`b?_^g=2c*9%=0Ox>J}RG7x66Q8 z&9-NTjvLUU`DU7W?a05&Q0Jwd0oe2ShzU<0`@y}2{mZ7#C&;AUWhjPZ1 zu{mn3hGY0}G|)*f`Ag8}sk&gUnUbMq-cK{OcdxWVk6W_o%ufQ)n#%#ExA}*3FBE9ph8a}57UYoWOc^3P<1q)@(=ieg! zE#Y5_u<*h!2y0d2qlToAmWByO%9M#!ICXhV=3H$azDv5rlxAmZCauOvYkEX8Je6Sy z8zS{(&eiTB=6Bn3(m#eJkz`ljA$Ia^QtmB(B{K^|=J}}OMx;OV51wgAKd9qdYDgTZ z)7*#yLeX9zJFj@NrZtVz2c-AkC!KOPN{<1GhPjEYOnv~BzD*J`jx{bE*E?hZ5upVBS>z(svJ(%W%zn=bIzO@Ip`JUc6M7gtS zzHTAlc$&hAbJnUQy3iy~(@b%GVUMr!G%X!QlN`Dm{L|qxOgh36Q>8Y_iiTTF+$=I< zGU09%EJk>YQ*~bATu{_7aG}o%_0ccoi&O(>&CoJ;;pOfSX_L9`b#{48AQJC2_C6j9 z`M81?Qa-boqNYUhSq&HDQEA0rsH2rN{(6yKLpiC?6WqrLe8pRZ^cUnah`Wt7LeYZ5%YnH!YdF z<(t&)X8h+;IIRmv<4S)P$0NCVod* z_>wu+NyW_E(_Xmdek32?Ifn_MM)?=0B&m0&?mpFl3exITM@q+se(NLb5-YE~mh zh7jcPr(-8ZF)0Dgh~D+Y-PIwv*^!-1A4GDJe173wRl(}yg;w~Wm6jC(8&gv#S*GU( zWeij%>Bfd{W6D1oL3fFKdqeFP>-KYrpy`BkKsq9wKy))kA`V5v(P^jAx1+a@suDFu zn~5U($l^g#kxBATHN|uXclsTSAmW9)v*aJI2})g6c6CAW0=g_31?jTi!(8^~ExFOS zorN;o_l_EFi5aeZo81+o2*W*gLJfMLCaQ}AwAJ3}YEr9fP0C)DDr&7u*`)*sbxFn& z&nixoNAcB`9B`!XVTRmp1i>y+YV~+H{S#8?%^>%Jm8~s}WeX5i{mB!2(DJ%OjuAG- zlCB_SM5|Wp7&b&N@Q8%$b!73#tIEgkkOql?O-@y@N$#sb{FA-ulbZfPU0JFR7T6uW za$5FE=pBBY+97V>6KYnebez}W*+JgL)qx!UqB!n#OCj|ay6NMHro`*@fW zW{=X_quJ?rR}nP=+4Z+#`e{5h%qEsYPJcWnbRS7Wk4(KJ_Nw}l>Ut8;{szcbUvDR> zS+w)hdL_!5^scwP>*L<_PR4yz7A|z)W^*$)W66!sfu2F!CdirLuj=Yga^%uaiQo{jwUSw|OW?nqv!?T5(5%BE4>LlL8sw`S=(kh! z9fRkDM@Pf+a&AW zoe41f!(RdZKYm5v2K@iS+|13~AUA@~gD;PQ|20>h1pa?Alhu;hMd2a%Pp9ZR2LC&E zy%GKaAO3Z51AlUCm*HD1M}CVxKv2#L#}E0cQsWPRF96>55P7m($o`}0e4ST9GMN(B zUmz!)oA1A!xe>C1vi&^#HI2Uiw?@}4`u^{kThaG>=9VLs#@vRty{PNss?G4iHS^s} zLE^L7p2Xi-Zc75lj+ue(o(zA_a3L8jv0`n5P$phXz}*xv09DhN-@}UMbp2n!`)jz+ zV%?Z7WFg7r3cW0Lg0xiyiGvl&)wsuW2NrV7I$e2f+3?O_h^pXU>uXcker}v=f(o+( z* zec%d^J3T?U^d;NM`SA0624D0PV~AovGkHJKVY9i|nn|k^Z4q-mJn@@Sp8)PhBdQ zPqgQ)=h_}&rmExN{iSOYBrS9bsd1Vrn?%>$Ohhi$47vso*_OJ5TL0Tm(BQXPz zfpao_tddxEyQiz{KTB#?qGu&qB&M!F**q8vle6MgC;XZdA-%fMf8BUG%#HOkL50wI zB~!xzVyhqJ8pA!Z{e!0YjH+AQt|SUo z?ILJao@FG+XGr4~IZjIJN5V5!nEjwT3TM!TUJ1xEg|3U?VoR`s@FL(i-Rz(WG>+?2J^&`{l@BjVF;ovVQSBqXvN%mlH}XjJ`hs8AsP@%Es~=X=!%F zR3%F&7XC#%@Z!|9GK+NRIwqe^Cl*T9>?s5xCntm7p^ks%b+s;pl|kRj$H{;4*2?hR zTk?X$g+{fo;chm#2=rQc=MelSWvz|;VW3|Ig(%5zk?%xT3h#=q+cQMQ~<~6FljdI@Wd^l|JCkA$e&inC`)w3&-LC3>#E4kN+qX?&+ z$l;}A9UT~p{_lTTGAkwg0H^J4>{l?mj7WV@VXOzIog4C(?rE2-@{>`3I4o7@6#6Bd1ZMj zO;moPBtGs-%^f3_3dYYegRWFH7pCgR_Ak)bPuM5JCm>4@2sN@Oma8(;rizN|iW2>j z2&Rh5Qo)G4!hjRU_-&V$33bRMZe?W<1lz%htr)lkn=h77K=5n3LnxIK+1@Qs(gPZIL+xU#*3&QZKc~Frr&3Gg zgC zPR3iA9SFv}OUoH|$9yJM*L>qeT7AOQ%i0tBguqmm+gw?0^JTeVq&P{+HNDg}2$>n^ z)Z5punSXpAtVi9Yyv(NSs^mgej0(OLRtVLL`9$kyW>Q{f z(6Tm20TL``7FLQjJB}&iOVop-KfVMFm#T(o)}}IwkjpR{y1YBuvE$Y+Xu1RawnLW* zH_Vw=@$$IWFir19eCIV>nt30UIPS6vGT|ul#_Ghwexh=cw={8{8I=}8GS~xSq54je zIXIzOnA1A(qnP>WE!2N2F|S?UII_GvPFt!od0Gnlqax2#_dnf9X32pyD&!ZF z7donTCOL_Kh8$jjikR)+NfxG}1^rj(b$Vtjbw!~Zx=}HPF=Q0D#$rR0aHdJhlQI-P zQww_Y;zE*!8#MH;D@;!>p^m)O(~Hy7OXY5$P)@cv#vP2u+(y;N)SM~6DU(?-wxu06 zNkyE9q)T1l#8F8LUNbU)ocS!&6fhh}E;E^-2>8E;lA=T->$RcZhDJJp zKQwkcn;?hEmFR-Dk%-C4mXy=qk8-XJiKHvwXx^-)Kf&rtONsv)7_)htv|(14-onnz zTyZmfell4up?QuE5mxT%yLDe>0lS0wi7~0V(^*Yq9mws0C)@dka9wVO>F=poMfG`j z!9~55Y4q~ z6g`3D%?Js|TV;?8r|U;d?g3uM6nTb)7q~~ta$K_z zaCtaW7=toM(>62Lx3rkKzE{dsUtidKT}*A4)};|xUMEa!6fU~v0iiT>c}!mBYNRb@ z<8bm5vX|-(<&TGnNlYdtRW!n4tM5fDB33=K(eyne^*7x!Q1l4i4S54&9x<~R%6vom z@JjMMu*g5;jVRn{hq%VEhA@t+-5#6*Zyca!O#0jAVDvnmpnuSdZd2*G0zp(q;00M+ z)Sw)wGg^}*(wviWj80LTH8VGq2}qtr2?6MBmU}i`Un3WeJm!2!%~+t=Y1;Z!M0N=Sjy<0B+6|at>^G{@!e=tYqS6nD` z9=8ukH83q2!1rk|_)jHT*p{aF*zlfnQI-b;%8rLps*a0f1gk6N3I!+Vp7SudmT?7Q z5*lv7-a*Lapd2<5u2q?K(FY9Gis)j}=@|H-TFyM9yTiFtsz>!2T|n1()B*dSwj)n0 z9n0yegHxKGk@1dFDdfgGe~7*>5lm{)^sn-j>Kke_xsQq1*r#dytN%py%umhF@iT5D zFF66V)y&L$J`e0AiBKT%q3m{L+h%)6k^ia`NZ#A6e^KwJU zjUlk_Y81m)4V;TY25pTDeN1Z3S@~Codw5ZF6)ZmhXu3;MDam!IV zNu9FKK%Z$fT|fVgT{rJ?y$`KufvgLbF%}W`pj7`tHm?!2aIg#G z_xs2MgD9ZOnk!lv_*;Vb!@1tZQgU1`*kWx4cQ{B#aK>`eMby}?>*|l8h#Ao?jplI5 zG?pCaURsWl=|w6S#@lsQeEpOi+0oSx>h81&S#!72s>WMe?Fgfnqy27!{JIBH3By|V znZ8rjW)?y>3!(Y50y>6PxkFc(t50gu6$oyC6tLlO5SZ*V;1i&Y1$GOZ1)JG{d-7#q%^jug>w&@&_6D{nb7 z@6F9j4*4}08Q3|Mo86w^(K=h9m?HH5OGnvzdxs%T*H=yp_^o~*e- zzmAg=`c-Icv4Y-mE#ex4oCSsc3LG`$3`#){aIXC7BEeqH%rJ8sdT88Rb`Jt zyn!GGyxQf~b zj2efG0GDrG%()={&uZWegU}6dSk|fP=a4zGDnO67F@XPx^}F4jWON9IRtSDXVP`ma z3kKKPJHRPpxQXs^2ha4G1+_~vQ=B-4^p8^sG&q)exbd?fYmQ$X!Wx##C2uyN!u>R# zGc=(-%X76Cyky|K5lh9a5_|mqtG)Oo*}#+ZC;2b^#dOtj2e5w{=%2Kqz5P-4MmYoU z!XpQ!r{>JewFa{fOzMy&fJ|rMrj%t(O^`*QoV0E%Hg%0 zLxo(E0~d^ds)1+Wa7*rM*--mMvhm4Y#y%EJ{X_nU9Znd3n04XlgCwJ-P_o#Ll$1A_ zGaR(m40rD47a_b8H;9s9qv%%+>o5RK&TSZsi2Y?Fj@dxM*Qm5Q1Z24$#>u zS#Ki}7!#`9%lYt{QQ2h&uuwkjb1Gm9%!)%|Z6~tj#&9eJhZx@782Dpjs$^nr&w+`V z!0g2$`&QRxlP^jW0~b|h_39FRfG{~^+JWUkL$4VfCuHxDz?R<$+ZpG;FX?FW2b{Ae1G52Y9Vn~GA z+gIyDk+FP#Uj#plWg^KsBAv}dFD7y`Ql6xw(fUF$4kGO)!JfJztTDVpf~l(^@hoQ2 zrKBV*Dbxb-uFRbT6N;X42V){Xc#WD}gy`NB@MhbRI0lWbam=>b1_e6JH<3Dl>aH!t zI$Nch<0!ToRc|c3BNpjtKFwB-N5gxyZSo8VCT{g4Zk4*CVbsw3SSAy_Ix-;F&2tv~ zGBGtbzknsO8^LC)6Yh?Y3!Ds?Odhj3g~^1;s21##`>uM83+kz>8Wp-n<=10Fcu)XTdPK?BYgcN%JW&UXt8M{W1G|sK-rj# zZ1s8c5{nMcNL^Zhu-z9|Hx&bMq-R@gw|+{0^5Se1U421IJ-`2V*+;F(&3NUKT8 z3?Exm59UQ4Z<3Si_Om}o;#E0)sEfms#&g6Gd7scJkXv_zO4_}~)I%Z( zi`e_Dq*(?i*gRQ?4OQrDO4URh0PMo7mVt>;xM-)fKZ7QJf1lck&*J2*95ff`WD;r+ zd>d02Pj`=xZN84E3cRC#D&l2sKAgY~zt7V{4+THcd_g?&+mL&Ka9@%+@WdX|Su6A) zc{Y2R$*0{5UvP#U?u9&wNXxp6Jl6D|bf^F4XhKv2C^MUbuJ<;d=BVZ>40^9f-*x+( zk^+i~O({Uw`ucM8*)L07Zt4l2g*3rvxL?M?tW5wlnxCl@_bbeZ45o2ma)9n^}2 zp61YzeSs&Pz$^CTZhAU!mfug8Sf*e-ep;<^?QwoX>d&xCb31#bB;)U|NXu^^W9ND=)4oFatz(;^a> zLDZ%*Sy+7oKLTgzGMSF5VzzW_q8*d-^~wM$=lqX0w_}t3tAZFb(agnJTS-N>=J)Q{eIj_4JU!a z-b1Ji@y3Rc72<}rqK6ieh~{?*b7g5yD>J^v!1Kf8B7SyW^SPjV^JOQgR4-hgR%nDf z-JnAZ1tpcQSLFIFls^Ifh4PSHI5F10@5bYe=mk^SeAl zKiue5<8v;%KRyubaiR8hnFOB)SjO276>$|~n6ub4KNXLwMMSy|f?G~_r!WE>8~u9t zHM@G5oebrClzbN@T$Ai@x`U*yb@LiIEAD+v5$AQsaB(!p*;I&vd>+%cuZ);5-PB(mMtMcEq4S@H<2Cp2A%7UAI-U& zM7nN@7_9hhd&}nYZ}IpZv83Zz6IFj`3d1&Gk{w4UriI#fGJkfp;W|U@yZI&Dmh{z5 zEy}0N!o+m8=uLSn#sEH)ZQUP((N1u5#j;|xb3-0G%+-IqKV3Ih@E~0`$6RKbiw>=! z0}lTdIt!L9?#L#;=(w+!G!>ETJjv0F_J^Ju_<$HHaUa7~Ya-cbg`<<&J%JG`ZG9HN}q^F#p1uKc2D7-zEf0`3GOp^2duhJJcTM|_yb9lV@y=;i= z8ct+e^SN@USDNWtZJ&R=gj8m`DA)89e*jYxd<#?Jq-HO%#Wc4oe-nwql~r74WWI9LxWZi4>vOY9J;69jcJ|=kxwAYn^$n6{&NBbh!Tto@-mI zu@B+%F=A1--yH1v@51qe z;=Z1{$=5d<@@OkE7^>x6!KF{}^)!=UD73yui-Ou0_@_ceMmYV7lQ90kSc}9R7%9JO zq_;4e!W4XhVaJQHL0JJT2Vy4sUE4MxL2{bQn8>w@67M5E%gW9wBr`USD}jADD4B7e z<6XN*n_laR6D*n8rTWktC$g+6F^i&d>JAoHadt7MZx}xTj!sE_RNC~5__Y~p>?L~B z9x1ft#w?VOPpgQ$fZ|rG<-1{VFZU%2hW#=h<{~d`i5nJ4 zP0GxLpcwg8Di_Zx_o&vIlD10B<6?6uGM7TR_#3OO@_mc@UbaX(nGG9G>UZ2Hl$r^KKcNt}WEden<6;qFVyqjH2op z#g(-w2hU92_y??l+@jlD+7ZHCgx)Tiu0?>fM0aQxmY<*M6Jvrvq2|(e3 z92B&5dn6!^3LtVeZxes$r0#sy4zPX>S{ob(3~cLK_^Uu(=AUTFNv)lh;#~^&z#zPx zyw7|=_u-qRAC(tt`yLN41>X#QnNZ-AnqL#3m`Dlm1deJelUNS78^4jRG9uy}Zv}@Y zVV!I%l#91QUsPMM`K2$at zm9{h8xOMWP&%BslJM`iQ=EeLc%ooEi?(n}bFXq<`eQ~3_P;MRrlct{J!g)*#numq@ z;o3e*^Ki9!Sgb+tEZyMQ9p+i76vRc_{3gzpZhrm3{F-Ba{mT5B$1jG+ zEPL*s76-X6()WYfV0>y{$Pw;Id{s}&2juRgOehK0MctJxS|9-~{TlpBbRxl3yGuWsE8~^6`*9YR! zQ$a%?|7vyLl^~uhk>M9p&5J(sLeS9vqS*gJP>C;wUmPja)2A^Gj5esIu@UEfnQa<2 zNU5=r7!C~rAO8dynQo1Eo~zMNxf=02SEHeFHR5@$MnmOl#PeK@hRW55=eZgUm8%iY zb2S<&S0kS1YBW@?Mm*2eXsBF4JdjP$F!M1p06{z-Jv@Vq%GK!S8DvzhMnBKh$f#UF zKdKO9R2u(0jf=_^{PQ#}Dp&B&a|IV!q2QnA3NC_wf^oz6C)hX=|L$e$7yN^J&Yj;b zPQK24zK^fBs5o;gWi+Kl{3(af{#14{sO_(bbl`xXU+eAWf-|+HcK_1@6E^W$oOflr z5h~l8b5Dn#y;!{Mzs@_+AuqDEl#04>zRkO7Fz4Fog}O79oOq2VCcCK(FI>T+Bhe0X zWCr(x9k1MUI!&u1HKA|e1CGZ!+TEKbZYZN_Yg3KSom9$T}htdRm1mfH-;$m&sc5EPrqi4^BFcy=ml?=g%E#n%2~)DowXvw4*mPl&)+ zy-N;^*(l7h<-T4Ry8U79qLIU)+c}P&o59fS|G|yc61Ij`eLHtw6IvxYEj#jHD% zkfG!`E=^}{5Pwojaq0;9&odpbsI+%~9+Mba$C;HoZ14VsOrGuic#*zrUaT)l_!iG? zp#~1Nb&nR@Lgbrp?E_<@kpt0Cd<%P`m z=vrN$h3_kaKPydsMDp|{!;U_?udYJks#FhQ0QhA`22o?w)rdO^pHCrrPaEUOV_uS%5p+Lv-@{5(I18qih~&FSmo zadY1Rhn9o~L(9jQwB;_)GeCBQE-V!S1I1y+6R}w0dgXD#vDSx%+;16xpeQ%+@B$;i zu~fjJ!K)Px?;t$%kWUJfoGC**D8D3ZZ0#6WA1c)Ikn>Mj&%Nr{zs-DtbsRsqPT9vEiIfD!azL+f3 zZUC%sZ0yDK0(=A85V4S@G`#;DVB-om0@&i9$VxdmW`0qC{SM;eNl_7B!|hh()+4=> zGdV!N2Iu_tm7r$-V-M*xrne=fxezT0(2DA0K?dNGJ~z*I{bzF&)0}~~mHoM;Ua-{9 zL2Mwt|9Q+ILuidVY4UBM^=L>-*ykxB{bfTEHD3RX>0#iN@e(-~>InMiJXZ_9qQoj2 z33RQ$8X*P`A^54_&2g24;;NUxZv*JtP`k)-d&z5-1sbIBVMXSr|5!{`Jp?Q3Js1BYz2{zy;^}JJ zJ8cLV?tHT)_a(FB{^tCL`+9N6eTJTe9=fbRu>*>mx)(4WxjDZaZ#u91Df%D29}dk| zs&OzkVe6)Ar_jk93R_@l)s^Vy??pdvry37CsjrFk($bj|L?aL5UUn*Wtp9_AnGMkz zy`O6NhJ{0HiXi%sx*F;Z3Gaj2AArgb!eCCuWo=N*D&Q2+Sldg7(MF0Hsw)$Wbsgd? z&8IDOu`5}mNRFhRwjGu-BDkxd8wvbDr5;*)`LbBYQ zh3;$l343~~M5FGoZ>#V}m?ZaRVu5b95Y&v_dk?`gJ6-)j=g0w)L42LK%A)t`k2T%NO>t~NXY+C^)iM}MUA$7aFTsF#$P6{_3^jkM_Sfh< z*>BZ_J@|A?)m_*g^rjy}rAhNmYiLvYu)qpuqABMv9#t#V>oPtf{sBv9(;*Fn51aAg zK2RruL`J`eM2rZRQW+iFP$@>mXn*u`uNvcNW}zyS#~fM})K_ z8t2O^vRC1LcDt06!_wF2_wm%+tQPIEV)$<)Dk4-59g3szjvQ=0pOgd_G~%ZeT3=LL zHIDOlIen4?dk;1}pIsHyB0NHrmjt;a@KnSOMPyY@@bIx*$LolV)g;12&mbvqSs(|M zH;>*_tGWD5R1y&%LxZl`_qX$#{Jv}Uuja<`3b~UieZ%aqoNO8GgkP=zF<(_e&Gr{qnu_u!ypuj=Om7;3TA8&WgGnZ=uv$4NO-9 zb2h-lfgPp)l*H#WM1P_*M1QOyil0tHMRqn2)y1vU<$zQdaI&15yEV#D(_nyPR^~jh z!>IF{&%-ar3HORW-Z&0e9EU%i9A4mJTJB_ugjIQB#qc$(3r{oyruOxmzetEe3 zFdbH#rE_k!pm_qx)7X8p#sIeY0y7vZa!gVg^iebpL=JEz74k9ZXlVTslIG0K+KK?t z$K-fl^*bkZzi1B05y8|GVqELlMRVifuIMEd$$DnJhJ$_U@{^Zp=het-OV5j?8VU?T z1Pd9%>BLmv!Ss033$@<5q94*)ZDgGev0RG%^AJZ(4;0m=+w~h><`IKI(zT%DZi5)S z7HnFnlt2@YB3rF%m+$S!UfWw&E}WHwX06i@vAv7r_)2UpXA9W#*9y3@vvEf=CGjZg z|H1i~fL?5>%zZOrMrhF+m%38EKV#JQYI}bZpP@d%+O&n~3U|533xHHv^W^9ye@eVt zj;#~Jxao!lb-F;H-hYXFcfY7J)mp9kAV{^8PAhkbs6N#F2`<{z3_r8UUEcjmm^TiT zt;fh(C(z%vGH!q^)}DrebMGs?OEBW>mfD zsA%iH`ls_4IKB@!O~(Rz@t~P4yAsC$NgvX#1mFrp)9&}UlmDz#QNUAo^Gg^0JH30! zCu{U4p@-V9)rwX90<+yrsV{oT@BJ!m_A>}xsw2P@IrtmmGYKOOGv0o!)~w1_CDpA{ z(u2S{U8@y%JhE2WY(Bs8c~9SY_e)_e;Tx9GW(tj{=q= zIV^!aWv=r2OH089yL_&9|4(=;=MlMUxZ%+}3=ZaQzUQtpcMCB)a2H#EpIuQ6E=NwP zeb-{1aGrg4HZ5>rd&pD>LcYNIZCt?OHPOuR1!LLK3PLO1O}l6sl`CkO{J0RGF~dOE z>H}f621475Ik3vsZ42!J7D7Xn;BNA^MVa z4Wevj!_;rJz7Jkq<4Ug8L1v)ea0|yY)`I3o3Zh_)9f>{Ldz$xg5FOW+cK=*v<;ciA+?%5E7r&4k3*k>7+?VRC|A zeN%P?_3~1(FS~Mk2f^2c?)CyZQhMW`L_a(wwqRG|HP6amoCVwCfv4iBYmPgq2XtvC z6^vb$`rf^L4hnHOf8C5t-NFjShYC}n;X$LRlG7SPwcFaVt)N2lgjiSK*mxj49rT^_ zF%+bA8?)K5c|65wA|2k-)qmlUgYHvBZr5qf{tWpVN*9j}3D0iV1Ig*$NEMo4~ zAf8_k^ossCE7&;s#@Jra3zf+>^mV$5caMxb)IMn)nPW{nTapy&G=!n8E=;DLpI3d?9m&tW^r_xO*#_-5xNn)X6w6$H|0URvzsTG zO?hZf#yy8Ud9djPsnqPt5#310tz!V$c1`7-)bcMgUF@apM-!l;T6a_!6jU^c>sYRK z_4kgp@Hvd3}SeBnI!O`pL=`>a$UNSM;OmqcwRZt(j+scqU!H zXsF+13`qJdOex;_HxkAfwRbkr)h7lj2V5Cz`M|e+ofH z+5c6>tj^UW6Yl`RR+>ogunj@0Oq?3#fzbt-f5EK@L5s8-QDR134K_!NO@tEdBtV$6 zvW%!5U2+K}2i|3+YK}>*YPwWDPfai9GG9%f!eyZlVD*!uLR(>2=X&i&bHv#8esWai zg-1hF`GKQCw0R=bSx`*O2T}E$;i%?=0cPKir)nT3vgxxWr>@A4sSJ&kJ7N4Ve+fr% z3;Y<#kJw;qj0bu=_5aAu=)mmUBXgp;h*;O}gCr!oXeVSe*sR%v5u2qA4KVzL1SP>y zPdgb8vAnzjF5+UR>3SO5YPbk2OG8|Q?B^%rAJA0&#ry+Z8JJ_5%P|kbYy2eq!}Pby zKSTrj?Z~>LvN7ucH-AJ@JCq?Lvi<7E>Gq?t`w9zTduxu$W^FTFvgbxq*W?cneqcV( zAcfV#KhjTHGPJMxoI{|8GVQZRF&9e=b8&2lxtN5pYF_!G(w9-6|Wi4IEOI*i3 zQXWgK$Zfj#;>)J1Ykf1;W4}Md8$aR?DmMZdkyM=-7S>^g_^_TIUeFhG)UaaSa*GHJr$+B;CGCb{6T>0HJk&lXy~nnV>Jatcf*S6ko7LkIQHhQcymUw=Fo1 z_$9gXB(YODstah1IRJpIZJJ2Nt9i!wRiT3njPUa!?!Q%0L}N)76k0z$zbcv^>Ntvz zl(E`kKPH+fPA#o`xfJciFP&6lffIhr-#*V|ris!8H=>d4O^4#pHYr(`DwzT{HGhn= zwc+6wGAVXo=kTn6-MahnK%eM;$w)J5LddF(#tZ71NY*$D=3vzvUSY97`p;u)7sSge zgmE3s*t*%b;%g;7pmZ7%j^JZpPjI_6!Hu6>T0*pEB2F&6>HJ!DwkeKN~U)jTLQM%|&}AZzpJS|DgbO!(Af&2KJG@LhVYGd=k00 z>*+$mT%7=NZaz_!(a=No6?urMmD@!~70u@n5rHRX;+0*~13xixU)xbF1pkY(z!oQG zGb60CgkELw)Y-qj|0NJ`-3sA;@Q9A8yZ>3Jmy`O6AzM!cg@xq9R^SgZHi*QcWvX^x zqG{Jt>q8^8d`x~cusxP?<#Rh=m$w)Qh^Xoqbe0X{)2AeW%E%XgBm_zr zEE!mB%Kl)!CUD-vyM!mD_nN2<(@P#<&&>T4pt=NQ0AGDuhMRqU!VBCm-mHSYwM=C6}d+8Z` zRN!_OL?ais-uOqk@h)zKeVaKz41$pY7c4I?kL(GpNJDu=k#LXS;*uOgQkyE7I+7I( z9AHVt7QhD1E1%XJaS)B6uBW1`T23}I{D%EbI%-9##NT=8*;2;utrfYH-pXT3C0c`x zsbH&?F&nSR%6x>$)JGb(#UIWhYsLdvOX~=a9K0;`@MaNah23?KK;wi#0IfwxqPZli zviAr`M@XhNqN%gMprgQ5#-F2wHhb8`tPD*MH9sQSkq0zYbkbcyjfjzo6Gi4dpixHl z9igiZ`-Y#{Bg)xvZck50n} z2JdVkCP>O_M@hdXTuoXV1COOH z%a4Z-X49#Xu|j+C_tNAy@>=s$w68IC(s&qdOY*a8!dqn?6L4xfzT4kU<5su!FMvQ&Ui!*OQeH<Y;&zg#mN_GdX(vJRus^MUPH~5A-#RgpNvDZ?xC|#Uo09hzlQ%r zi)t}hfj*(W9JS_;3@nwxTAL;NyJ3HZ=&!Dx{^o>=SYS)zHBSmma@{?F)HVj57{QZ5 zY->jGq~8>tH1JrCCA|)6h$Ec8a=5nA7maH|_7@2mmbD7`4dvKYh-!}1`otH!KNZnk zqlqr%gk}#+d+U~@Kgc%x#dC5>Oq6NeU^U1efn*9j3bl*xiKUhV%@S^caWN}i@MfB( zQkp!STni?&M$;5Av_M)$ee5uGLNl`ISaxd+T_GB;3bb61FfzglKp--$6c{36KGEs_ z8eu$!K_8!-{EslR+Q}yRa2!u!%Qp}$#yZY|^ zvL_}!oNLa1kKs-BN55D@%C2bvc~x)=Ph`F=g$xO|wa+nVP4~1OmhNs5iN$}6CW)<9 zKfxcyGi5>@)9FcnhR%9FUMQ_>p0BHX8)F_GFZwAx;Kb({@2Lj+z0rgH-id=X@88+M z#uzMq-vSgcvy2J%3OLGJ#_i7IZ=C$i2Y^K!9O>gKiNFu*-^Ok!`JD&#b7S9>RoJV4 z-(5b1+H&?!ZDAb|>b3~qfl|WT2Jss>jNd>8y(+E)eRlPCjajs%7b`LeyG7Glw%Hg$ zm997jtMaE)nw4#pNqp8=L5*GcA#DY17_uvC3$&GL%o&yusO^YYlf|qy(h6F7g2Hv! z2&yg0181lo31-;p2ZAQKEnPjhh+kHEtRS@edO`CzV`Ne6angs#fX;m!svi(5cWC`g zW9np-OQMPc47SEow~M7y){v#p&h1AhL9qNKXC-IJ$u!!`*{HK!MFy6>Tg;rX@P0#* zec|?WF@ATE0O1eDsE+4AkQZZJVQy7{E41c{q zw7=|^tHcTsTd-{@3w+J0`C^^moRiMT;pDs7v%4~5Bl$~9)D6dM^&TuEg_gVRxO{!% zL8?oRx7v=if)G}y?g$a`1x=S(sp>_N+ z9?x^8))C_&Z+`zl>ZM8~M!9*tAhdETH~283J2%$WrNvQUwPl}zy?i>>^;A$DQg^oE zLEji4Zz=`=Nm&w$s~1N(oyGE3uBMFeC*Iejw4|uAaXzc}f~bX8$|K)->uEY~soSLZ zkdLk9=@Ly7VoKKoqg94hQA&o^-_}miQSwJjef#S?8&Q~px$%;(%f=$#zDM48xf^*0GdHq z;%PFkaWHttlL@pFcmLaSYXW(0Ae)Rt)yfR_&Na{1pmB_)Rtk&l*U^|-^PvnvCCVR1 zR!64iK$ZiHzAYcv-UZgiPsr4#z9u1Fd?NT7O<8I2NUzE~%&Cw5!%eKIfVBa&MPBB2 zoL#2DMxHG5!A*Jp!)``w0K1c6NV527yK5GX`CRc8SN>Oym0|42^1g7Vs07 zQeE99%Q`VGt)Iy9b~pC2_Y6HM?2CEbBf@ia{D-@-hgYx@XgRS)s)NI2 z6KAM9MLe9`oB3DtI66M)5!5gg#<>lYP)bNb+p9dvHch}|M}8U__br}r0+LV>!<}pS zO>GvUGos*d6wu~-uyHd==1-ZIh{$`-=BP6XnXxY0pn7rsRBQUk|%8TWEC?=&0zwmg<6R!n< zdbDz%r4E>!-B#*m94EG@{V0N2xzlCkl9+PufR>26?eR~H@=~<}>uwV=L%w>V_&cNC z1fN|iZu)_AqS$+6o@fq`Mro98NQk?Gzi!)eQ@}%s$B-Bd$#-UA0G80vmNM%RC>Rpr z{J}VrB^0u$W%+o}?tLMyp3d$j?GIsSePKfE z;tMi~zZd*VtH%PO=8=Is9}8?7|C;Pb)^7+h1((I^Ry>-9J#X*!^q6)h$;!i+&S!gV z4E)k+8*HUlO)Es`D5dw3{G&`pk-E4%7RK)N4N-E%QX6%kY}*T>3X7$FP|gJqN2~9g z4=9$R+@R@?veHfj(P#@1-K;`jp!PiezfUK`epg` zmyscaB`S_OUBSH>H!}vkc)oSa@=HIf0*$FQfvCfwC0>-%P=nT{<06} zq6Fm0V&s3eRrsxLd{sI*yEYI%=U6PeQ=j(gxPNGM+O`PEaN1rFuGVSWCYM-tcc?>T z4H3Kw|YcnIYiX zwW*NUWPL!6!znQ1VKFl`lH&lMW_S3wp`m!fD!o;3Ql8!@kgH?Jxo%fYOdZIyw8?bc zZYFtu=GEC!&0W%=T<(v%q+8|(iW@Y|-goqC?>pwjU4jC`Swg(;7?hY^sL87)+Cya0 z$@ckUk&EcI%0wnDMOju1sag%N+>M8+4~oSsh#)W%cL2y}qF7wYv*)B&>eo{65uw6) ziBh#vh`ES0GtJz!ZIz0)nCF_lYpxr@txIb9{z#6#>qnZt>pM-~SLW!uWGb3MZaT+( zq}TN7?($aZmIczH{!5+-Otm00A0?S6Al2)_%*9jhYrNwnj`$LO*-@8OWUnlyD3Ztp9FsR*`|LjEcG3zN)fe3JdcM7{HDUp{|0g@Cq=Z}_7djeHi5Z;c7GVT-!?;1TB3-mBfgNUc8X+6 z&{Sh!x5%@ks>=mW<>c7WV(fIp*ilmK0S&IzOv8{6o0DM~zC0hv-)RwcB!8#H(-8%8 z@~dOy*Z-HjcY%+qtP{U8nQ4-?p(lkF0(4=Nh^bQ{7}SJ8n3GJJGwq~JDbjG0YS~cN zinW=cWho_LCN!s~gQ&QQvg*EG{_M(%yRvNol}u8Sw1v`(O6#=PvrU9?wd*`Z59~wisKK%)QvedatrGmLj5^p$nsT48lir%00*QJ}bx-NrE zhV(Zf+!(?i*4EU_DSI}kT`JzB-Y)F~%!pG!L&dk%Hq}}HW=T@PG*Dmg-d=Y~pO*LM zW$ypU#ek@i_rW_=oHu>)u*mzCmiN8)koP|%P6gN&wY;KRgNs}h$@Gr70t{EODpOiSz4{i@s{>& zfCJ*2SLLLhIA(Ud-OenEjjXDT$k$XOn9j2}cgxfruq)J2__gn0a)LVg7e;-9+B)Rm&}?%F4SjlK0^WHZI>i~(x<9X4&$R>(!~+UV@>3NbhRFQtAWCj}laVsvK6 zYU!~Y$@g^pM7|`my9!2fr&~pW^OTZt%k+iM9n7-_@KuRs=ZR21Me~H)DlpP_@$aNL z(_uO=2vCbv=}j%??k%)wh3`BL6yu z9TtAQ#Y#OQz0>^fsLZ8|M#81lGzt5T7uD%S=j1xS}yzg+v!0l9Z>?L%ugCeNsSNa9`+2A+3Ar78qwFol#!H$5pp(VA86(M)kRKjWyy>!i zoXNWuu5g&SaLC)syL4A>%TFzabnfAjJZGdIW?3zG^7QvOiQ4Le&X-;8sr*2`i1Fno zGIz?Hu~ttdNo8qxduxBxcbsb9gIX+)WjX{m#vUsO=>Gx#ahr@IqUmZW zqc;(H!zm%@EeH5R`PG7{xn(%HPu>MLK0*h^j^|H(eav&TS01%nf{zNiY&H8kUI2|U zV$Oty6i}m~!xhZxY?peMe{%QS2JfBxT*&dyd4DcbgW_V`eEzBLOYbj$DbnK*_DGGR ziW3=!77qWs{#4zKZS92bZ)f^WzC2Z*JMnpd=j6{f+4l+c;vi`VIY`Pk4wCB)V3QPI z_>=BEz_|dMY2Vi(wpwJw-*GN(4rXpUXNoS_9U{Iq*q;|E^V)F=n3L(pz#nU*069Cf z_K`L0nP}!Fyg&CRKjYs1qVNm9#dA_CfYBelfiGQy^V1(IXyJE{^K04R&F z`<1-@ch3aZ(44wyJZE>XSlbR_2ESNao(>NqLi)#|P!RR_6Nw z-mx-YFR?@nyeq(gh;Nl}R;W(UU9Z0Nb-97M&^}vC&nQ3jr;e>6hXrF5;*0WURC@>h zROob^63x?}f%QP)$}NM1vO5kkGmb4(q=|$I&c5nB3c_3O2@l*t-1u%1;Getd4XF&% zoC%!TBzf;4R6Dd4fNjsTio#hN59GI7jd}++?s9#sF1cDBLUUA^aoF2bHfRX@f3$=Q+bno^bV*)g5G zTO|?Ko&5Ksx|3)9(M|&Ym8n~yqmxB@=>GUKyp*?n$N=O%iBw7Fq;hV-CQ+t_gz}7{ zjodcFr09jpGN@Qd-062ey@~VLx0R7Uu0$3qg6Fo@8G4Q8^_}~b>Thwm0s5q_*$BWWLsA<>N71gwr`QHAzBA5A!KUy8quT&C> z-u5d7R1F>pG^l8{`jk?$kk!vU2(obQ7U&QiW^+jCqAeTUE^#0llxH^;AT!kP07KMu zoEl^*mgkX_w)fNkU^+94t}!dwIPA~=fR+_xX@05tm%x>50}cM9u!i=AcLDEN;QgqA z_l*_HX-XmumU7GlCRYPdPvh_cu-o5IQ}+#-IydSing6z0+^5|efh?YMpHx=LZ>T}Z zu*EN_8KG1fqGVU#zTs)jr-Rt z=EcAM9&YDOXQP#mZST8Ydx;--)zKB}H<<#$5qAoC*(LGEk1B#PODGH~)e+l>96Pqk zSGmK4&uo#=N-3JN_2h73^LR-{NzxpXWPS=!2 zq*SMePZXC&BqW=5rV{-0`E;`DA3bN?vqRHOoeq=q&2$4?`7GXp(XM&EtXtAmhoF}= zvxDVXw~T9nt~!5p_efXA;1712>@fG0PpUHM?@QfQUXZ+yh|tR1;vhDwGP}OYHUCcC zlq?e^?pF0=;^!*Qvff)KLr)|2ges9GJfi~i0Cp8Dbt93~nUAyKL_DFSz4Kyj<{R6X z)YS2uDDV5MP;|($e10}3$CQ8wysZ>C@3%tfA&cE1*$GJ?qRyq;o&|xACEr7)$ubN6 z`i!__taMJp=#dl2uUM5eBjrIiDJY$5%lUL}<|;lcG$A-H%M18ojZ{8vXL9=sH7b(1 zS}BAQ#QCQC=yI8s#2IQPgl@Iu#XF{DO)qn3k*q{v0jJuTTb3IL4>|E;o(+D+86e>nLCxucoPNJRVDHtWw*%tHb*tg`D@&3Zc!x#EFE=V z(+hRtDyqHW(JM>nhjvU8qQN99xn9>_vz#!)b{b`wcd^WSSmquCZ*AS&VP#(^b{~LZ zg1y}2xwXtd0-L*P;6#rRSyz_tJejVVn9N~r=H5PbIXHbewZKeSZtwaJxo$aU!*~g( zJt0xEQ1Ch9diW@@wRQJLQGU#>+IUU6zNn{`6%Pd9Yj9svl9)SjP{Ux(F4r z=0TzKl#I~Z>TMR4+9XpvPL6pIsr4Sa*@dulTeda8-#-4p1Xc|eaN9b_->_wukou!N z^=5F@UNB9>-Oh4(om6;b@DVPIoqqsFk}%1f$oxkX9|l(=`Q3-tr> z+ND)eq$L6{FWx06r5W79b$i*aarX}Bdt5(YrH0z^Q87AIjSo!TxtT*-rB4}IEQNJE zyv2BgRs-QOMwl8a_nvMZ%})UMh;q=kcHt`15gOq?`gNh`ys zn@-ls;u4w}9^Xv;K_k6Ob!mQ!eOt4A+m-Ha7DTXBufDw0?e;GGI~T}5#hjbrL&lvS z0azKc!a0H!gHc48rHri%I462xwy+YY)bRGZ0naoKfm z&_8CxGxhtd%*<|K{xb9Tx&5C|5Y$LCgnYFj{L-rL^F`;kiFptcrIp=SDX{6jt)Hx* z++X-ttA021KCM0zq^Z&8=Qw*@Y8MR6Rfqo0{Kf=W)INNaCODUE>waIF`(=V%{~vnO z-D5e^+)8;O^$HNTBl)U~92^%EBv6@nAH$YbV$LB&5M+N0yZ`6ovOqiBZ^%)DYVrk8 zzu&oixj6scD2Uth_H7pMv^4LX+B%_{qQ}n*bfI$>`XV0rnb-=;zB(d`=)HK0m#!*S z{t!c_p@z;5LIy|F1NZ~s9FxlCqFw14$<>{F6)zvNYr3inIeh-AZfi@x`>*R8BHi?@ z|5yGkMBVp&j(8<(=J03{ubNx_z`tGVH*vwe`d{Vd`Uio;u5yp3^shQ(s?DJ@iHG;K zogWSLq681iV>oU|x9CgPS;ZxZjr zQTNypa}kI0B&P7q8}vzmV|jvkN8sjMR!w z+`kv6wYoxLk6X>uB?ZYQaoC}Owy5dU;j?!IEyrMhrf^|H6_m;>mBwH=#(J3p; zaBrfD96;*#l~YzkL%*PSG{H(&;8dcPt|=4PVA=%@G}mbBOQv^=?=^%LMZ#}4*!QYu zjn2#Hp#V^&AeC}KDkx#Mmh_H*vw~;QA9xfCSW~rblXKkv2QJDNMk){l0GECllKc>Sh!#Y??;zLEYeBTdy7Bwiv}))(c;xc^!p#BZn7FJn{u z+!e;AE6-XH+D(*QEK`@G9I7PLOP9+eH4&1@B$clqjYPO7@d1uM4Gx)_Hz(j4chvWnE&r1779%KI*VhWUlKX6K~1`HegmI6b@Es# zn#fWU+-lm9Lr&d64#Up8Lw5YML&;y0BkJE(`wASiekW+!QyW6pL>lb-y?fFbfHFRP z1^-@m`ijv0D3NlL|4&~m8Q)t zB5Jrl1;U((n@BPca{L6w&X<_z7f81_piQ3oK@F()jk>7FcQ+uw| zs>9HL(@Bb-rWj-{Y(Hdn|dL8^@C^{J5AAfjX12@0tHNh)0}Y(addyO$4ktJIzN+ z7y|yQn+vM#8&s-&*lA}9EOqb$D5-}yMtC(SmH|)S z22T=qt}YWFYw@iHeJ`e}#L9A}qrT8`E}|@SS@p}&sk;=(PELu7$fN2^J9A5{Wlyo~ zK1w-m2uAeDDs(6Q*IHAT`4VS43j!beIg2-~nvepGbDD}u?h zNulHKidMX9=mzI?K98Rfw$Z-9J%ENY7r(ronv%p6iTCaZAV^nb{+5Oy0YS3lJ2&O`LdQhA&FvJ zWa=_EZmrTwik_i49|!0JcUw&X z96b#0$Iv=PopY%}lbBAN+~{NfOdBX0x80+XK}lN0)G#C>-{v5ST}OW8-_XCvfVu*D zaNX1u5Qs=Lq^PJ632>1|Bd9_eJqv8KM@XqgM|;3VXF*E$LrTF%bx!(g(zL=yyY0fz z;t2U?)g^9e@?rbNQimp_p!7HCF-ADHrlgh&#^AdgQXEEDbCVriTG7gnqEH+2&~4f) zOQ1WjgsnEf;FhJH$Z-}hIwhV(Qfs3fQ4@_9y_$Qpp3SlbcyMU9-}|+vINDDx_sYP9 z7%^&S4WS8Tl7{*&@hesfcGFAknRr0f3)WaB60M zUlyI}w%_VI*GSzj>+1NO=911mMp|;=L+B!{sY}az@l2pBIURV7_%98%-Etg}A>tpp z*>swL#9dS-ttm`2DG~(nDXJ+Hc&vM?rp~wO!%m@3Qkd~EkYUCjZE;>wSev2}_;q2c0mvraQ)I% zvjm#kY0oqMdm@_pPOp?+{ZK>rmDQ(<_I!uCMrv|)cE<^O9*BlM6-m{X=S17|Iqu(HnEyI(an zZ9G>9c&0JOruw$pK+*8CM*7EGG*PJeobBF*03Dtxf!O}V5!Oj>#-R(J&{`rxn(C%0!R-(`i{EG?y zl2!Z*b&Ap<1eHpLH09a)FX0XGx{ zC4q9#iXgQKrml*G_jq`R&6Suf*l0xD1m1jPJ1ZiTy-1#`D*~I-*Saam7u3s=7X$1` zX;h^O9fPx+V(Ni_&sqvY``fduW0rA7Yj1$X_)@Rw@7?Y4+t_r_awyiexx~zEQ}~g^ zEfySEmnkg|?b|&l@6Gf14_-3wty>m5h01>)hYr|a4^>8h=1tLxi$?axo4T{xZBsfM z0A)-`6h$vF-&*~gdfS8!!&+>P$SvFvvbp32G%L}XDMsgSfePKBy!}f?PpY8yO9~Q) zTBEb!W{XWfmA*<^sjOyncJhKLD4dB@t%AO36^craaAYALeRlWIF86%{skeNqUx#(M z2#E(jXMz(5y@48URC*>S{W;mPh^W!f5^x9!IMoHqG1%^?mll?dE|Moqqs&>Yb_)PeEGWOm zu^?Crfw_f1jEHO#IZh>X$yy@{Aa!9Wk2^l+b>a=)`L^o!Py6o5`equ3y7RtGHy z)AgxsQ(-b^$i$;r*%l2Um~M`s0N5SQYEeI3E^061{vJU&zlbmEX*w+{*@NQ#Zmfvm zH`t==KI<#$cYP&?`I&U;2QatBX7-UqW`iD*y0)63UFcv;YWTssvn8W8I9CaX(rJ8DxY#VzDHL$xiqKr!;X zrcMSgg@YVnW;n^q111F_CS$J_zL%LcS>gTuJz-}38{JW#IVWnHK`@?K$bihU_J|CO z;~M%j$?aD~v;TV?`W{}RV>*QDkHrCpx6deG!{^L7a;4u4%De-ybpcUaAv2f7r*_Bv zW+Zhhrr&Atu-S4;N!&L1^Ysp1HPUE)e8`ad)jFJ{`z3T=*#LIf^>g$z&CEBT@u3-1 zSj(%wELQ*kBd+R4G)5Za;G};XOY9Y-KI`pIwi9AoE@FJ44@hVUM7@$jynQQ~mIL1Rgq+&0h&2t?`>5i|hfmxtO^gO$)T=mDPpGEXdbn3niY|=a; zJ!?A$lA|A!E{{B--r>Fmtuc!y@Ps}gN+)H$B6Put&4PhfQ;~J2(Ub5%TXA&`JND4@ z5ZqihVmAxRZ+reM@7vNn@4Nq1_f9siBi&=~MzlI+6ffaI^N55I+U&wfPpdPtgwv>K z_H46k!gkKfzmr)+CHt-kN+dDVkpN++Vr!}fn$RmTzK#5GlcrQq%dJLyl>S78T0tyzwk24Q;!qSli;$6pZJsjckf_YY402Pa0z;{ z+6Vc`^LAq;ST+I?=~3^+GXS9u{`9^d>#xUZgpk$m*N1vlU7X8h-tThn81#-Rc$NEA z>UXMF)U?Sh2>D&{KoxXjbaa~D)kA=XiWpd5G*DHLd}T1oY|1W;JBD+^96D?{jgjnI zXE8|K@zis{C_Ojv03p&@#C}sG*4pn0dk%q=!I}PDnZP{%KDp`l-w>pZ_ObzZ>LN=r zzdlI<*&9T~Bcnzqn7jo4M0Fu!2D8J(tyI`d4Ys%1`l6zCPoo-uu*wvcyC2oFdVvxM zCcJ5E&Jw=yOQ=(;omPz!zcxkk2&$R28)sP{e^13@^+(kFTDpJ?Kh^wlRP)PG%`Zna zzZ}*4a#Zt6pY;67#6r_N5bBGTRspaGzY1^>hB<0^Qn62EYISA)bB!R1VhC^NHqVBN zN!-6AV%cXHn+r`8!@rJV5&-i~sXiR{H)1Di2#xWNWqu3dcKTOUEE*b(BI75lG8ZFn zotF*m_aCG_QSz5U?1IW!?`d}k0wkO~i{Ssc<+vhQ$!-Z#oOj=`CRGXcIW0)T_J(TpUo zlxO1^eN;q_?KD&TuOs4S%BH|=hvGncZ+SW9h=YyVQu+O4Y-%cOKU&;&W_ttKQBG^! z{kH|mxoOXVHx(H4nWY6*zp3p08Z#++q;RWn=Z}k*mSEYb3bE%_q4@Sov}A&PGarZc zdk}k3XOxo}Ias(`+0f8Usv5Sx$AyJtDU`4+^-9=6kR&UGKly_ZiWE_lYz&Im`atE# zY?O1tYYIa$PSJ-xRbQh@63H<&!<5Pp{xJ~r9zE$4^|62Cy zGWShL{on$W%HH0gPYd7{D2_8Zw+;L|7ip$X^*nt6FeE)D-=`qr=Rizj5vsSnD@V6C z{rd~;ZzfOhC)-d+gQg9dKFkGwzA^@X8k^_I)XJVeBFcU)kDX{EbrG1- zIq3}lci?B-{5~-FQ-vH=XGYeU?wq_YZA;al?m2mH{*vo`4Z4KEkHMD39sKgt0t$hN zW&TbS0UH%*jM@t!FY^?6F*cEfeFAlH8s}t(&&pgpFLfXYj)P(K`&VlkBafSYN&pI} zc})TQ4*)oP5Q+I+Ei@-P2p=?{IWc>FNgK;Jx}JZ%@k8O%=OA4wiV z>JvByF7y6K$MXJ2@626{>LYp(`)fqdR{o0Ge+X0awN(>@&is7UD2ZIjnaY2tW}N>} zRig0(|DhUu#7F#xmS$$uj&UEVc_;UwDzEQ(L4TtA5c(8^Zk!Q>l$6M_F%QH{t5XW8 z#Z1g*+2;yTVa5G8Fl;Q5RRTv~;!jVP=P|Q2F=>!)E^SnoJj1^-ukb4z>zJYiyFf584QnmN{p<>|~B>C@>K(P&sj2oYOUVmh}5D9LA}#o}sq zyxGp-mbjVOa0iUKc09Vr?Dz45e8E5BT{G3wF2*%uQ|deN3hE_Rm&7N^2LbzG>Pp!( zxox(3JW2YnI;4<$%k`qNzUi~%BO*pEQm!0ax>sF_hEN7jcBS+Rvp-z%swVwO|%HzH$b|D_MQuWSD$`4kKH9?M@Z+TT?AF=;=CBa_@2 zX;%vlBaTdRPtnUU{!6bjGoJs_y=p*lAi-owQ+k{^Z=7s{b7f3ysr$XTQ@%6e@+HnQ z`3Y~L3bHq4c+ZHvx=Fjw&_Mn@ZL-#`GkPcGr;nX9GJEW#(BhG~6Kl4bU@fBK^`sz~ ziq9s_C%m-rOg-gRewiF^nbdO=rX)Wa45F#}i?OFRPjH87LHWg>DAPrOrPgQk=LB~s zrEk+dQLLrLJNGM`qEe5Gk5pRyL|v355(c!IUvnv=qL*wy_QXFj!=pHqRmU@m28mXC z#%aXtk=~%zIZgp$9EsG6nfWDo`=apRd3&~9v$s3)#w@|)MYR6819!;#38@(qsfb?G8aq(A&z`NjXdVm zwcs@rVR)2ySpO>H#865AhOb;p5VgI1NqxM#O7AF{7?dVkOt*S$GoOHRn5|=C6f(AwL-jaz06a_;o-0vZE5VV?q zZ?=N<_Py%SUYSSrplSy$sAV1#MusrnrMO+;cwcj$ z4@s~%{6zBX5&juFS}gOUa(PhQO1#pLS?VK>xXJn1PejuI*AFwR9LCHehekcddb}o1 z@E7YL5x~ymfVeol+@5;-%vBpl{ozmM+8)is{8r|R%aCZJnMLKQ*-`nYAye-c6s3A- zhPNf(J$g^Tmi~*o*qA30qF>&zB}m^}$?Vmd%2tkn@4hdLOhsd4LCu?d=V6L( zuZduk$Mb2s$W)m?ZIm+SPw#cju6iG|W|yK@%uIbTuGvM@#|m|4X4LF`k$h@q*J6}F zyGTOS9;OitjsED=fmB~E`Ltp_%uImK(X+j7w5th!8-Gu$-9QH=Qf8b{NA{z!3I1D= zTYxg%t-z9eq?+;?ox)7uu!42EF@PhSGW9X=@S>Jg_0h0d(KeGsbbkm7(Q5Mqt}k|D z(NJk?evm&F=r*_=p#Zj`^J45`^T>ck$W%=)YeE`6wU1A8gEQ({I%*UFzxW@9oE`BcIgT|er{8jUBY`!WW-X|zAYxLl8dKCj_+clJDRxH$v z2n4FNe=cK*Wl9^G}~sA_%qKKX!yA#s9ETxA_b8!mZ?9djwKLO~D5!|)$3 zig0V2%|?2o0FnZB=}&tBEV?)gN-uocBXDtzO#R5Tn_U+3j*Oruo)hX!3lQu|T&jV` z+rSvhy$!4wT5khu;epousqU%SVZZnu4jyK1@*p5z9((T>+#^k7x$IGVGy*W*{s#P# zshhNW*xdc1xTQR(Kv&B@8jQne|L01pBY-S!pLa1b_B^QYI29}Ld`f9x&c#AUafB0i zAMsw3_3`*A&0qG6<1g)~aVYuQyugX^6$3Iy>5l-MG+2=CPR`_L`EgJZ_atD@KWGT#n&RC8 z_QatECjorr{!s=^j|HF$wxd$)r z2r4r)q?qF7V-GmFg)G zuQubMxzRM?+yig);XOzH20*%&wwj}vd{+_LKB^e|R$nTD=LJCKu%{+Y+Tr!b1iYut-cVlJb(;x@PB`@O0N$I>W3}=(I$emOGsSQ>`C(VWq=QC2!)DCfk?m6DC5!yZ?^469@wv7K^E?zK4#Onpp~fvt%NZGf4O&PY9>1GZEYLgXCCj3c3oHT zE^8SHb5}@&itLBQ7VLE-F$D-AHfymp^x>4EKn00ej$$2zcNHz25(jd&;!%e@N9`XE zF?fOqoLz}q`U(r8U7x_u3MP&y{M5@O_B=QiE8rju%_~yuTT_Ak_k?!9$i0altP1Vu z^FljTsdQSZfH43!B8`eT`v!b^jAJJ$P* z&GQj<>NRX7XVaDW5+l4gfS1mo%!dkfQJO&!Bt@Q8(&V`J5b{^olh9u)^PJUEs6)Ky z@pibO%w3_QJd!wN*W*=Q9~hbzYOIkTO0=mbs%v$bl3`PKl>3tBTj9@?B!l@6Sk96% z1^6srVRjH%XdWtnh0Xf@V(GZkGc`M6JsH?d|WF3}z zkr+z8R9FIcM`P0>$dngr!>@O#cpF5#IRxF9?QfnN?YgnzT}J9Y1t0z>>x65sk+#+E z`kJ`E+pH-|&hX~FnR#zpd0a&bd~#gu4PAQ2up7il7U~_sZYuI_ynj6GMnwEImUCN4 z@@ffoxiaodC3*uatDa}2DYwsyJAP?*r-lxRwD}T2 zGuRt>L}C->41G{aVk!wY+ctZoztwx(vvg;T^ilwn{*1muokA6=Q;vpCLUn#Ep*oUC zxI)M4&^`R72vZPHB|#BZ8x|G&`dSiQ8=Kb@HiV8hgx)mUk43{E_8K9 z`f1@kg+I7ZBx{kdRWwfe!<=Q3RB(YuKS#uAIC@(Bwvta&IL=hzGgLT^wxh6>XT_av z;epxN6Ri(JAIJ*;DpJKF0LqKZf!If|2KiBV)JJq=&QD$t18}zraQh@3j3Vu`6{$rb zwv8r=W1vn}d`F%?7$e`HKANCDpNxE?rqkHOm_3<6!gaK$qb(M*+8JbCYQWy}EfF*3 z+V`r5#BQY4FS>!9k%@j9$6mDg;l9??lEQ+)t-Ko9K-(j?M{fNyx3n0^{G6#ncTPn6 zAMpgtiE{%PVQzHPQe@~19mVs_--`=C9-{K0JSN?B7OGI5g(IOnHTJ@QasxkkC=YcD z$At1MMDp0Yu#h5SMYucnG-Atb@T(Xc|9>6A6Uc|~EIdgF&zHxC@O*hBgy+lX4M#(G z78XK_)ilGleHv}-3@dV+TFkvdDs>3YH7bNB+VyS;--(p0_C>lbbnbxfM2Qg&8=V)! zD(e6qsFModQIT@is!*t zl8@)vPdrbJ3Pbsy$Mdjn9UUz+Hl7DbaUO_rO=0Nhn0TIRPmJgBvpoq{)BHG#J{r$6 z{C_*1X9=(fQldjzBLhD)CY~o+2^Ziu{YU9(5bA*V` z=Alb4Ob%d~Dl{6mOB4_6-$kNv7xXlu2~fHZPZOXN6Zt*usr%ZoY)269)N{B!%Jvx5 z2@&*UIa5`f5aC5C1c)qnnoyw=(R5bD6tDrc4%$TH#Yl;a%N9-y0@~qz>p5X#r-DD! z>;Cqoz9am07Qu9S`l#;i1O5_I3^XGXc8_!d+Ntza-hIB4Oa7^#jbb z+H2svpgXVt?Un?T_*FQOSamS*jP!FXI6o=80gRf8#~~XNtkyIkra@v!i7hhH|G<4{a(Ob>%p|e1X2r?j42*V%YduWu2toIpT$ojX%mt~dNYO3fKv7cW9T?M_Q zTtAWnUgP5Ni96pUe!)P@BZz1z3TC{%P>1|kK|~`#MHyxHkP()t26G()QER1%Y@T=C;0TcxzBcINL`6w3BoG!Xx`Tp3U8XdUHPS*3$_UO|;yL zCR(ARiL%R9!tq_ZVra7mGFo3QuI|JE=0j@CcFFj9)JXq9STp1(r*V1IS-65=ni^BI zR3fle@39+~OE#D6k=OE}HQjADlRea4xB{}zUFq+_mClks-rt4vx6$kGO&BK$V(`WQI2AK#^icV4d=V*M+uGMFY8>ZM#$*qK|`<)pclo*{` zc@>v+AL9z`HXd5C+_z+9G_)PBY(PxZf*dp*2HKZ z`Tk!a-X~Ao72k^-JTcy95{$B`LO&_Hz^aqQgNdrFCeOtEwc?|QtF`!9ODs>srw=d} z8*7$;sI#A0J`FB=IUzpcat(BtN3bI9ha*hiAa;B4z*Yhw!Pzp?3bqAQG{5l|gBRSD z)%Pa4SF4MJ*oXi=CV=Qly(UW;0zmOdL4=-$284Z$`Wja@gb3Qp!^U`d;V%W>C-hgl zS4Y?AuqBzglZGw*r5vjNlz=7Hh{qt~WooqqKF7r?)hc#P;*~&`XJAGj6R)&M$1BzR zPveywiC02hhc;*YqQHKqkSoy~o-EW#QSdyy)uh&o*4?jH zN8n9J8`P%FBX287N&0jW+pSWh3)3@RPQXK9{CZHP<6$Qv(WzR=N+4Uo^a#DIc!=uOVfiCWQk0QwRiXf9l+t{>w zge(gs9?HUlSoUpYZi56pH7J5Mz26K11ma$~5dHS1Qyu(a8VP#pC+KObk$#zb(xH){ zC-k|aF`|DdbifoyYz6w9M(D$srD#3*RDRcDW{0B#tfhFKO*^_+nB;Wh*t;ZyZyZH}@fygZq*c!`x79%u-u)fmR z^rJJ!kilWr$n^QuLC9dakU{@#K_bL-(9u!ko6Zhajrjq~-zKPB#al)mG-Nk-@l3H&M3w>z`UaO*->XkDfzC?lk+u+Oce-e}C!zX{vVfXBAb` zy&M-bR6jNtSN8i~E?&=`ufl~?7%uog4HA69nw5>>mmL0)BKNBJTS;i4;)PIEQ<)hp z#F%)Y9c}>@IOOYUlxPw$w25F(S7|LJW2OK<|K}y*g`PChUjzW&F<$5>GQWx!g3X3( zsCc1b3AJI3`p3cf|9CxwD*5|`FbzswCh~tu8n3baJuw(ackg+IIM=89mnkh)C_ryT z4~)Fc&#?b;@bg~>`U(aG$o(ow%7Yb_S~G%rIVwt{js*LvI$DQ3}r-%T$} z{=0rN(E0tF9wW+k)f+co+HaO#@`r`rARinJ6tVTs#zQVXt!r=g6~yc|%w2j8s}Eb`00?Go_UcP_<`UOt<{pn` znsTWFhM9WS0AkX?$c88Q>BQ4PC+qCS6F9s5)a*cU^)6VU>fHn-ne4Lcw$ri$)2jzE zbB~(U13d5F?Y}Y|tn*#DF*w)ZpP>>`#r_RG3#VQEm^V7QUperD7Oq~Z9p15@S09E+-VZ9!Peh5C4a7sPLiPn+j$ z<6r;47BD5jnG24E$O~0^f1Oi$KhOC}(@vjwLXveqw37>|XN#lG46<@S$E|hYiEMK%i zo+#tO3^|#>Nx7Vqb23X#uxv>60IBSt`5k8~@PoxgCvwaOZ#%aJ#aAu$pO<61h=EoV zmtrkDVx+~?JZ`@RDkMvBUstsHy#$U2W4WzX?kO2xCpG#CNbe4VKvjsvv!j`Xf$GDs zlh^YnWVv;o28a6s_6j<}h9Jt?6hOd{6&RJZQDO)N_A`>nv(XNiF z5aM-eGDphMJSqI1H=oYcLCKA&rquatF!kK!!t?b-r&hL;{4@K4KlK)9U6O|!RmHKG zViYslw~XUhp&rQE@;6T7EbpmvxHe0!o!eim$Y`S`qm7!3Hfl23 zsL5!fBBOdQ+n))F5p7L>qb912nyBWt_?Y@q@nz&u#3b^j&$vrW$;#efI=@gRhuuTU z)?j54_Z{>0r*j!;oX?Irza%N(>JPTw!=dfedMDa(%*>EUt2X-iV^O2Ar{0No919<9 zql7>&u}KYnNg`<4T)gEeu@k0V38-kHwH?Qj5At=aBA7UgyE?!M^D}Ed&*wCKHevlp z083HKS_wu(VEb|}q2-86c;sRDc{RYN?mN~iFude>1UD|*0F3|(JW!l$u99zAS z{@{9whFcjh#ArcyopYrPs*T#mZsDgHVAzCc64kjVz-!Zc@`VBN`f*%1CQFH)cjsoF z`EiLLOqSz(dVE}t^N@?8tg-oBcdfbywJ_>4qMR-r?QvC(hJ~5}!iir5U zP^Ae!3IYx)tG89}>4*80rgAgC#FxY`$@V8GA;5R4uI-;{NjnvU-6F5k>z0G+h zH**Hf=>Z5zjU-C7pT72;$<6$O`kK)&JVCubVo!)LHRGD)qs}wc+j27>{n#IE`D^dg zvYw`q^`)ocXvD1NX5KW?q&M5xQF1dE(b9z3J|Wy^Bp=;=HS*ldz)71bddH^n{h9oj zkA;^(?#<2Ie7#ybz4EFrPtdFY-9FiSd0Uo@NY*bma~&_-gD2Uf6XLsxEv)VaL*(}( zGD;7{K8T`fA804bPH{t*nM)34tXy4t#q>LHN5jbd6P6=-R_Pqys?diGgiCL)^ z{fTL*H;a;IVhS!BT#vgD5`;=A)QjvPd&t|NdcLA;PJ*X$+Y;|LQ-l7*3^R4KC^<)7 zR^J|c0;3fc$r5)X(`reio|-i$7I43M88#sN#`HAG{6#@axq-yrnV3)$K{GX6l*n%z zY0(`(`7}DQBD+{ec6AlP4p0IPQ>&^3rw@|Mh>HPzn4}NrIpkvTXd57)Bc#F~y)ZJ# z68|x&_+yg_%ts37dhF3UWBoHRECXQ!-_nb!2z!yzN%? zz=o`~dyqopq*bSjvGaPY>gBHMuyw=hidg;T>!D zYitD(&L`6BP47t-37Jj$DNo{&mi$dfPrjEbWLdPBR&HqtJyx5~HfPyp1+r~w*q@vp zBhh>Eat5BmXc=U0a3PSqJmpSurX=1EAfChDd-$8m-`|*atb%6_XMKr7YVUArB=k1o z)Svi`zRJWVUz}KdP~(T{Zjw&oxt8D8KHIFMe-$j7XK-^vfX8BRYH(3ehtMhP^^Wm6{LI`#9y%k%y8nZDY z9d$NVJiw18=K$_D=}H9Gk>ybK63e-v#3K6rLl$XUGxyfa5x94rvckWyjK<%@onH4D zlePAp8}rSo@EVKzhpf;WgAeeiHMLWG2gkC?|xmpF7$@wy8+R4Ntuzpibi1oQ{8WaAdK}9ezH}M z6~B%j)W-I2A3dcY<{!vCo4R9GK?488?amVX;TO({XV$Ck__NHkbdz2TQZ9mdn)t|p zv5$P4N8lte<5a{m3(3)?Mg^x3bieX%G$qZn^9&Y9^-zA;UwcU39BQ3-=ih$l-HA`U zbA+_?(%{esCf>RI``(@MiFZ!>fxZK;?rW^DQg@UTBxic71K)IMeyl^q-bDb!=sX3D zLLNGXf(mkns`C$cP$Ta98h$LykhP6vNuvor@+q`h)L+Q718oyBLyx$awaAWLsvOX? zC?|@9>BFZKT+d$ze_!M8Klpo^zd`;K|0Ar7Vm5RLtGnbv=_Lh)iH;SR8EZVTatB6` zH7Bx}6GeIpVv8WuVQ_Zgm<+Ll&e&gSKU%{5vALq;%uQFw8Vdhsp#fQ`N;bMyv3!1` z66dS;P>XPl&$ze8o({>f@^~iS2Mcdv0CcO>@seyZwPo=zch&hycVy3c5iL^8Zuzbz z$891G!+A~W7R2n^*{GLR$aZeRv0Sk{{eHG-fmOQ>Hh>z-QmL2XDI1mNda^d|6rnmP zVwZwObs4(3CCL(k5C+S5Z*=+ORmRm4Wi-2JFv`Ln;FwxhhBL*3YLi8chG)c=b|t=I z`I`k4qWA&N8Fzo3^|mrMQ*H8NF*~&P(ggz0Je$pIQ~+8+^YzcZaSu(( zX55m7XwX_l)|acu^^&gW=BDTcEP0ItKjgV)f+}OqG{w`)RnkLVh51#Fm;uWx6hh-a zGDddpX)vM#owb{B1@!r4`KIQ3NljHg785(EFRhU35X;Raf^Jxw z`k?($iiiI)nSj|&QcrYS0*#-ZN5x&(X*H2Vis+O4%^_ptg6`;vi-y)L)eGZ98SzwK zWq4hgv2h*S7Xn^dog2K0Q|R8%%m%*6Akqrvq&|kC2B`l8@rvqjrEFIGL6K)AIHH}) zW0h5+HWcOHH|5x|RiJnbkSqR7oGke(QZh#?o;n0Kc>7fnUKF)Pr*=Oo0E^FgvAgYJ z4BAYNk~@qo)1_Utg;P|QsovI_y4+B|)uuJ(iWE0mb%yU*!jk}yOXU;G#jigS%~W*K z4DB3MwL+nBrbCTaWZ^^Z)qJl$9GD~g!7*QQiYj_DIF0_mXEXrkR5N`{diOx2tFbI` zsrYgqk7b&O5$|{BV6JE7yqA8;>=9lec#mlwviyD4Bl2DTYq!GLw*7}2%i0rX@@-QzFcjm)#Ya9=E>Ur z;yfvm%bw5RQ=NDk?q2s{IIek_ODjEj9zHE_2IJZxnb2j}Py z-Xq_8-#dM8>hQ$xrM>S>k?$Fut6@F)rjfpx6ZPG#pvwEelZ5Bx=2{EFs$237U{@hg*H)575!=EWzi>JVn68zBQ2?R8lKM@X_I$w_3 zH^HMlXWU&&i*zQQS>0@99%^S!rB4eoYiouTfZ@OKVX9NVD`tPlNZ-rtrq1V$^iu-0 zspBY_iW3)b5=d0?@1(@L`L~GDbgARTiPQPFgre4|gpSPP+jUFcrFnIi06Zp*oWu*QX%>JM==otWqF*R<*t%k(SIuz zkJQUF*e4CH{S{m&T84qn?sXS{L?EpTgL?91eeJ7(w=+$Tu_L2Wd1eeZhWKPjtQgrJeh0Z2V6b!vdvM$gGeOCUQXwoDF)*w0cPrQ>y_uPsN;{358LNk9)IH zyWFqtS(&@xp5Op_dBhoZ>s}Es)*o#`l8gI3R<2~Nj|GwDV*VkisjdfObRK~gnN(a$ zCMkGR=5BhK9hHGIa&|d$XHaqqcC6eD%2ns8AY;qKXGPpH_0-*K_$pYgBI4gC!sDY7 z7~uO@m6e+d9@cXdXK=D4{Y4D4z+M*s=`*(%zWG{ia68+ri5Z#j=;J&H=Xc6fo-sPV z!$V9PDN^VtSO0L7H}A<`{0J9Q!^OsW;g^sOJGPWgLpt1+dzQ*asiUQBwWzDwPOZax zfAWmurxw(ZvA6RqUXo)gxKT|jh!OTa49D?SlroyXb=O>kqxiaj zs2B3BTc27<#fY+3V@e6{RCg!a8((u?;7e;zcc!aSWvLn zI`HYbpnIB{g#|4%px!(U7eINlk~b%Ql@EUV2p_zh58_i&UO2bp0e|AX7BvHbj+l)_ zdoI?phMaI6zhsv34Gf+lZ^({Gzo^#A878PPbTrY#h)0J%cJQ}VjSN1IOY*IanFM-t zDm{Yl{~oqeZ;)z}Rp&Gi0VnQ5d{ly|$3qhD{`@fOlx|k9s%6F0PM|d#y2*J$tRAwY z?m@aDQ!<$my;EOk%%-uy3fclnYUc*pE*>*4Bxo@KpT~^>Tue@;X|sFH57~@rTj!Ps=@oEc);A|e!)i4l_R^tYC9U8^@`4T zs4FRtEeNQR>&)foW*om0UqN(#=bcEmO7mzqeMr#oIW)_>S;pp%q5r!(KqvZ(Wb?D- zB*xD}1y=3cDx>q?xt>{4nmApMa%^)iF43Ls+--I4 zn!@GQ@TB*@9wwAgGuh@uZ#g# z`W_j)ntP^ROH4RtJN-jv@RM(%=Nc+iK0p|DM2r<}YnM7|ZyVVpTFU-*#W%SwV)X;+ z_^^`t#R*3g42-{5CAh*_8SCHR9S^wYJP%m$aRmHFgi!WHD=N`4b%XTXe?L!m ztP&I(b9Pd>M5(4uM{0arJ{=1a@%yw9-GLZajM_5AevQ>$C-r_bbV)T>fiBB=-x7YHdZidJm^=6Ud|wndxYO$ipJzTK zDwTPl8xTXm8N!-BQ5Fj)DoR$}gUlQxk{>WgT%o<)Ww_2$p<%}Qz5@Q4&4ByWz#-*7 zez!MhuBkzrxzkVlOqMS?{c*pmdU@n$qy^^+&!ao3^xnWG<8?|rQ!|T-Vwnc8Z@gj- z_Nl_$0ArYgULsoI0}QG-3si7&kvpFr7@JQicqKqNop{9u)F;d>Qcqewzyk3BD>Ymi zHKK2s0F>uBCorP-vLyLL5?QJ|?nGYz&e6_vc^^6iku|+eb_j?8Scs0LrkK)=iSox1 z-Bq&)L#Tmntf*26Ly;=5zbMnpevxZ1Vb+>u$?MfwW-STT3CLDi>v|YozzX+^ew1>1 z(?z~@M#S6sBoRQM?@mw-YH@jt@^#2Z?uUg|LOlsR{MEBSB>tJ=wU0`*L_}}0auTvw z&Mp#5Y~21hf3rnmWVU5|RiY0;(DLL&OKI*Hqh!WY@||=Sc@CaY5_B-U2qKdb>XFLu z{zGk_1K+?qXBLz*5@(3V3LO zlrock!!Hqb*3gKQ%EartD_-&5RuI=%>$S?u>&N|QvLGi17#z-E>ar02Lyk=0T-+@L1nJIIN~1Xn=Fre z6-W6-L|zU0dld$!hEovygueb2NqRB~G&F>#qkK1W%Ed1R*v~(Whx)J*xwpV}qiI1a z#wK{${jg#%^`r`omSq;1Ush77z||0;@3S{6@ZDH-8CYKijn zLs^1SP?nL(A{hB}N*eWB-k5vXNxpA3@WvC`MCZsru#I}V?Lq{5BuT3-_qL!OnJQ{ zcp+(uv4{bjvim;+1x{IbdU9GTg=<^Givr1j`0? zt6aY{d&;GgyY$9maPs`r2ruRFI-?v;9H$(-oSHhfkz^)O zjgJoALfI0vTO}UEf@@E8`>oW0V=;esEZn{7H1To44Tn56vGDFyzrk!r)v@jz|1t5* zb_+JxNdlcKh3KsjqSvf)@{p%3E~(lMfU=D{KgrN2b8B=8LZf^;lxrtD*!}8R1qF5C z!1+dJqugM=814c}o-;LWhD8UPrNx9VBxtFFt{oaHvEQdC|J7zbQC#y0I5Mg;!Xp~^T zrScKJv{qXUnuV0D5eg>m@h!cyYB_Hwu8ZN6z&J?HS8e&9utIy|8KVAx@L-eE=RWx> z89?G%!COIQkO%#!0L=XS+pE4?{qo^ne*#z90GE3u zqb9$T2bEr4)D;|awV2=f7oQS+_1gAul1q`wls((zIk`zz+Cz!M2$dB87$EE2rZB&yqC;EV1W zojItz5MyKGA2^c$mstN37!KGU&otSU4VeZ?8tg=KE(CHB0Mn3Z`WvpoP{Y)6%e}oz z^9>hsvy~T`htL#2?iQpKfK>hjW<^Qy6NKQp?e zfT!3`9d9F27Z{&=f@_v@uDOMl1yDuR;q0`Gg&fHHGh`jvXW7MG-~N$jq8STm?MeYd z5cW>@ck6Z6I=F{=bXo73(*!Xtjn=I7<&wp@9XOrc`Z`$j6zPZh{Ouh>iv(oiVpQ~D z4kA^~aOv8Q#QaZy1Jr8l%zrdFM?MI$LG$NL#!B@?$`{H+Fxm%y7br+Hc?Ml?TA(>< zT0lTDEsENzBh*$Mzg^MJQ4rJv>If~8W5-rm(_X2WRHf@{Blx#YrHtlL14>d_kq5pn z$sp&b3PbK^PDgV9+l7Itj9D1Gg{7r$b4DjMGfU|nnXh<=#QYa}^V!6M zftGg8w)*R3#ejneTijYKxxVniK3gTU+a623g*7lr?4MACK)GgdkPnCAcs% zWAZ(gzjW_bC3pijZ- zU!o=MJe>GM+_}1pO#S8K;sKkS#RrvVvg8MSZ_L5}gQD=IzIb@ss_B?(B_0XEtSQ{H z>i0v%R{#IY-ur;nb(QI(I}IqBM5s+IRobp9sc4gy1lrOXl@kuh!5-iUB&BU?IN==r zGzU1sA)y^w(NLQR8F40~(TU0^&O|3OMn#8lqLLIFs%cAYR7PV(jSjJ?rB1Y{QGV~c z*0uABSw&KuS{P#`_ZR4U6f5K!n$){E&TmiEakBO zk3s2`IM$*ksM!x?ax}F2J(HyAxPeyqc8h-AzB}X`_SHu zr}#brSn*UAJgCRuQ;1WFscG;jTy#qnJ(vezzVc2d)n*5^$8}M3H?f+rP&Q`}E6kjQ zgYNZLEWFOl0+IFEz29RErWL{mv54{&LO;%t=>s=8=0@|Y^l@)|o)Kf7K4yTD=F|ZN z(`ybFB;}^-!Tdg!1V%g%?_8Btj{b=J>1QM`{$qx}uPg7^vzOpx#sHq(JdWezIA=V* z^r0W`U6R5ffOqHMNWs8&&F&lLYkZ&YZoHb8ThsWi)P@V)&jsK1L`CDVf$wJTelwc_ z?+%9d=I2-L+xmeV?6imXtvd0{3)mz`-|7Z`9SRjyrLM-y8k!M8q9V#;jOsX zPx;P9d?@MKQrsS^;2y%+T)gKzi?_^lSa8`C&u?+Tj2G4s!kJz#3Jv4n2Ng9CSXH|4 zT8Dx~lfh+AJ=?hZPp{bdp51@U=)SXJ;CuKu67}dq$j28GMo=!JY90CoK~p^6i$pJy zAP&|q$`Xc!1FyvC3UtVfKoO>}Co)hnSfpIY{s2#kga`6*yNS0>QGe*2D0|&SF}cff zYsrq6rFvk*awveM*Cvk3yHBXykF3n@$DT8Gz%c8(D*2Na&}y=oQibjv%ny7pYyXuj z23lT@&vvP|T!!s#U0gc6yr_S-T6Gx;nth5{9?YdY$`CIi>mN{SF4IM*1GVtA5MKBW zUJr7idml3qqUohD-91TV1{97b`iOnSN-j_^W4()5j2r(TfDqGHPwU-z1yODa{f;z(|+3@j`3fyKshWjf6M?a>n zyiDg5IpphgijIC*U9LlN;eS|Nbs6(DuKkz7KX9z*0X8BuAS|NC0|TQU6M0j7j|7TF zKP)<-VN`3#{u+ZHR-&Mr2|^sr15Zvqn~QH%rJI4|_Pb`Faf8>2$_GYtI81^@gh6A) zv1fCNHZNyTdC|zjOxx7N$fs$&4ue>*D30%3J&GG^Z^mp>#>e2mkMPL4dBQM5H#nN#@3jMZi~3zNP&VMTCY0i0+2}ASn_yA-1zk9J1cXIq%4Y%% z0qnoAi16LX#ljqTq-gVbS`g%mLeNl-`x*&a{lyxIt`M{rChBb?P@UdHZ2N#_8IKm5 ziK$i7PN-zDo#^KJl6Io4_7=sY&IRk-Z6?PTbB0$yn`Uy{+e|hUy-YKCXa*wFpSgzc6P-bTQk;v9T=og+NDzLN`$IZXyH31P0{+VJxV z_9``*aZpFW$Uf4|2l{2}b&3LN1E?SmOc>s1pTz_^C7QIDP_ zoD~;xYNZj#F&tv}%ynnVX1>Yy+2dmmI4e4^AqzVX0~?m_4J>!1I2pzL zOAfIZSUEOhbj3ZyIeq)@k0~57zzFTGZ?I*x539m~N7gRYpXYdbZ5iJ7nYFeo3$Gto zTef`m4LAW79C$-;?==H_sPse*zJ|DW(|O$b+`DOhplms($Y0mPzVCWq27fa@Zh=YZ zxB9vNSvfG5-G3Y-J14}8+xz;^at{tC=zW90KpTeQZhRE(0Ze}HSuX2W+$pKRk}?OG zQOIRJ7h%Zq>RF!GDIdt=TU?4Ou+bYVLP3<{aCD#uuW||OEk6&WgbXSYk7oz-*OOUZ zv4Euurd@ki<9h-rO5zHK=k4)6hKG9eYDO;tUhW+vdS&V^RFQYNYl8X+Nqt0E9}TRJ z7F{1d!fGeGe>d{aI;qzg9$I*1_tFJ?NCf*^IE`A3nmI9h0FMhZKYUkvAvV65IXs$! zR>S)|%tFy)@U3R8Vy|zx!tDf9)gu{&Sc`$bs<7l>ub%4=bBY+!SYAOj)Ys9=8y?S}DbH{5Xp?%F3Z96W!qZ`JgZFD!hE_Xn{5Kk$U9 zH+#J?&%bWiGpIO3EF62=lbz?7T`Z`6!$w+)7ea^E@M@di_xovlGU!!rcx?C2-hef% zK1cQ#b}a*(_dbO;`^;nomL2o_9pA9G|2ocJF~MCnN$h8O7WbXNvT??PsFr>#xa|0{ z;~Vx4UZHL0zZORdyX&rPvb>qmi-v-L)7A!9BkNo@VY!)=&~RB7#Gz0GJnbg zihDC#R3`5EWkz|cb8qIohO5Q653UZ}?0O(r_Oz*iv%47rz3CTgzt`aLaLF)o~DW{z30dJ_Y1S1nF}=3zv_;`{I-t zL(Ir0>urp0c)a>^fQ!C|jTAg&dVJxyepZ}&Gz*Wt_|*G3aOH35M1&M!b_e3Q1T3s^~+uc=v_8< zF=Y3%s(aGL`zaOvlnQ@Jg+GPD=N|>Q4*7s>&p!cwCsKYNjePzGUAjvFN`suPzFH6$9s! z7!!6c;7xY=49e0!Vupa%3}GMazve^N|F9_Iwt?_%8H@Elkh%@4v;Qm_{iK6h%>LR> z=tH&QV^Ijs{@UZ@QQJ%-wa-D^Z})q zzv>hXK4fxz3`L^L;&*s^KE?YV(&E9}vWk#RloU(7sDB^D|1acsVb$ND_}nWP=jXr2 zC&e-K(etWOp0@kXQ~>+F^SA=ulZi+A5CP*o=9{w*qBoZQ{?R3P+Hl|P^RrLlPDf0Ts+Psl;=8jW6vcF|RNo_GVia0fb>Rx!$XfLq{dY$H-S86o zLc8Cd!c}{rd*#CA%Yow7`5VSxoW?s>r~@yBsMc5b%OfDOUt+nW37lM5)vh%+=)X1i z>*ha*i7GaL!dL+5V^I8H4yKp&%r8#0#JE;@0oxPibT!^D{v-|r^r1j{vMa7X&0e*|AQ?5T2o&5{*uP7 zwwcuO6XgntI0NPC+05BraO$#qLf!KSzWo@+yT*;td=r6u4o%PHLo~XSMwjqdWFzY3 zw|o|Xib4ZF4P}4vM|kr+ZEip|dh?S#xIgra=8=cM!l`Fx?V}53(v7TOUQC_H3Tv$X zk&v~2_X7$qpa>6C_TUjnl)>hf&_HFw>|uSYM4PHJY@XNC<%_LMZ7rYJOU~i`<}8+A zGq?{iT{&oA8 z`rnz#$s#VdFR?c_Jg~11r2&(1FGx)O18171bQf0jUe5OTApV;AT3GdgML$E3g#vte z^8SI$Py8-}PtW}I!@6`=Jbe{f=p-I@+?&}4Bvlm}$ow~i;R=nnt>E#$%+G`Cye_o6 z1Sj+H9P7Y}VZsBMA4!wi4X*1)3zt8Fu`P5Wvj;4lIYz?v&UfOxpkG!r`%`pB+txn8sL z1w3c5M(n=&*2_PODBX7!jZ0@vJH`XkQki%eT=%U=`^$Mq8%LpDu`q?dm*Q*?-|+GH z?AOska5MIG7gu4$%y{#KEx%pZ{ih}PaL+>zB2PRFaRhW=#Z|BZ2iX7mGvII=bj1qr zDA6jNCZ0d%)5fsjX7Y;_PyjBi0nOyTYwVbq{4Pv!Z&MF^wybc0)B~m&0r6ZCT3o*V zd$ayqg1@HqnfB&Zgf0~vXVBF%@WMARCBUfq#O$Ly&Vq)34+6ev;K_x{e}KzjaNnnA zF>VCj|EGpu^FZ>SvN!$tZGY~bl0ypU*l9+g2e|hVaS1c0B!*^=0(0Gx{$x`tx62 z|4mx|GFN{K_2VpQUxst`P~^AtKlB0k4;ufgcEdkp{EyS$Z~T|`!{1~4pQArv{CDCA z)7b{&f9`|u*BO5lC#KGZjQ@9g;a_k36$9`W!hg2wZ~j}C<-QfG_8~0K;2)+3lttoS~73=NMO@8-w;Pf&o zYREqsGG@qQB>Pr;=MJQ>FY}*C_O9qRs=sQm51PP-8Mtpni;)bNzz-S8&l*W(0v|Gx zuimJ2X5XfD_L#umZ6TNWA)We)zcPWR7`QL<8yZ{jpb7l6k?b*&?>B+>8_7l^sqNgi zqTEO}8A-7-7l9`-{}IKBxdt;O;j^1rASy_bI+5fG^JwQt4WYT=I{>=h6` zk@;!(ILSVd`7iJ-)4ufFd1mEB=1d1l*PGnvO=8?4UQXTQ~ zD*^0X@g~ady_|Gnu!TumTWn3Zm(JRk#02-52je@Yx*Wf1LgdOl zWit1}f6n+nPXCPYKS%#5vvGf9VtOA29y--@`u$ z|JlF)q8ab@t+@1)2wHma|8a?Piwx@@cUN$VHmQdCf?gW1Aj66 zV;uUIWIhglN#=L)7X^WGSn*llD~;^6Bk*S#|EkZyf8lnW_s`QmXZ+9MkmlJl@b_hY z{PP)jKQnZE9-gyP2EXbH@K3;h_W2+FLB^W13_D`*Zybhy1b(q6euVZk>c3I_cboou zkN$f%{u(5Sj%6O2rs{G{+W;)P|%M~~ro174DF{z)GiSs33vv-zn% z;*{Xh@!db)j5{sg+5PjPV}H2h*v~K7{j;KFc*<}5XBo%lGxD=veJt?)dA;$qC$P}_ zEjAdB(-$FJ+e-DN^;^2*?z?IWOfczj?Xn)W@a+{`>pm}Q9me1l(-;2|HspO*{VFZ~ z1$+zO<>Kr2PoA*Z!}mgt|2sE1j?Ltn^s~P{b4}N)mX6=_Z&w}5c=nQ|;|q^H^w|5( zF5ukUnW4Psw-RL(LmS*cyNpGHeXSSn1MEvy6ANt;dmjwroziZ#1I)%bRDjdH?92xUYd&UG3p8D(=(VPU0JbjaP^TDy!AO=n*B9CN7jef zI2~R3mlHU7TH3ep+Rn$h_)y*@rkzLW3h>mWGhXMoQyox%aH?kQW3He0^-MMKfPUNP!zp9hz`eEUNEe;jen zj$%@#%STVTRrJmVtiXz#UtL(XP)2TVh-y9B!ATxzQW;; z{cB%l)hqC<-`>oRF$f*;zfQieFa!@$E4=&c`yYqe#r0@tpt5wfed&3-l#>LJ-j_^z zd~?YE@BY7D0>2)8t(hVn=UMtW`z#%Q=WDoPbtZ)tlp21I(EUP( zg&r0cXN8^@>I975%M)54v{-1V(6G>Yp$$S4LVJYv2^|!A zK_c|Bwe~{P5kbe#)-|uRs}}_Ro57F0Sjq4}q?~5r5z)$8hH! zR}Z$~oUE-z&pU|0S$(HGavW3Z!7I@9*9NQ0>h6q`)Bnz%wgl)q5t;F5XYf6)ewNEh z@P(ij@AKhd@FC#iGjsD7JaW0)Ts_S14CIstuk+ywJnK};7OudZxGSC4V`CxDxe8ke*Enx< zZgg&Q);sTTwmQ99tUXe;Y9`@i&zt@KsG`jkAd-vffA6^LF ztN*kIr~ZRJd}YklPy7sUwiDV%`(qxQ>F0qj5uEa=xEmk0)SN!x3iKA|ekTQL_4GHn zax8}+c*@ywz%6ue#xb4!9-MX*G`qO&haP+h@L>kkROT5zjBw_Jd1Q}M`Q(dz{5l`M z*2kyYpW&?X;rTxPO+NmOK3@5F4D4z7MF&aaQIQ^gDK7ZwJUU1k?w3!m&!w0;q{}aH z7G1!JIF;aRyZe8e+ID?G+Vv@E*F&Hv#+US8I}Qzd#vk^J-cHjm>Ok2qR)RAA;s3Y< ztq=WZ!z(o%`kd*%86P*47@ZQip@q8--Gx}$=ZJk;ns278v(CCqfV!DFK9sMW~bfRfnSHS3x6Hw z4yVGYa&Bdp*KP|W>k)GcS~YE`a>~Ku#+A4L`~jy1u@rD`cmzEm3~A+T(lJehYWQz- zFkpzQMC>y71JGXq$$*4!MoJY($-0P6qtan~I$9H48dubYE{`>#W`pG;hBuG?0B58_+L`dtlPfm&SM=i*$44SR6Pp9j7K^du9%X;ukU_0eYT@K@Cwx0chZAX{(uL64fu=)jeVyd z$VLrey$*a#;!{53eXf4yF9mreIQz{EaQ3GmP|lC?K$$gXL1^$>M(+XPX@}0Y#}2lG^kvxXiXKaUYBALKtenAhw8ipK?@uqEIiFqi zKD+?Df(qKL$1xAi`e+b5GHJ@CWy*}>tAAkl5}`rCBZ4alAAa1(6`wKVc8SQ9h}=q% zJNTH9Gr2*#IAQq2qk8(}9QwZDw@UmOk+*u)qXyqIW$2-A8*1^S@L4Y!J6hyA!hAEI z`#)gXT@aN0Y8?441oihD+pkNmaQiju|Kx7B+^+yX2|Y(ZZMhEiyZ8!(Ux0i_@Fk!p zJviITLGa3hXFTM_Cw|I&9&EFi#@Mf_)jQ?cFkiBNLSI` z*wNV;Z%nDiWNU|tb~d)O?!+~ac5}R|+iUW*8kF@ivM1f{nE`{3fxaDhFXS3PZMpXi z8oVBq^_&Z;K>hQQBhQ)kQunN({`pAXITLInXf&6ZSvUwdKLKfz}dbUF~&rlHmAuk z^Mx|uH+t$4Voq5{Te36W)fJDan(E5M>4bB5huMEvLLs-hftgqYx4_nP!3h5GfZ{@kl{{K5|zIV7c>?OzkH!#dVQKX?V|?_c$jUd}RO zm%o2a&Kv!w{v`Cgp|*e3T`>F+vz&HT|IYCC`W*SK$R9M<>pj9-+h{NQRdC3)mu>Bo z*jrXprsAEQ9i0+kb9=leiD3!-A>JP2*6ZTXq$Q}2ed35mANz41cm-<9C;ua+zAhk~ z^Jd`f^Gz#|2NrA59t529c4&)+jfMg+FARz{+(MM zY+tLvD^QCc^x+8)&ivGW)Yw-Ds>f&G1qhEham-p!-a@Zk&+>bR9Tpb#k0PFROurwz z0%iIvpT3V7{q>-XJmtYzf1@8a_#h}d*&)cafHFSoqwW(1Ukyro&p}Rs`rD@+|EI6G zc>A>DfB#hmKb38$ZJ${=hVQx5(8<>rYS*Ln!rS@Cknpyg$6%jzw0jV|0=4)B56*I$ z6g(E+-WqLRv%R%Fx_DoJID=p8gQQq~idPjjc2S{Nt6KpJmDZX=<&hhncqA$$mtMUO&j;_d=b#5?{*oSC z+y4))evZ?Vg5Qt+(202y^G??Yf70O{9pfx+FBnnuq((sZI|F4UXz8B?ya zNRQXO5aC2o6kKA#?c3UUf^)K7g|Jvhrf3%td#9qF9; zXE%Nk_+H=#pg)NI3D$`TE!$WXZHr@HMgMi2u2g4hJ383*#138toM>lflr@RDTSs?i zV|-0hMM0JD33=g$UCr0Icv0B zEY{`(gOyH`>{MYNO53rx4^@vyaOK$B;q|I6T&wv=XGe2qv`w|dqp^6WN=3IPkSolS zLg9)Hpj92Ixaz_M7n@=XxALv+sdy)5rueIp(Nv3SHw6*A51!UWm1u3>fdVlKG1m%r zL}SzqUCnLrc3lRW+q*HZrB=>!JL9UU)g%tSwk6t$YkbG{d$|U2f%2YsW4E@_1lGh8 z9gV0Hs&=Kc%CtW{ed+V%7U5a;bX$dI8=;E`&o)a}Dm>c^U7_&o>vT$ZwrRQ?;q4qa zLwGwMpMRgR$6k16g|~C$8R2bfoEBbB`k;SGc-&IZZd~}+3V%fS%Y+{mex>jSgy*$` zZjbOhj!)Mke7<3w7U5S3UoZT1!iR)^v+yOt^IAYxEc{yx>#P?3t->qeuNOW?c&-by z6Q1h=y7>o9`SF@bHz)j!hIM9zx8wPY@UpyhriHiT`IPYN^K|3F7aP_&B)r{U9Txs( z!H0y$a$37S;Y);13V*Bc4Z`CI5$!_4zfJfO;nxdaC_LBFbh*OYb05ovf2V;RM|k#O zy155Td6gN~nH4@L{2AfPg`XClV+Gx$@S6{l{?VuQI;i zlAv9o$lLL)KzKX8DdFw-l`H(>_$9m@zp{k46Z|1+UFE zY$;Q9(e`FsGS>t!_%QvNidx%^on&ic$Gf6C7v(FXsjlv>mey2MRaUJnTvsRqpf9{= zF}xM>+glMsRTizQajMG8)XtKd?6B!nx_Gdt5OaN;|Q=W)(TA(+PbxNn00DdsEEF)Gn#B^ zZR}FDTUh)~W!cRop|V0doLjy`)Cs@VUM?)Jhx^*PTad=B`chTfO1rz%#`>EL*3o{S z3L-;rfy$t66WSl!8EtRGj3(UCjOkHbYx{kAXoRi0D(X7!>tLOguQgYc%8pb=C)$ic z!|debNDK=iu?|%gtP#sbYVBOSZqoslwib1~N7sYM>`DTVggdh5+1sO?il>vUxzo=s;Uo`85K2VPF1)vJl;#-dxycm@DX%D^x z{5gbM$1yzxUV+;6O2d~-{1VU%grE1|yoQ9oV(@%W&f$`fOM?2>n-g7Ty|}W?(8(4< z?f%W`dkw!|@KCehvY$BJl+Nb}j-<4+_D;hN>oVaP1g}7wunV7puT}O%He$zwa{@!V z;4_YTSS~%It{z@XL*NxCaq7+cs*Cg5IU=}!pEqN#vHP%`8;g0)ZLmDgK@ZcL0HvM? zr~+j=Tl<3l;l`(L1o}8Q+w|2@lmB_h5f6hF z2+n$#6?`0&bCeQLB{=On{S9+paS)VqnjBCa-*?{S%t2F6D}|2TXZU`hXFhKD!9GLR zKWJ#-9&>+Su*dL|)#iTFgz)}-&I6w`a{V7O)Rsf>H{Ehzxo3b^piS6!+6lT5>-jC9 z#l>nz=zhmpx6aVDYYn~WhMTl!XKO6pvGyj%&|;xQLe~jhE40v1jM`cr1s~nnh*Yr8 z(TE97JLHo$M`ie?U&67Uv+m|1%H5lcH=LI<$Z8Tv> zv-y^_YdKjUjZz6*J7!z=wRLpGJN>-5WV^VzfF>K;wsA9|Es3;Z;1G>}{rhT%|Iw6p z!KVx@6yBD9pRfGGQvMbn^Wl30*ZIDoDZx6T)A<%&wRN<2m|#bBVFxAN;iwXGol?gTwm*!F8@v~k<9Qya0`<4E#1WIvo)KwhzV<%xMH4>w zuZEtNc4^Od4M{oW{)55o{ZzZ3J|glLyPvuQ_RPU9>)4);ik&tehmX5BuNy0mnQ?=z5`%&lvplBPRZ=(DOpa|H|(z3b7^`8@}6Fec*-XHUy6AB5Q`nu8MKOdBmb3#+2 z2G5c9X7v?*#e`4%hoO05k3FApM#ig)jVJ8yk?*?o$aa?lp7~{YF#lPPyZBqcHvqTo zUXiDI36nt3oj>EOO+EVG< z(b^vC*wv+~^vHqz2;pyqlKN`9>wxhFgHRWSk(%yyeeJ{e;A{++>oI9_m03fFHdh9? zFvb|A7Yd*^m)uVNR)*;%RAucNJsQ1S|Lc`=F)iOW^>F%GLv1;1opSTZa#{^ufm-~4 z50ClqDIY#8xZfUoJ~HPorhTiNWjRP@F`bo9xpuJr&xjt%(D~OFgTFTU*z;S-m*g{O z>90)q`e{S$_3Zr9hId5nV(pK6TU$?=ai$oQdylJO-y|r@h4$rt-?f+ha~g6=aJJ72 zz?XoY0_8lp7?hQ3Mi)88=v)d-hoPKyCT9oOW^c=Xv0`%rD|&9-QM#4|pXw^~HYT#$N$`5_o~& zOF&0GIOTi6E70&=l>zSkZzy{g*~Roi&zSb0K$+fYk+=ETgK+DpuO7StJ$;E;U(5>K z^J>Efm&m$9!l#7x3VzfvxYd~_c*dDb-S6r|Ze~IHgXpv4ch+A___?JrvkkAxdpsu29TkV~kt~!vZvxY=A*Ss`c$VswJ@J`%! zM*Phi&^I<$1-Z9I$MCA#%WJG3uyzqDSo3AofpB?kS$R-% zXm{1YZHQ61ZR6&uvTc!S9Uqxwctur3oj)8|+fW_W8Ddd5;lN#YvRoMG#%38+ZF@OP z$1?_}#bJ0!dUl**A791_soJE|rhHoFz}J*-+8hq(VkNA{A&x8OrQe+d<$5v&ssv|) ztNXdzZz-P-UgImwIMA}(P&;l7`Q*1+dAUwr@ZcO5#ymLFpZ%!PzHI{mS5nL05yH2c87=&nITDHu=iG!qC-{&wQZ= zFEimIf@drgY z45x}&$s2HvsTsSO?Vd0MI}ya<9w(n|xDR|&G{s&~Bqq8c$OUXS;#p_9V4{l4w;xQ&I=|R6R z2(YpxYCDc%TYC(yZG%jEN*<2$$V0cfQR!Uj+*3E5wo_rrqKa-PT6_Is`UbW6U^(1T zQFZH0iGbgG%38DKMfa~@G}x_*B^HNw0xbx zFV?;|_sDwQ*faB+Y`on9_QNiYTeja01GkRtuO7StCC+|0HD}@<0(~v`G2m06l}@)l z)kwJ|;7ea{^{^arz$;KY|K_;DO{^}YPJbi(bV>Z9zX$X5;zi#0iFae9@;=ANr>|Od z4G=YUvY!|H)~o}jkuJygt)K;hv!9Itr=3Hfl#hTaQ2)4495&_YlpE?F4;;aBf(D-w znkBR(V8RIdAMd49a#n2|HUr+p#0bXF=RMQ3!;q!rdtUGa(FT z9kBkaV_W3<(K@Dexn)2p>*aY3Eyd3|wxv?bfKr#2XPIAypLMI?@$%RC_@$So+m{R6 zYwsn%z41BLd+p<%otO99$F}5+&)eaY=UyvsDZ#5<@-9!&NA5Oy6J6>0!#?>u@YFxJ)yNh0r_1;IKF5(U!Tt9&N4ibEPTyf@xZY5|T))VT)fjw_+#jPo zOmEbeUITcqy(|BaZqG^Jc0R&(HtoT=F3$sR<5U00AKmzD>$%{G-;Ehm0H@^d!5f0; z?soV@^X>Rm;~DF!#c&Pc5AEf6Q}`!WAJac7`iQfu26>Y zHR(CvwtR_aESUV9h8**M5Y*xvD^4!SacIW~XgTOg$Pa<~$K!$znff2T&rtt(T)5YS zU-+0%X-~n3;hhdc3-32{?p{Of{nk^`uIzr^avKk}(An~^mj#BgyjDVu0=4y%yEF%! zacoa5z>k31_;R~f3ow2J@=9=)&v{RLj@3gRob4rS9*=_0ad-xFC2ohzq5!!^M3;|t z$!8Ayj_Ff>Js$sMy<~y1{0E`00MyKDl+0_?;=BhZu7Kj)ie3{mLr&=wy7Vjv`{1Ba zhj+@#oDRse;i}9PgBxB02}*b3K6G5`;-6Ybac5@7OJW)kFMcr%LA2?xj>~;>J|o!( zjZCE#sip9@5tQk0eqv^E#5wn%duh1B{c*VsN=+4_e-pTB;1OpV z!fdJz;=184O`Z8+K5+jOk{m+=X#E^bt*e2qAVP`I@og7nz$sIs!%bc`YCGKWuslwH zLXPEL2g-bG(s~#kfR<|EX-5_8x6YdfhHZd6OE`cW()shUM2F(}x_>eImmHhuhK1+Y zLU%y;Ov5^RgcsK75uRfRU5oG>%joKb=U78mDm=#;xaa98yMMi1_({>%B>b50Ey5oXzE${P;roTR=h;6a z`~kr~EBuh~Bf|F!|Bu4=34ciVUg5`uxBKhIg-;4TCwxNq7le-quP>@7Z~J^mzjToL zm%*E)fkXt4eNb1IWB2EeN`o&rVCu)9SO-3AcuC#~N_^WNON6)WuRwU)zVd{(?JGxk z+um(@w!Jxm+x9m11(UvQ|Fgo|{y8Ij{f27Zp5*?O!V3uu?QT!C@?kg$-=KGZG`o0T zo2Pms9bM+&3LtyGyKHlX7PR~(@Kw>2e%Ay6o~7N0)6M#ppP|g!TDz%Z?3`FSsv5?}c9d zESeVM^Kg-9vwdm}>&=c-84ffr1_WEX8l#>1Q8yjI?wiSTYIusS%h?3m%c?7b0bDj} z@ZwOuR|D^P;Q4n>ZM4G~pLV>7Dw=4$AFnIG18x#t;R`Lp0|*SEx#MO$Bf-65 zIy@QBJKvketjt2o^HbP4miRf`Q_7knxj3z`1bB*9P{tc ziUQ5eeBA}=|Bf!4A-B^y$c6EoU!wNDHd934@mxSvx3Ivqg^T4Ej&?T31F;yDQ86}6 z^*QjM+}ClRBQ*BMK7C0Jef8fo)P%b8`&sjP9UaU*I0jyUo|JfrGg#=r+4X?R(kz4Y zdSXuGExy%<7l2ogwfF%a9`oTRJvhgRVIS_8*Fykt^bmH+hZpi?@aH@@(;xTY1#)}|kk0>kAD-~x>%l9?+VszZXF4ZAc}-1uaE2$$u@LwiClkLs z@LcaOKJATRIHTNpP|i;Kfwu@w`Mzv}*MssJdk}KUgXiVA^+$b&AV=K4eqDdkw3ouK z8tPxa_I}rdPyXD{l7BXI^q`@=6NdIl`0CFY-rgVTllRn&37rrMQ?z4#Ip*hH>e|aO zybks&P~t3y{T`h4+W_8+pYh>Ge0b?=UHy!o0p7-E{U7k)EZ>mecVo}O>#RMeJo1cx z$fJ+-edsb%KYgIQMx7S?2CQ26Y`%3jU=`Sgb*gn-BXX_Sj2#)_u&e>H~z2>ZvbB?`f2a% z>s)!Dd>yPw9<9=3yjVJmRePnK!ujjo@=%pMyFy@!76s^Gv$qpq$$jgED`&b>fG&aKr1|hUO88RMHHc zF6}#cmDwNZ1!aHE0p)mc{JU(AOWgD2jp%Ny5Mp;rE|Prg^=iL+dK zt}*3O0Gb7U6u2EnY0nhGt<&uuyh1?Y{oHx{#_o(oBe>CAMMb6?=zc=9*D{r>9p=*tu zEudUGMPP3UXd(WwSJk3Cxl>|C5nHa#8`H~W3h@=>Ene!wbHEetTM&CLNk8x0>yrT$;Efd)ipq6SyqgR>qQ$qdqGJUO)J94$5 zP^F!}esV-EXStD^_>0Nktk8L(Ij=PMdg1N+K~75j+WN{6+?L}>ZR zhp#vJ?g2dj{uJ=OC44@{?1v#pYLa_60kYaLKHKH;H75R<)u#QofO^~iR^Zk#{ng+V zsI~8a5080p-G4ne>uFf5vl&{h++%WMmx5sOSJj(>pTiY0Kj|bXg8}W*dcsRN}p0F||US&sTvIWbVWZY=C z`gM>tDA*C$P_~$EEvDdjtH-9!)|j>i>!x@LFZNhmo;aAGZ|_KyC8B29?a5z}j!{-y zSr_j~)y7jPDC+Xyfdt;xb{8IF)bsUhL--DPp= z@Y<={g)V=yZZ#D`2~Yj#2zXCS3U6*n(_7YqCyS*Z#qPzXXL#H0ZT)!dW~ro=%Lcsp zWrqn(3uk$yrK`(Q1l|5*0>ln>Fdblc){j||qWv${H3bZA%GTv6ucNaaZiqJSXzrv& zJX>vFT*p#I12r!pT-0OA+ttJT8a>|fWr(MIwSd^o^3!~6OUEwTe6cQf9>nao3-5+* z(kRbm7{3{J8YR>n$AUMck8>-*D@fUKuGfc$efX#k?-zXHVe|e!`(DM2o6U8gw9>r) z(EnUY=oW+Ly~)s^@BOvw1rOb5aC_bGKbKM{as{i5oXxQR`IMyCGm~%RMnvA~-Ty`t zK69O+dBWT4*BQC~`R5lsBJbZ9D84bhoE`ACoH;L@DKyukG0;`u*8`7$+UwQob#8o~ zRnA?TuVw8OF{kfQD_b04ACgR?#^0AB)n3Y6FXeo)RYx}5jo4rUaz zMW6dYkao46@Qa=M(L$y8+lSvUehv63{Ak8Fex>;J;Wvz51AYoWi@|H%p5w34hSN5> zr0}$vu0?p3DP2VP48uC5!n4iL6$sBf(&Y%xGSW_X%TjNaWk4Bkac>%a#`B*4p{$qZ zo{c|A|Y2R1C8 zTi+ZB>(z5Dj^9^SZ?Ol-1K~RYch+v(SQFS(Szc9FYxUr)d|DVznO9fhkb7-ydC-fL zh04orN2D|yJIVo3lono5h2#6Rc;=*9KX*cryUgp|oYG3u@4Vl4V;dN`#ckKD^B8yq z%6`RsmfU9Abq*-cD@+6L1ND#h2k$ic>*YPVjDIQQM&IV@XFc>mj{^0tL;62x;)iz| z>R*3s75q?-!Ou&6?D%rv0f}E@=;VEd+WobHcS$^X-m5`)n4%r+T?yZjx4ZVTZLMGL zmIwRL&^uh5Z94+K5Y&!C&O2S4eP#^!kO%Ma;M~8-0>8(Dk9u&{Pak+CIO}Jy)U4yz zgEIb6-~}E$95D5j56b#G47mfKd=5!TV^~85VRW_c?T&V}xX&${P?I0ppTEKAy8wOc z3kguB_dk9AZK>4f%f0{hAoN?u@=SnNpe#@3x4z7zUkJ+dM+CnC`%mVCJObf%VtkE( zZoz6e2wICVm*-V@9>bhP;pq|77s5L|=Ou!zO-*<(za39wHv`7^9Q1n)osF$M`W}eg zal^ZX_;SP^%Fxk1+M6Gg@_~IE+eSbYXk$+oJ~Gn9B(<~c;}GPnWBvAmSD<{q<2$s^ zpp@s>Sh~^Z&jsar{}kjyl$Y`;*<|n>P>z*7kW-*JA782yvd@Vf`J~}T|IW}1!R>Qm zBZ6lL9U3%pc7Ak54nCd#hzUO`a`yT?|F8+S@55Lx@>>r`dh(oE>Jy^(Lxzq?Je!{> zDZSZ{$v-I}#{4590glv9$ zeR#-&GryA_ocZhb;Ui&JKgYKj;Ps-P^6M*Ioc-%4XU)uPI-Ho5PJey1KFM?T{DLC*tkRe7#v0?(I^RE{0cdHR6?Dyx*F-&n)UN z;%MCaUQ^~nkMHj`?QuWoYY}G{`BI?W^4^NOacZa60p9`JQ)}>6TiN=s^;~?1TYfC>4Dbq+ILrG~y^HgD5ClF6n(AnD zdZIWp2RV*!$vaK@AyBrr3{VB?{RRtI*v|KM@IDSUnT01sJ6v}Y9{t7p;r08;K=r%A zsyfP8-uk-6eBgXQ9px(m=2dLLctUSh)ox&@v77cdcNu%8#hxKh+E06#&mqBEK$*`R zQ02i3?l$oqP~wL?IQjEi4So`I75F;H^?~;P*tGN5^M=}TuDi!AN2Z?-UV-L`{Ooh- z@`FD4ts-yrP5SWtK0M<+>FG`R@bx}?Ie6M*^{0Gz(1T|}Zq9>qJna?SuK#1tNIA?I z8ksWGuK()=uNQn~*5LL!WY_;;kqe95`1gz)lGM(w|JRFLp2(GozO1Ly^-euy_#p|O z5xea5t^Y}b+x>u}!rOM!zs)UI=39YRq|&aO1{Y^LIq1RJFAjL{Eb#f@6{v3I7>lf) zmZ+gfZu?VI(JXmsP}f{#JI0hIBVfKGUDw#WV8l?T7z z!%uo}rr#HH^|Ksu!F%JM_Tbb%EV$|YQQiCRLb$ajFYfB2{5g+4_U9qsw7UhA>F0wg zQ2+JFUXRraOO3+c8fx3sA!#4e5^k?Y7EcQ9zaAYBd3#^+r0^DXZD!W2VX6pE`+ z!o^pBp9gN+HStuNi!(pxAg@3#WM^o=+BxTuqn%^mDMwuQk9JoN?V9)CrcxDZeMechd2M zdt9FNL$EKL>nwg}Ge$_e4R|Kg`@J+iqsi0GOoPtyhNBG~j`zC^w*E$a<uamYE@|>qsQabwSc$R;oUU?Y43?Ysp;*XY_ z@ufJNKF;)cA zc+YY}sh8u~#Di|WxPOofo^}$~*Wo=b{!N6Jz7IGWy47#*_tpBH(nug8Z6!Bd~bM}2so2j_T^v(MGf{y*izTRb?&3k6=0 zqJEZu9`WF`ug{0i`tUIi&VID|!*2TAJD>C5)IaUPsec5#0%iKNKYz%LPy1&)IIqK_ zA93+~@Wqht1GV-S{*8-M|9QwO51#PhCEzX2@>uz|ZhVekDd2??pU*s;{HTk+8~l36 zN02e5&wd;};NrA*;$yCUh&e|*IPL2NuRueeHtpopr_$TYtWSPS&f=~RLTRvbh& z#*I&Xrv&%g7yhP6_uSEo+n4%=312C=Ex$gGewIfuc$+?o$vN%8S$~Ipc=2al`*i(+ zxAB?3!{Di>AC&e7eR#o$!5vWIM+HB3%-DP8XNFpPTOem0(<=e5K z$Xk7s?fO-T+~Pip$Q7J3a$A3C=;Ho~ z;HjUO@UWye`7OiGKWXUnXAC_sY3O)&rna|dr=j+K+$o92@?d^19C6Eo^NXFWK_l_9~`|61(&jiFXw;g?-~jPHO~AQn&f@DdMB`S4dvyUGWp zy@S9@BtF|i>8Pup?V$&9g@RLl@~bY+^Qb+LSAqw~f79rXfR2MN_!{BW`@+IpBK) zUjll@gEPIu;FSkg6K;Is=X`jJ4=(`kjep38Cp|dzWqjM!Py3E|aE>DxkGeR0d61t4 zW%+U4m^10(tH3uveh3*O&UsYiJ1$-Tz6bLBo*Z$u$EokS_!ZzMA-@NdxGukwroKx- zIX*a`g&utTaW_8gZvn47c)=6t_>>1{d(3*$#c6K|~=^XUnEUyOe3Y7M-Jsh2K z|ZKuL}>lM!6 zsis7fKYaKu3am)A6_Xa@1GSy1qq!NzG$Ikz*`_*oL{(QyJKuJtQuyS{uANW>65WZQ zCZ!r%@HM`kR{XkDb8D00oAmLAzjZ3fPwL^;v{KvKk_w+#QOT|tez0&C66sQL{2H6v zRIH~-b?t0cJ3HGF(BGuu_(hS4m}-p1RE*LP#pfnf7ZPiW_Nby*mukhwW$?{X)!vCL z#+npf&!&>GR(!^<(|icLC)uiYHpSG=7W}&LYim?HyLN)ccBoj_4%NPspA+1PM=|gR zw&71Z|8(I`s!g@F&E zwH?2m+f`!%xl1Kf6Us4`Xhw}Usis?QQTTgnA$~VOA1eI431q0fwF#eQf=E(<+kv>d zpe@*efZy@I=3UbN;M`>0$nPFBe^QYU%W>}@#UdtSEK6cOsP&Z6Ff7jVjYeA^{BSS zUGM-%HK~?FQuUzldJ>)ZrSRK@`cI*5(M*u5uAZ1~2VFhQpu6x@0)(Yn@uR;@b;MGt zBZXhA85F-*5)?nQnT`~G&=cK`%r~L>kjuobq}r8?;n#&n-ITACD9a;P3RcyP3RO&Dh9c>*ml+4)uiy+IJFCY{BOdq8NYZ>9QB`2%;wJR zJ*uk-zxz-)iS6hJDEbbVy!~E$g>t*<=!V$}?HZZgj)Yp!_Mo^CND^Ouk#V-+lo@AB zKsheYVjN0>T0H;z?l_f&@S~6`6rAJOK@ZMxECyZ)&T)L!gL51o5!}C@k?~=3{XY7k z^y_%x58U*rf8L{?=e%$$cm?X$KlKr#_sIKP{j_iJhpv8JN7jR9d^=8`^x#bY zpa*Ap3{Jc8d0m(heDpy#9hTGb|8(W3=M>^8P@7KJhpz;0abAxjKXT*qetw+?uLnQ> zOBc^Uf(Ib4P~f-7{bQTX?5rzCJI25(P>ZXx>G+)B{_>oD(&VpKX!tH#;W+Q^j<%<8 z)4#IzuGXYS?}$%t3cLdD*E{qhqqpv7hMsxKQ2TtcwR7I5Z$k7DXFHz$wQ1K!Ksmp3 zKo5ZsXSlwc2)SC(Vqp%c7GIjp(p+j z!mVRHYz41CZT=>G_yNI#a&E|;V~Pkr*SSn9TrIpk$5di@!ApCMT#>*kNe|BQPl8vVjL-bnKkvq8zd7TN^Qu2~IwI!Nr+B2fPAdeB#cZ)A7T=3lWU32kEtbnb57T ze}xw8%QMubU+Rg^{N{jHpce1-;UU2xqMiT#af9e@$S~<7r%Zb}B=qF>4PW|ShK9dq zs9oi^9|c~4`p1*gA!Fa%Dz`k?K3f({dn*NH z|KB6=nLg7y>Rjru+!LU@uXF%%1)#*4^5m-w-UIr3@cBzFb;g&t<=OLU#xjofg}^J& zu=)J8b7Mt&7d|=I80Uj&H`c`QyqA7V#>Bw*-ibL59~-*Sgw!UZ?TasqKu{T;yuo)8 z?PG14=jWwwyNyARZj9epi*r?YXNT6{4d)Yd){fG>$KSX1lT(-e{hOWP$F4ru`{*l& zUcIyQ=}RBpi)%{G9d(~M^ZVv+H9URkL#57mb^Rkx?!VoXV?(1`p2)V7vn)NorzO9( zeFZ%@%QY9g0=4)aA71alS$=aKob@*(IL~*}@qTIj;#=d4UdBrevHnLyPKtlA9}aos*iVDtsn6!)lm}<}!#;fFYut2*pZ4JqAD#!^>Swu4dT^$n z1m6IeRzuxKkdUuJUHzv$Z^wWe?H^E`Fzdz zWiDO^z6A1xFpTyy{)Ls7I<)f?DC6$|RiGB{z1-k+pv0Gh61R@uNN7U{X{*rp3f%?T zg4H$OI!j8Nly9~pr5q{c>@(112;pAp*e=uV>*HB^0J7zM6zN;XdL0ydRF9D#y+6Ib z%=_dgtUU6+1o4Ysm#)tNuMlq2OZf2h9-QSd>%n=wO?hzYFS#OJe-?O!a9h6nJviH0 zso?Nx=f580_DcTVXz0*Yh9-qhU2XWm>kPH$iR}9UN<_}?$F|7(#ch61dF)~Nj(F^4 zewOE^=jXHsr~H24luLke99RjeK&hYQ)q|%qbh`!ReETqPJKhst|9TUDB`C{t7;*}f z@i|_!_H@fA*`f|Y2PU7Up!7uq=?7e?{*Yp1W z|9n1Qt5&VrYO;u)F$|NHVHj2|mR8MLETUB_E2CM%bi!mY&M*l>oXI%D4nvr8!jQ}{ zj57>jhjE4uLzp9cZ@1^;{@C;JdTw#fd4JxY@Ar@IqszL!?vMMA$Nll+`SbOByoF4? z0l6A>Y)|{=@2~t*!2?jg1$LZA-JeaU*M<3)Lv}%}PdvcabNy}$=t~Se*Ig!L)4##c zd+XcKb9`1O`0<&4qoL>hXX)o&M>`7pndM8Cfr z?`X)bmr3}4wB>rxg=5M43cAqO{Jb7*O_|raY53EH@9~-OxKBvaN>JM~PtJ4FA!=Pk zf?TItBu_oAt5L5D?K2>|pw>48^yLA4XF%U5dVasrIr#1(7Jf<3EbLKA@U-0ukG#g= zx4iQCyI>7H{PyD~`1x@xw@dt^U0V5t`Lch_u(!b)kmHmHy5JP`@ds=2B77DAA80_8 zSEjBgx~O>BSp~Xcvj2GqPoGu11fO%#zt)QTHs>!Y#IMZvR-Ce;@ceU@;M4N3RG&K)F+U1{+t6nnf}fngH_W*O_(cDbV(;Na=F9v_C(3>UoD8`O`Bs3`b3WWjO5X!L z`_~92h@Nx3;V`8y0lm*T!@ddi{QmFR_?IK!wVi?M$ztsF(=jhAy?2sfgo1BO!uxtD z^5h$SR|s-4{>@ZsPsX<=O$6f8}c?o&-GGg=sBLL zNBI66&mKch|0+XI|6Ir}sN3%{^lZO6pwB$gk57G1K;I5M+i3u~FPINkeNMIL zgU9{m9+hX~>xwY-O7QqUU+l`7lwI^QicPY=ZU38+6JAto+^VSe$5~>h?>AJ4tou`E z*?QR z=(+wor>gchgC{^vhkZN9{!zan-S=nzqaeFry!Tzx{MKpp4bV>YfZw*8oH_k8`9OVdy)-mAeI|aIZT4*Z#G6;Lyx(-2 zW@SyEnuDL9lUf{?vS*)|J$WW`_v+@%Q#qeHeU_Ko>hrf=@cHedIvIn_SCuN z7a4f{otE7@&U5f32Hr?Z4c9TZiyo{qUAV4mA-kZ~=bsp^kA|%EHtbph`tpF@%?S5z zH}u@@Y79NMhXlxs&u^{dlVDiB*rxpbf;-)h$L*y_?B`@m%gls>&U?LK$Ng0jWEY%; zg^Nv~_XidA_MCc>AD7#6y|%|y)dJkr21=6X_4DN0k5~2S zgA_Z3DZ)*As`}MYiurpe>g(rbIUecjXG{E;C;Pd@$dl_M7qSZmOY)Dyz5LjBmYT>B zp3JdhSuZljf~8Vqw!u;+GPfC)B9S>(EO{a`2bL_6IoB*{BJ&;(OOnV+`hWlTrFgAu zUhnZ**_8QSg(-9JOqr|2lt)44pDujXN{8+LkB989ILrI(SyLa(XMfn6@%M#n zwtqlCp9q=PtJBV1TzpYc$tn86=!9j(D@%+EBJWGi#0Z}?$M>kd&Y53}WF;&Ic ze29u~`r8nav;5V-Z88F~3o<_SMFD+kKwlrwml}G;OFLPOR~*P~xC#0MiO+2~JzMF= zf?WH#u&WY1_Zgc}uM5X}HDniLeq6Vk=J_g@Ho6ZEC%82vfk zE$68Flm#HS`)1g4{?eCt+c!2{cUp0={x(2=+{(`N<1()d$S%mZoY$xoYTP?f&vt4I zJ>|F}-+v_J4A`^(YCK$T+$d+uXeDn_>%5&`V2`55r`m|aJPIJY;0CeJc_Q4tHDF&O z_BvkFd467u*B;Pk1oW|x*&h9AUu)>OKC?t0Jl=OpUJ0_F2p;d7MPGKi%D+JN7yA35 zDo;@L^XDjTPM3blc|@X6AOG`3W}aLp6~%r(c}!?A`hn$P^%#2gs~NHj1`BnJrwiAl zdHmouY2M$YttspKm)ieuyfBYPJf}0`^BUQdj|j;6_=IY&9D@JZo}S;5m43gOdpcwn zjCxXy-?rCe95GHDhvtBNwb<);5hdaAS`9t(Og&$12eDu*)JXxYhQy-yN?jIYsQd#lB>-s-ORq zV*N9UJN~SwzmI9>6H332%0!=_O*nMIp=KR_%alb2VLog>f34piuE&;g_54E)Zn5*hcsw7A=U?!wZOIZm z&3bb(Gz*mF~GK@U*PDCvzYp47 zwWN5(A^4i2LOk58qO6pJ_4pND;pfY9?{ehtf_i?o8G6n~J!GxtdZ@b6_h)~jAoI9J ze_jtp|K8WWZ3g8UiWVs>UHtP8L}&S$|cwPdX96aq31mG7s|WYgC~h+iknS&x6N@BFHXCJ^PWe z-q(BkdqdB>)*E`pn-AFqwf_!7&vB^Ndc0=QdF0+0-cAH$SM+SB*3h$^qJX|5pszRd zobSkLKYj}2F6f&Qdf!+w-Prj5=c;#R*M>RSvw=OfF|b3CIUyCD5Je@VCb{=7Gk2Ys2X^z`q9 z9-_4wWWNf)1aL0iyITt8W~y~ppB29T;y3tlnb!{ZxnPsnyC;X+&kxuqLZ*GfOl7w| zV81?KpD*@RVqcaW9=|nUzd`KvI7Zzbo^N|VpApcXqR8Rqt~m7@;Ei}KGamcj`A0vGILPgY*8xtSsovB& zb^1(5YJ6y)d7oMr@gVolv3241ZJJQ83)k;z$S%nC_5>pzRQ??g#NhrM)*bZG7%=KN zZt)GiKl|4Kdlw9zm+0?F>AFU3PX*U0uD)MU$E!EualTeVW<0I$3g|Z(dfJyfoKC}zehp$rJ>`st!{cl>^y8rKe$3aO2RRS+O&E~WL%o?eF7;5%#Bn%o z;f=kiv*+r!DJ{ISW{Os-XYS(NzG=C~?lWZzs7EVjcFyF?Df7HHx>8k0 zw5IyOJaZfUJlNkIkNe|A{T4&d`6+_zf_fZcpYZ)@zs=CozTVJt`x*<`1?fK>-0_TB zA1&a+klUbd#yHHJqgLYSc<+e6dVQ9~Z#XW&xuiN*((PN%`|Y#6#w~t)?g!hUM>wkj z$o{;QRBqUIcxv;h%5l89xECKJRBerz+}^TY^z|IyX4t2J)HA;=hTfY$$Xd_#zWsI^?Qy)C zAiH4X*UB#WyKwu&m;C%`-y`;VehUKnWXOz1J?&czJ=cGc=<(7k!RHHezE|z1u2#&* zQEU`eoT=pUm5K$YD7MIXM7ywgwiC?;qQ#H-aK39={QmHM+;$^h&S%=ozMk{79riA` ztNmr}9uZzU^}^u$7hQ*{_Y21Ms66$!H5u)2oT?zRouiLd@B0PWdb4_eBUrEHw41{9 zY=`~Hc*XAzkBiOHPVjw_nqewW9jEL@6)*UHOZ0GMr}GKM3BKRbbc>3^e0cx4^))}A zd609FUz%-f53l=rZhv`@T`+qp-(|r0%!MnK;yFPJ?|WV7z3_CCtfI3gPn&ay_m#h% zosuUWHOZ5(?mYd(Nj%1~9EJxS4w*Pnt3^J-Q)f?|v`|^$Ju-ZH=-jiW@;$f}ti$6$ zy#iXaPF;9G7JkpiItib`r#@>2e&MOm|Egzsj!1pEF!VVq&R%lvvQ@p`1g)%>;C;LN z^A|74SDl2N$cHf|i^#qCiae3JIR(Yu4I+MyX$dP(g6mN4Cu&ac=Y#7b(1qMNQrWfq#kZ$jRGZ%(*ZnrJ&m5)f^!sa!Py393 zeJo@b^;%yZ&}SKX_OI2@Grvkh&-l@Agy+*?=;@zt=s7-dkX?}NQ|>bK+`p8Fesi4a zkG}6acAAo-WLz3VukZIpi@q~k+0}|(-|vkOed_|H*Y{&Pc2}~#-@A=J#Ea69pqMVt z)u#%(_fYi(Cn}bWQEZa!x<#lzZxSJXdc3pV^v8?i<3e^x8IQ=fl&%o?+*v} zSJ9%^?+*v}dmXahOPBYNvxKeEPS)+JofN6p?Q9W!^!ZAkCEOBduSD$h`_WA$%09TC z)$ePURV)2(?PptWRd$>Q9%l>Q@#l%>727b5E~w|R#n5v*tcI-hoY&-ceSdGA8+yvk zhMw`$A!~oW57g1&`n=W@DtykqPDL! z?Af0ZvDf;JfWF?)d-?vuZ;$zQ8G4R)W-)=}Ow924ic=q#fzUaP!aWkiO-&$;60C) zgI7m*-~CJD@OtJm8}0K9fZrjk#`Peu{wp=^Mc~7b^FE8Q;xGUlCyqneH%gxia-Bzj zE~v+`Zihb(yk3ZeZ0dI!dd^!zK%e@p@6UOPfNc722_?}nc9SP#0Qx54?)>%w>mkX=ysuQs61H}o8zxE|l1{cD20L;R`V1U=iS z1UY^QU^BR@<9)@&>b}}~VVh9rQ~!hC9`h}Q?Di8+pMO-At9a4(sO=+H&JRjOj=oHN z{$0n9_*Z!RHi@tGX#sr{WVWyMs{{HBL(hEM3_ZuI+|YBJ=KtuoPkl6G7YvS9!xO4M zrS~cZ_4@p&LiEg!^HFQW=X%JO_&R@Ur{6yFZQVutNivSsW0J?+igERdy1hIj9{Upu z*#)(}!_ad-SZ3(ie%4QZKJ>RByMWHO#?aHgz|gb5I}APj>kK{HYy8=d&*Mr0WEa%& z(|+;wj2{V^`rvq1$b96Ksr44QMzK`Lcx=DXh{yIbB%W?B&inNgC{Wm5S3tiZpwENs z!jAs5?=kdjuh!7By;^%D{#S+ljfU)kx_#I2^^CXO(DVE$a)7Tt0dflL(`@CxocfWz zejn6Vz`hKD7@zIujrR3?#xZxdkyakK3iluQdDi9LyYDXEBP>FVi+e@+-n%;_KNqe( z?^=pi>JzV}@MTf{em|OxcG!<<$S$b+Y3=UkL4B*C=entY-UjnPZYPnT3-VhW+$H{# zQ#xLyceM!v6pni$?MB#ft@b{;?i<9`Ce&8|rnTe+guBKVHA5@}CdVe;f4l zPdi#ZhjP3;Z+N(3-homt?N^TVJK~gqI=_^?RQ#Q=<9>Prm?3(OLml+GFux4Q zX8+3eR{lvK^J{=!`*Yqi$NB!e4^jm?7i9Y!-|dE;?UzHQzE%2D^9R+Rv=l{aPsM0q zlGs^i%Xz8TB~A3lf$QBG@8`kwJl0k1Ou-q#N!BD2OOp8c*6 zy^7a&ek_?M>ln#PSS9n=dX%aU&g*aaS>itjXE~E`wlx*Gu<-RvoOkJ>WV}BBjeoX; ze&HW|{@r$yq`fA||3CH1Oa0IKsoP8YFa7fV%RJNmOTU``(y#2l^mG5qak2hOzYfWB z4(=B(#J*bJNYb+2l6JK&^z~?iJSY8Ic12=$w$tidYW>~5E0h#EB=o6ug!8@icBsvH z+1hF^2z_pU?0juK8hX=u&pACb+uq`w;~eKqx2|>GaoX*>>`nG)yW3tIdfmFtu5&u< z!>n^duh|EO?zNVL?s678PdJN16P+_d4>~{Ed)gn`mstmdW;^dWE1a9GFYvpfzq9L| zb3*4i_ga@b%kX_z3!F%&HuRRW&3@Fm*M7jd5^b%rX4@m3KihLdk2^nDPud~pn$URX zY3Jt9^3XeWacGB~?c_U`Ipagytc})Tq4v;B$9BH7$A#{+<~vV?c5~jtH@2=pmbuRG z&?R<`bBBGu72#ZH-{*X6Ul)qC7g+~7cUg<vULe*y)xNx;gZeb-um7eMBfB^a`%g9t`anDzh)OU$)oV-#I6SE)2coOtqFf zv384fQ)pReaA>}h1=tP8WwMN?MP9v_mik%CbPn>I&R@5*lUyBXlXoWR{g@Ut-M*Z4NE9&vw$C z)z;sgZ0AXg-|cAq3hOcFY`Y_LVCWxCo%KqnA#|a=-AT4`ozI;$&STb@p_B2Q@%x3g zg+6kAv9EI`gl5@4SRv=K&|gB;&Ksetob%xIl6`Tg(|X9B7CO+0vhN6W_B9uD2%R5^>B51k6T#~FvwyT*AvG}%sbmN~`FclITr zKRVx7jaXePLeE;MPMOo@+!0E#TCB~^bI#4yi}o}2UZLl#-9r zhgR8dS*f8cXN-NgbFj13$;Nrw@u7X40d|!0ioKuxn*E)fV%_K*9%>DJ5L)Lf30)QX z&iT}N1}CQbJ9$`ptL(iI<8Pt3&@-W9tp;aIsN8ucbc4OzI@i7muS@JBo!9M$?X}M9 z)>?aiYynqVQK12$-#KgT=bU`3jHg0(TRnDlsKI*1deNB|8f?F0O|mvPh1Ocg%g<8nS^7qm`R#}$3PkaV^jXXJCgX<16v=Lu!Y zl$<1Nkk9|7<*0gnoZq?sNQ>uj-pzT+YV6YKQ$oFU*bW1{=@t>Cn)`T zkop8rpFdGwbdb`!AkWX{ALyTF@cb$5V86e7U!@v$E~v*NCDGS2UL<5wUvo&fz5p`y z-D_n$CaUijEW1s~HRmXHh`u99>5D{PRjBl><-$cu-*AigRVy+-@BH^r)xTJf{VSZ{ zpWk}+udxeR(;c>d`^c1ek!J5l*Zg5G%;^!ogo;}<>2_viQ(z)t%! zeyO2nJ{gcreM3NB7SNA9%#Tn1cIcUZ_Z?~+^m$OPqH6Quo$NcgG z@slAlzwOW3(SWt-ImOMw*3II7r()An;qht%@k%5f$CLTTCHwv1_|=QvvYk-GfPsS| zheQpJj*T1ZCM2b#W#tu>)iibd!bE;WPqi*Kih~kDi9i{EqIG0{dat9@;g4&IdGgwU z>xFa9bw!&v{JHq!_^0774}WF&Yr-F$%l=0Jm>ctE&I1Py8Wb5hc<_)RLx)C14I4Im zcytWb-=M(*hYT1R5fvKd47Yh#&@Xy?n^M$zEdqJpZo`r4I#{pgj-!;m3S{4UV3z<= z&-Bxe_UDJ=5DD1@e{KB#|M?j{+zD|tW1Z|`p7i){!gz9=D?zTYOpxnIkALlP;p1Nd z*%dw4=1xP;g3tQ)mHH%2(Ps&J`#0I|zxRF+WIaC|zk(@h{1QQqUkmhl{5YTOsMm%1Qphf- z^VIv}q#tF!`K9z%^3?nDrXN&&bdT6!`((VTseXLEFWDpU_4?YLuJYRea{C?&>ijvs z=^4I1*H7n(D*viIWV{m;8-(tjN-h(9k<^z7TX$3XBGIQweU`9B>br$`!f4UE!X}B6 zq~i#)gh`^$i&O1cI*!mCE%kdT_O3rU|A-cL?5*^@>rc)jT zl+5t)jf8BDe^Ef67SQ+14)HR5 zmn3YE^>3Z2>Mf~{7S@Qq^$ex&SfJP~tP*{i*jEXgMBgeb6T7_Alz*1A*C6^P?LS}X z^?0Y`_~YfBr$A;ug6pnfsl;8Z7+iNXqVJIHJh<+vmMFg}@z?D~%nfh9P1@J`^ngAJ zGV=@0W8S$cesqCiP~RZ>qO+A=$L~Hhy!~3p=5f9`SFN{Fkn`LFyNC*P1R@J8lGRm>3)2khculg>kR9V$5Cs(%C7--%r6Pl`Efo= zq1T1}(U4tm*7@fw;wSz5cS^+S|G@PD$A!7EeQqnPo%4URUKY%Ndje*`5nv`f zBjHh>SDMUmuzbRbTXE__~N*=*MUFMS1@D3CF$*dX7Rh$R5Xm=DNsRU$e{9}^XM5;UqP{X{MHj#tgOemst65o8zC$~#rp5Z?q$8qj}pDv772iXO6{IU`iKNn>DF6j030Pi=pU*P*60=Wiu z?c&e=Wt1xao(om~NT8hWn3ILKPh{)}Di`*Z)YC7_>g=-J;y$n@v$?&xRCAbS1h z_|+Ts+?(f%y({l0CkeAw$atKt7%lbsdNED(u8ddrGG*6$96ndcNd-a~-!x%Wp{lPD z=1IJ&(^P$#)c46p^hMfVzh556Cr$GGt$eD^RPE{Y5pl6U-W<<1JwDhzI3KA2eH3ID z^?H5R8+y)Hy67!=K3PB4s-Hs*KG#|$&w=abX!|_(FVD5UHR{HFr$68H@(*L9FHCyR zIjGBi=+oi3?~eK4xgDpS`sFQG{qn*MC(NJn-LUqA*2pnP5k`qf6e%8;vX;m+nnOttK4U}4HKTz%G`9y@rSpR>2#{EtmX z54m8ErBRd0M?YP$V&fx+M655oGiBB#U%x-%!F9Wj|LW1FX8zrpaPObf7yNVb;jKT_ z4IY`FT+&#Rcg}u$FRv`PX+X>QML8X7)Vy^IYs!^uU9Q+9^{v7x(U)DK^bOii)=!z} zgY(gFnZ%X#lqPoFx*eI16PJ`^E-XH4Nfv(aWF~$N?i~CE70cPU4^@oMf$?q>zr||; zp6n^aKV5MAx45owzTn*>*7ceZ5d*QC95g5bI}82=3w^m=bFEUX&(QR1uEAaX@yspw z$7AHK{&acBPK(kZ6#mb_RKTOZtJ|_#HYU8diygYTJDUBUy=CfA@`M+ zoV4xUhWYPQ{BS|`$;VZnvvp!!^8ulT7cQOm^fyC0ww`$XAKy56@m~j?b>M-qYll33 zK+5<%C+s{YaptIDDRXjG4q5V-AEEoF zQ1Hc?>q=HEzPmKGw7ERed7<)OzdTj-d0|%V_jmp9)YqlwZPnw@q+Xnmb(DOs;Xub4 zxDIcnM%Y%w8ZIl_a&UmPBSKb0Q7F%QJu=c7Sv<63aO7T9-+PXxb^9%Tp^7GsoS4apvu zL_ce;@{5N}s@N?Mza>Gx*pM}J(V(J%7-RY-iEkzBYQ^>`@$Jnw;FBdjvG@WuJJ9!j z$2Vxx&$zea%W>GhFF|h~fe-I;KQR=f91F4>0CL}vB=S+9IW9WqAY%f3D3U&8NSp$Y z?No?d2R7l4vEy7QP!inmwqizN-q-l^&hf00xK*&N6Z_XdmJeOkMmNZ^!wrw8bB>9@ zTFzE0gE1Q9kic{w-#j14}u}ES>z8y{{_hQc8WY` z|M2$pm<1WL=OAl@J=*fNXK(K$+m_^(fH*7}!UB+aRDiYkW1Dji@Jy^LK+4q#>Ns^5 z$Yb94gH-(uAp3MT$QX}-?8^(_0PuBiAow20E;^ z+a%6Ti92=z^58LU3TPfzjMr(0s@o(4b61M}qHnMrF9AFJ<_4j4=;DMI8ciC0WVf%aI7`X#q zD#e`cI#T5nkBeT)y?x@gTpYeFTc@gRIUTn1#eOX>mQh{_`Um!ruK4bRowSkEOCzta zeET^L_4;U?F+ag$UGg!iPuU<#Z{Gs@yavf_3+%Rw&(PFg_h}WM)vyc8iF3u=$l!66 z=OxCNdh2Zw{JHKlx&D4@JapeRP5WS*rtW|8TDK*vL7rSM&w(tjirhQKBQOTVjynIz zRqLjMefy1bGd$X84TfJ;x{BWjvV0D5KkkBTeFT!zS_s)&5~1%lH#jeltK8?XMX#F47uZ+;?A_Cov1euNrK? zAIJ7hsryjm-dILXW#Z3==Dc;ZQOL0l&i6iYuFW~b`fNK%weu~=GH`~HV?mZlBKNi% zjq~+2Dh9{3M%vDY-DkhvnQTx^oX(#Vwya$6n`mF<5o)oz^&Pc4qb+U%%sWsdVn{KfilCAtk zf?S)4A|DHy`Scghm3Ud=Q^Xj?eC?DN%e_xqTh;|2+qhliI*@HVF7=<|qY4i61Mo3Y z+PUiEq}IV8?M@K+WRd5He5S~GBA+dCvB(#Te5uG+i(D!4%_83-a;?a^&q2oE(@fDH znnx}*FKIF!EfQxt$bLkf0%E<7m^3u}deImwbKJsS8*4`LP}i~CHuV0?v@v7qbAjKo zGkvtJFnPWj`;$PHr6BvXLhAdBjd9IbAmdRkwzXiByv~rXNjwwe+%6HhMPwZ}W;Cur z7WEzL3W@EWiR)4PF~&t8%Vi*Q{FBt{Seh|Lj&%}ev-oZUd&F=00xz6(D#-W+BA1K2 zPUL$;-XwCP$i40La~{$x?Z)LH*WmHQoDcK0%xA_zmCu_X&%?h42Y>?>sry}VAkQ}s z1|9G?kmGSO$g&V*e{_ETDb4YVK27aYN+ou+wABbYXsaD$EC2$k_K{~W`KjhQt*E={=NM%)^U^cr5v$YYC-0< z1!QiWAajd2SLGHDGPiV)x#fes{WjN}xo_ERu(jKi;MErVhT?wD6m`GHJcsWtR{cC} zB^p4f1KICKMSfA_-v0D+?HxH^&bwe6QKI~h1X*T_e6q;PMENY(1KG=4*2db!OXMDq>*(N%J#%Z!un22d z_TUcQ>EZcR2F6>@$F4Z$fJkdkT*t?DkL-wU9o`g`H?-H+TeAqwHrS8$!Vy*)s0;JR zg}@vOB&Q0Hwsk_;)?>G;<6(iU>r&X8eQOi{LFFpmDImwHH(o#6T!q9cfo-4bKj~xFS`{bq z3KcgF42#S2dz_zh4Kz!<6xc|+{&h3QS?)vjd(Lc*pB`Ufd4+284Upxis{%RjI%-k) zbyUI`YJZXrTXURx^V-#12kZOG>(`FszqTz;QrmK$u@1%y^uLe2)|+eBcwK(I8lRg$ zmghijCxg~`q7?^HzFp+|McyiM8^}3+TjViA@czK0fmxj6ER3sOQ)4*ya?Qbd7RKfN zhulyUz70p*fcs0RXKcVLozfNsb3a`VTiP{)EN87(auLX4=2V2X$Be=`#-f2??}4yh z7v{sir1QdaT8(&SPCB>V?e*8zL+lVM)}Cht&tx*HRjy}(EY%|4F0z?>MjmnxoUgD> z7oB%kAn!WFN`aBiS5vR=h+C9ycaY_Hk<&#sbEser!_<8p&M{?Obo&Lx;p3KrIQ_P7 ztV5kk{H-dNG>}EFAMak|BL8^Hwo4bP?Y0)SE#mhN@%t8JK1H{wHFgh3`C*Y8MeYJw zz5_WfKZ0JT%AbHqeTC*ncOD`OuDqYnsOvwpX$;d@M3IgpUPGtSHpcHWAcw zQz)-1K$e@t<`K>B?P+F-;+ z{D-m!NWa-2{qjKCtpI6v3COY@q}|IP?cN7z_fL>^J3*FVcPYD5K-!%J((VF~c56YF z8$jwC!4UWYNd222OBcwo{in#@`ps7R;kZS8*RP)MAjeFKv=01wT=!lbd$x{g+P!FW z-frIUnlV_|Cte${?W_yJ*Fb$O-fKd=o@2bUqEJWML}4Zvf?NnjfR&)O*Za}X?h?E9VRqQnv|Y8GXO9i33^o5?SXF6BlWX2s|&r{YQiJ zIRo~6&QAiz%uV&GjjutLls_r?Sde9g$a6)$O5`e$uYW+<-vY916uCD?-a}mzJPNCO zb*+-;7WjyL_*#;2X@5|)@fpbS1IY3Iq(P1O4v=fyc}U5_K6UY*6)jK&TF?Z zuR3QxgzNY=_$s<9A#XKF=8s_nqf1$t4mxu8#yz&wGKqz7%As02%jsk?$1! zeIR`w5n1OKWboOCtbDc2v`f4m@sE21-}i_==8*%kJSwt|8H`mVv9`cYY?2~2sTips zi}R?;e;CMoPX{?(%R!FWr6S)94uJeQ$bH+i$CR7{(mo%g|4NYKaXH9x3rPD-An)Pq z{GujqzxP4<{S&0$Paw;vM&-93NWUXM z`b`7rHy5N|K1jb3kmYKS_IH77_fe7mEb{9je*`k#*C69LPpEh?Amfb(S&jr5rwWV& zuluul?(K0f3i7iezW`F7^cVGP!5nZ1 z%i!l!d&5E6js;l`1RXF*WOF~>igx0%@mX1{vI!mo8Dqg=+C{6t;unn-urFig_f+k}k529b;URR!h8G*wl&LR`J^& z^c&%yn?)~Gc|>ltEEj+5!*L+X5|Mk`GyIaouM9TTVi$Tr`3(X&R^!3`#>9NzBunC? zh;J@^1LsHcn^l~ZAmi@$qH5+=DXWVYX5d1NcmVW0=xzs06s5rhv+{8S$+UHo}*fn{XCHVi$RuRkov1cf0O8I zME{`Z_4@gD8M6nT*|p;=|A{5FzpubNu)Q{kze5=NvdSx2m; zI2eNbmdKxgEIxYt$O13{KFkoFgYoSTSNyMru;g47=edT~TQU-ajJjPo$a_3*gVoBM{yg8%&dW(4SPAN#c7itznK#T$M14b8}lwq3$V*swndAp4ggECAULR9SWMx*23? z7rA#$aX+4|uBj4`7yWu*lk%pD@d?Os_*<&&lR%c6K(2xNMg9UrCxaa81tKp8sb2%K zRDm4J8$~wPamTN$WAFOW!@Y0Hh zJzp;XStfm~Y$t=XJw@c@AlrQuWZMHjQSxpeW5e<;6wLE23MX*U^UITfVcGLZ3Cfwa2{q+K=0@_^`H0BQfW$nT5%mB>z)@{a-O z?}98xfb`1-+0J5+?G%Hwy9{KxS@aDc?YD{iPmuOILD~=f6#t=&2WfXE$ha#&+Fb(D z?i!Hg4v=>5g0$NX((Xr)c0)eHe<*Pv^~ZwLXM)t9F8Xso&TFyA-g=XJuhz1Ef4{k_ zd#{Ya!dwU9k8QX7K71`Ef8O`nZoTBWN!SK5{~aLv6AK^ajh9xAyv_$%t`hlLkQ23b~$tYZZ~1Jfljs$sWTY~K^#PeJB0<_j;Bbr?wbB$4w)UM2E+ zk?TNi?=Oh_y6C@@`fsHEXQ|)wOJ%;ae?(LjKhACxz~mFu@doueJqr=V22mhh$4))iMZZAl){iJhL`w0>& z17z$1kZo0fY`c#2`WVmig@@JWCoBJ@+P)8D(QU=V4YWpNhkstDOxmjd5ueS$A9MZ; zWQpIY`>r#;Zm;a>td^5;C z-z{7q>6kval`AGuvnj^dbG{@IJuA>XdvU zpyM3HgSv0t^EH8IX&Vrk`p>-$0MWj_{VnFLaQ zB*X;BKYTno~!7Np&yAnmq*wD=py@~P88R0>Y zE!G>323rk5wScHKKo9^jk&$hUotRa(!$U*;~(YzSHq5>p8f-gEH@L zA85z+dw-j8S=gUQ{4vwk_2JuZ$6&k9`_C5YXzL2oK=wHg4ZNe8#4ExXP~!WN}9*S?A-uzc24+H_5o9z$WY(RE{l?(aLuZ zkR<`+t92mrt^t|*10p{m`qw~~4?vDpr^x15b@Vea-{0lO(1RW7x`(B?G72G*7Io~s@b_?l_lz3=`k4t2Cm z5$1yIUn$7`R)cyCwae=+kYz}m(v1LF%(d$&Yrl=3m;a;rYcOj=AmYc4Z9u z|D5QgLA~Flu>s!{M4v~zZwx{kYMagqzCYBATxgfM2l~!q`4n9L;x+5O1zGmnQ^~zC z0-w|E(lKCXM-RmBT;ccUTICsA<9=58UMkK#Aj@MQUu_ZjeUb4~2cA9-K~H&m!$p;sUI><#UBqc&XFMJd4|Z3fwXuAWO+m6PLSjBsnnZu z99OjKbvp*%@)rFyp3!d|&=kTI9nUvu&%$=Ow%AhohVV6z?}o1dm{<|0qisCM+|xnk zL|wPM&L6Mxz89pfciu*Ztid=3;CH7mhcx6seJkvCitX+DDBr(>eD$5kiTkSd(m<9X zkh!lF`B{+tcvIv7@oHbOJIFEvU zkxv%+9FUp|M7~|*dqg((qjkvrH)Whd@YzeQ1={PvHaNF!cZ~4npsq=Mzg-^M$w2*J z{B@u%4XlznzSgBl>NsDl)Aagoe9k)WAl2UsK$fdS)^loJ8#l{bG{8pmy`O2wKUl@F z5>=e|2}*7{RLO6H%wa^5lE;CRd)wI?pR-!i_j6Vpmo8~HA3o+e(%6YAMk>fMMdaR? z#`BCR=c(;%K5R13J?IzsPMciu-2%Jd+R2}!a##bh^tK&%7NS&Qbi&R%SA14J z)6LIF^z*!4=HV)C3CObb2>5}ogDgLR-1iJVGQ3^gzGlomgRL?4NGtpk>}tJLOHP>* zW3_MtxCwuppVy@BJ&-M3kfPds5ajxNQskFFZ{Gvb?hBA*C+N-dQA#%V*H-ba_t!fA zF}n}2hT=P5)R9c(O+Oa4$MXwD-g#&EetSLIGmqUcv0_n2+hmYqmm_is$Q)_2NnQ^< zTIHJz^3`&YF9yB-$gy1K8MD`5D|Tc5&t1C6mp)r0#tz2heLnddUePfs=9M5z706e& zhvQ=aq)7<{6B+fS2^oZSvV^vOL zKt@Oexh@VD`8bdz1MF|ybUT{C+~ZcNu`7@`6%w~j*bH{ykFgIqPPH`!G~3$M=f_ec zb`|WU{iJsBjXqw*nv|yc^=FV{|02l#y)W{&AlKnBlhv_yCdgxK5y)ffDv>W0{Y@Z` zv3G;qhd(Ov%is{m_zm46Hv1$l|c z7l4en7G%5|LB^{E8E+HFc+Y{1^g77W2{O)i;85^Kkz>=9ydTK8$spsVgN!=|WZcCd z;}(OAcp1oY1IV~OIUwUL0vYK%kfj`Cylo)k zeGW3eogm{4%~bLB1R3ujknxTN8E-nsNVy=(*&ySr0vYEyka1oInb(IP<9r1&jy+Sw zi3S;A9LRDA$Zc?v$mV%aF1915Q!L+6F`n(hOhwS*j9iX>pGBQ-wbl>@Y0H$tzI7hvLuVF z$IZM)S1S9n4A|s}-EHFcSCA1t1)1~rqStYP41T{{wZx9jQh6i^vq14pT94PvJ2hL` zWP_BKihQZaH;8;UNXtKq{G#aJ6ZuDwxelA7_PP6jln((}QbESg5_vhu@m~#cyssB| zgXr%EY5%my--Dc=VL2+^Q6Tr(XMmiivqA2oe+P1&szA=u-5}@bIgsn-HE;y@GZ+ny znrmAzU@|xooFZ}-Nd1{0?bm^$z(0bqU^BQI_@>C6qW>DC{pwRx9#?|Q>xxrtYczNl zNcjVBckp|V<*r=S-{9Q-e_f8*cZjvSy|Uly!A4n!ZRi`z4q@y(wKkGLmK>04p+w~M z!c8FOxJ~38AjdBDG&Q!#Am=y-wo+Brf^nI_7b-FMr|8~}g@3(d$mU;fm_UJQqzH0jfkR==B zs|P^#w?*V*&rsu;2l9O90+BBRIj+}%^qY33+Sc}3pthwdklWs$K#udMJhd&22PrQG zIUeVU{NO@0uG>KB2P{(Ke>6xr2V}ejAlthTWLYcv8$kB0TI$WQ%fdMQZ?E++;|5!! z?ES6qrvTM;3fpJlxbhgo%xfPApQpt9@OgrXRgb>Vwgu$=Zo9})i&cFh$T7(j7J?kB zN@2aQMYtVgyePD#*FnZv>h&2Q%h@1bEiF)T2}s+EL^j8@tjr%*?|lM!FN5_gjKI9f z^$&*kw+`{lbqN!zLfYbMeJt_bw+%dZm$+Qzln%0FgM4-P*-D-YGUp7D&75n@ocaA= zU#ok7tY={aopVM*c+RU4FPQUuX^Xxagf`Pvm@&e=qVnkslCwv&b)t ztkjk@GtYB@G&Ss zEOQLlHhtDhTQ5jkx1Ou!q(vggax_T&Qqi9;`f}0V1afR|6WNJ_Mh0t3uASOBTNz(0dlD2(bd6 zACXwS_a${Mz4gX@m)^Pv^kE8O8P8r7tx~z&2(sKOvd-0f54TPaRvrP_S`*093bNlHfgI!QBI_J}i{`zX9?5a1#2$OGYAY3F+w(!T zwVL%t-l>LXfW>zEt@&fs_Z9DSa%+IAcUM_dRKA`mWzGqayGa!YGS(L%sJy zV8g>1*AnC$nq3OyEc$^ zAA_{p0n#E=uI$Eu)K3CwUnFv=$W`e-vH9S7G(TKKw3Tvvb+J(?h}x9 z(N`+FaUd-wfGlYs=XRRN=DsnnpSAT{+lP7{XJo(k1{jZp{ouJ3GstTUUx#BW`tB#^ zOU|o>8$kAXGswPe1HCZ>^*T*O9KJpsWYKx|_nk0h$cwhyU>k9j@{0#qCWzeYKf-_h zrbhg7U{fS^4~pN@AR`RCTE)};n&y6_N#aDqmL&$9LTaS z$Uf-!L3*F3dN%y?R2|Z8ro8`Iha>aj@fS@-dowz@{X=qBh>Nray37jBvzYn2go*KZ?LUI{IUOw)~og| z2Pv-=`BjlW5c!)Ml|J$&WnT=^{z8%CZdUSGkmUf8Cx|>nWSzhI{P6UCudmZ2|3dhb zi{D)mXGFCc*K0tQ8j&9p`4y1|+@kb*fPA%3i|H9!~+; z&bc7VYLI=nOk}eUWkw%#Uo_3bsev)@i!jJ8nP?zS_ zs{gj2J@(J6V@|<78!o+ll`&0YB=EYd0@_IeU z@;=B{``oSM{XyCuEV9{$BBKxee!rgN%SddOtZEA6i@3pcoFZ+)+$xpVcS`%8i>${g z_}QJfE7f|7y9fQ~=evQ$FR2rH_UrgRs($8zl+O_PAE1|aozf2hsUHEdW)G>~N9qrh z`e{->Q|hy&e!0{aOZ|mXzsJ2Q{=OjNA1rdR$f+PLPQOph-M1j+%kNiyl_32#ihRGw zn?-(J{y^1#qWW0Eh4-xq|kQOWdr0iCMw5tMH^R3kXEcJSRf{cmh zZ?%l?_cN<0GIz-js5WvyzA6DL@yGV=6Wv3i`%L6v4=VjgkmW#-Yc5G-bKT~moj!8l z*%aC^694qtEqSi*zB&(eX|T~H6YT}})nlcO`D;6qx?b;Vx4@otA4~2%pqER7>e~>I z`aWYD_@2m8J)Yv11M2m!+Zy|jigh%|k|(l`ZC=xtRj6%iHEhi5Ve|PT^O|0_mGp>e zV+zPJ3*@TgN;2HcT^@~;V z91rXmlYe^MZAbnrW}n&ved=d}DW)yEAW%iSXDF*nZ_ z(vUM_ZGla1tl)Rn8qeGI`JQ_|@3pA!b3$f5C4W}=Tn93vX@609Edts0St4Hsa-1qe zHs>HOFbAF-*zQ@!$o$jeU-&}b@y|tF8fVFOfRtr|nGY`Yo{I>rW)-Gd8O} zt^rwY5&3SB>qY(+WR5eQROcyqAmtK}=gRkp{3yssPlGIPioWD2Wq&zH%{q|fUXcEq zKwg(W4SM%Fq~3a3`HcYSu{X$aI7qu=LHeH}@>-E^1)1NyAUzsEmRCXgwSn~O7J2_B zB_9t`f2znUMJ^Tj5s-181sU-*kmX~Lk8D z>iZ(4FNUv=Ezk7b79&u{Oyfc3pDxS?IS%E*T9ExfmDMe;k9}74@dS{sW{bQ4q-_Dn znCFSStG)%FEvrFa=wBuA8pOX9>~mi^0(nlswqo2HUB5-O-wm?-3*;;3d6myFko)E7 zAp3Zu$j^ancUO4^>w|fhUaj(OlQ=s-wi~-uwVMpG-5jt4e{5?#NSi-`ERRclpSDK$ z_lydDuiEJppNJQ1%f%n#T@A9-iu{zwcC%_H6XcxaiCh9Qr`JG^#os}0ZzEq+^#_2A zcm&9j2C|*gK-v|8TyqbJ{5;6@*D3NhAmc~=RmIsIWEl@KPAW+I#USII1JZsCNXzR% zmfJwuKL*nNU6A%4gS7t_NXx-5Df>|%?GFZNe-udjsUYohL0X;#vYZFfJ~;0GsT{NC zU~43v>ka>A5p`Uxka??-Hk%}e4x!bea&v`gAal+WmVwpyW1f$LEL%j@d7951)k%Bp zu;~`N5icvhv7p&s)2~_ll3-IPc6+~~{E|Svnh$avtrEEcWV~ynzFO++d{~uLe248$ian4`iH8BEJlB zJ#>lujp%!%e&}n;Z!}0h7o`3EQa@4Zj}dt>$Y+u+5&0_7Un})DN&THtf4|f>Nc~$< z|E|a%iu|d_UyA&l$O*5jyb?k7FI8kc|Nm3Q;P?`a&wQBQtm8fFFKePu+N+fI>xC^K z$9+4qGlhkuI_CBHO&eVw{f6pe63B9d$hvRleec#vwJ*$qO@Y|`Ui_{P z`UUO_b&GE!>{`Wk^qZ>913}Jze{DuzquNY^jd|Q@6rVPU(d*mKZ_egQjEuL8@sM#z zZC7ot09j6XTaDqFAh)@tAjf(W$gzG7c=V6c$o&j0fLHbYHrs7Qn>7NbKe-TLk z^FaESf%Lo%WVr{VeB|V(tqH4%6~VIB@v|GG>~z!MLt#J6(W~` zjC&o(xPJf{_W_V`p9C4P1!VaMWW34mt7oR}{6IY`)dcbxs%JZt{uPkze*iN67a-&R z3^M+(zpMCTK}J3pWSIgoP9;eH+d=x*gY@4F(*Grp`Mm?u^Hb0(A1c3NLHcEa^qUXT z?`)8Mt3Y~O1+r`ax#sT_**jO2XB@h)UiEtZKgBVJ47SGGW3BMJjOtj)_E`9AAD{bU zh8b<)=d(LM>ia(5X6e&5kbT?ED0u z=U(3SPGz^-$7&2;0r~0!kv{_&Cpc!_vlJWs-~WodR_#-wK2dQJ!F2r5_YA3922#7a zOZ9CX$iCHp-2U$e*{5-zD!U^=mT4g4oFuY&4U&L%gSiFeG2U-8;B%`@{`YaxkA>}V z{Mb_08{zZQiS~lm{`sh*Z8^yKsRh}GEh3ZDlgt?}t%%Rm>pel12_Rn`DRP?VgLwzf z&og9$17 z2ZAiqMD87rz~`|O#J31`6=M4!NbhGruF3C39{!Dz_XDX<6?wMEOGUmJI#tisOv@E0MhwiQN14x|vn~>KX`n9E=x6YL9mgWL&!j#5 z>tr08gZE}7RK+azVs0-(bugiZ> zbG8v=X%<)75Qb6J4M#*X!iH)ZJo56 z_@ing6U>*_t3bAQ9mw(rsn_u|&1;KhiML67+Q4q{J9wvxa}vn@p>U6N~q__s>DZZPs^6*CcJx$~FsHe&djQutm9 z{bUcsCta=dL}Z;<=G14K>&nO7Rfk_FOU zx3en-&p{HdSL0eDan?)RO&}w;f!p!N*f9~Rt)(E_{=LW>MSei!M@9Y_q(|lemHR@F zaw*9D#62QE3Nqf)Aj_Mg?*Mt+8i;wNeGEto7o`0`Aj@Qsc9(&)s|2aJ4W!+DAj`8L z?Y;qN_cKV%utCahEXa}!((Z1Mb`2mkPlJs23dqt0(r##^vKtLjvmZ#iWRN8Tq+J%s zc&CHZEC*@#|FHKqa8{0M|M#kYmMHR;miXH67GqIz^}R{pGyQ`hz=eiLd0 z?WzSNWtMAQ1Xb;Fvjeso(;yhxNB>7{AoJX2aLe3`b%ISdDCq~4{{`!Bx&J-u@%^-V z`kL{_KTD!^iQ*`#SerJUj1H;$Dkh0K=hAb}U!(g!3-QyKmO;rk)}#53@5{B+A=}K3 zEfals==ttYvX}J+zqISetZRdBF?KC-^j?7Ojou58^yTw2Q+Yp-V&4oUbx>p8h3~MC zL^&cR_xB6J?+@!Zn(JJnZ9f&xsGZa_%wCR&LUU*c#KJ65N)y@1y$Ju z>z6~-C!0ml^KPN`ouT@6w|>0!ldV5${R!)zS&zn&JRhe0j;-ds*z?|@d&@*W9?G!; zluWXImi73$FT*eW{G_%>G(J5R*QL(KRAR;FL;2)W;au-H*S$Gmjvj@o=2h#TKyBkK zdW1D_E|h)+RKA%|GTZ%a)(^+HjiI*F5GecIq3jQUk|L;Wcs7*XdDd^Y{xFo^Gf;l7 zL&+CVew{W5etn?qwt}+T8A?V&*Vyd6t1U2>tpho|Q^=c@26Dr11>z_fzSligruQ~Fr3&*>R4MW_1P8$wcjqKB1O&eM8Jj7_TjPf9rxZ_qVEC!#wO-tluWF zev7VrhOBd8c0pF|!hXT`Gbs6_f6%{xlK3{RAx`ppBcEyfy^-pRMB~$A%CgjLw~CnY zb8WOuayAJ$w}+Cetw(bme|=N$u~cGHZM%={_bpW3Lk5Ixdf33Ap9&=R4;Z5kDqjk|?K5w$-jfZHdi7O}S9=3zSX! z&BGWrgwh8?<+>1RO}}CNWB1oXNt;2TzZ2Acbh7pHp!~0digz=VJPc*G1S;+a)_=6# zW{cq070Rz4lt*gwpR4p^UgnH&%&W3rjpC#q+Bwqg6k zhArB*O#imC@*!dDv!UcksG2%$8}wdK^>1W7J~vBlShG*ZucOs2Q5;1P2Yj76M+L-- z&k-hxT<6kr&q2eny3Tzqw+nUc2^IGQ>m}ANw|}tJ+t5le#TO@6dQ# zj;2o8PJ$Z2baM{W_!gTh%r3-G`w*yc@-#8Y&##4&`=Me!XZjc zS)mwh!fVL99Yd~SDESvu{4Ixvn%{>yhP=B|sO^lM!?wBsCby~e`>Zd4vLCQZSSwdS z>32Y7m<1L8NhtXaswR+DcU#lygL~A(upVs{P)a_WC*LXW-(WaVn zp{g!`8dEXU9F$qFh8hb^iDA2j=Z8YcdbdVFpRZsM38e;p}3?SEdfQ64=MN9^)_^=+-kYcE#q9l5?87yh>_J(8&YinJQ@ zh!d|T+HQr;bCdHF{UyxxnNXEnZoM384SWT)26o&t=p&(W9ttI=K=n_8I>tR^y#~tv zBPjcCpd@2Nu-gL4Z?N@)tRDyEcRG~cR4BO-D*jw3zZanF-iEUK3`%}+f0w<2{ctGz zeW2{eLfM}LCFeuMeF4gD36$LzPWf46S+U1~emWL2+Q+_A7j!8*C@TTQ(8AZa_U3EQrGXY1|hS24u+ zgWt)Jb!ezH7fQCX-f(Qe2K$aCUSUaPKQCs&eof%GuM7&uqn3PHTJt9-7oEaVS#U+mlB@K8fT z&NSa*`wqY^{uz}j`@IPz@2(|ISv2mksc|kx*Bm?uCI7JATs>8evs7_({d+*$hVQS5 z*B7nPVMm7eFGESUqr!1+52*EC0F^(=m(Jw>>8@UrL$6iZ{@{O11j`XW93d-d{e4eKm-eDE)TidPKFcxcQ!9~=6{LFwbI z7g=wJ-MMw5UE1g73mv}<+e+I%1$C@=4(izPKGboj&idC-8CxG0YUv6k1E7voTU*}& zs-AtIu44|i9@X=oGW|7HiN{dnyroccR0&ntLa1>pGc%8Oy-@XyfQ5cu>%OT)A;t|* z)?Zlv9!f@@5PT1Tk_pz&vVNuY=Ej(Of6VO0?~f_>SgIVS25KzzF!RKavo}=RKqwgw z^=uF8`$CQDXqY^=S#PeEs6BmN>s`xa$DLu$bG#+6-p@Oo6l&=ORoeonmgUwbPYAVi zJUR4_hLUkmwH|Lhe!Y}c-u!t~-`C%f?}cd-doP&mB^tBVuj*LwOX@kZmOSz6DNGV2 zw8^&ItcI#deTh@T^PW($8IqXg1;^SMqI(2*nr#2p6J-Jm=7JaHJ7e?c2@;KMjv9F1?+S5Xu-6w^yRzk&X zSWju6Co7?T^;cuJ)VAxM9(<30DZWko9$LBmCtw?$7o01%sd1E^5n{GGGvw$9m7~A) z9VUl)`5Tm63e|tB+gqL$+B-nCZwNJy8(H7W{rg*w#vL*HcfPZ}+G8nlj8a(cIR9|l zzpVdeJ${W@i+}t+j`T=WqaOADPF)*WXQy5-YE1IUrCrZYf|5i37RLD=RLhr8Ywq9H zyG{w`hhw153s+me4a&X>D*sbZ@*0%g5~%Y-w{wENDb#+t9hBuBP=2GK!AGa zhO(avW$_%8{bDGogR)-Z$bHwJujT+ZidnyfQtVVl>e(x9v?vYFNc!VQ0K?(N`l|6P<{tM`5g^qSq$ZO zHk4cnKCz zKJ}gTt*3>#9|z@evGu#5gpR;Zc+i8gXM?=M*WKM@w?i+ALs9_5zn_Ho3tFZo#^^dHtdu8bF24%mo z_06sCW__gfbD-o(w_oG-o2}nz{S7Et>h?Of|K#=-e-C-vLFL^7N`|_9XSW~Z_M_Z> zyxX_DD$MQaP;+~=_4}aWeE=n2K#l(!x6i*iv_B8k{(vOF?2^Dvd+uwG3v5goCa%p|)Jw@?a#h$d`VK7Z18}0aR-NCKH@#BJ6d#B7qr@^VOhvN4E=G+qIVlkAwXFb}s@%Loa+#mM&71(sR zHTVsJlEK!S@=H7r{6=9j-geXMcSX8i+MM>W?;PwF+4fue{loG8EwfL% zir~`~#_LIck4WOduntCG7hi*m?f0GI{F)x86Z+$_Jk2uj7+h_&rg_t%-kOW!xAI2Y#OzT4n6_rwbB33Xfx zC8yjQ^odY1*?Lq@^7Ty<_jfh^LXW>3AKN$lf4Jnn5O*$=te6$%qt*Rk{p3K6XAsn} z=oIVcLdi95PdpHgtp!kaM?v|W3?)T{s|c#l)+J6*!P7sZjNUY);{zhK?0L>`PJ>mQ9@ zmlV6!GP4S5j5SbWu7|2!F>>+O^8=w|2~?gf=Z5{_2PnPeBcXqNsAtDR#e58^|3&wI z=>E!ip?x-#%!SJHxb^t)ru=v2t4FWQM3+;u>esF9P!oMqllFD_NK}Kyq#7o4N}ZeB zM;jlDYFt-6RApJ%s4ku+3jF+HD7hUf?yFEOE1~jrek{!6Nl?DcZQJzr^z%hEV~~Fd zaU|tX`KqB>YN7Ha9;ZF+|5x*9lkaO#Yhx+YI{67|J~HM9-yEn|TS8su4z->Km1`d; zDTI3MdxG^-q2i~H;Xl>W@ADQ{g|U}9UnNvM3!y4n1~vA~C&G9JK#gw%RDThal)`cz zs`W*;b$v3m_Vmx1mN|DGc1^vGUS$8Bo(efe!}wfecgslL2i7}0wHB)!Zx(j(??i|{ zA733}?DBMoc^*uOnZvbc6Zht89D6bL%k4A#nGoYas5x6@z5BDNF{5pjt?xPG7!&`! z!g|LYi;rXqoNoWwZfo#QyQb^&P}p{(7qm=F;h}L{10}aX)ztR6u$_8BZO6^5Zwu9b z>+@l|Zu>&e_kohbq2d)WEP{N=@{fD2uP4=H_SX883zYqoCv* zs9epBHEPf9tIu()*W_!)a&N0yj=9J=mP2Lf@G_qf;GsObLrH=4hWN4fkJLEccx>FC zTVeaf_Rn6FT6a^Qfv$I)e%Ow%{i#s$c)Gv-hd3_!gkCr|X3Y+3aG88w3HeH)q{4bb z{kkXqOyl^FD&c5dIv?R&fZV7DLP_LJOxhTG4v{_)#k z-5v2x$WsKBCz`M37=8n5<3!5YxS)0N7+BzWoZ*=BplV)XeHF}F67n1mB@bDjYyE5M zE39{UH`s3oCEHow$@)=HIqtXqtNYh|FW8?7HLmlmudx2J_0I2y?YIw=jDfN*bo;q( zzr^iVx&0Qm-{bZN-2SE8J1q@yxswkM4Yl7t1a(~c0_xb*>w}<=gX+H?O74O> z$Ir2T@`vGEJ{w9t`Y4>c2iFGu2dHsmd>r(1p!DXp>z{4U-nxCFKi@IGL4A+f?loPj zqt7yEe=6}hsB-Qav);`5B#bQ=YU~A2Qf!u)Rd68>)%}UvdVd=9eozuWH`J0hdF=P^ zoTwJHMWS`0M_G^5WB)K>#gF~UC!hJw^`3Jj>Qd)0{vO4ghr_X`CpPid?w;3%*P!Wo z&(A~588DgC`kU52vi`aC)lg&T{6(wQj%pXRz<|Rj}^?Wxo-WYyoAT4;A-VsGd`x z>?T9Wg-~`kLdC6vvTwaS*mr@l?*%0TpzMc3*&hXEUjk+EcPRTAP;v*9{hwa%{PrK8 z+3A@64XIjV!@TVmImdG6?C^D{VURfrs4hFZ6RlAWN& zQUbN@M$`xWekhB_t-lE+A484xbL;VaWehbpHwWq0PqLLLr|MS?i+g4z@?a!mzDwPQ z)V_+mvdy7S90pa7`X>AN4EN20l3(54^829ohk7>1dVCCJ)YV8|KO>QSK*!<^h3h2G zNwQIHJrqZr*Q@b8=L*{L=!;|o`E*RkiyB~#+oCbVx{^<$_Vm|ji66qa`#?#q^=ST+ z$B_fV>xf}Z{4>i`!pxTGKeJ?1B0_*W@Q%T%LdfIo>$VTz>P#kf5@5bAz zfVMpPBAGzG^lhb@;>YyJHe$oJYQuSTaLe2|*sJX=*M95IK~I^Z*B>W|8gQ zg_3Wrudp5;bK)P&LF_tOdL+@f2lYuE+iK#(#}U=t^IxI9p-?gvD#l&bpRoR-^&0D+ zSpV7je!qlmb|lpPeX8|~q4xC$tv?AB{}m|t%>A8K2mcMB`UgPCPEh{Gxc^l5PjP=4 z)L5>yo}4G&JF8?~qIqwQJ@lO&S7+9=sb2G)T=J7Bug(RkXG-7H`5g3X<9lVZT-ze3 z>X(}xu#M(B`oFX5$sPXh&^H!JuCyN28^1QFr7p#pg-ztY%Jw&hq4z=Nt|-XG2?PC9v(Fs+(m!uVt&G<&{uU z4K>$oS_S=3sCh59{uR_UYs-vQ$@@+tq4a~GX9v;u=<&Ic-8s!^|<}WOD}8*ydSZVy=Q&yuV%J*ak{Uq2_s( z^=GZWX8mpJUs_LO2K!M^+v9MkNX6EtLT!h~t-k~n|6M5g&i&on1^-Q;`nQFWeW3i$ zaQ{W_|GWDu-2a99SGvD-`{3ULYJ3|u=*joT7wFoknE71W_DbI0ot^TEGI{QnuOz-d zkLsVgKUa}+1UAZ_H0Dn(Z5m$z)O;12WiYaHO}UfT32nDQ$(z=rn$zzaj(8;Oi>t89 z>JWUlhmwP!@*WSBx5WCT?yqqB3s4^KLXGVw>#aKmzbvRY>qE)bPif}_TF=dH zmHfV}J)ru}gR;LKDtQ8iSmHf@?9#DEem^{8f)w>5&y#-K} z9S&9RaZvT14kedE{dV?rs5rMm#d!oOPBm1Vm!Uk~hKlnsRGfM!Y2Pix=>!$$Sg1JX zK*hNjD$Z3<9y6ih+zl0HE|k0i<^KWH@3Vgf_517@-GiP3)xRlJ`&Mq>-R=8AoH;$`H7c%7g;*N2MNA1dAuDA^k- z-r-Qc7k`fR%b?=l3KjoesQAx7#eW&9-nXG5eGC9n4AYFc@(PNr=j8|)^C;kefv$J^wXj8TmqG6I#ixnP%EZ-iFHa8I-Jsij%cLh|?P?&QPd0c~Eiog7O#*73V0ZI448NMee^2>iBn) z+vC@!MI6_nW8VMQ*khASUYl=|Sg$_2wo}cT*M1t4q&trs9-5QOH%~o(lx^55<@y?v zL>2QR+Zw3(tcRM(te%0nQ1e~@wH=DhGP4S5JJmpK|9Ug4SI~2zes{gVdNI^-uFQIs zS!33lS=6fakPEfW3d~{{o!6@T{1qtKt9Ka7kx*l~$@&ve@-&q1^VZ{QqOyrKk^JtF z+Z%uPh-@Xwp~#xQRfAIJx=)|Rb6x9t67)r5$n*1~pk#veXkDg!S3h~>SAxw9+r41F znsh(TV{QHYExI;YX5UrVW%UjDw}z^HM=04BYD|Y%FNDe$)soKi--ynf=lbklZdSvd z8--XqKuMwXC}#XMMAiHdX9_mcZ8zI~U;mC@jr}q|CZM&WW!hU-q)b-JM)~|uukDs>wGL-#WP-~zLN>)SJ^&c4GYz1Yv zJ(S%DC@F-pJ0Gg$GAO%iq3kN4WImMLPMZb0J)!LOhq5~yN{XTEu7Ya0$^Cb_|52#z z`K0yuYv^IjUo^LCWAgV)-)Q`sI*K7t4LZ)Lwne$A`*iWs$qPljr_*7~jPe*h}?LihjR z{$JhSVaw3J36%d(sCc_VNrC%MbpM&|Ki~aj?qA!SC%?DtnbiBgIwp(?Te_bg1U14&4SwF)1vDPPBKgW9d7@GS10C z#utq}oz4B;sC*>Krel}3U%`;neXx#P@q2`rB&Irl1=Kt&fT~M<8QX>D{h(xs^{A%! z{pg~n!f|*sHpR9pvtRk|_?6nP8k=Z6j2;^DUI-&I zvGHT;KSIrQVt8t8QH_6;;dijJoBZDSWgb_qa}=1xP*P^S%B(T#VTYYUT|J;=BkRMh z?*)_V%X)n6W>%-(yOthF^7xCgY`fHTskrfVsX3HS{5?@ET5;c*D~J}{`-|S2)a>() znT_To9;;}dFurr4YAS`2tKFVH|MBl=Ni#BPn>KQiiokaHT8 zTykK@Jp*bj+;4pWl*KRB+m8RTJH^YEpWE=^WA?nl*b)V{GeeJAG?=iStViVqP*So$~?FKbHs=F6=E9}a3 zvd(jgCsEzHAE)~I?U1_fOgXgib=7>=ywqF`HL9N2YRto-=3p#r*w_63t%Yvuepr}? zAy9b>pw{WhP(6#FVr_hQh_xS7od-kJdI8iu!v~;V<30=Z8uulrdxftQ8@PZl7nv@gV1jFki!<=4%Ypd`*VxnE^Fl^PuK_ z3DkV8f|{?KBg1?RgPN}~Q1dkzYQAPb&DT7r`C0-sU#p>ihq@1X zIMjXCW8Ho#)SOI#nxl)MqztO=|EX>GpXN#5_BG!2l^(}Jk82s!I5Uq9^DzKw-bO%e z`y!}$ErpurN~n2X2(=xSL2Z}JabY_RfZA>&ptfTX)OIa}idzY_9Tq~(=rX8z&pf78 zq8|^z-qivnNRr|!oEjDhM{w+{C&gMFA#Tl&<^Lc1~K7x`4|6XkqUB-2+S*N;P zab{uLf;K)z7ua{oTyt`1MpV@f+z) z{1&zB6jKkyO2*IAsz|-pHlDV;WNY|SNlsmR#oJU@{CX^Ao_wk?=*dFt^!y9g{UcP~ zSI-XPxnfGtv(5?Q-w?`vW9#v8m8t%44l0NkzC)#JqD}Rh|J}<@qA@C}@(tTHbxf0q zpI-kk+GIP%HSjbs!_Oaql4ou6)On%)H=%kyw!R!HMn*}n?+i7b?$+buscm9B4dZI! zyHjK@QDp7Y%9FKQ>bOeDlRmBiw8?e^)SMJS)x^_83w>tw)U?m6#;#SDT@Y;BUKqyH z1#c;0NhSwRfd1~nMHBwz^@LLHb z8JC3dod_k9q2iunJwCp~^J~Tz|GcEyC7LIVC%OIeQ^!(4y!7oq)wze`roZR6JgD_mz6`c(H2b2QX=CPCHB)5IJ1_X-ML2>Z!Q$C&S!OQCYC zhCOc#ad(805!M^x#oh;4?093baewYi+wXr<@Sg}(=VjKXLycp$^=IAxp4&fk`?qdy zGc(wCg{pC&^)sQQ6#m{kBtO4%LFzS9nd_eA7>gWpIn1~@#M=O>&h4S(FK*x4?W5g( zG*n%uTaWK+Rpe=;ci|d3_B%7OQ64=MM=al~@paPWwB^wk$yD-KQ?az0f2 zi>=4^wHl2#?YgQtUGJ~TZn0@1{$E>NA1j6=e*Bp=GIf52S2W%aXS&Y$P;Q_dqk&vv;p^bduSovcUor=OSd7KZ(8Ja$uTJJY^x?+US+^DVS*Pwa-+_8=%J zf~x;^w?{D|CjUQi`-W_81fr`B~)LiUl{RZpzT7TC1V(ZJS z|7^WuW$@n+D&Dr%|6=_J>nB;i%laJa)z+giMa*6=BhjAUywm8k7Ro)g<=9I)+!OMO z{rr4SC^^J>Db#ws399DLphrWHGavu%lb&B<~-G3cSUb8@5Gqigk+-v9or5_2E;{>Sq)7*Z$ z+wXV#%WnVN?cce*(}N+-E>QO__Om_>>K?|8Q1_Usq3&J01$FP@3+tV#!aa;$Q2pCO z-Lu*s>S%n7^(&z6HQi+WH7NV#Q1O3)8qfHL!aa|3q4aB^##atCzDKPuupVuT|F@al zvkl)<-!~DRF}07)@Ol_9JJdb`YAi)i+q~3zCDb-vXkFCUmzkLl2Ymq4HW^`E)Z7(8 z&1I=sX)c7?7R$`cIgSJKd1yY5g_227wQcr~P}_k}`r&iKx|{@6<0DY#kC&ioTw?uG z_t!&3+~|?8PX7X>?++F41SmNZD*qJg@pE0_OMm=am%iPb`^^f)l;}R4*12k_+&}f) zH)&qubKi8=J;z)OHG&mT^U(z#&BqX^_R&z|;%Q>KpT7nrvmXs}u)||vjxU7jxf&|g z-PWIkidh3S$GskR4ygKfhZ@6y)=z|57u8Vr8@HVw*2RTT>*6l!KSA}kc_P?%gNoN5 zCdU9(&+bs`^FXM18V4n(L#?-Sp^gFXSYHMe_a~@2+CCZLc7uxBA1cBSsJQu1aSw!& zCse%6p=4*Mc!xp7I|(Y@*--H=g^G6r zRJ=+k&qtu@djTrm+fec)RGgO8A#N8ay@&Oo)<;0)IRq-t@lbirfXZ_bRK)2}c`Bgt zJPakzL)Cr#(;R2=@yej$l|#jQ5Gvl2P@bh}vo(u6lfQq*qD&A_ScK zT93vZzc0Cj@yIV9o9O)`Q*3|ROTqsOsB!N5a;W`WD7_R)u7-*+-TgPZzry_wxc_kid21ZqzAw0SQ28!~YPlK8t`f>_Hk8bVvU|n-OWgm7`~#Vy(sAudB95lq;F< z(9|_PnppAsM#?9j7VY^=(%?1?pTjSx3As;(l3CUtvL3IcO10?oh6kj6-caq4MD;9B ztEZZHP1RHDeBV3YzoBBB^m?f0EU4PgwH~jhhP=@>jNfZkyF_uc4a6yjrOr0n&G3zfv&DV$4zlVyj?OS30{s~I&{dS1I zC6w#|wf}GOPUznkYK%uf$>~u2nM*={E>!<6P;xL-{}lIExc_1IzvTYb?*{)Zp!|nJ zNrBrZyM3D5Z-UxJ<<^ti=+rjJ&*Nn+TC;YdHI&Zq8(}Ns-`UK6X2QK&`Abx{j$x{& zc5Ldl%zLj@Vhru70VJi`HP)I5Y&42KFtRK3^Aaez*m_i7{B?XDb;z$An+3M3v)?~| z$FJCa8SitCmWN{O0wsG`Z^+k$bJ@5xzhzx!-wD`Fwe8*Zy?-sfRrXzs-E!M@T^jOk z2vtWz-u2e}zEF+*24RzLyW{M4O1fVsKFiMkesp}Ux8GE3X4)?6gOINW)OI@@YTX|P z)iV)lyH17idZN0TWBUENtXIOendvz5q4F(-%C{OSU(XL|*S>se%iKzyE9bXRvff9* zr!SQ31m(N8_2c00)tLO;=GANdznuAMsHxQcmF7ZN>sYO8L#{`m>iyLEdLM_}J3&da z^Uymp(e3I^wefcoXj=?$?t)K(e~DQRmNb+ zerrAF3)@4_NKbqJv22t_55*C4$2IM9d^!*2 zMGa6jrJoyP-N|uDuftHUIXQh zS#8!jSK{lCvkx4`LwOH@lJVA~yz$o~HI6$On=;!yXutWt<5zFL8f=>Sp2@M_gc?qT znv08|w#5u6xzqjgpjw``9@W?!yq`Z0C9heJ;wL|^ zlJ(p!CzA_KR}s4?@-YD3sK=|1GF%tVurx`*WfEuYj_@9!h3G*)4*K`!6H4}m$~_6HmQtvmJD}=+7HUrFq4K2HvNpY`@60T9&6)oSH4K2NVFXkSMNmDZ zP&HJ-1w530C6wgYh=%f2ZbM+xVO7PhJCzYy4RZ9Zwdz?m^^|jDo6lf^%OE)iVpK)}>I}sKale*5R;_ zhvw@HD4P-}pG%>}I~^+KEl~0Rlznscrnkq};4;@+<5=~MokeEl%7w~O0Gm30#IGNn zC%39aMzWq7D0$m@R7X62=Idb}sK=(M_)}YkSg%9LSJoS1b!(gG*u;5ZfaCUSMcgLd zub9_5#61Km@_49mPJ|lwG^p)TX8i`JIe!ppP0h3ZG?csq|I9Xs>W`TI8fHW^FOJ`* zO~{`IRbL@gy(QMm-Cr%n-aD7mHq><(zvWA`4Lc|N&&h`$6%HsH0k^7Vm|ZLIHTeSfI>#`XyP$3odp zuztGrv#eiW{Sqj@E8Tv*+Z)ZTepBL^KJ^>c_N-aII;XgMVYhGTIZKpzZ)vTW7c==7uNj&P-FNj z)c#mv{U)gEtp}|4=+9?-&<8=;?+NukyTh!X0rkJTORRU^BqRAhyp5skcZPc3!2Z^w zIr~4&zQImcXVt}wf78pbii9BocAr<-%k#pVjwVL;H8B3b#)f82rwJn#*&o-)#L>>yKMcpVRE_ZIi#-u;$m2!a2GoT4(lO z4ZCa>a-0SwJ8mBIC}#EsnTgI#%x8TxpV(h+pUnq_7`s5#eLK`RAF>|BjM&ugoM$Z# z`%s51LX1H$-_M_elDDDaF0;PU{ZZVA+41lDC3WXlk1G@QUg^}gY@E?WhC zFjSt2)}Msh=MuT0zZXoNTdYTUn`3gkr>@y5s;9v9%y*2X=4!L&))|Q*JXGJ=P;who zU3Xi5*m}pop}zvE&S#EurFX1J$y#^*x}@l?B$D8&`Af$+h!F z>Rnfjx!7Z#L|n;qa}HG9i=A(U*=0yZVh|72I2KB7upZ@Z>UD3K^UT6_f$cxD|G;fS zyas=+0r+2SaofT+$#qf{trP4<+4c=6`NH~l)<LS_3y0z3>Bx>uwXX;%5Dp&_Wj*H*6l~T z{UW!Qx&2zVzvT9}-2R^1>)igW+kdv6v30&R+ef>7tlLZ6ewo|L z+&;(cPq}@8+n2fh2e+?sd(Rz0-c6y#KM1OxJ>7nQ+sC^7Xtz&r`x$P33@XwmQ2WIS zsB`GLI|hAIsJPoholEnrUjijpL*<|8_V1z2u^GdIUw0_k2+D3#w{Pe6UERL7+mC@d zCqH8S87O%f%Kt6*_u47M9{?p=L-p?n<$oO1xqPzqX;9<18cJ@4vcJ{+HST}k{dMk- z_ND)BhTk^r-sJDp)ObJbuyd$&5LE4>%n9b@<}9dfx5)Z(v%@aIZxGa+jFO(o_8IKY zImfOG-$(AYyDX3ldNBC{aWkyTYu8})7GOo&hFYe z(IM^Hxah5L4z0s>we9cTEz~m?YFjq=XRrAzw~o!F_FanI3fo?@d+?nFf6uqlzKgMo z&s|Y|@H-buN}-NpS3@1&WKyJ=0n*ZR}kvj?|?9GM?f7TPJ%jqp9OWip9W=rEtEV0HNIz|j<2s;e-mn) zUqij`aV6AirA-gaNPbttT-Xb}#(H9OMq)$sU7)_pVQ(n=gP{D6gNpZe>*dxTf_iV~ zQ&9g!eg*2i{O?2k_vK5ddVhkF9tVYb`$K)sBG>v*s63;f@*EEJogODZeW&f2P~R6?B2JQ&kADWTa6ppS&=zY_L`GvNSuFWeN)gPXwTp~kZqN>)ORr{xhDiGk=Hq4e%h@ivE>!R_G| za8Ec09t1as$3VrK2qo7*`QHk+M8DVigHZ9Fg}enU@jBcFegp@@ui@747pQpcjtucO zhl)284xxQF>w7}Q+ZS#J$HML5iBSKSoeYP?m;0G`teg&21XDHdAFyz@3?ukCw`cSBKu?JM#1K~(`G~5TC0!P7f;NI{usJJ&k z$>UJ6R>!=Itzw>>7r9|#qHTX-n#dDi!ZieF^?&z`UT*PeTj zgIhLy$IAcC$NydPkv*_|qF0MO6Y2kP({*~SudSxC_9Yci`^*BUYnM8xYnhDkp}n7( zZ;pq$W|<0g4O0QNk1c@Or|O{2of*g4-^_=)b{G$JEio0!uLA0vy8x<=Iy2+Aa81w; zcBMTZ%6>eQ-Bhyz>Ri77>fB!kWtVY$IH&hB^P&94n^R#Q^a|J)E`ai@gBnLhk?S|} zq0Wutq0W_4q3kQ%zrd`6I_G7a5YB=9pzQLY;*N*O`ppWcbJ_x^b6g#iea4Bwz8{o* zzBwN1oHZ5d9998kzX0l-Q3qw8agytYIv3?botwr(*-wQ!*Hpk=;Q}bXIx}NJIJfkJ zd(fT_Kw5E%B~J3=l^8a zZ{|ZCCraP zqo`%dZxfc%uKAb`CGW}({^Itirik(R$#d+?(}M2+n9oD;4s+XIt)B@Mx5RoBFJe4y zq2o@s?Hu{~^UHNBsMo9G+2$z71Zm+ z8?D~~C3B&U`A=D22sQ7~Jp50y(R~KbVV&#CI3tX&A56{%)bV6I)QF~9uYj7r1yJ)@ z2W6jeW|*&jQ1h1$HDBY+sZde@=kw59eCD=+lT+s={kNcMUF%rvCfW8KDESB~&mL!` z#)#%O{k;!~cf-Cq8hgnkIMcpGXNR^kpz6C3D)wDaV{HAmFemFmS&p(k8EWm#hMJpt zsJWRuCFu7<^*;$Ur>{Z9dlxGH*HHO3IVb2lK=4RL!w)wvN=UE4s}=R@V$7pkstP<2Fe`JXaeldad}HCZ2TkIS8FmUAzH zs%1G;Z5_@F90XPKD5x<^fKz#>&QGCax%H^tCL!fRO`ig7HI{M~v( zjBJi02dw#=OQC(|W4GA0=bayXH@qOk%7vQ89jzBk3uBrEmFpp>vCX%>ZE09nBcS@D z+-qaDeslekrr)Po;yNoFbAefBW?T|-^@DkS{sxq^zBJUoE!4Ic2UYJxD2q$1-|7CL zmxXO}FidV6>u*B&Z+dx%w++<(v9tBPpkxfxvHCdclc46|EU07gRO?ql#p`lKIM&<^ zrMJ2=9K-j3I!1pFb!_hc_i!x!7V7$YNLjeIHwR{;ziItrsQ!AWV|T?>!GAVXylSX( z#LH0m-h$e`A4A=Vdd`HZ=R(*WUTOUXD5-?HXE@jT0;qawpk7D3Z+$sbzAvv0 z`F?`R*ZR7UFAFN)`cU~cfqMP1we{gpvNzlS9%}t~sC=hE-Ge;W`sGmZ?y|nN_5S~t z{_H;O6J1(t-{j|O%DvxKGbfT-sJTvDALgnL)V`Q!y%6fSSOR5N4kgu4b6yKI$B7yC zhgv&%P}{Z;YF{aV+VrqH)NlpG2b^BAZxw4E8oehrkx0;sVkZVrBbc6~JUzWi#>e-~m(hCtOY z8mfj#Q1d?>%3=^AJ{M9=aNV;iV(?*%pP1FRnoB_~6T@l2@smVK)i?LAkOo21p_Y#!k+@4xnQ|qDF z`37K{XZsVN(dEO2+ zo_TJsxiiFB0yY1iTVDYs8FvN$E>P#*?V--K`&yp}b&f5!{vcGoC!p$k4(hu8P3s>+ z$x0}{#N8ooCn&#OP=5WP{@=BY^i-aVk=9taip zaH!+e3D(bqlB=NnuZN2Nr1cZ;3CF2YsN-7idxKprl-({+dG>|MGZre(c&Ovwsn$!N zo5ewKbbSzCX){r|MyV?et^&lV#SUF+AY&93QG9e!0ay!TY&(YDui z(DkC`t;4Najr_ljhpzF|w(6GD&j5~iC?ioydmfKS<}qJt(=j+|o6MZbe+JaHnP)CB zSGhmu!O%YpYFmymC&Q#a)V7*uE^+%RsBM`;Ze1ftWIqgD9Ai#4XQbI>G7i}dfKlD) z*9!6X6~*5_Q$IWSZ1QkeC!0aV+7l+%In?=TCX~e&@Xwr|((fzO_!ys<6Y}+e%9jU~ zuMjF<2`uNKT#vf#c_^D2D4+MBo-{6`tj5}{)~3*ns+6K z{k6tFsgD^im@s$jR$}!flroicbejiMpJD{#d z7eHNSzH0p)sB_MzP}hlHTVDxvo!fFjILCB=(z`-k7x#s_KHS{;5U6TKLamYmt&fEo z=Lt~rF$L3Y^iZcl+{&X1q_Q#BcVUC`Lk{YNvob+O_t$>>I z`(Fw%K8K1q=H;MY3e`U!D)tLd{{0t)c-ug2gJITFjtk7gAKeB$_-tY-pJWKf@&e*WRF~#Q`3oykk0q*Y{!AF#geYr%hQV^9CC^AlcDS%hdL(o zemm%YhdL(QY`q5RSn#3szVC!%z-3VWrz{Ei&xV?#d!Xvu=-p6%F4TCowH{wT<$q%R ztZlBF>ksR0vnJP_@=J8xtmBvJUUF~hx?A#I6YGw#$ab3o81?T6_T>uql22|u%p!SWH9|gT5)bY3{)IPDP^}$ea zc87{H8Y<3-Q2zgbidPM_@4N=J&%9^-GbsO+Q2uRegMT+D83;AMTe^Mf$D#cysI_^g z_2jnTf6~dL-7ZUvtFPd%Kidr|}_JooHp!$!1igldzwasmF-zhbWIa9m}$6o-Iug-BZKIQ*3 zJY+K#N{)rHxfyC6?uDx5Z77R>L(N0$y0GrIfLf3FP;u5)>)QI8`pn`Ok7KNJOo6I# zCRB~{p=w+TRpV-?8hd^gY8(#JU#I1N9_l?D%61~uetfp|3!!3N2NnAcs9aT0xu1ZN z8mMvCL(Rj_*4H-9we_bTQ%8Ihwq=(uLf(9+I>tlQH5IDP3Mh*OP~)nDsxM<1_CD4X z^IUEJfRY!jzij<`>rsu#_iLQG=6~Zwt|jYB-eb>0v37>4XJ05e((T7X)mLmiikZ&% z@w?RVOC77yz6)WUpJ)6t%uk8I*iyedBL}z7>@HZq`qR8p~#s3#g@kKXMQy^Hu= zj5iv8hnVanimQ1ShdrEnOq@sj^y}Ig&W(AK|8pAkZK!QFlq`aZy=#5gzK28Ur$LS3 z0;u(Ph4t+3!n*tosz2xZu)l2#rB8rbuV-41#`LzpDV1- zaR1Fv=gvy&e|BE~tbO(d`mMt*iPU%HgzYfQ+hL(=TxMqe6y|P#Il?S5OU+6sSqL>3 z%dBUv3i<$;=jRik+kIc_M?u+7fwH(1%6^9XE1=fHgVvvgvVRxK zeuH0w{Xi($7HXa3S>GS3=VGY1)7@SUwQlaSJ|D`i9?I_DZg2nZu#UPy$tFgul#v#$F& zTSD~@hl+P7)c@bkwq6Pq|2inS9qRvR_gjAw%KmjI|Bs;Tzjpu6Q2(dNXc7E#pzJq? zvL6NYKb*f>{~MJ5B~WrL)c?cEtv>{1{~DD42T=B3x&J4q{{gjZ8T`9J*$;%Wp9S^* zp;xTG59R+Ql>7*D^rThLv!U#JLiuk7Wk1aQ`B3i>+RyqiQ1<6R*R3{Yke?O{Qcm@umtvlw^^@p{{q;b_K%?I`UY+SS6gr2rgieO5dER@Zvh9u z9j)&LB?rNQu+aKMsC?_U4f%h}Y@Pi4U`PHBvN`Qt;UG8wZUMK0Tf&{;R`6BppSZss zZcTee`_{?N4dy_NYXH<|Dz>ve5^CH>!foJLP~$3vL*RARE8w>1PeG0A1-Ko2%lfBK z@+}++|7CrhbwYikq3SJys{ahQJ-ig^n&K9y`W}G#eBpfSHBj}{L47vkH>mp7?a(^; zIm4b%`etw^xCc~y`#^oR<8bRIK*{NF7g%EbN~m+eHP(~YPP4=RjmL1l`LpM*|F!?W z>vLcK>GlZs;%;g3UYyp2MC&Mzht}KpCsVJL%eu5qOr~9H3nZn=Yplkshw{zh`_&}5 zP{+ao>&0f7S!LFk^-%U%>)PKefI6oYL!INwpzNyLUt`w0J*$)b%>pRloZqzcNu25tuHp^lANokM#r z)Ul-i>R40k_A zDTdMZkN$_+h?!iMMc<1OeaBxqxqS~vuCdmV>ncfv0{8)IhVHEZOwZYQu>Wq1KzD&P5eCwO}!U% zLBCMbmYar}3ZTY5en8MqflbY8_U7#q8@0$zq*tqZt(hPDUemx3V+d^Yy_U^<4@-P( z3pWqBS3$`EgHmg0>bu_tICl}YCAPl@O5REL&t{*CeP&~XeG^-7?aM>sz7$GkSZ~-? z>$gi}Khv?ksr6LkIAd~KCyIF}PC1l(V7(zuQ{S0u|9WgQw+{XTpkzC!?UN6+{!em$ z6fd3W_W>&%d$QxraJ+eNv16@2IOJ-*O~}<9D%aN5_kyxG1nT^7ENlf&gObTmuPHCI zUIjG=(}uK8zE-V(dJX!W_0>@Q>9zl-dU)S_?}4IEQ_JW zw!-aQwhJ{5fqK0?8YaJ|IPG&oB}0SV7f{k=SctzX)EpiI75`$W`1e3rE`pk~Z=vF! zzI~XZ*Pvd1*IJ*pL+j-0?8+TO|9epO%b@&MLdDA%9^!X~%GU=f{~)OQI76-P1~vWy zC^-^pTbu^t!U z`F#qt{eFb1x%2Kp9}3mqAwP^^cc}Gt64dtZvPW1iM?mS1{v~YRccAXaY`C7GXu z^V~ewxCBa8L5(qIWEk@>s4+hT_Gc~En;1ZwVAL5)3U@4#VD`_veyIFn(S zpZDK4k_Az=pPVtgHBSYkI77J>6b5XfA9kk-G zFwY+x9_INssQH>QHt5r!<|chTWb3->lg9CMU0LMqUEvrD;8Mr=?T8R}iz7pxZbt>Z zFKjZ8zL))zroWfH)Om(G##lJXafTFzxcftm=X|L3UwCxTPlA#&pz_v0?Qi#w3;WUY zP!``E6ZF+k{T;^#`l}9S$X9;Upd!*GH#> zwh_faKLAP!p<;ajl`Cgr$TbXVeN3@_K9tAh)~|=Mza7fI3TpmehLZQ7YFcJ}B~%@) zPYrP=LT#6FsO|Ks^`-9bcUq`pAk=mmVtp5=yd$CVj)A)7Jq=1OfI6R+S-%A;?|*gs z{ZapLPCKT_eKYgZa81!~Qm8c_Y7FC{#xxabJ6D(sprp>sI6cf=Kd8COhq50JB~#4` z*l1slos(9a5$0sGGlSm-XQj?j^79+FueleZ_y?u;y+|`u(sb=R@PXdW}!P1z`*?LCL5K!x;Vs zB^N>Eo#yr%+xEm_V9H`^t3sA?s8td;u?UP?Z?bAO)jj!G1p|&1S&K#{EAT5Pf(I`Wr%Yvl-%k5~4f?@eD!~24N6Qgz@$m3Ly-_ zAPj}y=ew@&J=UzIhrR#%w~znc$L~CjHJ^1}*ZFo`_v^avd)9)D)t%9)d92NhmMdFS zHLK8>spfXRYg%UC)v)Qc!9N>H23n8CpZxz$RrVi=O>&Ku_W9W{T22o&W%A zpvGDNmF*a)Wpp9bG3k2i&%nmU6xH;1nYv7BU4J$4C3R5CB=fpZ=OCy$M?uv&5z2BF zRGsC}xpUX?UN!DLBg7gFlWR_FZz=0`<`-cbnTG9L+rMuAb*cXC8lL}a`3wkCI{FPn!?UxR*KljAp^WZ4Y!5s2Q7MUx|b!OL_LjPc>I!Bw6%sH^! z?=PMeY!^Vy;rKa0p91B3DOCGSP;>gY^*5pB^joO89DQrhPlmET7fLRNiZcz$Za!4c zS5R{s`$w45O`-H1p!&DGEzIo*n6QUBC(AAk`r}aXUVu6mtFpcvs;)1f>ZpT~=C_AB z+CsHtLY<@avpyIqUOv>h+6e1ML&X~h74Li~xdLij*FeSj7^)}xju0mo>Uz~K*7t#m zGZHG!aZqvwRGhP+S}ujE<2tCbfH~Ix3FZF?l>cHVc>~J-9jKPfJHx%#c5|C0o;R9s zS9lKRDyZj=-hq0q=O?J=XMTrzE@s%>;W?P9vhZBX{r7}>nM>j3^nV8RT*n%ydzww| z4bNA!g?fHwbExM#`dQxvD(-$zd5(h0dj?b;hus&R`*;9Kf5CdUdExnwOQD_%c@V09 z!2Mxdc~H-T90@hvlcAmmnP~k=DEp_No(K8zfp8CX`h(%QkB#St=R7WjdJd%NL*e<3 zE>QXaI2dmBaColc6Sz0}AJ*?(&@A!2&utcl{;qHz`meAaf23LByEHq&gJ{o&2f_hR zarb}+!-K3J1(o-7sC&KFL)Cpd)Hoi5x|jYG91g3Y#{D@Q0e`f<`=i0`0I27@ilFSy zfx1$3srBoj*00;3o)5d%`Xf;5*Yi;8*DFx69BTbq2~|)0v5=<|)bn23LRscPJ%4qe z_0dpyieM%@)A|Kad8R?-xe-d{Lgl#!YQ1?3YQ1?GYQ6aoDu0XekiR3;z3^P92)jew z+dshikx=;V_U?(hw$_53~96V_P&398RkwX!Dp<$1XXV}RJ|WVNi9^pKf|ri zn>`Vp_i7J&qxXQScPrQj?r42)sCq|0&F_g&^_~w^@10QfJ_NUh%b+qYhkfA})_;bo zccUl6vkx0X=~+1XR68LCGml^^S*n zzH18H4_*WF;T*UxyceqO$Kn2PiS@Uk>Rts^_t#K$uZ60+V&TuID0I1A+z+vzp z>qkM=eG)tXPOyF5E2N5a>wuYmIZ63TxKlr(ud_@_g) z><$ls=Read@twbKLh0+E`a4&I_&woa=-XJ|2`c`+Q1K6jl4GFi`>S)H|JVP&<9W9K zt;;cEyH>H@X+OqNujuQu%0!>1mw25o^EOav)R5 zO6xV&;Ws?UIP>UFW4U@{9(c$>KIl6bL8*#N+`b?GyY=m%Yiya z&4&qp*kArISN?9Vgc^4Z)VXDRiSwKJQ2j+v`|1*?eRmnuzP!?`G2@kC-=AaVLmkVC zppI=NZZCtXyV9(I8gG1QIJV@N`B2A_BB*0a3DmKs%Q*n;mMz6&t-=mDK6Q;1LD&+hON>*F1uRZDeW-`AF*CpD%o>D{7 zwc}OvX)ap45#qIh((B`;Jcn51m;?*_ z|1Bu{pP?lFcF5fZN|OKQu7tc%O&elXFMj_$ty81V7t3FwbwJUTck!1EuRG5pueJ$| zA0(tNZL%E(Rr5HgF-(Jz-7>%b3QB&4@@er-SO=Fu={Ht~7|%h)*!5 z?2Dn~0;pVGgiP&)vv< za~-d09B1D9%xThkKlXzV;|eJ0@KMmSpd@*Y<6m(PBP$+jJAVED@l;QLMY?5b@$@RQ&> z1gfvzH)$V{Z@*F4Z0H`u=vBf0dMKF#HO{-NKjQwUq1J^Z)}wr>Ouc?v?HX1)UizmY zUtgHK%!=K9GnCw^e)tGf-e;`843o$ESNZFlf2I9mpM|*H;UHd$b&%VRhst%D^>d)) zN~mr6dh2uGzpEjtDRnL~SBK?T={Pk|V~l?u^5nogUW$FT+rF&{Hch_>HeH}W&C8OPcCDgUx{k{sehz5*qmKwa-#2NkEy>JTRrDo$@G%N?NN>;o0&P^dV^L&^D2aqfePvk0o5 zB~Wqx1!eguRGc57;xzj@#OVMfeW3hbgSy`OrS-K?@tS`V;&p`b$cBnH5GvjfsJcc# z$w^RgE{2MGJye{3K-G0Wl;t9*ILn~otbmI16_liX>wED~arTCaQvem`7%0orpyFHr z73UhLIJZK{0x16#-v$5Aq5OY>vS{{w@b3uaza^CaKq%QC%Kvt#>%k}e5cG*q^;`lK zZw8dd?NIeR1QqXDsCZRS@;OwTKcM3FS`+lWpsp*QWW55a|23#Q??L7H0xD7+RG#>c zAx|b$p59Qh8&sYnq4v@jTK^I1I{3jqh5pZe4tair$`h*#c{YKHv>8;MZJ@@p8&sYH zpkypmp6{XRP5ULpNr$Sp3zTIas5m=A#n~S!&f!o}3>D`+s5oCh)w2dF&PKn6IPIX~ zYylN#092ekpkx$OoR`*yIPXHmSp^m62Pn%X>q4A%P;s_^inA@07k|r`vBKPG6`vJ3__T2g>pgs5r+%#W@Qq&QvIw4Hahr zR6XxO#rYH}&KfAojeZYt+C#8a{NKeao}cTH z);G3kZRW~ORUInY6=xJi-;Jn*y*d_5`my2pMR!I&igt|&B&5(|n*ufVQmDD8 zfSTi#Ffki{gt_YuHJ5o%b6aRmF-xK5zQSB->Z~!!o9FlCQ1T*Fy(^$<_|p9wn%73Z zZ^p+%7D^&dcc_?oaFp9lgOZD^NAZ*Y_iCc!%)-X~xgDB>m_uN~-+H}&C-z5US{BC( zTNE_s!?w;RXF1MHZ0Fhj3;Q?UDAcwk)OdG-nwuzIDjOSbx#JJRS2E6=2Fo1pZMS^} zH5aj_!L})EEMH^a4_oa#Irhzmh4#PRZ4W}lFNYe>M^Nj~_fYFl=Vr|l>(EN53f4fi zH)$U1+e7(hL#akGZw=IO=XdLE;z7@b>fg@#j@I{t+8z&u$}<)! z&sk7;E`!=uuY;0%pyEFO74LDVYca1t`LA&QD);~3_RV=lNb@}yD*gdb@sEbO?{yNC zOm_b@?!OHt?ti)eNvQQ=vGrGZ7E1Tg-iDIo&pLIi7pd!MR%?Dge9snz%?rXd(3nlC z&;Knwt9LxsC2gx%huU^4+g5E{kzU-oP}`+y(p+j@H8#!DvR|b&{7&z3+VT>u;ZJUQ z^TcPfnqKe=e|4CI*|tx z{%)UQy%a`y6t~v>ZK*q|&vTvI;92tIYe|j${~~dcpV7+RIKN}}bL`rX@^Ux&{p z3$a^apWp1$a+46F2h?0`V?Bx$G4Z{aey(22ZjfURv;8>vw~4R!ezQGE1GpBV8uOs!3aI&vYK)lpJ*zhw{%4|U9_5;s*?%=`x@m}!4JCc7*T+iw z+&#HPm|4!Fp8FhM4-h zaQWYaL(i`lIal{?#Eh)#{H&8|>-K2(1pl$-{&Zk%a7 zd4E|(tTjOQf|5vGR3u(Le*MfeI<sw}aWZ+P8uCT)4xMKYf{bloj4YJjRW`C44mrTE?H z6Z6bM7)dm@$=9RS+aGDYAH%Y2#rHR*T^`f(nbUfOxmpY*SM?4#ZiQMF&skpuwJe6` zggGCvRnW&k)pQ1wTm>}`)1l^imGvK?{G0X(_U)mh50u?*P<{tN*&PiP_f#mk6w2;K zD7)F#7g~P-%I_^GzfYj#Hz>ajTL-@_q3i}i+3f`-N4fuODEqst-)sGO>u*8%e*)$I zJ(Q&P4Su;$_3Z&=R{&*q43wPf{uxkt=39Rj%DxK9{zEAF#r^I21^b<#;_nY-e>jwV z5tLjE6?Z9=-Fr}WUqIQ_K}nl!g54odb|*mDodsn#6-sV)f2I51asOxTU+eyE+Xnx> zQ2x6>$-z+ZPlsx`66#$08K`sX8tXr}f1?4xzCBbuTR_Fz7E1D={CY)Ki|3*I--Po27|Q=UD2WdY{@X(N?+N99 z0F=d1Q2wVv`A>rKzY0q3fb#3HefUhU2b4Y>D$Y?*mZw6+nG6+Y8dRLwQ1UQLeC~zv zUuykB>$Onv)1!l-^>e5T_lK4dDF2(F^4|^R{|J;UfjX8ivwnVl_$+@LlqAkk{GHl0I%i4W zD3(4ZzB2mktF{fDxBOSNhwFhmH+nr#`L$l^r87v4ZDC7&;%#`1u#~pEL~Ho7i1Fp~ zHo0vaZK>A{S9`wF_YL#b7iu1dLCxzpa~jl)&4Zf%Wl+mwwV6(A>3z*%CQXTUF*>JO zL7(2QgOc?9f*#Fh@_pg>x8ZY0Uu^0>Z$zIF()nJ>0pXV=Huzb%CHIVy|9?CAb4Ako znL|Sjk3q>(*6V9Z`Ak;lx>jMg&bFP01>bs~j?_0i)Thq?nYCdoldwy!ci;gb_i!kw z_vf?gCc|4-L{}sepPe0}=|Rn7eR*jf4}g-)g0KvSL7i(JVf|$&iQ=X*gU@p99&VUt zl=G~%Z`XrE?7^Zx?@i#n@=u2e86IrjSMy9Ky5QSLDiCPeT4guh8jauODYqeiy!{;vq`z@nqvP_$Ey&V zq=kF>sq0+Rjbb4J*ESCg`4&LQi%|8wX1&_|>(!C6{;iCbkNwgQYaYwyrLi0UCFfd? zVx@k@k9`-`$GO-ovh9we>M z{~v97T!_6Xlw@14&yjR(r^GRbU{h$j`|Ver>eq?y7EAd*UDZ|Q7_qU>-MOIh^f~LXg$ z$F9Ahj$I?H9}AU#Je1vJsLymaKvj9C^@pJB)VFApd}+l&PlwXmTi?ujPwU%Q&$Ygb z_1&%SZ+)2c!>k`}eXR8pte;{1OzRg|pJM$g>(^Sp#rkd5@3sD<^%t$bWBp_6wbs{K zZ+S)-ZwILHZehKj^=MuGpU#Y}TE)7h?G)=&+hJw9s&e$0^BH$mw|K0>@P@xcrhTsZ zBGFvvRnwv2dGumpCSN~P9{H?t&b7`t;G8g@he3(&q1E3ayS`)##F66G)I*f4)K z@$xZ>@jAz~Ut@Zj?N%K5vO<1iSC2yCQ#AZ5|-mmcc@16G-s5skR5b7KZRo5O+?Ze!DI8@F;>&f+&Q`dTW%I~Dg zM)CBDY9E^wKCesHx~aYb+VWr|lgOv>bkAXK74{m_YS+FNDp!js zVN4yN^nOsX3zU7H_2jWtCoP-$Wt4IbFB?hnxNc_0yo_61P7Ab?#SVeJxb3^h<(W52*fOFcAl;f1LX-a{o;C z-{t;=?qBNmFQMY>F*U?F5K2GZ`q@x%FNKm@++PNDT>aj9vrB`2Cn)Ka)W zqIDvbSp(v+t-jy9w%f`sRaq6Ci#ru|Oq_oxhD5chBGopsYr}P=@bZSoI!s~}u5+as z!$$Ma9cnyzP<6|8ir<$)NmfaigR7yW)cQ--Kem3;6`}t@sJb4p{*v|fSBCyAq55;J z4}{9SllA27C-cXoecJ}kKV&0OOs(T$QMZP3QcUdiwjbxkynfqHzMizf_tAB;l=*M?U!4`d&QsOzRuT${C%P3xXAjMP;v#-`ZNb> zeX6$piThLQ*^r*mqh&1fy{+$W zz0~?V>knCf)cPAY1^*AAq|2BhFXrRq2wnhzo?GCV(NKr+n>UAoIX3m>kAcs7*uWJ%xP-(_1@e$ zA!h-U9AQ1moqXNC-0`Pi6Zse38vMsY$rPxZBmNQeW1+@$g7xJ6X*KbpJc)C~8R1+} z?GnX__M!8(Xt)oZPQ288Xt8r8e7#>>=eCU7LOq+q#GFIL{?&TB($L=>s@^TFC)b&LC~M~(_*(DP zKY_M97|9~?YCiI!2B_Ln=Sbc1ALlxyYdqo#+VpGmboGyWOuxE8Es8O^EYvj~N+wy4*0to%-X-MK7^Y$4 z{@ld9hCSCmPUqbl;ub?miS;Y3&#_)={h!wB^R{aeYw6FgWzJiHO@rshlI~Nb&-463 zNqwB8`wEqgGYOlywqI-eUGES6^?oVWUTW++61#D>eFRGOcp%K{NT_9U64Y`jxBk5Q zUxkvs4~ATWq2varZS-EK^Z6&Ozv%wgq2@iR?eB6!&)&xC!g5^g`04XQ{e7Y89|k4k zpz4|i=kihwO&z~&^=|4i%^gGnG_w+}D{n=1*AygfgS-;l$jn;3oem7K{hoI*88K}H3LCKp?ao&Tn z`vj`zJ1Dz#P}1zNV3!VM*AeRaZBHn>ZJ=aFsCc_U+3gE;ujLRZyFw^A3CgY*%I;jK z=bJ8vvYP=Vv!U#6g|aJyS~ibD**yy-FG1P8YW*GS%c1PPar@71|GVqb|5v;4J;Pf! z`aQ!rULUi|!x)A@)jAextW&MeH5Z$!%%+bA`)qRv)Ved)`c!kSn9h2X_ChTBw&0>C z!+c!@CD&Px#+!PcSL9mDv8%Few?)A>2Wsx?eK&A#vcxe8u}}VNS#AGX$NNS7)U?S{ zVIDG}^d3;s2PWr>@!d2A&w#ajy-_@+E-68kmGU2JjlSPn1w>nf#`)EjgPm$Q z4obd$DcG!oil6av(0f7UngF$LTLHDNZnZ4vSy25sFfn#0IS|V3NT`;(tv?18=S3*L zx1sz#g_5;U?+$$>_@4-6e>Rl;rBL=aK*@bj@1BLq_Xd>xM^N^)Q1-D`gMB9`yZ%ru z?^*xY`daJhuZ1{WpyKp_inBA66hOUu4JzJBsJ!1o+5Zk@zj0Nt&xU$863YH~DEkRe z_Lo4}Uk@d7q27H9WgmMz*lz-5zZsPMHc&Dc%I-|4xEDj&T?b`%JCrPdTJGi66YHR_ zfflY^e;th0#SJm*-<%UGo5-^Lz0wCXth1arjqUHMo#z+liN6)* zd%w5Cc60<(?M2ovg^GXlzrt7+K#e8qouKazRq=69bGFp_JMKTCI-J|Q2sIaN-VNt4 zr$XshL*1*MZT%joe9uD3Yi?f&HFux7J$Zgg{}b!MhSra$J*!KL*d}S&v3ON;e^XDG zd*za-W}RcH){^ZS&i$a}4d)({SRHlCHuJqO&x4@!QBd}VZw zt-19~e)cl_zi8q(l6-#fAAX;5UAlpPYmPSq{yg3^_*|9rJwM6qv5!LiJ)vYP>(RWY zo+}lv3&+d?>>7I}tkJfl+*O~3e4j(fuTb;8(Pu$VhnmkQf5g-}rn`eHG&Mfg0C-Fgb4h zcv8+qs-pPzpCJFF&%(uDhd7TzNtN~bcskce`40A4$Bcc$y=z{o;|(b3|E=qTlCiMS zI!K z@z%h$KZF=Fq2vzhRo35!%K5eRF@vE>+zdqYg!e&fG~b*R#DYn&(kQ>Y^cs?L1t zMbP)s8a(6Se7U3RLS2)feZKpju>K;H$LrRYyZ(ijlM{cryyZax5l1jI~1$EByq4h7^|2x!mv2IkQd|N?1 zOFPi|ZczOPK}n(8$3gAU&aplT%IicO=vVNNTFM)dg@ea2?0QDT?Gsr@%CQ8*d@j0baTGvq{^%{+cphzmkT8Y){D*Q=6v^8S+6zQ zHVb~aQ0r!aS!_;s|9tCJX06-XHg|rrz$}JdF1bTm1e>FwWP$Z^>&c&KE0~AGb(Exg zb83r3`y;*9?cVUbVKK29dnPrip(q||xByBXwf>~_UWap+qSe!=3`fB+aK&Zur>2bjH96>iV-nm^LVV& z_)Th)uiWWevb$rJ*smN;N)L5C3?)N04zVwXIvzY_{Y5DGm)k#tI(}@mNwC`)s;0f5 zWCT?Io$jA+eW~^LpzJ?^k{{fEaoZ5@dZ_+6P%;lHUdN2k-wUe0Ka}ha)xWR%N4fu4 z_n+zh(ssfBAt?VRpyVYe|4rM6{;i?<2SUjZsQzEv->O6CZx1Csq53PK=Ivegf9(G6 z-QTTa=-&n^{*F+xFO>h8?w{)ZtKEOA`(JbaO80-^{$Jd`FW1bKe-u>wW1-{>sQ6E~ z|7G{T<^GS{-m-J>YX>EpL9K^9ttYk>e^*@|>r=GOr7|m*-(*ee+UWm~%1@$tbZ(+L ztM+QRJhU*z{6K*^bW){*VQP?DAveA+-sKdAhBSRVsb z%k9=v$Fm_lBa81fPx)WLLT_h_9BZ|izG=wa*Bl1N^3oX2bz2Ek-M>JMu?5#+q;Cr) zyF=Lzv7Wr|n$^hiNIaYJu%Bc0yeqE8uDJPoH=OsG#7*9hVGS++WIUo)i)kxQ$_j!`87bIWnaDDaHrG{)Ce2<5cd!TYX z29;~??x8<*T#;UXZKN<<8_C%y92Z97Cz${hZzi1YSg*M6Q}=xfm8(~ezsMCn50fce z@5^_tT>K;jP`QdleU{?+f(pm{(KgMu2stvLS<;v{WB9_ZbbsqvH$6Al_C9ZX_9cY;sJMI*0X4&p3`z`%9e$(w&Bin64zWt%( z6sX!(KknCf)cP~l7h8YDdNe=(S%%-w-m=lpOY=Qnwa(dgU>H-bSzs1J&B1hY zzFDRI)NA{j@IOs68vakytewJK+`n^}o7gTvZw4h>Le1>}>&eHiDr(HyEFSBawr_0X zmFZQjD*pW51@%QDf4!=PH9Ssb5;OTYrDYMdnW(K0Lx;xP|xR30T5p}}EpVJ-$DIz9cfRz-o-5fid~bp0DYs~kkpDa=x!?N3)|1!E zSWDK+r2n~-9*M@J*XV;9u8Wn#X>47LYRlO((M%ty#7~_N6V>3wtIz|w}O(P*6aOJe&3?jzQx#;*!DjAK9uU4 za$listB|)EyX5;!i-v?6UWbyEP~+cnpP+9CRqr6{$@7>?%&6An=g-wHQ5>y9V&8)s zF3)kqOP)(iV#AzE@2C0weCJ&R73Z^k!+Lpse%KzWp>otgZBNbj3wj4A`;GPw+x?b9 zgT4ex-i8|2d)AZ3Rgg5UXnY%DxKF*=diS#wLz4VCAa{7fIhZzV{W)0eI@9UXxcZvI zpvE%}s&2)e=l5Si$;Jl+n*mTZsWm6BhsD$dN(K;548-gfExGf)>pW{O+h$MI2=k|hkEys_0_Pkx>LvTS9(Ubmaz^Wx2bJ- zBn{_X(>?C^!J&p6GaqVfMb=B8#!_az(yS4OhdMh$*$jc2kKs@;3Za(OSx|B%{Ev() zbss(7HJ3VGh2yV;$`>0E@^^=xo7~a7SI%ppNE>;yH2`G*AkX(;<; zQ1S^>f5$_^n2&>6#%DpD``u#wX(*4SQ1?hag1UFI4(cAs#)q{?+ym(erSAfD&*B>E z|A4Z;AIg6bRJ>(S`IbY?`Ik`j`~-EcByCjC)1l^MQ>glLpkxSCp8cS%r2b(&^YEbW z1J!>RR9z=R)p0gd9hX8yx&f+=xlna1fU4ses4e~_>u*EV@d;EN-$6;MBSOAwq4JeN z<(m(c?-{6kuR?iz0G01+sC?_7^2Lt~_i)-n>6=02>kE}{XDAr~HQv*q@=bxtHytY9 zZBY3hfbw`6D&H$m`Bp&1uYtN3^n>-^q4K4V4*52PlI@`K41>!1ob@HvJ02DKdqLH) z15_P*L)9@7D$-b}I?jZu<6@{fu7SGObhGujP<1>EHSP*1c^4{w=F#EaQqD2qp3`1X z_mobCx`%YF^>?7|A+5E(Nnx<>4i%>l)VOwms(UC@-A6#xeG*jWbD`=kfvWo^sJicj zneYMYk3-eH1gh@0q2wE=I&zK;bqR1R>$MaBiyax4Kj_+Cj6snFNpz25)8|v5;D*p)B6`pE+64bn04OP!gQ1z5S z)l&{t&l0G5mP1AS3aXx8q3UUQe5j`*)bC4ox4t!0Jv%|wvoDksLe+5{)bCDjcS4KA zZ&P+YG3Z-D^=}7N_nuI7j)1zyIR>iUGob3d7^>bGQ1#vpRqrEE8DD^^_bsS;KY^OR zZ((=%tMz6@q2BgT_4a_0ouTT?hpKBW>;XHU)FSaa*F&M&4~MF^2&&$5q3SJxiTQE#V&4he6eG1XLYGP;wDe{wJXFziGY6 zxS+R(sv{e!j)72h>;+ZFVNj7ygqn}Dq3XC4s*dZRert7(^?yRu@d#8M&qK*_sQjy; z^0hiO=sB<#`em>;TxY%QX`z2}sO7f<)V%Et<$owty(d7`dlpo^S3qUF8LHkgsCplR zs`q)A17EfNE>yjrL)H5ulx%!@sAnWpeMM09p959THBj~33RTYoQ1v_wRnIF>^?U>s z@jIw`V#T4JO`z)O0=I&_tZxfd&+bt58~`OJK-F;`R2^49)lmvn$AeIHyaIc})ll}^ zozWukyXw2Zti5roE7Tn3RO=oRKz@}dJclB=V+*UPKMjSv#d{nnx|`^>X`#2k3i*J29U#3*Fkdf0*?k0MR|{pAc21}-18TmyL)AABD#H+{`bI$2HwLP{(_nviuJx%<_052) zuM|oihgx2hQ29TE%Kr^i{`85V&JIv^J)!J&hO*la%I*lLc{~}a&U2yayc#Ogtx$E& zgR1jMs5+NGZnebTu>L+&onJxK`3sbEJU7%a1FDYOq3S4ys$&UM9o0~E)IinoGgKX| z&kJ>QfvTeqR2@4()v-6+78Y1P5~_}qpz1gWN~S~QzYQw?qfq%@fXYAQ{E&YnRQ}_j z@{fm#a4}T=>!9+NLgk+a2f)XyKL?e+3M&5xQ1U(0=c=ErC$1a#{~@aQoc33rP5)p2 zf6wpUhwqQxztMaDT22xz@9lVL`7S%I;kB8*7dL#K2a{O(q_8gZg<3a;L9ILEpw^{n zFtLr9%b?bp)llnT`sA=q_J!I;hMD7_;!J~Dhv!*eX0A5VF9>#hq1O3fP}{&bsBK~z z)H%yMa~V|J)n@vI_BV$?*^je64eH!$9@IJGGPkdGd-{}c?${T~PE`D1<~W$B18Uzo z&+W_1)oxF}C>$I5!i2v$&YWh>GnbjG&2*NHwn0+FhWY&~P?B{?!)q<6_pWPwEu}AZ zsrO&1?OO*WO{S*AO1fsgc|Qx*3Z8ztT!)0 zt%Gwe59`l+Q2M7(@(t8@f3tp0Nx0^64V1-e*58AY+$)0p?oe_hOdJcK_K6FvFL8f| zE5rJD2$W2KdQSBb>lIM>|8MWR64&jXYy7%huIGD=YwB=Sn4^K_NOOWY6G|4s=I|}+ zYoO*Lds^rpX-UwpH*g#(LJq}9BpqAlssC8hYYr?Yc z54Ep29%^6l9h6^`jusY`aV#4{~Lln7HSh_bN z=42YwcxOY&JgE729?I`^sBx`;idSd7`AtFJ0;)gP`VgqNCqTtL3ra47ihC=R{e4jJ zo`JIe2+F<|O44Qp`%EaizEE}tLfIV!C8t5fy%fssW+=NSq3m9QlI2i#-$U6ozd6|D zK-mp|lD(kpj)1DO2+DpERNQ|++20Q(Pea+i4YgdCLoJ7&p!}QO68tlv{C9@(-w#TT zfbu^B%KscF{~1vJrBL}7Lizs-%KuX+`3cIu&FtXc1%;qt6ypc{^V08dtgIrgOp=vZ2N_1Zs?9q1M%@*5{gwq2^+h+4R;h zN7?2Ob1a;~OY?9il-vt7Pp?{k+x_pkz1Ho&LCx#N{|J8lp!7YgC*N1AWn6!$H?m)o zpK8%dvGtmJYWQv>z1Li*Z#r#xFp>gl&^<=&cPhToRb#d<>Q!5w6iav8Y5Y-JGNbnB zUUTyO(+2NTpXZ4F*C*-!A-(?OCeA%Bj=sTy|BbjK%>QXnve0_8zBTq+13Bs89Hau< z26ZGo7nnXb#O(tmCqd2CB9j=u-!WQHCY(qH0HO`eqFH{XuHFpw{W=_8@O5m0hE)bcHXn!D%SAJw1A)bss3$9~K4);L~= zM~R!Vy;jnub>%~-{pycU`_g&1!^$$Qr8 z$CLE`x@|WJ<5`1E`cuKLKa^~5z1}Znz0bApXzUX8j0w+0jea`BnG7XYK+XB>)*rF{ zto1jo_kJd&I1uWqbzIGQQCIl~De7K*@ZlIeo->@_xU7 zF+}70yUfbues@~C^`CnomPFfwGOLE1vl<>Rrajy6c!5c58Fk5aHPkwrj*axb<}h;{ z)Hvvh&GY-uq2$Tuf=?Bctb&Tw|M@UC)1l_>HR~Th^?wZ|8!Zm~ai}>TYW*0fxjhZa zelnEIfU>(8D(?MIc8j3wUWSs7pzJ!m5bSzG+3f&jw>Oj=31wFc6))|@VAmGPZgVKv z9?C8s%I`sKTI~&UGQYe`XW%nhN-C8KSR!f6j7L@ddvO5RLt^~^NCa8FKL&@Xr z{|aiIsC9c{-S#!v;{U+9oi#w;*VwMn?`u>XiR#k2t#vatvEh0>@1;hr8PX={3sw6t zsIiQL8rw7&ttqrS3t?#P`3A3Pu_;7Cyg`lTgzh_es7BUC8}F>h!rXI zuOM#nc^oFOMb4@B>-@g!vQW>VP;tgW#k>ltz76d|Qjc5nsY&*89B+~1t$=lob@D4A z*LbL0=RxI~1~rd2Ld|0-)Uvr3YWX|{<@p@cXP^PE{`=ZCRP%<~Q^!)}vE-9SQUq0R z2~@phQ1w2P_gcXid_wrs|HHypysvp>tRkh zKy0?>Tk3DbIs%42>mxe$z#@|8d9&Z z)jIDA>}-?U_014tHk8~0Rm)>gwY&gT%YwIp-M3Kk8t@Ssc z=KCG%A6nn&U%|dLRDWlvb#EK%JGpJLyA?(|O3kARAI98~<% zpz6EA?K9kdtJ|Zw`_EGUOVd7W^H}l|=sM49n-P}z;Oa1cqoL++5>!2N%tcUju7Ik0 z9n`qGzRUiEmu!ba$+gyJT2J1G<&iJ(zt_Y6{0=*{MWWn#O`P0tzgtGEeyq*_(%%iX=w%zsiyE)Y_&OeJil5w(YO>ZM{6y&=yK2L5*bw z)H?eQ>-V_-38-;IHKsE4HyGzTceVZN94~VPc^cUc7t*fyzd}j;{ZP+Vu(7=B?eCm# zAoh}xu-Lw{-S$4z7=MErW9tvX_Lc>;zHA27zYmlQg<5|OwSF>`-OW&T&$|5;sP(Jb z`WH}keLoD_(SA_+NGQKaP*MVQEV$nKT&SL>q3oAJ*?;H$-=MA?#a9OZu2Ao`g0kNg z>YC*d){lqszZyzzhPsA5*ZLz+_BBxcKS0@c`6$@;hPuW*!1_K=_6I`QkAb@8eWCR$ zq5L0)lBc2W3of<(9+Z78l>b^N`<@>M`~FZ{*Ur`tgt9*g%6>A`_k`SH{Vpi~SD@rw zsHJ*!^{jJ${L}DEXL~5ghO*le%I;*hpAGeF<3-kIK-t{~GhwCmH=+F2LP?9y!n2d@ zprj|1{nk+aJ3!eV?f#RYp7A=%`V~<2H$vGz4D}4;GV3)^{&}B=_`{&?vmR#sc&Kgj zWb4W2xwUMMsr!ikZF<(`EjdnQ$I@$CtxWx1K#gCb`OGs$c_BZpO;uKn+0<}(;LS3P*fZAqO zx@V7q2EOmQ@^_5WT3ZcSviPZ1@ZE)SONvuWMzOlutgU_AchMeC*%}tZq zFgNX?&X+c~-Uq6GdnhS_iS^U^Y1aR0Jxp9nO*=5w@Kty}$Jx ztnXnx-}(saM_51Jdg{Dn^lTa1^p4bDstohcwtbj~X^t@uHn{%Ps%flM+TJl;eM(Dy zpLD$~_s1}fVko)LdQ?Z^I-9?zA=mjzu}R!p@HtuXHLkSQ&0?+7hQv5qD`=9-{g=8B z|0O7C_e;<_KuIs_eXQ?feOK#8LoJVS)~C8ZsxM+jXO44eonn7}^JSQ$0@qw+|5_OT zHPkQ+O6FT%V!hMakTVBrecILfNT_xF4yf&|)4FhO(gW(;qd!!{U7^k?hW-}L6@G=% z)BXs4h(+p=2i1I&~jZ{P&>jzJRj(0ZN(>O?F+O;`ZaO>_PnumHi4R`>&wve@ygpe>TnWVIn?E#D|IaFcBY0rou#gn1~M(@nIr9OvHzh zKcMWhngshkQ1*vI*%v|CkB5>;Q1&ySTJD0fe+$a~6Da$yp=2GDefy1q-JVc(2SeE% z2_+{(*N;*K~%I*m$SqfzrZ8QH> z%-F6~tanjVJlO#4P78#-_w}|Fqx2f8$qfze;Qp`?u&=*)lEG za!MN1M)w-pG}HfW7sm@*B!1I&V(U=rl~6JZYMErE#}oUOTqyk=XD6#NRsC}c1^>Y zYluAx8|6tD^QVk9)lupEYL91xT6Tq!^VUd&y}Z^c|sM?gJ&G zq56-7+MecFe+tTf8I=8UDESV`ZhVK}cL|i;3@E$Xq2w_ryO*HiFNd=G63XruDA}Z= z>w~h-w|;>2W37*e^1leme>#-h4dvImQ`irj4y8|l%6lD@Un!K|gHTcdwclvdIoNLo zW#1plem5xl1E8c3D()FjcC(@E?uD{@97T835DlRsxoY-C-D)|m}4tAEQ_uMON+ zP&|pop(DQP7&X1&dR4}69p}@Yyk6C~<~V)w$$=V6KGfKXpvGDPBfm<&uYr>9tw(hy ze`oG2>XKh3+weeMdOvN`;I|wqXLgsMZv)kT9n|_ewQJCChSL9GeS!6Ftw(uNnK%!= zxbgGga@W;?-w%}xgvvb<*6*j2&Uqa>Ja3cqUEy=M<|)62p~kWZN?vsPdNn28FRpeC ztL>BCJs!*BrFex97?u_T7J7(-`D*|L9KfySdYe&%GB$cQ#`JTjyKEx<*>@{ck2^! z9}Jayy7k#m>%e+BQhp=4)H&wcuS#(mxkfvB>k#i6D0$y{6hHBuU&9-I=U0Veug2DP ze|`^K-w>}5N}hwN|5fWz+=v;S8qU3r-76h`jeWBF#bbkcY21%M$qJ~rYoV4w%WWE; zm-;*@&z!|Og>}3uev-lFXgJZaZg<<;*6U-Xd}m6IW5xT&W8HbF&i$a|LhBpy&A0D- z>?&E6t$zZw|NRbX%zr@T8ozDWAHM~qziYkL`p?!k8W8$hLG8bLS?_0k zjP;YOpJjcL_1V_%u)f)LA>Niy@dgeI`kqkw<50(kH>|(!{*T?h%l6^e`VXk%(%w4+ z{S+v_GN?EYL5-{2{V%%zU+(|V{hzsijr&{g82mdx`FDXj25tovKhOR9xqqnjL#!We zeVp}T>(^Vq$@*>9qh@kZYK-|% zb5;Zs$7ZN8m%+unWYc4(5aTf@{YC4)T94{)>^GoGTt|nUUFx^vy71l8slPI*eM*_@UhUXjcMY`; zhLX|dB&hkD0~hkr_+q;So6Vv0sGjxiBRj`H>^5}#nX-F`F&|34wq74o`@=DR9*Yw9 zNIQppRV*(a%jBgRrbEfYP;>sG+oL!UQ=e7Lb*yUJ*TKv^Ld^JHA?B`7F%Pp|43+a2 zsO5UV-eEbO45eQI)!%I2Fn7;DEzj2ZLC=EfKND(Qyuf-?Qld( zs{)U&a=$Q^8mQdy{k@$)>G?3Romek{rGEcDlze5q`_Q2GgPNCJttWpbEhca3Z-J;S z68Y&>HM`+?w_+!M232kHDWF~Ni=FFi>(N{#pX-%*%+s+cvt6bAUi~+I)%L5A?XZxq zJCyXY-dMhPW>_Eduo-2$^X+$0s$a^vPwe}}U-*u)Z`%Xnu^e8SpP^84 zu=VbYmR$5QD$HBfcM5AyZ_6QA*{7eV!xKuMYO zM$RJ}Ynyp+7{frP;#W(*UOvqKbIxWHPt?I6(jo0;cIh)4hivxLrD}bb*|?-RvC85|2t81Xz;%p zN^XX#^M=F1y43mbpf^3z%LJ-_ztLgY+;vpYpMsK?q1Kfu>&g42Dr$)8+YrM&&&?XW z=P7@Q)^V-Rs$*tp!+luM(GB-un8a#aW1K$uAkxO5y-e2MDS^BJlu{f>t7o;h~?d9U*rGLH*m z83eUGk1{8^eHPT1%c16|8fw&aFj_u?h_CmjLCFm3QT@rspqx#^Hd=;Fh3($4-MZfU5r|>rvc@iT|5Q`adfzzY^zYTNGmC!hFB~07|}tiko&)(9@yvMR6l0&NGv) z6PG!5j{Wjsp?xoO+vQNPuY)=sEwKI$Y%E88u9V|urE}HdCuw_fh?@)Zc_~(*+b)61 zH4SQ=yTSThP}}VTP&GXbC9lGNS4&h=#ME=y8rN3ic=1z0{2Zt{@~s!asl1fy6)0I{ zy*_q_^?&Ow-X*Nl9merlhnM_ELP?SJdVj5B~>ZGz*>blk;Ib*zD^Bm4At ztbmtt-3cX+Le=ns^(yzL){yv}mpj7$sU)@s=d86~+hWe)cqw)fluUt&J=6Ma?tj4T zo1GEr8vtduE0p9z#W~FVr@DWl`!8|-4EHaAiFvl(ZG4#T!BEGOL#>|)H6PQV^38^l zyP@)>j`^?jjIP|PYUCN%YR^rj^g z8tcwyh5r4Z!4!ZXMGXWSYL*c58eNV`^npG=Rn!-2qi<^f2I3pyZ>JI zKkfc6-2bclTb>jAyFta>0jjQ3t)FB48tb<~<$VB3o^yZaiLMJK>VlG?Za)bs?riIK zSueM~6w0p}N>;o7g>ysPe?j$s3MId|{pj;T`>9ZJq4jI5-v+h3=2}mjOZeVkyzAdM zM@#(9%mrzi)@H8kRMnxPU2#TXTc0b)U!rkE=V*iOZFsIQEr~wH3MTY^a!1q1N3upz2ryHKvY} zgWeOWe|M<%A#NY;_7kAuTm&VvpzQ8|iZ>t1f0^~Z7X&>I%I`=h84qQDw)^jP|FiC| zcK@~)2LA(~>?c^CZoLX>d8AJX=UQ7poon^8J_yS1AgH>IgR0{KD7gX3|7NI`2cgaf zpRoP{l>Z7S|2n91#f>kDC!Pt;g3`00>~?{&I|wSz$xw0;Oyq%jHuwSSr(PUSJga*# zRR6xbhU0ttS|K9XE z&t=!k!`u#rS_Y%dN#-1=&pC^%uYg)A>#TPz3ERkEsAW9boCFnb4%9lo$odMXb$T6? zeb+03-C(G7b+q+KQ0v?rsC8(O+gCuXL+hZHJ#S-!8K2&dgObVCFSLHM^=SE~{)S$W z_pytxYw)~g@;&NJ*k;E!{I>Gc`?igJwrYEAs4W*tcCcPwOVT~-a@SFSO_A-cvfp*7 zekq?bs_k2jonwdl>3jb9{S~S8)=Uqz{Qc6>l7roDUUm22`Avp!P+_l!krPYqtlz8mj*@s65|8 z9dCcL9={{(%Q`{j$%e|a9hB?|mFIA%ePF9QL)>;yal1ps?F)7M-O>7$t0+_Goj+%4Ylt)Yi>CHUJ0e&4|VK#&iXBP1^YXp#ybzHjwhh%coFJbnltLd9?S z&k#QoN_s=ZPu+(9x9Yl2o0)cQqxWeGy`LK8xtZv>nq`(l&0#gvT-TYIWubi#)Up@_ zHM}I*Y+&WE(IHLNi-CyVa%zGRUCgMRY>xnQC&;8}rtF6~Tts9y5hIM6-ISR^e zB2@jete0D_hRMh7N%w_y;uWZM@f|4n8fqW=ll4E~zgu@QGTG-0Z+P6-@u0@@Ja}G+ zJKCHCXYo?ZwQk$u{!qgnP`M9+|2Ajpvo@QB<69Aak`l8FE_SRo4}`vKm>fH!k?V3f zjyDq9akg*qVDQg^ny*~z`$En4aH!f(fm#k{TfYb<=liQ|E~8tkSckNXM!!=n-*v4c zrljlq5PL9G?$PEXILm!6Ldn-qb=N`J#vclGcZRCBzx9z&$GkC6>*WPd$GmCQZ-Me} z^>A4KH-XZ-K^@z+hB~J140UYV+j;?1{v)B}M5xb#u?)$=G+yv0z*wl|>upYtQA zW7}%$KSRZ9vLM9U80y%SWxWqn&tRxy*s<16hl)QLYCF0LYP-7y>X>ww_4!copM;Vn zQ0LokSpNhnZtI01PZm_%-cZM!?V*lEL!kEQ2U$M?D(>-6at73AlS$U6L&d!l%Hlbw zW6$T-e}u}j(IerQk^yzB=?-;F*xLFIPc11QmBCl*JuTap%MC@M*X? zd7idzaL^I$Ld znDt7io*JllKfxT>^s#uXH*62Lf}2_23M$?}D9M9;;85$Mp?W4k#k&&rg|pz+@Sm_B zeAxO^Q1L3E-WI^=;crrFG9tA3vLfTh6CYua0j@~dW*+H+;&j18Qc+WWqntuxCcO4Tn-1p zo8eCIN$X49{|;2%PvI``2e>nA@&wo2U>hhs6KXs?p`<_D74B?(C{+C4pz^hTGUU&K zir*XV4!4K9!67gY9%TIpsQAZ2$r*4DILZ2SsQ7QfJ>kkl@z`GQHz@sxr$Ya6Q2Eb* zigX@S{-tnl_zoNbzkvI|HP-)t%HQhgkiQe$7rlq|{!shxX3()zjb*@oA;a+a)r ztt+2(RJKJ>>wXE;IZc`MN~m*z8Z-V(u*-q+%ZEDV7nvncQs(|jsAG7I8LtTaIZ*HN zVIn`&vAD!6gOW=3*FYUpKuJE-yCSGKB~Zu2GP4p&YTO@xE*#r(%zP**f(d`9 zV^o=0>HZq`$Da?!oE$SBCj4Q-AL&ht2FfzNIN0St9Si;sd+!3K*L41Wubs?! zo@X+rB$M;xd}hSZAQ%x65)1}GFk%oIjG9)QY8pXQFi6ohbdYMa!BlBcbeaxLD{AOa zE!Ck@Qc|Wh)u?FypYLAxp6utz#NmCf>;3(%_xi8vTF-aiYpwfU_c|Z;+UuETxCE40 zY5wa#_30Z8H=BRXQ(n#_P<_`tFw8$teeX)cb)d{f^WO}rFU@({<0MdK9;m)@38=nt zB`A68K=pka4L5@_In*KbZF-KcDbn}Pg53TtHyG)3<|HW9ymrVA^V)EK>%r9ZLyFAk zY*ZohJH8E%pWa&V8?VQmpv)Uak9f}0(?RJOwefJSpZ5Re>ugSaDPdPz_&be%ozagP z{XD3A`Ge6xyQlL&nfUpgjmZ5}TK7>d_ib0@k_zt#}$Aq)F9C^PD} zX}qz$+niB;jaEsR1s3jQQ0D9o&$k?u`Ki&*82y6LZyEjgi$47Gpv?QA@+|iyPtOLG z=HrYW_;RXVz4bwN6}-{+H(HZS-%H_@Spz;~a)$lhyL}H-JiYSDcH9G3zdX-mwGsYR zx*%lI$2gN(HH5cchnKChD2*u57mfAswO|H;ey4XC{60@auQ2UJ};wbRq*f$|^p zx?lIr07V}P%3K6Wp9eto`Ir3JuLoCyqKmpx^^Ma0S+Qtz7XRhFt1GBa)+Va+!*35( zTG~319h$#*oytL_p~~o5P`WjO(xqAMv3C%w-}L@}2W2*G_H+-ZG~V)8FYm{o@}%A9 zzk>2ly#3kIrhCr^)0d^ZZ>r7=)3VOevchDo1tq%;R9d<~_l}R}SD?&hP+`ipczP_T zbf)KN(Gc~4=LhjVP<3OY#aU(iwcs+7@r1d(ZS;Fa4|+E>j-rXVeA_VGm39|*7PdyE zmqE9gTe51c+^wmc1khY=Rv;9W;iF+S^PPy+p zT8y_{vSMvu-rs$kOF)@(jgI1opCepmvQ`qN*~0z8_CoW+Hd$7%i+XYma&NO;Hs5-h0RGlpN*wfd7D)R@89>B~G5!8c_68p!Otl`+53fP_weJ{d2>;#igL=WuU@;1ZrI~ zD5vcSIHF}ECyBfW}(Q}NhG5UC; z&ocU4qrYnO6-M7|^le73Gx|QGpD=m@D82t^?te4)kIlU-$Ll)@49mypc}5>*^hrja z35NLvO76|(f4BMn2vmNxng5^6|2^~nuelfG`glizigzq1c{9v?wzV|a>=^WfXur)d@pt-9`ALkfQ<^-cpF*;?Ph}zo8thP`}g z`7vp{&yPyNEwHfX8Sj@rg?FCut|8pGQXkK3Q06gEY5vgYDEPz?d`vy#=5D4bHg#N5{&166xYtMw+(TtQ@BPj9jcHcQ)}1=D!yh= z@wI~K`#2lReB522OcZB$w?f*t#E(qy^6L$CSd!C?3rf!t#45we3iLyzMA_J#Gry1WMNyP`b#k z+n$Suc|Avi3j6qQANE;L@x5a7UqSir&fTBVVJ@h7z%fRj28R5g`rxvOp8g&v^DwBm zA2T|h4_iMWAJX$BqW$-Hd-wT0p5)0$4~+xTYuwAJ`PDRO$NXwHox4HhS0Q14kDXjawW_PJ|s~Wee(AEERql@a6QquZQ?QT=2%&jV< z{7Db9LJkK7{v^fnqFZ{p9Jruw}5GpKU-094)kuhBWvd>)p8O4B4zan1x~ z=7EYc%A3!M>Fc=KNxolLYI0XuJnKQly9rdjZULodVmf01R&gH&%A9X>q(l1s2#prU zTEg|U_M5qjmv;mxvlvubzi4!W`A702OixR*#k11*)`6|Y`#VtS`5UP8d|>o{LB&gDw%rl^Lf5F_#XZW<904mP2K$!+m;Vv=%+d-xE1*7-c-OHT^itjj3W-%zf zvq0tH&7kURm(k6YzMp&yl>alJ^m`GMoGwu2Jy3E!0M(yO-NVmqXMv*Uf#N?N6#r?U z_`d|oTn37N1*pF8CQ$3ik3gMmDcaMo7sr62D?s_*3`)Yipw^p@8NJc`dqBzi7?d8l zU+{Vi1*OM0P%F|P-Yb<{+mJd(073f z|6@=+@LNW21;w8|%kvKcWk!SIp9CuWbWr^JgE}*G3aGO}^+qq**UPyOlpa@sGB<*f za~mkW3Hy0Ddw_~}4yf=)gEA+B3f}-K{7O*q-U=%GJ)pur49c{E3jY$Q@UMUh|2C-b zAA$;>v%e2t0xJ9zP~lz%749ui;XVLmf+`>G)B2$QE&hHzJfzR{aHsW)(@2xd0#IpP z3M%cZK;^}H!%c=;K((>N0lv*m1J$+{fReP-=vAQF{(4YyHi4?MTMQGkJv|MS{{q9M zhO0o8-FmPMtMYjjl*v8N>sk!T3TsB!W>qkjs9 zbrMwgS3sHfK!qDI-}6lc6>b)&aPvW#Q_TMy^S=rd-`CCkHgmrh6yGDD%qCFbk_){2 zVW8w!f^wg0?#Gz>8KC&T49Z*sD%=)O>Haq;{`|vyxS^oTE}+7l2rArJpu$}YO5Qb~ z%Dg!u)SE|4ruqiurE= zRZstJbhs|LDXUuRie~CnROdbohcGW2$Jw90zR{H&8R@HjOZvU~m(;b$x+AEwxJwrx zG_>?Ff^pwhSmRGL?UQTUDaycLwmt@eKTpv-uqqjaQyN7ia>XddB~TG&;_ds8}Z zY)_`!_|_Aq&BDE9e1A*lOKZ1Ro zzAE0n?kS4BkM26k$9MD5KEBsM#rJ{HWi{S^JSejURKHMrjHfq(DvMW*KKxi;CiS4o zd4NYb z{w}C_*H4VTY?05udqJ58jsB(4?MDC3=uV@f^8MeAMU(T~h+ub@enLh4Mzz()%9DKB zYYZCh8>1IhNY(%zhRAGgW+n!hro8M+Ffz2 z=h+=pJ7@q^-!C%yYV*GtRK35$=m$ZC>jcI3uDSmkRN696^>PP+3ikt0?c-rk?cyb) zUpN0Ppyd1;l*wA`+er~9GX@mjzM$mH110xpP<*F?GUtGr6D~7)6)3)Mf#SO#RLJLz zeif9Qw?N7HCnyt~=G$EYloG8cgA7rtur4WRgb3@ZLVgW`J^6yG*bCg%*_f0TeS<3NR<3o86k zpu*Rh|58xvxeJY62`c=3pu#@_D*V&t{{pD~=Z{9e3o87iGky5oL4`j6lsODk|FqEP zrJ%y!0_xk5%q5=Q4^(_}K=B_1%A5@9eCt_8Uj~Z*6;R)lY%zN5S-IgiB?p7@e-f0u zUxAYMJ5c7&;1KX#qx+rh!xw=HKO9u}DWJ@rpuXKW!02N@$zKF2{NwL8Fy( zvtcVJxt*Zeuluqu_j1E3Q0}!xHySpZd#ll%hVCmqd^spnWms$2XxMDn3QB&b(e6Uy zH>?6>>J6Jf>Dyv-yJ5Gv7cTSZtN_)nstxNwwTmWDrp2(`up1nMdm(wJ_Ell^zZ#74 z(dtq~^~K)pLQv*vqg#!B7F1rpWOV%9(oD6Xv^xOh78&u$E`5Ldd2W@zWXIn-_UaU! z7mLm?q|?P&t~ki-7u{SDo`JRd9-{R0Vt#*@Kil8au)}{C%_HT$a1gC0<3 z(&e6>0m@VxeVoxJ8GWYF7a4tp(LF|QHQF_Lc^RP045Op=Tr`65b9vuqKo^pq%AbYs z)EnP!U-NRtEcbHv1!WF5x>xQ_&Sft%ne~KUX*_S5jLBDcnY}WK$*b5|J_}AE>%_ zywT?v-DvcUM&E7pPmF%s=#PwE{BUonrzi;vX@>;J?J17&^XDjigw@bN2M)AvL{3}(5 zSCJLhLFpCGHj8WgDj(M|p!}jVr|*T9P4jj3Qo^mau$Nrtd7lSUc*F0$NA2)^jOy7; zlT&#;WjSESZwyLr@UkuhL(}BxAAw5WqMK6V>eV;ACt*~d_arQ|ILF=WdFOzO?0GFH zv(e~Tw|V+dQ1KmU^bqwui#eH7?ay&9L#7`ahxR!(%0Dtu*)(r03fEZCyK}{*u~B)a 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 V0&WYqE#S6*+X8M2yh~c({{uY!s`~%{ From 41f1058469f0f338321502d31e2534f6bc3d08e4 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 3 May 2018 23:09:39 +0100 Subject: [PATCH 194/339] ZXHawk: fixed edge-case tzx tape image parsing exceptions (red heat, live and let die, etc.) - #1158 --- .../Media/Tape/TzxConverter.cs | 91 +++++++++++++++---- 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs index ac877f15d2..fe6ef6fe6c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs @@ -1242,7 +1242,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.DataPeriods = new List(); t.BlockDescription = BlockType.Archive_Info; - int blockLen = GetWordValue(data, 0); + int blockLen = GetWordValue(data, _position); _position += 2; int stringCount = data[_position++]; @@ -1591,37 +1591,89 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // dont get description info for Pure Data Blocks if (dataBlockType != DataBlockType.Pure) { - if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || (blockdata[1] == 3 && blockdata.Length > 3)) + if (blockdata[0] == 0x00 && blockSize == 19) { - // This is the program header string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); + string type = "Unknown Type"; + StringBuilder sb = new StringBuilder(); - string type = ""; - if (blockdata[0] == 0x00) + var param1 = GetWordValue(blockdata, 12); + var param2 = GetWordValue(blockdata, 14); + + // header block - examine first byte of header + if (blockdata[1] == 0) { type = "Program"; - block.AddMetaData(BlockDescriptorTitle.Program, fileName); + sb.Append(type + ": "); + sb.Append(fileName + " "); } - else + else if (blockdata[1] == 1) { - type = "Bytes"; - block.AddMetaData(BlockDescriptorTitle.Bytes, fileName); + type = "NumArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); } + else if (blockdata[1] == 2) + { + type = "CharArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 3) + { + type = "Code"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + } + else if (blockdata[0] == 0xff) + { + // data block + description = "Data Block " + (blockSize - 2) + "bytes"; + block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); + } + else + { + // some other type (turbo data etc..) + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize); + //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + block.AddMetaData(BlockDescriptorTitle.Undefined, description); + } + /* + if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || (blockdata[1] == 3 && blockdata.Length > 3)) + { + if (dataBlockType != DataBlockType.Turbo) + { + // This is the program header + string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); - // now build the description string - StringBuilder sb = new StringBuilder(); - sb.Append(type + ": "); - sb.Append(fileName + " "); - sb.Append(GetWordValue(blockdata, 14)); - sb.Append(":"); - sb.Append(GetWordValue(blockdata, 12)); - description = sb.ToString(); + string type = ""; + if (blockdata[0] == 0x00) + { + type = "Program"; + block.AddMetaData(BlockDescriptorTitle.Program, fileName); + } + else + { + type = "Bytes"; + block.AddMetaData(BlockDescriptorTitle.Bytes, fileName); + } + + // now build the description string + StringBuilder sb = new StringBuilder(); + sb.Append(type + ": "); + sb.Append(fileName + " "); + sb.Append(GetWordValue(blockdata, 14)); + sb.Append(":"); + sb.Append(GetWordValue(blockdata, 12)); + description = sb.ToString(); + } } else if (blockdata[0] == 0xFF) { // this is a data block description = "Data Block " + (blockSize - 2) + "bytes"; - block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize).ToString() + " Bytes"); + block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); } else { @@ -1629,7 +1681,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize); //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); block.AddMetaData(BlockDescriptorTitle.Undefined, description); - } + } + */ } // update metadata From f36e252d5e1c9ac6cbb52e2303bc40c28bdb722b Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 3 May 2018 23:19:24 +0100 Subject: [PATCH 195/339] ZXHawk: fixed tap tape image parsing exceptions (operation wolf etc) - #1158 --- .../Media/Tape/TapConverter.cs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs index df4ec5a3d5..c74e3fa5a6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs @@ -184,6 +184,57 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum the start of the code block when saved, and parameter 2 holds 32768. For data files finally, the byte at position 14 decimal holds the variable name.) */ + tdb.MetaData = new Dictionary(); + + if (blockdata[0] == 0x00 && blockSize == 19) + { + string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); + string type = "Unknown Type"; + StringBuilder sb = new StringBuilder(); + + var param1 = GetWordValue(blockdata, 12); + var param2 = GetWordValue(blockdata, 14); + + // header block - examine first byte of header + if (blockdata[1] == 0) + { + type = "Program"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 1) + { + type = "NumArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 2) + { + type = "CharArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 3) + { + type = "Code"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + } + else if (blockdata[0] == 0xff) + { + // data block + description = "Data Block " + (blockSize - 2) + "bytes"; + tdb.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); + } + else + { + // some other type (turbo data etc..) + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize); + //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + tdb.AddMetaData(BlockDescriptorTitle.Undefined, description); + } + /* if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || blockdata[1] == 3) { // This is the PROGRAM header @@ -224,6 +275,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize - 2); description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); } + */ tdb.BlockDescription = BlockType.Standard_Speed_Data_Block; From d4eb3da5125e40b33921272dd7a8d238f3f59db8 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 3 May 2018 19:07:51 -0400 Subject: [PATCH 196/339] GBHawk: Restructure to pass all other tests (as before) --- .../CPUs/LR35902/LR35902.cs | 1 - .../CPUs/LR35902/Tables_Direct.cs | 2 +- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 141 +++++++++++------- .../Consoles/Nintendo/GBHawk/PPU.cs | 4 + 4 files changed, 95 insertions(+), 53 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index cf4576b297..7e60233816 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -497,7 +497,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 if (Halt_bug_2 && I_use) { RegPC--; - if (!interrupts_enabled) { Halt_bug_3 = true; } } diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index 3325ffcc1c..f091c6db79 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -111,7 +111,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, HALT_CHK, IDLE, - HALT, 1 }; + HALT, 0 }; skip_once = true; // If the interrupt flag is not currently set, but it does get set in the first check // then a bug is triggered diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index d819b614d7..7c97350f8b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk x_tile = (int)Math.Floor((float)(scroll_x) / 8); break; case 0xFF44: // LY - LY = 0; /*reset*/ + LY = LY_actual = 0; /*reset*/ break; case 0xFF45: // LYC LYC = value; @@ -110,7 +110,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { // the ppu only does anything if it is turned on via bit 7 of LCDC if (LCDC.Bit(7)) - { + { // start the next scanline if (cycle == 456) { @@ -125,24 +125,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle = 0; - LY += LY_inc; - no_scan = false; + LY_actual = LY; - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } + no_scan = false; if (LY == 0 && LY_inc == 0) { LY_inc = 1; Core.in_vblank = false; - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; // special note here, the y coordiate of the window is kept if the window is deactivated // meaning it will pick up where it left off if re-enabled later @@ -153,8 +143,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_is_reset = true; } - Core.cpu.LY = LY; - // Automatically restore access to VRAM at this time (force end drawing) // Who Framed Roger Rabbit seems to run into this. VRAM_access_write = true; @@ -166,6 +154,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } + if (cycle == 452) + { + LY += LY_inc; + + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + else + { + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + } + + Core.cpu.LY = LY; + } + + if ((LY == 153) && (cycle == 0)) + { + LY = 0; + LY_inc = 0; + + } + // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -185,21 +201,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // the VBL stat is continuously asserted - if ((LY >= 144)) + if ((LY_actual >= 144)) { if (STAT.Bit(4)) { - if ((cycle >= 4) && (LY == 144)) + if ((cycle >= 0) && (LY_actual == 144)) { VBL_INT = true; } - else if (LY > 144) + else if (LY_actual > 144) { VBL_INT = true; } } - if ((cycle == 0) && (LY == 144)) { + if ((cycle == 0) && (LY_actual == 144)) { HBL_INT = false; @@ -209,19 +225,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } Core.REG_FF0F |= 0x01; - //Console.WriteLine(Core.cpu.TotalExecutedCycles); } - if ((LY >= 144) && (cycle == 4)) + if ((LY_actual >= 144) && (cycle == 0)) { // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY == 153) && (cycle == 8)) + if ((LY_actual == 153) && (cycle == 4)) { - LY = 0; - LY_inc = 0; + LY_actual = 0; Core.cpu.LY = LY; } } @@ -234,7 +248,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 8) + if (cycle == 4) { if (LY != LYC) { @@ -250,7 +264,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 84) + if (cycle == 80) { STAT &= 0xFC; @@ -263,7 +277,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_write = false; } - if (cycle == 256) + if (cycle == 252) { STAT &= 0xFC; OAM_INT = false; @@ -278,9 +292,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (cycle < 80) + if (cycle < 76) { - if (cycle == 4) + if (cycle == 0) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -288,18 +302,28 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // here mode 2 will be set to true and interrupts fired if enabled STAT &= 0xFC; STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } + if (STAT.Bit(5)) + { + OAM_INT = true; + } HBL_INT = false; } - // here OAM scanning is performed - OAM_scan(cycle); - } - else if (cycle >= 80 && LY < 144) - { - if (cycle == 84) + // here OAM scanning is performed + OAM_scan(cycle + 4); + } + else if ((cycle >= 76) && (LY_actual < 144)) + { + if (cycle == 76) + { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + } + + if (cycle == 80) { STAT &= 0xFC; STAT |= 0x03; @@ -309,14 +333,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // render the screen and handle hblank - render(cycle - 80); + render(cycle - 76); } - } + } + + if (cycle >= 452) + { + OAM_access_read = false; + OAM_scan(cycle - 452); + } } if ((LY_inc == 0)) { - if (cycle == 12) + if (cycle == 8) { LYC_INT = false; STAT &= 0xFB; @@ -333,11 +363,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (STAT.Bit(5)) { OAM_INT = true; } } - if (cycle == 92) { OAM_INT = false; } + if (cycle == 88) { OAM_INT = false; } } // here LY=LYC will be asserted - if ((cycle == 4) && (LY != 0)) + if ((cycle == 0) && (LY_actual != 0)) { if ((LY == LYC) && !STAT.Bit(2)) { @@ -360,6 +390,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LCD_was_off = true; LY = 0; + LY_actual = 0; Core.cpu.LY = LY; cycle = 0; @@ -367,7 +398,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // assert the STAT IRQ line if the line went from zero to 1 stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; - + /* + if (stat_line) { + Console.Write("OAM: "); + Console.Write(OAM_INT); + Console.Write(" LYC: "); + Console.Write(LYC_INT); + Console.Write(" VBL: "); + Console.Write(VBL_INT); + Console.Write(" HBL: "); + Console.Write(HBL_INT); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + */ if (stat_line && !stat_line_old) { if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } @@ -393,10 +436,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - // window X is latched for the scanline, mid-line changes have no effect window_x_latch = window_x; @@ -411,6 +450,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk fetch_sprite_01 = false; fetch_sprite_4 = false; going_to_fetch = false; + first_fetch = true; no_sprites = false; evaled_sprites = 0; @@ -543,7 +583,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (pixel_counter == 160) { read_case = 8; - hbl_countdown = 7; + hbl_countdown = 5; } } else if ((render_counter >= render_offset) && (pixel_counter < 0)) @@ -840,7 +880,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (going_to_fetch) { going_to_fetch = false; - sprite_fetch_counter = 0; + sprite_fetch_counter = first_fetch ? 2 : 0; + first_fetch = false; if (fetch_sprite_01) { @@ -1060,8 +1101,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { - OAM_access_read = false; - OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 1dc152fd00..2783853b34 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -21,6 +21,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte scroll_y; public byte scroll_x; public byte LY; + public byte LY_actual; public byte LY_inc; public byte LYC; public byte DMA_addr; @@ -68,6 +69,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool fetch_sprite_01; public bool fetch_sprite_4; public bool going_to_fetch; + public bool first_fetch; public int sprite_fetch_counter; public byte[] sprite_attr_list = new byte[160]; public byte[] sprite_pixel_list = new byte[160]; @@ -171,6 +173,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("scroll_y", ref scroll_y); ser.Sync("scroll_x", ref scroll_x); ser.Sync("LY", ref LY); + ser.Sync("LY_actual", ref LY_actual); ser.Sync("LYinc", ref LY_inc); ser.Sync("LYC", ref LYC); ser.Sync("DMA_addr", ref DMA_addr); @@ -217,6 +220,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("fetch_sprite_01", ref fetch_sprite_01); ser.Sync("fetch_sprite_4", ref fetch_sprite_4); ser.Sync("going_to_fetch", ref going_to_fetch); + ser.Sync("first_fetch", ref first_fetch); ser.Sync("sprite_fetch_counter", ref sprite_fetch_counter); ser.Sync("sprite_attr_list", ref sprite_attr_list, false); ser.Sync("sprite_pixel_list", ref sprite_pixel_list, false); From e29b741bd41d18b62b3bbde8dab37f5079187ecc Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 3 May 2018 21:26:33 -0400 Subject: [PATCH 197/339] GBCHawk: Halt test rom work, ppu still needs re-work --- .../CPUs/LR35902/LR35902.cs | 33 ++++++++++++++----- .../CPUs/LR35902/Tables_Direct.cs | 9 ++--- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 6 ++-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 7e60233816..51605f8499 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -301,7 +301,15 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, GBC_INTERRUPT }; */ - INTERRUPT_GBC_NOP(); + if (!Halt_bug_3) + { + INTERRUPT_GBC_NOP(); + } + else + { + INTERRUPT_(); + Halt_bug_3 = false; + } } else { @@ -324,16 +332,26 @@ namespace BizHawk.Emulation.Common.Components.LR35902 if (OnExecFetch != null) OnExecFetch(RegPC); if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + if (is_GBC) { // extra 4 cycles for GBC - cur_instr = new ushort[] + if (Halt_bug_3) + { + RegPC++; + FetchInstruction(ReadMemory(RegPC)); + Halt_bug_3 = false; + } + else + { + cur_instr = new ushort[] {IDLE, IDLE, IDLE, OP }; + } } - else + else { if (Halt_bug_3) { @@ -363,11 +381,9 @@ namespace BizHawk.Emulation.Common.Components.LR35902 else { cur_instr = new ushort[] - { - IDLE, + {IDLE, HALT_CHK, - IDLE, - + IDLE, HALT, 0 }; } @@ -492,12 +508,11 @@ namespace BizHawk.Emulation.Common.Components.LR35902 instr_pntr = 0; break; case HALT_CHK: - // only used when exiting HALT from GBC, an extra NOP is added to avoid HALT bug I_use = FlagI; if (Halt_bug_2 && I_use) { RegPC--; - if (!interrupts_enabled) { Halt_bug_3 = true; } + Halt_bug_3 = true; } Halt_bug_2 = false; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index f091c6db79..eb3346900f 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -86,11 +86,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 {IDLE, IDLE, IDLE, - IDLE, - IDLE, - IDLE, - IDLE, - OP}; + OP_G}; } else { // if interrupts are disabled, @@ -112,7 +108,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 HALT_CHK, IDLE, HALT, 0 }; - skip_once = true; + + if (!is_GBC) { skip_once = true; } // If the interrupt flag is not currently set, but it does get set in the first check // then a bug is triggered // With interrupts enabled, this runs the halt command twice diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index d3f179e2f4..4cebae5985 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -402,7 +402,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // also, the LCD does not enter mode 2 on scanline 0 when first turned on no_scan = true; - cycle = 8; + cycle = 4; } // the VBL stat is continuously asserted @@ -420,7 +420,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if ((cycle == 4) && (LY == 144)) { + if ((cycle == 0) && (LY == 144)) { HBL_INT = false; @@ -454,7 +454,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 12) + if (cycle == 8) { LYC_INT = false; STAT &= 0xFB; From b3647c6e052ee58fe46fbe8df88f90cff4b50ce5 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 4 May 2018 15:45:18 -0400 Subject: [PATCH 198/339] GBCHawk: adjust PPU --- .../CPUs/LR35902/LR35902.cs | 16 +-- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 129 +++++++++++------- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 22 +-- 3 files changed, 86 insertions(+), 81 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 51605f8499..c4b39bfeb5 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -57,8 +57,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public const ushort RD_F = 42; // special read case to pop value into F public const ushort EI_RETI = 43; // reti has no delay in interrupt enable public const ushort INT_GET = 44; - public const ushort GBC_INTERRUPT = 45; - public const ushort HALT_CHK = 46; // when in halt mode, actually check I Flag here + public const ushort HALT_CHK = 45; // when in halt mode, actually check I Flag here public LR35902() { @@ -294,13 +293,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 if (is_GBC) { // call the interrupt processor after 4 extra cycles - /* - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - GBC_INTERRUPT }; - */ if (!Halt_bug_3) { INTERRUPT_GBC_NOP(); @@ -309,6 +301,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { INTERRUPT_(); Halt_bug_3 = false; + Console.WriteLine("Hit this"); } } else @@ -502,11 +495,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src]; break; - case GBC_INTERRUPT: - // only used when exiting HALT from GBC, an extra NOP is added to avoid HALT bug - INTERRUPT_(); - instr_pntr = 0; - break; case HALT_CHK: I_use = FlagI; if (Halt_bug_2 && I_use) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 4cebae5985..fb3c2e976a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -134,7 +134,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 0xFF45: // LYC LYC = value; - if (LY != LYC) { STAT &= 0xFB; } + if (LCDC.Bit(7)) + { + if (LY != LYC) { STAT &= 0xFB; } + else { STAT |= 0x4; } + } break; case 0xFF46: // DMA DMA_addr = value; @@ -346,24 +350,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle = 0; - LY += LY_inc; - no_scan = false; + LY_actual = LY; - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } + no_scan = false; if (LY == 0 && LY_inc == 0) { LY_inc = 1; Core.in_vblank = false; - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; // special note here, the y coordiate of the window is kept if the window is deactivated // meaning it will pick up where it left off if re-enabled later @@ -387,6 +381,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } + if (cycle == 452) + { + LY += LY_inc; + + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + else + { + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + } + + Core.cpu.LY = LY; + } + + if ((LY == 153) && (cycle == 0)) + { + LY = 0; + LY_inc = 0; + + } + // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -406,21 +428,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // the VBL stat is continuously asserted - if ((LY >= 144)) + if ((LY_actual >= 144)) { if (STAT.Bit(4)) { - if ((cycle >= 4) && (LY == 144)) + if ((cycle >= 0) && (LY_actual == 144)) { VBL_INT = true; } - else if (LY > 144) + else if (LY_actual > 144) { VBL_INT = true; } } - if ((cycle == 0) && (LY == 144)) { + if ((cycle == 0) && (LY_actual == 144)) { HBL_INT = false; @@ -432,16 +454,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Core.REG_FF0F |= 0x01; } - if ((LY >= 144) && (cycle == 4)) + if ((LY_actual >= 144) && (cycle == 0)) { // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY == 153) && (cycle == 8)) + if ((LY_actual == 153) && (cycle == 4)) { - LY = 0; - LY_inc = 0; + LY_actual = 0; Core.cpu.LY = LY; } } @@ -454,12 +475,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 8) + if (cycle == 4) { - LYC_INT = false; - STAT &= 0xFB; + if (LY != LYC) + { + LYC_INT = false; + STAT &= 0xFB; + } - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -467,7 +491,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 84) + if (cycle == 80) { STAT &= 0xFC; @@ -480,7 +504,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_write = false; } - if (cycle == 256) + if (cycle == 252) { STAT &= 0xFC; OAM_INT = false; @@ -495,9 +519,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (cycle < 80) + if (cycle < 76) { - if (cycle == 4) + if (cycle == 0) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -511,12 +535,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // here OAM scanning is performed - OAM_scan(cycle); + OAM_scan(cycle + 4); } - else if (cycle >= 80 && LY < 144) + else if (cycle >= 76 && LY_actual < 144) { + if (cycle == 76) + { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + } - if (cycle == 84) + if (cycle == 80) { STAT &= 0xFC; STAT |= 0x03; @@ -526,20 +556,26 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // render the screen and handle hblank - render(cycle - 80); + render(cycle - 76); } - } + } + + if (cycle >= 452) + { + OAM_access_read = false; + OAM_scan(cycle - 452); + } } if ((LY_inc == 0)) { - if (cycle == 12) + if (cycle == 8) { LYC_INT = false; STAT &= 0xFB; // Special case of LY = LYC - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -550,13 +586,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (STAT.Bit(5)) { OAM_INT = true; } } - if (cycle == 92) { OAM_INT = false; } + if (cycle == 88) { OAM_INT = false; } } // here LY=LYC will be asserted - if ((cycle == 4) && (LY != 0)) + if ((cycle == 0) && (LY_actual != 0)) { - if (LY == LYC) + if ((LY == LYC) && !STAT.Bit(2)) { // set STAT coincidence FLAG and interrupt flag if it is enabled STAT |= 0x04; @@ -568,7 +604,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - STAT &= 0xF8; + STAT &= 0xFC; VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; @@ -577,6 +613,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LCD_was_off = true; LY = 0; + LY_actual = 0; Core.cpu.LY = LY; cycle = 0; @@ -610,10 +647,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - // window X is latched for the scanline, mid-line changes have no effect window_x_latch = window_x; @@ -628,6 +661,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk fetch_sprite_01 = false; fetch_sprite_4 = false; going_to_fetch = false; + first_fetch = true; no_sprites = false; evaled_sprites = 0; @@ -811,7 +845,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (pixel_counter == 160) { read_case = 8; - hbl_countdown = 7; + hbl_countdown = 5; } } else if ((render_counter >= render_offset) && (pixel_counter < 0)) @@ -1136,7 +1170,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (going_to_fetch) { going_to_fetch = false; - sprite_fetch_counter = 0; + sprite_fetch_counter = first_fetch ? 2 : 0; + first_fetch = false; if (fetch_sprite_01) { @@ -1375,8 +1410,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { - OAM_access_read = false; - OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 7c97350f8b..00eb0d539e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -78,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (LCDC.Bit(7)) { if (LY != LYC) { STAT &= 0xFB; } - else { STAT |= 0x4; /*if (STAT.Bit(6)) { LYC_INT = true; } */} + else { STAT |= 0x4; } } break; case 0xFF46: // DMA @@ -302,15 +302,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // here mode 2 will be set to true and interrupts fired if enabled STAT &= 0xFC; STAT |= 0x2; - if (STAT.Bit(5)) - { - OAM_INT = true; - } + if (STAT.Bit(5)) { OAM_INT = true; } HBL_INT = false; } - // here OAM scanning is performed OAM_scan(cycle + 4); } @@ -398,19 +394,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // assert the STAT IRQ line if the line went from zero to 1 stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; - /* - if (stat_line) { - Console.Write("OAM: "); - Console.Write(OAM_INT); - Console.Write(" LYC: "); - Console.Write(LYC_INT); - Console.Write(" VBL: "); - Console.Write(VBL_INT); - Console.Write(" HBL: "); - Console.Write(HBL_INT); - Console.WriteLine(Core.cpu.TotalExecutedCycles); - } - */ + if (stat_line && !stat_line_old) { if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } From c9898c452a5a861e228dcbcd5daec183e33e2611 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 4 May 2018 17:52:10 -0400 Subject: [PATCH 199/339] GBHawk audio: Fix wave sampling in pokemon yellow --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 27dd958043..ad756ec936 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -666,7 +666,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (!WAVE_DAC_pow) { WAVE_output = 0; } // avoid aliasing at high frequenices - if (WAVE_frq > 0x7F0) { WAVE_output = 0; } + //if (WAVE_frq > 0x7F0) { WAVE_output = 0; } } } @@ -689,7 +689,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk NOISE_LFSR |= (bit_lfsr << 6); } - NOISE_output = NOISE_LFSR & 1; + NOISE_output = (NOISE_LFSR & 1) > 0 ? 0 : 1; NOISE_output *= NOISE_vol_state; } } From 43f24357371e81a350b3c14ad614d6a8701dbdc1 Mon Sep 17 00:00:00 2001 From: "Dan B (Narry)" Date: Sun, 6 May 2018 16:10:08 -0400 Subject: [PATCH 200/339] Filter out forward slashes in FileSystemSafeName to fix games such as "Super Mario Bros. / Duck Hunt". Fixes #1088 --- BizHawk.Client.Common/PathManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BizHawk.Client.Common/PathManager.cs b/BizHawk.Client.Common/PathManager.cs index 51826e93f5..a84563484a 100644 --- a/BizHawk.Client.Common/PathManager.cs +++ b/BizHawk.Client.Common/PathManager.cs @@ -231,7 +231,8 @@ namespace BizHawk.Client.Common var filesystemSafeName = game.Name .Replace("|", "+") .Replace(":", " -") // adelikat - Path.GetFileName scraps everything to the left of a colon unfortunately, so we need this hack here - .Replace("\"", ""); // adelikat - Ivan Ironman Stewart's Super Off-Road has quotes in game name + .Replace("\"", "") // adelikat - Ivan Ironman Stewart's Super Off-Road has quotes in game name + .Replace("/", "+"); // Narry - Mario Bros / Duck hunt has a slash in the name which GetDirectoryName and GetFileName treat as if it were a folder // zero 06-nov-2015 - regarding the below, i changed my mind. for libretro i want subdirectories here. var filesystemDir = Path.GetDirectoryName(filesystemSafeName); From 37ace00fea49bca7761cb7b0cdaa8a830262f1fe Mon Sep 17 00:00:00 2001 From: Scepheo Date: Sun, 6 May 2018 23:05:50 +0200 Subject: [PATCH 201/339] Extract Lua file enable logic for toggling and reuse when loading for the first time. Fixes #1138. --- .../tools/Lua/LuaConsole.cs | 98 +++++++++---------- 1 file changed, 45 insertions(+), 53 deletions(-) diff --git a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs index 1b0994ffd4..5c03a82218 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs @@ -260,22 +260,8 @@ namespace BizHawk.Client.EmuHawk if (!Global.Config.DisableLuaScriptsOnLoad) { - try - { - LuaSandbox.Sandbox(null, () => - { - luaFile.Thread = LuaImp.SpawnCoroutine(pathToLoad); - LuaSandbox.CreateSandbox(luaFile.Thread, Path.GetDirectoryName(pathToLoad)); - luaFile.State = LuaFile.RunState.Running; - }, () => - { - luaFile.State = LuaFile.RunState.Disabled; - }); - } - catch (Exception e) - { - MessageBox.Show(e.ToString()); - } + luaFile.State = LuaFile.RunState.Running; + EnableLuaFile(luaFile); } else { @@ -815,46 +801,18 @@ namespace BizHawk.Client.EmuHawk private void ToggleScriptMenuItem_Click(object sender, EventArgs e) { var files = !SelectedFiles.Any() && Global.Config.ToggleAllIfNoneSelected ? LuaImp.ScriptList : SelectedFiles; - foreach (var item in files) + foreach (var file in files) { - item.Toggle(); + file.Toggle(); - if (item.Enabled && item.Thread == null) + if (file.Enabled && file.Thread == null) { - try - { - LuaSandbox.Sandbox(null, () => - { - string pathToLoad = Path.IsPathRooted(item.Path) - ? item.Path - : PathManager.MakeProgramRelativePath(item.Path); - - item.Thread = LuaImp.SpawnCoroutine(pathToLoad); - LuaSandbox.CreateSandbox(item.Thread, Path.GetDirectoryName(pathToLoad)); - }, () => - { - item.State = LuaFile.RunState.Disabled; - }); - - // Shenanigans - // We want any gui.text messages from a script to immediately update even when paused - GlobalWin.OSD.ClearGUIText(); - GlobalWin.Tools.UpdateToolsAfter(); - LuaImp.EndLuaDrawing(); - LuaImp.StartLuaDrawing(); - } - catch (IOException) - { - ConsoleLog("Unable to access file " + item.Path); - } - catch (Exception ex) - { - MessageBox.Show(ex.ToString()); - } + EnableLuaFile(file); } - else if (!item.Enabled && item.Thread != null) + + else if (!file.Enabled && file.Thread != null) { - LuaImp.CallExitEvent(item.Thread); + LuaImp.CallExitEvent(file.Thread); var items = SelectedItems.ToList(); foreach (var sitem in items) @@ -869,8 +827,8 @@ namespace BizHawk.Client.EmuHawk UpdateRegisteredFunctionsDialog(); } - LuaImp.CallExitEvent(item.Thread); - item.Stop(); + LuaImp.CallExitEvent(file.Thread); + file.Stop(); if (Global.Config.RemoveRegisteredFunctionsOnToggle) { LuaImp.RegisteredFunctions.ClearAll(); @@ -883,6 +841,40 @@ namespace BizHawk.Client.EmuHawk LuaListView.Refresh(); } + private void EnableLuaFile(LuaFile item) + { + try + { + LuaSandbox.Sandbox(null, () => + { + string pathToLoad = Path.IsPathRooted(item.Path) + ? item.Path + : PathManager.MakeProgramRelativePath(item.Path); + + item.Thread = LuaImp.SpawnCoroutine(pathToLoad); + LuaSandbox.CreateSandbox(item.Thread, Path.GetDirectoryName(pathToLoad)); + }, () => + { + item.State = LuaFile.RunState.Disabled; + }); + + // Shenanigans + // We want any gui.text messages from a script to immediately update even when paused + GlobalWin.OSD.ClearGUIText(); + GlobalWin.Tools.UpdateToolsAfter(); + LuaImp.EndLuaDrawing(); + LuaImp.StartLuaDrawing(); + } + catch (IOException) + { + ConsoleLog("Unable to access file " + item.Path); + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString()); + } + } + private void PauseScriptMenuItem_Click(object sender, EventArgs e) { SelectedFiles.ToList().ForEach(x => x.TogglePause()); From a21a11b70e9a58210d3ab109dae97e0ba4c0eef3 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 6 May 2018 21:29:25 -0400 Subject: [PATCH 202/339] GBCHawk: fix window timing to match console behaviour --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index fb3c2e976a..973b9b1fe7 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -705,6 +705,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // if the window starts at zero, we still do the first access to the BG // but then restart all over again at the window window_pre_render = true; + read_case = 4; } else { From 9c800cf133dbcdd5c2b355241a0e760c4c092689 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 8 May 2018 20:20:55 +0100 Subject: [PATCH 203/339] ZXHawk: Buzzer/Beeper implementation now use BlipBuffer - #1158 --- .../ZXSpectrum/ZXSpectrumAudioSettings.cs | 4 +- .../BizHawk.Emulation.Cores.csproj | 2 + .../Hardware/SoundOuput/AY38912.cs | 811 ++++++++++++++++++ .../Hardware/SoundOuput/AYChip.cs | 32 +- .../Hardware/SoundOuput/Beeper.cs | 232 +++++ .../Hardware/SoundOuput/Buzzer.cs | 9 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 10 +- .../SinclairSpectrum/Machine/ULABase.cs | 4 +- .../Machine/ZXSpectrum128K/ZX128.cs | 6 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 6 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 6 +- .../Machine/ZXSpectrum48K/ZX48.cs | 4 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 10 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 9 +- 14 files changed, 1087 insertions(+), 58 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs index 204d406d7f..0eb6e977cc 100644 --- a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs @@ -22,7 +22,7 @@ namespace BizHawk.Client.EmuHawk _settings = ((ZXSpectrum)Global.Emulator).GetSettings().Clone(); // AY panning config - var panTypes = Enum.GetNames(typeof(AYChip.AYPanConfig)); + var panTypes = Enum.GetNames(typeof(AY38912.AYPanConfig)); foreach (var val in panTypes) { panTypecomboBox1.Items.Add(val); @@ -51,7 +51,7 @@ namespace BizHawk.Client.EmuHawk if (changed) { - _settings.AYPanConfig = (AYChip.AYPanConfig)Enum.Parse(typeof(AYChip.AYPanConfig), panTypecomboBox1.SelectedItem.ToString()); + _settings.AYPanConfig = (AY38912.AYPanConfig)Enum.Parse(typeof(AY38912.AYPanConfig), panTypecomboBox1.SelectedItem.ToString()); _settings.TapeVolume = tapeVolumetrackBar.Value; _settings.EarVolume = earVolumetrackBar.Value; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 980cdafec2..75b1906e1a 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -259,6 +259,8 @@ + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs new file mode 100644 index 0000000000..dc5f101adf --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs @@ -0,0 +1,811 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// AY-3-8912 Emulated Device + /// + /// Based heavily on the YM-2149F / AY-3-8910 emulator used in Unreal Speccy + /// (Originally created under Public Domain license by SMT jan.2006) + /// + /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.cpp + /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.h + /// + public class AY38912 : IPSG + { + #region Device Fields + + /// + /// The emulated machine (passed in via constructor) + /// + private SpectrumBase _machine; + + private int _tStatesPerFrame; + private int _sampleRate; + private int _samplesPerFrame; + private int _tStatesPerSample; + private short[] _audioBuffer; + private int _audioBufferIndex; + private int _lastStateRendered; + + #endregion + + #region Construction & Initialization + + /// + /// Main constructor + /// + public AY38912(SpectrumBase machine) + { + _machine = machine; + } + + /// + /// Initialises the AY chip + /// + public void Init(int sampleRate, int tStatesPerFrame) + { + InitTiming(sampleRate, tStatesPerFrame); + UpdateVolume(); + Reset(); + } + + #endregion + + #region IPortIODevice + + public bool ReadPort(ushort port, ref int value) + { + if (port != 0xfffd) + { + // port read is not addressing this device + return false; + } + + value = PortRead(); + + return true; + } + + public bool WritePort(ushort port, int value) + { + if (port == 0xfffd) + { + // register select + SelectedRegister = value & 0x0f; + return true; + } + else if (port == 0xbffd) + { + // Update the audiobuffer based on the current CPU cycle + // (this process the previous data BEFORE writing to the currently selected register) + int d = (int)(_machine.CurrentFrameCycle); + BufferUpdate(d); + + // write to register + PortWrite(value); + return true; + } + return false; + } + + #endregion + + #region AY Implementation + + #region Public Properties + + /// + /// AY mixer panning configuration + /// + [Flags] + public enum AYPanConfig + { + MONO = 0, + ABC = 1, + ACB = 2, + BAC = 3, + BCA = 4, + CAB = 5, + CBA = 6, + } + + /// + /// The AY panning configuration + /// + public AYPanConfig PanningConfiguration + { + get + { + return _currentPanTab; + } + set + { + if (value != _currentPanTab) + { + _currentPanTab = value; + UpdateVolume(); + } + } + } + + /// + /// The AY chip output volume + /// (0 - 100) + /// + public int Volume + { + get + { + return _volume; + } + set + { + //value = Math.Max(0, value); + //value = Math.Max(100, value); + if (_volume == value) + { + return; + } + _volume = value; + UpdateVolume(); + } + } + + /// + /// The currently selected register + /// + public int SelectedRegister + { + get { return _activeRegister; } + set + { + _activeRegister = (byte)value; + } + } + + #endregion + + #region Public Methods + + /// + /// Resets the PSG + /// + public void Reset() + { + /* + _noiseVal = 0x0FFFF; + _outABC = 0; + _outNoiseABC = 0; + _counterNoise = 0; + _counterA = 0; + _counterB = 0; + _counterC = 0; + _EnvelopeCounterBend = 0; + + // clear all the registers + for (int i = 0; i < 14; i++) + { + SelectedRegister = i; + PortWrite(0); + } + + randomSeed = 1; + + // number of frames to update + var fr = (_audioBufferIndex * _tStatesPerFrame) / _audioBuffer.Length; + + // update the audio buffer + BufferUpdate(fr); + */ + } + + /// + /// Reads the value from the currently selected register + /// + /// + public int PortRead() + { + return _registers[_activeRegister]; + } + + /// + /// Writes to the currently selected register + /// + /// + public void PortWrite(int value) + { + if (_activeRegister >= 0x10) + return; + + byte val = (byte)value; + + if (((1 << _activeRegister) & ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 13))) != 0) + val &= 0x0F; + + if (((1 << _activeRegister) & ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10))) != 0) + val &= 0x1F; + + if (_activeRegister != 13 && _registers[_activeRegister] == val) + return; + + _registers[_activeRegister] = val; + + switch (_activeRegister) + { + // Channel A (Combined Pitch) + // (not written to directly) + case 0: + case 1: + _dividerA = _registers[AY_A_FINE] | (_registers[AY_A_COARSE] << 8); + break; + // Channel B (Combined Pitch) + // (not written to directly) + case 2: + case 3: + _dividerB = _registers[AY_B_FINE] | (_registers[AY_B_COARSE] << 8); + break; + // Channel C (Combined Pitch) + // (not written to directly) + case 4: + case 5: + _dividerC = _registers[AY_C_FINE] | (_registers[AY_C_COARSE] << 8); + break; + // Noise Pitch + case 6: + _dividerN = val * 2; + break; + // Mixer + case 7: + _bit0 = 0 - ((val >> 0) & 1); + _bit1 = 0 - ((val >> 1) & 1); + _bit2 = 0 - ((val >> 2) & 1); + _bit3 = 0 - ((val >> 3) & 1); + _bit4 = 0 - ((val >> 4) & 1); + _bit5 = 0 - ((val >> 5) & 1); + break; + // Channel Volumes + case 8: + _eMaskA = (val & 0x10) != 0 ? -1 : 0; + _vA = ((val & 0x0F) * 2 + 1) & ~_eMaskA; + break; + case 9: + _eMaskB = (val & 0x10) != 0 ? -1 : 0; + _vB = ((val & 0x0F) * 2 + 1) & ~_eMaskB; + break; + case 10: + _eMaskC = (val & 0x10) != 0 ? -1 : 0; + _vC = ((val & 0x0F) * 2 + 1) & ~_eMaskC; + break; + // Envelope (Combined Duration) + // (not written to directly) + case 11: + case 12: + _dividerE = _registers[AY_E_FINE] | (_registers[AY_E_COARSE] << 8); + break; + // Envelope Shape + case 13: + // reset the envelope counter + _countE = 0; + + if ((_registers[AY_E_SHAPE] & 4) != 0) + { + // attack + _eState = 0; + _eDirection = 1; + } + else + { + // decay + _eState = 31; + _eDirection = -1; + } + break; + case 14: + // IO Port - not implemented + break; + } + } + + /// + /// Start of frame + /// + public void StartFrame() + { + _audioBufferIndex = 0; + BufferUpdate(0); + } + + /// + /// End of frame + /// + public void EndFrame() + { + BufferUpdate(_tStatesPerFrame); + } + + /// + /// Updates the audiobuffer based on the current frame t-state + /// + /// + public void UpdateSound(int frameCycle) + { + BufferUpdate(frameCycle); + } + + #endregion + + #region Private Fields + + /// + /// Register indicies + /// + private const int AY_A_FINE = 0; + private const int AY_A_COARSE = 1; + private const int AY_B_FINE = 2; + private const int AY_B_COARSE = 3; + private const int AY_C_FINE = 4; + private const int AY_C_COARSE = 5; + private const int AY_NOISEPITCH = 6; + private const int AY_MIXER = 7; + private const int AY_A_VOL = 8; + private const int AY_B_VOL = 9; + private const int AY_C_VOL = 10; + private const int AY_E_FINE = 11; + private const int AY_E_COARSE = 12; + private const int AY_E_SHAPE = 13; + private const int AY_PORT_A = 14; + private const int AY_PORT_B = 15; + + /// + /// The register array + /* + The AY-3-8910/8912 contains 16 internal registers as follows: + + Register Function Range + 0 Channel A fine pitch 8-bit (0-255) + 1 Channel A course pitch 4-bit (0-15) + 2 Channel B fine pitch 8-bit (0-255) + 3 Channel B course pitch 4-bit (0-15) + 4 Channel C fine pitch 8-bit (0-255) + 5 Channel C course pitch 4-bit (0-15) + 6 Noise pitch 5-bit (0-31) + 7 Mixer 8-bit (see below) + 8 Channel A volume 4-bit (0-15, see below) + 9 Channel B volume 4-bit (0-15, see below) + 10 Channel C volume 4-bit (0-15, see below) + 11 Envelope fine duration 8-bit (0-255) + 12 Envelope course duration 8-bit (0-255) + 13 Envelope shape 4-bit (0-15) + 14 I/O port A 8-bit (0-255) + 15 I/O port B 8-bit (0-255) (Not present on the AY-3-8912) + + * The volume registers (8, 9 and 10) contain a 4-bit setting but if bit 5 is set then that channel uses the + envelope defined by register 13 and ignores its volume setting. + * The mixer (register 7) is made up of the following bits (low=enabled): + + Bit: 7 6 5 4 3 2 1 0 + Register: I/O I/O Noise Noise Noise Tone Tone Tone + Channel: B A C B A C B A + + The AY-3-8912 ignores bit 7 of this register. + */ + /// + private int[] _registers = new int[16]; + + /// + /// The currently selected register + /// + private byte _activeRegister; + + /// + /// The frequency of the AY chip + /// + private static int _chipFrequency = 1773400; + + /// + /// The rendering resolution of the chip + /// + private double _resolution = 50D * 8D / _chipFrequency; + + /// + /// Channel generator state + /// + private int _bitA; + private int _bitB; + private int _bitC; + + /// + /// Envelope state + /// + private int _eState; + + /// + /// Envelope direction + /// + private int _eDirection; + + /// + /// Noise seed + /// + private int _noiseSeed; + + /// + /// Mixer state + /// + private int _bit0; + private int _bit1; + private int _bit2; + private int _bit3; + private int _bit4; + private int _bit5; + + /// + /// Noise generator state + /// + private int _bitN; + + /// + /// Envelope masks + /// + private int _eMaskA; + private int _eMaskB; + private int _eMaskC; + + /// + /// Amplitudes + /// + private int _vA; + private int _vB; + private int _vC; + + /// + /// Channel gen counters + /// + private int _countA; + private int _countB; + private int _countC; + + /// + /// Envelope gen counter + /// + private int _countE; + + /// + /// Noise gen counter + /// + private int _countN; + + /// + /// Channel gen dividers + /// + private int _dividerA; + private int _dividerB; + private int _dividerC; + + /// + /// Envelope gen divider + /// + private int _dividerE; + + /// + /// Noise gen divider + /// + private int _dividerN; + + /// + /// Panning table list + /// + private static List PanTabs = new List + { + // MONO + new uint[] { 50,50, 50,50, 50,50 }, + // ABC + new uint[] { 100,10, 66,66, 10,100 }, + // ACB + new uint[] { 100,10, 10,100, 66,66 }, + // BAC + new uint[] { 66,66, 100,10, 10,100 }, + // BCA + new uint[] { 10,100, 100,10, 66,66 }, + // CAB + new uint[] { 66,66, 10,100, 100,10 }, + // CBA + new uint[] { 10,100, 66,66, 100,10 } + }; + + /// + /// The currently selected panning configuration + /// + private AYPanConfig _currentPanTab = AYPanConfig.ABC; + + /// + /// The current volume + /// + private int _volume = 75; + + /// + /// Volume tables state + /// + private uint[][] _volumeTables; + + /// + /// Volume table to be used + /// + private static uint[] AYVolumes = new uint[] + { + 0x0000,0x0000,0x0340,0x0340,0x04C0,0x04C0,0x06F2,0x06F2, + 0x0A44,0x0A44,0x0F13,0x0F13,0x1510,0x1510,0x227E,0x227E, + 0x289F,0x289F,0x414E,0x414E,0x5B21,0x5B21,0x7258,0x7258, + 0x905E,0x905E,0xB550,0xB550,0xD7A0,0xD7A0,0xFFFF,0xFFFF, + }; + + #endregion + + #region Private Methods + + /// + /// Forces an update of the volume tables + /// + private void UpdateVolume() + { + int upperFloor = 40000; + var inc = (0xFFFF - upperFloor) / 100; + + var vol = inc * _volume; // ((ulong)0xFFFF * (ulong)_volume / 100UL) - 20000 ; + _volumeTables = new uint[6][]; + + // parent array + for (int j = 0; j < _volumeTables.Length; j++) + { + _volumeTables[j] = new uint[32]; + + // child array + for (int i = 0; i < _volumeTables[j].Length; i++) + { + _volumeTables[j][i] = (uint)( + (PanTabs[(int)_currentPanTab][j] * AYVolumes[i] * vol) / + (3 * 65535 * 100)); + } + } + } + + private int mult_const; + + /// + /// Initializes timing information for the frame + /// + /// + /// + private void InitTiming(int sampleRate, int frameTactCount) + { + _sampleRate = sampleRate; + _tStatesPerFrame = frameTactCount; + _samplesPerFrame = 882; + + _tStatesPerSample = 79; //(int)Math.Round(((double)_tStatesPerFrame * 50D) / + //(16D * (double)_sampleRate), + //MidpointRounding.AwayFromZero); + + //_samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + _audioBuffer = new short[_samplesPerFrame * 2]; //[_sampleRate / 50]; + _audioBufferIndex = 0; + + mult_const = ((_chipFrequency / 8) << 14) / _machine.ULADevice.ClockSpeed; + + var aytickspercputick = (double)_machine.ULADevice.ClockSpeed / (double)_chipFrequency; + int ayCyclesPerSample = (int)((double)_tStatesPerSample * (double)aytickspercputick); + } + + /// + /// Updates the audiobuffer based on the current frame t-state + /// + /// + private void BufferUpdate(int cycle) + { + if (cycle > _tStatesPerFrame) + { + // we are outside of the frame - just process the last value + cycle = _tStatesPerFrame; + } + + // get the current length of the audiobuffer + int bufferLength = _samplesPerFrame; // _audioBuffer.Length; + + int toEnd = ((bufferLength * cycle) / _tStatesPerFrame); + + // loop through the number of samples we need to render + while (_audioBufferIndex < toEnd) + { + // run the AY chip processing at the correct resolution + for (int i = 0; i < _tStatesPerSample / 14; i++) + { + if (++_countA >= _dividerA) + { + _countA = 0; + _bitA ^= -1; + } + + if (++_countB >= _dividerB) + { + _countB = 0; + _bitB ^= -1; + } + + if (++_countC >= _dividerC) + { + _countC = 0; + _bitC ^= -1; + } + + if (++_countN >= _dividerN) + { + _countN = 0; + _noiseSeed = (_noiseSeed * 2 + 1) ^ (((_noiseSeed >> 16) ^ (_noiseSeed >> 13)) & 1); + _bitN = 0 - ((_noiseSeed >> 16) & 1); + } + + if (++_countE >= _dividerE) + { + _countE = 0; + _eState += +_eDirection; + + if ((_eState & ~31) != 0) + { + var mask = (1 << _registers[AY_E_SHAPE]); + + if ((mask & ((1 << 0) | (1 << 1) | (1 << 2) | + (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | + (1 << 7) | (1 << 9) | (1 << 15))) != 0) + { + _eState = _eDirection = 0; + } + else if ((mask & ((1 << 8) | (1 << 12))) != 0) + { + _eState &= 31; + } + else if ((mask & ((1 << 10) | (1 << 14))) != 0) + { + _eDirection = -_eDirection; + _eState += _eDirection; + } + else + { + // 11,13 + _eState = 31; + _eDirection = 0; + } + } + } + } + + // mix the sample + var mixA = ((_eMaskA & _eState) | _vA) & ((_bitA | _bit0) & (_bitN | _bit3)); + var mixB = ((_eMaskB & _eState) | _vB) & ((_bitB | _bit1) & (_bitN | _bit4)); + var mixC = ((_eMaskC & _eState) | _vC) & ((_bitC | _bit2) & (_bitN | _bit5)); + + var l = _volumeTables[0][mixA]; + var r = _volumeTables[1][mixA]; + + l += _volumeTables[2][mixB]; + r += _volumeTables[3][mixB]; + l += _volumeTables[4][mixC]; + r += _volumeTables[5][mixC]; + + _audioBuffer[_audioBufferIndex * 2] = (short)l; + _audioBuffer[(_audioBufferIndex * 2) + 1] = (short)r; + + _audioBufferIndex++; + } + + _lastStateRendered = cycle; + } + + #endregion + + #endregion + + #region ISoundProvider + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + _audioBuffer = new short[_samplesPerFrame * 2]; + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + nsamp = _samplesPerFrame; + samples = _audioBuffer; + DiscardSamples(); + } + + #endregion + + #region State Serialization + + public int nullDump = 0; + + /// + /// State serialization + /// + /// + public void SyncState(Serializer ser) + { + ser.BeginSection("PSG-AY"); + + ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); + ser.Sync("_sampleRate", ref _sampleRate); + ser.Sync("_samplesPerFrame", ref _samplesPerFrame); + ser.Sync("_tStatesPerSample", ref _tStatesPerSample); + ser.Sync("_audioBufferIndex", ref _audioBufferIndex); + ser.Sync("_audioBuffer", ref _audioBuffer, false); + + ser.Sync("_registers", ref _registers, false); + ser.Sync("_activeRegister", ref _activeRegister); + ser.Sync("_bitA", ref _bitA); + ser.Sync("_bitB", ref _bitB); + ser.Sync("_bitC", ref _bitC); + ser.Sync("_eState", ref _eState); + ser.Sync("_eDirection", ref _eDirection); + ser.Sync("_noiseSeed", ref _noiseSeed); + ser.Sync("_bit0", ref _bit0); + ser.Sync("_bit1", ref _bit1); + ser.Sync("_bit2", ref _bit2); + ser.Sync("_bit3", ref _bit3); + ser.Sync("_bit4", ref _bit4); + ser.Sync("_bit5", ref _bit5); + ser.Sync("_bitN", ref _bitN); + ser.Sync("_eMaskA", ref _eMaskA); + ser.Sync("_eMaskB", ref _eMaskB); + ser.Sync("_eMaskC", ref _eMaskC); + ser.Sync("_vA", ref _vA); + ser.Sync("_vB", ref _vB); + ser.Sync("_vC", ref _vC); + ser.Sync("_countA", ref _countA); + ser.Sync("_countB", ref _countB); + ser.Sync("_countC", ref _countC); + ser.Sync("_countE", ref _countE); + ser.Sync("_countN", ref _countN); + ser.Sync("_dividerA", ref _dividerA); + ser.Sync("_dividerB", ref _dividerB); + ser.Sync("_dividerC", ref _dividerC); + ser.Sync("_dividerE", ref _dividerE); + ser.Sync("_dividerN", ref _dividerN); + ser.SyncEnum("_currentPanTab", ref _currentPanTab); + ser.Sync("_volume", ref nullDump); + + for (int i = 0; i < 6; i++) + { + ser.Sync("volTable" + i, ref _volumeTables[i], false); + } + + if (ser.IsReader) + _volume = _machine.Spectrum.Settings.AYVolume; + + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs index 161b93176b..cac7b80229 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* /// /// AY-3-8912 Emulated Device /// @@ -177,31 +178,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void Reset() { - /* - _noiseVal = 0x0FFFF; - _outABC = 0; - _outNoiseABC = 0; - _counterNoise = 0; - _counterA = 0; - _counterB = 0; - _counterC = 0; - _EnvelopeCounterBend = 0; - - // clear all the registers - for (int i = 0; i < 14; i++) - { - SelectedRegister = i; - PortWrite(0); - } - - randomSeed = 1; - - // number of frames to update - var fr = (_audioBufferIndex * _tStatesPerFrame) / _audioBuffer.Length; - - // update the audio buffer - BufferUpdate(fr); - */ + } /// @@ -394,6 +371,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum The AY-3-8912 ignores bit 7 of this register. */ + /* /// private int[] _registers = new int[16]; @@ -585,12 +563,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _sampleRate = sampleRate; _tStatesPerFrame = frameTactCount; + _samplesPerFrame = 882; _tStatesPerSample = 79; //(int)Math.Round(((double)_tStatesPerFrame * 50D) / //(16D * (double)_sampleRate), //MidpointRounding.AwayFromZero); - _samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + //_samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; _audioBuffer = new short[_samplesPerFrame * 2]; //[_sampleRate / 50]; _audioBufferIndex = 0; @@ -805,4 +784,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs new file mode 100644 index 0000000000..6607e79e1e --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs @@ -0,0 +1,232 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Logical Beeper class + /// Represents the piezoelectric buzzer used in the Spectrum to produce sound + /// The beeper is controlled by rapidly toggling bit 4 of port &FE + /// + /// It is instantiated twice, once for speccy beeper output, and once tape buzzer emulation + /// + /// This implementation uses BlipBuffer and should *always* output at 44100 with 882 samples per frame + /// (so that it can be mixed easily further down the line) + /// + public class Beeper : ISoundProvider, IBeeperDevice + { + #region Fields and Properties + + /// + /// Sample Rate + /// This usually has to be 44100 for ISoundProvider + /// + private int _sampleRate; + public int SampleRate + { + get { return _sampleRate; } + set { _sampleRate = value; } + } + + /// + /// Buzzer volume + /// Accepts an int 0-100 value + /// + private int _volume; + public int Volume + { + get + { + return VolumeConverterOut(_volume); + } + set + { + var newVol = VolumeConverterIn(value); + if (newVol != _volume) + blip.Clear(); + _volume = VolumeConverterIn(value); + } + } + + /// + /// The last used volume (used to modify blipbuffer delta values) + /// + private int lastVolume; + + /// + /// The number of cpu cycles per frame + /// + private long _tStatesPerFrame; + + /// + /// The parent emulated machine + /// + private SpectrumBase _machine; + + /// + /// The last pulse + /// + private bool LastPulse; + + /// + /// The last T-State (cpu cycle) that the last pulse was received + /// + private long LastPulseTState; + + /// + /// Device blipbuffer + /// + private readonly BlipBuffer blip = new BlipBuffer(882); + + #endregion + + #region Private Methods + + /// + /// Takes an int 0-100 and returns the relevant short volume to output + /// + /// + /// + private int VolumeConverterIn(int vol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + return vol * increment; + } + + /// + /// Takes an short volume and returns the relevant int value 0-100 + /// + /// + /// + private int VolumeConverterOut(int shortvol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + if (shortvol > maxLimit) + shortvol = maxLimit; + + return shortvol / increment; + } + + #endregion + + #region Construction & Initialisation + + public Beeper(SpectrumBase machine) + { + _machine = machine; + } + + /// + /// Initialises the beeper + /// + public void Init(int sampleRate, int tStatesPerFrame) + { + blip.SetRates((tStatesPerFrame * 50), sampleRate); + _sampleRate = sampleRate; + _tStatesPerFrame = tStatesPerFrame; + } + + #endregion + + #region IBeeperDevice + + /// + /// Processes an incoming pulse value and adds it to the blipbuffer + /// + /// + public void ProcessPulseValue(bool pulse) + { + if (!_machine._renderSound) + return; + + if (LastPulse == pulse) + { + // no change + blip.AddDelta((uint)_machine.CurrentFrameCycle, 0); + } + + else + { + if (pulse) + blip.AddDelta((uint)_machine.CurrentFrameCycle, (short)(_volume)); + else + blip.AddDelta((uint)_machine.CurrentFrameCycle, -(short)(_volume)); + + lastVolume = _volume; + } + + LastPulse = pulse; + } + + + public void StartFrame() + { + } + + public void EndFrame() + { + } + + #endregion + + + #region ISoundProvider + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + blip.Clear(); + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + blip.EndFrame((uint)_tStatesPerFrame); + nsamp = blip.SamplesAvailable(); + samples = new short[nsamp * 2]; + blip.ReadSamples(samples, nsamp, true); + for (int i = 0; i < nsamp * 2; i += 2) + { + samples[i + 1] = samples[i]; + } + } + + #endregion + + #region State Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("Buzzer"); + ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); + ser.Sync("_sampleRate", ref _sampleRate); + ser.Sync("LastPulse", ref LastPulse); + ser.Sync("LastPulseTState", ref LastPulseTState); + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs index 1f6b9ad20c..7a12444892 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* /// /// Represents the piezoelectric buzzer used in the Spectrum to produce sound /// The beeper is controlled by rapidly toggling bit 4 of port &FE @@ -146,8 +147,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _sampleRate = sampleRate; _tStatesPerFrame = tStatesPerFrame; - _tStatesPerSample = 79; - _samplesPerFrame = (int)_tStatesPerFrame / _tStatesPerSample; + _tStatesPerSample = 99; // 79; + _samplesPerFrame = 705; // 882; // (int)_tStatesPerFrame / _tStatesPerSample; Pulses = new List(1000); } @@ -297,7 +298,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } samples = stereoBuffer; - nsamp = _samplesPerFrame; // soundBufferContains; + nsamp = soundBufferContains; // _samplesPerFrame; // soundBufferContains; } #endregion @@ -319,4 +320,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion } + + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index f3ff00fd09..7ba831e9a7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -152,8 +152,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_renderSound) { - BuzzerDevice.StartFrame(); - TapeBuzzer.StartFrame(); + //BuzzerDevice.StartFrame(); + //TapeBuzzer.StartFrame(); if (AYDevice != null) AYDevice.StartFrame(); @@ -183,8 +183,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_renderSound) { - BuzzerDevice.EndFrame(); - TapeBuzzer.EndFrame(); + //BuzzerDevice.EndFrame(); + //TapeBuzzer.EndFrame(); } if (AYDevice != null) @@ -365,7 +365,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (AYDevice != null) { AYDevice.SyncState(ser); - ((AYChip)AYDevice as AYChip).PanningConfiguration = Spectrum.Settings.AYPanConfig; + ((AY38912)AYDevice as AY38912).PanningConfiguration = Spectrum.Settings.AYPanConfig; } ser.Sync("tapeMediaIndex", ref tapeMediaIndex); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 79479b4789..7f479eb635 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -552,13 +552,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int VsyncNumerator { - get { return ClockSpeed; } + get { return ClockSpeed * 50; }// ClockSpeed; } set { } } public int VsyncDenominator { - get { return FrameLength; } + get { return ClockSpeed; }//FrameLength; } } public int[] GetVideoBuffer() diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 3ef13c4559..74b6744893 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -28,13 +28,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new ULA128(this); - BuzzerDevice = new Buzzer(this); + BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - TapeBuzzer = new Buzzer(this); + TapeBuzzer = new Beeper(this); TapeBuzzer.Init(44100, ULADevice.FrameLength); - AYDevice = new AYChip(this); + AYDevice = new AY38912(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index ed9d65282c..a6eec74beb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -28,13 +28,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new ULAPlus2a(this); - BuzzerDevice = new Buzzer(this); + BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - TapeBuzzer = new Buzzer(this); + TapeBuzzer = new Beeper(this); TapeBuzzer.Init(44100, ULADevice.FrameLength); - AYDevice = new AYChip(this); + AYDevice = new AY38912(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 9cb24e8e5a..5b000e543e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -28,13 +28,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new ULAPlus3(this); - BuzzerDevice = new Buzzer(this); + BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - TapeBuzzer = new Buzzer(this); + TapeBuzzer = new Beeper(this); TapeBuzzer.Init(44100, ULADevice.FrameLength); - AYDevice = new AYChip(this); + AYDevice = new AY38912(this); AYDevice.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index 98968ff315..db8105555d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -23,10 +23,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ULADevice = new ULA48(this); - BuzzerDevice = new Buzzer(this); + BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); - TapeBuzzer = new Buzzer(this); + TapeBuzzer = new Beeper(this); TapeBuzzer.Init(44100, ULADevice.FrameLength); KeyboardDevice = new StandardKeyboard(this); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 6e954e3430..0c76d3e863 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -28,16 +28,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // restore user settings to devices if (_machine != null && _machine.AYDevice != null) { - ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = o.AYPanConfig; + ((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = o.AYPanConfig; _machine.AYDevice.Volume = o.AYVolume; } if (_machine != null && _machine.BuzzerDevice != null) { - ((Buzzer)_machine.BuzzerDevice as Buzzer).Volume = o.EarVolume; + ((Beeper)_machine.BuzzerDevice as Beeper).Volume = o.EarVolume; } if (_machine != null && _machine.TapeBuzzer != null) { - ((Buzzer)_machine.TapeBuzzer as Buzzer).Volume = o.TapeVolume; + ((Beeper)_machine.TapeBuzzer as Beeper).Volume = o.TapeVolume; } Settings = o; @@ -56,8 +56,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { [DisplayName("AY-3-8912 Panning Config")] [Description("Set the PSG panning configuration.\nThe chip has 3 audio channels that can be outputed in different configurations")] - [DefaultValue(AYChip.AYPanConfig.ABC)] - public AYChip.AYPanConfig AYPanConfig { get; set; } + [DefaultValue(AY38912.AYPanConfig.ABC)] + public AY38912.AYPanConfig AYPanConfig { get; set; } [DisplayName("Core OSD Message Verbosity")] [Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")] diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index ea4c7df76d..01b4c6d4fb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -103,23 +103,24 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SoundMixer.AddSource(_machine.AYDevice); // set audio device settings - if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AYChip)) + if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912)) { - ((AYChip)_machine.AYDevice as AYChip).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; + ((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; _machine.AYDevice.Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYVolume; } if (_machine.BuzzerDevice != null) { - ((Buzzer)_machine.BuzzerDevice as Buzzer).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + ((Beeper)_machine.BuzzerDevice as Beeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; } if (_machine.TapeBuzzer != null) { - ((Buzzer)_machine.TapeBuzzer as Buzzer).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; + ((Beeper)_machine.TapeBuzzer as Beeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; } ser.Register(SoundMixer); + //ser.Register < ISoundProvider>(((ISoundProvider)_machine.BuzzerDevice)); HardReset(); From 45c37ddc47009e97b2a8f652169ca5ba97556f62 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 8 May 2018 20:37:47 +0100 Subject: [PATCH 204/339] ZXHawk: Removed IO port contention for now (this fixes the slow beeper music problem). Will re-implement properly later - #1158 --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs | 2 ++ .../Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 2 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 3b53dd28e3..13e5d5ec5c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -52,6 +52,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void ContendPortAddress(ushort addr) { + return; + /* 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: 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 4568de663d..64ec430ef0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -287,7 +287,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPortAddress(ushort addr) { - CPU.TotalExecutedCycles += 4; + //CPU.TotalExecutedCycles += 4; } } } 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 ec718c8343..5aed81dd52 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -220,7 +220,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPortAddress(ushort addr) { - CPU.TotalExecutedCycles += 4; + //CPU.TotalExecutedCycles += 4; } } } From a4942f0fb81ee28bea925756f56e602bbe6747e1 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 8 May 2018 21:29:01 +0100 Subject: [PATCH 205/339] ZXHawk: block index is now set to 0 when switching tapes - #1158 --- .../SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index ebe6733f2d..abaa462406 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -331,6 +331,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum try { tzxSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; return; } catch (Exception ex) @@ -347,6 +349,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum try { tapSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; return; } catch (Exception ex) From f79e2853c0977935963ed9b2d1df64d882f53258 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 8 May 2018 21:57:19 +0100 Subject: [PATCH 206/339] ZXHawk: Added to PlatformFrameRates.cs --- BizHawk.Client.Common/movie/PlatformFrameRates.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/BizHawk.Client.Common/movie/PlatformFrameRates.cs b/BizHawk.Client.Common/movie/PlatformFrameRates.cs index 63fa342b55..30b5e63853 100644 --- a/BizHawk.Client.Common/movie/PlatformFrameRates.cs +++ b/BizHawk.Client.Common/movie/PlatformFrameRates.cs @@ -6,7 +6,7 @@ namespace BizHawk.Client.Common public class PlatformFrameRates { // these are political numbers, designed to be in accord with tasvideos.org tradition. theyre not necessarily mathematical factualities (although they may be in some cases) - // it would be nice if we could turn this into a rational expression natively, and also, to write some comments about the derivation and ideal valees (since this seems to be where theyre all collected) + // it would be nice if we could turn this into a rational expression natively, and also, to write some comments about the derivation and ideal values (since this seems to be where theyre all collected) // are we collecting them anywhere else? for avi-writing code perhaps? // just some constants, according to specs @@ -59,7 +59,9 @@ namespace BizHawk.Client.Common ["C64_NTSC"] = NTSCCarrier * 2 / 7 / 263 / 65, ["C64_NTSC_OLD"] = NTSCCarrier * 2 / 7 / 262 / 64, ["C64_DREAN"] = PALNCarrier * 2 / 7 / 312 / 65, - ["INTV"] = 59.92 + ["INTV"] = 59.92, + + ["ZXSpectrum_PAL"] = 50 // according to ryphecha, using // clocks[2] = { 53.693182e06, 53.203425e06 }; //ntsc console, pal console From a679a8a7f2348ae5dc06a11715f3ec82c9b2dc68 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 8 May 2018 22:01:10 +0100 Subject: [PATCH 207/339] ZXHawk: platformrates - more accuratey --- BizHawk.Client.Common/movie/PlatformFrameRates.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Client.Common/movie/PlatformFrameRates.cs b/BizHawk.Client.Common/movie/PlatformFrameRates.cs index 30b5e63853..40ad9b22ff 100644 --- a/BizHawk.Client.Common/movie/PlatformFrameRates.cs +++ b/BizHawk.Client.Common/movie/PlatformFrameRates.cs @@ -61,7 +61,7 @@ namespace BizHawk.Client.Common ["C64_DREAN"] = PALNCarrier * 2 / 7 / 312 / 65, ["INTV"] = 59.92, - ["ZXSpectrum_PAL"] = 50 + ["ZXSpectrum_PAL"] = 50.080128205 // according to ryphecha, using // clocks[2] = { 53.693182e06, 53.203425e06 }; //ntsc console, pal console From 828dbba1a5501707db2d7236436eab408818d050 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 8 May 2018 21:47:35 -0400 Subject: [PATCH 208/339] GBHawk: Rework CPU to poll INT at the correct time. --- .../CPUs/LR35902/Interrupts.cs | 16 +++--- .../CPUs/LR35902/LR35902.cs | 6 +- .../CPUs/LR35902/Tables_Direct.cs | 56 +++++++++---------- .../CPUs/LR35902/Tables_Indirect.cs | 44 +++++++-------- .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 2 +- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 10 ++-- .../Consoles/Nintendo/GBHawk/Timer.cs | 4 ++ 7 files changed, 72 insertions(+), 66 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index e20cbcd90e..51c926aec8 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -25,10 +25,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, - IDLE, - IDLE, DEC16, SPl, SPh, IDLE, WR, SPl, SPh, PCh, @@ -40,6 +36,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, + IDLE, + IDLE, + IDLE, + IDLE, TR, PCl, W, ASGN, PCh, 0, IDLE, @@ -50,10 +50,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, - IDLE, - IDLE, DEC16, SPl, SPh, IDLE, WR, SPl, SPh, PCh, @@ -72,6 +68,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, + IDLE, + IDLE, + IDLE, + IDLE, OP }; } diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index c4b39bfeb5..c2f18c8f6e 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ResetInterrupts(); TotalExecutedCycles = 8; stop_check = false; - cur_instr = new ushort[] { OP }; + cur_instr = new ushort[] { IDLE, IDLE, HALT_CHK, OP }; } // Memory Access @@ -127,7 +127,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } } - if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + if (I_use && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; @@ -501,6 +501,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { RegPC--; Halt_bug_3 = true; + Console.WriteLine("Halt_bug_3"); + Console.WriteLine(totalExecutedCycles); } Halt_bug_2 = false; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index eb3346900f..b7dd1ccb0e 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -25,7 +25,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 INC16, src_l, src_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -39,7 +39,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 DEC16, src_l, src_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -52,7 +52,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ADD16, dest_l, dest_h, src_l, src_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -61,7 +61,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {operation, dest, src, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -134,7 +134,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ASGN, Z, 0, IDLE, ADDS, PCl, PCh, W, Z, - IDLE, + HALT_CHK, OP }; } else @@ -146,7 +146,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, + HALT_CHK, OP }; } } @@ -170,7 +170,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 TR, PCl, W, IDLE, TR, PCh, Z, - IDLE, + HALT_CHK, OP }; } else @@ -186,7 +186,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, + HALT_CHK, OP }; } } @@ -208,7 +208,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -229,7 +229,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 EI_RETI, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -257,7 +257,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } else @@ -269,7 +269,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } } @@ -298,10 +298,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, DEC16, SPl, SPh, - WR, SPl, SPh, PCl, - IDLE, + WR, SPl, SPh, PCl, TR, PCl, W, TR, PCh, Z, + HALT_CHK, OP }; } else @@ -317,7 +317,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, + HALT_CHK, OP }; } } @@ -327,7 +327,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {operation, src, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -336,7 +336,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {operation, bit, src, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -357,7 +357,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 WR, SPl, SPh, src_l, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -379,7 +379,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, src_h, SPl, SPh, IDLE, INC16, SPl, SPh, - IDLE, + HALT_CHK, OP }; } else @@ -395,7 +395,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, src_h, SPl, SPh, IDLE, INC16, SPl, SPh, - IDLE, + HALT_CHK, OP }; } } @@ -415,9 +415,9 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, WR, SPl, SPh, PCl, - IDLE, ASGN, PCh, 0, ASGN, PCl, n, + HALT_CHK, OP }; } @@ -435,7 +435,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {DI, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -444,7 +444,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {EI, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -452,8 +452,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { cur_instr = new ushort[] {TR, PCl, L, - IDLE, TR, PCh, H, + HALT_CHK, OP }; } @@ -474,7 +474,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ADDS, SPl, SPh, W, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -487,7 +487,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 TR, SPl, L, IDLE, TR, SPh, H, - IDLE, + HALT_CHK, OP }; } @@ -500,11 +500,11 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, W, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, TR, H, SPh, TR, L, SPl, ASGN, Z, 0, ADDS, L, H, W, Z, + HALT_CHK, OP }; } diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs index 79d2e67341..c1f68bc4f1 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs @@ -14,8 +14,8 @@ IDLE, WR, src_l, src_h, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -32,7 +32,7 @@ WR, src_l, src_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -45,7 +45,7 @@ RD, Z, src_l, src_h, IDLE, operation, bit, Z, - IDLE, + HALT_CHK, OP }; } @@ -56,9 +56,9 @@ IDLE, IDLE, RD, Z, src_l, src_h, - IDLE, operation, dest, Z, INC16, src_l, src_h, + HALT_CHK, OP }; } @@ -71,7 +71,7 @@ RD, Z, src_l, src_h, IDLE, operation, dest, Z, - IDLE, + HALT_CHK, OP }; } @@ -96,7 +96,7 @@ WR, W, Z, dest_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -109,7 +109,7 @@ WR, dest_l, dest_h, src, IDLE, INC16, dest_l, dest_h, - IDLE, + HALT_CHK, OP }; } @@ -122,7 +122,7 @@ WR, dest_l, dest_h, src, IDLE, DEC16, dest_l, dest_h, - IDLE, + HALT_CHK, OP }; } @@ -134,8 +134,8 @@ IDLE, WR, dest_l, dest_h, src, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -152,7 +152,7 @@ WR, dest_l, dest_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -165,7 +165,7 @@ RD, dest, src_l, src_h, IDLE, INC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -178,7 +178,7 @@ RD, dest, src_l, src_h, IDLE, INC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -191,7 +191,7 @@ RD, dest, src_l, src_h, IDLE, DEC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -208,7 +208,7 @@ RD, dest_h, src_l, src_h, IDLE, INC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -225,7 +225,7 @@ WR, src_l, src_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -242,7 +242,7 @@ WR, src_l, src_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -259,8 +259,8 @@ ASGN, Z , 0xFF, RD, dest, W, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -277,7 +277,7 @@ WR, W, Z, src, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -289,8 +289,8 @@ ASGN, Z , 0xFF, RD, dest, C, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -302,8 +302,8 @@ ASGN, Z , 0xFF, WR, C, Z, src, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -323,8 +323,8 @@ IDLE, RD, dest, W, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -345,7 +345,7 @@ WR, W, Z, src, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index 7370214a3b..c7c5068f87 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -124,7 +124,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // These things all tick twice as fast in GBC double speed mode ppu.DMA_tick(); timer.tick_1(); - serialport.serial_transfer_tick(); + serialport.serial_transfer_tick(); cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); timer.tick_2(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 00eb0d539e..b627310025 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -335,7 +335,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (cycle >= 452) { - OAM_access_read = false; + if (LY != 144) { OAM_access_read = false; } OAM_scan(cycle - 452); } } @@ -924,14 +924,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (LCDC.Bit(2)) { - y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); y = 15 - y; sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { - y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); y = 7 - y; sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; @@ -941,13 +941,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (LCDC.Bit(2)) { - y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { - y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs index 1a6aa88e0a..4107a8da63 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs @@ -93,6 +93,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk write_ignore = 4; TMA_coincidence = true; + } + else if (pending_reload == 1 && !reload_block) + { + // set interrupts if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } Core.REG_FF0F |= 0x04; From 2f513a3ed588edb49e5b3711a109a6bdf33e7a4f Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 9 May 2018 11:31:13 +0100 Subject: [PATCH 209/339] ZXHawk: Better SoundProviderMixer implementation --- .../SinclairSpectrum/SoundProviderMixer.cs | 154 +++++------------- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 6 +- 2 files changed, 43 insertions(+), 117 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs index 303eda38b7..b5ffebe64f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs @@ -8,13 +8,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider /// Currently only supports SyncSoundMode.Sync - /// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length + /// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length (882) + /// (if not, their buffer will be truncated/expanded) /// internal sealed class SoundProviderMixer : ISoundProvider { private class Provider { public ISoundProvider SoundProvider { get; set; } + public string ProviderDescription { get; set; } public int MaxVolume { get; set; } public short[] Buffer { get; set; } public int NSamp { get; set; } @@ -45,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum EqualizeVolumes(); } - public SoundProviderMixer(short maxVolume, params ISoundProvider[] soundProviders) + public SoundProviderMixer(short maxVolume, string description, params ISoundProvider[] soundProviders) { SoundProviders = new List(); @@ -55,29 +57,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { SoundProvider = s, MaxVolume = maxVolume, + ProviderDescription = description }); } EqualizeVolumes(); } - public void AddSource(ISoundProvider source) + public void AddSource(ISoundProvider source, string description) { SoundProviders.Add(new Provider { SoundProvider = source, - MaxVolume = short.MaxValue + MaxVolume = short.MaxValue, + ProviderDescription = description }); EqualizeVolumes(); } - public void AddSource(ISoundProvider source, short maxVolume) + public void AddSource(ISoundProvider source, short maxVolume, string description) { SoundProviders.Add(new Provider { SoundProvider = source, - MaxVolume = maxVolume + MaxVolume = maxVolume, + ProviderDescription = description }); EqualizeVolumes(); @@ -150,134 +155,55 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var firstEntry = SoundProviders.First(); bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp); - - if (sameCount) + if (!sameCount) { - nsamp = firstEntry.NSamp; - samples = new short[nsamp * 2]; - - if (_stereo) + // this is a bit hacky, really all ISoundProviders should be supplying 44100 with 882 samples per frame. + // we will make sure this happens (no matter how it sounds) + if (SoundProviders.Count > 1) { - for (int i = 0; i < samples.Length; i++) + for (int i = 0; i < SoundProviders.Count; i++) { - short sectorVal = 0; - foreach (var sp in SoundProviders) + int ns = SoundProviders[i].NSamp; + short[] buff = new short[882 * 2]; + + for (int b = 0; b < 882 * 2; b++) { - if (sp.Buffer[i] > sp.MaxVolume) - sectorVal += (short)sp.MaxVolume; - else + if (b == SoundProviders[i].Buffer.Length - 1) { - sectorVal += sp.Buffer[i]; + // end of source buffer + break; } + + buff[b] = SoundProviders[i].Buffer[b]; } - samples[i] = sectorVal; + // save back to the soundprovider + SoundProviders[i].NSamp = 882; + SoundProviders[i].Buffer = buff; } } else { - // convert to mono - for (int i = 0; i < samples.Length; i += 2) - { - short s = 0; - foreach (var sp in SoundProviders) - { - s += (short)((sp.Buffer[i] + sp.Buffer[i + 1]) / 2); - } - - samples[i] = s; - samples[i + 1] = s; - } + // just process what we have as-is } } - else if (!sameCount) + // mix the soundproviders together + nsamp = 882; + samples = new short[nsamp * 2]; + + for (int i = 0; i < samples.Length; i++) { - // this is a pretty poor implementation that doesnt work very well - // ideally soundproviders should ensure that their number of samples is identical - int divisor = 1; - int highestCount = 0; - - // get the lowest divisor of all the soundprovider nsamps - for (int d = 2; d < 999; d++) - { - bool divFound = false; - foreach (var sp in SoundProviders) - { - if (sp.NSamp > highestCount) - highestCount = sp.NSamp; - - if (sp.NSamp % d == 0) - divFound = true; - else - divFound = false; - } - - if (divFound) - { - divisor = d; - break; - } - } - - // now we have the largest current number of samples among the providers - // along with a common divisor for all of them - nsamp = highestCount * divisor; - samples = new short[nsamp * 2]; - - // take a pass at populating the samples array for each provider + short sectorVal = 0; foreach (var sp in SoundProviders) { - short sectorVal = 0; - int pos = 0; - for (int i = 0; i < sp.Buffer.Length; i++) - { - if (sp.Buffer[i] > sp.MaxVolume) - sectorVal = (short)sp.MaxVolume; - else - sectorVal = sp.Buffer[i]; - - for (int s = 0; s < divisor; s++) - { - samples[pos++] += sectorVal; - } - } + if (sp.Buffer[i] > sp.MaxVolume) + sectorVal += (short)sp.MaxVolume; + else + sectorVal += sp.Buffer[i]; } - /* - // get the highest number of samples - int max = SoundProviders.Aggregate((i, j) => i.Buffer.Length > j.Buffer.Length ? i : j).Buffer.Length; - - nsamp = max; - samples = new short[nsamp * 2]; - - // take a pass at populating the samples array for each provider - foreach (var sp in SoundProviders) - { - short sectorVal = 0; - int pos = 0; - for (int i = 0; i < sp.Buffer.Length; i++) - { - if (sp.Buffer[i] > sp.MaxVolume) - sectorVal = (short)sp.MaxVolume; - else - { - if (sp.SoundProvider is AY38912) - { - // boost audio - sectorVal += (short)(sp.Buffer[i] * 2); - } - else - { - sectorVal += sp.Buffer[i]; - } - } - - samples[pos++] += sectorVal; - } - } - */ - + samples[i] = sectorVal; } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 01b4c6d4fb..14ed0a439b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -97,10 +97,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Register(_machine.ULADevice); // initialize sound mixer and attach the various ISoundProvider devices - SoundMixer = new SoundProviderMixer((int)(32767 / 10), (ISoundProvider)_machine.BuzzerDevice); - SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer); + SoundMixer = new SoundProviderMixer((int)(32767 / 10), "System Beeper", (ISoundProvider)_machine.BuzzerDevice); + SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer, "Tape Audio"); if (_machine.AYDevice != null) - SoundMixer.AddSource(_machine.AYDevice); + SoundMixer.AddSource(_machine.AYDevice, "AY-3-3912"); // set audio device settings if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912)) From 66b7f3732739b222521b5bd627890b2beb1cd449 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 9 May 2018 13:47:59 +0100 Subject: [PATCH 210/339] ZXHawk: +3 UPD WriteData command now partially implemented. Data is written to the disk object in memory (allowing castlevania SI to get ingame) but changes to the disk are not serialized in any savestates - #1158 --- .../Hardware/Disk/NECUPD765.FDC.cs | 162 +++++++++--------- .../Hardware/Disk/NECUPD765.FDD.cs | 2 +- 2 files changed, 86 insertions(+), 78 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 742f9cf70f..09ae8ba25f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -47,53 +47,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// The currently active interrupt /// private InterruptState ActiveInterrupt = InterruptState.None; - - /// - /// Stores the current data flow direction - /// - //private CommandDirection ActiveDirection = CommandDirection.IN; - /* - /// - /// Current raised status/error message - /// - private Status ActiveStatus - { - get { return _activeStatus; } - set - { - if (value == Status.None) - { - // clear the active status flag - _statusRaised = false; - } - else - _statusRaised = true; - _activeStatus = value; - } - } - private Status _activeStatus; - - /// - /// Signs whether there is an active status code raised - /// - private bool StatusRaised - { - get { return _statusRaised; } - set - { - if (value == false) - { - // return to none status - _activeStatus = Status.None; - } - - // dont set true here - } - } - private bool _statusRaised; - */ - - /// /// Command buffer /// This does not contain the initial command byte (only parameter bytes) @@ -170,26 +123,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// private int OverrunCounter; - /// - /// Signs that the the controller is ready - /// - //public bool FDC_FLAG_RQM; - - /// - /// When TRUE, a SCAN command is currently active - /// - //private bool FDC_FLAG_SCANNING; - - /// - /// Set when a seek operation has completed on drive 0 - /// - //private bool FDC_FLAG_SEEKCOMPLETED_0; - - /// - /// Set when a seek operation is active on drive 0 - /// - //private bool FDC_FLAG_SEEKACTIVE_0; - /// /// Contains result bytes in result phase /// @@ -348,7 +281,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ private byte Status3; - #endregion #region UPD Internal Functions @@ -1742,16 +1674,38 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else { - // not implemented yet - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_NW, ref Status1); - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; + // calculate the number of bytes to write + int byteCounter = 0; + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; - // move to result phase - ActivePhase = Phase.Result; + // get the first sector + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + for (int s = 0; s < track.Sectors.Length; s++) + { + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + for (int i = 0; i < 0x80 << ActiveCommandParams.SectorSize; i++) + { + byteCounter++; + + if (i == (0x80 << ActiveCommandParams.SectorSize) - 1 && lastSec) + { + break; + } + } + + if (lastSec) + break; + } + + ExecCounter = byteCounter; + ExecLength = byteCounter; + ActivePhase = Phase.Execution; break; } } @@ -1762,6 +1716,53 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // FDC in execution phase reading/writing bytes //---------------------------------------- case Phase.Execution: + + var index = ExecLength - ExecCounter; + + ExecBuffer[index] = LastSectorDataWriteByte; + + OverrunCounter--; + ExecCounter--; + + if (ExecCounter <= 0) + { + int cnt = 0; + + // all data received + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + + for (int s = 0; s < track.Sectors.Length; s++) + { + if (cnt == ExecLength) + break; + + ActiveCommandParams.Sector = track.Sectors[s].SectorID; + + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + int size = 0x80 << track.Sectors[s].SectorSize; + + for (int d = 0; d < size; d++) + { + track.Sectors[s].SectorData[d] = ExecBuffer[cnt++]; + } + + if (lastSec) + break; + } + + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_EN, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + } + break; //---------------------------------------- @@ -3476,7 +3477,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // store the byte LastSectorDataWriteByte = data; - ActiveCommand.CommandDelegate(); + ActiveCommand.CommandDelegate(); + + if (ExecCounter <= 0) + { + // end of execution phase + ActivePhase = Phase.Result; + } + break; //// result phase case Phase.Result: diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs index 44cd6dd9ab..bb2030ce26 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs @@ -225,7 +225,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// Disk is write protected (TRUE BY DEFAULT) /// - public bool FLAG_WRITEPROTECT = true; + public bool FLAG_WRITEPROTECT = false; /// /// Storage for recal steps From 056c24e4cf095e809aa1b20e6aaa6f8af57e6082 Mon Sep 17 00:00:00 2001 From: feos Date: Wed, 9 May 2018 17:17:58 +0300 Subject: [PATCH 211/339] tastudio: attempt to fix greenzone decay failing to drop states --- .../movie/tasproj/StateManagerDecay.cs | 72 +++++++++++++------ .../movie/tasproj/TasStateManager.cs | 6 +- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs index 7cf5fbdccd..65156ea512 100644 --- a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs +++ b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs @@ -9,8 +9,14 @@ means that we lower the priority of a state that goes at that index. Priority changes depending on current frame and amount of states. States with biggest priority get erased first. With a 4-bit battern and no initial gap between states, total frame coverage is - about 5 times state count. Initial state gap can screw up our patterns, so do all - calculations like gap isn't there, and take it back into account afterwards. + about 5 times state count. + + Initial state gap can screw up our patterns, so do all the calculations like the gap + isn't there, and take it back into account afterwards. The algo only works with integral + greenzone, so we make it think it is integral by reducing the frame numbers. Before any + decay logic starts for each state, we check if it has a marker on it (in which case we + don't drop it) or appears inside the state gap (in which case we forcibly drop it). This + step doesn't involve numbers reduction. _zeros values are essentialy the values of rshiftby here: bitwise view frame rshiftby priority @@ -59,7 +65,7 @@ namespace BizHawk.Client.Common for (; decayStates > 0 && _tsm.StateCount > 1;) { int baseStateIndex = _tsm.GetStateIndexByFrame(Global.Emulator.Frame); - int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; + int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; // reduce right away int forwardPriority = -1000000; int backwardPriority = -1000000; int forwardFrame = -1; @@ -76,14 +82,18 @@ namespace BizHawk.Client.Common else if (currentFrame % _step > 0) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything - _tsm.RemoveState(currentFrame); - decayStates--; + if (_tsm.RemoveState(currentFrame)) + { + // decrementing this if no state was removed is BAD + decayStates--; - // this is the kind of highly complex loops that might justify goto - goto next_state; + // this is the kind of highly complex loops that might justify goto + goto next_state; + } } else { + // reduce to imaginary integral greenzone for all the decay logic currentFrame /= _step; } @@ -104,7 +114,7 @@ namespace BizHawk.Client.Common for (int currentStateIndex = _tsm.StateCount - 1; currentStateIndex > baseStateIndex; currentStateIndex--) { - int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex) / _step; + int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex); if (_tsm.StateIsMarker(currentFrame)) { @@ -113,14 +123,18 @@ namespace BizHawk.Client.Common else if (currentFrame % _step > 0) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything - _tsm.RemoveState(currentFrame); - decayStates--; + if (_tsm.RemoveState(currentFrame)) + { + // decrementing this if no state was removed is BAD + decayStates--; - // this is the kind of highly complex loops that might justify goto - goto next_state; + // this is the kind of highly complex loops that might justify goto + goto next_state; + } } else { + // reduce to imaginary integral greenzone for all the decay logic currentFrame /= _step; } @@ -143,30 +157,46 @@ namespace BizHawk.Client.Common { if (baseStateFrame - forwardFrame > backwardFrame - baseStateFrame) { - _tsm.RemoveState(forwardFrame * _step); + if (_tsm.RemoveState(forwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else { - _tsm.RemoveState(backwardFrame * _step); + if (_tsm.RemoveState(backwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } - - decayStates--; } else if (forwardFrame > -1) { - _tsm.RemoveState(forwardFrame * _step); - decayStates--; + if (_tsm.RemoveState(forwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else if (backwardFrame > -1) { - _tsm.RemoveState(backwardFrame * _step); - decayStates--; + if (_tsm.RemoveState(backwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else { // we're very sorry about failing to find states to remove, but we can't go beyond capacity, so remove at least something // this shouldn't happen, but if we don't do it here, nothing good will happen either - _tsm.RemoveState(_tsm.GetStateFrameByIndex(1)); + if (_tsm.RemoveState(_tsm.GetStateFrameByIndex(1))) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } // this is the kind of highly complex loops that might justify goto diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index 705325ee6c..0c02cb910b 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -262,13 +262,13 @@ namespace BizHawk.Client.Common return _movie.Markers.IsMarker(frame + 1); } - public void RemoveState(int frame) + public bool RemoveState(int frame) { int index = _states.IndexOfKey(frame); if (frame < 1 || index < 1) { - return; + return false; } StateManagerState state = _states.Values[index]; @@ -283,6 +283,8 @@ namespace BizHawk.Client.Common } _states.RemoveAt(index); + + return true; } /// From 32f78cd3112e21f3cf365751425b8ea47a7e28c2 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 9 May 2018 20:05:53 -0400 Subject: [PATCH 212/339] GBHawk: Cleanup ppus --- .../CPUs/LR35902/Execute.cs | 4 +- .../CPUs/LR35902/LR35902.cs | 3 +- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 118 ++++++++---------- .../Nintendo/GBHawk/GBHawk.IDebuggable.cs | 2 +- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 116 +++++++---------- 5 files changed, 101 insertions(+), 142 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs index f68b2689c8..c30a5675f8 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs @@ -4,8 +4,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { public partial class LR35902 { - private int totalExecutedCycles; - public int TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } + private ulong totalExecutedCycles; + public ulong TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } private int EI_pending; private bool interrupts_enabled; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index c2f18c8f6e..208615ced4 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -295,7 +295,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 // call the interrupt processor after 4 extra cycles if (!Halt_bug_3) { - INTERRUPT_GBC_NOP(); + INTERRUPT_GBC_NOP(); + //INTERRUPT_(); } else { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 973b9b1fe7..8447de3f2a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -58,9 +58,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override byte ReadReg(int addr) { byte ret = 0; - + //Console.WriteLine(Core.cpu.TotalExecutedCycles); switch (addr) - { + { case 0xFF40: ret = LCDC; break; // LCDC case 0xFF41: ret = STAT; break; // STAT case 0xFF42: ret = scroll_y; break; // SCY @@ -350,7 +350,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle = 0; - LY_actual = LY; + LY += LY_inc; + Core.cpu.LY = LY; + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } no_scan = false; @@ -359,6 +366,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY_inc = 1; Core.in_vblank = false; + /* + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + */ // special note here, the y coordiate of the window is kept if the window is deactivated // meaning it will pick up where it left off if re-enabled later // so we don't reset it in the scanline loop @@ -368,8 +381,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_is_reset = true; } - Core.cpu.LY = LY; - // Automatically restore access to VRAM at this time (force end drawing) // Who Framed Roger Rabbit seems to run into this. VRAM_access_write = true; @@ -381,34 +392,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 452) - { - LY += LY_inc; - - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } - else - { - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; - } - - Core.cpu.LY = LY; - } - - if ((LY == 153) && (cycle == 0)) - { - LY = 0; - LY_inc = 0; - - } - // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -424,25 +407,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // also, the LCD does not enter mode 2 on scanline 0 when first turned on no_scan = true; - cycle = 4; + cycle = 8; } // the VBL stat is continuously asserted - if ((LY_actual >= 144)) + if ((LY >= 144)) { if (STAT.Bit(4)) { - if ((cycle >= 0) && (LY_actual == 144)) + if ((cycle >= 4) && (LY == 144)) { VBL_INT = true; } - else if (LY_actual > 144) + else if (LY > 144) { VBL_INT = true; } } - if ((cycle == 0) && (LY_actual == 144)) { + if ((cycle == 4) && (LY == 144)) { HBL_INT = false; @@ -454,15 +437,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Core.REG_FF0F |= 0x01; } - if ((LY_actual >= 144) && (cycle == 0)) + if ((LY >= 144) && (cycle == 4)) { // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } + //if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY_actual == 153) && (cycle == 4)) + if ((LY == 153) && (cycle == 8)) { - LY_actual = 0; + LY = 0; + LY_inc = 0; Core.cpu.LY = LY; } } @@ -475,7 +459,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 4) + if (cycle == 8) { if (LY != LYC) { @@ -491,9 +475,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 80) + if (cycle == 84) { - STAT &= 0xFC; STAT |= 0x03; OAM_INT = false; @@ -504,7 +487,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_write = false; } - if (cycle == 252) + if (cycle == 256) { STAT &= 0xFC; OAM_INT = false; @@ -519,9 +502,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (cycle < 76) + if (cycle < 80) { - if (cycle == 0) + if (cycle == 4) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -529,24 +512,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // here mode 2 will be set to true and interrupts fired if enabled STAT &= 0xFC; STAT |= 0x2; + if (STAT.Bit(5)) { OAM_INT = true; } HBL_INT = false; + // DMG exits VBlank into mode 0, but not GBC, so this line is needed + // (This is important for Wacky Racers and Altered Space) + VBL_INT = false; } // here OAM scanning is performed - OAM_scan(cycle + 4); + OAM_scan(cycle); } - else if (cycle >= 76 && LY_actual < 144) + else if ((cycle >= 80) && (LY < 144)) { - if (cycle == 76) - { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - } - if (cycle == 80) + if (cycle == 84) { STAT &= 0xFC; STAT |= 0x03; @@ -556,20 +537,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // render the screen and handle hblank - render(cycle - 76); + render(cycle - 80); } } - - if (cycle >= 452) - { - OAM_access_read = false; - OAM_scan(cycle - 452); - } } if ((LY_inc == 0)) { - if (cycle == 8) + if (cycle == 12) { LYC_INT = false; STAT &= 0xFB; @@ -583,14 +558,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } + //if (STAT.Bit(5)) { OAM_INT = true; } } - if (cycle == 88) { OAM_INT = false; } + //if (cycle == 92) { OAM_INT = false; } } // here LY=LYC will be asserted - if ((cycle == 0) && (LY_actual != 0)) + if ((cycle == 4) && (LY != 0)) { if ((LY == LYC) && !STAT.Bit(2)) { @@ -613,7 +588,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LCD_was_off = true; LY = 0; - LY_actual = 0; Core.cpu.LY = LY; cycle = 0; @@ -647,6 +621,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + // window X is latched for the scanline, mid-line changes have no effect window_x_latch = window_x; @@ -1411,6 +1389,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { + OAM_access_read = false; + OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs index 778b17acfc..8d5be5a227 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs @@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int TotalExecutedCycles { - get { return cpu.TotalExecutedCycles; } + get { return (int)cpu.TotalExecutedCycles; } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index b627310025..13d5237742 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk x_tile = (int)Math.Floor((float)(scroll_x) / 8); break; case 0xFF44: // LY - LY = LY_actual = 0; /*reset*/ + LY = 0; /*reset*/ break; case 0xFF45: // LYC LYC = value; @@ -125,7 +125,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } cycle = 0; - LY_actual = LY; + + LY += LY_inc; + Core.cpu.LY = LY; + // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } no_scan = false; @@ -134,6 +142,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY_inc = 1; Core.in_vblank = false; + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + // special note here, the y coordiate of the window is kept if the window is deactivated // meaning it will pick up where it left off if re-enabled later // so we don't reset it in the scanline loop @@ -154,34 +167,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 452) - { - LY += LY_inc; - - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } - else - { - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; - } - - Core.cpu.LY = LY; - } - - if ((LY == 153) && (cycle == 0)) - { - LY = 0; - LY_inc = 0; - - } - // exit vblank if LCD went from off to on if (LCD_was_off) { @@ -197,25 +182,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // also, the LCD does not enter mode 2 on scanline 0 when first turned on no_scan = true; - cycle = 4; + cycle = 8; } // the VBL stat is continuously asserted - if ((LY_actual >= 144)) + if ((LY >= 144)) { if (STAT.Bit(4)) { - if ((cycle >= 0) && (LY_actual == 144)) + if ((cycle >= 4) && (LY == 144)) { VBL_INT = true; } - else if (LY_actual > 144) + else if (LY > 144) { VBL_INT = true; } } - if ((cycle == 0) && (LY_actual == 144)) { + if ((cycle == 4) && (LY == 144)) { HBL_INT = false; @@ -227,15 +212,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Core.REG_FF0F |= 0x01; } - if ((LY_actual >= 144) && (cycle == 0)) + if ((LY >= 144) && (cycle == 4)) { // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY_actual == 153) && (cycle == 4)) + if ((LY == 153) && (cycle == 8)) { - LY_actual = 0; + LY = 0; + LY_inc = 0; Core.cpu.LY = LY; } } @@ -248,7 +234,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // there is no mode 2 (presumably it missed the trigger) // mode 3 is very short, probably in some self test mode before turning on? - if (cycle == 4) + if (cycle == 8) { if (LY != LYC) { @@ -264,7 +250,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - if (cycle == 80) + if (cycle == 84) { STAT &= 0xFC; @@ -277,7 +263,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_write = false; } - if (cycle == 252) + if (cycle == 256) { STAT &= 0xFC; OAM_INT = false; @@ -292,9 +278,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (cycle < 76) + if (cycle < 80) { - if (cycle == 0) + if (cycle == 4) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -308,18 +294,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // here OAM scanning is performed - OAM_scan(cycle + 4); + OAM_scan(cycle); } - else if ((cycle >= 76) && (LY_actual < 144)) + else if ((cycle >= 80) && (LY < 144)) { - if (cycle == 76) - { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - } - - if (cycle == 80) + if (cycle == 84) { STAT &= 0xFC; STAT |= 0x03; @@ -329,20 +308,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // render the screen and handle hblank - render(cycle - 76); + render(cycle - 80); } - } - - if (cycle >= 452) - { - if (LY != 144) { OAM_access_read = false; } - OAM_scan(cycle - 452); - } + } } if ((LY_inc == 0)) { - if (cycle == 8) + if (cycle == 12) { LYC_INT = false; STAT &= 0xFB; @@ -359,11 +332,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (STAT.Bit(5)) { OAM_INT = true; } } - if (cycle == 88) { OAM_INT = false; } + if (cycle == 92) { OAM_INT = false; } } // here LY=LYC will be asserted - if ((cycle == 0) && (LY_actual != 0)) + if ((cycle == 4) && (LY != 0)) { if ((LY == LYC) && !STAT.Bit(2)) { @@ -386,7 +359,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LCD_was_off = true; LY = 0; - LY_actual = 0; Core.cpu.LY = LY; cycle = 0; @@ -420,6 +392,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // i.e. just keeping track of the lowest x-value sprite if (render_cycle == 0) { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + // window X is latched for the scanline, mid-line changes have no effect window_x_latch = window_x; @@ -924,14 +900,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (LCDC.Bit(2)) { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); y = 15 - y; sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); y = 7 - y; sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; @@ -941,13 +917,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (LCDC.Bit(2)) { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; } else { - y = LY_actual - (SL_sprites[sl_use_index * 4] - 16); + y = LY - (SL_sprites[sl_use_index * 4] - 16); sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; } @@ -1085,6 +1061,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? if (OAM_cycle == 0) { + OAM_access_read = false; + OAM_scan_index = 0; SL_sprites_index = 0; write_sprite = 0; From da1d190cce92e4cda3e9e6d7ded108923d27aba4 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 9 May 2018 22:18:10 -0400 Subject: [PATCH 213/339] GBHawk: When windowing is active, start mode 3 earlier then usual (for pokemon yellow sync) --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 8447de3f2a..a3ea968b3e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -825,6 +825,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { read_case = 8; hbl_countdown = 5; + if (window_started) + { + hbl_countdown -= 2; + } + } + if (pixel_counter == 158 && window_started) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } } } else if ((render_counter >= render_offset) && (pixel_counter < 0)) From 0d4418a1cc40347a9aa55226f924f7209ffb37d1 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 10 May 2018 10:42:36 +0100 Subject: [PATCH 214/339] ZXHawk: Disk Drive code tidy --- .../Hardware/Disk/NECUPD765.FDC.cs | 1729 +---------------- .../Hardware/Disk/NECUPD765.FDD.cs | 21 - .../Hardware/Disk/NECUPD765.Timing.cs | 3 +- 3 files changed, 86 insertions(+), 1667 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 09ae8ba25f..90c8998f78 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -200,9 +200,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _cmdIndex = value; ActiveCommand = CommandList[_cmdIndex]; - - // clear command params - //ActiveCommandParams.Reset(); } } private int _cmdIndex; @@ -871,119 +868,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //ActiveDrive.SectorIndex++; } } - - - /* - // If SK=1, the FDC skips the sector with the Data Address Mark and reads the next sector. - // The CRC bits in the data field are not checked when SK=1 - if (CMD_FLAG_SK && !Status2.Bit(SR2_CM)) - { - if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) - { - // increment the sector ID and search again - ActiveCommandParams.Sector++; - continue; - } - else - { - // no execution phase - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Result; - break; - } - } - - // read the sector - for (int i = 0; i < sectorSize; i++) - { - ExecBuffer[buffPos++] = sector.ActualData[i]; - } - - // any CRC errors? - if (Status1.Bit(SR1_DE) || Status2.Bit(SR2_DD)) - { - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - terminate = true; - } - - if (!CMD_FLAG_SK && !Status2.Bit(SR2_CM)) - { - // deleted address mark was detected with NO skip flag set - ActiveCommandParams.EOT = ActiveCommandParams.Sector; - SetBit(SR2_CM, ref Status2); - SetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_IC1, ref Status0); - terminate = true; - } - - if (sector.SectorID == ActiveCommandParams.EOT || terminate) - { - // this was the last sector to read - // or termination requested - - SetBit(SR1_EN, ref Status1); - - int keyIndex = 0; - for (int i = 0; i < track.Sectors.Length; i++) - { - if (track.Sectors[i].SectorID == sector.SectorID) - { - keyIndex = i; - break; - } - } - - if (keyIndex == track.Sectors.Length - 1) - { - // last sector on the cylinder, set EN - SetBit(SR1_EN, ref Status1); - - // increment cylinder - ActiveCommandParams.Cylinder++; - - // reset sector - ActiveCommandParams.Sector = 1; - ActiveDrive.SectorIndex = 0; - } - else - { - ActiveDrive.SectorIndex++; - } - - UnSetBit(SR0_IC1, ref Status0); - if (terminate) - SetBit(SR0_IC0, ref Status0); - else - UnSetBit(SR0_IC0, ref Status0); - - SetBit(SR0_IC0, ref Status0); - - // result requires the actual track id, rather than the sector track id - ActiveCommandParams.Cylinder = track.TrackNumber; - - // remove CM (appears to be required to defeat Alkatraz copy protection) - UnSetBit(SR2_CM, ref Status2); - - CommitResultCHRN(); - CommitResultStatus(); - ActivePhase = Phase.Execution; - break; - } - else - { - // continue with multi-sector read operation - ActiveCommandParams.Sector++; - //ActiveDrive.SectorIndex++; - } - */ } if (ActivePhase == Phase.Execution) @@ -1014,108 +898,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - // get the correct position in the sector buffer - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - // write the byte - ResBuffer[dataIndex] = LastSectorDataWriteByte; - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } /// @@ -1349,108 +1131,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - // get the correct position in the sector buffer - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - // write the byte - ResBuffer[dataIndex] = LastSectorDataWriteByte; - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } /// @@ -1771,108 +1451,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - // get the correct position in the sector buffer - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - // write the byte - ResBuffer[dataIndex] = LastSectorDataWriteByte; - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } /// @@ -1984,130 +1562,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - byte currByte = CommBuffer[CommCounter]; - switch (CommCounter) - { - case CM_HEAD: - ParseParamByteStandard(CommCounter); - break; - // N - case 1: - ActiveCommandParams.SectorSize = currByte; - break; - // SC (sectors per cylinder) - case 2: - ActiveCommandParams.SectorCount = currByte; - break; - // GPL - case 3: - ActiveCommandParams.Gap3Length = currByte; - break; - // filler - case 4: - ActiveCommandParams.Filler = currByte; - break; - } - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - // get the correct position in the sector buffer - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - // write the byte - ResBuffer[dataIndex] = LastSectorDataWriteByte; - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } /// @@ -2191,16 +1645,38 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else { - // not implemented yet - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_NW, ref Status1); - CommitResultCHRN(); - CommitResultStatus(); - //ResBuffer[RS_ST0] = Status0; + // calculate the number of bytes to write + int byteCounter = 0; + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; - // move to result phase - ActivePhase = Phase.Result; + // get the first sector + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + for (int s = 0; s < track.Sectors.Length; s++) + { + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + for (int i = 0; i < 0x80 << ActiveCommandParams.SectorSize; i++) + { + byteCounter++; + + if (i == (0x80 << ActiveCommandParams.SectorSize) - 1 && lastSec) + { + break; + } + } + + if (lastSec) + break; + } + + ExecCounter = byteCounter; + ExecLength = byteCounter; + ActivePhase = Phase.Execution; break; } } @@ -2211,6 +1687,53 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // FDC in execution phase reading/writing bytes //---------------------------------------- case Phase.Execution: + + var index = ExecLength - ExecCounter; + + ExecBuffer[index] = LastSectorDataWriteByte; + + OverrunCounter--; + ExecCounter--; + + if (ExecCounter <= 0) + { + int cnt = 0; + + // all data received + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + + for (int s = 0; s < track.Sectors.Length; s++) + { + if (cnt == ExecLength) + break; + + ActiveCommandParams.Sector = track.Sectors[s].SectorID; + + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + int size = 0x80 << track.Sectors[s].SectorSize; + + for (int d = 0; d < size; d++) + { + track.Sectors[s].SectorData[d] = ExecBuffer[cnt++]; + } + + if (lastSec) + break; + } + + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_EN, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + } + break; //---------------------------------------- @@ -2219,108 +1742,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - // get the correct position in the sector buffer - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - // write the byte - ResBuffer[dataIndex] = LastSectorDataWriteByte; - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } #endregion @@ -2361,126 +1782,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // use STP instead of DTL for scan commands - if (CommCounter == CM_STP) - { - //ActiveCommandParams.SectorLength = 0; - ActiveCommandParams.STP = LastByteReceived; - } - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - byte oldData = ResBuffer[dataIndex]; - - if (LastSectorDataWriteByte != oldData) - { - if (LastSectorDataWriteByte != 255 && - oldData != 255) - { - // scan not equal - UnSetBit(SR2_SH, ref Status2); - - // scan not satified - SetBit(SR2_SN, ref Status2); - } - } - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } /// @@ -2517,129 +1818,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // use STP instead of DTL for scan commands - if (CommCounter == CM_STP) - { - //ActiveCommandParams.SectorLength = 0; - ActiveCommandParams.STP = LastByteReceived; - } - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - byte oldData = ResBuffer[dataIndex]; - - if (LastSectorDataWriteByte != oldData) - { - if (LastSectorDataWriteByte != 255 && - oldData != 255) - { - // scan not equal - UnSetBit(SR2_SH, ref Status2); - - if (oldData > LastSectorDataWriteByte) - { - // scan not satified - SetBit(SR2_SN, ref Status2); - } - } - } - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } /// @@ -2676,129 +1854,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Phase.Result: break; } - /* - switch (iState) - { - //---------------------------------------- - // Receiving command parameter bytes - //---------------------------------------- - case InstructionState.ReceivingParameters: - - // store the parameter in the command buffer - CommBuffer[CommCounter] = LastByteReceived; - - // process parameter byte - ParseParamByteStandard(CommCounter); - - // use STP instead of DTL for scan commands - if (CommCounter == CM_STP) - { - //ActiveCommandParams.SectorLength = 0; - ActiveCommandParams.STP = LastByteReceived; - } - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // all parameter bytes received - // move to pre-execution - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - break; - - //---------------------------------------- - // Pre-execution - //---------------------------------------- - case InstructionState.PreExecution: - // instruction is read/write - SetupReadWriteCommand(); - break; - - //---------------------------------------- - // Execution begins - //---------------------------------------- - case InstructionState.StartExecute: - StartReadWriteExecution(); - break; - - //---------------------------------------- - // Write commands during execution - //---------------------------------------- - case InstructionState.ExecutionWrite: - int dataIndex = ActiveCommandParams.SectorLength - ExecCounter; - byte oldData = ResBuffer[dataIndex]; - - if (LastSectorDataWriteByte != oldData) - { - if (LastSectorDataWriteByte != 255 && - oldData != 255) - { - // scan not equal - UnSetBit(SR2_SH, ref Status2); - - if (oldData < LastSectorDataWriteByte) - { - // scan not satified - SetBit(SR2_SN, ref Status2); - } - } - } - break; - - //---------------------------------------- - // Read commands during execution - //---------------------------------------- - case InstructionState.ExecutionRead: - break; - - //---------------------------------------- - // Setup for result phase - //---------------------------------------- - case InstructionState.StartResult: - CheckUnloadHead(); - if (ActivePhase != Phase.Idle) - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - SetPhase_Result(); - } - else - { - // all result bytes have been sent - // move to idle - SetPhase_Idle(); - } - - // set RQM flag - SetBit(MSR_RQM, ref StatusMain); - break; - - //---------------------------------------- - // Result processing - //---------------------------------------- - case InstructionState.ProcessResult: - // instruction is read/write - ReadWriteCommandResult(); - break; - - //---------------------------------------- - // Results sending - //---------------------------------------- - case InstructionState.SendingResults: - break; - - //---------------------------------------- - // Instruction lifecycle completed - //---------------------------------------- - case InstructionState.Completed: - break; - }*/ } #endregion @@ -3201,55 +2256,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } ResBuffer[0] = Status3; - ActivePhase = Phase.Result; - - - - /* - - - if (ActiveCommandParams.UnitSelect != 0) - { - // we only support 1 drive - SetBit(SR3_FT, ref Status3); - } - else - { - // HD - only one side - UnSetBit(SR3_HD, ref Status3); - - // write protect - if (ActiveDrive.FLAG_WRITEPROTECT) - SetBit(SR3_WP, ref Status3); - - // track 0 - if (ActiveDrive.FLAG_TRACK0) - SetBit(SR3_T0, ref Status3); - - // rdy - if (ActiveDrive.Disk != null) - SetBit(SR3_RY, ref Status3); - } - - ResBuffer[0] = Status3; - ActivePhase = Phase.Result; - - */ - - - /* - - // ready - if (ActiveDrive.FLAG_READY) - SetBit(SR3_RY, ref Status3); - else - { - // set WR if not ready - SetBit(SR3_WP, ref Status3); - } - - */ - + ActivePhase = Phase.Result; break; @@ -3377,6 +2384,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; } + //if (!CheckTiming()) + //{ + // UnSetBit(MSR_EXM, ref StatusMain); + //} + return StatusMain; } private int testCount = 0; @@ -3589,35 +2601,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return true; } - /* - /// - /// Processes written data bytes whilst in command phase - /// - /// - private void ProcessCommand(byte data) - { - // capture the parameter byte - CommBuffer[CommCounter] = data; - - // process parameter byte - ActiveCommand.CommandDelegate(InstructionState.ReceivingParameters); - - // increment command parameter counter - CommCounter++; - - // was that the last parameter byte? - if (CommCounter == ActiveCommand.ParameterByteCount) - { - // clear down the counters - CommCounter = 0; - ResCounter = 0; - - // all bytes received - execute the UPD command - //ActivePhase = Phase.Execution; - ActiveCommand.CommandDelegate(InstructionState.PreExecution); - } - } - */ /// /// Parses the first 5 command argument bytes that are of the standard format @@ -3680,158 +2663,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; } } - /* - /// - /// Initializes a read or write command in execution phase - /// Data bytes are going to be read to the CPU or written to the FDC - /// - private void SetupReadWriteCommand() - { - // set the active drive that this command is referring to - int US = ActiveCommandParams.UnitSelect; - DiskDriveIndex = US; - - // clear everything in interrupt ST0 except for IC and US - UnSetBit(SR0_HD, ref ActiveDrive.IntStatus); - UnSetBit(SR0_NR, ref ActiveDrive.IntStatus); - UnSetBit(SR0_EC, ref ActiveDrive.IntStatus); - UnSetBit(SR0_SE, ref ActiveDrive.IntStatus); - - IndexPulseCounter = 0; - CMD_FLAG_MF = false; - - // clear status registers - Status1 = 0; - Status2 = 0; - - SectorID = 0; - SectorDelayCounter = -1; - - // is the active drive ready? - if (!ActiveDrive.FLAG_READY) - { - ActiveStatus = Status.DriveNotReady; - ActiveCommand.CommandDelegate(InstructionState.StartResult); - return; - } - - // is this a write command? - if (ActiveCommand.IsWrite) - { - // check write protection status - if (ActiveDrive.FLAG_WRITEPROTECT) - { - ActiveStatus = Status.WriteProtected; - ActiveCommand.CommandDelegate(InstructionState.StartResult); - return; - } - } - - // StartExecute - ActiveCommand.CommandDelegate(InstructionState.StartExecute); - } - - /// - /// Starts the read/write execution phase - /// - private void StartReadWriteExecution() - { - // set execution phase - ActivePhase = Phase.Execution; - - // set direction - if (ActiveCommand.Direction == CommandDirection.IN) - UnSetBit(MSR_DIO, ref StatusMain); - else - SetBit(MSR_DIO, ref StatusMain); - - // clear RQM flag - UnSetBit(MSR_RQM, ref StatusMain); - - // setup HLT & HUT - if (HUT_Counter > 0) - { - HLT_Counter = 0; - HUT_Counter = 0; - } - else - { - HLT_Counter = HLT; - HUT_Counter = 0; - } - - // we require 2 index pulses as standard - IndexPulseCounter = 2; - - switch (ActiveCommand.CommandCode) - { - case CC_WRITE_ID: - case CC_READ_DIAGNOSTIC: - CMD_FLAG_MT = false; - break; - default: - CMD_FLAG_MT = true; - break; - } - } - - /// - /// Process standard result data for read/write commands (at the start of result phase) - /// - private void ReadWriteCommandResult() - { - // clear st0 - Status0 = 0; - - // HD - if (ActiveCommandParams.Side == 1) - SetBit(SR0_HD, ref Status0); - // US - switch (ActiveCommandParams.UnitSelect) - { - case 1: - SetBit(SR0_US0, ref Status0); - break; - case 2: - SetBit(SR0_US1, ref Status0); - break; - case 3: - SetBit(SR0_US0, ref Status0); - SetBit(SR0_US1, ref Status0); - break; - } - - if (ActiveStatus != Status.None) - { - Status0 |= 0x40; - - // check for errors - switch (ActiveStatus) - { - case Status.WriteProtected: - SetBit(SR1_NW, ref Status1); - break; - case Status.SectorNotFound: - SetBit(SR1_MA, ref Status1); - SetBit(SR1_ND, ref Status1); - break; - case Status.DriveNotReady: - SetBit(SR0_NR, ref Status0); - break; - } - } - - // populate result buffer - ResBuffer[RS_ST0] = Status0; - ResBuffer[RS_ST1] = Status1; - ResBuffer[RS_ST2] = Status2; - ResBuffer[RS_C] = ActiveCommandParams.Cylinder; - ResBuffer[RS_H] = ActiveCommandParams.Head; - ResBuffer[RS_R] = ActiveCommandParams.Sector; - ResBuffer[RS_N] = ActiveCommandParams.SectorSize; - } - - */ /// /// Clears the result buffer @@ -3855,51 +2686,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } - - /* - /// - /// Called when a write operation is asked for and we are in Execution phase - /// - /// - private void ProcessExecutionWrite(byte data) - { - // check command direction - if (ActiveCommand.Direction == CommandDirection.IN) - { - if (FDC_FLAG_SCANNING) - { - // scan command is being processed - if (data != 255) - { - switch (ActiveCommand.CommandCode) - { - // scan equal - case 0x11: - break; - // scan high or equal - case 0x1d: - break; - // scan low or equal - case 0x19: - break; - } - } - } - - } - } - - /// - /// Called when a read operation is asked for and we are in Execution phase - /// - /// - private byte ProcessExecutionRead() - { - byte result = 0xFF; - - return result; - } - */ /// /// Populates the result status registers /// @@ -3945,52 +2731,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum UnSetBit(SR0_US0, ref Status0); } - /* - // initial defaults - SetBit(SR0_IC0, ref Status0); - SetBit(SR1_EN, ref Status1); - - // check for read diag - if (ActiveCommand.CommandCode == 0x02) - { - // commit to result buffer - ResBuffer[RS_ST0] = Status0; - ResBuffer[RS_ST1] = Status1; - return; - } - - // check for error bits - if (GetBit(SR1_DE, Status1) || - GetBit(SR1_MA, Status1) || - GetBit(SR1_ND, Status1) || - GetBit(SR1_NW, Status1) || - GetBit(SR1_OR, Status1) || - GetBit(SR2_BC, Status2) || - GetBit(SR2_CM, Status2) || - GetBit(SR2_DD, Status2) || - GetBit(SR2_MD, Status2) || - GetBit(SR2_SN, Status2) || - GetBit(SR2_WC, Status2)) - { - // error bits set - unset end of track - UnSetBit(SR1_EN, ref Status1); - } - - // check for data errors - if (GetBit(SR1_DE, Status1) || - GetBit(SR2_DD, Status2)) - { - // unset control mark - UnSetBit(SR2_CM, ref Status2); - } - else if (GetBit(SR2_CM, Status2)) - { - // DAM found - unset IC and US0 - UnSetBit(SR0_IC0, ref Status0); - UnSetBit(SR0_US0, ref Status0); - } - */ - // commit to result buffer ResBuffer[RS_ST0] = Status0; ResBuffer[RS_ST1] = Status1; @@ -4008,127 +2748,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ResBuffer[RS_R] = ActiveCommandParams.Sector; ResBuffer[RS_N] = ActiveCommandParams.SectorSize; } - /* - /// - /// Sets everything up ready to return the specified result byte format - /// - private void GetResult(ResultType resType) - { - switch (resType) - { - // return the standard 7 byte format - case ResultType.Standard: - - // status registers - CommitResultStatus(); - - // CHRN - CommitResultCHRN(); - - // make main status register ready - // set FDC busy - SetBit(MSR_CB, ref StatusMain); - // set direction - SetBit(MSR_DIO, ref StatusMain); - // RQM ready - SetBit(MSR_RQM, ref StatusMain); - - // update buffer counters - CommCounter = 0; - ResCounter = 0; - - // clear down status registers - Status0 = 0; - Status1 = 0; - Status2 = 0; - - // move to result phase - ActivePhase = Phase.Result; - - break; - - // return 1 byte ST3 - case ResultType.ST3: - - // commit ST3 to result buffer - ResBuffer[0] = Status3; - - // make main status register ready - // set FDC busy - SetBit(MSR_CB, ref StatusMain); - // set direction - SetBit(MSR_DIO, ref StatusMain); - // RQM ready - SetBit(MSR_RQM, ref StatusMain); - - // update buffer counters - CommCounter = 0; - ResCounter = 0; - - // move to result phase - ActivePhase = Phase.Result; - - break; - - // return 1 byte ST0 - case ResultType.ST0: - - // commit st0 to result buffer - ResBuffer[0] = Status0; - - // make main status register ready - // set FDC busy - SetBit(MSR_CB, ref StatusMain); - // set direction - SetBit(MSR_DIO, ref StatusMain); - // RQM ready - SetBit(MSR_RQM, ref StatusMain); - - // update buffer counters - CommCounter = 0; - ResCounter = 0; - - // move to result phase - ActivePhase = Phase.Result; - - break; - - case ResultType.Interrupt: - - // commit st0 to result buffer - ResBuffer[0] = Status0; - - // commit current track to result buffer - ResBuffer[1] = (byte)FDD_CurrentCylinder; - - // make main status register ready - // set FDC busy - SetBit(MSR_CB, ref StatusMain); - // set direction - SetBit(MSR_DIO, ref StatusMain); - // RQM ready - SetBit(MSR_RQM, ref StatusMain); - - // move to result phase - ActivePhase = Phase.Result; - - break; - } - } /// - /// Sets everything up ready to return the standard 7 byte result format + /// Moves active phase into idle /// - private void GetResult() - { - GetResult(ResultType.Standard); - } - - */ - - /// - /// Moves active phase into idle - /// public void SetPhase_Idle() { ActivePhase = Phase.Idle; @@ -4176,14 +2799,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum UnSetBit(MSR_DIO, ref StatusMain); UnSetBit(MSR_CB, ref StatusMain); UnSetBit(MSR_EXM, ref StatusMain); - - // active direction - //UnSetBit(MSR_DIO, ref StatusMain); - // CB - //SetBit(MSR_CB, ref StatusMain); - // RQM - //SetBit(MSR_RQM, ref StatusMain); - CommCounter = 0; ResCounter = 0; } @@ -4205,182 +2820,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum CommCounter = 0; ResCounter = 0; } - /* - /// - /// Runs the execution phase - /// This is called from the Drive Cycle routine - /// - private void ExecutionPhase() - { - // are we currently searching for a sector? - if (IndexPulseCounter > 0) - { - // if the drive ready? - if (!ActiveDrive.FLAG_READY) - { - ActiveStatus = Status.DriveNotReady; - ActiveCommand.CommandDelegate(InstructionState.StartResult); - return; - } - - // is the head loaded? - if (HLT_Counter > 0) - { - return; - } - - SectorDelayCounter--; - - if (SectorDelayCounter < 0) - { - // get next sector - var ns = FDD_CurrentSector + 1; - - if (ns < 0) - { - ActiveStatus = Status.SectorNotFound; - } - } - - - return; - } - - // still bytes to process? - if (ExecCounter > 0) - { - if (SectorDelayCounter > 0) - { - if (--SectorDelayCounter <= 0) - { - // we are at the sector data - SetBit(MSR_RQM, ref StatusMain); - } - - return; - } - - // IO sector transfer - if (GetBit(MSR_RQM, StatusMain)); - { - // overrun - SetBit(SR1_OR, ref Status1); - - if ((ActiveCommand.CommandCode & 19) == 1) - { - ResBuffer[ActiveCommandParams.SectorLength - ExecCounter] = 0; - } - } - - // decrement ExecCounter - ExecCounter--; - - // has transfer completed? - if (ExecCounter <= 0) - { - SectorDelayCounter = 2; - } - else - { - // next byte - SetBit(MSR_RQM, ref StatusMain); - } - return; - } - - if (SectorDelayCounter > 0) - { - if (--SectorDelayCounter > 0) - { - // data crc delay - return; - } - } - - // check interrupt status - if (ActiveDrive.IntStatus >= 0xc0) - { - ActiveStatus = Status.DriveNotReady; - return; - } - - // is this a write command? - if ((ActiveCommand.CommandCode & 19) == 1) - { - byte sr1 = 0; - byte sr2 = (byte)((ActiveCommand.CommandCode & 8) << 3); - - // write sector !!! - // assume no error for now !!! - - sr1 |= (byte)(sr1 & 0x25); - sr2 |= (byte)(sr2 & 0x21); - } - - // increment sector id - } - - /// - /// Unloads head in execution - /// - private void CheckUnloadHead() - { - if (ActivePhase != Phase.Idle && ActivePhase == Phase.Execution) - { - // unload head - HUT_Counter = HUT; - HLT_Counter = 0; - } - } - /* - /// - /// Starts processing the result phase - /// - private void ResultPhase() - { - if (ActivePhase != Phase.Idle) - { - if (ActivePhase == Phase.Execution) - { - // unload head - HUT_Counter = HUT; - HLT_Counter = 0; - } - - if (ActiveStatus == Status.Invalid) - { - // default error st0 - ResBuffer[0] = 0x80; - ResLength = 1; - } - else - { - ActiveCommand.CommandDelegate(InstructionState.ProcessResult); - } - } - - // are there still result bytes to return? - if (ResCounter < ResLength) - { - // set result phase - ActivePhase = Phase.Result; - // active direction - ActiveDirection = CommandDirection.OUT; - } - else - { - // all result bytes have been sent - // move to idle - ActivePhase = Phase.Idle; - // active direction - ActiveDirection = CommandDirection.IN; - } - - // set RQM flag - FDC_FLAG_RQM = true; - } - */ - #endregion } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs index bb2030ce26..d5e7d9f4d7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs @@ -227,12 +227,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public bool FLAG_WRITEPROTECT = false; - /// - /// Storage for recal steps - /// One step for each indexpulse (track index) until track 0 - /// - //public int RecalibrationCounter; - /// /// Storage for seek steps /// One step for each indexpulse (track index) until seeked track @@ -311,21 +305,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int SectorIndex; - /// - /// The current command that the drive is processing - /// - //public DriveMainState CurrentState = DriveMainState.None; - - /// - /// Current seek state - /// - //public SeekSubState SeekState = SeekSubState.Idle; - - /// - /// Seek int state - /// - //public SeekIntStatus SeekIntState = SeekIntStatus.Normal; - /// /// The currently loaded floppy disk /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs index 12f64af0f9..4fd33e062e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs @@ -95,7 +95,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum StatesPerDriveTick = TStatesPerDriveCycle; } - + /* /// /// Called every cycle by the emulated machine /// Simulates the floppy drive and updates execution phase bits @@ -117,6 +117,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //RunDriveCycle(); } + */ /// /// Called by reads to the main status register From a0e5e0e593d25171312d1fc9dcb46e96d7b93542 Mon Sep 17 00:00:00 2001 From: feos Date: Thu, 10 May 2018 20:32:26 +0300 Subject: [PATCH 215/339] tastudio: actually don't drop state above last edited frame --- BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs | 4 ++-- BizHawk.Client.Common/movie/tasproj/TasStateManager.cs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs index 65156ea512..4219f31eb0 100644 --- a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs +++ b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs @@ -79,7 +79,7 @@ namespace BizHawk.Client.Common { continue; } - else if (currentFrame % _step > 0) + else if ((currentFrame % _step > 0) && (currentFrame + 1 != _tsm.LastEditedFrame)) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything if (_tsm.RemoveState(currentFrame)) @@ -120,7 +120,7 @@ namespace BizHawk.Client.Common { continue; } - else if (currentFrame % _step > 0) + else if ((currentFrame % _step > 0) && (currentFrame + 1 != _tsm.LastEditedFrame)) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything if (_tsm.RemoveState(currentFrame)) diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index 0c02cb910b..3b2920814c 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -505,6 +505,7 @@ namespace BizHawk.Client.Common } public int StateCount => _states.Count; + public int LastEditedFrame => _movie.LastEditedFrame; public bool Any() { From 678666ddf0667422db59e18d59b593e43b967fa2 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 10 May 2018 15:31:06 -0400 Subject: [PATCH 216/339] GBHawk: Fix a bug in I Flag check --- BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 208615ced4..c49ae6b72c 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -151,6 +151,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 FetchInstruction(ReadMemory(RegPC++)); } instr_pntr = 0; + I_use = false; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -302,7 +303,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { INTERRUPT_(); Halt_bug_3 = false; - Console.WriteLine("Hit this"); + Console.WriteLine("Hit INT"); } } else @@ -335,6 +336,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RegPC++; FetchInstruction(ReadMemory(RegPC)); Halt_bug_3 = false; + Console.WriteLine("Hit un"); } else { From 05352235924b2517de757abb21c3351adcf4e821 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 11 May 2018 15:48:21 -0400 Subject: [PATCH 217/339] GG: Terminator needs SRAM for background display --- Assets/gamedb/gamedb_sega_gg.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/gamedb/gamedb_sega_gg.txt b/Assets/gamedb/gamedb_sega_gg.txt index a8a1d105bb..83b92ad5f6 100644 --- a/Assets/gamedb/gamedb_sega_gg.txt +++ b/Assets/gamedb/gamedb_sega_gg.txt @@ -432,7 +432,7 @@ A438F5C42A849E8224F8D3AFE010655E Tatakae! Pro Yakyuu Twin League (J) GG Sports; D5D52E216698B34A3DBE95D5990C52C8 Tempo Jr. (W) GG World C7570D82D91DC353D7DF445453FC0646 Tengen World Cup Soccer (UE) GG Sports;Soccer GGLink USA;Europe 67388B1C9758E7F0C0957EB9A788611A Terminator 2 - Judgment Day (W) GG World -B9190F130F728BA8C05D82D5F01FDE21 The Terminator (UE) GG USA;Europe +B9190F130F728BA8C05D82D5F01FDE21 The Terminator (UE) GG SRAM=8192 USA;Europe 5E203CD85B4BC7D81B54D8B407E0F307 Tesserae (UE) GG USA;Europe 082DBE19BCFB1A9D16423BC79E940826 Tintin in Tibet (E) (En,Fr,De,Es,Nl,Sv) GG Europe C0246EED0C11EBE7A9B144A00ED61DF1 Tom and Jerry - The Movie (UE) GG USA;Europe From 942454e32755353e3cdf605a91b1c204c8dca60a Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 11 May 2018 20:08:42 -0400 Subject: [PATCH 218/339] SMS/GG: Implement SMS compatibility mode for some games (Technically the screen is supposed to be modified too but not implementing yet) --- Assets/gamedb/gamedb_sega_sms.txt | 6 +++--- .../Consoles/Sega/SMS/SMS.IEmulator.cs | 8 ++++++-- .../Consoles/Sega/SMS/SMS.Input.cs | 4 ++-- BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs | 16 +++++++++++++++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Assets/gamedb/gamedb_sega_sms.txt b/Assets/gamedb/gamedb_sega_sms.txt index 6701daef48..d185513659 100644 --- a/Assets/gamedb/gamedb_sega_sms.txt +++ b/Assets/gamedb/gamedb_sega_sms.txt @@ -137,7 +137,7 @@ C28AA80489A467BF54FC1403029A83D3 Champions of Europe (E) SMS Sports;Soccer Eur 1F706162F833F1DCE9513988601D314B Championship Hockey (E) SMS Sports;Hockey Europe 2C709BA8CDC3A1B7ADC5BC838EABF6F3 Chapolim x Dracula - Um Duelo Assustador (B) SMS Brazil 08511C5CF0DF0941B10EBF28050AFE51 Chase H.Q. (E) SMS Racing Europe -3CD1A4C27E330BBA7703EE22AC83B856 Chase H.Q. (J) (SMSGG) SMS Racing Japan +3CD1A4C27E330BBA7703EE22AC83B856 Chase H.Q. (J) (SMSGG) SMS Racing GG_in_SMS Japan F90B86478445D220D386460E55F3B74F Cheese Cat-astrophe Starring Speedy Gonzales (E) (En,Fr,De,Es) SMS Europe 747A206EAAADF48714695E8B4CCEAD7E Choplifter (UE) SMS StereoByte=203 USA;Europe B556297E5BA2B95ED3F3A76A47402E1A Choplifter (U) (Beta) SMS StereoByte=203 USA @@ -368,7 +368,7 @@ A31CBBDED45F66633FB38B1C1BEF9B08 Opa Opa (J) SMS Japan 2CA2064302F51F724E1F2593369A0696 Operation Wolf (E) SMS Light Gun;Arcade Europe F64EA159120C018E05FB95AC8533E9EB The Ottifants (E) (En,Fr,De,Es,It) SMS Europe 558C793AAB09B46BED78431348191060 Out Run 3-D (E) SMS Racing;PaddleSupported 3D;FM;PaddleOptional Europe -2A3BDD1A6C35EEEDBF1A794ABFB57B87 Out Run Europa (U) (SMSGG) SMS Racing USA +2A3BDD1A6C35EEEDBF1A794ABFB57B87 Out Run Europa (U) (SMSGG) SMS Racing GG_in_SMS USA 458FC29765865FDAAF3F56808C94D8A6 Out Run Europa (E) SMS Racing Europe 029EE92155247F8A282D63B8A6DD23C4 Out Run (W) SMS Racing;Arcade FM;WhenFMDisablePSG;PaddleOptional World 946F3E6C2F0F546A8EBE55C8170ECC78 Pac-Mania (E) SMS Arcade Europe @@ -409,7 +409,7 @@ FC40576778DE28CC254B588A3A46D2A6 Putt & Putter (E) (Beta) SMS Europe 58B89D62438407F242BBE713F1D945CA Quest for the Shaven Yak Starring Ren Hoek & Stimpy (B) SMS Brazil B30E60B91960A0F2A89FC133410770DF R.C. Grand Prix (UE) SMS Racing USA;Europe 61AA7404E23836C79E2A689322FFE190 R.C. Grand Prix (UE) (Beta) SMS Racing USA;Europe -D087B25D96F3F3E9338B0B5EC4FC2AA5 R.C. Grand Prix (UE) (SMSGG) SMS Racing USA;Europe +D087B25D96F3F3E9338B0B5EC4FC2AA5 R.C. Grand Prix (UE) (SMSGG) SMS Racing GG_in_SMS USA;Europe FFBA9869948E2CEF2BDA6F805CC72087 Rainbow Islands - Story of the Bubble Bobble 2 (E) SMS Europe E80AE00D8924F2BADA5949BF75995D87 Rainbow Islands - The Story of Bubble Bobble 2 (B) SMS Brazil 7A080A155AD6A806DA88AA1D3576D78C Rambo - First Blood Part II (U) SMS USA diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs index 59ab099595..e848c03d2d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs @@ -10,7 +10,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem { get { - if (IsGameGear) + if (IsGameGear_C) { return GGController; } @@ -58,10 +58,14 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem Cpu.TraceCallback = null; } - if (IsGameGear == false) + if (IsGameGear_C == false) { Cpu.NonMaskableInterrupt = controller.IsPressed("Pause"); } + else + { + Cpu.NonMaskableInterrupt = controller.IsPressed("P1 Start"); + } if (IsGame3D && Settings.Fix3D) { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs index ffbe65cd1b..58f1a4683c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs @@ -587,14 +587,14 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem byte ReadPort0() { - if (IsGameGear == false) + if (IsGameGear_C == false) { return 0xFF; } byte value = 0xFF; if ((_controller.IsPressed("Pause") && !IsGameGear) || - (_controller.IsPressed("P1 Start") && IsGameGear)) + (_controller.IsPressed("P1 Start") && IsGameGear_C)) { value ^= 0x80; } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs index b0bfe21403..d0e5e4e27c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs @@ -35,6 +35,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); IsGameGear = game.System == "GG"; + IsGameGear_C = game.System == "GG"; IsSG1000 = game.System == "SG"; RomData = rom; @@ -91,6 +92,17 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem OnExecFetch = OnExecMemory }; + + if (game["GG_in_SMS"]) + { + // skip setting the BIOS because this is a game gear game that puts the system + // in SMS compatibility mode (it will fail the check sum if played on an actual SMS though.) + IsGameGear = false; + IsGameGear_C = true; + game.System = "GG"; + Console.WriteLine("Using SMS Compatibility mode for Game Gear System"); + } + Vdp = new VDP(this, Cpu, IsGameGear ? VdpMode.GameGear : VdpMode.SMS, Region); (ServiceProvider as BasicServiceProvider).Register(Vdp); PSG = new SN76489(); @@ -150,7 +162,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem Port3E = 0xF7; // Disable cartridge, enable BIOS rom InitBiosMapper(); } - else if (game.System == "SMS") + else if ((game.System == "SMS") && !game["GG_in_SMS"]) { BiosRom = comm.CoreFileProvider.GetFirmware("SMS", RegionStr, false); @@ -212,6 +224,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem private SN76489 PSG; private YM2413 YM2413; public bool IsGameGear { get; set; } + public bool IsGameGear_C { get; set; } public bool IsSG1000 { get; set; } private bool HasYM2413 = false; @@ -309,6 +322,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem port &= 0xFF; if (port < 0x40) // General IO ports { + switch (port) { case 0x00: return ReadPort0(); From efc35a5a0df68e76a7c500634c9b662200717669 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 12 May 2018 11:25:51 -0400 Subject: [PATCH 219/339] Gambatte: Fix halt behaviour Fixes #1187 --- libgambatte/src/cpu.cpp | 20 ++++++-------------- libgambatte/src/memory.cpp | 26 ++++++++++++++------------ libgambatte/src/memory.h | 4 +++- libgambatte/src/tima.cpp | 20 +++++++++++++++++++- libgambatte/src/tima.h | 4 +++- output/dll/libgambatte.dll | Bin 185856 -> 185856 bytes 6 files changed, 45 insertions(+), 29 deletions(-) diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 0fcebe0ca6..59d88303d2 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -633,15 +633,6 @@ void CPU::process(const unsigned long cycles) { //Halt CPU and LCD display until button pressed: case 0x10: { - unsigned char followingByte; - PEEK(followingByte, PC); - PC = (PC + 1) & 0xFFFF; - - //if (followingByte != 0x00) { - //memory.di(); - //memory.blackScreen(); - //} - cycleCounter = memory.stop(cycleCounter); if (cycleCounter < memory.nextEventTime()) { @@ -1164,13 +1155,14 @@ void CPU::process(const unsigned long cycles) { //halt (4 cycles): case 0x76: - if (!memory.ime() && (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F)) { - if (memory.isCgb()) - cycleCounter += 4; + if (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F) { + if (memory.ime()) + PC = (PC - 1) & 0xFFFF; else skip = true; - } else { - memory.halt(); + } + else { + memory.halt(cycleCounter); if (cycleCounter < memory.nextEventTime()) { const unsigned long cycles = memory.nextEventTime() - cycleCounter; diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index a5fcc148ef..46dcc6e28a 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -154,6 +154,9 @@ unsigned long Memory::event(unsigned long cycleCounter) { switch (intreq.minEventId()) { case UNHALT: + nontrivial_ff_write(0xFF04, 0, cycleCounter); + PC = (PC + 1) & 0xFFFF; + cycleCounter += 4; intreq.unhalt(); intreq.setEventTime(DISABLED_TIME); break; @@ -265,8 +268,12 @@ unsigned long Memory::event(unsigned long cycleCounter) { display.update(cycleCounter); break; case INTERRUPTS: + if (stopped) { + intreq.setEventTime(DISABLED_TIME); + break; + } if (halted()) { - if (gbIsCgb_) + if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) cycleCounter += 4; intreq.unhalt(); @@ -311,7 +318,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { } unsigned long Memory::stop(unsigned long cycleCounter) { - cycleCounter += 4 << isDoubleSpeed(); + cycleCounter += 4; if (ioamhram[0x14D] & isCgb()) { sound.generate_samples(cycleCounter, isDoubleSpeed()); @@ -329,17 +336,11 @@ unsigned long Memory::stop(unsigned long cycleCounter) { // otherwise, the cpu should be allowed to stay halted as long as needed // so only execute this line when switching speed intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); + intreq.setEventTime(cycleCounter + 0x20000); } else { - if ((ioamhram[0x100] & 0x30) == 0x30) { - di(); - intreq.halt(); - } - else { - intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); - } + stopped = true; + intreq.halt(); } return cycleCounter; @@ -651,6 +652,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned case 0x04: ioamhram[0x104] = 0; divLastUpdate = cycleCounter; + tima.resTac(cycleCounter, TimaInterruptRequester(intreq)); return; case 0x05: tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); @@ -660,7 +662,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned break; case 0x07: data |= 0xF8; - tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq)); + tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq), gbIsCgb_); break; case 0x0F: updateIrqs(cycleCounter); diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 95a2e18f16..658424f151 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -42,8 +42,10 @@ class Memory { bool cgbSwitching; bool agbMode; bool gbIsCgb_; + bool stopped; unsigned short &SP; unsigned short &PC; + unsigned long halttime; void (*readCallback)(unsigned); void (*writeCallback)(unsigned); @@ -128,7 +130,7 @@ public: return cc < intreq.eventTime(BLIT) ? -1 : static_cast((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); } - void halt() { intreq.halt(); } + void halt(unsigned long cycleCounter) { halttime = cycleCounter; intreq.halt(); } void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } void di() { intreq.di(); } diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 5272443ba2..7b206c3e67 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -119,7 +119,7 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T tma_ = data; } -void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) { +void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq, bool gbIsCgb) { if (tac_ ^ data) { unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); @@ -142,6 +142,15 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T if (data & 4) { lastUpdate_ = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; + unsigned long diff = cycleCounter - basetime_; + + if (gbIsCgb) { + if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0) + tima_++; + + } + + lastUpdate_ = basetime_ + ((diff >> timaClock[data & 3]) << timaClock[data & 3]); nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3; } @@ -151,6 +160,15 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T tac_ = data; } +void Tima::resTac(unsigned long const cycleCounter, TimaInterruptRequester timaIrq) { + basetime_ = cycleCounter; + + if (tac_ & 0x04) { + setTac(tac_ & ~0x04, cycleCounter, timaIrq, false); + setTac(tac_ | 0x04, cycleCounter, timaIrq, false); + } +} + unsigned Tima::tima(unsigned long cycleCounter) { if (tac_ & 0x04) updateTima(cycleCounter); diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index a3c0c995bb..a66f987837 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -34,6 +34,7 @@ public: }; class Tima { + unsigned long basetime_; unsigned long lastUpdate_; unsigned long tmatime_; @@ -55,7 +56,8 @@ public: void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq); + void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); + void resTac(unsigned long cc, TimaInterruptRequester timaIrq); unsigned tima(unsigned long cc); void doIrqEvent(TimaInterruptRequester timaIrq); diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 20e91644161b286b91edcc243f6fa79ba422aa58..892f994e295f5505313d018699cc7c84f7467825 100644 GIT binary patch delta 67278 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 delta 67858 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 vKUAVXgyi0?0$ Date: Sat, 12 May 2018 11:49:36 -0400 Subject: [PATCH 220/339] Remove errant ReadMe file. --- ReadMe.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 ReadMe.txt diff --git a/ReadMe.txt b/ReadMe.txt deleted file mode 100644 index bc60bf4b01..0000000000 --- a/ReadMe.txt +++ /dev/null @@ -1 +0,0 @@ -TODO: From c49c585029131836c5bc15bf75f1f7256b8b1489 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 12 May 2018 12:05:09 -0400 Subject: [PATCH 221/339] Revert "Gambatte: Fix halt behaviour" This reverts commit efc35a5a0df68e76a7c500634c9b662200717669. --- libgambatte/src/cpu.cpp | 20 ++++++++++++++------ libgambatte/src/memory.cpp | 26 ++++++++++++-------------- libgambatte/src/memory.h | 4 +--- libgambatte/src/tima.cpp | 20 +------------------- libgambatte/src/tima.h | 4 +--- output/dll/libgambatte.dll | Bin 185856 -> 185856 bytes 6 files changed, 29 insertions(+), 45 deletions(-) diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 59d88303d2..0fcebe0ca6 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -633,6 +633,15 @@ void CPU::process(const unsigned long cycles) { //Halt CPU and LCD display until button pressed: case 0x10: { + unsigned char followingByte; + PEEK(followingByte, PC); + PC = (PC + 1) & 0xFFFF; + + //if (followingByte != 0x00) { + //memory.di(); + //memory.blackScreen(); + //} + cycleCounter = memory.stop(cycleCounter); if (cycleCounter < memory.nextEventTime()) { @@ -1155,14 +1164,13 @@ void CPU::process(const unsigned long cycles) { //halt (4 cycles): case 0x76: - if (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F) { - if (memory.ime()) - PC = (PC - 1) & 0xFFFF; + if (!memory.ime() && (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F)) { + if (memory.isCgb()) + cycleCounter += 4; else skip = true; - } - else { - memory.halt(cycleCounter); + } else { + memory.halt(); if (cycleCounter < memory.nextEventTime()) { const unsigned long cycles = memory.nextEventTime() - cycleCounter; diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 46dcc6e28a..a5fcc148ef 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -154,9 +154,6 @@ unsigned long Memory::event(unsigned long cycleCounter) { switch (intreq.minEventId()) { case UNHALT: - nontrivial_ff_write(0xFF04, 0, cycleCounter); - PC = (PC + 1) & 0xFFFF; - cycleCounter += 4; intreq.unhalt(); intreq.setEventTime(DISABLED_TIME); break; @@ -268,12 +265,8 @@ unsigned long Memory::event(unsigned long cycleCounter) { display.update(cycleCounter); break; case INTERRUPTS: - if (stopped) { - intreq.setEventTime(DISABLED_TIME); - break; - } if (halted()) { - if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) + if (gbIsCgb_) cycleCounter += 4; intreq.unhalt(); @@ -318,7 +311,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { } unsigned long Memory::stop(unsigned long cycleCounter) { - cycleCounter += 4; + cycleCounter += 4 << isDoubleSpeed(); if (ioamhram[0x14D] & isCgb()) { sound.generate_samples(cycleCounter, isDoubleSpeed()); @@ -336,11 +329,17 @@ unsigned long Memory::stop(unsigned long cycleCounter) { // otherwise, the cpu should be allowed to stay halted as long as needed // so only execute this line when switching speed intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000); + intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); } else { - stopped = true; - intreq.halt(); + if ((ioamhram[0x100] & 0x30) == 0x30) { + di(); + intreq.halt(); + } + else { + intreq.halt(); + intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); + } } return cycleCounter; @@ -652,7 +651,6 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned case 0x04: ioamhram[0x104] = 0; divLastUpdate = cycleCounter; - tima.resTac(cycleCounter, TimaInterruptRequester(intreq)); return; case 0x05: tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); @@ -662,7 +660,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned break; case 0x07: data |= 0xF8; - tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq), gbIsCgb_); + tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq)); break; case 0x0F: updateIrqs(cycleCounter); diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 658424f151..95a2e18f16 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -42,10 +42,8 @@ class Memory { bool cgbSwitching; bool agbMode; bool gbIsCgb_; - bool stopped; unsigned short &SP; unsigned short &PC; - unsigned long halttime; void (*readCallback)(unsigned); void (*writeCallback)(unsigned); @@ -130,7 +128,7 @@ public: return cc < intreq.eventTime(BLIT) ? -1 : static_cast((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); } - void halt(unsigned long cycleCounter) { halttime = cycleCounter; intreq.halt(); } + void halt() { intreq.halt(); } void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } void di() { intreq.di(); } diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 7b206c3e67..5272443ba2 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -119,7 +119,7 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T tma_ = data; } -void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq, bool gbIsCgb) { +void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) { if (tac_ ^ data) { unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); @@ -142,15 +142,6 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T if (data & 4) { lastUpdate_ = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; - unsigned long diff = cycleCounter - basetime_; - - if (gbIsCgb) { - if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0) - tima_++; - - } - - lastUpdate_ = basetime_ + ((diff >> timaClock[data & 3]) << timaClock[data & 3]); nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3; } @@ -160,15 +151,6 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T tac_ = data; } -void Tima::resTac(unsigned long const cycleCounter, TimaInterruptRequester timaIrq) { - basetime_ = cycleCounter; - - if (tac_ & 0x04) { - setTac(tac_ & ~0x04, cycleCounter, timaIrq, false); - setTac(tac_ | 0x04, cycleCounter, timaIrq, false); - } -} - unsigned Tima::tima(unsigned long cycleCounter) { if (tac_ & 0x04) updateTima(cycleCounter); diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index a66f987837..a3c0c995bb 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -34,7 +34,6 @@ public: }; class Tima { - unsigned long basetime_; unsigned long lastUpdate_; unsigned long tmatime_; @@ -56,8 +55,7 @@ public: void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); - void resTac(unsigned long cc, TimaInterruptRequester timaIrq); + void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq); unsigned tima(unsigned long cc); void doIrqEvent(TimaInterruptRequester timaIrq); diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 892f994e295f5505313d018699cc7c84f7467825..20e91644161b286b91edcc243f6fa79ba422aa58 100644 GIT binary patch delta 67858 zcmb?^3w(@6_y6;35<(Iims}78ai>yL6O=58F5=cws#;o_>eZ!DRCyAcG+B=neXv?> z>0L`z8%+=-Q6yDGP(?37TivJWMf+C8HUICKnP)G_2Jid(=kqz)nKNh3oH^&rnaeZJ zZraA6w2eXQ}EROOtS~JHdQv$HU>T@zpd4; zcN^jQ)MK~@>n)LUwO=`|S^YLd*T4Qs`J{HNKBMywVsssXo-);2p{)#J4EF>vLT6BK z`3J34aJHPJtq*P?zpw2Jo^N{c8*NZXe`PCrVNs9h|EXYogd+5B;1r9qrLOIzDuq^V7?cD_M> z`D|`?vrTTg8Tz-V=he$r^`tekDAiv*RCU;DD=5{z4DBkHYL%fmrn}3utcKAoiip&l z_m_IvqB?7k?`arYuM`w%1*=0OExl=Pt!JZlrqsjQghrzy%g&MO+P0$HyOGZuQ6uLY zH5n8-&T9KDI%iRuR8_^Ku~jP1hUP4iOjT8h*%^VWF_0<(oUM<51;hgJZPRPSSS&o#|zRIa-je97C82VQGax23v@5ny_=_4mZ zWo7F5rj!eKl;vzEC}-DJvkx_m=i>NBB>$+4Boeipeb`FbhnCGhrtuFe|LACDpK2)k zuvJEA_cm#-bf{yZp=h;Fj0t3_w5q49>Lus5A0Zm}*|W%j&H01;Q|wvfqDa<3lJsQG z8x)wGMV&^fUUn9FC{p#av&cb_3d+tR|3oS{JBw6e&Knk@7GFHvE{(L7MUL4=Ew+_U zww(zrF88-*kw%Np_}iC~LQ$_lb{07+Qs(R|@>Qflt?I;>*zBxtv>ss{2KL>A?)Thd zwP#L4SWQ%S!@<<$B(!t&Cz6!Z^C#Fi(f;;Ngh|;aNw$LD-~&~OYA)+p#v*M^SpU9O z#AQ=zTU8B4BC+pZX4}sT;k8{3LfjL!azDS~M8ETT8E}bxbBV@m+k=cbU)!NI36E|> zW*(v*$ShiDNmh&CvM02`;jOx!06M8>kwrbO24~w<5`tWA*=F#GXEK>H708_&|F%Ql*zNPP>|V=XIL=EOyC(74l)yYh6RL7E6=cykeQ5(Y9o!R zDk-=foK#9bBh`_lpi+xkQmp$Y#=T9(uqfuFJqQyyqj-H5C^ENiyJf&J)0WlEKewULt(i-r@-c_sGD$y}aQ;c%$TsmN4m zPROBe2j5#1ts}MY_`h7VjzYAMn64hOX@4d6mm5H?7-ilUk|YE%=T%y4`$&1Dj?@Mz zaxTf2n9cGeUsdT{*CB{G>&^KPCnxNwQ)p$ix@6IoH<9<-Uv=gtGI~X-XD(D#X^%7w zN34Q8rm%mtqR6)6(@G^N+eS`c=;y{3F<8b?w{!pD-HxMfhr%NLEL)*OXpy%u;evMi z@CZ$97SeT33AIC>C)!E!yP$_W&zzGF_yQ_SBwy7mah;WxKQI$`1#iPG3?ULoh|m7R zg-9@jNVIIla)^Y0Q;>@Fw0W*}s)~4Ti=BZI6NeE@;sh)PZFdIQZF>|516c@DRb;o3 ze{=EmUT)eDDHx7pqhVptmR200VGRssW_Tb;I2D@r>N{)mx4BFx8kv4*fkqgFs?QSH zwuyx2K4gEJ8sIYM`BbZV1O8~hP;1U$e|I@rS1X-A!%U)BJ#WrecWKtB+sA6(Ra5Z6 zs>2=%-bYiMmBjD-H&?LLP;j!Rg2`fqkS}MjY|gLLELm;4G_TGXle-6JHhGRB&Y%9J zMYV`j$WCyFT~(MJ^?7rcT6nlt$u;~+?JAGx#n~#y+gNO|v3ZpnNo6F6O9%;wK`c~3 zGNR)}#d@G$sj!lpBWYWKWr>bi<;ru^yv95skBy7svw^T<&|wmIBvg^jf+-_D$cPzL zRjJFbV&sl)&)VQ7F>}vCOAv!PCLD{}r>vglruFtLM(oS8HZyssSlE||703CI2dSmS zX97tdq`)#gTP37EE=JVFO4^RX`*FoUC)p)?mUI10lRloxS$zGbnF%znM{pb_(LmdMQbvObFcl0 z7!=dB4r563n=w!je@3Po=7 zjMkUOya5Ab+o;&nW7>lcA0}{3auFIWMg47OWFDC67wfU;&@CEa2C3uRZ`z^Oh7odp zhgBoA0d0bn%0kS5mYyXkHm8yzvniRTf&sHUhdpVNcpQ86EXwKMs^TkvLHju`qQU9*# zXv^9~ zh!FCXZnQZzZU^azCZSu4OM>MxV)>Mlb!?gF*b48Ch3PC#DFxI3`KCvlDt+2xa1>CX zcbqy|R%@I}6%wo_31sZc+E37<=l<=yOd1MCs{;*Y?YeQJGon@h((=e5z94z zlt4~F2(Pg~2=`cE%)Wn$b%oO25sT6KidZ4=2QQ5Jvxx-dRj7uzo%^4%1Q6BU*Q!loh)#R?tEH~N8DTWu8sd55=G zHT@A@6|T)s3mKh3i@5XuRjewa26mib4HSt6_INk&=viZ57k_ArE7W2dPbn@BObOb@ zR`Mx!b_AgZJ?3&@?ryH&)wy%jmp2VurA$5LntWUM-Lz52xw}g5e~B$t>4bxsX;um~ z!2+XCPu_#xa35oys6BLB=T1-JPykl)xxW;uW=noQ?UUOgEMkk2&h`}&&cyYSM{nF_ zQ+J!C*t=1iN)dMdH)-<(b$9$}ru`E_``>xSX=qtUfxV|5MN(AX4nB^ig~>7S$?dsv zv`@QtRl;sALpjOt(GN_9av{S{)nw4;Po<$j3ji7gI3uH2t3L{?du*69BDd^4f%<`^y+quIcBpOjNrOo8^xn!*VB#z-aqn7mH9K$7Jn7$mtC1aRQ977!V@==c< zjKY^O0u{iBYE#<%OfureBs=jC(-4=8wXu(5xMb90eGA8M$r$D>j^UCWZ-xVVeHlk_ z(HPM}jv=Oe{h~j`Fttfmbdq#NRheW1cy%rrW7v&jDD5%pLQri`T*qnsaT-#~L}bnw zj71b*np9F?Ir0sg2r3bh%^96hrVhXAITz`ZCs57Qe&Hrvz`UA#_F)ff@?H3XQHp39izq` z&Mvw^&z510#?=@~jYaXsLRn*}C{u?wsnOWa54akm9p2a4fw*qdE*IXQ6_cx#NNVM! zuUVHOSu5wT4p4_j)o5iLYsIyXB%@)?Z_$?bj!~YuQDe`6A@5Q&HD=na$esq7J(YD)y4Oj`o}r;b!@{qSkG1>%3AeM21oh~XcR_^?jVn&3la6!96F6U#@7 zdC=E(EtHk+R+rcaXJ@CJ?5s129^dJ=!^_2X$7Ar&a|wPf*m>6tEYhRgP*O+g=B}g6 zA|08M!cnFU?^;7iS@$!$^Eg7ISf9-?JdQBT6ple0p(%`^MspO8BMdWuV|W~4m~I?H zMi#Z2GfZufW%@CSEL4>Z>_5gc$tZ8^HC2V=G>+KWp2)AvuJN;=-FA&VE8$zEPkhv- zE>#pmy9!pTW%KFX^+4INM&>UN{_}rzl#VOh%&En(*2d{pm6>n5) zoGrCbot{nAc+x?Fi32)OAGJx>`ox-wV#k=i1Q9LPU_|E88w*C%zTpgP-uA_b zXmm;6f4zoE*%87GIgq(s9I~%hDb=mQCc+!CU@=bUMIX$BsLerA%X~P97jvbQJAf*D zp4q$j*Ff7@+3&Fa@70=JbQ{OpZZ0Je0q$7}#6&rr{-*EUfJ?ulfdOOsy zih~vij%VQ1cWf%DfBLgyyt{$tBEBB_nYZipg<;rEPK9$vuKqXoV}Z)g>pXPti%$Tx%42adufWtx zTiA*bKDtVCRG<%)@A5@VD)&Xa+!rxv+(pceu(&hmQ+-jB%3aia7G(qRPG7{Na$m&t zeG!wx2P!_g`2Fx3sr>dYe5u@5t1B5FFL!6&o+u7jXQNzkjed^{FkVYoV`;$G1s*Gc zJ3ZNp+GL1|qUtfEev3A08YoRh%~g}DlKt697y7qp{NigxCynr!|T;IbQi4%?V^KRt* zy~Z={DUHxbwFf!uJ{xXnj2(Kb{Ys!F<9L$^qRB+>CeQ3K`ZYS);nA;K>ywol$oQP~ z$toI1b~i9$SpP=d(7b0y^eXL*VKMzrS#jVE%ks;i^HzEHRxd!Y%aAkMl6$essX2F> z66uE9@)sI+rpH0X>H8=WWaVxq$S5Jm7`Gr~!Tz7Sdiuf2|GKB+sev=atby^Ofr;J? zEZ$9*2fF>Ug7+m!Ex}Vb3)U@o2uHTtMOW+GPbq@YrCnP0`RzL2v!T|L45NROsrg@v zw6fvhX6mIfHngE@WDoA+(h0dC=k^x8Q$-@ef%PqU`_R2J(+f zGUT62Z9K%=n2R>d8GTWv4u8S39lZl^HJ-`Mh5f`M6}YwcueEHlu`Y;dAJcGX@Q)q>s2 zOu@uM9;8Q1H<$_k}mC_pJ#Z@X?I*WJZNAs&lcW`WT4So{{^sAvs=k*VkH9a`i) zQQ~&c;GJOG_Nh;n^0DXf_}hq@|5#XmZlOEtL<7ePSa&EZy7LZ_-rbqAgW6TjzJzv- ztLu549qAsZ!i#Fqk)#|sqs+a3O*LXe5YD;SB%0@Oz1di2=n1ec zKR`k)UPmqVsKsZmRD(NmehUghMpoT!bQ$_(;}oSc>!)yNgm3I06C zpzp@%Y4uT!-bGGj2Kn^?)Y;na4~&92{EAdO$7ib3*^OFKT_PSQ6({A4z&!jVZp_q! z7s7!Jw(A*Nwjioa3USQAR5*{3tL&hM9VFuFu24KPP#N8@E9R>XqlqMiSE4l>U+mRV zi3p5J4S5~1N9}cRmCw^)-}fgKsbmzdI@k-SUebA*?E_wk%;r_es}33>0z4u|;hf*4MIMWEy|fZCul=i!#Cr;(CMX1PH{&r!D&^~`y>#LMfDKrLXS z>|9{b`QkqV)#J8bWaroUGy-X)1yyq0VS!MBi|UH{rM-X@?Iat!2OnGU=-oU;BlF!r zG*9bZ=*yxXnU&iPZev_}CE-Lo4Js~EoZPRxhh-kGPxh>>K9!-qtGcJ{=kV%e)!M34 z8C6$|<~>~?j@Kva)>fa&Je&9QgiC!`@5wN=Ri`pmT@iDK+avJy6t7RVuB|?mc{cCq z7xP*5+6VJjpBaNWQMIxA+^~)%y`1hL3Ev1}Qd!EJiCjJ8IDEN36OQEe5#;V1vB>F)lyZN(fl4nPkbme_U$w~ad^R>} zxhheS-q=?qvUV+%s7U|e4kjO4f4BuC6W3CUiu4b7En2j=E0JAmsYFG3s;^39OH>+& zj~czAGJODQ-Dn6e-;9MxDU7NupVU+t33Cf|^YBO-*MI5d!w6)hTEwLy{ZmweNMsN0 zG9u%0gR%{5Wt7b>ptEPxc0P@OkPK3dP;^ghIK+Nq1aE{{iFpF3C{tTqDii(F8}Yi# zR-&%LA5$B#h)d{d*mK|>@5y?{Y-Th}nc5nrGNa*Fd0l2Yqb_BN%N4OAtky6U8kHYn zm1`fcn>SS-u!$-nG^rY6);SaAt~pBUafuGQ!#FxkR;;Z%o7aSK`v%1H&tXJ zP0b*!KUc+OU$Qr4ALChSlCtA@mKvt)5T0eX@+jYjv_DGY;P;~*WYK^+GN_bkt>EM7x0Q) zCV%}Io+X(qZTISvc$Q?M>?ocknUGa<3(u2KRNRASNhZp+#>f?AtuBTAliY)1$3l*)$^I~qiXuUJfa+#v_<~++~iq?a9mdg~aU*lH+xJ=Rd zA1qs&OaU&Lq7{7)ugQgq)<5G}E>nX39?x=_5_AX8a+wnJY@X#ZCFt{bmdli&Kf$wH zrUd-~maQg}f)IfhopHH()`y0o67<`7O)gV{-iBwnLzU=_d6qj=iE#^pJ5-5&f!{jd z{#2r$<5})cCHgl!%l)ZDFI8B+nous8PJh5AF7nHry0^ zo8-B!_hD%g=DL0xOOqtm^)@U`VqDk5S(;?HuITlVxzThv5i;1yuhU`(ag}c-I>La6 zsrtcg)GUEj^pB+xZIkEKbVYx;*QO#)rh-(YDH=$igAOS3C&*Yp=zh9tVCKacc{ z1Uj&u1iI!W&}tZu1nSTDkU-b;9xP1)UD4aKGzoM?Z^qIj&=tLb!ZIY$72U+rq@pYO zzg?+kB+wQ8SEO$wP(LQn6)%ATg+QAa9}?(_{uWDGzoN3Phn{i=%PLW=^F{O@qNLcJ$}{?z|qX%gs9{VYq9K!57TS(*g;Q{RvDjRcBf0{!VFkP})<@>4G8FEK_W z(VzMvmL`Et>(8+?33OV2f~85I)A~a!O#+?P?_p^Y=(IkVrAeUEdLN{3B+!!gNJFQS zaug4*9?vAYpkME7c=c)hZk+v+{NWa#B5s^gtUp!3`=|A%{ zNu-}d`bGkEfk2j^y)VjNfk28gh)ERfl86NQ25KV}EntjDplE$2 zOOrqe`lBpO8cNXbXK4~BK_AZ2Bv68GVQCU5LBE5gNuUJ16Vh&hX#GccT5^cjpXa|x zDoXGY=x!m<@9hk)PSDS=GzpZZf6dY)P@4V)OOrrp`c9T6fztF(SegV%)8A!jGz|aO zUqQw#QB8p=@NCZ#RN^I2KD0&}dXO<94W;R$k@opqHI5qay38Q1T39o+S6`?f#%?u; zN1f{>{;0E|_QCWP;z{S0wZ;R~39ayT9j*tcH@UDOIBpgk$GpLwZBkY|+uVYr_p{B5 zZx}BacYAmyf3A9~cN*A*f$@b|?2)U_w|j*=TMc<$XYx>1$g_jVgL}y0JEUCC({X9y ze0t|@cio(M5h{}hA9+pFXGTSP-*#Bhrn(*IQi+%POGSNLmY8LzKlF7&{ddiu{$H#A zu;93PjTZE*@$CBE*Iatu`ON>I=Wfk2Z>r}LM#HaaL8(zbu8?$&eFZld;6%QxWzNXP z4vXJTC>NYMYs*qw5BAonvlZ#I+81x#@tXV6#ojNfUApkTbJ6vCTdgt}wUA5VH_C2pgjb3_wF>8*qP*zTRn{rb z+ZfwcGjGPNwyrTxdU*3qZewVp=QM6L*1?oU4}1iFeMZUZ4d#fv7Z zSkEXcdKT=}GrCQdzsk_ugf@?RXztdG6e?O8x01C)SDt&9~+H^0nuKw0R( zoUs@bYEG|edeEN*x<-uK1N|8n%Rc^`$2c>3N7ueYCtVlIx<2*7O*@1YjMqYu0v4O`3^wBho7|VQz%*kyao#hq6MRr+Xc@1~G$$Zsnev^Gg%_ep$>kPg$Y)3?w}lM_UmRCH*Z# zj~apTGR!6dqgqL^o_0s+hqU$?os{P%vu{nNFpm4lIK>s$Uq!pac)QuWU312Jpipy` zRBM~2b~A68;*EIyV7h92GlTE-*GQDNW(zRd0zyEltCyM~NnQc%9GR$E>x{Nfh9s#6 z`9bqXlRV_TfSH@iyG+Qt(9ZNkS?I}}aTFA4&WdXCQjE|oXvno{byjCt_EowfUOx0bcbWcc%XAy00W7MK-P`%0fVbk~*Gq1Mt#gW+bu z5IdKw7X8WXwnmfV{v6Dz-onk`t%Aeh7qyBkBi=rng(%oqyS%KS^8Nj!%)>$hNj<+2 z-vFM}rOViH;@b_i;quUfadyPi_Er@)PUCI=P5GlA+ zni5o+VprKlGiCdqjXH<^$@%#a)!?&(vxubH?*nI&gP9EVxS<%pEDq+;Kc8 z)SP3;dWIm3gwh(W-K3>Y*V+NY z)lLZEV{A-#%8Ju!a4$P-Jl26|k=tB*gpi1&h z?cpU&>%M*u`;sXweMt-UDxYrRRXn2TBamLi#u{t9!6?gS(Of+wXw(w?48X-Q{0e}j z?IC_Q1!l4R63X9-EB?jb&MuYetLu_)v&#Y@Qk5i)yekd=pN&HBRu9t#>{q-R%bBxq z`8enU=*J)^XU@xrT#at?a(y_;jIW^d{07Q;bh}60t>zxkcILEdNf$8i0~&i$5@i-| z#lk%3JoiiSic8lMPu$eBPNDLrwP7!{2xWT^51w$4aP6g+8h5+z=A6z8P8EuF@TI6H ziIqjYD3%=`1o$xE<#e|n*KlKBFETy`E-nvqO*F2p$fO0>y51yeRthN_Da@5AD_r?1 zlAdGG)sUwvckcENHm)~mL&BPu>~3GE#V(7wi@9+uxp5+vch!PiEio=ou;{MYml^By zBMVt%ejvMHSU-4FjmWG;FK^DSuGS30=KS6Kb~|3S5Z_W|@guMG9g%6!FjtKN(X9y_ z$_4gwUVTB^x!j1#YZn-c_k{YR{->xMDHPFHTkx`>h#y`6*S5-#k?;oomI{q$EZ3p* zAdER@)eFXGb_LSsVO_CxGiN@HjBEQgLbj`HQQ+}KK=`h0kl0l^&e7NX#I}p<%^TPE z?=M-<+U30ZJls}vxr>)yw(HjG(VO~9wCLj(&uizkn=TDDOuZ>X{8S5bzyxjXiWb9| z|HZ-o#EW~dZ16TI0UnfNsWN^%WKlm1M)57}Z{qHv_)!v0r=BNLbLMHSd__C<140#b z6YcEhN~~%Q{mhBA=K<%?Oj)v~@5zSGSRS%WeBi;)-)bjGtvg84Ccq)UWk3W1`ar-O zz-OKCTQ+!DIvHRCYyx}<_!kg?$IO!fr}6AOwVNcZ1QY?jr)vd)w@FfGz@LD}yGzmw zfVF`AfL{PdG2H6jE=j)wQg9tD0Jrnn14aX$0lWg3hP#YI(bIPUhXMZqw8RD0zJM60 zD<1GBpe?R*Zv^ZBJcdi~N~3;~lneL*kd7B4&HiizM|1SOF6N zMD!Fs7Xp?6b^;Co&H(-c2pGs|BJkM>a35eA;Ca9jz#D+g0OGS7pML`6L6W2a+9gU- zK43_aBz=>lNYa$Sk`#8gB)t#V2KW;2J>XA(Y?Y+O0AiGY&jkPt@ByF@a02ibpwAHa z03Z)g1+Wg4q{V;(fCj^$Ilv~s1wc?T{0ML-V3dM?j{{x@6aeZEm!$gv>j4)5gGazw z0Mbaj4GNeIco*#-8L z6j}nLFO#HJ%P|T7R8H_a(g7Mq1fc&4z)DG)1Tf0!bGie<1HM`j>FAPZ0kxCrR3OoeIyYXLHXYdl~sARq8A;EohD2FL}pd`yxa1k41i z1{4C$0YaupQZK-hfR_PB0F54p=>RVSjsO}xfqH;X0OtVBpM>)PluZ2F4)_yr_frTy zfOi1pfT*V>X*^&B;4q-wGm`W$APry#ybM?acn9zipbT&da1L+*;6ELs2hav^JKzDp z6M%VuY=C1r?EfATp8@s&{s3G91kZqkfX;w;fE6$fkPcV|SPR$$*aDN;sA+&G{6$T zYQQ^y&43+%Qou1l=xmq;&=JrJkN`*qj0a2wtOINWYz6EA90q&`a031T#6BlU{Qy?L z7=SVv|DFQO1!Mq<00#gk06ziF1FixB=Rjh>XuyMj#{she>3|%-D}Z+aX8^wgE&&4O zN>W2W3qU8p9e|mD1pqr>lcfch;03wzJFdT3{ z;8DO#zyg3Buo6%P&|+T??>P`#9sCMp)u>P_KZLqt5)`Y}#Sc++FpBV}+6$Rb?UUD=JV>ikWGWr9IKU;2U`Vy-D?(6K&dU7ZZ60N`g+f`WuQJ+D zp=`55d;axiEmr#?p`$*Kq-o0`5M9kcW*W=T2^%t@EJK$wkmrI_4R^KFqSuD^yk}K4P5q3x$u)JSQRJk z3p123gJpygB3MQ!p%2RlCA4N4p@cxn7)tnag`?~lxp93>sNwhus39UZ;?325jfOA9 z?{Yf7c?i3;%%a(KtHOe{;&-B3s6Z#HB|Y&gC#6>Pg!3aC)Rp<|>~E^V4lmWNzZ2b; zesHuZ%nG`mM=V?U&yOO9m>fB1ou65Bt`wEN$ktE>-4XaH_V zPI@JZhuG6s1A`wz9N3xV)=`drER=q})V^#kXe?@IN!k*@%MYT((=VPjnvN452f4GF zbv^{^%frtG*uJc)2r=g^b%2R_s>_L^6`4ooH6ZH5o)|XxiNh$mH&jPmmWF!PoUN7k zsZn!YHlyFXReS2aRQw=l?%DSuWz*v3+ST`8H2tb-8SD4PSK=pC?ZeZo_M}p44t_e- znzJYlbxV>CCr}6NNhg4jNOpUY(`qXkh0jW$W@w8(=vt2yVa|IQKSP@TL2G%1cJhM` za;heO7%q?1B0s!c?yC*`FjjsFQOm70njl9R@pDiTeV0wbC_A)0am zHA8!RLw6I}cy&YjMjNL=J0;jf947Iwi&&!_*)T5bL<4BvE^U@AC2}-MKDXwikJtKd z9A>%_uBjW}kOygRuD!DTWwX>pKE~Mw!E7aZOhPxe>~ar)jaLvkNxx`q6jE!ujgqSpj|`)>3KhTRaQ-OYo%w)-I)1+oix~k=I$?cB#X2$b82#YH3y2 z^SM_xPm_cCK1a@&oD-1nhW5;-FNMD~Tb8z0QNMFkNv8eSK-rn2HOlQ0_ArVBmfV+0 zox`aG?@iMXlcy#Aw<-F|nHQLjkyuDBlo>AC^PMF{Pm>VxO+#WFQA@fYga~Rg#NH41~VQO z?;Qie>;hJ)&UmEfi}XaKpWx|$$w)t_h3@Lz{a>86d5Y4WHQ^zO2%Ainh6N7!8M#R; z$>QfFi=Ya7#bD3VT}1!Z5k1)~isFU(vP{z9Ns- zMieQHoQNC@|Kbpf6_vWTRuD<<0dhC{^Vr!OB5v| zhoVvVpi3z!K#f5sO@5UYCJ`7qkB<`i)8k0WHG zELl6h+bNf8CqLiQ^Cw7e&L8|7&L*f~Bv2%V95<1S%A+E$WFx^;EK2 zme_Z{IcpQRne*%vhqsPuBuhi4yc~_59xYGwD^2RVnB+{dFQ$6Kr!4M>lBDMN;*=u4 zB>SSS$PJlNWWjf+kTwRzXDp`AB=ewBREa_X`4E_X`Z;ae$IVQg zU^(>*b0$6MN>tNHxfWYx6~3Si;ViN5ZgW;5Sv1f7k+!L{ZBNpA)$l3lO#Bi?PF~;q zh;6r@#lD<+Zk@6l@}^V2p#OBI*6E8V)=NllCoI&BE-vTNr@N(J&J^lFlw?mQ(V0P) zFoqVpovGVqrwbr~mb7hFd_s}wls9KAgshg#bh;yA&X`FVbvd0o-fP(!2)ZFt?9?0t z4a5i7$MFyP?s4QuB1`-VCM3RZywp0S7!3;TdA9#K^iin&{Y7W_Bdx{$mdZ!)?$5dM znC`ZtEmGa5_}rJi8Ktsq8&h2}S6wyDxfCb#HjwltG-aK#oV-8Lp6+&rYMM&CMSVh% zLZxtD&;ATi(+_Lc_jj~NXlYJ6t34I#tsM5f>l8|WbiRf@#TQWdb3a>ER?gxNZ80Xs z#98s<@{{ym&S;N)*)oCRO=^HMp&>bgjr3=$3^Zqzzyz2_PjM$)A>z&v1FCUkk+Pw#!Zb89-_Pv8ON?q|Ut6&D{ecBzDYB>< ziz4`Hl!T(+>n7CSYabIc*_?3^4j{rL9uQ)TmN{2{PLaw+?z`O&*0F2kvi4l6uiD7_ zp?jNCJAAx3^V#?e)olok90d1+gv+Y@o9R7BP$nD}fxzoa>f>`+_ok@S9KPS4OiGc! z&U%)l7NIH$rSkSj2-ZHvN->Ja^rAV01CJKL>SjX0>Ow9riTYle)jGx8Dg#OYZ}xl3 z$MIqyQ!HA0_O}7{#F%OL+2+X!X&2;#>nyzg7amQJWLy27r#Q(0y(Nu*G8ualiST`|KeSw+Eu2Sf6j~VUIu`Y_a zl@xbEk~R=Pyffe82z1>u*Wd57l0AKc$ zqq4{M+uAN4IUzxW;m?oyH%$HYtZ_baL}l{qmk zDa`wo#ItCagzxUNwJ_Fxu0YSGR}szs$N8of@qGX4ibe||z77&z#=Qyxk&EFQ0d3s5 zY~IdA%b@Dc1$q$@MPCD?1Qu(lYEE!TUzNIR7yhYX3RC&)COZ9vdrsI74itG4wUgfj zd(OFP39ZXflLo{<;n+i_l*KP$OlVDyWje~4-}A9x*J!~&E0P4ZBEdq5twqxKh8h3k77_Es&b?f<6vSnnyH%u7xo>gM{q!zlPUGNyoeHIp>Y}|tF-A+0p37lu}aC9^^bR@DmwtHL_4!rwxA)GYO{3d1**AZW5hq*{k+ zhjiI%RS(+hRE7QG)P6ZRDmED!OI8okLKk6m4gwRq6vrk^!nDhyG0Wl<7*$4SkDcmZ zTKI&v>{N<;Lc4gXk3#cNs2CC||E;ERne4&_ezWQSub-v8O{4OBLSO-Xl9^+z+U9S& zD5PAgiti6JWnvHd8QdOI*mZV7QFX}148ayAm_riPHALNS-Lr!;ie^VxRgKO^l5z%N z!eza7{<90rFa-s}t7%bV_1jlb7e{B)KnGP2%A6-7V&IE^m zA^&J&zt6NX#_T{PSv~1&0_q{0d!qXJ5OJJ?ebfVbQ%s69C$m4AU_)tw<+C&Sew!aU zHf+;`mZce{seiy7mn`b_hxWh^amq<&RaHewA!`iM*;%iHykZ&VY@CxvRZtUIqt|)h>p*v#pm8g{G3%5G0;1K*FKhBK2K2L3uV9wiXD<1&46GuQI z;0eHsfbRjZzvHndpb&5kaJfP|Qm(Zj_pVC4--6p#$@sCAv;xDQ2E!%sIM2LV%Q|zL zyivUflEs(v)oY9W#ROXKf~jXkRh#2l ztOYw>_UuojIdO2acMTrLXXlPZ9X$h#(hpXmVbPe246Qo*;)aR-Fi0$_g7N>Q^9l@+ zey|+mVpg!{QYIE z@5P6H!AvbJgPHM5Q5jC;5EIzO8=avLgKjxHOd>xQ2jsH)%QZrz*_>s76Q1V=O_ z+1YP{=8v0d%^5i=QOzoXvdVtFcq;v@R2SjsxO3ynG@!DUNsdoO%WW0a8W=w^&TQLF z3eLG3yR0l)!s?$Q&-DZ$-$lkp|9*PVvww6SC;ea{RZekN_E6L$V5=(%N+6->Cqkj_ zQbca#hU2yeX)4;y#+=c{w+s(^)qEQ@bsgUB61@T$AEI|gHGEa&WCc+i4M*a`oIkpW zm!=+DPD0t@h{SFij}J8=Fy1B+t!)MPqOcMYi$Xo!v)xqR?dDGO>}Y1H;?mh2p0)4v zUHduIM(E!cla_5e05K9W{+$~F@zY2|RjwKpXgjFr6~V6l&kk|9=}*AcRD|Me3!WGO z2f-rcBEEr7{S+xxqiHXt?ZK3)V!YY08JYD7sSV8eI{c`-s;cAgHT_4dtaHaH>Fd_^rc z_N1@ln?4Ej&=Aem@T{yCT>35vA}uKt2_z3S6#-pSP0p$bOiQVtMobLEhdFO(GbjvB zGz~dJRYN18r%>pLzH^u>%Hrp=KM@FD=}49Bw#oQljueW7Vz!M$i&ncvXnhb9ipgZo z=CE%F%ycrhJ!Dc$5$caqX+#_P1Q(NRJP*7xnmJ7K|z-fQOgCMEOz>4 zarJ)BmK(h2(&a+W%4xnU7osv=q&QgY^tYXDTKq^U#{F=e~@hc@upofPVgz228iY&?->3O2k55RSwcAIy;#@ z9D_N%B)zIVN@tzhXJb~VXbf#pbafVwp&6BsG6o5?r0%nwQQD=!bJf3s&g=6bgO6sv z7*CTO-a#>^6&P#G0%MJ7Q`jQYMx)J~*@yRn7MW$vgzPPmPeII zsFZf5a-Z!xRFbj@K`NT}d|CmW#7n9EJ&#m003|KKx6^adqi0o>j?2VC$8UdMm6kbn zl^u`W=$L+-$DUoM<$!FlX$bD5yAIp`M{ju*jwiP|8FUk*!O31#;cGHiw!#3 z$2AXrxNq0!e4TBq_~E`dr;*Hq%j`-m=laPitVCj$Veg5>cO=j^Az#sM3@fi3%kXx)f~->bi4JBFtM&?;WN5A18J z`9Q_a4mob&(S=guiOMZ5s9_g=D&+kn4f3H^KS1x!pRhmG?Ew^D#d_~)WN;ILMjq?R zA!mY8K#I#>haqT1R4Dhuv5B=Gx9XngfvR46vBRglNaA(^L}8E{0_S(;6}7KH0f!JE|2vMk{H^ z{Qy2wTc#C`r~HC2Py}E%8(j}k@6MgD-U2WH(nS09`%n{ma;k!CcGg5BoXY6;K{gI# zVl7RL_#GDFI@_X-+gUy%Ux}s)?NPx{Yeq-PyiJGn*Avpt$q9k%z+P|YN#len}yJ;ySWi}B&)`~eGdr01fB z`n7X&5juDQJWyAemna@=ENCLAX;uBqI*=R%i7S}hD2i3|+6L80@hY7x+EtI?c+(N6 z7>__*Peu4c5it6{ML!+4Al=%7L&63J`@;r*AgN!1yiB!NMVm8zBg3a1OhBmwP3rVY zt4r~Rkik3jR3H>&`vu1=E6g-z`yNlF*XeO|gEP#6H<=d`r8%#(NUxhF&SCTcdtv-Eys&@2^rH(Or4*{|PZ%@SqJW*OrUY6(7${`rH7 zJFKk#3!5pfe(K1n``8<&SYjcLnUf9|pAEM9X`!f&Wo9IOr04Jpj1i96I?=X>G(#OjVp0;bd2d8AT8C#?$i{%OuM~4}HFD*I#(20&tx^|Hd=H|KKeWz+-^L zfPjC{1Hg-bodD|v95(>I1el=Hd4N39?M1vp0mujZ0*JxVB_HqyAoh|ZJq^fH@b7oP zjLSH+1(>g4tp-Q|ybj0(%)AZ;fcjWob_1*cRQSo#UVQ3+c326I2h0GZ0agRn1AdZa zNjAw+OTZk!3c&9Gf7HDn@FYOKti4`|MaZS_uIxO|qUtzsQ%_mdOR1CTm~9@B}ECW>Y==c3p6N!}53Tm4!bXgZ(L*=sXsc1qK} zx-tp3c)DG^zZ346NpjC3JdI0#1;OESekV%en9V4p>uB`n7?~$WDf$b<=pcK8p$-lh z%WUpEJD85`gqBq*$}@^^2bA)gjC?30qjP?`ADKSDAYW<}3v!#0$EyuXmw_BR4*WC` z8o`D=nzNj2(1<$8hIBmdAZN$T(z}4hC1P}sLsU z=rj~%e`NdUOeaG)A#6J%PfkIOPJKI{N1x;~b&O&`u3X29%!V@9qYa`zGsK`jZ4?U! zpfv|XISe%avLlba(=r2PdwiAAI7Qi3R_0-kN62&Nup5ONs~1uk?f+1=&Q}=?HI%LJ zRYoHXW!cZmj-^}W){bdIbVCfI@glE?p)-h8t}gb1IbOOB zn`h)b$Id%=)Wyfn1fy7V;~pbVq-OnGx0fdTUE;YaDc5)Yb4gm34- z{E-3jnnIM9BOSj5$qh_bpWy^2L*#Eva+KqfP&9buNl$iUQxdZo`tik+MBchp zQ>3?hQHe&0$j*o&9LWvkknr5cy;T}bDs7WLy`lVxziDHNqfNNnAxg#azGe?eardXN z2J4V|ho(4|qBEwZ3>V?p5iWmWy7;XlD?%Qn_;*G-^z0J%l$$9zYX2 z-ZEs!a}0S+KabG^WGjX&6Uc8lvJFG-5XgNT*^MFJ2U5MFf68&UGn_`aK3DWNIPxxr zR9Nk+IwnABXB0y|A&_taLO#ooV;##P<^IZGmI(z#A(sA<9?5A&GW4b91{%v&sydmW z&$`gRq6*LtGxV1(^ihs}grT>%&^Tox>L-EL2jO@YX=M(hSq>WYx{ehq5zJ?}1ss>n zaSFpd&TtiTK=cwrjOU0HAYNyPyE$Sk5L+0cH%BA_aTEysOFY0&*Rh1hCi7K4$APAD zZIs$?H{kWS!<{)J$$0Ru{xc z8CB*LyPX?#JafkGKE%&4w3&RT>Dxygwq|mcJi`&xT#iYYjt!r!D!`nDH8J`ab{8%i zSf`YL)oR0owy|N~%ZUP9K#j(mmFA4oT<@(Oc1&+B_l_x_XtE`;q?v@HfB@`6hsN zfLGhgk4Wz#LGUr)LjgxR$Wnujva|{C8{pnfvh)%FHx6t?*Y3Ist+?FSnDE9IZRZVUDke ztQDo3I7}VnmZq!sJLJyD^zI->n~vY_D5jF9I?#M0O4d?I4lB9c^n^eLb_6m}koWHh z^46kcCY4+n&l$as68Xfc&S?1*;Xf7l&`!WFW%xmzfPY-z=MjD|!*3*fw7{Pw{58kS zE)cFsXSucf4W+{z!#V@Mh2s|x{w0pzLii^+{v6?xIKD*};M;Kg$S%NNb)kN)Y$r@g=j+e|5CG@&wGNt1%!xM4c!LdMH8$qJoX*$L}yVo5Y2jnJ}iFM5ADc3b+ z8YKxRnIuY1Q%P^5#EOzoQ8J*iVWgXFX z%7LbzhH=9LBDSJ_fymOa9kMTrEKRn^J}$B_FJ%*W*3mx>-HH%pvApb$p^UF%GnMUS znJCBCadLDk$52|!(Dr2rTiFkxz0ImIyxQWSj=1h}tNfnva+#lL{a{C(yW|(;SVzuX za*LjA1{*cq+t7QPK#-=s3dlu*9pB$2M>fBb0Op>ACrpFb%wI*Cx^qILnu!X7Y+u!o}jNF$FulzJF0fba2eo{KcdV~t`#9^+U( zL~hwApc?v8A4l;JObw^{ILe2}VSP&axK(Jp&#f)N@&!H1_f@uxm%ZJ`(S4}g%ycv! zZ%(&zJTVjpbnQX!)fu{k#DxzsiiC7k@h(z#XYOleke3_9g8Z0~r@muL%=#YD#vuQU z6$e9&g8qYeN5f(0<>GkOOVgZq_Dvh2UnwjXD0(v5kv2?@GNr}QE8vdLhRMCcpG63$ zu~mE7Q8i3%7XCoBvW~EX^O5}S$?_rD^xmDTM#zz|@7yWO9@Ynv4Y3b|*dZfXvc5H^ z#c&i`N(M$INCRjwcP=j9Izm3>XS&=gKjt2JwqL}Cb3WQXyT`G4tURmL_S;;lQ}NdK z!dS}=77%jA1;S=g4$FOVnDQbgO3F!$xz4_zWk}9A^6cH+S!KdsV|X~$1cRYao-2=S z7aZ@QQP!M$@pasLQ2k>;%1iF0S>`G@qLAz^u!t(nL;iI)#03D8aFCzt?l^y+9Nz4R zyW9}uUK*aeyE~%C$>HtuYLG%SXv4MPIL6hGZrL;Lm2`SVHoN*K*4kl1SF14tu zWApuTlSYw7Ew~Z3^kI=*99tiiqr#ss@O-O3mYgQ9tK*aB<;ITF^W=c2jZ9vI6)ut> zBWHQF>#FDFNIAnXeS#boKw`|IWK+{r-e$uQv`VLbh&D$!KAs@A2wP^Lc?ZT(&8dzv zRC5Hc`S5Sp1bEhT<+(=k$>{JIw^EvW^Slp+4>1E*Ln-%><*#*e%zOZ6E)|`aavDDE z*&NEBc5Hq?e%RE!6X^k80mQA-KxBo5FLmUl!mdAcbUZOpZf^Rrqa$mgJTNwhc-rOR zIYVQU?H24}ERj|_EQ)ujvD4wx&QeG?#BKpHpqJy)L@X``}?pi5ooJgJN|q`jtU#?!3HCpZ2U%B_5~oVXpym! z@!}>nT#|)@46G*S;Miq4`)pi?EcFmicc$?=+LsXhstM80Q9K1!UntOg*fOuZx2>b- zIXNPtzb`i5M>|?il@m?*(T>NaVsG|dwBxq9acsSY1JF)jDpW7o#0TQ{`q@ z#eX{$tN00yi&N#{BUZN}t+(-j4i7 z>6Dt*y%FGhCY*kb&H0z?cfTU8Hqq)&;xnY@CZSlHm znPFP#FH0;lwB<)-rKRzH&&*u7kni8`KQEup;d{=RGy6RA%$#SQ=bDO`OdpZY+>cB* z`qa7xYKBD=jBve9Oakm>ryOBvc>TO`}*K9V}JBW?rXSHTHG+%0#z9!RE z9(@G4-3xZRVgv63TNOvcJIXo={9Qq?EPiH~C1dm~PL=PTB6qtDu}x=+NaZ|Ve6eJ2~9 zDm=>YIpBX%e4O}J>#S7g>trQmLv`M%SXJlmVkoOEN6snH5xEx-dh+hH;>`5tq&sTU&CZnxM^kGZjMQE(PNl%HXF6yoHNg^ z=A71(u@C%LGWJHvWJ^4mNOtsXkc^r>+eFk)pUqT>t}%UX2v^f*?1`{zWZUUjDa18QOieRChN)@BN;iYgb@cp3 z(YJ)k7byB`KE2=6xFEECYD_Fd`i%;Pg&Hk1!e#ArTqV7m=%q|EUqQ?~_H_u&W4DVJ zLi7}7+Hj!~6Geem+e|Btl^14ULvc%p#MISRra!?a@q11$5bei=<4@7%vMY6n9Y zyrG%+QV*SiTo~rvLW)ou-toH0mR5P}n|=YG{$R;0O2%ggGKMAHHuk2h+s5A7J@M0i zCBx#1b_bOV!{-Th%b~M**QkCax3(QPc2{X=QOT{T^!}!8kNkWVPfG|vQ7=c#p;c98 z#?ZJaG5RZG)yD#4;%pwzd&!=&`AD~g-eI+-?JuFTmh|?gdg&iWghr{OAQ4+LLvAbG zrOP?$Z8|XhVJ1H4*FVjsN_-4wXm%Z|%0xGpx-9)m?HQF*qKmv1fTe%Qa0{yB3eGHc zQQhz_U{@ci{bMl@vx4z5p0pS7Sf?f9=#%o&C<(%X5i%+d$rZo~U_ zxJC6@ycsWl?ZS`4tV72AGWJc0DKi_& zS-SsyYA-ZDCDI#pv87cX`}x>+$Icb)3kdkRq?deWE*}>YLOExvlU?_Xy07jKPuqEX zwBy(&i~#gwuo`%2)Wn$~p4IdCG%kM0@HmS3Ai)znV;ArV&BMek^jHE1#^8`&@A={l z{%Sz-fbE<;28!6q*<{dS4-Qv>=5OO{wOqb|XYjS2&o=O-HoieVvXv*qJ$sa{GCT;! z)l5eL==>nAdAD4>l_$nLjL^(&F0@CzH_CCp{A4R{)?z@zn2wu#GNyYv-m>PXoV=Y! zMUaSur{jPVv`@<}9vcK?Zk0M0{4znVG48v0v!tmJX zhnwnq6Ufjn-@~|JlZqVnb9yzScAzIfq4d+0L7#e0QWl zRL2>gY?`x=%-haeigusKT|0Qx0EhI2>yC3Q_Q?%f@FLW{YU0y{btfK%;fpU}r;n(_ zf>gtP<`bx8SNH^K+R0jw**oA=e@+YS9bc)HNZrVBob<`2!%aKSI3l}5il_j``j6#} z+qi?jD!n^!d6yA5f=xRXeN63`BxB#@&E0pZl@txiUnsisL3Nxt(gRvMy^{~(bEVzO@8vhjiC%t}sGTMc zczL?`tgGj;myfja$31uM=i^0aWE!R-`uyLLl6=pBgS;CTpSF`%4)IgsVq5v&5BUhb z%+uo|9>J5J|BSO)phrP>gZhHfK+&LDIr=bf&T~Ao4)f)lzvW3h!Y7D`TUz3Q^O6N; ztFSOy7E;BXK(_DbB?ujjjJ&dFdoSB0eAKIrv{bm2)m{I0fJBKO^MuQ~ZUPt|M?J zpcQ6&b<_p>E0p2qyQS?bzDj&QLcabL@6emlQS9;)I;_;|VO4F3ubJskie6hRZpI7k=e?dMbF>`dodf|HYHCt=b z42>-e`t)y^@MjI)V#22woNdsj_oefzvH$BjlP=_&_MqLnHVW8n3|=C{G1vRcPUBwm zJ?MX+KS8yiAQX8Lk(_mvCnhTv;*FpIpdp~UK%+ntJj<{0UpT+TQ(DWX1hjN_!)fMz zscnbhlEKGr%OTP+0~g-$pf^Wwg7{VG48TL?K{6{qJk85Ip9P5K0_PWgw~FTc0goe4 z4CH*UXH<}I(394Vl1dBxRpF+vQ zL{!R6Ibl zo^Djw_y)-j5S(zpFLodW7wh2D9p(RmMNIr@zi^tk&JWk&QZ@@g>O=g3@$w=&58oN3 z-dqkwaD&uCDVUxEA{eQ=LmH(XPT>c2xRe_kr*5aIb* zB;D0d8AZxFq3j`@O+<8BOa^C-p}CPq2MSY=}5YLgNz^3ig0s7MG|I!3D> zG*Rr~h;oOCGERpL)S_*?7W>HTaAeZX#3-Z~4rB6wHsSJ>aFK~y&$)2Xino)Y5h4i- zgm!@D5i_VYs5kvqG?I?^pgv*-Eo!2J&Gq&BbU7vE6hAywbU8vvYe>o~x@=d{Xp-DxiY`YgF@waMqRUN{)CX1i;pU>tQA!#`QtzV6 z(Mo!dr1nLZ9ZE_gDXHjkjFNt81u4AfGOjA?A@Erd{rBLT3wjQdYBo{3fkzAUbtQX6 zV)7g=(*ZVfUEQiA>5ddZ{69M60kkFhvI|Mk!jUF-MB)MpMIwI7Ye$`o3y4;&hvnr+ z(NdgvOg3wZ;dS6KH4@~IrWjrul@OKSD1N*_BGOznvmV#R%bSX@`1^e}SW(gDWrQA+ zcWIkodRq|L^)*pyDE*E`2#bOcXAJMth5<$#rNuvVxq{8*bjFCIb?9+Z>eZCcP7^j( zhrOvqo4Kl1$*L%kB+87@{o3dWlR)QaByhipVXms7o}6g0K7eNCdrZ!c!|ZjJd=u8!-GtD_^XPLlQjuVq z3PTV3<@(%09fqwEqQ`uK%>Fgsh0D(Iq6kH{C!K-;{B^`c${g>4;!XhPp#G#8GKJs&#Ay zQ|s_5@!*bN3-gkr;GV47R$Ju2M3L(LWD}fVZ>pB-hc7;sShrRnDjrzlYrXuc`jg4xhR9xT;a#;u?^0nR`t2#Uwvnxyi4lCIoYhQ3MVD_zm8W9$fzzN%ygy9H zDs^;}YnzE~9WxPfTS@;aP<4b|Va=}*kzv^DbCDLg{P@+{ zY16aDS`UN4LZC$UYc7^XT~EzLExku~O<|u=@~7saL-@NdBcSwT>4#a}9Di<-ZJeT+ z_+pbB;1nM?0@1iEKV*9lp!!2B84g%$Hp+w)ab34A(TKJ%tOcO@D8lg*A?>Ok!HH13 zZRSL_T!&x43Z{A;!Vg%BmqqqE77H6E)+!vZ3nDFz|g_zKl)cYORNJS-YvNS~TR3FB82f)+`y>MkHW`(4mcJdHvj#Fw598ZBo|4 zL2eT&}C))S&54#ghULC zd-vdqZCmsgpJvZ0>ho+JgLvI~=2OhdX0dnC#+ zZAHJ}bNNa-oi8`F72WQ6s|otE)k^h$p88*){?AtbLs6F6R{tozM*VN9G{+Uc-%qd8 z*{d|G)&GOCPdhO`tQwbJZL3e!|M%q4b|NeE?HS~L>|cQU{u0@$z34u84}uRj`eBky zN>~5GBx{sh|HCAUm27^PWVSZO50f}U*mGAaV&w_Epfx+7i_B1%#;3bp5 z*9H$8e8_}9VB`cN`*L{G=-)E9jF7fgb$^MhkmhY?Px)*|ai>UXBQJCm8{HKjaiy=Z z3Was{hqc}r)KH&o^fiGE^%X{+-6$RV$2y&g#`+6N?`BS}6MEj{>^g5EFs)x=^ts<_ zz4srDWk#QF>|MtGIioME)^@qI8lN%xGNZ3ldUVmVzJz|)hUr%|&hy+yYKExhQ@ZuDgV zTK}|GGv^Q5zRa{}vC&ujXwotErAD6z`(zpucA*n0F#$Ecggl$m#Sn{#>ERJQ#C?40 zXyJ{J*`tI@=H`m>*S&dy%d%0TQr6rjay_{{MOj;qcd|Ih-L{oF$x6GxnVBO*;-#^^cBV~?@6PV*SW>xk(DK;P{ls=E;k%bm68{V*rr*p z=|p{2xlemS3dM&3clF-z^a#3W=q_?6XwGYb&3RVeQ+#2#g)^)o*(rpbY7|DxlPxf> zL0C6$O%0*Y6PXHpz8Z@#bul&=)>^n8)h?nL9H5Rs+>`lF}3gf7qt$%~Qh zrK2b#qEzX0z4JwA6FWZgg0vYf_h(Y#Rr+nVp+vsFawO@K{FAh=sxQyl)k1pb2!|sM zRrUSZ*Z8WAs>RC6Vi6ip1H+xAFwFKVco`z;HmWx)TP5VEdBTxWj3TE~Q}{*j-PMxOi$Z|GzBAbvCiQSU$5r#0DU>x`wPlLsEGH*a{(Qi$w|?(9}{*yvViy<-(;) zHH(w(QV|$}ra-mnvee4v^F?q<9&*9V;GePLZP3-{E~&9wWg}VYGt8C$vVQ=2Kl`!; zB3`Tzp0rYNZ&17Q({|2(EaSnfmFo*%ce$6mO`Y8D`jO)&j-51a{M39$q?E1k0mfc!BJ*R19f#sR#N(;^_tc?BMCo>D1T4J|O*_lAD)`Sj0HA zR3zS9k%FI^Zh@V}n}UI5{xS~~EU>G{OR)<4dUzmy1QkEj4Pxua7OB_iWP2%^#n`d8 zE&!#ga5T7*<$VZ4c27zX+b(wteu5SBNL>>BSwkJuvV33SF-CzrDMbdqBpu7J?M#=G zmx-tcN1@NMs3!Q!drw#HYasnyFf#Qk{q+H&1zCR}X@(m8qd?k7#)k+N(ls!f8g4Bb zF1|?sYtlC;J20KGOY)~>Vv?im3;Z@N=mFD0N|uwCi^TRdP`5dON^dX?k`u%l>d=YG zMs8g$T0{;%gP-M9w&)h{!{zzqqPIIUgk|R2SX53E7S%n3MRkp+39D+7tD1dpFw6c0 z6WCvfvC-u6FC7+O{x;NIX9HOw4kOuc04c{5yrGQNFg9+Vk?QZ;MJ|riu#o&<^tp}Y z;>gkqptUM8Ngs}!P6)ZoHVWEPbBj(Jm3l?M) z^4treQ;4f29@v69Y?94Zhy-`NtOuyS%&DN`f~W`U25C(i9>{jUw|+Kh`v$TZ$ax6g z;Ai76bBB#N#)U9P_h9De8ZJF6M3+uj2(VrtdxhzO{2>r0)@G&HegcSUuSjj z$QJ1-&Oqk87$R#|iD0*VHD@=1oNwte`pc?^a)POca#P|1S^QZWe!)6OO(!*o(`605 z+@#{sc(k#GI$S;eHm3KSFCreu46(tifr5-MB+6j0+-GDWlDrml&=j<>tm4SeR*U8d zl`nCY_%b^0ZC$5&S%$9>t=;LbqTHYy6^^pw%5R`ES#E*N`=ET2PGh-295b4&If2an z8fU#hdbYsr0##!5dUxG4(!fyF9oH8k2EWehW-NbMUoxsg%q4@%54Kl8PORf-6gH44 z&5Jgc{sw1rL08me1%FMQLiQ;a&E1vjIXel;r!m6p5!`DM9?>|0jB+1nt?J17LT=H4)7 z%?VYy?wfJ&gY-y*Z2aZ=c(T)rm~+bT>4$Bgab&L=AXLd#tUL03I?U80_W%`}bQ{Z* zMqM1_>z}T2J=%Me_DY1bp_G}1iI<5(`b$77x6P)!tiSM(SWc=m$ zW>yvopS87$CK_7sTz(1T%YKHlM?h^oI%j`b%}h0cRhCM#R@rM~&Tlyz0Lmx(WD85i z#bfPmRl1lG=O%=pQ9s*io(rzpx9k()4kx*^_#8p2|@hy@28nSz z{RsXPHOa%2NtqoFW3Ee_{Q(Na!R(aOK$dzj6zu}%eB&@2K(XaCWvQ1rTX(rPZip0t zlzXG0!1vuiS)@aO8p}Eo#gT(w5zUgzu5$KY&@yF@D{EXRtJBGnSHzH;D}zy9tcK`! zlJ)eCafvcn_l7F{9S94hFl0w|{&Ia>+3Z#L#|z|jui}}Bx2a&ig344hx}HN=LmgJh zJ{!6GRna*p)K{Y0)I$5516^B5|S_-xwl)6I~q+XUyUlXkzNf>Y(1-vIsuydeV zEc4FEL9dClroNzV9kI6r#rBd5UK7pw`1B(>VaEgN)C&d0UvNSoOXwcK5~jpdMdwB3 z`s}`f&HT=S-2%J$^1Ii>kd*Fb3myHw&K(b}8PPH<_0!+OZb#nnx>(^(MR0def6!f^ z37`VdGSDWF7xW3}4Cr@Izz2fGfjWY20u2XE1krD@tO9KX?E`%Q`W|!@6u%Q$f(C%b zf@Xr2fZpCIV%UB#r$LuMb}teJ4e-oeE7k?==}WgE|(ng zj)0fq-zic~;oYvCTympQm2VX->s23Z6{(&!9`Ui&J+Zmo|K$NAowzMy=i&tZ1eycB z2DlD0Vt1l@AkFK7;U!W_^N@cz4e+Wp^PCMe#Y{{rwi?&*%>-hn{dEXbbpyzlB>UhsHhx>_q_K&!7|F z%kj~J&p=d&D&XJUxW@pd@&G2-ktSgC~3+lm$L_1m1$W2mMd8YvDbdT?Wyl$VPJZ21qp>@b-I6 zA%F$WZgC8z^VDT?qO37>^V)Ryqheozs0LL}qIsV(Jdg@YrOpG+#(IHTs|?tD zHU=hY@9z{N=h$e_DP%@C8B_zl5EwXDSHKQ*ZFZ&!`Jda!=-33N)+gG3gIK5v-? z-rwy;F1lmoXcg!Q17F1M89ZSmXbE_y8;s8`Z{(8u33LiJ zgm1ruJ_b*C1jOJwtM|VN<(JET8QlXt;X@#IcL;f4EU#da0dEIB52E!(Ik4TUn)i3O zkpqt$a=Ebgcg&H)?mJL1^n`zbR)QxCf^Tg-r4RHu=zwnaIu!3Afba!6&)Y+rP&3#NehJzJo-nuqV+A~60SL=-h4a8)1YhqkLpMR!-Mv|R z*T}0j{VkLbo+-j2P(Sd53qd*H{k>rR9x{LL8F|$vfL6ku@DWf2c)}APFL=Uppi1x! zTw-vcsW(!q5#W0>mBH2Idqb6Y-_CkXK)41p9DF%&`@3ANUki7msrTyss{#%@p!sZ|>!7|w340zgJaEE? zdg96h-u4mhgh(?R_$r7dI{U}C{(a6mf1e!r=4OJHAp+rNp!MJhuY%qK&yM06K7ks6 z_jkmRgYLsB3{vO`EoaawaBBH`-pC_o`4$CtBY+t4jEw{D?;#riozLaw?^+|5+Xm2j z*b|QW4*d$A@L^CTc*4b?D)8CoFn+$*t>V0Z@%Dq}ama|dr~gk+v4y}+H98<2c=i{q zFZ&J6|GVbXfk*x@k%2yU9bxZFSim4Ne>WYu>&T-P2k%+EXASEB=!_T6tr**1xEa8R zFzjzEvB49z2Ni-(2fhaCfEyCdP_ghUxMZQnu_-pu@HlhDju^fY*y*aa!I>_$$nbTp zJH#co-o}5>|F1yU_z#*8v<*Druv+vhc$^?(yFrvOPK~jyOeh{F&e%(auK+gXT8|TL zY_{QX7K;T7t;aDdR$}-vU~quZyTOzg0f)M9?ArtYiw$1}%z_v61PX*Rb`0m~*jey| zHHIg=6_1@spAGyL^doqj)nlN=U)%`xW-i7J!qP!ynP22>1 z2d5%oHpm5@@EMZ9mjip*(Nf@Dz?eu($Lo+8@TsP_gF;^j`~XCE(K^2+^zd=I*~}=x zRze`$2igXnuz$3`WPu34Kkr?%3iE(D%xt@#=tPBXLNWDgtw zqC~TRGYwBTsI6clVUrEq1sVtbFwoi#X`v--z#H5~xB)oS@HxPlh9`W+@JoQdgXq?L z3D~y1PKfXZ!xPR0QGtqqL(||VMaDV6pFnxwYk<)mw2hmX8;#%sE;f7_@O2QCbS?0b z;R#!I66_TulnQ(ebOQWZ;Hu8(BKU60fftF^YwVJ)$3%DFUtQ2$u(5ZAE4&-}-vuGJ z8~Um{nhHE&XAota1Ds^|0^n{C4X(Yw@E)3{XC=>r$bK2{7Z7>DojnC>0m?<1bgF?) vKUAVXgyi0?0$Z~2K*b$`3SOWHdB0WNv)N5H=<|F3`FtwV-Bn#(U0q#$ z&TQseQJHT=tybG!TkG5eMgE;p+QmoN8YoIZon>K4j8h`5MZI=JH=y2=>UiU`dQ4}JV!BRI_t`v#@njS;{3wbE{ZZGb zcNlHz=c%#An)ggNe~N@b1E)wHCKosNRc zPeACgQD`rP2HBmcRT&2(DR`WN^(lCQgE18RmV-?w_#Fr1oO<482s!o4qj)^tAnuyO z6m_H(MU{GnwIa&znKGV&CpnnF?I_gjM+($@#<-zDGxawky+I#!kg=-4jkaH+j57@e zsPC=s>2RyfH$wkT{fz#XOaIc9Qsi8Y8Ju2itdhjR}ozihJ-RnXbDK_|QUjG@?Q# zH*Ob2+H&brAHg4XXG((A=C8V5^ zMWkO)C}O%g1rr@;>4bM-OlG1$1PWM^`vj>vN8| z{mm)SSi=u}nTWZ>qqBHK3f!4EqI4hThp|FD=8DHS@#y2=rykD_cVwKgqe(~Yp4v9* zJTA|~gvhZj_vv_-e#E8!>EHb}`fr3MmrTx4FxZ~%$t9ygur`9ECrAF^$h=&tG(vUr za>=F;s+X5bCWTN`UM^V_LiO`6B82ia(^&hjO{e&&391mq>o&loT+exsrq)9 zgua}F_UFB;C}~OG!C9qxUiytvhU`=n_vYWw{uQbEdahaabR(+ifPSOk=WH46b-f7! zsr}A6-1{Geopsv_aSyx8A|i@XBhHvxke1qSJ<&MaYY}l27@ru~P2(F;GY{22$tir) zIYKXlk@=1Mrfs^D`KKinI`uE~`gtCmgdpFUxek1iStdu02DwKhzjWSEn>E(B-{$m2 zlB2-QT{ou>Me^O;d2{-U2zTF{6cOPLoRcad+=X*oBEp?GX9Oa;n>4EHq~H!PQYGuS zQd?1?N}YO1k!eru^Aa_NQ*)$!gpTbUCGvCE&KWHt+_`hciO3J2_T)@N1a+k@=_uGt zvIqJVYDE1oqkF1e;_{A8fLZPKZ$4L1;nERt|7Fi;gwY{kBMnTcdSQh@)Tzeoc71J= z&l~TyYpV6{Tkc5Hi}gd4v){+4!};_IHy7gdCZgmNM}CPXXR;9J)uynhoVyXJFwR2| za}yZ8Ul5<6&s9TXgQegp@V3;6a-?Pad<#CgRqi{;7D1<}A zljYSEvrQ#!v{E1T5<*;BevrB|=rM-E@JPbXpp=l>a+!zb26|)!ilCkaq3UrUk2|Wc z9M4N6jF7=hdy#TgEg%C0Sc>!xu~*_zx_;h2rV_26aTMr#jg75(jehAUn|9?mwzp!x z)!(4tV(M=$691WBgo0g`f=2`^nA&I`1oRG3QSO00&5b1sqU_zkoZ72NNPCSUcI|#K z7PpS$hD57xt-w%d-aUtPA$+q=77$VD(S`di%F{)9I+MT&q5s3-uwQ~W zL?=*1W@JREY{yEDq+*RNBadm#lMhkx1~-2`n|{%WNHkv5eiC>a>Mt6E+wB7;W~z$P zfH5Vgoh!}hsY((@usKo1q&aD_kD2#Y(uSH76NfXW_+LpLLd!O1vDynh*GMGp7L+11CXgGl?AE@d2XO+I5RX%i=6ER1imWqfysI+PA)j4quLx>poZ88mEJ{QwT-mJGHk zF=yCJNXwyIVZ^kpZ}%Jyaw?AOtiLKSpz}C0m5QGKlPG#BigsjA0T%x3zF^yD#c!Pz zD21F0mB!A6%434(1OdfEtbr2P8Z-oi@aD`)+E8m?;&9f`=?qx|p@XfVEBJ^m0PCmb zr=C#gKCTK!Q?JMziu%abO{r73U$n|S|TH~C7S+(7H_Ye-HI{InN*VQ z_G{@XkF@062hR-d;iRO>{#Zi~r#k)X9!|u>Q42T?@&c^?k00dTO%zZD6>WZl%F8hI zVcBNXhlz@F8JvqzA+1KVl|uq^n_5X4JuzfRU}Ch|zwAbHNzd<<3q8-Z^gO>(&+D!l zpp8FG4Njbc8~m*q)LBbhMgICOy~z$&&cTcpj(mHw>XZvX8JH2(|FBBm8i*K0FriAJG+=fPx2%c!K^jRPH<38$z&Z*=a|q5E7+D#gGPIaHWMY@kW+$Yq>mn7Gr`4E;O#`(@|H44w}fWe!boFjjGf}?-kha%h`*UtRwX?jk! z9mr!`dF{?T4!V!06dku&lMAsR;`yDq>t1eqn(JjWkxdh@ueX5=V_&^!i@};T$yQlS ztrB~ZTV=Ium6rns#?$SsC#}&@d%B<|Gxtvmn1nS!6b*&0Ns9GYQz43kbChDa=E&z3 zN9%g+Sld(gHJ4)w;m9X5)5sTqAsGPVF8U!d>b!o)e>cPza~`u9<_(g7e5JG$@|9FB zx7)xLX)8^Ia+*ylN!e7!1g6h~CR+)kZ_RPtMuN+w4c`90Nv z*akbw+x{EYQTDA)*$b2bN4fd^DlUL4-T)*eJpZS4@E>ew=ATm)<(&KI4Jeo^RP*HKNc`__1@dy6iYTO&*B+eqYp8hg4+r(=8ZTi9AtWA%*an-*+9 zmpb!5`LIG_mv(~nTqyPYQKg<~?fBCPTGJ($t`B%+4EX6qWs&Jo#XP-F_wXi!>9{9g z(8BlvKCY`hZFd;|R?1aB&V`rC!aoW1Zx!Z%KY8&(+Z@~`52aHn>_Wqu_DEDl7@cLB z@U(^2&}lfb8yKVJN86v+B?OwrD;TL~6`hfX0n8HTR!%^_sj}k0{lb-e4y8@)4 z!zR<#=n8vO9dOQ&3VVFbDm)`q_*+1Q2KmB6z=~3XePLG!5N6T{Uq})JQ!1bg_`=8D zAzxUPJLNq4YAX0?VIORo7sY$YVUy;s;kR_WfvJ1&L`xKEPB%^}N zn?wpprpb+-fg(wW#za?&6k;K1T8b1Q8Pn9|l$sG)+Ji+KEbIq|!I4dKEioZEPwwWn!|ZO(!`(Qt2Lq>%{5yCw;sRIQ&bp|= z{sgoCyeNeIo^LJo)KB~$A7b_jvws&jeWbk_`{=Ojzh(A=L)rHWu#Y76*9!KL%)Sk? zuV0P*youqY|Bl(eTPUO-C6x0z<`hCXF~t6rFIf5*X72?~AK9cDd-Jvcds^zT0qN(B zJ$*Xa+e_|J^(_P3{`G!O4`22SM6ZZK83nsG#lS)#Sj z?rI;oHAFFKnCJ<`bf!8wKV!voX2o;?P9NF5nqqRsbE^tBL_N)%B~nCR=af4|iskd2ViOHDS7{M(j8L_`7-)$xy0aDo#t9Bj}3M*urA9`Xk+C zYB%dZuUn~kOPCfSwN&BvkL)N9*x4mJI*_q^WS9=-_(y)!2;lerh3c&z)XiistIkdn zhFDo$$^?GtVfaxS{;$=gJYd%>3_I?CTF6?tx_ni|qfFpe%BLzd^~M={1NNft7tWg8 zb~xF2S63jU$%-D;t*F~}>CyY(T(GJ+SMQruMaMn>Ea==}$>dj@@JU;>@m_{-18$9M zGM?0igSV2$rr-T$h1`sYory2^N77j;$ZdCXR zPzzSl3;X99mmmXn*NI=4MfU1Xf)z;3DSLV)FJzQ=7v?i7Iawg)lD4u1%<&*WD!0GH zq5-Hi62r7ccR@`XP?f~g6yb;~Ze-S>#MBO9h&A(IIBIHyFx2KN;fP6N;fUvkBPR6+ z5o7)(bv7K-HLE{iHzufJ2QEnWL5u=?BB)g1i${nIQe&ohLv_7qta5Y}h!xrawuGVP zxk)(UrQwK4<>83$3L@6TaDk#p<>9EW4x$balgh&pM}{LNm4_q#YNVoQHLHBX$Kg~S zj*$UIA<9b?g5tqjrn7?aL6=%EgQfl_XCv0!Kuk!3W&~G<$xgOkUKl7;G9`_dzfm=n zG+X{e)l^cUIZxw(U-neVh4f|K#fX}`!84@+4_`SUH+vutY>qm7<-EY0GeX?W3Y-|H zBLN%Mw2zvmiF;GS!4I2|oFuL_Q4mWp+-d4ri4#W+C1#5t4uU~F5Q&YpG~}18kYASL zKUGpufgO9XA1Kl0H^Y^i=?s_zc4X@v|LLs8I*d)EHDYoxuMrcmMx=A)-j%zsbW(Dn zn2%zfdpLT)hrXbr7}D0|4}`2j`cNTRJB^fK)@gZH;@N&mE%2*y=E9fI=o-whe_MqV z;=cJKu0o2eLTYFg?%rut;iiF41y$kSiSMEU>WBhdvVakl1soVYpwT*DgDYZQG}?|x z&;ebXI7dh{HGPJhl2S5kf?7{YAN_>;ld%Z2yQGgvQK8mPzuT-8vj3Tk(c5uB% zNwG!;#EJ%MzEWDWBT)C1PyEkyA4i4Uy`2jgCkvSvD1?uNqj%6{Cfg5&(J-TN9JSw8 zx*k_~yigz2+WYvT4))n^ik*r+wEtuo$7CvDdJ&f}S(b2j@D7D!L`;l_Z|G|O`whXR zDRFURmqKr1zhf$KI%Dj=p=Ad`p^rGXWhz*Q^c)fphqypO+C4c=B2AObLQaro5Tm;a zjY&5)ZAP4eB#?PeJie%S#5!jn{CD!R`$!!xQCqXPN)UK2Nl zJhR)hMo1q^(pwzzB!{&|!<$<3Zd*FMKeelA4i5EyIY*IT`9I;kicsa6+}|seILrMl z0|VIwGGfx*ETIi8XD$Q+9iaQ=4v%G(mq~$tFuuI0wY4L; zqxcUgXg0AugIpqyUKEr|j6bQE((tIiQP9(vToXOvEcPBjPJ?w#mNQ=Ikpr^6D3Vnc_a2EOB1F5n#AFY47xuPPXP{&y|tIp(0s65kmX!%8H-WxE= z?Z)kr#UJvxYq5Qm+46LwCMo150;_NilgqtJ;*Q&{Qt@2H19U5`D72XdYa$cPki=A~ zhD=0Y$>k7vs68&5C%D1B8bB)2sZm1mU`nrtNEh5}uZc|5G9j5l^5C9baMR5ad8m16 z&I2vT_Twd$Igm5e+<;9t(S-!oX<4LbMV)s-qe>5$W?$ZI5(zG zrLGRnNUae*Bj*cltc(3cjkrK+k;~+(Y%shyuroL#HOpl(2KPEI@;UO?6R(gZxW1Wt z*~!SLGeti~>R-5jQ2paJQV*n_7Ij|e4&!qPZhdR|ZqH^?w4d4-!-6z&o?w{Wf<>d| zyNqbTt@~>~_I{OHc)0arT@;mjDTIqEI-vP2|3e8@58EK}Q+tZGub!W9$REfa-1f6Y zc52m{vJ;N7YgX~WCUA|&Ppw;1e!>M8A8ZNd`f}~5VQR`wIL@wx@xB{HeroHQ@)It& z_+Yzun6ua1nMdVYojFx^^Sx+j*O^sD_jaUhM6p!P1O8N@9x@y;!~U%x*hLR4z| znnWdBu&BXyvM*T+7>ae5(R}|rZ{bmcT4`1z1vl~&$()tittwe?TTT~b)IL?oC{43W zZit|xhN((NX%<-OeBLzZ(Ct4GLaLS{W_VZr@tg2)ebY6==(w7Q(G1Ti3WS274zeha~X6djzO;z9W- zTA4q%RgPTCjrSe-|6m5xBtZ-)TFHQ7Ry#Z+7*Nv$=cjm3eu`G+4{pZ^BL7L!e=q}T z#2^L~tz;0~!Y!CV%`JTPJ?drT?5Oh`@auLjM}r0vvAF%eV9YS<)Vfc zqp_7Mkcm)c!*H3XwQI;kDD&vmEMMrBZqWRwT2a*c=4&EX$Zjo9e`+X-n%tZjE)%sS zGKnoUp^HGR2xnf0T$k&@P2R=Cq*OF2R7kCiw~X~eQKjg~MQCRKL70GS0x_^sE12K+ z4sICvUP{p9sanOURn2wC1q?NkC{jiOwsGK1o-XC_4Q}3g4bV{|RiTrGJwOgPz6bUC0{9hisdrPF; zG)CkkQ7Ed4s1V3*{vyUkA(P$wRz!tNcJnh46*Ae)ogyk^vYYuLDrB;ouW^)Qs`{P- zU&M3K}iaJG zp{N)$i=#Ek)KbdiE{ie8ii|=}F{VpIg-r2gvWN!uJcmh)OJ)-elMI*4NQ5spgdTgn!Weu{L+wqp8SO}Z zawU~TQd}|%IZQHKG&gXVWVmRq;V{W?(Ok)4lHsDcjKd_uMKhbjB*R5>F2a|S;l|f1 zvLnSpbMrS;ZT7bNXKYmy<@e_xN~Y_O-K8XPTu0IB*F!AHHS%rbLJBqCK1k=c^oDY z&YAN$Od^~!XKkj-q#VG_t@Iyg)M*~|zIlR!4}T<59+RcWNXFQbu4Z6N|ZgNCE7y&0J^dFF33 z*K(KyvYRh)m;|z$J`R&Wc2nmt31l}l4wFE3a~6k5AiH@FAugw)0W1(NR|4*?o)oAX zGa?Py%{B<9M!rcBnTR-e^2!{zZDS@$vlMc z+{a-ODBj$`VG=0bEI{~j0xfxw1d0z4Xo3`I9`hlA;>`yrtZ_F>F{dzsBuX*Ia+m~4 zF^6-Q1WGXna+m~4F_Sn<0;QOp5Wbv1mtG)&QbGjU1+9^WwDJy?Ri~K8I7~K_X@1IK z5-8Iw;V=o5X>R5)36yEBxlP@m8`0D z)BjOb19F0HJ6K2bhb^AZ8cQ--h1J!TY>}_IxR{WKMT@u~P$u)fZ8VQTB;3mZ$x=EqP0z>W+_+??g8@p$>uJJNLSNj~dke06-ShYMO zGvD_#8!<)Mh;{F82nOGyzy*aj`GUf$R{(+adgWWa=KDvmkdjxl#1)14&scBeRl8ut zZ|V9cf&exY_&^Qa7%@kIbtT)-%L=`L`*;23ysXpHE4WS+m38`?>SSCur;hBO{hzk1 zE~VMDAglZLHyG&11QQ_tVZ%R%Y`IJuV4JtX7&5n`#xFEwzC`aZU4qApl zUS7i12KEO3f4{(FjseGTIvYT~I2p+Q$*D!k`uL144_ke+ug}ubgxIM;TKadb5hFPs zd6HX$qOvubAQ*bhX~9$6Wyjw0k6rfrM};vvsdHJz9A_2v1Q$h7SyU@73a=f=kG}== zFZOy?g63hpYT+I(% zk4CbtMXlWt)(=4*{v?7CcVlb&()psAAr7gJ6eQc|zo2G^B)(`+TQWe`d3Ql$?ZR@_ zOp1^l-*EJ%R*`#S3>wkw)vTHi`fyiOAJhPfsOg&2FOLUsm@ddy5@%~u#XEtWD zhu<{Rb$VasD305$m*Ik5XsQ0fS^Lv-jB9d=$fK*FXFc2rP*P+ z^V~|Y-T%~`XG;blbBs*MV4BW`L{Vu-*$4*DF*d>{DduW)@oJuFfwxi@=gA?`bUtgJ zQlTi`+)Rw7?(^EL7;mm(gSzvK!t6%c^hx~XTjA2TV?$3gL!K08?_(?uMcGfT28Hfj zTty@rMWcm4q$}LBq2c})R7KpNsy4kLD*r*?+w$~=mNL=_nZ*uNEPpetfVc+6%h~Pk zjZV||iap2GcLd2B`IIH^GAXYr(xDhq0d=;dE0K_L`Hp|q|r<8h4*s@aP`93exJAnf>PW+#0aY|`oZ4A!xtjW6?Mqk`M57c zYJ3Hpg+4Zlr7}M7wrNhoi#ZY*s(yCRIO}x{q3b95cAY}JdaBApt@Kt=XN3Q!Hw-Rf;YnU5q=o%p#^a2!e@W{Y8o`1&z~MGE z8ZT{ZpC|Js1+OyB$hq z71nThoO$uTVd`~W%H}h=Evwmb#nPa;WDsJ#^zthyrh&$lqo}N$9l=25ps5ML;?%98&r=@pNI+mY>Xp&7)U%i?Yo#Se#Z;NP}6Vt zXxo78oAYoVuLgfS{y)O1c@NaucZipjy;0=xz|1h^GP!{q=v9={f#ji!Hq z7Xax!@Z&*%Uja>fqF}%sfMMvtj{@%Qr6^zH*3*34s(KDk3iuvC7tW3Wif~V_6{>qZ zU>e{_z&w8Qj`!kE_Z_fN%QZjL3r<-d+1EN($gS;C8z74|o<(4EPrC z$pA%peYK*T!1Fgi?SWth=nCi$_%C1{fM~pUt^&LZ*a7$)U`tVyCV-9rqUnR@Fu=qV zO;PSg;90=ifNg*S03!Mp&))%coQl#LFa$6TFbyytKr~D6Tn*R*I0iTih!~_Ov4Bnh zqDjUx9WWPAZ?K}Q1oTK%ltRD_TAHF90X#kgKjd`1qFe)T0VV+M13U^?1|UWs;`tXK z#)X;y`UA!Q?gtbAegh;8g>ZnKfI7nzWhg)g_yOI9E6Pkj9^iSv27tB)|4stv)!aUS zT)-EA4kNLh2iO500&peV?aT%0U3bD0ILD{fPDZzpw(zaxeAaC*bTV#Ru~^< zvX4xF&$$f-4A?SJQ3eCo;C5&Pi?|8%6s6gts6?isd?+9#dEDcWxtYy&_H2&OHECLULmmjIg$MfuPl4?KG633J`1c*46F0&3q6=72ar zTR>O94S=zLDS(-PEWk3rD!@B{!+;+Ee*kJd0H5%H25*GGRRAa8Uce&&4`2o0H9$UK zC*U){B|!8HXbaE{&=)WiFb;4JU^!qlU@c%PU=QFZpd9cQAmKrDQNSR;O@PT7{yhMA z6yOCE0Ez*h1HJ?N3b4(D1_1GZVSw>~{{S8YEC6@_PXb;5d<`%GzXQ}+5EjrJ&;ig3 zFdgtPAP2A$1&jgQ378Ie7?1;4 z4%iDg0-#?6`2}zZP;UVC4grDuL9l!`~>(5PVt$2>h)L$6t=IF1|iJ6I#U(JPTG=I4|T*+9#upujC-oJ}DX z0UWIeftwo+r_r}3fSeqTDJ{-`bA`jv6?xzi!{KOs4je5`s|iZWLEsd|sY+~!q;$Oj zNWal=Rg*~C-9uyZFXceq`l>;elFs_eI(GMuZM7)w{z>AwmTVs_8%g-JCjUMhD{Sfo_$bipM4 zg*vv;9IPv~*H6TxzS@bH)KJ~0d^bIyHm=LRnS*MaF%GPbwUpxHh*ZiZj!31vz!9mG zJdQ}EJU|gkDYrfer8HabdtpUHqdiOUvyA?+lQ8GbDV$xWB6hLid?mhh)G}2Wp_e4# zbMd7v{jmR{8_LW1YWAUu*ip-jM_-BWN53~&5nGDvNe7)9#jj5?X@MZkxRO?w8>iD3 zjFgsSm89*G61Tmj@#QOXwT(^TZIL$An-vN4P*2txq`17bws+;&I*QS6MbfWVdY0v( zJ)L??N#;rf=r01IC8II??H<7Hl8^d_o_ju8QTpwlRm;7*qCDD>zw|XQ(T{XH{Aqd4 z!G{|Vb?>X-bMbq5SU|b-+WN9($mjBIjKtc?k)Ox(>q?E&uV&!)AlKjZTAXUD*UFf` zW|3{$V#EIW&g4k^B&BC$rpuF7>hj{3GhN<=E+mzt?N5PFNIr~|7?RzS=6AUZN8x!M zNtuT2jqY_x5sv)F@hgJ^inq)yWU7t7aAAd*wB|t z8j&yq3^r0xi9@}aqNqYyR4GzqQHNzwek5fY=9@j~mo8hp)v?i*snAXd)+YN&JgiO1 zjho*Z8|!ZX&3lw}sE$g`qpsUyks8o zRW}OXxxJ3Igi=f+VQoU4(H!1t+_?6hX3t~EQ$E|}uDJA21N=~8iI>QdbBsM}C)-*- zV)R^h_O`AMQwJOz=W?HqnK{Z;QRvd6T-PZv81DPiH%(!D9-L+1r$(LUUHyt?BK|PP zyOp}!e@4zi{J$J`|JmswL?#O2(u&x>*3Wx)sv4y~M8-J6TkDK&{Pf(h5BbFGy-5NqgOjRHO(n{;%J{umfXLwy~`YVGEjyHMcv2pqKo zc9bJo3uTGSc%3b?4Do~>()NkttSjr43SLsFV& zs~-<%$)eN7vZ5>QoQj^3nm2Fq2z@tD;}8Ri4rOrjq#_+jlMznK+jg~*?slR1 z%h76XjFx1pB#2A@!v8MHaCtWi@xOqR25z)5V~1b$8{wEXz{sEb&W6-W{rMmmHkzG7lU2S|{d`IL~kfro_bDuH1 zq?NtSU8>^CT!CN8%`A!3$U{5z!>Rg_5&D7De!CpG?|_{n-$Sl<W}L#N=EJF^#(ItC|aKSs|q$KX> zZTlF>3kLy4B*mxS4)WSt(vO}-I+G>oLi!C;r^6_Oq$D>Lp$TD1gNoTMD-0@RF^*r~7<`Bb&CaQu}Rp zg0p2UUq1dNCK^^mw=#+#VMYk~{#IvQ9UlFjDDzcot_B0;{?|AVaPgAyisL zqXC6VeGUrJr%UKF^)mWQWvyRUp;GIx5>_e9I=DVV^{m6jqq{phBcXyh9bBFaOufAP z?QiliNc5jZosu^b{-N((I^)?f(ehjCyXC0O}4H&sg@KfSD z$Z#+roC;)mh2sAnw?bS~hT|J*Xk!#|OP^HF;1o}iZr76s8_)rTfJ( zC%XOt|3^E>1G=eKY>Gh19)o&|x1PM7^0}tyRI21oe#XNUOw$XIm1NUJ0YS19Ng<>J@HLeM z8u?$uR+RyLPKpsCK)*j{wsy2hMG5@7zxjCCj#Y^=XPx$>CQQXo)J{&x{9R4Sdy_QV zkB1wnF%PS`Wg%QRK2*z^(}l7IC2Rka)*u>nWN3hl9+j!CSSdZ~5mEtjKffQS2L_!_ z%kV+!;T6WQeUaMl_;7~#76_8pvkBMN*DK(kRh$VQ*>Am_i`4t$ff`YhwuI)1!aJ&# z$LNEARJ{CdzGE=H`c4)AR~e|OsHk}ryeUotNe+BS&!ABnpVE2rNAkvwf@f&}cYm*; zsj0Y%owC3@sF<(t6@71aZzh4@%=dU$v{XlGX(}0SGRxGm@PC!cc=wAIoyfo{O#|M% ziX9C0+8g5A|3=3HU7|=yo)HP1J?l>!6As*NOB-l>aG-S*%@y1`?Wo-rxrka$;dp|M*9;#IhDNrEi9`X**_xO@28AkRm2U{wu$NZoPUQk}*H3IDtK>d}OQQx_s-A{ye8km*xNM2$FOpWo}*x zVU&y;py^f`1a*EuBZX%@jS#6GJw-fdgg}g}EqMcXUW^YPdvxlj7$rhYtT*_I!!?^D zdl9&0Zhn>unH{aW*HP6A?fuTrsRIp=M;qZ~ena(-{kElkqwlA8#Ek-1)sg)K%+Wix zzExTCOJm)qy;?mr0DW#fjc^j-E{VanF~G_Hta0(vc2|reg3Yc)DEnVU%jW%dZt3PM6D?p?TX%^L9-Qa!~6k85m&BIlGW&6BR+3SohRPU&48-cuLMGB6JJs5V)#C9lfD{8RUU&$aBmmqa%gVy z`#dKS?yP!0|EFQVSocNCs=gOLa9yq2$)DSB(?Hg*#IfjAG*7pZ)IKa-hCQON+;+eZ ze@FvEL`gCaL23Bb9%rB-0{$=&xN!cz^<@XDa1z!aUie=eF;dBbcmgKP$ub|e=v{7M zMQ-k00V|@}mt{qhgMM;ERuWeg6^t)jKM$i(ON?@QNG(6EbY;8bcnK16-B=d{4I0L> zFRM1gE)a@gxTyS&;w*9#YNm(!g-z!PW)9Ce@slb+Gfd3VM4hrkop??urkgG?`An{? zU={aa#|e8A5p!1zp?yC6RZ~J#LC%4Huk55iyw|D(r%A#|E*p zensrrQ!0=O)KC=}QxSWVk<=-wA|oqe3mExZH1^@DV(k^Nj|1!S^87}xP59_;J6-x- zPwk4>Ij4*{UyVu}jWS2*dub|$PU?G&dOj_IX?UTOoB~?EaEAb+$T;KNSDkE2?lt0% zrmJN}?$N#)dAJzq+px?=EMsgA|2z&_@&AFD23cw5ODo`-&ZI}EU+<$S9c`3Lyf&}c=jMjskrN&%RezS6qW|hyL$V8JH*@I*?F^)O2V;Qa zTKh*Af;T)`eHv@YheZ^Z*MXm%3OcBQk{tO;UUqZZNhr5vG|4eeechrHrC*|H_R{Ow zl7jXbh`Zmvjoz|3ZFKu4EwVdE$n_d?zv){4uOXQC+99)fW{C05H&@mpkBnxk_2uuz z_uu5Wm@#i9jL^UICxCjW*C6hx`i`M;BLfSC+suczio*~(5cz~0b__Xe;pp8ToHpJ& z*16$(u{1-q5M&8?xvv3XW?)*Dxos8KAfKOuFfW%Fm|eiDn1eIS)(BT; zHyzyE-#aq55f>#RA#>Ze6qwye&pnJla&9U9J902SLjJYKDCxkD(8+a~2%3knka6Vu zzD5p6NZlY+FZRDtOvFpSL~2x#xdAjuU%9eMrWzBDx3m@PG3Fefyop;Eu+ z<9vQcMw1jQe%xgP!M{&rGvoB}mbZn{6$rX7g6L>l+B|d-aQ$Mze2ws9TI7 z-`-T0gn_h6HW;hF-P=^gnvqO}j%@mQl+4XrjO_0YXqW@XjJcI24T(4oaO${KXzJTt z`k%>9Q-K-v@w>5|&S|zI*FXRQoOTEsTy2NfTfA^x z1jLHiRdE~&rhcs#B^9qpbY%Yr)c9sq%2mY)iC1a(cGbSTT&v$t#?m-((jB*(5B`A? zKPR(mhAUKNJo1QWB%*q8aW3s5M0Uu6Ed8@)w}ZC^KzSBp1hWv?T9J4H0ohBvKC8Fd=Z8hSU#Ey=-Ih6qsK~+pJF=V>kLe)Cs&Mv zSUmZ}tbZvzBnb`1AfeeGudk|-@Dw1I21tD|v-T`)g2)F0Q9g9RvUe+u_&DmU*{2b9 zL2{{^DxS5}Z-J&h>RC&@a}D0H>I@1;{{9hOmcV`DLUC$BJgj*q?3#OU!Sh7mqg)oz zE_+p?$3X;7t0h<}-yvx$8Nq(0;YD3tjbQFF1t&IfVne7~HG9eJ+AQt}G!L`HKwn4wBE$EN~mr!i~qd2qsNEuQ9@Tn7l@|Aor#26EBx zFQMVlHA(APd&`g^Q=6=K=8SV>HwR75oQW9``lQjWDL3Nh(0ElcDL&P6{U|_xKw@&M zrE-_bb7N<{ey-=n1R7|xK=m`Xt%gZqn!Fq4Qq}u>?*D!g1R?QwCR~TEeQf%vj=31b zGUx1=%x&By9XWYOb)Otqoy)2aF3zcBe|ra-*H?0JWRD12etJmGjVRx27@VtVNUl{R zB?c}1bN`jd=qR}5ZkKoXD0~aB5Slx1>fDj66G|O8Jv-Xi@J$}2cGYxOWb*Jn4)^O0 zYSzJ66}UHon6y+~N!cK(UcKk<2v*W+2${8a8+e$5L0(-=^y)}JO&XOdhK|quw}KI3 zhfxbyVI=NNkeV*RfC9}F5mtiMAsHtjqgfi9YY!sT<^JJzs3KL*oq>@8#dbD&oob>b zJWev?x+u+)I~5N-B{DfT1_6hA4)_2WS&0mUR>m_l@M{MG%vRd+51O4RHv9j_N=|D$X+7JGY6VFIxM43U<9x7b&chJQ9oQ| z&)mkkr%3@!>#79--9HKPAdP9VFE0A#zR*6@+Uu%K~x`0sMFTJPsit7Hdr@Py^?5;!2QEcbnsD{a{o zoOfi{y!TM)d-Y=X@j69*d(rV2cUhgRA{FOl5k*>2S#3}56A;O)s25ZZdst3a5T}59 zPJ}MZkC6*aM6=7J;_O0)XDuu%j0VP|zS(~`@?Z(d=9mgxF62#H4X5FFiNu?giCH1N z@K>)mJDH~E*yzAEBJDQQ;jsTn%v4`4d98-S435SwtqsVSeH(3jH2lAN(c?CQ6a4h=j@c_T^gjQ<}eZsuQ* zq8@a$F&@L95Jwg+i_<^*9SrO7M>d_jv)s;Jb&fAwrZ1GIc0TC@zDgf%FGqxF-a_?fIuNr zS2$kSNT}n7JKg&hJCACpN&^DX&~*We(2KK<=b}dLvbk*6tfHVx{$t~=a$Z8?#c4Y8>`#N%ML9P&C7OY3=t4K{=#p|7VVz&9k zX${|ET}w&cw8(zBq+qFbOX<0^uOki)Sm}B;D?M2(5a>ALGOCw4zs#aRt4Z~6EvV8r zRkUw5EvLn}9JCJMhJwqximFfb!8b!TlOooy#;Zmy_>!^+Y!|tT%-T33W4Owv4qv3X31b|kuwTj`#`kvO>FOoQv251^nxsdsUyma9NZfBOm6ZX<$82393W z*#nX;oXX-tSnn9s-(@rGFLU!fkd5TN4bP0$nVZK^d_fcMm|ED?iLZ;{>Hti_JO#Jv z9b==4p4~)d;ie3T=D8;#;6MEuh<1VqwnfH-5+e3XGw^Ri4jG1Ccao`W0lm|F5*e(z zWj{la*JvaCQcC7YH6^lu!c`_dLi^eGgVv?z8h`%UQd=62Ap?AWs80Pf4H?C`E9l|K zZcmwnpY<0!O909zD^7I)_9zgE`vOXrIgy^?c96xEi3M4!9r+&Z{h{7wqEVk{Vzxs5!BvY>@j1lev7-S*YsVEMkwIlc1UAJ9OZyi}o6D$mlO-uurT zR`&1zgUbx#G5NSctoDPevLm3;L{vB>bMFAO*G~wBCyEk8DIijyk&W*yt^t4cGE54@^0Z)wd1EJ6Vg zm87uMD8-#Z3MPOQq<@G(Qj}GdqRxvf1(8;hqFMnc_!0`XgIIJ4HM|y1pxyD4@Q?z7 zN*(;-$UaBf{-P80T@k(*Bw}VAM~|%SN+M=w9-kkCF3;Gh{#{pc?arf+$Qo)ldrr7A zGiE`yrN|atH5*n2-2UWn+hjh1?TSGYGkWI?8i%cl3D~L_W^GkWz*fZ|Y*h@xR>dG} zRXk>GRSd&c#X`@p1e(=(vuHj_Gd13V4(b>?u%X!b4BnUn+z+_*EZ!di_|IY%cl9rL zSp-lDu>Fd+k^l<;j^7kzIA9r|7?Aoq930?dz<@t+un71N@F&1Hhjc(aY}5<^ECuWY ztojo#v;l_vjd?6!CEy^y4|wCEqO`d*7Tal;@M;9$W55#Z3I2&^2TXkb3wRmu5#T7` zZ$J}F{imp^k_UJj5N%VHs{oG!-UT#3{sh2kL;X`T=KtBEJMW5O&k{SA`VnkfW=y6H z%NZvEgCRED|H{HOZEx%jWBZ@9KYI4h(XH4cy|+^AhaM!Gq1`G@U1{9;SN2sLym=K7 zWL%TEjW+97&%840!fHrAv##vE8LcX_{b4k_aII~x-xzhFz52S5aUrP-@o+p*2*ztL zugP0KkQ8@jE3FDi z&sSM^sf4apTvl`(%m*_0Jfwsw6`d$>dN>>%-5}pQ#>skWmiA~97RY;okrqzu7)Rd{ z1{b?zUPueYK4!()D8Q8J)&ZxAqHgzEXj!-MQ>+A{7rT>({KZ2p`cw-o=na~?K+LO= zT=_zY)C)~dfa@Y~M#XcnT5AqhX;V5P0A0gyI9hW8S1TNjdKPeh;=ONqWv5 zV=xY9jn8lGtZn6s`|=t&n)lVrK=X@hIp6oNYO7l)sU5c!5AI>L2Uz5HTWCo>&Wc+- z%(+f=jSlT$S6ehPPg_emsbCsIP?1JRuIyp*-nb#VSgnWY&7QilQ=OmdQ%9(=*qWn^ zzs}`%Z|WJc=bW_SvbTI`#bpoqNW_Q6ZkiiVi#RJM!x_58>J-mfXeqa0#X~!VvWP{K z-Qd?8j!DBmkt0Quw!X)TOZ(m^;#%x4b3$$UCo3Lo)8_kDGwid_QnV5)9&FN&TyK%D zvCvZE(xjh8GMjWu;T*(;e!kNNwJsJ|Gd&-ECK|u}$dZTWMEY+md?uHrv%Jn%>KDe(Cb4e{5GX>TRMWmVc)2 zXS?dNT{G90S4-{Hl-?P^O`-rz9ML>pu&MIaD;QW;Jz!I}_!`z% zJGXM<&T?g*;PRe+kh!)T6haZibvzR^D|oBEnyRWteP^T9&bF5x;9R!({A^Y*zJYqk zrmpn$YKX$--50K~7O29|`zTx6Hq8<48$u-uB%-=Z)XeuuLp8eTz0*Rq8P6Io__?9_ zuHDxC9^Z>i)lRLV?g>{Q>FmpNF0eLfc2~O3QCDqddz#al`T93gKeo*|>Z=o{j?%Kb zpd31Z#YN($|3j}D&1(&&`rl?P8dC9kASb4C;zjceTmlkTaUyKqLnBd!wZ$OpA%2XM6^F8{>q7;`(m1_ z1GHj}ECa{H+NE%C4e5CRpmaw zr+^0N&A2z=F1&ahmRHK0c(l}(&r#16bn#~@S_%y|*=n$L>W-@lcT7O^_JZ%PP)Dn_ zhV^{?;?+mhwUt`#>)1{mr=BU$5VPCV+kDS;RO8j3eOo)KErC7K zQEg-Uc7pGYM75c(Q75&7ZRZ4ER#&x&Z)7Jm-nMFj&)FFfcP9)f^Cc#qFt&Lql-OI6Un24b64Sez+8S!e z>?g#0iKyKji17lE+#QG{iFkkzF#_=-5KzF`ZeV+u(n~1Z z#8>Z1wJj9D>CP*W{-{WwMd{;2_;m_jCBnxjTwjD^dmwz$r}hBP5k0`aP=ps!_!$x2 zNZ||-{)xgjh;TwrggcAy1cYt2F~0ddq1<15&-R2Nob(m+#8~-)@9&;!J9UY#O(JZ^ z@vvz(`Lsk#Fvi~GyD3RcteXid#Sqnlqdk3Xda1Q+krGFq6S!leh_q*& zAu))2z>&nd%LHzjg^L4jn#8RTIH!e+2d<^SMXnaOUwp@}QWI+rq1Pfmp>QAdC7G0n zqy-f26}e6jkCvD|!1Q2@?@W?fN3A8(Q;>elH!4Z(<(t$S>G>1}a~IN=iS)gceveEa zg>fI6*3CD{3{bZE(#t?19(Rzz$q(#&m zE+mc@(LxdRZSAAhvH2t}QQ+>ExWr`O(m2w}Hzrw)Z_{izO=M{OI#l0_Y2i@4w#)m} zXpybnaNowBYMX-f$?Aa!Tj%S2i>^}_sq1{v1Ju?@FAlMC;^P#X$@qgY1p(OzGVZ{X8#!=%@X9q9naD}d`3gbN_r z0MTNIul_(a*>+Ew@A`r23|p-<-v{=BN6ON2rbK+(L8A zX;y?PG|q~&L}ZAF`1Ylsr}eflt$}H0MM&f(A`*EEiD^q!eT$mdy83Q(s=eWavYo06 zK4*_pT^F?t0=Yc6su2CVFK>|gqWVigx54U~2=&>5&xWWcY`6W<4+O!RNblong4h7~ z%0kJ-(mpFri>3YCGKc&pF{=>d>nyY+f7#b?n0iH{oGQr=_wzZ2!BbD@=bJoCjqQ6q z@YVK47GoBJnq3odZeVrLS-KaJc?@zDdK>rnYm*yto-?T0V|T(#4ec&j?IP2ZlCO2%8;k0v^|uxId&jYN~J^cCHz#%i?%QJOb3 z;S%pn4fT#C`yK>e19PMFi*nB@_DB!OQ0guahr7Tg+a06eG9uITkC45mEL&i1p&@5J zI09L@yGZV(W%NUp1qlJbq$$YL6MZwrs!dzmT#2^?xrO@aphVwl;5%MZ9Ut4SilFU! z_>PWMn``&TbdqYQ_f}$qK{d3VNT;6PxoSFQfTd!CIAA-0xTxhj%c!e)$4C|YC8-5+ zO#BFMi|C(4TBWu@-VUW5^DP;tHfy@C5^dEr_LeJso5!g)+8SNyt3O_CHPsH9k-=(w z>LU~j9v8Y-MU)D}scMK%1ma5|tVO-G<@?kgfh+PY8?WAc!(ZJo%+J%D(fqFL4U?i~PO= zQ`E+dW3i}V3d^->?R*bRQCr0xkm;f$urIhGfxpbrh9(%XSkPIzv8<3ifIY!?e2&@- z%Omg?ZTgJ}QU^W2r8bJ)(l)fzEk5Vn5ciTymc5VK^;6DKdkp2?%HfFE+uQnHxLZxN zUE9|8!`)bXb!_XyhhL#5G-|z3wm*+Wo&pCB!y;XH@om;nBI%n_%JgtwPfv5E^xi3GgNg7qn}-D(^T=skz!1~juh1yDq+#s88A{RWuJR_>zF{_#4rMA zpo~oM87OxcrN`}8#n$7A21?%)HBj277z4$h+Qnw@aYE=^FDEz8^|Fk-54m1^kbip9-F7ej51dc;mro^&B44JIg5O|1^Pq znP6{?#YD?CD2X$UpE26tXZh0{9xpn@8{Y01`}?u~Tkqdx;e*_6)cY63QoX-1SbsWV zXO59zoS3E(HqN-&6H6&Z8}+_5PVrRl8Cp;Ej?#LnHx_G#J=ObXZA0~j-vjzMQ5|bM zWj#8V_lenyThjkLWx*jnmyZ+Or`b`18_sRrjS!mKy2UikZI&20ZXU*Yy4~~eJl@VC z{uZ%;=BV-r)etmY=-DkSLUy0VW83H0ZqQ-A32)YL_Mc_=BHT{8`y_*bNkhaxQaUk< zwg2Tt$&|H7rbaHKWDaPx&y2Y}qG`t5id=3uW8M^D%$Tub5dVMr_^lB#q5y7%qjF>c zruQnjwtzz#rFbAr7R-RZB78A#f9H-+s(9lAL5pcFI82B7 zOmhnn!z^v{X|6&(vKTYQATvpQYaSIUcPz&9z(!N=GvPcCZcI4-G~tX2SI-kyLUg7& zey=c@umthj`^MMPWN=v1G?~ay+7>0#&l68b_Y#bgst}dKRx>5a0Xa_!p*d~1NC;8W znQp`|Lrmn?WX8jId8b?PTY3M(ST}qaEHR69lob#2x%?~XSjubL(6zqF?pe<&2m103%KtK@Dp*lju3vyAsi*xOJ%B$VWc$})e5#dn9{$TLXA0gy6_{~rF^eoKCzOgehW znl&J5QAT#@2+KPRf$)H4;#eKp!H7Et-pxf+h6duek=sMlYd&?K&+_}^2TLYCzRw2LVuwJxdCou7o?2_ELq5}b$CAmZIoaW4-&0UsNxAn8EQoC|TXW$#5zO>2J&^}3L zBW0Bg>r!fw`1ZM<HH_ znFH>Qn-`;w5vz-`$kIn~1s7T5%a8I2?m+r-*P4E~WH8!>zInyT1L|xTN;jf1GkhrR zOD4V_$S%#b45*nGO$R2_%(G;djG<<6 zhGiBzLx~B^^xHp>T5w?$#GEjze58oSI06TpoEN($6LFMv#O)ttmgEFdtb>_{ouLv3 z6{erq0ES?oghh4=6vkQFahg=v#U?v-D{CubJU=3H%@BN` zNc;l7zXs!Dv0FY`B<9j#LIxCo`8Swm;GAD?%`|@=$qY`TPiu5?BQ%5Xmk~456Yvj@d5m_ z$NDC{;^UL@`8_-;f|7~7hleOFHL@aT@|rm@P_-p-Vj^>a&=b+O#y z!HprGB1QR+up)uknx`y!Xr9jU3(`D!7rGE1J#bM?v%1;mo3buavC1SGd7Dq$ren`O zSUyiX{KF7wgSF@-IM4XCs*; zBX2W(JLbQ4@qOh6tb`k%gAw~McCI%or+#1_K?U7q1eygkL#U)L-YCT0&rDnwwNchZ z9lP%?%FRI~TQdSmv==TdYGtfRZog5YBYGjb-bRR7BOS;7gA(Fw}tYmmuCoTPfw@)e1es4_pCh3XNb_ubWBk=X|VL^k}S{F zBfKXU@IQC?ke?N)on-Jwd>lXGneh>i;H_tT#@XGVfuK&HR-oTcadrXpv0QtMx8W;1 zyN>Z^I6v?C`w4!Zh*;7NFTtN){dO%Lz;Mj5XMYWn=d0pp z-Y`C1hd2LwWb`GT!HYfFm*8mT~KaF6nU-Un5KJ_dabx)sGH`Ud=Wpr1ikK@Fe)9d@f+`zKHI z9Q%|1#Q74>-UdF;uiYCxak58NYR3^6G&nA|1|n^KaN_e4`Yr`Kia(RNexd`W^8!Ed zIQM$27STrVa+&5Y+VG8@Y=1F?^W~l=1B9LPzVhWj(U)(RUk8fR)=w4Ec*H+rPM}eW zmtypYfHAjHwh0o6e2E+Y5Dym0?qMP-<#rV~)rbh*jJPt_=$ZC@^6?-MA5TAdk~84I zg3#)08+ltU)v4e<^1UDt+bo!-mrHmCK)<&4kv}0g;i^w;d=$kl6|3NguCh(Ah>4Hr z+LSs?v6sfE@M?KiFjA+VSur!m=a=kKM-@CwE=O>))I%wlUQi-)r2a$l6d5aMQA- zR1&0a_6fRMjcK0V2KsgiCA(W@go)_Du}T_9QX=nUD5Wb*1YqxgVL%^F!JI2;qLO}; zrVR<1rm$x-wa;W6V(E99c*)#_AXq z6eC-k%v2`b<*9Iyi5pE!gh<0yi(4W@5+5qZ0@~cVgxZ3xobdpwo?j$wGD2;7ETIRL z=pa3o(w4|w5h7Z2SR$(Sr5^?rU5_%PCrRpCbUoUTo*}7o(RI5ajVCFo=z5GH zwI(UN=sHG_^?DjENJREQeDJR3D{0Ie#+qPDff`8izDP`z#nJ@`)C1{ZLy~VqqGvv+ zLS~{ZaV@8i6fNxI<@b>oKDQtdpCyaEQO5A0XVgDrr&gk!2wWuZYK87~b)nG{q`MWm z*Vl#+m0+)0)GQIH$IPxp%J^t25f;C}w9#E_oa&RSluZzQd5G+=S(kd6bVMQjiCP#) zLXI|^qzrp%ak3KIYf+D;NG;+f0L8rMR&}pYvV;)hw~5lmfd`6t$VO}smde{k4Q?{YpQkO*CDd+gZk|B&+?3?=kR-*PN*AzT|-wA zL`vj+m;R(@N9PEf$mGDd`B}D46x~v<#!;C*YOvFmi3WSUAs%@r$ilqjEf}BEkaj^X zP86x`hBx4#`-jnfS#T$nST9x}Dqfkxwd4(iypZHl>&FBoRwvAJaJG{Zd=^yI)&yuM z-D+zzIBMoX+8C%NUND?u8kvyCvy7EHLf09HL#~ME3OU*#l0`s;T;ve%*>6S@vHajY0f4#>@uW~~ zb-W?(ND=*dPKideHDSX6bte&ypB3p;_W*XL;(Ie(cYAjgock!Ll z+E(;zU+_8(0QmATuiMho`$KNSkrL~@(9|u)$zaaa*Kn9p-=cvLuggho(Wckrg0^Bj zuazg;ii>=KtZFAV+OFW+Ho9~XFJzVOueP3(u2j)owA(4yrHWfyEk#-pMfZ+)=Unvs2dk}zwyFfHt?$WIX`=g0Wl$6y z39Pod!O#Zo-?Hl*CDz4|@}r85II6Apf~AejCD!TkUumKpA1Q;{iym0*+|phoCJqdz zrrlBpi<~vqJeaD4_koq7-u69 zHCGiK>Ua>JVT6^?NN7mA0|E3|M%ZG=m(Ch>v@Qd0q*M3Smslhh1zPGZ(q{CcK!H4r z_zr6cS{?UM__gUAK$~&kD>mkgTYz-e^d2Ff>L{{;l9d#@Kz`Fv^c;Ua1lKd|nDM{T z_`k#W|EBT(7L=tSt*i0hVf??%_-`@(|7O@Iy-Mebp*dmvzb5B&605@E3_%$GFH7E8 zmj3d)|He1M1rN81<0C%Nm*1Cu!`j@h**1H7?iq zl*XMJ!w;+Ue%11OI^6?WHZr9LxxNRKVZO%U8npqtpygv4f6`c~agxsHB`xP@`GA&t zXxU8fu7fH+hkjbjLD zG1d5&$O>uRuJ0|Mx=D-@m)pzlZxXM&3qRt9-fI;Gn)WqXU+>>s?>MIITj&e5-q9kR zTCFc^slQ~T<7V|tWw2i7Y*PWOQ0t%8`i$>X#$|tLG)t=OZQ6dF*5}qKyNm{fYqj2` z_2q^hm*^=oq5mjD_NT(f%n1Kha`_($SD5zo+WvQig{EHH!)If(Xpz=?f6;c@{t?ns z7jgWm0%~=@qdG$Qk6Q1iT4;^d*Irh7m)1Y7_4VItJy-U{T3@K`YlYI6YQ6IZUf7!Y zM6Hfcq654LAic}8D+BL~#Km47(OXR7o{S-)tfQ>S5iaQ%B%B*^M2&O}66GG(&B7b8 zZLr`T??d7UcY8OftTG~1O69E-I-?X8+7#Am*`aZxj+d*$FZNRD{NFIHWj-pNo7L(B z%#4^WNR8I-*xbB;EJYQlOrsgk)>i5Bw7&k?=J9MXD%=sP&}AU5YN1vzjkzr{tVmW7 zGTJCKGi=!+xm%iNm=UM!Yc!hiT=7cpC4FyPJe$rix3vmzB`UNfDKsONYq>tr(|>_j z86awg$^%P9`|hqgwV}pxjTIUTo>L{O9jfdca{p2hD>Igf(_?L8l&(x;MS_}rTra3N zWm8nRS7Yt-I<3)Kud%p=y+ck}E+z-n>R=|1E*IT0P0{wSie3C-^IXawQTke~$18ZF zf7dTn=#u^1Vo_vni4wi5O!a=ZXp>^9U8PEG)+J|~$|YBxbBkC{T%mZ^Z*(8))p0b6 z=r%AJv|^iJE1ps}1v3nHbDXe%U)@C5g%)9W?qP4k{2IcpHHCT7SBN#DTlptAYycMY zM{jlsouD#0AlT>6xyg`an-UQpP=-@X(QWaLSH#J=D}^;E1Ie)G1dEgVSBfZ^yGq3Q z6>Ju3Q|z*Em9RwSY;8)e>RG{@%C2y=2o17*Dj00pMjzcwq}LZZuhNwvJzgVEt`dnM zxhDmyK@fW5+LU-BdB2WZg`B)n*h9PsEI`q7Rbbg;!Xj#)l_!ctjGxUkx(1_}s1HW0 znSwnIg*X2Kkw9Z1V`boK5f@LD^Zlzrg_O;%)uLU{X_R*d3`3QIH!p{$?S^mxwewHG%J+7m-!d1=TexWYEmU@*t~3`SdCg?T0!_ zzf#fO>60a;Muj!g^}Xg)0pGte-!w4{i$$ot&Zk6mXv3y<^7JhgxdA<1zkA!>*tAD7 zpP9Sx%HF>p>`1GC+xt(rXa0geOR?# z%$7Cl#REJ?4%{Gyr5!g}L{*K~u73}DT5Ov|$J8PPU!#5w## zw_(ee1N;bi=^4@2ZS%)3$cHl9@X)*vS8%S;+!y?r1AC!%fJW+!o6D$83mfBd4HvGI zU<(V*55grwx5bvJ{-Bw!tIO*v^F#|v93GoDD8^;aFt4!}&gn17L9MT0Gnx89zCXh~ zn-zj=x?oC{?Kg?8!Cq{g{t4^6Dnhy({kb%mb=15=G192e9Tkbk;^2Icl@G zzrQ!1v*RGMlFekJAQpx@IP-$eVtl8dHz`^EceCggVq63oH|Vw-(50#5H?(&_Sy#jLL2`#0y zKWlv|khMA#j;Mw{tP;&()|s!4Q8aUzJ-Z_lqhjanG$J?gz(0MBdTsI0slFe-O^~Yu_b_`@*e_xr0`Li$-J*-F29rXcu z{w||}BLf*c6m0v)IBlR(SNcMbA%6i~7U_@~h5O2lapch#M0$2kZ*`+giS=i(7lK)< z=^-qzm!0LE=FI;LXD{hweC7CLD+}e(%v$T0D{vFck^NpoAFHk9?3Qz!)#)g{G7q${ zz>SyT;jJ&@sv^)t{wa z2}Qd^VPLcf!~T!Jk*(Mj*gkx{F9s+@AmyQGC^%aU3anBkZYirs6h}Vsl1Ogt`jg|t zJI=-!_848$Ls_Fv9(_p+8&n>Ila@hVGI8MMkLSZkl*xK1)X=BlLFJgvj_iEprnqv_ z%W!4q$i*+?^+s7M!480O_fRQmOb4^(I^6Y48+qns(Jh{(2-XQyVMIYwo3c#Vh8-HN zc7kmHUC_ZzvK+omw6|aGrJlCZ(*>&peS9-euGuEiTbV(AHwjh-`tD}=;Wm*x(A0PC zioOc^3sLAVD8Zj4^on2!^I~eF^P*g)-D|KJjbG+Y=mV5#+r_Yyo_cY6>RpvP-YYYr zF?h#I_L0v(E zK@&j>L63nXXgg>h=nSYH6!5-a?LfDHMuO(QFJjnAFwcRiKsBHrK>?l*c8KTv+!^~- zwW;bG;KeO;;=1|h!r?eAhpya5Hv!Jqdg!WrbW5QN%tT?eCz#UP3f)ZTD!fg3q#g#3 zxcYBxr?CI;6*n8>|BYANGv{@&&bltc&$;1k(RWDh8_G4sG}gSP^aEnD$im)hC@4RoyoT*dt^Jh4n z^8MW+`34cfdpmE<%qTEpG^W)0Mz-B=V2kH~XnDC(MBFgfyLO9s`P}Xsmete2BaU0$ z&to~8kA(* z;L<|(8&Dt?LWCTP*`!y6o7uoT3Zl$>SG=_HeFbl#orpkq05lSO4e*~>(oP5OyVRv+ zaQ$vn0D8hud^b@Jp0E?>ZSWbuZ#{To17BE$ce;Drcp^a!oO|%=Pw!x@jDS4gIjn>0 z!P9z}w(DAUV*UWW_Z}WeVDAO~-OJb&(gUx9r~+(1V`o5QQwuzLK=C!e{ne;FR;!L9 zI4t=nW2xYsNnkpCs3K=VE<2?f!FNGT3+uZ< zBVkWC3p5kF?<$&B*!1I;E1)O5T+7%-@Pv+YjO_$ZI0A&#wcB8JK{$c{!h#EEDDZ{A zLl@Cd;A?=pzf)!R0^h7tjahREYv%7&BiMk>A8}={cqUBxS=o4jF;>nT(EBdRX|aCX z-;D=V1RVFr=}(|6@Pt=DBfESP(pYe!07lnii>v;taBS>gZB{Zi01JUgoU(Kdb|K(`#?s;fXd~7 zdum%eE7HMRe;_l$e0(qNz`BVr1K+e~f%o+(kgwqbkPCXkpFsKGeSHe#YsdzzfS&L{ z&_?hMeBFN!hzj8Xj_k%U2;I2GJ@BkI0NxuIxq!X174HQ457M;+rVmz;Gk^;;UkLoC z=4*i&IobwTp!q`J&O6kCumX5sC@O}Q!#fXVci*WZIDnHh?**3Kr7|XbYnWl-+ciX@W(1H_KqWLBd3$|iI zcLj77Eku|;7BYCkfuO(&)CTz5ICw-*AU2+}#ULtNA+XZ~l`)PQV4rBd7I}^2H!@9=6@gS;zYZ+P>Wbn&5`yN!|MsKVKL$86TC2YXw9zoAU z?R_0q6>JgCSE==f!;vy zPVlyMIQkAmUeq$+?;zt+K8?u?1Ri(D^_)GqfwNphXmpW*_jR9<8|}-DXlm#QyFbHO zIe5Z(pbGGf{xk68SL^sJCL{2Kw`@Y^1)sYGl>||Rl|v2JpFC{jY1;+52A*&aDEduw zMxg0sBYd$OV;p+Ie}aaBCp-sof%o;Mk(VtClm|WGWY7xmzTUN2ZYWJJ8sR?B4j2&@ zy?~|zPq-Cy41AL-4ZqS1o#RDZCg=(KgBUzhPT(n!4SX$d#!I+-c$CNkUe~;L<=(xC|5u|5PFH4-k!-Mjso7G5OwHuX2_I8^X;y zamm2@I>eeBXqf-Wv)2C&)C@+1uYy*9Cp-h%44yEj0+SAS!cibEcwdK@ud|GtW<%aY zpj)zUSs&0$*bv?VS_+ zebH9E!`y=HD0kvnY}=mGGA z-Ts9)HsA>dfXcuVP6m~OcU(l`->Fuqzl2Nlz2dRghYk4|t%rF&7x)**nA+>n2Yyj{ z*S|3rTv0sx59a@^zv;+8(-lV;cn#wfnfbcl$Q4K4veVEvdCf#K4;y*fPJ@!*{v!P9 zzv$rL3I7Xnf@i;Twh+{X=Kn%4UxRWHfZa>%Z+|ErTc6lg&12gX`%ClKTE!-T$iC4n z2cBGZ%m2b91W$-PUaSB-VY>!gR`A%z#hwBoV}`w34EP>IF?hiaa%bw|OH zVB-L;1*L-D4Ezj4E2PuF&`ydcOwoKQaH!@9r)YjU@B=sg(Y^Ty@R|-F4DGBF0%E_d zQOhyFlyr0?WSk1z2g(5N1zrGAOAtnMQM?T}S@SO7A`lg{0C+_6Zesp@lVH=4&=ugq zuIQQI3xIRFA#w0|z(XJ^C?UQCHu{7G_+fW6Eo^FmX+3f2;KR)To(0*!6E={Hj8l8z z^Gy)(Il!lS;rdhOdK$vlAmVd+3pN8pMl*r?LFD1C2mT6jAW_;8LK_QeP#{8bUDtvq j{0@|h0uhpbx(qxa`Jjm>%mvkf$Is!U`Yqp9BmDmlas4P6 From 38b84fce09f1de677a8c76e644d1a1992c20a6b5 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 12 May 2018 12:21:15 -0400 Subject: [PATCH 222/339] Gambatte: Recommit Halt changes (now with Gifvex's improvements too) --- libgambatte/src/cpu.cpp | 17 ++++------------- libgambatte/src/memory.cpp | 26 ++++++++++++++------------ libgambatte/src/memory.h | 4 +++- libgambatte/src/tima.cpp | 17 ++++++++++++++++- libgambatte/src/tima.h | 4 +++- output/dll/libgambatte.dll | Bin 177664 -> 177664 bytes 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 3db9a3c7c0..8ea6714cb3 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -634,15 +634,6 @@ void CPU::process(const unsigned long cycles) { //Halt CPU and LCD display until button pressed: case 0x10: { - unsigned char followingByte; - PEEK(followingByte, PC); - PC = (PC + 1) & 0xFFFF; - - //if (followingByte != 0x00) { - //memory.di(); - //memory.blackScreen(); - //} - cycleCounter = memory.stop(cycleCounter); if (cycleCounter < memory.nextEventTime()) { @@ -1165,13 +1156,13 @@ void CPU::process(const unsigned long cycles) { //halt (4 cycles): case 0x76: - if (!memory.ime() && (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F)) { - if (memory.isCgb()) - cycleCounter += 4; + if (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F) { + if (memory.ime()) + PC = (PC - 1) & 0xFFFF; else skip = true; } else { - memory.halt(); + memory.halt(cycleCounter); if (cycleCounter < memory.nextEventTime()) { const unsigned long cycles = memory.nextEventTime() - cycleCounter; diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index a5fcc148ef..46dcc6e28a 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -154,6 +154,9 @@ unsigned long Memory::event(unsigned long cycleCounter) { switch (intreq.minEventId()) { case UNHALT: + nontrivial_ff_write(0xFF04, 0, cycleCounter); + PC = (PC + 1) & 0xFFFF; + cycleCounter += 4; intreq.unhalt(); intreq.setEventTime(DISABLED_TIME); break; @@ -265,8 +268,12 @@ unsigned long Memory::event(unsigned long cycleCounter) { display.update(cycleCounter); break; case INTERRUPTS: + if (stopped) { + intreq.setEventTime(DISABLED_TIME); + break; + } if (halted()) { - if (gbIsCgb_) + if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) cycleCounter += 4; intreq.unhalt(); @@ -311,7 +318,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { } unsigned long Memory::stop(unsigned long cycleCounter) { - cycleCounter += 4 << isDoubleSpeed(); + cycleCounter += 4; if (ioamhram[0x14D] & isCgb()) { sound.generate_samples(cycleCounter, isDoubleSpeed()); @@ -329,17 +336,11 @@ unsigned long Memory::stop(unsigned long cycleCounter) { // otherwise, the cpu should be allowed to stay halted as long as needed // so only execute this line when switching speed intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); + intreq.setEventTime(cycleCounter + 0x20000); } else { - if ((ioamhram[0x100] & 0x30) == 0x30) { - di(); - intreq.halt(); - } - else { - intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); - } + stopped = true; + intreq.halt(); } return cycleCounter; @@ -651,6 +652,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned case 0x04: ioamhram[0x104] = 0; divLastUpdate = cycleCounter; + tima.resTac(cycleCounter, TimaInterruptRequester(intreq)); return; case 0x05: tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); @@ -660,7 +662,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned break; case 0x07: data |= 0xF8; - tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq)); + tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq), gbIsCgb_); break; case 0x0F: updateIrqs(cycleCounter); diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index ac2b5d2d17..af9758eab5 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -42,9 +42,11 @@ class Memory { bool cgbSwitching; bool agbMode; bool gbIsCgb_; + bool stopped; unsigned short &SP; unsigned short &PC; unsigned long basetime; + unsigned long halttime; MemoryCallback readCallback; MemoryCallback writeCallback; @@ -129,7 +131,7 @@ public: return cc < intreq.eventTime(BLIT) ? -1 : static_cast((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); } - void halt() { intreq.halt(); } + void halt(unsigned long cycleCounter) { halttime = cycleCounter; intreq.halt(); } void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } void di() { intreq.di(); } diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 5272443ba2..68d49189ef 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -119,7 +119,7 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T tma_ = data; } -void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) { +void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq, bool gbIsCgb) { if (tac_ ^ data) { unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); @@ -142,6 +142,13 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T if (data & 4) { lastUpdate_ = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; + unsigned long diff = cycleCounter - basetime_; + + if (gbIsCgb) { + if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0) + tima_++; + } + lastUpdate_ = basetime_ + ((diff >> timaClock[data & 3]) << timaClock[data & 3]); nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3; } @@ -151,6 +158,14 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T tac_ = data; } +void Tima::resTac(unsigned long const cycleCounter, TimaInterruptRequester timaIrq) { + basetime_ = cycleCounter; + if (tac_ & 0x04) { + setTac(tac_ & ~0x04, cycleCounter, timaIrq, false); + setTac(tac_ | 0x04, cycleCounter, timaIrq, false); + } +} + unsigned Tima::tima(unsigned long cycleCounter) { if (tac_ & 0x04) updateTima(cycleCounter); diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index a3c0c995bb..a66f987837 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -34,6 +34,7 @@ public: }; class Tima { + unsigned long basetime_; unsigned long lastUpdate_; unsigned long tmatime_; @@ -55,7 +56,8 @@ public: void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq); + void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); + void resTac(unsigned long cc, TimaInterruptRequester timaIrq); unsigned tima(unsigned long cc); void doIrqEvent(TimaInterruptRequester timaIrq); diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 4c02731caf11359d434846f5270e96c7848fd240..6cfedd62b455eb9ea2308fcfcb1ca11b220db73d 100644 GIT binary patch delta 60971 zcmb@v33wDm6E{A+*(4isY&b%~nE>Go0tx~lC|O8gfuNjnBbNb15iy7gx``V|)^P`@Y*3Ncmax#|F61d_DD9o@Av;co~PLEs_N?Ms_Juk zCb>V?%l*0D^1h}k(u&*rJCa*gWKDIvnP|Yh;kPDcBR{d#`j*rA@4A+A`S0s3@8iF3 zwY&$vyK)~$B>GgxTZwn#_j0QbT0YKy-)uPzzrVK{pGd!#TfM>aBk!Fto9dACf5S5+ zY39;!vlP|nvJ%N!nD)mgo!FF!q}3+rPqQR7QdY2rO>!F={PY_lRWeCBw$LQaThXc+ zOIJ>_UhHnAzNtMMr=*(hWVuBy(?YhQn=m}ZgGZ4cKN8M?OzNT$hOOeJqgIz<;BU}?9Tm36Zd^${L@m3&Y8_@#L_1vNzO`6 zcSY7<_(a?2kR*d0zjm3qmcNUNbRpX6+#2)2LN(oA0crSKT85`b?J*Yexo@hFHNm&z~%0?@v z!zZ>YoMe*h?yTe_yDJ0#x#KQ0GD+2Od4!fD?e5XZNuDhaD-$d?nWScl%hHNjl#P~7 zte5hIrCm&tKU;`WFQN-^^@$*^NfG1OSfx0kT}=8!KVc1(^Mb2sz1HBmtzH(Js61V- zZ%mg7eq5=_f9i2r=17v&F0u_~)i9=xdB5fjh;RI+1woBCHNYh&&({eE7#7<0R6^ZBA~ z)|n<#;rq(U`e#k+-%}oIFoca)_BWW<#A0{lB*%`mJAdwGcW1jd*lnwkxUM_NW@zX*4SOo22 z0&zM|@gHTC_<2Tz2_bTXbB>RX|SeBbPZVr#jEZmQ|Wv1!T~&vu4*avM0*L6c&)Avq?j7F@4x6-G`stLyK4!_%#iom<^hsNmTxyq{9;I=7VrPoO%+JFY0x;u|{1qM35JD>KVl;^ciObGWtG$-7YIXry=#${dFj??9Q8kmCI( zb1G82`()0PWwM*>B%|e>mBJ*cEy7TWtdPsgG;>Dp9b{D!Alb{X}E7| z_;q!)eUWN3UTyYAEDJ$#f4i>so@WGm;Vya2B$lK@Ry->x~+$ z#fpek2N#fmmXpQH&RHUlWy(jWY-==~)Yc+6Kj*716&03wEE0DNXYfoQfjn>B?um3c z$rn1Jx)dD}*+Nd%cKKMNr6^d@5x`tg^;xZEZoERx^lJmJbAb>gQ(60+(F9M zv`!0%%9fHu-9YUc^HE@@-@UaBAgqQI~Hs!S6$M9A=C==)t&K%4o zm0TYp`@7^FA&7@IkLj{Xi|FveC%U7Kd5&vd_at7$pu^`fhm_N$mZKg;MS+g8$@^&< z?}xhdy0>BvBoQu!>aXeQo?KOOU7a1FA!l)rGjx&E5xyNwX4yelOdrslWWX8TiRC9t zkHdNorQ-Q)||)5U$+T?T3d)Y=yis^g43+P`nhNYR;T)gL6_)1S2%*Ktib?KOA1cv;kV;&b6@~MuQeTSHyiAk(Hi3ksimFIm`q$REAd1R-F^3*83d`E#H z4zUu|;sr)%#uu2O0wXlzm_LT`_6W@!9CcQpglZgflw)|<1<~yIdD4s+xn{46;zBcC ze7V2~%}5*VNr54%SaoUl6UHGHfI4b|U=phFVs?QMnsH1|j;Twt-dr6UTAgxSbgl3%Q1A!5mam*_mQzE=?LN%&M%Muu&8OQVy7@--*bPyO4tj|4-1x9GbdCVLWOfv_zv7Rul8gX*f zP7dWJ5=P_Y4+xCVjO1!X0wXlznAZeGXvQ(i1V(7aF;55#$rSUP=9t4#!Biv79`KOS zs4Um)c2Qnv#wB(JhL%pGnm$Y-$4M73N)m~5tU>yZPWp~7kHqL|6x*iLNbMTMUe!sY zKS*3HxfJu4Rsf35j8z@gN<`6!}l;W3WsE-=Dl9Mei*gvU6>DlllD15V_qYjzUNeW#cg;9R3--DD@D z?Yv=rldwzXZ5P}0qCwuyXBduqF~~yH;oGT_X|X%NJ~gDG>eU*RbDsw(PvJm%36fD9 zNQx0{Qf))=eV|Vep19C4gMNk;R1&#IYZycldB`WpCrIF+12GsXX&=)vQz+{-3g6n? zPz3qgE21!n2=e<4B@lyG!ay#;FF+hb_RiKVzpEV~!KLdl&;4C?-r_vRLXK&d=s=-H z6MZHPJmAuegt?PAgo5rUoA!a&ND$I%Cf@1vLnU5g3S1^SU73I2>FZr=dbL~eWU!_x z6cSY>fmqbfyWeYpXyLmmxh9r(zxhF8fp@?0H|&1<&IU2ALu-pdc72Zm&2dajZO6im z8mK*(QM(;vjzE`Nw`=u85PSD^+^Qanvy!D4Cxx_nBPvISD~Sj7fI4^lXcLNbY0?DZba2iiwA@BTZ0pTdXp)6pI;ryr6m zsiOeJN3aUU)WJ^y5RAWV4SomjX3&8=9IU`!>A_n-o(jf)0Q_~k^x!xC_QNdfFzZGP zChKu7%g;PIug?S8Sg-&~^H-oZx&`q@r`pUI|Hv1?xUcmKBAWaVjQLnFW^zI>=5;=1 zhfizrKrrq{eB3@}GJi1U!CI07Zy3@&jfqCXNK z=@6iEdFq3XgGF(6qaRsnU#Fo%7+T%difhFm^m|VQzZ8`dl9~W%oGzDVI=UYc`fVi^ z?=dPT<4MC)5N&XKrMZ35^k(X{2}yH(lC)m_s;x_^Eli0{4Y&8XvA|}!zzn$@hrBjS zi8$!h+Y)6Q_*qMK98q4or>$w&8YTC^_R8c5mhdDfP;nEjuZ=$@s#cC+ zL$56dvX;fyeBnLS)yCn_I!M)@a!tQ@+0ZmBohmFaAx<0iPfZzogc%S!2l zZVo$fbvYr_vO}$$Sg4fcuTnPpKUrHF&?~S9Ea?e^9+`R%$Up=2u&mVsI{0Wt4><4W z=?&eS*lZ_P(L|59z6PO|8GaHHbl3Zbu@S`wh@(~%FAFB@C1Q1m+a|nI6qm~R1?GIZ z0CKRp65|ZbDefpq#vX^pOvGIzw&ye~fXKJooW&R(J5eoSI!7r{MlSO!B|5gje)U$c z>)W5Y_-Ps^4eT z)gh&Fg;=*@dr7-|vKe|+nSOZ#PbeK0cme)4#A-cwBWWJ5OADeI?AtlJekerJace<0 zJa^D>dY)S*MoXj2Qc;H104~{agF?h%6dEZCkv~K;>Zr)yVJ$@&a!Q?L9HgDU+s=0< z1MsJUbrzyB%-46_{4d`D*c@miz>z5N$*>T3Wwjl2qQwY_1M&U8OaMFrC=30;B?C1Lm(CMBR#_o zn{M%|fIM-7a=N1&v~brR^)E=?xIsaGXQlg>BahsmoWH{?-g314C%4?7pug9S-AvI@ zM6v>$Ci7iX*^idUi)9xq@)ypZm}mbH>VeeNBCiPBq3a#wmv_Y4#`D}UEPs5RPu!uJ ziz>XvC->bzHGl8+^Dplo7dv_Jx<})kpBSAvLmW`SxVF4Xi8*j7ke@STMDo6FJMPI4?xDbuPtxjO z9s#4f7ucx(kt1sIap&m1nJ&bUy=$`h`yh?8)$N0{!_*)B8;AbrDAiW_ z4wVDRg?uMwH0qn1c-zTtaGnsOkE2G9(zD6A^@6NUUJ>QVoPOoSg;YNQME42sH~wTH zfDG*?fS)${l_#>VyuVjki1OqCUwN#52;nDyvOWR+{`#gfw*a}uCqTH*uRM``<^BEm ziYQM$^OiTpUq1mv_X+U#@MJEaZVyjBM7@lbiqv_Id*qWMI?;*rZkN%fJR`EylZ;S6 zk1AXDHC7%sk^!W&4v#j7BEN_jv1DC^VDziaJAd0iHMNDr(eC}6}C4N)|mmsXl_ zNtVlD-xBNijF5@27Gd1}1N&+__j%A_} z?mD9|W$G$SG$UR|iL&G+uY#1RtE_)#ju&P5`qNvOTvu0N|DOC+2W}bePeWMnofxsQ+s* zqA>MTob`*U{EnC}wCAlQ|5lxW7s&-xUm!vDtojrQvQ5=~B&eZPJL*1v-M?R4A*fQp zOgcEKo<&O3Y^qvgwva>{%s-)~Vk)?_$J6L3?ddl|U*}5L9r0Yr$ zrz2LBBwZ-aL{=zd)_xarqtMB$eJ8R)C$sh+krg_bwF4q6bTVtjA}e$F*RWHpHdYiAc8b;PA}e%C(o#iM=#->&5?P^BlGaRQ`OR); zWs(*p^1^vZ+BNZ9Q|OeW{mQd->GWrrp;MB!UlbHNC21dttk5Z4dsAeEPU)KZFSkGM zQO?SAtx%AKq0+U-L{{jOuH7fHLZ@_XBG1;P(=@M6>DtYrpwKB@Yb~;*QyWLQ)<|SY zr#9tUn8=Ztkh^k~5ACE7a_2>k^g-@xkt21GJHm5yY4e{_VIeTC7sW^!)Nt5l9aoS6uGWBd79L?t}WzgQsTNcho?z}>)PEsO$uDsMj?HpsdE%Ga8_QYDHFq~ z*jRMkTg~^MT09p-DqYtid72curd78#6uPGU!PBJBHSH&!CWWqPU-2|4bWJ8!p{v>qo+gE^YGZkt6uPPn zBCQj31SzHh) zbXl9o)1=S^?M|L1g)V3Vc$yTtp!MQuQs{z~!qcSC1&5Z%Go;Z4ts&AkQm7$U=t6)( z7hp9~=p-I42n$`%4)ZiAbVl35)1=TDt%#>dp)=ZBJk7_SGumpNA&t&x%Xpd;I-@y} zzL7%CU1Xs%0Seu%D>R79Aq$<+(s-H_+M{*lX;Ns9*2cjzq|qKNo~Ow~d$dTNCWZEB z)rr(HQfQC%2hulE=#QPG(4GK=j>2kWp%N~K6xyS08a>NTC@}$h!Svcz{N?>l(F7bQn%^XiZ5f->xt%hNnp* zrkQw}G-BGNmc&CEG3`80lSWMYmZwQ0rhSI=jWqgf2TWAKghF(6pH_A6!;xg8m0S?n zh-pvrG&#+rJ;~FgkV%`*)1;6|%jRiP$fQl+X;R3f-NDnOkV)&0bX^KL=rx`GTq9F} ziCXFkg>gZokV*Teg<&DH_A5`5LT2q8Pm@As?F*hJh0NN=JWUFjwcR{T3YoRdNb3qY z0*(=lk!bUFG7;b3cn9le;YhO3BU}(EWY+FOI>W+e4{eG_lSbM|ktU6_fg(*BX?;YR zG}5~8w1daB)*6|*OhguH4TWrx`vWX=9abZSer(Q_AdR#ykrpu&s~zEK(kE8i%hRM! ztX9m^q))8&j)P}Np;+xzo+cB;YR@5kBZZ#ZMiz<(=@Y9B;c1GYB<*&d zCWVr;TX>pZ9Zu5P@eFB{q&4MfQYcA_;b~GRNi!jRBZW*{p`-wXzJ=AuLgmfKDx^@7 zww*y2W_WcVmmi_0Yp-(xnJ8Ub$zAI?A6ZA6@;_4UpHSmzb?lRPRC_D?Te&o$ouxq&KP}?XrWiaC#S1N9G2gpK>p${pqb)j)bcT2r z$M1Q%cuM{CBJWe`c{KW#D;*~Xe@Z=c5j~|2f0Vd-Y*uzoPU4?1&;~aakD~qFQM&#u zW}1NO81^FLZHeHQDLR0T{^5OrmmZUAf5iE}`DD!xKZAz&J(zBO0u1_f`rLJN*Zw-9 zg?_C|wkiv!j7ZK7kP!G-dkARW7`hYn-@tp_i2OgrQ2m*0P17HP_qyDaOO_h8iJs?= z$)(5nR(V){;M07cQ{B%Ep(^g6_M3RRW%DBRa2 zd^uIs?gQoHsV!rT+ZC`Nj>4SZgRhw~VS1!#(?XJb@7V_ERCEzy%ql9i@_nx0Dt-;( z0BG{Cm;II0J_L^J6cAsfXqIj-?YB>n4i;ae;N?e1!LYSd)j1`4dSZ|p5TfDtK7#n_ z@Z9w9@D0Q~>OJL^=}CjL>(`2|Kk=M8FuLwX8=wufyw+8R;W|wTn~~p{-mOFy z4v6!`t~LKxy!GvkizT8oZ+wt9s22Wa>_vfQneBrQ!ytJFt= z@BJm!w!QO5LWw|`pRV|1@rQg74i zO6CLY8waX%8ah)0m8Tv^bNqOxq`%Nb3(whj=i6N%nRuVD7Uw6dVg^~u0xfVrov@Az z3Ob#IGHZ&cbt=l*(iEO-0Nt@T9hS$%%=8d7+UiV(* zrRIpB)>een;dRpUPo;8R4|Yb0dnj0U6Z2yBEW`2LNU7nkD(fF=FTNh-y$1RiT3Wl) z!SDY|zq6L?^?s#5f8{j7RCgDops-D%q3?TyP^|&y!WyqCb#%i|eH=7$WBNq9A`i;P zJVmea&{E!--#_)`DlrYCHs7*#jIXFxh-ViyLV)`8r+i)X%sn)kg^Qb>FDp+z9IR6b z`(B@EI6a;0>rtX|+OSWQZaGcbzVkZvy5buSfw9ItcIhsEr&J6FQp#qOVYDwg02h1k44z2x!<5 z!T>V>&jL0Az5vwgBuQfcuLC@Q2s{Yx0T=;T3>bnj0Q0qt;2U@#yDumZ3Xa2!wx zxVeiYO#?gx*w+PTM{&4JG7<0)AQx~6kc_K1w*saDo&anBW4@Ew5e zy|o7P01O0-0}#)B_+1Qm8?XcLDc}s?H$VvP4iZlse)|JP10DoC4OkB-1vtLMe<8O@ z(!YRsgi1$18ekaUZooqTCx8U4#P11!2B-$a;p%P|z#zbKz^8yHTsdLB3d3t04;(A01cji zZvZa?t^y1pJ)cB`1Cj>A#DFD$Er9O;4TfNl0H}a+z(0U4LnUcCU?t#Vz;!_1EJ>oz z4Xgqj16YQ^|LMad=~2KYK!ZEb8vrgq1t9!Rm2{;Hlt7uuorL{(0MchaWwqD5{bhAX$-m*0Lv)pBfxKfo9;q? z2CN611@s&X(*jliUIMHGybJgUup4j=PzAUQ2pfkU0!RjQ2hg|8?guOaJPmjeuz^08 zRstvod=97v#Eh4u7J$xxG{6wRc))DHbAZo27 z;5gtLK$9tGCE#YjZGgdmF@R}+2LNvaJ_3{hJ_Vcx`~dh3a1D@pH(~`a0x$y-1%+K3Y8-D}4SagzRaT!|}c0g9a&q=M`FJWVxevzNk!j zqG_8=pxU;YB;r0=jnXK>_flY4C1{G{i8x0tPn!ezXmJg`*}-VE)CO&MFdA)hKq@8(fKlnmO!GQ(Z(FKO-idL8(HYU8JYj^44pV5vy*3NOOMPOJVR&B z$UH-t2>Q?{GC8Z1*IsF=|8l*t0exfQP9L@Uf0o7;K;aH{m znf1+}M0#lswYy0`BV~;v+?2zU5xOBK3PQKsa8rLl&@I(bRj1v-8rI`qt%QVFk&jSx zgOn&|9dU+1zT_F*AZ0wG8)O5|=muFv8N(ottb{@0i{8pz6>{e<%kkM?&%#{nj+T@z z2(ONNcLfl3_qg_U=XaHM*MKW_86VKbmv-s1+;{J*j!OYUmVB`LXLcEfJEuG?@?p~@ zUoALU9XD@<@@#%m8h!t}I?e;h-9NK!6<-7AtS6nEHRmQ;Tw9jWJHJvh3RQB#!Glh7 zGeygP)UmZO21noqyYnm{4|e5k0LJbPJ(t3Bn>eNDcroe8pJfeJ2(AWC3n_@4Ks9~vlc&x?}ra^wZi`KURTRjFDxKM z4p+xDdWNcdIk^MZ%w0+Hun9S!Y;fJozEZw)bz`q9kxy^w!*$3?o(IueNr`TQ-MK=E zQeBA(V00x;=}LHj%2ob;x+{IXyp!CnVbs$w??LQ#j_DOeKB&x)?`(b_R#0|#o+k$G zbV-|-?I246$3Ea@D@Wv^rqu~bvu9q#$=>#7R)*h?n5mBYRvGJllx}7R&V1Y6ysU$&{*Z#ie-%(AiKSq4B(deXU5$9ndh4F z`g199#}_~sfu&ACRL?Hu+;cgmyNZ=@%4@Ofa~ORyI?Fw@0h-fuixTntixdBSm`Tud-UL-MH6VC*0Yg^feHnVz16bUl#{or?4|W$i0HyRO_Bs7g8V0jlVuIM?OF zo$88VtWh)4WgTZUbkVuuQWe`P4NbRlz64Wy!5>Cd$B8trIviCeDDS@-$6U((R~@E^ zUzN-?tMK&Z+clNbW}_2i6fT;YB_Gao?H-4m?cmZ$_^HOfY@nthomsehI(|H52WbF& zmPQ=Uf?QZXHVhnF`B--l<#r-<9rm(W>o47aG|H70CBD|0rSKx{^02{ta?#p{Bk)No zI~-dDcR1;w4^c>p*yS%g$CdkEPcT__D@$J=$G$B(^ZIz!foiap415&t@@}VO@xVoe zPO3~+{>8BA{HHpn15RX*DpTKRAAJ-<)Amqkkx?k`Ql5XKeGJ1g#kK?0Na?*d9#sy# zL7&%ew7x6;onjG#kY1YRU7% z1!c^}Dz-|oy}P6GSW?)X@E5BmgJ(RDcIq9R$5C4omIx!osDo;(%{fng+ZrrV8y#KuA7+Bzxx0K5F zCQeCaRt6Y2kfdrbUGH@&>Jh|}~j2g(U-dgn32bs)t&s62mrWTRpT;~BZcgt^^ znuJkwVAP8Juyw0?QBdO73|X%eX2?57_ny#B>-O_j&je-Ehdm-FWbih!vRzsJVWQ*j zzpJZLi{8aFX3hTv>9k*S>WPNTfQ8d`EC*$N3^2kqo?%}>o?9*i8X=kZ9S%c^Q4FhA zvMMN}NuQD;;9uo<9=^DJ(g+1L8K~SHFN0Icxx+p~ZteiLZJPyoS5Yj|87?_p{B#yS zN%%?KB9m~Nb-*E)Oc7w8Q6@j*%g!xGI7`iG zR~ClD{p9y7Hhyx<-iWwbIv*|YhU=H++8l|@-pp=iDHU@Tro zfdtPECAz3}%R*w3BAQQ+t-I%r8Kko;Ggf)K`1%{kQs~l^w zi=w8wj5-09S2^ejmu)E*Py8yEpQyCayHLco{j|R>kt*Jhr8Xy;YR|k(tHWXDB_;NIZp*Oj4SY{uG1Dsk~P_Kq=pP^OUM7 z<$2^%mg^c0E|Z)`CR>t6cFDO@UXg)cEG*%U3|F4;Zl3UN9(mWbf*fz3;cAYb)aMOX zSLm)*{@R+XByHpB2w3`NwieQovc#s+}_Nzxk|ZzdlQpdr4($R(yHVwGBh5z^B}gtO6%b{ zvJPx+cNbZdupJ#t@6RdfwqrWGi+X1DQK$1~)WG{E@)`1K%JyCR@WA}i1@U%Sq0v3l zJpet13-#obLKOTHMLn845>j3jj^S!H_)ybM#j~?>Ya(2yvFw9ERI%bWGmtEgrBvy; zYltNaREij-a95|OrpRok9itXINZGyXrg{`eC>;9tCFQSOOH#N@J{@Gqr#%@1X#(Sd zGvs}Pg>w9F`)DujR@U#%tp8b08nyCGTMlECdFHA7!^+j&y-k{?+`6ZggC;?eQ*xqEgaDLv62eWgHHcNv8$ z`q5XjxTGa8LDY3qX}yATO>mD7zfb7DpC{lAtil2kpml_-a-N*0wLrSII5)&Of3W86 zAu&=;a_*k*D6yb{TyP4B)Pf59Z(VX4+E|M}ID^dbU(oE^a*@(bT_@$$Z&1MQUN#$T zz;j2>$K|jrHYTTmjx5u*fT{ae_Pm3$mHSGYnNEJH6qJr^P0ZHfHRJ>|Dh_d!zb~gz zIwlxrWj|EV+oD`8Z9XxO?PM`$`@)Zn7W>-CYtSQ6>VF7FK7hvn&jYptz5v|xF-AbZ z8vrZZH4HH1kTSpQjrv9qTzU!XEqC0Wmz4Hegdk)k~($|fK8e0CXwV@5Lm z3QDPME2d(}mIg7?8dCRLU@a&@0t!UkETU>HW)eizrgOlCe2hOGQoL*x%qf?3FW=D3 zn!g0xcsnfpmh$9ow>a>Y*wMlQqSHcQD#Ene-BTuO4_!iypHq;WNDiRUHaxNXoE;2YlM_8DbKMuf4JiOCATUz?+04nNv$@m#Oqe9MCi6=FG z8U5%iH1>goM!ldcgJ+b2Ow%3yqza$wmNi0VTR+qfTX5Y|DnbrL{-%v|d&9K&dsP zB_~yUfW5Yv?grLtCI{6jCW_BJk3bM| z^tZGSg&lCTLS|~|lVZ(p6I25_MfVkSprGbK*m<4%3o^C!4o9iT47p$)CI*!5px6#I za%4P5q6);{`YMQo^IMDeG;IMILj^4aOZp!Lyn0iTu=p`~u8EWa&BS4*H9)s|I(w;kjDQunarw(TbxH6+7IX_hQ*`3fq53#+ReX; z=NRad-$GdSWxPnaY%fv{oY@hg&5`WtvTWC~Qe-xy=fqe`P9t_ItEF0u%6KEgsSo)s?g`9FtupoGPQ(;%ZJpAy;N+Kyv8|t&W)OqwKY;e0K1=Xkz zG(x*PqFp<&bJBi@^tQ;Fe;%3I)-a=#tdhnkI}Sgdr<~lZbNQRNZ}>}iJE(+xck{ADRY!nhPeu!he}$azog`# zl5QtSG<)#jc{p07Y4x~^Xf4^jJipZHI*P~&VgZyy7qS*&s*`raR%83dj{qDvGsHzL zcU~^mfX*w9hwGK+r_y2_o2m@?Q=Cf3bgt%k0V|a(xu6s=N*^DOl|Ppcr>L8dwI4rk zt3GCT?m32xl+y`|ar*Gta4J>ZAF~-w6Nb~ZDEh+KX#HN4O3c;!@~%4CK^5y$3;8OT zPfb+~jrKJdLyH7LGW1qzA=%K;HW&5CzeQfuE<^-tYBM}kMOEYIFaG`4RkLFgWMEX_ z%Vtk{;g;Tf+5D2goyPUB51^2=k7V?Tk0N;&54pH;qJF3I~ z!zyd6CFwXN=wi~jIC*$=9V<#}K1H#8#V=Hl0Z)vvy$#UhwixIwki*R{|mU}=kinL;FB0K;&5s9Avju39v+_|M*v;V#1SO5ldKp9-MMTJfkYk?MU0i$_^4jlVH{$$pr&j<} z-HQUA@7_kKohT&)<4a74)Gs_w6aoDv(5Y+qhNzxcjuvC@>-vB&?o3Ns0U1~~yJ7SK zI=g6A>K+~{j4q2UL#o(7SO?T?dcN;)aGT#~S5b%QF}$Ej-`23cb_svP!@CYRiKAn? zdkxvUeLF;u{|`|ueiEz6&!pO?k3e-d77&^mH7*B9El$1qPv zD4e@v7E~j>C*n7!MedGqlz*a;drTaiT zC6o?`e=qM`@Y>}9CE`Rg$Fd|$DG>WpZSEHlTo~q8(GS+UR7i}oo|4r>;0u)U0?rmt zQFcbFf~^=cv+)76?1&Od2GF74MN~<6yl?H>b?>8sbYiU+q+|eHFIWrjZJ8qkjiI%v zqp*O^yi?cENS|Zl6YO*lqp)ZtLfQx7G}^&;i4=#PjBUJu{5Tpjt4}R_u@F)*6%5JU zLnDXuL{r@m)D`{s_O68lW9+-ppjXvn8%^Y?%Oo0QUCaD>?|<*GdjAeGWd8pcF&(s@ zb@9^x|E6FAA(o%{-O%9l+#|QcU%w(5oGDT;rGp87PP`7LAJJr-T5$#uN6zp1IjqIc zM^dj(`ywYA%Nm@4$tOJjzQ(%(vEr}JmTKXq!?#rAp6z!_Md7l#E5LJMQkAqY)v3ia zp#^7v7G!^@L0VMS)FS+Kt_88y(xO%csQ5Z6HiKDo2^QQ`=iqm_oc|>~EH*L99kpg2 zD?1lu4^d;G8dly3^pm$&>PC;FR1f&=uCcQ{AK%1VcLkML>S*19g~95~c^JAaL$T<4@e_!rRV3@$=e zVm$_U4-kD8chLaP0X_u8pnNi5?K!2vsV9|ZPIYTM{G4~*!-4rfPvH)YJ8qxy*(o|I zWv53s=Y!AYm&MU28cpX-`Z)Wt^3du0TR6kWHN=q9Gj|Uiwy&LkQ{L6J*U_p7y{G0h z_Z7ROwEwb~>3xqf^~*NwZ6*K9?j1>n_4!f=-hdTM;nGnFCj8$9o3+BFbZA|;bRYk} zyHfdOYVrxh;Pzao0{&R=XSn;vdb-`jAFCIx$)|a3lXBZv+dA!mF06a$K0GOS(S*w# z<$0yRNCHDi{x{m`ew#XcHH&vV>GxzFC$>FtuL8>j-(9!3j;NF0b!%pj#1(+JhDJV? zo^~)|JL$>pXR!ZCcacbv&h9btxON~f2RrslcG-c)syLuX5>*^9c=R$KJ`o^;Kfu6K z{?~!Vy$v*rDiGHg6gn9NZ6&8+X7s1gq71aBYttNfDk6wWIT3pK7o?dK1s@Madr8pd z2BV3?%;*V0XmKNe)J^P`d2c7f9otRYKe|(Ri$T|o(8pmI1B{wiz7<>QP#l*e}+L8{0c2lpysVW`>p`$YSJ16w2r}O)I&jQ!D&X@soYdN zZ=(kXAl9!%1dTSDpoIpbQC9=)??t4HzfhE;o(9@4i*UW~R>$tgy)Nf2elyRP=l2SJ z=wh^3Hb}aKoJQWimxbMKu+KEeI{P>ykH+y{X4}N+WKJu<&;C z8zbA zw3`6RGe{{RL*f#ZoDU$nC- zgR!p__DcuTx_MN>w3vS4i|+m9q=~((*e-TxzUYBql{AIcgC78ItHdTq_6cCpD3YYd452bb1!m6-H+Kc$Sjri8sAS zn0O`Y(jn8rYfAj(;f{O@Dxk-&xLWmMHr>%&)DrUKf3;AWO5M>fb7VG0UehjO7yxn& zM~={uUkT*v9NAY#mJ8&29N8X7`6^w8q|zU9TtmY3xvIS?kh?gtx`n9RC6LEB^1P13 z4NNNj2S^}!=w*RVJwhN~QRtvSaP3S2eEX&hIz5KJ+%$kwL> zA{&Sn9C1(}#sJZSBZ>tg1Bg*TXoqpkoTpubRBQfAA@ausGPaZGyFbeI# zSqL^n`NVH+$Xe*fosHJ-Uk%Ikl(XQHL|mY!%|+^)Z`SKDVCgq(y3J$Sb9PQ4`pI^eW6 z30Kvv`DcX93zX~s^lCeZw|`qxCb{oO@Qe>&#QKiR_VP4k!qt?<@d32{g7%cM`fA7c zQx<>uA%Y}NQ@*;|p1q`m{Clfu<}{_xzg-)2_hZbN4%z*cMgMkecqQCdKBqm<3#Te2 z|MrglYX%J1_9{2vA?4qH6C9N$CjAAdhe{FwUz?fK1HX1XJqf=L>FK5THGudIBYzI? zHy{@I_5i})?gx|~0+<4r4{!pW2doD)4Q11%wxNteSNwL;VPqJSo&|IXXVU$E6Mz_W zU)+*#mR`HRc;} zS6hd%wx$CU)fEw}kvchyC7IStR7Zp(qlB>*ro}p?oG5qclz)lRU8i&n2PK+QZf57z z55riTx(Yb9pVE!gPs3SD_L{&m3-F5szNZEFaRPrI;cpT6HH5bc{0ZPq+IXQ)L@oGg9-~t->ai9u(((vsx{wG5#)%rutLac}I2Pxf1Z5qW|!2ldTA`18?1>Q;c zaU#8q(zl581xiPWblYgwN}U_U8mp6{S(5q&rJkeImz1iYRBSX-*C{oSQaxgjT1=_? zD7877HB?`TVbSb&bz=^hGjsXC@X7idUmAR&B~gYTqD(CRzyYaNQ#O?lVha1 z%*uM2#*S2{HDKK$auJ-EhPv=QTMv;D|Os1~^z^u>=Yk3QscBRi zwmFIFKOI?f$D3(28nd1sSI`lRY>;&A97f*1F?TgJ8Z*@(>+GYAJh5Zb#M>U<+F-wx zlcRh`V;$4f*E(UauctoPku^45?!&jNdb57yIg8$q<7#XQYi^SI&~{DDOkq9Z|M-mr z*VuHOJEAU6VNK${szK}kPk1&TDJe~1N0_Nys=E9pme4IZRd;;c_C5?RhxLcz(MNf* zp55Kz)84T9!Tt&9Qa@U~W%nsLbrbtC#PoR2k}Ws01tIY%7^G^caLWNT{Z=+Vu@A-y zBWUpA%ah$~JNdBS9&3TuURICa%HkYhf+^FTk$jz>=nZy{rSR^5GcQc|Yx+rF+?QQ- zfx5Z9d5jhC-jv}OFD#kzeiZMj*DWZcXu4Spi6Ht;4ry#@7X65?BB20;GzNS2&1!B> z*0||NAKg%76!p>mH>;aKZ`ZRnJ+5^PMO$}Of9}beIA-a1(rU2#E)vtXW^v%D+jppm zM|4(n32eVNFIxFNO{Dl-l5T>3bZ$W$la%Fd8ht_FeAWhgYt(i|eYF>hZ+z58Hrg6D zs*8HK7rWEcpo`kPH)}rI44z^BW?Z=+)k4Nq97+cgDg@ztEy5>)a2f<-S8E*SuKZNc z%G9;J*~mNo=}b+=aRFKplI4C7FGh~Bh=BlAPJ#eVrNabJ$e8Y$ygH?u1gb@xB;#PH5?U}53S=om*3MD;$SF2N56ZXB@FO|jBKa!$rs@Hi$?bnC3h>P{F5|K{@zgHjX zgM#2d!D;u`DCo@#gOrfZ5i&lTtTqY=o7@GzM=t}ehT$8m! zgnR%nw^e_;i^XG01o^_J--;yl&*Qw-25~!E2iCe%9WemvUf0oj=Oe$K;3dMwQ0d(~ z9TGRWwff-zmSO7ETD?2~OX_y5)!k!R)0Rz9yykSba;@kYVXf8S8LTPR>T@!%R{v5h z$Y8_nNN7cRcsq&%9c8R9C>}ScA}DyA{fK2=t)SpzZ&GU!48`E!GWDP>RtIIW7L6_b z!q5SzpNwwp*H4CU?J)uSI;Y-Gdbbw+q;+fRCt+lkSV|_COnRL^CkEE}v)XDP>g-~u zqDM8AL=nVpqfjy^*fLpMgQxvV1N~B>dTcyvqm~S0T}^i-iXf&V9MA^qS>3EX^z0jg znwY5e8-ySZODvf*h|LV?(W5yj_7BZ=@^RbNSl&7#VB%t$5mh`s!&Ee?8!C9b8IQ8i zbfnL!QA1fmr{)k+bHs})!ifnlE2gzkwAr|z<_<;EvXJ~yB!?sUIVBTJGlgLO{_R9P zO)tT2Zb}i`Fl;i?sm(-s3er<`{M|@fb^KJMuj}|}NdKazrz0)t=^02LY|0l-FJ-aD zw`OofYnecQLJ-%YNo^arn(%jexeYF;*Rxm?Q^%(K#JX|Z9&9^mw)?8Od>D&kS!yL% zL#fSYI3|qVJAQx*OEAq4Livlo+Y(4xBi`nIO+=d7e2Lm}O}EFYu{1udjTL5~p+X|O|g8mj30;c zwIaMUW`ul8{-~92}51)Vt|ivo75HKFks*~M2OUfxVxg&t>ds3SY_b- z#+&=Ce7p&x@#Ze8SWEmF6(%{ng4lGeS~U@;2)SyjNi5eiCsJKEi4BZjT^bntN${3u{wpgdvzttJ zt7_NDtTkJ!j-1TyM#;^S*=ZA7sk*0N<+4_NYYOYca!Wp&!qzbdeIg`Q?mx*cf9bNB z@m6AnJaXz_`AUXs1dQ9?j(gaJq1Y>B6b_D}ytQ~a4nU>C5z+W=1agJp$k_^q##|~h z=k&7C%kQBXX$=>4%ali%w@V=6aMb498$wqV@%@`(LPS~cCij4-lw&6x=Q2V*t1{c1 zA6I9l4R4+`xgy2WKK=$~pX5Z7b+*cAE>z|sWYarmQ7=DLGo{F z{t6h;mNp_`eq-?|22Vq?x_TN0#f4_|qiJlC!$L2yhNhIe2EjJ;!56%FEMA!>xz=J^ zOcq_)riLEf0-MPn+wiVlW{Ta++k8T^$z^y$n5CO-c#$Ae9&S!MHop1~M3gP0>^8FCBMp(YM~A8_rnAOLmbByZ})X>6iza^V4eAgBIJRa&ykT za=eY-h#d$Cz2$P7d?@Aj6P^>5xr>u=V{4E(r!6%P?x8Bc0QsH`EXQhd55}8{lwJ^W zeGuLW9)x!(51sgR7ui0=bK=JnKR6GBWVp?Z5prf*h$n3h0(F4;=S()mim%m7!kYrl z{YkbdcTd@;&c26DiwpS>frM~~<{cn~9A%3s`Su>RfSFp|Rx)N58)jldO4i-WW+g`7 zG6xR}V6}8SCsOS3Ug?oLFR-QwdvPI(4#Q}DjUz0Tt_or}`au2m1=g(bYEW%k(_vKKfmz&2)w+_kY`rkB zWZc6>9+ONML>&E0{csg)Xr(L!KZZ+9!0uEZTFDx9NHLg2wQ+5Xyx?B5Sa27r@2zCb z)OjcpHYyaw=TPy6VWX&mTl6%24JATP(-}l(Ud8PTj|a3Y?qMUZD^w_|XoQ-LMY(bG z7dPm*>QHe&m5wsTyk`(qw-80}d>OFFI|QQ2+m2{mdJj65ADW#(`Q8vV)@O@85d`ebpzHn^1AgOP&ch&ZA{~jsq7`za8TSq zTnqG5A#RV64>Wi(*M$yrc(0?QUS8iQGYo4L!os;HLX8Z$;_y*kP53DC#cLv?E`Je+ znEOPgQ(UxBp{{U*kq>moJ4e-rF0zIpabF+dE?}Rpe2Fz@rT9NCLbqVfKR5XN_K`Y#4V%m!RNr62#+te=RKwS@Zl=)DC8=xK$`Cx2JhqMWWRPFmV``VN|L~Z&Go4_hc7QDl(?6!NhFlhwf zHb6%}1HjcHCVdY$3ZPlp_?JFrtZlSyjE!D-pK6yw^GnJ$vKJXUS2FQEHp^svJOfYC zpMQGG9<1@)(f5}e{{Z!}d)5DZ$X2sYOXh86`K-m_op{m;xE+uPxVD2yUjeoOUR3LE zVJ%H@7fWv5!mgOD8z*CqbQMme>C7Gak=lD3J7Nl(QexT89%iPSC#g^Dz*?ZoBz4se z_EO{N6LHz0Ev6u@FLs?!qUA)@zLULTil3zZw3Bsd&}#yC`s2$pxV*y|epK7$YQrFeMQjiS6v_KeRdsTcnfLem=k@P*`a9>; zsr6RXt$S{D^%H{MFHi1+-zR36yz@hTng?aikN7Y?Rj&Mq{}Enz4`VWY8(v#(sp2d6 zJ2Io1XY=C9`>Nr~<`2o4`}shAe`V!<-X6Bl8%jfcErTZ{n>m8LsyAvKCREsv% zTT*OkebrfHw%ZNfYcQlgcB>Bmz~DR+US{xhgCTt=opwflqltGfaLQgZ{5Ycs1zhN( zGx(Aa8)hezympeaO3?eDD$wVkZ$SM~Y@#2)p9K93x&*2NMVc@Qj{_ykZAW-Q<&h)& z9Ov^Y|Lc36AJ+c09yq9UP)61;3@;p6Rs)fCQ@HRu6#4-R_9K5KJ;(U`UsUhL(cCqBhnrpDdW zkUq`Gr$*@TDmnTTPfqn(qDND3mJYsKu0e34)bV19ouW6PC_PetziH~tDV!Xa2(Oao znx^ic;LSStZrS!UPwx1a&bn+-sCx^9oSA~C<1ESqQr-{ccvA zkzE!pq=F#vP`{wN+la==ZK7XVpV@`ETKq{?oJ9c+=>h~0LC!ggZtjt*p|_9KR{b9L}srMHjQ z(ll8@#t&*^KaBZc#Yauj23;4*AAjO5+=@D!nj$bR}MiQ%D?MbftxorlCq_{-fwhf|Ayf)Vt_PqLQ8>>Bgcf zNlJQ{q}D}OS}LgxNzIF{VAR;Ibif6P8@w04nAg*kRI`P8mY@^BY3Rr~LG+wrnFp|& zp)+4e^8Fxs!E_xm32ljvIF_VDk@T?qJ%|C*7l~Mx!1Xc)3@u`>%3IE3$!sr_qt9b9 zy);Ml1iAJ+y7>1>NJvZCUDzlQX@<$JLT!BLJdaJ?6tXd0>mj=;&I)Z4ML$hMcG%NP zJxyj`K>DMMFqDKGV>ntHb~oY}Ep{-XM~m@BG~*~-i}HI)=x1Gps7JKvw^|I1sZXWz z7d)-|jnM>c^s*M^j9)MWH=W0-Tbe<&xN^fUd}kOol?}MWGdc_`plecs?$dBvO1kqg zEz+AkS|eYOt1j_Y>1{)5GgtjKv$5F=vg#6V$5+VHu%d-gj{O@RT{lcy#| zU!!%UYZ%tPPK>FaTTwxqY$o`ccEIAO0{9*t-W#%Ab*TIPx}-~~WREj$Pk^aQ3VM}X z{r9MF=2!MPb(zOWw{l2^9eMPACmA`{9*)HUJ^q5ocW4pu=?z%X)>S}_;v%&{aCNP; zo!A8)ul?cn^4lTLCV7?ZbApvN7tG1h1%41zRkk!}qQWznzsi;fmM&(Mt*znoPu^&h+o{GYa~Jc089nffR18$a<4)O7{AVWStV$XsYyafkJ3oeyAu9(5 zK(%oWg~1DP@v)PkzYymb8jOp#jaPhp>@=BkjdzUS|LdQ1v*zYJV!H|kH|%%h(rbKE z!lTrJ>Qd^f?(4T!cCO={nxA=|+Vs$>k8`>wUB63Ctiw_ya+h3D$Nw|wW;7%#h<+y$ zQ2Q~KBvm$N#m0xkE#1drX@kbP7f|~Jf^l^94YgCT!xq1db0XKSIy{ZSUq*P9?Tw8e ziM#ktiG^$T4u!9wSr0Gsd&9f?&$#QVSK1~(Q#yT(sxIWvWed!z0!Zn`qUl zUmP{;v$e3uS-tTfS`R}8J*h5fuUujiF8;XOYZJrx1eqKmoN1X@o2nzI>ylc4MT18F zcDR*4-Bu{?i4bX6oJ@@n?fbliJ9EyhduPqaSv@qRE@>-51FQTw zU5P({5b&#qI<@?++#ezOcn45?_!M(;F4bTZ^&t`6Vid;t*{q`9`7B3HQNIKfoz04< zOPYd2^bA?FFKaJ;A`-ijhDu%Lod}?xjKt1`eCmj*qirF0l}_y&hw;o_WDl=BL6b<4 zT_6t?-)UQkR>#d7PIx-n(xHyT#BQU9W+UCzJ%`D%NHHj?t(KB!%bz2$kN8*&u4m>a z>icc=y+eJ!uD<)CEOnV()VEW8->$yH)i<5~Qqn1S=iZ#uXT(_@AlA0>L+^-D9KPX+p@ztLzx#9Tg_mympuYO${Sf8NAov zbc0I_ZZUYx;4y=*n)F^bvdWa!k6C3p<6MKc8#D&&fRW!d_`Sgw4BqvEj=#aky^Z|3 zk=q$Ll-_OS+TNki8&c!nB}@sk3<`rMOoVPm-eKf51{WAiGvO&l{@7qBhlh+j&R`B9 zX03)#E9RqlJD7-l)#9$`gM~uLXKnIyta#Pydx!5B!&w;cHQs?g^t0?l<~?#$u!Q8T-IdtzTvIu8+A7 zKMZ0lU?QwE0e%FK-cz~DA>zW}LEDokl5Z@XtGkXbO?Rn0gOlcIeT|V%BuJ4YMsN2g z3I2AnD9MtZB;k?%mck{2Zc$CQWBSOu-C|p1iA(t7wwLr5ymI$FqMCdC^L29BL7h$M zd5x|M8ht-&3>evIaI=Z$HsL1{bUObVrZmk*$Mdp)Ng$LFGX<$J`W*`z7ZB?<)m$Y8 zL-8D3>+_60Xxb@MF~_gEBF^75dK98Z_>6)XbT`Sc;!hnR`GD`{I1dQhsIFEQX>a{XO-RW6AL4wOT9YFnvU}CBSqU= z0zNH!I_b9X87wteVX)A|3v|?WP8mN+oE+<1qN6){nI<+^VY-^fgqOH#6Fgbt^!#pY_Kc~srj1v_g9Vz{dmhzxp~~khZ=7DBN%+*UGYjs^ z%N<)Vehej8d7Ve>p#w*lJUmW3drR@-`kqB$q%DE$Bmo~<7q+vlPsOT_#b8f2S#|>A z;hFj5u17?|12Ku0;J??aPD+32a>177>m);ASdg$p&~bzvZxV*p4SN&jml1ZkAxyT) z74Jo|xq{(NwJtqX`o@d!*c8a2PnL`tFRqU#6YHm14uBe7lf@H6tFV%JLe7{jTG}gc zCzR!J%=?v|Mouox~SqgHJy5I+lsnB%OO zDc+;9icPTul}bKEB*&L5Y;5h4@smWWX2rV%D_MYCQMig+ks`-T5VkhiufrI}#9==A zmnhby+l7 zWJcCNLBg0vMXC}o;Hj-CgRrbcLf$<=BsKG+xP_>RTL;pG2n(R-uT2z5VGdZmM$soB zVa$l3=*cN`;+^@}5!;lJ+IP}qk^} zF~T=rZk-@ndb2H!Of5@yEc#%{UPtHxQEOX2laO*qW3}tu#Lkbj(XiI2imz={p=Ohs zl>JeW5>M@6{rKg&$s&0mHKg@(@P8=@)!X_hX~<%IdRQBEiIQtA#jQntmiEhqUYEV4>LGjOaDX>j(u$(P7&?Hpr*^`dQ{k? zZK`OWS9cly^>UL4RQ4p#?K>mmX zgnfx@F->$xu3p?Yp<>8t+?ukEkkh7#_DOk7%(5Y?fttK7J6zQ&tYWe0;C9SElq~KR zs>)M^O)Sx2=`MuXm!PbmrXM@vRO<=FJzY2=DNo#qbYps_qj@V33-_8(L&jHIQVD=} z$@iv+wo8!iJmIhfmHb7XsBcUB{B2ntX3JvvVjd>n zphRFRo&${2*V9>sb>nSeJtFg#{VYrc*5f ziKr1Uq|3<8#^V=eE=)#RSIFG*!I|RcTQ1)ZRX*;h%!} zKL=%zL0cd!Tex%N{@J3{oq0(4GtdsKO(+wq%p(Mg=qB>gZGzc)L@>`<>;(h`l`VQ% zBc1FrN2I0Zm7wuK&Q&J8kSxc}Q5|>T9Fg4Fk4M5NJV%aHR+Q3U)>wys>J8b*J&1zp z9-1R=wNZ6<$kam7J#)BOHc@w{)2Y3&ot&;9FSO zy-i1Q>4vD6<>I*_d8`{jD?kM*82Jp3WQ}#Kk<6pRS#%FO<(!vJd~PJO4aaEm()fotre2ok=Zk?|N_t@@2j~-( z1nON7i9dC=>M-o;WjStvc(k8?7-ye?LbYflt8Bv=o>!P3bi@qZdRay-6rGyU^(&w; zg}>73u~4{D-7`2_07_h^t5Pq^)eA*;ZwXGgxo0+K9tx%`E=DQ&e&`BJ6biS>);Ju8 zN?=}P>G%rA4C5>0>>1FzCazWH_HbtJ7VZgii|lkKy2{09*?*CU@&*=Q=gLA{9uvtb ztKK<0ih8FfJvD-*9=EeZv?a|Ne#GI6ICDNpb)&vzZ=yqQvD$<#ofq+ZJk+Cxv7l8! zMi>Gf7)%ZsnTRA8f=X0|I3KWy+z>~mdC|{G7vp~lK?jX}gDmgziVj}3l(Q^Q>-DAr z7^;nQCf}v7Sq|oMFZWPBdgafd%V^^{s!nkXoUJOIq=)Lu0yOE)4fCyHBh=)L- zX~rt6tCFj(ikZVBS;cbnc2KP;WFwih=9?N+0L9x>|`a*_t%D z4QP=z5vN3iSeuEWJxaYCkxM+4fgC%id%v zc|1zV-$Lgi9STh0R=GZo{Avl7emRmC;XaU8$yvm1&Pq+JdRcZY5(%&xTqLeb3t(4d zUq3ch8mk6bE-J#KWpEF+z9N%#E5vZCn%ZOkZ zcp20pHv!$aNf`FG+lRMc7hya9AjT8flWt$4k{^dIe6ubvgyRuvFBi#a z9wFE!P{0H?$a3^@(JtvM)~6hiI${a+Ht6#tpxn4zbo7RT!eZdT0i8q;eay&l$Hzsm zIBepI!vKlv)+{e%e`73GcA#s zpgfQlv<~z)&>Ns?(08C;K=!SIwFUJ84FydA%>u0eNzf~x{h$Eo5-8z0Jh*@cfgX8I zB(vFI)`E6`{GjhZm!7Nqs90;IJMZzX2 zlBIW(h)oEoT@%Mn8#lI~V7$vEZ`vf<{)>H^f6*j2&vx}6kX;x`q9NtyHa5*r`ZtR9 z4Jn-2C^F;^|Grq2n?z>i5Gmdb_tHbU$HCa+AX=E$KTm_F=jyLPXTcMG17dhgslh}2 zZy;LiSKuzt0?u}d>je5iDbN!>i8sPd@TI_lmYVkgdtqNwC+J&2s_}>pY++lFF=#P zlRMxQCu4<=dr<>0Q*aBVmdFEs2BHUl!ed_42s~kUF&r=89l+o5&XVle<2Z2}FE&*x zS{wo75O^PS8TN!nK{0r&4+6)Pn7n`oz4#)p0C@*Cu4614JmIULLEtNZ_^Wg_9DMz= zKX~#J)NDYrf+su+DgjRzwGl^ugLeS;Zo=yh@E*xn!e-sNPT)fvCm-;5((-#t(dPo8~Kk+g?EJVb8Y1Ij{ro zWAG^M1k$E4it7NfmvH@6Gh#E_0ub>&;8nx3ml^B&3OWlSyMTdRx)6k;-q5X5??QkL zxfZ&D+~5iC0Of+W`Vh#!Kz}>ygZ`#UFE*#V&)5bCgv^g?51z0)h@L(O*MatdCmgXC z9TU6<`22@xdhiv%jUVX>lmefu0*_X#sAlXWh&pKy==_8+JA4I%S3iY~7e=L@G1d}( zht?DT4EZ1kKm8H4ho0~l$OWG8BFGKC-r)eA91%xAQ^6A+11$w#aT0&la7Gu-1w3+2 z^Fg3@@Xx3{jPUxJ%?D9Q@urwP3?klj0f)|mXei=EuX16~WwN?5$i*@BBA!f;4x#uJ zoesS7H^xST)G!8izN9n8i(q!oWu^Bryai_8f+*1d@Wd4zq23RILXdyt70^B;N_YZv z5IkNCvu8k*0bUBTp;vV}c!A93gQx;{Yt3%Frfu-%npGPfugzIAj;CjejCbbjJ&>vy zPJu@^<=etB?YM)C5uww@uHyb$1 z@IK(zh7SO@MKdYu!`WO=%__|QJ}@`j zsx!_8erfms@N!==!dv7CtIhximN4f~oq-3K0=GVS9h|^l?!_Y?5_Q7Y z_BKd$SYXKaMtJ@{&L+WzFztRk$CEuUY=pLV0K*^Byce4W*ux;IfoCL|7o_-6oSgF_~ z5r$J9@QUHBjzMxXlH)K7{yD1yksOO}f!xp&ehkV5PxuX}0KC;dNDjq0prz0gZe50E z*#coJgqJ~Yg10&g$%)u&Ij$4*gfD^)k_|BAFeLP>z@>qna3RPJH=7T56+|7p!B;4h z6VdZHhBa(FUNBo$;hI6PI`*uNKyncFS%aFvhVW&O4?N)^&}Q(2IcquF0iN)F(7WIp z{Cc=d(1m<{gma4Rm@~loia{l!K<-(q@FQ^ba;SNwS zc*3_qo55Qhb#U}~)kz8!2ne01Pz^qGvI6m;6BdYnK+;JI=xE>x=|qMA_|VA=q^A=a z3P8jYZUJ2dZ}I*qui!&Zp~9Om{}VF<o6p$4~@Mcz_6egL@H|!w=mav;_2o41|+X`6vnP-yjY$v-&2G0l??FSL$6i@>+hNUPqb=KNcx=*T z*9?!1ylgax?CbrN;K`S{=sV06;0dv#m=$;-5Yzs9TvzbeHq4#`QO4LR%)}3x$2Mb@ zZ+L7`X2%SVZOrW6fVS}f4;dbNYuSTGjUIT&@YsyY9z16BK<^9|GKX!Ha7EVIzd3UvLS)mjJ7-V73BZ1Dsi>SHnJF9f%ek^)6o6kn8sd=uPm1 zg&gs~`+#Ev3Q4sGhJ3(;XF;{FIqL;;4#b{D4S)mjGLlN|2JVFSop?X+D98buvq1dt zMJ1X79AbFFHj&s!0UIZ99jG<K8$Dr6lt~D9ABbu>0+=3+!Xx7h;CrAf@P6QN5ZMsMHPgJK8Ls~rBY1#wKvbYY zV71{1e~A%nBoev^oE?ix1ilbBtvNQKfzJc(15rTDo zX9JIb?9X8SC#H@R$T%ZGu>K(8bAV5RD4`9&?+u@mDA;%q*-Qdrle%*K2Z0wrDe#HY zwh7u^;YWc8$){ffo{*gQIVc3-wYK;whCrTr^1>5O=mrIz7T9~Y`0WZ&5-y*7Q%sHN zO-_?MSOH}8TcY(NS=(5dz06UTQsyknD$6eGUFIswDRY;NDD#x|mKgzWZkF>uZKM&XM8h;wsonPOJ8-?q)jYq}P^}ELFc>ba3lcrG}lJ8%NG$>P& zCchLSty{lyv`M=pHA#w)qa`U+mZZ-GHqfFrVGp;Pxk8ry4n&bPvb2aOmt?7e;aVa~ z8*2+z@jrm$OVrO;BbKTjVx8D(HB4^K)~oI0fox-8jy#Wr*$SeZ!Lp<;Rl@?CF}Io; zI9RTqtvUiz?%QvXq^*)8K(Z+ZTv1n;v^5lhZOUGkgp5sDkeq6B>`of9YoGb2A1^!| z7{~^9v`doGFFD;AQG;QhJ|3FqMaB|FqDNNdB zQ=oG9K#qJo_4OBOWHH+>%#!J{P^_<|U7v2P1 z_lNgmL)4eUyG1t}?Zu_5N5Z+RzzCAnETSn>)jkp3EK^2#an+rzz8=99+Z{o|&PH@% zf2xfl^Vu+UePjbxq?ScKCqK7Y?Gg2Wy#xlbD&Ok?d%3M)Mt#qB)u!&Ju*~+~SS>}_ zyTRy`DOE-!*eejLn&qoG`-XIGUYON0ohcOu>ti>~?45=Y}pv$Egf*UEN4=?I?-DWnoE{C8cm9lF9+)sO``>_d%PZ z_#pC9R;vu9oPr{iI>EYY>Yp)*YFzDmBFIuU<+82djooVh+I17&QzWVV_vJ6`0Uog} zZMo@|BEezH9zm9Ix)6+ z_u~a-Y) zUafE?S_fVi`wlA?NN0rEYr$Tj7c%?6fpe|em%fbZ2X}>PTm(}M%*@J^aJ$gWVvYX-{qvf2E zf+gv0grO9%M=2}S12Z}olcCXF%h6r?PlHA%$~&w*a|V*U!)DGz>LmD_nOW3Kq+H5X zw|k1)L20GRvKfWvTQ)1J;^4df3v31bZEp1vXqAnO0@lwM8bd7}TwT(#xytG&BP~dJ zmSsu?+dvXC@{3Q}aZR^uE_d&_|0cX=$@^>!x^2PzvyZ#ow&lvAyb?Io7I46Q%BTSY zn^D81Dz`hI8zr-#XP}`a$Oa=R@f~L&3!3DBzx9Er!LnHuv1;c6GSG6ecWm zV;_;k!!0z5SIymwfB`Gj4h6Z7UF-g#;34yI=6i&m#NyuL@o_S1QSIfhY8J~@yO*1J zA&;9Wr=J+fn@wrqX4A`}0zY1k{nrd;BT~6q%Y+VD#esN> z+a9}Qzm4Zz5AkRvZ?s!PD zS9_o4AjA8mhS&G~Y4%8c-3>lG5(D){xQ_#t$M3hf)@+Gj|9~Jz+^qLo-b3re+J_ExuDx>8yw^Wp;yqy-;w;F!< z1-DzQ(`eb^E>V6&hr}v=uLw0)GYH05p2Jrj5_U@T;z3?2`H>jtD{+q@(e4p>1P!hh ziM5|TokZ3gX}*FS!vUlk0*qXep_HHt@leAO^$?Bbxk8@a6jI7P&M+2ydK9qaBKV*C z=+*yd0^=4kMEcAiR0pg?6&R5(c(Lkmg|_#z8Y|sEaUN=vVZ4512+0R9E=tcIh`l3^ zBmG$IP^H-c$c-OBNxC`k__6muD$d6MpNOZiq28L{3ilKbESb{* zu5YZ;&4}x>bEG8QTky#4B8pR!jpEG(hB(B+RgV=Iu9>*M(E|iZsK%G5m;3Vuk;jc< zCj~}m#(BQunA>TVz%?UAuGtzkgII_tFI3~@|LMnDFEpdN_1^?WXvQ&z1cvyF>=j*=dQM=dDly^c(>dmLntgGWY(_=7X5B<_VKXkU6)?1*BGrsx5;;z? zAH_&cG*Yq#t=J%uwlzp=3=(NwgS5~fk@_HUwUiRfU;2}v7|dAfQKcv9H8Fi}MPL3E zCgx3et`Fu?$Dn8oF>~gr0WI&5iwprV2>%dWAKRPzQ22*q`Us5h565&C7~vj{X(lkjJseXLhPGgnUvDP!Kg^R+|3A*V`XD^`L|m2ZrQCB^49Pq=vsliro3 zJ|Z!kD5QMl1|KxA)PTy@YhtMp`U3Z@it*r>#0j?xJLefH#@D3eG^c_M3OlApvAe_Z z0i|NSrQ@N@} zO579?=cff!7c z^lxcVDU>w>0@Em#Gj|k0{`QE-_alP*en$z!;E~XiOYjPibRv5O^BJdajS&CRjhI)C zsh_p3ZRe3;-e6GXjv5hz(5N8Ns(&i(6MRJQUgjN_De#f#Wu@McW-MpzxYgCW;z4Ci z`|=J~R1?el+uJpoRJfo|SQHGsqmi7PV~E!$s86Z5x8f=+{`>Ix1L-xsjN%M;}5PJvG9P%yK7r{=`uk zhzs;1?l^6p{Qs3lPJp~k*ypIJ-mU%0x4E~dXNLqT1bI{Z$RqFk|5cC5KwP{Zakut3 z`~RUI)T2U>w}xG>nZ(CSWQ_h#Gl0I;>bbx6DM0b#DIhvK=YRP& z{1hMl_(%KUr#%TC0R9TJ@>78L$;1Bm|4aAZ0P>(e{%_Oyo`~0&N}Qe{HTG9;1Jb-} z7}1|BJL-mN)#fpHt39BP1q-k=VR^h^=k7=~-BDd!Z|jGfPmTVVWq-`%2Y<{zJXo!V zU5s6Z6Z~iQ=A-8S>owqhETG2<%cz~d_}OdCYDU7Kj*U|p!pAMWG{W9SP~GIpAp{jlV^nY zEWstc4ay+qXgsW57wFG+!$U0^FT@V9C3iXy*j}T}B#4H+T_wlFo+L+!JxNM}gy)>= z1d$NNZlvDe772DRiR}{OftXR=sK@mP=-A{FTQkOvq<$(59m3S={>C)^)!g-Kfrff* zLYZ`>49{tWdcnlu*(Ei$P4zs8HkmzJug678IFFy5*KO1Yvx(ijbz7TstF(gdvZd&f zEC0?iryC+Olrn7NS}`kPE0+|2M{S~GdjTO@VHp3d-v!q5G5rjYphFjkk!(B#gJ{{k(!AA*XbZr!Z> zX^>l`gF^c2K;B~G3qN=qrjNXY^sVZB&DC4F66&=LPN&ME)#uY1TZDStI=syvr?rv; zRzkbxuXT%-7ksbAbW7sf=DMwuZ%8jp0|Kg$1S6zhgZ@UG84vG?C_-j#HO+MQ#g3j( z%!--f;7Z8!(DD=rUX8Si4%6X?r-Mclfgn7HFnO1(;no@%m7 zf}IAh+p!akv*Q*)Kh%MXr(x}O{6sUwza!XbB)c6u(M)h*qplg-ctN{zD_c+NN+PJq8nsej(b+ek|wA1&-2%jfg? z#^m3@`HgKIl8!?OhT*w`4$(u|QeQVIq6|$XLZjQ1AqKM^`v_5p9CF7(9*@CcO+^`U z${ovid+lO-G5~*w=Us;1qWQGQ+jqsH5c$r#5VDF-8DcP7l*h~5-hT%V&=V0Ii%}d`A!7iFS=uu0cz{k~{Pn?jNjT$`0C+~69?Ea1K?cFr4n%71lM&TOgBxpd5C_Us7 zapl`9ucz{!Pwp1g`xNI}B>c|DyS#S~{YsQ4xB4m1M{J^b7xxZ<7esM#uD{}*TU77z z-jVR2C{K>@SDxktqInnh4hTsUCtv$5PFo~I^e)c>!-$L0wq(8A_vY|@W^c|A2L)hV zYfc3{Q8Yp$g7XWr#0?^T|%)#VKb^J7E$L@wa= z9^SBrdKt}>)O!xOlpjTOq8Az6E~9B#Mns8g7D53%s&p;!hMLn?fY6URi?@E1w|gl_ z`Dz8dyVkChTX#CV)96=JM2;8px+rA#Dn&GXl)qBsZSPWE zg@~jl`zu5)zD*$_=@(m}QO5iSOOeHV7D%_Wid?R5rBXioKqy4cyiHX^(iftT|4|g{ zPdXL1zYHrAENS_&NDm}0<0S#*$A~?&s-?klB|^ZcR}7h?uio09Hsrb6XhS5us->sH zWBo-{8vWZ79EUa#R?q*RXY>@RfsdS2gU>LO?@h>3Q3>~)SqEiml%?^H>Jq0I%zzy! z3Ud#dg(*{`u%|51%(981EIG@gG-dpkwTox|X5oq!+%(*uX2q28U)cA?WgRbj`%Zje zcg;?W!%kTLQ1kh8h#0uF?+z3iai5wRQRa3U5zTB(ZBdqbBcA?f%Q5p|@lQ%wo9@;h5o9tHWe15Y=|owp$dXQ!y;o#OC(1SzS<;EJb$Ir6I!!Zl z0;gR+SN)uoN*)(kVW=2=pU4X5#pqv&tgusz{)xy6JH_a$MON4;MqeVb!cH-I4$s!m z$?l1uA4*L_#pokMNnxlM-6pa^rzE|L$O@g3^j0D(bV}0ei>%NoNskm+p;MB6Lp-Mx zIwk3U+IhZ)QXZYUdvr?DcUM2*bW|kin?zRVl&-%gvO=eHT@zWMQ@WlnvO=eH{TY!Z zo$QW^bp0`r7fPk;V|eyZUQVF?Wkt2iIlP zYTVQpG{qKi_F6K_O?@UOkP0{T$vjO8+|)-R?SJkRQ|H?|+-^t3O`0+>o{Bq*=ixUD zrFtTlLmJ)GV|bbrx}gX1G$~}ip;t8_?MR~=`gxuvg>L8-JWUGS(2wvmDRe{MgY=yg zs>Kz$;iJ$6n28ix#O07eH}q^u+qsFZ=~)=!$-Zr%9nJ`Z1m+g|6uPc$yTt zqL=VADRf2uf~QHLEBbn*@1)S>uSlUQq>$a?v+rOwGSLh!h!nb_kLPJp=%PN9r%9oU zx{aqvp^JJqo+gDZ>Zv?U3SHEbd72cus3#!pUm;`tHGyk%(Z@vh8w&k`hXuky7xjZY zO$wdVck?tUbW-2K)1=Tz{Ue?xg-+^gc$ySCsW0VeQs|_fk2F^Z{m*B=#XK;7Xd9X6 zq>n-$!;fU4eq0b)=%k*`)1=T2{XU*1g?8vIc$yU2p(pY*DYQe6;b~H6haQNuJ$o%_ zv_r3IbUPD?Zf4~Q?eI~kfuYbglob}*p>O1AQpm2a<7raJt}o|lQpm0^;%QRIZr8JU zhBUJ4Sv*Y&+4X5i`}dhp=(A!hPPbkP@zH1z{75EB;eyCScD)g$`7H_4Yx6W|#PkqK z+xaOwoIGquOr#RiFY+{L#Pr{Inlxhik4WE1qa6ELMyl&Qi$n`d74Zl z>vjjvkVdjTkEh8-vi>+vlR~oo2v3tjvOa{TNg-M9h4h^iTC$ZCl6@4KVJH;AbrQF6bjUfkiL^b5?6@d-WdJf zzSs2&%tj`9jte3a1?o>Boe|2%Mtzb9vdH7OTO$w#!D|ng|O4k?jG(RGjt~+>!G)mX!A$=!>N{Yxr={^drgd531XFLp zIGR7A9r=QKsB_zpK?pb3_g|iAnVbN%lN zk52k?cxLF`NcaQ$Dh~eOn!Tx7$97Z+eS94RL~1OGKwFFhF&l1&C% z@u_|rA*orP zs0rg6);t373+E;2PtGIh)Bm^&osNz{3|mg6&U{R#AcigHhakQMP3gbV=M2PV;A$Gm z6}{18BKrCxeGG-N$-zx|O~IU%X{ zkY%6N*4=e}NtW1qvK|5o?PVyKYIW^vz^klt3c z&!hf)+e+>{%j4HQ#QFJp_2);EnlFg*(}v?8R=1&w#-w9v<0)XBpLdyKyvv*dP3ixOcb6fH>wiV~?Yh6xw8Ns+@21|D>U@vOseqg;i_-s$ zw~SjY?l{2@GdZ53K$w0l-0a0JQt9fuT$GEebP_bB|9Nk5`d;8_2DGc`dXK4f8P7@l zR4Ps~efuPNSxuiFMA0|hzmvN?X*yXwFg-}#yiPqjJ;|OO=BMJ5Xt^=MwlAX(i@*aZ zWf=v5p;qN!`-A-ZCGCyZg&e12N>2wlReC5dHf*Qhxha*;Rj_Q8Ap`RhOm27|9^hihykCM05S7tPi^VO`$I-!}mcSc&s#X%Ci=A}kG(U2ci z*ZbksZy3m{r>%X5^K(qigOquoiD#+G0i%;C<@mzWl|y)T({#Uh1cPqqI2$8Qrk0)! z6eiGr3nE|gzSvK3Y>AT851(ig(!kK?`)(gU#QE2f`>kl$}c95q0{L^&8R? z1p4MtjfjpgoN11{(bJ8QH&S-4R&PG(HRv`@i&MKkRa?%VX4)_~iEJpVxlgsI;oOwB z@8H}}zWGn%7<3W=tiKv?Tjz=?^W&*}SZzTywoo6Q>95C$DYMa3Q@@Z@;(p^T_0O5j z#k=>OW1%}&dldBQZ*W9G9oGKa_Ir4T6NkCzV_c_##2Yh0TT9VS@mL4P>x}n7!{pm{ zJPHi!SQ-M)Fh8BIbG0hB>3nh395j|=E=9W{%9Vqz>u>S!QZGH-y^Ds|$IYJWY3Hg{ zo_6XEJb$U-eshJ(ZH$rZ)mit+B`ehOS^m0@FmKUh)4b}y*+HzGI&5~+X4HY8eB_r> zI-;?_>NYX5XB^-Thzt1&zA-=zu zvwJ-ZWqri@@R)JCERCcU$&%y(#NRDRodJ^o*?3V3%ev8LBb7a-cpht0_+1^1tj8eX)nNJz+%8IKs25mj{&?5*a^4* z2*(5S4uC+MEEov*0&o%_<0M9FKp(&yz{`L)0OtVpaQvhdV1XU~_5{0 zUjcst>f$_8N5BL?C)j2lu1>%Tz{h|cfJ1ZuB#-?25bi00Mt&Cq?UjO)8PM+ zNIVUA7qAO(S2vg&uo2(}47gvC-U5U^AW8iJ&jUUN{0fMAP?GKkJOS7OI1UI+hr)n~ zfW?4q03FcIDoK3-^8xPyz6ZGN_;*itL@MBIKqAG+;L1eZW@$ONJy3 z2W$jf0?g?tNvnDx766qtm>WFz_m-r!0PRspN}39nOp~N90l!a|q$=5JuuLFJn z+&2`x1n>di1faHk7&HVt5BMAqJ{-OQOa-h3oCMS#AxR?uD!>Iu9*Hgl=m!`Eco;Ah zFcYv8@D|_$z!pF`;3(jCz~6v|qcA7|x&itE?4$8-I$%B^5AYeF1aJUw0&oFv9S|`Z zqZS|oFa$6W@H8MBuo$ok@G;;=z#o8r0D)uBLO>HhJ3u<%F~Bo`e1J9v{(ldNO@Q5i z!+;xr$g!vl&;rmAkO6oIFdpCpXn?hVPXWb%a=>B0NkIK^7|H?d0S^ND0!9EP0iFVE z02Bds0S=6V|BoYa7VtlS8<6%e#(lsLz$1Xi0rLP3z+%8&z>k3602cw*03nZ{rGQ3& zF@ULn*#J900ce1=fKLIHfGdE2@#xBS{HqUW4rmW}5bzuz51;~812zDP0J{JO0Fe_U zDFKiSNCk8Q*Z@NT;{huH>j7T?N&x!+#{g#lmjL%n#Ow*k0N4lN-&nvjKo%eyunn*W za0E~RI1i`-1Wv*v1Ly}B378C+30MHg2P_4w0sI1R0saO^lc6vm0gwzx18qyw3Zf!a_I#aCTBaZ)sjn`mb2lx^kZ~<%l68v=l}RE5LW)Z7 zc7b+qxjK45y@p#jOQ0_cEvUh=X1V&wf_ifPa`oE<_1H}HX;ee|$smp78M>E7M&cRT zQAQ@2XJ|(mnMlfn(Ooz)e{1SzCu)b$of~q$h}=n$+pVdWS2ld@g}CUBewVVaX> z-Y`zt>o87y;T8MxfPwcf!8d$e<#ybGE-aZ7;*Pye2wTC(<~GM~6*gy&Dw~4OZR4}K z^bzkzcDZAp14BQhy#4n!Wsglc=K9zH11~&2=SO#J$rAO1BPor(7VeJi48Hc?TepaB zhjZ4cf;Fo(-D_06LT~I=q51S@Q_9O7*XyZWa_8GO#-Wn{*SjD`s*kVbI&;Mt+ZWY-+ESzb5G&xAWpnBz&OU*+1Ln%-lmCAWydyyqFGDdFH`%vWYYE7%-vKTxX`s<|rA2YZXxQ z)rtA{gputmn_t7%#Fa`X$eV=q;o~YVU$E2Ey$^ehC7|oMrJK%N#NK8+<3L2(pRpGS07hau^wvp zqGci57BfUn8}4Sx8>sQ9h-#w!)e&zDmpXZYadL-B!RWY*$+1w@cHTV0a7)hGd?cdTyS%~(l zxfJb+waS)OItzK7=Tr9B99KhUBmWc6JFYgLi_AX3yvH5;SZ?7ji>I>iw7C@H{R)C4 zwg0P&mu9jmBD)^Fmjip7@2cTGLDI0k% zXamnVN(Aj=K_iDi#;1Z-A!wiFtNoWQkPq)v_bz=Rrspik&)G#Pqnffj)%JQRo1=y= zJKnN+9@)^boQl<=8k+%Osb!0S$P}JTPXabIuP|C`5!7b?HewgEO$dguo5)+YB%`!V zIZOmGX$(mQ?c6r)avxT^Z40D-<$U*WK9ean%M>QY=OIJEI8zh~9*OiMBRv-BheSGP zJkpu!rIj7qo+$QJCAy|6#$e8Mxmv8T{;WZe{Os~WpAc)`%vVp zFZoFgP}11h6XQDE9?YH(@@jrPfPp zkVd&Jg-@+%!cusV;2f-5UU+WJ>@e()+2H3&c*lXcnu3xPu_;Gg6Vz>QC&;d^)T3{Y zWZMhdzB7uopc*ViJ?G=K+|Mbwx#x5F4ysI1uVWN-{O8VUfnC&cb>rIRQS=3>twGQt zBVXC1o>|*G`V>}vR#*-z+s+%x)w=8YurBK4b#3uu3`^Iw4ov|Wn_FL~->iF%Emt$& zeK>S8WOLpP?}^EYf(zxa4*=svK(=&RL1pTq~pUUj?m z_39I!Jdr^2QU><5gr_!Kz#xI_ij9STeUi!KA0AP=Y-|ug&U1Vn$jv@sW8Gl#zUyN( zdt;NvU7@-1XNF?bnV}rWIqh!58*15l&f@B-?%H@?7=;9O(bdmi`>c`uAJpEZa2+Ns zORkP|+MikBq8(~DCfVYAP-aB~BP{Q_@iUoK$p#t`ci=Q`{69xAtf{y~3l!=HuBnB< zzsd1DI&q|bHwx-`K#A|4$yp1si&sP?!fXY-0{ayxRNRtL-dp)bsUoAf>gZ~t&mWEX z2d^v@jEFSrZ>TQ){NBVBkHE%-{ZN&Gca+EAC6EOuc2GV2d6ORFh@se4&Cv8F;%6E* zCZY9`pU78Nes22XoFEL@l?@&Lh0cyG=z~@SEltPPzGZVROH_Lms&?$i*@|!nMzBSa zE+ZhNV%+=y{{0Gf>)R!%F@Syxg?>gs{8$BkhhPV-pe>u74*l7dQW4mQb@}r zquKOgFVx5`B(XW!#m8_#SVNXgfz;6XWlgmghtco@wc^ zr}w09K&PIkEaiT5sucXxN7Kcs6V)w;YRReHQY{N%M;wo%kKo<3Zb2`VvWqDr&9T`g z@mk6qG04*>iYRToxpt^y8yD+V(>B*rFK?T3Cxha1m( z58a!d#q%y=g)i^kOhKb?&lD6&YR@lQ$S*urIP=SiY&dm$_W_6FJZj*bANdSr1!cRf zncXwDWKO(IQE6((Ea-tq=0aV6BRFjcqDWYTB-jGVDnqaym0Z?kR2+c`g%E1qHcYk*VG+ZXHhHjlw}){!u$@ zTbROS^0}m+@{_9$xO)r18OpBSLOK4cLVDe=)Ze#d*1D?$4T-sO)_3shq8Bs1Q`5fc zEdS(E=X}-JPBUnr=m?AX`118Tq)Hx5u=#m!A+jo$(EGLrT#58xU~?XI{QSK4LC~`h zM%g>IBPl)K9X&fwSa&gXD2xRgrf^Bm!vv8x<&yBcpX=fTAg*(a>vswC@xwe4ptppq zGM=2NH=yvmMcg=ubNp_}-a%rdtmNz+$0;%AE+y|E5?%62@ZYi!0}%B8^hab4T|l$% zpO2J&>?SF%{fYv%g2gnh;~}G~&)2XlZgPGCI-*qn0!;0X+j7dss~>K!FOS%#mT!Nk z2{BtXuO=s;QLk^pP_rwmPP%3D*N%z@Q9)-(P1}(;#+Pk`U_0u?MmG-nk2j!4BT3$i zjWj?Pz)--mfMtLSfb@OX_5d6JM8Rd@d)3c(tgB@PK^xkcD2TnXT%B6dhJB$fD;XR{ zbwjr{h3ch}{c%RF=nH6CV#%eCy=51Fp<cWTyc!X}EQ`mH#G<<2yW4xs<6~&fq`& z)8zw-_$)jsKIGbe1f?dkWHC8yp?Z<*!uQOh+CztNyaf@(b63Hc5A z3#i1%UpbA*rmW1d2|*NkcMg2pEoV-1^f)p{K*_?gtZ=L25BwI&pV^hQtvP?t&}Pjk zqZ^sZfDGlIj4p*VZeRdTg^j}Mm*80uEzZ<^A6sAB)Ks1MalHM@2YJYFkturuzKOW3 z-$3~?$4aySZ#WgYP(Z&5lrfjmqh~Q2!|3m?1o1l%|8ho zwAp8Sv)IP*^mL8F#+(OD06P0S-`2L)BjlY9-c|U|1KYi#X^s7)dx4n+4X-c zBzZZE51B4x&@IL6CZ|NtgW!OX z$aNkan)ugYf=|h{D5>j+YfrICWc;Y!3dQi^?=(9`QB+L7j%6ttT*u2wM_p52C8Zqc z#Nu>}$3;mG?cxX}UdK|%1yQOd>N(!^X8EtTaIAMj7iQT#HtXPBx|9FXb5R;YZ&`A7 zzT*gLPS5>!eiYQ_KlP1qF*C{Ku<0rply*j-H_6VZPZ;-*}|tBcB3 zq!hJ;@=fyk%lP3Y>}5lm8iR{vbDw%J7=mafa;B;JB*0EF zuoHd4K^V#!;Br0|idadd!cNCDTzDWQAYr#S2BJp%Y8#>T9$YD7VCv{!l{uq%iz-Bm zt}KM%Zs5kt6Y*|j{IuW!6jM~v73LU(i)-79c+>$~PNABMy+wTGUj#k!LSduk=$AaU ze8IbLw*SIAP*^Eb_LsT>9j8JZ6(OakVuF|*J5T|or4_+W$0AargSVtk{w2*NNuH58 zAMMg_hVdY#C0F~h+>&R;`Z2(RC`KKo^^O=fxEp0)-K*I5<91Hmy2ZU*YC zKgt~33zfCeU_Jws4yC!1kPMxKYEsG^r|frUBXX6?5%>i-=-{W?Z${FzhEq+J>|%3? zS8Oiv9Cp6Ob5L)yEbJ;;!Pj`M^!zUpC~Ea&fjx#A)5bU^NbK@6gKz|S9C9kO!X?$%UG>ob@96;_e~VXgx(E}!XE1+M=}ylH4y*#eSuT$ zjHGdECs}%`13s-A)K7sW)UZz;pim|w=>e!5ef{2#u-G7?iLVUi;cJ)-92-A1%>ZBK zoXSJ(j*9u*nH#}tEXNE3!n9-sYd~4RLD$@Ed=&Z;2z{Z!YDdN&Z(r~lcxbW}TrS%O zC^w*{I;TGewllC@Ht-c|gs~cW^8(%0781@{&@(j6K?X6*WEu(gm}JzL%$)3kgf=@Df?}udN4k@HWT;S zPTc&J*-Bk9C?={Ge!h)xfxtOVRii_d+{H8PRH#>^9_U@|72Nn4M&M!ST7l2r=%;}7 zXm+F~OBJzf=UA>UEp7^iGuM)>D79lM?E^sXF}P+m%-%7Q^3TUw3+%&#a4(dEGiR<& zU%{f!Ljualbh9@cMR5m4WsuF!8;gW%@cZBz0WM<^NRQ-c1>F16?Ev2ev`~l{`wAzptVEh`1ODLJSb`S*10girWEk5}&(|@SX-W9x z8gLv2hi5>z!$@$b^1d%&I;o2yMPnV{RLm}-rha;NtkFqqlXfb(d^4(hmt2P$^hpnU z9OQf2N2e6whX-J!Kate0LZXLwo;;JiKOIB~P1S$#CLTZrhf#@Ow7_u|_s~l%)Tz>r zX3-gO+?Zp(ruc0hX2gl#$h4Q}A?fmgd-r6YdhlL+^@U5u6VvKy@wCaTR;FsFcoRn< zgKCN3TWv+nYJYf}S4*6z)>ErpRLdntiJESrfq`HXA;t86VO5V2bYzq={sUf^z%kBP zG7pxXj zD8l5vi2pG5V(>?Szj6+ZOO4EXK?m*PW<9jfLHC6J=Y62x+dypm(GH;6bF;;6Ey$B( zx0~i$zKw<7w@#;x$U(FrI+C`W=&a?#yO!)fi0%!DK7=cv*7rEu1egbS6A*hC2QdK4 z0N(*d9>EzWz;VD`KOiarWq_EYlJo+g9B}0*Y;fO?$N+8v(vM*j1C#-70J49=!5ToL zUvO#%Fc6|gR|o*qJAt!pfOUXtr*WtX*QuzdDy@hivt`pTBHb1YKDBl3m!Rd_#s^%W}2= zBMA&8&7V2f2ENV`?T4Nx5&QY!nqce{eTgT;W}fdSrnW?#3XxWsBs@Q){4z5i1D$9) zm7c)mJZQ4dGsy<~<7OTYP#5E51wAXa*)gEfjb4%vX)}3@GWVID3myX+7H20`Z(T(WKFa8E8lS&|+(u${ET=nRyBd3ULaGOPlFR zG4tnhM%xW``yVEP_{EmbQP-*7CV#m}HUfZVd#JJnX!A^yCje-M2kj$&H0q(Ct>H8; zKTf2`pCc8pv`=aX^S=1Sew?8MQOucXL0s+&wAX? zO;R_fI?sFDr8Z{kZ)W>i>t8eP-Q#LKV6y*ck`3eTHS=bVvuK;cY3cR}9d`>jGoJlnL+~YNfMsGDb-IQmTHux``8Z$cDC^K(#u6{<|Zgi+l zT$<@XqpH}}z?wbE)1ijqy9OfYqD`{V#$YpVbf{->5a*T-Wi~_fsK!xKs+nlGG%R5B zsbMb}%`m!Ax{>G4Z4d@2yl_pmAD!=G>(~iq-n;Lt!y^jOjP54cXhv5v@7;A4_A=S) zgFJ{^Meu7z--!giy6>0`F*=WXozngiZlN>YiM#tD|=8ev<9eH$y z`Z41}>2b06!vnK+>Y39WyPFg*W%EUIdJdgUJI}ljJOLf zdD<_fHYoGvr~}X5UC-vjfKV|@#;dQKO^_QBSA5})vxj7stiF1#MPlXCx2ucIB8l}s z^kMLlSo$=Vbx&mmV;Snx=Ud<#@ulYz)br<}MhPd#Y< z*$nVDZd4b^cYK(Px`^j#;)z%LU5bkD@wl%6lgNPIh8NDfv@uX#I#q4*PxHjtQ~gy& zX0lGD%7V$3_fAz8pN*FpVdB+o|9m5#y{_i{JIG!q3^mY$CY(cISnIoED;q$ba$OIi z$-5vbkRy+BZ5A585#r_EA$pMdYQ^#_Y%1z?9Cj>5E z;HGliD2}U~2d23kVH1ccKrG~lP69Cuh?N}COdv9VCoTKDV{V2&1;Iued2#N-FR}nM7`^3)3}S^&CGRM zmhNK3ec9eHbQ5{$FaC|!AzvKs>!i}249c0PFVi)a;ZGH8o+cv2j za@L!{^Cj=u$zjj|=VFp@%-528QrP@}`s9C|?*8;q(yL`XCKU`waD5Z%&4?8xnQiiT z_1pha;_`fG-39GGHR4*!c$*I?K#=N>SNmRT&cfB%*E-0_c;I}k?Okg_s^w=*gzT5q z%4;oaPxD4M2l~LnYV+%zqsC2w0q?%X4cJbdaXrC46T%k)-UNIKm=VOJ-MAhz(&uqy zfdU_gs|mz62KiY4CtwYr2tfG#xSBxupOOC)AR}EHkPPSm_%Mh~lr|wjumiBwfYZTD zY8b+#?SSimNuf+y1;F!QN6C%*ufyN-lHi|v{)K;*(?NwQ_y=yfWV&f1+jVcru5-v< zD17wBP$rj#sl_*6VAjH}?g32R_}}X+i9VY|SP&OAM5!kI@C5)=2Itp&b8IPl>DKQJ8lGg?k0WG)P64OuCrW3?~CVWJfR|0m(+3w)CZ;2#qB5fQ+* z6?ld4VFF(QytX-##c6*7tDUD*t4O4pL?JbvQX?s~j8d;qY9FO`Q%a6P>c1#fTk8AM&@VAJT^?b9tCAaYxSey`T<&}XqEwW7DTfq^1p+%HNeW3 z2WwwNBO+XbDIyxlX9sKUXx35QF<3iPi=~ETBPucfEaTa>+G-06l4lw;8XH0DC1@c% zV~}YqXrW64txB5!DZxDxK>D82?LwCe$_EA|2^5E*tPzya28GVq-bd-qEP|s#dZq&P z`#{pHle~>z+lKV)4CFGNN!4cF#X{sKOiDT^y$s4pqBJxqHc+Yt@Fr>lYJ+lsXCQ45 zC?6P3z`PXHcF<3z2p)URZwPv z@`ORTBq$k_?SMcb{_vNo1ooeP+T~cZcwax^6O0*D`C21OJ88&1Yh-B(MRu@}O%mA_ zBC9 zMeP&Vp#XVFFD*Ecy~Gl(Y?fJ6_N5Zk4gJ=gs=dFNNB5CPSOqh}8+EHRJb2nwnE$r0XO7iAZaM8nHlm zr9o%_!pl6JATJQO5K`{345khLA8Vx@XvFSg^R&PuW@Bx%{z+_OMDt862c39!95qf0 zZOq_lkyzplP&oHg^_jTv6g zWZz?w4XwACd1A+n5^sBa6O;WdPL6~b4gOcVYPDLSm9x6?R?3fbr5C%xjCKv+xlp4e zU9{|0EK#1;g%18}n_98^;wN1u$u(}Yx*lAX!s^ENtwC!6Pq@Ml7Nw@J{Y-wP^Rm_~ zA$3t_!|$<8pjvNdcjz6}i6_Ht1r0iYY%TAekS;w)OSS!7ifpaf&jIr34n@iB*qng) z6=(e@@N${9@jf=I(K~IcV+OATypU?$#)pK05utEepl0pBV(m`~rp$tjd= z0|uVz?_DsQ#H_1X9C+&TOKRfL{?r;`J$ccdPiXSQhhGd6O!TS@*ra|1^`b@yT(z}9 zXoA{$Y0;fnd|an$vf0+y;?`PPCpJ)iwzamX6HA;n16$kuz0J66H>!nAEs^W$|lGGt;h~_!VF*k+frl=51%r!hF|x!)E-O2BC)WgmY2r5 zr~a=cn&4#p3;Lw?b6Rn)Vok6);n9MqQcj_HXCAcfjnnGbbxPE(reOh`QdF-Sn;gV? zYIZAYP-lHpvV#Z{qwmCO8?7v%`Jm)m!zlJ_6Txndv}c=W7p*KYwu_e-^mOv^OUe9Y zu|~Aac%I7=E;d7w4+k@nwF$#nygY(mYX_63dWv)_>aSh=DUn=5iDa#`JG?%{K#Ok6 zqc$yBD;dV(hd z8*3#!SUs%bkN3bTzPom%2kSo|8_PPc?qo-I8c`iNr%kF?G!6jnmdN2FbbezIF%8X9 zMrb$GiZWP(xS1x}t2b?H?A4n-=i2-0O=}ucZ<1ebtj+9#6%d(vK2Imej~SH?ZDcgW zsC20IY9=aOW}-z4_y~(4h;Xrx8Y+t4^;bi$;t(%4($Yt_b(+JZhTp;azq)EpRN>zhTIpU@K!%3IH=Xr zMUq-QLNHUSr*q~6xorYpfXM?`r}&ipMEoleAhRSe?+NqL0;~9J&NYh8f-Re3aG)gP?pQN~`L@ zT4=9~V9ol+M3Ks#w6)CAFluw4`%Tch~ODD6!u{)&nB8Xjjwc@2-#AisuUSd-9VFOL`KP{WLUwR59zS8;EIhT*G)mO7d}#WJ-u zquFQS^_SmtcgKEWz2(}NG1y+1ue~~kWy^=dwc2A@&-jQE-`G!rvs~Y<(q@ijt>pvD zwAEu-6SiF2IhH+wqD{uJpX8@s*KUksb%Bk4n6+Z_i@HC|)-t=&eJtL@ahuR!1wnF&vbUcGtYN znM&=g5@^^XwK{eN&{;#==i`B$WpiXG-plC`nsV^)>5PEyD+8^L2H)a4X+skGP3mFm zH>vM^^}o+>4oPtA3~>(Whm%~|;0f&B+8NHiEqYHXRU#eEzR7fGCv=x~bUaI`7YW=$KMFe^^HjP5!dpw*v%rgqXgPGDp0f%N8GP)eDTPBhXNFZhd){IeJIw9T^F z8s1MS6?zW*g3i=_YsDudGE+ji#)oyQQhK<;ijYv!12dJFw1cziI7$L>3Cwhk3`CRC z!>sw~)HeJJD7~LrKQ|6pTNvJ#(qbmE_@uzJL$l%wtSBY;Ej{*Howh(Kx64|V9;)@4 z$dVg}2liHe!h221Io>iDP0CEOCd`U8S~fvjG!gfgfhlE+0A!1Vm-q4b({U2V`LR^J{@f=e@$zuND#Rhgd9 z5AR4}O1t0wIbH=S#Sb5EK*bqpU(bqLVpNPh3Mp-N!t6_A!I5-6<$&uly$`l|M7ZO0 z075mg>sIhc?r#pC{t^VYQkrruWoOE_EW~jkfDH^x(<&#i!R?<04exF_p9-}8TNaqr z)T)$yMfQUENl0h3B{ao#_|L z39-92A_CyYFdld*WMXT0(W*z;944RYR)nWD{bd$jG-*1U+9>L;$FNsVFNI;7ep}Ix z`Rv_*1|ya;=_Np;*O`<9SiOu%HvpQ(q))Ur7O^z8yy%BTY*{FKU7PVbtC#RBPC$7- zN2Wu77yzbft6pdIsvB5#mr-Fppz_l-)m1VXSFSq z#aWAIaEb}oEUn!#R;R^pIFja7ZR~L~FSu7P5Zvk76U#7a-$R9J2M-B?fTl*8syL@T ztr4C+V+XZYG}ws=O5b3$d&SyKy1_rs%=_B>7obgR z;F}np;}Ape8hcdv0WK_ryW%S|L%wjB8FI!bkBX4v=f{bb)nE&ff`$o>GARQ+9#CwEXALb=nt zzw1H4F`CnS?|6npg3#X&A@&UR68M4>I@1EwlZVdK(m*PtZF&>;8W*=~2j0XE$m#7= zc_Odey#I@JYS-Unt?UC#aYpeMIIowY#zJ=pX*!(~O-)S-sXE3VCH6AzoxOGS>3EpZpxlS*E^vz3B7+jE?$c7MSc9UNm29Id|1wY8 zxe6aCnWM$5X5;V}>dDn?gnV(XR=%30%3lvDy1JSz3t$yROV+Zl8Jk)({9X2_9Lf68 z6X9>H%2H=;QTh9(dBAkQLx40u zGQa{b&YbW|`mhn!q1Ivi9oz9XB`BjPX9IhKv3H8_S!OM@rJu1Cte`0MbC$~*Y}>}9w*W5y9s%4Bs1LYW%%q>RLxp%y`NPGc%Z02e z(DL{=OmWWq@fawu{V+$X+QRnBW#fwWe92}r`SKX;{$kvh{5eJ&SGYS$v=+K-rvUB+;s)70O{TFDGhvkfit|Po!!P(vtTXnE9|Z})mm+5 z3)v=Z<966{hcf2sLt6OP>>S&u{qr>&f+4nd8N2rXwfCjrQ5D(Wbt~zlvyl$j!`6YYD2vUe0Yw@C zjSfPqsEh;Bfv^dZ5E2Lx=!UQaL1@;(l_o)9l)z-S;5}A7!W8%0UFCWJr)K+Q>#EFBNL(8j8 zJ%m@3T9)X4SR1aT)y_Yxbx7iY8^xBdv<2D(Vf$KJ=jo6^I|LqG)_^vZ+d?c~-Kd|X zWZ^eSq?0-KqkRm0l?q>~aIgxWq_9|_p*Pa`R@whJSfvZuLkV|o74Wh$*g%MFvQvFO zpVZhzP!lK`MNI^ygFK2l0lR~)1Kj``3c3wsgpUD#KWHLo8mJI7AM`=((?_+RG;MJ0 z>SJ1QM3=$=*e5+Ar^h|G+HeAs4IB zZkk*CdP3`@jjK)lUb|h>Zmym9gO;lC#zSJ>N$m!0qS$j%%js~(61rBsbR~y<(OYQ! zfvfdqfjYL+`iZy(I2F1r5!x9ox$|%tn59PqZ$w<-f`EzbQg1P#LCeUZ?eBRvzEc{1 zaHNeMX-=VT6n?R{Sl0kwwmF=prc-opl;Lktcx|8N`1m{s|5j9nf7D0(jPT5lOyM-| zof;>@SBj3OwDioOreJ(g$4>Q-!HHrtf?K4Hw@3Kzruc*0k-FzptJK?2_#qj-QtWA+ zx{ZRj%HTwC48c94Ro!Dy_qGT*JrhaO{~M+TA!RZt?}M_du>Yu~^bGcD4oju5UI=^A zq)a1aI+U};#2+yMTv;s2f5g0TX0dqUM=fjck;UdYm*#1>-)Is+KYs9MVFfZQuvnZ$ z{ItD)Vw3u!=y+P|nC(ND`F0SN)$PC~CY;tfjD&1o&V_rOI|EM#Sj z61w%-TBf&!K4L;KW44@uQ}?e@_*G9Oiyv31InH1f+Ia>ec)WPyjMmONLE1eliXidz z%FIt@=|AD_?v~+mWPmkaic>_8%wx95}c!(5Y3ZzM26=RANy9(D&h>@gXcqzu! zg?bfZrc0Ak3&pcPX-=)X_~9q)MM)CzXR*qN6xRScT`Z>-pzbp*fduU-LY03kr&Ul& zjJl@2DHlu5YAO6bN?WDL(S48NFU8J~PS48TH7fI1o zS6Fn#Yotig6;ZlkQ4c60bVanTc#0Iipny0FPFGZu;y5NkXkv8DB+_8v2}P`~$R-6= zEKtPhibl$2kFJQ<6+6k|X0(b3$5vcwt4qC+*Xdg;E+y;I7?N@;E~V%ao%4PA+KNl5y3~%O_7#`Xbm^OJ zklIvS!ewOH2R<)xWwIpr`VZ1qo17KCxQh!|%o{$!;b(0~} z(U$1TOp;P~YO?re2p7$TGS%a1jT)A5(aLG;L#~lMpLEH zWR<{m=aIl@6+>N2gKEc~*LFu}@j|<(<#angm-@noVbgqnO67B;NH5}O?fks>%SEkS z_UA=Pt(Mr&A+A1eDYN^;%8OcOctM_r_24dg%EqFYOz@36jZY*Pr-z|mO}TD-OolZT zi>8ZOR(dUR`>(0(Y%wim9olY@->7x%zM=>lkTu7q;%jf6| zZr9ZCk!*Q4CTP#&0PNDXd2hdMn|cxd2=$jz{>%Qnw-4J+O-5Q+Upnu}t2krFwNu-U z3f9WeyaKHy+WfAid)~^VU>wc42TW6Hoh}~wXG|mu=tmjlHFe!hx>cictVl6;Upmrn z_$~p5oCHdu_t9G6!)vgPJy-)Z3WdZ*;GnKmmOU?l$2)i&Xcw?TzM16J7D2GeVud+b z&T>MChwh-|+ZS<646;3FISP)t^PuGi#mR@dnE|Aja16S$UrY=dzX-i!Dqdl(M@@B@z6Uuj7f-LB#pWsO*JRZC9sy^Olg#kvK%ST_Yu zPs>}K+Cl8Ps`c;P7a^lojSPYs5^Xw$eo0J9xCZ)P5^Z;e5|b=lbv`Mfx3K=9^-Nmz z>(!>x1$mP#uY_EWkFIT^x*FW`S zjp*Klb;iLOaa)u2X6kQfDpnG^FB;JBKCYgFmf(wo<&=)N?6%_*a027Z85XGI#R|yttUP`7_?XTQ1V=|Ml{KClUwExtb2GvUG!{ zVZrbCM)A?cN_2X~kdO9=J{nKc>I6>t`WrFY9no=voEf((7 z?hq9geh=;|Us$+3>w8=g`f0gMsh{WVp((^+>$b9*NV|0+3b*^7;}_hqr)#>`uFs7m8I;e6UBO_;6z8k5HTty?8mj)qvCh1)RtE<damL?kNqE@!*9oD{kWuK5r79w zynKdy>Zo2v%MkE-It>v=@pxWgjcoXVcCA-fIr7ls+by@F)o~w&8=Fq|v+1W18^5byS6yC@tbw&;>{SoIlgaORCbl+$H&ny zwfV6;U-M)!aAr(Gd$yrJ^a(G_pUskx4keirQWUdtd%~X^zIWa?d`jz9B8dStm{3@5tIg-RmQGE zD#CvxZc_T9_oQ6^jYOZ)JC(gl*>6;O-$7|tbWGxUrS~g+P}ifYHW&$gDh-`SB`!B2 zd?MxG*AkZ*_8%$xuOxa6y|VWz`wFFJA4|J{vR^`a8Y*rTU{?{AstCakWCZ(n5+74~ zyVCo=mHIVG?|5Ize$}FtN+0-_N=MnRR(db&@f{Fj_T$oEl?q@gg1`198&8bXg5vEI zo_?)wfgC#CEIFi#6pkyG`g$dI!{-dC{6P`AnY(woQ@JKwL%FlooyL7Vb_QHrdu3`0tGDXdo!gFj2VZYeUM{~OjVmf^uGszAR>^j|jTgOT7+ z23{3Vzo0Ujfg&`Mvf-Yi0D@` z)3n+f@8yf4^L-v!RCh0F$<(yd8tc8%E~M;iy`{ZFp;O^>m0ppus}Y$K_{n_RQW@QU zgDP0A#2Pj9Tq@i?OzK?<>zBxQMJk?4VOcBt8j=4Xzdy-U(bC)}4nN4d-|kx0QjK^0 z`rKKj%#BHIG@+t9myir@t0Eo|14&bFAdMx=@O zC~BO~ET%=_1V%j|Qxr|(krCd7vLrEwkh&i!`NX;DJbk?*Qq<2xc- zQ*g2t5O2@m>3AMFjwXzAAkc+uFzXuqkltV_ea4~|C2}^48?z%i$bz_4LAp;zV!22R zQ!&-1#n0C&rgsLnj5gU(C5%|+CZdY9_@UVijoA)eAudnnT_aE%@%1#`HJu?x^G`Lb z2o&%PQ8<-b;woQ46ESc=Y}cFjd2u_i$XR%%+olKE}58Il!|IoFMdUQ-nc|` zewcSluUpbGoifP!#hNK>v(e^@9_C$A-K~u5ko9X*AMn08+`^a0Y~rk#aVSm9_D1C+ za+k`$Y$pO65r|uex$HXMh_cnmImA7i+oCB?+)8A-uA7aP_9GT<7Dlp3b*5D6!8^o< zvw6={GBf{rRG14(P0t&biav$Bvt|=_74lBeHnONNvMS^;oywLWD`;E)e9)`b(JyoR zGS#|eucGvbfYvQU8HhFi8X?QdS?Z94-ge2tUBqpcpe`RTl9LCurPy4A>9I`gE5d9W z6bNdkop(WJH0K;l$&GqCNj{V5u4a=uGAR=u&%vx+ z1~sZNhxf!r#_VEb?iND;7CY`Sxui+`2+Ju}tMr_q7&&mBK((u`^OGiHq+Q8>Bu;VH9A=8nH_#@y2US@wHMN(*Kb+WR^B z4jk_)E_`UpWQ5%^qdYHv{><^SW=xuv|L~;zl9DOo%dQ_V{_ZKWrihP<`N)Ab3uk>w zrj(WBJUp8UU{95|S3{TMa4;uyYVS%UNLMC0l=9oO z8Zo7mi=+!kJR4e{4QFkEE{KGA{5s4&L+0_}gRE$bqWL)O9zS~uXI{IU^}13N{B8nL zSZ4PqmZlA4(IHDP!ogjYV&6R8?hadL#%6=+u#BM?W?75kEUF(b%0{1921GF%9u2$E zY_km`GQyR%y>hO=` z3>)zhq9Eqs`TRx;Rd=h1DdYXS7N`{#T_>~>(?Dg=If2H{De}vB^23FQm8W#xCuM`2 z1=8<#CDW*)-Rupe@|AuOkg~~)RR)`_;&>UK zpXq-M|2Gdb_A%M8VOh*sz|-?tE&holXqg_2e8%FkYN10`CRvM(WU&LRlyedGL$cU{ zXl5B3#T)^ga|Sx3vI)!L)B--hGc1-3E3vZVv2iSUKrBn{ml#S2#<}$#JrKj(_3*BK ziT|g!R`uwA>F^Y7wxN-5B8vG=U_tpk4z*XKrlpM9C>x7zB=XC7dS=lN_$Q&Dy?S2g zUqcq_5j@0o0smPMBnKBxlQYYdZAlah{f+^88G}k?83~@hh?#EA@3#5E#yNp>kD!E0%+WN zS^TgpZePUvd+JNEziFPvc2F=Sdm%=bw?J2?qENV5HpkH-H5Q4k%*LNcOiO_>jeQMb z8)SB7S+hnmYrjZ$go`_}?NQ8rAy!;o#ACATi!?R{)JsKbmPN8%B#Cmu)k8_#Eri0vixV*Mo=u}}2z~)1cSEbWhZV|_H z&D)n_OB6_6^SI5RvJ7*9(tE;_K?_5@Rd5Xx5nyA31 z==B3_bdzR1ULdrvqtOw~!bz#}gk^Eq%RAlTU#GGCAlqgXh1%>u0_MqhT{ps~F+1ro zrpU%DH^&p#F5y`o|9Z49sEF+KYR2nw>IT%p&|yX;c_e6)O1HI4X@uh-Uvs+Z{_4)g zQMpN&rD<%rHsKtW%{UwUjGiFc1_5-VRRL1pC5m;S8^)i7A(Ixis^5cK(T|Te_q$ov z`@IBxIRJ;rBt$VBR&o7O-gXFl%*x-NY`#uMrF`bIO7CxZMct=2N76OMy=nA6rfrr22c#IoRL7`~sQQ?4K} z4eyd;KEWcY2y-1}_GtoheW9_aBO1%tiUQtZhLu26n43Y=^gwZOaD0_J2UR zWKX&SDZ2bVbYn?}0#UeG9&p5JL=tx_;~hKrk812k&<;HUu8x9u7S@U7%lMdEgAE!> zIz=6iH1&BIBMoJ;;2jw0r+_jj4EeQ|o8yYC<#^FpDF!a*{d?D+!vOt7V^8VP=$edW zEp_BKr}2Iz{)DJ+X`mh<#1oWRCD2RWXQ1!4#O}eLca2A5D(WHAFI%_sci!_YiG3cg4M~^zjui!cG zh^v07l25w~et!L9{RrKahi`2lLNCSHKf~0kYvSr9KHeL_oCIufYwWZHkO*2L?brpeNjb zcgqg&eqc$OJaAE?`_|#;8@y zUIX$bJnqF!05*iLSHj5wJ^+0FQE6WT{5xK+>T!L@7(`2X!a|T0JmGp!d+>f>TMt|b z6d~+VKmfTKM&d=Y6FlLApt0cHz^QN`6o3yq62Ozw!HYNI%fJ&p1@eO@+y~kVJ^-96 zupk9*+lr+kh+5iH59TU%V+0Yvo)7Hv?&7qwMz1C;9WaWdpyh601xlNKY;{a58U^6^gq=;fXy^D*bhfMVXqe? z?*Q&rd=2pUKQM3*IRvDAWK;;kguSv=!u|-@ke}i>$PJ$G5~u{c*#U7wtp{rocqDuX zAiVe%4o?S9*ed`J5_kvD@J0|G0DTHQVbZ%8q~Ptqf`hnHz!w4UJtQmO2Ht%bJX*Bq zea3cz^s5FK{1E;I4;%-?y#A3iGCL;7i4l$MMk8QDNJqwx15fxUoE5Xco1GNomly&E z#6su^?*{q66BdB{;KL3I@Z`Agj5>+TArRgT`V_pqfw5Ok%K`;}Q-7Ac7x)?I1Z?m= zo5h7>ZSV$|omMP&AQ8jy4CFZAeYD$P-rdwxdbsAe#Jn8!=mOl#?FH1 zy2l%M_6CR|<0U&g4kA5Xu(O9QNqrG;-tS5eH2f%pLqI7=lW-oW2Y9?hXFab-d%Qnq z-&{feQwDh3&f>1h+F&aI`zMGp!*&8Tu1WHCO=C}jNRM~g4BwgRahp9bT=&$_36*)s zj4(0=r&EF_41j##%^sN{o;Z!&3)+nU!o{Ef_^?X`Jh^H<1J#2k`~!3Xd`-N@z6VhS zLckMk@GOFX=1bD>k76};0X8AvyE&4t2X5)A=>yQ&4eM@@KK}>6Sh~xEY{03CF9HS> zPiXC-Y=Af602_KVb^_@{ADVjc{+&Gwq5{7&{sac z)d277hsdab8~6h8NXXkC-{s;Nj`TS1ik%-Q^$gzz+;)TH@ur?V3DWNcz<(*e9+;V{ zG6ou6Ey87>9(ePmGyem@iP{i;2pS6>hoZ0*gW<-ZCI!aaC<|c&?js(B3jinGqzVD7 z93pM(H=~wA5f|yWfJwv9S~O1p{{o_d`hf3xM&NNAM)km(@{j;{`|a@Yfb?br4!%S3 zPGIl9$P667=ut8Q8?fe1nL!A+3-1xAYifWM`1spTJn+`BG99yT&O=VSCHKLB1S7)d zK}RS8aN{@`!4KT@faI|~ft?0X4MOA5yb}};EPqhic!3)xf=A2wf!F23sfXH!9e!vz zasHzumj(kDr?}JjxP#9lqfbqu(%nTUI-iT2T(lRf7tcF zrp}aw@B#B@!J!3x5%9i;Wmmd^y=LQi9Qyj%9(*$k*$GL`$z`A#=n1!h4uJOq4X32p8A(3KtDpwh5bj@s76wmv3>3c+ z^MA9Wk?Rgf_flM!a2^ttgPh>Kz?2GjkHOo4&w}XQUjrPpO!8*OA~`C_@i+_iW(OoW zCVvHOf}SvTIcmKNLM$AJ>7Y8w0BCq332Q-zpeH;A3W6s*4Qd2$b~cg|az4lkM;_rR zP91{mSZ&PJ|#A^nePY&=B(9$k$t10VJhf;W2= z$=g`I78Qe@@HFTEc*4weh!38y6684pftW2I2KQXE&k+3to#9U;T(ushh7I9HkPAHF z(;zp+1seWEvyag1KP126e9$h~13hdZgx4VuHh?|=Pxu??D0s8q4^Bk=%!Uil8)rAf zZ{guH9Edm0av)xw>41R-evg2kZ7V*725+1J5x)iV{|YGgg2;&QD98!k>`Wvl;}akk z^n@>h3c%L@4?lrKF(3H0qp7Q9_l1CeeNOUjplc_1n)QIU?ovE(zUS|9=JEo^Kd-S8 zBat&w)^n~Ao>cA5w)Sw%{+kiW2F`a`C`zFDY&+@CcFi7F} z^AM8~Kn)-?yC%^c(51YCfeAe!xe|-On_Y@#cca-ANiNDaKy|PuEIfd21yA@0=qPx? zZ3oc*XCS!WMRUFnrpZu0N2r&CE&3;PqS7v;P=@&MH9lpZ!3!ZQ=XbpHL z@HG%U)ndyo>-aUCiO^%0E?ca4Z1H8s6dwfMd=xewiVWr%62OOjo#4q2`sO#7Zov~` zGc!8?o^bLp^ecF5SZ1GqC}V6=X1DxD^4Q?aURHbn*f}Wm*g(xTC>~pXnJ4qO48XQt z_PFBxz|3!z9{9N8v4@ytey8-nt%~;p$DP0(5Cy`PZiemKECikqTg7#r@NbHD1J8pR zw_^Us-f-3(6bVNv_K!0^h#Uxn^(WDnbiZ$CcKMoJy5#D`N5;&7xP)%dDDZ^aLF2*K z0B`#l_X6-PV7HJ=6Q4A)wdXwOKLq&B;l2){Yc%ZWg`S+kYe2ie6MhCd0G{yP^XLNb z^}y)gF?oTv0bgm7tKk4}D8o)jTykOWFl@+c++M>o0bIlFJz(lUb_fCBHV&R@4>bJ8 zguNoLr3E&GeLybo_zZ|u;6)}C+6O!X$35{3TO6`MZrJ1i$ABnJ7jT*43Gc;6lAZz> zjRSKSGzl@9~l<_uY$_JvpAF-L`H;T6z>9VQ@kHo3!)6_fYx})6ZTKQP8p;#0Qedx z2)+*Zk2bJ_2fGFsnMei2{7=j<5Dl~uz}O^|7)EyBl(yJo0lo;>J{c_qo^U9LGA;mm z6kiGa1Vrh43hbC7`AXokAhO>D)KcYJ0|&6*X#AOuL}{Z2ZRn^+(-G49fFO86dI7K% zH6SGC{t@tm Date: Sat, 12 May 2018 12:55:42 -0400 Subject: [PATCH 223/339] i hope this breaks something --- .../lua/EmuLuaLibrary.Emu.cs | 2 +- .../Interfaces/Services/IDebuggable.cs | 2 +- .../CPUs/MOS 6502X/MOS6502X.cs | 2 +- .../Calculator/TI83.IDebuggable.cs | 2 +- .../Computers/AppleII/AppleII.IDebuggable.cs | 2 +- .../Computers/Commodore64/C64.IDebuggable.cs | 2 +- .../Commodore64/MOS/Chip6510.IDebuggable.cs | 2 +- .../Serial/Drive1541.IDebuggable.cs | 2 +- .../ZXSpectrum.IDebuggable.cs | 2 +- .../Atari/2600/Atari2600.IDebuggable.cs | 2 +- .../Consoles/Atari/2600/Tia/TIA.cs | 2 +- .../Atari/A7800Hawk/A7800Hawk.IDebuggable.cs | 2 +- .../Coleco/ColecoVision.IDebuggable.cs | 2 +- .../Intellivision.IDebuggable.cs | 2 +- .../Nintendo/GBA/MGBAHawk.IDebuggable.cs | 2 +- .../Nintendo/GBA/VBANext.IDebuggable.cs | 2 +- .../Nintendo/GBHawk/GBHawk.IDebuggable.cs | 4 +-- .../Nintendo/Gameboy/Gambatte.IDebuggable.cs | 4 +-- .../Gameboy/GambatteLink.IDebuggable.cs | 2 +- .../Consoles/Nintendo/N64/N64.IDebuggable.cs | 2 +- .../Consoles/Nintendo/NES/NES.IDebuggable.cs | 2 +- .../Nintendo/QuickNES/QuickNES.IDebuggable.cs | 2 +- .../Nintendo/SNES/LibsnesCore.IDebuggable.cs | 2 +- .../PC Engine/PCEngine.IDebuggable.cs | 2 +- .../Consoles/Sega/SMS/SMS.IDebuggable.cs | 4 +-- .../Consoles/Sega/gpgx64/GPGX.IDebuggable.cs | 2 +- .../Sony/PSX/Octoshock.IDebuggable.cs | 2 +- .../Consoles/WonderSwan/WonderSwan.cs | 2 +- .../Libretro/LibretroCore.cs | 36 +++++++++---------- 29 files changed, 49 insertions(+), 49 deletions(-) diff --git a/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs b/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs index 60c0eb6164..2ce9d776f3 100644 --- a/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs +++ b/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs @@ -169,7 +169,7 @@ namespace BizHawk.Client.Common [LuaMethodExample("local inemutot = emu.totalexecutedcycles( );")] [LuaMethod("totalexecutedcycles", "gets the total number of executed cpu cycles")] - public int TotalExecutedycles() + public long TotalExecutedycles() { try { diff --git a/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs b/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs index 69d140890a..e1655b888a 100644 --- a/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs +++ b/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs @@ -41,7 +41,7 @@ namespace BizHawk.Emulation.Common /// Gets the total number of CPU cycles since the beginning of the core's lifecycle /// Note that the CPU in this case is the "main" CPU, for some cores that may be somewhat subjective /// - int TotalExecutedCycles { get; } // TODO: this should probably be a long, but most cores were using int, oh well + long TotalExecutedCycles { get; } // TODO: this should probably be a long, but most cores were using int, oh well } public class RegisterValue diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs index bcfa5a80a3..c92ff5db95 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs @@ -204,7 +204,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 private set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); } } - public int TotalExecutedCycles; + public long TotalExecutedCycles; public Func ReadMemory; public Func DummyReadMemory; diff --git a/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs b/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs index d4e292ad18..e6477b56fd 100644 --- a/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs @@ -145,6 +145,6 @@ namespace BizHawk.Emulation.Cores.Calculators return false; } - public int TotalExecutedCycles => (int)_cpu.TotalExecutedCycles; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs index f36cef6f8a..1284ab2dd0 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs @@ -104,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII } } - public int TotalExecutedCycles => (int)_machine.Cpu.Cycles; + public long TotalExecutedCycles => _machine.Cpu.Cycles; private RegisterValue GetRegisterValue(KeyValuePair reg) { diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs index 29ae7368c5..29d110c705 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs @@ -63,7 +63,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 _selectedDebuggable.Step(type); } - public int TotalExecutedCycles => _selectedDebuggable.TotalExecutedCycles; + public long TotalExecutedCycles => _selectedDebuggable.TotalExecutedCycles; private readonly IMemoryCallbackSystem _memoryCallbacks; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs index b64b70cb3e..67ba9a45c1 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs @@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } } - int IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; + long IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; private void StepInto() { diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs index 8f51fa55c0..e869a3ecfb 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs @@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial } } - int IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; + long IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; private void StepInto() { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs index e086ad09e3..caa161e0d7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs @@ -142,6 +142,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum throw new NotImplementedException(); } - public int TotalExecutedCycles => (int)_cpu.TotalExecutedCycles; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs index 75c16f62ff..1a5068cb10 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs @@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } - public int TotalExecutedCycles => Cpu.TotalExecutedCycles; + public long TotalExecutedCycles => Cpu.TotalExecutedCycles; private int JSRCount = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs index af5ea5f124..bb6aa0783d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs @@ -106,7 +106,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private bool _doTicks; private byte _hsyncCnt; - private int _capChargeStart; + private long _capChargeStart; private bool _capCharging; private bool _vblankEnabled; private bool _vsyncEnabled; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs index afdc4168bd..343f831e5e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs @@ -67,7 +67,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { get { return cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs index 8739d2d616..73ca2c072e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs @@ -142,6 +142,6 @@ namespace BizHawk.Emulation.Cores.ColecoVision throw new NotImplementedException(); } - public int TotalExecutedCycles => (int)_cpu.TotalExecutedCycles; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs index c80c5832a2..78d5f9a4e7 100644 --- a/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs @@ -95,6 +95,6 @@ namespace BizHawk.Emulation.Cores.Intellivision throw new NotImplementedException(); } - public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs index 57809282b0..eb97242ded 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs @@ -44,7 +44,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs index 2b9424e729..71592fbc3d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs @@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs index 8d5be5a227..a1323f8564 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs @@ -69,9 +69,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { - get { return (int)cpu.TotalExecutedCycles; } + get { return (long)cpu.TotalExecutedCycles; } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs index f9d8d42534..075ee71d27 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs @@ -44,9 +44,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { - get { return (int)Math.Max(_cycleCount, callbackCycleCount); } + get { return Math.Max((long)_cycleCount, (long)callbackCycleCount); } } private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs index d6b4d8d276..363895ef05 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs @@ -45,7 +45,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs index 5aadf2b2d5..6a7b66d383 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs @@ -100,7 +100,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs index d1fe787ab8..58b920f85e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES [FeatureNotImplemented] public void Step(StepType type) { throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { get { return cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs index e78ef46cb3..d2bb1f5212 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs @@ -39,7 +39,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs index 92fa298885..37ad308671 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs @@ -69,7 +69,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs index 5fa09dfe3b..5b74440545 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs @@ -46,6 +46,6 @@ namespace BizHawk.Emulation.Cores.PCEngine throw new NotImplementedException(); } - public int TotalExecutedCycles => (int)Cpu.TotalExecutedCycles; + public long TotalExecutedCycles => Cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs index 3a9bf83be6..2f142e92da 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs @@ -142,9 +142,9 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { - get { return (int)Cpu.TotalExecutedCycles; } + get { return Cpu.TotalExecutedCycles; } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs index ba82d0ff1a..9b5e7363d9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs @@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs index 47cc4221e9..b71f3223f5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs @@ -89,7 +89,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSX public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs b/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs index e1defde253..49f91a1824 100644 --- a/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs +++ b/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs @@ -131,7 +131,7 @@ namespace BizHawk.Emulation.Cores.WonderSwan public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles { get { throw new NotImplementedException(); } } + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } BizSwan.MemoryCallback ReadCallbackD; BizSwan.MemoryCallback WriteCallbackD; diff --git a/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs b/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs index 243688c6f0..f52709963b 100644 --- a/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs +++ b/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs @@ -17,9 +17,9 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Libretro { - [Core("Libretro", "zeromus")] - [ServiceNotApplicable(typeof(IDriveLight))] - public unsafe partial class LibretroCore : IEmulator, ISettable, + [Core("Libretro", "zeromus")] + [ServiceNotApplicable(typeof(IDriveLight))] + public unsafe partial class LibretroCore : IEmulator, ISettable, ISaveRam, IStatable, IVideoProvider, IInputPollable { private LibretroApi api; @@ -92,23 +92,23 @@ namespace BizHawk.Emulation.Cores.Libretro public bool LoadData(byte[] data, string id) { - bool ret = api.CMD_LoadData(data, id); - LoadHandler(); + bool ret = api.CMD_LoadData(data, id); + LoadHandler(); return ret; } - public bool LoadPath(string path) - { - bool ret = api.CMD_LoadPath(path); - LoadHandler(); - return ret; - } - - public bool LoadNoGame() - { - bool ret = api.CMD_LoadNoGame(); - LoadHandler(); - return ret; + public bool LoadPath(string path) + { + bool ret = api.CMD_LoadPath(path); + LoadHandler(); + return ret; + } + + public bool LoadNoGame() + { + bool ret = api.CMD_LoadNoGame(); + LoadHandler(); + return ret; } void LoadHandler() @@ -150,7 +150,7 @@ namespace BizHawk.Emulation.Cores.Libretro [FeatureNotImplemented] public void SetCpuRegister(string register, int value) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles { get { throw new NotImplementedException(); } } + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } private IController _controller; From 073d36e40f23b878e96f769794043e70c8f3a5c9 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 12 May 2018 18:33:11 -0400 Subject: [PATCH 224/339] GBHawk: Fix Airaki --- .../Consoles/Nintendo/GBHawk/Audio.cs | 15 +++++++++++---- .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index ad756ec936..b404cf0df3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -108,6 +108,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte AUD_CTRL_vol_R; public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + public bool timer_bit_old; public byte sample; @@ -726,12 +727,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk master_audio_clock++; // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) - sequencer_tick++; + // the sequencer is actually the timer DIV register + // so if it's constantly written to, these values won't update + //sequencer_tick++; - if (sequencer_tick == 8192) + //if (sequencer_tick == 8192) + bool check = Core.double_speed ? Core.timer.divider_reg.Bit(13) : Core.timer.divider_reg.Bit(12); + if (check && !timer_bit_old) { - sequencer_tick = 0; - + //sequencer_tick = 0; + sequencer_vol++; sequencer_vol &= 0x7; sequencer_len++; sequencer_len &= 0x7; sequencer_swp++; sequencer_swp &= 0x7; @@ -883,6 +888,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } } + timer_bit_old = Core.double_speed ? Core.timer.divider_reg.Bit(13) : Core.timer.divider_reg.Bit(12); } public void power_off() @@ -1028,6 +1034,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("sequencer_vol", ref sequencer_vol); ser.Sync("sequencer_swp", ref sequencer_swp); ser.Sync("sequencer_tick", ref sequencer_tick); + ser.Sync("timer_bit_old", ref timer_bit_old); ser.Sync("master_audio_clock", ref master_audio_clock); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index 26a3e6e20d..1953dbd410 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -37,7 +37,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // some of gekkio's tests require these to be accessible during DMA if (addr < 0x4000) { - return mapper.ReadMemory(addr); + if (ppu.DMA_addr < 0x80) + { + return 0xFF; + } + else + { + return mapper.ReadMemory(addr); + } } else if ((addr >= 0xE000) && (addr < 0xF000)) { @@ -265,7 +272,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // some of gekkio's tests require these to be accessible during DMA if (addr < 0x4000) { - return mapper.ReadMemory(addr); + if (ppu.DMA_addr < 0x80) + { + return 0xFF; + } + else + { + return mapper.ReadMemory(addr); + } } else if ((addr >= 0xE000) && (addr < 0xF000)) { From 258fef46d7d58420a1e8b15a167fdb4b8ec5fb45 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 15 May 2018 08:25:27 -0400 Subject: [PATCH 225/339] Gambatte: Fix states --- libgambatte/src/memory.cpp | 2 ++ libgambatte/src/tima.cpp | 1 + output/dll/libgambatte.dll | Bin 177664 -> 177664 bytes 3 files changed, 3 insertions(+) diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index 46dcc6e28a..0b92460ca5 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -1143,6 +1143,8 @@ SYNCFUNC(Memory) NSS(cgbSwitching); NSS(agbMode); NSS(gbIsCgb_); + NSS(stopped); + NSS(halttime); SSS(intreq); SSS(tima); diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 68d49189ef..979ffded65 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -181,6 +181,7 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) { SYNCFUNC(Tima) { NSS(lastUpdate_); + NSS(basetime_); NSS(tmatime_); NSS(tima_); NSS(tma_); diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 6cfedd62b455eb9ea2308fcfcb1ca11b220db73d..79c5fccd1b9c23678848457896ba16a1b4358723 100644 GIT binary patch delta 28029 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 AxBvhE delta 27631 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 From a185f334876a52c463427d14f2b6f56b3d4c8105 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 15 May 2018 09:44:39 -0400 Subject: [PATCH 226/339] Z80A: Add a WAIT state that can puase the CPU on reads / writes NOTE: a wait state is added automatically to IN/OUT reads / writes, but I don't know if this is already accounted for in the cycle timings, TODO. --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 1 + .../CPUs/Z80A/Registers.cs | 8 +- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 150 ++++++++++++++++-- 3 files changed, 139 insertions(+), 20 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index eddf74d0a4..5f4ccb2437 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -18,6 +18,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // variables for executing instructions public int instr_pntr = 0; + public ushort instr_swap; public ushort[] cur_instr; public int opcode; public bool NO_prefix, CB_prefix, IX_prefix, EXTD_prefix, IY_prefix, IXCB_prefix, IYCB_prefix; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index 14e447bc65..0e8dde3435 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -45,6 +45,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public bool FlagI; + public bool FlagW; // wait flag, when set to true reads / writes will be delayed + public bool FlagC { get { return (Regs[5] & 0x01) != 0; } @@ -109,6 +111,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { Regs[i] = 0; } + + FlagI = false; + FlagW = false; } private bool[] TableParity; @@ -125,8 +130,5 @@ namespace BizHawk.Emulation.Cores.Components.Z80A TableParity[i] = (Bits & 1) == 0; } } - - - } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index f26582dd41..c3786015a3 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -75,6 +75,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort I_BIT = 60; public const ushort HL_BIT = 61; public const ushort FTCH_DB = 62; + public const ushort WAIT = 63; // enterred when readin or writing and FlagW is true public byte temp_R; @@ -146,7 +147,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // Execute instructions public void ExecuteOne() { - if (Regs[A] > 255) { Console.WriteLine(RegPC); } switch (cur_instr[instr_pntr++]) { case IDLE: @@ -174,6 +174,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = false; NMI_(); NMICallback(); + instr_pntr = 0; } else if (iff1 && FlagI) { @@ -200,14 +201,24 @@ namespace BizHawk.Emulation.Cores.Components.Z80A break; } IRQCallback(); + instr_pntr = 0; } else { - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null) TraceCallback(State()); - FetchInstruction(FetchMemory(RegPC++)); + if(!FlagW) + { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null) TraceCallback(State()); + FetchInstruction(FetchMemory(RegPC++)); + instr_pntr = 0; + } + else + { + instr_pntr--; + instr_swap = OP; + cur_instr[instr_pntr] = WAIT; + } } - instr_pntr = 0; temp_R = (byte)(Regs[R] & 0x7F); temp_R++; @@ -250,6 +261,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A DEC16, PCl, PCh, OP }; + instr_pntr = 0; // adjust WZ register accordingly switch (temp1) { @@ -297,6 +309,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = false; NMI_(); NMICallback(); + instr_pntr = 0; } else if (iff1 && FlagI) { @@ -323,12 +336,23 @@ namespace BizHawk.Emulation.Cores.Components.Z80A break; } IRQCallback(); + instr_pntr = 0; } else { - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null) TraceCallback(State()); - FetchInstruction(FetchMemory(RegPC++)); + if (!FlagW) + { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null) TraceCallback(State()); + FetchInstruction(FetchMemory(RegPC++)); + instr_pntr = 0; + } + else + { + instr_pntr--; + instr_swap = OP; + cur_instr[instr_pntr] = WAIT; + } } temp_R = (byte)(Regs[R] & 0x7F); @@ -336,7 +360,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); } - instr_pntr = 0; break; case HALT: @@ -409,16 +432,53 @@ namespace BizHawk.Emulation.Cores.Components.Z80A instr_pntr = 0; break; case RD: - Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + if (!FlagW) + { + Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + } + else + { + instr_pntr--; + instr_swap = RD; + cur_instr[instr_pntr] = WAIT; + } + break; case WR: - Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + if (!FlagW) + { + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + } + else + { + instr_pntr--; + instr_swap = WR; + cur_instr[instr_pntr] = WAIT; + } break; case I_RD: - I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + if (!FlagW) + { + I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + } + else + { + instr_pntr--; + instr_swap = I_RD; + cur_instr[instr_pntr] = WAIT; + } break; case I_WR: - I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + if (!FlagW) + { + I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + } + else + { + instr_pntr--; + instr_swap =I_WR; + cur_instr[instr_pntr] = WAIT; + } break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -572,10 +632,28 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = iff2; break; case OUT: - OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + if (!FlagW) + { + OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + } + else + { + instr_pntr--; + instr_swap = OUT; + cur_instr[instr_pntr] = WAIT; + } break; case IN: - IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + if (!FlagW) + { + IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + } + else + { + instr_pntr--; + instr_swap = IN; + cur_instr[instr_pntr] = WAIT; + } break; case NEG: NEG_8_Func(cur_instr[instr_pntr++]); @@ -601,6 +679,42 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case FTCH_DB: FTCH_DB_Func(); break; + case WAIT: + if (FlagW) + { + instr_pntr--; + } + else + { + switch (instr_swap) + { + case OP: + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null) TraceCallback(State()); + FetchInstruction(FetchMemory(RegPC++)); + instr_pntr = 0; + break; + case RD: + Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case WR: + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case I_RD: + I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case I_WR: + I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case IN: + IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case OUT: + OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + } + } + break; } TotalExecutedCycles++; } @@ -672,10 +786,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("ExecutedCycles", ref TotalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); - ser.Sync("instruction_pointer", ref instr_pntr); - ser.Sync("current instruction", ref cur_instr, false); + ser.Sync("instr_pntr", ref instr_pntr); + ser.Sync("cur_instr", ref cur_instr, false); + ser.Sync("instr_swap", ref instr_swap); ser.Sync("opcode", ref opcode); ser.Sync("FlagI", ref FlagI); + ser.Sync("FlagW", ref FlagW); ser.Sync("NO Preifx", ref NO_prefix); ser.Sync("CB Preifx", ref CB_prefix); From 1b991de966b46fa4db9a8e9acef4edc41c0ae012 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 15 May 2018 14:26:48 -0400 Subject: [PATCH 227/339] GBHawk: More Hlat bug work --- BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs | 14 +++++++++----- .../CPUs/LR35902/Tables_Direct.cs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index c49ae6b72c..8decc26e76 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -310,6 +310,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { // call interrupt processor INTERRUPT_(); + Halt_bug_3 = false; } } else if (temp) @@ -324,15 +325,15 @@ namespace BizHawk.Emulation.Common.Components.LR35902 }); } halted = false; - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null && !CB_prefix) TraceCallback(State()); - - + if (is_GBC) { // extra 4 cycles for GBC if (Halt_bug_3) { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + RegPC++; FetchInstruction(ReadMemory(RegPC)); Halt_bug_3 = false; @@ -348,7 +349,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } } else - { + { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + if (Halt_bug_3) { //special variant of halt bug where RegPC also isn't incremented post fetch diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index b7dd1ccb0e..b5e005948f 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -114,7 +114,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 // then a bug is triggered // With interrupts enabled, this runs the halt command twice // when they are disabled, it reads the next byte twice - if (!FlagI) { Halt_bug_2 = true; } + if (!FlagI ||(FlagI && !interrupts_enabled)) { Halt_bug_2 = true; } } } From 148f6d4332232df0727a91ca1699930f9e257361 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 15 May 2018 15:50:33 -0400 Subject: [PATCH 228/339] GBHawk: Timer work --- .../Consoles/Nintendo/GBHawk/Timer.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs index 4107a8da63..5f3fb8e9ce 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs @@ -93,10 +93,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk write_ignore = 4; TMA_coincidence = true; - } - else if (pending_reload == 1 && !reload_block) - { - // set interrupts if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } Core.REG_FF0F |= 0x04; @@ -135,11 +131,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk timer_old = timer; timer++; - // if overflow, set the interrupt flag and reload the timer (4 clocks later) + // if overflow happens, set the interrupt flag and reload the timer (if applicable) if (timer < timer_old) { - pending_reload = 4; - reload_block = false; + if (timer_control.Bit(2)) + { + pending_reload = 4; + reload_block = false; + } + else + { + //TODO: Check if timer still gets reloaded if TAC diabled causes overflow + if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x04; + } } } From 6f2ae8cbf2c8fb506ba91e28b72e5f8a7de3b548 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 15 May 2018 20:44:20 -0400 Subject: [PATCH 229/339] GBHawk: Fix window timing --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 24 ++++++------------- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 13 +++++----- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index a3ea968b3e..23bacd905a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -825,17 +825,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { read_case = 8; hbl_countdown = 5; - if (window_started) - { - hbl_countdown -= 2; - } - } - if (pixel_counter == 158 && window_started) - { - STAT &= 0xFC; - STAT |= 0x00; - - if (STAT.Bit(3)) { HBL_INT = true; } } } else if ((render_counter >= render_offset) && (pixel_counter < 0)) @@ -1009,10 +998,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (!window_pre_render) - { - window_tile_inc++; - } + window_tile_inc++; read_case = 5; } window_counter++; @@ -1089,10 +1075,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (window_pre_render) { // here we set up rendering - window_pre_render = false; + // unlike for the normal background case, there is no pre-render period for the window + // so start shifting in data to the screen right away render_offset = 0; - render_counter = 0; + render_counter = 8; latch_counter = 0; + latch_new_data = true; + + window_pre_render = false; read_case = 4; } else diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 13d5237742..677599bacb 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -701,10 +701,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } else { - if (!window_pre_render) - { - window_tile_inc++; - } + window_tile_inc++; read_case = 5; } window_counter++; @@ -770,10 +767,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (window_pre_render) { // here we set up rendering - window_pre_render = false; + // unlike for the normal background case, there is no pre-render period for the window + // so start shifting in data to the screen right away render_offset = 0; - render_counter = 0; + render_counter = 8; latch_counter = 0; + latch_new_data = true; + + window_pre_render = false; read_case = 4; } else From 2144195bb827cd572ed6cb175329a884bbe25385 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 16 May 2018 15:50:07 -0400 Subject: [PATCH 230/339] GBHawk: Clean up some unused variables --- .../CPUs/LR35902/Interrupts.cs | 20 ------------------- .../CPUs/LR35902/LR35902.cs | 7 ++----- .../Consoles/Nintendo/GBHawk/Audio.cs | 9 +-------- .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 7 +++++-- .../Nintendo/GBHawk/GBHawk.IStatable.cs | 5 ----- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 6 ------ .../Consoles/Nintendo/GBHawk/HW_Registers.cs | 5 ----- 7 files changed, 8 insertions(+), 51 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 51c926aec8..6ee944e95d 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -4,23 +4,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { public partial class LR35902 { - private bool nonMaskableInterrupt; - public bool NonMaskableInterrupt - { - get { return nonMaskableInterrupt; } - set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; } - } - - private bool nonMaskableInterruptPending; - public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } } - - private int interruptMode; - public int InterruptMode - { - get { return interruptMode; } - set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; } - } - private void INTERRUPT_() { cur_instr = new ushort[] @@ -92,9 +75,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 skip_once = false; Halt_bug_2 = false; Halt_bug_3 = false; - NonMaskableInterrupt = false; - NonMaskableInterruptPending = false; - InterruptMode = 1; } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 8decc26e76..2df6418354 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -564,9 +564,6 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ser.BeginSection("LR35902"); ser.Sync("Regs", ref Regs, false); ser.Sync("IRQ", ref interrupts_enabled); - ser.Sync("NMI", ref nonMaskableInterrupt); - ser.Sync("NMIPending", ref nonMaskableInterruptPending); - ser.Sync("IM", ref interruptMode); ser.Sync("I_use", ref I_use); ser.Sync("skip_once", ref skip_once); ser.Sync("Halt_bug_2", ref Halt_bug_2); @@ -579,8 +576,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ser.Sync("stop_check", ref stop_check); ser.Sync("is_GBC", ref is_GBC); - ser.Sync("instruction_pointer", ref instr_pntr); - ser.Sync("current instruction", ref cur_instr, false); + ser.Sync("instr_pntr", ref instr_pntr); + ser.Sync("cur_instr", ref cur_instr, false); ser.Sync("CB Preifx", ref CB_prefix); ser.Sync("Stopped", ref stopped); ser.Sync("opcode", ref opcode); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index b404cf0df3..c272d52839 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -107,7 +107,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte AUD_CTRL_vol_L; public byte AUD_CTRL_vol_R; - public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + public int sequencer_len, sequencer_vol, sequencer_swp; public bool timer_bit_old; public byte sample; @@ -729,14 +729,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) // the sequencer is actually the timer DIV register // so if it's constantly written to, these values won't update - //sequencer_tick++; - - //if (sequencer_tick == 8192) bool check = Core.double_speed ? Core.timer.divider_reg.Bit(13) : Core.timer.divider_reg.Bit(12); if (check && !timer_bit_old) { - //sequencer_tick = 0; - sequencer_vol++; sequencer_vol &= 0x7; sequencer_len++; sequencer_len &= 0x7; sequencer_swp++; sequencer_swp &= 0x7; @@ -941,7 +936,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk sequencer_len = 0; sequencer_swp = 0; sequencer_vol = 0; - sequencer_tick = 0; sample = 0; @@ -1033,7 +1027,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("sequencer_len", ref sequencer_len); ser.Sync("sequencer_vol", ref sequencer_vol); ser.Sync("sequencer_swp", ref sequencer_swp); - ser.Sync("sequencer_tick", ref sequencer_tick); ser.Sync("timer_bit_old", ref timer_bit_old); ser.Sync("master_audio_clock", ref master_audio_clock); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index c7c5068f87..14570a59f2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -63,8 +63,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (_scanlineCallback != null) { - GetGPU(); - _scanlineCallback(ppu.LCDC); + if (_scanlineCallbackLine == -1) + { + GetGPU(); + _scanlineCallback(ppu.LCDC); + } } if (_islag) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index 9268eaee52..c7f2f425f3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -72,11 +72,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("REG_FFFF", ref REG_FFFF); ser.Sync("REG_FF0F", ref REG_FF0F); - ser.Sync("enable_VBL", ref enable_VBL); - ser.Sync("enable_LCDC", ref enable_PRS); - ser.Sync("enable_TIMO", ref enable_TIMO); - ser.Sync("enable_SER", ref enable_SER); - ser.Sync("enable_STAT", ref enable_STAT); // memory domains ser.Sync("RAM", ref RAM, false); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 3a0e26ba7c..ed6d09a237 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -28,12 +28,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte REG_FFFF; // The unused bits in this register (interrupt flags) are always set public byte REG_FF0F = 0xE0; - public bool enable_VBL; - public bool enable_STAT; - public bool enable_TIMO; - public bool enable_SER; - public bool enable_PRS; - // memory domains public byte[] RAM = new byte[0x8000]; // only 0x2000 available to GB diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index b4bdde86d6..a3ef71cad3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -373,11 +373,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // interrupt control register case 0xFFFF: REG_FFFF = value; - enable_VBL = REG_FFFF.Bit(0); - enable_STAT = REG_FFFF.Bit(1); - enable_TIMO = REG_FFFF.Bit(2); - enable_SER = REG_FFFF.Bit(3); - enable_PRS = REG_FFFF.Bit(4); // check if enabling any of the bits triggered an IRQ for (int i = 0; i < 5; i++) From ebd6408315a15256d700e4719702f6ea02d31ef1 Mon Sep 17 00:00:00 2001 From: nattthebear Date: Fri, 18 May 2018 17:35:28 -0400 Subject: [PATCH 231/339] add h, v regs to snes regs list --- .../Consoles/Nintendo/SNES/LibsnesApi.cs | 11 +++++------ .../Nintendo/SNES/LibsnesCore.IDebuggable.cs | 2 ++ output/dll/libsnes.wbx.gz | Bin 360173 -> 360119 bytes .../cpu/core/disassembler/disassembler.cpp | 2 +- .../bsnes/target-libsnes/libsnes_pwrap.cpp | 11 ++++++----- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs index 4f61f1c23d..0af74a5e41 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs @@ -215,6 +215,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public uint aa, rd; [FieldOffset(28)] public byte sp, dp, db, mdr; + [FieldOffset(32)] + public ushort v, h; } [StructLayout(LayoutKind.Sequential)] @@ -300,17 +302,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES [FieldOffset(176)] public CPURegs cpuregs; - [FieldOffset(208)] + [FieldOffset(212)] public LayerEnables layerEnables; - [FieldOffset(220)] + [FieldOffset(224)] //static configuration-type information which can be grabbed off the core at any time without even needing a QUERY command public SNES_REGION region; - [FieldOffset(224)] - public SNES_MAPPER mapper; - [FieldOffset(228)] - public int unused; + public SNES_MAPPER mapper; //utilities //TODO: make internal, wrap on the API instead of the comm diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs index 37ad308671..7736c32b70 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs @@ -46,6 +46,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES ["Flag I"] = fi, ["Flag Z"] = fz, ["Flag C"] = fc, + ["V"] = regs.v, + ["H"] = regs.h }; } diff --git a/output/dll/libsnes.wbx.gz b/output/dll/libsnes.wbx.gz index 67a8e75a6a094dc50f029a9ff50e869546018ce2..c6442082844979109bc8e87cf655e1702acd587c 100644 GIT binary patch literal 360119 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

  • 95TtDd$jm1@GY3RweVUuEf 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

    MO4-w)w;wSUxJLhyRsMT!F`@r|P^`EPPq^NF z516qo^7CD#;?1Ul6=q#dD;lU$NxV4)M+ktpyV&kd!Lv!u-B~IwZzvUa?I{)C$G3UM zYo+4RcbB$-S?ih&*=0M-pzI)=5Foi+5$O|< zC{cgar2c6`l+-_DeTU)C0iz3|6`5W&`jCMc zy{jr@^!;V3(V_lAH1nn0wrJ+BI3e?A@cs42*gqb0vboY7G`V6Eevt<8p8a}myHC$- zAI5F}kZfe{O#ow|}o{|F%%}`(Xbz*#9~V+{3c}RgL|-31{|m^nI&P8(`Z9AA5tXBwTP`q@K9<#^4$a}X8CQ*a@y~G3%7mY{UO^2Xl)+^ zB#{57(EheBkH`K$l2oJzxddJcl>$r2Blm?oXv#7*B?0?SnfsslWpw{Jd^n25&+53v zPwJS(pSVJK|JYJZ-v0{7Da-L|4j)gBkB8JIf!jF|c6PiLz;b~3lp`T_IuQ;Z3K2vi z>^&MH9Q_#x?`kxDN(7Te;%`J)LFXO>`b(=R%T~r8sO5Oxs4VJ_>_ayr4G>?WOnnkQ zJ_fQ7B(p9@`2L*Qs-AIL+x)(6`B-{>u3O%ho_;7Yt-o-9Zn<=z7Mii#(@>3d#1xq| za|n+{?f(bZYdCvWkPZD>{u7)(CX&zXiQI2ftTc%0iW4eu_C4?XtW4Bd(rCv@v*ol(_hvy&tvDlw%`Mr_Z z@rs#Vl5Z&crM7OY!+%{)*d_^0%sS;=c4ms=6W8s6=M-FrZsz`Y$`qmPuw~&c1W%)Q zeK$dhvP>A*Xx}6F-qPL&EzOo@1@|;%i|eMD@r7eMlrL7a3sh5o1{vES8@iYR0dC&- zqs!F?y|Dyw-4__3!o+}O;Xzw>ideCf0^J*r--<>nJQyVa!B&gwrD<#Uy+=2jOI$xT zW8J(pLnvPOw#7Aay2X`hT8s{+@V4y^UHpnW#C5Z%DoiT{&8bkh;E_>P6|H4=!^%GP zF9gT5KI|A7eCw1fOOh2zoTM}WDBt525LrTvr`wd!;OlT@q zW-d#jJpjCLrSD6@`_XLO>DKYK?rgF0i=$zt8Um);w5GleA;cpfo{pbWeU6$qohHO% z;cA7B-=n7VQJRoJ2MZJLa-g6pr$L4&24FrJNT`0Pd3jKR=ro9)11>JpRI?}z7|$WQ zXQg)uI~_(tTI~Hkks3AUYXn$n-U4Dhr;(oPy?{uMvG^n9E#7{_yi+4T*6TkE(hX7N zGrjG^yn=i=+Wtp)G4eq;yN~rwab4pfx^eykIfX5Qp^y#2AI z$}rPzM6-A4cX_iH*hv{0U(21*U#z4{IdeYEq4vXujtchL2~<+67vO=qX>q+c7mzh> z#Vx1~ci;pRV34|BPXh^r*y`-p;sI=-z#7^!;NrW<#gmZB@NIlmO-e9NO4M+^IIQLL zp+`#>#vLXllx-2$F?}6*4cl`LqEzN&+x}4{%GQlo&2$v9#ez{yo}8DJ#L3H zUtoL2z$y?ZW%YI$PXG?F!NC(jOpK3EfSQJU~hLjn48h*P2_(s3*^f5j}Sx z;ReDDgim9*k#Hm75^$~>O&W7V-y0ykY$6F0Nem-2j?g$lQ-E^g489-u18_X8=)m z_n&}gY4ES?FJYeHnrR>haNEY(sZ{TiZ5wwHz^52&tETA6%4{3o4)EUPy!%*)?Tq?8 z+s2&)lzj(dcW(fygJw2nT+ANQ3=FPHvukY|-Aw)*+r}MCerTlpWm@?IwP3%dWl!c0 zm=}@?+8?RQ5*BwUACJbUk8_%tU%JQcjWq`@w`5mAS6qf-qbo8wm2SpMz@3=>sl*1O zaIET;nL@afIa2BaE(HR0IV||H2LXb`I0%vgl;AW-p$5QY&u$X~F30$)&|Kg$x`{t! z62^=0yrRfu%$N0rS7K|`=Q^!%5iwTYPL&509M_G{t}9GL6EFo8qlC^WHS>{g;s(aM zdS?vzq>m1qWGV*`P-O#T3;KIxHYSFfYwb(o4`}aSW~yvQ)$fPU`%u}Af|8Z3N@w{e zy50FWk00X|<$m1)c+wrpK5U(D*5%PzWY(LNeTZu72;I?qP|gPb234-CvrgBQuPehD z2bh|QZA9!>t!a@w=`&M)b_D0>z7i z3;dnnBNnF-N0hEi(XIN(;xvQnf5(};4XoTZmhPnYecqJ8flaAn0U`I&Lb+F3rRm=| zlUI|4a7eydi+<#j{kB^{I{8PWrHBTc@XQOWNN!8GQH4)MKSFkNBtPjeiVYW zRmbMf!C!>u&raaXzP~;e)xJ&O$FT2NuvGgXSp3MK@NCieLvX$+c?GF^htF`Cen4^W z!q;Ff#eD`o<8rqxI|qy+lG+&_DY)40n0zrsx@{?#rgzp1qP!Rzm?qNrgVR(4W+?sY z_qgJgT|Z6PO$Dt_4Kj>pVXX)Z**s-g#!-6$_0&EfH$`?Kq5) z$-

    ivctB~BR_sbTLfK9rKD2WTCiC*mP(UM0yL$5DL+ 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 z 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

    h2)g-}+j}^hT-IJ)rkY^)#AXpULh>fC*i%o99 z`%#?n^?Otco(p9}3o1+&z3d)#9)Pp=7MMtdz*oTG78LJ}L%vz|JVeY!nOeD1^HWO^ z4m4*7{GFPUf=I0UxH(6QlvCqa%I*OvwFoKuh?HB4*x6L{S5kgm#8S?OBT^0$DQ6Z% zO8G`nNXi$Bx{&fgMB*M*3Q6g?mZhxfCSN-$Qc{{l;Zhb}%i>)kQl9^pQl21E4h%{; zQl#v=hm<|Sq>SlG%H~NV<-5rw4>rL$WjOy}@#Ht5T4S}>e_K3=7f&!ugu3_6a`xN;Sxj-eV8;k0i z-uO{@*8rC{G_Nkz5#0JFK`=@D|_a{eIF_t#2D3p*DRT%G>d7B+# zUm8U-kXQnWshWIr z#eEn3pkf)7LW4uUKZF{FyNgb7XoYtoTjA|S`VN>URitqj7ao*VyhE?xj*ipa9d_OE zRF%6ZHJ|ET@0iu>D5*svh6O)a4b#XjziEJ`r*G2lTQ6$Y>`dEm-v;VsYqPf*-LM-P z*MYy^oYo)C_~t1n!x_Iow~n0)q3i?3LIu2y4%Eu;x1crG+f|FGIX#T@25!%@+@4s~ z9#NnHhg)vyKCn$EPPXiKdg-yX6bC|~?M`XG>Djh-75ZbUzH_=QK>Gp|hK zlhfStX>2{J%{k>~(^%#JQd6NKDC-(BpX*~_alZbDF{dCI>cAuITZfphM7@_nP^WE; z9uTnINUJrhl|c=cFV;XAC5g3sT~Nzw^<}^M&gA<(s=SRDMB8QzBM)5%Mp3smsh~lF z1DE2hjY#rtw%@lw3ftvO*|4nHcO)c}^HkVNs1s2z1mN1f*)8X%LHnpR(U%ZSPignf zLZZ^|Gs}xY8pV=X9jIVI;P=rDYlH6HWQV3m$Z7pOD<(tTBWc!C> z$gJ{k78>XMDbBXcmyNSuTMZ_s#mMNp3%Tg~vxRCNdATq&$7Jl}0!vka4K19tFp%~7 zu&ntlob^1FH330Pjpr6dPK{?!jSu|6#S97*b3|ci=ISeHNrAKj!_uDpgUj4JKC;X; z2>YJh&d+Tfub%svJtA6fX~mN9BH9bZUuHHMV*AaKcY%}n}0Gs1EupU+o!Un&eIr;&$3fopXRL6n^N<4 zT$q~g2CyOe1VqECk2sdThK*9@oS2B<{s_7=52Z)Qd=7xe=(ApT%&gN}zR^AF?e5Hu zdyojQL@`o_-8R=zY#q#KmvQ#!MABPl=_j<pV)}&$&YoUn=Ju1jt`sPK;&H3OEGB zPu$LKMmb#cMkyT3^twq-()n)zHFTQgRHC=M7soj0thvPa+&fEVOq$u=?9 zn#NnjCjIQORGE=+yG-x>P)uQWi1h0TiNMj+v6ya;MjR1|6u8{EvIcY*bMY%j&$zse zJzeEDzCSb)Xu94G4}X?{%y!0`=K`{4>&<4SXV|Y&!2M7%AIa5T{ZwYn#)m zGo3hSsAhq;dA4+;@9uirqA`&oLALzq<>wr0r}c{kQs3roxwlc@&EbO0{+ZbbqPZAwK{CQieMLJm9?cmsasBAo&~F89`gqp% z^RBhqeAZ$!deDJL^=wRb7>li?W^|^F%J-~87zfOm3olL7V93!uwaciC8nk+}19slS zp?lZGIc+OZ*EDJY5`DV$wF$^@v@D>#uq?+@3*k$qUU07Pb|iQo?R)QrDEAy5 z%NVTlPtY`k9+A-J2wBy+WokeN_@m8e+@XfY;h0s8P6NZ~Zo~C4kWTE5j5CVY=a%KC zPShRFCEmtZmv?Qn>lx45AugLY$M&^Vj}FHS$*X&WBL|@zuZfMQQntc2H8F; zQNxHd7`6tze7G&!F|%(UWV2zzcv-8rF;2IEYBart8qdmLIX~lR59dYp*;s(!-iEs< zE>4?pJhmJx{Gk9>f_d7*+|KbRN_^OaAR`(TMY|BCu7oK$I1tOZuY*?!#V7VcqgR;2 zqx%+M+}4==K}xt&=UHBFYqWFdN=He12Txopowx~^=P22PpfAZ!C~)!JWTEN`Xtxj& zoKEq5{4L*mHX@Xc5G?+VUJ3xdIGzAtOfLd}K9m#ocmQ0ZPXoTi|DE2l0(wC9f0x+5 zjB^+SJaCZ4#_U25Vid?a>lp%u4Hj%T2?=DYb=(yD)0Z#?c;T=u!46-B-wD@qOQMEK zcMxa0>lU%w@o@3B-SMM1m|;?|tY-$ethXb^lw)5u;?XEXvu1ENqx@SD#1`!KtKe9? z)PfLCqHicQ^HQXO^YJLMx#yC}xt?KwE5QGSsT0ZmO$+b|mBsI7F%IC}g5_Ar1%7+I z>Bk!)U||V@kn&*`O8=Zt!GuVp(}wkZ5CBX|b_CJR>8T^}B%RInt%`XGY|(so>WG;d_@~F9*IbUOvj=z z?WeEU>9o9p0Rf^Ymw>w#i6%U5*C2m&L^(|12iQ8H8C8(%9*7B4|s@ToTUQc_=?3~ zu{Xhm`v(gy$XTD@f}90LKtmi1bu}QQczI#0=Z|iVS%0{^KSu)?=rnem=jS1ScUzUq zwvCYiYAeV9i$9cr;SJVHu|9yq_ZKDtCt?mE$Tn=1dU=+Ou;ENJ)dp;^RYSgd#)Fe| zcLkAYZNflw#7I97fSWe{Fd%?5vdU<;nYv}Pz}KPar6c`#x{jYdL3f|7O*odG)Xbrh z|BfUQL_N8SpKOJ1H=Z1$(f!_qwj3Pc07nvB_xXhgzjhK0dNJ!=t~%bug2gdwbZWk- zNB5m*+i7}mQ}}`1tQ~T~=0wktb^&Pgq1D*jkU0C%XgrkX_ZwKFlD5J%)pZVn21*&tNk&Jw-^Em`JV9 zL$YVxPFv`{k=cJV?7ja@sS_dZLLe9g7_&-%1JOet&l*sQfJVuLk9qKOA7^b zoe`v~*M1BgJ-<7j5Vyuz`mnkOKVu*#^qz|ZfSrIIF!UmXLD08=J*E5U$wRLf!0iQ> zkc&q)9s!v3PekPa!Ym4{QsidN(VsMVkl0we%p@@@|L;QLGOJhEuHD2JVewb&H@_ zmmUX-CG-Upif-sk>`by;wxtS6cLbrdA3JiF?ID(xx3> zwgL%OjZ^EVW$^TApygA`3MNmLlww-bSXKZ)_K}I<6lyps$9`ih`X{RxOST!QDU9Q< z94Usr?Hsaax!8~5M_(&YJo%gug&G4tSkrJM>0d!4-v1hk5X^P37j6=TkMxPMaRODgqTIAqY{X^P{rdGOSiJ8ZYll4Rh2i9k>)Z zjc<5i(aI||g&XLy7hP7F@cI8;jz8+Bo@ohq|+O7#0 z{U-PfYng8S0;BJ}9-)A_e?ut<1Q}JE(^|GCqnbqpDHBIaLLj!sApwEP5T9Z-=!65w1H<@Khs(QB_|%iE0iKOasTR6yZ3+kck(6qxM%$OYl7R!I zXEP2gH*CxG@-u@ta6kYDQg{n7BS@u=QB(>a#~a_zF%wP~BuepPJl@23Fp#-as^U_3 z=8i+KYQ>|SS(M9D6~>`<3x_)Y7*eWYwVV*mRlK6_I6if;Z4nYxY!ectb{y)UK|-Py z;hjySk?E~Fi%Hb`SFu692Z#44n1BpgauzEHDAa}f?<5r~<7~~WS*&Lh5n+gq@sQ@P z=&&|phVgEMo~5ssO?4)w0Z5_=I(9m#&!^oDW_JPrE#O947ACx32(sPWqiV&4v0y^ip%5Gv+~z1b4AhN=h=AM}y2??q zA91-6%;gTcQE|C~?h2pd+?l&?2y!`uwju=bR@YN;xk<^yI@ z%=tHT-iJf<`%+AMJQd?8*%D9BPeY_sZ&w9GxicRORgci!^4!+_<+-2W;*$b5sEKDR zJbjvZ;`!EtnLqPfO5sJUlow;Gqw8(w#K^?*v}+FrS=LV&cbGQFfB& zk-+>>8pa=AM50VQv_NmV@^YCfM1-b11Ytu1G%!e+DQMmiE{R#D&!uQ=$}hR)(-nmr zvly;lb8_jy7o{$WY{#4oTLsoz?Cd2DY#2WJ85#vP#xg5 z={3Y^h3e$8T??rV>}!JiGVK&nD}eUjSM z-w7TJpb^Bw8+^`lCn;plfbC!)$ro-?Bc8J2{Sa@wt-dgaT0Y%{R z!VtZ6TuOB2N_F+4RUDMn-%t4_*EXL#{ z(rlT`FsY!U0NLm?NFD_|$L*dh-TPAyK$j7}N{?Seo!ZU2Hj2G?9TZwTzzsw4uI-5^ zJ$`PG9jEE=h7c{A5Ur9BEgOiI-L9F2b#V)P9U(@XDl6n9WRC@+Ro;QzvBrKTxBt~v34x^aRKFc4cD^(oKg47tH zeHI-m2&FdZRFT+aftFm@2b+lSI7+`cTv}cre6%0$ALg6=i+KO=Tc(>WA-cJZ_YWiK z=47IqUv=<#A)(m*XAlRg1nejd29C|#Y8Pa525*Vk&5NY3N>5iD?2?OkIe%bIXb;bB za9l8LS&}j5_G~vl*mkz$W={G(^KPE;yYBw=Il@wFY+b zT8V|Z$^-`as*a!|lQ1D)GSx0&2x-#7c-JPI;#a*QIN&`xOlev5u+D%tvn#pCr^x!O|s`Cgm`@)Iz7rq6ki~-=7~+V;!e|^HaUXI^Q_&|CToz^Gc7M@ zuQfP2ZX2SEGJR<9JSm=yGM?oqP^U@=c*QQ&@g*&VLdTc2f#XYL54**%>%egHtU#!< zdFFRA&%p=(tJTnhytmeujw+8GvM=VD7BJ7`z-p*wJk~cOc{QYkRzv2Ly{(7-WbI0z zqO79u{DUJP&}cEF6_wedC>3E5)#MD#8028scG@?LtZau?tD%9Ki74Cm({jq}REO#m zN_L8Y7`7Co43|nV${EE%2owsY@4owf|Cs^IPI->}Z@F*z-+SMG?|t{aH;s1@z0ikr z5&F=_wo&w<`h)jFAKE-5_N_Nce6|ukCxqd%Ga~|@ryCictr=nXJbS)COcowhdMR@( z$PmaJv@&K%V>o=i&;nYljMAY~1WHcIYHpf3xQ%cGfAOg2o9-}I}}qxcPM%k)(<4VjLTKgIphdsn0<~&hPm0X9}Kg` zF*d_|+Y!nzuRK{|^QDtV-Je+Wul6S_Vuhqk3Pohs#Qj6$hJ*gE{fU=n|L6S){lD6u z&`02Ls(lnZ+7kc6{)Di0Gwv45j*wojiD&86JUdi+O@8$pmR=ju8GYDHEIw1<@mf5n zrX`@j)08xu_5NG9MC!tHA;69o_GmZH8^{+U8P3XPWD^vnG5Vo&Hb$pYVQFW!F_sD0 z7b9EpO`DrFI`;qeC!Vcw&lrT^S38TD=gSi?d=i?=*?EVB(WwKfH5!BbBx+`d7krxp z-vhsL^+ek|b-b0>1{>51XL7HkZA=1T6@j1NOseT*g-UKVuK4umV+BI%0TjY%E8#Rn zPr%tgobucp6{A*!?4BA@lg>a8h9YUSC_H*0F-~wyP$d%;x&gQ9u}xLF98F7-TK$e} z=B(00^+lRQXfFcHJsr|SW%YJ|Z#I(hPMjBR$06#wPpa=mTThtU{)SZBonrAEpAd>w zT>`6}ZlQL!AEx+Ao8W98W}X?NdgvbgUK!lI1@b=B^%?b*i4 z=z>h&T?bp-8L(y(@c`Mfwci6ZSk0O}J6pA9-;QySyEruMEo9gh%4(j5aQNX@Fv6Q{ znS9>WSo#`Lg!i~Tt&MPe+lkUdAPGv|iD9?C%aI}ded&zpGeSm9_MgCeaUXzt0j2Na z0h#NG(-@YoBAN}B%Eq+RMi9dgO(=g5L1LAtq6kFn+e^Uac_8QCNobJx@>0oTpFxoV z#yETSPKqYa-dT(A5t)Aid-l%7lPuWl#wV%gJJq9|d-wJ1%5Q{k`_y6f_7wv*?AM>A zeG&SzhJwN=aFO6b&`s7Sj1(r9X`DGqt?(bW~~ZipoZ0P{BiR!^*5=HBwcjT{e< z2$cKbn-o~1s;sx+qb+vl@>zp`woMx&m)RpZu-(3IkCRk;q{qn=d#K0Bj#LTgqmGaC zIAKQ@X1SH@QQUzoLm+#nYInA1c4yT0t!j5Bi?fE~$+2QxFXh-zF)9akhH&6}Mwpvd zfsx3Y#h{O>9GkV)2wXRh^zS{iq`L6Vtn$Oi-aHwv2nMWd0{hkcdUpxGUUKX0Cb!;c z+{tPIzD&SsYetA$@ANRYUO1#2?9Q72XI@=_^yO9YDD23^!|cziBoLFWC5PTSFnBvh zGTDH!G=Wc|1Z<}-CMG*f_?v}g^m#0aisRW~#54{^>8@Tj5Y!IX`4@aR5aeCgh!6|=ky$JN4gE!w~P3nh!3O#u<1D-A3Da` zaJGNiE90n{E=TBq{hnBv)eWTY++ng5cD$> zdL6W2OtCZhBcjKXCSj>wtOx?LlMMs%kbvsRLW(mLXiZ5PBYP26n{CD(4-NoUe_YWL8L`Yek)-4CVrG@@iyspeno4faKLmE6w+XDSYJd zTs#5bYLMBLEs)NBn1RoOmo`9EBgj}r?Tdjo|^!wzeVD|D`ol7PGbI0PW=hOIq zk+Jfe3AW6E2TVdex4G||;L94X__AV9@*F)25df_X7L-(gHaEXI%eh348cv#5p6HY{ z#91?29-PWr1gePhWksWkSdzoC#%cb3+Ih0VOKBwMdbVS#j1_C6E&w_!T9ggm!5HrV z4(koz6j?8TXnZ&dAVoY1clZ)-6gHBFY-Q}2Wrcq0ke6tzkFAM4bNe#M(@-?C}vwsnynJ8aRnaJ2%K6W3Od(B~`@f!t| z1uu?s;E@gdhN;u=K;z^4hw(XN=g9H#MZVg$E;-(of2{BNbrJPhN2zbNAkx|!b3Sgt z8r`3_mq*|}%&VY|dgP6qsE!`o1EbEiFLEh-)(G$A%SgoYfnQ zvKDW3&cU!MgYb6D&2J-Tw}X&59(p4*U~74NlhUNLW#_k%3GyH%0C^3EnCWLsZTNGd zkZ}AL9Xk}1!V1#LkQq3?L#2a8lg(svW=b++CDH`hpWlR%X;liD%YT3$C<&_U*v*$i8V!-Mlueq&vLisVQM4 zjg_HApY!i)K!#@QV?erggino|UT)RVL&T24=H{l~M}IMgXe0Bap`c@o7mMrNSMRRc zSMQQLM_ZWBL7gb>5OU^L`YT)z+R2BkqrKbzMyQ zi5b2|H=uR;UKEtV&)jg)5lOnD2@nDb5WrOcuI+GgG+l`Z-z`#b_z)ehz77ev?1 zu>arNZ_=Uck%%I19`X=Ng*1*k^*umRL}1{t=;W9k$V8T)n3!(c^%S>M**&58Vlo>up$hAj-xktDfG$bccc~c#pg$7JrQ8aq-Oft>N zhm@x!Iz{43;fXhk#8Od9iJtB}AW_Ec}!`D_zaaG!s8=)1WZ98M(Pm30si%e zp>_(aRRc|Gpq2vE#zq7JcZ*(CB1periSA+yDiBMnR@$8A<&=))`Z}J4F`ibhU+>!T zklXMy75!cmeT~e(MN1%@-@?1&$%*I!xZ_jl(4-&E62%yRnR->Ri8&UMc_#h=*@l~{YpkxaGl|Hm{f!K zhu}6X*r^3OWUxTnzck%@I*l%p(RO@xW^2(740-9HH1)}70#lZ6<^wDg^*HcdEPqkl zSi1&4u?T?dn5Xe?42Kn9&lzW3yC-vv@nJWmDP9Uj0Y=3Gq?SH-06vL?wq3?YccKC; z02|$GEOy^&9OVhidQ3X#aGtgFQw7*-8t0hd%`Gp9y$!4!ckqxgDhFnk;sKp}(ZGih{czEx_~=LAemtjO@4MAiV{zyG;m+#|c!op9gYvpvHAl+4n}?aa zK_kDE$d3=D_iOZKNx#QcGf^g`gwbOS{LR?ag+viW;hlBewoRM<#uOpee!#6Bl1ftB zYH~KmS}39(DhMK&h$yJcD1?4?t)^wjh<+d{h@gj{*Lo2g`>~KD%=AGhUC5n9Ek((7 zsiyDTS=ZGxd|3A0x%ZxXzkAQ#Idjh4Z?M!J=)eGUz+>saFdrf`v*A_f!SC_K;cSMSiu z>!{y^4uahm#Gpa_z)cSa!g1^QUsMy@u4Qp&La+*jXdbji*ohKEJbI6&M4jDfi9zM0 z7)yrNl0gDu5x{TCd|Hm(kP#~0?9Znt`$Jp+o#!KbnZj*ci?zc5R7VxncaHVeT@0PZ z9#e>r5Pp^I$U?q+4|b$;qggedxF6znCDUfdo^d^wZPK-+Rv^rq$IgxF#Vng$HXz_9 zP=_2HX!W4Nfl}*`gog~D3bGFZ_-uizNekI+9=7EU=UVmG;Mo8@@}UPDGj1`M_df(8 z$FY%>A=5i2yzj_RIu(sD?&+yShKxDx8izc^_ZSr`Q8EqgQIu&IEYQ7vmT?;k}$ zr3?H{UVkZ+Av6z@1WMW)E_{=N%NvJhTdS!^(?8a}iigD#;Nq)lmIuI{BgLU%hn!+P zj|woKnTHfZr@wo6*r~sQZ(>8ALu3p~enX8T+zsYRII4K^H2Z5i`A|Kz?JaEBJxhk&xWUAt6PH8NYW+F2zK!j*QIyCb*dYg ziqf5Sp zNO*7ynTv0AeHEIq@?SZ$X>Q74_sbgqNOolrJ}lF)04Q5jOOpwbsx9jE%mQ+}J+A z0z~*bn_?M&V|;l=&g~Jp#T}-;^Kg*IaY<*k;$`-JoHX075X>Vb=z@q%!)~*KcvDr~ z>$j zVxZa7SSV(>pOhOkg+j`O*HZek+Nf>k%67P_3)CI1V56N?IFc?kRaI$nIK4Iyq!x~` zl)VCPD~gOy50mt_SJ1F7wZBcmTWS0U?>b{fct1p^!P^#1$Ga_>j(0Ii-Q1MB)Ji$&pT{Q!o-~UW7j)Tf&3< z4W5sO&Hv+uV|`;qz`h0@AI7aO<>9JeEKdF;lGtQErcoZ4vgEb&%-Nd$O4sNA$-RMW z>u3KyoY{e=t2py!yS%?o9^0dyO|~Bc!n33LGrD~8h20$jLBcygvp_518Ne&xC3paC zf!l;Kf%y{oT^GAZD}=e!a%g!lr&w(0%(MyJK57j;Bcbyr}fy^TT{^4_Ol`0zXA7G`$QXqh1C4C9$2T^Bbp!PQ@cU)!fMpc(kfx)Y6ob=up+V8?#zSz(zgjxkH2n(oP zrnSS`)Q-~{V6W8DX^&y|#bUd6Y4>u!wfkv>bh~sv-p~7E?^NrcWx_JlUeZ!wDQb6U zi7>VgpeJwS9Y!yvO{*pCFEM!A$#xakiD|Ai>#l$ zHyPKsxK_xz_V~G>>wLbS^T&D4=ihg)Z$0&#I#1FDuf1U_8hL$-!~i$SZ%R?=^U z(elgO2cNjPe_RgET^w_Nj=J}^FZ=snkNvY9@Y{8?OKdCr7D3z3HqLKb(AKkc^V=e{ zc~RLv=c!=kSvnQ_7n_{uZ1Sa`?FU;$>SFLopCd}Fi0KK>>DKiXJhUroXP zPi!;VqQ*vGr;9B}`|)DX&WDW^`}MK25o6_D;UXv-Tr0=BN$hY8O9J{z=6VRMMx zLwnlT0%ES9Cnu2BDBv>2kj!*E@FS)cXpSt zyQZ)|4R2VcT=7;R=_pO5qGF0qYiADjx>1F)0D=A+$Y>}=R} zVn5z>Ea6UKUWv)DSc%Z=R)J6ddZ z+8)MEfo(4K^<8Jn8QUJVkk})%(Gx*i8ut6Auw6iV%GkeSJbOuO58AoL-hy2twkmB0 zW4FK#5u1^=u(4xc8;X4r@9c-;=KhDxBX%F{A!7@}e)t%+Gij$9`(w0cPl@eJ+rrq( zuuH_2r_E{XTG+l~Q`0^>7PLcQYl*#g$Jw36HiFG6b_eZ9WAnnkObXkHwDpWl8s*u; zV%ySYGWIm=Jh3Hc?;kbyFKl1-imJHZwcd*VN%b{^Vu9LI5- zkdV*_xtTjj2%!-|NRH(gxfz>t${!Tv&M`$%Qd4BZ+;h`@44ciBiRGBN=H6!3Cgynk zUVrfF)wKT~e}4XWe&630?fWy*zp%yctbb{f#8!Y!H#>`VmDtP}^sm`ow8O;4!^W7c zNgE<|1?*t6Ik$uM*=gxt*hXfPXcNSS!sflT{-s?iwqyzV*X(54FtHi1JI!{Xtu8hW zHp*-j+6PI}zp!Cuvu*`#oY*0-A!ZY4BgHm`&CRv`rR^#gM$qmM8x7mT>^<5kV*A0?FuR|&gV?&T*>9|WX^T%v|1LuRnjJwK5PK1J zz1bGDTg7gJjWAo39<&i+XTo+in?~DCY){xqX7|t*osj;8efZk?m-dR-=L^xlW(U#6 ziaiCp(riQ8Nn$s^jxqb`X3(|~I|;U}*#K?fap_;!@2{+XX_LiPhP`8UF6|bvj~1YR z&Gx08DE2VyGP8AP+lpNUJIw6sG~V-$N&mt&GkcCUAhsQB;Y;gZ+D&4A%t!y4oklxe z>|NO1X1mk27P}92q1kG*Uye%u!VWO|G?l$Bb~tQ(v&U&Si){}3;f3`t?F6x3qtU-+ zC(yPLn+m(#Y$w|MBhtUHbIewxy(D%KY;Us_FIBW)o<~iERM;GROLtHdO4# zDDirox5&TPRo-k8Krh3#ne8f`1F-C%!ZTmRDLACmrs zy>E6t?Rl|J=AwVi_NQGh_89DPv-N1lid_Rc((Jnw-p|C2hizr{67A=M(!a39&#ZrG zFNm!Gn{IX%?FO-#k?3Euy=cS5#>2*#tx4NV>0j6|vsqX8CdCec4KbTYyGCqd z*xV=9zqF&o=Fdj|nhmFIDmDc+)@*y)4+o@wVP}{v4e+0j*l5@uX7ACi726NChS~kJ zqs7*R&3^o6Qpdeg8T+fOz<>5vDS>kCuOb3v?60N={@q_?CjZ@Er6!kufAyQaD~pXO z3d48C#k7`dUy_QEOw!srC`v82Gv(fx;=!6&Q){fDMf8EmOzF)S$^+p=ND$&dEYZa> zGnlF+v~+6M2r;!2QTzD*b8g$w#`bQK^D@F^DkRj|I zn$0+v%sI$L{oH5ka{vOUTRq5#YX6z&PJT^So^+w^#jFbg^dHFUGQ=pRF6hs7!Eh)* z8jn-&N4s~$kf;ywLGdYkY=%z7Kvd@Wvj*ea`CjdB~x8fEL0vuf@!Sx+QAi)hqn-yb;`jD$wqGryA7iM~eSh zBWM@5DF$DH-{nt~b4@BK%yY^Vtu*UqS`H4JXIji_hTsfyW|Epwg44Q&w=mcxQpvKb z%8=XIJ09oT@7T3V)n&4@%|{A#|Ffy_V>mi^Zg98q)Y*Z!tMWIY_-EO3Ge{1mL5|3yEo|AkTOf8jrzznv`#eElzKu>RM!W|sA?qFqzG4vU#c#j*P| z%@6%h!Fe1u^{Z04Uj71_rnVbK;@>5~^Z;qW;Ob z8ysaqMw85BtB;#)VE>Z5R5UxZOXz>4d%&J6cEA8hzEf{_(9pQaHtgcjrjh*b9|g2h z_MX9*Xo^>2-$sk|ZR4%JZOpHm46wX0-^8EuOg7;e&UROf4Mxv~&WF^X7%!|zrdek7IWmHE`fCTV8F0NKHglVI&<5bANvH*d0tovyFK{?eJu~Utg9{TxE=qhBYsGKgOCDW@}!O5oOtF4qYymsmgew zf`5ApIlqm-Jv4RD9-Adbps1I?qC_4gP^2yDRBVf*RM0j6Aqin1!XSjf2+0UT5QZY8 zAPhqojxYjYB!Y~PieQ6wKGp~!&RG%fg@SHZ69{L%Mb-((;GXh#d|OA+#;=Idm`nTv z4;ZrLAtF0&xX0t&_bCQmju`e_VsQ2yOB>39_{j&Qdq*j^cMLNsfHy>Rdpy#{3Xu#; zxS{3T1%WB73@Dw;oR0(n@cw^UrG^q1T+VmZGhS58jDPZU1U z4xV6NO35Y6T9NX`wW_@<4R;|Ax?Bvb*t!t~X{@iYX0!0A?*>nmO!yXqOrEcaw zfZc)d$)^)pIl_g6NRxVt&bXdGq)kKTp#e0;3K8f+>xl1#asrhpj zE}cIte&PhRU{vqH@gs)E3My3bCMEft3i(^BbkwU;I#OoIJ~r-(?uW9LPYL~d7} z8=j3zsz$g8NvJoRCYQX~L!ltv%~HuoRkjguOD{6Mb*Qz#rPgMaFv8F&gj=i)mn2Gv zlKW*#y)ms=qA5HKx6oVFuQ?@R^59L9rDC%rI@?jis}h*^gE@Fl z2M;rFBnQ`><=_qmrl7(Tn^Dz=@$4f3p1p=;59i>Qa~#ZI;0O+my1>D)43zbH1UZ<@ zz*G*#UF4vJfi?~vDB++45SM{{{8FGx&n7mcX=pUX@2I2rMuG2BVL(6q%BbBB;W^9| zNMK8SCT=D(lxzm@pG5F2$Fg=JtEw;7I9fd;eDy>mbHn_!fJRvSgS0D+j-m*{J2@aR zfr&~aqC)Tlyb=)&8e~H@uz~oD;(-^&Trh3_ak%@%_o=Ep` zx{_v}juWW9RS(ahU(lD!P;SK({d6)<(O`rTt5Ewprz`g(6%#b43-)okZU@qi`t682 zrdxTQPK1q%(NbK~ru&`s3T9?kVF)4UenG+2yWXNHXE*(?*+c)|@1*}9exv`@JLrF{ zAOFU7RIz#L!V}Nlxxig%BDhjcIOGED>Fj{69?ue)qRU#;=y!N-PZ`_0x%egDS}A=lc2g2 zz`#x%*=Y`$zQ2aBD+!N;WPF-nik7Mei?z%2;56+LJ(!_gs0Sa@5)tPum?vU(IWAa} zh-4L%p?JNPsRt{y+x6g5?G`<_M7vQBE<-&0;9~76Jvc|}tp^usm+HY(t%n|*RjPH@ zgI6L@cr1N+uJ(a~0@b|$oUmuFYh67@6LSd@A4ONR;iRVsTmP594<*jy8M{ z+v>$1&x!vPU!rZrZ=+H@gwDL!Umb1r4a2r0NB+#x+pfXAhQ4kOhQ&Y#1@jx2IA<-c z40eJ_64b7+Y&WRxpwj(_!H`=?J=vRP9)FAtQIaNC+WKxW1~dWUP>MD{4;3T0MQB=K zDf|s(z~9hgS`XZq>7l$;0<(+cetoMF!y6arp?b}#hbpx@_0Ur7Ha)aNOV>lov>Wu$ zV#G%d&C#yZLkqPl^iZmnqK9T_7waKF7Y%&~mdE;_1o$0^M#BOzdWi6=1%4sIFC_5` zb-*tG><-~qk7!O5&GsNRTFHq&1n6q<`&rpCF+}(|k_vO&$FvK4`7t1B5zfw+4=fJ@ z%htZCtrA$G)IbOYgDVW+Rzp~VN)ptruxvM|?x1j8-)V1EMe zY5>>)zc6^oeE$={Z`J?7FPiZ4cfc>|zu;%{>_OyT8hHSb5VXbD+TssT9ANw^bMR5| z#3q4Zclsng-e->~Bm~zZ2m#;=rmF61$)G<9AiiY=5sU;8lmsy>!-OP3^a~LE#>aSN zJad}rsy>UspM>iKx#Ff6XS`jT8Q3r8! z7P2#y*Wz=K8-sfQ1y#~S&DL1tZM1pnQxSF&Q@cG%e20J2*Q)DrkW?14!p&JuYT*^I z463i{@_{OaGH5(V8iy!37z_!rW+8GBW3}DfPz83bal}Cc&Pv=_$Hm}Ol)pJ%ednOC zlm#7_IXvhoN+%-4Es0_jgAo4W;5w_i?#I;c8x{iw8?)CKIp-tzi|JDA46qe{rFzJU z%Nudn{7myT#{BNna{=}rgc0U&*KwfROe%*`ZV_!wLjg_i#E`AuYfv?2GzX?gee}& zqnU^@qPo7OJ}YnGj=v1YR`|eZeZZkY&mac|_3ergk1_%l_iE10(YoM2+kt_Vq5R{3 zFj|@=T=Fm+GD&!m1vP#_crS9MR-TvH7nP zH*)ETQC>lzHIUa1YPZe;O>30oExHSo=4Nf_ocYYR*atP z!X2tR6QW3yTZ2fng!IkgC>SP+4^~RD?|^Y}&2w!ViEg9D#1h|fp7^^IV^T8#mKIII zBL4LArLYGJql9qgw=8TMC4|vJc;9y{Y=97KmzB)!1#9H~ zB`n;&lx0;UJBlv%j=tRPdta=w2EINf%6zkk%giM;-g1$LMN{YIHmrM0SU`&-J5MdO zpW99gq=@D{1^HNOrN;sYAnG-%pP;`@QgU_FIqmJsBd{eRUz*9VqOgP10|79NacW zoMMl4>G8g&$Vu7q8ZbsYijhx-^vOawhZ}`YedLYO&Rq7%V)kk2=bd~iHv2TLvrix9 zntj?ak9`^!@u?Cb*j+ZC?LHywQk$etx336Y@+x?U~ofry{dY1)Y7Wu$z5Sq)!DApN`Mv*ZbzMU9X*ef?f8hfabxO zC)lR~>C+&%5pH=u@(DloiGl{qWL-n;v_fz>U3-s}3hFF$;}Ez8Zwu%7+Ls^7Ni1qi z#|#JS;HC9?I)(KWm6KCvu-d?gu-dLtZ4Im4YKVd_Oy+{?MlgmoW(+6K?S$bNGluz{ zF??-=8N=`AFoyXN48MekKA4sb93kvdtE3l~lNXQv$!*P-7@mBhCEWJFK`_P@%@GVo zPh(x@hlic*B^CJD*#m)&82TlK4^L+d!L#)lbQ!Vlc%%c!`m1Dc@p-9H3dfT06Cq4~ zZz?xnLZktoN*&9&0R;h3u&ta6-aDMpUT#La-<(cpSDVrHcSbvYxEbwxW;5FU2-=ym z`C}c~z}-2rmlgJZGk9dz%Ir1&$o`VEf0x;xiEQ#o&c`c`g};g?s|-xe*p{&8febLl z%OTlUzK<=I6FhBG=BOj#)6Oj7?*tEXE;%G`kgb_z1}S=WCy>4{gET7wQk4Yhpe@F< z+CN|xgET7wQcsBJn{a7&g^lf&M(lPpxXNsBdFNhv)n@LM)ic@P@`%Ct5W%4L6E>JF z3^E9^giBT`jfh@`NPpuFUIv>btHz-!*fWw1X5(z$`=}gxvO`upMjawCUcd}@z*I2w z^*m?G;%*2Vw^4s>p}$)Cj;&0_iy`&vc=CPmBbL2GvPF>L#z>(D4G%?$HL?+4oB=fx z%bf!tL5;v9u5mwVfNhX$4zmX&dmC6(&qeh$7E2U@Q>pKK#?h)hHP}ppf^PYqRm20V zT=N60&Ef%8UQC*upZYqs`Cf{`Mx1sxmY60%T`ujzaVWi(Uia`0dTs_StN#WDa(aG6 zT-GMrjiS?^-Zr?ZI6G_b#_@O2Q$Q&7Y{5e-Qk_#`OSUxDVL=VcX~snuv)Gdz3&KC+ zD$AK!ira*JQ0DDXYUJX4+`a-|f`nJ##W|c8-Sa#R;mw2Z8Wv-@8_N*!;x&wUXdRs$ zN*R>C!+1#Q=0l-rdZ$CdZXTJ|&LeYomU(3Mna+dE8X1|15W()V((Xbk@8$N&5)IQt ziCkIYU0EWn{k-nP>B?|7@R+j7F_r#^7T4B8kgPxQO!ebWzF z`2Z*f>%oPuvAu-@cgQT2vp$!sh1qG6J!!?De6Wo}N=Vs0lHCiL?70W=Ci=g{dn&ej zuQ<@B8@$=8C2|a=ViALBb*duXgBP%GsItb%O?sZ8OK{)w#pr7ARoO}MxdX@)3yV@^ z+E1owGR=^wRi<{C=JR@G0Y3tr`PpT(6k_*ek4di;Pd|+Sy^k%P@6wYFRu2ODnMi9V zGTI4iJ7I4p^4p0q?LIhjAA$LW{x(FGNBqm>QCn6b5 z5z?ZB$v8;{#*%R_EicpQv{CL8m6qrA)(BddvwOXDA}I@y&{M{FVjrM9QTT+A0|(S- z;xfBr8LJ&-tWW{-B(IZlyWN97=!|1|K+5w;XFdccq+kpw9s@y>6cmug0thS$3yMfp z5mfovG&BMTKuK*DFrollMA3eBD8pYFNkz{Fo=4D^DkrY*Wr$U0iUbEY&ni88?t+0q zhj(@nB&i_jX0LBI3nb%KcjLLjgZQ!}*~!sdSN_%1hU_GeH+-jF6^+9L{7v zkc*g118F@g$PAD>&I!^A^1V}nw409-nrwWd#suzqu!UdZ+Wo0~b)iTQL7ERH9SJ4*@d&8KdOUA8zxy{RVJcO|&SiK@<{YN+P0o$4!@=#xj53Q{PjmSl zT{k&@C$V1f2PGxWeMmMr4-=z)SJrC+f+Jpnn79d8;;*n;9cXRNqs$ObX5 z_QK7=qN^9#6oJzQetcyaJ?1}Z%+#EMfU0Xa{wxm2^Nsy*2gnDlr(Tf)$AA*^fN(zu#NC!U^&uM$B~z5`^3l-JG5Ym%z(^2Vuy;bMT=zrD zy`Zp4#dr}StS^T##|BW4gn{;YuZ9$G^EQ^*)J9*TFNRP00Kome*kPrNIza6GBX**B z4MJx_G&i!)xfZSC^&#VNwg==1tHLS)xd;VZI79~wLhR^<0IbHrWJ`$`&{N3nq0W%v z=*2xjk7gCr9g?AFx6%3}Pc|j_d>*vPaDGCwQNZ@v`|#PqAzc*5c3E8ihI&asY|koT zS153-LwN>3{mx4T!d=LnhfM=Y-oF^#N4*Bm$PJ`G2KHu%ML0CBtD=2M#&G8;dK8KX z-O*qg^-^!_mfqMv#Z#BpKi2d3_cKfNp?fz{2U%V zC*i-b0e^)<1}mzotd*z?pzuhm35C1C7&J(@e)ved(^(B8uT0zIBx6d}+J1!T-?UwM zTvSyQo&knMg&_lk1f$ZTvVx+5iW+2~v8J-3y=BT>KN59Z!;&pr3tbH2O0`|i8<<$*v}Ik~jVSwGF> zyLGGn{tz1fWH3^Dc(PfM+9r1Kd+iyGNx?j-K-!w>a1 zbJY8`Rx-ZgA8m(S0j+7Vh&+IZvP7b2_qoWgY{1OLxGZit;)l#4aNN)2MLIc8$dsD1 z(zwj^V+3vwiCYd5r9kV$@MBzA8zI+!#5JQm5J~h!8&NpmyO!@Vni}i>&UcL9+Xf)` zS3$9XHfRYa`?9DB_L=dDuySSf{ZzFXpP;6Flz#$j0Ny6>($*lURw5`L51~8jQ+6&lYo~9@`m)Q}i;6|@ zi(xOFe`fxmIKe#c6gX#TeS0EIM`kWg$XSLbl^ovBl@6^p zG$u^UiCjZ1-Nm)w=lMRud@VP*Joo*tA%L<>QdYEH)__;a!jR*7i<#yuO+;70#Re7E z$f*V@wb8|5n_Ljee^A4*FtU9nm1T7`8C|#%aQ5RN=a(yekX6OZqPNYs+A)9GOm5BB zP|KU&&XKAMj8qTZ8&sJu@gi?%)#-vZI2*XP!Bx{@=^j_|RlU&~x2R09{U6R++|34D z(ybZr!|okBD@ru3?}ECGQ1>8G7pjhqTcP^RHY&(x-J8Ps>_jQ@B)!O^yNB`F5bvm- z(*VzSXD`K?t1hCBv==?$4$j9_+lBHS>&r*vHhPbOo)(ASVXo{lu!?VVjg#AfonMiO zvHOK4lXF3ZRhnUyM zz+RewSSzKmgAx&QaI8q~vm9!9mEc&2<11)S8FXYXSqS}N0eiR%u#i707I$>i`Z6t3dM=+VDP z-wINoZ#B<*ibjr}J*dBT71X38gS#5-jcnyc%+(QztC>I$;|5u`j=70^#PjcTI3q^3L(y2$KXk}H(%8? zHAL53l5srCJEIh(9JGcTnyX_gW)`^0_!CS`NVR=Q#HL?F1bHSfzK-Scy)9}|Mu_x$ zlHR+GwN0mRCr?i;U|Hj8hrUq{ioWg8cM*EHo$9!UJAE`=xLV&4UEdZH_Zh-taI-B3 zFf+F#;2w$k)=0W)dDIgr=o4WP$lak0))U9&+k)-6L*7>6J#si{YltI#IF1(L=-)b` z@_r!RFSiaOZ5?sEHXO${#BtZi%3DRe(}$Dx9dR5n9LH+n*kvnwHJiMeuvPHm>Npa4 zmKq%kXud~RuA4t_(!9D1J5?*9%Lp7z9S~C!FMHz zJ5&`nAw=9^B+gFhiAYA2Yz#IEcaqgq;K9XvncR2NJnu zKf&oIlDg|i-S%r83))!Sm_{ck1o}JQyNT7sDt25mbe`X@waOP(sjafr^;vpo!@Lh% z-g8vV#sxLZn~+eWKi|-|M@0sVK0FsWx4>k>kF6r5AesWZ7R2DE+>FPxdT&xgO+ge< zab>cpY^P*HLyfy|4AHgc9d}{WcJbJdIIf2HJ~0^&AHD;(Q2(GV*3f|VJ7xhqpy9N9 zbxgZ$$e88<#W@(oJ>=L38q*$QN*q&GnrM`WGZH15DW4P19fw2{C59=ZIUGy<^x@`t z&WW@5GR8jKlpEKC%SMn_SwO^|47vZPpw7(W*?mZHbwygL7akOaWc6vYf!1=Lk^$_O zC|8$0fbskvCCO-2%9_2yx;kaNj!?%h6VZ75dUc26*9&Vy$FC<5#~fq1%rTPhMpPTZ zFb8Q;wZk~(930KnF5&^JCW;Ch!<3yl9M3S^pKj*p#?+1P9*c0*MJvw9v(y`}oP$Qr z(9R!g9M1q;K%>9<8aNvrv`?48Sn7U_;pfTP%<6lAKd7ms;s;NWwKV;qa5Yxah*DSW zN<*M>S$VPrx2+^PK3W&Nh9sMfO}sKU7E5xR7N@SamDR`+p3!&fAdeS3{w47EcPGZb zvq44AlBxPMyDqM&Oj(f04Vg30EPrUv=M>;uD-ygv-!%Y&4B*Hz4j)u zIo;RS=-kM4R<>!ibH9BA{#R`(Q=Ay=;fbq|W_3SmL4>ZqsX7O7GJ&<)xjSf0PD)9n zZm&C0Szx7l9R`*fH)D)ACm8^_xF%%*5qufic-%{gLv4mP;zoH$nlei4JZqG%8~yzb zEzG0elHoXb%zG|)yc$vY32ONXzDrp8ZvjZU+9;PtaX*PfY>n%r#wluz zQ+(yamCqyg$s;R&HRFFkP@|xDW3_V`fx2&nM1JH5ZmHs!@g|8yax6oQMR6=&jhQ%h zip2ae^?lKn>IEJ|nSLDtzIi;9IkUob!bJ2v2nIK&3r3LwjB*+eWVHVE6_&gPTphUR z?+=WH%I~rwbQh6-S675i=_J-f41sp{lvHJn+CSn;dbe}p_QHwaOXm&$SP*yZM$veV zTF~>|K@X7oIR{0OFCL3LhIrRpayC~`UVW{p!C>DenU_@dmqWp!3cWrEOQP< zaZteBn0FUNv(o=zygkRwd{`P1k&vD!0|p+xyOTndE{rdFUm zfKZvUzt=DW`{Yg46e}2G!ZKIIel8#0V@xnrOr&QT!`}7vh%50YzXld1`FQ*^nvTVP z^l<&Od9!D&qrggXXUsEy5Pu{>$k6kJoNu74z?21_>Cv18*TeCoC4E1GjX!9ri1?#{ zju@n!4Hrh;VKXlBw-+k)7@Q01eNlyU$8LXg#rR^r_$+=}EKfvk+8ors+p#a=p=ew> zpLE|;I2H+zo9=lyUs)6xSXl$@n8xfZEaaVmrv9bue zN=ujVR|Zz&%Cad6BEB;EVwBUdH-O|!F@FGh_DYR~A4G{mOO1joNJ#_>aYXqh1#`GH zhFH8OF!&7`+u)$;-whlkuZG^roflsrmt^`;V;8ob!bIKZ&VvkEb*3d?##f{ z@;tn~xOA5K(w`e%Dk$d-p`1R1GHnROHiR;Nrna5n@mt0lKzWNOO)ezxAKo@=p~W29 zUPLXZp_J-r+f%MpDJiIP!>!}>1p%4QNt9n$>by|t5wP+L%MYL%rRuU*iR_*)iL5KJ zRwaK6a%lAy-uzFh_nIo_dB|bIg(JhlVM{%I2w5D4)KIbqf|=~yCxuR}1I)NR>iD=Z z1RtI2RD7Il(ZF%+=|QGObXsWC78=c>Xd)yR+!Evk-TurX1{)6!HvVC-94CpeA5`_f z;liX!9V)Gb^%2_eDm|p;xO;e&@xA>loIK(SUGLoP`)|4l5$scyFCBTfaTi}2f)e_r zq1An2ZllT4pJp)fE6e^wNb#HrYfjC=r|}-sb=X8z_z|ygdM2-p+;ZT#_K`6|a@m;;&CY)KR8%5oB(kxIhn4-q~= z>Z8hkI9UFXkbj(eBIK{PEA>;|xf3CMwN?4L7-C6g{L*_#rU|)wQtXs}b}HvL(*5*z z4rb7{hslfV%2%Af+(7w@m9MRylO?|g`9&wIvbHDLp5Kh?9mk6Z+(>`tLY9!ONjL}s z*4UM^>Ewr=;((N6H7Pxrx_?;MWofEDB<<=9L^OSDsy;U5%8A(ljZDAPK-L15CF+!? z>gw#LsRM&A3sNVq5L-5Kt5#z7#$ug)R(6&7sblM~XvCG$_M zZ;$NRwJb#6D@5N|Hs&Ykq;GnJ6lde&;O3%4TmkIp&o);c3sX7z_r?C!bPDWeuB<`8 z>Y37v_ZW`8xEm+n_D{uT{$wT90Q)MAX+~B`r__pe?5CNm#6Pz=jbA$EM?fc#vT}aL zrKEoBlj_NEEioQ`CDkb%iG>K}GKblopQ(S}aHfd!!;rjjid%@qx`-b`SB$1{e#=(L}tiifGkE z^zSYrFr8+yH|>NivM1UYIu?M`yB*0x8-P6Vhi*#4_OsRA^~V`udM=bcjMw|*1oeHa z!er*L5l>z?9wEbE)7%-?L9*RlFdaXjTZCTfx!HJl!AU~13hi_RZ4x{iLAwOcLvWe| zFG8?Xf|nwAI)dienF;Vmi&8KOvf%Wy_~KpTmKiYB6QZfI#(UXuVvoF`_Q(z;Nxeej z7Wr5XkB^`M7$7q?syaa`bQkC1;&3ZrrGbG|EI9hW1011~F(5}iL+N>j(p9-&0g%2= z3lF4b6-W!p84X{0!UAdB^g%{JvyOs|nE@c(CRtDR2#}h?1Ib#ng+ENu545VcIfS#xV3FosIt%pyF z5o(eSrBoeC={l4$btp~Lp_Ii?Y8V5LY_ckoKKX;NlwN^x0$3Wr&BF0y!G)r5Q>2U< zpx;My{cDaHaiX_udPG+zsj+J6*g$GgY=G>RieMueLP;D-LMW9(sR*TWC>^0p4rL-V zkwX&^%HmMgcAhbe6w((2Lz*#4o1SI6q1&Rt`sO66M=6axi{wNg>e_Sag8OqkgJ#iL z5qAOfDcqwMCk6HBKc!I1NT>aG;d}J9WVJ`HN>O|CZ7Cr=`d-?2F?`ovwQN4E3gO#Z z#k3(Kl|0OcNvuNS09C3X-TiN(_fAbYNySHux$`zy`8QO%LB5sQ>%Nw+k3g8UWp$X zrzD}Y&~q!g{BwoaY0%oK*Pyl2pA1?%?KNoabfZCQr+vI!d%!5H2r54-1t}1Atmo{j z3|d2VjHc(^*Fe5i5 zxinN!JhY*THY04PXGlW@Qi(2LtvXrOGa0biYICKNVOB>-bIlf{A21uhMC$CVTTr1n-Wac&`bM_fvlsyboG~@IE&@mO<9>UK^gLR>%82 zKQrEM!4I|c5WHU_#P@W(&)4z3P{(_nj`zhn-j_1o*Tt%M-}C3~@t!?O@IH4tyyt<= z{lTz!uMLZLshU(o@cy2L_xT#$7ixH~)9}7n!~4>(c%S?4@XlZryc5>*g0Oz~Z=lgW zcfop5rHb|4gFt?HfJX=UIf;J*@*}R4!*nadg8Z>knS5QVBlN~`0gxLd>nfLk{Mzs! zZ^{>tpQnq+9jAeu$=;KT1mx!p1^Kn%L4Mr^0eRXK4di_OlQ54CxEVCDJyK_eNu8+z zd)@{H_H+25_80=}hlE(E1G`!W_8cA9H9D{#(1HC31N(&-71+mZ*dExMV+q(-LeU^p zN5pZv1Tj3Mku;N%%sQT#<`(4z%#|9Lt2Hps(ZF1zf%yRq%#VZtGn5z+l4k(4Li1gw z_ScSrsof^tNOT8&&6TtR$dN}Y7n!~^+S5HF{y6vXS-!kq1N>>kKO>#jP7-2iCUt5S zRwL}5NfxB|%D{+^btYdHBgdHGGl3bGjyE)!_C%_DY$bF#fzPz7Ms74sU{w@T41V#X z)JWZkDS?RcW_wp$n%iVD2?2TiT@q?ERU%}>maz;|3>u945`3HEJ~h{OnxB<<`chq) z?n_9SN4vr*b6=OP%)MQr%&e{;Wt?4_GWWeh%FGl3@?Nhgv-w9+rY4G%87!mK#z@xZ zS-wR4XuqwhuY63;SbksHWb;`3w%z*5H`(vaVHeIve{ zD$3cU^x|2&+$W@j&g{*h}v}rsDVuzrCupJicdV(jN!fQGM;ZwU%!@&e&;H+SCgF zdNDrz%io9^)BCxS#i;U8IW0BH>M-?vx+_h40`l&1BQsaxehO4p2)N5SSrPg?V1n~VQRCFIqVLX=Y(Zy9x!VlDj#vAG3&o>nZ0tB9? ze7_+Q{4|-f%=gIeIs!)ZB-ZTE{}Ub`}= zRnWL@#_cxaXSSwjc`96o?HDLqt>R9oVqiYXJyNu-sf0Q!%_O4&eN_qVbndmOcQcAL1O8~mU{B5wv5-eZN zJ_}8dPI5XXfL~qN&vxtyQB(KCf==>V{EPUxej&~$zHqz&YCYFU;Cr-_J#iO)U@@z? z67wDZ-am7L{FBzf{)z4o|D1UN`zMmrD%jG_{@KtT>Yoq(4EU#( z{4ssg4*gTB`{(yRN9doq=ZEpnCGFy$mxuAsE#dv6R~*xTkvM0dE#RDOZU4IQOxK+w z`i{guqjfdKIk&X2b1rM6-VEnIoX5^7Bee?7Ze!;h*B0uWKiC^qbsFQln_uA~uKOob z{3EkAanA1G!ZaX%y~&W%qU!4?@U1h^OZRHFR7TSI`cQl}6v6nuKI~8&qmeQAVi^7d zVj5|5-GTrO#hcb?LvhTjG!*|V1mx|O5SXuACquDmTQop%!?_q^+|zTbT5uLbv`k$J zl@_~azbf$gdb;@AfyDh6z-vz zjvZNXay-M5wPbVtZq-q6BOIt6BWP zm&xKc3juk{B?RXFAH?GK50J%;({+nK!;>G=E?_0YlP$A-aj?0gUA~h*|ATH`%-UK=5|m$zUv1X*84*SCA8JYQMM6OKg$Rk_g{tCf z`blxyIi$FCVlJ&Z`r^A({L}nb)%7ISbxDt|>m1~Pzk_w16r$@vp}HQR>Y5s?>#n-4 z*{o}drfZ_6>w_(#>;A0k6K9V%sLQ0HZZ?#{t2sx(RV~3tc;p-%39ZP3`EE;CB)C;F zDS20mD0xFmkdm`nG$q|Hk&@R50eP=LNR;edBT6b;NXgA-VNMPt6cZGV*~U;XgI69O z0A1+sd$WT!-Apq&`cKDO&YOKqbZM$%pJ+7WI5PL<7Pw5OQ=k`E36@hZT36wK2BD9>z8v^BM>%Su^ zZ=;XPLq3%lXP#}+bRK>U+4n=o(9F;IMwodP7`-=QckK2O?(7(ugw5ak&I+Tz?!FOc z-|WSvD11E-0W0#cc}DZ>SSdXsN6j{7Y@zv+W^mzvRhVz3EpBTXq@Q{*j0yf}vjrQYu_C>@F|lJW$vGz5+H{1yMJXN;F6h|n}%;qhMI zkI(p5)0{wQ49WP1UGaHKDRmX{_52nrVX-jv{6*S%$7NN8aU2CFprpBQ;XtuaUMGks zFDfdEikjOLbB~I$Y!_Cx%%Ww>lFV$Hlona0XrhUbhyy`Ma3rrJqNsqP_N>D_r?(0H z;pcSj;rsla=j?Oud+%nvl1Ly(<#&M6`bp0cIYq$V&8R~mAsiM$I{ZJZ4+?(%O9irg zL?tO@z$V2>kRreKPe8xw2{A*6DJl$fmT?tk6H!qFNjcF|D!!DzS^HPk3!X#oo>G6J zh!5PTATER?>vv4Yq>4i;P^ca2!gC3hius}<){4aeQIU%Fc44M<3k2@;TYuXHl5)97 zUG}yv*T1f5c&8Lb zrT=5Ma?Hf1+xS0uZtE?&ciq4C5c-vP{X{Q#2=)1GOY(|tz>guyiU0@e$B8a3n3qI( z`0ssf;N0?soSWg1*$nvYD*jNk#l1CQs>`VOl?dLSTwD{Lhz^AU)eCZXy*@y`W0F0F zj>q%v(Ld!D0WSltm=E~#KPZo1q_S*qQ~Vh`SaexFq#FEkIVlXrdH4Zy7(9i?dc#=o zeQoj8?E)=?C^8}!u13bMfz0gr^{zuKwnhN&)u)%7;ieO}@UnXo&- zA0EH|CO0k{MLOyR{MYE$(XcHDhm`r%x^trmmLH?TrUJ~-%&F{8T=qMoY&R@>mRjCm zFKSuve(}gg! z_NqpUGikfRNZgtQa;uV|IpVt`vYaO+1sy`rLs08yP3d{B;)4}AIy-e*Yi`I;&}_Xc9vw3bdg>lN2=LDng?Xy6dXb_rFK-H%}KB-~TR#9`&wM*>Em9WuabnBbL3} zscbVY>u;2O4$BU3DtmSWl`S_kOu@2EoXT$FvZ+Q{8ZXnDhdHUyHU zI~RtNI|cJ~ccxzecUrJzKkOiOcxgy&cnbyf41D_p_l(lN?irJBqn_cOY4r^3xu6H; zpZtyOtv(S8dFD8Fxi2^+rD>R8=dRC_@419gs3=>yL~ZbZI=S+9Mj85hJ`(($P-aCh z-i+RIGUPdb4p&J$+gT%a)(>Gj@d{dxpeqsTTBh|gSLtU{g9z7Da0!eXEI5}4>1PVJ zwG?r~5x23liUZM&(G>2L2aSQK$WrnJpmRz7I9HW0OusOMTy ze{~o~y-0awMmQNn zP3=GP=(b9r=?vXBhoLVBG+jX>2%0$*p#30e_JsVQ)Dtr2=sjWlIp_%uGyyJdhJe8= zU_4hlUPXrMEEZ6Q1wgh4`1vvl7>5FWgk+W0OwC78wSTnV=w)7DN|sHNeadKj@M2M= zvsqO8pLl%mQlh$(sBIA-$`6ueR5BtcDqqdkqY_pKQ7ONq&B|s}x&~8JzMmnpG7A_Y z0>*^PtgHlleF+8FQNYKLGzE-e0j`FCq_ZI4kxN!Hi+7V|cBlsrit%i7r_@Bic6K^m z+3ZI)Q-^@f?wTVBrXzEEGcAE>4+<@B8`BaLt>_}s9!J`#ix$mp(!7OcXIiAtUK5&K z(N+`9hO{>?R&j9HJc3%u6Z?$8A>BNt~^RFMvtaKwj;T?veQamc4cF_(cpdDcv`u=6}NUDmUw9f2X|*MtMgG( z!b!^hK_Df#*lIvF)qvjU#+77a&d0(j+{%??Q=DrTkh2Uq7a(ai+LeR&;ky}nqjfKU zM!WNZHlLfZzS^tYT0gk`d1B0gtC9RiyoO+Xr|w{VzXr(~!m5RH;qsPyC3z@GJ`CB8 z&cb zAkN%qS+8!PTA*y+LKCM3au!0;tcAvqja7JuUJFYdPz!d=W>jsl`L2y@LI&BqPi$WN zk!(V?*mNbEuM7m6r9~DeT5-k*Cz?4Og!6@Pq7~jm3_%Q-xu+tba)C$^p3k(VUJ| zPEQSxZ5?yk2`6#0Z0i(f=Q-ruhMXTDX*x1%06CI9Rd?jTX>jD_bCqqcfy1c1KAvi{ z*ZA7pUORf1S(6y#nSE|vcW{bJ^F+qZUA~>2s~bo`qHO7NkMx((l$~R5c=9|}hvHT({ z>NUbB>R?7Ziav`T723h0v1hFSrK-AAlV#n=W?D_*0FCAu4YCDy6X8}Nt|sAR zA#G~!!`d4NG?$^fCo}X~h^(O%bo1{B9g5JeAZhl5$#&`qXQ${rA>}0Wgr&b*{^Yug zKe@M(>4-^U+MP^)JxTVLeZ;i8m<}V;dqcr=FAZ0uaIXN4!q1$U!a4IDcjh9+DJwwE zv&bogr0K$2kc}h3B;AEt=)!iVvJ1Iv@kG7skrPmMMS-^V_AvLq0p#@RiR3h1MaGPY zEOt3N4cQ_#lEh~B1F>Nm)XRh}4v>k5X_JL^E>R{PMZ10mX)hwJ7?P$t@AV^h8W`?) zpgX(HRCXNeJeWGxx4VpS%;v%4*tAAgm-2x;a~%7i3pgdEX^q6r`A=Zy`gfyHQMUA` z4Sl&s*_6Na$lsNHar{4{t#Hlg1(6}oMgXRTF5CHbJlmNhs{p%#ZgL=WFhaj{Xfv{h zw8J``2=~)?#zir1tl&NuT$IAiCtPU=;HEjMIF5ZDPT?NB(-_C%JoItwN(1dA!wf_K zkHD+OvGQ0}etw)bj-_^$HIWtdz9Djyp*(skA3f@T9xcz;mKrKR`5g^2(QKy2INATI zHKD>q5H7P1?tk;Om4T^!5f6?-1scQ9V`CZGQ=l;ldhs+upG2qwl4ehM4YF}9W%PvV zxvsGPbt*fD%SMgY%N{xgWnVd6#oFB8pK9}=9p<97R>fX9;HEMw9FVP_f5TvC-hJ2- zOhQ+V(}i{hp%j-|^b|>5aE89>BA>KKmQPyL#A5i}4EmZxWU&5a_kPTcl<-!FBEXPX?;EcXOpiHLhI9Vw_Z5pWm?Jc0;zq21&CrvU`)3jbn}bKVZ?}_M6oZ z<7!DmoaRfzCS~ByETpk~_cgl^r)=&zg*L~c&CRE@vpF;5=R3(Jo@t$gHeYD*iuMrE zY9Z||jh1Wft3KpWWCKtD3Rn3=aWq3`^_0ocO`z=w`gIUMo1LoS8O;(qwYzHDjdoX5 zx1!x8)}#FrW>MQ8!=hrpl9^WRb6!4)qFSP;r%yVq|AVNm-Z$#%y`#APKUvx7CLol@ zzisApQ-`OUB9LwNiO&K_L#;8ohGw7vekOx|#^+&=q8!ACTi=~Pw+01*TkB3(&B|U! znpN#~)DSQO=^sj+O;3;$6UB)k&h=ivnFUERJoQ=H9iw$=j~{{X^gdD9@U-hk;qgl{ z!jn{k!?VxR8ch-)&zy}L;-D?1!8s_whbrRS##^XCQnqwmUvLbv;4j4A*yHGLJ@of} zNSgk(hHT(HWcd41HuxKHyt4i_4k3Sowi^DXi@$ju+=|oLlhRS_$KX#_+mV3e;j$FrQL=`3 zRhI=owg{L<0!q4pfT_o{Lke}Ow5b*076~qvai0ul++@MUDx5drK0#c44Hu_y^BLDh zaB+;AC^!$n#VOpLUlBJEaY>M@Sguz$T)xznpc#<_g{uU8Q)1qv3qc`Upo&EJuGVR%qUCnO@uOM1`q%DJ_=}yls;l_?1q9%^P^Bew%}eQoHyc@9M%rS%sb-WwjgLIL*XhxCqyvxa7Thdwm<_3 zntUrj{WWN=*;uDDG)SPi?08FowiBhf%JJiw2#rB#W~K#AGAB9@9vmT?XnUnUEnP!l z#B5pr5UnlJp3kh}aF$}DMtRRhV>pYct`BGJtCd+(i79zvbAF6xuuLVaym`1boLz2B z!Jun0oXxpK_TtK;?T65#D9Cms9;%{89^J^JRT~VC?8d_%qg^bIY({gD{^RM(9^q9w zb{?R4)Sf+}YvR!xo!BG0YDPr|>HarlJ5C+cmXqeT-kXhmIDm}dRk}&a;mZ&4B|DHU z(02*i388V2H2Z8YWaEk2K)ug?ng)G#=0U5kxu^sGtv6F+!wfps7%1b-YFTWnL-~$m z^BX^W{!Mel%RK2HB?GA!(`E}TWw5GOIs%nRVxmW3rT{ z(Uf(Eif)g_tlP_~otB`PR>#Vf2lt{YHPMwjA!#?t zM%hVNw$a`yhUjNM3emOC%_p#oEmT6O6{RF|?RBaFKN=7!_wsq|<=s~sb)Li;Hb10a z+y?y7e^E5z7mU8r}azXknVu z{a?t&bzO*Y|92#MWxs+@+M4Jz%X~Uf9$a?7u z=}a3aG(VxGD_S7Yl6`RftI_N=&Gj$QJkJDESl}u_uL<<278DlDmi2Ect$z`knQB38 zp62?Op>Hr0uJUdon4w)53fTf(PSEBEeKNJO1JQavUjKe-J`7Wx2O{s1Ql67}T|1z! z_}7r6K(7bqUu*aCQ#h0r9oE;Or`Z+fooW5^>+AXsb-K4HwiSXe>kFDnUOE zV(2<=fhoVIXsCB-2za2g2C zi)4gT?YMQ%H9BzCcm;<9g0run;&>E?#K1`)IExr3nq^%$cV}EIhGakzLJ@L00p7sK z@a9#btgBTxRB5H9@@Szk?tjF@V&L`nKN6$=;Whn_!_xmCvG4KW5G}fpS}Z!fz|BC9 zZP9?&RtCDu?v^NGQAJopPbH>#EsLs2MbuxyqC<(0fu?+<6pL1q*cVSMLVw>?R#uhW z&WHZKDqEBX=X3GV+Bcvl4$1zHLH8aC+W(Q5emp#%v;W)0`@cgWy0l*kacQsl?#4fw z?a~~ttc}0>ve48kR`$*#R?<^#S>&-?`X{=WD$n53P6@H`|9Ad(P|p8I?Aw3PT1#DJ zpFE=MRBo)Lsw@&*x-Bl&QUlsuKqE0|yMsaJe+k8B03FiI*DNL3w&3S&q$ z7E-U{^ZnlgfnNDTVm{H#ZF{A|4)RKtps^QqY?-|F*b+SU*ZvTVU6xFYeLUCwN;1Mf zV`o1XV@agO=2Byuk43}Hl+&*J+1T)GZ0r5jN^i>Yp+7(56X2YvaSB%m8Py^-%A0{p zFK`><@%^X$RwL3~@GC`r1}JE>oC+EnpaTr(*?kP!fI$!M^Mh7#LBB;5&~rI( z-%~-yBFek4aFQ7Be*zkXK|5NYZv)PM1#}3B1#KA6QjEdsW_HNg1#M?sLQAtR#Msu= z*hZb>wnd`;i|xK=5?c}pscSiu)QHhSip4np+AHT@Olr*Dz~n3#SCVsoPCGf5Y^CIE zyf;Jx8YLD3%FcESSRdn`0l%M$0W4w;;j^m&$3}?(EXMZ_d*u6vIQ;#?o)F9U?L&rrr!#70NZY{VmpMUzPE z`?fnUB#TH)pvQCAA&I+%LUKB2Y!4kf!x{TzGmTxa+nOTofz6e0Vnk;%MRbdlC#$-D zMhr|5vqh@4o5?dK^_~COH+N2&(uDX*c-zBlH^v?h79leStAuTZ7y%Fls-qD>$S`Tt7 zyJuOHU77G$T@00@JLLN>rm|~?b>-h|Zxep|p=}<;84a9{!_ek2IQ;}C2IF+y5g3IO zVG@PJne8Zy*+5Y!BRb(~ck$`-OEj}sW(EBFX{cysG2$2P>j(ZRbQ&;2P`ULX_B@O9ydn9-V2MG!20hE!S=9iRD| z1z}s+pGjK_suSU7S&@ih-#8+FEf*>?xCtl4Alii_2FowI-6xrV|QEM_=kWN z0%&6eeVq~C=K-{_0lmMKK|3<&m92sH3?MO|8>YAId9#-6S-I6J5pLPP`(2Wg#Coq2 z=F}dH_XSh$TZ!gRQAu-sM#EPBoOu_l^+j+j?pY$+^1dOvKt>W|@ zfQoM_<f;$~Xfz1-dbtSKK(&apSC2Lf9ACKjDb zJ)|>ill4yKzF3yPq$06ckQ)1y{;ALxtt1O3Z}d;( z^G*X2P((M~KOjA;ykQ7V%;)07IwPih%D#@f) zuD5P3ne{C4TMcwL#W?|-hQJADaM}ybkBEfRbiMVi>(;F4ACdT4vm(G%v4KWn)PF%+ z!)Wo>2i1QP^Py^Lr~WVJ`oAuy{_EH^DV_SyV|TB!;_AMuy!-IO-*<=__(FV)5aOk$Ds@ERaS^rhy;H zG}IdZE~(OJC7G%}1dX1cLBGKIzgpIRrg41r|GM(GS6sPmr0dEsb!FqLXeTMV3Sf5v z>{1&lWrfO>KS}BT4_8~4Fw9BNj2Dt@BoLf5zp*ZDAlL(5rnm-|fg$+d#T^0+O!46E?!{%0 z;ts(bf^V<}ce!#7y@fY;=}gb}-*fHXdv~+R?mFpHMfz-5_vg~lM989WwT$M{aco%} zvH$-STTsX5%N%3>m$8l3{p*kqh_8mcQh3Z=4;Q>IuS93B@2`vYBJ!w3v>FI~c3Kt# z>U*OmM#@Z^ia#>x&#-yII)AMkn*u@(Oj&Ep6|aeCEUXZ+D7-DkjHOvBX5su_u>*B% z*BG(Z5)r#>&42FA`(vw#8$Tw-#JQt??OpMbK3v>-5qyvV_p8uZHy6Q-Yh-6veG{Ec zzQ#EHi+K+*RIjW}GvJC7XJqNLX*`?;xoCQ1wKT0SO?R(0HheDBX)I-TE*-9&wOwqq zamObtIJE1l?BYnf&Z~`Um6&Z@^g@~TgQ=iuS5A-L)56q2Q-?C$_*IyuUHz}ePmNe= z8sd(QISq;R@w4ILr|1Dq{O9o#B^MAxt3lQ0E8o-V279A0sL{9EVu{A8$>S+Zi&e4@ z&%TI0)LZpu_I(wy@Uy2iv#;|ak$t&W8TUM5?i9^cqmms=gL@u0-A|+2l;tDNJGj{1 zTB&~jAsgUZ`DX(L=>~L6V>TeLP&8nl-BvH>BKppt~BHvLWsDDe0gg<+!Az zjfND^(mX@Te@{u_hE(aUlKL9bh}%kPV@S1h%GEWbC3-r_8j@>|lJXnUxm`+1XGlHX zD=Cg4m42tBV1{TZVU&>Yol*Jh`!oyU*wjPgtA5lgjx)XnU*`i92Jk1=-~mBe00+QH zf^Q!*aK+Uig5V*5EuIFm2u`sCDj!C$8NiW1gZ2co0U~2*P)h)QMcAsqs9qvU(C3dO zia?jMlQyIWKth5tx)>jQW<|{d$N_*~Kv-7^62Z!oL|g462)@_A3$UEv8bAcVcmbZT zR8>M5$k6o&^#QmErUBRhsuBzW@M8uF6SM|!p+s7OY5*Ys-{t}oVI7#7hXm;a5CU&e zS*HlTze6TrST_?q0tiFG%qBR^63C0;1X}=n038VC0CHzo?Y7m>CB!DZK27x&MSpXd9ppPI9KnOaxli=A~vir~%*s_%Q>ZC@Tke zOppP<2XKbqXB6td0k>5E{>sN%$*3Dq=Eek%a6$PUJ8LjQH!@WG38cjfgz$uTs}ZBZ zQLP&@QI?3h==DiiJ^L*tpDHuPy5>C;!3 zUsN}b;2eMpB}T&BEk~g*qgKB%%tMf%O-RSVBLc^1d4iq*K7iZ=O#wV;PEvvjY$sOa zx6=Uf=oavacGgXTWGG?7T5yoy-Ag177R=QIw*=tUDXk_G90I`SepWw%HL$Xwxh)0Y z$8wg7QKLi@Q-$AuwTh9p*OXuNwK5Xag1H+hYlC^1Oxd?n+5ea3^kO`(kS2tO9bhj( z)C(-SFkeP+6(9&Oj$oev?0_7Eo&?LIflqdzDZzMHxlnlpf?lixiJOO@8Gr*Nk_iy# zidR5|K>Rd?>mQ;$0QUq4yv3`m+z?MO`ulSnaR8eM9sxM9|I8*hEr5Efjmm$a%75I} z0P=OZAa900xN*=1(;889>4`K zieLwT1E4d(f`Fcc5i0vYYmp8|u`qz*1RVhUSf(-&)CO>)%drSb5TJxLNdP?h!MZgG zQON^FPPj0h`$&Tph7EbOoZuRO8zsgQ?6;I3pMY%jB3NOeXV})85ljH^0E7_q25!EFsILHlIdc5Repc826Oe*x{l`BZ`AMD*zJI9Pmmgsg~<{uA5*2m_cRy=x;! zoe9u?Db|A+$^56qTv#{(>JW?oh=ff^X>%GjL3GB!=mv=P10*0Q4dB4()~m4q*#X=r zafKiuE9UtRLDWO6K`60|;Hm)JrDgVvBiM%$K8#OK0iIu1V~p;$V(3(aMq=ZuLNJg3 z9)$?pG>26hf~o+E`T1WsKw$yAUh($EQ7`g|_dBZBHdarVVn>6-@)k-ZarZ>rLpk`s z-eGLbgrb1BBUN#iFt`2mrPx_h#f679tu{W6j1*xaScJ%;It;~r`aFYciUu@rgoidu zu`{%3ik*r3B$s!}Vx6c|H1fldpMBB@6B#M)UW7U-=@@Yf=R%4dHC+M;*W>XW3(c8A1H_%f{0oUue6i|6tUdm35b$d z5d0mNoh?{e?Q?5GCMo-<{jVn=GZiQC2fjA;OU%Ce;!JpechvCSZLl- z#2Dx`+V+qYw7nqt;D#dM($OV_piiO&jJ*h6XqgX|T1=l>SkFOtKvz?`ld*wDCXdBg z#m&eo$N@nN_iElnb=5Yw=8K#jZhGFxB7x=Y)v4E9yAt0=&n;ywC`zcz7gn0>J2?Ax z&H}CgJukvfH{WQl=gi#5O7ih4JVyKhmp|C744QCDyX12fYs{59V$G#H@lHPb6^P1b z*VFDZb3~{_0kl9xFdW15#p@PA5qf}U&3WH{&DP^HEb^oN@lI-%K&UR@ol9m?=)QW#I!r=uxEW0-Y#gpewwN zlpx|tSpqg!r2J7tr67zX8G|CpfX;;gd|C^!`T>T! zu!o25#T^S@3v(2|TKxI|zcSnc4%j(@zZDd)pO)@mL=+~HD-zd)z-(xyyq~rW?UzCO z@@V*h$OVodQk{EQhCwuQ6&|1d%o_=wWG3um5yh<4)k%fo8nbwDOI(ig>G$10yP|A+ z6|3UY7i>5#M1<(bj)Z7Y`B8jo@cF3x7(kr3fr{6M`iV2(4p%Ip`-6E+S?G4HvCYN87F`)&fTw&ZuVxqa*RlbSnl{r;pnf8TzkCVzVl zpTD!NjFi7GJ?#4E&=rx^M|ZTkKFUi~)f%9Y?o!a??JHVYCSfPKo}yjJzI&3?Lpr`W zOy0k|L-YRGN7qYXsZt(Qz~n=c-XYQ7jN+XVJxLYJ|0ELi`Hk~yFQ zz=xd;;H)zf(4chj2D@-eyQ&r~40AO5|4Z3Fy7NYLwB@t#HXMW>U7!9d>r?Id@{xbu z5q`hpDg1l;owCBC`4NR(`YHr(t3vSB zvFn+B*0AdtT*Cl5l}D&!eR}bN)|W9?sh+x}hI#(_$py;uR|Kvuau$pr;E@ad#sgf= zX@xFFS5rrHHT9p*hj}UCcA)(WTHk@DgzP~7u2DIfgW$Dr2m^_)*`fCR3nUaCL z`_e4lotNp=e+4Zd*(6;6^-w#Q7xbA>gc>ug$%f)kD8l21k}iPslxwcNZj5=fd5jtN zSF%k2l{)_?Z^bZH7n93XY~cl%))Fj<`^^wSN&S|p%i3y%2A0<}1<#w!S7;)bDn-yV zOpf3^G=jhRhrNZa^buD!&uE6?it926 zT#FBrZ!VhK5&5QI?cwuHWll)G+3;r6`DSlU#C-GKXX<=2Od8%l=FKCPZ-#w_0sL$r z3E=u6N&ttY2LrfpAPwLN=}G{PPgeqHNLK=QY&s2KEBR*HoQU~m!kmuHH#dK(VttP} z5%LW?r{nX@o;9kGXTH%P`R4gGhs!s|XNTk);~P=u8*_Fm`6hOaI^RqiEa7$G>?4(L zo?eY`?P(%#J!P;0*QZTExHhLzxZZ41;CiD;f$KPv0@rIzZCc-aKdU41%{JfR^UcLs zA^GO|SyAVk$ypKejm4+VHybjg`0aW9h~=9NUJT$h14sbJWhw#OFd!JfoB=d|a|S2@ zoHalR;LHI^0AC((*!9gRuSd)`@vnDmzB#x`#rpZLMaVZ#z1H#hW|Dg3wAVT$-}GCh z#;cNRY|BY+Kqp8lrIJcDlKgwCo5TpC@$QFGly<&qb@I4ukOaQ*Go#H}diNki{HNfm z)dYYr8W|OWehCZ;<9YKk8E;+|1EE16ewjgmn9F@!=imEkFm%Fx*rG#w`%aFnA&p@J z&pn$r*uO^A3R6R0ky;`(BinRnnC+j54e>9lh?_~k-QD7#Tp-j3b3}o-9rmy7@i*_j zFX+pMR;r1dm?8Pn_-eG-LvPNA;7gxB4hDtsTKMwubj6pVf7UB1Up5k7{ub)XNv~*p zxmEJzZ_uoh#+RdB3HtIkN-LOB{5h)r?r!nKxBeEs#ec1z4{RJ5GJo!RIqLb7O^-N# zR{N_f)fBiyQow(wc35+8-0^hWwxnN}yLN@0Lod<{`t3P1T>ko(LZ-Q$?VslMdz!?e z@(|xnI48unWiLg|^Eb{>VN*M^-R4mnS#OezJYF6Y;kgG!K|<2uM(G)GYnzP?Bzg`% zpO>0K=JRPUMm?XuZHhRb7k{Eo12HCf|M%h%o6lo3BbKL0eqQiG#A&zug^rzeZ6B*9 zzkx4An09--(DBo5oO_ROUHoB_Xs-M*!L$KJ%}@N%ryqQ_gsRJ6U@Dc4 zPh{f^&>C8i8NL;?KZwTp1ys~b5a@fDB!csUn#-U^kiHwAmFXJ<{fqs6oiFv;O#bmRa(W47+$m>Kr=Z>bm!Hu%B{E(jPopc&3O9`kUUbPLE2t%q$9(!@ z&!S!dC#&-RnG$r5f2KosrpjVymA_#*a78Z5YRcxfV@#$YWgPIzOpzwSGAsTyZ(?jjy&rsQO-SiHy=fN`~+H>AA zjXi7oMX)C_9}={DSoCx#AMWp`;)DC?sQECzUt~VqwN%B2L#bi-;7^tKpnLjA@gYzF zd`OT7oFHl8pEfOOKE!G9lRGUGKMAQS{PdpI0r(l58WBI@`x^W_etJ9k5U;go{8OR! zWS*|F=c1=Nz@FPqk7&;Ttvw%})^0v@((>Vz@=!h$ou=Z$r1Gfw@WN@4`Ec!fDn9J# z8-@>G_LcZ>;K?J!hi&fyA3Dhc-YjY1fAq<;TWwwW1k4MCsl3Fc~5kJJy#ne+S6NW&s(Rqn-86}e0ahg%7@EORq^3Y zchr1%?9|A782OHh58s{=h7YSxk@)b#<41}QYZe0^I?DrIA!*^i_wi`?VA`Mp}x zf~U5R3Oq57cK~>Loe~i|JKxrT=k7jX!PDa0pg__P@!~E>@sDwZ-RAMCAb^Uk?DF&v z_0ei5c;$UwE+kfAyk9R_sNS#Zr1AYH3mv~- zS56aI7!xRm15P_x^76tm;^oSp62Q@x#Ge#D!%>WfTe>B}c0H1#>B-`AZ#9Xb=i{2) zc}7|A^he&Ga~1Uv|6cO`uPkcNrJWoGboA)rj|C*LZif9|ueR>Z%|d2@C*#?$^>)d2nj&LDzhH&s-FJhR}5pm6J`-8nDp zdHBsIh2+DD&Zy7BXPgxNSq;YSs(Hv$k_~_EAvsa645`c}^!)O-K2_po(%Qen5%h@D zgU@-qd4pMQIy|V&%VvVsu2ipoLi2x*(ERU+{`|j3WP5L&tJvGvU9vaH5y@VYV()ME zF!nO-wRdXw5PP4qM{V!>-6Pxkz?+J_$H?~fwMViyQL&fp5PP3Z4zYLcqfy(tDmlEp zFuy-GM=|%LWXaq?k7_4(av!FslWBcXzhCJSMn@f(Ng!3vuJfVetOA5m1ck2mS{U+I>)$hdNtx4a@^S@~#BC7u0nfTMM_c*4_IR`TdZC?kUU{_!j~! z!%WEKmfCZn;C?R+D@%WZT2S9>DVwdE2t{EZum&09Sivyr93kfi5LP3K_WIrvE)({x z_d{#m?uo$-g?%?$Jep^}#FYY9QE1>@e6qQ6SAscl6M3SdIB7EsE5Pp7gUk2Z!msm}t zzo=75ubo-h91B)?9lD87&#LS|0j3QWVSA-N*1kQ)Tvr{-&8SMOfr5dXVdp^`C?*JY z3zVRu04wafbQWQj6^emCvnaZruFhkae8( zuEK6Ydn=w!gAa^ab!RIPafYLdn z6bq$+q!a_CbW&oWl!2wHTqq49r4%TgOG+5$Oj1HAS)_zgP!Ib~l!96W#HN;hp~!Zj zl#X%F`oH6vG*5>+#-njc8Szsqu5ke0cKj)d4iD$t2A@Sf%qj5Cw+d`x5w14` z;+jZltRc_@b##BN{eDFz?>fWk>i(6#%L|Y@YYT*5WjtdjvU43G=fK~M%e9F^ zN{$cwf`FIr!_8s?Z(qss!pYbco79pXy`&xm!grkG1M;(@Y#+`s6FM!=ByX2A3gnlv zmz$AYxe2+XG9FaH8n~nLLVaE`t=~Hp8|N8FT{;e(@3I)veR*kEHbXhho0r1yzPwa& z(>;+jXZg6PoxnQf} z+)1YSda`2^H#rumX23ThOnD7vc(BosD9WX-W?U)N!tWd**(> zn%%=$bGF5peE%tC_vJBW0g44KtI;4PoeM3^u2FCel?HFDfysB)6}Uzl4aG&gE649d z#2%_W&1`$vn8K7ClWS`p0@D&gvTPu4U&W%eX8W%!bSqBhDoPk=aau`@65BWDqJIM7 zR;Q;V3mV3Hbwi+EEcC-ZC2PGpoRBj$--azmXgj|HM+82C|BA{Labsk0Xx?s|33Nkc zaVR}d3{bF50tz^hysNp-$=D<4u)QyOP$YXe9K8I`lGnxHbVwSRHi5{c}{V*rB*0 z0H~JGKi5IfFOY_Ye3OTFC5^&5p%UKH0R^d@jkXKCp=WAk=G;N+e#N?~a{5n`UrT?< z>`InN1;Xd}>w}J~b*NUaE9@sSUj0N}cZM~2gess<4g*=v7yC4Wi;g{3cg}ltogm5X z^?>|&ah(~fP!B2Bf{AxB=6VC4Ra0^x-*r=ZzH=AnI6;RQZ_*P@Z5wt9^G^UlaXpI+gzK$5H(O$yT8< zsDFt8&Ug>a){_WA9rtV{H?7L#$z0DlOM!eugC9$iL4%DU?%M+WRwTAmAi0S8ZYJf8`SuV$<&C#VAY3GRjyPO5ARWfs&c-x8u_M4*tVR0&FEBj|LftC za~gsA5S!PaJzaO`M?!rrLqY=FPq)R<8(zFOOuEMlF(hI?^Md{isPn>oeHsz$)Axr@ z3jJe;ep)a3^hT^8vQH)@Y&`2wEW=t|`XN2zz~=dnfCa!XB(TIX=L=;0TgciPujCvx zgmM!(hmRD7agIkw;d;QEntSow)Va_P*=AaU>pLu2-LapOwcE#^f^d5vjlW@yHL+5n zVf_7H2a4%f&`0JbH+8Hcr*#e=+!n}yrna1#ChFM4L+Dsn6LBp6h~n5kn$(W{`VqBb z15IkjdiA@AEJ6P{e1e<3#Ic*26vzI!8XfytlkC`4O|oOpZ=#OOibSf-( zQN{j}ibe1V$}NDqobwjYR8sL0jf$BX6^Cn7gb_+%1keq%xdOBSWmB3YB%Ibnk#MS_ zUmvA(lA`I%KS5J?@8iQgn7}Sjk7Vv@re*T8%nY0SdXoH4^WT$ zpebW^^EI{xfP=6;a9Sh!y0Ve_y0H=A@UTeWkoB+vho2hNzFzyV+Sgw-YT>ZCk;38Q zMim@ZH7an};zc-o*eE->qEU8ob|ZzureuV}u!kf#>}e)|k5K^6a#QbaAQnO$Vx96P z(M`~n|21fA z%PDC9d#vt^MetcNS#sbT4QdCv9#T8-`39{6r#DasPJ&OXu%9~6(V#eR#wv8+Lk+S6 zaX{+8#(L_&r%yx&vJa9u?8=sNm?t=g71BAZz&)JKVWUn!JU&9_uy-Dm=djguzJe0= zg-MJ~UWLKQ>#38~lh@VM5)MkujAqOI|@a7YR}Zb z9zAE)127gZ6dP0WyYDiW?!ueq(rS8h3f}Bq2VGW+BJ{|`%6qU9I%IaQGkFg13*c@O zxeHW>>U4Sj1A8Ze3!GpdRgrV_=|()KGDxK|{WTk)t1Dv`bQHJHxflh#2-n~!yhp19 zVxH25diXd?)@-Q8ffm9*e)BiYB7GQ%VMjkaC&2)oJiv}%3`j-CmvhcuQt!Isx)I=R|@k{84j29{zMc$W76s3&>1Kib3NQTn*1oVGO zSF9~e7LzJT*NiR1he_uxX%ww)dLLbC10hrtwoikGAKjmfJrmH!1|r2FeU4A|8!oEH zsRJvhyYT?NFd3^-v{ld@g=3~Uvo0An>Rcqq8DcOZ4VW}sS z(nu)5VzOdpfK-c~rKFO{bf%U} zJzFKyC}S=VA?|rXrRx@krP8wdf>dH8D$$M^?&tI*CoQBzVj>c$_`V>K_H`jd!cR&h z-v!k8eW6ZgA`)rmeoCar&esxYyCDoiuv$wbf4F8?ttL{}1B5U83@wTD$9{6-cY~Tp z^#(POnha_pF&ZMpYKW9Th_oY~{@%cT4Ux+ChY)E&JRuTTq$SescxoZmM-gDk3%SX3jksFbHHstI8cNQ}gym-bQ?Sw?7Cv^YFH z7Pny0vhdB8X;`#&FCmL3ghg-D8`T;Xt=6z;t%gN41~rQ|8PqKL(ts>l!V(r;xL3oX z^Y@0ZXb?+Tw73O}IGW^Q=3>1*C~?C3qo3_dWJ3#|m$?ZZP5-kqLHzC+9&ES}7> z>v&&0318Mhco;{bd>ks#1(cA1KtdVM`$kBC9+`kGj*){rA^}F)|4TCC8g2kw{|+T# zguykun=qoAYk0CSBH1;(r!bb_D2y<=hNlW6QsI0ap0wv@?FNN?a7r;1 zeV6YPi-())gnhibXD)#z`x)=LAVCw=PJ~)29Wk(8Hap{f7#-BP0Y2{iP)u5Sdk^H= zSW-)z)3*@H#8>*ESmK5^A@2f#rto2t2M7#-!vWa`fi5J-oD{LIrHE~W>p;(DZA1}1 z3C#+8@L8FXkMMSAO2Qt^bxMx|P{fdb56Det@o{FB1$P`3KZ~MJWzN1Vy?}ss(E$MO zy*C1Q-3oa9mvlZP*1iEH)v^H|uLh<6Mc$Q%M^R+)nIsc}f)kEF2)71=6_B_bi2+T> z1Sc9a>~if2iozng9*9YR6(o`b7{_Q@W5+z7s3%DkZ_|eA;=-9pa@PAK$I&G zlG)#@SJl&F>ea7aRlR!k`qk@~nL7U(3bvwx+iBxGrotOF>5&+V z+!(oeSD3uYg1^s}(cgqJ`^*&<8P?LCztj?NJFPZcS`PqMtE+YxHV!q=VdW=*(-Dbx zkWd5uq|$;EJ9~O{_?^5Azond&9^b4?sR}+36R)lDimKsfnl5n_Td8ugD!lq|3|^DU zNqxxBkN>FBD}Dz_zln&AU7%^WTl-bhu(kg*JXFtV_-9Q`!^gD$^)x(Wn1(-j#5DZc zL(>p!$pE4Q9^WMV%%djZYL8(OwmTQN%M^Jn$v6p{1)hYw`3y9>I_Ts7v~T>Mma~gZ z1U&dJMPPV5EuMS@FrMSM1`KUHZpkUJ=W^2_`*txEvU*=`8hrSZdB(jLRE^SRM-8L&=2O8&DOaF9ShKiNw#k2GOaVJRy%y{{vbHCpKJW-9;wmk?nlz&LeOB8G}!HyO1m9Gw7_on z_E@&tEo8f0?JQ-t6L&ng7?QR-twA-~6^rK2aSC#S!gjy(6)Ar zu+P1MA8cG`N(hZOiH&kdWl(B_sBkM@ukhPR^7(U>;S;YmQr+2_>PY07@1zZoh?q3P z4PBCEP8a91J1kyyKx{6vb8cjs8ctPKIU8G$g_bYcIrYp17^^F6jvGsR0@C&wQ}75# z8w?jtG+>+zIcG4&JCKz)F&crcYdjcm;jX8}&4x zCkwe%Fg4U_FuP(~0e-5=lBo&e<%5}H#UwoKGuq+7?3RQ)J;9r`T1Yvv?7I#*UHd(s zO(PH`bj$Z1*mcnxb=!k9=pa%z(zv^Tas5CHz0_w{T2C%ModF?gm<5xS^;*n=H<6pF zn!vN5Do@P$HwzBhjI*HA0|Jml(^Aln`T=xRDbL3?o&~s|_(91$ z4JP=k*WZsePNXv+VPtB4PYsjf0sL{`F1ijpDx(19kQS*4J7M|?5pUa@rRHxlc4=4K zcDTy0ym4L^_q*R(XHfcP#aq?N=eJ;C5^+EJzzH7~gdMjFpe}xDUooWlXgLJL@V4ir zTlER|cg*p+6V9GH4|f5H+lH9x-w|F%!uHHew5zFkq%O7m`eq%lT&7u0hq+z2y*?QY z<5vhWw7cH8sbQm?bZF{YNCnY59=t^r@10p$&f8ln7JO|@ZT(b>Nkal)N>a*2U2K{a zlZU*pF!jB(fE4;_?&am0qrLK0=4xD4E=?YAa+5?OS1DH@?Ag5hdPQ=?v}#p2Z&H-)Iv zT2$9>B-m&62-Wvo9gtM4t;^X+P+dg}jaFU5)5*jqNQJGL6Z0!bBCI#^X;wf!1)nw_ z2cI@kokzx)aRq0g(^Za>3z5!-Rh&(n${n8xT={jIjifq;*87BW!f(p@@e2w50QT zhn6Muw~R?^GMU%aD=DVLb;i{xf;{!??-}6%a8sLgC4$A&wbrwmI99l*w<;t z&((9B-W{N4yC^a!X&R;LYJvbiGXlUf^#Qw%+W!u`t?@w_d}TyTDke1m;~{LcR#j8a z1?IQ@beXg3#@B!VGWHw50z@fGjl@=;{sMv$I>gg0vQaG~Eng$wwyjKB}Ot_;&t88YRB z#7l9wZ9^!lyLSN!(pqs-+<|e0u6?3@ccT!hF4DUPBs(A=MN+0 z{jJT^>6KDZI)10U+H`M8I}T_SiOPkgwUIp#ATldQ>bL7Fv7>=l@i(6Ub6dNbOhu}{d|q?%6@UOPMvM2bvps%vUW<( z^opTk^RXReAQ(jVM`EBqjoaR+Z}8Wj{%RxrZ2RTuPh|S8I`mr$`qPZ`#h@4mUhs($ z$>+a8uwaDnv6B5Y>R1W)lssF4lYQ-Qz9hUd8mZ=d#w1T<;DaVv9y(<5kL&r6iLmjZ zK00i|*??rj@A0{=nFqO!RCdlO;i*Yy zO1wBzQgR)gDM@rfC=pL`Pgt?#>tkPW8yI#W<8_XHm**&K#~ zD3!GxXG$Cp(ZcI9zrvpPl%2N6$E+lnR|&ss<@5=M?1>;qzMsO6zp}N@|Fgf{;Qu)d z2V)1h`$_-vG(;!n(WqnXF(g`>P?Q`mF-l2 z36hf}eJ5R95Bh2D=9YBwFWu&J-$L}A?uq1H`dv0&B!?N?OI@~CDxy*2P%qWicoUu% zht+juZ6O*l$k>SY+EFEKP{|JAME_f=gpOrhdWa5s4euqmRnUyNHOPHW7Zax*$YxH7 zx+MM4%?$KoItcp2Bs2Xs_Z#TXX&QvSEdc#-O%3#yw+}-9xV74=(|^5r5c=nNg8t^X)#-F>h{PAZR`ExNCf99{Q@!?Nt5rqCPnUa2lkA9052Ks%2 z@n_`E2Ks4HLFm7x=r{1uZxdypU(qfYe?Is6=-(5J|H+E}wLbdS8~J}tF#I3hYxVJe zXfXaCf65P|O%UK8A>b&AN7tt1XBE=;nOo%QtzC&m&U zrL0FuaY=kMwwK6g&t29EYW6X*`Syf_W8W8U*XKAW-)a6Oukb3dul~uRdRw63X z_*E)ij#d7Oc){;idx7f7X8cO$kSse-FYAb%%mv}-ikpOag0ldBJfo;6xy%T@<#D~h z0UAoni}#H0L|PsZ_Vbu1uX_tSPk6=5{G7dFdc;bXD%pfcYhWi$D`)e_Y|#O;;WqnN z3FWQ#iqF|elW(38`r700is}9;KZxZARTg&Ap{G z-QAD?+WLO3d&Qh{xVrbkCA?y?4PG%juOU7z=N2iinA13j)reb&`EEFqi@)=eMcUvy=r4xUD9?|nF^Iztf-QHW zp739eo-r{>w#P69fI*g3~B5WXhA*3u=YLErvl`L2&Wx;5Y1*1I=yiKye_D95^q#GGj-V!3W zkcRRjlo0v^HwkctCtDPqtBa9Sl5#w~?uo|}LCDj-qYxyx@Ec^O0t=GS%J zNr5FqQXN6!44lnr5+skI4&2P}6C@e@V2DYOj4%n3ktRVh+9XKEngq#sAxORm7lP#E z7Ly>U+~Oxlq9a(4B-Ifl-6Ob#NtYEQ&VYhMwL3!>BtvvTGC~(5BXvPCS{Ec^bwM&d zpdd;5b%NwZe?ijGC`hITo9LbYY!oE=`Q3EmTpu14WUddMVZ|)GqZe&R$?YN6BPsyd zFCVdidYesLOY=>{MpQxDCn-%bjLOfj=0R#$cRRCxt3`hUH##*&yz_K=ZgKPAU*&t4 zf``KXXsL&xMishF7Amypr&X&Rd|EX+q|Ry8afP&B&M$`rR{QHIQ+$E+hmE^Si+(I-7>wV0!+stVDDACqWZ~ zLNV7Hbr<;IaD50S5_IOPmUxodpXS<4@d?s99?Lp2$a+GM$ST#^ois!q=ri8Pn(@~V zYdQmkjag&U(oZpK&;@)(C#Qe2GI7GLe>#5c`CpvUkA&Wi8@d6?9Tn>^9<#fpOh0Bv z&v8ud(L;2M>>@8Wr|A(y*U914AP08GAj07Xv! zDY%0*=$KGpdY9k|8|}#t88_PbC&X5}+lFX-U7WV|+H<1lZOBaV1t#GaHho{C9w{|7 z>Yfy1qehk1ZPY6ViJ|+^sK*LKqxzJ}M)fP@MkSQ0ru5ZQahyV~73)Q#HYTG{eSspa zAQdCPlXdO5!b&@RJEkBBJ?fD?s!9K)kCMrM>8$i$It6#2O>thBf1fO4|E2ektB_)R z;;^01E$G|K>Es)VOasY*DI8U~QzEg* zL?GoF80k)F?i$$Kof7RD80}7J;~Ln;ozlTIu!B1#)-^B|Us87s>;lIHhs%?ia1w|& zBjn5sc3?V}c}Lv2!g_l!F-T?mc9jYbrtDJoVA`G(Lo44?rMd@G_c2HdZrKJ?%0BQc zt%6$9vtF;#LA6hDp&G2kdTBnXuDGtUxv(b%?Tz+HfqUcddhU%6kPGts>iQ$8p8b)% zUiHW1^;g*+RrXm|*&j)U{be*9;KEs=D@6);yd+h-= zvFEzP#2)pjoY-SN<%vC-eg*0K^wbDWA=fu0Vq#bJ$HZ=Tf{=Yy!pOn|viFbc$j+CT zkbQld5!tbly2xU6WEW=(WE)B(vTY@dY;%divr$j2Em6o)7};P#7JHnK#Ytq3ln}DW zpLArS%*Y0qkY)HGdv3o%Hky%jRmg6Z$hzrxexs*ib!6|Y5y)2d1G48%60%p;FtYMB zgzRrVo^4oTLbkNO5!s40b&;L=N+H`kOCWo3jYRhH8b{G~InkkUotB^e)k=>``8L6j+>BtVO z7Ra<)fo!DU*|yb;EJPsl_;_}9wF%k&L?bfK>bl6T*O67_3uL9MC9*A|28{dF3Qw7y zDp{?NB{Q;n2-$;zXE#e^<5v^1lgD&q_nMIhivvjdP zoK=jhaFxRIf}WbaN+D~^$XXM!$AO|?Xo!nn5h*nv@j4R30g0uCm$isShQ;-WAyC!o zQ3e$>FC2=g{^)Jvqx?6I$RJjm!&y5G7Y+<~LtdP~Z3VzJD}7w>HNQyS#okf8LAHIo zl7l?V!!NkNK_1>*sRw!ZB$t9b{B5N!16HbbN3G-_4}afnLkru)$5`lvk@oyq>YS<- z+&OV8sdHwY)H^43rKxioCKx-X<;uF9GkCY^oErfII8UvRopTNe!g_jzg7&kXs$8K! zPCyEs^GsiKjztL0`y{egRuHo1eaql;D@@3qh&LjewxTYwm0v1k1v3P)VG7wuiR=$L zp5c0Gu#Rj`kwErCA0P`C%iu=bv4C<7e$xv&ohu@Mqcsr&<-5Lm$9`WFsAIpDchg*9 zu@YgFKxkV_e9v@~?kUUH(nlrT8}pV1Tnlk>p>SBIaK!9aBp^)kH`4x8(xeTfM=* zy+0DNyyeWlPnQe+tyoR~pE%C3n)>McTf96F|K>0Ec$I^Ci(aEGUne`%M_-)dTQq~g>D$58`}%~>-7^Mb4X;v;O>slGb0l79+h+kXpW@j!uye@0Qa;gB#Vguz}>*#HUL z^jQ(2M;NRK-zzIZA&PjiyK|%*X{+({*?q~&R+?_lRI#_{{a^<6Bc8h`tNS|h5h1m` zqR0a=vTVF35~<1$l%-3Sc%4gB&hbBgLH<%zXqo57ZURo_DnSP+&!#Uy)c1bHo;TQc zkZmB6%6hNPY&`9IJR!)bQtcjj0PckU z?EGe`WM|}W_0Wg;NTPd+hMAo`df>Jz(_T3Vr zdt7L)Z$-PkQDwNR)qo3yp4K;oSv)Q9N5OXHmUi*HT*3XhkSJ}H7lm}O1{Z=uCxOlL z&2Lb8AN|>dKaycXZV?RYG}O$n8Wg}d#Y35|8|V9Bh&kVGk*_ZvdY&Q5Q}BnzK_c+# z18#`KbV?1lL%x&#yOH#ypcKuAL?Y1#e*%uN!^p}OetiPGCnvxnT*F@Tn&(1SP&k7> z6kpC$Nmoa^@+4DH25Nj!29Q)UNiRdb)`w#nO`NVT2DvtoYoWv8PHvQr z^JF)(Uty291HSh^X8*!##SxF87=fvcq-)`~FhSGZd@Mq#EI(5t_@@#5z~2avb8bgs z8K3R{qDKrS@kv->%!2_Q!`BjDZqI+iDs14*e0Xkc-VW~GxDEd) z*9QBF=I!$;vW_LY&e2O|J)~D^*OT6;0@Rb7%co{g&4kSa=0y=2^BIIy^(JMvcOU@l zWQx=)Ci`3zP?-m84vQzptf7E6~jC zARPsyGnt*z63%3Ha3bjNXffpvgAx+SpaD!@;EGfZnvC=8%6O5fRJugq zBHbN|S&smQ=|F+wS(A}Pc2~SN>JeOpB9-pO!^D(`>^AsN1Two)&<~CA(EWRQrAGTQ zLFkus8lTOH2JC(foP26YgnsZXE?GCB9OfmO4};+^j)YV8f#r#C53$d(QuIRC(RBCN z*a_*b>SWiJ)O@I-shAM2ij#nNhd>g0DhcC*bVL%x9IlIlTtC5u5IiNLs1x#1$^!(y z=C^-nD5$uHk~B!zHSs&AtJ>)X6;w9Jb$pPkhJb?>%@|CMh;-Lu)Qd1D*3tyhwo;T$ zI&3xq;2i}MteCJ#+3heR9EjaAxgaCN8&&fg)3oeKgC7~d50ud+fI{2>?IMn%D1M+v ztn?^bOrSe(45vyTUpV z{DvG%!VrK_Hr;htx_@Joi=%j1A}C2Ak0@OprCB$l9bKV(C)Dzwf!^}zFV9!WjDQ5|pIX3dYlD3E-2sU$S0;jWf3kp^+934 z?~RU6c8}Kq$>5stcH1F9-Xg`;lA0cC!b-rO3KHMD?SjtYq;wZUU{6FY^zIU`e2h`4BaBh0526$j$OC!We;cka zcG>eR1E~TW7nbonOSgv@;(0cpa=@PWbVC$?R)lkID_I#2`**pEN+7fb#uMnc#>7yIm=j|=&4^_OGWmUYXu^M@Eht3=9zv{=H<|r~w@@L8-=FdOz3(lOso?=!H@x`ou zW08SB`8OE(GiQ;3KWN^Lbv7BX`t8SrHRtg~l3SUJm|IUS(rsUQ>S0bH*U5##nsccm zmY0fZ>Or2unE2&@r8SaFLQ_Rk5H|YFi>mS59<<#xNXoagg7lB-kCgLiCbtq_vWuBx$D|Zh-W1~*v%H`(;DF4-k`j`Lcf`56(uc$xz zqEWxE!_MjpzOjTQ`AYo!{DNPHT&GjGa(^JS`@t$d3X=>Ju2}v~&Kw%IjSj3NK@r`G zrsryX)1dcp+o>pO@PY^Hzyn+M0i@of)TAhteYh`s(S`rr z`gWcVR$sxLA)dk<=uimeJq)PCjp_2nYbfR^i`Jui&-%KWRjlIn6y5uk1w6yc@tZYG zPU|fT4Ac7W?TypAWPxE?Z?>(%CDOJ<3k<>0moCu7-s*=*?7hi1C^q;4HJ{rpV6nGy z4T-%@?P4rq<-(SPLN?yxunEVp3$WW_ZS05dtJ(9b#dZg4DKhj#23p{gXmL%Fi>g!p zrdxzAYVj0Paa@a&$v=sJ45=^NrsxIEI@5-QYa?_N=oNRXrA*@*|mi8jU`46_p^ zoW9gv(0vee^D9940DX@8JE9!Hl$i)+9W%BcHCr&fDmNNR)s9ke2-WIT*1{?`ZRy)@{5z^~{{&;AnH5B-dO zYZD`fK5cHBfL4A5#>j20V=P$mL8Km|MB9ygsHgIEDZf2Cnul4?mN&UAGcCTh4A$Q~!sO@gAp|=|f;(=R}ViJU=7 z@ept`Ks6Rarb!(FWaw5Sd*R)`h;}u1x(CW$$b1Gpkoiym?)R=A_usiB`EeI$S1!IR zbJqzF*E5J{5kSREEK4!BcFGl*`-IF}2X<2ZVE0q7=T2dk2t+qrH1x-?04-RqKm9|1 z-fD8mP<&K?3>_{Rddd}`QG>n4{vPWM*x&D08#A4#4w$J3w@u$?Hop&u5GF31i$)+S zlDP43z|HOd#w*d5(9Gtr`r^6S>g!JKB@UxKn)#j6o!kX$`{ouTRvMqjk2>7JfyrF3 z##F}U;+_=}WPHL96#CHV=9&@*_oDLlTzJ%5=rK0Hi~C^%j7yMfUXFtRj6ie0xkSc^ zop1?BA*VZuYaU|H%WouYmdUO%e%%UheGFWqnIqkOXBRBSzX#3A4to39Uq7{x!S-=1 zjJ@gkdo#+L-u+Z05JeNB5aFi0*%6z9??0E>Us@kuX3uZkm|g<_O)3e~mgpe?2pq^@ z_s;=cgno*f86dPPQTjWlyJO4K@CIH+s*VfelR5lxhF&=1#4q*VMSl7&NjZw>C;h~$ zGK2%x4ugSG{*sKJ;N%zzfsUG#%RG}Q&%Qu;__mu3*Kh*;_T^|Y@O_3KignG;@_eUK ziuu70`~WhWt~`A8JYOB>t3tjy#aDCr>I`4aFdE)NN@`P=LnQ6HWNL3O?2}r<{bCF-IlOmjv_#}(k z856J;c&e`nv#>1CeX>uZVtsu85tg$UloceMTpM6nBr`4B78~#EL*MhV)s46i&Gr!? zQeO+x&n9NRfLIH3E)lYoV=a6G*{JJ&-EHzyjC0ke7%Ob*b8x(_EsnU-M;IX&9V|jQ z+7jaIj|WB(p3^8kGx@zum&y!q#et6lEay?5Pk-w%Q4_^PeF-%fC+Y{d)+eg@T(z91 zjpIvbf=2zk20!-Hono$fHjMO5(!DS8DZ}aT9b?%jCJjZ{)#dxbz_NRxQNAw?oM)*? zzSq7eGHiqm+$L!`Myz??^^@OcZJ5D_nFU2V?C0; zR$f9u@|TvwUtnqZkwvcWS0^tQ)>B@33Y5G&R&e#FQLb8EE;Pu?hXDYHh84&t+@k?% zXn~NHcNPd~nOIY>+-gzxF<5ac(i@gJ3X@7i3y7nGh2h*ru&a zCPZ&p)4;8QZCcRTc_A>J)favp9oGdKO0UmmN7XO!3$^Mr4JCF#xbB)5^!57LJY+;* zws)^Fj;5m7`e?#aEUZJs5PEI496}3c^AK7HJ7dwnJw$Ma?y&P2Fp>c;W; zza!)L48DQlQ)jACVV%jN;&lN<`Qaz;%O{HiIztnQfG*Cb z16QoIl}^az2EWcGJ}2aGrFyAnUG!&MRDpmZQ0r3!YW<0p_#Xd*-ITRO{44YWZ9Xmv zuVkM|bA1leOjclv%);v+rn*x0}y`9M(5+7!mZN(7* zq6--9wFwQR&b}l}>g=yHFzf77Bc%RvCDk1qDz;KC`#n4JPq?D`^r6p}Cx(+~FnEWY zklrCneq$pcA>*vX(EB(cEg)8X{vj6|1j1jCLa$~{_Krr0^3182cHbk73Ar~60YgJ* zw;*jL_eN5V8^s_l!$Tz4MnbvY3u8~e4a&BHr$Tto1vTj>^6FBfpUA;bV+iz}xZ*9A zjOg?mbidr8RCa*8(&Mc(sk6_xp#d2LAn~5>YO~bYX>3Q+mOLgSuEGEWqRm!62;WQqmB<>R?L>(jy=%d_#mYWmh0S0^K{kOau^6dyP* zJ*NGGte9`fC@+oo&EV}%&VHVB8B~8xpuG&=raU7!B2|Dqd>c$79kNNT*zDAz2J)xl z2V><<=M>+a%Xi0<6@-(IA~~Ov6KIdc$xI~Q;^btP3cPq+H(k9mqt4c5Kqzl#0NE5m zB~2QxaxS~EnH_jzGh1!F)W&A6zOk97Z*1o48=Hmt#^x=3W3x2i#wN$#a5bXNb%qdV zfAnp9?h)Gbyy?8y?ZmG=zoOc=*j;O-#qQJThQ;nJt8uaW=XBFz_jf@FF&C#B7P~j5 z>x_veVUK*L*rJb{{RE#jdr5W*O#==1^Y(Cov;G@(lr1SY5x5fx|t1 z70ai)%e8w^sQ&Q=KK8Z>U=Pd6;T_&5W_y?(Mi|~{9D=Ql)wszE_uu5@)N_*;nxi&( zww$Zq$Tg0~3wjyK1leom(88=<*@rF+~@9~yE-BN>I zODD5rp}xQC;@jWN_U-S!6JPQVpT;BdcKjl?j4+1veg-wlZQf1O3?s6s*Jc=z9i|yZ zBnH&T*VO^CThfGjUOiQ+=hkVgp4U!Q!|{@yI>#yGdKxLX9Jy**S+q$PcrZ#ItiD#| zgVo)te6Z$Nln>VDa=aE>#kP*Na8<@F92VBy(g{n9S>C2N8*ZmU$e+DmyR9bR;Gd(@ z0spQr{?G5%sxI+-=7cNO^y&yuUu6 z%DFh;-8|evC7!ee@`e0VRAxL|Yn zw9kfhv`@eHaU+br$o(5!LH{gKHshPZrT)1#)U1Dc!>>aBBpY%!SJX}2qB)dtL$9ux znd56F3jOR@9sO)(xYW;%w-2PB*-k&q_TD5`vVtmUFh;7gE1vuRNM)ktjb+uYJh*B$ zSE^=0iNhaHCW@wWU`<`O<*^=9!TMQwkN|1^`dR!G*3S-=|JU`iC#MMg?7k_1^s|g9 z|G9p4N!QQ9r_|BU9RDZ!Syh&de~?Y9;Xg0=^s_&oZ-vFGZMI=G468D(hE1~#t09&_ zTK}@t`j;)&zbsupvkxHs>@43v@t-(F{<&GKpEaIK`q|UxTJc+N3PTBRh=9w~qnGNZ zqhXCQscF3~8U0V|t*?2{*3s8UO?#WP3a_6HeyTIH?FZS>7Nd$5dX*{~>t+vUU%hTN zO6q2*ap%Sv;z0pp15mBMhIS+is@Xc|Xyr)YJ2+qnknPA48rsGzp`jIJnKiUUMh)%G z8e=%9Ao|%ARiFjB3be;h1sb87j8*^EQx)1DSQU!!qj&kSroB`XP?K3-f7`KkPhb+$ zeg%ntQ+L&|PXkey2~H?32@o_u2pR|;7#Yyv5Q6`KONIn@9ae^0fMCHPxJ!^>!!0lz zhHs4FZsU7*SJv)VWLUy4f$gq$*X!4}_rAWi-=$I?b-U3ph60twD1j_tl>!w&(TIx> z`?)<`Wj~ju3-%Lwk(HQ)HWWM%MCayL3b6{UL)Y|au3jfZvxCG=R>qK}*<#}q+Bnj1 zKEf1(Z6aFE77!i%u{6gXr8%4hVQ%WS647n>Ba~ai-;epuX_j9zqB$Cd#=T&IT6Q_Z`B3wl< z9V`U;oXTD3zjkM#u`LgcA-o*J#3YyKL*pH3;q8FXeRJ1@``MxCy z6Uo(xU{;^=61xL?h5W)Oze@^_nnk8C$?rw;CAw&y8GFZctoZ1$3-4ElDVpaq(VPmu z=|aK0Z~z~`3vc1xV<#*4eH*A?Q6H6IaQY|QE6+4nq@BWQn(nO8T#SMfu8C_6llI{2U`I!{5i5uc>EnWEI~3 z+>s$sla}8g2ZM;RicfK{inH$$_TL>x|0(YTE0KYkvOhQ+<>Kdp1BjB0x@$2MY*DS2 zUhA$uUL_>e#|uKzECfQLVH(EX@4TQ1Cx~C%kJ5)lqJEAxqqAI8E{Gv1qXcId@GbeG zrXrtG-yrQ$HOe{4iDo6VX_r23s1!MfjeLuLoMb}%kIlM_%1twEwNVBlH{!ickN*IB zV6+QDS5<_rj#iO<=ql;64?>UsLI)m^{BhhJI$NCG9opGJb9d;>&)prmU;JCaY_2EX zV79X(4`!|Rv6rd(je1R?Y;(uI5z0#4>d5jDr#i;uBie&8-D>K<(3Hc zb??*l`pd7^>jzo1&)DH#-(OkWUcJAvw0(Tn@cyc0@Bg{`EBV{Ac%Pv?zrWJ)9=m!^ ziy~|-Nqctn{-#|P!VFjMFUh2XJwIupVYbgdaerk4dIYxLFm~qJ-1^ATsxu!jS~&9w zm{Qa^pA&xgnASO86qToAobwh@zs4}JK8gJPxR@>@gg@>p{Bbv}KfXT-bJDH|(LCzF z>*=2iNf2PXy6Z`U^+V~Ag|_rI-pL(Bm_P%Bb$oVu6z0JT8S;FIlch+^{N+flFJltL z@x1sxIWJD<#TTMDm#z7Tq4;7d(#V+>+7+DDmOEfad$?d^IAq1M2G#7!8;AxuCZw_! z^p?FTRsW!TD)_;n1Z2}=h_EPcE!;7>-c}YaviDJnz(oPesH<*KwG(TkGDVqimBV&x z&9E!i`!dQ|Wx6=iaR1VnI~0qym!S&NEgAYZPl+8B1QCifB#|RNWjrrsp%uHO zBeLgVPCL2|QqCx6J-4!W0$GwDQO*v(4RcO3{1*O84?JDd%?D`CQ*LL%j4DZTV9^5mOt|HbWvtnLNO-t{o$&idZzr(^j-AOj$VUR+uEU9=1)lpa5dy^C^cDR~3 zQLm!NMz92EC@J0zFMFkD4r%LnEA10_%LVt3O_d3sq?mTr6*Tp`3gBqq&bL^)2Q!5i zwYf7pTwd#h5s}ZIKfgJb>rB33`0EuwN9CF4VVaf0aKa_~e#eBy=30{deWHC1*G-F~ z;7tet+e}`jrUhJ^4i4jgX>-1WM=vrAK(mtoGHh7fi13-F)%xUhwSB@ zz}GebvTe#cyW#}GBhe$OAtk%Z)0ret-PnL><6p&D_!_y$PbEHSru*q8=X94DNaHN9 z4p(mQvycnWoWghP6)%t%P|=kme$}69s}Bgdfmt&II5n{|<9wcqK^ca@MPN`{IjM(X z2<_Kh>d6fa3}6=#AB44Nl0KX1u)Taw20|kfQy}XJ5Qd_?R!A$4Oh$GKfR4fzU?+f> zbb|wNhyfAR>+%V$t^yIC1^>tDbmc@h1D!66Tbrzwt!X{?I-wT^mZX$65hVxqX@q)? z5n9ySUgxN12kK3(tEiXJ+BA@ltx{3Xt!9NUpew5j22!ha9@I@H|MnC1ml$mUSD)+g z+Mo6MH|#GZHVD|WTsOAA48yfZG`kkHzdG0AwZAOW#8Dr=mcagW)|XQt0AChseTlBI zAkqDr-umFHU?*H9*x3FeR}qvqoUD5eAAP zBBKNdHT{60=yz$T_5sEmqF+8>mk!_29U$e5nhem3;-TNf5quZ$O%Vtx7$Ed;a{T!1 zyTb4*el=eF_Fnl0euuB9_?^FEjNh3dTKqCy(c+iv%D3^GHAIWwjmuH|&RzaB{ANxT z_&Khq_!;xhOBN<2wyaf3?8j%5ww)nJ+w8Y1hoI`^fwTww*&q+Q#aoZ5JRLsjWe7AYHP=iE6aTutGMSiTL$8ni39` z#DJ3~<|phjPFP^bU^7aW5L_`)eQ+g~w9mp8!}bM|tbarK1FE1wW?~QmR${lacst_d zn>DhwOWhNaYY?^l}@7FwFSElx%3U4ykTlx!z+W#iGq!B5uk5diMLIrJD#=F zwh-FV>=&xDm)E^v73+D$#XxCP=+Sw0k&U)qx964^O?LBsFw2VAqmg-)9i%m{p%=Ag z*`19$oy~>Xu6g_^B8F5I9_vsOH%(Vu`#8aPpwEozV9U?3hb{AoeoU;L=(d44?2@1P zVNWLc5^0Yw&}vQWQKsabH|jbMKxejinV90kCwz*N)%jfgMDtnh!+g$sik;7P9~-iv zZpi#JpCt*}j^}MN=-Pf5fVTY-v~9`TUbJf4o~E|760|MJ+b-6%T}*9rqAgPcS52W} z0!-d~Ve77As|bQHfWyPxHMqMw!HQc01P@ZIxVyUsn&J{%idzX*+@VMXw^E9}LJK_L z(Uwx!+3yZ+ZupN}es}ZD3cHj9?(3JkvX{LlVN6CCW8(gZF&SZw$+dVIljZ7N=jnKR zp6&Q#dG^UHl*cz7d1jb-_V2^Snt_d#G$}ULvG2yuG|hUQbiKkO?dL^;i@npaKL^dxoTP7o$7qS&ef^4~UHtmq@)KAJb|DMWr=%>BjntQrlZI&$s z)te*i5ZH~(KAoU!XCqqyGuzm`$W{Q^dOMJ9+Bak?U}np&vwgU$vSk(Z;_vEud0D-| zRPTM9-3G&sPHeUH$etE+IXz@<+2QM^4C#n~d)RL&dI2;dca6IG-{r~Zhdz|%n@CvaW5AF1KS5OwL zUnj=D&A;QJydvrm5wy}OhFZxw4W1to2Gzy&b|Xflb3~{1{~?{y;V_hmmd7 zPGnOL=#`*f$hPS-Wz!DX>g#N2DO*+J8}-d^wA*P+tHYSq2Ku8A^zk2Xzj_UgdIijS zUbajEQyM0On?&vAa9B-Y{lB3m>`U&d;`{Jk7{u-t1Bw>5Q?qW8Ctf#X0 zkF1TJ2k*dAQ4i>qAwSXDnCRd9|F6iV`*?X<^^scCbKTbUinDsXs9xANyS)lKpV_Kp z{gu`COmjYhwj+oLGTrX8|P{6^UtQ?_BEUPshR&gxwmsp@$c^?d)%|8~s( z=D+i=13*xa%?NTR4nczSeu6jW{}$%o_wW3F)$@Og=Krgh|64j+_c%8Hl+Ev}aq}{> zUD}3hGt6xM+bA1^z5*dz3L{%(WW!@*D`I3DVP_BF#lJ$3?=ktH(e=B+pHhVs+9<yXLZG&R%nl_R`2~H1n|-7(`uJ@2;od@h zK<=&G)a50fFcv-0WlS7wV&}dhDEJ)H`JE>D60M?|fpT-aK6oLjO4w zo89{d>dpT^_27+qF*m3l-oXCX^TNdbWv6-&*f*&6(T#emv4Qaz z^%fZQe9d}0K2yDsRBzp9``FLAp~rrO+dB4NhC0;#7q%m;7qeH$dMK-xm)S?p&FCZC z?89?2^#P&({vUl*HTnqpNPWPY`~N!ifj6-KKk5B{o%a7HvH!2@Y^T1n{ZHBag!O@) z&Frrs%1#otH>@YKA!I#;tp=Nm*&SpH37Z=B^*5`XNY))~w_(?`J;-cpvVp?-!%kwh zAlZ$=&Vy~t?DxUSjuN&jY({2pkZmGtIoL;Et#$+1+`>9ww=p}CY;=^}KDeswFlHN* zy(sJ+*qY4dBD+f1X|PF|jT)rvAYt3V2Dz;E0@=F47K2^K>`Jnkg^jzSZ9irQl70Kp zZtuXBWws940AaVn#(c5bOk{n89S3`f**61~?JaBzSRZCjlC36eKG-hIE+Ly*Sl4B3 zi!j@ZY}f~J{=t6w%+5cu{=%+<4PZ7E*?GbagPq0f^8w0s71jf`HM57wmJ>ERY))q9 zl643hc}d$>pRBeE*$0t!dlvQpvt`Nd5q25uL}n9{ohGa|Y$Imx_gA)^u(e<_FuR*< zF=5lgK8&{7sbu4vb{la~+pWyDC3{EMW3WS+ElPH)u!~@8FdOTw>^Nb2z$Rh#HrW=! zR)W0~Wwl$#<`XtK>{@2Wl6Aee+b0*a?aOR)vR8#Y09%IHykyr2I}7&5M|S@Aqw`^2m(FtZKFo)y*)Hu{6rW+S^y*h#P_ znRWJ6)>~Ll*qO|pAzMq>La?ovT}n2+uIG2(~)2k9#ZIQP{e$4rce0EhTJb*xT=|b|%?`*LM5%jJ9i-?LanE z*Z|l*%$6kUC#(-_X=Z=*qWiC~y9wTQNJ5 zY;=UU|DMt|E3=KsUKDl@?6WuQ{!4b1u+w0-GaJ>D?!UsegB`-`1+sO8Ee2bK*_C87 z3mX@p?eEv@{!8}lOL6~&y~b=EvH`+wgp)LxZ~ohr-?6-3vV|BzHha z0tABJ0R@5JP=W60p0Irb4>#HEZ~E_^9d74#XJ?7sCD>ia7BblM#10kg1Z00cHpagv z6kAoWEsza2*yF^e5NttYHyi96V(-L>@oy&_{~BypV%G`wHnNQjwk)yz1iJ~@+y$BsDuHQ2Vqb{6baWNRC2 zF=7h~b``Q&4L0$ZVn00=<6mUoJTk_=#GVsuEo9Fd>~>;j3pNX~OAK}_v8@E_>44*3 zgKbJ|pkU7-TiIaq6Z@owIMszU}g{C1X~o@?J+#l#aG||3FVnCbP3w=fG$CY-Qn9j`g-%=7`)zG2^@QR7X@O#fBWm=8DGH)*XV_9MQ)2 z-yymN5iP#|P;a%tP9=7YU_Z9O?>~cWOKe}kUPZRH!4@O7v|v{uo7G?w4=VP1l=%Kb z_DvMFPt&}o(*amF!}+i7nVWs#u0R0JY{;bU)PSM({MJE!?%y-L=SplRzpI4bENy6_ z30B0WopFQb!Re+Q1)4Cffx4c7QQo1pB9}FU%S){x)EWj`nAp$vt?Y7SGaKx$1ByK> z*a67CzR%rPcM<#f+Y^lA&e|SCM z?poPEWLFyOWMa<<_CpI`2i>Do^Qu(mP5-90f>fuQ2wTBu!|a1_G<%n_x49_$Ant2F z_F`WHP_;((d)OK;VV@_x5y{uRbZt*W(im5@DduKochws9Y2M#G#mLVJ;b#^2QLiG0Nm^q7_;a713b_xn z&l}=I>}msGH%=;#QV)rE{zUzM?s_u7#N2hT;LGpl7EA>ed`1MH3PA^1M(?pcBQjtQ zFJoxcF5ZB}XvVxD>LJkudxR)f`vgW%`!}mBwNU%L8`a7wpP`rLH}bF2IOV8(p^Z~c zyJS;%72#5&4SeIT`4lZN-0rC9PYuSMX|M99pC1l$VP{$i`#cw2-Uo8mZn}BlvL1kJ z&EP;TGuR2lW)$qZroi@h(LuMXgC07C{t6<2irCD8U59KAgY_f!iBnwvLiT-xas6wjVs{9(F0z** zc*>n_lP8mS%AKBn5Dq(niJ&QWH27=5N3mHL9!280uhrbiQ|@&8bO8~V*9f~kEhD_g zCGS_saY>M`9+&i*$P+_JF*1S_V^zOFkliPSqx+jXzQk*h@QnXWw)j7qn*C<81PBBoW_0P>Q)w|UQ^com!5U~pdy8+pp2J263C&3O!Ha^Ta|8JwSqhRYJdj(ip z!h>^uI-X}q=^>&Mb_6rQYqP7n5Mk69EpA|A5YN58HmHf`;Jbxk`xJ-Wp4MUBT_b+E z>>AFbQF)(n)cj6TY{808xNA7IdmrUKY%93`a?3mQ*sUt{kT7fN!3`nx8wPub*p7nD zi|jgsok472!G3N4?9f|0r&f2P1)K-&z~7mi$BHYM2XdT8MW_hJ74s-*=w%_jmA9;V z(^1dYU_DzXXTi2dHu9!-^ABAnn?E4wf9C5k6m|fGo#hsV9e0_h+v>2V+{Cb(fTm~E zO|i;>yL?A}!t6CzDOWNCYC||10@&cE4aeBv!v;?WHmpShzeK}*SMQz4cswhgMg#Xj z_dZQ^2MVljU((5o(1qGsn|(}X!zgVi%7!>LbU_18FLO87R5Gjc=8TqQv?ihjH5$Fy zf(9{~LR(L7COJDB-nVDNCRNRY`ySvv9NoB`{8w|gLvv%;!A5S3ecI3+3~$e&@@4$E zPCqq(n5&8EK+q5z_KsxNdEEoGJvT@0)|V8JYoe__EPPNOe{FcZi42R_5Qql;73of| zsf(MqS>QuYvvkL|W9t_7;ggJfL}|kuRqY4vyNvtz(F6B!-l&NiyENUIMWXfGN0mRL zi|{Z_)My!2jAyF1He@3oDjJM2SR4Lqq#8kfRHGdG&5KkaO~q`a5auiAjHU!Dy&(o$ zmK77)QvHJ%*Q>T%e-+n%i60yFvji%Cjf~Lp{q7z)Y-D3t~Pw%z#Wj)OR)MPRL`TSI~!PCIcL-tte!0#%0b@wt_@e&z>b}4 zxQhm!SnPIaYK+zKWJY%~YDaX9Mw@DsJJ3|74q~4f$r~GSqo1x4$W@~8BTlL7{5A4@ zJ$H9Dl+cDJY{<)o%-Y~!gPjdOTeD#w8z!^isWvPH!y@|Uz5druweDT%+}U^qyrT3M z)Q^J2^(w$tXu6E>>9A#mPbdPYNKJ)57O?uU!}cqe+VO>o4^()lcuqwu6;V{UsJKPN zRV+r-ssn38=2EZ4DY7m*u3R&sD7+Lt8rDv-!DGLEIBEz`%l@qc3gFF9z?vnsDn*CD zdKE4(!Lhk!8<$JxGbZV;;ry3-P90ds%T_0sTBvCT%vnW8FLP#t<%Jx!I4FG`wjWUV zfQ$o5jG|;POj>vO!+JNT{=mOlp*PAxG_asat&5(N8IIqmH8<^?IOFcJsk&_;72!w0e?Uo-vX23C@Pr7bBIv(NWPN>+ z(|!545-mWYEwm_Ey>$QNNYbq;bf*g45xB)3lyrZT9*}5V58PelFJspo^HLPW`)F-i zyH;XviHgx@H(Fj%#Ap3sB!nP#9zJ`&^rmXh5FsJ_Vec8WSHy}vVyApoNX$^bbMAer z_`^TnJNJzH&OP_ObH|td?vL-d9Ls|^97C;v11Y&=?S=(6zpraAV^)jd5D_E|5VTf- zbkEtCOJXbwR~E{Qeiw8H-$~?4S9?+qibk#QJR^bCs0%0IH4Z%ks;Bo`JfW&@=o9!qjSM3rMX4PIm;p^>S^vS`1 zT+LUO@mRm0gU62s@RX?c6(kVFTmbhq>l=KMsh==QQz!XTfc9eocqANmfHY0=;4H{{ zrcHSXNiZ20e`Z0cU9!kZl`-u_eGC4r@TV16RW<-=1GN^)DMt8VnOLm{D^lbL4uuYm zVz3FuP~cNBmjN$>I>1|xyW3v(rRH2UdoK{*fY(Z{6YhEoLpuSyHiGdeNNxjx4HP?_ z2hk}3dsL(GBMS@V9-dg1SM>14vdW7dD zV3rLe z$n2=KU6B)_)(%BZj9NPtIVo!GQsm^QwOf%>qShWoPK{c76*(gM>!}{_T}nb7g2g~=sQjIr}%t30goc_NDAWctS@K+wJaZ5Q`B^9<>I!-zLJ^ zS-ua6Oa}$r(_ozNbkwhodDBt56{&SQqTe@*hk?xIyNWYxR_myrAN#=&g)5P-ZiZ59 zaj|s4f5kJ`5*qjGX#@FZT-#+MRd#_L<9dS%Q>cBV`^&XqHL%yr}b+`2Z*64y1bwSP>mDBvm51qfs zoK~Hh6nAxCGuL4}keKS7EKT`n3`zk6m;PUT+6jS%`%vo#g)0t0>M}G;)-O2_4|UW` zwG1)|__{fAl*SW&J(RViV=FIVYN3F@&0D0FFwzSC$|VfK$&-pyM$MGbf;6ipSZVB| z2CAnjrrpZRHazYm;Bd0i2VjHtelDjuPg?W-f-;W%n#6jg`QuNpF#^$R)JgT;VEJCe z{P7q}%mp$0FwSVYy@r(%JD@IcTLyVP?v2Hu@QBlc&?gr#cbeo*zysQ&=)rbFC6%#I zyMa1+Y)V@eZu%uW&Ctdy=eWvM-~YrHKa1Ybk6M%5@bt|(0 zhkG2h&6_hGj*GMqB*6P#uCk!ct9!wspRYpoBOeA+Kbj?BQ}lUIfRu zu_)H3VoZWapAJ<-a6&bS6^dtLe|QsjMY}mEaK!BYPzA+dHpfv}dr>!$9C7Cp`#ZTL zcpM6PVx{-u^ADN}t_Nz}55z-SGub`k>IMK;C1k-K^*CRiR|cMdX?GbX46YC~fpkuU zMhO4E>;nw-W`XIc>Sp!nhh-;^Er4=!_v|8YAsC(FFtFsKCqy7*x3DDAksO%#S6H6Tm0^j65X}@UX@f;K zfYiqVh+`TejxBSzAzhal*X4)qL>Dtl)7enfxchjDh=0ub0_Zn<<5woo>I}d7gIX-qC-#Nyyas0i$Z4d^r6Jp;Yb|Tc0 zSZd#wh%E#kL?qTkbPSzpM{8eVi6yo~5JfQ`v`P%Mm)fcDfpkpNzID_%_uOUf8?U~Y z6La33-~aZWd)~Wk=I@`6IBc1j1F(O%$AyT35`N>xW0YSe2 zD~;3~2Nf+qg~3!9p&qEB4pq@^vSyn(K)GI&?GAIBSJ;MlvM0XmsOlF?J7de3}qOqxOg8`q?=5|dXuWiFgd$lL@6d$ z;i4*P5fuY*g?u;Y+_pz5UyQ1WOjW@~RXdJqY%@N$ttr)KgHio7DAhNEvwRWJdxKLg zG?2=>2B;E{YKB4QYNtV{+Qz6d4T)+|noPCIuv6tU<#V;=9#T!=R5KaXbip!JL=y$o z3mvK6r2^GaT!%MwN~Iyyn^HmbU6-he*2q+0-A>g~P?Z%R)m5EQ-O?%5b)BPhjZt+LEFDGER#0VW zNR{gYs!B+;SEF~3sdj0MYQILQ_Gp}Cw}`f3 zg!>bvYDK6{|BAv01-u>l4bL387j9QYFYFa?=!F-2|LKK4RM88^1e|)I$5(zY9F-t? zVby?BFYM?0w-1jk+o&zT+b+~A41ArC@?|1OxCux?+)FCX9v8L(smF(>5qLBF%z9!9ysB zIk}foiaR9j7*-)K{Lq^?R;5cRQzc7j`5&>AR;tocnv)_grNyd!DP=X{OKHt*TuLKV zwv@)JbSaHd`2rdxq9H0@N_Q1pN=2(+Dg7kpIX|9+vd}{HtU}ALL?LwbVsx(+i7tP& zO!r8!(^YT8%dhwr(w$Nm-Fb!5WhqF>vAXOuU&eP2phRRf( zY*c;Ys8%%Ob2f%j)#p@=8C65U(m+JD1l8`SMyQT@fvQhgT={9UYNj zoa9S~w``C6x8Wlb@FRs~*Pl`>GFyQ?1z#?W@QsdwIpjr1G?ul1(^Gfu<&@&?V9cRo z9&w%sKSK{M->cGS=nM=6`TIz|J8c>iIlpG2xF&pKfd9|nbpye$=cC(K3G@zY3_(;U z$ydqme!?Z?Oip20`FqSli|sLq7TaTf8*zKg6~k$d@sv65F?brsj?s!I1XMh7$pvf0 z(Df21rew7}bbE-B;6wq;u-GY#kN*L#(yO=IM-KkU@x&no#1TIZ1mw$9D^`>X zgb($?=EAKD8U>Zw+Ak}jRfJ*#f+o2H?venKfP7S}2C#ZdibBMY#3a}Mo7vsl%LlbB z{p3e(=DnGD^WK{`^JZseXMb(Pe#T?SH3Tt8d(|>Xo7!SrXs zteDE>`ugXxzh}`j;2C7v%3BIREx-*&p|5r{{=sT2;s)vf7h%m|AHZ`RC)C9}*V$<-S2dwTp6f$~vcFHL zmHxu<#!F84Jo)IULS|l$$v5Qf9W!QbMc07{qJ2$ z`X7zNl&vv*Y@4;gJOCzG^#QP()ieN(H)V1vVS`_dD`{X8x5K*-ite%|!w%D%Om=wn zQnMWm!;6}@9e$5OMz+5Z>%(KnwYHJj;kw08VsaCoHZbpI0mKH6L1 zDqRLN?#)O%B*db-!dyoFJ~7`*s&2+2=SCK}SzJ>l7VR5RO)gMfi9(?_ z2-Lt!r`m$*BS5;AV$s+gK+JGq?l(~#J9i1;sm3!XL<=pn$YM0%Ts>DL%Z2)RF21IE zxTC#oJKKr9bzz%NcbXU9W)|Ju;l7|O>>n>SjvbCHrq?KVhn`~9&uPrgMF##N0biT{ z2yMZjz^;K2?YCcgAh*JcV@H}t=%1_|fL5p|84iWUwih1(KHg-d_hZRcj;gHx;~X(T zGn<&N)m&pD_EL0TJIe62>b)>4_#Jg(n_(MUa{1K(zYDjGnLuM+gWtc~^~;Ch`fqFq zx&B2tow)rE(14L>z({p71`fjjSG}RNb^Ic35nGXptTD(MV_3wadalz8DVKAzSVXj} zWE7q>X0LVoLfUxan@+Wn9wgF#Nx(NFrScqaYaKI$-%j{9o z4oRSZA3xj>Q`sSjQ7Gk+v_lft$phYKpB>j-Y6pyq~i{9grHfhnR9!WnWaj!?( ztVO4JB>j-YbdRJTl9=I0K>4+aqn$qH{cwelX%DC;L7S zKM#TC!cE`oDE+iy^P#w0(}Dd2_?+qq|tF{CjW7Vv%2NO>TD-f}Mnj zJ`+#o)%R)EdUA!(=PAw7z7!tcF;5*bV4TvfuYt4U4!2z-n`3x4=UqX zjZu6&P_(x=e{^La8*ixwe(i5D-tnUhm#_>%^EVJdSlirglt}%SMhPjYdp%l}4HZ`c zgDvj0+L6z4?O@f^tH0$(uvR_Bzuj}L??(=27aI$guCrNOOMU=R_ZnaAl@IEhnaB`@ z4C!JTgOT%Qiz)5ot2PF%=U^>BWChH!CYjTGTGovmd{@s37+J@f(|)OC-O9n|cvcRN zKB=cD%Q21nTOT>x8TOGwI1V}i`o*K{kRz;BF4w$??8P?t7xM{!AYiaJ$p(EPLlxQkh%$%A)(0P4OFSW0o!yrrKF2xXhX9bL`qs?ip zFY~O`96SXOSpl=Gqs(aswXBCZSgB_PjONl)6n4BgyYOGu8t2!at#4;kbAnACKTcWk>%9ZcWriF?q{lnGe!2@*3QRJ8jbji^K zTykV%OPO~gn=&~XIr(AZ!Gt~Fw?#fS>L!4LenMmvUU_i8_QmS``qze=0<*iBzc##n zmifE&FA=oLKKKQjg|gm=tVGHZMk-Ue8q_jnm3U~fr!w{;B!uc89#;PVUjM7l^ZK_4ynfidQ9p${HBnwfdgywnKg~3g+6xGzW@@D7 zX{2hqAe9+L>bWpd`JB}EG*Uw~Qq^6MvWAhmC5%)eCv|WQCw0)qNtL!ERj!dLLVD<> zjh6e&q`m}1cg<>~6h?|K&SZk*VT<__Pix3`Q$GFO17tN0u6s0iqXxS)cq@aks+O)_ zRRUl>0i!hF0RoaWU?u@s8Zd=`nHrEqz#5PC&H= z3<*4PS9AOC`_<0Su7U`m_5j7L$3Sh9%ea|`$a7X)74X7qyr3RD|@FWNLeDN_3@X7K08c;~U z6b*0@aGM6qC*Wocm`T8B4ag#3mLkMVX(cbfML3A~qUQO>EG{2v% zMiOX(zitbMj{}Bg4EPfc>v%Pwg(tPJUJK7^VT%@8gFNh}g%@aHoEF-(aDWyL*1}<0 zI07N=bI`tAKU{fsS~PWoU2g2J)_^f3Zzdu9K8q_Dq)pHF*z=S_kurhD{v^&5$*ZZ5 zEKyDe{2?5dGEd1=lq?`p@@j(?!m|Rn@&ImhobsNMiM`jnqm(4Ge1Qi`T7!5UPi_`V zPI{Z;QoN0EVo5N?TAit2nD^p!YIs9{DK(9^&neBwYb1^`#Y~^|E4evMEGb2@wYjD_ zea^o0yk<&F7c&ovCHqo>cSI|UMfbj(80>cqXY1Mu@yqu1u7(Z_G@l=Hb<7k;(7~$_ z%Z4R8#1XVLv`ePfrN4aLZjrHng|k`1=w65M4slED1#j3fGTNJwHoEj{F)p?iu?+9g z$hvOXg-T}rkgvtdy%|d1GIrrZBC_1$75kQoQ%mc|iM?#y7ahT-+B+zN(}BeZa0W8T zjgUT4pA|QluyTV$HNpOF(s9(E+| zcZjziN&MO&PWZ$jPg^;Js!gm|RHIBAHbij_8>R?fY=nAGL*jDp(YO>OJFMPpha9_Q zJ@94F-X+XzR=!9!g!2yf5nkAYQi`w{zeWz>RX_pynmLHsbJsKV*&jpboSi1us0Z8h z{V2#J3eRvkXq9y3)@X5RMW(d|I+#)c?qll3UNPcTKCz$yrH~~vvUj#Pd@r@1 zIF0EnLf2V@x{vY;+>dL6}N= zV@W@T2(&S=tK<}28S;J{ZA=tf@GEY){?Ijj5D5DaJlHTvzz&_ywRE@zZ*oy1BiH3MlX9LM=}J1du8+mqzfwASI?sCwxWtObFMP1{F3?^zDWaeE zSOi{FU7iB4F!7s3W3fMtdeU(hLHa7&IChOhYQNk*Qqm9Kwp${c-C@3$AOmK`@;B;z zU{;x!prnpfmmnHYwzGXgv1sCuG+4-1fj{-=`?=UCL~joS=7wdXRI)K6J7tqvPpuU* zzshT6nh+3PjQM`zL2)@di;VN@8|)U>(jiuK#-|a*lYsEjR5W9e%Q_B?_M8o?_!-Jd ze;kdwiq$y@BWMXt;^Rc|vsYLThKSJ~V%NlAWj`C$srF1lI~j$Td1b{iR6ipkiHzNs7A)~mU(ovnMm zFmT1Y;r3rwHpr2JQ?N9~i2oz5#`J*rInMVV*fb?QhcC($P^9};9nu<}jA2xLU5IiH`> z(Fk_xjXJ?>1QWEvRO&vXR9OD2L3q@gofyHdScB^A|9f-$zu8IqKdQCA(egtS`vV{J zL;j>$`}0S`5&E$8Qf*+pcb(Vz?e@S#I2p?i<13Yx|w zwmr57>b(dI(YM!nvty5entl}2u)w|#0X)qRJ`Kl3O|id%L}_%7(A}{#F?bmuCT(tP z^*={?_1JsTK@$6)b`jCoJJEC*ay3?_(6ZwqZF#^ID>loxv)AQe{xnpvPeCcUTCS;S z_SW|F9*rGcaS1I-jINf2q}`0MA{N9B2E{;`ttHqXJOa#+oQoANDyEj~ZjslV`++WZJqh`2!?#q!@Askxdn?ny4O|CLRw8-%Fs6gml-$t5bg-t1(Q)ZI zC;7cNZ(dnV}hn2X6qY#-axL$OqTKB0rLsT+DQ!Q1X|}Ob06~7#(y8u7fRrp#zL3(7`WuYdZK5 z&xNt1qYmA=qQbSKxruAQ0=F7#_U=+EhoGj!16V`A^|k=XGZh+{g~PD19@vl@N_j4E&; zf9qo;_EwRQF^JuTM8$r{M?&6#xLBNQh4(G;F^_94gIg+-?OOmk63d*T;dEvDE5Ny9 zr{mW-G*Wn(YA=CfSc6DnwX)HINW;BdtFj_+BDD<=>~7Oc#lQLfVt0i5++5Eg<2kis_GF_ zdo|JwOsn6%9w>8b?FEd~9hI!T={*G?6yd#{T1zkH=?)5{(&+TWYPjAT06o?dj`^(} z%oSfsW$d~51v8QMs}adNhB+bxs!ETHjR&)`{ZUr)9bh?zSgyjHrlwiWN|xg=Q~J6b z6ta3ivgS&f`_zbUr$>96yUF%DQB1DzHg?0=JrZZ!SvFXFikeYwS2+dt4-vrrfkV=7 zE1CTR0OJY*hmzjcp(GDN{EqF*{J{yIm-sV$01dHwSn1}gV!Cb(Ctg$+$M2u$0Kkc{2@Z>|CNlz zVPJ8jiNzry_Pj{C4jd0pLlUqK#77|s%xcV!BzCJDay5|FYOBx>uo@3;(05&kE3r># z`lv9vEcMOz%6Z=e(@!HR3$K;)_GYX{!}e}vJPW@sV?4)hL#W)5;80SM*$g#hEO_#Q zkL*bME657gG8~MxMxcZ>KtWHmT;)JFk}JWzKKHFU*Zh@FT%ny4elnhP#vCb5P5Fi! zfS2j=3ku%0kEE&yf8cP>i%Jg|O`tw?AH9BcxwNV>X5c`3bHO5j@ zH+r96C@<4*Kz(0}Nk_4+kyeKPSeuE;mT$pC*l$Z8laz%bkJ;%ua5i!QW)ssy79?Gc zKwWi)@jQmt5l&n31V0ecDK?XR4^d}NMc9h) zZ1qrKx0e;^?xm)F8^7*KS@?n=;Z;h?za|0_cx||g%PVHqGzcW_5ZRuOAm<{*Uh8F} z&mIaQg=`;cuFKio~G{99mim=;W&1bFsJZv8C4qzd|+x58`jA`s8=x-gk7a9Q#vxC z$g?Qo5u$)Y(x@^5eg{Ai-YL~wN_nZ~QhcSX=3kcUF6DI~0_lHYH9SZ)?5=0db5AKF zeM1S769+Koc`GHqRnHi>l{0`QrCf20fdc#@iDZHJjDc%)2FB?Oq;UqGF4GwpP7L_# z7y|+7%eU2&1zb|flzKC3X4nT;>VXngdX%>lrGCZJ*6h%gT2*2w^^s!6uBVigx`AX8 z8d;4Csm4E^W-|Ga2|LB!+6!c1Y6!&|66RKCmUMKhnI+^_yOnUaI=)16t4Ss8-Rc#bgCUHA?f|5uS4vE7bxScb z?9ULuuvf8RVo-0U?$}@nxz!`&R@L9aOZtU}kuA8$V%bVR>LF;LkUuZ$`vq9{Nmnwa z$Ojy* zP1*J?bhBDY^8J)_^AvRRG9xUMrS`YzJfsI}6=4;_FQ5vS=oRLjpfxvM=lvX6ia#H# z_h*3!Uhl2E-ns&6OOPHzIg1Sjl%%^+m$APE`@Wc}k^=qsb;CUv;T{=ybFeC1o|v%M z9(esE>l5|ckb*_DuNdLP`X_csNv&&1knIrU!|$Py4{3G!k+tqstlcdJUFaY0E!0~* z6BoOZuG-ZbEh^;G%TBJO?-R6**no3FbF+I!CHh5%{LD6XnVj2!(6v2}Wy`-2fLN5e z1j{rxNy{mJ>JrgC6j&v7nJ;yT1qtZ&a_Ta*2aw?5VW~73cA@d`uq)|}Dl-qitP0~{ zqSKS=q}sQ&^4foo(6uxNRj+SlRsV#lPF?6p{fX#)zLn~o?@RrOQSW@!1tbW~V)f3{ z>Ye3Ex+bjN%i7iZfG70TILlxp)Jc zneAFK+ds6BF)U56kc{8<1)vlRfW6oS`y#?1^4_k-v2A zc?=*HAL;&xtuz!>CwrFl>W6KeW1`d?0ICIX`TA`X{pMRl8%qOG$ql|y9_a-d z2ht3I3SQ6!n%kfo|azNIFO!>-STh+){3o7nm_W}UWDwcFMRilarqcC<$+1O(Qe zLjZ)$;gO!@nAIE;fhdiY`;TEOcYiHdw4#YNKMxdHAAiMeG%RhK-)whp`#ZBc=ZO5$ zoJ&Yt=io3~%ZY1mXGHQxM1=a5L3WD2V>b$s%>mi4t^=~|x($(y)5tD1$Xfm98X@*J z@xJ13GK~=1nmQXHe%sW^2=NS%Vedz^_m_A^+9@9rx73rnjDowA>&aDqbzvWD#NI}u z^T&TM+(8dMG*)Ion{B@WzP1_on{AoJ;U9+)9ip>4tKykt=s|6i$rJ@^Xnt1OB!xiEZR)o zm$+gF9{{h_;r@UrLcVB#l(ghIVzL*Qyn`{hXl#F$5A(y)oBLR#&m+Ym=QVxRW8@+h zETbf?W2$?BB6xIzx`U<8<*9R7>ZUfPpgYUDqxbY3bNvg5Ea(9yBZbL`CPxp?mVzZ> zL0be3Dlc&3JI&vxdtsUlHUB^?x^5KX`TjOzXa!$uH`C1YN$?fXk0p;svgSHhZf@tU zUmF9Z$3EWI?4*CRxt)`~$k@LOeEVnStsR@&dF$;`(u8M8RlkR-F70db*8P#S?&qC( zYgf5>{gdFyzyx9;Alw?5O_NniZ5wR3OXQfB7ibXn)#dX?ri-G;Yb+RJFS~_^^k6SwN)_y_?ulA;qB$Y;z=Qg#USh|40 zNK&eeBtt)A2KnHo&U>u*^mdR$`ZH){F@FO9wDee1fcpj#ar)`!tj24BNathMkWHP; zEHBt(%q+dE;+F#JkF$zTZR9gcH+tiXdT&&|rRFA=gvWZq6i-D|bpp-i@LPiD1PLtb zOyCOj%}L>Ry@-!TiI24)jPW*HcnL^8yGAdHUr=8gUJ9YXd{kW2NkK8tTHchYoO99fEL z%UHEP51c&8s=c09Tem{%@|OY*8)Z^Z6+e#@+dIxY)U<5q@I3HS&;*TR8#+D@oLs3t z59|<4Bg~c!T|Ez6RATOBeM-(X!pze~n7QT=W^$(^OiE|Z10%b59(bwP%)>LqosTdd zX+3?PF~U@I9AW;@*$DHiE=HJ(!|IJM?tFy#p;qsB<9T3g$LE0&ojniq1+q{(EhtP< z>~vv7%Jt+O2i=K)uMgnJm@~_jwCllmRO~5ZF9F~M^;ZwmSP|8;4=p~p?vJ-{YgmYw z=uweNO0p`{)_u{|hhx#!`xW8TQ+A@4v(YaM{X;x^eZ4Sv5E(85gwp*G1oulx7a)wx z`7;dT2A&XwK)dgP~1kZlq7~*WX0n5`6^u@WlpsNrQ<@g}m+d>WMhh3bnp@WA* zaQbV_L=^S==`>?8E%2a!$acC`eeboT@hfW);7;z0Yx>hc!0VG9wpn!ji}|-(X{J8< zh%2dJ4oQ(uvk7oM0T8>n<1qhf)h&Q=PQZa6EwMsez8~dWNq4X^?r{Jh7XY8|1N=g~ z%Me7fdzzi`ioI!eJPuao#=4T~5kraDk@URL6t$J|Eb4`ME#-i%y-C%{6|!&;h|xzz zmm2e9+KKrTNU`L|+IsQwa{N{C6N58+I5k)SxD=ZXgi6nA($047)l#d&PJA9h6q@Zp4ruCq={p zJamJk=pJdakaRiS$iC{D>)LSdB(h1uL-@5x%~o8~%{Wb9WV|VPM=dzQ<-o3k6W=WA zR8JJ&1hl(KKjZadm$(z1_e-k?$kO#49-Ll#Iv@mOEtOZD4U4X>L?BZncgg299Uv z?Xm>72Jug=Yz>{SL*a6H(38kd_O5O~l23RRphf)+Wh+d1FKA%|(&Y+91M+j6GuAOi z$Us&RcRo;-+U!_MP<^PCf7gp7vtNZ@v)V1mS)HOg= zntFso_p$!<83L{mfqtN9EHo1DkRM4<4Aa%?6XHofSM{ed8JrT zITQUwoPK@>`irK3_AC?a+ZpXEIPFP{_6-K@YZ>ivEu=3-UJ%?R%kRnB>MRPV*RYIiP5V?-yS)J+eG@+snqaB_M` zltm91VpL5JiBWVriQyc{#CUglcwYeT>wv=Qx+5wYdl-(GT6GW3F0e!#Uhl@naA`Ud z-ibhMYco<~civC=OdVIHcSiY=A}&1 z&6F#cZeHd^UqS$3S0GUA&*C?ezjfdf9syv@I|@O`l|gk6Q7t})7;O;?QXwT!(||(T zs@DP3M*3@W8eTT)orZ3_nD+>yEi0NJ>p<3h@ibx1y&~2;d?_;?YNDk zT7Ef6W%u)sn8L{0xQ}DSP57hYfqD_ z)vubg5u%z|;$1{jn4|M%>ROqcx3MPPhBMx}fj6Hp7U}M+6W?FI+c^(iIC3mAW&a#VdOLB)G*pFG1}{=wx_*l8tA%B zbRT7OCosAXXmlUubY~FV0z4<0hBfzf%(dEYh=+?WBvnT~a+gkFU=7Kx< ztf?0-7{Lnmpn?lq^?sLM1{x@={#u*gY3S}jj#h!rm{ZM%4kqU2dq_<0Jsnh7a3QMG zdTunLK0!ey;=B+c+9VHwI=7W8Ez~Bn8NvA-2reAPh~*6M3F9~+;TBFPfe59wFb<*H z5dd}F+A~PEUtflspDM@|T*U^-c;sO72y7P802&EclwJg&2*;4VzCn$ zAX1H`{ZR4xl+XzJGU9lqpJH|f`4-c}Vp%$ySUf((m{?o|CLz-TU?9`nDIEsWdw{}V zIt_#AaSW!%*-ZHLvn0@XDkN8^p8{%E>ig|V)g98>&D9RrM?#BX{t9U(dxa&=M z;Y~#7oY>>^2qIJ?z#ckw&V`+brYL<2aU#87Bg?e1LOY5cjPn&R0{((GDPWYp_OWbu zaiGr5pI7{JY2rXB2=AO_rIpXhT&na7af@^iF`raH@HU%;;_7>tzR1-6eWUd7hTj#Kr-1M6I^U}`z7H;C zd>>kB@V$Gf!MD_rZ;A6Q>3nC%fAS$4tK-wO(VvB$|6C=ky~X@K5d<7#fV&$5juYTp zQNc6R^GP}uj@(p`ebaeFwX!C%8tLGu_Y9jwu@&MW3zt8N`Q=u+*G+wp?Q*PuO@Q+) z+(N3opU0tx#~jR-Spr@@bjZi(uDef)t*EyOI3WFy?Ud-A3JQ|M8kn~(@{ND0H7M89 z(8a8)nOWC8ay{TSf|+$?5M0aC$+{8=c5#@jYXlVg_!7HCuBUtKml&#jbV&#M;?;Xn zCm9^Ph;I?QVN&`zj>9!1wQ`e67%nd{`6M7U2nzMI1q+Rwq6)BdLVwQveHX! zhU!}vQv>%hx2T6MugA-d!&8F2#UcU}0J!{a%)DV^9iGiZ-61PP+rtVtC%Q^@@wR#j z)(5SB#I^o$2dyK4yW2wSS~}!v-oY>tVVz=e~JsnW%eJKrYHge>2=Q`i83Oe^$@zu70QXBO9 z8#IYe(npN+us6E3`#bCLX#$wJrH#zk@&p=z-ek=C+)_&~H$LxE^8p(DyTkfc`|+lw zpfho^7cp^vw8#*5+M;ua>)IY50$oTA7GGhWCfK_CFH>7fpzcYS(t_ zVgkkiD06Ke1i(m1G^gBUPI=UvvMQ89tFCrdXGN}F*=Od;Earj(%_I}dDU-}8^UWzM z!z;U8q{%SQHT58Mcl-Bmx~pZtmbin!|kv;BX((+`)$= zI^jc_(OnXpMt8Z=`L~Cfygkfelvn`HKNSAn$#Y!FK-P}qo2gAyQ|J|I?w1F`uJ&d2`t{=H@sUcuLi2U z!YH@ZC&42UbesomJP7h&fLW)=`#nB(-oS$!d2lBWCi37XJa|MeIf$2hilY@CtmDCJ z6nwV^I%eYy^pVXp9(>Hx%FXf~X7X-i2at@Rn=)FR-61-prSst0819U5Z6}GXeJv!T zhJn-^Gf2nWpkoqs%$+*sE*#I+!XPT_CJ~z_zGAbG^Pjcqi4-i;)Jj%u{aogRG$H;O~_e@-oo3a zfdaJ^{nd;@wgEte_>-IQwXg8`wA6_C^cUKE+OIkr&H2r}<{#}YIqVeW-|Q~4aOBB6 zjeOPuW^rbj}Yn~n;E@!?e~6bhxYrZ#oQjm&tbGEz8cHQpBLTa_lyji z!RMc$8?#crJYTAJHgna_JI8Ef&f<{j9mw}z*73#s6zD|n{O;=jv}?LA@GcZ%`R^H) zF&dj62#s+Wk2c?7Xx2t#;|UT7MSDsLAD++GW91k19nAB?dgD2deCogDfzyFH|K@Pg zdlC1(C%)?1=)PPSGPR z#pCb{|NS?~@V!>R{OgX#5;!37NZxI0UK0|-v`@Nz& zP0(E@N}#Qf=}Ge%%KiU=HI&nqf8!d;Z*AdgD0c%6?EU=TwuS;5rRRu3V}0a!MAz#h zmDaA;M|N2{TOWDJ(#iUWvYJ|&`eF70^i@UpbdKGEH`pe@QpKXaqu@gi00X~S&Hbj` z$3Uyn7l*)2m(SpIsj>ctD?%+ZwQ~fn&yz*>tF%Rx_ZLetx49<}NGuR-Y*LCgukn5UcYF}w2HSES_ z?L46IxKa(7)pBw+4}ON&tKahdx~|B7_(+I+0D1NP@Hct?849Y`phPF{@5))^5e~AK zlE^XjWR9s>_ey7d+&u(f;(4E&yu*r)-XYKI9e(s5@D5-9JH5k&Lb!MEk$2cs`)~9P zb5SGNi{T%>AKA5kh>GaiKa8<<<{$i#=kgEI30olbVdf&P1oGRwqP7vb;jy58FLiMo_oskSN5(1F3KW`?+2`cR^Mt;TVYXQZHI*g z1sVl%xCAN%6_%Bik=<5cbW=d`wNX@5b{}@%hl!LGn1R}bc3F1Vz1wP8nj9kA|IPc( zWtUL9%ij;bx!%mX=FOX9XOjDHdGa>P<+*TJC{s+!-NvuPz|Y!StfN&dZEpQwOQ#+{ zV^w93F;sPS|NW*#)w`(Uk*oK36x;LfA6;U5k1i3~dEvied^x>8<4gPLJifg1W%%*s z-P4T$U|6s5vV>UBjoVm&rb54;tXSz8jNKq<-CL9MM?mgk-KXFJ| z3Y%-pH)i)S-?+Aq`9@R7`NqMa#*-HZFtOhDlHVE+zKQr##si+iy!N*<>Vs6HKD)P3 zulo-e^}60h{j!bhFaO#0+}9s_zUNf7=Ydbc+w;h&h5@Y)Hy@1IXtLjLk^OEk+wY&q zet&JX-#aZw+M?gI-&Lm=%|~~e?e~>BZIM?Rgxw$rn*b=^(WtyG%=sQSeiPVyINg;y z@MC05Q?$#Wq0949sl^di|r;_&Ek-#kY&{Lg~b~n=f-)i*2!@Vpi3fngNg$a$* z1xDX(aMCld`9Q;j7|R~ zXasajj}OFd-Q~o=%~tVf3SJIU^@*evReM9Ar>6%OOD(}^6#Ogo(k*F@_5~Fc)a#ex z9d^2)vDt>Vo0muC@6|ys4n}C_~k2Tt#)~o#m>$LXg-5A{djh)=SxmAXG6HvZE0iB;ft)ql#@`(0;z+!&4>R(&Sf6(E?(RZp|2iXN6{x=#^ME=|k(P4I47u;cD zZqz#r3XJ>~<+Oeyetj9cW^GhR>?;>I#IHKHD~&;exc3B*sITz`BHt$22%5oWHH?!? zR=^}(aJoW#_AL+Fd6MyW5dIFfG5s4%^y9#6O63B)LDd>++KaU8S*{gEgr2pAMlR=UVKgtC%@vPOuk zT`6lvgt%*~c?XERGGO4_6Cn)^WNU4H*Fz<~0wKOndaM<{DY6z(R*Iz)42H~8y?eQveS&7)r` zQ|OsI)6d%AZWq6M}POS61 z2ROQy0w26O&>f(+3f2T7$t1V(0mB;mJiwypqg?bcF1k?^T^CgJIu!MN4iN;sAMe~* z?8oOSZ&jqw-AfVT9{z)i=tt-uU4S3M!2r=iFln|ZP#7id2`O8h?Kw3`{7U+6l+Ug$ zN(hYijCWxuskI-nUzAP!D1Sn2-1}r<==~f2F2?VNQ`@j!2{5|UH$Qbb)~D?#y6<67lP4ni_TOBUg=yqQFS#J^k*nc5E9Sps47GxzJ_! zW4P-=am%kPL-mkq{^4z?gi2Dm8DCZ}o@X05N|Ljifj)juK4TEwP#PIb#FZmKL`*Od zJHr2E{B4mY{BMd1g+DB9+Rsk$RN`=cO0Y3Qpf@Msnc@7ZAw86cU<;*YJKT7*qAG_f z_FU$0lg%mvJOU^-Z#X57oQwoAsm%ZtwWx0|A|#%iTi~5=i)4CPuKp({KXx|Uc>fGZ z*`DJey%loRXJ~D_C)<@#>6c))>-UWU!NWc!g7FKt=C2~@Ek!Dlg!(_sR~F}b4qE$= zT$K!p1?hL`d}u5C$h-7=egv<^!Ob1y!Mn!}F=gDrQ=d>l!DZ#!RLoDPa)?>h{B3Dm zdV3AIomB)z|M<-_1LJqrFcA(TX}+3dQM69B(6hfsL-IgvIA=VK13jRMV&{Rj%6;na~>%W)_Sn9is)LX(gcx4GHukAMfLePC3~W@!{&KdNu`7Nw zGuC&unXzfjEP!8Z%*r5T_5E(!sE>L$RaqAa=l-k}q_0!p)Q zFN!Pl{+~Dg@h*NWuMk}Dj{PnIqs=~aLtB1r@>G1wu23a&93qBfGJY^iX5v|9$+XxH zM;sCRf%x(ZuaL#)vvifuOCgmX#R-6?23aJ}oXIMmdkv|4!zy77ox#?y$*_hFwuZU2 zIcyD2b&zGfoUP$$t2Jx5h%FyN1PgPhKGJHK!Era3&0y^+HWy8X1-TeW-jfQ%6Gi!K zLdn+#EY%b*P&zU`ALxlWuq}ctBCv-I<2xLx*oMJsp1Nlg_|k5+UxtEhZ!y;?=5&{Sj@B5VMVb+oDH z(Pmkg(WXhvf~Ek>EUOGpSyJ_Sr8L1UR~^<#b|ZF#s5$v)d~z{5(j=Ydp+$IF4ke3e zjschT-4Wo-zH3Tc3^#^UdV)QOOj zC=;PLqDq9~i6Rk7AZkR&Wf34_V@YBW1edZVRYG5>i@8#T8h-+ds&+G6O++U|2Cj^- z>2R=T1}OT7w==yxpqX#)6EoEZcPVm@8nIF2Mnr^pd;MEpPzmH$uPogsf7g|zyVEP) zwdn!>&tl5m>Af0db$z`E5&tdYrLVJ;FJ2U@O)xy!sog?uSxQHSoU?Egyqy}~WM?QR_?M3dSsSM4NJU>og3q$Xq`rh)F(aV$j4 z-l@bOO@QN~(b5mNH_nsefX=Q#NEzqw&XW_bjEnc=Bq-w& zJUK39oC}Y+8IO<2(kykRLym4EX~B4;XZ`$OfIj@y@)r_9eFE6u0sKMJ-9N;m@2b zY&agQ_a614)r_}=w$E)rW_)?*V#^IP?iv0WT0z=M!-{9sUIHtg*321STdcU(V#QZk ztoRy>6<=qu;vK?@-^y0}#FJGkuB^yX(vC+Az3DiU5kFZ{$3ZsYYyzOc7@)D6UzrZpqi_mb@dRC2!jlODBv0H;^QuDoFMDIzhT|C1J1hiN#^}A@q?!b6Ym+m&6NIIcpxWXM9PALkn`FY#N?TI z-~~MUynfmEZF^(NhF&>Q82TCboZk?LUGkX|J+1Ua-O{H`MOZM>p`k*?~;`l)$x^bTUEcX!mQL3vL@d9Y1HKn&f}%Q-^#_gYJf z?zaNYw=;r3UhIEg0Yi>)W^_A}#8iqcz5jA3{s<)cKR~q3w;O`!5R|nBS%zgr%{@k^ zTZ^DN8;MOqQ+>`zN_j?dMxQehf55QpEQm_At#b7V{pspAzuVeP3AL1N9kr3&Vdge> zegc+oWj_h}ZtDv8@&!%2mf(B(M{eQZ_59sUWZF*wAl=1}4}Lu;I<3%F3MEn~Mc^=z z?)yWlvxoz@@Q|*It6C5VuJHVQ3^RwX!<4U*B*N=E_m%J10tC<@l@7C%M45r8k_w`G;VK0NBZ`|n$u?aN0Irr%m#Z#A3WyizUn|gmkpwMYic=p$XebD3zqKRqIF47 z{-?lqsE6;9jRtEhm&KkB)&%#ctv1n0Bt?YnQL(u|Fl3(!>Pz)%U;5JFj_Jbh4^>O; zJEBY1>btb70yp+kQLi2>=@(LNMD2Zzp6O^um|NeC2zjqg4PMbLx2)*i|2{h{)7fcx z*r7Wul;RzDaX6=Cy5Y2Zd6C&^alLPLT6RPX_1=nlH7M_M7qQba-Mpg9S*Oj|5D>1L zZz8-8PG;|9Jmt*%A&mOG@gCIY8}$&vNhrmB5Q7dS(EK0zX}Pj{*M2r?4`&nZG_2L9 zs$5^yl1i75wR)P*Zw2kQKQe?Wk=$axy=)kEERkZ}vCQ%ukHE`IX)gN+>)Us~D>h`7 zJIot080Du7q0S;^!wz+KIurXbt#Va-6d`|{Lx}i8fFpon(*#Y!UqM3PUEZChN)UnU zMI}&0(wJv1B#j9`M`%N5{W4y_M^SA}|0^VAVSyQ^kfxwETppF3`YpVM!Mx5aU;QjX zhkFiL3Eld3Yfw)YOL=6RGr}#(?|~SvnHUfOuYWpKPxEt)m~phZRJym@bBqIuQMnk( z^Wo1=x!aezD}55IV7UNTN~BaVH0h&859-mknO4p)Z!_(|^e_n1vn`UmUCF%7#Jn(V zGp%uG+f1bi1;!pzfzii{)IBC?YkiNY*w|FPbyyVd`~IyWE!~ZDcc*j+!b%AWNP{#; zr*tme-7VcExpa4T!_pnU(f8;39LMv|PVCHHb6<1cbH1)~c4y_2_2&(ee;6uE-Xu0) z962$o0n;~p=IkvHLoNso9F$#AtN555gZNq_q1a6UslISKQ(@a(8tj;epAv##Ck~(G zTdtICAAziR-6t$rEi?H&B7nunEmlNw%Ao+Eap-b!)EeFQg5|%>f>3)#OoOLS1bBF>ZuWS>+oGLvIUz>-n!iDy98l3 zo?~1t^ILy*I;U^i^Iy_wEGPVAZDn_*-QQ=Sjgxw|j+I$A>w+(Ts(ZC?P8zQ)wtn|% zt0%gBC0MPLfg=S*iXSL;!cIBCqA5%{k-~DGd_uSn`9XV-^$j&^YH_nC|AE$e(!9l; zEO2k!0zLh$ahoHR8xgKlMXW$;f=-(k(x9wQ0X~UHAZ}h~1WsL}>X&ar}cN zgK0Le9hJ3M7WHSwH=C6a@t2~`K?%%J_$}FLCW>zgU;R?>Bpm&!TrWJ=WsqG5yN%Ea zo976zvqWZ4W&d1J9_{^EP~PCk5-&mdp_@#q_%DUih#tIqR0}A{hDUI= zWb~qL5+5|uI5D!c3kHN2Ts-_R(ZngF)IS@ta!*J{@kvU)B;V#w8XvvSDk($l-f3Om zaxjQkj#D}`L1sUWw_h2Qf6{5YH?|dPSPc)abjFPOdS7~pDZ95TtAR|h{DB48ZU?{lvVYaYeWltIG=!_IdFN1c0EfANJmfp#?y~n4d*JAgx zrGQP0U!X%}?=#rZXw6P*HTRYF2SJ)U3U3{oimG^Ym`}9igr2iOpb5wD$Kfxs#E7p_ z1$8&3>O&l5keF83+)o|t(%0aQOYcIuj>b%D?`b;POn=^AqbToNid*2`eRgv#{}{pM z7~&{7wxs0YvR)U^Lgc1#8C*GDtvZ};HK3z!*u^?+iB=_%O!i@jgZxKrT8{g4ZuI_g z&k-S6W1{**Q{TwJv1`icpY-v~6>4M(rmh`2kGU}JfMKzP3Qb24`Sq~W%=?(>Z)Ab1 z;yjGa>6h8bQ~3@RyR+lTyB1Urq+*dWcGBkPW9O}Nic}7tTMmDAlO6KJvktzRo<-oI z?8o;Q_~sEVl;iyC^O6jDywhS9zgNypiHA>tX^+H^O3eqxW=Ys1Ol|a{X35x%G}riP z>L26U*>Ae21&)}iKdOckqBvcN19dVH2FR6D=Z?uS7@a-e6x)C%;s-4a8PW%`u&BC_ z`#G-Di#Zh6$7_bt;`G{I{OmZo&=wkjU`O4$)2|aEy&AvM8$LOGSsCErXR_BCMjiCA zDCwH8N8)GF#wT7Sq9BfMu2Ks$a^w+Yx|z^Wb*m>$?s*?e2#)k~a?$$G`PH1DhhKX` za&MvZ*sQD?Uo!`t%!TC*)0epWE=uPf`{cHbBp+zn|%Lfg( zJ`5JOmE(1}AU@NlKdgyd>MTh3I`%)~i`~C>n*H(jif;lBePm#t~ zH#lijzOo~8Uha?3*v@o>s=2{FEU|=+dhp7JPFj_Im!Q_}pz9%Wk><+~Tv4tV{lv@o zrSs4@!%YxKlp;^o_iy_u1=T)1U)`;`oe4_eXMx+cO(p$3!!z|`E{E<$Ka4qYv>0u%QmR4-&5(@6@ruB_M;c4@vZ*d`k?}os48pM zjI__UZ9IRDZ#n7@PMw*luDH~m-rZsBCKvC028b%Kn+(* zZDroN6<*|`kKA%b?}T|(R`a1bK5)3dGE&JRjdHzPpF_k_?lgdStz5mqehM#y;4w$9 z*pkR|PD9U)mpVT2WBQWt$|z$3&tJdqt~`oDYteSzDoh92$-e()`6*vtr#6ujxSZve z*`2k?onIOFlQdzI9fYLTgM}IvFzxb3x5D0z?0taKX8}9dTj+x4sW3ineZhuA^WhFx zV!Ll8y-h1C4OHR!S;~jTy15WR#D5BGoZsi@o+jIrrF2&R)Njb z0Gb-C2PH17a6+Y+6Et}H#Zi0YML>cU2t*BpADWznX0M8IkoHae|Mu`|| zH^uD3y19E-ztfapdAu5bA9K*$6~X$|M5GGm;jVxzz6~w^BB~5h z`(PCZcF%*%(Y}OzvTW6JtcU-)Y>6u^x@5@}@6#)kgi1GU>W6J{VuGq-7vRE?CJ&yB zAt>1DcH!8JOLbN<_KA*J>;Fm5|9e1Pr1py0#!Buu(uDM5LERb;@VBiIcOLC%R_w_O zo}Qa}`l2uI-EC|b>ke^f_wOwvvmB~V`!y!8-T$zsE@;U#od~9_h)!Sj@a6{2;&DOT zZeBscJ*Apu(cbiatzMKS6jPwg(3q$@+7GNlYoUYVaw_;x2Wh z#DOeyS|C^hdmp_*CcIZfn%g$4Tg%$(eC(K;%t3G@=SNMhER|aBbmlvYW}oO4b(UcMx+{zAII6rVD@(Ra?p5!zp{;oE{#ttk{Y zq>o{usyU<|-ovD0&K~N3Tm8kFJ+#_S(;|$#x`7a>X&s#-G-kA}QrqvTq#o-b{S`A! z^Ik&9@9L7n9*>2d^GHWfPJ?)Ts9KYD>G<(REijop)^D}KGx4tq{XOl(G!?~o(2Gq2wu*>mt)VpLq=D38p22ri`v`Od&hKz<*5HswEjJHel z)aMlkhRCc$Exb^glwX`)qKtV6PnV!5m3)Mp{i>$>-o$}q5R2_;qfZsyL615NlY#F( z;zf_{_9#S44}B$B1T7d_i)}$7t3dLdWcaMJszIb6M7pu6bAi@*exDYg+|tHxgyp=f zci3}y>3QyI7}9(Aq{0(3TRx~^NmD+l*y6eKz2mSM)mZ0+OXKw3QL^Lk@a%OtBWm5b z>#EhHW~=WTX~}PRza7Qf@WeQC+E^CgM30gXNcG;quYby|ewv6ug-vx-m|XM-Yw~1} z(2?aHg=;f7c+9YeOeRdOfF{58n0aP6kshoqq)+@Xhf7;3awt?%E4+~?ySpP2&-*cS zxoVuMepxc@j)VDX?g)k0-2~oQ4hj#)6*nQ^`?4JATw0^gtZ_^_b{vK!MyeBbL`T_N zUEp-h+k3wYIcR(0aEGl&Khp^qw0g%2^J)^sTLojP6*GTbIPR7=xNc^Q$v`GHeryxO z&$7+QI`>{e5yJag&xBM_@46DeTCfNb$Da9Xt6;?wdJ^ci#MPe{vA2j4Rt+ z?m@-TOZ<>inxd*VhOerXKXy`1$Ms_TT4^9L<=UpeTR2y0^$_b^wziY7su8M351~?KPPj(Uf~SWvUI0M174x#BVagTw)QG8}$g7R1}RX)nRaE%^Ri4^6KcsKOf$* zd?R_qQrMr`T?9PBn?0mvA#{rHLwL`UVdq}6{D zWqS5i$is6!;v5Dh{~@zZIlaWvqtCu%3jN&OKxKPAdg5a`%KBmA6!6<>IYi1YmcOqezT8Se|R$48ci`t7>V zTP@DSJ^bCsADBd0xAGEbH zuaFx&`*MacH6XVpUGl_^<5S!Xvz2dkZQ8)Hac%Jt+eh&(y^Dk#-3bQW_A{={hoZ1Q z6UwD@sH|Gy%XHNDZPsLT6>_T%Z*rx?SH&z+lpQI05`sFP_ORz)w~5*y-#x`At(s74 z-Y%anK9Z(kgIDdJoju`8(!abM_B^H~Wq5FUkUc%6c~*E{X8efo^m-~~q^F;Uk_CBH zv>f_eimr|Mm21c%&HpGT>(P>5Gs=-?u*3|g#V+}Q>Teha%bf(PxQbT#qnQ3D5v8`j z;(2@7S*UdenpGsabuF}{E+@PKFPAc7KS&x%2MWkUt^ko{=JaKXQeP8s1y(4b?X#>C zp3ahMM_YTHwsP54pAFA(xXWSsaU#c@?L3>r(JoNTBH@HK8;`;5!|eP2#uCt<>EuzF zKfQl})XeI78NOG-=YsQPOoM^XRRJ^i#f;;tdTD9 zY+ucLhN-y^W0Fv?g8B!Rf<+_)W%IuPIpOMxVO)y^Q`Z!(9K2 zZ+@G2D8hvg^)mFW<(%%a7f$mc>O{u1y2V`+y}q-CEK@(6ND6ki@4MJRhcgS@p^AC5CeY#hg7!^bw8e}Qqj!sWcI5&0-&3WOy zMBgllKbI}$ygXK4XGS-Pu6UX3YH#mkjx8BKU#66NGnd;=8vm@gQ|W;;@m+fr^U4r+ za-nQ^jGYwvvKtX6+UgMW6xgmW95Tci2wS8;m1If?TvZD+8mQ8wbA)2d=(TvVb0?^? zGO$xn8p1K3SW}MbK|8GZt88z}t}~nrkiADf_21tPQGY!ioN^DVv>@~eV1L7b_LHx^ zL_#u={L`u7r#ix%^K`Z3Rud4~%~hG?*7Z=wy}M6fcu@%=serp(!USZnHC zr^(g+r)-FFvm%>m)=z8BS=L{>J9_A)u%<7gTM5&POi|)@H!1xQ(VEmzNu5}5EU4kM4}Ch;co4E2N~IemnQE~kua96`&{ds;1mH~8`yYSZ4|I-?<9aYO_O5AAf@5p< z8ol0?=DmYM+K^i0_E0Qrd}hvlAWG+(*AqRKDNy>lZ#iDwFjrqnU(w(H+loQCZzY;;BSUUR6reYFtHPsGD~#UXX^ z5xfl2CHZ3vHbW~w7b$0dr({-3OGv$*K(P2EWT=@YqJeuX0rQ)jESFQVPAH)l%48O7 zM7y9z`k>7vvP0Ps1Md=qVl(&4XD9BI$gY1qZr51$otf6j6XhMJYvA)aq*9{&&cL~u zg(4!+NLd(r%12J1!;7gkG9oi!frgNiLMnwY3)fJkj~C6%CNrDw~()CBQ`IRbN0r~skrwOsez(rPVVypI8J=)rZ z)s>NVM0!3?T*OR8=roZ53KYnp!OU5L_(&@KObR#;k>5WgeBXPT(Jd0Zu%RvH41e2ql-jl>BtMOt1ZF_$2I#=+v~&468Fs(QHrW2BL5H zFnqnL3zc1NJAd-?C_9>UCfof&8V_B6c(*NFvN~p+?@8v~=*jCaU*rirdG-jXwmr)B zWQA72y>w-Tlirp;L6H|8R`HBh%P-m_>X+M~6}wk1M}t>)Uw6|Es@-p{T;L$2H{~zR zrpq2~Xe%){A5mWfjy;aN4n4X*YOi)b1m%2lyA{b^VXduO(ITK_G{-Bod>H`svrH!3 z2dGiFeaIGXCX9VXTai)qaDF)`S$#Qf2)$prss(0lS+5%PoYIVDnP043r9VE@*1Q0J z3;G<>X8iE7MkGY`H8%##VMof@HjE6bwq-QtK^j_HfQ`t^%>tH8q3(0@C~d>VUZUf6u8PAflo7h?-a5_emA4tvT4Ovyi!!pg%QZ#T z?&Qlj`6IyooY*{;zbftu%Z%}uM@h@Yyd(JPSF2LZh5FLU)^Uaib-Q`HgGs6yRZ6|? z%ggPxj@wrU%bp9okL4tF)uf#6CLVKt(yqi_au=Vlyv9Y#ta-f9()h-kCoecwgFTFo zym~@|u^vjz_~vwM#13!o($zCgpS{zd!Z{ur-7CYIZg*UFFBgxgz4oPg{m&~V?mbR- zqstK%+pXm2y0+0}#y<~g+ jN=4{7rt0(C~z(OaL93}?tjMbhafx{glooiNsjNB z#!aILF!QFz32Kcpd^MkYEPQ?*X)iFZovs?+Nk9i*&x^M4S?zFML;Ae$@Gm>H9&&W+ zYjHW6*%ua0rY<(i6cx!X30CVNb7a2Kct;Vo(|W+Bp#h6z!Vh5;$HE!hWZDJb8c~7A zW%n9Qwt-U4eu2r-ie>0W%OnS`-+c?ZJQ*IkEACA6$q`=%-4+9=)h;SK^^R^D zhalok=0uu%++-I|I|hzp?py&V6N$B=Gh91NZWopFllKNQ(QiH;8OrHN=_zJaJAC|7 zK9)09z?R9D%a+ZS&z8lO$Ckq;V_N@ZrRK{lieu=o71M+#Ee|x5n3{ipFTO)n3o~48 z$e}z%QV~^#XtTl3uUav*Yr2<@C%Uj6i*JdK=4G5p$j;f;Q0{i?bd&ECB?h4nquZ7s zIFeY87#bPWhtXsEn4v&HBSQ>>NX8xP zLo7v%EE~H;x0xuOO(BB=>7e>y$9K&R9*#)#vnQMGqFPu|%Jw9ihJi@^3wrx6sOd)v zav5|;Q=lRRl?*=Qii*Nc(l%B8Z%7$kOho=@zE4S(NO6B7UX&|RP|D!S{F3|x76K>P z?_t@>V z889P85bF@1Q9VhZih{>4_Q~wt+PYUpyY^x9o*@MX5rc@GVuJcHAna+9;$SLp^=N@t zUsNyqCEg_(e-+;kDm%L^TOsze=l3#|%vaxoE!zxxQB}~0X}^7ViOd;TEqtK<8)uyh z%qz>6Rvb9VXVo0}ysj#2^T;Lr64@2n<%aPuv?bLWAacuHueu5ve<$CFs}FDpJ!j8# zG{Eu(Ut4pkYhP4%WzL;DwG1xg4(&4<|HLOa)GU`VSg%a*|RIwBe(dT}>Z@X=4+h`t|u%|esSMze(CxzNg$oEX~~X2W{x za$}PjLQltpc;gF6uw@G=3P$W)`dM$5%JySfTUrW)s!GglQ3R5knxdA@10i!r8-e_L zq_;tWCtSrrgh32BpPGFqGNVJo>}q%q`WN@{kJU*0)31NHoF-UpuHj)R<71w{c|EA3 z+OV_xBd+V5mYJdIvlIC9uX~<){6W^22O8kJ7P@Q63CYFZZ99l!EBU=_EbqG@Fk8rX zpsb2{)ny(gpL3!mW~OYvoA`~i_IXws2b2afnsPEBQj^loU>m3`2bF7E#v(gJ<{cdz zl^oDrn#nA!8I*H4e^8LslWf3yl+hp*qM^xRqh3Pu+{b|%4eF<7LO}Cb6&hTlX2zU81X`_90>ln&GH<->T#+6&3>rV0!3*1has_diz)ghZY=67Y3vj?O|vQk)7V3U|sAq z;g7vO64J-ct~Jg&f)gF-g!`KUQWm>PnC;XhF)44L>1SF ziA+Z!5NDp*^=|k;PH#DIq8V}q3I{W|ibDjP<3M1rbeVLca#TT>UCx52Wh|Zp??A-; z^+7U!g4v-266Hb^`K{||VxnJX>?xbGO?U^Vz8w;BXU-a{cS5%+_KY8-Lx}-@X^s#`NW$0TmSUlsD>-Mv;$7}ZY?3#%nJR5O|9%JA|SSSsP@W8r#%`bp3 z!mc4vH+_`r)s^8C9d9xBK&ZgIP|jmv_h>j$b~^uTA={63d;Ww*RT6(h;G^Bg_)`bF z4Y7BCfbCD5BS!!FcFfvt+Hj*nSar4QN0VA1=jQ^ro1ioLcL<|CPJg>cP=8k{Q5IJr ziYV}c1yY+THjB;Y+(aDk*K)_r6C?3E&)!kU<0@=_Nu0g(j5e5Uqy7&>FP!#FD_h$|56L`^HeC=brCq3r2TF~jD+F1#~Og3B}(?&5mo&`}M% zC+ad%NPZAFN`xa#Qa@5i_hZR5XQS}FR8F)uUFf{}!F|U+IQNf z&Mh%*MyxHY*7TuaGX!>Nnhm)K1my;9OVM==nRDuP*WlqAYmb?AaVLRJ{Xy` zACj8bNnk-Q-ZnGJh=D1nY1Vi%OApbLJNw{3EQN=#NjXId;F`*sB3pHh`X}*$^Qq38n+%2PYES5@X3yZOLxn zJFt)PZOLvnB>m+8@JzxOkvmopZ2dq#&0-)B{AaVc;&ycC^7&{viXpx^zFKB zhbW^WDT56%$RVL^zfPaAlzU>X^-ow7#3Vhb#SPLOx%HxCJk`FtnBR;!iFDpVIN31h`v&@)BWz$Qqj@Bi2zb-P(QCyZNd9vJp7qE zAT-IU820CiOjJ`5E}ssOViW*>a~vTQ{TSWj0g@kp1dvkFM*}3(C8JCQun3ssQxtH@ zO^Ky}$z~r)o-DvX?t8!l+m=|If>wqA68j!V90T|ePKAn}czK_hsdD&E>}QzO0sgW> z{K3eXXyjq_$&wP8ENqkcGkqW)gdWE9;zZ!1UixYVPXc97jt#ygiwwR@OdzlnVu1ax zr|pu;rwm4Qv-=_+#1fkk!>sW9>)S+fTU&jbUv9y2*jscP2yKH>&A=%KioanupnH?j z0{^u+?kj$2S~M{qcdY0r?1~ThbJI|{Dgk`UJ!sy`Fxx+SKLH9;3QU;kxPNG}Bt5?zaV>$uA}w zL){meNuTPHS`W>LMMV=Oasz)m;YefmV$)#fV;f`dVB=!PW2sV zqABNN0x*(cwVSGp!SsI{C^{G_DtkwHBCkDBZe$MJiV%BZ^)Qu^N}d=F zoRo;R_l%~NOTz84NOb{9P-DX1ma_adh~oN`=iM3kTuAnqXY%gC_rabseqX6Glto@4;GG~0Y+Y#cq0f;NFTt3y&UN~P`T(poNt{7P{fcR z`eZ!eYmDk+BB=m?lHqI8y@ieIC(}{BF0Cl10F*t>#4WTp45?OXGFgx^3hz~z|A1lM zb{SQLa0)}XE@>i@<GajaLPIPj+#}@@8aFk5C&q?SfXjtZ7C8jZ;Boquo!I(_Q+56(8%x^7JD=>Njm-z{24hE!ck1du#B zC)X@L8LnYqVaE9LQv#I8>Jd1R2^Nn7p?RcCWC9Oha*(7l?$o3j! zy`K{gznV}BUz+Sd%_#0hYBhv?P<6D3VS1-TI7$(OE2m468) zbQht_tS&Fk8JpAq+Dk#1`9D}_L6*>xR8UaB*tv9A+L0+U>@Lo}Z~McQSq_?Q@8fzk zeQgBjcc_jYq4&aLF_7NaXrQh4v*AcNgez zuLy$i=rKGiAV;VkMLCZA5~l2w4=d)t0LNSUb4-^7>z{`^n`zWA&|u86MiVoZEWX>( z)bz@!c0+oiqqtZ+!v@j;CrTznPLs<||f{ueF6h<2&~9GJ-b=0>zFvmAhwEnUOI z5T+F#k}Yl-7Lvo06~fm4 zM#?VY|9V~Ol)_n70f0+qf|i>!IHe%D;G)jQgHOh$PkdgP-wHW#IVB`o0ZIc8ut`V= z`ypkce$`jScc5elIjEQ_~AvkT=~ zp<6}KEyV7ue+bNR+n~~*j*j&iA43P$3+sO+k}VzNFalT|gSF{HV8hS~clCN8w8;qx z2=Z(O6e6dS``m>*a`L|SheGncTA}jUR+4!jUL`h$P=QfvRnqCPKQW;aS5L1a?s~<3 zque~VP2KW|bb|o7+Kil8LW~6zyN6+59hrpPOoDq!YJX|2vocziAZjphh~!6HK1~W>2+_j=&5uTRB8k02fy9-nN9{%rk&6r3E&^6 z7}@@)p0mZ#y%Io-FIRi-f=KI*!f7~Y)xmt-m ziK|z$_qP!?`mA$-$>!WOUa}6reI5VkD^)Fje_M8=;ek8d;IAW6)NTI`a71{b{*os9 zivzG0)V=;dIU8jcYimLN7jQR=c^h^Ty4c6~dY}5KQehk@$olRMl!Sic%Q9zL-*HAT z!hMGCGg#fSh0P_a8ipg)_yG6*g2?&XYl485+@Z}7u^Pdx!uC^aqs#@8dl*boOKVJ- zWt<`WZCT#(43nAH{NH>N3jqVj+PqDaQ<;(|>q%epK`K_OU-hHi7DqGzwLNoxdWYbi zpnSa6v+<~sta@x|Embp1LkyT*mZS?w&Jn+Q?vf5lUhQSAu%iwkACZyfZh}8k&_gev z>WfH*ZoQkJM^p*(vyS{E$CqrEUkd|pXz4SjlM6sXU#@AG=>c=bfvBOHLWUGVZFC|( z0P42g(QQDc0ib9re97T2=D*-NF>Twt;{lIb+XpJ3&VIkY5e$2@Yp}g^OK47F_@J0I zsb46SVo$I7^j$GQQ5K#+Az{thvPRvk%;iGY%z__qio|qF##($Gvh}NHT4gQ?-en7`4sQ5_0Kj7Qg#gPcJMAb0g87)2-O)V84TjPX-bH?Jh*5WRZ8AmwoX&hz{iF63wpec^3s zFKK|*dAJ@iMj1PzfD=2+A9U>sI)%SwW^-()DLcPE?7HUZg^o!!Wqx)AjSyNoldbyc zL22gL=JX@dW7{k~c^q9#Vavc0;xyUzDWSs%BVZ!jv{(R8;t`C6_5*()BKeHM_csq6 zzyyj2KDsY&jv4-XHbU>(TKd@O|4A0o^MdPw+W(lK4(v!$?5MU!P;Bt#k=!OD(L0fA z=%!3V??@nLCX9z6g3y;C`-IR)m0%i6O=_6PEY^VT!U*tcJp4ewFS5G;7s1o}JZA=# zlOagJ9e)@1|6GK2W4c|m{}F+x8$WF5UxO^d7Y(jFsEGb2s>+~mh9|bkqTYW#g0l^R zM0;XnBGb^aFFi^zUU85-Dxk})%4$<#zhaFOK~U68u0r&IaMyfMprZeEo^!$$koDqVyB-Y71Hxe*1Lp%Ce7v=<^N2Ym^J89*W1rw_6@FsHo&#J|6Rs^vZQ%h zLVzWqPoaO#R>BYqH|>CxC-!8>3CM$U#>6|`AFIrO@^4GX`!>}B1$~)FK35^sQ`}71 z$sJf+tRuvcQe5esD=AZ=Q!ta+Si_r5Z2RYlDfz6{rqPEahIA$a28pMw6+*bO1GcMl z;dV#Du;Bs|O(q+a5ZVd`piVUT2%OhdB)})zdm5?YOqT#QnXwW z>Y%R@d^uUse-?r$Ea`t5^%g3S0GJ7XEriELz$UQHBBms0|7}F*t>tT%(>r89=-q_8 zEpw$nh$A@+cqL^!L_kAjdO*<3Xn=GB8jI>ga@Yz;-WN_ge$?UGa{Qc#ge((mJxJk+A_uji5qy*nW; z1KJj*KIpp&|Nm+J<6B>u?rv-vKaCDu{bPD|0(S6t0D&D7AiRtN;Rklazzp?3!~(!I zo~}ZGE>|Pdqa3_dCFXt`Cs`|=2dWMrjFWO8=?!YVajic z{vzz)TGTomS`Z?A7(%&EJ1QwwjB+C8$tl6(9L6`_;QnCSj#5&waxs7D33KHXWzVU+ zoIUPBaLztA-AzjwaJunKlnJM9l;x&;_5oK&(+nw9DlLtdA)+aTcLGU7O-66~BLFi( z<&f!aK1gAG7zl{HzAYGmpV8@k%HT|U8krc>nP>yS_jPlzWqVJP$40pX>$yP*HyTVI zWZU(4py!Hm5R7w4sLH5{&P^{F%$!`@ak~|M80423F^= z(JrB+HzpW-R*(b0;;I!eyawIqP>QR75&B6?3N*T`Xr{agl2g5A0R~fQ6m6)h8$B?J zeHgMSK87*m#2Y69uBSOtT42d`M~3!L%aILGr6@IQ+W5AQw&r>#Ms^0-;5ozp=ZD(x zI$u066a2ea>YPHEHWIN_Fg61lw;bhtU5|L=eSb8?O=J=(+fJ2o<%tPjU&bE)nCZRa9stM-g6oNL;fb{ zKt!1iO_6p{Or;VRQKd;$Sv)Vjg$%{J zSJI8W_96|xSe?RV6QbriVpLk@_|xJ1L=5!G`aRqCv^?_>&Qn*<)Zi>t&|UAtRX-h~ zZZ_P~{Jp=`Gw#e87efiAJadZ)0&cOeL$yZY?Q$XZ6NbT-q-+tD7A>y3&aK!;=|tr+ zxJ>k6vy{kyeEJMH3s;eA{#}Q~$E`R9tejH%`|3=y$3FaarG8DHU*y*_Z=7yv>i5VR z&AF>6|BQJx)7D5&WS5@@oEM>ZSbK>0A^0q9zsAAsr+aTcYL4pSorbEBL%xk#h%auQ zP6|J@kh!I?Z+n?5Sc$fmbm*Sl^JfvVKJD{9PQVEga3HoR8Doa)VMZYev@$5rDbNqm z4bd~vG0~UOmD2yb=T@b=q`SPEetKG3l{-iN=9%t)eGxH?ZBuTtQ!CN2q zmQ;(=b;gT}i7FqAKH6;t1O^XOO;xqeacXL3I`&HGUJu>lkTkM9He=cIp3 z`*a)2O3boPwC|)CBO4uRs_KfC{#am4!fS__Y2g{zDRAXaJi9CDQ9xx*fhU9d1~ddO zmG4<7tOU0x`_;>ss2m<@FL?x59A*ok0OfvIXKE)PHJyOGiSJ4R0~HvmdLQ&mvp+pq zJO+T>lk_QkH7=f4E0(RY)sXL)-}ia)o*(l@mJo1IU^#RJk$7Dg8w^ZJO0^hRv2RXU znsH9Wy+5=vh@9n7W9O$Km<(Gaz-KmcS=BbAe{f-M&wP8{so$o;20}ZfwFRo?pTX?R z+CLOJr#`}I3sG1IdwJ92-jg{-Prileu&5n zu9jX&{x=nsm^@Ki{MG~_VUv$ znv0jgpH2FxEB2*R!D+6L7s`w`_MGfXk=!dPtHcB zS+DyBS{FTj((Iy?lNXIc?FxBfi@F%bysij&eHOp@Vl#`u%V~}2d~Me1+)+@W6ty!# z9xUXZ`c$9jcUNKmvo?x)eT5vungbLq_}CX?zl449vG3fz4rT-6d-(GRc6XY(d!2WJ z(r!1*6Y>T-u@ISyOGB<+WH09A)Lz6j@*(W3)g1y}ZX>*+XJL}ovh={f_Fi$B_8FMs z_0RgMsfyb?V_qkdQL_rnQ|+n|+=&*}p#NG9h^e%~~3dB_?ZCBjM0 z%ZZx_J@QjMc4y`kxszjuEuVSuCr7_^W#o*)Zlal)WflG4Vq`ok!C_3sGZe8*WZ1S$VST~d%nKW(9>nm98MHcTAf1san6(sqBm;$3^f7muL2DWIEE-F~`?{_KB2>y=6tN3zTL z!nYb+(=E_v?v%7dXF;M6Kaa2=H;{b0T_MA7%@yf$l@O()ir9iMsKc2ys6CxJHW>d^Wt7<`@3&o4dZ!Yp zOp>hK^x{7yQW+%iy3NolsWdsm_c{1${X@$!)v5X^l#UouFQ}DpsIs$ zm07V}kf%dWzIzh?I8F1B*Bc083-+hrk`T{tLx7kEV_N&EjUOCr$ibh7fBtl#Lc;f+ z;e$_7tJDw^Q5c24?^%jaVDL;0;@BHbkR6=yYHz_FS@*_~TMLT?eZf*5>T9hKSZ>`6 zDWL)*d;Z*CW+Q8;l|e}BC)xLs=_(s*ryRfAlG5^u=yF*;8;Fvq(Qv=R$+0Db*@OQ? z7$b_vE@v2>Jz_DHd6G<#X&|_X(jR zZ_B1p^!KCtb0GO%n*(VIq^EsHcTw5b#?T@;of~(iEo`0~ar;|6M_Z~I;W>p^-(-_} zfmpZwR63`g;)Ox3y!IM-KxES6*QI`**GrF^@cB$cXP(jybXQ*MN;>=Li=Q3&FW-zX zov84h94hKWs(GW-%%AqEOHMb1AFc>#3H!vub&~Bz91rTpcb^III!HI#qYFLL)TU>f za#efY9#xGnT>AzDNTWX>RSI^mh)=!0M&FFlz5_gl=q7r)#lR+k3)8A6IfosFzUT&*f09Dz@7jEi4Go)_IZhX~VT&=WE zh)_GiauEMkkxPrSW&emZk(o&_s$`w3@W}n%zoLY9<$~tzHxp5=Rh9UmW!w9_QS5i% z41fOV_CP;m^5%m3n^?(&GFa_v{DI!*);&Afv*z(e@f9#H^7n#gyYG6&8GQR=K1mI6 zY;FmEw$di95M?=C#IsS(h4PwY?qnoEB{qb8D=ox8G ztRlMXy6s-K?mp#;7#XAMC7Q{jGVWxYFI_j*t@Ckr1r-P)j4G-D#BIv?*co)x?flJN zkqxiuz?+tGj5BIYH8ICCWw9*#-#^x~roS3XRE*p}2Lj z1=*_LfiUb6yb=bfvrR9}oD&{TY;R_oZEiGI5+GmCr8c`fT-?*mnqV(?{PbgV9pMJR4yF7)Kqk)E}ukQ=2mV3H@DJ{q$_Ac zF^>9GscyfhJWC;w9rdRmTyMF&b<;->S&8)x>4bOV`1VV9zvxf;V=;orOzaI3*-0>2 zXZlfK1?^$hnt>t#GLFbxO!@nxV1mx{7EJdRdORb4*3}q)yEa;^ZFeQOL5k4i^XAto z$LTedx{Xg@RpugwyIrD5wR73jnsALxF4NK<`%j7bE|qb3KFUXo1^&jo;L1x!otMCIF1SJv<~vU+;J>oC%MQ%1sarO%4$ax<;#dBPz{y#hd9Yz!uxMbd0ahV|{DB}0@vOH!mpM3?1actHE)By&kNVBJ12b=8c(1$7 zdE#N*YqE5wBj3iwm=NC?=*D4U)G5{mVoV`8*S7z`^ByFnhu<5SG$O|E4e1y28w{-m zQ_8nu2xii5dAMRki;^G35WaBu`S=^W!QR( z=%ht#KCPss?B2EsEkT#;cSbysw_+;o()tTVV^(19ChT4`4)HmMAKJCZGs5BuxG6G3 zlul;YGR${Vgknc)&$W@Zfz6ZuZZ2NBlJUFy{%L=cFH)&@KtW+kWvBnH;iBzQp>!$X zdav@dlM5`KgxQ`U6f$)Iai2PipW=H<(o)xeLrrGcXb7cjjdFdSD}g)jOPwpUa{+!7 zqIUIYD;O)Y(}@s6D@5k02JEIz9VxT(Ck82sRtU{X9as^kOzLtn`q`y()Js#?t27qI zWsBcSndba$`c0JG?n9R{o!rH^h+fe6qvtEq<9jb1{Nsq3woWLub{Tim^{nZil3QV= z6eyf0cunO`1cp1Iv(hQ*gIDDR>BHh}{ymTT6$0PvehFHXfunBEXy2j-Gp5jeGP!f$ z6XJ@&i&7tvhw#)|MLE4>({URdBB{r;{NHUbTDqR!LMz+jWOP@Zu=$S638FR~btm6O zv;_d$9DCMWpgv%=K%Uh}J+#NxDp(`>=$p%HEw_%_b`VHC!vDAX3;b4i8!i{H`jV-6+JJXo4M72obu!BH-R3^@9oA<%tO}3XURKq$`|s}oaQptgsYNPx9o> z``+)a@BZskeQNJs^`lpH*ExGv)Q{`OwGQ1L0Uzq9{s(|_N766$b>X`tLEP_Eb?wMb zNBbbm@Y~pZ>w5*3@RdJw^eb-P;a|M|t!Ghh^Szk5#&qR}5cij8@DTW#_s9hhBnB}c zra}QFp+K3V1Vx;izjyNHMYXlZ8qn_JB6u(I{B+_4Mi71*ZN0xDp$qTlUEa?=znyx1 zdQ}j9i^Tac-F#-rS+^!Zenj}mFCYHz?I&K~7v`fvtrf3e#CJM9(2 z4OkN4@jq~G%Dlk;JN^%@Gc-c2#3P*c!+<-ou?Ts4Pg^WB!Q7gY&Hvl3OsVER6Mn59| zsGw-0M69K`$0wsC#AhrdDGHMEXrM)`k@CP;+Eo0B5BI4Gr;lcHb)g@1Gm|mbX=#4l zfBBqz%{%6w>^|q*jUXlYNV5l~gaKxIAMRe>)d_d_zGKru{)No{2{Cjw-e0}KaVMqT zzi%}(-T!{lBHQKp9q(U1*70=t014;`{)NMRe&64-4)0D49`BD$zX)1-S#?^AvBjGq zZPMubdF4#mP#RBkTZ`ENAkn6~07rQ3@r|QXS#yp1bbaT*&xyN;U&;EiVipgY98ZdV zPQeS|6A1tqx&>6e!Ti(wmb&Oh=(`YhtGFLEQTk2#H327c<({jL+iE^XE#C6+kG_V_ zG81n>xpGEClSIll?{Hg0fDdy&GOn~`an{Go?&4nqI4(t0iONq<|7^Gvx%JDh<334j zP)Z(r-ni!eZ5x4B3ra`_L=BgBDeuC~0lb`-h1I?!f5JenRX=;*p;o{(-g|Bw&uDsI zGoZJL-k_!^17lB;2i#C3fP94BjbrXz95PW46&OTu=wUhf`e}2y>4?p{{DP+zWVYmZ z!pxu-y*wGNtz~0gHkZt9F0KCPhwFxAe^KrwWT}V^D$n1AM;e>UvWq<{p3P-}<^psw zSn`oMF>26{rM{&kl!i4u;7w(Z;`o%Yn4=h;+iFUkar1jpGvZ@EBx}U#ilV9+R>>^G z7{^7HZhQ>l;>$?83I8elw#NmInG1KPy|>NriWd9Ap~O9dj=1ksb91P^bu%m4ykMNMh1Rw{!e}ZqgusP-%MBnLR(~Zf7ufvve;cKU0Cafo6@!q4Nl*_lIP} zngf&}yma&x-Nd;AsVk8pj-#Z}Qd-%&JAw6Z7lt%_Tc!v(5^Q+>bRiq@*tNwHVrc8& z34MY>J!LnJoGb7hee#d6_gESV!5T#htDf|aUfCYzkzeI)^SJKWNqG-SoyrAmorXpb zyO+N?U(Xw2e}x38 z%nE?2O+2CN8zs~F)ctypPa={9JuZ<#-?O60mIps?Lp@+8-ff?U1y(fM#sCzXTeiC%wEa?}BXmQa3 z$1=9*+3y)!c8i=>5DZ1W#`-wvu0H|X1k8evU^ zR>RX^v)(yqI6Z}#pl;M*HL9Cx@U+Nds=5YWP(5(LnN_GuTX5kMVipTbMxx)W z`PteEu1k=#x{wokg6m3?Dt{qNy^NbQWW#x389ChZ;}Bko)~9>jb#=7tLXo^{Eu96x zpl#D<{XwrmWbzuTJrYqmD*SRsez;Cms#Aq7;|iWCnEE0uo335SHYrYUsw@+0c1r(i zf=&3}&Q6Bh6Y@IiTJ$d(o0pKe423*p@{_hQ z=VWKqVrSj?cJZEWmd9;F_a%4tWyX#^w8oB|;zHWS4iPj*mrLBf4`9zH#RECaP2_ni z`)q(Y$m4AgS>^Ev`u*SGgJ-tI7_W_vp|kyFgb?&A=U^<;peMZb%+4EIWx}>qX*>oBY?Ra!Y z(eh=bk$KX**4x`eGhIsr3yRduO;BQ|y8de>dvePQ{^F4dXfV6=K9iBb;$${6<;&+) zCh3VbuZ`dtnGrhz_W>oTFZ+ZC#k(VOYv37OJWJwPD41tn%5T7>xBx3s_Tx#>>_f8D zCehXYUP<1q?ak1xS9dv+xF=BPQyJJ~;*fH)PznAii>oqat!`=HREE z;$*54eIw5<5jV_Bca1-MC3nT17=wBiW%&D@Q;Qi$Ixf{D^(`A<7tN(UObVqDJF)6d&=I`@A>|EuR1e5t?b;*{sIj{E4<*s~6Yu;3h-~_+3S9?m(CoTl-gLm@h;8=t zne-yUqSH`wt@b+U+(`~8(DVi0+WYoVtwt^h8>+K;jASeC!i{?Z#jpNgXb-Z)yL3PP zV&>df`BmWBp~9W91avL39v(FFBw1va<_bHLNAL{H@C`m^m5d|xEEUu@C}qjuQ(Op& zAT*yF%{)g>k4lAUuX9zSJR+bB3dHXBi@aYYmGUNi!EZw5I>M%A{z#KM(_ssD^e`GojDfm+t zMTLQ7m+LbcL&(;^qMwA?;$1j@xz!<|_g&iFjr{X$2u_g*0jGFRx1|n^D5W7Iqyed}MyC_&wnp-GmHOQ(EBFuLQk1&P9@cP%){=4;kb;9u z@Agw?=u!$8H22f!g)tYxPJE(fc1f8FbYlnnUM%QqWR73*{suyKrZ73KE#1#S6t zpX|OjkGU#-y{#f_&+oenesLN6M?iN2&Z7AyQ%3+V>)fJb^H;9nCS+DVq*1z?4t!A+ zVw+&$@0>n1m7XXdGhMg-Ib}a;Lc)~L^}dw`}EN{ zmn}8Yc#Zu4Ly?WE0&mAr5+Cy&*Uy(W03#*m_5A$XkB^F?Mn`txESs!G?^uq+P6z${ z+-sVr4Ib)E9?N_eyx2pE0}eu&slGqkIo~#fy1aT_^DKW>$M1p`}zXRVX}P-xjDA?iL^GEF3hSt@d?~nysrJOSH0HHr@y%8I}^XfmB`wIk?KBS!E*= zB`1V*Vv$?*$UCwAUf^AU0eYc(d24aCPr;b#Kj)`)41F6zA(r-|pl3bd=;ts}@+{@q z#R}7(sWe95_u^`JayFdo0SZC1aBnlXGg{jNn!RMSZ=kv3x5DumMQQLy@ju29#o8^t zmdty)%O-=c`*Ew5>hjV%0hrVl4vsnL?BsvisX&c4h##{-l^kNDtULbNStWLtv7Hy= zKV~}Dm5kuEiK*V2ZgSwn5p*IM-Bo$&K|WvUkDPyXxg7buIKgTZ<;4(XS=ETaX(eWc zXtANej21Ofl102bgYh*>9zMZBRo`2`Nv9-gax74XGsT)+s%Rr$24`L|(NnbT&qxvm zFsDc)`e;cR{kdpKUb~;)t7_aYLVPnjT9hbj5FS?O4Z*XRUMIhyWFj9ziNIW6VB{uV zh~jHU-GG`9lomI^;6UVt2)Z6{G*&>a0U5Ll?7PYHu8kT^z{Oe6-Hx$-$(WBnDy79u z%d&aEyo5TN{%z+!OV5Po0i|?hQ~s3Hw0A9T{@NTdzhSuC@om|(@>npcp(rOTH3SFC z{u>V5fHrdUcdrfsecO|w&>S;Xty&L}kErk4)%Z`g( z4Xez473o*3?0oq3#p`VRozm?x=zx+_UxaRZF%+_$v$qS))mFP!L91RA&~H3AFetY> zJIE25%Q?86u)M+YP21>c9E_@&68bb&8AVp*<7v{O9#?&Z^h0yo14>lQuaJNgsVZ*u zu&(D{e>3B&i1YK*d_0e(_)k4&WHdJWmg9|W!G`C(g-xhnvJygLopTm9J;R+j{*ONV zLo6~=>o5{*yRdiUp#*=cM@}Ju6>=B|E-!@1KI*3L*e>Cq=uXysbA=~p`} zh)I}5lqL;whV>mBhIN+1*a7W&jRia5g-6&zdwnE?#{M!Ie=LY5f)k(w`#=bFXTdpm zqpVx;BCHX^09sxsIaThwHK>5y%bQF11x;G z1z$c8{jDB#_M>Gy!z2PKyo(#b*#dACBNDXU2e(E)zw?QqbEQ~Z1RckJRsszbK)6c7 z+Il&gn9tHTk*AM#js(G4AOnF2PuE}8`{^mgLF#B62u#gtut$w= z$56A&1~GCEwHykN9=MODrEPD^B`5>({-vcz0cUtVg%Dwe1QAbajJeo$oj5(LLf6Ut zg&k#bU9XJw`x)QWE=>xu5^ZyE#xw5}?tFT2<{DG<-;ui?FtAoOe9{e(-_EJo?$8?5 z@dudStf`3@erVEvBK?wtC0I~4goN0Kyaq?OLaZq{x75cP$oXd*la3PTMw4`W!{d`A zd?Sxp6O%~D-ZBIq*o}U0={;HQat9fGUgoNFwU@Az5}^{Lu9_E(qL%E`)Ew`mm>Ljv z>&EaFBYt^qzRcEluHd?QDJmS7Y4IZ}6B9Y5&*Vh*wg;K1phMZ(Yar-+*K@c(IBzZU zrrb<1?$T(4QIQSuh=@kfoqmgY1CcLJ{qnm=vBT(TA7*HpE#WqVXh)SIQ6^gYn$0p5 zCWT42bkIoa{A+c7Ns<)5bgPR+Q=lYh7q2$Uke?>}O3+iGUil%Hv|BpBUIgpcg*eKe zw#}-|z%?!Hg_HYE${5cglrhSdbAe_y4x_So&x4OK@&Qg{T`r@o!M+Ts*Q7qx4}Te? zJLgaaV=WwZ| zH8=PQc+ubvCr?H8jY8W}(QOPhfPgx-{Dq!@3%A^jrmjtg=Jq<4%XsxKZFOuAT%Nq; zcctr zIkLak5n(&2*W*Rv!v!K*rOFjai_ONM*iq=jFYl9DTi1vQ>6amjjbyh(T)AvxFfSE1 zH!Q1G2wO=>R*aD)IGSw(j~|GG2XzYr@kwA4ufM(Q%v^u}5YTFPS^e}~e~*3bdF{UK ze(iQT*&ZBbi($gTb~+zUp$zW~j{2KRdLxTHY&>{41;lWpbibTdU3c{;?R3rTdq^g@bb@atM?SWElUFwfEzOQ zD;!G}+b$ z5W|b|A{N|g)xQ|Ffe<@v-PYMcB)`G`WxEbL)Q&dvMJ~9-{OblevP}nV)iF5&&jZXs zT%@~g9QL4RYFM`_e3MvcOSTKXQ@5aobmObb_m`4IFU3Yqwrh#dU+Q_R2Rx}Y!wKUA zW95(hwYf189!6E1Ly9==Z=-Ez!jn9XWi+!)VZHR`o+RSrM0`mqWnmW zo<`IUAR+IZLT=r04&cL;WKup)VP|PAk6{|6lqzAfl0%(0F{y!pZ{B?H(J|+edk`?u zav+4=g#2*dxL6$_70y(N{i+AzPcoLNym2IxbJoZ++-@&qK<+QCPgnnq0SL5pjvo=$ z#R66Li#T7}y*rtHVCuXC7#`Vyd`Nubv58%Rr?l!#wDi64X|hEF@S&)Q1Mwdw#3_y= zT;*yJ=#I7@86x~mXQ!6(<90|$MZ#J(V4_@86!?Y`*#FJ}JbA7VW$e})@xoP>BJ{0Q zx&y+jJs8K}F(;P_92dZTrMOg!-;Rk65d_TfaXX`G!~Gr3iY%qu=TTr%ot zOAf^hTYXA?ubYTt1&V!eJ~qE*g@fm@+7eZ-6_s+ zN^abGxTW%QDI0$3Eu%f?2ZpAf*$w-Tjg0o)b(HqnFrrL6^JS!IS)xM1%qnD~ojvMx zM-VKV1{nb;#-Mf-npUk96vP0d8Ge4LYo^0n;(QFf0kV@%Jc|~J27EQ;P~t$>UyALC z*m&3G{LI<6%AQ?to7hegWu|gibof+_Xzwo>IJZgYf8bTVBloPvWDn~Vf{a4u__En)$L{d8D%Tk zWWyEK2s~cV7D5)h(NUbK!_XWycGM$6^7k`jlLIQ8DsHRJP=4|h}_ik;)20X zX`O_l0oIgOH{$C#W2pdt*~aF7TGY2t)R}HNopC>|()!XZJP3Q|^$=pLq;pRikikL! zDvtPuuWfRQnmye7^q~EmZ=sVzj8fjUkB*1b>AOx|$@9pr5_rHzDMWTyl07g%!ltb@ z#pjLFh~mhutM3_{mof4Q8XSkS!c|yDn+}AHj162dk_=`Dm(k7S^!Dl}-AT^^gWC3LHr=!j0lm_;x@77_k1LZF z8V}CcVdKFKZ6hImy9JbU3qcXfj5dWdrfDgKCJjv?{?0({(iYNa;HrB4Jv1m0g0bz? z!3#tZFF}z-ri@#{q-Ms8>JOj6a+6&ws@3K8F}sqwe-*9LHmuud8%+C^bT6y;cV=DC zY{XZF!Ewpme&kr&Thcd3^oNk~eIBUDS0YQ-MtCF~vw|j_R^VGdP4-={*2g(e=`Hq}+h19+M1oggGy5iuP@2C=Xx<|j_J z)EhvHP@F`}!O%+F>a(1$;OqCeH9DK6xJAxww7Q4d!}@^LaI+gGttQmh`I78vzj<$A zSwhH~gU8jpj&ZGo0nYg!p6iVSLSRIhhOyr1hoY5dD$qj2MHkI;gP2cRdQimLJ&!(W zn7&ZK>W~b?T&KY6TEhv>*C zy(U-gNvXFFCaLDiY-%PN4-`|$QaFSgH`ozF)IgzU6>d#u6?P4jikb^~-t<&NBxDvB zRz3XVf+wxuMRkS=sq~%89XkmX zS8Wy>x(;?6X8mvO$i$a-uzBaXIR%kfIqnUFmSni(g63|p5{Vemjo-=~e9G>^8(fR8 zY_y!I$c}TVq0ld;(1n_;22*wMt^J$v5DzRVe5Y~5xyAML!P{UF56*C7IG@Pxxy9ZvH^<@WgV-$Q$~A-E^KF?&P-h(wGIHhjFOPnEwz6+r0<_17;sBHg zzn!kPv`_b8Z)n*Y;p+X$wLo7$3gPY+yeuKzK?IFvPX2}^5otrVE@8jFK>Q9infxkr zZf_BCRn}f=8Jw5cO7oWNpMMD5etF1+Tf$fDSe&2(>R$Ay5t?C{L-pEHv2(0@L9gSD zz+KIfICYM1-S#b2?@MSK+G^;#gJ%^7NVpp4V|imrnU&r;(sI@!ftF%ix-RZezc94_ zu&Pc$!)RGO)fi`e`8ZAAY+RLZPDE4+{i$(g0R>P%&fVi{jn_PTMpzaqK(H+kf33v& z*cWP?6ONe@qGi@_Q*$gZDv(+h!t{h6t~ZEPJWRrRVOKpktF|sU z4+mhs5i%&GpcQQGkPU%&agO71T9hu|^ z2m@#%xPfa}u+Fgq;ev1?Wd%k4t0q;p`a>mYA-?mE0mS;IAr)@w^7fM46&Ne3&E5J= zRC9*EV5AHyr_q}yqAfodO+;SjnF>aN?|P0pJ2zDK@o@OL4mc1a)@EQs9qc3SQgdUQ zNYbv&p?(tto!e|G7E=vFCty!3HnT>T^IZ&uR{}vq)y3`9_&hsNte?b8BphpbUUN(W z-l%?Q`XlG~`!Bma5(Kx6E6-zy_>H;}n)uNN1T*;oke#7u|8 zZ{V$`e(dt1oxGEf(k|HTFBBQiON?)NXgG}n5Fc=V{N6DMJ%~W@>&#`#&{b<0w{Cj) z`M4$5GEUd~Z=|Y(uY0yUx8MWbiRckqJ_)9R@`mBX5JT&izHLcGZVI&zC3QEfi7q87 zJ*ZqE!(w|830QlPcOU7?!1c2P_8ZO!3(JmsAYLk2ZTGmQk*VwqRw;s9ely)+wRM-e zzxWe(_r#pZRI*|eb{%2Ky8B%IQB>Hn;JAc9bMXEUx^8nfAA$eU$Ovh@$@KnNgRlzl z!0Ekk``GT1(#XzPFfR9vgmUJ5KC{hGh1sU&GCITkPC8tDvFHbH>HrtTB@9k?YQ|oh zLJsfPy4bDwd;hhFUnFIYpEzsuJ!BVyb2YT&Ez~a&x31arS+pdAmUUP&8%cP3mE)8> zR~HFrbm@7fe2m@2l!K~Lcj}u9!;&^~QT0gn#D~;NB@ODCQL+zaq5EO59@e)l?B(Ua zU2r4XC`i4YMx6agJf3W9ot}YvW47%4(5wq-_0EI1LhaZG2;7u#edQnkgL%%UKHXNX zN4@zk+Q61nP|@2asK zBU`L>ux0pHg9s%;?pWu<#QOT_R6VWxTK-KIAdEj%wTo;|L^shlVgR24@pka*q54jd zAO8;h;UX;by4d(`f1mX28-pe^O5NN;5TSc3&23K|r+jZyq8O^Z>kWRj^9y@Dy88$F z?)^b;CJ`?uL{{UYw?0_Tlqd8M>rdeFuW9Dx;&#({nwG<9`_Tf>S8#iztJ1^YyGi)dg!Si3nt!=8M}$vEm!z((=LclTlsE&_K1!H14J zjg)%BoPkJ4_?KzezCQtS@Zh2O@~t7`Js)?142gEphY{z1NX;jh^GW3bf<|tsny{-~ zG=i|J3?rn;*>7EUfOSy;saE)2sb-*Mz;qsx1me{p$oqh@3 ziOSE2*s>7IXjsKr9kAA5Fck(o&R(!jxDY$zPsdD5$xBo-0k4y4v5y1T=)_@uqx5u32yo+s03_l3 zFZW&bdoI1sW|wxsRljob=MI;Vs(z#zYg0)qllD-1b1b~mW?&U0%EaYYGskG5`=#39 ztAd1nPmA;kUo~m+sk^!_ zvYZS71p40Tvo1G&io8Bwo}v%=1>N_XeImkouZ>zq)+)W|2b>x+qks9}v!eo?5Yy<> zgtVS+i5c;)?XnC82R)3XeiGVRzsR_>MS(|&E0Od^d=amEkNK)M`UkWQ>I$LfW%`8K zZzzhC?R!rlQ3Pt^bzyQa+_J^X9l?yT%_@2_*he~XQ`ypGx$ zN!@4e=A%m%cMY>J`3&LJEWR4H+Z}{2tj? zh4{`LoJIMh3L_$WE5q-J+w0`?X77*U)Dgam!S4y!vvYVu3Ou!c`wJm|cn5&#Ngi~9 z?eQP94HS233*NIv^2^v`MLI>_^2_db`y#v(L{dlZ!e&e%-xHXfJHwjs%+tW*>Dj85q^=nr8 z!TF`C@w@NAe#%e?`%ioKuBXzcA>=E~8zN<&-RiLUaEY{h>lBmez@#sM;a*BF=+KTy zWg=aglTe9R`Vg!sZh=4guVomIY=v^vQ2PP*m^+7FaB4XNYFBWmxPBrqu@F4SA*=Pl z1Ve=Nkpwa(==fF62R>mifiZosfFsHulkLApgt{WdqmIq6fUqeZ95>I~kF+n0phA;6iYrYsA~O zo86}=abj3EnYH9K!czDkDw2g#lo)?kEiDpN@rBLc$5QF&uhm{62ep|4Noj*I?Q|R! za@r}2*`*=Udaco&8%Wsr8p{vm4L+zI34)%J?vIt~@!`hbknka80d*UDi5u&MG5y|k zzr_2%_vui^7jTIg#OEoco7qF|XQAF{^2+SGdMJ7xrhG zEj>aShAmkyaf0KJ%k8y^@;g;+#vC1XS;Bke&u`C6)`2KQP5koR`eJzPU&jMkMh+(9 z{9BG0K@Y~UA)qJd7DV+OCF1DUB&!W1a)%Kov*nRwiUmS6NH=}&bFFLZ5nq z%m;XzEY5Hq_Tt@9i88G3bMF_d@$AAsiaxBB!MpOIaER;HHFkK*5?{N~$2z~UggvnH zq;eM}a(jHcM@KQ?Fk9`*GX?@u@#ZIH29U|X39V3{wNuB#rk8~OL4*hnfH6r zM%p0JbJ^n@)Zf%%slNcvyPapvPj*_xXT9&xo?jUk@h=;)l1J^$j!C%Y4I`b_;B0Df z*y*I@c=y|lT@nRrsP9^;m=*P7S1Ji-(2P$T8- z^vxRZ?fnEU<$X`&;|IKR2SC1^Vn!HPj^}RD0e{C3F(0v8 zeNrO4$60vCk@!`Nlyt=nwaV* z{Et%KY|L)u?G*QcX#P!d#+P*!Gyoqe8=MTtMBs|k4`PUX9JU7 z;~ufIw|q_iEe(?v5c+Yszf4C(ROU}nGo+onOlzA|gFAm_z^t|F!V>Ra|3MX9nvms5 zn&ht#=+fdQ0N02lFU-@tZ`dDe@#-a&e{DITGFhkHiZ80PKkJ0kYuUW{mmacs$r-Ga zbe8g;aXpS!XkJ`nvmLIbx>i{&sJ4gLy(8odMbW%R9Tv2o$1QFJ$!r4j-S3n4=fb6vas`33!4hm0s)%rXZ9;*TYCiC?H-wSk7tj4~?Br zVCA`*_wnITH*hbQ`H|5J^)Rxf$+(oNph8(7ht zB8Y7mQZO4xCJnYLWP^ifV0kUwN^rRDWgQ$@SC6 z*yQ;?nre4xRyg}2cA9}<)b+2zM3O1pt*EFwh%|q1-6jetQ}UTviNOtVQAypR)g?U! zDpS_h*FefAZ#^uK%<$GctzwfLCfd$OR@SDfrWNMe@P|1&gX zfoJQA3i0`n7>j~|e!^<{D2dODW_`iietEx{Hrhalf~A5`I@6J?cxk+Q63|4B~UNEV~36(+NC;fwc$aQ>gG&%^c~2sTm<^e;0e7s4%3mfycJm` ziS2nE>qlC;UaV!&E7>w&`%G|v1?O9Bne3_gYkH=SkL`>gQfFgF3;DeAhX z@X2jw!!+;)1?5NqlU#MCU%*L%AREg!5N%oM$t0G6Uf)@i-q$ocHNXHKU|&eyM72Ds z3QJRhBh*!)dSEF!M!`fOsye2i=d48&Mj|x8O@vDLO$Rq?PpAVzsQ(@Rd$MU!Q{J%X zX6Bk&%;teH1cJRQk)f)_N`ee-_2NZ_d3o#V7t*RX#I4SwZdZznwr7koz!~-S_RggM z8B)joppOZ(iz`9p9M5TJLa(-XbuXzS@r*IUZUO>OHXIq)aB%%LanNl`-C|ZJr#JQE z{iimIMfGJ=0(df*bPq`gZ;oU@_`0xfK^Vrha1)0gBHeHx-jVt|0y%5k3>WJ+iJtU^W(@?zO>aC>}jR$rXG~Y+dOHz z!(cdXeX#|Kj3e-zQHB{-LrwgQzv7r!LzJ+&iMNXBI{b=sX)rC*O{HX(&+!|HtEej? zlqslC6#`)#Lhd`-AUQF0R5? z3Q#^8J^+>9BIaB&5I*rZ#D+f7njKr|?ni9y^xT&lh1IJeP&Hwgc6(u_RZeF})aDyJ zU3*Gq!){phz>z~5ZR(C^#dehI;vfgWofRONLF8!!NDXJDlXR+$9fx)gYy?S_&WuBE z2IytE2fom@1&1GSrZF>VAIeJj`5q?%vg=(d%iQ)4j)eGgd>UAJY<^2#I^_0G&ajYI zLj!EwGKIK4GmkT1+c-cNaQru2xzX-+JoYYU!Mh_N5*-u<9e0oROHmYo#Y&bc6AUU7 zvdRLIJo3)i?vg{=@rlF2yM;>GG{;&BQzD#hRZa$~@8E{)FEU7Xs)B;>i z#twog$v?l`Y@L3dAk$&4-r#k9;bQpr1Mz4ry%w#PAf08*6w)W@TRuwRRTmIw>FXToi3$| z8xn*e7t;An3=>F3g~xLP1OVF5Zfn{0`Ih+|CzrOv^_9JnNApV zYo%pOCvz6@^mptuS`%8=n7 z-aGd3mw4duZ+{r&W+7F|3D*7yHN=3@o(i8%7N9y4OpwGDQLc2+E4gwde#p=C!F^G~ zLul=7SF@ayv<}o;IrH~>I1tEgx{@`mColh_8sPI%8M@eb>ZhbGDOpPlpppoMGBqQQ z8B6|-_-b2^{=#OMzY>JYZ{?=4si6bUQ44D1S$w8{SYHMtxj!s6Q*U((-@o4pKg0__ zzdi}g(MKWW_2WDTY9#~&kCQFThBf+JVnYxPz zrf-vRHxr64NRA4aU@!-icc@C|@a4I9arl^ii;wa%_C&y2?jt!EXy_MW;F@sJsSc&E@p= z#qq>4ea-DpdaDt}5KO~^`FeQU$P=c_4;3QaX6#Bclo5HDxwrG0-Vtp?+G_($z65p} zlyL=wn68|d@G+3@{J&$z9LVTLvQ^fs+_}rf{nKbJDMG}pEIp^(ht>=F#Z>}E2S0n= zV~O^2vbaRD`1R~9pm1#;Ws-c#SSOU z`-L3)o{=a%N60F z@3XjYo#}iD7wh?eo4Rcm9{G^}Y&umJY&^4Urw2nBOM%SB^IHC#3|VGq4zOXt86 zN_IA9xGDsCRO56%LIZOBzY*JJk6p=W)2yqB%xJu;h8us#Q{5l;?rEC#PQ=~PuS(Cs z6V>~S4#|7UJ3xE>8f5ZNVZyH{0{4GAc!T7a-l3tYBg&GQ_@n&csNx}5jz6lq`O;oI z4c6nvv6$A?W6wMDsv7YErVP0Q9CEP+++ZVN;iE($t0G$^Z}13y@K}giJ$RyNJQi%C zqeZ%{r7;Mmq)7i{RISpY&nT{f6~F~4ZILZ|1}84?maq7j?fsNgC31sXc5?UOY5)R8 z)?%bscm^ddZ#^-d>r6qu6%3oe4XTbWnI#Z1O9gEaJ5+*7E#@F09B8lFEI78;j+}ti z{nkcc+lfKj1&Jm`(a}4e9co7nOmRVbFEu6z4L>r>f69zwxi%|0Y*ujfOL=vq%UOXsO7(8&0vjOy` zuQ+f->R7$%yt+N!UD7};Bv#AsamFO+9vP53w7+2aV`o2+lB5H60E_(y{A9KpZ;zHYHE%=zm`; zf>B-St3mFm8|EI)=utpa(T0mcZ4YO&-$|MtN>5dqPth_a0@gh;EsqA;YDx5`4%(hG z`>k}tERB2JgXF|?3I6I8p|wBFy|tKUj+t*#0 zps?g36Jf`69HK8vmeww^E1~Qlntlt?F3&+I_9x?Ezm{me>gt|7ze-@A7+FA!XF$C3 zwPQ|3x|d1&6u;sx<05Cdz1hx;l28Wu=6|YGntj8;wvVyT#}Pjo+H0AZ2fE^TsST-Ju*Kdf6>WHq5cUge(0e(%>K*2 z9^m_esJ!#hP94^R`UZ36&V5)V414cjV#Gy47<(m8W*gK32Sp%j$ zlFVZ~c#63A=cFzG$~a>PJBRd@ZNN~PVO-=P$OFLq-(%5Sm_piX(rRj6@T6n&U+twPU*(@*a`-8#j7|(srkHa zaVbWOjqTm8-hOUefCUBEi(YtI%+*hj$GZ|w5ZkV2(s$Zod(nViD~KCe`$D@s!Yr_t zX9Ib`HxCS%4Fyg7>h*$+L#Sy&GCxROqb!N(f87OFL8drZ4Z+4ts?=;Z*F3doe-dyj z2#KTcVS5D=2$k<_k|8@^34Z6|dw?sUm!uBdIO?Y;aQ2p=wcKCSY7QrC!a1a%3Q7AF zL#&xU9%F(W7M?GIs?zOrPIC3OBDZ%T&iMbr+c73JZZ`0K?NRW_np$?26o-oya8)d{ zHb3?!vL(MZ9+n1?QSQ2VzM;@f!Y#f13ka= z)-vG!q+TTGj*?8RtNX_#Vu2^$n5tqJOT5rrsMHcAG)uvK74`}$s3E9$2s3tCm(=-v z+?3IHHD6~VN`=JC9g9o3v~+H4@c}Y!)C)H{Fk4)zFrKz8Uho-o9Zx5#v0%OnZ@LB# z$^$`s-Rxoi?EGCPXy;E3PGaik_#Y=?XhQnDwAKo#(J&s~y`W@&w zBo{c3(HlPENNHIQ7SIOV>Dun(g|UcE;ZOHys+*dc$;$EY`nyx8jH{zM-)QUAr;Z>JVxoHEQYfFA0l1qf5eQxR{c% z0M?geg%S6eLT4>8@s<8WhI3ueX#@H)?0z3_zAF60&lK}rs1urh{#A46*!D;lSoOGV z_(p5mlYV?dkCQK&pOh6{MkKSv4W;t$xljsX9I0A}J3_#$Pth(&gR%busX$i0Jcy1$ z#OIXc!PEb$?tUl~pLaN*z^2rfr*);AW&Nk`XFKcN1%%XOHv?O#ZjH#F7Pl2~j~rCdiMU&C zs>E$Ac;TFqvrjEK{>-h}n!nIUYsg6}DLv5@9;l8wskzMRe%@H=J!Z^>sSz;2Adl!V z=HfKf81r3qvM2u%hWWv`AtUNmehxcoeK3Igg8c#`A`fjYsBi!<%q3`-okfJRXHhv= zQ|#Vt{BvLuB5=_+{Yb1Q&tN}i(-?mjSNOWlTj*|3`%LK`Ls<=_!wS`hQRSPBaDi}o z3~tuwA5ud%?42%S?LAUI$B*;x$5{dcr2fpYa>(VLUM931N*byQ4ErN;XR-BLK5>T2%`%RS~XT_bmoAN}Alz9>bgOCC#>4a0cs^g z*PS986v_yiQ)kusVZ}kYYl@lGKn-97uIWSuzxsP3}<<9IWH`3|3`9A0GXmadz zP??*<8@tax^r}Wd)aZb{7^#6hBSiR{VnU3@9>3IBo$G*MCI&N{FJsmd#Q^wpV({V;>VmkWo>oy(hlBVPLW7cdsZZgK2j$8d~O-CP;*hZ8p zvZswvp>tI#1^c-W^;j{L)WdkIOY{-3%^o|v5;R&Gk=RNK9A~DrhHLTKU{^2ksI>U846%2D&L`~_R;c)K_1JDzxz== zVm^f&TMnAoKLsb6B2Mh7D?4N&&a~#QD^(WMwthIxnvD;8l04Ym6oI|CqAyA$3_)`ace9NuiE$8lP3Oqvhbu51mwnLB9BYGAZ_p`O*Q9%vxY9p7vExddeF&pNn%_ z)LGp@zA{%4*$tIm>(%P-r6~m!ula*nOG>`Wb=T*Po2$Os_LCcOxgzGC z5J`2JQ(B3r*11Cx)o<2UDT&&e)w}BU02}Rk6kQ#PFjfs}cvnD;EpIiI6)ru?!->1! z3GO=^+%I^Z8LEo@(Y`$?T=k1bZz4>kkoqx8coHT};%y<+=zNd6U){7awHR6kNBFG7;w zJf6I~B)HUBMxEHTKdGBEIpB`n%WZ7%9+(}lW@4aevL_x-a}Wi!^)Yc{sN$=>+h!SfOEdoYO2E@Kxp(GCsyH2~M z<`nHa${c=Vk;5%DTEW#h9t|6X4z%1-p?0P_wraOjXLV8R=x(0FEfsk*th3zGF}nGK zC2Qb|@3NM@s)5n2_q9)I?78&S+}#(C+W1<{WuqnBOiTDlJ%yL7d7?+4u8*?nQnb*A z<4k#=<*aZBtsg+4o^s1GEXN~Isz!^oFMjIWWM8~Sa;I8y&+I98ot^cKXp8#;X)i$w80413$AiIm~3LQVq9&D3XJ+7GaOgU796?Q+D#@fmvxz4beOlh73SsDoa)ZqZR}8*_@*Dj?NMja z-K^Ue=NcTUJ+_mgdVGXZF3Kn_-62#%r&d^;E(Zg$&;N)KEEG$k@!;TtK9&0b!~szR?S!#U@?lH=w4!Cg)vtva*! zr0Vz0CS;q<(3|vapCk`E%WNiEW;02f&C$tb^YM+!Y*q{qvuU6pW~a^Ob&9sx6lt@` zaheS|=RE=JK%sq@%_Pjms=i>D(`;U+6!vb%D01NFjbDi;`Y<5In0h8<(dN3MY2*A9*ZMs5lq`BAo^BRbGVn;ZbA2qYz$3KuIEgo}yJRdG{s}_tA#(rVp zBFZUx-JtclfRzk?2Ue(-zqv@8GFd*zN=YQe^RG)J#T+R>x4kFDYA|wPTEpY9eu_oL zvFVAF_=|NVB}SI`LhJ%aDz88v=xx4R>1|62w%%?@=xq{m_P6wg!>ywwdK-U&)>~~{ zZ-Pe0J}-KsAbK0PUb5b*wcc9Rwe}$DNO1KGf>|9Vu83v+Xzm+oV z@~2aVvsPN&rAhy5n}NjIB%}58HO)V9JEnO`?LR!fUzGWMl{+!Np)kK+bk1+XJC$IwZXSN;O$90l2+taU6DPe%@}!UAj)R)RMNQEXjh8i(q9c4aWD-Ogw4)A{Ui>qtKP3y<2K`Rs>ROFp|4P!S=YjX3hz2t*_M zCH|AoJ^_Gv^4SJ8BVzA#+UNfvnll6VaaS1ES@RT8gWHC%e)P z@+#*1AqmPs9l9Cm(65hMEe>4Ap=z^-swWIdQngvLcB+26MpQi;uqLYhX>cE^KBGod zeGZ_9s*i$bWFJ@~s-6IFs`}Q>#CeT9^vxndFH>$lQ_hM!=elId&0)$VmXMm9Gj6`j zxY>Hf&12hGHRJG~1e|$@(A$j5&1#y)jC(70+?&14xaG5C#%%zb){I*KCF1>PR`QH{ zopS8cj`)l_oA#iJ*0{%YUGX?&eM^ z*zc>OwYL4GaH9%pax=`rDo(HWyW{5csTpV+$l63!7aO2TB*-oPfV=lZnB`yc2^W7@ z(`jK6iNf$?xR6z;RizVTRccjfhOA0mIY_NalF7cDF+^il^P2S7l;Zw+-xLQEFX!r2pXnS(Gwu%Yx{Z(IlcB`~8&x zB^kHi6NUNmyHpGu^Z)F$FE&6__Eoq^^OT?VbAKLN{wsPV9Z-5boptPl+6E-4t!{u* zZN-+_rd}y(D|M)?RH?0a^=dnKU{AH}Nf-3A6`)tOl_smL)K=Sx1Nuic#Af!&ckFhC^-ley==WcYVp}s}k%h>)TTq@eZBZh^J^{w%fZ#Ie!OVWrJ(wn7b(wm8n zF6D9eR+qm6CGem|>Bka#YzwtzzP*qFy|=ZLXS?FuJOWD1mQQ5eX|>Io?PeDznw8ki z_!s}dUY|d2qkVRw{dSEMInoOt7~Be{8Wln6tlkQPmlC}382Sf`DqZ1@yGF> zC*DMWbFuuMPZICTx-Xi4qhktrCz-j@dMA0I7h4`fN%X0wQE#u8J3Hccw-r<@FdkX_ z+#|AxrY33pfvUBOEY7VGWgiPzlgQ$>)IK7Mb*n_r!vQ@a3pYd~yRB0690fQdiysJZ zyT=p1qF*!cbqcnrr$wthE$Z9jikk(h-G;BSga+a|exXh5Bpr_UmF1uIg&BW6M%CWl zke#0B_qcY4Cvn-$sB@kfuTu%mqyt0N)L`mc3V~}qKZhjh2b4nx}ozs%ca4F)Qn92f|1;eR9ETl+t$61 zqO>jlb2(BX^={$&ErLzXn8>S69ab~ZowdYxt8GEhcU>LE;O1-0U}fW!`SIT-0CMz4 zCM9nDkqJ*G>E^SD<0s6EA5g~T&4kqm-;21||9@QGG`CY;m)jw)l%lQoCFJ#5@(%w0 z%A1}fuiq)Jl%g(&ydF#5k*$Bdytw4<@;3BS(nj+CfULRIsl$I))-9Z}T5pJc(6UM? zI_zHUy^)pbldS5}Trv0>73IyqTK)ETpH%s+>hWIr=`jn8SFRfFYH*DX=Itsa?{`z|IZ|-OZ?A!d`I|_*}fD~CJY2t7^7stybswXeq&-G}-T zh`IXcbZktRy!|w z2PC-ENu`(Ai7VraffJwXFm0QS?xhR*$+(j)=&)d%F6fwOq%P>l&_}_RC+eXf!#oEo zEqAosh8ol;Uq8`dw%iie1e8UJ(g4Yf7WRw zXf`{5I9SDI0!B^$Ww?v>71;y!AL{b(5!GJuMaQsmKNBbL?xlXwxZ1`+eTMIk?sa6R z`Abfkr*tPSKdCO)6!LlTGP;!t^Xaoaq@9VERr#?EWs{u?n1)Qvuz1JY5fSl<*ke zPy^oXKgrsy9-M~;^vF!Ymv?sD9#n_LgDbv+#%UEib|oevOQUjc$Rl;Z|+ znr?g;AX(SBP;4P5fFtC-fVhy`Q9#IT0kK`+F}l|-?PhFig%xChWq_&m?P%vtl!j|5 z+AdHGjPC;2Qh1kAi0?l$WFaLbKfr9vsr(js5M(|@er~W*CfTF9%6$Nt&9~|D^#Ga8 z_k%E-!+_Wfnkf4R7rz)?H4B|qLxqjAg&Xf_y8Xd`xy}9Ne$jux8&Hp+D9FA-_X?gP zc&gw#3Z5ePmV$c(-x%CoH{cC8bAjggp8r5~HBz6dSA$f0Z*tW1o<|{}=QDMKV*w_2 zn4+q406<#?eN56-b^}=3RZ$vG$=j8RJJ`LuWmDiU!`K&5)@L5x4^H~iheA2$8 zO!0ZT^cOe-0G}r$_qk`8X8Z7VF{4b1(c7QLg7P6C$L3m&^wr2QSj(|$T6Z~Gpdd#V zK+Ewpm}AVhDIy2GzX|5p?~W__{sR&>@_tJ6dSj1qD)czl#mBikd7KmVI0L<@Ecuc0%ubl#qk-uku6%D7P6#;Bul1ApDC{3imk!N7Ik#shwg{UCvOx(3?nY;E;)+qym0R!eIu z4RCIwEn>aS;;#rK4#Nc&MiD@_qQV%FP>DdvP;^jm2JeL7umO;*2r6Y(0DjlxSdmWv zFWir?<^B&yxHCaF+!>gJ`wHN^nXloFvN{I3_&5laeyT=7%PxWN2qN&9k7C%j&KU30XCq^xnAHcrKu~@ZT`Fg|7yjt$(V^ zXV~1c7g@=9p646Jrvutv1_tu8z}a`z5mva56wbA8nQ;DtMa#a&rasVw)*{Rmp_>Wy zMR@xYggZ>ADnga_5gH-TX%(&2GC<;x6tOms4RnX(@=!h|V{rzM$+8D@C(CwBCd)5? zv-^lP^%Zt>hBUPSfi^xsK%Wap!pg~eHsFtT1QHnBC!q|kHa^rEPj?&d9c%nSYrGra z^wP$|t+6G4AiDc~$se6`Yavi?Wgs!#u(_lF@iQ=gfHquspf>}LA_Rim2=#(oZ)LXv z66Cj_8{{%ff?NtXS%+CpIW<@uLD4@ab?G>5m_=^p>{DtWwGk z2pra=?uSsMEs&U0V{@wwkQDi2@F*B4845#U>!Y+eBmuIeL8Z)*{;KW+bguq^f&4Sz z{kCR!7epHk|AfU|)vYnxd_Mv-gryjf&=TKO_J2q$gbZ^S0Z0gwLDpdeV3qakzjr?;R}Rk))qv+{|Idj8tuG=#--N)X-7G| zIvwz5-AG7alJ$q$PgYW}pI15&H*VhWdbpd()14t5#C0Q(%WQ9L8$%4Kb>0ux- z$=ztQ_9NikO4eYL-WGFO3g}^NHHM$fT!-~uQWmj`6E6an3-e&$i_w9|l7_<%0I zz1WgJ5N-S}vwQ!3)pJe%O7yCtZ$PIjKSQA9xjFsA{4z6cnB zLBeo9qi*JW8xa2@paF`PS$%9WYYIqa*MM$jRV=g10jC;~M(b8(+pQ?pP1Uf%ShgSU zzY3aS$?SbZZ|c7T{!W?=A)jZCc{4f-L7xABz$<~};yQNuKHUmlu_BWJJr#^Mo(|}F zVhRT5iD7^<(OkM?TkcC3aYeT3(5!R-%$MbBWh%8RNwO-QY`E zvA_G2k&h)pC&7dh$$lg}HUlzb;jI{Wd}kiuSsqKwW3GBgnfX}29EETOz*`KyYG0I} z)J30yVD6lMu&D(1o>+FhuR4T`bW>8qNCzc_johlFkdcOvQ@yfFg|0+c z6$(p{RiQF~Ou&D7%lX&QLH`pQ$6Y|;4Bm`JbgO~*&Fc#kCf-WG*6Qb2fwejv>i!>U zxHvvpnEEZeNPcC{+RM{|Of=iD~~^xKL5d%xFcPeS4C8$Qs;^9d)F^{NJ@VauCp0 z0g|?Ivd;qi$uJ)h7{ClDV{ak60wIK1fE&UDOgDtFm>R;<62cf-l)vP%7CQjZQM2Xz zgHGyg5U9N&keGTcbEyo(Pt(%YdIi9J{*Mp!P>(>U2Otza2DqVa$81M1QJub z+uYIsJ#)0ch=lG8lnh-1iJdpDFo!T8=gg`gn?NOiwNVssF5RR3{7-j#{|Q7dnngbs zvOu?*z7fz7{}DrOEnUE&JXqlG}n2;($(o}>tCR`!HOc8EUAzZ;C zDk*xWY3P@mUexsbhtY2ly*PSXe!B{R%fAD1|Ch_3$mRXJ7|-LC{PEnXJT|`B!sk`= z`BFFo_4>oQjR3KcN{7VVN{1py{y^YX`UI4(Xi^V8ZLLTPK=+}B#!~^^!O}3;!D;|b zV{5uHHYLvo5~ov&d7pSFb|l%4Eq4R}r(XbVXQOcTKS1}pJiGoCAa}XtV8W*Nv3b1a z)*YT{9uv(&G4c7|Jo*57t9l9}eO&?3bir~4?zHEB?Aq(28)h;XU65q|L!xsVntwGf z=1Rsd@nXKrA}9u&y+19ndmZq|$sg4Np z{?qEK3P{4r$@>bx-vdiP0$bkcZo1`Z<0rA@9e~^TeyH2{ZcG~A1vvYD)pe7DE%^h{ zi(Zoa(Mfk90`Z z02|v3tiad~hx+&2r>yX3z`f@_g6ZCKdtmB4x3h%wFd@nQXYHl}I_2wOxF6})z;vg^ z6`0geMI5g&$HL}#y0hze27`V|Mwb7{Qn6hAH%rt~>M(jFw4<|M*A`6b+6*|`f6;}w z2s=7eZbvyL%tNpbG?%@By2#YYQmoumgn1^65+Or`mrdv=LVa1$V^s+EMnLZ<`VrCl zntrG0jYR)R^bY9Uwd$)Lu4R`i%H`IwI8(VCZG0)~p}JUU zIQKumpUSxq35UPIjD&^Q(vfxi4a~{is#VXos^0Rde%1I?<>{$!g2~R?ltqj{qE!nq zF4GVse+0=NI4gfN$sdScV)am1H7x;KMmJ&wme|Em@4EsQS=ow!|AsFG!i)|9PDzVv z&wa$zPQDOv>Sq;x6=75|f$l>f&};U_X&buxrjCjriM zYoxz4^5v7XIu3!_yI4(~02vkKvi$*m_BTNS$ySH@-~O;>>jay<#G1Vra4K4}g{0Xz zN#u5}<#y;^`}`Y7%xxnYE-QhA&%aS!_v#kWKGM(D_*>7g~S6x1xx&tnOMJQf4a z*ITqZXJOOc6d*tf8RWkpnR4$o}Fr1TVJr`^@WBAlgi}^)9A2 zGrgAR)kRM;y}Id_i{Ae93BLb>9<6_&eE$bazyH%93@Z!k>AwFvAD#l2_kSJrCU5|V zz5j#6zyCv!`40r&|3P{Gx5?iBS&_+rz5g?w4%quY40-!pe4I5Qi z9S}YBH}h%}Iw>zgkogZJru@3Oya2?fJRF6lp99=8V0_c}LrGcQ|7($OH>;@`AW5{0 zFMh2XX>*I{N{guMe?(N=>N(XuF(LwCU*t=j~;tlnKl4b-v`)d8uI+BB%Ed?ssYk7)j{cs z`Wtjboo`o5SQ|V7V7MO`7Q%EV^2xh&BL59INjS=Pjj?h31&EgVi_wm**9tRD{}Rw8 z^r`Xp08Qc}4CD;J@0%AO{t*fiI6{qtIWJH(ne;Q)u0Z0jwYOH91McCX1tvYx7Vz&v z*IOEO0H;26%2wdVU)35ZW{sS@^Xx_rqT#Xwh@TrdD73W=uz_8N6&TnLp*C9=YLV~) ztLbGx#$-l(@sUs&qUm{d{((k+o+RfV6CM%a-V?+;2!WXIM!+xaKVrVgTB@!wTX5KV z{sG*_sdAX^<5XEpeVjTM!@1DvKh{p`{~buI|7SGn&j!5uvrSqI==Zn2Gw*i+{YY>T zhTnBlA+{`LVOa$)Vg*0{h3c|+8Uz2KLI1ma|8Bnj?eYFKJ^!06kcz%Psa3ULO8~ZN zlCjXsn11vQ8=+&Me*T-QlYARg-v0qIsg=v?FTjs%F(eShe5lQVkFD_AR>u^#g=z7{ zpK=?V`hyPnU}&`GF@}6J0`WbBK(Y2f;*hkoit7RK)ATA7Iw}>gj=D0w%@jYxk``hK ztLfnF7UE$rP<%5~LOk?6$A^<8I3Vo7vh%{JlF&`ip2puV4qpYwZ|3dx{0G?cpYaGF zht#QHl9OR&pRj9v0o^_NWBB=Wgz!6#@+nzJY(i`wA7Wjo4t**H{0cx8J)q-O0mI)N z!Vp%#g@9Aen$N$@?*D*>y4(0K$~gjqoM#rCC+Fcj2#4djO4iAhL2@`=isc`UYkUAp zL7fZw4ZjYd;nx7?tsHH5xHU8g&_IV`L_&{CiS!)Zf4f_WyMSm;v-|?5x9PW;ev{~* zi{1{McL1rXhp)BE<>YeD=3K7L*H+Jee*m%b zA0&2kUI~-;UqImSmkH%(k=AK~8CGN(pgsGX@n--%{Jn(1;cqzLyl5`HtVlP{rIYb? zfW7|0kZIb=^m>3dJH+NnDiD|*Wuf~0SL(;1C^VYYmwom!6OOk*sNIfEDrv$V5e|!R zDqDq(A`~2AS?)ui%5M?SmjOC%Utzc}o)%!b1O7fH1O6W1d}=j4CpFD|l*VUSO;1`) zj{}-*AL9=Jnr%-E{+Y1EXR&klaAbI!iOTfE5f%Xye>k~2?z^Jm?*;Zru6J= z1Y#V6AnPAUjIkFQF86pbPRJz6t`=oyfX+z+PUJBe*6?pui{#Aa$*bQ@kA$v6CxPlf z?Yt(nzoCHqeIb8)M+?ozWpS;HT+2-VtqgWiO_Vf~Fm5i8JI2h=u~uYyYK@IQcJz>h zy$?agA3??+kWqqbJc|K;lx9K#vv4w$J<3aDI+R3SGRG0Xe} zSl)iuCI#Ut1b4qXRTy3tPP~U&YNAmx`4X@nwMJZ~O!0%%>02WRy$2E3hbw zVPBv^csjsXn1xK0al|9G9Bom*SX-BOV#p(JOni z1Abz{1Q7-`X6el|VVDR{G(mXLgkB=lX^JqygpMMdC#$uu3gLDZO(W5ZHAnAYdVSNc z68(sjZ-~yBpt9=Wi|ukzx%`&6UB+eJKL_3~mJsEt!v6ckQ!C(`l6<}&&hV~lu1ACQ zfPXaj1QPdZ=p6(({{ey1!5ncNJBNDkdD@B$26P(rHQocz)4>o7P6wRRgG5$bg5m5fqWj|Z(T(o{tXo3-$2={zKIq+p4B)KI@nBiq9|}Y zs}j}@waQ>^QtKS79JPvItx&5F)>5ozc2e;|3}?x=+G82mL@^Oa++atc5z_#`eWn$Y zH8TwGr*c;abE^yB^q@`|f9z;6>52{})DR&eLNgP}i!gpPLLC!Ih|p7}MrDhuki~Vh z>Dh7ZMU(vxkhn_ygwY(_1`;L#ZZ?IzJB#0`Jf4*v34ew}AAAbb&gB09FZ=gueuvVw z?{B#Did@RG?fXUSvFk}|JjUpg?*2U-;*8GHfbYYOu9ScWAP{LQi>fIg!>gQ8ZUp$l zdnF`z{)6g`s1%l_aEYJ7g=eR5q=}|*5cr=IHa3Z+kd3V=tibS7SPXHNXX>h)jV`N*Wvsen^ryobWMJWA-BeNKzI9^g^Bx2 z@tS8|qs(iV>lJ-;mfPQdVn@q8 zET4avP*H@|a$`+3;am}xNQo;Xz3vDXm~cRZ%_0<1A^bZ6`bN(CIQX`xG)5NH?+V1 z2mHCS1>&zgNO0<}fkr~PesO%~L-2YF@TWc`F!fbuZW#rZuB`~MbAEHxOyFUJl63N|?pP$)fAhw|N4g>C?nokqKmYE(3-3XtU7 zdeDACwvzXwi3Q<|1!e6gOpJO1nCfs<;|0ya$yykE*J(ws`{-5f2f)4_7Lq%`pLz@)ia0FQ zd6Kxz?BZNG-#ajfJErT-itgg>%%OWCZjZylm)_OLryNW9VZWb7H-L_Esi$)Ho&QKG zD~5RxZo?Kd_!K8P2&gD{Ob`%x9t5rd_96a}u%0$#x0OUZ?;#?yNf<>lPbua5OtTKWY%x6NO$~a>4MJa6DRuqJ#M>;l5E{xKk=5x7luc+U!|POs z>IOu_I!9|%Bp;93JZ{@_o58K0+p{8P$wc%cVnd5;b6LO0dVBVF7wuZDe-#5di@5(4 z?$2>R4>DIYl>KhXe%d_cY@@#pr<@2>kE>XwO9Y?LZ{RmuERBOYg6V{@YHKlu!>#eu!B|mKoOot;gKIKTz;U>$DJs}680`f$80Y8?TuCQ7kZsDKWm*Qa3e6dicN@Ik zaCMGMWZv1Oo-th-if2cYx>hvyEHuH0Cl8IdZ~d+TB}+Z+xzndQR#~u%sxsqPs*PBE z#A-g&GEJ@QEY%pRl5n^Rt z!wv9UveeQc8a$Ui_iVQceC|wb91-sU0DG#ml*M1lI8Of%K<#3>HE)<51H$ zRDKPi`WZs4HiTLvL*28

    @@ -208,31 +244,5 @@ namespace BizHawk.Emulation.Cores.Components.M6502 bytesToAdvance = 1; return "???"; } - - public string Cpu - { - get - { - return "6502"; - } - set - { - } - } - - public string PCRegisterName - { - get { return "PC"; } - } - - public IEnumerable AvailableCpus - { - get { yield return "6502"; } - } - - public string Disassemble(MemoryDomain m, uint addr, out int length) - { - return Disassemble((ushort)addr, out length, a => m.PeekByte((int)a)); - } } } diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs index 7e4ad61e6d..9724899735 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs @@ -7,7 +7,7 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Components.M6502 { - public partial class MOS6502X + public partial class MOS6502X { //dont know whether this system is any faster. hard to get benchmarks someone else try it? //static ShortBuffer CompiledMicrocode; @@ -537,7 +537,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - DummyReadMemory(PC); + _link.DummyReadMemory(PC); } } @@ -610,10 +610,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 { if (debug) Console.WriteLine(State()); branch_irq_hack = false; - if (OnExecFetch != null) OnExecFetch(PC); + _link.OnExecFetch(PC); if (TraceCallback != null) TraceCallback(State()); - opcode = ReadMemory(PC++); + opcode = _link.ReadMemory(PC++); mi = -1; } } @@ -622,7 +622,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode2 = ReadMemory(PC++); + opcode2 = _link.ReadMemory(PC++); } } void Fetch3() @@ -630,21 +630,21 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode3 = ReadMemory(PC++); + opcode3 = _link.ReadMemory(PC++); } } void PushPCH() { - WriteMemory((ushort)(S-- + 0x100), (byte)(PC >> 8)); + _link.WriteMemory((ushort)(S-- + 0x100), (byte)(PC >> 8)); } void PushPCL() { - WriteMemory((ushort)(S-- + 0x100), (byte)PC); + _link.WriteMemory((ushort)(S-- + 0x100), (byte)PC); } void PushP_BRK() { FlagB = true; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); FlagI = true; ea = BRKVector; @@ -652,7 +652,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 void PushP_IRQ() { FlagB = false; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); FlagI = true; ea = IRQVector; @@ -660,7 +660,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 void PushP_NMI() { FlagB = false; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); FlagI = true; //is this right? ea = NMIVector; @@ -692,7 +692,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 NMI = false; ea = NMIVector; } - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } @@ -701,7 +701,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp += ReadMemory((ushort)(ea + 1)) << 8; + alu_temp += _link.ReadMemory((ushort)(ea + 1)) << 8; PC = (ushort)alu_temp; } @@ -859,36 +859,36 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_WRITE_STA() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), A); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), A); } void Abs_WRITE_STX() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), X); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), X); } void Abs_WRITE_STY() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), Y); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), Y); } void Abs_WRITE_SAX() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), (byte)(X & A)); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), (byte)(X & A)); } void ZP_WRITE_STA() { - WriteMemory(opcode2, A); + _link.WriteMemory(opcode2, A); } void ZP_WRITE_STY() { - WriteMemory(opcode2, Y); + _link.WriteMemory(opcode2, Y); } void ZP_WRITE_STX() { - WriteMemory(opcode2, X); + _link.WriteMemory(opcode2, X); } void ZP_WRITE_SAX() { - WriteMemory(opcode2, (byte)(X & A)); + _link.WriteMemory(opcode2, (byte)(X & A)); } void IndIdx_Stage3() @@ -896,7 +896,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ea = ReadMemory(opcode2); + ea = _link.ReadMemory(opcode2); } } @@ -906,7 +906,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { alu_temp = ea + Y; - ea = (ReadMemory((byte)(opcode2 + 1)) << 8) + ea = (_link.ReadMemory((byte)(opcode2 + 1)) << 8) | ((alu_temp & 0xFF)); } @@ -916,7 +916,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory((ushort)ea); + _link.ReadMemory((ushort)ea); ea += (alu_temp >> 8) << 8; } @@ -934,7 +934,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } else { - ReadMemory((ushort)ea); + _link.ReadMemory((ushort)ea); ea = (ushort)(ea + 0x100); } } @@ -944,7 +944,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory((ushort)ea); + _link.ReadMemory((ushort)ea); if (alu_temp.Bit(8)) ea = (ushort)(ea + 0x100); @@ -953,12 +953,12 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_WRITE_Stage6_STA() { - WriteMemory((ushort)ea, A); + _link.WriteMemory((ushort)ea, A); } void IndIdx_WRITE_Stage6_SHA() { - WriteMemory((ushort)ea, (byte)(A & X & 7)); + _link.WriteMemory((ushort)ea, (byte)(A & X & 7)); } void IndIdx_READ_Stage6_LDA() @@ -966,7 +966,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -975,7 +975,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Cmp(); } } @@ -984,7 +984,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _And(); } } @@ -993,7 +993,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Eor(); } } @@ -1002,7 +1002,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = X = ReadMemory((ushort)ea); + A = X = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -1011,7 +1011,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Adc(); } } @@ -1020,7 +1020,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Sbc(); } } @@ -1029,7 +1029,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Ora(); } } @@ -1038,13 +1038,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void IndIdx_RMW_Stage7_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)((value8 << 1)); @@ -1053,7 +1053,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -1062,7 +1062,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -1070,14 +1070,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 + 1); _Sbc(); } void IndIdx_RMW_Stage7_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 - 1); FlagC = (temp8 & 1) != 0; @@ -1085,7 +1085,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -1094,7 +1094,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage8() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void RelBranch_Stage2_BVS() { @@ -1142,7 +1142,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode2 = ReadMemory(PC++); + opcode2 = _link.ReadMemory(PC++); if (branch_taken) { branch_taken = false; @@ -1213,7 +1213,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - PC = (ushort)((ReadMemory((ushort)(PC)) << 8) + opcode2); + PC = (ushort)((_link.ReadMemory((ushort)(PC)) << 8) + opcode2); } } void PullP() @@ -1221,7 +1221,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - P = ReadMemory((ushort)(S++ + 0x100)); + P = _link.ReadMemory((ushort)(S++ + 0x100)); FlagT = true; //force T always to remain true } @@ -1232,7 +1232,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { PC &= 0xFF00; - PC |= ReadMemory((ushort)(S++ + 0x100)); + PC |= _link.ReadMemory((ushort)(S++ + 0x100)); } } @@ -1242,7 +1242,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { PC &= 0xFF; - PC |= (ushort)(ReadMemory((ushort)(S + 0x100)) << 8); + PC |= (ushort)(_link.ReadMemory((ushort)(S + 0x100)) << 8); } } @@ -1251,7 +1251,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + A = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); NZ_A(); } } @@ -1260,7 +1260,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + Y = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); NZ_Y(); } } @@ -1269,7 +1269,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + X = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); NZ_X(); } } @@ -1278,7 +1278,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Bit(); } } @@ -1287,8 +1287,8 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); - A = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); + A = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); X = A; NZ_A(); } @@ -1298,7 +1298,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _And(); } } @@ -1307,7 +1307,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Eor(); } } @@ -1316,7 +1316,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Ora(); } } @@ -1325,7 +1325,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Adc(); } } @@ -1334,7 +1334,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Cmp(); } } @@ -1343,7 +1343,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Cpy(); } } @@ -1352,7 +1352,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); } } @@ -1361,7 +1361,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Cpx(); } } @@ -1370,7 +1370,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Sbc(); } @@ -1380,7 +1380,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); + _link.ReadMemory(opcode2); opcode2 = (byte)(opcode2 + X); //a bit sneaky to shove this into opcode2... but we can reuse all the zero page uops if we do that } @@ -1390,7 +1390,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); + _link.ReadMemory(opcode2); opcode2 = (byte)(opcode2 + Y); //a bit sneaky to shove this into opcode2... but we can reuse all the zero page uops if we do that } @@ -1400,13 +1400,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); } } void ZpIdx_RMW_Stage6() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); } @@ -1415,7 +1415,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Eor(); } } @@ -1424,7 +1424,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Bit(); } } @@ -1433,7 +1433,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory(opcode2); + A = _link.ReadMemory(opcode2); NZ_A(); } } @@ -1442,7 +1442,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory(opcode2); + Y = _link.ReadMemory(opcode2); NZ_Y(); } } @@ -1451,7 +1451,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory(opcode2); + X = _link.ReadMemory(opcode2); NZ_X(); } } @@ -1461,7 +1461,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { //?? is this right?? - X = ReadMemory(opcode2); + X = _link.ReadMemory(opcode2); A = X; NZ_A(); } @@ -1471,7 +1471,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Cpy(); } } @@ -1480,7 +1480,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Cmp(); } } @@ -1489,7 +1489,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Cpx(); } } @@ -1498,7 +1498,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Ora(); } } @@ -1507,7 +1507,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); //just a dummy + _link.ReadMemory(opcode2); //just a dummy } } @@ -1516,7 +1516,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Sbc(); } } @@ -1525,7 +1525,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Adc(); } } @@ -1534,7 +1534,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _And(); } @@ -1696,7 +1696,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Eor(); } } @@ -1705,7 +1705,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Anc(); } } @@ -1714,7 +1714,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Asr(); } } @@ -1723,7 +1723,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Axs(); } } @@ -1732,7 +1732,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Arr(); } } @@ -1741,7 +1741,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Lxa(); } } @@ -1750,7 +1750,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Ora(); } } @@ -1759,7 +1759,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Cpy(); } } @@ -1768,7 +1768,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Cpx(); } } @@ -1777,7 +1777,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Cmp(); } } @@ -1786,7 +1786,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Sbc(); } } @@ -1795,7 +1795,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _And(); } } @@ -1804,7 +1804,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Adc(); } } @@ -1813,7 +1813,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory(PC++); + A = _link.ReadMemory(PC++); NZ_A(); } } @@ -1822,7 +1822,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory(PC++); + X = _link.ReadMemory(PC++); NZ_X(); } } @@ -1831,7 +1831,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory(PC++); + Y = _link.ReadMemory(PC++); NZ_Y(); } } @@ -1840,7 +1840,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(PC++); + _link.ReadMemory(PC++); } } @@ -1849,7 +1849,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); //dummy? + _link.ReadMemory(opcode2); //dummy? alu_temp = (opcode2 + X) & 0xFF; } @@ -1859,7 +1859,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ea = ReadMemory((ushort)alu_temp); + ea = _link.ReadMemory((ushort)alu_temp); } } @@ -1868,7 +1868,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ea += (ReadMemory((byte)(alu_temp + 1)) << 8); + ea += (_link.ReadMemory((byte)(alu_temp + 1)) << 8); } } @@ -1878,7 +1878,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { //TODO make uniform with others - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -1887,7 +1887,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Ora(); } } @@ -1896,7 +1896,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = X = ReadMemory((ushort)ea); + A = X = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -1905,7 +1905,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Cmp(); } } @@ -1914,7 +1914,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Adc(); } } @@ -1923,7 +1923,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _And(); } } @@ -1932,7 +1932,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Eor(); } } @@ -1941,19 +1941,19 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Sbc(); } } void IdxInd_Stage6_WRITE_STA() { - WriteMemory((ushort)ea, A); + _link.WriteMemory((ushort)ea, A); } void IdxInd_Stage6_WRITE_SAX() { alu_temp = A & X; - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); //flag writing skipped on purpose } @@ -1962,13 +1962,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void IdxInd_Stage7_RMW_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)((value8 << 1)); @@ -1977,14 +1977,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 + 1); _Sbc(); } void IdxInd_Stage7_RMW_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 - 1); FlagC = (temp8 & 1) != 0; @@ -1992,7 +1992,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2001,7 +2001,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); @@ -2010,7 +2010,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2019,26 +2019,26 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage8_RMW() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void PushP() { FlagB = true; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); } void PushA() { - WriteMemory((ushort)(S-- + 0x100), A); + _link.WriteMemory((ushort)(S-- + 0x100), A); } void PullA_NoInc() { rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)(S + 0x100)); + A = _link.ReadMemory((ushort)(S + 0x100)); NZ_A(); } } @@ -2048,7 +2048,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { my_iflag = FlagI; - P = ReadMemory((ushort)(S + 0x100)); + P = _link.ReadMemory((ushort)(S + 0x100)); iflag_pending = FlagI; FlagI = my_iflag; FlagT = true; //force T always to remain true @@ -2107,7 +2107,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - PC = (ushort)((ReadMemory(PC) << 8) + opcode2); + PC = (ushort)((_link.ReadMemory(PC) << 8) + opcode2); } } @@ -2116,7 +2116,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(PC); + _link.ReadMemory(PC); PC++; } } @@ -2125,29 +2125,29 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); } } void ZP_RMW_Stage5() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); } void ZP_RMW_INC() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); alu_temp = (byte)((alu_temp + 1) & 0xFF); P = (byte)((P & 0x7D) | TableNZ[alu_temp]); } void ZP_RMW_DEC() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); alu_temp = (byte)((alu_temp - 1) & 0xFF); P = (byte)((P & 0x7D) | TableNZ[alu_temp]); } void ZP_RMW_ASL() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2155,7 +2155,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_SRE() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2164,7 +2164,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_RRA() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2172,7 +2172,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_DCP() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 - 1); FlagC = (temp8 & 1) != 0; @@ -2180,7 +2180,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_LSR() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2189,7 +2189,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_ROR() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2197,7 +2197,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_ROL() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2205,7 +2205,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_SLO() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)((value8 << 1)); @@ -2214,14 +2214,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_ISC() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 + 1); _Sbc(); } void ZP_RMW_RLA() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2233,7 +2233,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode3 = ReadMemory(PC++); + opcode3 = _link.ReadMemory(PC++); alu_temp = opcode2 + Y; ea = (opcode3 << 8) + (alu_temp & 0xFF); @@ -2245,7 +2245,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode3 = ReadMemory(PC++); + opcode3 = _link.ReadMemory(PC++); alu_temp = opcode2 + X; ea = (opcode3 << 8) + (alu_temp & 0xFF); } @@ -2264,7 +2264,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } else { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); ea = (ushort)(ea + 0x100); } } @@ -2279,36 +2279,36 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (alu_temp.Bit(8)) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); ea = (ushort)(ea + 0x100); } - else alu_temp = ReadMemory((ushort)ea); + else alu_temp = _link.ReadMemory((ushort)ea); } } void AbsIdx_WRITE_Stage5_STA() { - WriteMemory((ushort)ea, A); + _link.WriteMemory((ushort)ea, A); } void AbsIdx_WRITE_Stage5_SHY() { alu_temp = Y & (ea >> 8); ea = (ea & 0xFF) | (alu_temp << 8); //"(the bank where the value is stored may be equal to the value stored)" -- more like IS. - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void AbsIdx_WRITE_Stage5_SHX() { alu_temp = X & (ea >> 8); ea = (ea & 0xFF) | (alu_temp << 8); //"(the bank where the value is stored may be equal to the value stored)" -- more like IS. - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void AbsIdx_WRITE_Stage5_ERROR() { S = (byte)(X & A); - WriteMemory((ushort)ea, (byte)(S & (opcode3+1))); + _link.WriteMemory((ushort)ea, (byte)(S & (opcode3+1))); } void AbsIdx_RMW_Stage5() @@ -2316,44 +2316,44 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void AbsIdx_RMW_Stage7() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void AbsIdx_RMW_Stage6_DEC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp - 1); P = (byte)((P & 0x7D) | TableNZ[value8]); } void AbsIdx_RMW_Stage6_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp - 1); _Cmp(); } void AbsIdx_RMW_Stage6_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp + 1); _Sbc(); } void AbsIdx_RMW_Stage6_INC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp + 1); P = (byte)((P & 0x7D) | TableNZ[value8]); } void AbsIdx_RMW_Stage6_ROL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2362,7 +2362,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_LSR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2371,7 +2371,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2380,7 +2380,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2389,7 +2389,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2397,7 +2397,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2406,7 +2406,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_ASL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2415,7 +2415,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_ROR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2428,7 +2428,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -2437,7 +2437,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory((ushort)ea); + X = _link.ReadMemory((ushort)ea); NZ_X(); } } @@ -2446,7 +2446,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); X = A; NZ_A(); } @@ -2456,7 +2456,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory((ushort)ea); + Y = _link.ReadMemory((ushort)ea); NZ_Y(); } } @@ -2465,7 +2465,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Ora(); } } @@ -2474,7 +2474,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } @@ -2483,7 +2483,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Cmp(); } } @@ -2492,7 +2492,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Sbc(); } } @@ -2501,7 +2501,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Adc(); } } @@ -2510,7 +2510,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Eor(); } } @@ -2519,7 +2519,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _And(); } } @@ -2528,7 +2528,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); S &= (byte)alu_temp; X = S; A = S; @@ -2542,7 +2542,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { ea = (opcode3 << 8) + opcode2; - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void AbsInd_JMP_Stage5() @@ -2551,7 +2551,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { ea = (opcode3 << 8) + (byte)(opcode2 + 1); - alu_temp += ReadMemory((ushort)ea) << 8; + alu_temp += _link.ReadMemory((ushort)ea) << 8; PC = (ushort)alu_temp; } @@ -2562,13 +2562,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { ea = (opcode3 << 8) + opcode2; - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void Abs_RMW_Stage5_INC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp + 1); alu_temp = value8; P = (byte)((P & 0x7D) | TableNZ[value8]); @@ -2576,7 +2576,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_DEC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp - 1); alu_temp = value8; P = (byte)((P & 0x7D) | TableNZ[value8]); @@ -2584,21 +2584,21 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp - 1); alu_temp = value8; _Cmp(); } void Abs_RMW_Stage5_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp + 1); alu_temp = value8; _Sbc(); } void Abs_RMW_Stage5_ASL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2607,7 +2607,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_ROR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2616,7 +2616,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2625,7 +2625,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2634,7 +2634,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2643,7 +2643,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2651,7 +2651,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_ROL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2660,7 +2660,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_LSR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2670,7 +2670,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage6() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/IMOS6502XLink.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/IMOS6502XLink.cs new file mode 100644 index 0000000000..06a990c2a0 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/IMOS6502XLink.cs @@ -0,0 +1,18 @@ +namespace BizHawk.Emulation.Cores.Components.M6502 +{ + // Interface that has all the methods required by the MOS 6502X to talk to + // the emulator core. + // Should only be used as a generic type argument for the MOS 6502X, and + // implementations should be structs where possible. This combination allows + // the JITer to generate much faster code than calling a Func<> or Action<>. + public interface IMOS6502XLink + { + byte ReadMemory(ushort address); + byte DummyReadMemory(ushort address); + byte PeekMemory(ushort address); + void WriteMemory(ushort address, byte value); + + // This only calls when the first byte of an instruction is fetched. + void OnExecFetch(ushort address); + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs index c92ff5db95..83d94aad78 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs @@ -6,10 +6,13 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Components.M6502 { - public sealed partial class MOS6502X + public sealed partial class MOS6502X where TLink : IMOS6502XLink { - public MOS6502X() + private readonly TLink _link; + + public MOS6502X(TLink link) { + _link = link; InitOpcodeHandlers(); Reset(); } @@ -62,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 for (int i = 0; i < length; i++) { - rawbytes += string.Format(" {0:X2}", PeekMemory((ushort)(PC + i))); + rawbytes += string.Format(" {0:X2}", _link.PeekMemory((ushort)(PC + i))); } return new TraceInfo @@ -206,39 +209,17 @@ namespace BizHawk.Emulation.Cores.Components.M6502 public long TotalExecutedCycles; - public Func ReadMemory; - public Func DummyReadMemory; - public Func PeekMemory; - public Action WriteMemory; - - //this only calls when the first byte of an instruction is fetched. - public Action OnExecFetch; - - public void SetCallbacks - ( - Func ReadMemory, - Func DummyReadMemory, - Func PeekMemory, - Action WriteMemory - ) - { - this.ReadMemory = ReadMemory; - this.DummyReadMemory = DummyReadMemory; - this.PeekMemory = PeekMemory; - this.WriteMemory = WriteMemory; - } - public ushort ReadWord(ushort address) { - byte l = ReadMemory(address); - byte h = ReadMemory(++address); + byte l = _link.ReadMemory(address); + byte h = _link.ReadMemory(++address); return (ushort)((h << 8) | l); } public ushort PeekWord(ushort address) { - byte l = PeekMemory(address); - byte h = PeekMemory(++address); + byte l = _link.PeekMemory(address); + byte h = _link.PeekMemory(++address); return (ushort)((h << 8) | l); } @@ -246,14 +227,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 { byte l = (byte)(value & 0xFF); byte h = (byte)(value >> 8); - WriteMemory(address, l); - WriteMemory(++address, h); + _link.WriteMemory(address, l); + _link.WriteMemory(++address, h); } private ushort ReadWordPageWrap(ushort address) { ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF)); - return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8)); + return (ushort)(_link.ReadMemory(address) | (_link.ReadMemory(highAddress) << 8)); } // SO pin diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs index 1c2f2e73c8..8d3e5125b0 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs @@ -10,11 +10,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS public sealed partial class Chip6510 { // ------------------------------------ - private readonly MOS6502X _cpu; + private readonly MOS6502X _cpu; private bool _pinNmiLast; private LatchedPort _port; private bool _thisNmi; + private struct CpuLink : IMOS6502XLink + { + private readonly Chip6510 _chip; + + public CpuLink(Chip6510 chip) + { + _chip = chip; + } + + public byte DummyReadMemory(ushort address) => unchecked((byte)_chip.Read(address)); + + public void OnExecFetch(ushort address) { } + + public byte PeekMemory(ushort address) => unchecked((byte)_chip.Peek(address)); + + public byte ReadMemory(ushort address) => unchecked((byte)_chip.Read(address)); + + public void WriteMemory(ushort address, byte value) => _chip.Write(address, value); + } + public Func PeekMemory; public Action PokeMemory; public Func ReadAec; @@ -33,13 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS public Chip6510() { // configure cpu r/w - _cpu = new MOS6502X - { - DummyReadMemory = CpuRead, - ReadMemory = CpuRead, - WriteMemory = CpuWrite, - PeekMemory = CpuPeek - }; + _cpu = new MOS6502X(new CpuLink(this)); // perform hard reset HardReset(); diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs index ddd47146a7..11846447a6 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial private bool _motorEnabled; private bool _ledEnabled; private int _motorStep; - private readonly MOS6502X _cpu; + private readonly MOS6502X _cpu; private int[] _ram; public readonly Via Via0; public readonly Via Via1; @@ -31,15 +31,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial public Action DebuggerStep; public readonly Chip23128 DriveRom; + private struct CpuLink : IMOS6502XLink + { + private readonly Drive1541 _drive; + + public CpuLink(Drive1541 drive) + { + _drive = drive; + } + + public byte DummyReadMemory(ushort address) => unchecked((byte)_drive.Read(address)); + + public void OnExecFetch(ushort address) { } + + public byte PeekMemory(ushort address) => unchecked((byte)_drive.Peek(address)); + + public byte ReadMemory(ushort address) => unchecked((byte)_drive.Read(address)); + + public void WriteMemory(ushort address, byte value) => _drive.Write(address, value); + } + public Drive1541(int clockNum, int clockDen) { DriveRom = new Chip23128(); - _cpu = new MOS6502X + _cpu = new MOS6502X(new CpuLink(this)) { - ReadMemory = CpuRead, - WriteMemory = CpuWrite, - DummyReadMemory = CpuRead, - PeekMemory = CpuPeek, NMI = false }; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs index 4cc9af86d3..79ea69a9c5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs @@ -28,11 +28,31 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private bool _leftDifficultySwitchHeld; private bool _rightDifficultySwitchHeld; - internal MOS6502X Cpu { get; private set; } + internal MOS6502X Cpu { get; private set; } internal byte[] Ram => _ram; internal byte[] Rom { get; } internal int DistinctAccessCount { get; private set; } + internal struct CpuLink : IMOS6502XLink + { + private readonly Atari2600 _atari2600; + + public CpuLink(Atari2600 atari2600) + { + _atari2600 = atari2600; + } + + public byte DummyReadMemory(ushort address) => _atari2600.ReadMemory(address); + + public void OnExecFetch(ushort address) => _atari2600.ExecFetch(address); + + public byte PeekMemory(ushort address) => _atari2600.ReadMemory(address); + + public byte ReadMemory(ushort address) => _atari2600.ReadMemory(address); + + public void WriteMemory(ushort address, byte value) => _atari2600.WriteMemory(address, value); + } + // keeps track of tia cycles, 3 cycles per CPU cycle private int cyc_counter; @@ -292,14 +312,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _mapper.Core = this; _lagcount = 0; - Cpu = new MOS6502X - { - ReadMemory = ReadMemory, - WriteMemory = WriteMemory, - PeekMemory = PeekMemory, - DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch - }; + Cpu = new MOS6502X(new CpuLink(this)); if (_game["PAL"]) { @@ -334,14 +347,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _ram = new byte[128]; _mapper.HardReset(); - Cpu = new MOS6502X - { - ReadMemory = ReadMemory, - WriteMemory = WriteMemory, - PeekMemory = PeekMemory, - DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch - }; + Cpu = new MOS6502X(new CpuLink(this)); _tia.Reset(); _m6532 = new M6532(this); diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs index 32ac8c29f3..41a953d2a8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs @@ -44,13 +44,33 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk private readonly ITraceable _tracer; - public MOS6502X cpu; + public MOS6502X cpu; public Maria maria; public bool _isPAL; public M6532 m6532; public TIA tia; public Pokey pokey; + public struct CpuLink : IMOS6502XLink + { + private readonly A7800Hawk _a7800; + + public CpuLink(A7800Hawk a7800) + { + _a7800 = a7800; + } + + public byte DummyReadMemory(ushort address) => _a7800.ReadMemory(address); + + public void OnExecFetch(ushort address) => _a7800.ExecFetch(address); + + public byte PeekMemory(ushort address) => _a7800.ReadMemory(address); + + public byte ReadMemory(ushort address) => _a7800.ReadMemory(address); + + public void WriteMemory(ushort address, byte value) => _a7800.WriteMemory(address, value); + } + public A7800Hawk(CoreComm comm, GameInfo game, byte[] rom, string gameDbFn, object settings, object syncSettings) { var ser = new BasicServiceProvider(this); @@ -60,14 +80,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk m6532 = new M6532(); pokey = new Pokey(); - cpu = new MOS6502X - { - ReadMemory = ReadMemory, - WriteMemory = WriteMemory, - PeekMemory = ReadMemory, - DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch - }; + cpu = new MOS6502X(new CpuLink(this)); maria = new Maria { @@ -255,7 +268,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk tia.Reset(); cpu.Reset(); - cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory); maria.Reset(); m6532.Reset(); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs index 17380d3aea..af0b2fc36c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs @@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public partial class NES : IEmulator, ICycleTiming { //hardware/state - public MOS6502X cpu; + public MOS6502X cpu; public PPU ppu; public APU apu; public byte[] ram; @@ -159,11 +159,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void HardReset() { - cpu = new MOS6502X(); - cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory); + cpu = new MOS6502X(new CpuLink(this)) + { + BCD_Enabled = false + }; - cpu.BCD_Enabled = false; - cpu.OnExecFetch = ExecFetch; ppu = new PPU(this); ram = new byte[0x800]; CIRAM = new byte[0x800]; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.CpuLink.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.CpuLink.cs new file mode 100644 index 0000000000..13ae69905f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.CpuLink.cs @@ -0,0 +1,27 @@ +using BizHawk.Emulation.Cores.Components.M6502; + +namespace BizHawk.Emulation.Cores.Nintendo.NES +{ + public partial class NES + { + public struct CpuLink : IMOS6502XLink + { + private readonly NES _nes; + + public CpuLink(NES nes) + { + _nes = nes; + } + + public byte DummyReadMemory(ushort address) => _nes.ReadMemory(address); + + public void OnExecFetch(ushort address) => _nes.ExecFetch(address); + + public byte PeekMemory(ushort address) => _nes.CDL == null ? _nes.PeekMemory(address) : _nes.FetchMemory_CDL(address); + + public byte ReadMemory(ushort address) => _nes.CDL == null ? _nes.ReadMemory(address) : _nes.ReadMemory_CDL(address); + + public void WriteMemory(ushort address, byte value) => _nes.WriteMemory(address, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs index c1f2a5b4fa..07ab8aa44b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs @@ -9,18 +9,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void SetCDL(ICodeDataLog cdl) { CDL = cdl; - if (cdl == null) - { - cpu.ReadMemory = ReadMemory; - cpu.WriteMemory = WriteMemory; - cpu.PeekMemory = PeekMemory; - } - else - { - cpu.ReadMemory = ReadMemory_CDL; - cpu.WriteMemory = WriteMemory; - cpu.PeekMemory = FetchMemory_CDL; - } } public void NewCDL(ICodeDataLog cdl) From 20f10b9311fe415b46f0bbbe19adc59e7b9a0563 Mon Sep 17 00:00:00 2001 From: feos Date: Mon, 21 May 2018 22:25:34 +0300 Subject: [PATCH 234/339] bsnes: remove embarrassing leftover empty space from trace log requires rebuilding --- .../libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp b/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp index 0bca14ea6e..77a9bd4a9e 100644 --- a/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp +++ b/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp @@ -441,7 +441,7 @@ void CPUcore::disassemble_opcode(char *output, uint32 addr) { strcat(s, t); strcat(s, " "); - sprintf(t, "V:%3d H:%4d", cpu.vcounter(), cpu.hdot()); + sprintf(t, "V:%3d H:%3d", cpu.vcounter(), cpu.hdot()); strcat(s, t); } From 3291c773eed29f6f27b6f616ee592f9a8578380f Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 23 May 2018 10:07:59 -0400 Subject: [PATCH 235/339] GBHawk: Clean up window implementation --- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 78 +++++++++---------- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 29 +++---- 2 files changed, 53 insertions(+), 54 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 23bacd905a..fe5a9db913 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -616,6 +616,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void render(int render_cycle) { + // we are now in STAT mode 3 // NOTE: presumably the first necessary sprite is fetched at sprite evaulation // i.e. just keeping track of the lowest x-value sprite @@ -642,7 +643,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk first_fetch = true; no_sprites = false; evaled_sprites = 0; - window_pre_render = false; // TODO: If Window is turned on midscanline what happens? When is this check done exactly? @@ -678,20 +678,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Console.Write(" "); Console.WriteLine(pixel_counter); */ - if (pixel_counter == 0 && window_x_latch <= 7) + if (window_x_latch <= 7) { // if the window starts at zero, we still do the first access to the BG // but then restart all over again at the window - window_pre_render = true; - read_case = 4; + read_case = 9; } else { // otherwise, just restart the whole process as if starting BG again - window_pre_render = true; read_case = 4; } + window_pre_render = true; + window_counter = 0; + render_counter = 0; window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); @@ -700,7 +701,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_is_reset = false; } - if (!pre_render && !fetch_sprite && !window_pre_render) + if (!pre_render && !fetch_sprite) { // start shifting data into the LCD if (render_counter >= (render_offset + 8)) @@ -791,10 +792,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; } } - } - } + } + } } - + // based on sprite priority and pixel values, pick a final pixel color if (Core.GBC_compat) { @@ -827,13 +828,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk hbl_countdown = 5; } } - else if ((render_counter >= render_offset) && (pixel_counter < 0)) + else if (pixel_counter < 0) { pixel_counter++; } render_counter++; } - + if (!fetch_sprite) { if (!pre_render) @@ -865,12 +866,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } } - + switch (read_case) { case 0: // read a background tile if ((internal_cycle % 2) == 0) - { + { // calculate the row number of the tiles to be fetched y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; @@ -878,6 +879,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; + BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; } else @@ -886,10 +888,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (!pre_render) { tile_inc++; - if (window_pre_render) - { - read_case = 4; - } } } break; @@ -917,7 +915,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2]; } - } else { @@ -942,7 +939,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { tile_byte += 256; } - tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1]; } else @@ -952,10 +948,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { tile_byte -= 256; } - tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; } - } else { @@ -989,12 +983,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 4: // read from window data if ((window_counter % 2) == 0) - { + { temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; - BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; + BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; } else { @@ -1015,10 +1009,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } if (LCDC.Bit(4)) - { - - tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2]; - + { + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2]; } else { @@ -1026,9 +1018,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (tile_byte.Bit(7)) { tile_byte -= 256; - } - - tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2]; } } else @@ -1054,9 +1045,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (tile_byte < 0) { tile_byte += 256; - } - - tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1]; } else { @@ -1064,9 +1054,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (tile_byte.Bit(7) && tile_byte > 0) { tile_byte -= 256; - } - - tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; } } @@ -1088,7 +1077,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk else { read_case = 7; - } } window_counter++; @@ -1127,12 +1115,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_read = true; VRAM_access_write = true; } - } - + } + break; + + case 9: + // this is a degenerate case for starting the window at 0 + // kevtris' timing doc indicates an additional normal BG access + // but this information is thrown away, so it's faster to do this then constantly check + // for it in read case 0 + read_case = 4; break; } internal_cycle++; - + if (latch_new_data) { latch_new_data = false; @@ -1141,7 +1136,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk tile_data_latch[2] = tile_data[2]; } } - + // every in range sprite takes 6 cycles to process // sprites located at x=0 still take 6 cycles to process even though they don't appear on screen // sprites above x=168 do not take any cycles to process however @@ -1200,6 +1195,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } } + } public override void process_sprite() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 677599bacb..087a47d296 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -449,19 +449,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Console.Write(" "); Console.WriteLine(pixel_counter); */ - if (pixel_counter == 0 && window_x_latch <= 7) + if (window_x_latch <= 7) { // if the window starts at zero, we still do the first access to the BG // but then restart all over again at the window - window_pre_render = true; + read_case = 9; } else { // otherwise, just restart the whole process as if starting BG again - window_pre_render = true; read_case = 4; } + window_pre_render = true; + window_counter = 0; + render_counter = 0; window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); @@ -470,7 +472,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_is_reset = false; } - if (!pre_render && !fetch_sprite && !window_pre_render) + if (!pre_render && !fetch_sprite) { // start shifting data into the LCD if (render_counter >= (render_offset + 8)) @@ -546,7 +548,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk hbl_countdown = 5; } } - else if ((render_counter >= render_offset) && (pixel_counter < 0)) + else if (pixel_counter < 0) { pixel_counter++; } @@ -602,10 +604,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (!pre_render) { tile_inc++; - if (window_pre_render) - { - read_case = 4; - } } } break; @@ -780,7 +778,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk else { read_case = 7; - } } window_counter++; @@ -800,7 +797,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk break; case 8: // done reading, we are now in phase 0 - pre_render = true; // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here @@ -819,8 +815,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk VRAM_access_read = true; VRAM_access_write = true; } - } - + } + break; + + case 9: + // this is a degenerate case for starting the window at 0 + // kevtris' timing doc indicates an additional normal BG access + // but this information is thrown away, so it's faster to do this then constantly check + // for it in read case 0 + read_case = 4; break; } internal_cycle++; From 704356bea2cee8d13cff5f39648ce12c8055b7f3 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 23 May 2018 14:27:22 -0400 Subject: [PATCH 236/339] GBHawk: Bug Fixes --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs | 5 +++-- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs | 5 +++-- .../Consoles/Nintendo/GBHawk/MemoryMap.cs | 4 ++-- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs | 2 ++ 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index fe5a9db913..b4695e81d8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -644,9 +644,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk no_sprites = false; evaled_sprites = 0; window_pre_render = false; + window_latch = LCDC.Bit(5); // TODO: If Window is turned on midscanline what happens? When is this check done exactly? - if ((window_started && LCDC.Bit(5)) || (window_is_reset && !LCDC.Bit(5) && (LY > window_y))) + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y))) { window_y_tile_inc++; if (window_y_tile_inc==8) @@ -665,7 +666,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // before anything else, we have to check if windowing is in effect - if (LCDC.Bit(5) && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) + if (window_latch && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) { /* Console.Write(LY); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 087a47d296..0e41731261 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -415,9 +415,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk evaled_sprites = 0; window_pre_render = false; + window_latch = LCDC.Bit(5); // TODO: If Window is turned on midscanline what happens? When is this check done exactly? - if ((window_started && LCDC.Bit(5)) || (window_is_reset && !LCDC.Bit(5) && (LY > window_y))) + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y))) { window_y_tile_inc++; if (window_y_tile_inc==8) @@ -436,7 +437,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } // before anything else, we have to check if windowing is in effect - if (LCDC.Bit(5) && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) + if (window_latch && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) { /* Console.Write(LY); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index 1953dbd410..12037bdc4a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -35,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (ppu.DMA_start) { // some of gekkio's tests require these to be accessible during DMA - if (addr < 0x4000) + if (addr < 0x8000) { if (ppu.DMA_addr < 0x80) { @@ -270,7 +270,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (ppu.DMA_start) { // some of gekkio's tests require these to be accessible during DMA - if (addr < 0x4000) + if (addr < 0x8000) { if (ppu.DMA_addr < 0x80) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 2783853b34..6c86840b18 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -93,6 +93,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int evaled_sprites; public int sprite_ordered_index; public bool blank_frame; + public bool window_latch; // windowing state public int window_counter; @@ -244,6 +245,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("SL_sprites_ordered", ref SL_sprites_ordered, false); ser.Sync("sprite_ordered_index", ref sprite_ordered_index); ser.Sync("blank_frame", ref blank_frame); + ser.Sync("window_latch", ref window_latch); ser.Sync("window_counter", ref window_counter); ser.Sync("window_pre_render", ref window_pre_render); From 70961dd1fc13d4440090efe1db5372e5d3df887c Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 23 May 2018 15:59:38 -0400 Subject: [PATCH 237/339] GBHawk: Fixes for Samurai Showdown --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs | 5 +++-- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index b4695e81d8..f27a8221c3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -378,7 +378,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_y_tile = 0; window_y_tile_inc = 0; window_started = false; - window_is_reset = true; + if (!LCDC.Bit(5)) { window_is_reset = true; } + } // Automatically restore access to VRAM at this time (force end drawing) @@ -1202,7 +1203,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void process_sprite() { int y; - int VRAM_temp = SL_sprites[sl_use_index * 4 + 3].Bit(3) ? 1 : 0; + int VRAM_temp = (SL_sprites[sl_use_index * 4 + 3].Bit(3) && Core.GBC_compat) ? 1 : 0; if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 0e41731261..b1b38d71b3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -153,7 +153,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_y_tile = 0; window_y_tile_inc = 0; window_started = false; - window_is_reset = true; + if (!LCDC.Bit(5)) { window_is_reset = true; } } // Automatically restore access to VRAM at this time (force end drawing) From e5516b45fbc76466db031048a0fa47d0ff26b61c Mon Sep 17 00:00:00 2001 From: nattthebear Date: Wed, 23 May 2018 17:07:44 -0400 Subject: [PATCH 238/339] Update tracelogger format in libbsnes --- output/dll/libsnes.wbx.gz | Bin 360119 -> 360119 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/output/dll/libsnes.wbx.gz b/output/dll/libsnes.wbx.gz index c6442082844979109bc8e87cf655e1702acd587c..04e04bd3f39aefedc1802e2bf8320480976e875f 100644 GIT binary patch delta 6980 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 delta 6976 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 Date: Fri, 25 May 2018 15:00:16 -0400 Subject: [PATCH 239/339] GBHawk: Fix Pinball Fantasies --- .../CPUs/LR35902/Interrupts.cs | 2 +- .../Consoles/Nintendo/GBHawk/GBC_PPU.cs | 29 +++++++++++------- .../Consoles/Nintendo/GBHawk/GB_PPU.cs | 30 ++++++++++++------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 6ee944e95d..12979e363d 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -12,8 +12,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, WR, SPl, SPh, PCh, IDLE, - INT_GET, W,// NOTE: here is where we check for a cancelled IRQ DEC16, SPl, SPh, + INT_GET, W,// NOTE: here is where we check for a cancelled IRQ WR, SPl, SPh, PCl, IDLE, IDLE, diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index f27a8221c3..6d85069d24 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -352,12 +352,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cycle = 0; LY += LY_inc; Core.cpu.LY = LY; - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } no_scan = false; @@ -444,7 +438,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk //if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY == 153) && (cycle == 8)) + if ((LY == 153) && (cycle == 6)) { LY = 0; LY_inc = 0; @@ -505,7 +499,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (cycle < 80) { - if (cycle == 4) + if (cycle == 2) + { + if (LY != 0) { if (STAT.Bit(5)) { OAM_INT = true; } } + } + else if (cycle == 4) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -514,7 +512,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk STAT &= 0xFC; STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } + if (LY == 0) { if (STAT.Bit(5)) { OAM_INT = true; } } HBL_INT = false; // DMG exits VBlank into mode 0, but not GBC, so this line is needed @@ -565,8 +563,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk //if (cycle == 92) { OAM_INT = false; } } - // here LY=LYC will be asserted - if ((cycle == 4) && (LY != 0)) + // here LY=LYC will be asserted or cleared (but only if LY isnt 0 as that's a special case) + if ((cycle == 2) && (LY != 0)) + { + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + } + else if ((cycle == 4) && (LY != 0)) { if ((LY == LYC) && !STAT.Bit(2)) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index b1b38d71b3..535844ebc3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -128,12 +128,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk LY += LY_inc; Core.cpu.LY = LY; - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case) - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } no_scan = false; @@ -218,7 +212,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (STAT.Bit(5)) { OAM_INT = true; } } - if ((LY == 153) && (cycle == 8)) + if ((LY == 153) && (cycle == 6)) { LY = 0; LY_inc = 0; @@ -280,7 +274,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (cycle < 80) { - if (cycle == 4) + if (cycle == 2) + { + if (LY != 0) { if (STAT.Bit(5)) { OAM_INT = true; } } + } + else if (cycle == 4) { // apparently, writes can make it to OAM one cycle longer then reads OAM_access_write = false; @@ -288,7 +286,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // here mode 2 will be set to true and interrupts fired if enabled STAT &= 0xFC; STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } + + if (LY == 0) { if (STAT.Bit(5)) { OAM_INT = true; } } HBL_INT = false; } @@ -335,8 +334,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (cycle == 92) { OAM_INT = false; } } - // here LY=LYC will be asserted - if ((cycle == 4) && (LY != 0)) + // here LY=LYC will be asserted or cleared (but only if LY isnt 0 as that's a special case) + if ((cycle == 2) && (LY != 0)) + { + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + } + else if ((cycle == 4) && (LY != 0)) { if ((LY == LYC) && !STAT.Bit(2)) { From b480476932299fd8a195144b6046dfb7799cd217 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 26 May 2018 11:54:01 -0400 Subject: [PATCH 240/339] Gambatte: Make initial colors grey scale Also make equal length frames false by default, fixes #1176 --- .../config/GB/ColorChooserForm.Designer.cs | 16 ++++++++-------- .../Nintendo/Gameboy/Gambatte.ISettable.cs | 11 ++++++++++- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs b/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs index 94c24420ba..ba9a874de5 100644 --- a/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs @@ -194,7 +194,7 @@ // this.OK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.OK.DialogResult = System.Windows.Forms.DialogResult.OK; - this.OK.Location = new System.Drawing.Point(143, 221); + this.OK.Location = new System.Drawing.Point(183, 221); this.OK.Name = "OK"; this.OK.Size = new System.Drawing.Size(75, 23); this.OK.TabIndex = 22; @@ -206,7 +206,7 @@ // this.Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.Cancel.Location = new System.Drawing.Point(224, 221); + this.Cancel.Location = new System.Drawing.Point(264, 221); this.Cancel.Name = "Cancel"; this.Cancel.Size = new System.Drawing.Size(75, 23); this.Cancel.TabIndex = 23; @@ -267,19 +267,19 @@ // this.DefaultButton.Location = new System.Drawing.Point(149, 137); this.DefaultButton.Name = "DefaultButton"; - this.DefaultButton.Size = new System.Drawing.Size(60, 23); + this.DefaultButton.Size = new System.Drawing.Size(90, 23); this.DefaultButton.TabIndex = 32; - this.DefaultButton.Text = "&Default"; + this.DefaultButton.Text = "&Default Green"; this.DefaultButton.UseVisualStyleBackColor = true; this.DefaultButton.Click += new System.EventHandler(this.DefaultButton_Click); // // DefaultButtonCGB // - this.DefaultButtonCGB.Location = new System.Drawing.Point(215, 137); + this.DefaultButtonCGB.Location = new System.Drawing.Point(245, 137); this.DefaultButtonCGB.Name = "DefaultButtonCGB"; - this.DefaultButtonCGB.Size = new System.Drawing.Size(75, 23); + this.DefaultButtonCGB.Size = new System.Drawing.Size(94, 23); this.DefaultButtonCGB.TabIndex = 33; - this.DefaultButtonCGB.Text = "Default &CGB"; + this.DefaultButtonCGB.Text = "Default &Grey"; this.DefaultButtonCGB.UseVisualStyleBackColor = true; this.DefaultButtonCGB.Click += new System.EventHandler(this.DefaultButtonCGB_Click); // @@ -290,7 +290,7 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.Cancel; - this.ClientSize = new System.Drawing.Size(313, 254); + this.ClientSize = new System.Drawing.Size(353, 254); this.Controls.Add(this.DefaultButtonCGB); this.Controls.Add(this.DefaultButton); this.Controls.Add(this.buttonSave); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index 2fd947256c..297b029f11 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -47,12 +47,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public class GambatteSettings { + /* Green Palette private static readonly int[] DefaultPalette = { 10798341, 8956165, 1922333, 337157, 10798341, 8956165, 1922333, 337157, 10798341, 8956165, 1922333, 337157 }; + */ + // Grey Scale Palette + private static readonly int[] DefaultPalette = + { + 0xFFFFFF, 0xAAAAAA, 0x555555, 0, + 0xFFFFFF, 0xAAAAAA, 0x555555, 0, + 0xFFFFFF, 0xAAAAAA, 0x555555, 0 + }; public int[] GBPalette; public GBColors.ColorType CGBColors; @@ -121,7 +130,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DisplayName("Equal Length Frames")] [Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")] - [DefaultValue(true)] + [DefaultValue(false)] public bool EqualLengthFrames { get { return _equalLengthFrames; } From ccb1dcd7f53099f656c1731baa1b947da7d867e4 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 28 May 2018 13:14:40 -0400 Subject: [PATCH 241/339] GBHawk: improve Tilt controls --- .../Nintendo/GBHawk/GBHawk.ISettable.cs | 4 +- .../Nintendo/GBHawk/GBHawkControllers.cs | 38 ++++++++++++++----- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 695c1a1481..b6acb3d8d0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public enum ControllerType { Default, - Kirby + Tilt } [JsonIgnore] @@ -79,7 +79,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk set { if (value == ControllerType.Default) { Port1 = GBHawkControllerDeck.DefaultControllerName; } - else { Port1 = "Gameboy Controller + Kirby"; } + else { Port1 = "Gameboy Controller + Tilt"; } _GBController = value; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs index 2a224ba58e..3ef5ffaf4d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs @@ -106,15 +106,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } - [DisplayName("Gameboy Controller + Kirby")] - public class StandardKirby : IPort + [DisplayName("Gameboy Controller + Tilt")] + public class StandardTilt : IPort { - public StandardKirby(int portNum) + public StandardTilt(int portNum) { PortNum = portNum; Definition = new ControllerDefinition { - Name = "Gameboy Controller + Kirby", + Name = "Gameboy Controller + Tilt", BoolButtons = BaseDefinition .Select(b => "P" + PortNum + " " + b) .ToList(), @@ -125,6 +125,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int PortNum { get; } + public float theta, phi, theta_prev, phi_prev; + public ControllerDefinition Definition { get; } public byte Read(IController c) @@ -171,19 +173,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // therefore this control scheme gives decreasing sensitivity in X as Y rotation inscreases public ushort ReadAccX(IController c) { - double theta = c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0; - double phi = c.GetFloat(Definition.FloatControls[0]) * Math.PI / 180.0; + theta_prev = theta; + phi_prev = phi; + + theta = (float)(c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0); + phi = (float)(c.GetFloat(Definition.FloatControls[0]) * Math.PI / 180.0); float temp = (float)(Math.Cos(theta) * Math.Sin(phi)); - return (ushort)(0x81D0 - Math.Floor(temp * 125)); + // here we add in rates of change parameters. + // a typical rate of change for a fast rotation is guessed at 0.5 rad / frame + // since rotations about X have less of a moment arm compared to by, we take 1/5 of the effect as a baseline + float temp2 = (float)((phi - phi_prev) / 0.5 * 25); + + return (ushort)(0x81D0 - Math.Floor(temp * 125) - temp2); } // acc y is just the sine of the angle + // we assume that ReadAccX is called first, which updates the the states public ushort ReadAccY(IController c) { - float temp = (float)Math.Sin(c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0); - return (ushort)(0x81D0 - Math.Floor(temp * 125)); + float temp = (float)Math.Sin(theta); + + // here we add in rates of change parameters. + // a typical rate of change for a fast rotation is guessed at 0.5 rad / frame + // further it will be assumed that the resulting acceleration is roughly eqvuivalent to gravity + float temp2 = (float)((theta - theta_prev)/0.5 * 125); + + return (ushort)(0x81D0 - Math.Floor(temp * 125) + temp2); } private static readonly string[] BaseDefinition = @@ -193,7 +210,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void SyncState(Serializer ser) { - //nothing + // since we need rate of change of angle, need to savestate them + ser.Sync("theta", ref theta); } } } \ No newline at end of file From 9a15cbf4d4f2187a608bdf802eb2b5e0cf8f1c79 Mon Sep 17 00:00:00 2001 From: Matt Burgess Date: Thu, 31 May 2018 17:54:57 +0100 Subject: [PATCH 242/339] ZXHawk: new ULA implementation --- .../BizHawk.Emulation.Cores.csproj | 3 + .../SinclairSpectrum/Machine/CPUMonitor.cs | 187 +++ .../Machine/SpectrumBase.Memory.cs | 14 +- .../Machine/SpectrumBase.Port.cs | 81 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 29 +- .../Computers/SinclairSpectrum/Machine/ULA.cs | 1008 +++++++++++++++++ .../SinclairSpectrum/Machine/ULABase.cs | 100 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 60 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 25 +- .../Machine/ZXSpectrum128K/ZX128.ULA.cs | 6 +- .../Machine/ZXSpectrum128K/ZX128.cs | 6 +- .../ZX128Plus2a.Memory.cs | 78 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 20 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs | 6 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 6 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 78 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 22 +- .../ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs | 6 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 6 +- .../Machine/ZXSpectrum16K/ZX16.cs | 18 +- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 44 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 53 +- .../Machine/ZXSpectrum48K/ZX48.Screen.cs | 49 + .../Machine/ZXSpectrum48K/ZX48.ULA.cs | 37 +- .../Machine/ZXSpectrum48K/ZX48.cs | 5 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 1 + 26 files changed, 1728 insertions(+), 220 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 6aa7c1e3c1..36b6f5462a 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -261,6 +261,8 @@ + + @@ -1434,6 +1436,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs new file mode 100644 index 0000000000..25a03161fc --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -0,0 +1,187 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class CPUMonitor + { + private SpectrumBase _machine; + private Z80A _cpu; + public MachineType machineType = MachineType.ZXSpectrum48; + + public CPUMonitor(SpectrumBase machine) + { + _machine = machine; + _cpu = _machine.CPU; + } + + public ushort[] cur_instr => _cpu.cur_instr; + public int instr_pntr => _cpu.instr_pntr; + public ushort RegPC => _cpu.RegPC; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; + + /// + /// 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)); + } + + /// + /// A CPU monitor cycle + /// + public void Cycle() + { + if (portContending) + { + RunPortContention(); + } + } + + #region Port Contention + + public int portContCounter = 0; + public bool portContending = false; + public ushort lastPortAddr; + + /// + /// Perfors the actual port contention (if necessary) + /// + private void RunPortContention() + { + //return; + bool lowBitSet = false; + bool highByte407f = false; + + int offset = 0; // _machine.ULADevice.contentionOffset; // -5;// 57;// - 10; + var c = _machine.CurrentFrameCycle; + var t = _machine.ULADevice.FrameLength; + int f = (int)c + offset; + if (f >= t) + f = f - t; + else if (f < 0) + f = t + f; + + if ((lastPortAddr & 0x0001) != 0) + lowBitSet = true; + + portContCounter--; + + switch (machineType) + { + case MachineType.ZXSpectrum16: + case MachineType.ZXSpectrum48: + + if ((lastPortAddr & 0xc000) == 0x4000) + highByte407f = true; + + if (highByte407f) + { + // high byte 40-7f + if (lowBitSet) + { + // high byte 40-7f + // low bit set + // 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; + default: + portContCounter = 0; + portContending = false; + break; + } + } + else + { + // high byte 40-7f + // low bit reset + // 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 1: break; + case 0: break; + default: + portContCounter = 0; + portContending = false; + break; + } + } + } + else + { + // high byte not 40-7f + if (lowBitSet) + { + // high byte not 40-7f + // low bit set + // N:4 + switch (portContCounter) + { + case 3: break; + case 2: break; + case 1: break; + case 0: break; + default: + portContCounter = 0; + portContending = false; + break; + } + } + else + { + // high byte not 40-7f + // low bit reset + // N:1, C:3 + switch (portContCounter) + { + case 3: break; + case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(f); break; + case 1: break; + case 0: break; + default: + portContCounter = 0; + portContending = false; + break; + } + } + } + break; + + case MachineType.ZXSpectrum128: + case MachineType.ZXSpectrum128Plus2: + break; + + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + break; + } + } + + /// + /// Starts the port contention process + /// + /// + public void ContendPort(ushort port) + { + portContending = true; + portContCounter = 4; + lastPortAddr = port; + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 5c31caeb7b..5a8455bc34 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -89,8 +89,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - - #region Memory Related Methods /// @@ -153,9 +151,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public virtual byte FetchScreenMemory(ushort addr) { var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); + //var value = ReadBus(addr); return value; } + /// + /// Contends memory if necessary + /// + public abstract void ContendMemory(ushort addr); + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public abstract bool IsContended(ushort addr); + #endregion #region Helper Methods diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 13e5d5ec5c..f71aee74a8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -40,88 +40,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Increments the CPU totalCycles counter by the tStates value specified /// /// - public virtual void PortContention(int tStates) - { - CPU.TotalExecutedCycles += tStates; - } + //public virtual void PortContention(int tStates) + //{ + // CPU.TotalExecutedCycles += tStates; + //} /// /// Simulates IO port contention based on the supplied address /// This method is for 48k and 128k/+2 machines only and should be overridden for other models /// /// - public virtual void ContendPortAddress(ushort addr) - { - return; - - /* - 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; - } - } - } + public abstract void ContendPort(ushort addr); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 7ba831e9a7..1a8ff0a328 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -32,7 +32,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// The emulated ULA device /// - public ULABase ULADevice { get; set; } + //public ULABase ULADevice { get; set; } + public ULA ULADevice { get; set; } + + /// + /// Monitors CPU cycles + /// + public CPUMonitor CPUMon { get; set; } /// /// The spectrum buzzer/beeper @@ -152,9 +158,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_renderSound) { - //BuzzerDevice.StartFrame(); - //TapeBuzzer.StartFrame(); - if (AYDevice != null) AYDevice.StartFrame(); } @@ -169,23 +172,23 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // run a single CPU instruction CPU.ExecuteOne(); + CPUMon.Cycle(); + // cycle the tape device if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) TapeDevice.TapeCycle(); } + OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength; + // we have reached the end of a frame LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; - // paint the buffer if needed - if (ULADevice.needsPaint && _render) - ULADevice.UpdateScreenBuffer(ULADevice.FrameLength); + // paint the buffer at end of frame + if (_render) + ULADevice.RenderScreen(ULADevice.FrameLength); - if (_renderSound) - { - //BuzzerDevice.EndFrame(); - //TapeBuzzer.EndFrame(); - } + ULADevice.LastTState = 0; if (AYDevice != null) AYDevice.EndFrame(); @@ -194,7 +197,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // setup for next frame ULADevice.ResetInterrupt(); - + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) TapeDevice.EndFrame(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs new file mode 100644 index 0000000000..72bd3bc3ba --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -0,0 +1,1008 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 3rd attempt at a base ULA implementation + /// + public abstract class ULA : IVideoProvider + { + #region Other Devices + + /// + /// The emulated spectrum + /// + protected SpectrumBase _machine; + + /// + /// The CPU monitor class + /// + protected CPUMonitor CPUMon; + + #endregion + + #region Construction & Initialisation + + public ULA (SpectrumBase machine) + { + _machine = machine; + CPUMon = _machine.CPUMon; + borderType = _machine.Spectrum.SyncSettings.BorderType; + } + + #endregion + + #region Palettes + + /// + /// The standard ULA palette + /// + private static readonly int[] ULAPalette = + { + Colors.ARGB(0x00, 0x00, 0x00), // Black + Colors.ARGB(0x00, 0x00, 0xD7), // Blue + Colors.ARGB(0xD7, 0x00, 0x00), // Red + Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta + Colors.ARGB(0x00, 0xD7, 0x00), // Green + Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan + Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow + Colors.ARGB(0xD7, 0xD7, 0xD7), // White + Colors.ARGB(0x00, 0x00, 0x00), // Bright Black + Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue + Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red + Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta + Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green + Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan + Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow + Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White + }; + + #endregion + + #region Timing + + /// + /// The CPU speed + /// + public int ClockSpeed; + + /// + /// Length of frame in T-State cycles + /// + public int FrameCycleLength; + + /// + /// The T-State at which the interrupt should be raised within the frame + /// + public int InterruptStartTime; + + /// + /// The period for which the interrupt should he held + /// (simulated /INT pin held low) + /// + public int InterruptLength; + + /// + /// The time in T-States for one scanline to complete + /// + public int ScanlineTime; + + /// + /// T-States at the left border + /// + public int BorderLeftTime; + + /// + /// T-States at the right border + /// + public int BorderRightTime; + + public int FirstPaperLine; + public int FirstPaperTState; + public bool Border4T; + public int Border4TStage; + + #endregion + + #region Interrupt Generation + + /// + /// Signs that an interrupt has been raised in this frame. + /// + protected bool InterruptRaised; + + /// + /// Signs that the interrupt signal has been revoked + /// + protected bool InterruptRevoked; + + /// + /// Resets the interrupt - this should happen every frame in order to raise + /// the VBLANK interrupt in the proceding frame + /// + public virtual void ResetInterrupt() + { + InterruptRaised = false; + InterruptRevoked = false; + } + + /// + /// Generates an interrupt in the current phase if needed + /// + /// + public virtual void CheckForInterrupt(long currentCycle) + { + if (InterruptRevoked) + { + // interrupt has already been handled + return; + } + + 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(); + } + + /// + /// Flash processing + /// + public void CalcFlashCounter() + { + flashCounter++; + + if (flashCounter > 15) + { + flashOn = !flashOn; + flashCounter = 0; + } + } + + #endregion + + #region Screen Layout + + /// + /// Total pixels in one display row + /// + protected int ScreenWidth; + /// + /// Total pixels in one display column + /// + protected int ScreenHeight; + /// + /// Total pixels in top border + /// + protected int BorderTopHeight; + /// + /// Total pixels in bottom border + /// + protected int BorderBottomHeight; + /// + /// Total pixels in left border width + /// + protected int BorderLeftWidth; + /// + /// Total pixels in right border width + /// + protected int BorderRightWidth; + /// + /// Total pixels in one scanline + /// + protected int ScanLineWidth; + + #endregion + + #region State + + /// + /// The last T-State cycle at which the screen was rendered + /// + public int LastTState; + + /// + /// Flash state + /// + public bool flashOn; + + private int flashCounter; + + protected byte fetchB1; + protected byte fetchA1; + protected byte fetchB2; + protected byte fetchA2; + protected int ink; + protected int paper; + protected int fetchBorder; + protected int bright; + protected int flash; + + public int palPaper; + public int palInk; + + public int BorderColor = 7; + + #endregion + + #region Conversions + + public int FrameLength => FrameCycleLength; + + #endregion + + #region Rendering Configuration + + /// + /// Holds all information regarding rendering the screen based on the current T-State + /// + public RenderTable RenderingTable; + + /// + /// Holds all information regarding rendering the screen based on the current T-State + /// + public class RenderTable + { + /// + /// The ULA device + /// + private ULA _ula; + + /// + /// Array of rendercycle entries + /// Starting from the interrupt + /// + public RenderCycle[] Renderer; + + /// + /// The emulated machine + /// + public MachineType _machineType; + + /// + /// Constructor + /// + /// + public RenderTable(ULA ula, MachineType machineType) + { + _ula = ula; + _machineType = machineType; + Renderer = new RenderCycle[_ula.FrameCycleLength]; + InitRenderer(machineType); + } + + /// + /// Initializes the renderer + /// + /// + private void InitRenderer(MachineType machineType) + { + for (var t = 0; t < _ula.FrameCycleLength; t++) + { + var tStateScreen = t + _ula.InterruptStartTime; + + if (tStateScreen < 0) + tStateScreen += _ula.FrameCycleLength; + else if (tStateScreen >= _ula.FrameCycleLength) + tStateScreen -= _ula.FrameCycleLength; + + CalculateRenderItem(t, tStateScreen / _ula.ScanlineTime, tStateScreen % _ula.ScanlineTime); + } + + CreateContention(machineType); + } + + private void CalculateRenderItem(int item, int line, int pix) + { + Renderer[item] = new RenderCycle(); + + Renderer[item].RAction = RenderAction.None; + int pitchWidth = _ula.ScreenWidth + _ula.BorderRightWidth + _ula.BorderLeftWidth; + + int scrPix = pix - _ula.FirstPaperTState; + int scrLin = line - _ula.FirstPaperLine; + + if ((line >= (_ula.FirstPaperLine - _ula.BorderTopHeight)) && (line < (_ula.FirstPaperLine + 192 + _ula.BorderBottomHeight)) && + (pix >= (_ula.FirstPaperTState - _ula.BorderLeftTime)) && (pix < (_ula.FirstPaperTState + 128 + _ula.BorderRightTime))) + { + // visibleArea (vertical) + if ((line >= _ula.FirstPaperLine) && (line < (_ula.FirstPaperLine + 192)) && + (pix >= _ula.FirstPaperTState) && (pix < (_ula.FirstPaperTState + 128))) + { + // pixel area + switch (scrPix & 7) + { + case 0: + Renderer[item].RAction = RenderAction.Shift1AndFetchByte2; // shift 1 + fetch B2 + // +4 = prefetch! + Renderer[item].ByteAddress = CalculateByteAddress(scrPix + 4, scrLin); + break; + case 1: + Renderer[item].RAction = RenderAction.Shift1AndFetchAttribute2; // shift 1 + fetch A2 + // +3 = prefetch! + Renderer[item].AttributeAddress = CalculateAttributeAddress(scrPix + 3, scrLin); + break; + case 2: + Renderer[item].RAction = RenderAction.Shift1; // shift 1 + break; + case 3: + Renderer[item].RAction = RenderAction.Shift1Last; // shift 1 (last) + break; + case 4: + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + break; + case 5: + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + break; + case 6: + if (pix < (_ula.FirstPaperTState + 128 - 2)) + { + Renderer[item].RAction = RenderAction.Shift2AndFetchByte1; // shift 2 + fetch B2 + } + else + { + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + } + + // +2 = prefetch! + Renderer[item].ByteAddress = CalculateByteAddress(scrPix + 2, scrLin); + break; + case 7: + if (pix < (_ula.FirstPaperTState + 128 - 2)) + { + //??? + Renderer[item].RAction = RenderAction.Shift2AndFetchAttribute1; // shift 2 + fetch A2 + } + else + { + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + } + + // +1 = prefetch! + Renderer[item].AttributeAddress = CalculateAttributeAddress(scrPix + 1, scrLin); + break; + } + } + else if ((line >= _ula.FirstPaperLine) && (line < (_ula.FirstPaperLine + 192)) && + (pix == (_ula.FirstPaperTState - 2))) // border & fetch B1 + { + Renderer[item].RAction = RenderAction.BorderAndFetchByte1; // border & fetch B1 + // +2 = prefetch! + Renderer[item].ByteAddress = CalculateByteAddress(scrPix + 2, scrLin); + } + else if ((line >= _ula.FirstPaperLine) && (line < (_ula.FirstPaperLine + 192)) && + (pix == (_ula.FirstPaperTState - 1))) // border & fetch A1 + { + Renderer[item].RAction = RenderAction.BorderAndFetchAttribute1; // border & fetch A1 + // +1 = prefetch! + Renderer[item].AttributeAddress = CalculateAttributeAddress(scrPix + 1, scrLin); + } + else + { + Renderer[item].RAction = RenderAction.Border; // border + } + + int wy = line - (_ula.FirstPaperLine - _ula.BorderTopHeight); + int wx = (pix - (_ula.FirstPaperTState - _ula.BorderLeftTime)) * 2; + Renderer[item].LineOffset = wy * pitchWidth + wx; + } + } + + private void CreateContention(MachineType machineType) + { + int[] conPattern = new int[8]; + + switch (machineType) + { + case MachineType.ZXSpectrum16: + case MachineType.ZXSpectrum48: + case MachineType.ZXSpectrum128: + case MachineType.ZXSpectrum128Plus2: + conPattern = new int[] { 6, 5, 4, 3, 2, 1, 0, 0 }; + break; + + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + conPattern = new int[] { 1, 0, 7, 6, 5, 4, 3, 2 }; + break; + } + + // calculate contention values + for (int t = 0; t < _ula.FrameCycleLength; t++) + { + int shifted = (t + 1) + _ula.InterruptStartTime; + if (shifted < 0) + shifted += _ula.FrameCycleLength; + shifted %= _ula.FrameCycleLength; + + Renderer[t].ContentionValue = 0; + + int line = shifted / _ula.ScanlineTime; + int pix = shifted % _ula.ScanlineTime; + if (line < _ula.FirstPaperLine || line >= (_ula.FirstPaperLine + 192)) + { + Renderer[t].ContentionValue = 0; + continue; + } + int scrPix = pix - _ula.FirstPaperTState; + if (scrPix < 0 || scrPix >= 128) + { + Renderer[t].ContentionValue = 0; + continue; + } + int pixByte = scrPix % 8; + + Renderer[t].ContentionValue = conPattern[pixByte]; + } + + // calculate floating bus values + for (int t = 0; t < _ula.FrameCycleLength; t++) + { + int shifted = (t + 1) + _ula.InterruptStartTime; + if (shifted < 0) + shifted += _ula.FrameCycleLength; + shifted %= _ula.FrameCycleLength; + + Renderer[t].FloatingBusAddress = 0; + + int line = shifted / _ula.ScanlineTime; + int pix = shifted % _ula.ScanlineTime; + if (line < _ula.FirstPaperLine || line >= (_ula.FirstPaperLine + 192)) + { + Renderer[t].FloatingBusAddress = 0; + continue; + } + int scrPix = pix - _ula.FirstPaperTState; + if (scrPix < 0 || scrPix >= 128) + { + Renderer[t].FloatingBusAddress = 0; + continue; + } + + int pixByte = scrPix % 8; + int chunk = scrPix % 16; + + switch (chunk) + { + case 0: + case 2: + Renderer[t].FloatingBusAddress = CalculateByteAddress(scrPix, line); + break; + case 1: + case 3: + Renderer[t].FloatingBusAddress = CalculateAttributeAddress(scrPix, line); + break; + } + } + } + + private ushort CalculateByteAddress(int x, int y) + { + x >>= 2; + var vp = x | (y << 5); + return (ushort)((vp & 0x181F) | ((vp & 0x0700) >> 3) | ((vp & 0x00E0) << 3)); + } + + private ushort CalculateAttributeAddress(int x, int y) + { + x >>= 2; + var ap = x | ((y >> 3) << 5); + return (ushort)(6144 + ap); + } + + /// + /// Render/contention information for a single T-State + /// + public class RenderCycle + { + /// + /// The ULA render action at this T-State + /// + public RenderAction RAction; + /// + /// The contention value at this T-State + /// + public int ContentionValue; + /// + /// The screen byte address at this T-State + /// + public ushort ByteAddress; + /// + /// The screen attribute address at this T-State + /// + public ushort AttributeAddress; + /// + /// The byte address returned by the floating bus at this T-State + /// + public ushort FloatingBusAddress; + /// + /// The offset + /// + public int LineOffset; + } + + public enum RenderAction + { + None, + Border, + BorderAndFetchByte1, + BorderAndFetchAttribute1, + Shift1AndFetchByte2, + Shift1AndFetchAttribute2, + Shift1, + Shift1Last, + Shift2, + Shift2Last, + Shift2AndFetchByte1, + Shift2AndFetchAttribute1 + } + } + + #endregion + + #region Render Methods + + /// + /// Renders to the screen buffer based on the current cycle + /// + /// + public void RenderScreen(int toCycle) + { + // check boundaries + if (toCycle > FrameCycleLength) + toCycle = FrameCycleLength; + if (LastTState > toCycle) + LastTState = 0; + + // render the required number of cycles + for (int t = LastTState; t < toCycle; t++) + { + if (!Border4T || (t & 3) == Border4TStage) + { + fetchBorder = BorderColor; + } + else + { + + } + + //fetchBorder = BorderColor; + + // get the table entry + var item = RenderingTable.Renderer[t]; + + switch (item.RAction) + { + case RenderTable.RenderAction.None: + break; + + case RenderTable.RenderAction.Border: + ScreenBuffer[item.LineOffset] = ULAPalette[fetchBorder]; + ScreenBuffer[item.LineOffset + 1] = ULAPalette[fetchBorder]; + break; + + case RenderTable.RenderAction.BorderAndFetchByte1: + ScreenBuffer[item.LineOffset] = ULAPalette[fetchBorder]; + ScreenBuffer[item.LineOffset + 1] = ULAPalette[fetchBorder]; + fetchB1 = _machine.FetchScreenMemory(item.ByteAddress); + break; + + case RenderTable.RenderAction.BorderAndFetchAttribute1: + ScreenBuffer[item.LineOffset] = ULAPalette[fetchBorder]; + ScreenBuffer[item.LineOffset + 1] = ULAPalette[fetchBorder]; + fetchA1 = _machine.FetchScreenMemory(item.AttributeAddress); + ProcessInkPaper(fetchA1); + break; + + case RenderTable.RenderAction.Shift1AndFetchByte2: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + fetchB2 = _machine.FetchScreenMemory(item.ByteAddress); + break; + + case RenderTable.RenderAction.Shift1AndFetchAttribute2: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + fetchA2 = _machine.FetchScreenMemory(item.AttributeAddress); + break; + + case RenderTable.RenderAction.Shift1: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + break; + + case RenderTable.RenderAction.Shift1Last: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + ProcessInkPaper(fetchA2); + break; + + case RenderTable.RenderAction.Shift2: + ScreenBuffer[item.LineOffset] = ((fetchB2 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB2 & 0x40) != 0) ? palInk : palPaper; + fetchB2 <<= 2; + break; + + case RenderTable.RenderAction.Shift2AndFetchByte1: + ScreenBuffer[item.LineOffset] = ((fetchB2 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB2 & 0x40) != 0) ? palInk : palPaper; + fetchB2 <<= 2; + fetchB1 = _machine.FetchScreenMemory(item.ByteAddress); + break; + + case RenderTable.RenderAction.Shift2AndFetchAttribute1: + ScreenBuffer[item.LineOffset] = ((fetchB2 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB2 & 0x40) != 0) ? palInk : palPaper; + fetchB2 <<= 2; + fetchA1 = _machine.FetchScreenMemory(item.AttributeAddress); + ProcessInkPaper(fetchA1); + break; + } + } + + LastTState = toCycle; + } + + private void ProcessInkPaper(byte attrData) + { + bright = (attrData & 0x40) >> 3; + flash = (attrData & 0x80) >> 7; + ink = (attrData & 0x07); + paper = ((attrData >> 3) & 0x7); + + palInk = ULAPalette[ink + bright]; + palPaper = ULAPalette[paper + bright]; + + // swap paper and ink when flash is on + if (flashOn && (flash != 0)) + { + int temp = palInk; + palInk = palPaper; + palPaper = temp; + } + } + + /// + /// Returns floating bus value (if available) + /// + /// + /// + public void ReadFloatingBus(int tstate, ref int result) + { + var item = RenderingTable.Renderer[tstate]; + + switch (RenderingTable._machineType) + { + case MachineType.ZXSpectrum16: + case MachineType.ZXSpectrum48: + case MachineType.ZXSpectrum128: + case MachineType.ZXSpectrum128Plus2: + /* + if (item.FloatingBusAddress > 0) + { + result = _machine.FetchScreenMemory(item.FloatingBusAddress); + //result = 0x00; + } + */ + switch (item.RAction) + { + case RenderTable.RenderAction.BorderAndFetchByte1: + case RenderTable.RenderAction.Shift1AndFetchByte2: + case RenderTable.RenderAction.Shift2AndFetchByte1: + result = _machine.FetchScreenMemory(item.ByteAddress); + break; + case RenderTable.RenderAction.BorderAndFetchAttribute1: + case RenderTable.RenderAction.Shift1AndFetchAttribute2: + case RenderTable.RenderAction.Shift2AndFetchAttribute1: + result = _machine.FetchScreenMemory(item.AttributeAddress); + break; + default: + //result = _machine.FetchScreenMemory(fetchA2); + break; + } + + + break; + + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + break; + } + } + + #endregion + + #region Contention + + /// + /// Returns the contention value for the current t-state + /// + /// + public int GetContentionValue() + { + return RenderingTable.Renderer[_machine.CurrentFrameCycle].ContentionValue; + } + + /// + /// Returns the contention value for the supplied t-state + /// + /// + public int GetContentionValue(int tstate) + { + if (tstate >= FrameCycleLength) + tstate -= FrameCycleLength; + + if (tstate < 0) + tstate += FrameCycleLength; + + return RenderingTable.Renderer[tstate].ContentionValue; + } + + #endregion + + #region IVideoProvider + + /// + /// Video output buffer + /// + public int[] ScreenBuffer; + + private int _virtualWidth; + private int _virtualHeight; + private int _bufferWidth; + private int _bufferHeight; + + public int BackgroundColor + { + get { return ULAPalette[7]; } //ULAPalette[borderColour]; } + } + + public int VirtualWidth + { + get { return _virtualWidth; } + set { _virtualWidth = value; } + } + + public int VirtualHeight + { + get { return _virtualHeight; } + set { _virtualHeight = value; } + } + + public int BufferWidth + { + get { return _bufferWidth; } + set { _bufferWidth = value; } + } + + public int BufferHeight + { + get { return _bufferHeight; } + set { _bufferHeight = value; } + } + + public int VsyncNumerator + { + get { return ClockSpeed * 50; }// ClockSpeed; } + set { } + } + + public int VsyncDenominator + { + get { return ClockSpeed; }//FrameLength; } + } + + public int[] GetVideoBuffer() + { + switch (borderType) + { + // Full side borders, no top or bottom border (giving *almost* 16:9 output) + case ZXSpectrum.BorderType.Widescreen: + // we are cropping out the top and bottom borders + var startPixelsToCrop = ScanLineWidth * BorderTopHeight; + var endPixelsToCrop = ScanLineWidth * BorderBottomHeight; + int index = 0; + for (int i = startPixelsToCrop; i < ScreenBuffer.Length - endPixelsToCrop; i++) + { + croppedBuffer[index++] = ScreenBuffer[i]; + } + return croppedBuffer; + + // The full spectrum border + case ZXSpectrum.BorderType.Full: + return ScreenBuffer; + + case ZXSpectrum.BorderType.Medium: + // all border sizes now 24 + var lR = BorderLeftWidth - 24; + var rR = BorderRightWidth - 24; + var tR = BorderTopHeight - 24; + var bR = BorderBottomHeight - 24; + var startP = ScanLineWidth * tR; + var endP = ScanLineWidth * bR; + + int index2 = 0; + // line by line + for (int i = startP; i < ScreenBuffer.Length - endP; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR; p++) + { + if (index2 == croppedBuffer.Length) + break; + croppedBuffer[index2++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + + case ZXSpectrum.BorderType.Small: + // all border sizes now 24 + var lR_ = BorderLeftWidth - 10; + var rR_ = BorderRightWidth - 10; + var tR_ = BorderTopHeight - 10; + var bR_ = BorderBottomHeight - 10; + var startP_ = ScanLineWidth * tR_; + var endP_ = ScanLineWidth * bR_; + + int index2_ = 0; + // line by line + for (int i = startP_; i < ScreenBuffer.Length - endP_; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR_; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR_; p++) + { + if (index2_ == croppedBuffer.Length) + break; + croppedBuffer[index2_++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + + case ZXSpectrum.BorderType.None: + // all border sizes now 24 + var lR__ = BorderLeftWidth; + var rR__ = BorderRightWidth; + var tR__ = BorderTopHeight; + var bR__ = BorderBottomHeight; + var startP__ = ScanLineWidth * tR__; + var endP__ = ScanLineWidth * bR__; + + int index2__ = 0; + // line by line + for (int i = startP__; i < ScreenBuffer.Length - endP__; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR__; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR__; p++) + { + if (index2__ == croppedBuffer.Length) + break; + croppedBuffer[index2__++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + } + + return ScreenBuffer; + } + + protected void SetupScreenSize() + { + switch (borderType) + { + case ZXSpectrum.BorderType.Full: + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + ScreenBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Widescreen: + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Medium: + BufferWidth = ScreenWidth + (24) + (24); + BufferHeight = ScreenHeight + (24) + (24); + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Small: + BufferWidth = ScreenWidth + (10) + (10); + BufferHeight = ScreenHeight + (10) + (10); + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.None: + BufferWidth = ScreenWidth; + BufferHeight = ScreenHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + } + } + + protected int[] croppedBuffer; + + private ZXSpectrum.BorderType _borderType; + + public ZXSpectrum.BorderType borderType + { + get { return _borderType; } + set { _borderType = value; } + } + + #endregion + + #region Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("ULA"); + 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); + ser.Sync("fetchB1", ref fetchB1); + ser.Sync("fetchA1", ref fetchA1); + ser.Sync("fetchB2", ref fetchB2); + ser.Sync("fetchA2", ref fetchA2); + ser.Sync("ink", ref ink); + ser.Sync("paper", ref paper); + ser.Sync("fetchBorder", ref fetchBorder); + ser.Sync("bright", ref bright); + ser.Sync("flash", ref flash); + ser.Sync("palPaper", ref palPaper); + ser.Sync("palInk", ref palInk); + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs index 7f479eb635..c7361f27cb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs @@ -4,6 +4,7 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* /// /// ULA (Uncommitted Logic Array) implementation /// @@ -78,6 +79,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public byte[] contentionTable; + /// + /// Contention offset (used for testing) + /// + public int contentionOffset = 0; // -5; + #endregion #region Screen Rendering @@ -86,6 +92,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Video output buffer /// public int[] ScreenBuffer; + + /// + /// Screen rendering T-State info + /// + public RenderCycle[] RenderTable; + /// /// Display memory /// @@ -246,14 +258,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region Interrupt /// - /// The number of T-States that the INT pin is simulated to be held low + /// The t-state within the frame that an interrupt is raised /// - public int InterruptPeriod; + public int InterruptStart; /// - /// The longest instruction cycle count + /// The number of T-States that the INT pin is simulated to be held low /// - protected int LongestOperationCycles = 23; + public int InterruptDuration = 32; /// /// Signs that an interrupt has been raised in this frame. @@ -287,13 +299,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return; } - if (currentCycle < LongestOperationCycles)// InterruptPeriod) + if (currentCycle < InterruptStart) { // interrupt does not need to be raised yet return; } - if (currentCycle >= InterruptPeriod + LongestOperationCycles) + if (currentCycle > InterruptStart + InterruptDuration) { // interrupt should have already been raised and the cpu may or // may not have caught it. The time has passed so revoke the signal @@ -350,31 +362,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// /// public abstract bool IsContended(int addr); - - /// - /// Contends the machine for a given address - /// - /// - public virtual void Contend(ushort addr) - { - if (IsContended(addr) && !(_machine is ZX128Plus3)) - { - _machine.CPU.TotalExecutedCycles += contentionTable[CurrentTStateInFrame]; - } - } - - public virtual void Contend(int addr, int time, int count) - { - if (IsContended(addr) && !(_machine is ZX128Plus3)) - { - for (int f = 0; f < count; f++) - { - _machine.CPU.TotalExecutedCycles += contentionTable[CurrentTStateInFrame] + time; - } - } - else - _machine.CPU.TotalExecutedCycles += count * time; - } + /// /// Resets render state once interrupt is generated @@ -401,6 +389,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + /// /// Builds the T-State to attribute map used with the floating bus /// @@ -426,6 +415,32 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + /// + /// Returns the floating bus value + /// + public virtual void ReadFloatingBus(ref int result) + { + // Floating bus is read on the previous cycle + long _tStates = _machine.CurrentFrameCycle - 1; + + // if we are on the top or bottom border return 0xff + if ((_tStates < contentionStartPeriod) || (_tStates > contentionEndPeriod)) + { + result = 0xff; + } + else + { + if (floatingBusTable[_tStates] < 0) + { + result = 0xff; + } + else + { + result = _machine.ReadBus((ushort)floatingBusTable[_tStates]); + } + } + } + /// /// Updates the screen buffer based on the number of T-States supplied /// @@ -444,7 +459,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } //the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna - elapsedTStates = (_tstates + 1 - lastTState) - 1; + elapsedTStates = (_tstates + 1 - lastTState); //It takes 4 tstates to write 1 byte. Or, 2 pixels per t-state. @@ -755,6 +770,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + /* #endregion } + + /// + /// T-State display mapping + /// + public class RenderCycle + { + public RenderType Type { get; set; } + public short DisplayAddress { get; set; } + public short AttributeAddress { get; set; } + public int ContentionValue { get; set; } + public short FloatingBusAddress { get; set; } + } + + public enum RenderType + { + None, + Border, + Display + } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index e769fc46ef..3ab198737e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -175,10 +175,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum default: break; } - - // update ULA screen buffer if necessary - if ((addr & 49152) == 16384 && _render) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } /// @@ -189,9 +185,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + ContendMemory(addr); + var data = ReadBus(addr); return data; } @@ -204,13 +199,56 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - // apply contention if necessary - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + // update ULA screen buffer if necessary BEFORE T1 write + if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render) + ULADevice.RenderScreen((int)CurrentFrameCycle); + ContendMemory(addr); WriteBus(addr, value); } - + + /// + /// Contends memory if necessary + /// + public override void ContendMemory(ushort addr) + { + if (IsContended(addr)) + { + var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + var a = addr & 0xc000; + + if (a == 0x4000) + { + // low port contention + return true; + } + + if (a == 0xc000) + { + // high port contention - check for contended bank paged in + switch (RAMPaged) + { + case 1: + case 3: + case 5: + case 7: + return true; + } + } + + return false; + } + /// /// ULA reads the memory at the specified address /// (No memory contention) 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 f88922132c..68639979e9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool deviceAddressed = true; // process IO contention - ContendPortAddress(port); + ContendPort(port); int result = 0xFF; @@ -56,6 +56,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Floating bus is read on the previous cycle long _tStates = CurrentFrameCycle - 1; + ULADevice.ReadFloatingBus((int)_tStates, ref result); + + /* // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) { @@ -72,6 +75,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); } } + */ } return (byte)result; @@ -85,7 +89,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WritePort(ushort port, byte value) { // process IO contention - ContendPortAddress(port); + ContendPort(port); // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); @@ -142,7 +146,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // store the last OUT byte LastULAOutByte = value; - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + CPU.TotalExecutedCycles += ULADevice.GetContentionValue(); /* Bit 7 6 5 4 3 2 1 0 @@ -152,10 +156,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - if (ULADevice.borderColour != (value & BORDER_BIT)) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + if (ULADevice.BorderColor != (value & BORDER_BIT)) + ULADevice.RenderScreen((int)CurrentFrameCycle); - ULADevice.borderColour = value & BORDER_BIT; + ULADevice.BorderColor = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); @@ -165,5 +169,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } } + + /// + /// Contend port if necessary + /// + /// + public override void ContendPort(ushort addr) + { + throw new NotImplementedException(); + } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs index 59d9108290..2228ed82c1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs @@ -1,6 +1,7 @@  namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* class ULA128 : ULABase { #region Construction @@ -8,8 +9,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public ULA128(SpectrumBase machine) : base(machine) { - InterruptPeriod = 36; - LongestOperationCycles = 64 + 2; + InterruptStart = 0; + //LongestOperationCycles = 64 + 2; FrameLength = 70908; ClockSpeed = 3546900; @@ -189,4 +190,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index 74b6744893..fc2369a3a1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -21,12 +21,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; + CPUMon = new CPUMonitor(this); + CPUMon.machineType = MachineType.ZXSpectrum128; + ROMPaged = 0; SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; - ULADevice = new ULA128(this); + //ULADevice = new ULA128(this); + ULADevice = new Screen48(this); // still todo BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 265a413002..26ae8ef939 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -344,7 +344,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // update ULA screen buffer if necessary if ((addr & 49152) == 16384 && _render) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + ULADevice.RenderScreen((int)CurrentFrameCycle); } /// @@ -355,9 +355,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + ContendMemory(addr); + var data = ReadBus(addr); return data; } @@ -370,13 +369,74 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - // apply contention if necessary - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + // update ULA screen buffer if necessary BEFORE T1 write + if (!SpecialPagingMode) + { + if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render) + ULADevice.RenderScreen((int)CurrentFrameCycle); + } + else + { + switch (PagingConfiguration) + { + case 2: + case 3: + if ((addr & 49152) == 16384) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + case 1: + if ((addr & 49152) == 16384 || addr >= 0xc000) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + } + } + + ContendMemory(addr); WriteBus(addr, value); } - + + /// + /// Contends memory if necessary + /// + public override void ContendMemory(ushort addr) + { + if (IsContended(addr)) + { + var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + var a = addr & 0xc000; + + if (a == 0x4000) + { + // low port contention + return true; + } + + if (a == 0xc000) + { + // high port contention - check for contended bank paged in + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + } + + return false; + } + /// /// ULA reads the memory at the specified address /// (No memory contention) 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 64ec430ef0..3eb942d51f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool deviceAddressed = true; // process IO contention - ContendPortAddress(port); + ContendPort(port); int result = 0xFF; @@ -56,6 +56,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Floating bus is read on the previous cycle long _tStates = CurrentFrameCycle - 1; + ULADevice.ReadFloatingBus((int)_tStates, ref result); + /* // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) { @@ -72,6 +74,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); } } + */ } /* // Check whether the low bit is reset @@ -158,7 +161,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WritePort(ushort port, byte value) { // process IO contention - ContendPortAddress(port); + ContendPort(port); // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); @@ -241,10 +244,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - if (ULADevice.borderColour != (value & BORDER_BIT)) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + if (ULADevice.BorderColor != (value & BORDER_BIT)) + ULADevice.RenderScreen((int)CurrentFrameCycle); - ULADevice.borderColour = value & BORDER_BIT; + ULADevice.BorderColor = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); @@ -281,13 +284,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Override port contention - /// +3/2a does not have the same ULA IO contention + /// Contend port if necessary /// /// - public override void ContendPortAddress(ushort addr) + public override void ContendPort(ushort addr) { - //CPU.TotalExecutedCycles += 4; + throw new NotImplementedException(); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs index c6547833ad..e164f8b53a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs @@ -1,6 +1,7 @@  namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* class ULAPlus2a : ULABase { #region Construction @@ -8,8 +9,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public ULAPlus2a(SpectrumBase machine) : base(machine) { - InterruptPeriod = 36; - LongestOperationCycles = 64 + 2; + InterruptStart = 0; + //LongestOperationCycles = 64 + 2; FrameLength = 70908; ClockSpeed = 3546900; @@ -193,4 +194,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index a6eec74beb..487cc386b4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -21,12 +21,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; + CPUMon = new CPUMonitor(this); + CPUMon.machineType = MachineType.ZXSpectrum128Plus2a; + ROMPaged = 0; SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; - ULADevice = new ULAPlus2a(this); + //ULADevice = new ULAPlus2a(this); + ULADevice = new Screen48(this); // still todo BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index bead71f975..68a3841244 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -344,7 +344,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // update ULA screen buffer if necessary if ((addr & 49152) == 16384 && _render) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + ULADevice.RenderScreen((int)CurrentFrameCycle); } /// @@ -355,9 +355,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + ContendMemory(addr); + var data = ReadBus(addr); return data; } @@ -370,13 +369,74 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - // apply contention if necessary - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + // update ULA screen buffer if necessary BEFORE T1 write + if (!SpecialPagingMode) + { + if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render) + ULADevice.RenderScreen((int)CurrentFrameCycle); + } + else + { + switch (PagingConfiguration) + { + case 2: + case 3: + if ((addr & 49152) == 16384) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + case 1: + if ((addr & 49152) == 16384 || addr >= 0xc000) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + } + } + + ContendMemory(addr); WriteBus(addr, value); } - + + /// + /// Contends memory if necessary + /// + public override void ContendMemory(ushort addr) + { + if (IsContended(addr)) + { + var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); + CPU.TotalExecutedCycles += delay; + } + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + var a = addr & 0xc000; + + if (a == 0x4000) + { + // low port contention + return true; + } + + if (a == 0xc000) + { + // high port contention - check for contended bank paged in + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + } + + return false; + } + /// /// ULA reads the memory at the specified address /// (No memory contention) 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 5aed81dd52..c3b53fff79 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bool deviceAddressed = true; // process IO contention - ContendPortAddress(port); + ContendPort(port); int result = 0xFF; @@ -60,6 +60,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Floating bus is read on the previous cycle long _tStates = CurrentFrameCycle - 1; + ULADevice.ReadFloatingBus((int)_tStates, ref result); + + /* + // if we are on the top or bottom border return 0xff if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) { @@ -76,6 +80,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); } } + */ } return (byte)result; @@ -89,7 +94,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WritePort(ushort port, byte value) { // process IO contention - ContendPortAddress(port); + ContendPort(port); // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); @@ -174,10 +179,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - if (ULADevice.borderColour != (value & BORDER_BIT)) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + if (ULADevice.BorderColor != (value & BORDER_BIT)) + ULADevice.RenderScreen((int)CurrentFrameCycle); - ULADevice.borderColour = value & BORDER_BIT; + ULADevice.BorderColor = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); @@ -214,13 +219,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Override port contention - /// +3/2a does not have the same ULA IO contention + /// Contend port if necessary /// /// - public override void ContendPortAddress(ushort addr) + public override void ContendPort(ushort addr) { - //CPU.TotalExecutedCycles += 4; + throw new NotImplementedException(); } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs index 3b2291a082..9e0289d53e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs @@ -1,6 +1,7 @@  namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* class ULAPlus3 : ULABase { #region Construction @@ -8,8 +9,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public ULAPlus3(SpectrumBase machine) : base(machine) { - InterruptPeriod = 36; - LongestOperationCycles = 64 + 2; + InterruptStart = 0; + //LongestOperationCycles = 64 + 2; FrameLength = 70908; ClockSpeed = 3546900; @@ -193,4 +194,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 5b000e543e..decfb7466e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -20,13 +20,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { Spectrum = spectrum; CPU = cpu; + CPUMon.machineType = MachineType.ZXSpectrum128Plus3; + + CPUMon = new CPUMonitor(this); ROMPaged = 0; SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; - ULADevice = new ULAPlus3(this); + // ULADevice = new ULAPlus3(this); + ULADevice = new Screen48(this); // still todo BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index 3f6499e916..1d9c7edc0b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -24,7 +24,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - #region Memory /* 48K Spectrum has NO memory paging @@ -88,11 +87,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case 1: RAM0[index] = value; break; - } - - // update ULA screen buffer if necessary - if ((addr & 49152) == 16384 && _render) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); + } } /// @@ -103,8 +98,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + if (IsContended(addr)) + CPU.TotalExecutedCycles += ULADevice.GetContentionValue((int)CurrentFrameCycle); var data = ReadBus(addr); return data; @@ -119,8 +114,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WriteMemory(ushort addr, byte value) { // apply contention if necessary - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; + if (IsContended(addr)) + { + ULADevice.RenderScreen((int)CurrentFrameCycle); + CPU.TotalExecutedCycles += ULADevice.GetContentionValue((int)CurrentFrameCycle); + } WriteBus(addr, value); } 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 5f7958f4d9..624df29d2c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -86,10 +86,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM2[index] = value; break; } - - // update ULA screen buffer if necessary - if ((addr & 49152) == 16384 && _render) - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); } /// @@ -100,9 +96,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + ContendMemory(addr); var data = ReadBus(addr); return data; } @@ -115,13 +109,41 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - // apply contention if necessary - if (ULADevice.IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.contentionTable[CurrentFrameCycle]; - + // update ULA screen buffer if necessary BEFORE T1 write + if ((addr & 49152) == 16384 && _render) + ULADevice.RenderScreen((int)CurrentFrameCycle); + + ContendMemory(addr); WriteBus(addr, value); } + /// + /// Contends memory if necessary + /// + public override void ContendMemory(ushort addr) + { + if (IsContended(addr)) + { + var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); + if (delay > 0) + { + + } + CPU.TotalExecutedCycles += delay; + } + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + if ((addr & 49152) == 16384) + return true; + return false; + } + /// /// Sets up the ROM /// 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 12f8850957..127feb84ae 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override byte ReadPort(ushort port) { // process IO contention - ContendPortAddress(port); + ContendPort(port); int result = 0xFF; @@ -56,6 +56,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // If this is an unused port the floating memory bus should be returned + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); + + /* // Floating bus is read on the previous cycle long _tStates = CurrentFrameCycle - 1; @@ -75,6 +78,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); } } + */ } return (byte)result; @@ -88,7 +92,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WritePort(ushort port, byte value) { // process IO contention - ContendPortAddress(port); + ContendPort(port); // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address @@ -106,13 +110,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - if (ULADevice.borderColour != (value & BORDER_BIT)) - { - // border value has changed - update the screen buffer - ULADevice.UpdateScreenBuffer(CurrentFrameCycle); - } - - ULADevice.borderColour = value & BORDER_BIT; + ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); @@ -124,6 +123,40 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } - + + /// + /// Simulates IO port contention based on the supplied address + /// This method is for 48k and 128k/+2 machines only and should be overridden for other models + /// + /// + public override void ContendPort(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. + */ + + CPUMon.ContendPort(addr); + return; + } + } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs new file mode 100644 index 0000000000..f1bc6f89ec --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class Screen48 : ULA + { + #region Construction + + public Screen48(SpectrumBase machine) + : base(machine) + { + // timing + ClockSpeed = 3500000; + FrameCycleLength = 69888; + InterruptStartTime = 32; + InterruptLength = 32; + ScanlineTime = 224; + + BorderLeftTime = 24; + BorderRightTime = 24; + + FirstPaperLine = 64; + FirstPaperTState = 64; + + Border4T = true; + Border4TStage = 0; + + // screen layout + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + RenderingTable = new RenderTable(this, + MachineType.ZXSpectrum48); + + SetupScreenSize(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs index 3b4eca3dc3..03e10d9685 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs @@ -1,6 +1,9 @@  +using System.Linq; + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /* class ULA48 : ULABase { #region Construction @@ -8,8 +11,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public ULA48(SpectrumBase machine) : base(machine) { - InterruptPeriod = 32; - LongestOperationCycles = 64; + InterruptStart = 0;// 5; // 0; // 3; // 0;// 32; + //LongestOperationCycles = 32; FrameLength = 69888; ClockSpeed = 3500000; @@ -55,7 +58,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void Reset() { - contentionStartPeriod = 14335; // + LateTiming; + contentionOffset = 0; + InterruptStart = 0; + + contentionStartPeriod = 14335;// + contentionOffset; // + LateTiming; contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); screen = _machine.RAM0; screenByteCtr = DisplayStart; @@ -160,6 +166,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum h += TstatesPerScanline - 128; } + // offset floating bus table + var offset = 0; + + if (offset != 0) + { + var fbt = floatingBusTable.ToArray(); + for (int i = 0; i < FrameLength; i++) + { + var off = i + offset; + if (off < 0) + { + off = FrameLength - 1 + off; + } + else if (off >= FrameLength) + { + off = off - FrameLength; + } + + fbt[off] = floatingBusTable[i]; + } + + floatingBusTable = fbt.ToArray(); + } + //build bottom border while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) { @@ -177,4 +207,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } + */ } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index db8105555d..a8d33eb03a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -21,7 +21,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Spectrum = spectrum; CPU = cpu; - ULADevice = new ULA48(this); + CPUMon = new CPUMonitor(this); + + //ULADevice = new ULA48(this); + ULADevice = new Screen48(this); BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 14ed0a439b..b182a80270 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -91,6 +91,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum _cpu.ReadHardware = _machine.ReadPort; _cpu.WriteHardware = _machine.WritePort; _cpu.FetchDB = _machine.PushBus; + _cpu.OnExecFetch = _machine.CPUMon.OnExecFetch; ser.Register(_tracer); ser.Register(_cpu); From c0d6c02b2ed49cca459a4449c34e6dd44422e3cd Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 31 May 2018 21:05:41 -0400 Subject: [PATCH 243/339] Z80: Recast core to cycle accurate memory accesses and wait state timing --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 8 +- .../CPUs/Z80A/Interrupts.cs | 42 +-- .../CPUs/Z80A/Operations.cs | 14 + .../CPUs/Z80A/Tables_Direct.cs | 330 +++++++++--------- .../CPUs/Z80A/Tables_Indirect.cs | 319 ++++++++--------- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 198 +++++------ 6 files changed, 438 insertions(+), 473 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index 5f4ccb2437..57f262bd7a 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -20,13 +20,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public int instr_pntr = 0; public ushort instr_swap; public ushort[] cur_instr; - public int opcode; + public byte opcode; public bool NO_prefix, CB_prefix, IX_prefix, EXTD_prefix, IY_prefix, IXCB_prefix, IYCB_prefix; public bool IXCB_prefetch, IYCB_prefetch; // value is fetched before opcode public bool halted; public ushort PF; - public void FetchInstruction(byte opcode) + public void FetchInstruction() { if (NO_prefix) { @@ -1186,6 +1186,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IXCB_prefetch = false; PF = opcode; Regs[ALU] = PF; + Regs[W] = Regs[Ixh]; + Regs[Z] = Regs[Ixl]; PREFETCH_(Ixl, Ixh); return; } @@ -1195,6 +1197,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IYCB_prefetch = false; PF = opcode; Regs[ALU] = PF; + Regs[W] = Regs[Iyh]; + Regs[Z] = Regs[Iyl]; PREFETCH_(Iyl, Iyh); return; } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 2c4f5c5204..648590c92b 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -33,16 +33,16 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void NMI_() { cur_instr = new ushort[] - {IDLE, - DEC16, SPl, SPh, + {DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCh, - IDLE, DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCl, - IDLE, ASGN, PCl, 0x66, ASGN, PCh, 0, - IDLE, + WAIT, + OP_F, OP }; } @@ -55,11 +55,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, RD, ALU, PCl, PCh, - IDLE, INC16, PCl, PCh, - IDLE, + WAIT, + OP_F, OP }; } @@ -68,17 +68,17 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {DEC16, SPl, SPh, - IDLE, + WAIT, WR, SPl, SPh, PCh, - IDLE, DEC16, SPl, SPh, - IDLE, + WAIT, WR, SPl, SPh, PCl, - IDLE, ASGN, PCl, 0x38, IDLE, ASGN, PCh, 0, IDLE, + WAIT, + OP_F, OP }; } @@ -87,23 +87,23 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, FTCH_DB, - TR, Z, DB, - TR, W, I, - IDLE, + TR16, Z, W, DB, I, DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCh, - IDLE, DEC16, SPl, SPh, - WR, SPl, SPh, PCl, + WAIT, + WR, SPl, SPh, PCl, IDLE, - RD, PCl, Z, W, - INC16, Z, W, + WAIT, + RD_INC, PCl, Z, W, IDLE, RD, PCh, Z, W, IDLE, - IDLE, + WAIT, + OP_F, OP }; } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs index c26a7a6aff..f9fe6f797f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs @@ -11,6 +11,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Regs[DB] = Regs[dest]; } + public void Read_INC_Func(ushort dest, ushort src_l, ushort src_h) + { + Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); + Regs[DB] = Regs[dest]; + INC16_Func(src_l, src_h); + } + public void I_Read_Func(ushort dest, ushort src_l, ushort src_h, ushort inc) { Regs[dest] = ReadMemory((ushort)((Regs[src_l] | (Regs[src_h] << 8)) + inc)); @@ -23,6 +30,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]); } + public void Write_INC_Func(ushort dest_l, ushort dest_h, ushort src) + { + Regs[DB] = Regs[src]; + WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]); + INC16_Func(dest_l, dest_h); + } + public void I_Write_Func(ushort dest_l, ushort dest_h, ushort inc, ushort src) { Regs[DB] = Regs[src]; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index e708b02bac..0bc5e7cc19 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -10,10 +10,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void NOP_() { cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - OP }; + { IDLE, + WAIT, + OP_F, + OP }; } // NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers @@ -22,8 +22,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {EXCH, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -31,8 +31,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {EXX, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -41,19 +41,19 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {EXCH_16, dest_l, dest_h, src_l, src_h, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } private void INC_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, + {INC16, src_l, src_h, IDLE, IDLE, - INC16, src_l, src_h, - IDLE, + WAIT, + OP_F, OP }; } @@ -61,11 +61,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void DEC_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, - IDLE, - DEC16, src_l, src_h, + {DEC16, src_l, src_h, IDLE, IDLE, + WAIT, + OP_F, OP }; } @@ -74,16 +74,16 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, + {TR16, Z, W, dest_l, dest_h, IDLE, - IDLE, - TR16, Z, W, dest_l, dest_h, INC16, Z, W, IDLE, - IDLE, ADD16, dest_l, dest_h, src_l, src_h, IDLE, IDLE, + IDLE, + WAIT, + OP_F, OP }; } @@ -91,8 +91,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {operation, dest, src, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -101,9 +101,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {operation, dest, src, - IDLE, - IDLE, SET_FL_IR, dest, + WAIT, + OP_F, OP }; } @@ -114,17 +114,17 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + ASGN, B, (ushort)((Regs[B] - 1) & 0xFF), + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - ASGN, B, (ushort)((Regs[B] - 1) & 0xFF), - IDLE, - RD, Z, PCl, PCh, - IDLE, - INC16, PCl, PCh, IDLE, ASGN, W, 0, - IDLE, ADDS, PCl, PCh, Z, W, TR16, Z, W, PCl, PCh, + IDLE, + WAIT, + OP_F, OP }; } else @@ -132,11 +132,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, ASGN, B, (ushort)((Regs[B] - 1) & 0xFF), + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - RD, ALU, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, + WAIT, + OP_F, OP }; } } @@ -156,27 +156,27 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, ASGN, W, 0, - IDLE, ADDS, PCl, PCh, Z, W, - TR16, Z, W, PCl, PCh, + TR16, Z, W, PCl, PCh, IDLE, + IDLE, + WAIT, + OP_F, OP }; } else { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - IDLE, - RD, ALU, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + OP_F, OP }; } } @@ -187,28 +187,28 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, TR16, PCl, PCh, Z, W, - IDLE, + WAIT, + OP_F, OP }; } else { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - IDLE, - RD, W, PCl, PCh, - INC16, PCl, PCh, - IDLE, + WAIT, + RD_INC, W, PCl, PCh, IDLE, + WAIT, + OP_F, OP }; } } @@ -217,14 +217,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, SPl, SPh, IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, - IDLE, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, - TR16, PCl, PCh, Z, W, + WAIT, + RD_INC, W, SPl, SPh, + TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; } @@ -232,14 +232,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, SPl, SPh, IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, - IDLE, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, + WAIT, + RD_INC, W, SPl, SPh, TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; } @@ -247,14 +247,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, + WAIT, + RD_INC, Z, SPl, SPh, EI_RETN, + WAIT, + RD_INC, W, SPl, SPh, TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; } @@ -266,14 +266,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, + WAIT, + RD_INC, Z, SPl, SPh, IDLE, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, - IDLE, + WAIT, + RD_INC, W, SPl, SPh, TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; } else @@ -281,8 +281,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } } @@ -293,35 +293,35 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - IDLE, - RD, W, PCl, PCh, - INC16, PCl, PCh, IDLE, + WAIT, + RD_INC, W, PCl, PCh, DEC16, SPl, SPh, - IDLE, - WR, SPl, SPh, PCh, + WAIT, + WR, SPl, SPh, PCh, DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCl, - IDLE, - TR, PCl, Z, - TR, PCh, W, + TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; } else { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - INC16, PCl, PCh, - IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + OP_F, OP }; } } @@ -330,8 +330,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {operation, src, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -339,8 +339,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {operation, bit, src, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -348,15 +348,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, DEC16, SPl, SPh, - IDLE, + WAIT, WR, SPl, SPh, src_h, - IDLE, DEC16, SPl, SPh, - IDLE, + WAIT, WR, SPl, SPh, src_l, IDLE, + WAIT, + OP_F, OP }; } @@ -365,14 +365,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - RD, src_l, SPl, SPh, + WAIT, + RD_INC, src_l, SPl, SPh, IDLE, - INC16, SPl, SPh, - IDLE, - RD, src_h, SPl, SPh, - IDLE, - INC16, SPl, SPh, + WAIT, + RD_INC, src_h, SPl, SPh, IDLE, + WAIT, + OP_F, OP }; } @@ -380,15 +380,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCh, DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCl, - IDLE, - ASGN, Z, n, - ASGN, W, 0, - TR16, PCl, PCh, Z, W, + RST, n, + WAIT, + OP_F, OP }; } @@ -396,17 +396,17 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, + OP_F, PREFIX, src}; } private void PREFETCH_(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {TR16, Z, W, src_l, src_h, - ADDS, Z, W, ALU, ZERO, - IDLE, + {ADDS, Z, W, ALU, ZERO, + WAIT, + OP_F, PREFIX, IXYprefetch }; } @@ -414,8 +414,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {DI, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -423,17 +423,17 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {EI, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } private void JP_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {TR, PCl, src_l, - IDLE, - TR, PCh, src_h, + {TR16, PCl, PCh, src_l, src_h, + WAIT, + OP_F, OP }; } @@ -442,9 +442,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - TR, SPl, src_l, - TR, SPh, src_h, - IDLE, + TR16, SPl, SPh, src_l, src_h, + WAIT, + OP_F, OP }; } @@ -452,15 +452,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, TR, W, A, OUT, Z, W, A, - IDLE, INC16, Z, W, IDLE, - IDLE, + WAIT, + OP_F, OP}; } @@ -468,12 +468,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, TR16, Z, W, C, B, OUT, Z, W, src, - IDLE, INC16, Z, W, - IDLE, + IDLE, + WAIT, + OP_F, OP}; } @@ -481,15 +481,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, TR, W, A, IN, A, Z, W, - IDLE, INC16, Z, W, IDLE, - IDLE, + WAIT, + OP_F, OP}; } @@ -497,12 +497,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, IN, dest, src, B, - IDLE, TR16, Z, W, C, B, INC16, Z, W, IDLE, + WAIT, + OP_F, OP}; } @@ -511,61 +511,61 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - IDLE, TR16, Z, W, dest_l, dest_h, INC16, Z, W, IDLE, IDLE, op, dest_l, dest_h, src_l, src_h, IDLE, - IDLE, + WAIT, + OP_F, OP}; } private void INT_MODE_(ushort src) { cur_instr = new ushort[] - {IDLE, - IDLE, - INT_MODE, src, + {INT_MODE, src, + WAIT, + OP_F, OP }; } private void RRD_() { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, L, H, - IDLE, + {TR16, Z, W, L, H, + WAIT, RD, ALU, Z, W, IDLE, RRD, ALU, A, IDLE, + IDLE, + IDLE, + WAIT, WR, Z, W, ALU, - IDLE, INC16, Z, W, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } private void RLD_() { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, L, H, - IDLE, + {TR16, Z, W, L, H, + WAIT, RD, ALU, Z, W, IDLE, RLD, ALU, A, IDLE, + IDLE, + IDLE, + WAIT, WR, Z, W, ALU, - IDLE, INC16, Z, W, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index c53b013a86..0ed80b9067 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -6,15 +6,15 @@ { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, IDLE, operation, ALU, - IDLE, + WAIT, WR, src_l, src_h, ALU, - IDLE, - IDLE, + IDLE, + WAIT, + OP_F, OP }; } @@ -23,14 +23,14 @@ cur_instr = new ushort[] {IDLE, IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, - IDLE, operation, bit, ALU, - IDLE, + WAIT, WR, src_l, src_h, ALU, IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -43,11 +43,11 @@ cur_instr = new ushort[] {IDLE, IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, - IDLE, I_BIT, bit, ALU, - IDLE, + WAIT, + OP_F, OP }; } @@ -55,23 +55,23 @@ { cur_instr = new ushort[] {IDLE, - IDLE, - RD, ALU, src_l, src_h, - IDLE, + WAIT, + RD_INC, ALU, src_l, src_h, operation, dest, ALU, - INC16, src_l, src_h, + WAIT, + OP_F, OP }; } private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, src_l, src_h, - RD, ALU, Z, W, - INC16, Z, W, - operation, dest, ALU, + {TR16, Z, W, src_l, src_h, + WAIT, + RD_INC, ALU, Z, W, + operation, dest, ALU, + WAIT, + OP_F, OP }; } @@ -79,20 +79,20 @@ { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, - IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, + WAIT, WR, Z, W, src_l, - IDLE, INC16, Z, W, - IDLE, + WAIT, WR, Z, W, src_h, IDLE, + WAIT, + OP_F, OP }; } @@ -100,20 +100,20 @@ { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, - RD, dest_l, Z, W, - IDLE, - INC16, Z, W, + WAIT, + RD_INC, dest_l, Z, W, IDLE, + WAIT, RD, dest_h, Z, W, IDLE, + WAIT, + OP_F, OP }; } @@ -121,17 +121,17 @@ { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, - WR, Z, W, src, - INC16, Z, W, + WAIT, + WR_INC, Z, W, src, TR, W, A, + WAIT, + OP_F, OP }; } @@ -139,29 +139,29 @@ { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - RD, W, PCl, PCh, + WAIT, + RD_INC, dest, Z, W, IDLE, - INC16, PCl, PCh, - IDLE, - RD, dest, Z, W, - IDLE, - INC16, Z, W, + WAIT, + OP_F, OP }; } private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src) { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, dest_l, dest_h, - WR, Z, W, src, - INC16, Z, W, - TR, W, A, + {TR16, Z, W, dest_l, dest_h, + WAIT, + WR_INC, Z, W, src, + TR, W, A, + WAIT, + OP_F, OP }; } @@ -169,14 +169,14 @@ { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, ALU, src_l, src_h, IDLE, - IDLE, - RD, ALU, src_l, src_h, - IDLE, - INC16, src_l, src_h, - IDLE, + WAIT, WR, dest_l, dest_h, ALU, IDLE, + WAIT, + OP_F, OP }; } @@ -184,24 +184,11 @@ { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, dest, src_l, src_h, IDLE, - IDLE, - RD, dest, src_l, src_h, - IDLE, - INC16, src_l, src_h, - OP }; - } - - private void LD_IND_8_DEC(ushort dest, ushort src_l, ushort src_h) - { - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - RD, dest, src_l, src_h, - IDLE, - DEC16, src_l, src_h, - IDLE, + WAIT, + OP_F, OP }; } @@ -209,14 +196,14 @@ { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, dest_l, src_l, src_h, IDLE, + WAIT, + RD_INC, dest_h, src_l, src_h, IDLE, - RD, dest_l, src_l, src_h, - IDLE, - INC16, src_l, src_h, - RD, dest_h, src_l, src_h, - IDLE, - INC16, src_l, src_h, + WAIT, + OP_F, OP }; } @@ -225,14 +212,14 @@ cur_instr = new ushort[] {IDLE, IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, - IDLE, INC8, ALU, - IDLE, + WAIT, WR, src_l, src_h, ALU, IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -241,14 +228,14 @@ cur_instr = new ushort[] {IDLE, IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, - IDLE, DEC8, ALU, - IDLE, + WAIT, WR, src_l, src_h, ALU, IDLE, - IDLE, + WAIT, + OP_F, OP }; } @@ -258,14 +245,14 @@ cur_instr = new ushort[] {IDLE, IDLE, + WAIT, RD, ALU, Z, W, - IDLE, operation, ALU, - IDLE, + WAIT, WR, Z, W, ALU, - IDLE, TR, dest, ALU, - IDLE, + WAIT, + OP_F, OP }; } @@ -274,14 +261,14 @@ cur_instr = new ushort[] {IDLE, IDLE, + WAIT, RD, ALU, Z, W, - IDLE, operation, bit, ALU, - IDLE, + WAIT, WR, Z, W, ALU, - IDLE, TR, dest, ALU, - IDLE, + WAIT, + OP_F, OP }; } @@ -290,11 +277,11 @@ cur_instr = new ushort[] {IDLE, IDLE, - IDLE, + WAIT, RD, ALU, Z, W, - IDLE, I_BIT, bit, ALU, - IDLE, + WAIT, + OP_F, OP }; } @@ -302,23 +289,23 @@ { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - RD, ALU, PCl, PCh, - INC16, PCl, PCh, IDLE, TR16, Z, W, src_l, src_h, + ADDS, Z, W, ALU, ZERO, IDLE, - ADDS, Z, W, ALU, ZERO, IDLE, + IDLE, + WAIT, RD, ALU, Z, W, - IDLE, - IDLE, operation, ALU, - IDLE, - IDLE, - IDLE, + WAIT, WR, Z, W, ALU, IDLE, + WAIT, + OP_F, OP }; } @@ -326,19 +313,19 @@ { cur_instr = new ushort[] {IDLE, - IDLE, - RD, ALU, PCl, PCh, - INC16, PCl, PCh, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, TR16, Z, W, src_l, src_h, - IDLE, ADDS, Z, W, ALU, ZERO, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - RD, ALU, PCl, PCh, - INC16, PCl, PCh, - IDLE, + WAIT, WR, Z, W, ALU, IDLE, + WAIT, + OP_F, OP }; } @@ -346,19 +333,19 @@ { cur_instr = new ushort[] {IDLE, - IDLE, - RD, ALU, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, TR16, Z, W, src_l, src_h, IDLE, ADDS, Z, W, ALU, ZERO, IDLE, + WAIT, RD, ALU, Z, W, - IDLE, operation, dest, ALU, IDLE, + WAIT, + OP_F, OP }; } @@ -366,36 +353,36 @@ { cur_instr = new ushort[] {IDLE, - RD, ALU, PCl, PCh, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - INC16, PCl, PCh, IDLE, TR16, Z, W, dest_l, dest_h, - IDLE, ADDS, Z, W, ALU, ZERO, IDLE, - WR, Z, W, src, - IDLE, - IDLE, IDLE, + WAIT, + WR, Z, W, src, IDLE, + WAIT, + OP_F, OP }; } private void LD_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {RD, ALU, L, H, + {IDLE, + WAIT, + RD, ALU, L, H, IDLE, + WAIT, WR, E, D, ALU, - IDLE, operation, L, H, - IDLE, operation, E, D, - IDLE, - DEC16, C, B, - SET_FL_LD, - IDLE, + SET_FL_LD, // BC gets decremented in here + WAIT, + OP_F, OP_R, 0, operation, repeat_instr }; } @@ -403,16 +390,16 @@ { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, RD, ALU, L, H, operation, L, H, - IDLE, - IDLE, DEC16, C, B, SET_FL_CP, - IDLE, operation, Z, W, IDLE, + IDLE, + WAIT, + OP_F, OP_R, 1, operation, repeat_instr }; } @@ -421,32 +408,32 @@ cur_instr = new ushort[] {IN, ALU, C, B, IDLE, + IDLE, + IDLE, + IDLE, + WAIT, WR, L, H, ALU, + REP_OP_I, operation, IDLE, - operation, L, H, - IDLE, - TR16, Z, W, C, B, - operation, Z, W, - IDLE, - DEC8, B, - IDLE, + WAIT, + OP_F, OP_R, 2, operation, repeat_instr }; } private void OUT_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {RD, ALU, L, H, + {IDLE, + WAIT, + RD, ALU, L, H, IDLE, OUT, C, B, ALU, IDLE, IDLE, - operation, L, H, - DEC8, B, - IDLE, - TR16, Z, W, C, B, - operation, Z, W, + REP_OP_O, operation, IDLE, + WAIT, + OP_F, OP_R, 3, operation, repeat_instr }; } @@ -455,23 +442,23 @@ { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, RD, Z, dest_l, dest_h, IDLE, IDLE, + WAIT, I_RD, W, dest_l, dest_h, 1, IDLE, - IDLE, - WR, dest_l, dest_h, src_l, + WAIT, + WR, dest_l, dest_h, src_l, IDLE, IDLE, + IDLE, + WAIT, I_WR, dest_l, dest_h, 1, src_h, - IDLE, - IDLE, TR16, src_l, src_h, Z, W, - IDLE, - IDLE, - IDLE, + WAIT, + OP_F, OP }; } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index c3786015a3..867b83a1e0 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -76,6 +76,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort HL_BIT = 61; public const ushort FTCH_DB = 62; public const ushort WAIT = 63; // enterred when readin or writing and FlagW is true + public const ushort OP_F = 64; // fetch the opcode, happens on cycle 3 of fetch cycle + public const ushort RD_INC = 65; // read and increment + public const ushort RST = 66; + public const ushort WR_INC = 67; // write and increment + public const ushort REP_OP_I = 68; + public const ushort REP_OP_O = 69; public byte temp_R; @@ -90,7 +96,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ResetRegisters(); ResetInterrupts(); TotalExecutedCycles = 0; - cur_instr = new ushort[] { OP }; + cur_instr = new ushort[] + { IDLE, + WAIT, + OP_F, + OP }; instr_pntr = 0; NO_prefix = true; } @@ -205,19 +215,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A } else { - if(!FlagW) - { - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null) TraceCallback(State()); - FetchInstruction(FetchMemory(RegPC++)); - instr_pntr = 0; - } - else - { - instr_pntr--; - instr_swap = OP; - cur_instr[instr_pntr] = WAIT; - } + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null) TraceCallback(State()); + RegPC++; + FetchInstruction(); + instr_pntr = 0; } temp_R = (byte)(Regs[R] & 0x7F); @@ -255,10 +257,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (repeat && temp3 > 0) { cur_instr = new ushort[] - {IDLE, - DEC16, PCl, PCh, - IDLE, + {DEC16, PCl, PCh, DEC16, PCl, PCh, + WAIT, + OP_F, OP }; instr_pntr = 0; @@ -340,19 +342,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A } else { - if (!FlagW) - { - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null) TraceCallback(State()); - FetchInstruction(FetchMemory(RegPC++)); - instr_pntr = 0; - } - else - { - instr_pntr--; - instr_swap = OP; - cur_instr[instr_pntr] = WAIT; - } + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null) TraceCallback(State()); + RegPC++; + FetchInstruction(); + instr_pntr = 0; } temp_R = (byte)(Regs[R] & 0x7F); @@ -432,53 +426,16 @@ namespace BizHawk.Emulation.Cores.Components.Z80A instr_pntr = 0; break; case RD: - if (!FlagW) - { - Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - } - else - { - instr_pntr--; - instr_swap = RD; - cur_instr[instr_pntr] = WAIT; - } - + Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case WR: - if (!FlagW) - { - Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - } - else - { - instr_pntr--; - instr_swap = WR; - cur_instr[instr_pntr] = WAIT; - } + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case I_RD: - if (!FlagW) - { - I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - } - else - { - instr_pntr--; - instr_swap = I_RD; - cur_instr[instr_pntr] = WAIT; - } + I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case I_WR: - if (!FlagW) - { - I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - } - else - { - instr_pntr--; - instr_swap =I_WR; - cur_instr[instr_pntr] = WAIT; - } + I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -606,7 +563,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (prefix_src == IXCBpre) { IXCB_prefix = true; IXCB_prefetch = true; } if (prefix_src == IYCBpre) { IYCB_prefix = true; IYCB_prefetch = true; } - FetchInstruction(FetchMemory(RegPC++)); + RegPC++; + FetchInstruction(); instr_pntr = 0; // only the first prefix in a double prefix increases R, although I don't know how / why if (prefix_src < 4) @@ -632,28 +590,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = iff2; break; case OUT: - if (!FlagW) - { - OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - } - else - { - instr_pntr--; - instr_swap = OUT; - cur_instr[instr_pntr] = WAIT; - } + OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case IN: - if (!FlagW) - { - IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - } - else - { - instr_pntr--; - instr_swap = IN; - cur_instr[instr_pntr] = WAIT; - } + IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case NEG: NEG_8_Func(cur_instr[instr_pntr++]); @@ -668,6 +608,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A RLD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case SET_FL_LD: + DEC16_Func(C, B); SET_FL_LD_Func(); break; case SET_FL_CP: @@ -684,35 +625,54 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { instr_pntr--; } + break; + case OP_F: + opcode = FetchMemory(RegPC); + break; + case RD_INC: + Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case WR_INC: + Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case RST: + Regs[Z] = cur_instr[instr_pntr++]; + Regs[W] = 0; + Regs[PCl] = Regs[Z]; + Regs[PCh] = Regs[W]; + break; + case REP_OP_I: + ushort temp4 = cur_instr[instr_pntr++]; + if (temp4 == DEC16) + { + DEC16_Func(L, H); + TR16_Func(Z, W, C, B); + DEC16_Func(Z, W); + DEC8_Func(B); + } else { - switch (instr_swap) - { - case OP: - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null) TraceCallback(State()); - FetchInstruction(FetchMemory(RegPC++)); - instr_pntr = 0; - break; - case RD: - Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; - case WR: - Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; - case I_RD: - I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; - case I_WR: - I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; - case IN: - IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; - case OUT: - OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; - } + INC16_Func(L, H); + TR16_Func(Z, W, C, B); + INC16_Func(Z, W); + DEC8_Func(B); + } + break; + case REP_OP_O: + ushort temp5 = cur_instr[instr_pntr++]; + if (temp5 == DEC16) + { + DEC16_Func(L, H); + DEC8_Func(B); + TR16_Func(Z, W, C, B); + DEC16_Func(Z, W); + } + else + { + INC16_Func(L, H); + DEC8_Func(B); + TR16_Func(Z, W, C, B); + INC16_Func(Z, W); } break; } From b9dd43545536d15432ded4c28e56a3ad794c1b1d Mon Sep 17 00:00:00 2001 From: Matt Burgess Date: Fri, 1 Jun 2018 17:38:42 +0100 Subject: [PATCH 244/339] ZXHawk: Started 128 and +2a/3 new ULA implementation --- .../BizHawk.Emulation.Cores.csproj | 2 + .../SinclairSpectrum/Machine/CPUMonitor.cs | 108 +++++++++++++++++- .../SinclairSpectrum/Machine/SpectrumBase.cs | 3 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 3 +- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 49 ++++++++ .../Machine/ZXSpectrum128K/ZX128.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 3 +- .../ZX128Plus2a.Screen.cs | 49 ++++++++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 2 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 3 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 5 +- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 9 +- 12 files changed, 224 insertions(+), 14 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 36b6f5462a..6ac5775daf 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -263,6 +263,7 @@ + @@ -1437,6 +1438,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 25a03161fc..d2cff11e0e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -39,12 +39,112 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void Cycle() { + if (portContending) { - RunPortContention(); + RunPortContention(); + } + /* + else + { + // check for wait state on cycle that has just happened + // next cycle should be a read/write operation + if (cur_instr[instr_pntr] == Z80A.WAIT) + { + ushort addr = 0; + bool abort = false; + + // identify the type of operation and get the targetted address + switch (cur_instr[instr_pntr + 1]) + { + // op fetch + case Z80A.OP_F: + addr = RegPC; + break; + // read/writes + case Z80A.RD: + case Z80A.RD_INC: + case Z80A.WR: + case Z80A.WR_INC: + addr = (ushort)(_cpu.Regs[cur_instr[instr_pntr + 3]] | _cpu.Regs[cur_instr[instr_pntr + 4]] << 8); + break; + default: + abort = true; + break; + } + + if (!abort) + { + // is the address in a potentially contended bank? + if (_machine.IsContended(addr)) + { + // will the ULA be contending this address on the next cycle? + var delay = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle + 1); + _cpu.TotalExecutedCycles += delay; + } + } + } + + } + */ + return; + // check for wait state on next cycle + // the cycle after that should be a read/write operation or op fetch + if (instr_pntr >= cur_instr.Length - 1) + { + // will overflow + return; + } + + if (cur_instr[instr_pntr + 1] == Z80A.WAIT) + { + // return; + ushort addr = 0; + + // identify the type of operation and get the targetted address + var op = cur_instr[instr_pntr + 2]; + switch (op) + { + // op fetch + case Z80A.OP_F: + addr = (ushort)(RegPC); + if (_machine.IsContended(addr)) + { + var delay = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle); + if (delay > 0) + { + _cpu.TotalExecutedCycles += delay; + _machine.ULADevice.RenderScreen((int)_machine.CurrentFrameCycle); + } + } + break; + // read/writes + case Z80A.RD: + case Z80A.RD_INC: + case Z80A.WR: + case Z80A.WR_INC: + case Z80A.I_RD: + case Z80A.I_WR: + addr = (ushort)(_cpu.Regs[cur_instr[instr_pntr + 4]] | _cpu.Regs[cur_instr[instr_pntr + 5]] << 8); + if (_machine.IsContended(addr)) + { + var delay = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle); + if (delay > 0) + { + _cpu.TotalExecutedCycles += delay; + _machine.ULADevice.RenderScreen((int)_machine.CurrentFrameCycle); + } + } + break; + case Z80A.FTCH_DB: + break; + default: + break; + } } } + #region Port Contention public int portContCounter = 0; @@ -78,6 +178,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { case MachineType.ZXSpectrum16: case MachineType.ZXSpectrum48: + case MachineType.ZXSpectrum128: + case MachineType.ZXSpectrum128Plus2: if ((lastPortAddr & 0xc000) == 0x4000) highByte407f = true; @@ -160,10 +262,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } break; - case MachineType.ZXSpectrum128: - case MachineType.ZXSpectrum128Plus2: - break; - case MachineType.ZXSpectrum128Plus2a: case MachineType.ZXSpectrum128Plus3: break; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 1a8ff0a328..d6bea89b67 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -169,7 +169,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // check for interrupt ULADevice.CheckForInterrupt(CurrentFrameCycle); - // run a single CPU instruction + // run a single CPU instruction + CPU.ExecuteOne(); CPUMon.Cycle(); 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 68639979e9..bff1ec4334 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,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPort(ushort addr) { - throw new NotImplementedException(); + CPUMon.ContendPort(addr); + return; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs new file mode 100644 index 0000000000..cbd98f1128 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class Screen128 : ULA + { + #region Construction + + public Screen128(SpectrumBase machine) + : base(machine) + { + // timing + ClockSpeed = 3546900; + FrameCycleLength = 70908; + InterruptStartTime = 33; + InterruptLength = 36; + ScanlineTime = 228; + + BorderLeftTime = 24; + BorderRightTime = 24; + + FirstPaperLine = 63; + FirstPaperTState = 64; + + Border4T = true; + Border4TStage = 2; + + // screen layout + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + RenderingTable = new RenderTable(this, + MachineType.ZXSpectrum128); + + SetupScreenSize(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index fc2369a3a1..ed556a3025 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -30,7 +30,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PagingDisabled = false; //ULADevice = new ULA128(this); - ULADevice = new Screen48(this); // still todo + ULADevice = new Screen128(this); // still todo BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); 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 3eb942d51f..2423078a83 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,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPort(ushort addr) { - throw new NotImplementedException(); + CPUMon.ContendPort(addr); + return; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs new file mode 100644 index 0000000000..e9026ce5d7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + class Screen128Plus2a : ULA + { + #region Construction + + public Screen128Plus2a(SpectrumBase machine) + : base(machine) + { + // timing + ClockSpeed = 3546900; + FrameCycleLength = 70908; + InterruptStartTime = 33; + InterruptLength = 32; + ScanlineTime = 228; + + BorderLeftTime = 24; + BorderRightTime = 24; + + FirstPaperLine = 63; + FirstPaperTState = 64; + + Border4T = true; + Border4TStage = 2; + + // screen layout + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; + BorderBottomHeight = 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + RenderingTable = new RenderTable(this, + MachineType.ZXSpectrum128Plus2a); + + SetupScreenSize(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index 487cc386b4..363f1a52ac 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -30,7 +30,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PagingDisabled = false; //ULADevice = new ULAPlus2a(this); - ULADevice = new Screen48(this); // still todo + ULADevice = new Screen128Plus2a(this); // still todo BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); 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 c3b53fff79..d1c30ee3a8 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,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendPort(ushort addr) { - throw new NotImplementedException(); + CPUMon.ContendPort(addr); + return; } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index decfb7466e..4e6d889d55 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -20,9 +20,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { Spectrum = spectrum; CPU = cpu; - CPUMon.machineType = MachineType.ZXSpectrum128Plus3; + CPUMon = new CPUMonitor(this); + CPUMon.machineType = MachineType.ZXSpectrum128Plus3; ROMPaged = 0; SHADOWPaged = false; @@ -30,7 +31,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PagingDisabled = false; // ULADevice = new ULAPlus3(this); - ULADevice = new Screen48(this); // still todo + ULADevice = new Screen128Plus2a(this); // still todo BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); 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 624df29d2c..7dd6f3cd21 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -124,7 +124,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (IsContended(addr)) { - var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); + var off = 1; + var offset = CurrentFrameCycle + off; + if (offset < 0) + offset += ULADevice.FrameCycleLength; + if (offset >= ULADevice.FrameCycleLength) + offset -= ULADevice.FrameCycleLength; + + var delay = ULADevice.GetContentionValue((int)offset); if (delay > 0) { From 7220e9d3944241386216cb6c434601086939820f Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 1 Jun 2018 15:21:05 -0400 Subject: [PATCH 245/339] z80: fix some instruction timings for IN/OUT --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs | 6 +++--- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 0bc5e7cc19..4ad6252352 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -385,7 +385,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WR, SPl, SPh, PCh, DEC16, SPl, SPh, WAIT, - WR, SPl, SPh, PCl, + WR, SPl, SPh, PCl, RST, n, WAIT, OP_F, @@ -497,10 +497,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IN, dest, src, B, TR16, Z, W, C, B, + WAIT, + IN, dest, src, B, INC16, Z, W, - IDLE, WAIT, OP_F, OP}; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 0ed80b9067..0e0b69801e 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -406,10 +406,10 @@ private void IN_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {IN, ALU, C, B, - IDLE, - IDLE, + {IDLE, IDLE, + WAIT, + IN, ALU, C, B, IDLE, WAIT, WR, L, H, ALU, @@ -427,9 +427,9 @@ WAIT, RD, ALU, L, H, IDLE, + IDLE, + WAIT, OUT, C, B, ALU, - IDLE, - IDLE, REP_OP_O, operation, IDLE, WAIT, From db8b051b0089a36ba9c53591020d334bb1751dcd Mon Sep 17 00:00:00 2001 From: Vilsol Date: Sat, 2 Jun 2018 23:18:45 +0300 Subject: [PATCH 246/339] Resize unattended video if zoom factor >1 --- BizHawk.Client.EmuHawk/MainForm.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 25a6409e01..efbf1e9f63 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -3160,6 +3160,12 @@ namespace BizHawk.Client.EmuHawk aw = new AudioStretcher(aw); } + if (unattended && Global.Config.TargetZoomFactor > 1) + { + _avwriterResizew = Global.Config.TargetZoomFactor * _currentVideoProvider.BufferWidth; + _avwriterResizeh = Global.Config.TargetZoomFactor * _currentVideoProvider.BufferHeight; + } + aw.SetMovieParameters(Emulator.VsyncNumerator(), Emulator.VsyncDenominator()); if (_avwriterResizew > 0 && _avwriterResizeh > 0) { From 136943b812f2affdfbfafa9888fe2ee541468c11 Mon Sep 17 00:00:00 2001 From: Frequency Modulation Date: Sun, 3 Jun 2018 14:07:41 +0200 Subject: [PATCH 247/339] fixed some case-sensitivity issues (#1163) --- BizHawk.Client.EmuHawk/Properties/Resources.resx | 4 ++-- BizHawk.Emulation.Cores/Properties/Resources.resx | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/BizHawk.Client.EmuHawk/Properties/Resources.resx b/BizHawk.Client.EmuHawk/Properties/Resources.resx index 38184f2b35..7e21e9ffc9 100644 --- a/BizHawk.Client.EmuHawk/Properties/Resources.resx +++ b/BizHawk.Client.EmuHawk/Properties/Resources.resx @@ -1558,6 +1558,6 @@ ..\images\ControllerImages\NGPController.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - ..\config\controllerimages\zxspectrumkeyboards.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\config\ControllerImages\ZXSpectrumKeyboards.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - \ No newline at end of file + diff --git a/BizHawk.Emulation.Cores/Properties/Resources.resx b/BizHawk.Emulation.Cores/Properties/Resources.resx index b5e12deeae..783b8f5ede 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.resx +++ b/BizHawk.Emulation.Cores/Properties/Resources.resx @@ -128,16 +128,16 @@ ..\Resources\sgb-cart-present.spc.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ..\resources\spectrum3_v4-0_rom0.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\Spectrum3_V4-0_ROM0.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ..\resources\spectrum3_v4-0_rom1.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\Spectrum3_V4-0_ROM1.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ..\resources\spectrum3_v4-0_rom2.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\Spectrum3_V4-0_ROM2.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ..\resources\spectrum3_v4-0_rom3.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\Spectrum3_V4-0_ROM3.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\128.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 @@ -146,9 +146,9 @@ ..\Resources\48.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - ..\resources\plus2a.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\plus2a.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 ..\Resources\plus2.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file + From 32ae549c70d570972d6bb6b6fd5f3005a2c880ca Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 3 Jun 2018 19:14:30 -0400 Subject: [PATCH 248/339] z80: Add bus request timing array to work with zx spectrum Also some clean up --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 2 + .../CPUs/Z80A/Operations.cs | 11 +- .../CPUs/Z80A/Tables_Direct.cs | 120 ++++++++++-- .../CPUs/Z80A/Tables_Indirect.cs | 66 ++++++- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 182 +++++++++--------- .../SinclairSpectrum/Machine/CPUMonitor.cs | 2 - 6 files changed, 256 insertions(+), 127 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index 57f262bd7a..8feed65cbe 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -18,8 +18,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // variables for executing instructions public int instr_pntr = 0; + public int bus_pntr = 0; public ushort instr_swap; public ushort[] cur_instr; + public ushort[] BUSRQ; public byte opcode; public bool NO_prefix, CB_prefix, IX_prefix, EXTD_prefix, IY_prefix, IXCB_prefix, IYCB_prefix; public bool IXCB_prefetch, IYCB_prefetch; // value is fetched before opcode diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs index f9fe6f797f..4ec81fe298 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs @@ -18,12 +18,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INC16_Func(src_l, src_h); } - public void I_Read_Func(ushort dest, ushort src_l, ushort src_h, ushort inc) - { - Regs[dest] = ReadMemory((ushort)((Regs[src_l] | (Regs[src_h] << 8)) + inc)); - Regs[DB] = Regs[dest]; - } - public void Write_Func(ushort dest_l, ushort dest_h, ushort src) { Regs[DB] = Regs[src]; @@ -37,10 +31,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INC16_Func(dest_l, dest_h); } - public void I_Write_Func(ushort dest_l, ushort dest_h, ushort inc, ushort src) + public void Write_DEC_Func(ushort dest_l, ushort dest_h, ushort src) { Regs[DB] = Regs[src]; - WriteMemory((ushort)((Regs[dest_l] | (Regs[dest_h] << 8)) + inc), (byte)Regs[src]); + WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]); + DEC16_Func(dest_l, dest_h); } public void OUT_Func(ushort dest_l, ushort dest_h, ushort src) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 4ad6252352..c4ab95abf7 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -14,6 +14,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {PCl, 0, 0, 0 }; } // NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers @@ -25,6 +27,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void EXX_() @@ -34,6 +38,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } // this exchanges 2 16 bit registers @@ -44,17 +50,21 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void INC_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {INC16, src_l, src_h, + {INC16, src_l, src_h, IDLE, IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, 0, PCl, 0, 0, 0}; } @@ -67,6 +77,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, 0, PCl, 0, 0, 0}; } // this is done in two steps technically, but the flags don't work out using existing funcitons @@ -85,6 +97,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; } private void REG_OP(ushort operation, ushort dest, ushort src) @@ -94,6 +108,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {PCl, 0, 0, 0 }; } // Operations using the I and R registers take one T-cycle longer @@ -105,6 +121,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, PCl, 0, 0, 0 }; } // note: do not use DEC here since no flags are affected by this operation @@ -126,6 +144,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, PCl, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0}; } else { @@ -138,6 +158,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, PCl, 0, 0, PCl, 0, 0, 0}; } } @@ -148,6 +170,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IDLE, IDLE, HALT }; + + BUSRQ = new ushort[] {PCl, 0, 0, 0 }; } private void JR_COND(bool cond) @@ -167,6 +191,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {PCl, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0}; } else { @@ -178,6 +204,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, 0}; } } @@ -196,6 +224,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, Z, 0, 0, 0}; } else { @@ -210,6 +240,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, PCl, 0, 0, 0}; } } @@ -226,6 +258,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; } private void RETI_() @@ -241,6 +275,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; } private void RETN_() @@ -256,6 +292,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; } @@ -275,6 +313,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; } else { @@ -284,6 +324,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {0, PCl, 0, 0, 0}; } } @@ -296,19 +338,21 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, RD_INC, Z, PCl, PCh, IDLE, - IDLE, + DEC16, SPl, SPh, WAIT, RD_INC, W, PCl, PCh, - DEC16, SPl, SPh, + IDLE, WAIT, - WR, SPl, SPh, PCh, - DEC16, SPl, SPh, + WR_DEC, SPl, SPh, PCh, + IDLE, WAIT, WR, SPl, SPh, PCl, TR16, PCl, PCh, Z, W, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, 0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; } else { @@ -323,6 +367,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, PCl, 0, 0, 0 }; } } @@ -333,6 +379,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void BIT_OP(ushort operation, ushort bit, ushort src) @@ -342,22 +390,26 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void PUSH_(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, - DEC16, SPl, SPh, + {DEC16, SPl, SPh, + IDLE, WAIT, - WR, SPl, SPh, src_h, - DEC16, SPl, SPh, + WR_DEC, SPl, SPh, src_h, + IDLE, WAIT, WR, SPl, SPh, src_l, IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; } @@ -374,22 +426,26 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; } private void RST_(ushort n) { cur_instr = new ushort[] - {IDLE, - DEC16, SPl, SPh, + {DEC16, SPl, SPh, + IDLE, WAIT, - WR, SPl, SPh, PCh, - DEC16, SPl, SPh, + WR_DEC, SPl, SPh, PCh, + RST, n, WAIT, WR, SPl, SPh, PCl, - RST, n, + TR16, PCl, PCh, Z, W, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0 }; } private void PREFIX_(ushort src) @@ -399,6 +455,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, PREFIX, src}; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void PREFETCH_(ushort src_l, ushort src_h) @@ -408,6 +466,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, PREFIX, IXYprefetch }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void DI_() @@ -417,6 +477,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void EI_() @@ -426,6 +488,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void JP_16(ushort src_l, ushort src_h) @@ -435,6 +499,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { src_l, 0, 0, 0 }; } private void LD_SP_16(ushort src_l, ushort src_h) @@ -446,6 +512,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, 0, src_l, 0, 0, 0 }; } private void OUT_() @@ -462,6 +530,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP}; + + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0 ,0, PCl, 0, 0, 0}; } private void OUT_REG_(ushort dest, ushort src) @@ -475,6 +545,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP}; + + BUSRQ = new ushort[] { 0, 0, 0, 0, PCl, 0, 0, 0 }; } private void IN_() @@ -491,6 +563,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP}; + + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; } private void IN_REG_(ushort dest, ushort src) @@ -504,6 +578,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP}; + + BUSRQ = new ushort[] { 0, 0, 0, 0, PCl, 0, 0, 0 }; } private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -520,6 +596,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP}; + + BUSRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; } private void INT_MODE_(ushort src) @@ -529,6 +607,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; } private void RRD_() @@ -543,11 +623,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IDLE, IDLE, WAIT, - WR, Z, W, ALU, - INC16, Z, W, + WR_INC, Z, W, ALU, + IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, Z, 0, 0, 0, PCl, 0, 0, 0 }; } private void RLD_() @@ -562,11 +644,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IDLE, IDLE, WAIT, - WR, Z, W, ALU, - INC16, Z, W, + WR_INC, Z, W, ALU, + IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, Z, 0, 0, 0, PCl, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 0e0b69801e..25e2f06528 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -16,6 +16,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { src_l, 0, 0, src_l, 0, 0, 0, PCl, 0, 0, 0 }; } private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) @@ -32,6 +34,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; } // Note that this operation uses I_BIT, same as indexed BIT. @@ -49,6 +53,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, src_l, 0, 0, PCl, 0, 0, 0 }; } private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -61,6 +67,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { src_l, 0, 0, PCl, 0, 0, 0 }; } private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -73,6 +81,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { src_l, 0, 0, PCl, 0, 0, 0 }; } private void LD_16_IND_nn(ushort src_l, ushort src_h) @@ -86,14 +96,16 @@ RD_INC, W, PCl, PCh, IDLE, WAIT, - WR, Z, W, src_l, - INC16, Z, W, + WR_INC, Z, W, src_l, + IDLE, WAIT, WR, Z, W, src_h, IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void LD_IND_16_nn(ushort dest_l, ushort dest_h) @@ -115,6 +127,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void LD_8_IND_nn(ushort src) @@ -133,6 +147,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void LD_IND_8_nn(ushort dest) @@ -151,6 +167,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src) @@ -163,6 +181,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { dest_l, 0, 0, PCl, 0, 0, 0 }; } private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -178,6 +198,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { src_l, 0, 0, dest_l, 0, 0, PCl, 0, 0, 0 }; } private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h) @@ -190,6 +212,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { src_l, 0, 0, PCl, 0, 0, 0 }; } private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -205,6 +229,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; } private void INC_8_IND(ushort src_l, ushort src_h) @@ -221,6 +247,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; } private void DEC_8_IND(ushort src_l, ushort src_h) @@ -237,6 +265,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; } // NOTE: WZ implied for the wollowing 3 functions @@ -254,6 +284,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void I_BIT_OP(ushort operation, ushort bit, ushort dest) @@ -270,6 +302,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void I_BIT_TE(ushort bit) @@ -283,6 +317,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void I_OP_n(ushort operation, ushort src_l, ushort src_h) @@ -307,6 +343,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void I_OP_n_n(ushort src_l, ushort src_h) @@ -327,6 +365,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, PCl, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void I_REG_OP_IND_n(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -347,6 +387,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src) @@ -367,6 +409,8 @@ WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, Z, 0, 0, 0, PCl, 0, 0, 0 }; } private void LD_OP_R(ushort operation, ushort repeat_instr) @@ -384,6 +428,8 @@ WAIT, OP_F, OP_R, 0, operation, repeat_instr }; + + BUSRQ = new ushort[] { L, 0, 0, E, 0, 0, 0, 0, PCl, 0, 0, 0 }; } private void CP_OP_R(ushort operation, ushort repeat_instr) @@ -401,6 +447,8 @@ WAIT, OP_F, OP_R, 1, operation, repeat_instr }; + + BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; } private void IN_OP_R(ushort operation, ushort repeat_instr) @@ -418,6 +466,8 @@ WAIT, OP_F, OP_R, 2, operation, repeat_instr }; + + BUSRQ = new ushort[] { 0, 0, 0, 0, L, 0, 0, 0, PCl, 0, 0, 0 }; } private void OUT_OP_R(ushort operation, ushort repeat_instr) @@ -435,6 +485,8 @@ WAIT, OP_F, OP_R, 3, operation, repeat_instr }; + + BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; } // this is an indirect change of a a 16 bit register with memory @@ -444,22 +496,24 @@ {IDLE, WAIT, RD, Z, dest_l, dest_h, - IDLE, + INC16, dest_l, dest_h, IDLE, WAIT, - I_RD, W, dest_l, dest_h, 1, + RD, W, dest_l, dest_h, IDLE, WAIT, - WR, dest_l, dest_h, src_l, + WR_DEC, dest_l, dest_h, src_h, IDLE, IDLE, IDLE, WAIT, - I_WR, dest_l, dest_h, 1, src_h, + WR, dest_l, dest_h, 1, src_l, TR16, src_l, src_h, Z, W, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { dest_l, 0, 0, 0, dest_l, 0, 0, dest_l, 0, 0, 0, 0, dest_l, 0, 0, PCl, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 867b83a1e0..5291fb597a 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -18,70 +18,70 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort HALT = 3; public const ushort RD = 4; public const ushort WR = 5; - public const ushort I_RD = 6; - public const ushort I_WR = 7; - public const ushort TR = 8; - public const ushort TR16 = 9; - public const ushort ADD16 = 10; - public const ushort ADD8 = 11; - public const ushort SUB8 = 12; - public const ushort ADC8 = 13; - public const ushort SBC8 = 14; - public const ushort SBC16 = 15; - public const ushort ADC16 = 16; - public const ushort INC16 = 17; - public const ushort INC8 = 18; - public const ushort DEC16 = 19; - public const ushort DEC8 = 20; - public const ushort RLC = 21; - public const ushort RL = 22; - public const ushort RRC = 23; - public const ushort RR = 24; - public const ushort CPL = 25; - public const ushort DA = 26; - public const ushort SCF = 27; - public const ushort CCF = 28; - public const ushort AND8 = 29; - public const ushort XOR8 = 30; - public const ushort OR8 = 31; - public const ushort CP8 = 32; - public const ushort SLA = 33; - public const ushort SRA = 34; - public const ushort SRL = 35; - public const ushort SLL = 36; - public const ushort BIT = 37; - public const ushort RES = 38; - public const ushort SET = 39; - public const ushort EI = 40; - public const ushort DI = 41; - public const ushort EXCH = 42; - public const ushort EXX = 43; - public const ushort EXCH_16 = 44; - public const ushort PREFIX = 45; - public const ushort PREFETCH = 46; - public const ushort ASGN = 47; - public const ushort ADDS = 48; // signed 16 bit operation used in 2 instructions - public const ushort INT_MODE = 49; - public const ushort EI_RETN = 50; - public const ushort EI_RETI = 51; // reti has no delay in interrupt enable - public const ushort OUT = 52; - public const ushort IN = 53; - public const ushort NEG = 54; - public const ushort RRD = 55; - public const ushort RLD = 56; - public const ushort SET_FL_LD = 57; - public const ushort SET_FL_CP = 58; - public const ushort SET_FL_IR = 59; - public const ushort I_BIT = 60; - public const ushort HL_BIT = 61; - public const ushort FTCH_DB = 62; - public const ushort WAIT = 63; // enterred when readin or writing and FlagW is true - public const ushort OP_F = 64; // fetch the opcode, happens on cycle 3 of fetch cycle - public const ushort RD_INC = 65; // read and increment + public const ushort RD_INC = 6; // read and increment + public const ushort WR_INC = 7; // write and increment + public const ushort WR_DEC = 8; // write and increment (for stack pointer) + public const ushort TR = 9; + public const ushort TR16 = 10; + public const ushort ADD16 = 11; + public const ushort ADD8 = 12; + public const ushort SUB8 = 13; + public const ushort ADC8 = 14; + public const ushort SBC8 = 15; + public const ushort SBC16 = 16; + public const ushort ADC16 = 17; + public const ushort INC16 = 18; + public const ushort INC8 = 19; + public const ushort DEC16 = 20; + public const ushort DEC8 = 21; + public const ushort RLC = 22; + public const ushort RL = 23; + public const ushort RRC = 24; + public const ushort RR = 25; + public const ushort CPL = 26; + public const ushort DA = 27; + public const ushort SCF = 28; + public const ushort CCF = 29; + public const ushort AND8 = 30; + public const ushort XOR8 = 31; + public const ushort OR8 = 32; + public const ushort CP8 = 33; + public const ushort SLA = 34; + public const ushort SRA = 35; + public const ushort SRL = 36; + public const ushort SLL = 37; + public const ushort BIT = 38; + public const ushort RES = 39; + public const ushort SET = 40; + public const ushort EI = 41; + public const ushort DI = 42; + public const ushort EXCH = 43; + public const ushort EXX = 44; + public const ushort EXCH_16 = 45; + public const ushort PREFIX = 46; + public const ushort PREFETCH = 47; + public const ushort ASGN = 48; + public const ushort ADDS = 49; // signed 16 bit operation used in 2 instructions + public const ushort INT_MODE = 50; + public const ushort EI_RETN = 51; + public const ushort EI_RETI = 52; // reti has no delay in interrupt enable + public const ushort OUT = 53; + public const ushort IN = 54; + public const ushort NEG = 55; + public const ushort RRD = 56; + public const ushort RLD = 57; + public const ushort SET_FL_LD = 58; + public const ushort SET_FL_CP = 59; + public const ushort SET_FL_IR = 60; + public const ushort I_BIT = 61; + public const ushort HL_BIT = 62; + public const ushort FTCH_DB = 63; + public const ushort WAIT = 64; // enterred when readin or writing and FlagW is true + public const ushort OP_F = 65; // fetch the opcode, happens on cycle 3 of fetch cycle public const ushort RST = 66; - public const ushort WR_INC = 67; // write and increment - public const ushort REP_OP_I = 68; - public const ushort REP_OP_O = 69; + public const ushort REP_OP_I = 67; + public const ushort REP_OP_O = 68; + public byte temp_R; @@ -101,7 +101,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, OP_F, OP }; - instr_pntr = 0; + + BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + instr_pntr = 0; bus_pntr = 0; NO_prefix = true; } @@ -157,6 +159,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // Execute instructions public void ExecuteOne() { + bus_pntr++; switch (cur_instr[instr_pntr++]) { case IDLE: @@ -184,7 +187,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = false; NMI_(); NMICallback(); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; } else if (iff1 && FlagI) { @@ -211,7 +214,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A break; } IRQCallback(); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; } else { @@ -219,7 +222,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (TraceCallback != null) TraceCallback(State()); RegPC++; FetchInstruction(); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; } temp_R = (byte)(Regs[R] & 0x7F); @@ -263,7 +266,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - instr_pntr = 0; + BUSRQ = new ushort[] { 0, PCl, 0, 0, 0 }; + + instr_pntr = 0; bus_pntr = 0; // adjust WZ register accordingly switch (temp1) { @@ -311,7 +316,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = false; NMI_(); NMICallback(); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; } else if (iff1 && FlagI) { @@ -338,7 +343,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A break; } IRQCallback(); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; } else { @@ -346,7 +351,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (TraceCallback != null) TraceCallback(State()); RegPC++; FetchInstruction(); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; } temp_R = (byte)(Regs[R] & 0x7F); @@ -410,20 +415,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IRQCallback(); halted = false; } - else - { - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - HALT }; - } + temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -431,11 +429,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; - case I_RD: - I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + case RD_INC: + Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; - case I_WR: - I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + case WR_INC: + Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case WR_DEC: + Write_DEC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -565,7 +566,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A RegPC++; FetchInstruction(); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; // only the first prefix in a double prefix increases R, although I don't know how / why if (prefix_src < 4) { @@ -624,22 +625,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (FlagW) { instr_pntr--; + bus_pntr--; } break; case OP_F: opcode = FetchMemory(RegPC); break; - case RD_INC: - Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; - case WR_INC: - Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - break; case RST: Regs[Z] = cur_instr[instr_pntr++]; Regs[W] = 0; - Regs[PCl] = Regs[Z]; - Regs[PCh] = Regs[W]; break; case REP_OP_I: ushort temp4 = cur_instr[instr_pntr++]; @@ -747,7 +741,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("EI_pending", ref EI_pending); ser.Sync("instr_pntr", ref instr_pntr); + ser.Sync("bus_pntr", ref bus_pntr); ser.Sync("cur_instr", ref cur_instr, false); + ser.Sync("BUSRQ", ref BUSRQ, false); ser.Sync("instr_swap", ref instr_swap); ser.Sync("opcode", ref opcode); ser.Sync("FlagI", ref FlagI); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index d2cff11e0e..d098cc94a4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -123,8 +123,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case Z80A.RD_INC: case Z80A.WR: case Z80A.WR_INC: - case Z80A.I_RD: - case Z80A.I_WR: addr = (ushort)(_cpu.Regs[cur_instr[instr_pntr + 4]] | _cpu.Regs[cur_instr[instr_pntr + 5]] << 8); if (_machine.IsContended(addr)) { From 7677b75de3c0f31733b541415f848255d23d5d2a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 4 Jun 2018 10:35:12 +0100 Subject: [PATCH 249/339] ZXHawk: Start building on new BUSRQ implementation --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 139 +++++++----------- .../SinclairSpectrum/Machine/SpectrumBase.cs | 5 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 2 + .../ZX128Plus2a.Memory.cs | 2 + .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 2 + .../Machine/ZXSpectrum16K/ZX16.cs | 4 + .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 3 + 7 files changed, 70 insertions(+), 87 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index d098cc94a4..e345c44c4f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -23,6 +23,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public int instr_pntr => _cpu.instr_pntr; public ushort RegPC => _cpu.RegPC; public long TotalExecutedCycles => _cpu.TotalExecutedCycles; + public ushort BUSRQ + { + get + { + if (_cpu.bus_pntr < _cpu.BUSRQ.Length - 1) + return _cpu.BUSRQ[_cpu.bus_pntr]; + + return 0; + } + } /// /// Called when the first byte of an instruction is fetched @@ -39,106 +49,67 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void Cycle() { - if (portContending) { RunPortContention(); } - /* else { - // check for wait state on cycle that has just happened - // next cycle should be a read/write operation - if (cur_instr[instr_pntr] == Z80A.WAIT) - { - ushort addr = 0; - bool abort = false; + // check for upcoming BUSRQ + if (BUSRQ == 0) + return; - // identify the type of operation and get the targetted address - switch (cur_instr[instr_pntr + 1]) - { - // op fetch - case Z80A.OP_F: - addr = RegPC; - break; - // read/writes - case Z80A.RD: - case Z80A.RD_INC: - case Z80A.WR: - case Z80A.WR_INC: - addr = (ushort)(_cpu.Regs[cur_instr[instr_pntr + 3]] | _cpu.Regs[cur_instr[instr_pntr + 4]] << 8); - break; - default: - abort = true; - break; - } - - if (!abort) - { - // is the address in a potentially contended bank? - if (_machine.IsContended(addr)) - { - // will the ULA be contending this address on the next cycle? - var delay = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle + 1); - _cpu.TotalExecutedCycles += delay; - } - } - } - - } - */ - return; - // check for wait state on next cycle - // the cycle after that should be a read/write operation or op fetch - if (instr_pntr >= cur_instr.Length - 1) - { - // will overflow - return; - } - - if (cur_instr[instr_pntr + 1] == Z80A.WAIT) - { - // return; ushort addr = 0; - // identify the type of operation and get the targetted address - var op = cur_instr[instr_pntr + 2]; - switch (op) + switch (BUSRQ) { - // op fetch - case Z80A.OP_F: - addr = (ushort)(RegPC); - if (_machine.IsContended(addr)) - { - var delay = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle); - if (delay > 0) - { - _cpu.TotalExecutedCycles += delay; - _machine.ULADevice.RenderScreen((int)_machine.CurrentFrameCycle); - } - } + // PCl + case 0: + addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8); break; - // read/writes - case Z80A.RD: - case Z80A.RD_INC: - case Z80A.WR: - case Z80A.WR_INC: - addr = (ushort)(_cpu.Regs[cur_instr[instr_pntr + 4]] | _cpu.Regs[cur_instr[instr_pntr + 5]] << 8); - if (_machine.IsContended(addr)) - { - var delay = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle); - if (delay > 0) - { - _cpu.TotalExecutedCycles += delay; - _machine.ULADevice.RenderScreen((int)_machine.CurrentFrameCycle); - } - } + // Z + case 13: + addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8); break; - case Z80A.FTCH_DB: + // SPl + case 2: + addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8); break; + // L + case 11: + addr = (ushort)(_cpu.Regs[_cpu.L] | _cpu.Regs[_cpu.H] << 8); + break; + // I + case 21: + addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8); + break; + // Ixl + case 15: + addr = (ushort)(_cpu.Regs[_cpu.Ixl] | _cpu.Regs[_cpu.Ixh] << 8); + break; + // Iyl + case 17: + addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 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; + default: break; } + + if (_machine.IsContended(addr)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index d6bea89b67..a87c006336 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -169,10 +169,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // check for interrupt ULADevice.CheckForInterrupt(CurrentFrameCycle); - // run a single CPU instruction - + // run a single CPU instruction CPU.ExecuteOne(); - + // check contention for next cycle CPUMon.Cycle(); // cycle the tape device diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index 3ab198737e..df1d5587da 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -212,11 +212,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendMemory(ushort addr) { + /* if (IsContended(addr)) { var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); CPU.TotalExecutedCycles += delay; } + */ } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 26ae8ef939..d4c46af37f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -400,11 +400,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendMemory(ushort addr) { + /* if (IsContended(addr)) { var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); CPU.TotalExecutedCycles += delay; } + */ } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 68a3841244..e288b67c86 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -400,11 +400,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendMemory(ushort addr) { + /* if (IsContended(addr)) { var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); CPU.TotalExecutedCycles += delay; } + */ } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index 1d9c7edc0b..c17d90fe26 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -98,8 +98,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { + /* if (IsContended(addr)) CPU.TotalExecutedCycles += ULADevice.GetContentionValue((int)CurrentFrameCycle); + */ var data = ReadBus(addr); return data; @@ -113,12 +115,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { + /* // apply contention if necessary if (IsContended(addr)) { ULADevice.RenderScreen((int)CurrentFrameCycle); CPU.TotalExecutedCycles += ULADevice.GetContentionValue((int)CurrentFrameCycle); } + */ WriteBus(addr, value); } 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 7dd6f3cd21..2006f35c02 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -122,6 +122,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void ContendMemory(ushort addr) { + return; + /* if (IsContended(addr)) { var off = 1; @@ -138,6 +140,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } CPU.TotalExecutedCycles += delay; } + */ } /// From 1125ccedc3792d8f0ede6502e6340997508b0f86 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 4 Jun 2018 07:52:48 -0400 Subject: [PATCH 250/339] z80: fix a bug --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 25e2f06528..1a37a84f9d 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -507,7 +507,7 @@ IDLE, IDLE, WAIT, - WR, dest_l, dest_h, 1, src_l, + WR, dest_l, dest_h, src_l, TR16, src_l, src_h, Z, W, WAIT, OP_F, From 9f4b6f1ecf19f2a74d84fb83fd09eeace38eddd5 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 4 Jun 2018 08:57:12 -0400 Subject: [PATCH 251/339] z80: Bus timing on interrupts --- .../CPUs/Z80A/Interrupts.cs | 48 +++++++++++-------- .../CPUs/Z80A/Registers.cs | 7 +++ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 648590c92b..442fe109f6 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -34,16 +34,18 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {DEC16, SPl, SPh, + TR, ALU, PCl, WAIT, - WR, SPl, SPh, PCh, - DEC16, SPl, SPh, + WR_DEC, SPl, SPh, PCh, + TR16, PCl, PCh, NMI_V, ZERO, WAIT, - WR, SPl, SPh, PCl, - ASGN, PCl, 0x66, - ASGN, PCh, 0, + WR, SPl, SPh, ALU, + IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; } // Mode 0 interrupts only take effect if a CALL or RST is on the data bus @@ -56,55 +58,61 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, WAIT, - RD, ALU, PCl, PCh, - INC16, PCl, PCh, + RD_INC, ALU, PCl, PCh, + IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, 0 }; } // Just jump to $0038 private void INTERRUPT_1() { cur_instr = new ushort[] - {DEC16, SPl, SPh, - WAIT, - WR, SPl, SPh, PCh, + {IDLE, + ASGN, ALU, PCl, DEC16, SPl, SPh, - WAIT, - WR, SPl, SPh, PCl, - ASGN, PCl, 0x38, IDLE, - ASGN, PCh, 0, + WAIT, + WR_DEC, SPl, SPh, PCh, + TR16, PCl, PCh, IRQ_V, ZERO, + WAIT, + WR, SPl, SPh, ALU, IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, 0, 0, SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; } // Interrupt mode 2 uses the I vector combined with a byte on the data bus private void INTERRUPT_2() { cur_instr = new ushort[] - {IDLE, - WAIT, - FTCH_DB, - TR16, Z, W, DB, I, + {FTCH_DB, + IDLE, DEC16, SPl, SPh, + TR16, Z, W, DB, I, WAIT, - WR, SPl, SPh, PCh, - DEC16, SPl, SPh, + WR_DEC, SPl, SPh, PCh, + IDLE, WAIT, WR, SPl, SPh, PCl, IDLE, WAIT, RD_INC, PCl, Z, W, IDLE, + WAIT, RD, PCh, Z, W, IDLE, WAIT, OP_F, OP }; + + BUSRQ = new ushort[] { 0, 0, 0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, Z, 0 ,0 ,PCl, 0, 0, 0 }; } private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index 0e8dde3435..b1fd71d5b9 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -40,6 +40,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public ushort H_s = 30; public ushort L_s = 31; public ushort DB = 32; + public ushort IRQ_V = 34; // IRQ mode 1 vector + public ushort NMI_V = 35; // NMI vector public ushort[] Regs = new ushort[36]; @@ -112,6 +114,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Regs[i] = 0; } + // the IRQ1 vector is 0x38 + Regs[IRQ_V] = 0x38; + // The NMI vector is constant 0x66 + Regs[NMI_V] = 0x66; + FlagI = false; FlagW = false; } From 2661b0804cfd8f5d0ac67af66e9c7b4cb74a998a Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 4 Jun 2018 11:15:26 -0400 Subject: [PATCH 252/339] z80: fix a typo --- BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 442fe109f6..a84fc640c0 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -72,7 +72,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - ASGN, ALU, PCl, + TR, ALU, PCl, DEC16, SPl, SPh, IDLE, WAIT, From bff3f41c9af3114b48d165d4dd316661f0435493 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 4 Jun 2018 11:42:59 -0400 Subject: [PATCH 253/339] z80: IN/OUT access fixes --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs | 10 +++++----- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index c4ab95abf7..4307f34705 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -523,10 +523,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, RD_INC, Z, PCl, PCh, IDLE, + IDLE, TR, W, A, OUT, Z, W, A, - INC16, Z, W, - IDLE, + INC16, Z, W, WAIT, OP_F, OP}; @@ -538,10 +538,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + IDLE, TR16, Z, W, C, B, OUT, Z, W, src, - INC16, Z, W, - IDLE, + INC16, Z, W, WAIT, OP_F, OP}; @@ -556,10 +556,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, RD_INC, Z, PCl, PCh, IDLE, + IDLE, TR, W, A, IN, A, Z, W, INC16, Z, W, - IDLE, WAIT, OP_F, OP}; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 1a37a84f9d..0189229958 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -380,15 +380,15 @@ IDLE, ADDS, Z, W, ALU, ZERO, IDLE, + IDLE, WAIT, RD, ALU, Z, W, - operation, dest, ALU, - IDLE, + operation, dest, ALU, WAIT, OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src) @@ -410,7 +410,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, Z, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; } private void LD_OP_R(ushort operation, ushort repeat_instr) From 7dfd19de2b6b1e50ebe770aa907895f7c3c46672 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 4 Jun 2018 17:11:12 +0100 Subject: [PATCH 254/339] ZXHawk: Fix memory contention lookup overflow --- .../Computers/SinclairSpectrum/Machine/CPUMonitor.cs | 2 +- .../Computers/SinclairSpectrum/Machine/ULA.cs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index e345c44c4f..8cfd43c657 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -109,7 +109,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } if (_machine.IsContended(addr)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue(); + _cpu.TotalExecutedCycles += _machine.ULADevice.GetContentionValue((int)(_machine.CurrentFrameCycle + 1)); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 72bd3bc3ba..59abd58290 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -752,7 +752,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int GetContentionValue() { - return RenderingTable.Renderer[_machine.CurrentFrameCycle].ContentionValue; + var f = _machine.CurrentFrameCycle; + if (f >= FrameCycleLength) + f -= FrameCycleLength; + + return RenderingTable.Renderer[f].ContentionValue; } /// From beae64d563beaff5a1d981c9d24eead29275fc82 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Mon, 4 Jun 2018 14:27:57 -0400 Subject: [PATCH 255/339] z80: use high byte BUSRQ vector since PCl = 0 so the look up fails (oops) --- .../CPUs/Z80A/Interrupts.cs | 8 +- .../CPUs/Z80A/Tables_Direct.cs | 84 +++++++++---------- .../CPUs/Z80A/Tables_Indirect.cs | 54 ++++++------ BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 2 +- .../SinclairSpectrum/Machine/CPUMonitor.cs | 69 ++++++++------- 5 files changed, 108 insertions(+), 109 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index a84fc640c0..9b1426505c 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -45,7 +45,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } // Mode 0 interrupts only take effect if a CALL or RST is on the data bus @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; } // Just jump to $0038 @@ -85,7 +85,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, 0, 0, SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } // Interrupt mode 2 uses the I vector combined with a byte on the data bus @@ -112,7 +112,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, 0, 0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, Z, 0 ,0 ,PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0 ,0 ,PCh, 0, 0, 0 }; } private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 4307f34705..a41949ee0c 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -15,7 +15,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCl, 0, 0, 0 }; + BUSRQ = new ushort[] {PCh, 0, 0, 0 }; } // NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers @@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void EXX_() @@ -39,7 +39,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } // this exchanges 2 16 bit registers @@ -51,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void INC_16(ushort src_l, ushort src_h) @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, 0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {0, 0, PCh, 0, 0, 0}; } @@ -78,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, 0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {0, 0, PCh, 0, 0, 0}; } // this is done in two steps technically, but the flags don't work out using existing funcitons @@ -98,7 +98,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] {0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP(ushort operation, ushort dest, ushort src) @@ -109,7 +109,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCl, 0, 0, 0 }; + BUSRQ = new ushort[] {PCh, 0, 0, 0 }; } // Operations using the I and R registers take one T-cycle longer @@ -122,7 +122,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] {0, PCh, 0, 0, 0 }; } // note: do not use DEC here since no flags are affected by this operation @@ -145,7 +145,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCl, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {0, PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0}; } else { @@ -159,7 +159,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCl, 0, 0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {0, PCh, 0, 0, PCh, 0, 0, 0}; } } @@ -171,7 +171,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IDLE, HALT }; - BUSRQ = new ushort[] {PCl, 0, 0, 0 }; + BUSRQ = new ushort[] {PCh, 0, 0, 0 }; } private void JR_COND(bool cond) @@ -192,7 +192,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCl, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0}; } else { @@ -205,7 +205,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, 0}; } } @@ -225,7 +225,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, Z, 0, 0, 0}; + BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0}; } else { @@ -241,7 +241,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0}; } } @@ -259,7 +259,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; + BUSRQ = new ushort[] {SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; } private void RETI_() @@ -276,7 +276,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; + BUSRQ = new ushort[] {SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; } private void RETN_() @@ -293,7 +293,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; + BUSRQ = new ushort[] {SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; } @@ -314,7 +314,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; + BUSRQ = new ushort[] {0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; } else { @@ -325,7 +325,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] {0, PCh, 0, 0, 0}; } } @@ -352,7 +352,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCl, 0, 0, PCl, 0, 0, 0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0}; + BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; } else { @@ -368,7 +368,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -380,7 +380,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void BIT_OP(ushort operation, ushort bit, ushort src) @@ -391,7 +391,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void PUSH_(ushort src_l, ushort src_h) @@ -409,7 +409,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } @@ -427,7 +427,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { SPl, 0, 0, SPl, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } private void RST_(ushort n) @@ -445,7 +445,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, SPl, 0, 0, SPl, 0, 0, Z, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void PREFIX_(ushort src) @@ -456,7 +456,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, PREFIX, src}; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void PREFETCH_(ushort src_l, ushort src_h) @@ -467,7 +467,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, PREFIX, IXYprefetch }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void DI_() @@ -478,7 +478,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void EI_() @@ -489,7 +489,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void JP_16(ushort src_l, ushort src_h) @@ -500,7 +500,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { src_l, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, 0 }; } private void LD_SP_16(ushort src_l, ushort src_h) @@ -513,7 +513,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, 0, src_l, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, src_h, 0, 0, 0 }; } private void OUT_() @@ -531,7 +531,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0 ,0, PCl, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0 ,0, PCh, 0, 0, 0}; } private void OUT_REG_(ushort dest, ushort src) @@ -546,7 +546,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void IN_() @@ -564,7 +564,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void IN_REG_(ushort dest, ushort src) @@ -579,7 +579,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -597,7 +597,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void INT_MODE_(ushort src) @@ -608,7 +608,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } private void RRD_() @@ -629,7 +629,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, Z, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, W, 0, 0, 0, PCh, 0, 0, 0 }; } private void RLD_() @@ -650,7 +650,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, Z, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, W, 0, 0, 0, PCh, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 0189229958..b26802d32e 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -17,7 +17,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { src_l, 0, 0, src_l, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, 0, PCh, 0, 0, 0 }; } private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) @@ -35,7 +35,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } // Note that this operation uses I_BIT, same as indexed BIT. @@ -54,7 +54,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -68,7 +68,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -82,7 +82,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_16_IND_nn(ushort src_l, ushort src_h) @@ -105,7 +105,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_16_nn(ushort dest_l, ushort dest_h) @@ -128,7 +128,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND_nn(ushort src) @@ -148,7 +148,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_8_nn(ushort dest) @@ -168,7 +168,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, PCl, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src) @@ -182,7 +182,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { dest_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -199,7 +199,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { src_l, 0, 0, dest_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h) @@ -213,7 +213,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -230,7 +230,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void INC_8_IND(ushort src_l, ushort src_h) @@ -248,7 +248,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void DEC_8_IND(ushort src_l, ushort src_h) @@ -266,7 +266,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, src_l, 0, 0, src_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } // NOTE: WZ implied for the wollowing 3 functions @@ -285,7 +285,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_OP(ushort operation, ushort bit, ushort dest) @@ -303,7 +303,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_TE(ushort bit) @@ -318,7 +318,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_OP_n(ushort operation, ushort src_l, ushort src_h) @@ -344,7 +344,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_OP_n_n(ushort src_l, ushort src_h) @@ -366,7 +366,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, PCl, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_REG_OP_IND_n(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -388,7 +388,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src) @@ -410,7 +410,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCh, 0, 0, 0 }; } private void LD_OP_R(ushort operation, ushort repeat_instr) @@ -429,7 +429,7 @@ OP_F, OP_R, 0, operation, repeat_instr }; - BUSRQ = new ushort[] { L, 0, 0, E, 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void CP_OP_R(ushort operation, ushort repeat_instr) @@ -448,7 +448,7 @@ OP_F, OP_R, 1, operation, repeat_instr }; - BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void IN_OP_R(ushort operation, ushort repeat_instr) @@ -467,7 +467,7 @@ OP_F, OP_R, 2, operation, repeat_instr }; - BUSRQ = new ushort[] { 0, 0, 0, 0, L, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, 0, 0, H, 0, 0, 0, PCh, 0, 0, 0 }; } private void OUT_OP_R(ushort operation, ushort repeat_instr) @@ -486,7 +486,7 @@ OP_F, OP_R, 3, operation, repeat_instr }; - BUSRQ = new ushort[] { L, 0, 0, 0, 0, 0, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } // this is an indirect change of a a 16 bit register with memory @@ -513,7 +513,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { dest_l, 0, 0, 0, dest_l, 0, 0, dest_l, 0, 0, 0, 0, dest_l, 0, 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { dest_h, 0, 0, 0, dest_h, 0, 0, dest_h, 0, 0, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 5291fb597a..051b727a0d 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -266,7 +266,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, PCh, 0, 0, 0 }; instr_pntr = 0; bus_pntr = 0; // adjust WZ register accordingly diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 8cfd43c657..00f581c7aa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -27,10 +27,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { get { - if (_cpu.bus_pntr < _cpu.BUSRQ.Length - 1) - return _cpu.BUSRQ[_cpu.bus_pntr]; + //if (_cpu.bus_pntr < _cpu.BUSRQ.Length - 1) + return _cpu.BUSRQ[_cpu.bus_pntr]; - return 0; + //return 0; } } @@ -63,48 +63,47 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (BUSRQ) { - // PCl - case 0: + // PCh + case 1: addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8); break; - // Z - case 13: - addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8); - break; - // SPl - case 2: + // SPh + case 3: addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8); break; - // L - case 11: + // 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; - // I - case 21: - addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8); - break; - // Ixl - case 15: + // 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; - // Iyl - case 17: + // Iyh + case 18: addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 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; - - default: + // I + case 21: + addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8); + break; + default: break; } From f764c137ee924690de9142681bcc0d407776f773 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 5 Jun 2018 17:14:37 +0100 Subject: [PATCH 256/339] 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; From 6752a173215815e84145068150f2c3967841b152 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 5 Jun 2018 12:52:10 -0400 Subject: [PATCH 257/339] z80: clean up prefetch implementation --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 26 ++--------------- .../CPUs/Z80A/Tables_Direct.cs | 29 ++++++++++++++----- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 4 +-- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index 8feed65cbe..987c876bc5 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -858,7 +858,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0xC8: RET_COND(FlagZ); break; // RET Z case 0xC9: RET_(); break; // RET case 0xCA: JP_COND(FlagZ); break; // JP Z - case 0xCB: PREFIX_(IXCBpre); break; // PREFIX IXCB + case 0xCB: PREFETCH_(IXCBpre); break; // PREFIX IXCB case 0xCC: CALL_COND(FlagZ); break; // CALL Z case 0xCD: CALL_COND(true); break; // CALL case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n @@ -1123,7 +1123,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0xC8: RET_COND(FlagZ); break; // RET Z case 0xC9: RET_(); break; // RET case 0xCA: JP_COND(FlagZ); break; // JP Z - case 0xCB: PREFIX_(IYCBpre); break; // PREFIy IyCB + case 0xCB: PREFETCH_(IYCBpre); break; // PREFIX IyCB case 0xCC: CALL_COND(FlagZ); break; // CALL Z case 0xCD: CALL_COND(true); break; // CALL case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n @@ -1183,28 +1183,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // the first byte fetched is the prefetch value to use with the instruction // we pick Ix or Iy here, the indexed value is stored in WZ // In this way, we don't need to pass them as an argument to the I_Funcs. - if (IXCB_prefetch) - { - IXCB_prefetch = false; - PF = opcode; - Regs[ALU] = PF; - Regs[W] = Regs[Ixh]; - Regs[Z] = Regs[Ixl]; - PREFETCH_(Ixl, Ixh); - return; - } - - if (IYCB_prefetch) - { - IYCB_prefetch = false; - PF = opcode; - Regs[ALU] = PF; - Regs[W] = Regs[Iyh]; - Regs[Z] = Regs[Iyl]; - PREFETCH_(Iyl, Iyh); - return; - } - IXCB_prefix = false; IYCB_prefix = false; NO_prefix = true; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index a41949ee0c..240d2ce06f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -184,15 +184,15 @@ namespace BizHawk.Emulation.Cores.Components.Z80A RD_INC, Z, PCl, PCh, IDLE, ASGN, W, 0, + IDLE, + IDLE, ADDS, PCl, PCh, Z, W, TR16, Z, W, PCl, PCh, - IDLE, - IDLE, WAIT, OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] {PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0}; } else { @@ -459,15 +459,30 @@ namespace BizHawk.Emulation.Cores.Components.Z80A BUSRQ = new ushort[] { PCh, 0, 0, 0 }; } - private void PREFETCH_(ushort src_l, ushort src_h) + private void PREFETCH_(ushort src) { + if (src == IXCBpre) + { + Regs[W] = Regs[Ixh]; + Regs[Z] = Regs[Ixl]; + } + else + { + Regs[W] = Regs[Iyh]; + Regs[Z] = Regs[Iyl]; + } + cur_instr = new ushort[] - {ADDS, Z, W, ALU, ZERO, + {IDLE, + WAIT, + RD_INC, ALU, PCl, PCh, + ADDS, Z, W, ALU, ZERO, WAIT, OP_F, - PREFIX, IXYprefetch }; + IDLE, + PREFIX, src,}; - BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0 }; } private void DI_() diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 051b727a0d..5617d14940 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -561,8 +561,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (prefix_src == EXTDpre) { EXTD_prefix = true; } if (prefix_src == IXpre) { IX_prefix = true; } if (prefix_src == IYpre) { IY_prefix = true; } - if (prefix_src == IXCBpre) { IXCB_prefix = true; IXCB_prefetch = true; } - if (prefix_src == IYCBpre) { IYCB_prefix = true; IYCB_prefetch = true; } + if (prefix_src == IXCBpre) { IXCB_prefix = true; } + if (prefix_src == IYCBpre) { IYCB_prefix = true; } RegPC++; FetchInstruction(); From 978a93b6811cb1302a6a038a6bc742c80618e05d Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 5 Jun 2018 15:05:48 -0400 Subject: [PATCH 258/339] z80: more memory contention timing work (up to test 23) --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs | 14 +++++++------- .../CPUs/Z80A/Tables_Indirect.cs | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 240d2ce06f..55f08e4905 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -352,7 +352,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, PCh, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; } else { @@ -409,7 +409,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } @@ -445,7 +445,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void PREFIX_(ushort src) @@ -528,7 +528,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, 0, src_h, 0, 0, 0 }; + BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0 }; } private void OUT_() @@ -612,7 +612,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, I, I, I, I, I, I, PCh, 0, 0, 0 }; } private void INT_MODE_(ushort src) @@ -644,7 +644,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, W, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 }; } private void RLD_() @@ -665,7 +665,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, W, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index b26802d32e..4e6ce7adb2 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -350,15 +350,15 @@ private void I_OP_n_n(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, + {TR16, Z, W, src_l, src_h, WAIT, RD_INC, ALU, PCl, PCh, - IDLE, - TR16, Z, W, src_l, src_h, ADDS, Z, W, ALU, ZERO, WAIT, RD_INC, ALU, PCl, PCh, IDLE, + IDLE, + IDLE, WAIT, WR, Z, W, ALU, IDLE, @@ -366,7 +366,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 }; } private void I_REG_OP_IND_n(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -388,7 +388,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 }; } private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src) @@ -410,7 +410,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, Z, 0, 0, PCh, 0, 0, 0 }; } private void LD_OP_R(ushort operation, ushort repeat_instr) From 620c4b3c2e50d0aaa478aeba0ba6d498f6fd3dc0 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 5 Jun 2018 18:04:19 -0400 Subject: [PATCH 259/339] z80:more contention work, only IN/OUT to go --- .../CPUs/Z80A/Tables_Direct.cs | 6 +- .../CPUs/Z80A/Tables_Indirect.cs | 54 ++-- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 238 ++++++++---------- 3 files changed, 127 insertions(+), 171 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 55f08e4905..09342f9922 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -145,7 +145,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] {I, PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0}; } else { @@ -159,7 +159,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCh, 0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] {I, PCh, 0, 0, PCh, 0, 0, 0}; } } @@ -482,7 +482,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IDLE, PREFIX, src,}; - BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh }; } private void DI_() diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 4e6ce7adb2..5ac492c6f1 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -292,9 +292,9 @@ { cur_instr = new ushort[] {IDLE, - IDLE, WAIT, RD, ALU, Z, W, + IDLE, operation, bit, ALU, WAIT, WR, Z, W, ALU, @@ -303,22 +303,22 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_TE(ushort bit) { cur_instr = new ushort[] {IDLE, - IDLE, WAIT, RD, ALU, Z, W, + IDLE, I_BIT, bit, ALU, WAIT, OP_F, OP }; - BUSRQ = new ushort[] { 0, W, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { W, 0, 0, W, PCh, 0, 0, 0 }; } private void I_OP_n(ushort operation, ushort src_l, ushort src_h) @@ -333,10 +333,10 @@ ADDS, Z, W, ALU, ZERO, IDLE, IDLE, - IDLE, WAIT, RD, ALU, Z, W, operation, ALU, + IDLE, WAIT, WR, Z, W, ALU, IDLE, @@ -344,7 +344,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; } private void I_OP_n_n(ushort src_l, ushort src_h) @@ -419,74 +419,58 @@ {IDLE, WAIT, RD, ALU, L, H, - IDLE, + operation, L, H, WAIT, WR, E, D, ALU, - operation, L, H, operation, E, D, - SET_FL_LD, // BC gets decremented in here - WAIT, - OP_F, - OP_R, 0, operation, repeat_instr }; + SET_FL_LD_R, 0, operation, repeat_instr}; - BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, D, D}; } private void CP_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {IDLE, + {IDLE, WAIT, RD, ALU, L, H, operation, L, H, DEC16, C, B, - SET_FL_CP, operation, Z, W, IDLE, - IDLE, - WAIT, - OP_F, - OP_R, 1, operation, repeat_instr }; + SET_FL_CP_R, 1, operation, repeat_instr}; - BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, H }; } private void IN_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, WAIT, IN, ALU, C, B, IDLE, WAIT, WR, L, H, ALU, - REP_OP_I, operation, - IDLE, - WAIT, - OP_F, - OP_R, 2, operation, repeat_instr }; + REP_OP_I, operation, 2, operation, repeat_instr }; - BUSRQ = new ushort[] { 0, 0, 0, 0, H, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, 0, 0, 0, H, 0, 0, 0}; } private void OUT_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {IDLE, + {IDLE, + IDLE, WAIT, RD, ALU, L, H, IDLE, - IDLE, WAIT, OUT, C, B, ALU, - REP_OP_O, operation, - IDLE, - WAIT, - OP_F, - OP_R, 3, operation, repeat_instr }; + REP_OP_O, operation, 3, operation, repeat_instr }; - BUSRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, H, 0, 0, 0, 0, 0, 0}; } // this is an indirect change of a a 16 bit register with memory diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 5617d14940..ea5bcebfe8 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -70,8 +70,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort NEG = 55; public const ushort RRD = 56; public const ushort RLD = 57; - public const ushort SET_FL_LD = 58; - public const ushort SET_FL_CP = 59; + public const ushort SET_FL_LD_R = 58; + public const ushort SET_FL_CP_R = 59; public const ushort SET_FL_IR = 60; public const ushort I_BIT = 61; public const ushort HL_BIT = 62; @@ -102,7 +102,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCl, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; instr_pntr = 0; bus_pntr = 0; NO_prefix = true; } @@ -231,134 +231,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); break; case OP_R: - // determine if we repeat based on what operation we are doing - // single execution versions also come here, but never repeat - ushort temp1 = cur_instr[instr_pntr++]; - ushort temp2 = cur_instr[instr_pntr++]; - ushort temp3 = cur_instr[instr_pntr++]; - - bool repeat = false; - int Reg16_d = Regs[C] | (Regs[B] << 8); - switch (temp1) - { - case 0: - repeat = Reg16_d != 0; - break; - case 1: - repeat = (Reg16_d != 0) && !FlagZ; - break; - case 2: - repeat = Regs[B] != 0; - break; - case 3: - repeat = Regs[B] != 0; - break; - } - - // if we repeat, we do a 5 cycle refresh which decrements PC by 2 - // if we don't repeat, continue on as a normal opcode fetch - if (repeat && temp3 > 0) - { - cur_instr = new ushort[] - {DEC16, PCl, PCh, - DEC16, PCl, PCh, - WAIT, - OP_F, - OP }; - - BUSRQ = new ushort[] { 0, PCh, 0, 0, 0 }; - - instr_pntr = 0; bus_pntr = 0; - // adjust WZ register accordingly - switch (temp1) - { - case 0: - // TEST: PC before or after the instruction? - Regs[Z] = Regs[PCl]; - Regs[W] = Regs[PCh]; - INC16_Func(Z, W); - break; - case 1: - // TEST: PC before or after the instruction? - Regs[Z] = Regs[PCl]; - Regs[W] = Regs[PCh]; - INC16_Func(Z, W); - break; - case 2: - // Nothing - break; - case 3: - // Nothing - break; - } - } - else - { - // Interrupts can occur at this point, so process them accordingly - // Read the opcode of the next instruction - if (EI_pending > 0) - { - EI_pending--; - if (EI_pending == 0) { IFF1 = IFF2 = true; } - } - - // Process interrupt requests. - if (nonMaskableInterruptPending) - { - nonMaskableInterruptPending = false; - - if (TraceCallback != null) - { - TraceCallback(new TraceInfo{Disassembly = "====NMI====", RegisterInfo = ""}); - } - - iff2 = iff1; - iff1 = false; - NMI_(); - NMICallback(); - instr_pntr = 0; bus_pntr = 0; - } - else if (iff1 && FlagI) - { - iff1 = iff2 = false; - EI_pending = 0; - - if (TraceCallback != null) - { - TraceCallback(new TraceInfo{Disassembly = "====IRQ====", RegisterInfo = ""}); - } - - switch (interruptMode) - { - case 0: - // Requires something to be pushed onto the data bus - // we'll assume it's a zero for now - INTERRUPT_0(0); - break; - case 1: - INTERRUPT_1(); - break; - case 2: - INTERRUPT_2(); - break; - } - IRQCallback(); - instr_pntr = 0; bus_pntr = 0; - } - else - { - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null) TraceCallback(State()); - RegPC++; - FetchInstruction(); - instr_pntr = 0; bus_pntr = 0; - } - - temp_R = (byte)(Regs[R] & 0x7F); - temp_R++; - temp_R &= 0x7F; - Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); - } + break; case HALT: @@ -608,12 +481,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case RLD: RLD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; - case SET_FL_LD: + case SET_FL_LD_R: DEC16_Func(C, B); SET_FL_LD_Func(); + Repeat_Op(); break; - case SET_FL_CP: + case SET_FL_CP_R: SET_FL_CP_Func(); + Repeat_Op(); break; case SET_FL_IR: SET_FL_IR_Func(cur_instr[instr_pntr++]); @@ -651,6 +526,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INC16_Func(Z, W); DEC8_Func(B); } + Repeat_Op(); break; case REP_OP_O: ushort temp5 = cur_instr[instr_pntr++]; @@ -668,11 +544,107 @@ namespace BizHawk.Emulation.Cores.Components.Z80A TR16_Func(Z, W, C, B); INC16_Func(Z, W); } + Repeat_Op(); break; } TotalExecutedCycles++; } + public void Repeat_Op() + { + // determine if we repeat based on what operation we are doing + // single execution versions also come here, but never repeat + ushort temp1 = cur_instr[instr_pntr++]; + ushort temp2 = cur_instr[instr_pntr++]; + ushort temp3 = cur_instr[instr_pntr++]; + + bool repeat = false; + int Reg16_d = Regs[C] | (Regs[B] << 8); + switch (temp1) + { + case 0: + repeat = Reg16_d != 0; + break; + case 1: + repeat = (Reg16_d != 0) && !FlagZ; + break; + case 2: + repeat = Regs[B] != 0; + break; + case 3: + repeat = Regs[B] != 0; + break; + } + + // if we repeat, we do a 5 cycle refresh which decrements PC by 2 + // if we don't repeat, continue on as a normal opcode fetch + if (repeat && temp3 > 0) + { + cur_instr = new ushort[] + {DEC16, PCl, PCh, + DEC16, PCl, PCh, + IDLE, + IDLE, + IDLE, + IDLE, + WAIT, + OP_F, + OP}; + + if (temp1 == 0) + { + BUSRQ = new ushort[] { D, D, D, D, D, PCh, 0, 0, 0 }; + } + else if (temp1 == 1) + { + BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + } + else if (temp1 == 2) + { + BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + } + else if (temp1 == 3) + { + BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; + } + + instr_pntr = 0; bus_pntr = 0; + // adjust WZ register accordingly + switch (temp1) + { + case 0: + // TEST: PC before or after the instruction? + Regs[Z] = Regs[PCl]; + Regs[W] = Regs[PCh]; + INC16_Func(Z, W); + break; + case 1: + // TEST: PC before or after the instruction? + Regs[Z] = Regs[PCl]; + Regs[W] = Regs[PCh]; + INC16_Func(Z, W); + break; + case 2: + // Nothing + break; + case 3: + // Nothing + break; + } + } + else + { + cur_instr = new ushort[] + { IDLE, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + instr_pntr = 0; bus_pntr = 0; + } + } + // tracer stuff public Action TraceCallback; From 74e6f630c3322b67037c3611906752ded53bbdda Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 6 Jun 2018 07:33:49 -0400 Subject: [PATCH 260/339] z80: IO port re-work and contention --- .../CPUs/Z80A/Registers.cs | 12 +++++++++ .../CPUs/Z80A/Tables_Direct.cs | 26 +++++++++---------- .../CPUs/Z80A/Tables_Indirect.cs | 12 ++++----- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 21 +++++++-------- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index b1fd71d5b9..d7c7cbe90f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -45,6 +45,18 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public ushort[] Regs = new ushort[36]; + // IO Contention Constants. Need to distinguish port access and normal memory accesses for zx spectrum + public const ushort BIO1 = 100; + public const ushort BIO2 = 101; + public const ushort BIO3 = 102; + public const ushort BIO4 = 103; + + public const ushort WIO1 = 105; + public const ushort WIO2 = 106; + public const ushort WIO3 = 107; + public const ushort WIO4 = 108; + + public bool FlagI; public bool FlagW; // wait flag, when set to true reads / writes will be delayed diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 09342f9922..4b4b5e50d6 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -534,19 +534,19 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void OUT_() { cur_instr = new ushort[] - {IDLE, + {TR, W, A, WAIT, RD_INC, Z, PCl, PCh, IDLE, - IDLE, - TR, W, A, + WAIT, + WAIT, OUT, Z, W, A, INC16, Z, W, WAIT, OP_F, OP}; - BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0 ,0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0}; } private void OUT_REG_(ushort dest, ushort src) @@ -561,40 +561,40 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; } private void IN_() { cur_instr = new ushort[] - {IDLE, + {TR, W, A, WAIT, RD_INC, Z, PCl, PCh, IDLE, - IDLE, - TR, W, A, + WAIT, + WAIT, IN, A, Z, W, INC16, Z, W, WAIT, OP_F, OP}; - BUSRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; } private void IN_REG_(ushort dest, ushort src) { cur_instr = new ushort[] - {IDLE, - TR16, Z, W, C, B, + {TR16, Z, W, C, B, WAIT, - IN, dest, src, B, + WAIT, + IN, dest, Z, W, INC16, Z, W, WAIT, OP_F, OP}; - BUSRQ = new ushort[] { 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; } private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 5ac492c6f1..b8f3505fa4 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -447,15 +447,15 @@ { cur_instr = new ushort[] {IDLE, + IDLE, WAIT, WAIT, IN, ALU, C, B, IDLE, WAIT, - WR, L, H, ALU, - REP_OP_I, operation, 2, operation, repeat_instr }; + REP_OP_I, L, H, ALU, operation, 2, operation, repeat_instr }; - BUSRQ = new ushort[] { I, 0, 0, 0, H, 0, 0, 0}; + BUSRQ = new ushort[] { I, BIO1, BIO2, BIO3, BIO4, H, 0, 0}; } private void OUT_OP_R(ushort operation, ushort repeat_instr) @@ -467,10 +467,10 @@ RD, ALU, L, H, IDLE, WAIT, - OUT, C, B, ALU, - REP_OP_O, operation, 3, operation, repeat_instr }; + WAIT, + REP_OP_O, C, B, ALU, operation, 3, operation, repeat_instr }; - BUSRQ = new ushort[] { I, H, 0, 0, 0, 0, 0, 0}; + BUSRQ = new ushort[] { I, H, 0, 0, BIO1, BIO2, BIO3, BIO4 }; } // this is an indirect change of a a 16 bit register with memory diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index ea5bcebfe8..34ec1bb5ff 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // operations that can take place in an instruction public const ushort IDLE = 0; public const ushort OP = 1; - public const ushort OP_R = 2; // used for repeating operations + public const ushort OP_F = 2; // used for repeating operations public const ushort HALT = 3; public const ushort RD = 4; public const ushort WR = 5; @@ -77,10 +77,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort HL_BIT = 62; public const ushort FTCH_DB = 63; public const ushort WAIT = 64; // enterred when readin or writing and FlagW is true - public const ushort OP_F = 65; // fetch the opcode, happens on cycle 3 of fetch cycle - public const ushort RST = 66; - public const ushort REP_OP_I = 67; - public const ushort REP_OP_O = 68; + public const ushort RST = 65; + public const ushort REP_OP_I = 66; + public const ushort REP_OP_O = 67; public byte temp_R; @@ -230,10 +229,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); break; - case OP_R: - + case OP_F: + opcode = FetchMemory(RegPC); break; - case HALT: halted = true; // NOTE: Check how halt state effects the DB @@ -503,14 +501,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A bus_pntr--; } break; - case OP_F: - opcode = FetchMemory(RegPC); - break; case RST: Regs[Z] = cur_instr[instr_pntr++]; Regs[W] = 0; break; case REP_OP_I: + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + ushort temp4 = cur_instr[instr_pntr++]; if (temp4 == DEC16) { @@ -529,6 +526,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Repeat_Op(); break; case REP_OP_O: + OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + ushort temp5 = cur_instr[instr_pntr++]; if (temp5 == DEC16) { From 7f8084d4104015b749affd189ec15f64fe6a6217 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 6 Jun 2018 14:39:41 +0100 Subject: [PATCH 261/339] ZXHawk: IORQ check now happens pre T-Cycle --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 316 ++++++------------ .../Computers/SinclairSpectrum/Machine/ULA.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 2 +- 3 files changed, 111 insertions(+), 209 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 1623679213..8ac36b391a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -67,14 +67,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _machine.ULADevice.RenderScreen((int)_machine.CurrentFrameCycle); - if (portContending) + // is the next CPU cycle causing a BUSRQ or IORQ? + if (BUSRQ > 0) { - RunPortContention(); - } - else - { - // is the next CPU cycle causing a BUSRQ? - if (BUSRQ > 0) + // check for IORQ + if (!CheckIO()) { // is the memory address of the BUSRQ potentially contended? if (_machine.IsContended(AscertainBUSRQAddress())) @@ -89,129 +86,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } _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; - } - } - } - } - - // 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--; - } - */ } /// @@ -263,34 +137,36 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case 21: addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8); break; + // BC + case Z80A.BIO1: + case Z80A.BIO2: + case Z80A.BIO3: + case Z80A.BIO4: + addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8); + break; + // WZ + case Z80A.WIO1: + case Z80A.WIO2: + case Z80A.WIO3: + case Z80A.WIO4: + addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8); + break; } return addr; } - + /// - /// Perfors the actual port contention (if necessary) + /// Returns TRUE if the supplied T-cycle within an IO operation has the possibility of being contended + /// This can be different based on the emulated ZX Spectrum model /// - private void RunPortContention() + /// + /// + private bool IsIOCycleContended(int T) { - //return; - bool lowBitSet = false; + bool lowBitSet = (lastPortAddr & 0x0001) != 0; bool highByte407f = false; - int offset = 0; // _machine.ULADevice.contentionOffset; // -5;// 57;// - 10; - var c = _machine.CurrentFrameCycle; - var t = _machine.ULADevice.FrameLength; - int f = (int)c + offset; - if (f >= t) - f = f - t; - else if (f < 0) - f = t + f; - - if ((lastPortAddr & 0x0001) != 0) - lowBitSet = true; - - portContCounter--; - switch (machineType) { case MachineType.ZXSpectrum16: @@ -309,20 +185,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // high byte 40-7f // low bit set // C:1, C:1, C:1, C:1 - switch (portContCounter) + switch (T) { - 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; - break; + case 1: + case 2: + case 3: + case 4: + return true; } } else @@ -330,19 +199,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // high byte 40-7f // low bit reset // C:1, C:3 - switch (portContCounter) + switch (T) { - case 3: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break; - case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break; - case 1: break; - case 0: - portContCounter = 0; - portContending = false; - break; - default: - portContCounter = 0; - portContending = false; - break; + case 1: + case 2: + return true; } } } @@ -353,40 +214,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // high byte not 40-7f // low bit set - // N:4 - switch (portContCounter) - { - case 3: break; - case 2: break; - case 1: break; - case 0: - portContCounter = 0; - portContending = false; - break; - default: - portContCounter = 0; - portContending = false; - break; - } + // N:4 } else { // high byte not 40-7f // low bit reset // N:1, C:3 - switch (portContCounter) + switch (T) { - case 3: break; - case 2: _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue(f); break; - case 1: break; - case 0: - portContCounter = 0; - portContending = false; - break; - default: - portContCounter = 0; - portContending = false; - break; + case 2: + return true; } } } @@ -396,17 +234,81 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case MachineType.ZXSpectrum128Plus3: break; } + + return false; } /// - /// Starts the port contention process + /// Running every cycle, this determines whether the upcoming BUSRQ is for an IO operation + /// Also processes any contention /// - /// - public void ContendPort(ushort port) + /// + private bool CheckIO() { - portContending = true; - portContCounter = 4; - lastPortAddr = port; + bool isIO = false; + + switch (BUSRQ) + { + // BC: T1 + case Z80A.BIO1: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(1)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T2 + case Z80A.BIO2: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(2)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T3 + case Z80A.BIO3: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(3)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T4 + case Z80A.BIO4: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(4)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + + // WZ: T1 + case Z80A.WIO1: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(1)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T2 + case Z80A.WIO2: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(2)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T3 + case Z80A.WIO3: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(3)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T4 + case Z80A.WIO4: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(4)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + } + + return isIO; } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 6a3e7ca04d..345b468cb7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -797,7 +797,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int GetPortContentionValue(int tstate) { - int off = 1; + int off = 5; tstate += off; if (tstate >= FrameCycleLength) tstate -= FrameCycleLength; 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 d4170ffd9e..f6b01cc3be 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -154,7 +154,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum 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. */ - CPUMon.ContendPort(addr); + //CPUMon.ContendPort(addr); return; } From c80f873adf9830f45f7b5522e133795566787ee7 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 6 Jun 2018 15:37:29 +0100 Subject: [PATCH 262/339] ZXHawk: added CPUMonitor syncstate --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 192 +++++++++--------- 1 file changed, 99 insertions(+), 93 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 8ac36b391a..fbdc5d2134 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -1,4 +1,5 @@ -using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Common; +using BizHawk.Emulation.Cores.Components.Z80A; using System; using System.Collections.Generic; using System.Linq; @@ -19,19 +20,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region Lookups - public ushort[] cur_instr => _cpu.cur_instr; - public int instr_pntr => _cpu.instr_pntr; - public ushort RegPC => _cpu.RegPC; + /// + /// CPU total executes t-states + /// public long TotalExecutedCycles => _cpu.TotalExecutedCycles; + + /// + /// Current BUSRQ line array + /// public ushort BUSRQ { - get - { - //if (_cpu.bus_pntr < _cpu.BUSRQ.Length - 1) - return _cpu.BUSRQ[_cpu.bus_pntr]; - - //return 0; - } + get { return _cpu.BUSRQ[_cpu.bus_pntr]; } } #endregion @@ -48,18 +47,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #region State - public bool IsContending = false; - public int ContCounter = -1; - public int portContCounter = 0; - public int portContTotalLen = 0; - public bool portContending = false; + /// + /// The last 16-bit port address that was detected + /// public ushort lastPortAddr; - public int[] portContArr = new int[4]; #endregion #region Methods - + /// /// Handles the ULA and CPU cycle clocks, along with any memory and port contention /// @@ -89,7 +85,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } /// - /// Looks up the BUSRQ address that is about to be signalled + /// Looks up the current BUSRQ address that is about to be signalled on the upcoming cycle /// /// private ushort AscertainBUSRQAddress() @@ -156,6 +152,79 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return addr; } + /// + /// Running every cycle, this determines whether the upcoming BUSRQ is for an IO operation + /// Also processes any contention + /// + /// + private bool CheckIO() + { + bool isIO = false; + + switch (BUSRQ) + { + // BC: T1 + case Z80A.BIO1: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(1)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T2 + case Z80A.BIO2: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(2)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T3 + case Z80A.BIO3: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(3)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T4 + case Z80A.BIO4: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(4)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + + // WZ: T1 + case Z80A.WIO1: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(1)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T2 + case Z80A.WIO2: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(2)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T3 + case Z80A.WIO3: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(3)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T4 + case Z80A.WIO4: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(4)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + } + + return isIO; + } + /// /// Returns TRUE if the supplied T-cycle within an IO operation has the possibility of being contended /// This can be different based on the emulated ZX Spectrum model @@ -236,80 +305,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } return false; - } - - /// - /// Running every cycle, this determines whether the upcoming BUSRQ is for an IO operation - /// Also processes any contention - /// - /// - private bool CheckIO() - { - bool isIO = false; - - switch (BUSRQ) - { - // BC: T1 - case Z80A.BIO1: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(1)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - // BC: T2 - case Z80A.BIO2: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(2)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - // BC: T3 - case Z80A.BIO3: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(3)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - // BC: T4 - case Z80A.BIO4: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(4)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - - // WZ: T1 - case Z80A.WIO1: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(1)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - // WZ: T2 - case Z80A.WIO2: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(2)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - // WZ: T3 - case Z80A.WIO3: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(3)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - // WZ: T4 - case Z80A.WIO4: - lastPortAddr = AscertainBUSRQAddress(); - isIO = true; - if (IsIOCycleContended(4)) - _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); - break; - } - - return isIO; - } + } /// /// Called when the first byte of an instruction is fetched @@ -323,5 +319,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion + #region Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("CPUMonitor"); + ser.Sync("", ref lastPortAddr); + ser.EndSection(); + } + + #endregion } } From 535534a94a9a8af3a3801d7e4044371518b6615c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 6 Jun 2018 15:56:27 +0100 Subject: [PATCH 263/339] ZXHawk: start preparing 128k and +2a/+3 for updating timings --- .../Machine/SpectrumBase.Memory.cs | 5 - .../Machine/SpectrumBase.Port.cs | 2 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 21 ---- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 44 +------ .../ZX128Plus2a.Memory.cs | 24 +--- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 115 +----------------- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 25 +--- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 44 +------ .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 34 +----- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 64 +--------- 10 files changed, 11 insertions(+), 367 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 5a8455bc34..283f12cdb6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -155,11 +155,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return value; } - /// - /// Contends memory if necessary - /// - public abstract void ContendMemory(ushort addr); - /// /// Checks whether supplied address is in a potentially contended bank /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index f71aee74a8..3eba9a46f2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// This method is for 48k and 128k/+2 machines only and should be overridden for other models /// /// - public abstract void ContendPort(ushort addr); + //public abstract void ContendPort(ushort addr); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index df1d5587da..99065d6b45 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -185,8 +185,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - ContendMemory(addr); - var data = ReadBus(addr); return data; } @@ -199,28 +197,9 @@ 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 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render) - ULADevice.RenderScreen((int)CurrentFrameCycle); - - ContendMemory(addr); WriteBus(addr, value); } - /// - /// Contends memory if necessary - /// - public override void ContendMemory(ushort addr) - { - /* - if (IsContended(addr)) - { - var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } - */ - } - /// /// Checks whether supplied address is in a potentially contended bank /// 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 32a42cea0c..ec358785d5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -18,9 +18,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { bool deviceAddressed = true; - // process IO contention - ContendPort(port); - int result = 0xFF; // check AY @@ -53,29 +50,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!deviceAddressed) { // If this is an unused port the floating memory bus should be returned - // Floating bus is read on the previous cycle - long _tStates = CurrentFrameCycle - 1; - - ULADevice.ReadFloatingBus((int)_tStates, ref result); - - /* - // if we are on the top or bottom border return 0xff - if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) - { - result = 0xff; - } - else - { - if (ULADevice.floatingBusTable[_tStates] < 0) - { - result = 0xff; - } - else - { - result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); - } - } - */ + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); } return (byte)result; @@ -88,9 +63,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { - // process IO contention - ContendPort(port); - // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); // get a BitArray of the value byte @@ -146,7 +118,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // store the last OUT byte LastULAOutByte = value; - CPU.TotalExecutedCycles += ULADevice.GetContentionValue(); /* Bit 7 6 5 4 3 2 1 0 @@ -156,9 +127,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - if (ULADevice.BorderColor != (value & BORDER_BIT)) - ULADevice.RenderScreen((int)CurrentFrameCycle); - ULADevice.BorderColor = value & BORDER_BIT; // Buzzer @@ -169,15 +137,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); } } - - /// - /// Contend port if necessary - /// - /// - public override void ContendPort(ushort addr) - { - //CPUMon.ContendPort(addr); - return; - } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index d4c46af37f..aec8326d86 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -341,10 +341,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; } } - - // update ULA screen buffer if necessary - if ((addr & 49152) == 16384 && _render) - ULADevice.RenderScreen((int)CurrentFrameCycle); } /// @@ -355,8 +351,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - ContendMemory(addr); - var data = ReadBus(addr); return data; } @@ -370,6 +364,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override void WriteMemory(ushort addr, byte value) { // update ULA screen buffer if necessary BEFORE T1 write + /* if (!SpecialPagingMode) { if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render) @@ -390,23 +385,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; } } - - ContendMemory(addr); - WriteBus(addr, value); - } - - /// - /// Contends memory if necessary - /// - public override void ContendMemory(ushort addr) - { - /* - if (IsContended(addr)) - { - var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } */ + WriteBus(addr, value); } /// 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 478fab56c3..431444b67d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -18,9 +18,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { bool deviceAddressed = true; - // process IO contention - ContendPort(port); - int result = 0xFF; // check AY @@ -53,102 +50,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!deviceAddressed) { // If this is an unused port the floating memory bus should be returned - // Floating bus is read on the previous cycle - long _tStates = CurrentFrameCycle - 1; - - ULADevice.ReadFloatingBus((int)_tStates, ref result); - /* - // if we are on the top or bottom border return 0xff - if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) - { - result = 0xff; - } - else - { - if (ULADevice.floatingBusTable[_tStates] < 0) - { - result = 0xff; - } - else - { - result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); - } - } - */ + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); } - /* - // Check whether the low bit is reset - // Technically the ULA should respond to every even I/O address - bool lowBitReset = (port & 0x0001) == 0; - - // Kempston joystick input takes priority over all other input - // if this is detected just return the kempston byte - if ((port & 0xe0) == 0 || (port & 0x20) == 0) - { - if (LocateUniqueJoystick(JoystickType.Kempston) != null) - return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; - - InputRead = true; - } - else if (lowBitReset) - { - // Even I/O address so get input from keyboard - KeyboardDevice.ReadPort(port, ref result); - - TapeDevice.MonitorRead(); - - // not a lagframe - InputRead = true; - - // tape loading monitor cycle - //TapeDevice.MonitorRead(); - - // process tape INs - TapeDevice.ReadPort(port, ref result); - } - else - { - // devices other than the ULA will respond here - // (e.g. the AY sound chip in a 128k spectrum - - // AY register activate - on +3/2a both FFFD and BFFD active AY - if ((port & 0xc002) == 0xc000) - { - result = (int)AYDevice.PortRead(); - } - else if ((port & 0xc002) == 0x8000) - { - result = (int)AYDevice.PortRead(); - } - - // Kempston Mouse - - /* - else if ((port & 0xF002) == 0x2000) //Is bit 12 set and bits 13,14,15 and 1 reset? - { - //result = udpDrive.DiskStatusRead(); - - // disk drive is not yet implemented - return a max status byte for the menu to load - result = 255; - } - else if ((port & 0xF002) == 0x3000) - { - //result = udpDrive.DiskReadByte(); - result = 0; - } - - else if ((port & 0xF002) == 0x0) - { - if (PagingDisabled) - result = 0x1; - else - result = 0xff; - } - *//* - - // if unused port the floating memory bus should be returned (still todo) - } - */ return (byte)result; } @@ -160,9 +63,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { - // process IO contention - ContendPort(port); - // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); // get a BitArray of the value byte @@ -244,9 +144,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - if (ULADevice.BorderColor != (value & BORDER_BIT)) - ULADevice.RenderScreen((int)CurrentFrameCycle); - ULADevice.BorderColor = value & BORDER_BIT; // Buzzer @@ -282,15 +179,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } set { ROMPaged = value; } } - - /// - /// Contend port if necessary - /// - /// - public override void ContendPort(ushort addr) - { - //CPUMon.ContendPort(addr); - return; - } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index e288b67c86..80a9e9888f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -341,10 +341,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; } } - - // update ULA screen buffer if necessary - if ((addr & 49152) == 16384 && _render) - ULADevice.RenderScreen((int)CurrentFrameCycle); } /// @@ -355,8 +351,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - ContendMemory(addr); - var data = ReadBus(addr); return data; } @@ -369,6 +363,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { + /* // update ULA screen buffer if necessary BEFORE T1 write if (!SpecialPagingMode) { @@ -390,23 +385,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; } } - - ContendMemory(addr); - WriteBus(addr, value); - } - - /// - /// Contends memory if necessary - /// - public override void ContendMemory(ushort addr) - { - /* - if (IsContended(addr)) - { - var delay = ULADevice.GetContentionValue((int)CurrentFrameCycle); - CPU.TotalExecutedCycles += delay; - } */ + + WriteBus(addr, value); } /// 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 ef2fad7331..6d8fa99e96 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -18,9 +18,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { bool deviceAddressed = true; - // process IO contention - ContendPort(port); - int result = 0xFF; // check AY @@ -57,30 +54,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!deviceAddressed) { // If this is an unused port the floating memory bus should be returned - // Floating bus is read on the previous cycle - long _tStates = CurrentFrameCycle - 1; - - ULADevice.ReadFloatingBus((int)_tStates, ref result); - - /* - - // if we are on the top or bottom border return 0xff - if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) - { - result = 0xff; - } - else - { - if (ULADevice.floatingBusTable[_tStates] < 0) - { - result = 0xff; - } - else - { - result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); - } - } - */ + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); } return (byte)result; @@ -93,9 +67,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { - // process IO contention - ContendPort(port); - // get a BitArray of the port BitArray portBits = new BitArray(BitConverter.GetBytes(port)); // get a BitArray of the value byte @@ -179,9 +150,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - if (ULADevice.BorderColor != (value & BORDER_BIT)) - ULADevice.RenderScreen((int)CurrentFrameCycle); - ULADevice.BorderColor = value & BORDER_BIT; // Buzzer @@ -217,15 +185,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } set { ROMPaged = value; } } - - /// - /// Contend port if necessary - /// - /// - public override void ContendPort(ushort 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 45745dcd26..9102cfd72e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -96,7 +96,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - ContendMemory(addr); var data = ReadBus(addr); return data; } @@ -108,41 +107,10 @@ 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); - - ContendMemory(addr); + { WriteBus(addr, value); } - /// - /// Contends memory if necessary - /// - public override void ContendMemory(ushort addr) - { - return; - /* - if (IsContended(addr)) - { - var off = 1; - var offset = CurrentFrameCycle + off; - if (offset < 0) - offset += ULADevice.FrameCycleLength; - if (offset >= ULADevice.FrameCycleLength) - offset -= ULADevice.FrameCycleLength; - - var delay = ULADevice.GetContentionValue((int)offset); - if (delay > 0) - { - - } - CPU.TotalExecutedCycles += delay; - } - */ - } - /// /// Checks whether supplied address is in a potentially contended bank /// 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 f6b01cc3be..c500aab222 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -15,9 +15,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadPort(ushort port) { - // process IO contention - ContendPort(port); - int result = 0xFF; // Check whether the low bit is reset @@ -56,29 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // If this is an unused port the floating memory bus should be returned - ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); - - /* - // Floating bus is read on the previous cycle - long _tStates = CurrentFrameCycle - 1; - - // if we are on the top or bottom border return 0xff - if ((_tStates < ULADevice.contentionStartPeriod) || (_tStates > ULADevice.contentionEndPeriod)) - { - result = 0xff; - } - else - { - if (ULADevice.floatingBusTable[_tStates] < 0) - { - result = 0xff; - } - else - { - result = ReadBus((ushort)ULADevice.floatingBusTable[_tStates]); - } - } - */ + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); } return (byte)result; @@ -91,9 +66,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WritePort(ushort port, byte value) { - // process IO contention - ContendPort(port); - // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address if ((port & 0x0001) != 0) @@ -124,39 +96,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } - /// - /// Simulates IO port contention based on the supplied address - /// This method is for 48k and 128k/+2 machines only and should be overridden for other models - /// - /// - public override void ContendPort(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. - */ - - //CPUMon.ContendPort(addr); - return; - } - } } From af788ee108679e5eed38651e85de9a20c97cb48d Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 6 Jun 2018 13:55:57 -0400 Subject: [PATCH 264/339] z80: fix IO contention bug --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 4b4b5e50d6..4b6938b4b9 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -561,7 +561,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; } private void IN_() @@ -594,7 +594,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; } private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) From 275092528a5c4b9fed7f6a50dd22a0119c76b212 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 6 Jun 2018 19:34:46 -0400 Subject: [PATCH 265/339] z80: Fix some bugs in flags and WZ register Only thing remaining is INI/IND/INIR/INDR --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 28 ++++++++--------- .../CPUs/Z80A/Operations.cs | 2 +- .../CPUs/Z80A/Registers.cs | 1 + .../CPUs/Z80A/Tables_Indirect.cs | 30 +++++++++++++++++++ BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 29 ++++++++++++++++++ 5 files changed, 75 insertions(+), 15 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index 987c876bc5..3912d5668d 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -104,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x43: REG_OP(TR, B, E); break; // LD B, E case 0x44: REG_OP(TR, B, H); break; // LD B, H case 0x45: REG_OP(TR, B, L); break; // LD B, L - case 0x46: REG_OP_IND(TR, B, L, H); break; // LD B, (HL) + case 0x46: REG_OP_IND_HL(TR, B); break; // LD B, (HL) case 0x47: REG_OP(TR, B, A); break; // LD B, A case 0x48: REG_OP(TR, C, B); break; // LD C, B case 0x49: REG_OP(TR, C, C); break; // LD C, C @@ -112,7 +112,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x4B: REG_OP(TR, C, E); break; // LD C, E case 0x4C: REG_OP(TR, C, H); break; // LD C, H case 0x4D: REG_OP(TR, C, L); break; // LD C, L - case 0x4E: REG_OP_IND(TR, C, L, H); break; // LD C, (HL) + case 0x4E: REG_OP_IND_HL(TR, C); break; // LD C, (HL) case 0x4F: REG_OP(TR, C, A); break; // LD C, A case 0x50: REG_OP(TR, D, B); break; // LD D, B case 0x51: REG_OP(TR, D, C); break; // LD D, C @@ -120,7 +120,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x53: REG_OP(TR, D, E); break; // LD D, E case 0x54: REG_OP(TR, D, H); break; // LD D, H case 0x55: REG_OP(TR, D, L); break; // LD D, L - case 0x56: REG_OP_IND(TR, D, L, H); break; // LD D, (HL) + case 0x56: REG_OP_IND_HL(TR, D); break; // LD D, (HL) case 0x57: REG_OP(TR, D, A); break; // LD D, A case 0x58: REG_OP(TR, E, B); break; // LD E, B case 0x59: REG_OP(TR, E, C); break; // LD E, C @@ -128,7 +128,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x5B: REG_OP(TR, E, E); break; // LD E, E case 0x5C: REG_OP(TR, E, H); break; // LD E, H case 0x5D: REG_OP(TR, E, L); break; // LD E, L - case 0x5E: REG_OP_IND(TR, E, L, H); break; // LD E, (HL) + case 0x5E: REG_OP_IND_HL(TR, E); break; // LD E, (HL) case 0x5F: REG_OP(TR, E, A); break; // LD E, A case 0x60: REG_OP(TR, H, B); break; // LD H, B case 0x61: REG_OP(TR, H, C); break; // LD H, C @@ -136,7 +136,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x63: REG_OP(TR, H, E); break; // LD H, E case 0x64: REG_OP(TR, H, H); break; // LD H, H case 0x65: REG_OP(TR, H, L); break; // LD H, L - case 0x66: REG_OP_IND(TR, H, L, H); break; // LD H, (HL) + case 0x66: REG_OP_IND_HL(TR, H); break; // LD H, (HL) case 0x67: REG_OP(TR, H, A); break; // LD H, A case 0x68: REG_OP(TR, L, B); break; // LD L, B case 0x69: REG_OP(TR, L, C); break; // LD L, C @@ -144,23 +144,23 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x6B: REG_OP(TR, L, E); break; // LD L, E case 0x6C: REG_OP(TR, L, H); break; // LD L, H case 0x6D: REG_OP(TR, L, L); break; // LD L, L - case 0x6E: REG_OP_IND(TR, L, L, H); break; // LD L, (HL) + case 0x6E: REG_OP_IND_HL(TR, L); break; // LD L, (HL) case 0x6F: REG_OP(TR, L, A); break; // LD L, A - case 0x70: LD_8_IND(L, H, B); break; // LD (HL), B - case 0x71: LD_8_IND(L, H, C); break; // LD (HL), C - case 0x72: LD_8_IND(L, H, D); break; // LD (HL), D - case 0x73: LD_8_IND(L, H, E); break; // LD (HL), E - case 0x74: LD_8_IND(L, H, H); break; // LD (HL), H - case 0x75: LD_8_IND(L, H, L); break; // LD (HL), L + case 0x70: LD_8_IND_HL(B); break; // LD (HL), B + case 0x71: LD_8_IND_HL(C); break; // LD (HL), C + case 0x72: LD_8_IND_HL(D); break; // LD (HL), D + case 0x73: LD_8_IND_HL(E); break; // LD (HL), E + case 0x74: LD_8_IND_HL(H); break; // LD (HL), H + case 0x75: LD_8_IND_HL(L); break; // LD (HL), L case 0x76: HALT_(); break; // HALT - case 0x77: LD_8_IND(L, H, A); break; // LD (HL), A + case 0x77: LD_8_IND_HL( A); break; // LD (HL), A case 0x78: REG_OP(TR, A, B); break; // LD A, B case 0x79: REG_OP(TR, A, C); break; // LD A, C case 0x7A: REG_OP(TR, A, D); break; // LD A, D case 0x7B: REG_OP(TR, A, E); break; // LD A, E case 0x7C: REG_OP(TR, A, H); break; // LD A, H case 0x7D: REG_OP(TR, A, L); break; // LD A, L - case 0x7E: REG_OP_IND(TR, A, L, H); break; // LD A, (HL) + case 0x7E: REG_OP_IND_HL(TR, A); break; // LD A, (HL) case 0x7F: REG_OP(TR, A, A); break; // LD A, A case 0x80: REG_OP(ADD8, A, B); break; // ADD A, B case 0x81: REG_OP(ADD8, A, C); break; // ADD A, C diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs index 4ec81fe298..69c6b78a7e 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs @@ -757,7 +757,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagZ = Regs[A] == 0; FlagS = Regs[A] > 127; FlagP = iff2; - Flag5 = (Regs[A] & 0x02) != 0; + Flag5 = (Regs[A] & 0x20) != 0; Flag3 = (Regs[A] & 0x08) != 0; } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index d7c7cbe90f..7f392fbe0c 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -40,6 +40,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public ushort H_s = 30; public ushort L_s = 31; public ushort DB = 32; + public ushort scratch = 33; public ushort IRQ_V = 34; // IRQ mode 1 vector public ushort NMI_V = 35; // NMI vector diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index b8f3505fa4..53b6cdf22d 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -85,6 +85,21 @@ BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } + // different because HL doesn't effect WZ + private void REG_OP_IND_HL(ushort operation, ushort dest) + { + cur_instr = new ushort[] + {IDLE, + WAIT, + RD, ALU, L, H, + operation, dest, ALU, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; + } + private void LD_16_IND_nn(ushort src_l, ushort src_h) { cur_instr = new ushort[] @@ -185,6 +200,21 @@ BUSRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 }; } + // seperate HL needed since it doesn't effect the WZ pair + private void LD_8_IND_HL(ushort src) + { + cur_instr = new ushort[] + {IDLE, + WAIT, + WR, L, H, src, + IDLE, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; + } + private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) { cur_instr = new ushort[] diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 34ec1bb5ff..b4b1834cc9 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -506,6 +506,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Regs[W] = 0; break; case REP_OP_I: + + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); ushort temp4 = cur_instr[instr_pntr++]; @@ -515,14 +517,34 @@ namespace BizHawk.Emulation.Cores.Components.Z80A TR16_Func(Z, W, C, B); DEC16_Func(Z, W); DEC8_Func(B); + + // take care of other flags + // taken from 'undocumented z80 documented' + FlagN = Regs[ALU].Bit(7); + FlagH = FlagC = (Regs[ALU] + (Regs[C] - 1) & 0xFF) > 0xFF; + FlagP = TableParity[((Regs[ALU] + (Regs[C] - 1) & 0xFF) & 7) ^ (Regs[B])]; } else { INC16_Func(L, H); + TR16_Func(Z, W, C, B); INC16_Func(Z, W); DEC8_Func(B); + + //Read_Func(scratch, L, H); + if (opcode == 0xA2) + { + Console.Write(Regs[ALU]); + Console.Write(" "); + } + // take care of other flags + // taken from 'undocumented z80 documented' + FlagN = Regs[ALU].Bit(7); + FlagH = FlagC = (Regs[ALU] + (Regs[W]) & 0xFF) > 0xFF; + FlagP = TableParity[((Regs[ALU] + (Regs[W]) & 0xFF) & 7) ^ Regs[Z]]; } + Repeat_Op(); break; case REP_OP_O: @@ -543,6 +565,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A TR16_Func(Z, W, C, B); INC16_Func(Z, W); } + + // take care of other flags + // taken from 'undocumented z80 documented' + FlagN = Regs[ALU].Bit(7); + FlagH = FlagC = (Regs[ALU] + Regs[L]) > 0xFF; + FlagP = TableParity[((Regs[ALU] + Regs[L]) & 7) ^ (Regs[B])]; + Repeat_Op(); break; } From afd1ba38d8e4bfa1ec3f2a616733d6f4bc77456b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 7 Jun 2018 08:10:39 -0400 Subject: [PATCH 266/339] z80: Fix remaining flags and WZ --- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 25 ++++++++--------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index b4b1834cc9..1aade3839f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -505,9 +505,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Regs[Z] = cur_instr[instr_pntr++]; Regs[W] = 0; break; - case REP_OP_I: - - + case REP_OP_I: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); ushort temp4 = cur_instr[instr_pntr++]; @@ -519,30 +517,23 @@ namespace BizHawk.Emulation.Cores.Components.Z80A DEC8_Func(B); // take care of other flags - // taken from 'undocumented z80 documented' + // taken from 'undocumented z80 documented' and Fuse FlagN = Regs[ALU].Bit(7); - FlagH = FlagC = (Regs[ALU] + (Regs[C] - 1) & 0xFF) > 0xFF; - FlagP = TableParity[((Regs[ALU] + (Regs[C] - 1) & 0xFF) & 7) ^ (Regs[B])]; + FlagH = FlagC = ((Regs[ALU] + Regs[C] - 1) & 0xFF) < Regs[ALU]; + FlagP = TableParity[((Regs[ALU] + Regs[C] - 1) & 7) ^ Regs[B]]; } else { - INC16_Func(L, H); - + INC16_Func(L, H); TR16_Func(Z, W, C, B); INC16_Func(Z, W); DEC8_Func(B); - //Read_Func(scratch, L, H); - if (opcode == 0xA2) - { - Console.Write(Regs[ALU]); - Console.Write(" "); - } // take care of other flags - // taken from 'undocumented z80 documented' + // taken from 'undocumented z80 documented' and Fuse FlagN = Regs[ALU].Bit(7); - FlagH = FlagC = (Regs[ALU] + (Regs[W]) & 0xFF) > 0xFF; - FlagP = TableParity[((Regs[ALU] + (Regs[W]) & 0xFF) & 7) ^ Regs[Z]]; + FlagH = FlagC = ((Regs[ALU] + Regs[C] + 1) & 0xFF) < Regs[ALU]; + FlagP = TableParity[((Regs[ALU] + Regs[C] + 1) & 7) ^ Regs[B]]; } Repeat_Op(); From b0af0e48e0c3d8e5abdb69d7bf1b3654d68f1d09 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 7 Jun 2018 08:34:21 -0400 Subject: [PATCH 267/339] z80: IR contention update --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 4b6938b4b9..1618cd986d 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] {I, I, PCh, 0, 0, 0}; } @@ -78,7 +78,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] {I, I, PCh, 0, 0, 0}; } // this is done in two steps technically, but the flags don't work out using existing funcitons @@ -98,7 +98,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] {I, I, I, I, I, I, I, PCh, 0, 0, 0 }; } private void REG_OP(ushort operation, ushort dest, ushort src) @@ -122,7 +122,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, PCh, 0, 0, 0 }; } // note: do not use DEC here since no flags are affected by this operation @@ -314,7 +314,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] {I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; } else { @@ -325,7 +325,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] {I, PCh, 0, 0, 0}; } } From 843dc7a69a325f71453a12b0543d33282b45249e Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Jun 2018 15:33:58 +0100 Subject: [PATCH 268/339] ZXHawk: Small performance increase (rendering cycle now happens only when it needs to) --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 4 +--- .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 ++ .../Computers/SinclairSpectrum/Machine/ULA.cs | 4 ---- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 2 ++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs | 6 ++++++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 6 +++++- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 8 +++++++- .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 6 +++++- .../Machine/ZXSpectrum16K/ZX16.cs | 15 +-------------- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 3 ++- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 7 +++++-- 11 files changed, 36 insertions(+), 27 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index fbdc5d2134..5890279f31 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -61,8 +61,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void ExecuteCycle() { - _machine.ULADevice.RenderScreen((int)_machine.CurrentFrameCycle); - // is the next CPU cycle causing a BUSRQ or IORQ? if (BUSRQ > 0) { @@ -208,7 +206,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; // WZ: T3 case Z80A.WIO3: - lastPortAddr = AscertainBUSRQAddress(); + lastPortAddr = AscertainBUSRQAddress(); isIO = true; if (IsIOCycleContended(3)) _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 35161bf3a3..3b5eaa6da9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -167,7 +167,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum while (CurrentFrameCycle < ULADevice.FrameLength) { ULADevice.CheckForInterrupt(CurrentFrameCycle); + 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 345b468cb7..e953e2a9e0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -589,10 +589,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // check boundaries if (toCycle > FrameCycleLength) toCycle = FrameCycleLength; - if (LastTState > toCycle) - LastTState = toCycle - 2; - if (toCycle < 0) - toCycle = 0; // render the required number of cycles for (int t = LastTState; t < toCycle; t++) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index 99065d6b45..6be56554a3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -134,6 +134,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) case 1: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; @@ -162,6 +163,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM4[addr % 0x4000] = value; break; case 5: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 6: diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index aec8326d86..3fec363a6e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -236,9 +236,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; case 1: case 2: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 3: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } @@ -265,6 +267,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM3[addr % 0x4000] = value; break; case 1: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } @@ -299,6 +302,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // RAM 0x4000 (RAM5 - Bank5 only) case 1: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; @@ -327,12 +331,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM4[addr % 0x4000] = value; break; case 5: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 6: RAM6[addr % 0x4000] = value; break; case 7: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } 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 431444b67d..a2cd41da7d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -144,7 +144,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - ULADevice.BorderColor = value & BORDER_BIT; + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 80a9e9888f..742a10adb3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -236,10 +236,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; case 1: case 2: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 3: - RAM7[addr % 0x4000] = value; + ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM7[addr % 0x4000] = value; break; } break; @@ -265,6 +267,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM3[addr % 0x4000] = value; break; case 1: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } @@ -299,6 +302,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // RAM 0x4000 (RAM5 - Bank5 only) case 1: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; @@ -327,12 +331,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM4[addr % 0x4000] = value; break; case 5: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 6: RAM6[addr % 0x4000] = value; break; case 7: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } 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 6d8fa99e96..31c2e08e44 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -150,7 +150,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - ULADevice.BorderColor = value & BORDER_BIT; + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index c17d90fe26..fef7ea7b45 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -85,6 +85,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // cannot write to ROM break; case 1: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM0[index] = value; break; } @@ -98,11 +99,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override byte ReadMemory(ushort addr) { - /* - if (IsContended(addr)) - CPU.TotalExecutedCycles += ULADevice.GetContentionValue((int)CurrentFrameCycle); - */ - var data = ReadBus(addr); return data; } @@ -115,15 +111,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public override void WriteMemory(ushort addr, byte value) { - /* - // apply contention if necessary - if (IsContended(addr)) - { - ULADevice.RenderScreen((int)CurrentFrameCycle); - CPU.TotalExecutedCycles += ULADevice.GetContentionValue((int)CurrentFrameCycle); - } - */ - WriteBus(addr, value); } 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 9102cfd72e..bd4c2564cd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -77,10 +77,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // cannot write to ROM break; case 1: + ULADevice.RenderScreen((int)CurrentFrameCycle); RAM0[index] = value; break; case 2: - RAM1[index] = value; + RAM1[index] = value; break; case 3: RAM2[index] = 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 c500aab222..31fd2ac00b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -82,8 +82,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - //ULADevice.RenderScreen((int)CurrentFrameCycle); - ULADevice.BorderColor = value & BORDER_BIT; + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); From cd7df2ea07905e32ce6ad5337dfcd949061a8947 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 7 Jun 2018 10:37:26 -0400 Subject: [PATCH 269/339] z80: more contention work --- .../CPUs/Z80A/Tables_Indirect.cs | 20 +- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 236 ++++++++++-------- 2 files changed, 139 insertions(+), 117 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 53b6cdf22d..2a1779aee5 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -446,16 +446,16 @@ private void LD_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {IDLE, - WAIT, - RD, ALU, L, H, - operation, L, H, - WAIT, - WR, E, D, ALU, - operation, E, D, - SET_FL_LD_R, 0, operation, repeat_instr}; + {IDLE, + WAIT, + RD, ALU, L, H, + operation, L, H, + WAIT, + WR, E, D, ALU, + IDLE, + SET_FL_LD_R, 0, operation, repeat_instr}; - BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, D, D}; + BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, D, D }; } private void CP_OP_R(ushort operation, ushort repeat_instr) @@ -464,7 +464,7 @@ {IDLE, WAIT, RD, ALU, L, H, - operation, L, H, + IDLE, DEC16, C, B, operation, Z, W, IDLE, diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 1aade3839f..b335d052bf 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -80,8 +80,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort RST = 65; public const ushort REP_OP_I = 66; public const ushort REP_OP_O = 67; - + // non-state variables + public ushort Ztemp1, Ztemp2, Ztemp3, Ztemp4; public byte temp_R; public Z80A() @@ -482,11 +483,71 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case SET_FL_LD_R: DEC16_Func(C, B); SET_FL_LD_Func(); - Repeat_Op(); + + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0)) + { + cur_instr = new ushort[] + {TR16, Z, W, PCl, PCh, + INC16, Z, W, + DEC16, PCl, PCh, + DEC16, PCl, PCh, + IDLE, + Ztemp2, E, D, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { D, D, D, D, D, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { Ztemp2, E, D, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; break; case SET_FL_CP_R: SET_FL_CP_Func(); - Repeat_Op(); + + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0) && !FlagZ) + { + cur_instr = new ushort[] + {TR16, Z, W, PCl, PCh, + INC16, Z, W, + DEC16, PCl, PCh, + DEC16, PCl, PCh, + IDLE, + Ztemp2, L, H, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { Ztemp2, L, H, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; break; case SET_FL_IR: SET_FL_IR_Func(cur_instr[instr_pntr++]); @@ -508,10 +569,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case REP_OP_I: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - ushort temp4 = cur_instr[instr_pntr++]; - if (temp4 == DEC16) + Ztemp4 = cur_instr[instr_pntr++]; + if (Ztemp4 == DEC16) { - DEC16_Func(L, H); TR16_Func(Z, W, C, B); DEC16_Func(Z, W); DEC8_Func(B); @@ -523,8 +583,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagP = TableParity[((Regs[ALU] + Regs[C] - 1) & 7) ^ Regs[B]]; } else - { - INC16_Func(L, H); + { TR16_Func(Z, W, C, B); INC16_Func(Z, W); DEC8_Func(B); @@ -536,13 +595,42 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagP = TableParity[((Regs[ALU] + Regs[C] + 1) & 7) ^ Regs[B]]; } - Repeat_Op(); + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if ((Regs[B] != 0) && (Ztemp3 > 0)) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + DEC16, PCl, PCh, + DEC16, PCl, PCh, + IDLE, + Ztemp2, L, H, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { Ztemp2, L, H, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; break; case REP_OP_O: OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); - ushort temp5 = cur_instr[instr_pntr++]; - if (temp5 == DEC16) + Ztemp4 = cur_instr[instr_pntr++]; + if (Ztemp4 == DEC16) { DEC16_Func(L, H); DEC8_Func(B); @@ -563,107 +651,41 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagH = FlagC = (Regs[ALU] + Regs[L]) > 0xFF; FlagP = TableParity[((Regs[ALU] + Regs[L]) & 7) ^ (Regs[B])]; - Repeat_Op(); + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if ((Regs[B] != 0) && (Ztemp3 > 0)) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + DEC16, PCl, PCh, + DEC16, PCl, PCh, + IDLE, + IDLE, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { IDLE, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; break; } TotalExecutedCycles++; } - public void Repeat_Op() - { - // determine if we repeat based on what operation we are doing - // single execution versions also come here, but never repeat - ushort temp1 = cur_instr[instr_pntr++]; - ushort temp2 = cur_instr[instr_pntr++]; - ushort temp3 = cur_instr[instr_pntr++]; - - bool repeat = false; - int Reg16_d = Regs[C] | (Regs[B] << 8); - switch (temp1) - { - case 0: - repeat = Reg16_d != 0; - break; - case 1: - repeat = (Reg16_d != 0) && !FlagZ; - break; - case 2: - repeat = Regs[B] != 0; - break; - case 3: - repeat = Regs[B] != 0; - break; - } - - // if we repeat, we do a 5 cycle refresh which decrements PC by 2 - // if we don't repeat, continue on as a normal opcode fetch - if (repeat && temp3 > 0) - { - cur_instr = new ushort[] - {DEC16, PCl, PCh, - DEC16, PCl, PCh, - IDLE, - IDLE, - IDLE, - IDLE, - WAIT, - OP_F, - OP}; - - if (temp1 == 0) - { - BUSRQ = new ushort[] { D, D, D, D, D, PCh, 0, 0, 0 }; - } - else if (temp1 == 1) - { - BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; - } - else if (temp1 == 2) - { - BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; - } - else if (temp1 == 3) - { - BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; - } - - instr_pntr = 0; bus_pntr = 0; - // adjust WZ register accordingly - switch (temp1) - { - case 0: - // TEST: PC before or after the instruction? - Regs[Z] = Regs[PCl]; - Regs[W] = Regs[PCh]; - INC16_Func(Z, W); - break; - case 1: - // TEST: PC before or after the instruction? - Regs[Z] = Regs[PCl]; - Regs[W] = Regs[PCh]; - INC16_Func(Z, W); - break; - case 2: - // Nothing - break; - case 3: - // Nothing - break; - } - } - else - { - cur_instr = new ushort[] - { IDLE, - WAIT, - OP_F, - OP }; - - BUSRQ = new ushort[] { PCh, 0, 0, 0 }; - instr_pntr = 0; bus_pntr = 0; - } - } - // tracer stuff public Action TraceCallback; From 7ad65bd96ef83e3ebd4a7274bf1bb0213afeaf6b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 7 Jun 2018 11:11:56 -0400 Subject: [PATCH 270/339] z80: WZ fixes --- BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs | 2 +- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 1618cd986d..f305eafbab 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -541,7 +541,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, WAIT, OUT, Z, W, A, - INC16, Z, W, + INC16, Z, ALU, WAIT, OP_F, OP}; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index b335d052bf..8407f0d937 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -491,10 +491,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0)) { cur_instr = new ushort[] - {TR16, Z, W, PCl, PCh, - INC16, Z, W, - DEC16, PCl, PCh, + {DEC16, PCl, PCh, DEC16, PCl, PCh, + TR16, Z, W, PCl, PCh, + INC16, Z, W, IDLE, Ztemp2, E, D, WAIT, @@ -525,10 +525,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0) && !FlagZ) { cur_instr = new ushort[] - {TR16, Z, W, PCl, PCh, - INC16, Z, W, - DEC16, PCl, PCh, + {DEC16, PCl, PCh, DEC16, PCl, PCh, + TR16, Z, W, PCl, PCh, + INC16, Z, W, IDLE, Ztemp2, L, H, WAIT, From 83bbb83c64e545b7fd2a539cdc95ddbf83f9770e Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Jun 2018 16:40:48 +0100 Subject: [PATCH 271/339] ZXHawk: Missed rendercycle call on 128k border OUT --- .../SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 ec358785d5..9fe37c4c14 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -127,7 +127,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum */ // Border - LSB 3 bits hold the border colour - ULADevice.BorderColor = value & BORDER_BIT; + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } // Buzzer BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); From 25fe4a7a87c66a683d5e7bf8ff7ece66a39851cf Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Jun 2018 18:00:08 +0100 Subject: [PATCH 272/339] ZXHawk: more prep for contended 128k/+2/+2a/+3 --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 62 ++++++++++++++++++- .../Computers/SinclairSpectrum/Machine/ULA.cs | 16 +++-- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 3 + .../ZX128Plus2a.Screen.cs | 3 + .../Machine/ZXSpectrum48K/ZX48.Screen.cs | 3 + 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 5890279f31..9708236f5d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -237,10 +237,66 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (machineType) { case MachineType.ZXSpectrum16: - case MachineType.ZXSpectrum48: + case MachineType.ZXSpectrum48: + + if ((lastPortAddr & 0xc000) == 0x4000) + highByte407f = true; + + if (highByte407f) + { + // high byte 40-7f + if (lowBitSet) + { + // high byte 40-7f + // low bit set + // C:1, C:1, C:1, C:1 + switch (T) + { + case 1: + case 2: + case 3: + case 4: + return true; + } + } + else + { + // high byte 40-7f + // low bit reset + // C:1, C:3 + switch (T) + { + case 1: + case 2: + return true; + } + } + } + else + { + // high byte not 40-7f + if (lowBitSet) + { + // high byte not 40-7f + // low bit set + // N:4 + } + else + { + // high byte not 40-7f + // low bit reset + // N:1, C:3 + switch (T) + { + case 2: + return true; + } + } + } + break; + case MachineType.ZXSpectrum128: case MachineType.ZXSpectrum128Plus2: - if ((lastPortAddr & 0xc000) == 0x4000) highByte407f = true; @@ -299,6 +355,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case MachineType.ZXSpectrum128Plus2a: case MachineType.ZXSpectrum128Plus3: + // No contention occurs as the ULA only applies contention when the Z80 MREQ line is active + // (which is not during an IO operation) break; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index e953e2a9e0..0bec3a194c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -88,6 +88,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int InterruptLength; + /// + /// Arbitrary offset into the contention array (for memory ops) + /// + public int MemoryContentionOffset; + + /// + /// Arbitrary offset into the contention array (for port ops) + /// + public int PortContentionOffset; + /// /// The time in T-States for one scanline to complete /// @@ -776,8 +786,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int GetContentionValue(int tstate) { - int off = 5; - tstate += off; + tstate += MemoryContentionOffset; if (tstate >= FrameCycleLength) tstate -= FrameCycleLength; @@ -793,8 +802,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int GetPortContentionValue(int tstate) { - int off = 5; - tstate += off; + tstate += PortContentionOffset; if (tstate >= FrameCycleLength) tstate -= FrameCycleLength; 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 cbd98f1128..547e0fab72 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -20,6 +20,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InterruptLength = 36; ScanlineTime = 228; + MemoryContentionOffset = 5; + PortContentionOffset = 5; + BorderLeftTime = 24; BorderRightTime = 24; 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 e9026ce5d7..949ca558ce 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -20,6 +20,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InterruptLength = 32; ScanlineTime = 228; + MemoryContentionOffset = 7; + PortContentionOffset = 7; + BorderLeftTime = 24; BorderRightTime = 24; 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 96219e04db..5af98f944a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -20,6 +20,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InterruptLength = 32; ScanlineTime = 224; + MemoryContentionOffset = 5; + PortContentionOffset = 5; + BorderLeftTime = 24; BorderRightTime = 24; From f4c47dc80aa0924f0c76c33fc10c8a16f5016d9c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 7 Jun 2018 18:27:44 +0100 Subject: [PATCH 273/339] ZXHawk: modified vblank interrupt start time (128k/+2a/+3) so contention period starts at the correct time --- .../SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs | 2 +- .../Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 547e0fab72..3e1aed6e24 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // timing ClockSpeed = 3546900; FrameCycleLength = 70908; - InterruptStartTime = 33; + InterruptStartTime = 32; InterruptLength = 36; ScanlineTime = 228; 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 949ca558ce..90f505d1f2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // timing ClockSpeed = 3546900; FrameCycleLength = 70908; - InterruptStartTime = 33; + InterruptStartTime = 31; InterruptLength = 32; ScanlineTime = 228; From 6ae8f0187242f5d06704efb316dd9da2e5fb79e6 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 7 Jun 2018 18:41:50 -0400 Subject: [PATCH 274/339] z80: Fix some more contention stuff --- .../CPUs/Z80A/Registers.cs | 2 +- .../CPUs/Z80A/Tables_Direct.cs | 10 ++++----- .../CPUs/Z80A/Tables_Indirect.cs | 22 +++++++++---------- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 12 +++++++++- 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index 7f392fbe0c..7d4271373b 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -58,7 +58,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort WIO4 = 108; - public bool FlagI; + public bool FlagI;/*, FlagI1, FlagI2, FlagI3, FlagI4, FlagI5, FlagI6;*/ public bool FlagW; // wait flag, when set to true reads / writes will be delayed diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index f305eafbab..f91fb31afc 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -537,11 +537,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A {TR, W, A, WAIT, RD_INC, Z, PCl, PCh, - IDLE, - WAIT, + IDLE, + WAIT, WAIT, OUT, Z, W, A, - INC16, Z, ALU, + INC16, Z, ALU, WAIT, OP_F, OP}; @@ -552,9 +552,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void OUT_REG_(ushort dest, ushort src) { cur_instr = new ushort[] - {IDLE, + {TR16, Z, W, C, B, + IDLE, IDLE, - TR16, Z, W, C, B, OUT, Z, W, src, INC16, Z, W, WAIT, diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 2a1779aee5..bab93cbe1f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -17,17 +17,17 @@ OP_F, OP }; - BUSRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; } private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, WAIT, RD, ALU, src_l, src_h, operation, bit, ALU, + IDLE, WAIT, WR, src_l, src_h, ALU, IDLE, @@ -35,7 +35,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; } // Note that this operation uses I_BIT, same as indexed BIT. @@ -46,15 +46,15 @@ { cur_instr = new ushort[] {IDLE, - IDLE, WAIT, RD, ALU, src_l, src_h, + IDLE, I_BIT, bit, ALU, WAIT, OP_F, OP }; - BUSRQ = new ushort[] { 0, src_h, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, src_h, PCh, 0, 0, 0 }; } private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -267,10 +267,10 @@ { cur_instr = new ushort[] {IDLE, - IDLE, WAIT, RD, ALU, src_l, src_h, INC8, ALU, + IDLE, WAIT, WR, src_l, src_h, ALU, IDLE, @@ -278,17 +278,17 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; } private void DEC_8_IND(ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, WAIT, RD, ALU, src_l, src_h, DEC8, ALU, + IDLE, WAIT, WR, src_l, src_h, ALU, IDLE, @@ -296,7 +296,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; } // NOTE: WZ implied for the wollowing 3 functions @@ -304,10 +304,10 @@ { cur_instr = new ushort[] {IDLE, - IDLE, WAIT, RD, ALU, Z, W, operation, ALU, + IDLE, WAIT, WR, Z, W, ALU, TR, dest, ALU, @@ -315,7 +315,7 @@ OP_F, OP }; - BUSRQ = new ushort[] { 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_OP(ushort operation, ushort bit, ushort dest) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 8407f0d937..33c60fc97f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -159,6 +159,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // Execute instructions public void ExecuteOne() { + /*FlagI6 = FlagI; + FlagI5 = FlagI4; + FlagI4 = FlagI3; + FlagI3 = FlagI2; + FlagI2 = FlagI1; + FlagI1 = FlagI; + */ bus_pntr++; switch (cur_instr[instr_pntr++]) { @@ -663,11 +670,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A DEC16, PCl, PCh, DEC16, PCl, PCh, IDLE, - IDLE, + DEC16, B, ALU, WAIT, OP_F, OP}; + Regs[B] = (ushort)((Regs[B] + 1) & 0xFF); + + BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; } else From f33863126c21250708142a2440a28448e4f7dc18 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 8 Jun 2018 09:36:32 +0100 Subject: [PATCH 275/339] ZXHawk: Fix rendertable generation offset. btime.tap test now passes --- .../Computers/SinclairSpectrum/Machine/ULA.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 0bec3a194c..398c0c958f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -327,7 +327,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (var t = 0; t < _ula.FrameCycleLength; t++) { - var tStateScreen = t + _ula.InterruptStartTime; + var tStateScreen = t + 1 + _ula.InterruptStartTime; if (tStateScreen < 0) tStateScreen += _ula.FrameCycleLength; @@ -719,7 +719,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void ReadFloatingBus(int tstate, ref int result) { - int off = 1; + int off = 0; tstate += off; if (tstate >= RenderingTable.Renderer.Length) tstate -= RenderingTable.Renderer.Length; From 403a1032d8c9aa3ca3fedc44b26bdd423337c661 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 8 Jun 2018 11:07:16 +0100 Subject: [PATCH 276/339] ZXHawk: 48k Interrupt, ULA tables and contention tweaked --- .../Computers/SinclairSpectrum/Machine/ULA.cs | 65 ++++--------------- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 2 + .../ZX128Plus2a.Screen.cs | 2 + .../Machine/ZXSpectrum48K/ZX48.Screen.cs | 8 ++- 4 files changed, 22 insertions(+), 55 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 398c0c958f..0124f6721e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -98,6 +98,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public int PortContentionOffset; + /// + /// Arbitrary offset for render table generation + /// + public int RenderTableOffset; + + /// + /// The offset when return floating bus bytes + /// + public int FloatingBusOffset; + /// /// The time in T-States for one scanline to complete /// @@ -317,17 +327,9 @@ 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 + 1 + _ula.InterruptStartTime; + var tStateScreen = t + _ula.RenderTableOffset + _ula.InterruptStartTime; if (tStateScreen < 0) tStateScreen += _ula.FrameCycleLength; @@ -458,7 +460,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // calculate contention values for (int t = 0; t < _ula.FrameCycleLength; t++) { - int shifted = (t + 1) + _ula.InterruptStartTime + Offset; + int shifted = t + _ula.RenderTableOffset + _ula.InterruptStartTime; if (shifted < 0) shifted += _ula.FrameCycleLength; shifted %= _ula.FrameCycleLength; @@ -482,46 +484,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Renderer[t].ContentionValue = conPattern[pixByte]; } - - // calculate floating bus values - for (int t = 0; t < _ula.FrameCycleLength; t++) - { - int shifted = (t + 10) + _ula.InterruptStartTime; - if (shifted < 0) - shifted += _ula.FrameCycleLength; - shifted %= _ula.FrameCycleLength; - - Renderer[t].FloatingBusAddress = 0; - - int line = shifted / _ula.ScanlineTime; - int pix = shifted % _ula.ScanlineTime; - if (line < _ula.FirstPaperLine || line >= (_ula.FirstPaperLine + 192)) - { - Renderer[t].FloatingBusAddress = 0; - continue; - } - int scrPix = pix - _ula.FirstPaperTState; - if (scrPix < 0 || scrPix >= 128) - { - Renderer[t].FloatingBusAddress = 0; - continue; - } - - int pixByte = scrPix % 8; - int chunk = scrPix % 16; - - switch (chunk) - { - case 0: - case 2: - Renderer[t].FloatingBusAddress = CalculateByteAddress(scrPix, line); - break; - case 1: - case 3: - Renderer[t].FloatingBusAddress = CalculateAttributeAddress(scrPix, line); - break; - } - } } private ushort CalculateByteAddress(int x, int y) @@ -719,8 +681,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void ReadFloatingBus(int tstate, ref int result) { - int off = 0; - tstate += off; + tstate += FloatingBusOffset; if (tstate >= RenderingTable.Renderer.Length) tstate -= RenderingTable.Renderer.Length; if (tstate < 0) 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 3e1aed6e24..92472c0e3b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -22,6 +22,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum MemoryContentionOffset = 5; PortContentionOffset = 5; + RenderTableOffset = 1; + FloatingBusOffset = 1; BorderLeftTime = 24; BorderRightTime = 24; 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 90f505d1f2..2cd69dcaa7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -22,6 +22,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum MemoryContentionOffset = 7; PortContentionOffset = 7; + RenderTableOffset = 1; + FloatingBusOffset = 1; BorderLeftTime = 24; BorderRightTime = 24; 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 5af98f944a..96624fe1d5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -16,12 +16,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // timing ClockSpeed = 3500000; FrameCycleLength = 69888; - InterruptStartTime = 31; + InterruptStartTime = 30; InterruptLength = 32; ScanlineTime = 224; - MemoryContentionOffset = 5; - PortContentionOffset = 5; + MemoryContentionOffset = 6; + PortContentionOffset = 6; + RenderTableOffset = 2; + FloatingBusOffset = 1; BorderLeftTime = 24; BorderRightTime = 24; From fd889250b31d4baeeed019321d8a3996d5c78e10 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 8 Jun 2018 12:27:52 +0100 Subject: [PATCH 277/339] ZXHawk: 128k/+2 interrupt, ULA tables & contention tweaked --- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 92472c0e3b..461baf266a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -16,13 +16,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // timing ClockSpeed = 3546900; FrameCycleLength = 70908; - InterruptStartTime = 32; + InterruptStartTime = 34; InterruptLength = 36; ScanlineTime = 228; - MemoryContentionOffset = 5; - PortContentionOffset = 5; - RenderTableOffset = 1; + MemoryContentionOffset = 6; + PortContentionOffset = 6; + RenderTableOffset = -4; FloatingBusOffset = 1; BorderLeftTime = 24; From 7ced9fdc6a5a218759ab6eb7dc22c154b316a54d Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 8 Jun 2018 13:24:43 +0100 Subject: [PATCH 278/339] ZXHawk: 128k HAL10H8 chip crash emulation (INs to paging ports cause floating bus data being used to set the paging registers) --- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 9fe37c4c14..0596dce779 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -20,6 +20,25 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int result = 0xFF; + // ports 0x3ffd & 0x7ffd + // traditionally thought to be write-only + if (port == 0x3ffd || port == 0x7ffd) + { + // https://faqwiki.zxnet.co.uk/wiki/ZX_Spectrum_128 + // HAL bugs + // Reads from port 0x7ffd cause a crash, as the 128's HAL10H8 chip does not distinguish between reads and writes to this port, + // resulting in a floating data bus being used to set the paging registers. + + // -asni (2018-06-08) - need this to pass the final portread tests from fusetest.tap + + // get the floating bus value + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); + // use this to set the paging registers + WritePort(port, (byte)result); + // return the floating bus value + return (byte)result; + } + // check AY if (AYDevice.ReadPort(port, ref result)) return (byte)result; From faec0fb0e56f0b59ce5ad2fd2adaafe05c9d973c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 8 Jun 2018 13:43:40 +0100 Subject: [PATCH 279/339] ZXHawk: 128k/+2 High-port contention now reporting properly in fusetest --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 2 +- .../Machine/SpectrumBase.Memory.cs | 7 +++++++ .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 18 ++++++++++++++++++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs | 18 ++++++++++++++++++ .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 18 ++++++++++++++++++ .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 9 +++++++++ 6 files changed, 71 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 9708236f5d..9b8a822c15 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -297,7 +297,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case MachineType.ZXSpectrum128: case MachineType.ZXSpectrum128Plus2: - if ((lastPortAddr & 0xc000) == 0x4000) + if ((lastPortAddr & 0xc000) == 0x4000 || (lastPortAddr & 0xc000) == 0xc000 && _machine.ContendedBankPaged()) highByte407f = true; if (highByte407f) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 283f12cdb6..8bd72d3dcd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -161,6 +161,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public abstract bool IsContended(ushort addr); + + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public abstract bool ContendedBankPaged(); + #endregion #region Helper Methods diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index 6be56554a3..6c48210679 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -232,6 +232,24 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; } + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + switch (RAMPaged) + { + case 1: + case 3: + case 5: + case 7: + return true; + } + + return false; + } + /// /// ULA reads the memory at the specified address /// (No memory contention) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 3fec363a6e..73400dd86c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -425,6 +425,24 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; } + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + + return false; + } + /// /// ULA reads the memory at the specified address /// (No memory contention) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 742a10adb3..87a9062912 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -426,6 +426,24 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; } + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + + return false; + } + /// /// ULA reads the memory at the specified address /// (No memory contention) 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 bd4c2564cd..932da8497b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -123,6 +123,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return false; } + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + return false; + } + /// /// Sets up the ROM /// From a1d24400d042d9451e0583154c0226b6709fd540 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 8 Jun 2018 14:39:23 +0100 Subject: [PATCH 280/339] ZXHawk: proper top border height --- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs | 13 ++++++++++++- .../Machine/ZXSpectrum48K/ZX48.Screen.cs | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) 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 461baf266a..54a0fe058f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // screen layout ScreenWidth = 256; ScreenHeight = 192; - BorderTopHeight = 48; + BorderTopHeight = 55; // 48; BorderBottomHeight = 56; BorderLeftWidth = 48; BorderRightWidth = 48; 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 2cd69dcaa7..2611a7fd43 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -16,6 +16,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // timing ClockSpeed = 3546900; FrameCycleLength = 70908; + /* InterruptStartTime = 31; InterruptLength = 32; ScanlineTime = 228; @@ -24,6 +25,16 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PortContentionOffset = 7; RenderTableOffset = 1; FloatingBusOffset = 1; + */ + + InterruptStartTime = 33; + InterruptLength = 32; + ScanlineTime = 228; + + MemoryContentionOffset = 6; + PortContentionOffset = 6; + RenderTableOffset = -2; + FloatingBusOffset = 1; BorderLeftTime = 24; BorderRightTime = 24; @@ -37,7 +48,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // screen layout ScreenWidth = 256; ScreenHeight = 192; - BorderTopHeight = 48; + BorderTopHeight = 55; BorderBottomHeight = 56; BorderLeftWidth = 48; BorderRightWidth = 48; 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 96624fe1d5..119149c5c4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // screen layout ScreenWidth = 256; ScreenHeight = 192; - BorderTopHeight = 48; + BorderTopHeight = 55;// 48; BorderBottomHeight = 56; BorderLeftWidth = 48; BorderRightWidth = 48; From 3ce546b774d27d32db733e6f86b9c4e3717bf927 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Fri, 8 Jun 2018 12:28:51 -0400 Subject: [PATCH 281/339] z80: Resond to interrupts at the corect time --- .../CPUs/Z80A/Interrupts.cs | 4 ++-- BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs | 2 +- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 18 ++++++++++++------ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 9b1426505c..389fdf6e8f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -85,7 +85,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } // Interrupt mode 2 uses the I vector combined with a byte on the data bus @@ -112,7 +112,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { 0, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0 ,0 ,PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0 ,0 ,PCh, 0, 0, 0 }; } private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index 7d4271373b..8ca466d5e2 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -58,7 +58,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort WIO4 = 108; - public bool FlagI;/*, FlagI1, FlagI2, FlagI3, FlagI4, FlagI5, FlagI6;*/ + public bool FlagI, FlagI1, FlagI2, FlagI3, FlagI4, FlagI5, FlagI6; public bool FlagW; // wait flag, when set to true reads / writes will be delayed diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 33c60fc97f..afd07e1979 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -159,13 +159,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // Execute instructions public void ExecuteOne() { - /*FlagI6 = FlagI; + //FlagI6 = FlagI5; FlagI5 = FlagI4; FlagI4 = FlagI3; FlagI3 = FlagI2; FlagI2 = FlagI1; FlagI1 = FlagI; - */ + bus_pntr++; switch (cur_instr[instr_pntr++]) { @@ -196,7 +196,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A NMICallback(); instr_pntr = 0; bus_pntr = 0; } - else if (iff1 && FlagI) + else if (iff1 && FlagI5) { iff1 = iff2 = false; EI_pending = 0; @@ -267,7 +267,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A NMICallback(); halted = false; } - else if (iff1 && FlagI) + else if (iff1 && FlagI5) { iff1 = iff2 = false; EI_pending = 0; @@ -670,12 +670,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A DEC16, PCl, PCh, DEC16, PCl, PCh, IDLE, - DEC16, B, ALU, + IDLE,//DEC16, B, ALU, WAIT, OP_F, OP}; - Regs[B] = (ushort)((Regs[B] + 1) & 0xFF); + //Regs[B] = (ushort)((Regs[B] + 1) & 0xFF); BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; @@ -770,6 +770,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("instr_swap", ref instr_swap); ser.Sync("opcode", ref opcode); ser.Sync("FlagI", ref FlagI); + ser.Sync("FlagI1", ref FlagI1); + ser.Sync("FlagI2", ref FlagI2); + ser.Sync("FlagI3", ref FlagI3); + ser.Sync("FlagI4", ref FlagI4); + ser.Sync("FlagI5", ref FlagI5); + ser.Sync("FlagI6", ref FlagI6); ser.Sync("FlagW", ref FlagW); ser.Sync("NO Preifx", ref NO_prefix); From c47860fad7d4109294cc08823de934822214de78 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 8 Jun 2018 19:09:40 +0100 Subject: [PATCH 282/339] ZXHawk: 48k offsets updated for z80 interrupt response time change --- .../Computers/SinclairSpectrum/Machine/ULA.cs | 4 ++-- .../SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 0124f6721e..e875a09565 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -164,13 +164,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return; } - if (currentCycle < InterruptStartTime) + if (currentCycle <= InterruptStartTime) { // interrupt does not need to be raised yet return; } - if (currentCycle >= InterruptStartTime + InterruptLength) + 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 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 119149c5c4..0a4c236d86 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -16,13 +16,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // timing ClockSpeed = 3500000; FrameCycleLength = 69888; - InterruptStartTime = 30; + InterruptStartTime = 33; InterruptLength = 32; ScanlineTime = 224; MemoryContentionOffset = 6; PortContentionOffset = 6; - RenderTableOffset = 2; + RenderTableOffset = -9; // 2; FloatingBusOffset = 1; BorderLeftTime = 24; From 173bb2a50f4adb1e486425596534bed62b64595d Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sat, 9 Jun 2018 21:15:26 -0400 Subject: [PATCH 283/339] A2600: Add special cases for games that turn off screen to do calculations --- Assets/gamedb/gamedb_a2600.txt | 18 +++++++++--------- .../Consoles/Atari/2600/Atari2600.Core.cs | 8 ++++++++ .../Consoles/Atari/2600/Atari2600.IEmulator.cs | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/Assets/gamedb/gamedb_a2600.txt b/Assets/gamedb/gamedb_a2600.txt index a3a6adc0d7..abaf72a3df 100644 --- a/Assets/gamedb/gamedb_a2600.txt +++ b/Assets/gamedb/gamedb_a2600.txt @@ -27,11 +27,11 @@ sha1:b13080675116318af2b8cda7e71bee2a8f39be63 3-D Corridor (Green) (30-03-2003) sha1:76f9020ace43a20a11e3d540ebabc793e6fa4a40 3-D Corridor (Pink Spiral) (31-03-2003) (AD) A26 m=4K;NTSC=true sha1:136b7d61ca887a8687b48cf8b1ee6244d5090cde 3-D Corridor (Spiral Words) (31-03-2003) (AD) A26 m=4K;NTSC=true sha1:77466b40034e5d7603be2e1a46fc69803de5b685 3-D Corridor Demo (27-03-2003) (MP) A26 m=4K;NTSC=true -sha1:6b163aa967e4204a5bd98a59bd8e80f159004e34 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [!] A26 m=2K;PAL=true -sha1:e5a370e4f9531836e5b12951e44ea4a4afe9f5d7 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [p1][o1] A26 m=2K;PAL=true -sha1:21d983f2f52b84c22ecae84b0943678ae2c31c10 3-D Tic-Tac-Toe (1978) (Atari) [!] A26 m=2K;NTSC=true -sha1:f93a1067958a17c14ee72c6b611cffc16901f58b 3-D Tic-Tac-Toe (1978) (Atari) [o1] A26 m=2K;NTSC=true -sha1:fd8f2a6eb9248227edca6ae60de93ca38c0ed2ea 3-D Tic-Tac-Toe (32-in-1) (Atari) (PAL) [!] A26 m=2K;PAL=true +sha1:6b163aa967e4204a5bd98a59bd8e80f159004e34 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [!] A26 SP_FRAME=true;m=2K;PAL=true +sha1:e5a370e4f9531836e5b12951e44ea4a4afe9f5d7 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [p1][o1] A26 SP_FRAME=true;m=2K;PAL=true +sha1:21d983f2f52b84c22ecae84b0943678ae2c31c10 3-D Tic-Tac-Toe (1978) (Atari) [!] A26 SP_FRAME=true;m=2K;NTSC=true +sha1:f93a1067958a17c14ee72c6b611cffc16901f58b 3-D Tic-Tac-Toe (1978) (Atari) [o1] A26 SP_FRAME=true;m=2K;NTSC=true +sha1:fd8f2a6eb9248227edca6ae60de93ca38c0ed2ea 3-D Tic-Tac-Toe (32-in-1) (Atari) (PAL) [!] A26 SP_FRAME=true;m=2K;PAL=true sha1:a100ecfae6ef7f825648f7c9c1f1a35029f4fb5b 2600 Collison Demo 1 (Piero Cavina) (PD) A26 m=4K;NTSC=true sha1:f750d96d811c6cea9dcddf752ba3716ba3dd5dde 2600 Collison Demo 2 (Piero Cavina) (PD) A26 m=4K;NTSC=true sha1:5bb83bdbd49bf0c9cbbd0381e99dbb5b7f58d074 2600 Digital Clock (Demo 1) (PD) A26 m=4K;NTSC=true @@ -93,10 +93,10 @@ sha1:a1f660827ce291f19719a5672f2c5d277d903b03 Alpha Beam with Ernie (1983) (Ata sha1:f22872b1a1d6156f81335e605b83c180faae6209 Alpha Demo - The Beta Demo 2 (2000) (MP) A26 m=4K;NTSC=true sha1:ff8c849db0e963d9e3962d887fb389ef90f968c8 Amanda Invaders (PD) [o1] A26 m=4K;NTSC=true sha1:316c851551956c8e73956ee073f918380b9fa778 Amanda Invaders (PD) A26 m=4K;NTSC=true -sha1:fb9bba6556fc45904dac8750fa18155e6196b2c0 Amidar (1983) (Parker Bros) (PAL) [!] A26 m=4K;PAL=true -sha1:b89a5ac6593e83fbebee1fe7d4cec81a7032c544 Amidar (1983) (Parker Bros) A26 m=4K;NTSC=true -sha1:59657177bc7e2eaf7736ac121defdd891328ae00 Amidar DS (2003) (TJ) (Amidar Hack) A26 m=4K;NTSC=true -sha1:f4688fc8278a2c2040b3eaf90ef7323f6523555a Amidar DS (Fast Enemies) (2003) (TJ) (Amidar Hack) A26 m=4K;NTSC=true +sha1:fb9bba6556fc45904dac8750fa18155e6196b2c0 Amidar (1983) (Parker Bros) (PAL) [!] A26 SP_FRAME=true;m=4K;PAL=true +sha1:b89a5ac6593e83fbebee1fe7d4cec81a7032c544 Amidar (1983) (Parker Bros) A26 SP_FRAME=true;m=4K;NTSC=true +sha1:59657177bc7e2eaf7736ac121defdd891328ae00 Amidar DS (2003) (TJ) (Amidar Hack) A26 SP_FRAME=true;m=4K;NTSC=true +sha1:f4688fc8278a2c2040b3eaf90ef7323f6523555a Amidar DS (Fast Enemies) (2003) (TJ) (Amidar Hack) A26 SP_FRAME=true;m=4K;NTSC=true sha1:2d29ce1ff161b1cdae935bbbd84fc330254696b6 An Exercise In Minimalism (V1) (1999) (Marc de Smet) (PD) A26 m=4K;NTSC=true sha1:50e383e0e2e652e0b067f56bc3964cf6641139f1 An Exercise In Minimalism (V2) (1999) (Eckhard Stolberg) A26 m=4K;NTSC=true sha1:3ad5c5a35f5e198f35de6066df6c0d15e7b89e02 Analog Clock (Additional Frame Info) (V0.0) (20-01-2003) (AD) A26 m=3F;NTSC=true diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs index 79ea69a9c5..29fed58cb3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs @@ -33,6 +33,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 internal byte[] Rom { get; } internal int DistinctAccessCount { get; private set; } + public bool SP_FRAME = false; + internal struct CpuLink : IMOS6502XLink { private readonly Atari2600 _atari2600; @@ -338,6 +340,12 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 // Show mapper class on romstatusdetails CoreComm.RomStatusDetails = $"{this._game.Name}\r\nSHA1:{Rom.HashSHA1()}\r\nMD5:{Rom.HashMD5()}\r\nMapper Impl \"{_mapper.GetType()}\""; + + // Some games (ex. 3D tic tac toe), turn off the screen for extended periods, so we need to allow for this here. + if (_game.GetOptionsDict()["SP_FRAME"] == "true") + { + SP_FRAME = true; + } } private bool _pal; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs index 13164fdd0f..8619ff1347 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs @@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 { Cycle(); count++; - if (count > 1000000) { throw new Exception("ERROR: Unable to resolve Frame. Please Report."); } + if (count > 1000000 && !SP_FRAME) { throw new Exception("ERROR: Unable to resolve Frame. Please Report."); } } _tia.New_Frame = false; From 62d1005ed9d764146d250f49a24096f8bbb6c035 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 10 Jun 2018 07:48:44 -0400 Subject: [PATCH 284/339] A2600: Fix special reset cases --- Assets/gamedb/gamedb_a2600.txt | 10 ++++----- .../Consoles/Atari/2600/Atari2600.Core.cs | 21 +++++++++++++++++-- .../Atari/2600/Atari2600.IEmulator.cs | 14 ++++++++++++- .../Atari/2600/Atari2600.IStatable.cs | 1 + 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/Assets/gamedb/gamedb_a2600.txt b/Assets/gamedb/gamedb_a2600.txt index abaf72a3df..505744f0ff 100644 --- a/Assets/gamedb/gamedb_a2600.txt +++ b/Assets/gamedb/gamedb_a2600.txt @@ -93,10 +93,10 @@ sha1:a1f660827ce291f19719a5672f2c5d277d903b03 Alpha Beam with Ernie (1983) (Ata sha1:f22872b1a1d6156f81335e605b83c180faae6209 Alpha Demo - The Beta Demo 2 (2000) (MP) A26 m=4K;NTSC=true sha1:ff8c849db0e963d9e3962d887fb389ef90f968c8 Amanda Invaders (PD) [o1] A26 m=4K;NTSC=true sha1:316c851551956c8e73956ee073f918380b9fa778 Amanda Invaders (PD) A26 m=4K;NTSC=true -sha1:fb9bba6556fc45904dac8750fa18155e6196b2c0 Amidar (1983) (Parker Bros) (PAL) [!] A26 SP_FRAME=true;m=4K;PAL=true -sha1:b89a5ac6593e83fbebee1fe7d4cec81a7032c544 Amidar (1983) (Parker Bros) A26 SP_FRAME=true;m=4K;NTSC=true -sha1:59657177bc7e2eaf7736ac121defdd891328ae00 Amidar DS (2003) (TJ) (Amidar Hack) A26 SP_FRAME=true;m=4K;NTSC=true -sha1:f4688fc8278a2c2040b3eaf90ef7323f6523555a Amidar DS (Fast Enemies) (2003) (TJ) (Amidar Hack) A26 SP_FRAME=true;m=4K;NTSC=true +sha1:fb9bba6556fc45904dac8750fa18155e6196b2c0 Amidar (1983) (Parker Bros) (PAL) [!] A26 SP_RESET=true;m=4K;PAL=true +sha1:b89a5ac6593e83fbebee1fe7d4cec81a7032c544 Amidar (1983) (Parker Bros) A26 SP_RESET=true;m=4K;NTSC=true +sha1:59657177bc7e2eaf7736ac121defdd891328ae00 Amidar DS (2003) (TJ) (Amidar Hack) A26 SP_RESET=true;m=4K;NTSC=true +sha1:f4688fc8278a2c2040b3eaf90ef7323f6523555a Amidar DS (Fast Enemies) (2003) (TJ) (Amidar Hack) A26 SP_RESET=true;m=4K;NTSC=true sha1:2d29ce1ff161b1cdae935bbbd84fc330254696b6 An Exercise In Minimalism (V1) (1999) (Marc de Smet) (PD) A26 m=4K;NTSC=true sha1:50e383e0e2e652e0b067f56bc3964cf6641139f1 An Exercise In Minimalism (V2) (1999) (Eckhard Stolberg) A26 m=4K;NTSC=true sha1:3ad5c5a35f5e198f35de6066df6c0d15e7b89e02 Analog Clock (Additional Frame Info) (V0.0) (20-01-2003) (AD) A26 m=3F;NTSC=true @@ -328,7 +328,7 @@ sha1:1819ef408c1216c83dcfeceec28d13f6ea5ca477 Bump 'N' Jump (1983) (Mattel) A26 sha1:35bc4048f58bb170313872a0bce44fb1ca3217cc Bump 'N' Jump (Telegames) (PAL) [!] A26 m=F8;PAL=true sha1:ad48f4952e867a2b97900d965b69f50fddf8ba2d Bumper Bash (1983) (Spectravideo) (PAL) [!] A26 m=4K;PAL=true sha1:6c199782c79686dc0cbce6d5fe805f276a86a3f5 Bumper Bash (1983) (Spectravideo) A26 m=4K;NTSC=true -sha1:49e01b8048ae344cb65838f6b1c1de0e1f416f29 Burgertime (1982) (Mattel) A26 m=E7;NTSC=true +sha1:49e01b8048ae344cb65838f6b1c1de0e1f416f29 Burgertime (1982) (Mattel) A26 SP_RESET=true;m=E7;NTSC=true sha1:3f1f17cf620f462355009f5302cddffa730fa2fa Cakewalk (CommaVid) A26 m=4K;NTSC=true sha1:4ca390f8dc4d0f8dce889dfc21ddd675c5860095 Cakewalk (PAL Conversion) (Fabrizio Zavagli) A26 m=4K;NTSC=true sha1:def9502c5a37700ae03461b2d7cf2f73e91b4cec California Games (1988) (Epyx) (PAL) [!] A26 m=F6;PAL=true diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs index 29fed58cb3..4d9bb84286 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs @@ -34,6 +34,8 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 internal int DistinctAccessCount { get; private set; } public bool SP_FRAME = false; + public bool SP_RESET = false; + public bool unselect_reset; internal struct CpuLink : IMOS6502XLink { @@ -342,9 +344,19 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 CoreComm.RomStatusDetails = $"{this._game.Name}\r\nSHA1:{Rom.HashSHA1()}\r\nMD5:{Rom.HashMD5()}\r\nMapper Impl \"{_mapper.GetType()}\""; // Some games (ex. 3D tic tac toe), turn off the screen for extended periods, so we need to allow for this here. - if (_game.GetOptionsDict()["SP_FRAME"] == "true") + if (_game.GetOptionsDict().ContainsKey("SP_FRAME")) { - SP_FRAME = true; + if (_game.GetOptionsDict()["SP_FRAME"] == "true") + { + SP_FRAME = true; + } + } + if (_game.GetOptionsDict().ContainsKey("SP_RESET")) + { + if (_game.GetOptionsDict()["SP_RESET"] == "true") + { + SP_RESET = true; + } } } @@ -429,6 +441,11 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 bool select = _controller.IsPressed("Select"); bool reset = _controller.IsPressed("Reset"); + if (unselect_reset) + { + reset = false; + } + if (reset) { value &= 0xFE; } if (select) { value &= 0xFD; } if (SyncSettings.BW) { value &= 0xF7; } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs index 8619ff1347..1dad16b1d3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs @@ -42,12 +42,24 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _rightDifficultySwitchHeld = false; } + unselect_reset = false; + int count = 0; while (!_tia.New_Frame) { Cycle(); count++; - if (count > 1000000 && !SP_FRAME) { throw new Exception("ERROR: Unable to resolve Frame. Please Report."); } + if (count > 1000000 && !SP_FRAME) + { + if (SP_RESET) + { + unselect_reset = true; + } + else + { + throw new Exception("ERROR: Unable to resolve Frame. Please Report."); + } + } } _tia.New_Frame = false; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs index dcff99f432..69a1191c46 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs @@ -51,6 +51,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 ser.Sync("rightDifficultySwitchPressed", ref _rightDifficultySwitchPressed); ser.Sync("leftDifficultySwitchHeld", ref _leftDifficultySwitchHeld); ser.Sync("rightDifficultySwitchHeld", ref _rightDifficultySwitchHeld); + ser.Sync("unselect_reset", ref unselect_reset); _tia.SyncState(ser); _m6532.SyncState(ser); From fe3e7c3dd270e8a8c70443ba4327ca934b77a1fa Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 10 Jun 2018 18:30:12 -0400 Subject: [PATCH 285/339] SMS: Set stack pointer to RAM for Aerial Assult (W) --- BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs index d0e5e4e27c..ecdedb73d4 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs @@ -5,6 +5,7 @@ using BizHawk.Emulation.Common; using BizHawk.Emulation.Common.Components; using BizHawk.Emulation.Cores.Components; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Common.BufferExtensions; /***************************************************** TODO: @@ -205,6 +206,15 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem Cpu.ReadMemory = ReadMemory; Cpu.WriteMemory = WriteMemory; + + // This particular game relies on an uninitialized Stack pointer to not be 0 or 0xFFFF + // It does not appear from documentation that the stack pointer is initialized to anything + // so just give it a value that doesn't break the game. + if (rom.HashSHA1() == "CBDBAAEB62A8483D9D5A8757B0F19233DFB9B416") + { + Cpu.Regs[Cpu.SPl] = 0x4; + Cpu.Regs[Cpu.SPh] = 0xFF; + } } // Constants From fbba7c25aed54efe1476278bf51ed805f483db70 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 11 Jun 2018 14:35:12 +0100 Subject: [PATCH 286/339] 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 /// 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 /// 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 /// public virtual void HardReset() { - ULADevice.ResetInterrupt(); + //ULADevice.ResetInterrupt(); ROMPaged = 0; SpecialPagingMode = false; RAMPaged = 0; @@ -274,7 +276,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// 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; /// - /// Arbitrary offset into the contention array (for memory ops) - /// - public int MemoryContentionOffset; - - /// - /// Arbitrary offset into the contention array (for port ops) - /// - public int PortContentionOffset; + /// Contention offset + /// + public int ContentionOffset; /// /// Arbitrary offset for render table generation @@ -137,60 +132,58 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// protected bool InterruptRaised; - /// - /// Signs that the interrupt signal has been revoked - /// - protected bool InterruptRevoked; + public long ULACycleCounter; + public long LastULATick; + public bool FrameEnd; /// - /// Resets the interrupt - this should happen every frame in order to raise - /// the VBLANK interrupt in the proceding frame - /// - public virtual void ResetInterrupt() - { - InterruptRaised = false; - InterruptRevoked = false; - } - - /// - /// Generates an interrupt in the current phase if needed + /// Cycles the ULA clock + /// Handles interrupt generation /// /// - 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(); + } + } + } } /// @@ -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 /// public int GetContentionValue(int tstate) { - tstate += MemoryContentionOffset; + //tstate += MemoryContentionOffset; if (tstate >= FrameCycleLength) tstate -= FrameCycleLength; @@ -763,7 +756,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// 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; From 13a9c5bdc482ceb7992b725a3714efd2663b69bb Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 12 Jun 2018 10:16:43 +0100 Subject: [PATCH 287/339] ZXHawk: Added POKE memory menu option --- .../BizHawk.Client.EmuHawk.csproj | 9 + BizHawk.Client.EmuHawk/MainForm.Designer.cs | 24 +- BizHawk.Client.EmuHawk/MainForm.Events.cs | 5 + BizHawk.Client.EmuHawk/MainForm.cs | 1 + .../ZXSpectrumPokeMemory.Designer.cs | 165 +++++ .../config/ZXSpectrum/ZXSpectrumPokeMemory.cs | 39 ++ .../ZXSpectrum/ZXSpectrumPokeMemory.resx | 624 ++++++++++++++++++ .../SinclairSpectrum/ZXSpectrum.Util.cs | 254 +------ 8 files changed, 873 insertions(+), 248 deletions(-) create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.Designer.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.cs create mode 100644 BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.resx diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 5b51df2137..a5b9eaf9d6 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -509,6 +509,12 @@ ZXSpectrumAudioSettings.cs + + Form + + + ZXSpectrumPokeMemory.cs + Form @@ -1431,6 +1437,9 @@ ZXSpectrumAudioSettings.cs + + ZXSpectrumPokeMemory.cs + ZXSpectrumNonSyncSettings.cs diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 57d6c30767..d2d28f2b94 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -384,6 +384,7 @@ this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumCoreEmulationSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumAudioSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumNonSyncSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); @@ -457,7 +458,7 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.ZXSpectrumAudioSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumPokeMemoryMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -3401,7 +3402,8 @@ this.ZXSpectrumCoreEmulationSettingsMenuItem, this.ZXSpectrumControllerConfigurationMenuItem, this.ZXSpectrumAudioSettingsMenuItem, - this.ZXSpectrumNonSyncSettingsMenuItem}); + this.ZXSpectrumNonSyncSettingsMenuItem, + this.ZXSpectrumPokeMemoryMenuItem}); this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; @@ -3421,6 +3423,13 @@ this.ZXSpectrumControllerConfigurationMenuItem.Text = "Joystick Configuration"; this.ZXSpectrumControllerConfigurationMenuItem.Click += new System.EventHandler(this.ZXSpectrumControllerConfigurationMenuItem_Click); // + // ZXSpectrumAudioSettingsMenuItem + // + this.ZXSpectrumAudioSettingsMenuItem.Name = "ZXSpectrumAudioSettingsMenuItem"; + this.ZXSpectrumAudioSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumAudioSettingsMenuItem.Text = "Audio Settings"; + this.ZXSpectrumAudioSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumAudioSettingsMenuItem_Click); + // // ZXSpectrumNonSyncSettingsMenuItem // this.ZXSpectrumNonSyncSettingsMenuItem.Name = "ZXSpectrumNonSyncSettingsMenuItem"; @@ -4054,12 +4063,12 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // - // ZXSpectrumAudioSettingsMenuItem + // ZXSpectrumPokeMemoryMenuItem // - this.ZXSpectrumAudioSettingsMenuItem.Name = "ZXSpectrumAudioSettingsMenuItem"; - this.ZXSpectrumAudioSettingsMenuItem.Size = new System.Drawing.Size(201, 22); - this.ZXSpectrumAudioSettingsMenuItem.Text = "Audio Settings"; - this.ZXSpectrumAudioSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumAudioSettingsMenuItem_Click); + this.ZXSpectrumPokeMemoryMenuItem.Name = "ZXSpectrumPokeMemoryMenuItem"; + this.ZXSpectrumPokeMemoryMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumPokeMemoryMenuItem.Text = "POKE Memory"; + this.ZXSpectrumPokeMemoryMenuItem.Click += new System.EventHandler(this.ZXSpectrumPokeMemoryMenuItem_Click); // // MainForm // @@ -4530,5 +4539,6 @@ private System.Windows.Forms.ToolStripMenuItem ZXSpectrumCoreEmulationSettingsMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumNonSyncSettingsMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumAudioSettingsMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumPokeMemoryMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index ba02157085..3cbf90c572 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2487,6 +2487,11 @@ namespace BizHawk.Client.EmuHawk new ZXSpectrumAudioSettings().ShowDialog(); } + private void ZXSpectrumPokeMemoryMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumPokeMemory().ShowDialog(); + } + #endregion #region Help diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 25a6409e01..ff19f07096 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -4298,6 +4298,7 @@ namespace BizHawk.Client.EmuHawk { GenericCoreConfig.DoDialog(this, "PC-FX Settings"); } + private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording) { diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.Designer.cs new file mode 100644 index 0000000000..c2f10b196f --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.Designer.cs @@ -0,0 +1,165 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumPokeMemory + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumPokeMemory)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.numericUpDownAddress = new System.Windows.Forms.NumericUpDown(); + this.label3 = new System.Windows.Forms.Label(); + this.numericUpDownByte = new System.Windows.Forms.NumericUpDown(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownAddress)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownByte)).BeginInit(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(150, 109); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(216, 109); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(273, 13); + this.label1.TabIndex = 17; + this.label1.Text = "Enter an address to POKE along with a single byte value"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 52); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(93, 13); + this.label4.TabIndex = 27; + this.label4.Text = "Address (0-65535)"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 27); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(266, 13); + this.label2.TabIndex = 29; + this.label2.Text = "(Address values that target ROM space will be ignored)"; + // + // numericUpDownAddress + // + this.numericUpDownAddress.Location = new System.Drawing.Point(15, 69); + this.numericUpDownAddress.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this.numericUpDownAddress.Name = "numericUpDownAddress"; + this.numericUpDownAddress.Size = new System.Drawing.Size(90, 20); + this.numericUpDownAddress.TabIndex = 30; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(123, 52); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(70, 13); + this.label3.TabIndex = 31; + this.label3.Text = "Value (0-255)"; + // + // numericUpDownByte + // + this.numericUpDownByte.Location = new System.Drawing.Point(126, 68); + this.numericUpDownByte.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericUpDownByte.Name = "numericUpDownByte"; + this.numericUpDownByte.Size = new System.Drawing.Size(67, 20); + this.numericUpDownByte.TabIndex = 32; + // + // ZXSpectrumPokeMemory + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(288, 144); + this.Controls.Add(this.numericUpDownByte); + this.Controls.Add(this.label3); + this.Controls.Add(this.numericUpDownAddress); + this.Controls.Add(this.label2); + this.Controls.Add(this.label4); + this.Controls.Add(this.label1); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumPokeMemory"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Poke Memory"; + + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownAddress)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownByte)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.NumericUpDown numericUpDownAddress; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.NumericUpDown numericUpDownByte; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.cs new file mode 100644 index 0000000000..de0c799f24 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumPokeMemory : Form + { + private ZXSpectrum.ZXSpectrumSettings _settings; + + public ZXSpectrumPokeMemory() + { + InitializeComponent(); + } + + private void OkBtn_Click(object sender, EventArgs e) + { + var speccy = (ZXSpectrum)Global.Emulator; + var addr = (ushort)numericUpDownAddress.Value; + var val = (byte)numericUpDownByte.Value; + + speccy.PokeMemory(addr, val); + + DialogResult = DialogResult.OK; + Close(); + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("POKE memory aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAADLytDi9tOemQAAAAAAUy9EecLEsa1uPTUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPj11Mme + VakAAAAAAAAAAAAATS84M0akAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAgAAAAAAAAE + AAAAAAAAAAAAAAABAAAAAQAAAAAAAFFNTQBRUlIAU1RUAGJHRwBiT08Aa0lIAGJTUwBrVlYAYllZAGZc + XABpWloAb1xbAHNTUwB7V1YAc1hXAHFbWwBkZWUAaWFhAG5kZABpamkAcGFhAHlubgB2cHAAf3V1AH55 + eQB8fX0AgUpKAI1PTwCLWFcAhlhYAI9ZWQCKXFsAm1ZWAJJZWQCWWVgAmlpbAJtcWwCiXFwAl2BfAIBg + YACAZ2YAgG9vAI9oaACWZWQAmGBhAJ5kZACcaWoAmm9vAIV0dACNcHAAiXZ2AIB8fACac3IAm3V0AJ51 + dQCZfHwAnHx8AKNmZgCnZmYAqmJiAK5jYwCvb24AtWVmALBtbgC5bW0AvmxtAKx+fQCxcnIAtHBwALZz + dACydXQAtnd2ALlwcAC5dnYAt3p5ALh5eAC8fHsAun18ALx+fQDGb3AAxnBxAMdzdADAd3YAyHJzAMlz + dADJdXYAynd4AMd/fwDMe3wAzXx9AHunbwBhvHIAYsN4ANuLOwC2hn4A4Zt5APC3ZABte9sAX47+AHWM + 5QAl0foAY+P8AIeDgwCFhoYAioSEAJOIiACWi4sAmpKRAKGCgQCmhYUAqYGBAKuDhACniooApYyMAKiO + jQCyhYMAvoWEALeNjQCrj5AAr5eXALSVlAC9lJMAmbCEAK6RugDBgYAAwoSCAMWDhADChoQAxYeFAM6A + gQDFiIYAxoqIAMqIiQDMi4oAy4yKAMiPjQDPj44A0ISFANKJigDUi4wA04+NANWNjgDKkY8A0JCOANud + iQDWj5AAzJSTAM2XlgDGm5oA1pGSANOUkgDVl5EA1pOUANiVlgDYmJUA2ZeYANKenADbmpsA3pmYANuc + mgDbn5wA1aacAN6gngDqqZoA3Z+gAMyjowDCra0AxqysAMqpqQDboaAA3qKiAN6logDbp6UA3aWkANer + qgDWsbMA0rW0ANe0tADfs7IA4aSiAOGlpQDkp6UA46imAOWopgDsraIA6qimAOGoqADhrqwA6a2rAOqv + rADpsK4A7LGuAOGzswDlsbEA7bKxAO+1sgDotrYA5rm3AO+4twDot7sA6bq5AOu9uwDrv70A8bazAPG2 + tADxuLUA9Lm2APC9uwD2vboA9L+9APi+uwD4v7wA8sC+APXAvgD5wL0AkILJAKqXzACsu8cAqr/LALLV + 3QDawMIA48XFAOvDwQDswMAA7cTDAO/ExQDgxsgA8cbEAPTGxADwyskA9MvJAPLNzQD21dYA+NjZAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAMEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAqHCEcBQAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAayU9PSYbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdQlBSQiJpAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAM0pSUlJQPRcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnUlJSUlJGFQAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAFJSUlJSUkoQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzUlJSWVJZfxAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5XWYqKioqGDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASoqMkpqa + mqAsAAAAAAAAAAAAAAAAAABoNAAAAAAAAACMjJyuvLy2toYHAAAAAAAAAAAAABcOIDouBgAAAAAAc4yc + tsHKysPAriIKAAAAAAAAABYgRk1LTX+DEAAAAABukqXB4ejo4dHPQCIEChcXEwggTXV/k66unKMpAAAA + AG6Srsro6ero0dN/Rk1NRk2Dg4STrsbh4cHAt2sAAAAAbpKuOXPe6ajW15KGg4OGk528yuHo5eHPz882 + AAAAAAB4jCkDAxSoMabXt5yjt8ro3ePo5dbT09HTdAAAAAAAAABGcBFoGgFwdtfDwHxi2dpmZcrX09HP + z0MAAAAAAAAAAHh/qWwaOa6cz9PNZGPYsdzbzc3DwLk2AAAAAAAAAAAAAAAvhpKakoyg19HNyKS5wHtb + orZ/cwAAAAAAAAAAAAAAAAAANkaKWVm5zb1gYV6cXVxfNgAAAAAAAAAAAAAAAAAAALGvlTIuP1K5tqCR + l4xfLwAAAAAAAAAAAAAAAAAAsbPBenkAAAAAcCVYjE0scwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////////////////////////+f///+D////A////wH + ///4B///+Af///gH///wB///8Af///AH/+fwA/8D4AH8AeAAAAHgAAAB4AAAA+AAAAfwAAAP8AAAH/wA + AD//AAD//gAD//B4D////////////////////////////ygAAAAYAAAAMAAAAAEACAAAAAAAQAIAAAAA + AAAAAAAAAAEAAAABAAAAAAAAWlJSAHBJSQB1SEgAe1dXAHdYWAB5WlkAel1dAGBiYgB1bGwAfWtrAHh2 + dgB9fn4Ag01NAIRXVwCIV1cAhV9eAItbWgCgX14ApV1dAJhgXwCNYGAAnWtqAJhtbQCCdnYAh3x8AI15 + eACeensAqGBgAKhoZwCga2oArGpqALNqagCzb28AtG1tALltbQCxb3AApnVzAKlzcwCqdHMApnp6AKd+ + fgCpensAq3x7ALZ3dgC8dHQAvH59AMZvcADGcHEAxXN0AMhycwDJdncAynh5AMx5egDNfn8Ajo1wAOek + VgDGgH8A4p53AEZ2+gB8u4AAd8PaAIuEhACOh4cAjo6OAJ+DggCejo4Ao4SEAKSIiACsi4sAqo2MAK6P + jgC+gYAAvoaGAL+KiACskJAAtJeXALWenQC5np4At6iOAKmyjgC9nroAwYSDAMaGhADOhoYAxomHAMiK + iQDJjYwA0oeIANOOjwDUjY0A2ZiPANaPkADGkZEAx5eXAMySkADGnZwA1ZOSANeTlADWl5YA2JSVANGZ + mADan50A3J6dAOCcmwDVoJ8A7K2fAMOtrQDXo6IA3aCgAN+kpADVq6oA3ay3AMu0tADPtrYA3L+/AOCi + oQDhpqUA5KelAOinpgDlq6gA46usAOOvrQDqrqwA7LGuAOayswDjtrQA5re1AOqysQDts7EA57y6AO+8 + ugDrvL0A8LOwAPC1sgDwtrQA87q3APS6twD2vboA8b69APi/vAD2wb4A+cC9AJmTzwDHqMMAu8PMAIHf + 8QDByNAA7cLCAO3FwwDvxsQA5cjIAOzOzgDwxcQA9cbEAPPP0AD10tIAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + BQMJAAAAAAAAAAAAAAAAAAAAAAAAAAAPHBMNAAAAAAAAAAAAAAAAAAAAAAAAABojLy8TAAAAAAAAAAAA + AAAAAAAAAAAAAB0wMDAiPgAAAAAAAAAAAAAAAAAAAAAAQjAwMDAtGAAAAAAAAAAAAAAAAAAAAAAAFzIy + NTU5CgAAAAAAAAAAAAAAAAAAAAAAIjZYWFxcBwAAAAAAAAAAAAAAAAAAAAAANlxtdW11JQAAAAAAAAAA + PgcRDgkAAAAAXG1/lISAZgMAAAAAABkVLC5SVhcAAABNY3WWnJuLfB8UBAcQHkhWaX91dSsAAABNY2BM + mJeCiVJSVl9laX+WloSJgEIAAAAAXAEIC0tGjnR0dJaRk5qNjIyJQwAAAAAAJkNADBtdjIaPO1GSPYuJ + hnVEAAAAAAAAAClISWRcd4xwkGp8UE90VwAAAAAAAAAAAAAAKSQ1NYZ7OjhbPDdGAAAAAAAAAAAAAHNv + YGsAKyJoXFYmRwAAAAAAAAAAAAAAcnIAAAAAAAAATgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP// + /wD///8A////APx//wD4f/8A8H//APA//wDgP/8A4D//AOA//wDgP8EA4B8BAMAAAQDAAAEA4AADAOAA + BwDwAB8A/AA/APCA/wDn9/8A////AP///wD///8AKAAAABAAAAAgAAAAAQAIAAAAAAAAAQAAAAAAAAAA + AAAAAQAAAAEAAAAAAABjZGQAdmRjAHtpaQB/eHgAgU9PAKBaWgCFbm0AlWtqAKptbgCwZ2cAsGhoAKxw + cACteHkAvnJyAMZvcADGcHEAy3l5AMx9fgCFmXQAwIB/ANeUfQDhoX8AlIqJAJWMjACYiIgAoIaGAK2K + igCxh4cAvoGAALKKigC4iYgAuJWVAL2cnACss50AuqKhAL+mpgDLgoIAxImHAMeNjADLkI8AxpWTANCS + kQDYlZUA1J6dANqZmgDdnp4A1J+oAMaiogDOr68AzLKyANi5uADhpaIA4qypAOWtqADrrqsA4bKwAOay + sgDtuLYA57++AOy4uADxtLIA8be0APa9ugDswL4A9sG+ALCcxwC5ncIA06zBALnH0QC2ytQA7sPDAPLS + 0gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAZBgUAAAAAAAAAAAAAAAAACw8KAAAAAAAAAAAAAAAAGhAQDgAAAAAAAAAAAAAAAAkRESUYAAAA + AAAAAAAAAAAlKy4uBwAAAAAAAAcDAAAAKzlHPCYCAAAYCB0oKgAAAC0wSDs0FB0nLDlAOiwAAAANAQQb + Pi9DRkVBPzUAAAAAJB4cKz5EQjMiNSkAAAAAAAAAHwwRNxYVEyQAAAAAAAAxMgAAACEgAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AAD//wAA4/8AAOP/AADD/wAAwf8AAMH5 + AADAwQAAwAEAAMADAADABwAA8A8AAM5/AAD//wAA//8AACgAAAAwAAAAYAAAAAEAIAAAAAAAgCUAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAkAAAAJAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAUAAAAOAEBAVUAAABUAAAANQAAABAAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAkFBSUvGRl5TCkpwlYuLtxDJCTQFw0NmQAA + AEkAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACGAwMKE8rK6V6RET2klJR/5ZS + U/+OT0//ZDc38B0QEJoAAAAyAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYDAwYVzAwoopP + T/ygXVz/oFtb/55ZWf+bWFf/k1NT/1UvL9wGAwNcAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AARNKipxhk5O+adkY/+uZWX/tWdo/7VmZ/+qYWH/nltb/3hERPcfERGCAAAAFgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADEZGS1zQ0LXqGdm/7ptbf/Fb3D/x3Bx/8hwcf/BbW7/q2Vl/4hPT/82HR2gAAAAIAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAB1gxMYyYXl3/vXFx/8Zwcf/HcHH/x3Bx/8dwcf/HcHH/uG1t/5NY + V/9EJia2AAAAKQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPB8fNH1MS+K4cnH/x3Fy/8dwcf/HcHH/x3Bx/8dw + cf/HcHH/wHBx/51gX/9PLCzGAAAAMwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + 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 + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + 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 + + + \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs index 4cb9656858..0408016eec 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -7,249 +7,11 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// Utilities + /// public partial class ZXSpectrum { - /* - * CPU Helper Methods - */ - - public ushort RegPC - { - get { return (ushort)((_cpu.Regs[0] << 8 | _cpu.Regs[1])); } - set - { - _cpu.Regs[1] = (ushort)(value & 0xFF); - _cpu.Regs[0] = (ushort)((value >> 8) & 0xFF); - } - } - - public ushort RegIX - { - get { return (ushort)((_cpu.Regs[15] << 8 | _cpu.Regs[16] )); } - set - { - _cpu.Regs[16] = (ushort)(value & 0xFF); - _cpu.Regs[15] = (ushort)((value >> 8) & 0xFF); - } - } - - public ushort RegDE - { - get { return (ushort)((_cpu.Regs[8] << 8 | _cpu.Regs[9] )); } - set - { - _cpu.Regs[9] = (ushort)(value & 0xFF); - _cpu.Regs[8] = (ushort)((value >> 8) & 0xFF); - } - } - - public ushort RegAF - { - get { return (ushort)((_cpu.Regs[4] << 8 | _cpu.Regs[5])); } - set - { - _cpu.Regs[5] = (ushort)(value & 0xFF); - _cpu.Regs[4] = (ushort)((value >> 8) & 0xFF); - } - } - - - /// - /// Gets the IX word value - /// - /// - public ushort Get16BitIX() - { - return Convert.ToUInt16(_cpu.Regs[_cpu.Ixh] | _cpu.Regs[_cpu.Ixl] << 8); - } - - /// - /// Set the IX word value - /// - /// - /// - public void Set16BitIX(ushort IX) - { - _cpu.Regs[_cpu.Ixh] = (ushort)(IX & 0xFF); - _cpu.Regs[_cpu.Ixl] = (ushort)((IX >> 8) & 0xff); - } - - /// - /// Gets the AF word value - /// - /// - public ushort Get16BitAF() - { - return Convert.ToUInt16(_cpu.Regs[_cpu.A] | _cpu.Regs[_cpu.F] << 8); - } - - /// - /// Set the AF word value - /// - /// - /// - public void Set16BitAF(ushort AF) - { - _cpu.Regs[_cpu.A] = (ushort)(AF & 0xFF); - _cpu.Regs[_cpu.F] = (ushort)((AF >> 8) & 0xff); - } - - /// - /// Gets the AF shadow word value - /// - /// - public ushort Get16BitAF_() - { - return Convert.ToUInt16(_cpu.Regs[_cpu.A_s] | _cpu.Regs[_cpu.F_s] << 8); - } - - /// - /// Set the AF shadow word value - /// - /// - /// - public void Set16BitAF_(ushort AF_) - { - _cpu.Regs[_cpu.A_s] = (ushort)(AF_ & 0xFF); - _cpu.Regs[_cpu.F_s] = (ushort)((AF_ >> 8) & 0xff); - } - - /// - /// Gets the DE word value - /// - /// - public ushort Get16BitDE() - { - return Convert.ToUInt16(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8); - } - - /// - /// Set the DE word value - /// - /// - /// - public void Set16BitDE(ushort DE) - { - _cpu.Regs[_cpu.D] = (ushort)(DE & 0xFF); - _cpu.Regs[_cpu.E] = (ushort)((DE >> 8) & 0xff); - } - - - /// - /// Z80 Status Indicator Flag Reset masks - /// - /// - [Flags] - public enum FlagsResetMask : byte - { - /// Sign Flag - S = 0x7F, - - /// Zero Flag - Z = 0xBF, - - /// This flag is not used. - R5 = 0xDF, - - /// Half Carry Flag - H = 0xEF, - - /// This flag is not used. - R3 = 0xF7, - - /// Parity/Overflow Flag - PV = 0xFB, - - /// Add/Subtract Flag - N = 0xFD, - - /// Carry Flag - C = 0xFE, - } - - /// - /// Z80 Status Indicator Flag Set masks - /// - /// - [Flags] - public enum FlagsSetMask : byte - { - /// Sign Flag - /// - /// The Sign Flag (S) stores the state of the most-significant bit of - /// the Accumulator (bit 7). When the Z80 CPU performs arithmetic - /// operations on signed numbers, the binary twos complement notation - /// is used to represent and process numeric information. - /// - S = 0x80, - - /// - /// Zero Flag - /// - /// - /// The Zero Flag is set (1) or cleared (0) if the result generated by - /// the execution of certain instructions is 0. For 8-bit arithmetic and - /// logical operations, the Z flag is set to a 1 if the resulting byte in - /// the Accumulator is 0. If the byte is not 0, the Z flag is reset to 0. - /// - Z = 0x40, - - /// This flag is not used. - R5 = 0x20, - - /// Half Carry Flag - /// - /// The Half Carry Flag (H) is set (1) or cleared (0) depending on the - /// Carry and Borrow status between bits 3 and 4 of an 8-bit arithmetic - /// operation. This flag is used by the Decimal Adjust Accumulator (DAA) - /// instruction to correct the result of a packed BCD add or subtract operation. - /// - H = 0x10, - - /// This flag is not used. - R3 = 0x08, - - /// Parity/Overflow Flag - /// - /// The Parity/Overflow (P/V) Flag is set to a specific state depending on - /// the operation being performed. For arithmetic operations, this flag - /// indicates an overflow condition when the result in the Accumulator is - /// greater than the maximum possible number (+127) or is less than the - /// minimum possible number (–128). This overflow condition is determined by - /// examining the sign bits of the operands. - /// - PV = 0x04, - - /// Add/Subtract Flag - /// - /// The Add/Subtract Flag (N) is used by the Decimal Adjust Accumulator - /// instruction (DAA) to distinguish between the ADD and SUB instructions. - /// For ADD instructions, N is cleared to 0. For SUB instructions, N is set to 1. - /// - N = 0x02, - - /// Carry Flag - /// - /// The Carry Flag (C) is set or cleared depending on the operation being performed. - /// - C = 0x01, - - /// - /// Combination of S, Z, and PV - /// - SZPV = S | Z | PV, - - /// - /// Combination of N, and H - /// - NH = N | H, - - /// - /// Combination of R3, and R5 - /// - R3R5 = R3 | R5 - } - /// /// Helper method that returns a single INT32 from a BitArray /// @@ -264,5 +26,15 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum bitArray.CopyTo(array, 0); return array[0]; } + + /// + /// POKEs a memory bus address + /// + /// + /// + public void PokeMemory(ushort addr, byte value) + { + _machine.WriteBus(addr, value); + } } } From 651ec7f12228e9a5dc6acd2c9c1267f8ff01b7ca Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 12 Jun 2018 11:08:03 +0100 Subject: [PATCH 288/339] ZXHawk: CoreNameStatusBarButton tooltip shows emulated spectrum model --- BizHawk.Client.EmuHawk/MainForm.cs | 12 ++++++-- .../SinclairSpectrum/ZXSpectrum.Util.cs | 29 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index ff19f07096..1db1805b44 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -2752,8 +2752,16 @@ namespace BizHawk.Client.EmuHawk var attributes = Emulator.Attributes(); CoreNameStatusBarButton.Text = Emulator.DisplayName(); - CoreNameStatusBarButton.Image = Emulator.Icon(); - CoreNameStatusBarButton.ToolTipText = attributes.Ported ? "(ported) " : ""; + CoreNameStatusBarButton.Image = Emulator.Icon(); + CoreNameStatusBarButton.ToolTipText = attributes.Ported ? "(ported) " : ""; + + + if (Emulator.SystemId == "ZXSpectrum") + { + var core = (Emulation.Cores.Computers.SinclairSpectrum.ZXSpectrum)Emulator as Emulation.Cores.Computers.SinclairSpectrum.ZXSpectrum; + CoreNameStatusBarButton.ToolTipText = core.GetMachineType(); + + } } private void ToggleKeyPriority() diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs index 0408016eec..2444e87c30 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -36,5 +36,34 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { _machine.WriteBus(addr, value); } + + + public string GetMachineType() + { + string m = ""; + switch (SyncSettings.MachineType) + { + case MachineType.ZXSpectrum16: + m = "(Sinclair) ZX Spectrum 16K"; + break; + case MachineType.ZXSpectrum48: + m = "(Sinclair) ZX Spectrum 48K"; + break; + case MachineType.ZXSpectrum128: + m = "(Sinclair) ZX Spectrum 128K"; + break; + case MachineType.ZXSpectrum128Plus2: + m = "(Amstrad) ZX Spectrum 128K +2"; + break; + case MachineType.ZXSpectrum128Plus2a: + m = "(Amstrad) ZX Spectrum 128K +2a"; + break; + case MachineType.ZXSpectrum128Plus3: + m = "(Amstrad) ZX Spectrum 128K +3"; + break; + } + + return m; + } } } From 63593b4e2c0550b708248d5611c6eb8fb0fcceed Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 12 Jun 2018 11:40:11 +0100 Subject: [PATCH 289/339] ZXHawk: Media menu to change tapes and disks --- BizHawk.Client.EmuHawk/MainForm.Designer.cs | 65 +++++++++++++++++-- BizHawk.Client.EmuHawk/MainForm.Events.cs | 72 +++++++++++++++++++++ 2 files changed, 130 insertions(+), 7 deletions(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index d2d28f2b94..57df31fabc 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -386,6 +386,10 @@ this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumAudioSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ZXSpectrumNonSyncSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumPokeMemoryMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumMediaMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumTapesSubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumDisksSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton(); @@ -458,7 +462,8 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.ZXSpectrumPokeMemoryMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zxt1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zxt2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); @@ -3403,7 +3408,8 @@ this.ZXSpectrumControllerConfigurationMenuItem, this.ZXSpectrumAudioSettingsMenuItem, this.ZXSpectrumNonSyncSettingsMenuItem, - this.ZXSpectrumPokeMemoryMenuItem}); + this.ZXSpectrumPokeMemoryMenuItem, + this.ZXSpectrumMediaMenuItem}); this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; @@ -3437,6 +3443,41 @@ this.ZXSpectrumNonSyncSettingsMenuItem.Text = "Non-Sync Settings"; this.ZXSpectrumNonSyncSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumNonSyncSettingsMenuItem_Click); // + // ZXSpectrumPokeMemoryMenuItem + // + this.ZXSpectrumPokeMemoryMenuItem.Name = "ZXSpectrumPokeMemoryMenuItem"; + this.ZXSpectrumPokeMemoryMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumPokeMemoryMenuItem.Text = "POKE Memory"; + this.ZXSpectrumPokeMemoryMenuItem.Click += new System.EventHandler(this.ZXSpectrumPokeMemoryMenuItem_Click); + // + // ZXSpectrumMediaMenuItem + // + this.ZXSpectrumMediaMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ZXSpectrumTapesSubMenu, + this.ZXSpectrumDisksSubMenu}); + this.ZXSpectrumMediaMenuItem.Name = "ZXSpectrumMediaMenuItem"; + this.ZXSpectrumMediaMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumMediaMenuItem.Text = "Media"; + this.ZXSpectrumMediaMenuItem.DropDownOpened += new System.EventHandler(this.ZXSpectrumMediaMenuItem_DropDownOpened); + // + // ZXSpectrumTapesSubMenu + // + this.ZXSpectrumTapesSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.zxt1ToolStripMenuItem}); + this.ZXSpectrumTapesSubMenu.Name = "ZXSpectrumTapesSubMenu"; + this.ZXSpectrumTapesSubMenu.Size = new System.Drawing.Size(152, 22); + this.ZXSpectrumTapesSubMenu.Text = "Tapes"; + this.ZXSpectrumTapesSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumTapesSubMenu_DropDownOpened); + // + // ZXSpectrumDisksSubMenu + // + this.ZXSpectrumDisksSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.zxt2ToolStripMenuItem}); + this.ZXSpectrumDisksSubMenu.Name = "ZXSpectrumDisksSubMenu"; + this.ZXSpectrumDisksSubMenu.Size = new System.Drawing.Size(152, 22); + this.ZXSpectrumDisksSubMenu.Text = "Disks"; + this.ZXSpectrumDisksSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumDisksSubMenu_DropDownOpened); + // // Atari7800HawkCoreMenuItem // this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem"; @@ -4063,12 +4104,17 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // - // ZXSpectrumPokeMemoryMenuItem + // zxt1ToolStripMenuItem // - this.ZXSpectrumPokeMemoryMenuItem.Name = "ZXSpectrumPokeMemoryMenuItem"; - this.ZXSpectrumPokeMemoryMenuItem.Size = new System.Drawing.Size(201, 22); - this.ZXSpectrumPokeMemoryMenuItem.Text = "POKE Memory"; - this.ZXSpectrumPokeMemoryMenuItem.Click += new System.EventHandler(this.ZXSpectrumPokeMemoryMenuItem_Click); + this.zxt1ToolStripMenuItem.Name = "zxt1ToolStripMenuItem"; + this.zxt1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.zxt1ToolStripMenuItem.Text = "zxt1"; + // + // zxt2ToolStripMenuItem + // + this.zxt2ToolStripMenuItem.Name = "zxt2ToolStripMenuItem"; + this.zxt2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.zxt2ToolStripMenuItem.Text = "zxt2"; // // MainForm // @@ -4540,5 +4586,10 @@ private System.Windows.Forms.ToolStripMenuItem ZXSpectrumNonSyncSettingsMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumAudioSettingsMenuItem; private System.Windows.Forms.ToolStripMenuItem ZXSpectrumPokeMemoryMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumMediaMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumTapesSubMenu; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumDisksSubMenu; + private System.Windows.Forms.ToolStripMenuItem zxt1ToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem zxt2ToolStripMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 3cbf90c572..e9048cab55 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -25,6 +25,7 @@ using BizHawk.Emulation.Cores.Computers.AppleII; using BizHawk.Client.ApiHawk; using BizHawk.Emulation.Cores.Computers.Commodore64; using BizHawk.Emulation.Cores.Nintendo.Gameboy; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; namespace BizHawk.Client.EmuHawk { @@ -2492,6 +2493,77 @@ namespace BizHawk.Client.EmuHawk new ZXSpectrumPokeMemory().ShowDialog(); } + private void ZXSpectrumMediaMenuItem_DropDownOpened(object sender, EventArgs e) + { + if (Emulator is ZXSpectrum) + { + ZXSpectrumTapesSubMenu.Enabled = ((ZXSpectrum)Emulator)._tapeInfo.Count > 0; + ZXSpectrumDisksSubMenu.Enabled = ((ZXSpectrum)Emulator)._diskInfo.Count > 0; + } + } + + private void ZXSpectrumTapesSubMenu_DropDownOpened(object sender, EventArgs e) + { + ZXSpectrumTapesSubMenu.DropDownItems.Clear(); + + if (Emulator is ZXSpectrum) + { + var speccy = (ZXSpectrum)Emulator; + var currSel = speccy._machine.TapeMediaIndex; + + for (int i = 0; i < speccy._tapeInfo.Count; i++) + { + string name = speccy._tapeInfo[i].Name; + + var menuItem = new ToolStripMenuItem + { + Name = i + "_" + name, + Text = i + ": " + name, + Checked = currSel == i + }; + + int dummy = i; + menuItem.Click += (o, ev) => + { + speccy._machine.TapeMediaIndex = dummy; + }; + + ZXSpectrumTapesSubMenu.DropDownItems.Add(menuItem); + } + } + } + + private void ZXSpectrumDisksSubMenu_DropDownOpened(object sender, EventArgs e) + { + ZXSpectrumDisksSubMenu.DropDownItems.Clear(); + + if (Emulator is ZXSpectrum) + { + var speccy = (ZXSpectrum)Emulator; + var currSel = speccy._machine.DiskMediaIndex; + + for (int i = 0; i < speccy._diskInfo.Count; i++) + { + string name = speccy._diskInfo[i].Name; + + var menuItem = new ToolStripMenuItem + { + Name = i + "_" + name, + Text = i + ": " + name, + Checked = currSel == i + }; + + int dummy = i; + menuItem.Click += (o, ev) => + { + speccy._machine.DiskMediaIndex = dummy; + }; + + ZXSpectrumDisksSubMenu.DropDownItems.Add(menuItem); + } + } + } + #endregion #region Help From 5ec469f2f31b1b4ab3e95183d28307d4529b2845 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 12 Jun 2018 14:22:28 +0100 Subject: [PATCH 290/339] ZXHawk: Tape status (F10) now reports tape progress in addition to current block progress --- .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 8c7f6baf14..7b14980b30 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -410,6 +410,28 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum sb.Append(p.ToString("N0") + "%"); SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); sb.Clear(); + + // get position within the tape itself + sb.Append("Tape Pos: "); + var ind = _machine.TapeDevice.CurrentDataBlockIndex; + int cnt = 0; + for (int i = 0; i < ind; i++) + { + cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count; + } + // now we are at our current block + int ourPos = cnt + pos; + cnt += end; + // count periods in the remaining blocks + for (int i = ind + 1; i < _machine.TapeDevice.DataBlocks.Count; i++) + { + cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count; + } + // work out overall position within the tape + p = 0; + p = ((double)ourPos / (double)cnt) * (double)100; + sb.Append(p.ToString("N0") + "%"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); } #endregion From 8c3afc189a4a7a7d1faa49dfdd67c02d94dd0393 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 12 Jun 2018 16:51:21 -0400 Subject: [PATCH 291/339] z80: add a MEMRQ vector for memory contention for zx spectrum --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 1 + .../CPUs/Z80A/Interrupts.cs | 4 + .../CPUs/Z80A/Tables_Direct.cs | 86 ++++++++++++++----- .../CPUs/Z80A/Tables_Indirect.cs | 57 +++++++++--- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 12 ++- 5 files changed, 123 insertions(+), 37 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index 3912d5668d..6574be6ff3 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -22,6 +22,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public ushort instr_swap; public ushort[] cur_instr; public ushort[] BUSRQ; + public ushort[] MEMRQ; public byte opcode; public bool NO_prefix, CB_prefix, IX_prefix, EXTD_prefix, IY_prefix, IXCB_prefix, IYCB_prefix; public bool IXCB_prefetch, IYCB_prefetch; // value is fetched before opcode diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 389fdf6e8f..1781a5eebe 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -46,6 +46,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } // Mode 0 interrupts only take effect if a CALL or RST is on the data bus @@ -65,6 +66,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; } // Just jump to $0038 @@ -86,6 +88,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } // Interrupt mode 2 uses the I vector combined with a byte on the data bus @@ -113,6 +116,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0 ,0 ,PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index f91fb31afc..d5a2e8c3a2 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -15,7 +15,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } // NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers @@ -29,6 +30,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void EXX_() @@ -40,6 +42,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } // this exchanges 2 16 bit registers @@ -52,6 +55,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void INC_16(ushort src_l, ushort src_h) @@ -64,7 +68,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {I, I, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0}; + MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 }; } @@ -78,7 +83,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {I, I, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0}; + MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 }; } // this is done in two steps technically, but the flags don't work out using existing funcitons @@ -98,7 +104,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {I, I, I, I, I, I, I, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { I, I, I, I, I, I, I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP(ushort operation, ushort dest, ushort src) @@ -109,7 +116,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } // Operations using the I and R registers take one T-cycle longer @@ -123,6 +131,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, 0 }; } // note: do not use DEC here since no flags are affected by this operation @@ -145,7 +154,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {I, PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { I, PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { @@ -159,7 +169,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {I, PCh, 0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { I, PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -171,7 +182,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IDLE, HALT }; - BUSRQ = new ushort[] {PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void JR_COND(bool cond) @@ -192,7 +204,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { @@ -205,7 +218,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -225,7 +239,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0 }; } else { @@ -241,7 +256,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -259,7 +275,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void RETI_() @@ -276,7 +293,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void RETN_() @@ -293,7 +311,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } @@ -314,7 +333,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } else { @@ -325,7 +345,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {I, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, 0 }; } } @@ -338,10 +359,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A WAIT, RD_INC, Z, PCl, PCh, IDLE, - DEC16, SPl, SPh, WAIT, - RD_INC, W, PCl, PCh, - IDLE, + RD, W, PCl, PCh, + INC16, PCl, PCh, + DEC16, SPl, SPh, WAIT, WR_DEC, SPl, SPh, PCh, IDLE, @@ -352,7 +373,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] {PCh, 0, 0, PCh, 0, 0, PCh, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } else { @@ -369,6 +391,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -381,6 +404,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void BIT_OP(ushort operation, ushort bit, ushort src) @@ -392,6 +416,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void PUSH_(ushort src_l, ushort src_h) @@ -410,6 +435,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } @@ -428,6 +454,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } private void RST_(ushort n) @@ -446,6 +473,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void PREFIX_(ushort src) @@ -457,6 +485,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A PREFIX, src}; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void PREFETCH_(ushort src) @@ -483,6 +512,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A PREFIX, src,}; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0 }; } private void DI_() @@ -494,6 +524,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void EI_() @@ -505,6 +536,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void JP_16(ushort src_l, ushort src_h) @@ -516,6 +548,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { src_h, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0 }; } private void LD_SP_16(ushort src_l, ushort src_h) @@ -529,6 +562,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 }; } private void OUT_() @@ -546,7 +580,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP}; - BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0}; + BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; } private void OUT_REG_(ushort dest, ushort src) @@ -562,6 +597,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP}; BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; } private void IN_() @@ -580,6 +616,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP}; BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; } private void IN_REG_(ushort dest, ushort src) @@ -595,6 +632,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP}; BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; } private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -613,6 +651,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP}; BUSRQ = new ushort[] { I, I, I, I, I, I, I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void INT_MODE_(ushort src) @@ -624,6 +663,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void RRD_() @@ -645,6 +685,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void RLD_() @@ -666,6 +707,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index bab93cbe1f..9c13e3d062 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -18,6 +18,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) @@ -36,6 +37,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } // Note that this operation uses I_BIT, same as indexed BIT. @@ -55,6 +57,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, src_h, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -69,6 +72,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -83,6 +87,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } // different because HL doesn't effect WZ @@ -98,6 +103,7 @@ OP }; BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; } private void LD_16_IND_nn(ushort src_l, ushort src_h) @@ -121,6 +127,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_16_nn(ushort dest_l, ushort dest_h) @@ -144,6 +151,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND_nn(ushort src) @@ -164,6 +172,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_8_nn(ushort dest) @@ -184,6 +193,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src) @@ -198,6 +208,7 @@ OP }; BUSRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 }; } // seperate HL needed since it doesn't effect the WZ pair @@ -213,6 +224,7 @@ OP }; BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -230,6 +242,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h) @@ -244,6 +257,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -261,6 +275,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void INC_8_IND(ushort src_l, ushort src_h) @@ -279,6 +294,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void DEC_8_IND(ushort src_l, ushort src_h) @@ -297,6 +313,7 @@ OP }; BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } // NOTE: WZ implied for the wollowing 3 functions @@ -316,6 +333,7 @@ OP }; BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_OP(ushort operation, ushort bit, ushort dest) @@ -334,6 +352,7 @@ OP }; BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_TE(ushort bit) @@ -349,6 +368,7 @@ OP }; BUSRQ = new ushort[] { W, 0, 0, W, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { W, 0, 0, 0, PCh, 0, 0, 0 }; } private void I_OP_n(ushort operation, ushort src_l, ushort src_h) @@ -356,13 +376,13 @@ cur_instr = new ushort[] {IDLE, WAIT, - RD_INC, ALU, PCl, PCh, + RD, ALU, PCl, PCh, IDLE, IDLE, TR16, Z, W, src_l, src_h, ADDS, Z, W, ALU, ZERO, IDLE, - IDLE, + INC16, PCl, PCh, WAIT, RD, ALU, Z, W, operation, ALU, @@ -375,6 +395,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_OP_n_n(ushort src_l, ushort src_h) @@ -385,10 +406,10 @@ RD_INC, ALU, PCl, PCh, ADDS, Z, W, ALU, ZERO, WAIT, - RD_INC, ALU, PCl, PCh, - IDLE, + RD, ALU, PCl, PCh, IDLE, IDLE, + INC16, PCl, PCh, WAIT, WR, Z, W, ALU, IDLE, @@ -397,6 +418,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_REG_OP_IND_n(ushort operation, ushort dest, ushort src_l, ushort src_h) @@ -404,13 +426,13 @@ cur_instr = new ushort[] {IDLE, WAIT, - RD_INC, ALU, PCl, PCh, + RD, ALU, PCl, PCh, IDLE, TR16, Z, W, src_l, src_h, IDLE, ADDS, Z, W, ALU, ZERO, IDLE, - IDLE, + INC16, PCl, PCh, WAIT, RD, ALU, Z, W, operation, dest, ALU, @@ -419,6 +441,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src) @@ -426,13 +449,13 @@ cur_instr = new ushort[] {IDLE, WAIT, - RD_INC, ALU, PCl, PCh, + RD, ALU, PCl, PCh, IDLE, IDLE, TR16, Z, W, dest_l, dest_h, ADDS, Z, W, ALU, ZERO, IDLE, - IDLE, + INC16, PCl, PCh, WAIT, WR, Z, W, src, IDLE, @@ -441,6 +464,7 @@ OP }; BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, Z, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCh, 0, 0, 0 }; } private void LD_OP_R(ushort operation, ushort repeat_instr) @@ -456,6 +480,7 @@ SET_FL_LD_R, 0, operation, repeat_instr}; BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, D, D }; + MEMRQ = new ushort[] { H, 0, 0, D, 0, 0, 0, 0 }; } private void CP_OP_R(ushort operation, ushort repeat_instr) @@ -471,6 +496,7 @@ SET_FL_CP_R, 1, operation, repeat_instr}; BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, H }; + MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, 0 }; } private void IN_OP_R(ushort operation, ushort repeat_instr) @@ -486,6 +512,7 @@ REP_OP_I, L, H, ALU, operation, 2, operation, repeat_instr }; BUSRQ = new ushort[] { I, BIO1, BIO2, BIO3, BIO4, H, 0, 0}; + MEMRQ = new ushort[] { 0, BIO1, BIO2, BIO3, BIO4, H, 0, 0 }; } private void OUT_OP_R(ushort operation, ushort repeat_instr) @@ -501,6 +528,7 @@ REP_OP_O, C, B, ALU, operation, 3, operation, repeat_instr }; BUSRQ = new ushort[] { I, H, 0, 0, BIO1, BIO2, BIO3, BIO4 }; + MEMRQ = new ushort[] { 0, H, 0, 0, BIO1, BIO2, BIO3, BIO4 }; } // this is an indirect change of a a 16 bit register with memory @@ -509,25 +537,26 @@ cur_instr = new ushort[] {IDLE, WAIT, - RD, Z, dest_l, dest_h, - INC16, dest_l, dest_h, - IDLE, + RD_INC, Z, dest_l, dest_h, + IDLE, WAIT, RD, W, dest_l, dest_h, IDLE, + IDLE, WAIT, WR_DEC, dest_l, dest_h, src_h, - IDLE, - IDLE, IDLE, WAIT, WR, dest_l, dest_h, src_l, + IDLE, + IDLE, TR16, src_l, src_h, Z, W, WAIT, OP_F, OP }; - BUSRQ = new ushort[] { dest_h, 0, 0, 0, dest_h, 0, 0, dest_h, 0, 0, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { dest_h, 0, 0, dest_h, 0, 0, dest_h, dest_h, 0, 0, dest_h, 0, 0, dest_h, dest_h, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { dest_h, 0, 0, dest_h, 0, 0, 0, dest_h, 0, 0, dest_h, 0, 0, 0, 0, PCh, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index afd07e1979..a128c8958f 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -103,6 +103,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; instr_pntr = 0; bus_pntr = 0; NO_prefix = true; } @@ -509,6 +510,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP}; BUSRQ = new ushort[] { D, D, D, D, D, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { @@ -518,7 +520,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP_F, OP }; - BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } instr_pntr = 0; bus_pntr = 0; break; @@ -543,6 +546,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP}; BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { @@ -553,6 +557,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } instr_pntr = 0; bus_pntr = 0; break; @@ -620,6 +625,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP}; BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { @@ -630,6 +636,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } instr_pntr = 0; bus_pntr = 0; break; @@ -679,6 +686,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { @@ -689,6 +697,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A OP }; BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } instr_pntr = 0; bus_pntr = 0; break; @@ -767,6 +776,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("bus_pntr", ref bus_pntr); ser.Sync("cur_instr", ref cur_instr, false); ser.Sync("BUSRQ", ref BUSRQ, false); + ser.Sync("MEMRQ", ref MEMRQ, false); ser.Sync("instr_swap", ref instr_swap); ser.Sync("opcode", ref opcode); ser.Sync("FlagI", ref FlagI); From 9a906cd8af49e8b1bb6df2d5bbf90dc01bf3dbd6 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Tue, 12 Jun 2018 16:57:11 -0400 Subject: [PATCH 292/339] z80: add memptr variable --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 1 + BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 26 ++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index 6574be6ff3..9b308b76e3 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -19,6 +19,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // variables for executing instructions public int instr_pntr = 0; public int bus_pntr = 0; + public int mem_pntr = 0; public ushort instr_swap; public ushort[] cur_instr; public ushort[] BUSRQ; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index a128c8958f..a96e76beb8 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -104,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A BUSRQ = new ushort[] { PCh, 0, 0, 0 }; MEMRQ = new ushort[] { PCh, 0, 0, 0 }; - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; NO_prefix = true; } @@ -167,7 +167,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagI2 = FlagI1; FlagI1 = FlagI; - bus_pntr++; + bus_pntr++; mem_pntr++; switch (cur_instr[instr_pntr++]) { case IDLE: @@ -195,7 +195,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = false; NMI_(); NMICallback(); - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; } else if (iff1 && FlagI5) { @@ -222,7 +222,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A break; } IRQCallback(); - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; } else { @@ -230,7 +230,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (TraceCallback != null) TraceCallback(State()); RegPC++; FetchInstruction(); - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; } temp_R = (byte)(Regs[R] & 0x7F); @@ -301,7 +301,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -446,7 +446,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A RegPC++; FetchInstruction(); - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; // only the first prefix in a double prefix increases R, although I don't know how / why if (prefix_src < 4) { @@ -523,7 +523,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A BUSRQ = new ushort[] { PCh, 0, 0, 0 }; MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; case SET_FL_CP_R: SET_FL_CP_Func(); @@ -559,7 +559,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A BUSRQ = new ushort[] { PCh, 0, 0, 0 }; MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; case SET_FL_IR: SET_FL_IR_Func(cur_instr[instr_pntr++]); @@ -570,8 +570,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case WAIT: if (FlagW) { - instr_pntr--; - bus_pntr--; + instr_pntr--; bus_pntr--; mem_pntr--; } break; case RST: @@ -638,7 +637,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A BUSRQ = new ushort[] { PCh, 0, 0, 0 }; MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; case REP_OP_O: OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -699,7 +698,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A BUSRQ = new ushort[] { PCh, 0, 0, 0 }; MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } - instr_pntr = 0; bus_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; } TotalExecutedCycles++; @@ -774,6 +773,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("instr_pntr", ref instr_pntr); ser.Sync("bus_pntr", ref bus_pntr); + ser.Sync("mem_pntr", ref mem_pntr); ser.Sync("cur_instr", ref cur_instr, false); ser.Sync("BUSRQ", ref BUSRQ, false); ser.Sync("MEMRQ", ref MEMRQ, false); From 9166b0b9319d29b14993cd15b79cb6f169ca50f0 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 13 Jun 2018 07:55:23 +0100 Subject: [PATCH 293/339] ZXHawk: wire up +2a/+3 memory contention model (MREQ) --- .../Computers/SinclairSpectrum/Machine/CPUMonitor.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 8838abe75e..50ff4ced4a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -30,7 +30,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public ushort BUSRQ { - get { return _cpu.BUSRQ[_cpu.bus_pntr]; } + get + { + switch (machineType) + { + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + return _cpu.MEMRQ[_cpu.bus_pntr]; + default: + return _cpu.BUSRQ[_cpu.mem_pntr]; + } + } } #endregion From 7ce55e660176c08f1472ab8800f5373296cb90fc Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 13 Jun 2018 09:24:43 +0100 Subject: [PATCH 294/339] ZXHawk: ReadPort method was incorrectly snagging occational floating bus requests and processing them as kempston joystick input --- .../SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs | 6 ++++-- .../Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 6 ++++-- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 6 ++++-- .../SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs | 8 +++++--- 4 files changed, 17 insertions(+), 9 deletions(-) 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 0596dce779..a1dcc1427e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -43,9 +43,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (AYDevice.ReadPort(port, ref result)) return (byte)result; - // Kempston joystick input takes priority over all other input + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over keyboard input // if this is detected just return the kempston byte - if ((port & 0xe0) == 0 || (port & 0x20) == 0) + if (lowByte == 0x1f) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; 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 a2cd41da7d..214442e49f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -24,9 +24,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (AYDevice.ReadPort(port, ref result)) return (byte)result; - // Kempston joystick input takes priority over all other input + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over all keyboard input // if this is detected just return the kempston byte - if ((port & 0xe0) == 0 || (port & 0x20) == 0) + if (lowByte == 0x1f) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; 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 31c2e08e44..f71ab0af90 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -24,9 +24,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (AYDevice.ReadPort(port, ref result)) return (byte)result; - // Kempston joystick input takes priority over all other input + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over all keyboard input // if this is detected just return the kempston byte - if ((port & 0xe0) == 0 || (port & 0x20) == 0) + if (lowByte == 0x1f) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; 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 31fd2ac00b..632caceac2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -20,10 +20,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Check whether the low bit is reset // Technically the ULA should respond to every even I/O address bool lowBitReset = (port & 0x0001) == 0; - - // Kempston joystick input takes priority over all other input + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over keyboard input // if this is detected just return the kempston byte - if ((port & 0xe0) == 0 || (port & 0x20) == 0) + if (lowByte == 0x1f) { if (LocateUniqueJoystick(JoystickType.Kempston) != null) return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; @@ -31,6 +32,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // not a lag frame InputRead = true; } + // Even ports always address the ULA else if (lowBitReset) { // Even I/O address so get input from keyboard From 373db358054707a354ff91261d7644c0cfe6123c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 13 Jun 2018 11:53:05 +0100 Subject: [PATCH 295/339] ZXHawk: implemented +2a/+3 floating bus (confirmed working with 2017 release of 'A Yankee in Iraq') --- .../SinclairSpectrum/Machine/CPUMonitor.cs | 9 ++- .../Machine/SpectrumBase.Memory.cs | 7 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 2 + .../Computers/SinclairSpectrum/Machine/ULA.cs | 66 +++++++++++++++---- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 4 +- .../ZX128Plus2a.Memory.cs | 6 ++ .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 2 +- .../ZX128Plus2a.Screen.cs | 4 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 2 + .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 2 +- 11 files changed, 87 insertions(+), 19 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 50ff4ced4a..9ae88a6e8d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -62,6 +62,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public ushort lastPortAddr; + /// + /// If true, the next read memory operation has been contended + /// + public bool NextMemReadContended; + #endregion #region Methods @@ -87,6 +92,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (cont > 0) { _cpu.TotalExecutedCycles += cont; + NextMemReadContended = true; } } } @@ -393,7 +399,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public void SyncState(Serializer ser) { ser.BeginSection("CPUMonitor"); - ser.Sync("", ref lastPortAddr); + ser.Sync("lastPortAddr", ref lastPortAddr); + ser.Sync("NextMemReadContended", ref NextMemReadContended); ser.EndSection(); } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index 8bd72d3dcd..e3511847b8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// Signs that all paging is disabled /// If this is TRUE, then 128k and above machines need a hard reset before paging is allowed again /// - protected bool PagingDisabled; + public bool PagingDisabled; /// /// Index of the currently paged ROM @@ -87,6 +87,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// protected int PagingConfiguration; + /// + /// The last byte that was read after contended cycles + /// + public byte LastContendedReadByte; + #endregion #region Memory Related Methods diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 58753c45e5..c2968a08a2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -357,11 +357,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("PagingConfiguration", ref PagingConfiguration); ser.Sync("ROMhigh", ref ROMhigh); ser.Sync("ROMlow", ref ROMlow); + ser.Sync("LastContendedReadByte", ref LastContendedReadByte); KeyboardDevice.SyncState(ser); BuzzerDevice.SyncState(ser); TapeBuzzer.SyncState(ser); ULADevice.SyncState(ser); + CPUMon.SyncState(ser); if (AYDevice != null) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 2e9ff98b8c..448c7f9251 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -667,12 +667,31 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + /// + /// Generates the port lookup table for +2a/+3 allowed floating bus ports + /// + public void GenerateP3PortTable() + { + List table = new List(); + for (int i = 0; i < 0x1000; i++) + { + ushort r = (ushort)(1 + (4 * i)); + if (r > 4093) + break; + table.Add(r); + } + + Plus3FBPortTable = table.ToArray(); + } + + private ushort[] Plus3FBPortTable = new ushort[1]; + /// /// Returns floating bus value (if available) /// /// /// - public void ReadFloatingBus(int tstate, ref int result) + public void ReadFloatingBus(int tstate, ref int result, ushort port) { tstate += FloatingBusOffset; if (tstate >= RenderingTable.Renderer.Length) @@ -688,13 +707,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case MachineType.ZXSpectrum48: case MachineType.ZXSpectrum128: case MachineType.ZXSpectrum128Plus2: - /* - if (item.FloatingBusAddress > 0) - { - result = _machine.FetchScreenMemory(item.FloatingBusAddress); - //result = 0x00; - } - */ + switch (item.RAction) { case RenderTable.RenderAction.BorderAndFetchByte1: @@ -708,15 +721,46 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum result = _machine.FetchScreenMemory(item.AttributeAddress); break; default: - //result = _machine.FetchScreenMemory(fetchA2); break; } - - break; case MachineType.ZXSpectrum128Plus2a: case MachineType.ZXSpectrum128Plus3: + + // http://sky.relative-path.com/zx/floating_bus.html + if (_machine.PagingDisabled) + { + result = 0xff; + break; + } + + // check whether fb is found on this port + ushort pLook = Array.Find(Plus3FBPortTable, s => s == port); + if (pLook == 0) + { + result = 0xff; + break; + } + + // floating bus on +2a/+3 always returns a byte with Bit0 set + switch (item.RAction) + { + case RenderTable.RenderAction.BorderAndFetchByte1: + case RenderTable.RenderAction.Shift1AndFetchByte2: + case RenderTable.RenderAction.Shift2AndFetchByte1: + result = (byte)(_machine.FetchScreenMemory(item.ByteAddress) | 0x01); + break; + case RenderTable.RenderAction.BorderAndFetchAttribute1: + case RenderTable.RenderAction.Shift1AndFetchAttribute2: + case RenderTable.RenderAction.Shift2AndFetchAttribute1: + result = (byte)(_machine.FetchScreenMemory(item.AttributeAddress) | 0x01); + break; + default: + result = (byte)(_machine.LastContendedReadByte | 0x01); + break; + } + break; } } 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 a1dcc1427e..b34d860f86 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -32,7 +32,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // -asni (2018-06-08) - need this to pass the final portread tests from fusetest.tap // get the floating bus value - ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); // use this to set the paging registers WritePort(port, (byte)result); // return the floating bus value @@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!deviceAddressed) { // If this is an unused port the floating memory bus should be returned - ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); } return (byte)result; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 73400dd86c..ebb6f7c399 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -358,6 +358,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override byte ReadMemory(ushort addr) { var data = ReadBus(addr); + if (CPUMon.NextMemReadContended) + { + LastContendedReadByte = data; + CPUMon.NextMemReadContended = false; + } + return data; } 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 214442e49f..cf43ced6e5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -52,7 +52,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!deviceAddressed) { // If this is an unused port the floating memory bus should be returned - ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); } return (byte)result; 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 a95d06d8ce..53c0f3a0cb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // offsets RenderTableOffset = 58; ContentionOffset = 9; - FloatingBusOffset = 1; + FloatingBusOffset = 0; // timing ClockSpeed = 3546900; FrameCycleLength = 70908; @@ -43,6 +43,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum MachineType.ZXSpectrum128Plus2a); SetupScreenSize(); + + GenerateP3PortTable(); } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 87a9062912..fed504bf1c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -358,6 +358,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public override byte ReadMemory(ushort addr) { var data = ReadBus(addr); + if (CPUMon.NextMemReadContended) + LastContendedReadByte = data; return data; } 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 f71ab0af90..0f74207c16 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -56,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (!deviceAddressed) { // If this is an unused port the floating memory bus should be returned - ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); } return (byte)result; 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 632caceac2..e0a91aa2d6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -55,7 +55,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // If this is an unused port the floating memory bus should be returned - ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result); + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); } return (byte)result; From 837c681bd532d21d7b9269432f0c66cd568ef64e Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 13 Jun 2018 11:57:43 +0100 Subject: [PATCH 296/339] ZXHawk: missed a bool --- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index fed504bf1c..33509faa6b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -359,7 +359,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { var data = ReadBus(addr); if (CPUMon.NextMemReadContended) + { LastContendedReadByte = data; + CPUMon.NextMemReadContended = false; + } + return data; } From 93dab42ba6b71328b0557057c0c462749c234e5b Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 13 Jun 2018 08:29:07 -0400 Subject: [PATCH 297/339] z80: Start up values for SP and AF --- BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index 8ca466d5e2..ceb6017a7e 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -127,6 +127,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Regs[i] = 0; } + // These registers are set as part of the reset process + Regs[A] = 0xFF; + Regs[F] = 0xFF; + Regs[SPl] = 0xFF; + Regs[SPh] = 0xFF; + // the IRQ1 vector is 0x38 Regs[IRQ_V] = 0x38; // The NMI vector is constant 0x66 From ac95d9eb72c963d2f1d2325d71a16e0ac51792a3 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 13 Jun 2018 08:50:44 -0400 Subject: [PATCH 298/339] SMS: Remove unnecessary SP hack. --- BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs index ecdedb73d4..be58fdfc49 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs @@ -206,15 +206,6 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem Cpu.ReadMemory = ReadMemory; Cpu.WriteMemory = WriteMemory; - - // This particular game relies on an uninitialized Stack pointer to not be 0 or 0xFFFF - // It does not appear from documentation that the stack pointer is initialized to anything - // so just give it a value that doesn't break the game. - if (rom.HashSHA1() == "CBDBAAEB62A8483D9D5A8757B0F19233DFB9B416") - { - Cpu.Regs[Cpu.SPl] = 0x4; - Cpu.Regs[Cpu.SPh] = 0xFF; - } } // Constants From 2bae423df844f4a96c2d8ed38a97861d4bb217ea Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 13 Jun 2018 15:17:19 +0100 Subject: [PATCH 299/339] ZXHawk: Bit of a tidy & readme update --- .../BizHawk.Emulation.Cores.csproj | 7 - .../Hardware/SoundOuput/AYChip.cs | 788 ----------------- .../SinclairSpectrum/Machine/ULABase.cs | 796 ------------------ .../Machine/ZXSpectrum128K/ZX128.ULA.cs | 194 ----- .../ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs | 198 ----- .../ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs | 198 ----- .../Machine/ZXSpectrum48K/ZX48.ULA.cs | 211 ----- .../SinclairSpectrum/Media/MediaSerializer.cs | 126 --- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- .../Computers/SinclairSpectrum/readme.md | 57 +- 10 files changed, 27 insertions(+), 2550 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 6ac5775daf..de8a7e7314 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -281,27 +281,20 @@ - - - - - - - diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs deleted file mode 100644 index cac7b80229..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AYChip.cs +++ /dev/null @@ -1,788 +0,0 @@ - -using BizHawk.Common; -using BizHawk.Emulation.Common; -using System; -using System.Collections.Generic; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - /// - /// AY-3-8912 Emulated Device - /// - /// Based heavily on the YM-2149F / AY-3-8910 emulator used in Unreal Speccy - /// (Originally created under Public Domain license by SMT jan.2006) - /// - /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.cpp - /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.h - /// - public class AYChip : IPSG - { - #region Device Fields - - /// - /// The emulated machine (passed in via constructor) - /// - private SpectrumBase _machine; - - private int _tStatesPerFrame; - private int _sampleRate; - private int _samplesPerFrame; - private int _tStatesPerSample; - private short[] _audioBuffer; - private int _audioBufferIndex; - private int _lastStateRendered; - - #endregion - - #region Construction & Initialization - - /// - /// Main constructor - /// - public AYChip(SpectrumBase machine) - { - _machine = machine; - } - - /// - /// Initialises the AY chip - /// - public void Init(int sampleRate, int tStatesPerFrame) - { - InitTiming(sampleRate, tStatesPerFrame); - UpdateVolume(); - Reset(); - } - - #endregion - - #region IPortIODevice - - public bool ReadPort(ushort port, ref int value) - { - if (port != 0xfffd) - { - // port read is not addressing this device - return false; - } - - value = PortRead(); - - return true; - } - - public bool WritePort(ushort port, int value) - { - if (port == 0xfffd) - { - // register select - SelectedRegister = value & 0x0f; - return true; - } - else if (port == 0xbffd) - { - // Update the audiobuffer based on the current CPU cycle - // (this process the previous data BEFORE writing to the currently selected register) - int d = (int)(_machine.CurrentFrameCycle); - BufferUpdate(d); - - // write to register - PortWrite(value); - return true; - } - return false; - } - - #endregion - - #region AY Implementation - - #region Public Properties - - /// - /// AY mixer panning configuration - /// - [Flags] - public enum AYPanConfig - { - MONO = 0, - ABC = 1, - ACB = 2, - BAC = 3, - BCA = 4, - CAB = 5, - CBA = 6, - } - - /// - /// The AY panning configuration - /// - public AYPanConfig PanningConfiguration - { - get - { - return _currentPanTab; - } - set - { - if (value != _currentPanTab) - { - _currentPanTab = value; - UpdateVolume(); - } - } - } - - /// - /// The AY chip output volume - /// (0 - 100) - /// - public int Volume - { - get - { - return _volume; - } - set - { - //value = Math.Max(0, value); - //value = Math.Max(100, value); - if (_volume == value) - { - return; - } - _volume = value; - UpdateVolume(); - } - } - - /// - /// The currently selected register - /// - public int SelectedRegister - { - get { return _activeRegister; } - set - { - _activeRegister = (byte)value; - } - } - - #endregion - - #region Public Methods - - /// - /// Resets the PSG - /// - public void Reset() - { - - } - - /// - /// Reads the value from the currently selected register - /// - /// - public int PortRead() - { - return _registers[_activeRegister]; - } - - /// - /// Writes to the currently selected register - /// - /// - public void PortWrite(int value) - { - if (_activeRegister >= 0x10) - return; - - byte val = (byte)value; - - if (((1 << _activeRegister) & ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 13))) != 0) - val &= 0x0F; - - if (((1 << _activeRegister) & ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10))) != 0) - val &= 0x1F; - - if (_activeRegister != 13 && _registers[_activeRegister] == val) - return; - - _registers[_activeRegister] = val; - - switch (_activeRegister) - { - // Channel A (Combined Pitch) - // (not written to directly) - case 0: - case 1: - _dividerA = _registers[AY_A_FINE] | (_registers[AY_A_COARSE] << 8); - break; - // Channel B (Combined Pitch) - // (not written to directly) - case 2: - case 3: - _dividerB = _registers[AY_B_FINE] | (_registers[AY_B_COARSE] << 8); - break; - // Channel C (Combined Pitch) - // (not written to directly) - case 4: - case 5: - _dividerC = _registers[AY_C_FINE] | (_registers[AY_C_COARSE] << 8); - break; - // Noise Pitch - case 6: - _dividerN = val * 2; - break; - // Mixer - case 7: - _bit0 = 0 - ((val >> 0) & 1); - _bit1 = 0 - ((val >> 1) & 1); - _bit2 = 0 - ((val >> 2) & 1); - _bit3 = 0 - ((val >> 3) & 1); - _bit4 = 0 - ((val >> 4) & 1); - _bit5 = 0 - ((val >> 5) & 1); - break; - // Channel Volumes - case 8: - _eMaskA = (val & 0x10) != 0 ? -1 : 0; - _vA = ((val & 0x0F) * 2 + 1) & ~_eMaskA; - break; - case 9: - _eMaskB = (val & 0x10) != 0 ? -1 : 0; - _vB = ((val & 0x0F) * 2 + 1) & ~_eMaskB; - break; - case 10: - _eMaskC = (val & 0x10) != 0 ? -1 : 0; - _vC = ((val & 0x0F) * 2 + 1) & ~_eMaskC; - break; - // Envelope (Combined Duration) - // (not written to directly) - case 11: - case 12: - _dividerE = _registers[AY_E_FINE] | (_registers[AY_E_COARSE] << 8); - break; - // Envelope Shape - case 13: - // reset the envelope counter - _countE = 0; - - if ((_registers[AY_E_SHAPE] & 4) != 0) - { - // attack - _eState = 0; - _eDirection = 1; - } - else - { - // decay - _eState = 31; - _eDirection = -1; - } - break; - case 14: - // IO Port - not implemented - break; - } - } - - /// - /// Start of frame - /// - public void StartFrame() - { - _audioBufferIndex = 0; - BufferUpdate(0); - } - - /// - /// End of frame - /// - public void EndFrame() - { - BufferUpdate(_tStatesPerFrame); - } - - /// - /// Updates the audiobuffer based on the current frame t-state - /// - /// - public void UpdateSound(int frameCycle) - { - BufferUpdate(frameCycle); - } - - #endregion - - #region Private Fields - - /// - /// Register indicies - /// - private const int AY_A_FINE = 0; - private const int AY_A_COARSE = 1; - private const int AY_B_FINE = 2; - private const int AY_B_COARSE = 3; - private const int AY_C_FINE = 4; - private const int AY_C_COARSE = 5; - private const int AY_NOISEPITCH = 6; - private const int AY_MIXER = 7; - private const int AY_A_VOL = 8; - private const int AY_B_VOL = 9; - private const int AY_C_VOL = 10; - private const int AY_E_FINE = 11; - private const int AY_E_COARSE = 12; - private const int AY_E_SHAPE = 13; - private const int AY_PORT_A = 14; - private const int AY_PORT_B = 15; - - /// - /// The register array - /* - The AY-3-8910/8912 contains 16 internal registers as follows: - - Register Function Range - 0 Channel A fine pitch 8-bit (0-255) - 1 Channel A course pitch 4-bit (0-15) - 2 Channel B fine pitch 8-bit (0-255) - 3 Channel B course pitch 4-bit (0-15) - 4 Channel C fine pitch 8-bit (0-255) - 5 Channel C course pitch 4-bit (0-15) - 6 Noise pitch 5-bit (0-31) - 7 Mixer 8-bit (see below) - 8 Channel A volume 4-bit (0-15, see below) - 9 Channel B volume 4-bit (0-15, see below) - 10 Channel C volume 4-bit (0-15, see below) - 11 Envelope fine duration 8-bit (0-255) - 12 Envelope course duration 8-bit (0-255) - 13 Envelope shape 4-bit (0-15) - 14 I/O port A 8-bit (0-255) - 15 I/O port B 8-bit (0-255) (Not present on the AY-3-8912) - - * The volume registers (8, 9 and 10) contain a 4-bit setting but if bit 5 is set then that channel uses the - envelope defined by register 13 and ignores its volume setting. - * The mixer (register 7) is made up of the following bits (low=enabled): - - Bit: 7 6 5 4 3 2 1 0 - Register: I/O I/O Noise Noise Noise Tone Tone Tone - Channel: B A C B A C B A - - The AY-3-8912 ignores bit 7 of this register. - */ - /* - /// - private int[] _registers = new int[16]; - - /// - /// The currently selected register - /// - private byte _activeRegister; - - /// - /// The frequency of the AY chip - /// - private static int _chipFrequency = 1773400; - - /// - /// The rendering resolution of the chip - /// - private double _resolution = 50D * 8D / _chipFrequency; - - /// - /// Channel generator state - /// - private int _bitA; - private int _bitB; - private int _bitC; - - /// - /// Envelope state - /// - private int _eState; - - /// - /// Envelope direction - /// - private int _eDirection; - - /// - /// Noise seed - /// - private int _noiseSeed; - - /// - /// Mixer state - /// - private int _bit0; - private int _bit1; - private int _bit2; - private int _bit3; - private int _bit4; - private int _bit5; - - /// - /// Noise generator state - /// - private int _bitN; - - /// - /// Envelope masks - /// - private int _eMaskA; - private int _eMaskB; - private int _eMaskC; - - /// - /// Amplitudes - /// - private int _vA; - private int _vB; - private int _vC; - - /// - /// Channel gen counters - /// - private int _countA; - private int _countB; - private int _countC; - - /// - /// Envelope gen counter - /// - private int _countE; - - /// - /// Noise gen counter - /// - private int _countN; - - /// - /// Channel gen dividers - /// - private int _dividerA; - private int _dividerB; - private int _dividerC; - - /// - /// Envelope gen divider - /// - private int _dividerE; - - /// - /// Noise gen divider - /// - private int _dividerN; - - /// - /// Panning table list - /// - private static List PanTabs = new List - { - // MONO - new uint[] { 50,50, 50,50, 50,50 }, - // ABC - new uint[] { 100,10, 66,66, 10,100 }, - // ACB - new uint[] { 100,10, 10,100, 66,66 }, - // BAC - new uint[] { 66,66, 100,10, 10,100 }, - // BCA - new uint[] { 10,100, 100,10, 66,66 }, - // CAB - new uint[] { 66,66, 10,100, 100,10 }, - // CBA - new uint[] { 10,100, 66,66, 100,10 } - }; - - /// - /// The currently selected panning configuration - /// - private AYPanConfig _currentPanTab = AYPanConfig.ABC; - - /// - /// The current volume - /// - private int _volume = 75; - - /// - /// Volume tables state - /// - private uint[][] _volumeTables; - - /// - /// Volume table to be used - /// - private static uint[] AYVolumes = new uint[] - { - 0x0000,0x0000,0x0340,0x0340,0x04C0,0x04C0,0x06F2,0x06F2, - 0x0A44,0x0A44,0x0F13,0x0F13,0x1510,0x1510,0x227E,0x227E, - 0x289F,0x289F,0x414E,0x414E,0x5B21,0x5B21,0x7258,0x7258, - 0x905E,0x905E,0xB550,0xB550,0xD7A0,0xD7A0,0xFFFF,0xFFFF, - }; - - #endregion - - #region Private Methods - - /// - /// Forces an update of the volume tables - /// - private void UpdateVolume() - { - int upperFloor = 40000; - var inc = (0xFFFF - upperFloor) / 100; - - var vol = inc * _volume; // ((ulong)0xFFFF * (ulong)_volume / 100UL) - 20000 ; - _volumeTables = new uint[6][]; - - // parent array - for (int j = 0; j < _volumeTables.Length; j++) - { - _volumeTables[j] = new uint[32]; - - // child array - for (int i = 0; i < _volumeTables[j].Length; i++) - { - _volumeTables[j][i] = (uint)( - (PanTabs[(int)_currentPanTab][j] * AYVolumes[i] * vol) / - (3 * 65535 * 100)); - } - } - } - - private int mult_const; - - /// - /// Initializes timing information for the frame - /// - /// - /// - private void InitTiming(int sampleRate, int frameTactCount) - { - _sampleRate = sampleRate; - _tStatesPerFrame = frameTactCount; - _samplesPerFrame = 882; - - _tStatesPerSample = 79; //(int)Math.Round(((double)_tStatesPerFrame * 50D) / - //(16D * (double)_sampleRate), - //MidpointRounding.AwayFromZero); - - //_samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; - _audioBuffer = new short[_samplesPerFrame * 2]; //[_sampleRate / 50]; - _audioBufferIndex = 0; - - mult_const = ((_chipFrequency / 8) << 14) / _machine.ULADevice.ClockSpeed; - - var aytickspercputick = (double)_machine.ULADevice.ClockSpeed / (double)_chipFrequency; - int ayCyclesPerSample = (int)((double)_tStatesPerSample * (double)aytickspercputick); - } - - /// - /// Updates the audiobuffer based on the current frame t-state - /// - /// - private void BufferUpdate(int cycle) - { - if (cycle > _tStatesPerFrame) - { - // we are outside of the frame - just process the last value - cycle = _tStatesPerFrame; - } - - // get the current length of the audiobuffer - int bufferLength = _samplesPerFrame; // _audioBuffer.Length; - - int toEnd = ((bufferLength * cycle) / _tStatesPerFrame); - - // loop through the number of samples we need to render - while(_audioBufferIndex < toEnd) - { - // run the AY chip processing at the correct resolution - for (int i = 0; i < _tStatesPerSample / 14; i++) - { - if (++_countA >= _dividerA) - { - _countA = 0; - _bitA ^= -1; - } - - if (++_countB >= _dividerB) - { - _countB = 0; - _bitB ^= -1; - } - - if (++_countC >= _dividerC) - { - _countC = 0; - _bitC ^= -1; - } - - if (++_countN >= _dividerN) - { - _countN = 0; - _noiseSeed = (_noiseSeed * 2 + 1) ^ (((_noiseSeed >> 16) ^ (_noiseSeed >> 13)) & 1); - _bitN = 0 - ((_noiseSeed >> 16) & 1); - } - - if (++_countE >= _dividerE) - { - _countE = 0; - _eState += +_eDirection; - - if ((_eState & ~31) != 0) - { - var mask = (1 << _registers[AY_E_SHAPE]); - - if ((mask & ((1 << 0) | (1 << 1) | (1 << 2) | - (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | - (1 << 7) | (1 << 9) | (1 << 15))) != 0) - { - _eState = _eDirection = 0; - } - else if ((mask & ((1 << 8) | (1 << 12))) != 0) - { - _eState &= 31; - } - else if ((mask & ((1 << 10) | (1 << 14))) != 0) - { - _eDirection = -_eDirection; - _eState += _eDirection; - } - else - { - // 11,13 - _eState = 31; - _eDirection = 0; - } - } - } - } - - // mix the sample - var mixA = ((_eMaskA & _eState) | _vA) & ((_bitA | _bit0) & (_bitN | _bit3)); - var mixB = ((_eMaskB & _eState) | _vB) & ((_bitB | _bit1) & (_bitN | _bit4)); - var mixC = ((_eMaskC & _eState) | _vC) & ((_bitC | _bit2) & (_bitN | _bit5)); - - var l = _volumeTables[0][mixA]; - var r = _volumeTables[1][mixA]; - - l += _volumeTables[2][mixB]; - r += _volumeTables[3][mixB]; - l += _volumeTables[4][mixC]; - r += _volumeTables[5][mixC]; - - _audioBuffer[_audioBufferIndex * 2] = (short)l; - _audioBuffer[(_audioBufferIndex * 2) + 1] = (short)r; - - _audioBufferIndex++; - } - - _lastStateRendered = cycle; - } - - #endregion - - #endregion - - #region ISoundProvider - - public bool CanProvideAsync => false; - - public SyncSoundMode SyncMode => SyncSoundMode.Sync; - - public void SetSyncMode(SyncSoundMode mode) - { - if (mode != SyncSoundMode.Sync) - throw new InvalidOperationException("Only Sync mode is supported."); - } - - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public void DiscardSamples() - { - _audioBuffer = new short[_samplesPerFrame * 2]; - } - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - nsamp = _samplesPerFrame; - samples = _audioBuffer; - DiscardSamples(); - } - - #endregion - - #region State Serialization - - public int nullDump = 0; - - /// - /// State serialization - /// - /// - public void SyncState(Serializer ser) - { - ser.BeginSection("PSG-AY"); - - ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); - ser.Sync("_sampleRate", ref _sampleRate); - ser.Sync("_samplesPerFrame", ref _samplesPerFrame); - ser.Sync("_tStatesPerSample", ref _tStatesPerSample); - ser.Sync("_audioBufferIndex", ref _audioBufferIndex); - ser.Sync("_audioBuffer", ref _audioBuffer, false); - - ser.Sync("_registers", ref _registers, false); - ser.Sync("_activeRegister", ref _activeRegister); - ser.Sync("_bitA", ref _bitA); - ser.Sync("_bitB", ref _bitB); - ser.Sync("_bitC", ref _bitC); - ser.Sync("_eState", ref _eState); - ser.Sync("_eDirection", ref _eDirection); - ser.Sync("_noiseSeed", ref _noiseSeed); - ser.Sync("_bit0", ref _bit0); - ser.Sync("_bit1", ref _bit1); - ser.Sync("_bit2", ref _bit2); - ser.Sync("_bit3", ref _bit3); - ser.Sync("_bit4", ref _bit4); - ser.Sync("_bit5", ref _bit5); - ser.Sync("_bitN", ref _bitN); - ser.Sync("_eMaskA", ref _eMaskA); - ser.Sync("_eMaskB", ref _eMaskB); - ser.Sync("_eMaskC", ref _eMaskC); - ser.Sync("_vA", ref _vA); - ser.Sync("_vB", ref _vB); - ser.Sync("_vC", ref _vC); - ser.Sync("_countA", ref _countA); - ser.Sync("_countB", ref _countB); - ser.Sync("_countC", ref _countC); - ser.Sync("_countE", ref _countE); - ser.Sync("_countN", ref _countN); - ser.Sync("_dividerA", ref _dividerA); - ser.Sync("_dividerB", ref _dividerB); - ser.Sync("_dividerC", ref _dividerC); - ser.Sync("_dividerE", ref _dividerE); - ser.Sync("_dividerN", ref _dividerN); - ser.SyncEnum("_currentPanTab", ref _currentPanTab); - ser.Sync("_volume", ref nullDump); - - for (int i = 0; i < 6; i++) - { - ser.Sync("volTable" + i, ref _volumeTables[i], false); - } - - if (ser.IsReader) - _volume = _machine.Spectrum.Settings.AYVolume; - - ser.EndSection(); - } - - #endregion - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs deleted file mode 100644 index c7361f27cb..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULABase.cs +++ /dev/null @@ -1,796 +0,0 @@ - -using BizHawk.Common; -using BizHawk.Emulation.Common; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - /// - /// ULA (Uncommitted Logic Array) implementation - /// - public abstract class ULABase : IVideoProvider - { - #region General - - /// - /// Length of the frame in T-States - /// - public int FrameLength; - - /// - /// Emulated clock speed - /// - public int ClockSpeed; - - /// - /// Whether machine is late or early timing model - /// - public bool LateTiming; //currently not implemented - - /// - /// The current cycle within the current frame - /// - public int CurrentTStateInFrame; - - - protected SpectrumBase _machine; - - #endregion - - #region Palettes - - /// - /// The standard ULA palette - /// - private static readonly int[] ULAPalette = - { - Colors.ARGB(0x00, 0x00, 0x00), // Black - Colors.ARGB(0x00, 0x00, 0xD7), // Blue - Colors.ARGB(0xD7, 0x00, 0x00), // Red - Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta - Colors.ARGB(0x00, 0xD7, 0x00), // Green - Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan - Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow - Colors.ARGB(0xD7, 0xD7, 0xD7), // White - Colors.ARGB(0x00, 0x00, 0x00), // Bright Black - Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue - Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red - Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta - Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green - Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan - Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow - Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White - }; - - #endregion - - #region Contention - - /// - /// T-State at which to start applying contention - /// - public int contentionStartPeriod; - /// - /// T-State at which to end applying contention - /// - public int contentionEndPeriod; - /// - /// T-State memory contention delay mapping - /// - public byte[] contentionTable; - - /// - /// Contention offset (used for testing) - /// - public int contentionOffset = 0; // -5; - - #endregion - - #region Screen Rendering - - /// - /// Video output buffer - /// - public int[] ScreenBuffer; - - /// - /// Screen rendering T-State info - /// - public RenderCycle[] RenderTable; - - /// - /// Display memory - /// - protected byte[] screen; - /// - /// Attribute memory lookup (mapped 1:1 to screen for convenience) - /// - protected short[] attr; - /// - /// T-State display mapping - /// - protected short[] tstateToDisp; - /// - /// Table that stores T-State to screen/attribute address values - /// - public short[] floatingBusTable; - /// - /// Cycle at which the last render update took place - /// - protected long lastTState; - /// - /// T-States elapsed since last render update - /// - protected long elapsedTStates; - /// - /// T-State of top left raster pixel - /// - protected int actualULAStart; - /// - /// Offset into display memory based on current T-State - /// - protected int screenByteCtr; - /// - /// Offset into current pixel of rasterizer - /// - protected int ULAByteCtr; - /// - /// The current border colour - /// - public int borderColour; - /// - /// Signs whether the colour flash is ON or OFF - /// - protected bool flashOn = false; - - - protected int flashCounter; - public int FlashCounter - { - get { return flashCounter; } - set - { - flashCounter = value; - } - } - - - /// - /// Internal frame counter used for flasher operations - /// - protected int frameCounter = 0; - - /// - /// Last 8-bit bitmap read from display memory - /// (Floating bus implementation) - /// - protected int lastPixelValue; - /// - /// Last 8-bit attr val read from attribute memory - /// (Floating bus implementation) - /// - protected int lastAttrValue; - /// - /// Last 8-bit bitmap read from display memory+1 - /// (Floating bus implementation) - /// - protected int lastPixelValuePlusOne; - /// - /// Last 8-bit attr val read from attribute memory+1 - /// (Floating bus implementation) - /// - protected int lastAttrValuePlusOne; - - /// - /// Used to create the non-border display area - /// - protected int TtateAtLeft; - protected int TstateWidth; - protected int TstateAtTop; - protected int TstateHeight; - protected int TstateAtRight; - protected int TstateAtBottom; - - /// - /// Total T-States in one scanline - /// - protected int TstatesPerScanline; - /// - /// Total pixels in one scanline - /// - protected int ScanLineWidth; - /// - /// Total chars in one PRINT row - /// - protected int CharRows; - /// - /// Total chars in one PRINT column - /// - protected int CharCols; - /// - /// Total pixels in one display row - /// - protected int ScreenWidth; - /// - /// Total pixels in one display column - /// - protected int ScreenHeight; - /// - /// Total pixels in top border - /// - protected int BorderTopHeight; - /// - /// Total pixels in bottom border - /// - protected int BorderBottomHeight; - /// - /// Total pixels in left border width - /// - protected int BorderLeftWidth; - /// - /// Total pixels in right border width - /// - protected int BorderRightWidth; - /// - /// Memory address of display start - /// - protected int DisplayStart; - /// - /// Total number of bytes of display memory - /// - protected int DisplayLength; - /// - /// Memory address of attribute start - /// - protected int AttributeStart; - /// - /// Total number of bytes of attribute memory - /// - protected int AttributeLength; - - /// - /// Raised when ULA has finished painting the entire screen - /// - public bool needsPaint = false; - - #endregion - - #region Interrupt - - /// - /// The t-state within the frame that an interrupt is raised - /// - public int InterruptStart; - - /// - /// The number of T-States that the INT pin is simulated to be held low - /// - public int InterruptDuration = 32; - - /// - /// Signs that an interrupt has been raised in this frame. - /// - protected bool InterruptRaised; - - /// - /// Signs that the interrupt signal has been revoked - /// - protected bool InterruptRevoked; - - /// - /// Resets the interrupt - this should happen every frame in order to raise - /// the VBLANK interrupt in the proceding frame - /// - public virtual void ResetInterrupt() - { - InterruptRaised = false; - InterruptRevoked = false; - } - - /// - /// Generates an interrupt in the current phase if needed - /// - /// - public virtual void CheckForInterrupt(long currentCycle) - { - if (InterruptRevoked) - { - // interrupt has already been handled - return; - } - - if (currentCycle < InterruptStart) - { - // interrupt does not need to be raised yet - return; - } - - if (currentCycle > InterruptStart + InterruptDuration) - { - // 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; - - // Signal the start of ULA processing - if (_machine._render) - ULAUpdateStart(); - - CalcFlashCounter(); - } - - #endregion - - #region Construction & Initialisation - - public ULABase(SpectrumBase machine) - { - _machine = machine; - borderType = _machine.Spectrum.SyncSettings.BorderType; - } - - #endregion - - #region Methods - - /// - /// Resets the ULA chip - /// - public abstract void Reset(); - - /// - /// Builds the contention table for the emulated model - /// - public abstract void BuildContentionTable(); - - /// - /// Returns true if the given memory address should be contended - /// - /// - /// - public abstract bool IsContended(int addr); - - - /// - /// Resets render state once interrupt is generated - /// - public void ULAUpdateStart() - { - ULAByteCtr = 0; - screenByteCtr = DisplayStart; - lastTState = actualULAStart; - needsPaint = true; - } - - /// - /// Flash processing - /// - public void CalcFlashCounter() - { - flashCounter++; - - if (flashCounter > 15) - { - flashOn = !flashOn; - flashCounter = 0; - } - } - - - /// - /// Builds the T-State to attribute map used with the floating bus - /// - public void BuildAttributeMap() - { - int start = DisplayStart; - - for (int f = 0; f < DisplayLength; f++, start++) - { - int addrH = start >> 8; //div by 256 - int addrL = start % 256; - - int pixelY = (addrH & 0x07); - pixelY |= (addrL & (0xE0)) >> 2; - pixelY |= (addrH & (0x18)) << 3; - - int attrIndex_Y = AttributeStart + ((pixelY >> 3) << 5);// pixel/8 * 32 - - addrL = start % 256; - int pixelX = addrL & (0x1F); - - attr[f] = (short)(attrIndex_Y + pixelX); - } - } - - /// - /// Returns the floating bus value - /// - public virtual void ReadFloatingBus(ref int result) - { - // Floating bus is read on the previous cycle - long _tStates = _machine.CurrentFrameCycle - 1; - - // if we are on the top or bottom border return 0xff - if ((_tStates < contentionStartPeriod) || (_tStates > contentionEndPeriod)) - { - result = 0xff; - } - else - { - if (floatingBusTable[_tStates] < 0) - { - result = 0xff; - } - else - { - result = _machine.ReadBus((ushort)floatingBusTable[_tStates]); - } - } - } - - /// - /// Updates the screen buffer based on the number of T-States supplied - /// - /// - public virtual void UpdateScreenBuffer(long _tstates) - { - if (_tstates < actualULAStart) - { - return; - } - else if (_tstates >= FrameLength) - { - _tstates = FrameLength - 1; - - needsPaint = true; - } - - //the additional 1 tstate is required to get correct number of bytes to output in ircontention.sna - elapsedTStates = (_tstates + 1 - lastTState); - - //It takes 4 tstates to write 1 byte. Or, 2 pixels per t-state. - - long numBytes = (elapsedTStates >> 2) + ((elapsedTStates % 4) > 0 ? 1 : 0); - - int pixelData; - int pixel2Data = 0xff; - int attrData; - int attr2Data; - int bright; - int ink; - int paper; - int flash; - - for (int i = 0; i < numBytes; i++) - { - if (tstateToDisp[lastTState] > 1) - { - screenByteCtr = tstateToDisp[lastTState] - 16384; //adjust for actual screen offset - - pixelData = _machine.FetchScreenMemory((ushort)screenByteCtr); //screen[screenByteCtr]; - attrData = _machine.FetchScreenMemory((ushort)(attr[screenByteCtr] - 16384)); //screen[attr[screenByteCtr] - 16384]; - - lastPixelValue = pixelData; - lastAttrValue = attrData; - - bright = (attrData & 0x40) >> 3; - flash = (attrData & 0x80) >> 7; - ink = (attrData & 0x07); - paper = ((attrData >> 3) & 0x7); - int paletteInk = ULAPalette[ink + bright]; - int palettePaper = ULAPalette[paper + bright]; - - if (flashOn && (flash != 0)) //swap paper and ink when flash is on - { - int temp = paletteInk; - paletteInk = palettePaper; - palettePaper = temp; - } - - for (int a = 0; a < 8; ++a) - { - if ((pixelData & 0x80) != 0) - { - ScreenBuffer[ULAByteCtr++] = paletteInk; - lastAttrValue = ink; - //pixelIsPaper = false; - } - else - { - ScreenBuffer[ULAByteCtr++] = palettePaper; - lastAttrValue = paper; - } - pixelData <<= 1; - } - } - else if (tstateToDisp[lastTState] == 1) - { - int bor = ULAPalette[borderColour]; - - for (int g = 0; g < 8; g++) - ScreenBuffer[ULAByteCtr++] = bor; - } - lastTState += 4; - } - } - - #endregion - - #region IVideoProvider - - private int _virtualWidth; - private int _virtualHeight; - private int _bufferWidth; - private int _bufferHeight; - - public int BackgroundColor - { - get { return ULAPalette[7]; } //ULAPalette[borderColour]; } - } - - public int VirtualWidth - { - get { return _virtualWidth; } - set { _virtualWidth = value; } - } - - public int VirtualHeight - { - get { return _virtualHeight; } - set { _virtualHeight = value; } - } - - public int BufferWidth - { - get { return _bufferWidth; } - set { _bufferWidth = value; } - } - - public int BufferHeight - { - get { return _bufferHeight; } - set { _bufferHeight = value; } - } - - public int VsyncNumerator - { - get { return ClockSpeed * 50; }// ClockSpeed; } - set { } - } - - public int VsyncDenominator - { - get { return ClockSpeed; }//FrameLength; } - } - - public int[] GetVideoBuffer() - { - switch (borderType) - { - // Full side borders, no top or bottom border (giving *almost* 16:9 output) - case ZXSpectrum.BorderType.Widescreen: - // we are cropping out the top and bottom borders - var startPixelsToCrop = ScanLineWidth * BorderTopHeight; - var endPixelsToCrop = ScanLineWidth * BorderBottomHeight; - int index = 0; - for (int i = startPixelsToCrop; i < ScreenBuffer.Length - endPixelsToCrop; i++) - { - croppedBuffer[index++] = ScreenBuffer[i]; - } - return croppedBuffer; - - // The full spectrum border - case ZXSpectrum.BorderType.Full: - return ScreenBuffer; - - case ZXSpectrum.BorderType.Medium: - // all border sizes now 24 - var lR = BorderLeftWidth - 24; - var rR = BorderRightWidth - 24; - var tR = BorderTopHeight - 24; - var bR = BorderBottomHeight - 24; - var startP = ScanLineWidth * tR; - var endP = ScanLineWidth * bR; - - int index2 = 0; - // line by line - for (int i = startP; i < ScreenBuffer.Length - endP; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) - { - // each pixel in each line - for (int p = lR; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR; p++) - { - if (index2 == croppedBuffer.Length) - break; - croppedBuffer[index2++] = ScreenBuffer[i + p]; - } - } - - return croppedBuffer; - - case ZXSpectrum.BorderType.Small: - // all border sizes now 24 - var lR_ = BorderLeftWidth - 10; - var rR_ = BorderRightWidth - 10; - var tR_ = BorderTopHeight - 10; - var bR_ = BorderBottomHeight - 10; - var startP_ = ScanLineWidth * tR_; - var endP_ = ScanLineWidth * bR_; - - int index2_ = 0; - // line by line - for (int i = startP_; i < ScreenBuffer.Length - endP_; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) - { - // each pixel in each line - for (int p = lR_; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR_; p++) - { - if (index2_ == croppedBuffer.Length) - break; - croppedBuffer[index2_++] = ScreenBuffer[i + p]; - } - } - - return croppedBuffer; - - case ZXSpectrum.BorderType.None: - // all border sizes now 24 - var lR__ = BorderLeftWidth; - var rR__ = BorderRightWidth; - var tR__ = BorderTopHeight; - var bR__ = BorderBottomHeight; - var startP__ = ScanLineWidth * tR__; - var endP__ = ScanLineWidth * bR__; - - int index2__ = 0; - // line by line - for (int i = startP__; i < ScreenBuffer.Length - endP__; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) - { - // each pixel in each line - for (int p = lR__; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR__; p++) - { - if (index2__ == croppedBuffer.Length) - break; - croppedBuffer[index2__++] = ScreenBuffer[i + p]; - } - } - - return croppedBuffer; - } - - return ScreenBuffer; - } - - protected void SetupScreenSize() - { - switch (borderType) - { - case ZXSpectrum.BorderType.Full: - BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; - BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; - ScreenBuffer = new int[BufferWidth * BufferHeight]; - break; - - case ZXSpectrum.BorderType.Widescreen: - BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; - BufferHeight = ScreenHeight; - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; - croppedBuffer = new int[BufferWidth * BufferHeight]; - break; - - case ZXSpectrum.BorderType.Medium: - BufferWidth = ScreenWidth + (24) + (24); - BufferHeight = ScreenHeight + (24) + (24); - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; - croppedBuffer = new int[BufferWidth * BufferHeight]; - break; - - case ZXSpectrum.BorderType.Small: - BufferWidth = ScreenWidth + (10) + (10); - BufferHeight = ScreenHeight + (10) + (10); - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; - croppedBuffer = new int[BufferWidth * BufferHeight]; - break; - - case ZXSpectrum.BorderType.None: - BufferWidth = ScreenWidth; - BufferHeight = ScreenHeight; - VirtualHeight = BufferHeight; - VirtualWidth = BufferWidth; - croppedBuffer = new int[BufferWidth * BufferHeight]; - break; - } - } - - protected int[] croppedBuffer; - - private ZXSpectrum.BorderType _borderType; - - public ZXSpectrum.BorderType borderType - { - get { return _borderType; } - set { _borderType = value; } - } - - - - #endregion - - #region IStatable - - public void SyncState(Serializer ser) - { - ser.BeginSection("ULA"); - ser.Sync("ScreenBuffer", ref ScreenBuffer, false); - ser.Sync("FrameLength", ref FrameLength); - ser.Sync("ClockSpeed", ref ClockSpeed); - ser.Sync("LateTiming", ref LateTiming); - ser.Sync("borderColour", ref borderColour); - ser.EndSection(); - } - - #endregion - - #region Attribution - - /* - * Based on code from ArjunNair's Zero emulator (MIT Licensed) - * https://github.com/ArjunNair/Zero-Emulator - - The MIT License (MIT) - - Copyright (c) 2009 Arjun Nair - - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated - documentation files (the "Software"), to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, - and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE - WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - */ - /* - #endregion - } - - /// - /// T-State display mapping - /// - public class RenderCycle - { - public RenderType Type { get; set; } - public short DisplayAddress { get; set; } - public short AttributeAddress { get; set; } - public int ContentionValue { get; set; } - public short FloatingBusAddress { get; set; } - } - - public enum RenderType - { - None, - Border, - Display - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs deleted file mode 100644 index 2228ed82c1..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.ULA.cs +++ /dev/null @@ -1,194 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - class ULA128 : ULABase - { - #region Construction - - public ULA128(SpectrumBase machine) - : base(machine) - { - InterruptStart = 0; - //LongestOperationCycles = 64 + 2; - FrameLength = 70908; - ClockSpeed = 3546900; - - contentionTable = new byte[70930]; - floatingBusTable = new short[70930]; - for (int f = 0; f < 70930; f++) - floatingBusTable[f] = -1; - - CharRows = 24; - CharCols = 32; - ScreenWidth = 256; - ScreenHeight = 192; - BorderTopHeight = 48; - BorderBottomHeight = 56; - BorderLeftWidth = 48; - BorderRightWidth = 48; - DisplayStart = 16384; - DisplayLength = 6144; - AttributeStart = 22528; - AttributeLength = 768; - borderColour = 7; - ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; - - TstatesPerScanline = 228; - TstateAtTop = BorderTopHeight * TstatesPerScanline; - TstateAtBottom = BorderBottomHeight * TstatesPerScanline; - tstateToDisp = new short[FrameLength]; - - ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border - + ScanLineWidth * ScreenHeight //border + main + border of 192 lines - + ScanLineWidth * BorderBottomHeight]; //56 lines of border - - attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped - - SetupScreenSize(); - - Reset(); - } - - #endregion - - #region Misc Operations - - public override void Reset() - { - contentionStartPeriod = 14361; // + LateTiming; - contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.RAM5; - screenByteCtr = DisplayStart; - ULAByteCtr = 0; - actualULAStart = 14366 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; - lastTState = actualULAStart; - BuildAttributeMap(); - BuildContentionTable(); - } - - #endregion - - #region Contention Methods - - public override bool IsContended(int addr) - { - addr = addr & 0xc000; - - if (addr == 0x4000) - { - // low port contention - return true; - } - - if (addr == 0xc000) - { - // high port contention - check for contended bank paged in - switch (_machine.RAMPaged) - { - case 1: - case 3: - case 5: - case 7: - return true; - } - } - - return false; - } - - public override void BuildContentionTable() - { - int t = contentionStartPeriod; - while (t < contentionEndPeriod) - { - //for 128 t-states - for (int i = 0; i < 128; i += 8) - { - contentionTable[t++] = 6; - contentionTable[t++] = 5; - contentionTable[t++] = 4; - contentionTable[t++] = 3; - contentionTable[t++] = 2; - contentionTable[t++] = 1; - contentionTable[t++] = 0; - contentionTable[t++] = 0; - } - t += (TstatesPerScanline - 128); - } - - //build top half of tstateToDisp table - //vertical retrace period - for (t = 0; t < actualULAStart; t++) - tstateToDisp[t] = 0; - - //next 48 are actual border - while (t < actualULAStart + (TstateAtTop)) - { - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - - //build middle half - int _x = 0; - int _y = 0; - int scrval = 2; - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) - { - for (int g = 0; g < 24; g++) - tstateToDisp[t++] = 1; - - for (int g = 24; g < 24 + 128; g++) - { - //Map screenaddr to tstate - if (g % 4 == 0) - { - scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); - _x += 8; - } - tstateToDisp[t++] = (short)scrval; - } - _y++; - - for (int g = 24 + 128; g < 24 + 128 + 24; g++) - tstateToDisp[t++] = 1; - - for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++) - tstateToDisp[t++] = 0; - } - - int h = contentionStartPeriod + 3; - while (h < contentionEndPeriod + 3) - { - for (int j = 0; j < 128; j += 8) - { - floatingBusTable[h] = tstateToDisp[h + 2]; - floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; - floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; - floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; - h += 8; - } - h += TstatesPerScanline - 128; - } - - //build bottom half - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) - { - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - } - - - #endregion - - - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs deleted file mode 100644 index e164f8b53a..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.ULA.cs +++ /dev/null @@ -1,198 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - class ULAPlus2a : ULABase - { - #region Construction - - public ULAPlus2a(SpectrumBase machine) - : base(machine) - { - InterruptStart = 0; - //LongestOperationCycles = 64 + 2; - FrameLength = 70908; - ClockSpeed = 3546900; - - contentionTable = new byte[70930]; - floatingBusTable = new short[70930]; - for (int f = 0; f < 70930; f++) - floatingBusTable[f] = -1; - - CharRows = 24; - CharCols = 32; - ScreenWidth = 256; - ScreenHeight = 192; - BorderTopHeight = 48; - BorderBottomHeight = 56; - BorderLeftWidth = 48; - BorderRightWidth = 48; - DisplayStart = 16384; - DisplayLength = 6144; - AttributeStart = 22528; - AttributeLength = 768; - borderColour = 7; - ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; - - TstatesPerScanline = 228; - TstateAtTop = BorderTopHeight * TstatesPerScanline; - TstateAtBottom = BorderBottomHeight * TstatesPerScanline; - tstateToDisp = new short[FrameLength]; - - ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border - + ScanLineWidth * ScreenHeight //border + main + border of 192 lines - + ScanLineWidth * BorderBottomHeight]; //56 lines of border - - attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped - - SetupScreenSize(); - - Reset(); - } - - #endregion - - #region Misc Operations - - public override void Reset() - { - contentionStartPeriod = 14361; // + LateTiming; - contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.RAM5; - screenByteCtr = DisplayStart; - ULAByteCtr = 0; - actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; - lastTState = actualULAStart; - BuildAttributeMap(); - BuildContentionTable(); - } - - #endregion - - #region Contention Methods - - public override bool IsContended(int addr) - { - addr = addr & 0xc000; - - if (addr == 0x4000) - { - // low port contention - return true; - } - - if (addr == 0xc000) - { - // high port contention - check for contended bank paged in - switch (_machine.RAMPaged) - { - case 4: - case 5: - case 6: - case 7: - return true; - } - } - - return false; - } - - public override void BuildContentionTable() - { - int t = contentionStartPeriod; - while (t < contentionEndPeriod) - { - contentionTable[t++] = 1; - contentionTable[t++] = 0; - - //for 128 t-states - for (int i = 0; i < 128; i += 8) - { - contentionTable[t++] = 7; - contentionTable[t++] = 6; - contentionTable[t++] = 5; - contentionTable[t++] = 4; - contentionTable[t++] = 3; - contentionTable[t++] = 2; - contentionTable[t++] = 1; - contentionTable[t++] = 0; - } - t += (TstatesPerScanline - 128) - 2; - } - - //build top half of tstateToDisp table - //vertical retrace period - for (t = 0; t < actualULAStart; t++) - tstateToDisp[t] = 0; - - //next 48 are actual border - while (t < actualULAStart + (TstateAtTop)) - { - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - - //build middle half - int _x = 0; - int _y = 0; - int scrval = 2; - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) - { - for (int g = 0; g < 24; g++) - tstateToDisp[t++] = 1; - - for (int g = 24; g < 24 + 128; g++) - { - //Map screenaddr to tstate - if (g % 4 == 0) - { - scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); - _x += 8; - } - tstateToDisp[t++] = (short)scrval; - } - - _y++; - - for (int g = 24 + 128; g < 24 + 128 + 24; g++) - tstateToDisp[t++] = 1; - - for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++) - tstateToDisp[t++] = 0; - } - - int h = contentionStartPeriod + 3; - while (h < contentionEndPeriod + 3) - { - for (int j = 0; j < 128; j += 8) - { - floatingBusTable[h] = tstateToDisp[h + 2]; - floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; - floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; - floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; - h += 8; - } - h += TstatesPerScanline - 128; - } - - //build bottom half - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) - { - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - } - - - #endregion - - - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs deleted file mode 100644 index 9e0289d53e..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.ULA.cs +++ /dev/null @@ -1,198 +0,0 @@ - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - class ULAPlus3 : ULABase - { - #region Construction - - public ULAPlus3(SpectrumBase machine) - : base(machine) - { - InterruptStart = 0; - //LongestOperationCycles = 64 + 2; - FrameLength = 70908; - ClockSpeed = 3546900; - - contentionTable = new byte[70930]; - floatingBusTable = new short[70930]; - for (int f = 0; f < 70930; f++) - floatingBusTable[f] = -1; - - CharRows = 24; - CharCols = 32; - ScreenWidth = 256; - ScreenHeight = 192; - BorderTopHeight = 48; - BorderBottomHeight = 56; - BorderLeftWidth = 48; - BorderRightWidth = 48; - DisplayStart = 16384; - DisplayLength = 6144; - AttributeStart = 22528; - AttributeLength = 768; - borderColour = 7; - ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; - - TstatesPerScanline = 228; - TstateAtTop = BorderTopHeight * TstatesPerScanline; - TstateAtBottom = BorderBottomHeight * TstatesPerScanline; - tstateToDisp = new short[FrameLength]; - - ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border - + ScanLineWidth * ScreenHeight //border + main + border of 192 lines - + ScanLineWidth * BorderBottomHeight]; //56 lines of border - - attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped - - SetupScreenSize(); - - Reset(); - } - - #endregion - - #region Misc Operations - - public override void Reset() - { - contentionStartPeriod = 14361; // + LateTiming; - contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.RAM5; - screenByteCtr = DisplayStart; - ULAByteCtr = 0; - actualULAStart = 14365 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; - lastTState = actualULAStart; - BuildAttributeMap(); - BuildContentionTable(); - } - - #endregion - - #region Contention Methods - - public override bool IsContended(int addr) - { - addr = addr & 0xc000; - - if (addr == 0x4000) - { - // low port contention - return true; - } - - if (addr == 0xc000) - { - // high port contention - check for contended bank paged in - switch (_machine.RAMPaged) - { - case 4: - case 5: - case 6: - case 7: - return true; - } - } - - return false; - } - - public override void BuildContentionTable() - { - int t = contentionStartPeriod; - while (t < contentionEndPeriod) - { - contentionTable[t++] = 1; - contentionTable[t++] = 0; - - //for 128 t-states - for (int i = 0; i < 128; i += 8) - { - contentionTable[t++] = 7; - contentionTable[t++] = 6; - contentionTable[t++] = 5; - contentionTable[t++] = 4; - contentionTable[t++] = 3; - contentionTable[t++] = 2; - contentionTable[t++] = 1; - contentionTable[t++] = 0; - } - t += (TstatesPerScanline - 128) - 2; - } - - //build top half of tstateToDisp table - //vertical retrace period - for (t = 0; t < actualULAStart; t++) - tstateToDisp[t] = 0; - - //next 48 are actual border - while (t < actualULAStart + (TstateAtTop)) - { - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - - //build middle half - int _x = 0; - int _y = 0; - int scrval = 2; - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) - { - for (int g = 0; g < 24; g++) - tstateToDisp[t++] = 1; - - for (int g = 24; g < 24 + 128; g++) - { - //Map screenaddr to tstate - if (g % 4 == 0) - { - scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); - _x += 8; - } - tstateToDisp[t++] = (short)scrval; - } - - _y++; - - for (int g = 24 + 128; g < 24 + 128 + 24; g++) - tstateToDisp[t++] = 1; - - for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 52; g++) - tstateToDisp[t++] = 0; - } - - int h = contentionStartPeriod + 3; - while (h < contentionEndPeriod + 3) - { - for (int j = 0; j < 128; j += 8) - { - floatingBusTable[h] = tstateToDisp[h + 2]; - floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; - floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; - floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; - h += 8; - } - h += TstatesPerScanline - 128; - } - - //build bottom half - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) - { - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - } - - - #endregion - - - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs deleted file mode 100644 index 03e10d9685..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.ULA.cs +++ /dev/null @@ -1,211 +0,0 @@ - -using System.Linq; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - class ULA48 : ULABase - { - #region Construction - - public ULA48(SpectrumBase machine) - : base(machine) - { - InterruptStart = 0;// 5; // 0; // 3; // 0;// 32; - //LongestOperationCycles = 32; - FrameLength = 69888; - ClockSpeed = 3500000; - - contentionTable = new byte[70930]; - floatingBusTable = new short[70930]; - for (int f = 0; f < 70930; f++) - floatingBusTable[f] = -1; - - CharRows = 24; - CharCols = 32; - ScreenWidth = 256; - ScreenHeight = 192; - BorderTopHeight = 48; - BorderBottomHeight = 56; - BorderLeftWidth = 48; - BorderRightWidth = 48; - DisplayStart = 16384; - DisplayLength = 6144; - AttributeStart = 22528; - AttributeLength = 768; - borderColour = 7; - ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; - - TstatesPerScanline = 224; - TstateAtTop = BorderTopHeight * TstatesPerScanline; - TstateAtBottom = BorderBottomHeight * TstatesPerScanline; - tstateToDisp = new short[FrameLength]; - - ScreenBuffer = new int[ScanLineWidth * BorderTopHeight //48 lines of border - + ScanLineWidth * ScreenHeight //border + main + border of 192 lines - + ScanLineWidth * BorderBottomHeight]; //56 lines of border - - attr = new short[DisplayLength]; //6144 bytes of display memory will be mapped - - SetupScreenSize(); - - Reset(); - } - - #endregion - - #region Misc Operations - - public override void Reset() - { - contentionOffset = 0; - InterruptStart = 0; - - contentionStartPeriod = 14335;// + contentionOffset; // + LateTiming; - contentionEndPeriod = contentionStartPeriod + (ScreenHeight * TstatesPerScanline); - screen = _machine.RAM0; - screenByteCtr = DisplayStart; - ULAByteCtr = 0; - actualULAStart = 14340 - 24 - (TstatesPerScanline * BorderTopHeight);// + LateTiming; - lastTState = actualULAStart; - BuildAttributeMap(); - BuildContentionTable(); - } - - #endregion - - #region Contention Methods - - public override bool IsContended(int addr) - { - if ((addr & 49152) == 16384) - return true; - return false; - } - - public override void BuildContentionTable() - { - int t = contentionStartPeriod; - while (t < contentionEndPeriod) - { - //for 128 t-states - for (int i = 0; i < 128; i += 8) - { - contentionTable[t++] = 6; - contentionTable[t++] = 5; - contentionTable[t++] = 4; - contentionTable[t++] = 3; - contentionTable[t++] = 2; - contentionTable[t++] = 1; - contentionTable[t++] = 0; - contentionTable[t++] = 0; - } - t += (TstatesPerScanline - 128); //24 tstates of right border + left border + 48 tstates of retrace - } - - //build top half of tstateToDisp table - //vertical retrace period - for (t = 0; t < actualULAStart; t++) - tstateToDisp[t] = 0; - - //next 48 are actual border - while (t < actualULAStart + (TstateAtTop)) - { - //border(24t) + screen (128t) + border(24t) = 176 tstates - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - //horizontal retrace - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - - //build middle half of display - int _x = 0; - int _y = 0; - int scrval = 2; - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline)) - { - //left border - for (int g = 0; g < 24; g++) - tstateToDisp[t++] = 1; - - //screen - for (int g = 24; g < 24 + 128; g++) - { - //Map screenaddr to tstate - if (g % 4 == 0) - { - scrval = (((((_y & 0xc0) >> 3) | (_y & 0x07) | (0x40)) << 8)) | (((_x >> 3) & 0x1f) | ((_y & 0x38) << 2)); - _x += 8; - } - tstateToDisp[t++] = (short)scrval; - } - _y++; - - //right border - for (int g = 24 + 128; g < 24 + 128 + 24; g++) - tstateToDisp[t++] = 1; - - //horizontal retrace - for (int g = 24 + 128 + 24; g < 24 + 128 + 24 + 48; g++) - tstateToDisp[t++] = 0; - } - - int h = contentionStartPeriod + 3; - while (h < contentionEndPeriod + 3) - { - for (int j = 0; j < 128; j += 8) - { - floatingBusTable[h] = tstateToDisp[h + 2]; //screen address - floatingBusTable[h + 1] = attr[(tstateToDisp[h + 2] - 16384)]; //attr address - floatingBusTable[h + 2] = tstateToDisp[h + 2 + 4]; //screen address + 1 - floatingBusTable[h + 3] = attr[(tstateToDisp[h + 2 + 4] - 16384)]; //attr address + 1 - h += 8; - } - h += TstatesPerScanline - 128; - } - - // offset floating bus table - var offset = 0; - - if (offset != 0) - { - var fbt = floatingBusTable.ToArray(); - for (int i = 0; i < FrameLength; i++) - { - var off = i + offset; - if (off < 0) - { - off = FrameLength - 1 + off; - } - else if (off >= FrameLength) - { - off = off - FrameLength; - } - - fbt[off] = floatingBusTable[i]; - } - - floatingBusTable = fbt.ToArray(); - } - - //build bottom border - while (t < actualULAStart + (TstateAtTop) + (ScreenHeight * TstatesPerScanline) + (TstateAtBottom)) - { - //border(24t) + screen (128t) + border(24t) = 176 tstates - for (int g = 0; g < 176; g++) - tstateToDisp[t++] = 1; - - //horizontal retrace - for (int g = 176; g < TstatesPerScanline; g++) - tstateToDisp[t++] = 0; - } - } - - #endregion - - - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs deleted file mode 100644 index bfba0bf66e..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaSerializer.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - /// - /// Abtract class that represents all Media Serializers - /// - public abstract class MediaSerializer - { - /// - /// The type of serializer - /// - public abstract MediaSerializationType FormatType { get; } - - /// - /// Signs whether this class can be used to serialize - /// - public virtual bool IsSerializer - { - get - { - return false; - } - } - - /// - /// Signs whether this class can be used to de-serialize - /// - public virtual bool IsDeSerializer - { - get - { - return false; - } - } - - /// - /// Serialization method - /// - /// - public virtual void Serialize(byte[] data) - { - throw new NotImplementedException(this.GetType().ToString() + - "Serialize operation is not implemented for this serializer"); - } - - /// - /// DeSerialization method - /// - /// - public virtual void DeSerialize(byte[] data) - { - throw new NotImplementedException(this.GetType().ToString() + - "DeSerialize operation is not implemented for this serializer"); - } - - /// - /// Serializer does a quick check, returns TRUE if file is detected as this type - /// - /// - public virtual bool CheckType(byte[] data) - { - throw new NotImplementedException(this.GetType().ToString() + - "Check type operation is not implemented for this serializer"); - } - - #region Static Tools - - /// - /// Converts an int32 value into a byte array - /// - /// - /// - protected static byte[] GetBytes(int value) - { - byte[] buf = new byte[4]; - buf[0] = (byte)value; - buf[1] = (byte)(value >> 8); - buf[2] = (byte)(value >> 16); - buf[3] = (byte)(value >> 24); - return buf; - } - - /// - /// Returns an int32 from a byte array based on offset - /// - /// - /// - /// - protected static int GetInt32(byte[] buf, int offsetIndex) - { - return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24; - } - - /// - /// Returns an uint16 from a byte array based on offset - /// - /// - /// - /// - protected static ushort GetWordValue(byte[] buf, int offsetIndex) - { - return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8); - } - - /// - /// Updates a byte array with a uint16 value based on offset - /// - /// - /// - /// - protected static void SetWordValue(byte[] buf, int offsetIndex, ushort value) - { - buf[offsetIndex] = (byte)value; - buf[offsetIndex + 1] = (byte)(value >> 8); - } - - #endregion - } - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index b182a80270..37de2d0a63 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { [Core( "ZXHawk", - "Asnivor", + "Asnivor, Alyosha", isPorted: false, isReleased: false)] public partial class ZXSpectrum : IRegionable, IDriveLight diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 36eb6a6f07..300abfd881 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -1,39 +1,34 @@ ## ZXHawk -At the moment this is experimental and is still being worked on. +ZXHawk is still in dev but is potentially nearing a release state. -### Implemented and working (as far as I can tell) -* IEmulator -* ZX Spectrum 48k, 128k, +2 & +2A models -* ULA video output (implementing IVideoProvider) -* ULA Mode 1 VBLANK interrupt generation -* IM2 Interrupts and DataBus implementation (thanks Aloysha) -* Beeper/Buzzer output (implementing ISoundProvider) -* AY-3-8912 sound chip implementation (multiple stereo or mono panning options available as a setting) -* Keyboard input (implementing IInputPollable) -* Default keyboard keymappings -* Kempston, Cursor and Sinclair (Left & Right) joysticks emulated -* Tape device that will load spectrum games in realtime (*.tzx and *.tap) -* Most tape protection/loading schemes that I've tested are currently working -* IStatable -* ISettable core settings -* IDebuggable (for what it's worth) -* DeterministicEmulation as a SyncSetting, LagFrame detection and FrameAdvance render & renderSound bools respected (when DeterministicEmulation == false) -* Tape auto-loading routines (as a setting - default ON) -* Basic tape block navigation (NextBlock, PrevBlock) -* Tape-related OSD messages (verbosity level configured in settings) +### Whats in the box? +* Emulates the Sinclair ZX Spectrum 16k, 48k, 128k, +2, +2A & +3 +* Accurate Z80A implementation +* Precise screen timing, floating bus, memory contention and port contention for all models +* Full keyboard emulation +* Kempston, Cursor and Sinclair joysticks emulated +* Full beeper and AY-3-3912 sound emulation +* Tape device (datacorder) emulation +* Internal 3" disk drive emulation (found in the +3 model) +* Currently supports the following tape image formats: *.tzx, *.tap +* Currently supports the following disk image formats (+3 only): *.dsk +* Fully integrated into the Bizhawk ecosystem +* See the ZXSpectrum menu for all available configuration options -### Work in progress -* ZX Spectrum +3 emulation (partially working, see below) -* Exact emulator timings -* Floating memory bus emulation -* TASStudio (need to verify that this works as it should) +### Firmware +ZXHawk ships with the official ZX Spectrum ROMs embedded (licensed by Amstrad). -### Not working -* +3 disk drive - no implementation yet -* Hard & Soft Reset menu options in the client (they are greyed out for some reason) +"Amstrad have kindly given their permission for the redistribution of their copyrighted material but retain that copyright" +http://www.worldofspectrum.org/permits/amstrad-roms.txt -### Help needed -* I'm not a TASer, i've never TASed before. It would be really useful if someone (anyone) can build this branch and test this core from a TAS-workflow / TAStudio perpective. There may still be some work to do an exact timings and memory contention, but otherwise this core is able to play the majority of speccy games out there. +### Issues +* Tape images are read-only. This may change in the future, but with bizhawk's savestate system this is not strictly a necessity +* Disk images are currently read-only as well. There is certain write functionality implemented within the emulated UPD756A disk controller (in order to make games work that require this), but this is not persistent +* Disk drive emulation timing is currently not accurate, meaning that disk games will load faster than they would on a real +3. Due to how the Spectrum interfaces with the disk controller though, this should not cause any compatibility issues + +Any questions, issues or bug reports, either use the GitHub issue tracker, or post in the forum thread: + +http://tasvideos.org/forum/viewtopic.php?t=20004 -Asnivor From 5d401b20d34402829aba9c46cf1102e34d5bab38 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Wed, 13 Jun 2018 12:54:43 -0400 Subject: [PATCH 300/339] z80: clean up --- BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs | 3 --- BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs | 2 -- BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs | 2 +- BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 11 +---------- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index 9b308b76e3..318ef27300 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -20,15 +20,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public int instr_pntr = 0; public int bus_pntr = 0; public int mem_pntr = 0; - public ushort instr_swap; public ushort[] cur_instr; public ushort[] BUSRQ; public ushort[] MEMRQ; public byte opcode; public bool NO_prefix, CB_prefix, IX_prefix, EXTD_prefix, IY_prefix, IXCB_prefix, IYCB_prefix; - public bool IXCB_prefetch, IYCB_prefetch; // value is fetched before opcode public bool halted; - public ushort PF; public void FetchInstruction() { diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 1781a5eebe..6bc915c296 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -119,8 +119,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } - private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; - private void ResetInterrupts() { IFF1 = false; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index ceb6017a7e..b081996cde 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -58,7 +58,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public const ushort WIO4 = 108; - public bool FlagI, FlagI1, FlagI2, FlagI3, FlagI4, FlagI5, FlagI6; + public bool FlagI, FlagI1, FlagI2, FlagI3, FlagI4, FlagI5; public bool FlagW; // wait flag, when set to true reads / writes will be delayed diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index a96e76beb8..cc4d21ce60 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -160,7 +160,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // Execute instructions public void ExecuteOne() { - //FlagI6 = FlagI5; FlagI5 = FlagI4; FlagI4 = FlagI3; FlagI3 = FlagI2; @@ -676,14 +675,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A DEC16, PCl, PCh, DEC16, PCl, PCh, IDLE, - IDLE,//DEC16, B, ALU, + IDLE, WAIT, OP_F, OP}; - //Regs[B] = (ushort)((Regs[B] + 1) & 0xFF); - - BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } @@ -777,7 +773,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("cur_instr", ref cur_instr, false); ser.Sync("BUSRQ", ref BUSRQ, false); ser.Sync("MEMRQ", ref MEMRQ, false); - ser.Sync("instr_swap", ref instr_swap); ser.Sync("opcode", ref opcode); ser.Sync("FlagI", ref FlagI); ser.Sync("FlagI1", ref FlagI1); @@ -785,7 +780,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("FlagI3", ref FlagI3); ser.Sync("FlagI4", ref FlagI4); ser.Sync("FlagI5", ref FlagI5); - ser.Sync("FlagI6", ref FlagI6); ser.Sync("FlagW", ref FlagW); ser.Sync("NO Preifx", ref NO_prefix); @@ -795,9 +789,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("IXCB_prefix", ref IXCB_prefix); ser.Sync("IYCB_prefix", ref IYCB_prefix); ser.Sync("EXTD_prefix", ref EXTD_prefix); - ser.Sync("IXCB_prefetch", ref IXCB_prefetch); - ser.Sync("IYCB_prefetch", ref IYCB_prefetch); - ser.Sync("PF", ref PF); ser.EndSection(); } From e0b417a9b5deee46fc4921a8e437a672fe8928a2 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 13 Jun 2018 18:07:07 +0100 Subject: [PATCH 301/339] ZXHawk: Interrupt bool missing from savestate --- .../Computers/SinclairSpectrum/Machine/ULA.cs | 2 ++ output/dll/libsnes.wbx.gz | Bin 360119 -> 360119 bytes 2 files changed, 2 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 448c7f9251..6e5132bcd6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -1049,6 +1049,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ser.Sync("LastULATick", ref LastULATick); ser.Sync("ULACycleCounter", ref ULACycleCounter); ser.Sync("FrameEnd", ref FrameEnd); + + ser.Sync("InterruptRaised", ref InterruptRaised); ser.EndSection(); } diff --git a/output/dll/libsnes.wbx.gz b/output/dll/libsnes.wbx.gz index 04e04bd3f39aefedc1802e2bf8320480976e875f..c6442082844979109bc8e87cf655e1702acd587c 100644 GIT binary patch delta 6976 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 From 76318fbc7a522e31eb3af7f4317e40cf2853e549 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 14 Jun 2018 09:13:09 +0100 Subject: [PATCH 302/339] ZXHawk: Do ULA processing every cycle --- .../Computers/SinclairSpectrum/Machine/ULA.cs | 3 +++ .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 4 ++-- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 2 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs | 12 ++++++------ .../Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 2 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 12 ++++++------ .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 2 +- .../SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 2 +- .../Machine/ZXSpectrum48K/ZX48.Port.cs | 2 +- 10 files changed, 23 insertions(+), 20 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 6e5132bcd6..9a8de555ce 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -143,6 +143,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public virtual void CycleClock(long totalCycles) { + // render the screen + RenderScreen((int)_machine.CurrentFrameCycle); + // has more than one cycle past since this last ran // (this can be true if contention has taken place) var ticksToProcess = totalCycles - LastULATick; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index 6c48210679..770d861c5f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -134,7 +134,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) case 1: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; @@ -163,7 +163,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM4[addr % 0x4000] = value; break; case 5: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 6: 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 b34d860f86..256716f315 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -150,7 +150,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Border - LSB 3 bits hold the border colour if (ULADevice.BorderColor != (value & BORDER_BIT)) { - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); ULADevice.BorderColor = value & BORDER_BIT; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index ebb6f7c399..323e683b94 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -236,11 +236,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; case 1: case 2: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 3: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } @@ -267,7 +267,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM3[addr % 0x4000] = value; break; case 1: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } @@ -302,7 +302,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // RAM 0x4000 (RAM5 - Bank5 only) case 1: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; @@ -331,14 +331,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM4[addr % 0x4000] = value; break; case 5: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 6: RAM6[addr % 0x4000] = value; break; case 7: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } 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 cf43ced6e5..57fab06df4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -148,7 +148,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Border - LSB 3 bits hold the border colour if (ULADevice.BorderColor != (value & BORDER_BIT)) { - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); ULADevice.BorderColor = value & BORDER_BIT; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 33509faa6b..6bec98ab37 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -236,11 +236,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum break; case 1: case 2: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 3: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } @@ -267,7 +267,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM3[addr % 0x4000] = value; break; case 1: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } @@ -302,7 +302,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // RAM 0x4000 (RAM5 - Bank5 only) case 1: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; @@ -331,14 +331,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum RAM4[addr % 0x4000] = value; break; case 5: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM5[addr % 0x4000] = value; break; case 6: RAM6[addr % 0x4000] = value; break; case 7: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM7[addr % 0x4000] = value; break; } 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 0f74207c16..e7a4a7304b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -154,7 +154,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Border - LSB 3 bits hold the border colour if (ULADevice.BorderColor != (value & BORDER_BIT)) { - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); ULADevice.BorderColor = value & BORDER_BIT; } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index fef7ea7b45..7f52c2dcc8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -85,7 +85,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // cannot write to ROM break; case 1: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM0[index] = value; break; } 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 932da8497b..6d49076f41 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -77,7 +77,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // cannot write to ROM break; case 1: - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); RAM0[index] = value; break; case 2: 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 e0a91aa2d6..a4cbc9bd5b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -86,7 +86,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Border - LSB 3 bits hold the border colour if (ULADevice.BorderColor != (value & BORDER_BIT)) { - ULADevice.RenderScreen((int)CurrentFrameCycle); + //ULADevice.RenderScreen((int)CurrentFrameCycle); ULADevice.BorderColor = value & BORDER_BIT; } From 660fb500aefaad74c0949efc7275b5871efaedec Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 14 Jun 2018 11:31:09 +0100 Subject: [PATCH 303/339] ZXHawk: codebase cleanup --- .../BizHawk.Emulation.Cores.csproj | 3 - .../Hardware/Abstraction/IBeeperDevice.cs | 25 +- .../Hardware/Abstraction/IFDDHost.cs | 7 +- .../Hardware/Abstraction/IJoystick.cs | 8 +- .../Hardware/Abstraction/IKeyboard.cs | 6 +- .../Hardware/Abstraction/IPSG.cs | 5 - .../Hardware/Abstraction/IPortIODevice.cs | 7 +- .../Hardware/Datacorder/DatacorderDevice.cs | 3 - .../SinclairSpectrum/Hardware/Disk/CHRN.cs | 7 +- .../Hardware/Disk/NECUPD765.Definitions.cs | 4 - .../Hardware/Disk/NECUPD765.FDC.cs | 3 - .../Hardware/Disk/NECUPD765.FDD.cs | 3 - .../Hardware/Disk/NECUPD765.IPortIODevice.cs | 8 +- .../Hardware/Disk/NECUPD765.Registers.cs | 554 ------------------ .../Hardware/Disk/NECUPD765.Timing.cs | 33 +- .../Hardware/Disk/NECUPD765.cs | 5 - .../Hardware/Disk/NECUPS765.Static.cs | 7 +- .../Hardware/Input/CursorJoystick.cs | 6 +- .../Hardware/Input/KempstonJoystick.cs | 6 +- .../Hardware/Input/NullJoystick.cs | 6 +- .../Hardware/Input/SinclairJoystick1.cs | 6 +- .../Hardware/Input/SinclairJoystick2.cs | 6 +- .../Hardware/Input/StandardKeyboard.cs | 129 ---- .../SinclairSpectrum/Hardware/Rom/RomData.cs | 24 +- .../Hardware/SoundOuput/AY38912.cs | 3 - .../Hardware/SoundOuput/Beeper.cs | 14 - .../Hardware/SoundOuput/Buzzer.cs | 325 ---------- .../Hardware/SoundOuput/Pulse.cs | 26 - .../SinclairSpectrum/Machine/CPUMonitor.cs | 9 +- .../SinclairSpectrum/Machine/MachineType.cs | 10 +- .../Machine/SpectrumBase.Input.cs | 6 +- .../Machine/SpectrumBase.Media.cs | 8 +- .../Machine/SpectrumBase.Memory.cs | 4 - .../Machine/SpectrumBase.Port.cs | 7 +- .../SinclairSpectrum/Machine/SpectrumBase.cs | 3 - .../Computers/SinclairSpectrum/Machine/ULA.cs | 5 +- .../Machine/ZXSpectrum128K/ZX128.Memory.cs | 10 +- .../Machine/ZXSpectrum128K/ZX128.Port.cs | 7 +- .../Machine/ZXSpectrum128K/ZX128.Screen.cs | 10 +- .../Machine/ZXSpectrum128K/ZX128.cs | 17 +- .../Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs | 4 - .../ZX128Plus2a.Memory.cs | 3 + .../ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs | 7 +- .../ZX128Plus2a.Screen.cs | 10 +- .../ZXSpectrum128KPlus2a/ZX128Plus2a.cs | 12 +- .../ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs | 3 + .../ZXSpectrum128KPlus3/ZX128Plus3.Port.cs | 7 +- .../Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs | 12 +- .../Machine/ZXSpectrum16K/ZX16.cs | 7 +- .../Machine/ZXSpectrum48K/ZX48.Memory.cs | 3 + .../Machine/ZXSpectrum48K/ZX48.Port.cs | 10 +- .../Machine/ZXSpectrum48K/ZX48.Screen.cs | 10 +- .../Machine/ZXSpectrum48K/ZX48.cs | 6 +- .../Media/Disk/CPCExtendedFloppyDisk.cs | 7 +- .../Media/Disk/CPCFloppyDisk.cs | 7 +- .../SinclairSpectrum/Media/Disk/DiskType.cs | 7 +- .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 4 +- .../SinclairSpectrum/Media/MediaConverter.cs | 6 +- .../Media/MediaConverterType.cs | 7 +- .../Media/Tape/TapConverter.cs | 1 - .../Media/Tape/TapeCommand.cs | 7 +- .../Media/Tape/TapeDataBlock.cs | 2 - .../Media/Tape/TzxConverter.cs | 2 - .../SinclairSpectrum/SoundProviderMixer.cs | 3 +- .../ZXSpectrum.Controllers.cs | 43 +- .../ZXSpectrum.IDebuggable.cs | 6 +- .../SinclairSpectrum/ZXSpectrum.IEmulator.cs | 7 +- .../ZXSpectrum.IInputPollable.cs | 7 +- .../ZXSpectrum.IMemoryDomains.cs | 7 +- .../SinclairSpectrum/ZXSpectrum.ISettable.cs | 12 +- .../SinclairSpectrum/ZXSpectrum.IStatable.cs | 5 +- .../SinclairSpectrum/ZXSpectrum.Messaging.cs | 5 +- .../SinclairSpectrum/ZXSpectrum.Util.cs | 7 +- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 5 +- 74 files changed, 166 insertions(+), 1420 deletions(-) delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs delete mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index de8a7e7314..08933f73d1 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -274,14 +274,12 @@ - - @@ -314,7 +312,6 @@ - diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs index 2e4d2dea50..3988706911 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs @@ -1,22 +1,29 @@ using BizHawk.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// Represents a beeper/buzzer device + /// public interface IBeeperDevice { + /// + /// Initialisation + /// + /// + /// void Init(int sampleRate, int tStatesPerFrame); + /// + /// Processes an incoming pulse value and adds it to the blipbuffer + /// + /// void ProcessPulseValue(bool pulse); - void StartFrame(); - - void EndFrame(); - + /// + /// State serialization + /// + /// void SyncState(Serializer ser); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs index fd2a3016db..a62c6fe567 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs index 6421e99af4..b971e3c412 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs @@ -1,10 +1,4 @@ -using BizHawk.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs index 77282f1dfa..278d39b6fd 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs @@ -1,6 +1,4 @@ - - -using BizHawk.Common; +using BizHawk.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -77,8 +75,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// byte GetByteFromKeyMatrix(string key); - - void SyncState(Serializer ser); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs index d359103d27..f3cde8c900 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs @@ -1,10 +1,5 @@ using BizHawk.Common; using BizHawk.Emulation.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs index 2fbd89a0db..4a30e5eaa6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index abaa462406..6701d0a923 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -1,12 +1,9 @@ using BizHawk.Common; using BizHawk.Emulation.Cores.Components.Z80A; using System; -using System.Collections; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs index b5bba9600d..d9f5c9822e 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs index f1e89de394..0d025e4e24 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs @@ -1,9 +1,5 @@ using BizHawk.Common; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 90c8998f78..2e1a99a806 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -1,10 +1,7 @@ using BizHawk.Common.NumberExtensions; -using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs index d5e7d9f4d7..5cf84876f5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs @@ -1,10 +1,7 @@ using BizHawk.Common; using BizHawk.Common.NumberExtensions; using System; -using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs index f2e6d3e67d..b1ed8bce99 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs @@ -1,9 +1,6 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -20,6 +17,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion public partial class NECUPD765 : IPortIODevice { + #region Dev Logging public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv"; public string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n"; @@ -48,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } sb.Append(ActiveCommand.CommandCode).Append(","); - + sb.Append(CMD_FLAG_MT).Append(","); sb.Append(CMD_FLAG_MF).Append(","); sb.Append(CMD_FLAG_SK).Append(","); @@ -63,6 +61,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum outputString += sb.ToString(); } + #endregion + /// /// Device responds to an IN instruction /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs deleted file mode 100644 index 9ce1f467c8..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Registers.cs +++ /dev/null @@ -1,554 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// Registers - /// - #region Attribution - /* - Implementation based on the information contained here: - http://www.cpcwiki.eu/index.php/765_FDC - and here: - http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf - */ - #endregion - public partial class NECUPD765 - { - /* - - #region Main Status Register - - /// - /// Main status register (accessed via reads to port 0x2ffd) - /// - private byte _RegMain; - - /// - /// FDD0 Busy (seek/recalib active, until succesful sense intstat) - /// - private bool MainDB0 - { - get { return GetBit(0, _RegMain); } - set - { - if (value) { SetBit(0, ref _RegMain); } - else { UnSetBit(0, ref _RegMain); } - } - } - - /// - /// FDD1 Busy (seek/recalib active, until succesful sense intstat) - /// - private bool MainDB1 - { - get { return GetBit(1, _RegMain); } - set - { - if (value) { SetBit(1, ref _RegMain); } - else { UnSetBit(1, ref _RegMain); } - } - } - - /// - /// FDD2 Busy (seek/recalib active, until succesful sense intstat) - /// - private bool MainDB2 - { - get { return GetBit(2, _RegMain); } - set - { - if (value) { SetBit(2, ref _RegMain); } - else { UnSetBit(2, ref _RegMain); } - } - } - - /// - /// FDD3 Busy (seek/recalib active, until succesful sense intstat) - /// - private bool MainDB3 - { - get { return GetBit(3, _RegMain); } - set - { - if (value) { SetBit(3, ref _RegMain); } - else { UnSetBit(3, ref _RegMain); } - } - } - - /// - /// FDC Busy (still in command-, execution- or result-phase) - /// - private bool MainCB - { - get { return GetBit(4, _RegMain); } - set - { - if (value) { SetBit(4, ref _RegMain); } - else { UnSetBit(4, ref _RegMain); } - } - } - - /// - /// Execution Mode (still in execution-phase, non_DMA_only) - /// - private bool MainEXM - { - get { return GetBit(5, _RegMain); } - set - { - if (value) { SetBit(5, ref _RegMain); } - else { UnSetBit(5, ref _RegMain); } - } - } - - /// - /// Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) - /// - private bool MainDIO - { - get { return GetBit(6, _RegMain); } - set - { - if (value) { SetBit(6, ref _RegMain); } - else { UnSetBit(6, ref _RegMain); } - } - } - - /// - /// Request For Master (1=ready for next byte) (see b6 for direction) - /// - private bool MainRQM - { - get { return GetBit(7, _RegMain); } - set - { - if (value) { SetBit(7, ref _RegMain); } - else { UnSetBit(7, ref _RegMain); } - } - } - - #endregion - - #region Status Register 0 - - /// - /// Status Register 0 - /// - private byte _Reg0; - - /// - /// Unit Select (driveno during interrupt) - /// - private bool ST0US0 - { - get { return GetBit(0, _Reg0); } - set - { - if (value) { SetBit(0, ref _Reg0); } - else { UnSetBit(0, ref _Reg0); } - } - } - - /// - /// Unit Select (driveno during interrupt) - /// - private bool ST0US1 - { - get { return GetBit(1, _Reg0); } - set - { - if (value) { SetBit(1, ref _Reg0); } - else { UnSetBit(1, ref _Reg0); } - } - } - - /// - /// Head Address (head during interrupt) - /// - private bool ST0HD - { - get { return GetBit(2, _Reg0); } - set - { - if (value) { SetBit(2, ref _Reg0); } - else { UnSetBit(2, ref _Reg0); } - } - } - - /// - /// Not Ready (drive not ready or non-existing 2nd head selected) - /// - private bool ST0NR - { - get { return GetBit(3, _Reg0); } - set - { - if (value) { SetBit(3, ref _Reg0); } - else { UnSetBit(3, ref _Reg0); } - } - } - - /// - /// Equipment Check (drive failure or recalibrate failed (retry)) - /// - private bool ST0EC - { - get { return GetBit(4, _Reg0); } - set - { - if (value) { SetBit(4, ref _Reg0); } - else { UnSetBit(4, ref _Reg0); } - } - } - - /// - /// Seek End (Set if seek-command completed) - /// - private bool ST0SE - { - get { return GetBit(5, _Reg0); } - set - { - if (value) { SetBit(5, ref _Reg0); } - else { UnSetBit(5, ref _Reg0); } - } - } - - /// - /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd - /// or senseint with no int occured, 3=aborted:disc removed etc.) - /// - private bool ST0IC0 - { - get { return GetBit(6, _Reg0); } - set - { - if (value) { SetBit(6, ref _Reg0); } - else { UnSetBit(6, ref _Reg0); } - } - } - - /// - /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd - /// or senseint with no int occured, 3=aborted:disc removed etc.) - /// - private bool ST0IC1 - { - get { return GetBit(7, _Reg0); } - set - { - if (value) { SetBit(7, ref _Reg0); } - else { UnSetBit(7, ref _Reg0); } - } - } - - #endregion - - #region Status Register 1 - - /// - /// Status Register 1 - /// - private byte _Reg1; - - /// - /// Missing Address Mark (Sector_ID or DAM not found) - /// - private bool ST1MA - { - get { return GetBit(0, _Reg1); } - set - { - if (value) { SetBit(0, ref _Reg1); } - else { UnSetBit(0, ref _Reg1); } - } - } - - /// - /// Not Writeable (tried to write/format disc with wprot_tab=on) - /// - private bool ST1NW - { - get { return GetBit(1, _Reg1); } - set - { - if (value) { SetBit(1, ref _Reg1); } - else { UnSetBit(1, ref _Reg1); } - } - } - - /// - /// No Data (Sector_ID not found, CRC fail in ID_field) - /// - private bool ST1ND - { - get { return GetBit(2, _Reg1); } - set - { - if (value) { SetBit(2, ref _Reg1); } - else { UnSetBit(2, ref _Reg1); } - } - } - - /// - /// Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) - /// - private bool ST1OR - { - get { return GetBit(4, _Reg1); } - set - { - if (value) { SetBit(4, ref _Reg1); } - else { UnSetBit(4, ref _Reg1); } - } - } - - /// - /// Data Error (CRC-fail in ID- or Data-Field) - /// - private bool ST1DE - { - get { return GetBit(5, _Reg1); } - set - { - if (value) { SetBit(5, ref _Reg1); } - else { UnSetBit(5, ref _Reg1); } - } - } - - /// - /// End of Track (set past most read/write commands) (see IC) - /// - private bool ST1EN - { - get { return GetBit(7, _Reg1); } - set - { - if (value) { SetBit(7, ref _Reg1); } - else { UnSetBit(7, ref _Reg1); } - } - } - - #endregion - - #region Status Register 2 - - /// - /// Status Register 2 - /// - private byte _Reg2; - - /// - /// Missing Address Mark in Data Field (DAM not found) - /// - private bool ST2MD - { - get { return GetBit(0, _Reg2); } - set - { - if (value) { SetBit(0, ref _Reg2); } - else { UnSetBit(0, ref _Reg2); } - } - } - - /// - /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) - /// - private bool ST2BC - { - get { return GetBit(1, _Reg2); } - set - { - if (value) { SetBit(1, ref _Reg2); } - else { UnSetBit(1, ref _Reg2); } - } - } - - /// - /// Scan Not Satisfied (no fitting sector found) - /// - private bool ST2SN - { - get { return GetBit(2, _Reg2); } - set - { - if (value) { SetBit(2, ref _Reg2); } - else { UnSetBit(2, ref _Reg2); } - } - } - - /// - /// Scan Equal Hit (equal) - /// - private bool ST2SH - { - get { return GetBit(3, _Reg2); } - set - { - if (value) { SetBit(3, ref _Reg2); } - else { UnSetBit(3, ref _Reg2); } - } - } - - /// - /// Wrong Cylinder (read/programmed track-ID different) (see b1) - /// - private bool ST2WC - { - get { return GetBit(4, _Reg2); } - set - { - if (value) { SetBit(4, ref _Reg2); } - else { UnSetBit(4, ref _Reg2); } - } - } - - /// - /// Data Error in Data Field (CRC-fail in data-field) - /// - private bool ST2DD - { - get { return GetBit(5, _Reg2); } - set - { - if (value) { SetBit(5, ref _Reg2); } - else { UnSetBit(5, ref _Reg2); } - } - } - - /// - /// Control Mark (read/scan command found sector with deleted DAM) - /// - private bool ST2CM - { - get { return GetBit(6, _Reg2); } - set - { - if (value) { SetBit(6, ref _Reg2); } - else { UnSetBit(6, ref _Reg2); } - } - } - - #endregion - - #region Status Register 3 - - /// - /// Status Register 3 - /// - private byte _Reg3; - - /// - /// Unit Select (pin 28,29 of FDC) - /// - private bool ST3US0 - { - get { return GetBit(0, _Reg3); } - set - { - if (value) { SetBit(0, ref _Reg3); } - else { UnSetBit(0, ref _Reg3); } - } - } - - /// - /// Unit Select (pin 28,29 of FDC) - /// - private bool ST3US1 - { - get { return GetBit(1, _Reg3); } - set - { - if (value) { SetBit(1, ref _Reg3); } - else { UnSetBit(1, ref _Reg3); } - } - } - - /// - /// Head Address (pin 27 of FDC) - /// - private bool ST3HD - { - get { return GetBit(2, _Reg3); } - set - { - if (value) { SetBit(2, ref _Reg3); } - else { UnSetBit(2, ref _Reg3); } - } - } - - /// - /// Two Side (0=yes, 1=no (!)) - /// - private bool ST3TS - { - get { return GetBit(3, _Reg3); } - set - { - if (value) { SetBit(3, ref _Reg3); } - else { UnSetBit(3, ref _Reg3); } - } - } - - /// - /// Track 0 (on track 0 we are) - /// - private bool ST3T0 - { - get { return GetBit(4, _Reg3); } - set - { - if (value) { SetBit(4, ref _Reg3); } - else { UnSetBit(4, ref _Reg3); } - } - } - - /// - /// Ready (drive ready signal) - /// - private bool ST3RY - { - get { return GetBit(5, _Reg3); } - set - { - if (value) { SetBit(5, ref _Reg3); } - else { UnSetBit(5, ref _Reg3); } - } - } - - /// - /// Write Protected (write protected) - /// - private bool ST3WP - { - get { return GetBit(6, _Reg3); } - set - { - if (value) { SetBit(6, ref _Reg3); } - else { UnSetBit(6, ref _Reg3); } - } - } - - /// - /// Fault (if supported: 1=Drive failure) - /// - private bool ST3FT - { - get { return GetBit(7, _Reg3); } - set - { - if (value) { SetBit(7, ref _Reg3); } - else { UnSetBit(7, ref _Reg3); } - } - } - - #endregion - - */ - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs index 4fd33e062e..ee4224e8a9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// @@ -95,29 +90,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum StatesPerDriveTick = TStatesPerDriveCycle; } - /* - /// - /// Called every cycle by the emulated machine - /// Simulates the floppy drive and updates execution phase bits - /// - public void RunCPUCycle() - { - // decrement tick counter - TickCounter--; - - if (TickCounter > 0) - { - // not ready to emulate a floppy drive cycle yet - return; - } - - // time to emulate a floppy drive cycle - // reset the tick counter - TickCounter = StatesPerDriveTick; - - //RunDriveCycle(); - } - */ /// /// Called by reads to the main status register @@ -144,9 +116,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LastCPUCycle = CurrentCPUCycle; return true; } - } - - } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs index 45f35b412a..a78c2f77df 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs @@ -1,10 +1,5 @@ using BizHawk.Common; -using System; -using System.Collections; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs index 7cbb83c13b..7788bca84c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs index edd54dd9b1..e0828551f2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs @@ -1,9 +1,5 @@ -using BizHawk.Common; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs index 880e388f3a..87cf5f491d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs @@ -1,9 +1,5 @@ -using BizHawk.Common; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs index ba2d3ea467..850b371054 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs @@ -1,9 +1,5 @@ -using BizHawk.Common; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs index a3789b9e5d..e224646913 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs @@ -1,9 +1,5 @@ -using BizHawk.Common; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs index 82d9fd9857..6533e76f23 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs @@ -1,9 +1,5 @@ -using BizHawk.Common; -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs index 0616003465..7e23f843a7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs @@ -2,8 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -95,118 +93,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ? (byte)(_keyLine[lineIndex] & ~lineMask) : (byte)(_keyLine[lineIndex] | lineMask); } - - /* - if (isPressed) - { - switch (k) - { - // 0xfefe - 0 - 4 - case 0: _keyLine[0] = (_keyLine[0] & ~(0x1)); break; - case 1: _keyLine[0] = (_keyLine[0] & ~(0x02)); break; - case 2: _keyLine[0] = (_keyLine[0] & ~(0x04)); break; - case 3: _keyLine[0] = (_keyLine[0] & ~(0x08)); break; - case 4: _keyLine[0] = (_keyLine[0] & ~(0x10)); break; - // 0xfdfe - 5 - 9 - case 5: _keyLine[1] = (_keyLine[1] & ~(0x1)); break; - case 6: _keyLine[1] = (_keyLine[1] & ~(0x02)); break; - case 7: _keyLine[1] = (_keyLine[1] & ~(0x04)); break; - case 8: _keyLine[1] = (_keyLine[1] & ~(0x08)); break; - case 9: _keyLine[1] = (_keyLine[1] & ~(0x10)); break; - // 0xfbfe - 10 - 14 - case 10: _keyLine[2] = (_keyLine[2] & ~(0x1)); break; - case 11: _keyLine[2] = (_keyLine[2] & ~(0x02)); break; - case 12: _keyLine[2] = (_keyLine[2] & ~(0x04)); break; - case 13: _keyLine[2] = (_keyLine[2] & ~(0x08)); break; - case 14: _keyLine[2] = (_keyLine[2] & ~(0x10)); break; - // 0xf7fe - 15 - 19 - case 15: _keyLine[3] = (_keyLine[3] & ~(0x1)); break; - case 16: _keyLine[3] = (_keyLine[3] & ~(0x02)); break; - case 17: _keyLine[3] = (_keyLine[3] & ~(0x04)); break; - case 18: _keyLine[3] = (_keyLine[3] & ~(0x08)); break; - case 19: _keyLine[3] = (_keyLine[3] & ~(0x10)); break; - // 0xeffe - 20 - 24 - case 20: _keyLine[4] = (_keyLine[4] & ~(0x1)); break; - case 21: _keyLine[4] = (_keyLine[4] & ~(0x02)); break; - case 22: _keyLine[4] = (_keyLine[4] & ~(0x04)); break; - case 23: _keyLine[4] = (_keyLine[4] & ~(0x08)); break; - case 24: _keyLine[4] = (_keyLine[4] & ~(0x10)); break; - // 0xdffe - 25 - 29 - case 25: _keyLine[5] = (_keyLine[5] & ~(0x1)); break; - case 26: _keyLine[5] = (_keyLine[5] & ~(0x02)); break; - case 27: _keyLine[5] = (_keyLine[5] & ~(0x04)); break; - case 28: _keyLine[5] = (_keyLine[5] & ~(0x08)); break; - case 29: _keyLine[5] = (_keyLine[5] & ~(0x10)); break; - // 0xbffe - 30 - 34 - case 30: _keyLine[6] = (_keyLine[6] & ~(0x1)); break; - case 31: _keyLine[6] = (_keyLine[6] & ~(0x02)); break; - case 32: _keyLine[6] = (_keyLine[6] & ~(0x04)); break; - case 33: _keyLine[6] = (_keyLine[6] & ~(0x08)); break; - case 34: _keyLine[6] = (_keyLine[6] & ~(0x10)); break; - // 0x7ffe - 35 - 39 - case 35: _keyLine[7] = (_keyLine[7] & ~(0x1)); break; - case 36: _keyLine[7] = (_keyLine[7] & ~(0x02)); break; - case 37: _keyLine[7] = (_keyLine[7] & ~(0x04)); break; - case 38: _keyLine[7] = (_keyLine[7] & ~(0x08)); break; - case 39: _keyLine[7] = (_keyLine[7] & ~(0x10)); break; - } - } - else - { - switch (k) - { - // 0xfefe - 0 - 4 - case 0: _keyLine[0] = (_keyLine[0] | (0x1)); break; - case 1: _keyLine[0] = (_keyLine[0] | (0x02)); break; - case 2: _keyLine[0] = (_keyLine[0] | (0x04)); break; - case 3: _keyLine[0] = (_keyLine[0] | (0x08)); break; - case 4: _keyLine[0] = (_keyLine[0] | (0x10)); break; - // 0xfdfe - 5 - 9 - case 5: _keyLine[1] = (_keyLine[1] | (0x1)); break; - case 6: _keyLine[1] = (_keyLine[1] | (0x02)); break; - case 7: _keyLine[1] = (_keyLine[1] | (0x04)); break; - case 8: _keyLine[1] = (_keyLine[1] | (0x08)); break; - case 9: _keyLine[1] = (_keyLine[1] | (0x10)); break; - // 0xfbfe - 10 - 14 - case 10: _keyLine[2] = (_keyLine[2] | (0x1)); break; - case 11: _keyLine[2] = (_keyLine[2] | (0x02)); break; - case 12: _keyLine[2] = (_keyLine[2] | (0x04)); break; - case 13: _keyLine[2] = (_keyLine[2] | (0x08)); break; - case 14: _keyLine[2] = (_keyLine[2] | (0x10)); break; - // 0xf7fe - 15 - 19 - case 15: _keyLine[3] = (_keyLine[3] | (0x1)); break; - case 16: _keyLine[3] = (_keyLine[3] | (0x02)); break; - case 17: _keyLine[3] = (_keyLine[3] | (0x04)); break; - case 18: _keyLine[3] = (_keyLine[3] | (0x08)); break; - case 19: _keyLine[3] = (_keyLine[3] | (0x10)); break; - // 0xeffe - 20 - 24 - case 20: _keyLine[4] = (_keyLine[4] | (0x1)); break; - case 21: _keyLine[4] = (_keyLine[4] | (0x02)); break; - case 22: _keyLine[4] = (_keyLine[4] | (0x04)); break; - case 23: _keyLine[4] = (_keyLine[4] | (0x08)); break; - case 24: _keyLine[4] = (_keyLine[4] | (0x10)); break; - // 0xdffe - 25 - 29 - case 25: _keyLine[5] = (_keyLine[5] | (0x1)); break; - case 26: _keyLine[5] = (_keyLine[5] | (0x02)); break; - case 27: _keyLine[5] = (_keyLine[5] | (0x04)); break; - case 28: _keyLine[5] = (_keyLine[5] | (0x08)); break; - case 29: _keyLine[5] = (_keyLine[5] | (0x10)); break; - // 0xbffe - 30 - 34 - case 30: _keyLine[6] = (_keyLine[6] | (0x1)); break; - case 31: _keyLine[6] = (_keyLine[6] | (0x02)); break; - case 32: _keyLine[6] = (_keyLine[6] | (0x04)); break; - case 33: _keyLine[6] = (_keyLine[6] | (0x08)); break; - case 34: _keyLine[6] = (_keyLine[6] | (0x10)); break; - // 0x7ffe - 35 - 39 - case 35: _keyLine[7] = (_keyLine[7] | (0x1)); break; - case 36: _keyLine[7] = (_keyLine[7] | (0x02)); break; - case 37: _keyLine[7] = (_keyLine[7] | (0x04)); break; - case 38: _keyLine[7] = (_keyLine[7] | (0x08)); break; - case 39: _keyLine[7] = (_keyLine[7] | (0x10)); break; - } - } - - */ // Combination keys that are not in the keyboard matrix // but are available on the Spectrum+, 128k +2 & +3 @@ -284,21 +170,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return result; } - - /* - switch (lines) - { - case 0xfe: return (byte)KeyLine[0]; - case 0xfd: return (byte)KeyLine[1]; - case 0xfb: return (byte)KeyLine[2]; - case 0xf7: return (byte)KeyLine[3]; - case 0xef: return (byte)KeyLine[4]; - case 0xdf: return (byte)KeyLine[5]; - case 0xbf: return (byte)KeyLine[6]; - case 0x7f: return (byte)KeyLine[7]; - default: return 0; - } - */ } public byte ReadKeyboardByte(ushort addr) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs index 0f0f12110b..972f7030aa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs @@ -1,13 +1,9 @@ -using BizHawk.Common; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { - + /// + /// Information about spectrum ROM + /// public class RomData { /// @@ -83,17 +79,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return RD; } - - public void SyncState(Serializer ser) - { - ser.BeginSection("RomData"); - ser.Sync("RomBytes", ref _romBytes, false); - ser.Sync("_saveBytesRoutineAddress", ref _saveBytesRoutineAddress); - ser.Sync("_loadBytesRoutineAddress", ref _loadBytesRoutineAddress); - ser.Sync("_saveBytesResumeAddress", ref _saveBytesResumeAddress); - ser.Sync("_loadBytesResumeAddress", ref _loadBytesResumeAddress); - ser.Sync("_loadBytesInvalidHeaderAddress", ref _loadBytesInvalidHeaderAddress); - ser.EndSection(); - } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs index dc5f101adf..394c18875b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs @@ -2,9 +2,6 @@ using BizHawk.Emulation.Common; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs index 6607e79e1e..5b518b031c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs @@ -1,10 +1,6 @@ using BizHawk.Common; using BizHawk.Emulation.Common; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -166,19 +162,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum LastPulse = pulse; } - - - public void StartFrame() - { - } - - public void EndFrame() - { - } #endregion - #region ISoundProvider public bool CanProvideAsync => false; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs deleted file mode 100644 index 7a12444892..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Buzzer.cs +++ /dev/null @@ -1,325 +0,0 @@ - -using BizHawk.Common; -using BizHawk.Emulation.Common; -using System; -using System.Collections.Generic; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /* - /// - /// Represents the piezoelectric buzzer used in the Spectrum to produce sound - /// The beeper is controlled by rapidly toggling bit 4 of port &FE - /// - /// For the purposes of emulation this devices is locked to a frame - /// a list of Pulses is built up over the course of the frame and outputted at the end of the frame - /// - /// ZXHawk instantiates multiples of these buzzers to achieve separation between tape and spectrum audio output - /// - public class Buzzer : ISoundProvider, IBeeperDevice - { - #region Fields and Properties - - /// - /// Sample Rate - /// This usually has to be 44100 for ISoundProvider - /// - private int _sampleRate; - public int SampleRate - { - get { return _sampleRate; } - set { _sampleRate = value; } - } - - /// - /// Number of samples in one frame - /// - private int _samplesPerFrame; - public int SamplesPerFrame - { - get { return _samplesPerFrame; } - set { _samplesPerFrame = value; } - } - - /// - /// Number of TStates in each sample - /// - private int _tStatesPerSample; - public int TStatesPerSample - { - get { return _tStatesPerSample; } - set { _tStatesPerSample = value; } - } - - /// - /// Buzzer volume - /// Accepts an int 0-100 value - /// - private int _volume; - public int Volume - { - get - { - return VolumeConverterOut(_volume); - } - set - { - _volume = VolumeConverterIn(value); - } - } - - /// - /// The number of cpu cycles per frame - /// - private long _tStatesPerFrame; - - /// - /// The cycle at which the frame starts - /// - private long _frameStart; - - /// - /// The parent emulated machine - /// - private SpectrumBase _machine; - - /// - /// Pulses collected during the last frame - /// - public List Pulses { get; private set; } - - /// - /// The last pulse - /// - public bool LastPulse { get; set; } - - /// - /// The last T-State (cpu cycle) that the last pulse was received - /// - public long LastPulseTState { get; set; } - - #endregion - - #region Private Methods - - /// - /// Takes an int 0-100 and returns the relevant short volume to output - /// - /// - /// - private int VolumeConverterIn(int vol) - { - int maxLimit = short.MaxValue / 3; - int increment = maxLimit / 100; - - return vol * increment; - } - - /// - /// Takes an short volume and returns the relevant int value 0-100 - /// - /// - /// - private int VolumeConverterOut(int shortvol) - { - int maxLimit = short.MaxValue / 3; - int increment = maxLimit / 100; - - if (shortvol > maxLimit) - shortvol = maxLimit; - - return shortvol / increment; - } - - #endregion - - #region Construction & Initialisation - - public Buzzer(SpectrumBase machine) - { - _machine = machine; - } - - /// - /// Initialises the buzzer - /// - public void Init(int sampleRate, int tStatesPerFrame) - { - _sampleRate = sampleRate; - _tStatesPerFrame = tStatesPerFrame; - _tStatesPerSample = 99; // 79; - _samplesPerFrame = 705; // 882; // (int)_tStatesPerFrame / _tStatesPerSample; - - Pulses = new List(1000); - } - - #endregion - - #region IBeeperDevice - - /// - /// When the pulse value changes it is processed here - /// - /// - public void ProcessPulseValue(bool pulse) - { - if (!_machine._renderSound) - return; - - if (pulse == LastPulse) - { - // no change detected - return; - } - - // set the lastpulse - LastPulse = pulse; - - // get where we are in the frame - var currentULACycle = _machine.CurrentFrameCycle; - var currentBuzzerCycle = currentULACycle <= _tStatesPerFrame ? currentULACycle : _tStatesPerFrame; - var length = currentBuzzerCycle - LastPulseTState; - - if (length == 0) - { - // the first T-State has changed the pulse - // do not add it - } - else if (length > 0) - { - // add the pulse - Pulse p = new Pulse - { - State = !pulse, - Length = length - }; - Pulses.Add(p); - } - - // set the last pulse tstate - LastPulseTState = currentBuzzerCycle; - } - - /// - /// New frame starts - /// - public void StartFrame() - { - Pulses.Clear(); - LastPulseTState = 0; - } - - /// - /// Frame is completed - /// - public void EndFrame() - { - // store the last pulse information - if (LastPulseTState <= _tStatesPerFrame - 1) - { - Pulse p = new Pulse - { - State = LastPulse, - Length = _tStatesPerFrame - LastPulseTState - }; - Pulses.Add(p); - } - - // create the sample array - var firstSampleOffset = _frameStart % TStatesPerSample == 0 ? 0 : TStatesPerSample - (_frameStart + TStatesPerSample) % TStatesPerSample; - var samplesInFrame = (_tStatesPerFrame - firstSampleOffset - 1) / TStatesPerSample + 1; - var samples = new short[samplesInFrame]; - - // convert pulses to samples - var sampleIndex = 0; - var currentEnd = _frameStart; - - foreach (var pulse in Pulses) - { - var firstSample = currentEnd % TStatesPerSample == 0 - ? currentEnd : currentEnd + TStatesPerSample - currentEnd % TStatesPerSample; - - for (var i = firstSample; i < currentEnd + pulse.Length; i += TStatesPerSample) - { - samples[sampleIndex++] = pulse.State ? (short)(_volume) : (short)0; - } - - currentEnd += pulse.Length; - } - - // fill the _sampleBuffer for ISoundProvider - soundBufferContains = (int)samplesInFrame; - - if (soundBuffer.Length != soundBufferContains) - soundBuffer = new short[soundBufferContains]; - - samples.CopyTo(soundBuffer, 0); - - _frameStart += _tStatesPerFrame; - } - - #endregion - - #region ISoundProvider - - private short[] soundBuffer = new short[882]; - private int soundBufferContains = 0; - - public bool CanProvideAsync => false; - - public SyncSoundMode SyncMode => SyncSoundMode.Sync; - - public void SetSyncMode(SyncSoundMode mode) - { - if (mode != SyncSoundMode.Sync) - throw new InvalidOperationException("Only Sync mode is supported."); - } - - public void GetSamplesAsync(short[] samples) - { - throw new NotSupportedException("Async is not available"); - } - - public void DiscardSamples() - { - soundBufferContains = 0; - soundBuffer = new short[SamplesPerFrame]; - } - - public void GetSamplesSync(out short[] samples, out int nsamp) - { - // convert to stereo - short[] stereoBuffer = new short[soundBufferContains * 2]; - int index = 0; - for (int i = 0; i < soundBufferContains; i++) - { - stereoBuffer[index++] = soundBuffer[i]; - stereoBuffer[index++] = soundBuffer[i]; - } - - samples = stereoBuffer; - nsamp = soundBufferContains; // _samplesPerFrame; // soundBufferContains; - } - - #endregion - - #region State Serialization - - public void SyncState(Serializer ser) - { - ser.BeginSection("Buzzer"); - ser.Sync("_frameStart", ref _frameStart); - ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); - ser.Sync("_sampleRate", ref _sampleRate); - ser.Sync("_samplesPerFrame", ref _samplesPerFrame); - ser.Sync("_tStatesPerSample", ref _tStatesPerSample); - ser.Sync("soundBuffer", ref soundBuffer, false); - ser.Sync("soundBufferContains", ref soundBufferContains); - ser.EndSection(); - } - - #endregion - } - - */ -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs deleted file mode 100644 index 140c1d136a..0000000000 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Pulse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum -{ - /// - /// The MIC and EAR pins in the spectrum deal in on/off pulses of varying lengths - /// This struct therefore represents 1 of these pulses - /// - public struct Pulse - { - /// - /// True: High State - /// False: Low State - /// - public bool State { get; set; } - - /// - /// Pulse length in Z80 T-States (cycles) - /// - public long Length { get; set; } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs index 9ae88a6e8d..10a1728cb9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -1,13 +1,12 @@ using BizHawk.Common; using BizHawk.Emulation.Cores.Components.Z80A; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// An intermediary class that manages cycling the ULA and CPU + /// along with inherent Port and Memory contention + /// public class CPUMonitor { #region Devices diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs index cee9f524da..1e5901a6ad 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// The various spectrum models ZXHawk emulates + /// public enum MachineType { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index cb80c28bd4..6aab944eb7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -1,11 +1,11 @@ - -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// - /// Handles all ZX-level input + /// The abstract class that all emulated models will inherit from + /// * Input * /// public abstract partial class SpectrumBase { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index f49c84989c..5d38566152 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -1,11 +1,13 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// The abstract class that all emulated models will inherit from + /// * Imported media * + /// public abstract partial class SpectrumBase { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs index e3511847b8..bdb8416c3a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs index 3eba9a46f2..2d15f3f2fa 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index c2968a08a2..7d50d216b4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -1,8 +1,5 @@ using BizHawk.Common; -using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components.Z80A; -using System; -using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 9a8de555ce..3def75bca8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -2,14 +2,11 @@ using BizHawk.Emulation.Common; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// - /// 3rd attempt at a base ULA implementation + /// Uncommitted logic array implementation (ULA) /// public abstract class ULA : IVideoProvider { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs index 770d861c5f..374ccb13eb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 128K and +2 Memory + /// public partial class ZX128 : SpectrumBase { /* 128k paging controlled by writes to port 0x7ffd 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 256716f315..6b33600bcc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -1,12 +1,11 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 128K/+2 Port + /// public partial class ZX128 : SpectrumBase { /// 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 06b1d7d9ba..2c7d1a722b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 128K/+2 ULA + /// class Screen128 : ULA { #region Construction diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs index ed556a3025..009c882d38 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -1,12 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Collections.Generic; using BizHawk.Emulation.Cores.Components.Z80A; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 128K Constructor + /// public partial class ZX128 : SpectrumBase { #region Construction @@ -28,9 +27,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; - - //ULADevice = new ULA128(this); - ULADevice = new Screen128(this); // still todo + + ULADevice = new Screen128(this); BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); @@ -51,7 +49,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum InitializeMedia(files); } - #endregion - + #endregion } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs index 72c546da51..c5f833dfe4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs @@ -1,9 +1,5 @@ using BizHawk.Emulation.Cores.Components.Z80A; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs index 323e683b94..e8c384efd9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -7,6 +7,9 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// +2A Memory + /// public partial class ZX128Plus2a : SpectrumBase { /* http://www.worldofspectrum.org/faq/reference/128kreference.htm 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 57fab06df4..6809731766 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -1,12 +1,11 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// +2a Port + /// public partial class ZX128Plus2a : SpectrumBase { /// 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 53c0f3a0cb..7d9deeed41 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// +2A/+3 ULA + /// class Screen128Plus2a : ULA { #region Construction diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs index 363f1a52ac..b38ba2ef0a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -1,12 +1,11 @@ using BizHawk.Emulation.Cores.Components.Z80A; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// +2A Constructor + /// public partial class ZX128Plus2a : SpectrumBase { #region Construction @@ -28,9 +27,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; - - //ULADevice = new ULAPlus2a(this); - ULADevice = new Screen128Plus2a(this); // still todo + + ULADevice = new Screen128Plus2a(this); BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs index 6bec98ab37..54295e06d8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -7,6 +7,9 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// +3 Memory + /// public partial class ZX128Plus3 : SpectrumBase { /* http://www.worldofspectrum.org/faq/reference/128kreference.htm 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 e7a4a7304b..5e13f828ca 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -1,12 +1,11 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// +3 Port + /// public partial class ZX128Plus3 : SpectrumBase { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs index 4e6d889d55..c27f74a265 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -1,12 +1,11 @@ using BizHawk.Emulation.Cores.Components.Z80A; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// +3 Constructor + /// public partial class ZX128Plus3 : SpectrumBase { #region Construction @@ -29,9 +28,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum SHADOWPaged = false; RAMPaged = 0; PagingDisabled = false; - - // ULADevice = new ULAPlus3(this); - ULADevice = new Screen128Plus2a(this); // still todo + + ULADevice = new Screen128Plus2a(this); BuzzerDevice = new Beeper(this); BuzzerDevice.Init(44100, ULADevice.FrameLength); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs index 7f52c2dcc8..f59a7652a1 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -1,12 +1,11 @@ using BizHawk.Emulation.Cores.Components.Z80A; -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 16K is idential to 48K, just without the top 32KB of RAM + /// public class ZX16 : ZX48 { #region Construction 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 6d49076f41..e222e7114a 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -6,6 +6,9 @@ using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 48K Memory + /// public partial class ZX48 : SpectrumBase { /* 48K Spectrum has NO memory paging 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 a4cbc9bd5b..019b82e666 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 48K Port + /// public partial class ZX48 : SpectrumBase { /// 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 d572f44e37..9f01e83c04 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -1,11 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 48K ULA + /// class Screen48 : ULA { #region Construction diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs index a8d33eb03a..1f2508dd63 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -1,12 +1,12 @@ using BizHawk.Emulation.Cores.Components.Z80A; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// 48K construction + /// public partial class ZX48 : SpectrumBase { #region Construction diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs index b8cbf0968e..3d00d74cb6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs @@ -1,9 +1,4 @@ -using BizHawk.Common.NumberExtensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Text; using BizHawk.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs index 99818cc932..43f36a6a76 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs @@ -1,9 +1,4 @@ -using BizHawk.Common.NumberExtensions; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Text; using BizHawk.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs index fe185248ee..51ee9f75f4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index e9b5252a9d..bebf139c7b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -1,15 +1,13 @@ using BizHawk.Common; -using BizHawk.Common.NumberExtensions; using System; using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// - /// This interface defines a logical floppy disk + /// This abstract class defines a logical floppy disk /// public abstract class FloppyDisk { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs index f3e56b38fd..6f9dc4c130 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs @@ -1,13 +1,9 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// - /// Abtract class that represents all Media Serializers + /// Abtract class that represents all Media Converters /// public abstract class MediaConverter { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs index d7b456919f..034e45e5d0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs index c74e3fa5a6..945585cc71 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs index 14fc54a460..f61fc49e0f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - + namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index ee1a6bc4d8..0b1c4e910f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -1,9 +1,7 @@ using BizHawk.Common; -using System; using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs index fe6ef6fe6c..2533e1fef9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs index b5ffebe64f..55df2265f9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs @@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider /// Currently only supports SyncSoundMode.Sync /// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length (882) - /// (if not, their buffer will be truncated/expanded) + /// (if not, only 882 samples of their buffer will be used) /// internal sealed class SoundProviderMixer : ISoundProvider { @@ -209,6 +209,5 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index fa69c5164f..b44e1af74c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -1,13 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -using BizHawk.Common; -using BizHawk.Common.ReflectionExtensions; +using System.Collections.Generic; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// ZXHawk: Core Class + /// * Controllers * + /// public partial class ZXSpectrum { /// @@ -125,38 +124,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return definition; } } - - - - /* - /// - /// Controller mapping includes all keyboard keys from the following models: - /// https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/ZXSpectrum48k.jpg/1200px-ZXSpectrum48k.jpg - /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg - /// - public static readonly ControllerDefinition ZXSpectrumControllerDefinition = new ControllerDefinition - { - Name = "ZXSpectrum Controller", - BoolButtons = - { - // Kempston Joystick (P1) - "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", - // Keyboard - row 1 - "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", - // Keyboard - row 2 - "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", - // Keyboard - row 3 - "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", - // Keyboard - row 4 - "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", - // Keyboard - row 5 - "Key Symbol Shift", "Key Semi-Colon", "Key Quote", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", - // Tape functions - "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", "Insert Previous Tape", "Next Tape Block", "Prev Tape Block" - } - }; - - */ } /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs index caa161e0d7..ccb68cf475 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; - -using BizHawk.Common.NumberExtensions; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// ZXHawk: Core Class + /// * IDebugggable * + /// public partial class ZXSpectrum : IDebuggable { public IDictionary GetCpuFlagsAndRegisters() diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs index c87c9991d7..1276521373 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -1,8 +1,11 @@ using BizHawk.Emulation.Common; -using BizHawk.Common.NumberExtensions; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// ZXHawk: Core Class + /// * IEmulator * + /// public partial class ZXSpectrum : IEmulator { public IEmulatorServiceProvider ServiceProvider { get; } @@ -60,8 +63,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum get { return deterministicEmulation; } } - //public bool DeterministicEmulation => true; - public void ResetCounters() { _machine.FrameCount = 0; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs index da34d34c66..6861bb50ff 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs @@ -1,9 +1,12 @@ - -using System; +using System; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// ZXHawk: Core Class + /// * IInputPollable * + /// public partial class ZXSpectrum : IInputPollable { public int LagCount diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs index 5245886f7c..47e9bd83be 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; - using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { - public partial class ZXSpectrum //: IMemoryDomains + /// + /// ZXHawk: Core Class + /// * Memory Domains * + /// + public partial class ZXSpectrum { internal IMemoryDomains memoryDomains; private readonly Dictionary _byteArrayDomains = new Dictionary(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs index 0c76d3e863..59bbe6e9c4 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -1,13 +1,14 @@ -using System; -using Newtonsoft.Json; - -using BizHawk.Common; +using BizHawk.Common; using BizHawk.Emulation.Common; using System.ComponentModel; using System.Text; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// ZXHawk: Core Class + /// * ISettable * + /// public partial class ZXSpectrum : ISettable { internal ZXSpectrumSettings Settings = new ZXSpectrumSettings(); @@ -150,6 +151,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + /// + /// Verbosity of the ZXHawk generated OSD messages + /// public enum OSDVerbosity { /// diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs index 01419fd093..74ab460903 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -1,10 +1,13 @@ using System.IO; - using BizHawk.Common; using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// ZXHawk: Core Class + /// * IStatable * + /// public partial class ZXSpectrum : IStatable { public bool BinarySaveStatesPreferred diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs index 7b14980b30..4a3a20d6ca 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -1,13 +1,12 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// - /// Handles all messaging (OSD) operations + /// ZXHawk: Core Class + /// * Handles all messaging (OSD) operations * /// public partial class ZXSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs index 2444e87c30..8423f50e1f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -1,14 +1,11 @@ using System; using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { /// - /// Utilities + /// ZXHawk: Core Class + /// * Misc Utilities * /// public partial class ZXSpectrum { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 37de2d0a63..811ede903c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -1,6 +1,5 @@ using BizHawk.Common; using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Components; using BizHawk.Emulation.Cores.Components.Z80A; using BizHawk.Emulation.Cores.Properties; using System; @@ -10,6 +9,10 @@ using System.Linq; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { + /// + /// ZXHawk: Core Class + /// * Main Initialization * + /// [Core( "ZXHawk", "Asnivor, Alyosha", From 3e495e3a2862a69bea96728d89fe5bf04db2d22a Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 14 Jun 2018 08:02:06 -0400 Subject: [PATCH 304/339] z80: reset cleanup --- BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt | 8 +++----- BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs | 6 ------ BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs | 15 +++++++++------ 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt b/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt index 10804fde94..db37bed6fa 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt @@ -1,7 +1,5 @@ TODO: -Mode 0 -Check T-cycle level memory access timing -Check R register -new tests for WZ Registers -Memory refresh - IR is pushed onto the address bus at instruction start, does anything need this? +Mode 0 (nothing to test with) +IRQ BUS timings (nothing to test with) +BusAQ (nothing to test with) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index b081996cde..339fb6864e 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -127,12 +127,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A Regs[i] = 0; } - // These registers are set as part of the reset process - Regs[A] = 0xFF; - Regs[F] = 0xFF; - Regs[SPl] = 0xFF; - Regs[SPh] = 0xFF; - // the IRQ1 vector is 0x38 Regs[IRQ_V] = 0x38; // The NMI vector is constant 0x66 diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index cc4d21ce60..242b21dbac 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -97,13 +97,16 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ResetInterrupts(); TotalExecutedCycles = 0; cur_instr = new ushort[] - { IDLE, - WAIT, - OP_F, - OP }; + {IDLE, + DEC16, F, A, + DEC16, SPl, SPh, + IDLE, + WAIT, + OP_F, + OP }; - BUSRQ = new ushort[] { PCh, 0, 0, 0 }; - MEMRQ = new ushort[] { PCh, 0, 0, 0 }; + BUSRQ = new ushort[] { 0, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, PCh, 0, 0, 0 }; instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; NO_prefix = true; } From 6476b6739a98eedb76b34b1561858456e006e9ab Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 14 Jun 2018 14:51:13 +0100 Subject: [PATCH 305/339] ZXHawk: fixed render bool implementation (frameskip) --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.cs | 4 ---- .../Computers/SinclairSpectrum/Machine/ULA.cs | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index 7d50d216b4..f2c4de0c31 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -183,10 +183,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // we have reached the end of a frame LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; - // paint the buffer at end of frame - if (_render) - ULADevice.RenderScreen(ULADevice.FrameLength); - ULADevice.LastTState = 0; if (AYDevice != null) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs index 3def75bca8..0f46892833 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -141,7 +141,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public virtual void CycleClock(long totalCycles) { // render the screen - RenderScreen((int)_machine.CurrentFrameCycle); + if (_machine._render) + RenderScreen((int)_machine.CurrentFrameCycle); // has more than one cycle past since this last ran // (this can be true if contention has taken place) From 8edb61eb52d8b6a3d46da8283f35ecfb846b1174 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 14 Jun 2018 11:05:48 -0400 Subject: [PATCH 306/339] GBHawk: set to released and proper window time (tested on console) --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs | 2 +- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs | 2 +- BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs index 6d85069d24..1988ff98fa 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -655,7 +655,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_latch = LCDC.Bit(5); // TODO: If Window is turned on midscanline what happens? When is this check done exactly? - if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y))) + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY >= window_y))) { window_y_tile_inc++; if (window_y_tile_inc==8) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index ed6d09a237..1a65681be0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk "GBHawk", "", isPorted: false, - isReleased: false)] + isReleased: true)] [ServiceNotApplicable(typeof(IDriveLight))] public partial class GBHawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, IGameboyCommon, ISettable diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs index 535844ebc3..255cbf9d1b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -426,7 +426,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk window_latch = LCDC.Bit(5); // TODO: If Window is turned on midscanline what happens? When is this check done exactly? - if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY > window_y))) + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY >= window_y))) { window_y_tile_inc++; if (window_y_tile_inc==8) From 19908bdd0320181cb5233b2e04c178f43d48092e Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 14 Jun 2018 15:54:57 -0400 Subject: [PATCH 307/339] GBHawk: clean up --- .../Consoles/Nintendo/GBHawk/Audio.cs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index c272d52839..8ebbe4d9a2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -112,7 +112,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte sample; - public int master_audio_clock; + public uint master_audio_clock; public int latched_sample_L, latched_sample_R; @@ -714,13 +714,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (L_final != latched_sample_L) { - _blip_L.AddDelta((uint)master_audio_clock, L_final - latched_sample_L); + _blip_L.AddDelta(master_audio_clock, L_final - latched_sample_L); latched_sample_L = L_final; } if (R_final != latched_sample_R) { - _blip_R.AddDelta((uint)master_audio_clock, R_final - latched_sample_R); + _blip_R.AddDelta(master_audio_clock, R_final - latched_sample_R); latched_sample_R = R_final; } @@ -730,6 +730,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // the sequencer is actually the timer DIV register // so if it's constantly written to, these values won't update bool check = Core.double_speed ? Core.timer.divider_reg.Bit(13) : Core.timer.divider_reg.Bit(12); + if (check && !timer_bit_old) { sequencer_vol++; sequencer_vol &= 0x7; @@ -930,7 +931,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Audio_Regs = new byte[21]; - AudioClocks = 0; master_audio_clock = 0; sequencer_len = 0; @@ -1064,7 +1064,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool CanProvideAsync => false; - public int AudioClocks; public short[] AudioSamples = new short[150000]; public void SetSyncMode(SyncSoundMode mode) @@ -1079,8 +1078,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void GetSamplesSync(out short[] samples, out int nsamp) { - _blip_L.EndFrame((uint)master_audio_clock); - _blip_R.EndFrame((uint)master_audio_clock); + _blip_L.EndFrame(master_audio_clock); + _blip_R.EndFrame(master_audio_clock); nsamp = _blip_R.SamplesAvailable(); @@ -1089,9 +1088,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _blip_L.ReadSamplesLeft(samples, nsamp); _blip_R.ReadSamplesRight(samples, nsamp); - AudioClocks = 0; master_audio_clock = 0; - } public void GetSamplesAsync(short[] samples) @@ -1101,7 +1098,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void DiscardSamples() { - AudioClocks = 0; _blip_L.Clear(); _blip_R.Clear(); master_audio_clock = 0; From 637bb90b54b0ce11459c6ac7a3c82ca96949a953 Mon Sep 17 00:00:00 2001 From: bsmiles32 Date: Sat, 16 Jun 2018 22:57:07 +0200 Subject: [PATCH 308/339] [N64] Update RSP-HLE plugin to latest version. (#1213) * [N64] Update RSP-HLE plugin to latest version. * Updated RSP-HLE dll binary --- libmupen64plus/mupen64plus-rsp-hle/LICENSES | 5 +- libmupen64plus/mupen64plus-rsp-hle/RELEASE | 11 + .../mupen64plus-rsp-hle.vcxproj | 194 ++++ .../msvc11/mupen64plus-rsp-hle.vcxproj | 29 +- .../projects/msvc8/mupen64plus-rsp-hle.vcproj | 60 +- .../projects/unix/Makefile | 63 +- .../mupen64plus-rsp-hle/src/alist.c | 1022 ++++++++++++++++- .../mupen64plus-rsp-hle/src/alist.h | 157 ++- .../mupen64plus-rsp-hle/src/alist_audio.c | 310 +++++ .../mupen64plus-rsp-hle/src/alist_naudio.c | 333 ++++++ .../mupen64plus-rsp-hle/src/alist_nead.c | 556 +++++++++ .../src/{jpeg.h => arithmetics.h} | 29 +- .../mupen64plus-rsp-hle/src/audio.c | 128 +++ .../src/{alist_internal.h => audio.h} | 45 +- .../mupen64plus-rsp-hle/src/cicx105.c | 21 +- .../src/{cicx105.h => common.h} | 22 +- libmupen64plus/mupen64plus-rsp-hle/src/hle.c | 489 ++++++++ libmupen64plus/mupen64plus-rsp-hle/src/hle.h | 89 +- .../mupen64plus-rsp-hle/src/hle_external.h | 40 + .../mupen64plus-rsp-hle/src/hle_internal.h | 91 ++ libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c | 498 ++++---- libmupen64plus/mupen64plus-rsp-hle/src/main.c | 476 -------- .../mupen64plus-rsp-hle/src/memory.c | 74 ++ .../mupen64plus-rsp-hle/src/memory.h | 185 +++ libmupen64plus/mupen64plus-rsp-hle/src/mp3.c | 684 +++++++++++ .../mupen64plus-rsp-hle/src/musyx.c | 988 ++++++++++++++++ .../mupen64plus-rsp-hle/src/osal_dynamiclib.h | 34 + .../src/osal_dynamiclib_unix.c | 71 ++ .../src/osal_dynamiclib_win32.c | 75 ++ .../mupen64plus-rsp-hle/src/plugin.c | 495 ++++++++ libmupen64plus/mupen64plus-rsp-hle/src/re2.c | 224 ++++ .../mupen64plus-rsp-hle/src/ucode1.cpp | 951 --------------- .../mupen64plus-rsp-hle/src/ucode2.cpp | 930 --------------- .../mupen64plus-rsp-hle/src/ucode3.cpp | 834 -------------- .../mupen64plus-rsp-hle/src/ucode3mp3.cpp | 604 ---------- .../mupen64plus-rsp-hle/src/ucodes.h | 152 +++ output/dll/mupen64plus-rsp-hle.dll | Bin 51712 -> 76800 bytes 37 files changed, 6678 insertions(+), 4291 deletions(-) create mode 100644 libmupen64plus/mupen64plus-rsp-hle/projects/VisualStudio2013/mupen64plus-rsp-hle.vcxproj create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/alist_audio.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/alist_naudio.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/alist_nead.c rename libmupen64plus/mupen64plus-rsp-hle/src/{jpeg.h => arithmetics.h} (71%) create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/audio.c rename libmupen64plus/mupen64plus-rsp-hle/src/{alist_internal.h => audio.h} (61%) rename libmupen64plus/mupen64plus-rsp-hle/src/{cicx105.h => common.h} (73%) create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/hle.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/hle_external.h create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/hle_internal.h delete mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/main.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/memory.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/memory.h create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/mp3.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/musyx.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib.h create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_unix.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_win32.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/plugin.c create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/re2.c delete mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/ucode1.cpp delete mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/ucode2.cpp delete mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/ucode3.cpp delete mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/ucode3mp3.cpp create mode 100644 libmupen64plus/mupen64plus-rsp-hle/src/ucodes.h diff --git a/libmupen64plus/mupen64plus-rsp-hle/LICENSES b/libmupen64plus/mupen64plus-rsp-hle/LICENSES index e1403bd131..4319820ae1 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/LICENSES +++ b/libmupen64plus/mupen64plus-rsp-hle/LICENSES @@ -5,7 +5,8 @@ Mupen64Plus-rsp-hle is licensed under the GNU General Public License version 2. The authors of Mupen64Plus-rsp-hle are: * Richard Goedeken (Richard42) - * Bobby Smiles + * Bobby Smiles (bsmiles32) + * Sven Eckelmann (ecsv) * John Chadwick (NMN) * James Hood (Ebenblues) * Scott Gorman (okaygo) @@ -29,7 +30,7 @@ Mupen64Plus is based on GPL-licensed source code from Mupen64 v0.5, originally w Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/libmupen64plus/mupen64plus-rsp-hle/RELEASE b/libmupen64plus/mupen64plus-rsp-hle/RELEASE index 7a661112fe..0b19ad2357 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/RELEASE +++ b/libmupen64plus/mupen64plus-rsp-hle/RELEASE @@ -1,6 +1,17 @@ RSP High-Level Emulation plugin for Mupen64Plus ----------------------------------------------- +Mupen64Plus-rsp-hle v2.5 - April 26, 2015 +------------------------------------------------- + - Game-specific fixes: Bottom of the 9th, IndianaJones, BattleForNaboo, Conkers Bad Fur Day + - Support for MusyX microcodes + - Improve audio microcode identification + - Huge quantity of code cleanups and refactoring to improve organization + - Add support for additional audio commands: #16, POLEF, RESAMPLE_ZOH + - Multiple bugfixes in audio processing code + - Move global variables into a struct so code is re-entrant + - bugfix: microcode detection could sometimes fail after reset + Mupen64Plus-rsp-hle v2.0 - July 4, 2013 ------------------------------------------------- - Add support for MusyX ucode detection diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/VisualStudio2013/mupen64plus-rsp-hle.vcxproj b/libmupen64plus/mupen64plus-rsp-hle/projects/VisualStudio2013/mupen64plus-rsp-hle.vcxproj new file mode 100644 index 0000000000..3b6349f016 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/VisualStudio2013/mupen64plus-rsp-hle.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2EC7CEE3-C7A7-4F2E-B2C8-4DF6AFEC3E9A} + mupen64plusrsphle + Win32Proj + + + + DynamicLibrary + MultiByte + true + v120_xp + + + DynamicLibrary + MultiByte + true + v120 + + + DynamicLibrary + MultiByte + v120 + + + DynamicLibrary + MultiByte + v120 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + false + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + Disabled + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + Default + + + true + Windows + MachineX86 + + + + + Disabled + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + Default + + + true + Windows + + + + + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + Default + + + true + Windows + true + true + MachineX86 + + + + + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + Default + + + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj index f71023ea49..c73605209c 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj @@ -187,22 +187,33 @@ + + + + + - - - - - + + + + + + - - + + + - + + + + + - \ No newline at end of file + diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj index 1b9522852c..6ee59896c7 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj @@ -182,32 +182,56 @@ RelativePath="..\..\src\alist.c" > + + + + + + + + + + + + @@ -221,11 +245,15 @@ > + + + + + + + + + + diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile b/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile index ca5e657d3c..f9162a2bde 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile @@ -1,6 +1,6 @@ #/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # * mupen64plus-rsp-hle - Makefile * -# * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * +# * Mupen64Plus homepage: https://mupen64plus.org/ * # * Copyright (C) 2008-2009 Richard Goedeken * # * Copyright (C) 2007-2008 DarkJeztr Tillin9 * # * * @@ -53,7 +53,6 @@ ifeq ("$(UNAME)","OpenBSD") OS = FREEBSD SO_EXTENSION = so SHARED = -shared - $(warning OS type "$(UNAME)" not officially supported.') endif ifneq ("$(filter GNU/kFreeBSD kfreebsd,$(UNAME))","") OS = LINUX @@ -67,7 +66,7 @@ ifeq ("$(patsubst MINGW%,MINGW,$(UNAME))","MINGW") PIC = 0 endif ifeq ("$(OS)","NONE") - $(error OS type "$(UNAME)" not supported. Please file bug report at 'http://code.google.com/p/mupen64plus/issues') + $(error OS type "$(UNAME)" not supported. Please file bug report at 'https://github.com/mupen64plus/mupen64plus-core/issues') endif # detect system architecture @@ -111,15 +110,20 @@ ifneq ("$(filter arm%,$(HOST_CPU))","") $(warning Architecture "$(HOST_CPU)" not officially supported.') endif endif +ifneq ("$(filter mips,$(HOST_CPU))","") + CPU := MIPS + ARCH_DETECTED := 32BITS + PIC ?= 1 + $(warning Architecture "$(HOST_CPU)" not officially supported.') +endif ifeq ("$(CPU)","NONE") - $(error CPU type "$(HOST_CPU)" not supported. Please file bug report at 'http://code.google.com/p/mupen64plus/issues') + $(error CPU type "$(HOST_CPU)" not supported. Please file bug report at 'https://github.com/mupen64plus/mupen64plus-core/issues') endif # base CFLAGS, LDLIBS, and LDFLAGS OPTFLAGS ?= -O3 -flto WARNFLAGS ?= -Wall CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -ffast-math -fno-strict-aliasing -fvisibility=hidden -I../../src -CXXFLAGS += -fvisibility-inlines-hidden LDFLAGS += $(SHARED) # Since we are building a shared library, we must compile with -fPIC on some architectures @@ -147,11 +151,10 @@ endif ifeq ($(OS), LINUX) # only export api symbols LDFLAGS += -Wl,-version-script,$(SRCDIR)/rsp_api_export.ver + LDLIBS += -ldl endif ifeq ($(OS), OSX) - #xcode-select has been around since XCode 3.0, i.e. OS X 10.5 - OSX_SDK_ROOT = $(shell xcode-select -print-path)/Platforms/MacOSX.platform/Developer/SDKs - OSX_SDK_PATH = $(OSX_SDK_ROOT)/$(shell ls $(OSX_SDK_ROOT) | tail -1) + OSX_SDK_PATH = $(shell xcrun --sdk macosx --show-sdk-path) ifeq ($(CPU), X86) ifeq ($(ARCH_DETECTED), 64BITS) @@ -189,26 +192,24 @@ endif ifneq ($(findstring $(MAKEFLAGS),s),s) ifndef V Q_CC = @echo ' CC '$@; - Q_CXX = @echo ' CXX '$@; Q_LD = @echo ' LD '$@; endif endif # set base program pointers and flags CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ RM ?= rm -f INSTALL ?= install MKDIR ?= mkdir -p COMPILE.c = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -COMPILE.cc = $(Q_CXX)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -LINK.o = $(Q_LD)$(CXX) $(CXXFLAGS) $(LDFLAGS) $(TARGET_ARCH) +LINK.o = $(Q_LD)$(CC) $(CFLAGS) $(LDFLAGS) $(TARGET_ARCH) # set special flags for given Makefile parameters ifeq ($(DEBUG),1) CFLAGS += -g INSTALL_STRIP_FLAG ?= else + CFLAGS += -DNDEBUG ifneq ($(OS),OSX) INSTALL_STRIP_FLAG ?= -s endif @@ -225,23 +226,41 @@ ifeq ($(PLUGINDIR),) PLUGINDIR := $(LIBDIR)/mupen64plus endif +# enable/disable task dumping support +ifeq ($(DUMP), 1) + CFLAGS += -DENABLE_TASK_DUMP +endif + + SRCDIR = ../../src OBJDIR = _obj$(POSTFIX) # list of source files to compile SOURCE = \ - $(SRCDIR)/main.c \ $(SRCDIR)/alist.c \ + $(SRCDIR)/alist_audio.c \ + $(SRCDIR)/alist_naudio.c \ + $(SRCDIR)/alist_nead.c \ + $(SRCDIR)/audio.c \ $(SRCDIR)/cicx105.c \ + $(SRCDIR)/hle.c \ $(SRCDIR)/jpeg.c \ - $(SRCDIR)/ucode3.cpp \ - $(SRCDIR)/ucode2.cpp \ - $(SRCDIR)/ucode1.cpp \ - $(SRCDIR)/ucode3mp3.cpp + $(SRCDIR)/memory.c \ + $(SRCDIR)/mp3.c \ + $(SRCDIR)/musyx.c \ + $(SRCDIR)/re2.c \ + $(SRCDIR)/plugin.c + +ifeq ($(OS), MINGW) +SOURCE += \ + $(SRCDIR)/osal_dynamiclib_win32.c +else +SOURCE += \ + $(SRCDIR)/osal_dynamiclib_unix.c +endif # generate a list of object files build, make a temporary directory for them OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(filter %.c, $(SOURCE))) -OBJECTS += $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(filter %.cpp, $(SOURCE))) OBJDIRS = $(dir $(OBJECTS)) $(shell $(MKDIR) $(OBJDIRS)) @@ -263,6 +282,7 @@ targets: @echo " WARNFLAGS=flag == compiler warning levels (default: -Wall)" @echo " PIC=(1|0) == Force enable/disable of position independent code" @echo " POSTFIX=name == String added to the name of the the build (default: '')" + @echo " DUMP=(1|0) == Enable/Disable unknown task dumping (default: 0)" @echo " Install Options:" @echo " PREFIX=path == install/uninstall prefix (default: /usr/local)" @echo " LIBDIR=path == library prefix (default: PREFIX/lib)" @@ -287,18 +307,13 @@ clean: rebuild: clean all # build dependency files -CFLAGS += -MD +CFLAGS += -MD -MP -include $(OBJECTS:.o=.d) -CXXFLAGS += $(CFLAGS) - # standard build rules $(OBJDIR)/%.o: $(SRCDIR)/%.c $(COMPILE.c) -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR)/%.cpp - $(COMPILE.cc) -o $@ $< - $(TARGET): $(OBJECTS) $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist.c index c1418939e2..ef3adc2ec4 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/alist.c +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist.c @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - alist.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2012 Bobby Smiles * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * Copyright (C) 2009 Richard Goedeken * * Copyright (C) 2002 Hacktarux * * * @@ -21,62 +21,1004 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include "hle.h" -#include "alist_internal.h" +#include +#include +#include +#include -// FIXME: this decomposition into 3 ABI is not accurate, -// there are a least 9 or 10 different ABI, each with one or a few revisions -// for a total of almost 16 differents audio ucode. -// -// ABI2 in fact is a mix of at least 7 differents ABI which are mostly compatible -// but not totally, that's why there is a isZeldaABI/isMKABI workaround. -// -extern const acmd_callback_t ABI1[0x10]; -extern const acmd_callback_t ABI2[0x20]; -extern const acmd_callback_t ABI3[0x10]; +#include "alist.h" +#include "arithmetics.h" +#include "audio.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" + +struct ramp_t +{ + int64_t value; + int64_t step; + int64_t target; +}; /* local functions */ -static void alist_process(const acmd_callback_t abi[], unsigned int abi_size) +static void swap(int16_t **a, int16_t **b) { - u32 inst1, inst2; - unsigned int acmd; - const OSTask_t * const task = get_task(); + int16_t* tmp = *b; + *b = *a; + *a = tmp; +} - const unsigned int *alist = (unsigned int*)(rsp.RDRAM + task->data_ptr); - const unsigned int * const alist_end = alist + (task->data_size >> 2); +static int16_t* sample(struct hle_t* hle, unsigned pos) +{ + return (int16_t*)hle->alist_buffer + ((pos ^ S) & 0xfff); +} - while (alist != alist_end) +static uint8_t* alist_u8(struct hle_t* hle, uint16_t dmem) +{ + return (uint8_t*)(hle->alist_buffer + ((dmem ^ S8) & 0xfff)); +} + +static int16_t* alist_s16(struct hle_t* hle, uint16_t dmem) +{ + return (int16_t*)(hle->alist_buffer + ((dmem ^ S16) & 0xfff)); +} + + +static void sample_mix(int16_t* dst, int16_t src, int16_t gain) +{ + *dst = clamp_s16(*dst + ((src * gain) >> 15)); +} + +static void alist_envmix_mix(size_t n, int16_t** dst, const int16_t* gains, int16_t src) +{ + size_t i; + + for(i = 0; i < n; ++i) + sample_mix(dst[i], src, gains[i]); +} + +static int16_t ramp_step(struct ramp_t* ramp) +{ + bool target_reached; + + ramp->value += ramp->step; + + target_reached = (ramp->step <= 0) + ? (ramp->value <= ramp->target) + : (ramp->value >= ramp->target); + + if (target_reached) { - inst1 = *(alist++); - inst2 = *(alist++); - - acmd = inst1 >> 24; - - if (acmd < abi_size) - { - (*abi[acmd])(inst1, inst2); - } - else - { - DebugMessage(M64MSG_WARNING, "Invalid ABI command %u", acmd); - } + ramp->value = ramp->target; + ramp->step = 0; } + + return (int16_t)(ramp->value >> 16); } /* global functions */ -void alist_process_ABI1() +void alist_process(struct hle_t* hle, const acmd_callback_t abi[], unsigned int abi_size) { - alist_process(ABI1, 0x10); + uint32_t w1, w2; + unsigned int acmd; + + const uint32_t *alist = dram_u32(hle, *dmem_u32(hle, TASK_DATA_PTR)); + const uint32_t *const alist_end = alist + (*dmem_u32(hle, TASK_DATA_SIZE) >> 2); + + while (alist != alist_end) { + w1 = *(alist++); + w2 = *(alist++); + + acmd = (w1 >> 24) & 0x7f; + + if (acmd < abi_size) + (*abi[acmd])(hle, w1, w2); + else + HleWarnMessage(hle->user_defined, "Invalid ABI command %u", acmd); + } } -void alist_process_ABI2() +uint32_t alist_get_address(struct hle_t* hle, uint32_t so, const uint32_t *segments, size_t n) { - alist_process(ABI2, 0x20); + uint8_t segment = (so >> 24) & 0x3f; + uint32_t offset = (so & 0xffffff); + + if (segment >= n) { + HleWarnMessage(hle->user_defined, "Invalid segment %u", segment); + return offset; + } + + return segments[segment] + offset; } -void alist_process_ABI3() +void alist_set_address(struct hle_t* hle, uint32_t so, uint32_t *segments, size_t n) { - alist_process(ABI3, 0x10); + uint8_t segment = (so >> 24) & 0x3f; + uint32_t offset = (so & 0xffffff); + + if (segment >= n) { + HleWarnMessage(hle->user_defined, "Invalid segment %u", segment); + return; + } + + segments[segment] = offset; +} + +void alist_clear(struct hle_t* hle, uint16_t dmem, uint16_t count) +{ + while(count != 0) { + *alist_u8(hle, dmem++) = 0; + --count; + } +} + +void alist_load(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count) +{ + /* enforce DMA alignment constraints */ + dmem &= ~3; + address &= ~7; + count = align(count, 8); + memcpy(hle->alist_buffer + dmem, hle->dram + address, count); +} + +void alist_save(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count) +{ + /* enforce DMA alignment constraints */ + dmem &= ~3; + address &= ~7; + count = align(count, 8); + memcpy(hle->dram + address, hle->alist_buffer + dmem, count); +} + +void alist_move(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count) +{ + while (count != 0) { + *alist_u8(hle, dmemo++) = *alist_u8(hle, dmemi++); + --count; + } +} + +void alist_copy_every_other_sample(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count) +{ + while (count != 0) { + *alist_s16(hle, dmemo) = *alist_s16(hle, dmemi); + dmemo += 2; + dmemi += 4; + --count; + } +} + +void alist_repeat64(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint8_t count) +{ + uint16_t buffer[64]; + + memcpy(buffer, hle->alist_buffer + dmemi, 128); + + while(count != 0) { + memcpy(hle->alist_buffer + dmemo, buffer, 128); + dmemo += 128; + --count; + } +} + +void alist_copy_blocks(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t block_size, uint8_t count) +{ + int block_left = count; + + do + { + int bytes_left = block_size; + + do + { + memcpy(hle->alist_buffer + dmemo, hle->alist_buffer + dmemi, 0x20); + bytes_left -= 0x20; + + dmemi += 0x20; + dmemo += 0x20; + + } while(bytes_left > 0); + + --block_left; + } while(block_left > 0); +} + +void alist_interleave(struct hle_t* hle, uint16_t dmemo, uint16_t left, uint16_t right, uint16_t count) +{ + uint16_t *dst = (uint16_t*)(hle->alist_buffer + dmemo); + const uint16_t *srcL = (uint16_t*)(hle->alist_buffer + left); + const uint16_t *srcR = (uint16_t*)(hle->alist_buffer + right); + + count >>= 2; + + while(count != 0) { + uint16_t l1 = *(srcL++); + uint16_t l2 = *(srcL++); + uint16_t r1 = *(srcR++); + uint16_t r2 = *(srcR++); + +#if M64P_BIG_ENDIAN + *(dst++) = l1; + *(dst++) = r1; + *(dst++) = l2; + *(dst++) = r2; +#else + *(dst++) = r2; + *(dst++) = l2; + *(dst++) = r1; + *(dst++) = l1; +#endif + --count; + } } +void alist_envmix_exp( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address) +{ + size_t n = (aux) ? 4 : 2; + + const int16_t* const in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t* const dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t* const dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t* const wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t* const wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + struct ramp_t ramps[2]; + int32_t exp_seq[2]; + int32_t exp_rates[2]; + + uint32_t ptr = 0; + int x, y; + short save_buffer[40]; + + memcpy((uint8_t *)save_buffer, (hle->dram + address), sizeof(save_buffer)); + if (init) { + ramps[0].value = (vol[0] << 16); + ramps[1].value = (vol[1] << 16); + ramps[0].target = (target[0] << 16); + ramps[1].target = (target[1] << 16); + exp_rates[0] = rate[0]; + exp_rates[1] = rate[1]; + exp_seq[0] = (vol[0] * rate[0]); + exp_seq[1] = (vol[1] * rate[1]); + } else { + wet = *(int16_t *)(save_buffer + 0); /* 0-1 */ + dry = *(int16_t *)(save_buffer + 2); /* 2-3 */ + ramps[0].target = *(int32_t *)(save_buffer + 4); /* 4-5 */ + ramps[1].target = *(int32_t *)(save_buffer + 6); /* 6-7 */ + exp_rates[0] = *(int32_t *)(save_buffer + 8); /* 8-9 (save_buffer is a 16bit pointer) */ + exp_rates[1] = *(int32_t *)(save_buffer + 10); /* 10-11 */ + exp_seq[0] = *(int32_t *)(save_buffer + 12); /* 12-13 */ + exp_seq[1] = *(int32_t *)(save_buffer + 14); /* 14-15 */ + ramps[0].value = *(int32_t *)(save_buffer + 16); /* 12-13 */ + ramps[1].value = *(int32_t *)(save_buffer + 18); /* 14-15 */ + } + + /* init which ensure ramp.step != 0 iff ramp.value == ramp.target */ + ramps[0].step = ramps[0].target - ramps[0].value; + ramps[1].step = ramps[1].target - ramps[1].value; + + for (y = 0; y < count; y += 16) { + + if (ramps[0].step != 0) + { + exp_seq[0] = ((int64_t)exp_seq[0]*(int64_t)exp_rates[0]) >> 16; + ramps[0].step = (exp_seq[0] - ramps[0].value) >> 3; + } + + if (ramps[1].step != 0) + { + exp_seq[1] = ((int64_t)exp_seq[1]*(int64_t)exp_rates[1]) >> 16; + ramps[1].step = (exp_seq[1] - ramps[1].value) >> 3; + } + + for (x = 0; x < 8; ++x) { + int16_t gains[4]; + int16_t* buffers[4]; + int16_t l_vol = ramp_step(&ramps[0]); + int16_t r_vol = ramp_step(&ramps[1]); + + buffers[0] = dl + (ptr^S); + buffers[1] = dr + (ptr^S); + buffers[2] = wl + (ptr^S); + buffers[3] = wr + (ptr^S); + + gains[0] = clamp_s16((l_vol * dry + 0x4000) >> 15); + gains[1] = clamp_s16((r_vol * dry + 0x4000) >> 15); + gains[2] = clamp_s16((l_vol * wet + 0x4000) >> 15); + gains[3] = clamp_s16((r_vol * wet + 0x4000) >> 15); + + alist_envmix_mix(n, buffers, gains, in[ptr^S]); + ++ptr; + } + } + + *(int16_t *)(save_buffer + 0) = wet; /* 0-1 */ + *(int16_t *)(save_buffer + 2) = dry; /* 2-3 */ + *(int32_t *)(save_buffer + 4) = (int32_t)ramps[0].target; /* 4-5 */ + *(int32_t *)(save_buffer + 6) = (int32_t)ramps[1].target; /* 6-7 */ + *(int32_t *)(save_buffer + 8) = exp_rates[0]; /* 8-9 (save_buffer is a 16bit pointer) */ + *(int32_t *)(save_buffer + 10) = exp_rates[1]; /* 10-11 */ + *(int32_t *)(save_buffer + 12) = exp_seq[0]; /* 12-13 */ + *(int32_t *)(save_buffer + 14) = exp_seq[1]; /* 14-15 */ + *(int32_t *)(save_buffer + 16) = (int32_t)ramps[0].value; /* 12-13 */ + *(int32_t *)(save_buffer + 18) = (int32_t)ramps[1].value; /* 14-15 */ + memcpy(hle->dram + address, (uint8_t *)save_buffer, sizeof(save_buffer)); +} + +void alist_envmix_ge( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address) +{ + unsigned k; + size_t n = (aux) ? 4 : 2; + + const int16_t* const in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t* const dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t* const dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t* const wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t* const wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + struct ramp_t ramps[2]; + short save_buffer[40]; + + memcpy((uint8_t *)save_buffer, (hle->dram + address), 80); + if (init) { + ramps[0].value = (vol[0] << 16); + ramps[1].value = (vol[1] << 16); + ramps[0].target = (target[0] << 16); + ramps[1].target = (target[1] << 16); + ramps[0].step = rate[0] / 8; + ramps[1].step = rate[1] / 8; + } else { + wet = *(int16_t *)(save_buffer + 0); /* 0-1 */ + dry = *(int16_t *)(save_buffer + 2); /* 2-3 */ + ramps[0].target = *(int32_t *)(save_buffer + 4); /* 4-5 */ + ramps[1].target = *(int32_t *)(save_buffer + 6); /* 6-7 */ + ramps[0].step = *(int32_t *)(save_buffer + 8); /* 8-9 (save_buffer is a 16bit pointer) */ + ramps[1].step = *(int32_t *)(save_buffer + 10); /* 10-11 */ + /* *(int32_t *)(save_buffer + 12);*/ /* 12-13 */ + /* *(int32_t *)(save_buffer + 14);*/ /* 14-15 */ + ramps[0].value = *(int32_t *)(save_buffer + 16); /* 12-13 */ + ramps[1].value = *(int32_t *)(save_buffer + 18); /* 14-15 */ + } + + count >>= 1; + for (k = 0; k < count; ++k) { + int16_t gains[4]; + int16_t* buffers[4]; + int16_t l_vol = ramp_step(&ramps[0]); + int16_t r_vol = ramp_step(&ramps[1]); + + buffers[0] = dl + (k^S); + buffers[1] = dr + (k^S); + buffers[2] = wl + (k^S); + buffers[3] = wr + (k^S); + + gains[0] = clamp_s16((l_vol * dry + 0x4000) >> 15); + gains[1] = clamp_s16((r_vol * dry + 0x4000) >> 15); + gains[2] = clamp_s16((l_vol * wet + 0x4000) >> 15); + gains[3] = clamp_s16((r_vol * wet + 0x4000) >> 15); + + alist_envmix_mix(n, buffers, gains, in[k^S]); + } + + *(int16_t *)(save_buffer + 0) = wet; /* 0-1 */ + *(int16_t *)(save_buffer + 2) = dry; /* 2-3 */ + *(int32_t *)(save_buffer + 4) = (int32_t)ramps[0].target; /* 4-5 */ + *(int32_t *)(save_buffer + 6) = (int32_t)ramps[1].target; /* 6-7 */ + *(int32_t *)(save_buffer + 8) = (int32_t)ramps[0].step; /* 8-9 (save_buffer is a 16bit pointer) */ + *(int32_t *)(save_buffer + 10) = (int32_t)ramps[1].step; /* 10-11 */ + /**(int32_t *)(save_buffer + 12);*/ /* 12-13 */ + /**(int32_t *)(save_buffer + 14);*/ /* 14-15 */ + *(int32_t *)(save_buffer + 16) = (int32_t)ramps[0].value; /* 12-13 */ + *(int32_t *)(save_buffer + 18) = (int32_t)ramps[1].value; /* 14-15 */ + memcpy(hle->dram + address, (uint8_t *)save_buffer, 80); +} + +void alist_envmix_lin( + struct hle_t* hle, + bool init, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address) +{ + size_t k; + struct ramp_t ramps[2]; + int16_t save_buffer[40]; + + const int16_t * const in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t* const dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t* const dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t* const wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t* const wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + memcpy((uint8_t *)save_buffer, hle->dram + address, 80); + if (init) { + ramps[0].step = rate[0] / 8; + ramps[0].value = (vol[0] << 16); + ramps[0].target = (target[0] << 16); + ramps[1].step = rate[1] / 8; + ramps[1].value = (vol[1] << 16); + ramps[1].target = (target[1] << 16); + } + else { + wet = *(int16_t *)(save_buffer + 0); /* 0-1 */ + dry = *(int16_t *)(save_buffer + 2); /* 2-3 */ + ramps[0].target = *(int16_t *)(save_buffer + 4) << 16; /* 4-5 */ + ramps[1].target = *(int16_t *)(save_buffer + 6) << 16; /* 6-7 */ + ramps[0].step = *(int32_t *)(save_buffer + 8); /* 8-9 (save_buffer is a 16bit pointer) */ + ramps[1].step = *(int32_t *)(save_buffer + 10); /* 10-11 */ + ramps[0].value = *(int32_t *)(save_buffer + 16); /* 16-17 */ + ramps[1].value = *(int32_t *)(save_buffer + 18); /* 16-17 */ + } + + count >>= 1; + for(k = 0; k < count; ++k) { + int16_t gains[4]; + int16_t* buffers[4]; + int16_t l_vol = ramp_step(&ramps[0]); + int16_t r_vol = ramp_step(&ramps[1]); + + buffers[0] = dl + (k^S); + buffers[1] = dr + (k^S); + buffers[2] = wl + (k^S); + buffers[3] = wr + (k^S); + + gains[0] = clamp_s16((l_vol * dry + 0x4000) >> 15); + gains[1] = clamp_s16((r_vol * dry + 0x4000) >> 15); + gains[2] = clamp_s16((l_vol * wet + 0x4000) >> 15); + gains[3] = clamp_s16((r_vol * wet + 0x4000) >> 15); + + alist_envmix_mix(4, buffers, gains, in[k^S]); + } + + *(int16_t *)(save_buffer + 0) = wet; /* 0-1 */ + *(int16_t *)(save_buffer + 2) = dry; /* 2-3 */ + *(int16_t *)(save_buffer + 4) = (int16_t)(ramps[0].target >> 16); /* 4-5 */ + *(int16_t *)(save_buffer + 6) = (int16_t)(ramps[1].target >> 16); /* 6-7 */ + *(int32_t *)(save_buffer + 8) = (int32_t)ramps[0].step; /* 8-9 (save_buffer is a 16bit pointer) */ + *(int32_t *)(save_buffer + 10) = (int32_t)ramps[1].step; /* 10-11 */ + *(int32_t *)(save_buffer + 16) = (int32_t)ramps[0].value; /* 16-17 */ + *(int32_t *)(save_buffer + 18) = (int32_t)ramps[1].value; /* 18-19 */ + memcpy(hle->dram + address, (uint8_t *)save_buffer, 80); +} + +void alist_envmix_nead( + struct hle_t* hle, + bool swap_wet_LR, + uint16_t dmem_dl, + uint16_t dmem_dr, + uint16_t dmem_wl, + uint16_t dmem_wr, + uint16_t dmemi, + unsigned count, + uint16_t *env_values, + uint16_t *env_steps, + const int16_t *xors) +{ + int16_t *in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t *dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t *dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t *wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t *wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + /* make sure count is a multiple of 8 */ + count = align(count, 8); + + if (swap_wet_LR) + swap(&wl, &wr); + + while (count != 0) { + size_t i; + for(i = 0; i < 8; ++i) { + int16_t l = (((int32_t)in[i^S] * (uint32_t)env_values[0]) >> 16) ^ xors[0]; + int16_t r = (((int32_t)in[i^S] * (uint32_t)env_values[1]) >> 16) ^ xors[1]; + int16_t l2 = (((int32_t)l * (uint32_t)env_values[2]) >> 16) ^ xors[2]; + int16_t r2 = (((int32_t)r * (uint32_t)env_values[2]) >> 16) ^ xors[3]; + + dl[i^S] = clamp_s16(dl[i^S] + l); + dr[i^S] = clamp_s16(dr[i^S] + r); + wl[i^S] = clamp_s16(wl[i^S] + l2); + wr[i^S] = clamp_s16(wr[i^S] + r2); + } + + env_values[0] += env_steps[0]; + env_values[1] += env_steps[1]; + env_values[2] += env_steps[2]; + + dl += 8; + dr += 8; + wl += 8; + wr += 8; + in += 8; + count -= 8; + } +} + + +void alist_mix(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count, int16_t gain) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + const int16_t *src = (int16_t*)(hle->alist_buffer + dmemi); + + count >>= 1; + + while(count != 0) { + sample_mix(dst, *src, gain); + + ++dst; + ++src; + --count; + } +} + +void alist_multQ44(struct hle_t* hle, uint16_t dmem, uint16_t count, int8_t gain) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmem); + + count >>= 1; + + while(count != 0) { + *dst = clamp_s16(*dst * gain >> 4); + + ++dst; + --count; + } +} + +void alist_add(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + const int16_t *src = (int16_t*)(hle->alist_buffer + dmemi); + + count >>= 1; + + while(count != 0) { + *dst = clamp_s16(*dst + *src); + + ++dst; + ++src; + --count; + } +} + +static void alist_resample_reset(struct hle_t* hle, uint16_t pos, uint32_t* pitch_accu) +{ + unsigned k; + + for(k = 0; k < 4; ++k) + *sample(hle, pos + k) = 0; + + *pitch_accu = 0; +} + +static void alist_resample_load(struct hle_t* hle, uint32_t address, uint16_t pos, uint32_t* pitch_accu) +{ + *sample(hle, pos + 0) = *dram_u16(hle, address + 0); + *sample(hle, pos + 1) = *dram_u16(hle, address + 2); + *sample(hle, pos + 2) = *dram_u16(hle, address + 4); + *sample(hle, pos + 3) = *dram_u16(hle, address + 6); + + *pitch_accu = *dram_u16(hle, address + 8); +} + +static void alist_resample_save(struct hle_t* hle, uint32_t address, uint16_t pos, uint32_t pitch_accu) +{ + *dram_u16(hle, address + 0) = *sample(hle, pos + 0); + *dram_u16(hle, address + 2) = *sample(hle, pos + 1); + *dram_u16(hle, address + 4) = *sample(hle, pos + 2); + *dram_u16(hle, address + 6) = *sample(hle, pos + 3); + + *dram_u16(hle, address + 8) = pitch_accu; +} + +void alist_resample( + struct hle_t* hle, + bool init, + bool flag2, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint32_t pitch, /* Q16.16 */ + uint32_t address) +{ + uint32_t pitch_accu; + + uint16_t ipos = dmemi >> 1; + uint16_t opos = dmemo >> 1; + count >>= 1; + ipos -= 4; + + if (flag2) + HleWarnMessage(hle->user_defined, "alist_resample: flag2 is not implemented"); + + if (init) + alist_resample_reset(hle, ipos, &pitch_accu); + else + alist_resample_load(hle, address, ipos, &pitch_accu); + + while (count != 0) { + const int16_t* lut = RESAMPLE_LUT + ((pitch_accu & 0xfc00) >> 8); + + *sample(hle, opos++) = clamp_s16( ( + (*sample(hle, ipos ) * lut[0]) + + (*sample(hle, ipos + 1) * lut[1]) + + (*sample(hle, ipos + 2) * lut[2]) + + (*sample(hle, ipos + 3) * lut[3]) ) >> 15); + + pitch_accu += pitch; + ipos += (pitch_accu >> 16); + pitch_accu &= 0xffff; + --count; + } + + alist_resample_save(hle, address, ipos, pitch_accu); +} + +void alist_resample_zoh( + struct hle_t* hle, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint32_t pitch, + uint32_t pitch_accu) +{ + uint16_t ipos = dmemi >> 1; + uint16_t opos = dmemo >> 1; + count >>= 1; + + while(count != 0) { + + *sample(hle, opos++) = *sample(hle, ipos); + + pitch_accu += pitch; + ipos += (pitch_accu >> 16); + pitch_accu &= 0xffff; + --count; + } +} + +typedef unsigned int (*adpcm_predict_frame_t)(struct hle_t* hle, + int16_t* dst, uint16_t dmemi, unsigned char scale); + +static unsigned int adpcm_predict_frame_4bits(struct hle_t* hle, + int16_t* dst, uint16_t dmemi, unsigned char scale) +{ + unsigned int i; + unsigned int rshift = (scale < 12) ? 12 - scale : 0; + + for(i = 0; i < 8; ++i) { + uint8_t byte = *alist_u8(hle, dmemi++); + + *(dst++) = adpcm_predict_sample(byte, 0xf0, 8, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x0f, 12, rshift); + } + + return 8; +} + +static unsigned int adpcm_predict_frame_2bits(struct hle_t* hle, + int16_t* dst, uint16_t dmemi, unsigned char scale) +{ + unsigned int i; + unsigned int rshift = (scale < 14) ? 14 - scale : 0; + + for(i = 0; i < 4; ++i) { + uint8_t byte = *alist_u8(hle, dmemi++); + + *(dst++) = adpcm_predict_sample(byte, 0xc0, 8, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x30, 10, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x0c, 12, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x03, 14, rshift); + } + + return 4; +} + +void alist_adpcm( + struct hle_t* hle, + bool init, + bool loop, + bool two_bit_per_sample, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + const int16_t* codebook, + uint32_t loop_address, + uint32_t last_frame_address) +{ + int16_t last_frame[16]; + size_t i; + + adpcm_predict_frame_t predict_frame = (two_bit_per_sample) + ? adpcm_predict_frame_2bits + : adpcm_predict_frame_4bits; + + assert((count & 0x1f) == 0); + + if (init) + memset(last_frame, 0, 16*sizeof(last_frame[0])); + else + dram_load_u16(hle, (uint16_t*)last_frame, (loop) ? loop_address : last_frame_address, 16); + + for(i = 0; i < 16; ++i, dmemo += 2) + *alist_s16(hle, dmemo) = last_frame[i]; + + while (count != 0) { + int16_t frame[16]; + uint8_t code = *alist_u8(hle, dmemi++); + unsigned char scale = (code & 0xf0) >> 4; + const int16_t* const cb_entry = codebook + ((code & 0xf) << 4); + + dmemi += predict_frame(hle, frame, dmemi, scale); + + adpcm_compute_residuals(last_frame , frame , cb_entry, last_frame + 14, 8); + adpcm_compute_residuals(last_frame + 8, frame + 8, cb_entry, last_frame + 6 , 8); + + for(i = 0; i < 16; ++i, dmemo += 2) + *alist_s16(hle, dmemo) = last_frame[i]; + + count -= 32; + } + + dram_store_u16(hle, (uint16_t*)last_frame, last_frame_address, 16); +} + + +void alist_filter( + struct hle_t* hle, + uint16_t dmem, + uint16_t count, + uint32_t address, + const uint32_t* lut_address) +{ + int x; + int16_t outbuff[0x3c0]; + int16_t *outp = outbuff; + + int16_t* const lutt6 = (int16_t*)(hle->dram + lut_address[0]); + int16_t* const lutt5 = (int16_t*)(hle->dram + lut_address[1]); + + int16_t* in1 = (int16_t*)(hle->dram + address); + int16_t* in2 = (int16_t*)(hle->alist_buffer + dmem); + + + for (x = 0; x < 8; ++x) { + int32_t v = (lutt5[x] + lutt6[x]) >> 1; + lutt5[x] = lutt6[x] = v; + } + + for (x = 0; x < count; x += 16) { + int32_t v[8]; + + v[1] = in1[0] * lutt6[6]; + v[1] += in1[3] * lutt6[7]; + v[1] += in1[2] * lutt6[4]; + v[1] += in1[5] * lutt6[5]; + v[1] += in1[4] * lutt6[2]; + v[1] += in1[7] * lutt6[3]; + v[1] += in1[6] * lutt6[0]; + v[1] += in2[1] * lutt6[1]; /* 1 */ + + v[0] = in1[3] * lutt6[6]; + v[0] += in1[2] * lutt6[7]; + v[0] += in1[5] * lutt6[4]; + v[0] += in1[4] * lutt6[5]; + v[0] += in1[7] * lutt6[2]; + v[0] += in1[6] * lutt6[3]; + v[0] += in2[1] * lutt6[0]; + v[0] += in2[0] * lutt6[1]; + + v[3] = in1[2] * lutt6[6]; + v[3] += in1[5] * lutt6[7]; + v[3] += in1[4] * lutt6[4]; + v[3] += in1[7] * lutt6[5]; + v[3] += in1[6] * lutt6[2]; + v[3] += in2[1] * lutt6[3]; + v[3] += in2[0] * lutt6[0]; + v[3] += in2[3] * lutt6[1]; + + v[2] = in1[5] * lutt6[6]; + v[2] += in1[4] * lutt6[7]; + v[2] += in1[7] * lutt6[4]; + v[2] += in1[6] * lutt6[5]; + v[2] += in2[1] * lutt6[2]; + v[2] += in2[0] * lutt6[3]; + v[2] += in2[3] * lutt6[0]; + v[2] += in2[2] * lutt6[1]; + + v[5] = in1[4] * lutt6[6]; + v[5] += in1[7] * lutt6[7]; + v[5] += in1[6] * lutt6[4]; + v[5] += in2[1] * lutt6[5]; + v[5] += in2[0] * lutt6[2]; + v[5] += in2[3] * lutt6[3]; + v[5] += in2[2] * lutt6[0]; + v[5] += in2[5] * lutt6[1]; + + v[4] = in1[7] * lutt6[6]; + v[4] += in1[6] * lutt6[7]; + v[4] += in2[1] * lutt6[4]; + v[4] += in2[0] * lutt6[5]; + v[4] += in2[3] * lutt6[2]; + v[4] += in2[2] * lutt6[3]; + v[4] += in2[5] * lutt6[0]; + v[4] += in2[4] * lutt6[1]; + + v[7] = in1[6] * lutt6[6]; + v[7] += in2[1] * lutt6[7]; + v[7] += in2[0] * lutt6[4]; + v[7] += in2[3] * lutt6[5]; + v[7] += in2[2] * lutt6[2]; + v[7] += in2[5] * lutt6[3]; + v[7] += in2[4] * lutt6[0]; + v[7] += in2[7] * lutt6[1]; + + v[6] = in2[1] * lutt6[6]; + v[6] += in2[0] * lutt6[7]; + v[6] += in2[3] * lutt6[4]; + v[6] += in2[2] * lutt6[5]; + v[6] += in2[5] * lutt6[2]; + v[6] += in2[4] * lutt6[3]; + v[6] += in2[7] * lutt6[0]; + v[6] += in2[6] * lutt6[1]; + + outp[1] = ((v[1] + 0x4000) >> 15); + outp[0] = ((v[0] + 0x4000) >> 15); + outp[3] = ((v[3] + 0x4000) >> 15); + outp[2] = ((v[2] + 0x4000) >> 15); + outp[5] = ((v[5] + 0x4000) >> 15); + outp[4] = ((v[4] + 0x4000) >> 15); + outp[7] = ((v[7] + 0x4000) >> 15); + outp[6] = ((v[6] + 0x4000) >> 15); + in1 = in2; + in2 += 8; + outp += 8; + } + + memcpy(hle->dram + address, in2 - 8, 16); + memcpy(hle->alist_buffer + dmem, outbuff, count); +} + +void alist_polef( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint16_t gain, + int16_t* table, + uint32_t address) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + + const int16_t* const h1 = table; + int16_t* const h2 = table + 8; + + unsigned i; + int16_t l1, l2; + int16_t h2_before[8]; + + count = align(count, 16); + + if (init) { + l1 = 0; + l2 = 0; + } + else { + l1 = *dram_u16(hle, address + 4); + l2 = *dram_u16(hle, address + 6); + } + + for(i = 0; i < 8; ++i) { + h2_before[i] = h2[i]; + h2[i] = (((int32_t)h2[i] * gain) >> 14); + } + + do + { + int16_t frame[8]; + + for(i = 0; i < 8; ++i, dmemi += 2) + frame[i] = *alist_s16(hle, dmemi); + + for(i = 0; i < 8; ++i) { + int32_t accu = frame[i] * gain; + accu += h1[i]*l1 + h2_before[i]*l2 + rdot(i, h2, frame); + dst[i^S] = clamp_s16(accu >> 14); + } + + l1 = dst[6^S]; + l2 = dst[7^S]; + + dst += 8; + count -= 16; + } while (count != 0); + + dram_store_u32(hle, (uint32_t*)(dst - 4), address, 2); +} + +void alist_iirf( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + int16_t* table, + uint32_t address) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + int32_t i, prev; + int16_t frame[8]; + int16_t ibuf[4]; + uint16_t index = 7; + + + count = align(count, 16); + + if(init) + { + for(i = 0; i < 8; ++i) + frame[i] = 0; + ibuf[1] = 0; + ibuf[2] = 0; + } + else + { + frame[6] = *dram_u16(hle, address + 4); + frame[7] = *dram_u16(hle, address + 6); + ibuf[1] = (int16_t)*dram_u16(hle, address + 8); + ibuf[2] = (int16_t)*dram_u16(hle, address + 10); + } + + prev = vmulf(table[9], frame[6]) * 2; + do + { + for(i = 0; i < 8; ++i) + { + int32_t accu; + ibuf[index&3] = *alist_s16(hle, dmemi); + + accu = prev + vmulf(table[0], ibuf[index&3]) + vmulf(table[1], ibuf[(index-1)&3]) + vmulf(table[0], ibuf[(index-2)&3]); + accu += vmulf(table[8], frame[index]) * 2; + prev = vmulf(table[9], frame[index]) * 2; + dst[i^S] = frame[i] = accu; + + index=(index+1)&7; + dmemi += 2; + } + dst += 8; + count -= 0x10; + } while (count > 0); + + dram_store_u16(hle, (uint16_t*)&frame[6], address + 4, 4); + dram_store_u16(hle, (uint16_t*)&ibuf[(index-2)&3], address+8, 2); + dram_store_u16(hle, (uint16_t*)&ibuf[(index-1)&3], address+10, 2); +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist.h b/libmupen64plus/mupen64plus-rsp-hle/src/alist.h index 43f9f04bcb..398cdb8179 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/alist.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - alist.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,15 +19,152 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef ALIST_H -#define ALIST_H +#ifndef ALIST_INTERNAL_H +#define ALIST_INTERNAL_H -void alist_process_ABI1(); -void alist_process_ABI2(); -void alist_process_ABI3(); +#include +#include +#include -// FIXME: to remove when isZeldaABI/isMKABI workaround is gone -void init_ucode2(); +struct hle_t; + +typedef void (*acmd_callback_t)(struct hle_t* hle, uint32_t w1, uint32_t w2); + +void alist_process(struct hle_t* hle, const acmd_callback_t abi[], unsigned int abi_size); +uint32_t alist_get_address(struct hle_t* hle, uint32_t so, const uint32_t *segments, size_t n); +void alist_set_address(struct hle_t* hle, uint32_t so, uint32_t *segments, size_t n); +void alist_clear(struct hle_t* hle, uint16_t dmem, uint16_t count); +void alist_load(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count); +void alist_save(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count); +void alist_move(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count); +void alist_copy_every_other_sample(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count); +void alist_repeat64(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint8_t count); +void alist_copy_blocks(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t block_size, uint8_t count); +void alist_interleave(struct hle_t* hle, uint16_t dmemo, uint16_t left, uint16_t right, uint16_t count); + +void alist_envmix_exp( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address); + +void alist_envmix_ge( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address); + +void alist_envmix_lin( + struct hle_t* hle, + bool init, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address); + +void alist_envmix_nead( + struct hle_t* hle, + bool swap_wet_LR, + uint16_t dmem_dl, + uint16_t dmem_dr, + uint16_t dmem_wl, + uint16_t dmem_wr, + uint16_t dmemi, + unsigned count, + uint16_t *env_values, + uint16_t *env_steps, + const int16_t *xors); + +void alist_mix(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count, int16_t gain); +void alist_multQ44(struct hle_t* hle, uint16_t dmem, uint16_t count, int8_t gain); +void alist_add(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count); + +void alist_adpcm( + struct hle_t* hle, + bool init, + bool loop, + bool two_bit_per_sample, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + const int16_t* codebook, + uint32_t loop_address, + uint32_t last_frame_address); + +void alist_resample( + struct hle_t* hle, + bool init, + bool flag2, + uint16_t dmemo, uint16_t dmemi, uint16_t count, + uint32_t pitch, uint32_t address); + +void alist_resample_zoh( + struct hle_t* hle, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint32_t pitch, + uint32_t pitch_accu); + +void alist_filter( + struct hle_t* hle, + uint16_t dmem, + uint16_t count, + uint32_t address, + const uint32_t* lut_address); + +void alist_polef( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint16_t gain, + int16_t* table, + uint32_t address); + +void alist_iirf( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + int16_t* table, + uint32_t address); + +/* + * Audio flags + */ + +#define A_INIT 0x01 +#define A_CONTINUE 0x00 +#define A_LOOP 0x02 +#define A_OUT 0x02 +#define A_LEFT 0x02 +#define A_RIGHT 0x00 +#define A_VOL 0x04 +#define A_RATE 0x00 +#define A_AUX 0x08 +#define A_NOAUX 0x00 +#define A_MAIN 0x00 +#define A_MIX 0x10 #endif - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_audio.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist_audio.c new file mode 100644 index 0000000000..0d2dceacfb --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist_audio.c @@ -0,0 +1,310 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - alist_audio.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "alist.h" +#include "common.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +enum { DMEM_BASE = 0x5c0 }; + +/* helper functions */ +static uint32_t get_address(struct hle_t* hle, uint32_t so) +{ + return alist_get_address(hle, so, hle->alist_audio.segments, N_SEGMENTS); +} + +static void set_address(struct hle_t* hle, uint32_t so) +{ + alist_set_address(hle, so, hle->alist_audio.segments, N_SEGMENTS); +} + +static void clear_segments(struct hle_t* hle) +{ + memset(hle->alist_audio.segments, 0, N_SEGMENTS*sizeof(hle->alist_audio.segments[0])); +} + +/* audio commands definition */ +static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmem = w1 + DMEM_BASE; + uint16_t count = w2 & 0xfff; + + if (count == 0) + return; + + alist_clear(hle, dmem, align(count, 16)); +} + +static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = get_address(hle, w2); + + alist_envmix_exp( + hle, + flags & A_INIT, + flags & A_AUX, + hle->alist_audio.out, hle->alist_audio.dry_right, + hle->alist_audio.wet_left, hle->alist_audio.wet_right, + hle->alist_audio.in, hle->alist_audio.count, + hle->alist_audio.dry, hle->alist_audio.wet, + hle->alist_audio.vol, + hle->alist_audio.target, + hle->alist_audio.rate, + address); +} + +static void ENVMIXER_GE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = get_address(hle, w2); + + alist_envmix_ge( + hle, + flags & A_INIT, + flags & A_AUX, + hle->alist_audio.out, hle->alist_audio.dry_right, + hle->alist_audio.wet_left, hle->alist_audio.wet_right, + hle->alist_audio.in, hle->alist_audio.count, + hle->alist_audio.dry, hle->alist_audio.wet, + hle->alist_audio.vol, + hle->alist_audio.target, + hle->alist_audio.rate, + address); +} + +static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t pitch = w1; + uint32_t address = get_address(hle, w2); + + alist_resample( + hle, + flags & A_INIT, + flags & 0x2, + hle->alist_audio.out, + hle->alist_audio.in, + align(hle->alist_audio.count, 16), + pitch << 1, + address); +} + +static void SETVOL(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + + if (flags & A_AUX) { + hle->alist_audio.dry = w1; + hle->alist_audio.wet = w2; + } + else { + unsigned lr = (flags & A_LEFT) ? 0 : 1; + + if (flags & A_VOL) + hle->alist_audio.vol[lr] = w1; + else { + hle->alist_audio.target[lr] = w1; + hle->alist_audio.rate[lr] = w2; + } + } +} + +static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_audio.loop = get_address(hle, w2); +} + +static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = get_address(hle, w2); + + alist_adpcm( + hle, + flags & A_INIT, + flags & A_LOOP, + false, /* unsupported in this ucode */ + hle->alist_audio.out, + hle->alist_audio.in, + align(hle->alist_audio.count, 32), + hle->alist_audio.table, + hle->alist_audio.loop, + address); +} + +static void LOADBUFF(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint32_t address = get_address(hle, w2); + + if (hle->alist_audio.count == 0) + return; + + alist_load(hle, hle->alist_audio.in, address, hle->alist_audio.count); +} + +static void SAVEBUFF(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint32_t address = get_address(hle, w2); + + if (hle->alist_audio.count == 0) + return; + + alist_save(hle, hle->alist_audio.out, address, hle->alist_audio.count); +} + +static void SETBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + + if (flags & A_AUX) { + hle->alist_audio.dry_right = w1 + DMEM_BASE; + hle->alist_audio.wet_left = (w2 >> 16) + DMEM_BASE; + hle->alist_audio.wet_right = w2 + DMEM_BASE; + } else { + hle->alist_audio.in = w1 + DMEM_BASE; + hle->alist_audio.out = (w2 >> 16) + DMEM_BASE; + hle->alist_audio.count = w2; + } +} + +static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmemi = w1 + DMEM_BASE; + uint16_t dmemo = (w2 >> 16) + DMEM_BASE; + uint16_t count = w2; + + if (count == 0) + return; + + alist_move(hle, dmemo, dmemi, align(count, 16)); +} + +static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint32_t address = get_address(hle, w2); + + dram_load_u16(hle, (uint16_t*)hle->alist_audio.table, address, align(count, 8) >> 1); +} + +static void INTERLEAVE(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint16_t left = (w2 >> 16) + DMEM_BASE; + uint16_t right = w2 + DMEM_BASE; + + if (hle->alist_audio.count == 0) + return; + + alist_interleave(hle, hle->alist_audio.out, left, right, align(hle->alist_audio.count, 16)); +} + +static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t gain = w1; + uint16_t dmemi = (w2 >> 16) + DMEM_BASE; + uint16_t dmemo = w2 + DMEM_BASE; + + if (hle->alist_audio.count == 0) + return; + + alist_mix(hle, dmemo, dmemi, align(hle->alist_audio.count, 32), gain); +} + +static void SEGMENT(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + set_address(hle, w2); +} + +static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t gain = w1; + uint32_t address = get_address(hle, w2); + + if (hle->alist_audio.count == 0) + return; + + alist_polef( + hle, + flags & A_INIT, + hle->alist_audio.out, + hle->alist_audio.in, + align(hle->alist_audio.count, 16), + gain, + hle->alist_audio.table, + address); +} + +/* global functions */ +void alist_process_audio(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM , CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT, + SETBUFF, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, POLEF, SETLOOP + }; + + clear_segments(hle); + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_audio_ge(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM , CLEARBUFF, ENVMIXER_GE, + LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT, + SETBUFF, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, POLEF, SETLOOP + }; + + clear_segments(hle); + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_audio_bc(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM , CLEARBUFF, ENVMIXER_GE, + LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT, + SETBUFF, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, POLEF, SETLOOP + }; + + clear_segments(hle); + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_naudio.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist_naudio.c new file mode 100644 index 0000000000..bb53dd77b8 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist_naudio.c @@ -0,0 +1,333 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - alist_naudio.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "alist.h" +#include "common.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +enum { NAUDIO_COUNT = 0x170 }; /* ie 184 samples */ +enum { + NAUDIO_MAIN = 0x4f0, + NAUDIO_MAIN2 = 0x660, + NAUDIO_DRY_LEFT = 0x9d0, + NAUDIO_DRY_RIGHT = 0xb40, + NAUDIO_WET_LEFT = 0xcb0, + NAUDIO_WET_RIGHT = 0xe20 +}; + + +/* audio commands definition */ +static void UNKNOWN(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t acmd = (w1 >> 24); + + HleWarnMessage(hle->user_defined, + "Unknown audio command %d: %08x %08x", + acmd, w1, w2); +} + + +static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void NAUDIO_0000(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + /* ??? */ + UNKNOWN(hle, w1, w2); +} + +static void NAUDIO_02B0(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + /* emulate code at 0x12b0 (inside SETVOL), because PC always execute in IMEM */ + hle->alist_naudio.rate[1] &= ~0xffff; + hle->alist_naudio.rate[1] |= (w2 & 0xffff); +} + +static void NAUDIO_14(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t gain = w1; + uint8_t select_main = (w2 >> 24); + uint32_t address = (w2 & 0xffffff); + + uint16_t dmem = (select_main == 0) ? NAUDIO_MAIN : NAUDIO_MAIN2; + + if (hle->alist_naudio.table[0] == 0 && hle->alist_naudio.table[1] == 0) { + alist_polef( + hle, + flags & A_INIT, + dmem, + dmem, + NAUDIO_COUNT, + gain, + hle->alist_naudio.table, + address); + } + else + { + alist_iirf( + hle, + flags & A_INIT, + dmem, + dmem, + NAUDIO_COUNT, + hle->alist_naudio.table, + address); + } + +} + +static void SETVOL(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + + if (flags & A_VOL) { + if (flags & A_LEFT) { + hle->alist_naudio.vol[0] = w1; + hle->alist_naudio.dry = (w2 >> 16); + hle->alist_naudio.wet = w2; + } + else { /* A_RIGHT */ + hle->alist_naudio.target[1] = w1; + hle->alist_naudio.rate[1] = w2; + } + } + else { /* A_RATE */ + hle->alist_naudio.target[0] = w1; + hle->alist_naudio.rate[0] = w2; + } +} + +static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = (w2 & 0xffffff); + + hle->alist_naudio.vol[1] = w1; + + alist_envmix_lin( + hle, + flags & A_INIT, + NAUDIO_DRY_LEFT, + NAUDIO_DRY_RIGHT, + NAUDIO_WET_LEFT, + NAUDIO_WET_RIGHT, + NAUDIO_MAIN, + NAUDIO_COUNT, + hle->alist_naudio.dry, + hle->alist_naudio.wet, + hle->alist_naudio.vol, + hle->alist_naudio.target, + hle->alist_naudio.rate, + address); +} + +static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmem = w1 + NAUDIO_MAIN; + uint16_t count = w2 & 0xfff; + + alist_clear(hle, dmem, count); +} + +static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t gain = w1; + uint16_t dmemi = (w2 >> 16) + NAUDIO_MAIN; + uint16_t dmemo = w2 + NAUDIO_MAIN; + + alist_mix(hle, dmemo, dmemi, NAUDIO_COUNT, gain); +} + +static void LOADBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN; + uint32_t address = (w2 & 0xffffff); + + alist_load(hle, dmem, address, count); +} + +static void SAVEBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN; + uint32_t address = (w2 & 0xffffff); + + alist_save(hle, dmem, address, count); +} + +static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint32_t address = (w2 & 0xffffff); + + dram_load_u16(hle, (uint16_t*)hle->alist_naudio.table, address, count >> 1); +} + +static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmemi = w1 + NAUDIO_MAIN; + uint16_t dmemo = (w2 >> 16) + NAUDIO_MAIN; + uint16_t count = w2; + + alist_move(hle, dmemo, dmemi, (count + 3) & ~3); +} + +static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_naudio.loop = (w2 & 0xffffff); +} + +static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint32_t address = (w1 & 0xffffff); + uint8_t flags = (w2 >> 28); + uint16_t count = (w2 >> 16) & 0xfff; + uint16_t dmemi = ((w2 >> 12) & 0xf) + NAUDIO_MAIN; + uint16_t dmemo = (w2 & 0xfff) + NAUDIO_MAIN; + + alist_adpcm( + hle, + flags & A_INIT, + flags & A_LOOP, + false, /* unsuported by this ucode */ + dmemo, + dmemi, + (count + 0x1f) & ~0x1f, + hle->alist_naudio.table, + hle->alist_naudio.loop, + address); +} + +static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint32_t address = (w1 & 0xffffff); + uint8_t flags = (w2 >> 30); + uint16_t pitch = (w2 >> 14); + uint16_t dmemi = ((w2 >> 2) & 0xfff) + NAUDIO_MAIN; + uint16_t dmemo = (w2 & 0x3) ? NAUDIO_MAIN2 : NAUDIO_MAIN; + + alist_resample( + hle, + flags & A_INIT, + false, /* TODO: check which ABI supports it */ + dmemo, + dmemi, + NAUDIO_COUNT, + pitch << 1, + address); +} + +static void INTERLEAVE(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ + alist_interleave(hle, NAUDIO_MAIN, NAUDIO_DRY_LEFT, NAUDIO_DRY_RIGHT, NAUDIO_COUNT); +} + +static void MP3ADDY(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void MP3(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + unsigned index = (w1 & 0x1e); + uint32_t address = (w2 & 0xffffff); + + mp3_task(hle, index, address); +} + +/* global functions */ +void alist_process_naudio(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, NAUDIO_0000, + NAUDIO_0000, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_bk(struct hle_t* hle) +{ + /* TODO: see what differs from alist_process_naudio */ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, NAUDIO_0000, + NAUDIO_0000, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_dk(struct hle_t* hle) +{ + /* TODO: see what differs from alist_process_naudio */ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, MIXER, + MIXER, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_mp3(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + UNKNOWN, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, MP3, + MP3ADDY, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_14, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_cbfd(struct hle_t* hle) +{ + /* TODO: see what differs from alist_process_naudio_mp3 */ + static const acmd_callback_t ABI[0x10] = { + UNKNOWN, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, MP3, + MP3ADDY, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_14, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_nead.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist_nead.c new file mode 100644 index 0000000000..11dcb80595 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist_nead.c @@ -0,0 +1,556 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - alist_nead.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "alist.h" +#include "common.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +/* remove windows define to 0x06 */ +#ifdef DUPLICATE +#undef DUPLICATE +#endif + +/* audio commands definition */ +static void UNKNOWN(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t acmd = (w1 >> 24); + + HleWarnMessage(hle->user_defined, + "Unknown audio command %d: %08x %08x", + acmd, w1, w2); +} + + +static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint32_t address = (w2 & 0xffffff); + + dram_load_u16(hle, (uint16_t*)hle->alist_nead.table, address, count >> 1); +} + +static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_nead.loop = w2 & 0xffffff; +} + +static void SETBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + hle->alist_nead.in = w1; + hle->alist_nead.out = (w2 >> 16); + hle->alist_nead.count = w2; +} + +static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = (w2 & 0xffffff); + + alist_adpcm( + hle, + flags & 0x1, + flags & 0x2, + flags & 0x4, + hle->alist_nead.out, + hle->alist_nead.in, + (hle->alist_nead.count + 0x1f) & ~0x1f, + hle->alist_nead.table, + hle->alist_nead.loop, + address); +} + +static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmem = w1; + uint16_t count = w2 & 0xfff; + + if (count == 0) + return; + + alist_clear(hle, dmem, count); +} + +static void LOADBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff); + uint32_t address = (w2 & 0xffffff); + + alist_load(hle, dmem, address, count); +} + +static void SAVEBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff); + uint32_t address = (w2 & 0xffffff); + + alist_save(hle, dmem, address, count); +} + +static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xff0; + int16_t gain = w1; + uint16_t dmemi = (w2 >> 16); + uint16_t dmemo = w2; + + alist_mix(hle, dmemo, dmemi, count, gain); +} + + +static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t pitch = w1; + uint32_t address = (w2 & 0xffffff); + + alist_resample( + hle, + flags & 0x1, + false, /* TODO: check which ABI supports it */ + hle->alist_nead.out, + hle->alist_nead.in, + (hle->alist_nead.count + 0xf) & ~0xf, + pitch << 1, + address); +} + +static void RESAMPLE_ZOH(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t pitch = w1; + uint16_t pitch_accu = w2; + + alist_resample_zoh( + hle, + hle->alist_nead.out, + hle->alist_nead.in, + hle->alist_nead.count, + pitch << 1, + pitch_accu); +} + +static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmemi = w1; + uint16_t dmemo = (w2 >> 16); + uint16_t count = w2; + + if (count == 0) + return; + + alist_move(hle, dmemo, dmemi, (count + 3) & ~3); +} + +static void ENVSETUP1_MK(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00; + hle->alist_nead.env_steps[2] = 0; + hle->alist_nead.env_steps[0] = (w2 >> 16); + hle->alist_nead.env_steps[1] = w2; +} + +static void ENVSETUP1(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00; + hle->alist_nead.env_steps[2] = w1; + hle->alist_nead.env_steps[0] = (w2 >> 16); + hle->alist_nead.env_steps[1] = w2; +} + +static void ENVSETUP2(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_nead.env_values[0] = (w2 >> 16); + hle->alist_nead.env_values[1] = w2; +} + +static void ENVMIXER_MK(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t xors[4]; + + uint16_t dmemi = (w1 >> 12) & 0xff0; + uint8_t count = (w1 >> 8) & 0xff; + uint16_t dmem_dl = (w2 >> 20) & 0xff0; + uint16_t dmem_dr = (w2 >> 12) & 0xff0; + uint16_t dmem_wl = (w2 >> 4) & 0xff0; + uint16_t dmem_wr = (w2 << 4) & 0xff0; + + xors[2] = 0; /* unsupported by this ucode */ + xors[3] = 0; /* unsupported by this ucode */ + xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1); + xors[1] = 0 - (int16_t)((w1 & 0x1) ); + + alist_envmix_nead( + hle, + false, /* unsupported by this ucode */ + dmem_dl, dmem_dr, + dmem_wl, dmem_wr, + dmemi, count, + hle->alist_nead.env_values, + hle->alist_nead.env_steps, + xors); +} + +static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t xors[4]; + + uint16_t dmemi = (w1 >> 12) & 0xff0; + uint8_t count = (w1 >> 8) & 0xff; + bool swap_wet_LR = (w1 >> 4) & 0x1; + uint16_t dmem_dl = (w2 >> 20) & 0xff0; + uint16_t dmem_dr = (w2 >> 12) & 0xff0; + uint16_t dmem_wl = (w2 >> 4) & 0xff0; + uint16_t dmem_wr = (w2 << 4) & 0xff0; + + xors[2] = 0 - (int16_t)((w1 & 0x8) >> 1); + xors[3] = 0 - (int16_t)((w1 & 0x4) >> 1); + xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1); + xors[1] = 0 - (int16_t)((w1 & 0x1) ); + + alist_envmix_nead( + hle, + swap_wet_LR, + dmem_dl, dmem_dr, + dmem_wl, dmem_wr, + dmemi, count, + hle->alist_nead.env_values, + hle->alist_nead.env_steps, + xors); +} + +static void DUPLICATE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t count = (w1 >> 16); + uint16_t dmemi = w1; + uint16_t dmemo = (w2 >> 16); + + alist_repeat64(hle, dmemo, dmemi, count); +} + +static void INTERL(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint16_t dmemi = (w2 >> 16); + uint16_t dmemo = w2; + + alist_copy_every_other_sample(hle, dmemo, dmemi, count); +} + +static void INTERLEAVE_MK(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint16_t left = (w2 >> 16); + uint16_t right = w2; + + if (hle->alist_nead.count == 0) + return; + + alist_interleave(hle, hle->alist_nead.out, left, right, hle->alist_nead.count); +} + +static void INTERLEAVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = ((w1 >> 12) & 0xff0); + uint16_t dmemo = w1; + uint16_t left = (w2 >> 16); + uint16_t right = w2; + + alist_interleave(hle, dmemo, left, right, count); +} + +static void ADDMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xff0; + uint16_t dmemi = (w2 >> 16); + uint16_t dmemo = w2; + + alist_add(hle, dmemo, dmemi, count); +} + +static void HILOGAIN(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int8_t gain = (w1 >> 16); /* Q4.4 signed */ + uint16_t count = w1 & 0xfff; + uint16_t dmem = (w2 >> 16); + + alist_multQ44(hle, dmem, count, gain); +} + +static void FILTER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = (w2 & 0xffffff); + + if (flags > 1) { + hle->alist_nead.filter_count = w1; + hle->alist_nead.filter_lut_address[0] = address; /* t6 */ + } + else { + uint16_t dmem = w1; + + hle->alist_nead.filter_lut_address[1] = address + 0x10; /* t5 */ + alist_filter(hle, dmem, hle->alist_nead.filter_count, address, hle->alist_nead.filter_lut_address); + } +} + +static void SEGMENT(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void NEAD_16(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t count = (w1 >> 16); + uint16_t dmemi = w1; + uint16_t dmemo = (w2 >> 16); + uint16_t block_size = w2; + + alist_copy_blocks(hle, dmemo, dmemi, block_size, count); +} + +static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t gain = w1; + uint32_t address = (w2 & 0xffffff); + + if (hle->alist_nead.count == 0) + return; + + alist_polef( + hle, + flags & A_INIT, + hle->alist_nead.out, + hle->alist_nead.in, + hle->alist_nead.count, + gain, + hle->alist_nead.table, + address); +} + + +void alist_process_nead_mk(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + SPNOOP, RESAMPLE, SPNOOP, SEGMENT, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE_MK, POLEF, SETLOOP, + NEAD_16, INTERL, ENVSETUP1_MK, ENVMIXER_MK, + LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_sf(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE_MK, POLEF, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP, + HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_sfj(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE_MK, POLEF, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN, + HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_fz(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + UNKNOWN, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, SPNOOP, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, SPNOOP, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN, + SPNOOP, UNKNOWN, DUPLICATE, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_wrjb(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, SPNOOP, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN, + HILOGAIN, UNKNOWN, DUPLICATE, FILTER, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_ys(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_1080(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_oot(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_mm(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_mmb(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_ac(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_mats(struct hle_t* hle) +{ + /* FIXME: implement proper ucode + * Forward the task if possible, + * otherwise better to have no sound than garbage sound + */ + if (HleForwardTask(hle->user_defined) != 0) { + rsp_break(hle, SP_STATUS_TASKDONE); + } +} + +void alist_process_nead_efz(struct hle_t* hle) +{ + /* FIXME: implement proper ucode + * Forward the task if possible, + * otherwise use FZero ucode which should be very similar + */ + if (HleForwardTask(hle->user_defined) != 0) { + alist_process_nead_fz(hle); + } +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.h b/libmupen64plus/mupen64plus-rsp-hle/src/arithmetics.h similarity index 71% rename from libmupen64plus/mupen64plus-rsp-hle/src/jpeg.h rename to libmupen64plus/mupen64plus-rsp-hle/src/arithmetics.h index b7deaf6cc4..2250b248fa 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/arithmetics.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - jpeg.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64plus-rsp-hle - arithmetics.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,12 +19,25 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef JPEG_H -#define JPEG_H +#ifndef ARITHMETICS_H +#define ARITHMETICS_H -void jpeg_decode_PS0(); -void jpeg_decode_PS(); -void jpeg_decode_OB(); +#include + +#include "common.h" + +static inline int16_t clamp_s16(int_fast32_t x) +{ + x = (x < INT16_MIN) ? INT16_MIN: x; + x = (x > INT16_MAX) ? INT16_MAX: x; + + return x; +} + +static inline int32_t vmulf(int16_t x, int16_t y) +{ + return (((int32_t)(x))*((int32_t)(y))+0x4000)>>15; +} #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/audio.c b/libmupen64plus/mupen64plus-rsp-hle/src/audio.c new file mode 100644 index 0000000000..fac2a101aa --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/audio.c @@ -0,0 +1,128 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - audio.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "arithmetics.h" + +const int16_t RESAMPLE_LUT[64 * 4] = { + (int16_t)0x0c39, (int16_t)0x66ad, (int16_t)0x0d46, (int16_t)0xffdf, + (int16_t)0x0b39, (int16_t)0x6696, (int16_t)0x0e5f, (int16_t)0xffd8, + (int16_t)0x0a44, (int16_t)0x6669, (int16_t)0x0f83, (int16_t)0xffd0, + (int16_t)0x095a, (int16_t)0x6626, (int16_t)0x10b4, (int16_t)0xffc8, + (int16_t)0x087d, (int16_t)0x65cd, (int16_t)0x11f0, (int16_t)0xffbf, + (int16_t)0x07ab, (int16_t)0x655e, (int16_t)0x1338, (int16_t)0xffb6, + (int16_t)0x06e4, (int16_t)0x64d9, (int16_t)0x148c, (int16_t)0xffac, + (int16_t)0x0628, (int16_t)0x643f, (int16_t)0x15eb, (int16_t)0xffa1, + (int16_t)0x0577, (int16_t)0x638f, (int16_t)0x1756, (int16_t)0xff96, + (int16_t)0x04d1, (int16_t)0x62cb, (int16_t)0x18cb, (int16_t)0xff8a, + (int16_t)0x0435, (int16_t)0x61f3, (int16_t)0x1a4c, (int16_t)0xff7e, + (int16_t)0x03a4, (int16_t)0x6106, (int16_t)0x1bd7, (int16_t)0xff71, + (int16_t)0x031c, (int16_t)0x6007, (int16_t)0x1d6c, (int16_t)0xff64, + (int16_t)0x029f, (int16_t)0x5ef5, (int16_t)0x1f0b, (int16_t)0xff56, + (int16_t)0x022a, (int16_t)0x5dd0, (int16_t)0x20b3, (int16_t)0xff48, + (int16_t)0x01be, (int16_t)0x5c9a, (int16_t)0x2264, (int16_t)0xff3a, + (int16_t)0x015b, (int16_t)0x5b53, (int16_t)0x241e, (int16_t)0xff2c, + (int16_t)0x0101, (int16_t)0x59fc, (int16_t)0x25e0, (int16_t)0xff1e, + (int16_t)0x00ae, (int16_t)0x5896, (int16_t)0x27a9, (int16_t)0xff10, + (int16_t)0x0063, (int16_t)0x5720, (int16_t)0x297a, (int16_t)0xff02, + (int16_t)0x001f, (int16_t)0x559d, (int16_t)0x2b50, (int16_t)0xfef4, + (int16_t)0xffe2, (int16_t)0x540d, (int16_t)0x2d2c, (int16_t)0xfee8, + (int16_t)0xffac, (int16_t)0x5270, (int16_t)0x2f0d, (int16_t)0xfedb, + (int16_t)0xff7c, (int16_t)0x50c7, (int16_t)0x30f3, (int16_t)0xfed0, + (int16_t)0xff53, (int16_t)0x4f14, (int16_t)0x32dc, (int16_t)0xfec6, + (int16_t)0xff2e, (int16_t)0x4d57, (int16_t)0x34c8, (int16_t)0xfebd, + (int16_t)0xff0f, (int16_t)0x4b91, (int16_t)0x36b6, (int16_t)0xfeb6, + (int16_t)0xfef5, (int16_t)0x49c2, (int16_t)0x38a5, (int16_t)0xfeb0, + (int16_t)0xfedf, (int16_t)0x47ed, (int16_t)0x3a95, (int16_t)0xfeac, + (int16_t)0xfece, (int16_t)0x4611, (int16_t)0x3c85, (int16_t)0xfeab, + (int16_t)0xfec0, (int16_t)0x4430, (int16_t)0x3e74, (int16_t)0xfeac, + (int16_t)0xfeb6, (int16_t)0x424a, (int16_t)0x4060, (int16_t)0xfeaf, + (int16_t)0xfeaf, (int16_t)0x4060, (int16_t)0x424a, (int16_t)0xfeb6, + (int16_t)0xfeac, (int16_t)0x3e74, (int16_t)0x4430, (int16_t)0xfec0, + (int16_t)0xfeab, (int16_t)0x3c85, (int16_t)0x4611, (int16_t)0xfece, + (int16_t)0xfeac, (int16_t)0x3a95, (int16_t)0x47ed, (int16_t)0xfedf, + (int16_t)0xfeb0, (int16_t)0x38a5, (int16_t)0x49c2, (int16_t)0xfef5, + (int16_t)0xfeb6, (int16_t)0x36b6, (int16_t)0x4b91, (int16_t)0xff0f, + (int16_t)0xfebd, (int16_t)0x34c8, (int16_t)0x4d57, (int16_t)0xff2e, + (int16_t)0xfec6, (int16_t)0x32dc, (int16_t)0x4f14, (int16_t)0xff53, + (int16_t)0xfed0, (int16_t)0x30f3, (int16_t)0x50c7, (int16_t)0xff7c, + (int16_t)0xfedb, (int16_t)0x2f0d, (int16_t)0x5270, (int16_t)0xffac, + (int16_t)0xfee8, (int16_t)0x2d2c, (int16_t)0x540d, (int16_t)0xffe2, + (int16_t)0xfef4, (int16_t)0x2b50, (int16_t)0x559d, (int16_t)0x001f, + (int16_t)0xff02, (int16_t)0x297a, (int16_t)0x5720, (int16_t)0x0063, + (int16_t)0xff10, (int16_t)0x27a9, (int16_t)0x5896, (int16_t)0x00ae, + (int16_t)0xff1e, (int16_t)0x25e0, (int16_t)0x59fc, (int16_t)0x0101, + (int16_t)0xff2c, (int16_t)0x241e, (int16_t)0x5b53, (int16_t)0x015b, + (int16_t)0xff3a, (int16_t)0x2264, (int16_t)0x5c9a, (int16_t)0x01be, + (int16_t)0xff48, (int16_t)0x20b3, (int16_t)0x5dd0, (int16_t)0x022a, + (int16_t)0xff56, (int16_t)0x1f0b, (int16_t)0x5ef5, (int16_t)0x029f, + (int16_t)0xff64, (int16_t)0x1d6c, (int16_t)0x6007, (int16_t)0x031c, + (int16_t)0xff71, (int16_t)0x1bd7, (int16_t)0x6106, (int16_t)0x03a4, + (int16_t)0xff7e, (int16_t)0x1a4c, (int16_t)0x61f3, (int16_t)0x0435, + (int16_t)0xff8a, (int16_t)0x18cb, (int16_t)0x62cb, (int16_t)0x04d1, + (int16_t)0xff96, (int16_t)0x1756, (int16_t)0x638f, (int16_t)0x0577, + (int16_t)0xffa1, (int16_t)0x15eb, (int16_t)0x643f, (int16_t)0x0628, + (int16_t)0xffac, (int16_t)0x148c, (int16_t)0x64d9, (int16_t)0x06e4, + (int16_t)0xffb6, (int16_t)0x1338, (int16_t)0x655e, (int16_t)0x07ab, + (int16_t)0xffbf, (int16_t)0x11f0, (int16_t)0x65cd, (int16_t)0x087d, + (int16_t)0xffc8, (int16_t)0x10b4, (int16_t)0x6626, (int16_t)0x095a, + (int16_t)0xffd0, (int16_t)0x0f83, (int16_t)0x6669, (int16_t)0x0a44, + (int16_t)0xffd8, (int16_t)0x0e5f, (int16_t)0x6696, (int16_t)0x0b39, + (int16_t)0xffdf, (int16_t)0x0d46, (int16_t)0x66ad, (int16_t)0x0c39 +}; + +int32_t rdot(size_t n, const int16_t *x, const int16_t *y) +{ + int32_t accu = 0; + + y += n; + + while (n != 0) { + accu += *(x++) * *(--y); + --n; + } + + return accu; +} + +void adpcm_compute_residuals(int16_t* dst, const int16_t* src, + const int16_t* cb_entry, const int16_t* last_samples, size_t count) +{ + const int16_t* const book1 = cb_entry; + const int16_t* const book2 = cb_entry + 8; + + const int16_t l1 = last_samples[0]; + const int16_t l2 = last_samples[1]; + + size_t i; + + assert(count <= 8); + + for(i = 0; i < count; ++i) { + int32_t accu = (int32_t)src[i] << 11; + accu += book1[i]*l1 + book2[i]*l2 + rdot(i, book2, src); + dst[i] = clamp_s16(accu >> 11); + } +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_internal.h b/libmupen64plus/mupen64plus-rsp-hle/src/audio.h similarity index 61% rename from libmupen64plus/mupen64plus-rsp-hle/src/alist_internal.h rename to libmupen64plus/mupen64plus-rsp-hle/src/audio.h index ae39883b45..21c7a09167 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/alist_internal.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/audio.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - alist_internal.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64plus-rsp-hle - audio.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,32 +19,27 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef ALIST_INTERNAL_H -#define ALIST_INTERNAL_H +#ifndef AUDIO_H +#define AUDIO_H -#include "hle.h" +#include +#include -typedef void (*acmd_callback_t)(u32 inst1, u32 inst2); +#include "common.h" -/* - * Audio flags - */ +extern const int16_t RESAMPLE_LUT[64 * 4]; -#define A_INIT 0x01 -#define A_CONTINUE 0x00 -#define A_LOOP 0x02 -#define A_OUT 0x02 -#define A_LEFT 0x02 -#define A_RIGHT 0x00 -#define A_VOL 0x04 -#define A_RATE 0x00 -#define A_AUX 0x08 -#define A_NOAUX 0x00 -#define A_MAIN 0x00 -#define A_MIX 0x10 +int32_t rdot(size_t n, const int16_t *x, const int16_t *y); -extern u16 AudioInBuffer, AudioOutBuffer, AudioCount; -extern u16 AudioAuxA, AudioAuxC, AudioAuxE; -extern u32 loopval; // Value set by A_SETLOOP : Possible conflict with SETVOLUME??? +static inline int16_t adpcm_predict_sample(uint8_t byte, uint8_t mask, + unsigned lshift, unsigned rshift) +{ + int16_t sample = (uint16_t)(byte & mask) << lshift; + sample >>= rshift; /* signed */ + return sample; +} + +void adpcm_compute_residuals(int16_t* dst, const int16_t* src, + const int16_t* cb_entry, const int16_t* last_samples, size_t count); #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c b/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c index 1e89056915..bda5ecab18 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c +++ b/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - cicx105.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Mupen64Plus homepage: https://mupen64plus.org/ * * Copyright (C) 2012 Bobby Smiles * * Copyright (C) 2009 Richard Goedeken * * Copyright (C) 2002 Hacktarux * @@ -23,33 +23,34 @@ #include -#include "hle.h" +#include "hle_internal.h" /** * During IPL3 stage of CIC x105 games, the RSP performs some checks and transactions * necessary for booting the game. - * + * * We only implement the needed DMA transactions for booting. * * Found in Banjo-Tooie, Zelda, Perfect Dark, ...) **/ -void cicx105_ucode() +void cicx105_ucode(struct hle_t* hle) { - // memcpy is okay to use because access constrains are met (alignment, size) + /* memcpy is okay to use because access constrains are met (alignment, size) */ unsigned int i; - unsigned char * dst = rsp.RDRAM + 0x2fb1f0; - unsigned char * src = rsp.IMEM + 0x120; + unsigned char *dst = hle->dram + 0x2fb1f0; + unsigned char *src = hle->imem + 0x120; /* dma_read(0x1120, 0x1e8, 0x1e8) */ - memcpy(rsp.IMEM + 0x120, rsp.RDRAM + 0x1e8, 0x1f0); + memcpy(hle->imem + 0x120, hle->dram + 0x1e8, 0x1f0); /* dma_write(0x1120, 0x2fb1f0, 0xfe817000) */ - for (i = 0; i < 24; ++i) - { + for (i = 0; i < 24; ++i) { memcpy(dst, src, 8); dst += 0xff0; src += 0x8; } + + rsp_break(hle, 0); } diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.h b/libmupen64plus/mupen64plus-rsp-hle/src/common.h similarity index 73% rename from libmupen64plus/mupen64plus-rsp-hle/src/cicx105.h rename to libmupen64plus/mupen64plus-rsp-hle/src/common.h index fbfcfad0a7..cd5dfa6948 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/common.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - cicx105.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64plus-rsp-hle - common.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,10 +19,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef CICX105_H -#define CICX105_H +#ifndef COMMON_H +#define COMMON_H -void cicx105_ucode(); +/* macro for unused variable warning suppression */ +#ifdef __GNUC__ +# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) +#else +# define UNUSED(x) UNUSED_ ## x +#endif + +/* macro for inline keyword */ +#ifdef _MSC_VER +#define inline __inline +#endif #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle.c b/libmupen64plus/mupen64plus-rsp-hle/src/hle.c new file mode 100644 index 0000000000..ddc0404404 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle.c @@ -0,0 +1,489 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - hle.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2012 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#ifdef ENABLE_TASK_DUMP +#include +#endif + +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* some rdp status flags */ +#define DP_STATUS_FREEZE 0x2 + + + +/* helper functions prototypes */ +static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size); +static bool is_task(struct hle_t* hle); +static void send_dlist_to_gfx_plugin(struct hle_t* hle); +static bool try_fast_audio_dispatching(struct hle_t* hle); +static bool try_fast_task_dispatching(struct hle_t* hle); +static void normal_task_dispatching(struct hle_t* hle); +static void non_task_dispatching(struct hle_t* hle); +static bool try_re2_task_dispatching(struct hle_t* hle); + +#ifdef ENABLE_TASK_DUMP +static void dump_binary(struct hle_t* hle, const char *const filename, + const unsigned char *const bytes, unsigned int size); +static void dump_task(struct hle_t* hle, const char *const filename); +static void dump_unknown_task(struct hle_t* hle, unsigned int sum); +static void dump_unknown_non_task(struct hle_t* hle, unsigned int sum); +#endif + +/* Global functions */ +void hle_init(struct hle_t* hle, + unsigned char* dram, + unsigned char* dmem, + unsigned char* imem, + unsigned int* mi_intr, + unsigned int* sp_mem_addr, + unsigned int* sp_dram_addr, + unsigned int* sp_rd_length, + unsigned int* sp_wr_length, + unsigned int* sp_status, + unsigned int* sp_dma_full, + unsigned int* sp_dma_busy, + unsigned int* sp_pc, + unsigned int* sp_semaphore, + unsigned int* dpc_start, + unsigned int* dpc_end, + unsigned int* dpc_current, + unsigned int* dpc_status, + unsigned int* dpc_clock, + unsigned int* dpc_bufbusy, + unsigned int* dpc_pipebusy, + unsigned int* dpc_tmem, + void* user_defined) +{ + hle->dram = dram; + hle->dmem = dmem; + hle->imem = imem; + hle->mi_intr = mi_intr; + hle->sp_mem_addr = sp_mem_addr; + hle->sp_dram_addr = sp_dram_addr; + hle->sp_rd_length = sp_rd_length; + hle->sp_wr_length = sp_wr_length; + hle->sp_status = sp_status; + hle->sp_dma_full = sp_dma_full; + hle->sp_dma_busy = sp_dma_busy; + hle->sp_pc = sp_pc; + hle->sp_semaphore = sp_semaphore; + hle->dpc_start = dpc_start; + hle->dpc_end = dpc_end; + hle->dpc_current = dpc_current; + hle->dpc_status = dpc_status; + hle->dpc_clock = dpc_clock; + hle->dpc_bufbusy = dpc_bufbusy; + hle->dpc_pipebusy = dpc_pipebusy; + hle->dpc_tmem = dpc_tmem; + hle->user_defined = user_defined; +} + +void hle_execute(struct hle_t* hle) +{ + if (is_task(hle)) { + if (!try_fast_task_dispatching(hle)) + normal_task_dispatching(hle); + } else { + non_task_dispatching(hle); + } +} + +/* local functions */ +static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size) +{ + unsigned int sum = 0; + const unsigned char *const bytes_end = bytes + size; + + while (bytes != bytes_end) + sum += *bytes++; + + return sum; +} + +/** + * Try to figure if the RSP was launched using osSpTask* functions + * and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless). + * + * Previously, the ucode_size field was used to determine this, + * but it is not robust enough (hi Pokemon Stadium !) because games could write anything + * in this field : most ucode_boot discard the value and just use 0xf7f anyway. + * + * Using ucode_boot_size should be more robust in this regard. + **/ +static bool is_task(struct hle_t* hle) +{ + return (*dmem_u32(hle, TASK_UCODE_BOOT_SIZE) <= 0x1000); +} + +void rsp_break(struct hle_t* hle, unsigned int setbits) +{ + *hle->sp_status |= setbits | SP_STATUS_BROKE | SP_STATUS_HALT; + + if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK)) { + *hle->mi_intr |= MI_INTR_SP; + HleCheckInterrupts(hle->user_defined); + } +} + +static void send_alist_to_audio_plugin(struct hle_t* hle) +{ + HleProcessAlistList(hle->user_defined); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +static void send_dlist_to_gfx_plugin(struct hle_t* hle) +{ + /* Since GFX_INFO version 2, these bits are set before calling the ProcessDlistList function. + * And the GFX plugin is responsible to unset them if needed. + * For GFX_INFO version < 2, the GFX plugin didn't have access to sp_status so + * it doesn't matter if we set these bits before calling ProcessDlistList function. + */ + *hle->sp_status |= SP_STATUS_TASKDONE | SP_STATUS_BROKE | SP_STATUS_HALT; + + HleProcessDlistList(hle->user_defined); + + if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK) && (*hle->sp_status & (SP_STATUS_TASKDONE | SP_STATUS_BROKE | SP_STATUS_HALT))) { + *hle->mi_intr |= MI_INTR_SP; + HleCheckInterrupts(hle->user_defined); + } +} + +static bool try_fast_audio_dispatching(struct hle_t* hle) +{ + /* identify audio ucode by using the content of ucode_data */ + uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA); + uint32_t v; + + if (*dram_u32(hle, ucode_data) == 0x00000001) { + if (*dram_u32(hle, ucode_data + 0x30) == 0xf0000f00) { + v = *dram_u32(hle, ucode_data + 0x28); + switch(v) + { + case 0x1e24138c: /* audio ABI (most common) */ + alist_process_audio(hle); return true; + case 0x1dc8138c: /* GoldenEye */ + alist_process_audio_ge(hle); return true; + case 0x1e3c1390: /* BlastCorp, DiddyKongRacing */ + alist_process_audio_bc(hle); return true; + default: + HleWarnMessage(hle->user_defined, "ABI1 identification regression: v=%08x", v); + } + } else { + v = *dram_u32(hle, ucode_data + 0x10); + switch(v) + { + case 0x11181350: /* MarioKart, WaveRace (E) */ + alist_process_nead_mk(hle); return true; + case 0x111812e0: /* StarFox (J) */ + alist_process_nead_sfj(hle); return true; + case 0x110412ac: /* WaveRace (J RevB) */ + alist_process_nead_wrjb(hle); return true; + case 0x110412cc: /* StarFox/LylatWars (except J) */ + alist_process_nead_sf(hle); return true; + case 0x1cd01250: /* FZeroX */ + alist_process_nead_fz(hle); return true; + case 0x1f08122c: /* YoshisStory */ + alist_process_nead_ys(hle); return true; + case 0x1f38122c: /* 1080° Snowboarding */ + alist_process_nead_1080(hle); return true; + case 0x1f681230: /* Zelda OoT / Zelda MM (J, J RevA) */ + alist_process_nead_oot(hle); return true; + case 0x1f801250: /* Zelda MM (except J, J RevA, E Beta), PokemonStadium 2 */ + alist_process_nead_mm(hle); return true; + case 0x109411f8: /* Zelda MM (E Beta) */ + alist_process_nead_mmb(hle); return true; + case 0x1eac11b8: /* AnimalCrossing */ + alist_process_nead_ac(hle); return true; + case 0x00010010: /* MusyX v2 (IndianaJones, BattleForNaboo) */ + musyx_v2_task(hle); return true; + case 0x1f701238: /* Mario Artist Talent Studio */ + alist_process_nead_mats(hle); return true; + case 0x1f4c1230: /* FZeroX Expansion */ + alist_process_nead_efz(hle); return true; + default: + HleWarnMessage(hle->user_defined, "ABI2 identification regression: v=%08x", v); + } + } + } else { + v = *dram_u32(hle, ucode_data + 0x10); + switch(v) + { + case 0x00000001: /* MusyX v1 + RogueSquadron, ResidentEvil2, PolarisSnoCross, + TheWorldIsNotEnough, RugratsInParis, NBAShowTime, + HydroThunder, Tarzan, GauntletLegend, Rush2049 */ + musyx_v1_task(hle); return true; + case 0x0000127c: /* naudio (many games) */ + alist_process_naudio(hle); return true; + case 0x00001280: /* BanjoKazooie */ + alist_process_naudio_bk(hle); return true; + case 0x1c58126c: /* DonkeyKong */ + alist_process_naudio_dk(hle); return true; + case 0x1ae8143c: /* BanjoTooie, JetForceGemini, MickeySpeedWayUSA, PerfectDark */ + alist_process_naudio_mp3(hle); return true; + case 0x1ab0140c: /* ConkerBadFurDay */ + alist_process_naudio_cbfd(hle); return true; + + default: + HleWarnMessage(hle->user_defined, "ABI3 identification regression: v=%08x", v); + } + } + + return false; +} + +static bool try_fast_task_dispatching(struct hle_t* hle) +{ + /* identify task ucode by its type */ + switch (*dmem_u32(hle, TASK_TYPE)) { + case 1: + /* Resident evil 2 */ + if (*dmem_u32(hle, TASK_DATA_PTR) == 0) { + return try_re2_task_dispatching(hle); + } + + if (hle->hle_gfx) { + send_dlist_to_gfx_plugin(hle); + return true; + } + break; + + case 2: + if (hle->hle_aud) { + send_alist_to_audio_plugin(hle); + return true; + } else if (try_fast_audio_dispatching(hle)) + return true; + break; + + case 7: + HleShowCFB(hle->user_defined); + break; + } + + return false; +} + +static void normal_task_dispatching(struct hle_t* hle) +{ + const unsigned int sum = + sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), min(*dmem_u32(hle, TASK_UCODE_SIZE), 0xf80) >> 1); + + switch (sum) { + /* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */ + case 0x278: + /* Nothing to emulate */ + rsp_break(hle, SP_STATUS_TASKDONE); + return; + + /* GFX: Twintris [misleading task->type == 0] */ + case 0x212ee: + if (hle->hle_gfx) { + send_dlist_to_gfx_plugin(hle); + return; + } + break; + + /* JPEG: found in Pokemon Stadium J */ + case 0x2c85a: + jpeg_decode_PS0(hle); + return; + + /* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */ + case 0x2caa6: + jpeg_decode_PS(hle); + return; + + /* JPEG: found in Ogre Battle, Bottom of the 9th */ + case 0x130de: + case 0x278b0: + jpeg_decode_OB(hle); + return; + } + + /* Forward task to RSP Fallback. + * If task is not forwarded, use the regular "unknown task" path */ + if (HleForwardTask(hle->user_defined) != 0) { + + /* Send task_done signal for unknown ucodes to allow further processings */ + rsp_break(hle, SP_STATUS_TASKDONE); + + HleWarnMessage(hle->user_defined, "unknown OSTask: sum: %x PC:%x", sum, *hle->sp_pc); +#ifdef ENABLE_TASK_DUMP + dump_unknown_task(hle, sum); +#endif + } +} + +static void non_task_dispatching(struct hle_t* hle) +{ + const unsigned int sum = sum_bytes(hle->imem, 44); + + if (sum == 0x9e2) + { + /* CIC x105 ucode (used during boot of CIC x105 games) */ + cicx105_ucode(hle); + return; + } + + /* Forward task to RSP Fallback. + * If task is not forwarded, use the regular "unknown ucode" path */ + if (HleForwardTask(hle->user_defined) != 0) { + + HleWarnMessage(hle->user_defined, "unknown RSP code: sum: %x PC:%x", sum, *hle->sp_pc); +#ifdef ENABLE_TASK_DUMP + dump_unknown_non_task(hle, sum); +#endif + } +} + +/* Resident evil 2 */ +static bool try_re2_task_dispatching(struct hle_t* hle) +{ + const unsigned int sum = + sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), 256); + + switch (sum) { + + case 0x450f: + resize_bilinear_task(hle); + return true; + + case 0x3b44: + decode_video_frame_task(hle); + return true; + + case 0x3d84: + fill_video_double_buffer_task(hle); + return true; + } + + return false; +} + +#ifdef ENABLE_TASK_DUMP +static void dump_unknown_task(struct hle_t* hle, unsigned int sum) +{ + char filename[256]; + uint32_t ucode = *dmem_u32(hle, TASK_UCODE); + uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA); + uint32_t data_ptr = *dmem_u32(hle, TASK_DATA_PTR); + + sprintf(&filename[0], "task_%x.log", sum); + dump_task(hle, filename); + + /* dump ucode_boot */ + sprintf(&filename[0], "ucode_boot_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE_BOOT)), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE)); + + /* dump ucode */ + if (ucode != 0) { + sprintf(&filename[0], "ucode_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, ucode), 0xf80); + } + + /* dump ucode_data */ + if (ucode_data != 0) { + sprintf(&filename[0], "ucode_data_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, ucode_data), *dmem_u32(hle, TASK_UCODE_DATA_SIZE)); + } + + /* dump data */ + if (data_ptr != 0) { + sprintf(&filename[0], "data_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, data_ptr), *dmem_u32(hle, TASK_DATA_SIZE)); + } +} + +static void dump_unknown_non_task(struct hle_t* hle, unsigned int sum) +{ + char filename[256]; + + /* dump IMEM & DMEM for further analysis */ + sprintf(&filename[0], "imem_%x.bin", sum); + dump_binary(hle, filename, hle->imem, 0x1000); + + sprintf(&filename[0], "dmem_%x.bin", sum); + dump_binary(hle, filename, hle->dmem, 0x1000); +} + +static void dump_binary(struct hle_t* hle, const char *const filename, + const unsigned char *const bytes, unsigned int size) +{ + FILE *f; + + /* if file already exists, do nothing */ + f = fopen(filename, "r"); + if (f == NULL) { + /* else we write bytes to the file */ + f = fopen(filename, "wb"); + if (f != NULL) { + if (fwrite(bytes, 1, size, f) != size) + HleErrorMessage(hle->user_defined, "Writing error on %s", filename); + fclose(f); + } else + HleErrorMessage(hle->user_defined, "Couldn't open %s for writing !", filename); + } else + fclose(f); +} + +static void dump_task(struct hle_t* hle, const char *const filename) +{ + FILE *f; + + f = fopen(filename, "r"); + if (f == NULL) { + f = fopen(filename, "w"); + fprintf(f, + "type = %d\n" + "flags = %d\n" + "ucode_boot = %#08x size = %#x\n" + "ucode = %#08x size = %#x\n" + "ucode_data = %#08x size = %#x\n" + "dram_stack = %#08x size = %#x\n" + "output_buff = %#08x *size = %#x\n" + "data = %#08x size = %#x\n" + "yield_data = %#08x size = %#x\n", + *dmem_u32(hle, TASK_TYPE), + *dmem_u32(hle, TASK_FLAGS), + *dmem_u32(hle, TASK_UCODE_BOOT), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE), + *dmem_u32(hle, TASK_UCODE), *dmem_u32(hle, TASK_UCODE_SIZE), + *dmem_u32(hle, TASK_UCODE_DATA), *dmem_u32(hle, TASK_UCODE_DATA_SIZE), + *dmem_u32(hle, TASK_DRAM_STACK), *dmem_u32(hle, TASK_DRAM_STACK_SIZE), + *dmem_u32(hle, TASK_OUTPUT_BUFF), *dmem_u32(hle, TASK_OUTPUT_BUFF_SIZE), + *dmem_u32(hle, TASK_DATA_PTR), *dmem_u32(hle, TASK_DATA_SIZE), + *dmem_u32(hle, TASK_YIELD_DATA_PTR), *dmem_u32(hle, TASK_YIELD_DATA_SIZE)); + fclose(f); + } else + fclose(f); +} +#endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle.h b/libmupen64plus/mupen64plus-rsp-hle/src/hle.h index ea517e735e..a420b752d3 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/hle.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - hle.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,68 +22,33 @@ #ifndef HLE_H #define HLE_H -#define M64P_PLUGIN_PROTOTYPES 1 -#include "m64p_plugin.h" +#include "hle_internal.h" -#define RSP_HLE_VERSION 0x020000 -#define RSP_PLUGIN_API_VERSION 0x020000 +void hle_init(struct hle_t* hle, + unsigned char* dram, + unsigned char* dmem, + unsigned char* imem, + unsigned int* mi_intr, + unsigned int* sp_mem_addr, + unsigned int* sp_dram_addr, + unsigned int* sp_rd_length, + unsigned int* sp_wr_length, + unsigned int* sp_status, + unsigned int* sp_dma_full, + unsigned int* sp_dma_busy, + unsigned int* sp_pc, + unsigned int* sp_semaphore, + unsigned int* dpc_start, + unsigned int* dpc_end, + unsigned int* dpc_current, + unsigned int* dpc_status, + unsigned int* dpc_clock, + unsigned int* dpc_bufbusy, + unsigned int* dpc_pipebusy, + unsigned int* dpc_tmem, + void* user_defined); -#ifdef M64P_BIG_ENDIAN -#define S 0 -#define S16 0 -#define S8 0 -#else -#define S 1 -#define S16 2 -#define S8 3 -#endif - -// types -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; - -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed long long s64; - -extern RSP_INFO rsp; - -typedef struct -{ - unsigned int type; - unsigned int flags; - - unsigned int ucode_boot; - unsigned int ucode_boot_size; - - unsigned int ucode; - unsigned int ucode_size; - - unsigned int ucode_data; - unsigned int ucode_data_size; - - unsigned int dram_stack; - unsigned int dram_stack_size; - - unsigned int output_buff; - unsigned int output_buff_size; - - unsigned int data_ptr; - unsigned int data_size; - - unsigned int yield_data_ptr; - unsigned int yield_data_size; -} OSTask_t; - -static const OSTask_t * const get_task() -{ - return (OSTask_t*)(rsp.DMEM + 0xfc0); -} - -void DebugMessage(int level, const char *message, ...); +void hle_execute(struct hle_t* hle); #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle_external.h b/libmupen64plus/mupen64plus-rsp-hle/src/hle_external.h new file mode 100644 index 0000000000..5dcbf2ea47 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle_external.h @@ -0,0 +1,40 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - hle_external.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef HLE_EXTERNAL_H +#define HLE_EXTERNAL_H + +/* users of the hle core are expected to define these functions */ + +void HleVerboseMessage(void* user_defined, const char *message, ...); +void HleInfoMessage(void* user_defined, const char *message, ...); +void HleErrorMessage(void* user_defined, const char *message, ...); +void HleWarnMessage(void* user_defined, const char *message, ...); + +void HleCheckInterrupts(void* user_defined); +void HleProcessDlistList(void* user_defined); +void HleProcessAlistList(void* user_defined); +void HleProcessRdpList(void* user_defined); +void HleShowCFB(void* user_defined); +int HleForwardTask(void* user_defined); + +#endif + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle_internal.h b/libmupen64plus/mupen64plus-rsp-hle/src/hle_internal.h new file mode 100644 index 0000000000..ad1708bb1b --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle_internal.h @@ -0,0 +1,91 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - hle_internal.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef HLE_INTERNAL_H +#define HLE_INTERNAL_H + +#include + +#include "ucodes.h" + +/* rsp hle internal state - internal usage only */ +struct hle_t +{ + unsigned char* dram; + unsigned char* dmem; + unsigned char* imem; + + unsigned int* mi_intr; + + unsigned int* sp_mem_addr; + unsigned int* sp_dram_addr; + unsigned int* sp_rd_length; + unsigned int* sp_wr_length; + unsigned int* sp_status; + unsigned int* sp_dma_full; + unsigned int* sp_dma_busy; + unsigned int* sp_pc; + unsigned int* sp_semaphore; + + unsigned int* dpc_start; + unsigned int* dpc_end; + unsigned int* dpc_current; + unsigned int* dpc_status; + unsigned int* dpc_clock; + unsigned int* dpc_bufbusy; + unsigned int* dpc_pipebusy; + unsigned int* dpc_tmem; + + /* for user convenience, this will be passed to "external" functions */ + void* user_defined; + + int hle_gfx; + int hle_aud; + + /* alist.c */ + uint8_t alist_buffer[0x1000]; + + /* alist_audio.c */ + struct alist_audio_t alist_audio; + + /* alist_naudio.c */ + struct alist_naudio_t alist_naudio; + + /* alist_nead.c */ + struct alist_nead_t alist_nead; + + /* mp3.c */ + uint8_t mp3_buffer[0x1000]; +}; + +/* some mips interface interrupt flags */ +#define MI_INTR_SP 0x1 + +/* some rsp status flags */ +#define SP_STATUS_HALT 0x1 +#define SP_STATUS_BROKE 0x2 +#define SP_STATUS_INTR_ON_BREAK 0x40 +#define SP_STATUS_TASKDONE 0x200 + +void rsp_break(struct hle_t* hle, unsigned int setbits); + +#endif + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c b/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c index 28fcc8bdd9..ff424c4033 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c +++ b/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - jpeg.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Mupen64Plus homepage: https://mupen64plus.org/ * * Copyright (C) 2012 Bobby Smiles * * Copyright (C) 2009 Richard Goedeken * * Copyright (C) 2002 Hacktarux * @@ -22,49 +22,48 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include -#include #include +#include -#define M64P_PLUGIN_PROTOTYPES 1 -#include "m64p_types.h" -#include "m64p_plugin.h" -#include "hle.h" +#include "arithmetics.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" #define SUBBLOCK_SIZE 64 -typedef void (*tile_line_emitter_t)(const int16_t *y, const int16_t *u, uint32_t address); -typedef void (*std_macroblock_decoder_t)(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]); - -/* rdram operations */ -// FIXME: these functions deserve their own module -static void rdram_read_many_u16(uint16_t *dst, uint32_t address, unsigned int count); -static void rdram_write_many_u16(const uint16_t *src, uint32_t address, unsigned int count); -static uint32_t rdram_read_u32(uint32_t address); -static void rdram_write_many_u32(const uint32_t *src, uint32_t address, unsigned int count); +typedef void (*tile_line_emitter_t)(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address); +typedef void (*subblock_transform_t)(int16_t *dst, const int16_t *src); /* standard jpeg ucode decoder */ -static void jpeg_decode_std(const char * const version, const std_macroblock_decoder_t decode_mb, const tile_line_emitter_t emit_line); +static void jpeg_decode_std(struct hle_t* hle, + const char *const version, + const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + const tile_line_emitter_t emit_line); /* helper functions */ static uint8_t clamp_u8(int16_t x); static int16_t clamp_s12(int16_t x); -static int16_t clamp_s16(int32_t x); static uint16_t clamp_RGBA_component(int16_t x); -/* pixel conversion & foratting */ +/* pixel conversion & formatting */ static uint32_t GetUYVY(int16_t y1, int16_t y2, int16_t u, int16_t v); static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v); /* tile line emitters */ -static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address); -static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t address); +static void EmitYUVTileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address); +static void EmitRGBATileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address); /* macroblocks operations */ -static void DecodeMacroblock1(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable); -static void DecodeMacroblock2(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]); -static void DecodeMacroblock3(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]); -static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); -static void EmitTilesMode2(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); +static void decode_macroblock_ob(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable); +static void decode_macroblock_std(const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + int16_t *macroblock, + unsigned int subblock_count, + const int16_t qtables[3][SUBBLOCK_SIZE]); +static void EmitTilesMode0(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); +static void EmitTilesMode2(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); /* subblocks operations */ static void TransposeSubBlock(int16_t *dst, const int16_t *src); @@ -73,14 +72,13 @@ static void ReorderSubBlock(int16_t *dst, const int16_t *src, const unsigned int static void MultSubBlocks(int16_t *dst, const int16_t *src1, const int16_t *src2, unsigned int shift); static void ScaleSubBlock(int16_t *dst, const int16_t *src, int16_t scale); static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift); -static void InverseDCT1D(const float * const x, float *dst, unsigned int stride); +static void InverseDCT1D(const float *const x, float *dst, unsigned int stride); static void InverseDCTSubBlock(int16_t *dst, const int16_t *src); static void RescaleYSubBlock(int16_t *dst, const int16_t *src); static void RescaleUVSubBlock(int16_t *dst, const int16_t *src); /* transposed dequantization table */ -static const int16_t DEFAULT_QTABLE[SUBBLOCK_SIZE] = -{ +static const int16_t DEFAULT_QTABLE[SUBBLOCK_SIZE] = { 16, 12, 14, 14, 18, 24, 49, 72, 11, 12, 13, 17, 22, 35, 64, 92, 10, 14, 16, 22, 37, 55, 78, 95, @@ -92,8 +90,7 @@ static const int16_t DEFAULT_QTABLE[SUBBLOCK_SIZE] = }; /* zig-zag indices */ -static const unsigned int ZIGZAG_TABLE[SUBBLOCK_SIZE] = -{ +static const unsigned int ZIGZAG_TABLE[SUBBLOCK_SIZE] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, @@ -105,8 +102,7 @@ static const unsigned int ZIGZAG_TABLE[SUBBLOCK_SIZE] = }; /* transposition indices */ -static const unsigned int TRANSPOSE_TABLE[SUBBLOCK_SIZE] = -{ +static const unsigned int TRANSPOSE_TABLE[SUBBLOCK_SIZE] = { 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, 42, 50, 58, @@ -123,18 +119,17 @@ static const unsigned int TRANSPOSE_TABLE[SUBBLOCK_SIZE] = * Cn = alpha * cos(n * PI / 16) (alpha is chosen such as C4 = 1) */ static const float IDCT_C3 = 1.175875602f; static const float IDCT_C6 = 0.541196100f; -static const float IDCT_K[10] = -{ - 0.765366865f, /* C2-C6 */ - -1.847759065f, /* -C2-C6 */ - -0.390180644f, /* C5-C3 */ - -1.961570561f, /* -C5-C3 */ - 1.501321110f, /* C1+C3-C5-C7 */ - 2.053119869f, /* C1+C3-C5+C7 */ - 3.072711027f, /* C1+C3+C5-C7 */ - 0.298631336f, /* -C1+C3+C5-C7 */ - -0.899976223f, /* C7-C3 */ - -2.562915448f /* -C1-C3 */ +static const float IDCT_K[10] = { + 0.765366865f, /* C2-C6 */ + -1.847759065f, /* -C2-C6 */ + -0.390180644f, /* C5-C3 */ + -1.961570561f, /* -C5-C3 */ + 1.501321110f, /* C1+C3-C5-C7 */ + 2.053119869f, /* C1+C3-C5+C7 */ + 3.072711027f, /* C1+C3+C5-C7 */ + 0.298631336f, /* -C1+C3+C5-C7 */ + -0.899976223f, /* C7-C3 */ + -2.562915448f /* -C1-C3 */ }; @@ -143,24 +138,26 @@ static const float IDCT_K[10] = /*************************************************************************** * JPEG decoding ucode found in Japanese exclusive version of Pokemon Stadium. **************************************************************************/ -void jpeg_decode_PS0() +void jpeg_decode_PS0(struct hle_t* hle) { - jpeg_decode_std("PS0", DecodeMacroblock3, EmitYUVTileLine); + jpeg_decode_std(hle, "PS0", RescaleYSubBlock, RescaleUVSubBlock, EmitYUVTileLine); + rsp_break(hle, SP_STATUS_TASKDONE); } /*************************************************************************** * JPEG decoding ucode found in Ocarina of Time, Pokemon Stadium 1 and * Pokemon Stadium 2. **************************************************************************/ -void jpeg_decode_PS() +void jpeg_decode_PS(struct hle_t* hle) { - jpeg_decode_std("PS", DecodeMacroblock2, EmitRGBATileLine); + jpeg_decode_std(hle, "PS", NULL, NULL, EmitRGBATileLine); + rsp_break(hle, SP_STATUS_TASKDONE); } /*************************************************************************** * JPEG decoding ucode found in Ogre Battle and Bottom of the 9th. **************************************************************************/ -void jpeg_decode_OB() +void jpeg_decode_OB(struct hle_t* hle) { int16_t qtable[SUBBLOCK_SIZE]; unsigned int mb; @@ -168,45 +165,43 @@ void jpeg_decode_OB() int32_t y_dc = 0; int32_t u_dc = 0; int32_t v_dc = 0; - - const OSTask_t * const task = get_task(); - uint32_t address = task->data_ptr; - const unsigned int macroblock_count = task->data_size; - const int qscale = task->yield_data_size; + uint32_t address = *dmem_u32(hle, TASK_DATA_PTR); + const unsigned int macroblock_count = *dmem_u32(hle, TASK_DATA_SIZE); + const int qscale = *dmem_u32(hle, TASK_YIELD_DATA_SIZE); - DebugMessage(M64MSG_VERBOSE, "jpeg_decode_OB: *buffer=%x, #MB=%d, qscale=%d", - address, - macroblock_count, - qscale); + HleVerboseMessage(hle->user_defined, + "jpeg_decode_OB: *buffer=%x, #MB=%d, qscale=%d", + address, + macroblock_count, + qscale); - if (qscale != 0) - { + if (qscale != 0) { if (qscale > 0) - { ScaleSubBlock(qtable, DEFAULT_QTABLE, qscale); - } else - { RShiftSubBlock(qtable, DEFAULT_QTABLE, -qscale); - } } - for (mb = 0; mb < macroblock_count; ++mb) - { - int16_t macroblock[6*SUBBLOCK_SIZE]; + for (mb = 0; mb < macroblock_count; ++mb) { + int16_t macroblock[6 * SUBBLOCK_SIZE]; - rdram_read_many_u16((uint16_t*)macroblock, address, 6*SUBBLOCK_SIZE); - DecodeMacroblock1(macroblock, &y_dc, &u_dc, &v_dc, (qscale != 0) ? qtable : NULL); - EmitTilesMode2(EmitYUVTileLine, macroblock, address); + dram_load_u16(hle, (uint16_t *)macroblock, address, 6 * SUBBLOCK_SIZE); + decode_macroblock_ob(macroblock, &y_dc, &u_dc, &v_dc, (qscale != 0) ? qtable : NULL); + EmitTilesMode2(hle, EmitYUVTileLine, macroblock, address); - address += (2*6*SUBBLOCK_SIZE); + address += (2 * 6 * SUBBLOCK_SIZE); } + rsp_break(hle, SP_STATUS_TASKDONE); } /* local functions */ -static void jpeg_decode_std(const char * const version, const std_macroblock_decoder_t decode_mb, const tile_line_emitter_t emit_line) +static void jpeg_decode_std(struct hle_t* hle, + const char *const version, + const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + const tile_line_emitter_t emit_line) { int16_t qtables[3][SUBBLOCK_SIZE]; unsigned int mb; @@ -218,68 +213,59 @@ static void jpeg_decode_std(const char * const version, const std_macroblock_dec uint32_t qtableV_ptr; unsigned int subblock_count; unsigned int macroblock_size; - int16_t *macroblock; - const OSTask_t * const task = get_task(); + /* macroblock contains at most 6 subblocks */ + int16_t macroblock[6 * SUBBLOCK_SIZE]; + uint32_t data_ptr; - if (task->flags & 0x1) - { - DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: task yielding not implemented", version); + if (*dmem_u32(hle, TASK_FLAGS) & 0x1) { + HleWarnMessage(hle->user_defined, + "jpeg_decode_%s: task yielding not implemented", version); return; } - address = rdram_read_u32(task->data_ptr); - macroblock_count = rdram_read_u32(task->data_ptr + 4); - mode = rdram_read_u32(task->data_ptr + 8); - qtableY_ptr = rdram_read_u32(task->data_ptr + 12); - qtableU_ptr = rdram_read_u32(task->data_ptr + 16); - qtableV_ptr = rdram_read_u32(task->data_ptr + 20); + data_ptr = *dmem_u32(hle, TASK_DATA_PTR); + address = *dram_u32(hle, data_ptr); + macroblock_count = *dram_u32(hle, data_ptr + 4); + mode = *dram_u32(hle, data_ptr + 8); + qtableY_ptr = *dram_u32(hle, data_ptr + 12); + qtableU_ptr = *dram_u32(hle, data_ptr + 16); + qtableV_ptr = *dram_u32(hle, data_ptr + 20); - DebugMessage(M64MSG_VERBOSE, "jpeg_decode_%s: *buffer=%x, #MB=%d, mode=%d, *Qy=%x, *Qu=%x, *Qv=%x", - version, - address, - macroblock_count, - mode, - qtableY_ptr, - qtableU_ptr, - qtableV_ptr); + HleVerboseMessage(hle->user_defined, + "jpeg_decode_%s: *buffer=%x, #MB=%d, mode=%d, *Qy=%x, *Qu=%x, *Qv=%x", + version, + address, + macroblock_count, + mode, + qtableY_ptr, + qtableU_ptr, + qtableV_ptr); - if (mode != 0 && mode != 2) - { - DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: invalid mode %d", version, mode); + if (mode != 0 && mode != 2) { + HleWarnMessage(hle->user_defined, + "jpeg_decode_%s: invalid mode %d", version, mode); return; } - + subblock_count = mode + 4; - macroblock_size = 2*subblock_count*SUBBLOCK_SIZE; + macroblock_size = subblock_count * SUBBLOCK_SIZE; - rdram_read_many_u16((uint16_t*)qtables[0], qtableY_ptr, SUBBLOCK_SIZE); - rdram_read_many_u16((uint16_t*)qtables[1], qtableU_ptr, SUBBLOCK_SIZE); - rdram_read_many_u16((uint16_t*)qtables[2], qtableV_ptr, SUBBLOCK_SIZE); + dram_load_u16(hle, (uint16_t *)qtables[0], qtableY_ptr, SUBBLOCK_SIZE); + dram_load_u16(hle, (uint16_t *)qtables[1], qtableU_ptr, SUBBLOCK_SIZE); + dram_load_u16(hle, (uint16_t *)qtables[2], qtableV_ptr, SUBBLOCK_SIZE); - macroblock = malloc(sizeof(*macroblock) * macroblock_size); - if (!macroblock) - { - DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: could not allocate macroblock", version); - return; - } - - for (mb = 0; mb < macroblock_count; ++mb) - { - rdram_read_many_u16((uint16_t*)macroblock, address, macroblock_size >> 1); - decode_mb(macroblock, subblock_count, (const int16_t (*)[SUBBLOCK_SIZE])qtables); + for (mb = 0; mb < macroblock_count; ++mb) { + dram_load_u16(hle, (uint16_t *)macroblock, address, macroblock_size); + decode_macroblock_std(transform_luma, transform_chroma, + macroblock, subblock_count, (const int16_t (*)[SUBBLOCK_SIZE])qtables); if (mode == 0) - { - EmitTilesMode0(emit_line, macroblock, address); - } + EmitTilesMode0(hle, emit_line, macroblock, address); else - { - EmitTilesMode2(emit_line, macroblock, address); - } + EmitTilesMode2(hle, emit_line, macroblock, address); - address += macroblock_size; + address += 2 * macroblock_size; } - free(macroblock); } static uint8_t clamp_u8(int16_t x) @@ -289,28 +275,28 @@ static uint8_t clamp_u8(int16_t x) static int16_t clamp_s12(int16_t x) { - if (x < -0x800) { x = -0x800; } else if (x > 0x7f0) { x = 0x7f0; } - return x; -} - -static int16_t clamp_s16(int32_t x) -{ - if (x > 32767) { x = 32767; } else if (x < -32768) { x = -32768; } + if (x < -0x800) + x = -0x800; + else if (x > 0x7f0) + x = 0x7f0; return x; } static uint16_t clamp_RGBA_component(int16_t x) { - if (x > 0xff0) { x = 0xff0; } else if (x < 0) { x = 0; } + if (x > 0xff0) + x = 0xff0; + else if (x < 0) + x = 0; return (x & 0xf80); } static uint32_t GetUYVY(int16_t y1, int16_t y2, int16_t u, int16_t v) { - return (uint32_t)clamp_u8(u) << 24 - | (uint32_t)clamp_u8(y1) << 16 - | (uint32_t)clamp_u8(v) << 8 - | (uint32_t)clamp_u8(y2); + return (uint32_t)clamp_u8(u) << 24 | + (uint32_t)clamp_u8(y1) << 16 | + (uint32_t)clamp_u8(v) << 8 | + (uint32_t)clamp_u8(y2); } static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v) @@ -319,19 +305,19 @@ static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v) const float fU = (float)u; const float fV = (float)v; - const uint16_t r = clamp_RGBA_component((int16_t)(fY + 1.4025*fV)); - const uint16_t g = clamp_RGBA_component((int16_t)(fY - 0.3443*fU - 0.7144*fV)); - const uint16_t b = clamp_RGBA_component((int16_t)(fY + 1.7729*fU )); + const uint16_t r = clamp_RGBA_component((int16_t)(fY + 1.4025 * fV)); + const uint16_t g = clamp_RGBA_component((int16_t)(fY - 0.3443 * fU - 0.7144 * fV)); + const uint16_t b = clamp_RGBA_component((int16_t)(fY + 1.7729 * fU)); return (r << 4) | (g >> 1) | (b >> 6) | 1; } -static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address) +static void EmitYUVTileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address) { uint32_t uyvy[8]; - const int16_t * const v = u + SUBBLOCK_SIZE; - const int16_t * const y2 = y + SUBBLOCK_SIZE; + const int16_t *const v = u + SUBBLOCK_SIZE; + const int16_t *const y2 = y + SUBBLOCK_SIZE; uyvy[0] = GetUYVY(y[0], y[1], u[0], v[0]); uyvy[1] = GetUYVY(y[2], y[3], u[1], v[1]); @@ -342,15 +328,15 @@ static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address uyvy[6] = GetUYVY(y2[4], y2[5], u[6], v[6]); uyvy[7] = GetUYVY(y2[6], y2[7], u[7], v[7]); - rdram_write_many_u32(uyvy, address, 8); + dram_store_u32(hle, uyvy, address, 8); } -static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t address) +static void EmitRGBATileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address) { uint16_t rgba[16]; - const int16_t * const v = u + SUBBLOCK_SIZE; - const int16_t * const y2 = y + SUBBLOCK_SIZE; + const int16_t *const v = u + SUBBLOCK_SIZE; + const int16_t *const y2 = y + SUBBLOCK_SIZE; rgba[0] = GetRGBA(y[0], u[0], v[0]); rgba[1] = GetRGBA(y[1], u[0], v[0]); @@ -369,19 +355,18 @@ static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t addres rgba[14] = GetRGBA(y2[6], u[7], v[7]); rgba[15] = GetRGBA(y2[7], u[7], v[7]); - rdram_write_many_u16(rgba, address, 16); + dram_store_u16(hle, rgba, address, 16); } -static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) +static void EmitTilesMode0(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) { unsigned int i; unsigned int y_offset = 0; - unsigned int u_offset = 2*SUBBLOCK_SIZE; + unsigned int u_offset = 2 * SUBBLOCK_SIZE; - for (i = 0; i < 8; ++i) - { - emit_line(¯oblock[y_offset], ¯oblock[u_offset], address); + for (i = 0; i < 8; ++i) { + emit_line(hle, ¯oblock[y_offset], ¯oblock[u_offset], address); y_offset += 8; u_offset += 8; @@ -389,95 +374,86 @@ static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *m } } -static void EmitTilesMode2(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) +static void EmitTilesMode2(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) { unsigned int i; unsigned int y_offset = 0; - unsigned int u_offset = 4*SUBBLOCK_SIZE; + unsigned int u_offset = 4 * SUBBLOCK_SIZE; - for (i = 0; i < 8; ++i) - { - emit_line(¯oblock[y_offset], ¯oblock[u_offset], address); - emit_line(¯oblock[y_offset + 8], ¯oblock[u_offset], address + 32); + for (i = 0; i < 8; ++i) { + emit_line(hle, ¯oblock[y_offset], ¯oblock[u_offset], address); + emit_line(hle, ¯oblock[y_offset + 8], ¯oblock[u_offset], address + 32); - y_offset += (i == 3) ? SUBBLOCK_SIZE+16 : 16; + y_offset += (i == 3) ? SUBBLOCK_SIZE + 16 : 16; u_offset += 8; address += 64; } } -static void DecodeMacroblock1(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable) +static void decode_macroblock_ob(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable) { int sb; - for (sb = 0; sb < 6; ++sb) - { + for (sb = 0; sb < 6; ++sb) { int16_t tmp_sb[SUBBLOCK_SIZE]; /* update DC */ int32_t dc = (int32_t)macroblock[0]; - switch(sb) - { - case 0: case 1: case 2: case 3: - *y_dc += dc; macroblock[0] = *y_dc & 0xffff; break; - case 4: *u_dc += dc; macroblock[0] = *u_dc & 0xffff; break; - case 5: *v_dc += dc; macroblock[0] = *v_dc & 0xffff; break; + switch (sb) { + case 0: + case 1: + case 2: + case 3: + *y_dc += dc; + macroblock[0] = *y_dc & 0xffff; + break; + case 4: + *u_dc += dc; + macroblock[0] = *u_dc & 0xffff; + break; + case 5: + *v_dc += dc; + macroblock[0] = *v_dc & 0xffff; + break; } ZigZagSubBlock(tmp_sb, macroblock); - if (qtable != NULL) { MultSubBlocks(tmp_sb, tmp_sb, qtable, 0); } + if (qtable != NULL) + MultSubBlocks(tmp_sb, tmp_sb, qtable, 0); TransposeSubBlock(macroblock, tmp_sb); InverseDCTSubBlock(macroblock, macroblock); - + macroblock += SUBBLOCK_SIZE; } } -static void DecodeMacroblock2(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]) +static void decode_macroblock_std(const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + int16_t *macroblock, + unsigned int subblock_count, + const int16_t qtables[3][SUBBLOCK_SIZE]) { unsigned int sb; unsigned int q = 0; - for (sb = 0; sb < subblock_count; ++sb) - { + for (sb = 0; sb < subblock_count; ++sb) { int16_t tmp_sb[SUBBLOCK_SIZE]; const int isChromaSubBlock = (subblock_count - sb <= 2); - if (isChromaSubBlock) { ++q; } - - MultSubBlocks(macroblock, macroblock, qtables[q], 4); - ZigZagSubBlock(tmp_sb, macroblock); - InverseDCTSubBlock(macroblock, tmp_sb); - - macroblock += SUBBLOCK_SIZE; - } - -} - -static void DecodeMacroblock3(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]) -{ - unsigned int sb; - unsigned int q = 0; - - for (sb = 0; sb < subblock_count; ++sb) - { - int16_t tmp_sb[SUBBLOCK_SIZE]; - const int isChromaSubBlock = (subblock_count - sb <= 2); - - if (isChromaSubBlock) { ++q; } - - MultSubBlocks(macroblock, macroblock, qtables[q], 4); - ZigZagSubBlock(tmp_sb, macroblock); - InverseDCTSubBlock(macroblock, tmp_sb); - if (isChromaSubBlock) - { - RescaleUVSubBlock(macroblock, macroblock); - } - else - { - RescaleYSubBlock(macroblock, macroblock); + ++q; + + MultSubBlocks(macroblock, macroblock, qtables[q], 4); + ZigZagSubBlock(tmp_sb, macroblock); + InverseDCTSubBlock(macroblock, tmp_sb); + + if (isChromaSubBlock) { + if (transform_chroma != NULL) + transform_chroma(macroblock, macroblock); + } else { + if (transform_luma != NULL) + transform_luma(macroblock, macroblock); } macroblock += SUBBLOCK_SIZE; @@ -499,20 +475,17 @@ static void ReorderSubBlock(int16_t *dst, const int16_t *src, const unsigned int unsigned int i; /* source and destination sublocks cannot overlap */ - assert(abs(dst - src) > SUBBLOCK_SIZE); + assert(labs(dst - src) > SUBBLOCK_SIZE); for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = src[table[i]]; - } } static void MultSubBlocks(int16_t *dst, const int16_t *src1, const int16_t *src2, unsigned int shift) { unsigned int i; - for (i = 0; i < SUBBLOCK_SIZE; ++i) - { + for (i = 0; i < SUBBLOCK_SIZE; ++i) { int32_t v = src1[i] * src2[i]; dst[i] = clamp_s16(v) << shift; } @@ -522,8 +495,7 @@ static void ScaleSubBlock(int16_t *dst, const int16_t *src, int16_t scale) { unsigned int i; - for (i = 0; i < SUBBLOCK_SIZE; ++i) - { + for (i = 0; i < SUBBLOCK_SIZE; ++i) { int32_t v = src[i] * scale; dst[i] = clamp_s16(v); } @@ -534,9 +506,7 @@ static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift) unsigned int i; for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = src[i] >> shift; - } } /*************************************************************************** @@ -545,7 +515,7 @@ static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift) * Implementation based on Wikipedia : * http://fr.wikipedia.org/wiki/Transform%C3%A9e_en_cosinus_discr%C3%A8te **************************************************************************/ -static void InverseDCT1D(const float * const x, float *dst, unsigned int stride) +static void InverseDCT1D(const float *const x, float *dst, unsigned int stride) { float e[4]; float f[4]; @@ -560,22 +530,29 @@ static void InverseDCT1D(const float * const x, float *dst, unsigned int stride) f[0] = x[0] + x[4]; f[1] = x[0] - x[4]; - f[2] = x26 + IDCT_K[0]*x[2]; - f[3] = x26 + IDCT_K[1]*x[6]; + f[2] = x26 + IDCT_K[0] * x[2]; + f[3] = x26 + IDCT_K[1] * x[6]; - e[0] = x1357 + x15 + IDCT_K[4]*x[1] + x17; - e[1] = x1357 + x37 + IDCT_K[6]*x[3] + x35; - e[2] = x1357 + x15 + IDCT_K[5]*x[5] + x35; - e[3] = x1357 + x37 + IDCT_K[7]*x[7] + x17; + e[0] = x1357 + x15 + IDCT_K[4] * x[1] + x17; + e[1] = x1357 + x37 + IDCT_K[6] * x[3] + x35; + e[2] = x1357 + x15 + IDCT_K[5] * x[5] + x35; + e[3] = x1357 + x37 + IDCT_K[7] * x[7] + x17; - *dst = f[0] + f[2] + e[0]; dst += stride; - *dst = f[1] + f[3] + e[1]; dst += stride; - *dst = f[1] - f[3] + e[2]; dst += stride; - *dst = f[0] - f[2] + e[3]; dst += stride; - *dst = f[0] - f[2] - e[3]; dst += stride; - *dst = f[1] - f[3] - e[2]; dst += stride; - *dst = f[1] + f[3] - e[1]; dst += stride; - *dst = f[0] + f[2] - e[0]; dst += stride; + *dst = f[0] + f[2] + e[0]; + dst += stride; + *dst = f[1] + f[3] + e[1]; + dst += stride; + *dst = f[1] - f[3] + e[2]; + dst += stride; + *dst = f[0] - f[2] + e[3]; + dst += stride; + *dst = f[0] - f[2] - e[3]; + dst += stride; + *dst = f[1] - f[3] - e[2]; + dst += stride; + *dst = f[1] + f[3] - e[1]; + dst += stride; + *dst = f[0] + f[2] - e[0]; } static void InverseDCTSubBlock(int16_t *dst, const int16_t *src) @@ -585,26 +562,20 @@ static void InverseDCTSubBlock(int16_t *dst, const int16_t *src) unsigned int i, j; /* idct 1d on rows (+transposition) */ - for (i = 0; i < 8; ++i) - { + for (i = 0; i < 8; ++i) { for (j = 0; j < 8; ++j) - { - x[j] = (float)src[i*8+j]; - } + x[j] = (float)src[i * 8 + j]; InverseDCT1D(x, &block[i], 8); } /* idct 1d on columns (thanks to previous transposition) */ - for (i = 0; i < 8; ++i) - { - InverseDCT1D(&block[i*8], x, 1); + for (i = 0; i < 8; ++i) { + InverseDCT1D(&block[i * 8], x, 1); /* C4 = 1 normalization implies a division by 8 */ for (j = 0; j < 8; ++j) - { - dst[i+j*8] = (int16_t)x[j] >> 3; - } + dst[i + j * 8] = (int16_t)x[j] >> 3; } } @@ -613,9 +584,7 @@ static void RescaleYSubBlock(int16_t *dst, const int16_t *src) unsigned int i; for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = (((uint32_t)(clamp_s12(src[i]) + 0x800) * 0xdb0) >> 16) + 0x10; - } } static void RescaleUVSubBlock(int16_t *dst, const int16_t *src) @@ -623,61 +592,6 @@ static void RescaleUVSubBlock(int16_t *dst, const int16_t *src) unsigned int i; for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = (((int)clamp_s12(src[i]) * 0xe00) >> 16) + 0x80; - } -} - - - -/* FIXME: assume presence of expansion pack */ -#define MEMMASK 0x7fffff - -static void rdram_read_many_u16(uint16_t *dst, uint32_t address, unsigned int count) -{ - while (count != 0) - { - uint16_t s = rsp.RDRAM[((address++)^S8) & MEMMASK]; - s <<= 8; - s |= rsp.RDRAM[((address++)^S8) & MEMMASK]; - - *(dst++) = s; - - --count; - } -} - -static void rdram_write_many_u16(const uint16_t *src, uint32_t address, unsigned int count) -{ - while (count != 0) - { - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 8); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*(src++) & 0xff); - - --count; - } -} - -static uint32_t rdram_read_u32(uint32_t address) -{ - uint32_t r = rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8; - r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8; - r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8; - r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; - - return r; -} - -static void rdram_write_many_u32(const uint32_t *src, uint32_t address, unsigned int count) -{ - while (count != 0) - { - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 24); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 16); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 8); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*(src++) & 0xff); - - --count; - } } diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/main.c b/libmupen64plus/mupen64plus-rsp-hle/src/main.c deleted file mode 100644 index ff6525aefa..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/main.c +++ /dev/null @@ -1,476 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - main.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2012 Bobby Smiles * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include -#include -#include - -#define M64P_PLUGIN_PROTOTYPES 1 -#include "m64p_types.h" -#include "m64p_common.h" -#include "m64p_plugin.h" -#include "hle.h" -#include "alist.h" -#include "cicx105.h" -#include "jpeg.h" - -#define min(a,b) (((a) < (b)) ? (a) : (b)) - -/* some rsp status flags */ -#define RSP_STATUS_HALT 0x1 -#define RSP_STATUS_BROKE 0x2 -#define RSP_STATUS_INTR_ON_BREAK 0x40 -#define RSP_STATUS_TASKDONE 0x200 - -/* some rdp status flags */ -#define DP_STATUS_FREEZE 0x2 - -/* some mips interface interrupt flags */ -#define MI_INTR_SP 0x1 - - -/* helper functions prototypes */ -static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size); -static void dump_binary(const char * const filename, const unsigned char * const bytes, - unsigned int size); -static void dump_task(const char * const filename, const OSTask_t * const task); - -static void handle_unknown_task(unsigned int sum); -static void handle_unknown_non_task(unsigned int sum); - -/* global variables */ -RSP_INFO rsp; - -/* local variables */ -static const int FORWARD_AUDIO = 0, FORWARD_GFX = 1; -static void (*l_DebugCallback)(void *, int, const char *) = NULL; -static void *l_DebugCallContext = NULL; -static int l_PluginInit = 0; - -/* local functions */ - - -/** - * Try to figure if the RSP was launched using osSpTask* functions - * and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless). - * - * Previously, the ucode_size field was used to determine this, - * but it is not robust enough (hi Pokemon Stadium !) because games could write anything - * in this field : most ucode_boot discard the value and just use 0xf7f anyway. - * - * Using ucode_boot_size should be more robust in this regard. - **/ -static int is_task() -{ - return (get_task()->ucode_boot_size <= 0x1000); -} - -static void rsp_break(unsigned int setbits) -{ - *rsp.SP_STATUS_REG |= setbits | RSP_STATUS_BROKE | RSP_STATUS_HALT; - - if ((*rsp.SP_STATUS_REG & RSP_STATUS_INTR_ON_BREAK)) - { - *rsp.MI_INTR_REG |= MI_INTR_SP; - rsp.CheckInterrupts(); - } -} - -static void forward_gfx_task() -{ - if (rsp.ProcessDlistList != NULL) - { - rsp.ProcessDlistList(); - *rsp.DPC_STATUS_REG &= ~DP_STATUS_FREEZE; - } -} - -static void forward_audio_task() -{ - if (rsp.ProcessAlistList != NULL) - { - rsp.ProcessAlistList(); - } -} - -static void show_cfb() -{ - if (rsp.ShowCFB != NULL) - { - rsp.ShowCFB(); - } -} - -static int try_fast_audio_dispatching() -{ - /* identify audio ucode by using the content of ucode_data */ - const OSTask_t * const task = get_task(); - const unsigned char * const udata_ptr = rsp.RDRAM + task->ucode_data; - - if (*(unsigned int*)(udata_ptr + 0) == 0x00000001) - { - if (*(unsigned int*)(udata_ptr + 0x30) == 0xf0000f00) - { - /** - * Many games including: - * Super Mario 64, Diddy Kong Racing, BlastCorp, GoldenEye, ... (most common) - **/ - alist_process_ABI1(); return 1; - } - else - { - /** - * Mario Kart / Wave Race, - * LylatWars, - * FZeroX, - * Yoshi Story, - * 1080 Snowboarding, - * Zelda Ocarina of Time, - * Zelda Majoras Mask / Pokemon Stadium 2, - * Animal Crossing - * - * FIXME: in fact, all these games do not share the same ABI. - * That's the reason of the workaround in ucode2.cpp with isZeldaABI and isMKABI - **/ - alist_process_ABI2(); return 1; - } - } - else - { - if (*(unsigned int*)(udata_ptr + 0x10) == 0x00000001) - { - /** - * Musyx ucode found in following games: - * RogueSquadron, ResidentEvil2, SnowCrossPolaris, TheWorldIsNotEnough, - * RugratsInParis, NBAShowTime, HydroThunder, Tarzan, - * GauntletLegend, Rush2049, IndianaJones, BattleForNaboo - * TODO: implement ucode - **/ - DebugMessage(M64MSG_WARNING, "MusyX ucode not implemented."); - /* return 1; */ - } - else - { - /** - * Many games including: - * Pokemon Stadium, Banjo Kazooie, Donkey Kong, Banjo Tooie, Jet Force Gemini, - * Mickey SpeedWay USA, Perfect Dark, Conker Bad Fur Day ... - **/ - alist_process_ABI3(); return 1; - } - } - - return 0; -} - -static int try_fast_task_dispatching() -{ - /* identify task ucode by its type */ - const OSTask_t * const task = get_task(); - - switch (task->type) - { - case 1: if (FORWARD_GFX) { forward_gfx_task(); return 1; } break; - - case 2: - if (FORWARD_AUDIO) { forward_audio_task(); return 1; } - else if (try_fast_audio_dispatching()) { return 1; } - break; - - case 7: show_cfb(); return 1; - } - - return 0; -} - -static void normal_task_dispatching() -{ - const OSTask_t * const task = get_task(); - const unsigned int sum = - sum_bytes(rsp.RDRAM + task->ucode, min(task->ucode_size, 0xf80) >> 1); - - switch (sum) - { - /* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */ - case 0x278: /* Nothing to emulate */ return; - - /* GFX: Twintris [misleading task->type == 0] */ - case 0x212ee: - if (FORWARD_GFX) { forward_gfx_task(); return; } - break; - - /* JPEG: found in Pokemon Stadium J */ - case 0x2c85a: jpeg_decode_PS0(); return; - - /* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */ - case 0x2caa6: jpeg_decode_PS(); return; - - /* JPEG: found in Ogre Battle, Bottom of the 9th */ - case 0x130de: jpeg_decode_OB(); return; - } - - handle_unknown_task(sum); -} - -static void non_task_dispatching() -{ - const unsigned int sum = sum_bytes(rsp.IMEM, 0x1000 >> 1); - - switch(sum) - { - /* CIC x105 ucode (used during boot of CIC x105 games) */ - case 0x9e2: /* CIC 6105 */ - case 0x9f2: /* CIC 7105 */ - cicx105_ucode(); return; - } - - handle_unknown_non_task(sum); -} - -static void handle_unknown_task(unsigned int sum) -{ - char filename[256]; - const OSTask_t * const task = get_task(); - - DebugMessage(M64MSG_WARNING, "unknown OSTask: sum %x PC:%x", sum, *rsp.SP_PC_REG); - - sprintf(&filename[0], "task_%x.log", sum); - dump_task(filename, task); - - // dump ucode_boot - sprintf(&filename[0], "ucode_boot_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->ucode_boot & 0x7fffff), task->ucode_boot_size); - - // dump ucode - if (task->ucode != 0) - { - sprintf(&filename[0], "ucode_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->ucode & 0x7fffff), 0xf80); - } - - // dump ucode_data - if (task->ucode_data != 0) - { - sprintf(&filename[0], "ucode_data_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->ucode_data & 0x7fffff), task->ucode_data_size); - } - - // dump data - if (task->data_ptr != 0) - { - sprintf(&filename[0], "data_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->data_ptr & 0x7fffff), task->data_size); - } -} - -static void handle_unknown_non_task(unsigned int sum) -{ - char filename[256]; - - DebugMessage(M64MSG_WARNING, "unknown RSP code: sum: %x PC:%x", sum, *rsp.SP_PC_REG); - - // dump IMEM & DMEM for further analysis - sprintf(&filename[0], "imem_%x.bin", sum); - dump_binary(filename, rsp.IMEM, 0x1000); - - sprintf(&filename[0], "dmem_%x.bin", sum); - dump_binary(filename, rsp.DMEM, 0x1000); -} - - -/* Global functions */ -void DebugMessage(int level, const char *message, ...) -{ - char msgbuf[1024]; - va_list args; - - if (l_DebugCallback == NULL) - return; - - va_start(args, message); - vsprintf(msgbuf, message, args); - - (*l_DebugCallback)(l_DebugCallContext, level, msgbuf); - - va_end(args); -} - -/* DLL-exported functions */ -EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context, - void (*DebugCallback)(void *, int, const char *)) -{ - if (l_PluginInit) - return M64ERR_ALREADY_INIT; - - /* first thing is to set the callback function for debug info */ - l_DebugCallback = DebugCallback; - l_DebugCallContext = Context; - - /* this plugin doesn't use any Core library functions (ex for Configuration), so no need to keep the CoreLibHandle */ - - l_PluginInit = 1; - return M64ERR_SUCCESS; -} - -EXPORT m64p_error CALL PluginShutdown(void) -{ - if (!l_PluginInit) - return M64ERR_NOT_INIT; - - /* reset some local variable */ - l_DebugCallback = NULL; - l_DebugCallContext = NULL; - - l_PluginInit = 0; - return M64ERR_SUCCESS; -} - -EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities) -{ - /* set version info */ - if (PluginType != NULL) - *PluginType = M64PLUGIN_RSP; - - if (PluginVersion != NULL) - *PluginVersion = RSP_HLE_VERSION; - - if (APIVersion != NULL) - *APIVersion = RSP_PLUGIN_API_VERSION; - - if (PluginNamePtr != NULL) - *PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin"; - - if (Capabilities != NULL) - { - *Capabilities = 0; - } - - return M64ERR_SUCCESS; -} - -EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles) -{ - if (is_task()) - { - if (!try_fast_task_dispatching()) { normal_task_dispatching(); } - rsp_break(RSP_STATUS_TASKDONE); - } - else - { - non_task_dispatching(); - rsp_break(0); - } - - return Cycles; -} - -EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, unsigned int *CycleCount) -{ - rsp = Rsp_Info; -} - -EXPORT void CALL RomClosed(void) -{ - memset(rsp.DMEM, 0, 0x1000); - memset(rsp.IMEM, 0, 0x1000); - - init_ucode2(); -} - - -/* local helper functions */ -static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size) -{ - unsigned int sum = 0; - const unsigned char * const bytes_end = bytes + size; - - while (bytes != bytes_end) - sum += *bytes++; - - return sum; -} - - -static void dump_binary(const char * const filename, const unsigned char * const bytes, - unsigned int size) -{ - FILE *f; - - // if file already exists, do nothing - f = fopen(filename, "r"); - if (f == NULL) - { - // else we write bytes to the file - f= fopen(filename, "wb"); - if (f != NULL) { - if (fwrite(bytes, 1, size, f) != size) - { - DebugMessage(M64MSG_ERROR, "Writing error on %s", filename); - } - fclose(f); - } - else - { - DebugMessage(M64MSG_ERROR, "Couldn't open %s for writing !", filename); - } - } - else - { - fclose(f); - } -} - -static void dump_task(const char * const filename, const OSTask_t * const task) -{ - FILE *f; - - f = fopen(filename, "r"); - if (f == NULL) - { - f = fopen(filename, "w"); - fprintf(f, - "type = %d\n" - "flags = %d\n" - "ucode_boot = %#08x size = %#x\n" - "ucode = %#08x size = %#x\n" - "ucode_data = %#08x size = %#x\n" - "dram_stack = %#08x size = %#x\n" - "output_buff = %#08x *size = %#x\n" - "data = %#08x size = %#x\n" - "yield_data = %#08x size = %#x\n", - task->type, task->flags, - task->ucode_boot, task->ucode_boot_size, - task->ucode, task->ucode_size, - task->ucode_data, task->ucode_data_size, - task->dram_stack, task->dram_stack_size, - task->output_buff, task->output_buff_size, - task->data_ptr, task->data_size, - task->yield_data_ptr, task->yield_data_size); - fclose(f); - } - else - { - fclose(f); - } -} - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/memory.c b/libmupen64plus/mupen64plus-rsp-hle/src/memory.c new file mode 100644 index 0000000000..7d5066bfa1 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/memory.c @@ -0,0 +1,74 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - memory.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include + +#include "memory.h" + +/* Global functions */ +void load_u8(uint8_t* dst, const unsigned char* buffer, unsigned address, size_t count) +{ + while (count != 0) { + *(dst++) = *u8(buffer, address); + address += 1; + --count; + } +} + +void load_u16(uint16_t* dst, const unsigned char* buffer, unsigned address, size_t count) +{ + while (count != 0) { + *(dst++) = *u16(buffer, address); + address += 2; + --count; + } +} + +void load_u32(uint32_t* dst, const unsigned char* buffer, unsigned address, size_t count) +{ + /* Optimization for uint32_t */ + memcpy(dst, u32(buffer, address), count * sizeof(uint32_t)); +} + +void store_u8(unsigned char* buffer, unsigned address, const uint8_t* src, size_t count) +{ + while (count != 0) { + *u8(buffer, address) = *(src++); + address += 1; + --count; + } +} + +void store_u16(unsigned char* buffer, unsigned address, const uint16_t* src, size_t count) +{ + while (count != 0) { + *u16(buffer, address) = *(src++); + address += 2; + --count; + } +} + +void store_u32(unsigned char* buffer, unsigned address, const uint32_t* src, size_t count) +{ + /* Optimization for uint32_t */ + memcpy(u32(buffer, address), src, count * sizeof(uint32_t)); +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/memory.h b/libmupen64plus/mupen64plus-rsp-hle/src/memory.h new file mode 100644 index 0000000000..9c85104838 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/memory.h @@ -0,0 +1,185 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - memory.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include +#include + +#include "common.h" +#include "hle_internal.h" + +#ifdef M64P_BIG_ENDIAN +#define S 0 +#define S16 0 +#define S8 0 +#else +#define S 1 +#define S16 2 +#define S8 3 +#endif + +enum { + TASK_TYPE = 0xfc0, + TASK_FLAGS = 0xfc4, + TASK_UCODE_BOOT = 0xfc8, + TASK_UCODE_BOOT_SIZE = 0xfcc, + TASK_UCODE = 0xfd0, + TASK_UCODE_SIZE = 0xfd4, + TASK_UCODE_DATA = 0xfd8, + TASK_UCODE_DATA_SIZE = 0xfdc, + TASK_DRAM_STACK = 0xfe0, + TASK_DRAM_STACK_SIZE = 0xfe4, + TASK_OUTPUT_BUFF = 0xfe8, + TASK_OUTPUT_BUFF_SIZE = 0xfec, + TASK_DATA_PTR = 0xff0, + TASK_DATA_SIZE = 0xff4, + TASK_YIELD_DATA_PTR = 0xff8, + TASK_YIELD_DATA_SIZE = 0xffc +}; + +static inline unsigned int align(unsigned int x, unsigned amount) +{ + --amount; + return (x + amount) & ~amount; +} + +static inline uint8_t* u8(const unsigned char* buffer, unsigned address) +{ + return (uint8_t*)(buffer + (address ^ S8)); +} + +static inline uint16_t* u16(const unsigned char* buffer, unsigned address) +{ + assert((address & 1) == 0); + return (uint16_t*)(buffer + (address ^ S16)); +} + +static inline uint32_t* u32(const unsigned char* buffer, unsigned address) +{ + assert((address & 3) == 0); + return (uint32_t*)(buffer + address); +} + +void load_u8 (uint8_t* dst, const unsigned char* buffer, unsigned address, size_t count); +void load_u16(uint16_t* dst, const unsigned char* buffer, unsigned address, size_t count); +void load_u32(uint32_t* dst, const unsigned char* buffer, unsigned address, size_t count); +void store_u8 (unsigned char* buffer, unsigned address, const uint8_t* src, size_t count); +void store_u16(unsigned char* buffer, unsigned address, const uint16_t* src, size_t count); +void store_u32(unsigned char* buffer, unsigned address, const uint32_t* src, size_t count); + + +/* convenient functions for DMEM access */ +static inline uint8_t* dmem_u8(struct hle_t* hle, uint16_t address) +{ + return u8(hle->dmem, address & 0xfff); +} + +static inline uint16_t* dmem_u16(struct hle_t* hle, uint16_t address) +{ + return u16(hle->dmem, address & 0xfff); +} + +static inline uint32_t* dmem_u32(struct hle_t* hle, uint16_t address) +{ + return u32(hle->dmem, address & 0xfff); +} + +static inline void dmem_load_u8(struct hle_t* hle, uint8_t* dst, uint16_t address, size_t count) +{ + load_u8(dst, hle->dmem, address & 0xfff, count); +} + +static inline void dmem_load_u16(struct hle_t* hle, uint16_t* dst, uint16_t address, size_t count) +{ + load_u16(dst, hle->dmem, address & 0xfff, count); +} + +static inline void dmem_load_u32(struct hle_t* hle, uint32_t* dst, uint16_t address, size_t count) +{ + load_u32(dst, hle->dmem, address & 0xfff, count); +} + +static inline void dmem_store_u8(struct hle_t* hle, const uint8_t* src, uint16_t address, size_t count) +{ + store_u8(hle->dmem, address & 0xfff, src, count); +} + +static inline void dmem_store_u16(struct hle_t* hle, const uint16_t* src, uint16_t address, size_t count) +{ + store_u16(hle->dmem, address & 0xfff, src, count); +} + +static inline void dmem_store_u32(struct hle_t* hle, const uint32_t* src, uint16_t address, size_t count) +{ + store_u32(hle->dmem, address & 0xfff, src, count); +} + +/* convenient functions DRAM access */ +static inline uint8_t* dram_u8(struct hle_t* hle, uint32_t address) +{ + return u8(hle->dram, address & 0xffffff); +} + +static inline uint16_t* dram_u16(struct hle_t* hle, uint32_t address) +{ + return u16(hle->dram, address & 0xffffff); +} + +static inline uint32_t* dram_u32(struct hle_t* hle, uint32_t address) +{ + return u32(hle->dram, address & 0xffffff); +} + +static inline void dram_load_u8(struct hle_t* hle, uint8_t* dst, uint32_t address, size_t count) +{ + load_u8(dst, hle->dram, address & 0xffffff, count); +} + +static inline void dram_load_u16(struct hle_t* hle, uint16_t* dst, uint32_t address, size_t count) +{ + load_u16(dst, hle->dram, address & 0xffffff, count); +} + +static inline void dram_load_u32(struct hle_t* hle, uint32_t* dst, uint32_t address, size_t count) +{ + load_u32(dst, hle->dram, address & 0xffffff, count); +} + +static inline void dram_store_u8(struct hle_t* hle, const uint8_t* src, uint32_t address, size_t count) +{ + store_u8(hle->dram, address & 0xffffff, src, count); +} + +static inline void dram_store_u16(struct hle_t* hle, const uint16_t* src, uint32_t address, size_t count) +{ + store_u16(hle->dram, address & 0xffffff, src, count); +} + +static inline void dram_store_u32(struct hle_t* hle, const uint32_t* src, uint32_t address, size_t count) +{ + store_u32(hle->dram, address & 0xffffff, src, count); +} + +#endif + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/mp3.c b/libmupen64plus/mupen64plus-rsp-hle/src/mp3.c new file mode 100644 index 0000000000..4e8ea14afd --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/mp3.c @@ -0,0 +1,684 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - mp3.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "arithmetics.h" +#include "hle_internal.h" +#include "memory.h" + +static void InnerLoop(struct hle_t* hle, + uint32_t outPtr, uint32_t inPtr, + uint32_t t6, uint32_t t5, uint32_t t4); + +static const uint16_t DeWindowLUT [0x420] = { + 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, + 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, + 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, + 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, + 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, + 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, + 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, + 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, + 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, + 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, + 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, + 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, + 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, + 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, + 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, + 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, + 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, + 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, + 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, + 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, + 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, + 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, + 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, + 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, + 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, + 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, + 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, + 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, + 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, + 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, + 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, + 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, + 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, + 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, + 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, + 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, + 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, + 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, + 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, + 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, + 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, + 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, + 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, + 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, + 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, + 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, + 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, + 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, + 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, + 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, + 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, + 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, + 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, + 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, + 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, + 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, + 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, + 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, + 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, + 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, + 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, + 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, + 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, + 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, + 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, + 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, + 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, + 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, + 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, + 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, + 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, + 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, + 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, + 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, + 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, + 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, + 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, + 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, + 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, + 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, + 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, + 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0003, + 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, + 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0004, + 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, + 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, + 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, + 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, + 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, + 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0004, + 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, + 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0005, + 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, + 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0005, + 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, + 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0006, + 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, + 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, + 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, + 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, + 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, + 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0006, + 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, + 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0007, + 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, + 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, + 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, + 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, + 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, + 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0007, + 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, + 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0008, + 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, + 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0008, + 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, + 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0009, + 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, + 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x0009, + 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, + 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x000A, + 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, + 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000A, + 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, + 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000B, + 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, + 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000B, + 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, + 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000D, + 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, + 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x000D, + 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, + 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x0000 +}; + +static void MP3AB0(int32_t* v) +{ + /* Part 2 - 100% Accurate */ + static const uint16_t LUT2[8] = { + 0xFEC4, 0xF4FA, 0xC5E4, 0xE1C4, + 0x1916, 0x4A50, 0xA268, 0x78AE + }; + static const uint16_t LUT3[4] = { 0xFB14, 0xD4DC, 0x31F2, 0x8E3A }; + int i; + + for (i = 0; i < 8; i++) { + v[16 + i] = v[0 + i] + v[8 + i]; + v[24 + i] = ((v[0 + i] - v[8 + i]) * LUT2[i]) >> 0x10; + } + + /* Part 3: 4-wide butterflies */ + + for (i = 0; i < 4; i++) { + v[0 + i] = v[16 + i] + v[20 + i]; + v[4 + i] = ((v[16 + i] - v[20 + i]) * LUT3[i]) >> 0x10; + + v[8 + i] = v[24 + i] + v[28 + i]; + v[12 + i] = ((v[24 + i] - v[28 + i]) * LUT3[i]) >> 0x10; + } + + /* Part 4: 2-wide butterflies - 100% Accurate */ + + for (i = 0; i < 16; i += 4) { + v[16 + i] = v[0 + i] + v[2 + i]; + v[18 + i] = ((v[0 + i] - v[2 + i]) * 0xEC84) >> 0x10; + + v[17 + i] = v[1 + i] + v[3 + i]; + v[19 + i] = ((v[1 + i] - v[3 + i]) * 0x61F8) >> 0x10; + } +} + +void mp3_task(struct hle_t* hle, unsigned int index, uint32_t address) +{ + uint32_t inPtr, outPtr; + uint32_t t6;/* = 0x08A0; - I think these are temporary storage buffers */ + uint32_t t5;/* = 0x0AC0; */ + uint32_t t4;/* = (w1 & 0x1E); */ + + /* Initialization Code */ + uint32_t readPtr; /* s5 */ + uint32_t writePtr; /* s6 */ + uint32_t tmp; + int cnt, cnt2; + + /* I think these are temporary storage buffers */ + t6 = 0x08A0; + t5 = 0x0AC0; + t4 = index; + + writePtr = readPtr = address; + /* Just do that for efficiency... may remove and use directly later anyway */ + memcpy(hle->mp3_buffer + 0xCE8, hle->dram + readPtr, 8); + /* This must be a header byte or whatnot */ + readPtr += 8; + + for (cnt = 0; cnt < 0x480; cnt += 0x180) { + /* DMA: 0xCF0 <- RDRAM[s5] : 0x180 */ + memcpy(hle->mp3_buffer + 0xCF0, hle->dram + readPtr, 0x180); + inPtr = 0xCF0; /* s7 */ + outPtr = 0xE70; /* s3 */ +/* --------------- Inner Loop Start -------------------- */ + for (cnt2 = 0; cnt2 < 0x180; cnt2 += 0x40) { + t6 &= 0xFFE0; + t5 &= 0xFFE0; + t6 |= t4; + t5 |= t4; + InnerLoop(hle, outPtr, inPtr, t6, t5, t4); + t4 = (t4 - 2) & 0x1E; + tmp = t6; + t6 = t5; + t5 = tmp; + inPtr += 0x40; + outPtr += 0x40; + } +/* --------------- Inner Loop End -------------------- */ + memcpy(hle->dram + writePtr, hle->mp3_buffer + 0xe70, 0x180); + writePtr += 0x180; + readPtr += 0x180; + } +} + +static void InnerLoop(struct hle_t* hle, + uint32_t outPtr, uint32_t inPtr, + uint32_t t6, uint32_t t5, uint32_t t4) +{ + /* Part 1: 100% Accurate */ + + /* 0, 1, 3, 2, 7, 6, 4, 5, 7, 6, 4, 5, 0, 1, 3, 2 */ + static const uint16_t LUT6[16] = { + 0xFFB2, 0xFD3A, 0xF10A, 0xF854, + 0xBDAE, 0xCDA0, 0xE76C, 0xDB94, + 0x1920, 0x4B20, 0xAC7C, 0x7C68, + 0xABEC, 0x9880, 0xDAE8, 0x839C + }; + int i; + uint32_t t0; + uint32_t t1; + uint32_t t2; + uint32_t t3; + int32_t v2 = 0, v4 = 0, v6 = 0, v8 = 0; + uint32_t offset; + uint32_t addptr; + int x; + int32_t mult6; + int32_t mult4; + int tmp; + int32_t hi0; + int32_t hi1; + int32_t vt; + int32_t v[32]; + + v[0] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x00 ^ S16)); + v[31] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3E ^ S16)); + v[0] += v[31]; + v[1] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x02 ^ S16)); + v[30] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3C ^ S16)); + v[1] += v[30]; + v[2] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x06 ^ S16)); + v[28] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x38 ^ S16)); + v[2] += v[28]; + v[3] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x04 ^ S16)); + v[29] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3A ^ S16)); + v[3] += v[29]; + + v[4] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0E ^ S16)); + v[24] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x30 ^ S16)); + v[4] += v[24]; + v[5] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0C ^ S16)); + v[25] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x32 ^ S16)); + v[5] += v[25]; + v[6] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x08 ^ S16)); + v[27] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x36 ^ S16)); + v[6] += v[27]; + v[7] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0A ^ S16)); + v[26] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x34 ^ S16)); + v[7] += v[26]; + + v[8] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1E ^ S16)); + v[16] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x20 ^ S16)); + v[8] += v[16]; + v[9] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1C ^ S16)); + v[17] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x22 ^ S16)); + v[9] += v[17]; + v[10] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x18 ^ S16)); + v[19] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x26 ^ S16)); + v[10] += v[19]; + v[11] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1A ^ S16)); + v[18] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x24 ^ S16)); + v[11] += v[18]; + + v[12] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x10 ^ S16)); + v[23] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2E ^ S16)); + v[12] += v[23]; + v[13] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x12 ^ S16)); + v[22] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2C ^ S16)); + v[13] += v[22]; + v[14] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x16 ^ S16)); + v[20] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x28 ^ S16)); + v[14] += v[20]; + v[15] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x14 ^ S16)); + v[21] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2A ^ S16)); + v[15] += v[21]; + + /* Part 2-4 */ + + MP3AB0(v); + + /* Part 5 - 1-Wide Butterflies - 100% Accurate but need SSVs!!! */ + + t0 = t6 + 0x100; + t1 = t6 + 0x200; + t2 = t5 + 0x100; + t3 = t5 + 0x200; + + /* 0x13A8 */ + v[1] = 0; + v[11] = ((v[16] - v[17]) * 0xB504) >> 0x10; + + v[16] = -v[16] - v[17]; + v[2] = v[18] + v[19]; + /* ** Store v[11] -> (T6 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x0))) = (short)v[11]; + + + v[11] = -v[11]; + /* ** Store v[16] -> (T3 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0x0))) = (short)v[16]; + /* ** Store v[11] -> (T5 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x0))) = (short)v[11]; + /* 0x13E8 - Verified.... */ + v[2] = -v[2]; + /* ** Store v[2] -> (T2 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x0))) = (short)v[2]; + v[3] = (((v[18] - v[19]) * 0x16A09) >> 0x10) + v[2]; + /* ** Store v[3] -> (T0 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x0))) = (short)v[3]; + /* 0x1400 - Verified */ + v[4] = -v[20] - v[21]; + v[6] = v[22] + v[23]; + v[5] = ((v[20] - v[21]) * 0x16A09) >> 0x10; + /* ** Store v[4] -> (T3 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFF80))) = (short)v[4]; + v[7] = ((v[22] - v[23]) * 0x2D413) >> 0x10; + v[5] = v[5] - v[4]; + v[7] = v[7] - v[5]; + v[6] = v[6] + v[6]; + v[5] = v[5] - v[6]; + v[4] = -v[4] - v[6]; + /* *** Store v[7] -> (T1 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFF80))) = (short)v[7]; + /* *** Store v[4] -> (T2 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFF80))) = (short)v[4]; + /* *** Store v[5] -> (T0 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFF80))) = (short)v[5]; + v[8] = v[24] + v[25]; + + + v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; + v[2] = v[8] + v[9]; + v[11] = ((v[26] - v[27]) * 0x2D413) >> 0x10; + v[13] = ((v[28] - v[29]) * 0x2D413) >> 0x10; + + v[10] = v[26] + v[27]; + v[10] = v[10] + v[10]; + v[12] = v[28] + v[29]; + v[12] = v[12] + v[12]; + v[14] = v[30] + v[31]; + v[3] = v[8] + v[10]; + v[14] = v[14] + v[14]; + v[13] = (v[13] - v[2]) + v[12]; + v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - (v[11] + v[2]); + v[14] = -(v[14] + v[14]) + v[3]; + v[17] = v[13] - v[10]; + v[9] = v[9] + v[14]; + /* ** Store v[9] -> (T6 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x40))) = (short)v[9]; + v[11] = v[11] - v[13]; + /* ** Store v[17] -> (T0 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFFC0))) = (short)v[17]; + v[12] = v[8] - v[12]; + /* ** Store v[11] -> (T0 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x40))) = (short)v[11]; + v[8] = -v[8]; + /* ** Store v[15] -> (T1 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFFC0))) = (short)v[15]; + v[10] = -v[10] - v[12]; + /* ** Store v[12] -> (T2 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x40))) = (short)v[12]; + /* ** Store v[8] -> (T3 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFFC0))) = (short)v[8]; + /* ** Store v[14] -> (T5 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x40))) = (short)v[14]; + /* ** Store v[10] -> (T2 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFFC0))) = (short)v[10]; + /* 0x14FC - Verified... */ + + /* Part 6 - 100% Accurate */ + + v[0] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x00 ^ S16)); + v[31] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3E ^ S16)); + v[0] -= v[31]; + v[1] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x02 ^ S16)); + v[30] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3C ^ S16)); + v[1] -= v[30]; + v[2] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x06 ^ S16)); + v[28] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x38 ^ S16)); + v[2] -= v[28]; + v[3] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x04 ^ S16)); + v[29] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3A ^ S16)); + v[3] -= v[29]; + + v[4] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0E ^ S16)); + v[24] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x30 ^ S16)); + v[4] -= v[24]; + v[5] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0C ^ S16)); + v[25] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x32 ^ S16)); + v[5] -= v[25]; + v[6] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x08 ^ S16)); + v[27] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x36 ^ S16)); + v[6] -= v[27]; + v[7] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0A ^ S16)); + v[26] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x34 ^ S16)); + v[7] -= v[26]; + + v[8] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1E ^ S16)); + v[16] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x20 ^ S16)); + v[8] -= v[16]; + v[9] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1C ^ S16)); + v[17] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x22 ^ S16)); + v[9] -= v[17]; + v[10] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x18 ^ S16)); + v[19] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x26 ^ S16)); + v[10] -= v[19]; + v[11] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1A ^ S16)); + v[18] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x24 ^ S16)); + v[11] -= v[18]; + + v[12] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x10 ^ S16)); + v[23] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2E ^ S16)); + v[12] -= v[23]; + v[13] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x12 ^ S16)); + v[22] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2C ^ S16)); + v[13] -= v[22]; + v[14] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x16 ^ S16)); + v[20] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x28 ^ S16)); + v[14] -= v[20]; + v[15] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x14 ^ S16)); + v[21] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2A ^ S16)); + v[15] -= v[21]; + + for (i = 0; i < 16; i++) + v[0 + i] = (v[0 + i] * LUT6[i]) >> 0x10; + v[0] = v[0] + v[0]; + v[1] = v[1] + v[1]; + v[2] = v[2] + v[2]; + v[3] = v[3] + v[3]; + v[4] = v[4] + v[4]; + v[5] = v[5] + v[5]; + v[6] = v[6] + v[6]; + v[7] = v[7] + v[7]; + v[12] = v[12] + v[12]; + v[13] = v[13] + v[13]; + v[15] = v[15] + v[15]; + + MP3AB0(v); + + /* Part 7: - 100% Accurate + SSV - Unoptimized */ + + v[0] = (v[17] + v[16]) >> 1; + v[1] = ((v[17] * (int)((short)0xA57E * 2)) + (v[16] * 0xB504)) >> 0x10; + v[2] = -v[18] - v[19]; + v[3] = ((v[18] - v[19]) * 0x16A09) >> 0x10; + v[4] = v[20] + v[21] + v[0]; + v[5] = (((v[20] - v[21]) * 0x16A09) >> 0x10) + v[1]; + v[6] = (((v[22] + v[23]) << 1) + v[0]) - v[2]; + v[7] = (((v[22] - v[23]) * 0x2D413) >> 0x10) + v[0] + v[1] + v[3]; + /* 0x16A8 */ + /* Save v[0] -> (T3 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFFE0))) = (short) - v[0]; + v[8] = v[24] + v[25]; + v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; + v[10] = ((v[26] + v[27]) << 1) + v[8]; + v[11] = (((v[26] - v[27]) * 0x2D413) >> 0x10) + v[8] + v[9]; + v[12] = v[4] - ((v[28] + v[29]) << 1); + /* ** Store v12 -> (T2 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x20))) = (short)v[12]; + v[13] = (((v[28] - v[29]) * 0x2D413) >> 0x10) - v[12] - v[5]; + v[14] = v[30] + v[31]; + v[14] = v[14] + v[14]; + v[14] = v[14] + v[14]; + v[14] = v[6] - v[14]; + v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - v[7]; + /* Store v14 -> (T5 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x20))) = (short)v[14]; + v[14] = v[14] + v[1]; + /* Store v[14] -> (T6 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x20))) = (short)v[14]; + /* Store v[15] -> (T1 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFFE0))) = (short)v[15]; + v[9] = v[9] + v[10]; + v[1] = v[1] + v[6]; + v[6] = v[10] - v[6]; + v[1] = v[9] - v[1]; + /* Store v[6] -> (T5 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x60))) = (short)v[6]; + v[10] = v[10] + v[2]; + v[10] = v[4] - v[10]; + /* Store v[10] -> (T2 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFFA0))) = (short)v[10]; + v[12] = v[2] - v[12]; + /* Store v[12] -> (T2 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFFE0))) = (short)v[12]; + v[5] = v[4] + v[5]; + v[4] = v[8] - v[4]; + /* Store v[4] -> (T2 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x60))) = (short)v[4]; + v[0] = v[0] - v[8]; + /* Store v[0] -> (T3 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFFA0))) = (short)v[0]; + v[7] = v[7] - v[11]; + /* Store v[7] -> (T1 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFFA0))) = (short)v[7]; + v[11] = v[11] - v[3]; + /* Store v[1] -> (T6 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x60))) = (short)v[1]; + v[11] = v[11] - v[5]; + /* Store v[11] -> (T0 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x60))) = (short)v[11]; + v[3] = v[3] - v[13]; + /* Store v[3] -> (T0 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x20))) = (short)v[3]; + v[13] = v[13] + v[2]; + /* Store v[13] -> (T0 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFFE0))) = (short)v[13]; + v[2] = (v[5] - v[2]) - v[9]; + /* Store v[2] -> (T0 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFFA0))) = (short)v[2]; + /* 0x7A8 - Verified... */ + + /* Step 8 - Dewindowing */ + + addptr = t6 & 0xFFE0; + + offset = 0x10 - (t4 >> 1); + for (x = 0; x < 8; x++) { + int32_t v0; + int32_t v18; + v2 = v4 = v6 = v8 = 0; + + for (i = 7; i >= 0; i--) { + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + v6 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x20) * (short)DeWindowLUT[offset + 0x20] + 0x4000) >> 0xF; + v8 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x30) * (short)DeWindowLUT[offset + 0x28] + 0x4000) >> 0xF; + addptr += 2; + offset++; + } + v0 = v2 + v4; + v18 = v6 + v8; + /* Clamp(v0); */ + /* Clamp(v18); */ + /* clamp??? */ + *(int16_t *)(hle->mp3_buffer + (outPtr ^ S16)) = v0; + *(int16_t *)(hle->mp3_buffer + ((outPtr + 2)^S16)) = v18; + outPtr += 4; + addptr += 0x30; + offset += 0x38; + } + + offset = 0x10 - (t4 >> 1) + 8 * 0x40; + v2 = v4 = 0; + for (i = 0; i < 4; i++) { + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + addptr += 2; + offset++; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + addptr += 2; + offset++; + } + mult6 = *(int32_t *)(hle->mp3_buffer + 0xCE8); + mult4 = *(int32_t *)(hle->mp3_buffer + 0xCEC); + if (t4 & 0x2) { + v2 = (v2 **(uint32_t *)(hle->mp3_buffer + 0xCE8)) >> 0x10; + *(int16_t *)(hle->mp3_buffer + (outPtr ^ S16)) = v2; + } else { + v4 = (v4 **(uint32_t *)(hle->mp3_buffer + 0xCE8)) >> 0x10; + *(int16_t *)(hle->mp3_buffer + (outPtr ^ S16)) = v4; + mult4 = *(uint32_t *)(hle->mp3_buffer + 0xCE8); + } + addptr -= 0x50; + + for (x = 0; x < 8; x++) { + int32_t v0; + int32_t v18; + v2 = v4 = v6 = v8 = 0; + + offset = (0x22F - (t4 >> 1) + x * 0x40); + + for (i = 0; i < 4; i++) { + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x20) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v2 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x20) * (short)DeWindowLUT[offset + 0x01] + 0x4000) >> 0xF; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x30) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + v4 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x30) * (short)DeWindowLUT[offset + 0x09] + 0x4000) >> 0xF; + v6 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x20] + 0x4000) >> 0xF; + v6 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x00) * (short)DeWindowLUT[offset + 0x21] + 0x4000) >> 0xF; + v8 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x28] + 0x4000) >> 0xF; + v8 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x10) * (short)DeWindowLUT[offset + 0x29] + 0x4000) >> 0xF; + addptr += 4; + offset += 2; + } + v0 = v2 + v4; + v18 = v6 + v8; + /* Clamp(v0); */ + /* Clamp(v18); */ + /* clamp??? */ + *(int16_t *)(hle->mp3_buffer + ((outPtr + 2)^S16)) = v0; + *(int16_t *)(hle->mp3_buffer + ((outPtr + 4)^S16)) = v18; + outPtr += 4; + addptr -= 0x50; + } + + tmp = outPtr; + hi0 = mult6; + hi1 = mult4; + + hi0 = (int)hi0 >> 0x10; + hi1 = (int)hi1 >> 0x10; + for (i = 0; i < 8; i++) { + /* v0 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x40)^S16)) * hi0); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x40)^S16)) = clamp_s16(vt); + + /* v17 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x30)^S16)) * hi0); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x30)^S16)) = clamp_s16(vt); + + /* v2 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x1E)^S16)) * hi1); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x1E)^S16)) = clamp_s16(vt); + + /* v4 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0xE)^S16)) * hi1); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0xE)^S16)) = clamp_s16(vt); + + tmp += 2; + } +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/musyx.c b/libmupen64plus/mupen64plus-rsp-hle/src/musyx.c new file mode 100644 index 0000000000..d5a630a033 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/musyx.c @@ -0,0 +1,988 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - musyx.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2013 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include +#include + +#include "arithmetics.h" +#include "audio.h" +#include "common.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" + +/* various constants */ +enum { SUBFRAME_SIZE = 192 }; +enum { MAX_VOICES = 32 }; + +enum { SAMPLE_BUFFER_SIZE = 0x200 }; + + +enum { + SFD_VOICE_COUNT = 0x0, + SFD_SFX_INDEX = 0x2, + SFD_VOICE_BITMASK = 0x4, + SFD_STATE_PTR = 0x8, + SFD_SFX_PTR = 0xc, + SFD_VOICES = 0x10, + + /* v2 only */ + SFD2_10_PTR = 0x10, + SFD2_14_BITMASK = 0x14, + SFD2_15_BITMASK = 0x15, + SFD2_16_BITMASK = 0x16, + SFD2_18_PTR = 0x18, + SFD2_1C_PTR = 0x1c, + SFD2_20_PTR = 0x20, + SFD2_24_PTR = 0x24, + SFD2_VOICES = 0x28 +}; + +enum { + VOICE_ENV_BEGIN = 0x00, + VOICE_ENV_STEP = 0x10, + VOICE_PITCH_Q16 = 0x20, + VOICE_PITCH_SHIFT = 0x22, + VOICE_CATSRC_0 = 0x24, + VOICE_CATSRC_1 = 0x30, + VOICE_ADPCM_FRAMES = 0x3c, + VOICE_SKIP_SAMPLES = 0x3e, + + /* for PCM16 */ + VOICE_U16_40 = 0x40, + VOICE_U16_42 = 0x42, + + /* for ADPCM */ + VOICE_ADPCM_TABLE_PTR = 0x40, + + VOICE_INTERLEAVED_PTR = 0x44, + VOICE_END_POINT = 0x48, + VOICE_RESTART_POINT = 0x4a, + VOICE_U16_4C = 0x4c, + VOICE_U16_4E = 0x4e, + + VOICE_SIZE = 0x50 +}; + +enum { + CATSRC_PTR1 = 0x00, + CATSRC_PTR2 = 0x04, + CATSRC_SIZE1 = 0x08, + CATSRC_SIZE2 = 0x0a +}; + +enum { + STATE_LAST_SAMPLE = 0x0, + STATE_BASE_VOL = 0x100, + STATE_CC0 = 0x110, + STATE_740_LAST4_V1 = 0x290, + + STATE_740_LAST4_V2 = 0x110 +}; + +enum { + SFX_CBUFFER_PTR = 0x00, + SFX_CBUFFER_LENGTH = 0x04, + SFX_TAP_COUNT = 0x08, + SFX_FIR4_HGAIN = 0x0a, + SFX_TAP_DELAYS = 0x0c, + SFX_TAP_GAINS = 0x2c, + SFX_U16_3C = 0x3c, + SFX_U16_3E = 0x3e, + SFX_FIR4_HCOEFFS = 0x40 +}; + + +/* struct definition */ +typedef struct { + /* internal subframes */ + int16_t left[SUBFRAME_SIZE]; + int16_t right[SUBFRAME_SIZE]; + int16_t cc0[SUBFRAME_SIZE]; + int16_t e50[SUBFRAME_SIZE]; + + /* internal subframes base volumes */ + int32_t base_vol[4]; + + /* */ + int16_t subframe_740_last4[4]; +} musyx_t; + +typedef void (*mix_sfx_with_main_subframes_t)(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains); + +/* helper functions prototypes */ +static void load_base_vol(struct hle_t* hle, int32_t *base_vol, uint32_t address); +static void save_base_vol(struct hle_t* hle, const int32_t *base_vol, uint32_t address); +static void update_base_vol(struct hle_t* hle, int32_t *base_vol, + uint32_t voice_mask, uint32_t last_sample_ptr, + uint8_t mask_15, uint32_t ptr_24); + +static void init_subframes_v1(musyx_t *musyx); +static void init_subframes_v2(musyx_t *musyx); + +static uint32_t voice_stage(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, uint32_t last_sample_ptr); + +static void dma_cat8(struct hle_t* hle, uint8_t *dst, uint32_t catsrc_ptr); +static void dma_cat16(struct hle_t* hle, uint16_t *dst, uint32_t catsrc_ptr); + +static void load_samples_PCM16(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset); +static void load_samples_ADPCM(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset); + +static void adpcm_decode_frames(struct hle_t* hle, + int16_t *dst, const uint8_t *src, + const int16_t *table, uint8_t count, + uint8_t skip_samples); + +static void adpcm_predict_frame(int16_t *dst, const uint8_t *src, + const uint8_t *nibbles, + unsigned int rshift); + +static void mix_voice_samples(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, const int16_t *samples, + unsigned segbase, unsigned offset, uint32_t last_sample_ptr); + +static void sfx_stage(struct hle_t* hle, + mix_sfx_with_main_subframes_t mix_sfx_with_main_subframes, + musyx_t *musyx, uint32_t sfx_ptr, uint16_t idx); + +static void mix_sfx_with_main_subframes_v1(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains); +static void mix_sfx_with_main_subframes_v2(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains); + +static void mix_samples(int16_t *y, int16_t x, int16_t hgain); +static void mix_subframes(int16_t *y, const int16_t *x, int16_t hgain); +static void mix_fir4(int16_t *y, const int16_t *x, int16_t hgain, const int16_t *hcoeffs); + + +static void interleave_stage_v1(struct hle_t* hle, musyx_t *musyx, + uint32_t output_ptr); + +static void interleave_stage_v2(struct hle_t* hle, musyx_t *musyx, + uint16_t mask_16, uint32_t ptr_18, + uint32_t ptr_1c, uint32_t output_ptr); + +static int32_t dot4(const int16_t *x, const int16_t *y) +{ + size_t i; + int32_t accu = 0; + + for (i = 0; i < 4; ++i) + accu = clamp_s16(accu + (((int32_t)x[i] * (int32_t)y[i]) >> 15)); + + return accu; +} + +/************************************************************************** + * MusyX v1 audio ucode + **************************************************************************/ +void musyx_v1_task(struct hle_t* hle) +{ + uint32_t sfd_ptr = *dmem_u32(hle, TASK_DATA_PTR); + uint32_t sfd_count = *dmem_u32(hle, TASK_DATA_SIZE); + uint32_t state_ptr; + musyx_t musyx; + + HleVerboseMessage(hle->user_defined, + "musyx_v1_task: *data=%x, #SF=%d", + sfd_ptr, + sfd_count); + + state_ptr = *dram_u32(hle, sfd_ptr + SFD_STATE_PTR); + + /* load initial state */ + load_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_load_u16(hle, (uint16_t *)musyx.cc0, state_ptr + STATE_CC0, SUBFRAME_SIZE); + dram_load_u16(hle, (uint16_t *)musyx.subframe_740_last4, state_ptr + STATE_740_LAST4_V1, + 4); + + for (;;) { + /* parse SFD structure */ + uint16_t sfx_index = *dram_u16(hle, sfd_ptr + SFD_SFX_INDEX); + uint32_t voice_mask = *dram_u32(hle, sfd_ptr + SFD_VOICE_BITMASK); + uint32_t sfx_ptr = *dram_u32(hle, sfd_ptr + SFD_SFX_PTR); + uint32_t voice_ptr = sfd_ptr + SFD_VOICES; + uint32_t last_sample_ptr = state_ptr + STATE_LAST_SAMPLE; + uint32_t output_ptr; + + /* initialize internal subframes using updated base volumes */ + update_base_vol(hle, musyx.base_vol, voice_mask, last_sample_ptr, 0, 0); + init_subframes_v1(&musyx); + + /* active voices get mixed into L,R,cc0,e50 subframes (optional) */ + output_ptr = voice_stage(hle, &musyx, voice_ptr, last_sample_ptr); + + /* apply delay-based effects (optional) */ + sfx_stage(hle, mix_sfx_with_main_subframes_v1, + &musyx, sfx_ptr, sfx_index); + + /* emit interleaved L,R subframes */ + interleave_stage_v1(hle, &musyx, output_ptr); + + --sfd_count; + if (sfd_count == 0) + break; + + sfd_ptr += SFD_VOICES + MAX_VOICES * VOICE_SIZE; + state_ptr = *dram_u32(hle, sfd_ptr + SFD_STATE_PTR); + } + + /* writeback updated state */ + save_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_store_u16(hle, (uint16_t *)musyx.cc0, state_ptr + STATE_CC0, SUBFRAME_SIZE); + dram_store_u16(hle, (uint16_t *)musyx.subframe_740_last4, state_ptr + STATE_740_LAST4_V1, + 4); + + rsp_break(hle, SP_STATUS_TASKDONE); +} + +/************************************************************************** + * MusyX v2 audio ucode + **************************************************************************/ +void musyx_v2_task(struct hle_t* hle) +{ + uint32_t sfd_ptr = *dmem_u32(hle, TASK_DATA_PTR); + uint32_t sfd_count = *dmem_u32(hle, TASK_DATA_SIZE); + musyx_t musyx; + + HleVerboseMessage(hle->user_defined, + "musyx_v2_task: *data=%x, #SF=%d", + sfd_ptr, + sfd_count); + + for (;;) { + /* parse SFD structure */ + uint16_t sfx_index = *dram_u16(hle, sfd_ptr + SFD_SFX_INDEX); + uint32_t voice_mask = *dram_u32(hle, sfd_ptr + SFD_VOICE_BITMASK); + uint32_t state_ptr = *dram_u32(hle, sfd_ptr + SFD_STATE_PTR); + uint32_t sfx_ptr = *dram_u32(hle, sfd_ptr + SFD_SFX_PTR); + uint32_t voice_ptr = sfd_ptr + SFD2_VOICES; + + uint32_t ptr_10 = *dram_u32(hle, sfd_ptr + SFD2_10_PTR); + uint8_t mask_14 = *dram_u8 (hle, sfd_ptr + SFD2_14_BITMASK); + uint8_t mask_15 = *dram_u8 (hle, sfd_ptr + SFD2_15_BITMASK); + uint16_t mask_16 = *dram_u16(hle, sfd_ptr + SFD2_16_BITMASK); + uint32_t ptr_18 = *dram_u32(hle, sfd_ptr + SFD2_18_PTR); + uint32_t ptr_1c = *dram_u32(hle, sfd_ptr + SFD2_1C_PTR); + uint32_t ptr_20 = *dram_u32(hle, sfd_ptr + SFD2_20_PTR); + uint32_t ptr_24 = *dram_u32(hle, sfd_ptr + SFD2_24_PTR); + + uint32_t last_sample_ptr = state_ptr + STATE_LAST_SAMPLE; + uint32_t output_ptr; + + /* load state */ + load_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_load_u16(hle, (uint16_t *)musyx.subframe_740_last4, + state_ptr + STATE_740_LAST4_V2, 4); + + /* initialize internal subframes using updated base volumes */ + update_base_vol(hle, musyx.base_vol, voice_mask, last_sample_ptr, mask_15, ptr_24); + init_subframes_v2(&musyx); + + if (ptr_10) { + /* TODO */ + HleWarnMessage(hle->user_defined, + "ptr_10=%08x mask_14=%02x ptr_24=%08x", + ptr_10, mask_14, ptr_24); + } + + /* active voices get mixed into L,R,cc0,e50 subframes (optional) */ + output_ptr = voice_stage(hle, &musyx, voice_ptr, last_sample_ptr); + + /* apply delay-based effects (optional) */ + sfx_stage(hle, mix_sfx_with_main_subframes_v2, + &musyx, sfx_ptr, sfx_index); + + dram_store_u16(hle, (uint16_t*)musyx.left, output_ptr , SUBFRAME_SIZE); + dram_store_u16(hle, (uint16_t*)musyx.right, output_ptr + 2*SUBFRAME_SIZE, SUBFRAME_SIZE); + dram_store_u16(hle, (uint16_t*)musyx.cc0, output_ptr + 4*SUBFRAME_SIZE, SUBFRAME_SIZE); + + /* store state */ + save_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_store_u16(hle, (uint16_t*)musyx.subframe_740_last4, + state_ptr + STATE_740_LAST4_V2, 4); + + if (mask_16) + interleave_stage_v2(hle, &musyx, mask_16, ptr_18, ptr_1c, ptr_20); + + --sfd_count; + if (sfd_count == 0) + break; + + sfd_ptr += SFD2_VOICES + MAX_VOICES * VOICE_SIZE; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} + + + + + +static void load_base_vol(struct hle_t* hle, int32_t *base_vol, uint32_t address) +{ + base_vol[0] = ((uint32_t)(*dram_u16(hle, address)) << 16) | (*dram_u16(hle, address + 8)); + base_vol[1] = ((uint32_t)(*dram_u16(hle, address + 2)) << 16) | (*dram_u16(hle, address + 10)); + base_vol[2] = ((uint32_t)(*dram_u16(hle, address + 4)) << 16) | (*dram_u16(hle, address + 12)); + base_vol[3] = ((uint32_t)(*dram_u16(hle, address + 6)) << 16) | (*dram_u16(hle, address + 14)); +} + +static void save_base_vol(struct hle_t* hle, const int32_t *base_vol, uint32_t address) +{ + unsigned k; + + for (k = 0; k < 4; ++k) { + *dram_u16(hle, address) = (uint16_t)(base_vol[k] >> 16); + address += 2; + } + + for (k = 0; k < 4; ++k) { + *dram_u16(hle, address) = (uint16_t)(base_vol[k]); + address += 2; + } +} + +static void update_base_vol(struct hle_t* hle, int32_t *base_vol, + uint32_t voice_mask, uint32_t last_sample_ptr, + uint8_t mask_15, uint32_t ptr_24) +{ + unsigned i, k; + uint32_t mask; + + HleVerboseMessage(hle->user_defined, "base_vol voice_mask = %08x", voice_mask); + HleVerboseMessage(hle->user_defined, + "BEFORE: base_vol = %08x %08x %08x %08x", + base_vol[0], base_vol[1], base_vol[2], base_vol[3]); + + /* optim: skip voices contributions entirely if voice_mask is empty */ + if (voice_mask != 0) { + for (i = 0, mask = 1; i < MAX_VOICES; + ++i, mask <<= 1, last_sample_ptr += 8) { + if ((voice_mask & mask) == 0) + continue; + + for (k = 0; k < 4; ++k) + base_vol[k] += (int16_t)*dram_u16(hle, last_sample_ptr + k * 2); + } + } + + /* optim: skip contributions entirely if mask_15 is empty */ + if (mask_15 != 0) { + for(i = 0, mask = 1; i < 4; + ++i, mask <<= 1, ptr_24 += 8) { + if ((mask_15 & mask) == 0) + continue; + + for(k = 0; k < 4; ++k) + base_vol[k] += (int16_t)*dram_u16(hle, ptr_24 + k * 2); + } + } + + /* apply 3% decay */ + for (k = 0; k < 4; ++k) + base_vol[k] = (base_vol[k] * 0x0000f850) >> 16; + + HleVerboseMessage(hle->user_defined, + "AFTER: base_vol = %08x %08x %08x %08x", + base_vol[0], base_vol[1], base_vol[2], base_vol[3]); +} + + + + +static void init_subframes_v1(musyx_t *musyx) +{ + unsigned i; + + int16_t base_cc0 = clamp_s16(musyx->base_vol[2]); + int16_t base_e50 = clamp_s16(musyx->base_vol[3]); + + int16_t *left = musyx->left; + int16_t *right = musyx->right; + int16_t *cc0 = musyx->cc0; + int16_t *e50 = musyx->e50; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + *(e50++) = base_e50; + *(left++) = clamp_s16(*cc0 + base_cc0); + *(right++) = clamp_s16(-*cc0 - base_cc0); + *(cc0++) = 0; + } +} + +static void init_subframes_v2(musyx_t *musyx) +{ + unsigned i,k; + int16_t values[4]; + int16_t* subframes[4]; + + for(k = 0; k < 4; ++k) + values[k] = clamp_s16(musyx->base_vol[k]); + + subframes[0] = musyx->left; + subframes[1] = musyx->right; + subframes[2] = musyx->cc0; + subframes[3] = musyx->e50; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + + for(k = 0; k < 4; ++k) + *(subframes[k]++) = values[k]; + } +} + +/* Process voices, and returns interleaved subframe destination address */ +static uint32_t voice_stage(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, uint32_t last_sample_ptr) +{ + uint32_t output_ptr; + int i = 0; + + /* voice stage can be skipped if first voice has no samples */ + if (*dram_u16(hle, voice_ptr + VOICE_CATSRC_0 + CATSRC_SIZE1) == 0) { + HleVerboseMessage(hle->user_defined, "Skipping Voice stage"); + output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR); + } else { + /* otherwise process voices until a non null output_ptr is encountered */ + for (;;) { + /* load voice samples (PCM16 or APDCM) */ + int16_t samples[SAMPLE_BUFFER_SIZE]; + unsigned segbase; + unsigned offset; + + HleVerboseMessage(hle->user_defined, "Processing Voice #%d", i); + + if (*dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES) == 0) + load_samples_PCM16(hle, voice_ptr, samples, &segbase, &offset); + else + load_samples_ADPCM(hle, voice_ptr, samples, &segbase, &offset); + + /* mix them with each internal subframes */ + mix_voice_samples(hle, musyx, voice_ptr, samples, segbase, offset, + last_sample_ptr + i * 8); + + /* check break condition */ + output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR); + if (output_ptr != 0) + break; + + /* next voice */ + ++i; + voice_ptr += VOICE_SIZE; + } + } + + return output_ptr; +} + +static void dma_cat8(struct hle_t* hle, uint8_t *dst, uint32_t catsrc_ptr) +{ + uint32_t ptr1 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR1); + uint32_t ptr2 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR2); + uint16_t size1 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE1); + uint16_t size2 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE2); + + size_t count1 = size1; + size_t count2 = size2; + + HleVerboseMessage(hle->user_defined, + "dma_cat: %08x %08x %04x %04x", + ptr1, + ptr2, + size1, + size2); + + dram_load_u8(hle, dst, ptr1, count1); + + if (size2 == 0) + return; + + dram_load_u8(hle, dst + count1, ptr2, count2); +} + +static void dma_cat16(struct hle_t* hle, uint16_t *dst, uint32_t catsrc_ptr) +{ + uint32_t ptr1 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR1); + uint32_t ptr2 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR2); + uint16_t size1 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE1); + uint16_t size2 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE2); + + size_t count1 = size1 >> 1; + size_t count2 = size2 >> 1; + + HleVerboseMessage(hle->user_defined, + "dma_cat: %08x %08x %04x %04x", + ptr1, + ptr2, + size1, + size2); + + dram_load_u16(hle, dst, ptr1, count1); + + if (size2 == 0) + return; + + dram_load_u16(hle, dst + count1, ptr2, count2); +} + +static void load_samples_PCM16(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset) +{ + + uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES); + uint16_t u16_40 = *dram_u16(hle, voice_ptr + VOICE_U16_40); + uint16_t u16_42 = *dram_u16(hle, voice_ptr + VOICE_U16_42); + + unsigned count = align(u16_40 + u8_3e, 4); + + HleVerboseMessage(hle->user_defined, "Format: PCM16"); + + *segbase = SAMPLE_BUFFER_SIZE - count; + *offset = u8_3e; + + dma_cat16(hle, (uint16_t *)samples + *segbase, voice_ptr + VOICE_CATSRC_0); + + if (u16_42 != 0) + dma_cat16(hle, (uint16_t *)samples, voice_ptr + VOICE_CATSRC_1); +} + +static void load_samples_ADPCM(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset) +{ + /* decompressed samples cannot exceed 0x400 bytes; + * ADPCM has a compression ratio of 5/16 */ + uint8_t buffer[SAMPLE_BUFFER_SIZE * 2 * 5 / 16]; + int16_t adpcm_table[128]; + + uint8_t u8_3c = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES ); + uint8_t u8_3d = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES + 1); + uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES ); + uint8_t u8_3f = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES + 1); + uint32_t adpcm_table_ptr = *dram_u32(hle, voice_ptr + VOICE_ADPCM_TABLE_PTR); + unsigned count; + + HleVerboseMessage(hle->user_defined, "Format: ADPCM"); + + HleVerboseMessage(hle->user_defined, "Loading ADPCM table: %08x", adpcm_table_ptr); + dram_load_u16(hle, (uint16_t *)adpcm_table, adpcm_table_ptr, 128); + + count = u8_3c << 5; + + *segbase = SAMPLE_BUFFER_SIZE - count; + *offset = u8_3e & 0x1f; + + dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_0); + adpcm_decode_frames(hle, samples + *segbase, buffer, adpcm_table, u8_3c, u8_3e); + + if (u8_3d != 0) { + dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_1); + adpcm_decode_frames(hle, samples, buffer, adpcm_table, u8_3d, u8_3f); + } +} + +static void adpcm_decode_frames(struct hle_t* hle, + int16_t *dst, const uint8_t *src, + const int16_t *table, uint8_t count, + uint8_t skip_samples) +{ + int16_t frame[32]; + const uint8_t *nibbles = src + 8; + unsigned i; + bool jump_gap = false; + + HleVerboseMessage(hle->user_defined, + "ADPCM decode: count=%d, skip=%d", + count, skip_samples); + + if (skip_samples >= 32) { + jump_gap = true; + nibbles += 16; + src += 4; + } + + for (i = 0; i < count; ++i) { + uint8_t c2 = nibbles[0]; + + const int16_t *book = (c2 & 0xf0) + table; + unsigned int rshift = (c2 & 0x0f); + + adpcm_predict_frame(frame, src, nibbles, rshift); + + memcpy(dst, frame, 2 * sizeof(frame[0])); + adpcm_compute_residuals(dst + 2, frame + 2, book, dst , 6); + adpcm_compute_residuals(dst + 8, frame + 8, book, dst + 6, 8); + adpcm_compute_residuals(dst + 16, frame + 16, book, dst + 14, 8); + adpcm_compute_residuals(dst + 24, frame + 24, book, dst + 22, 8); + + if (jump_gap) { + nibbles += 8; + src += 32; + } + + jump_gap = !jump_gap; + nibbles += 16; + src += 4; + dst += 32; + } +} + +static void adpcm_predict_frame(int16_t *dst, const uint8_t *src, + const uint8_t *nibbles, + unsigned int rshift) +{ + unsigned int i; + + *(dst++) = (src[0] << 8) | src[1]; + *(dst++) = (src[2] << 8) | src[3]; + + for (i = 1; i < 16; ++i) { + uint8_t byte = nibbles[i]; + + *(dst++) = adpcm_predict_sample(byte, 0xf0, 8, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x0f, 12, rshift); + } +} + +static void mix_voice_samples(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, const int16_t *samples, + unsigned segbase, unsigned offset, uint32_t last_sample_ptr) +{ + int i, k; + + /* parse VOICE structure */ + const uint16_t pitch_q16 = *dram_u16(hle, voice_ptr + VOICE_PITCH_Q16); + const uint16_t pitch_shift = *dram_u16(hle, voice_ptr + VOICE_PITCH_SHIFT); /* Q4.12 */ + + const uint16_t end_point = *dram_u16(hle, voice_ptr + VOICE_END_POINT); + const uint16_t restart_point = *dram_u16(hle, voice_ptr + VOICE_RESTART_POINT); + + const uint16_t u16_4e = *dram_u16(hle, voice_ptr + VOICE_U16_4E); + + /* init values and pointers */ + const int16_t *sample = samples + segbase + offset + u16_4e; + const int16_t *const sample_end = samples + segbase + end_point; + const int16_t *const sample_restart = samples + (restart_point & 0x7fff) + + (((restart_point & 0x8000) != 0) ? 0x000 : segbase); + + + uint32_t pitch_accu = pitch_q16; + uint32_t pitch_step = pitch_shift << 4; + + int32_t v4_env[4]; + int32_t v4_env_step[4]; + int16_t *v4_dst[4]; + int16_t v4[4]; + + dram_load_u32(hle, (uint32_t *)v4_env, voice_ptr + VOICE_ENV_BEGIN, 4); + dram_load_u32(hle, (uint32_t *)v4_env_step, voice_ptr + VOICE_ENV_STEP, 4); + + v4_dst[0] = musyx->left; + v4_dst[1] = musyx->right; + v4_dst[2] = musyx->cc0; + v4_dst[3] = musyx->e50; + + HleVerboseMessage(hle->user_defined, + "Voice debug: segbase=%d" + "\tu16_4e=%04x\n" + "\tpitch: frac0=%04x shift=%04x\n" + "\tend_point=%04x restart_point=%04x\n" + "\tenv = %08x %08x %08x %08x\n" + "\tenv_step = %08x %08x %08x %08x\n", + segbase, + u16_4e, + pitch_q16, pitch_shift, + end_point, restart_point, + v4_env[0], v4_env[1], v4_env[2], v4_env[3], + v4_env_step[0], v4_env_step[1], v4_env_step[2], v4_env_step[3]); + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + /* update sample and lut pointers and then pitch_accu */ + const int16_t *lut = (RESAMPLE_LUT + ((pitch_accu & 0xfc00) >> 8)); + int dist; + int16_t v; + + sample += (pitch_accu >> 16); + pitch_accu &= 0xffff; + pitch_accu += pitch_step; + + /* handle end/restart points */ + dist = sample - sample_end; + if (dist >= 0) + sample = sample_restart + dist; + + /* apply resample filter */ + v = clamp_s16(dot4(sample, lut)); + + for (k = 0; k < 4; ++k) { + /* envmix */ + int32_t accu = (v * (v4_env[k] >> 16)) >> 15; + v4[k] = clamp_s16(accu); + *(v4_dst[k]) = clamp_s16(accu + *(v4_dst[k])); + + /* update envelopes and dst pointers */ + ++(v4_dst[k]); + v4_env[k] += v4_env_step[k]; + } + } + + /* save last resampled sample */ + dram_store_u16(hle, (uint16_t *)v4, last_sample_ptr, 4); + + HleVerboseMessage(hle->user_defined, + "last_sample = %04x %04x %04x %04x", + v4[0], v4[1], v4[2], v4[3]); +} + + +static void sfx_stage(struct hle_t* hle, mix_sfx_with_main_subframes_t mix_sfx_with_main_subframes, + musyx_t *musyx, uint32_t sfx_ptr, uint16_t idx) +{ + unsigned int i; + + int16_t buffer[SUBFRAME_SIZE + 4]; + int16_t *subframe = buffer + 4; + + uint32_t tap_delays[8]; + int16_t tap_gains[8]; + int16_t fir4_hcoeffs[4]; + + int16_t delayed[SUBFRAME_SIZE]; + int dpos, dlength; + + const uint32_t pos = idx * SUBFRAME_SIZE; + + uint32_t cbuffer_ptr; + uint32_t cbuffer_length; + uint16_t tap_count; + int16_t fir4_hgain; + uint16_t sfx_gains[2]; + + HleVerboseMessage(hle->user_defined, "SFX: %08x, idx=%d", sfx_ptr, idx); + + if (sfx_ptr == 0) + return; + + /* load sfx parameters */ + cbuffer_ptr = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_PTR); + cbuffer_length = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_LENGTH); + + tap_count = *dram_u16(hle, sfx_ptr + SFX_TAP_COUNT); + + dram_load_u32(hle, tap_delays, sfx_ptr + SFX_TAP_DELAYS, 8); + dram_load_u16(hle, (uint16_t *)tap_gains, sfx_ptr + SFX_TAP_GAINS, 8); + + fir4_hgain = *dram_u16(hle, sfx_ptr + SFX_FIR4_HGAIN); + dram_load_u16(hle, (uint16_t *)fir4_hcoeffs, sfx_ptr + SFX_FIR4_HCOEFFS, 4); + + sfx_gains[0] = *dram_u16(hle, sfx_ptr + SFX_U16_3C); + sfx_gains[1] = *dram_u16(hle, sfx_ptr + SFX_U16_3E); + + HleVerboseMessage(hle->user_defined, + "cbuffer: ptr=%08x length=%x", cbuffer_ptr, + cbuffer_length); + + HleVerboseMessage(hle->user_defined, + "fir4: hgain=%04x hcoeff=%04x %04x %04x %04x", + fir4_hgain, + fir4_hcoeffs[0], fir4_hcoeffs[1], fir4_hcoeffs[2], fir4_hcoeffs[3]); + + HleVerboseMessage(hle->user_defined, + "tap count=%d\n" + "delays: %08x %08x %08x %08x %08x %08x %08x %08x\n" + "gains: %04x %04x %04x %04x %04x %04x %04x %04x", + tap_count, + tap_delays[0], tap_delays[1], tap_delays[2], tap_delays[3], + tap_delays[4], tap_delays[5], tap_delays[6], tap_delays[7], + tap_gains[0], tap_gains[1], tap_gains[2], tap_gains[3], + tap_gains[4], tap_gains[5], tap_gains[6], tap_gains[7]); + + HleVerboseMessage(hle->user_defined, "sfx_gains=%04x %04x", sfx_gains[0], sfx_gains[1]); + + /* mix up to 8 delayed subframes */ + memset(subframe, 0, SUBFRAME_SIZE * sizeof(subframe[0])); + for (i = 0; i < tap_count; ++i) { + + dpos = pos - tap_delays[i]; + if (dpos <= 0) + dpos += cbuffer_length; + dlength = SUBFRAME_SIZE; + + if ((uint32_t)(dpos + SUBFRAME_SIZE) > cbuffer_length) { + dlength = cbuffer_length - dpos; + dram_load_u16(hle, (uint16_t *)delayed + dlength, cbuffer_ptr, SUBFRAME_SIZE - dlength); + } + + dram_load_u16(hle, (uint16_t *)delayed, cbuffer_ptr + dpos * 2, dlength); + + mix_subframes(subframe, delayed, tap_gains[i]); + } + + /* add resulting subframe to main subframes */ + mix_sfx_with_main_subframes(musyx, subframe, sfx_gains); + + /* apply FIR4 filter and writeback filtered result */ + memcpy(buffer, musyx->subframe_740_last4, 4 * sizeof(int16_t)); + memcpy(musyx->subframe_740_last4, subframe + SUBFRAME_SIZE - 4, 4 * sizeof(int16_t)); + mix_fir4(musyx->e50, buffer + 1, fir4_hgain, fir4_hcoeffs); + dram_store_u16(hle, (uint16_t *)musyx->e50, cbuffer_ptr + pos * 2, SUBFRAME_SIZE); +} + +static void mix_sfx_with_main_subframes_v1(musyx_t *musyx, const int16_t *subframe, + const uint16_t* UNUSED(gains)) +{ + unsigned i; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + int16_t v = subframe[i]; + musyx->left[i] = clamp_s16(musyx->left[i] + v); + musyx->right[i] = clamp_s16(musyx->right[i] + v); + } +} + +static void mix_sfx_with_main_subframes_v2(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains) +{ + unsigned i; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + int16_t v = subframe[i]; + int16_t v1 = (int32_t)(v * gains[0]) >> 16; + int16_t v2 = (int32_t)(v * gains[1]) >> 16; + + musyx->left[i] = clamp_s16(musyx->left[i] + v1); + musyx->right[i] = clamp_s16(musyx->right[i] + v1); + musyx->cc0[i] = clamp_s16(musyx->cc0[i] + v2); + } +} + +static void mix_samples(int16_t *y, int16_t x, int16_t hgain) +{ + *y = clamp_s16(*y + ((x * hgain + 0x4000) >> 15)); +} + +static void mix_subframes(int16_t *y, const int16_t *x, int16_t hgain) +{ + unsigned int i; + + for (i = 0; i < SUBFRAME_SIZE; ++i) + mix_samples(&y[i], x[i], hgain); +} + +static void mix_fir4(int16_t *y, const int16_t *x, int16_t hgain, const int16_t *hcoeffs) +{ + unsigned int i; + int32_t h[4]; + + h[0] = (hgain * hcoeffs[0]) >> 15; + h[1] = (hgain * hcoeffs[1]) >> 15; + h[2] = (hgain * hcoeffs[2]) >> 15; + h[3] = (hgain * hcoeffs[3]) >> 15; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + int32_t v = (h[0] * x[i] + h[1] * x[i + 1] + h[2] * x[i + 2] + h[3] * x[i + 3]) >> 15; + y[i] = clamp_s16(y[i] + v); + } +} + +static void interleave_stage_v1(struct hle_t* hle, musyx_t *musyx, uint32_t output_ptr) +{ + size_t i; + + int16_t base_left; + int16_t base_right; + + int16_t *left; + int16_t *right; + uint32_t *dst; + + HleVerboseMessage(hle->user_defined, "interleave: %08x", output_ptr); + + base_left = clamp_s16(musyx->base_vol[0]); + base_right = clamp_s16(musyx->base_vol[1]); + + left = musyx->left; + right = musyx->right; + dst = dram_u32(hle, output_ptr); + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + uint16_t l = clamp_s16(*(left++) + base_left); + uint16_t r = clamp_s16(*(right++) + base_right); + + *(dst++) = (l << 16) | r; + } +} + +static void interleave_stage_v2(struct hle_t* hle, musyx_t *musyx, + uint16_t mask_16, uint32_t ptr_18, + uint32_t ptr_1c, uint32_t output_ptr) +{ + unsigned i, k; + int16_t subframe[SUBFRAME_SIZE]; + uint32_t *dst; + uint16_t mask; + + HleVerboseMessage(hle->user_defined, + "mask_16=%04x ptr_18=%08x ptr_1c=%08x output_ptr=%08x", + mask_16, ptr_18, ptr_1c, output_ptr); + + /* compute L_total, R_total and update subframe @ptr_1c */ + memset(subframe, 0, SUBFRAME_SIZE*sizeof(subframe[0])); + + for(i = 0; i < SUBFRAME_SIZE; ++i) { + int16_t v = *dram_u16(hle, ptr_1c + i*2); + musyx->left[i] = v; + musyx->right[i] = clamp_s16(-v); + } + + for (k = 0, mask = 1; k < 8; ++k, mask <<= 1, ptr_18 += 8) { + int16_t hgain; + uint32_t address; + + if ((mask_16 & mask) == 0) + continue; + + address = *dram_u32(hle, ptr_18); + hgain = *dram_u16(hle, ptr_18 + 4); + + for(i = 0; i < SUBFRAME_SIZE; ++i, address += 2) { + mix_samples(&musyx->left[i], *dram_u16(hle, address), hgain); + mix_samples(&musyx->right[i], *dram_u16(hle, address + 2*SUBFRAME_SIZE), hgain); + mix_samples(&subframe[i], *dram_u16(hle, address + 4*SUBFRAME_SIZE), hgain); + } + } + + /* interleave L_total and R_total */ + dst = dram_u32(hle, output_ptr); + for(i = 0; i < SUBFRAME_SIZE; ++i) { + uint16_t l = musyx->left[i]; + uint16_t r = musyx->right[i]; + *(dst++) = (l << 16) | r; + } + + /* writeback subframe @ptr_1c */ + dram_store_u16(hle, (uint16_t*)subframe, ptr_1c, SUBFRAME_SIZE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib.h b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib.h new file mode 100644 index 0000000000..dc5e5bab53 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib.h @@ -0,0 +1,34 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if !defined(OSAL_DYNAMICLIB_H) +#define OSAL_DYNAMICLIB_H + +#include "m64p_types.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath); + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName); + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle); + +#endif /* #define OSAL_DYNAMICLIB_H */ + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_unix.c b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_unix.c new file mode 100644 index 0000000000..9d937ed370 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_unix.c @@ -0,0 +1,71 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib_unix.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include +#include + +#include "m64p_types.h" +#include "hle_external.h" +#include "osal_dynamiclib.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath) +{ + if (pLibHandle == NULL || pccLibraryPath == NULL) + return M64ERR_INPUT_ASSERT; + + *pLibHandle = dlopen(pccLibraryPath, RTLD_NOW); + + if (*pLibHandle == NULL) + { + /* only print an error message if there is a directory separator (/) in the pathname */ + /* this prevents us from throwing an error for the use case where Mupen64Plus is not installed */ + if (strchr(pccLibraryPath, '/') != NULL) + HleErrorMessage(NULL, "dlopen('%s') failed: %s", pccLibraryPath, dlerror()); + return M64ERR_INPUT_NOT_FOUND; + } + + return M64ERR_SUCCESS; +} + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return dlsym(LibHandle, pccProcedureName); +} + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle) +{ + int rval = dlclose(LibHandle); + + if (rval != 0) + { + HleErrorMessage(NULL, "dlclose() failed: %s", dlerror()); + return M64ERR_INTERNAL; + } + + return M64ERR_SUCCESS; +} + + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_win32.c b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_win32.c new file mode 100644 index 0000000000..fe77cf3aad --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_win32.c @@ -0,0 +1,75 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib_win32.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "m64p_types.h" +#include "hle_external.h" +#include "osal_dynamiclib.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath) +{ + if (pLibHandle == NULL || pccLibraryPath == NULL) + return M64ERR_INPUT_ASSERT; + + *pLibHandle = LoadLibrary(pccLibraryPath); + + if (*pLibHandle == NULL) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + HleErrorMessage(NULL, "LoadLibrary('%s') error: %s", pccLibraryPath, pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INPUT_NOT_FOUND; + } + + return M64ERR_SUCCESS; +} + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return GetProcAddress(LibHandle, pccProcedureName); +} + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle) +{ + int rval = FreeLibrary(LibHandle); + + if (rval == 0) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + HleErrorMessage(NULL, "FreeLibrary() error: %s", pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INTERNAL; + } + + return M64ERR_SUCCESS; +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/plugin.c b/libmupen64plus/mupen64plus-rsp-hle/src/plugin.c new file mode 100644 index 0000000000..e0818bdb63 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/plugin.c @@ -0,0 +1,495 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - plugin.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "common.h" +#include "hle.h" +#include "hle_internal.h" +#include "hle_external.h" + +#define M64P_PLUGIN_PROTOTYPES 1 +#include "m64p_common.h" +#include "m64p_config.h" +#include "m64p_frontend.h" +#include "m64p_plugin.h" +#include "m64p_types.h" + +#include "osal_dynamiclib.h" + +#define CONFIG_API_VERSION 0x020100 +#define CONFIG_PARAM_VERSION 1.00 + +#define RSP_API_VERSION 0x20000 +#define RSP_HLE_VERSION 0x020500 +#define RSP_PLUGIN_API_VERSION 0x020000 + +#define RSP_HLE_CONFIG_SECTION "Rsp-HLE" +#define RSP_HLE_CONFIG_VERSION "Version" +#define RSP_HLE_CONFIG_FALLBACK "RspFallback" +#define RSP_HLE_CONFIG_HLE_GFX "DisplayListToGraphicsPlugin" +#define RSP_HLE_CONFIG_HLE_AUD "AudioListToAudioPlugin" + + +#define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff) + +/* Handy macro to avoid code bloat when loading symbols */ +#define GET_FUNC(type, field, name) \ + ((field = (type)osal_dynlib_getproc(handle, name)) != NULL) + +/* local variables */ +static struct hle_t g_hle; +static void (*l_CheckInterrupts)(void) = NULL; +static void (*l_ProcessDlistList)(void) = NULL; +static void (*l_ProcessAlistList)(void) = NULL; +static void (*l_ProcessRdpList)(void) = NULL; +static void (*l_ShowCFB)(void) = NULL; +static void (*l_DebugCallback)(void *, int, const char *) = NULL; +static void *l_DebugCallContext = NULL; +static m64p_dynlib_handle l_CoreHandle = NULL; +static int l_PluginInit = 0; + +static m64p_handle l_ConfigRspHle; +static m64p_dynlib_handle l_RspFallback; +static ptr_InitiateRSP l_InitiateRSP = NULL; +static ptr_DoRspCycles l_DoRspCycles = NULL; +static ptr_RomClosed l_RomClosed = NULL; +static ptr_PluginShutdown l_PluginShutdown = NULL; + +/* definitions of pointers to Core functions */ +static ptr_ConfigOpenSection ConfigOpenSection = NULL; +static ptr_ConfigDeleteSection ConfigDeleteSection = NULL; +static ptr_ConfigSaveSection ConfigSaveSection = NULL; +static ptr_ConfigSetParameter ConfigSetParameter = NULL; +static ptr_ConfigGetParameter ConfigGetParameter = NULL; +static ptr_ConfigSetDefaultInt ConfigSetDefaultInt = NULL; +static ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat = NULL; +static ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL; +static ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL; +static ptr_ConfigGetParamInt ConfigGetParamInt = NULL; +static ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL; +static ptr_ConfigGetParamBool ConfigGetParamBool = NULL; +static ptr_ConfigGetParamString ConfigGetParamString = NULL; +static ptr_CoreDoCommand CoreDoCommand = NULL; + +/* local function */ +static void teardown_rsp_fallback() +{ + if (l_RspFallback != NULL) { + (*l_PluginShutdown)(); + osal_dynlib_close(l_RspFallback); + } + + l_RspFallback = NULL; + l_DoRspCycles = NULL; + l_InitiateRSP = NULL; + l_RomClosed = NULL; + l_PluginShutdown = NULL; +} + +static void setup_rsp_fallback(const char* rsp_fallback_path) +{ + m64p_dynlib_handle handle = NULL; + + /* reset rsp fallback */ + teardown_rsp_fallback(); + + if (rsp_fallback_path == NULL || strlen(rsp_fallback_path) == 0) { + HleInfoMessage(NULL, "RSP Fallback disabled !"); + return; + } + + /* load plugin */ + if (osal_dynlib_open(&handle, rsp_fallback_path) != M64ERR_SUCCESS) { + HleErrorMessage(NULL, "Can't load library: %s", rsp_fallback_path); + return; + } + + /* call the GetVersion function for the plugin and check compatibility */ + ptr_PluginGetVersion PluginGetVersion = (ptr_PluginGetVersion) osal_dynlib_getproc(handle, "PluginGetVersion"); + if (PluginGetVersion == NULL) + { + HleErrorMessage(NULL, "library '%s' is not a Mupen64Plus library.", rsp_fallback_path); + goto close_handle; + } + + m64p_plugin_type plugin_type = (m64p_plugin_type)0; + int plugin_version = 0; + const char *plugin_name = NULL; + int api_version = 0; + + (*PluginGetVersion)(&plugin_type, &plugin_version, &api_version, &plugin_name, NULL); + + if (plugin_type != M64PLUGIN_RSP) { + HleErrorMessage(NULL, "plugin %s is not an RSP plugin (%u)", plugin_name, plugin_type); + goto close_handle; + } + + if ((api_version & 0xffff0000) != (RSP_API_VERSION & 0xffff0000)) { + HleErrorMessage(NULL, "plugin %s. Version mismatch: %u.%u. Expected >= %u.0", + plugin_name, + (uint16_t)(api_version >> 16), + (uint16_t)(api_version), + (uint16_t)(RSP_API_VERSION >> 16)); + goto close_handle; + } + + /* load functions */ + ptr_PluginStartup PluginStartup; + + if (!GET_FUNC(ptr_PluginStartup, PluginStartup, "PluginStartup") || + !GET_FUNC(ptr_PluginShutdown, l_PluginShutdown, "PluginShutdown") || + !GET_FUNC(ptr_DoRspCycles, l_DoRspCycles, "DoRspCycles") || + !GET_FUNC(ptr_InitiateRSP, l_InitiateRSP, "InitiateRSP") || + !GET_FUNC(ptr_RomClosed, l_RomClosed, "RomClosed")) + { + HleErrorMessage(NULL, "broken RSP plugin; function(s) not found."); + l_PluginShutdown = NULL; + l_DoRspCycles = NULL; + l_InitiateRSP = NULL; + l_RomClosed = NULL; + goto close_handle; + } + + /* call the plugin's initialization function and make sure it starts okay */ + if ((*PluginStartup)(l_CoreHandle, l_DebugCallContext, l_DebugCallback) != M64ERR_SUCCESS) { + HleErrorMessage(NULL, "Error: %s plugin library '%s' failed to start.", plugin_name, rsp_fallback_path); + goto close_handle; + } + + /* OK we're done ! */ + l_RspFallback = handle; + HleInfoMessage(NULL, "RSP Fallback '%s' loaded successfully !", rsp_fallback_path); + return; + +close_handle: + osal_dynlib_close(handle); +} + +static void DebugMessage(int level, const char *message, va_list args) +{ + char msgbuf[1024]; + + if (l_DebugCallback == NULL) + return; + + vsprintf(msgbuf, message, args); + + (*l_DebugCallback)(l_DebugCallContext, level, msgbuf); +} + +/* Global functions needed by HLE core */ +void HleVerboseMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_VERBOSE, message, args); + va_end(args); +} + +void HleInfoMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_INFO, message, args); + va_end(args); +} + +void HleErrorMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_ERROR, message, args); + va_end(args); +} + +void HleWarnMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_WARNING, message, args); + va_end(args); +} + +void HleCheckInterrupts(void* UNUSED(user_defined)) +{ + if (l_CheckInterrupts == NULL) + return; + + (*l_CheckInterrupts)(); +} + +void HleProcessDlistList(void* UNUSED(user_defined)) +{ + if (l_ProcessDlistList == NULL) + return; + + (*l_ProcessDlistList)(); +} + +void HleProcessAlistList(void* UNUSED(user_defined)) +{ + if (l_ProcessAlistList == NULL) + return; + + (*l_ProcessAlistList)(); +} + +void HleProcessRdpList(void* UNUSED(user_defined)) +{ + if (l_ProcessRdpList == NULL) + return; + + (*l_ProcessRdpList)(); +} + +void HleShowCFB(void* UNUSED(user_defined)) +{ + if (l_ShowCFB == NULL) + return; + + (*l_ShowCFB)(); +} + + +int HleForwardTask(void* user_defined) +{ + if (l_DoRspCycles == NULL) + return -1; + + (*l_DoRspCycles)(-1); + return 0; +} + + +/* DLL-exported functions */ +EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context, + void (*DebugCallback)(void *, int, const char *)) +{ + ptr_CoreGetAPIVersions CoreAPIVersionFunc; + int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion, bSaveConfig; + float fConfigParamsVersion = 0.0f; + + if (l_PluginInit) + return M64ERR_ALREADY_INIT; + + /* first thing is to set the callback function for debug info */ + l_DebugCallback = DebugCallback; + l_DebugCallContext = Context; + + /* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */ + CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions"); + if (CoreAPIVersionFunc == NULL) + { + HleErrorMessage(NULL, "Core emulator broken; no CoreAPIVersionFunc() function found."); + return M64ERR_INCOMPATIBLE; + } + + (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL); + if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000)) + { + HleErrorMessage(NULL, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)", + VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION)); + return M64ERR_INCOMPATIBLE; + } + + /* Get the core config function pointers from the library handle */ + ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection"); + ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection"); + ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection"); + ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter"); + ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter"); + ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt"); + ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat"); + ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool"); + ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString"); + ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt"); + ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat"); + ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool"); + ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString"); + + if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSetParameter || !ConfigGetParameter || + !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString || + !ConfigGetParamInt || !ConfigGetParamFloat || !ConfigGetParamBool || !ConfigGetParamString) + return M64ERR_INCOMPATIBLE; + + /* ConfigSaveSection was added in Config API v2.1.0 */ + if (ConfigAPIVersion >= 0x020100 && !ConfigSaveSection) + return M64ERR_INCOMPATIBLE; + + /* Get core DoCommand function */ + CoreDoCommand = (ptr_CoreDoCommand) osal_dynlib_getproc(CoreLibHandle, "CoreDoCommand"); + if (!CoreDoCommand) { + return M64ERR_INCOMPATIBLE; + } + + /* get a configuration section handle */ + if (ConfigOpenSection(RSP_HLE_CONFIG_SECTION, &l_ConfigRspHle) != M64ERR_SUCCESS) + { + HleErrorMessage(NULL, "Couldn't open config section '" RSP_HLE_CONFIG_SECTION "'"); + return M64ERR_INPUT_NOT_FOUND; + } + + /* check the section version number */ + bSaveConfig = 0; + if (ConfigGetParameter(l_ConfigRspHle, RSP_HLE_CONFIG_VERSION, M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS) + { + HleWarnMessage(NULL, "No version number in '" RSP_HLE_CONFIG_SECTION "' config section. Setting defaults."); + ConfigDeleteSection(RSP_HLE_CONFIG_SECTION); + ConfigOpenSection(RSP_HLE_CONFIG_SECTION, &l_ConfigRspHle); + bSaveConfig = 1; + } + else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION)) + { + HleWarnMessage(NULL, "Incompatible version %.2f in '" RSP_HLE_CONFIG_SECTION "' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION); + ConfigDeleteSection(RSP_HLE_CONFIG_SECTION); + ConfigOpenSection(RSP_HLE_CONFIG_SECTION, &l_ConfigRspHle); + bSaveConfig = 1; + } + else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f) + { + /* handle upgrades */ + float fVersion = CONFIG_PARAM_VERSION; + ConfigSetParameter(l_ConfigRspHle, "Version", M64TYPE_FLOAT, &fVersion); + HleInfoMessage(NULL, "Updating parameter set version in '" RSP_HLE_CONFIG_SECTION "' config section to %.2f", fVersion); + bSaveConfig = 1; + } + + /* set the default values for this plugin */ + ConfigSetDefaultFloat(l_ConfigRspHle, RSP_HLE_CONFIG_VERSION, CONFIG_PARAM_VERSION, + "Mupen64Plus RSP HLE Plugin config parameter version number"); + ConfigSetDefaultString(l_ConfigRspHle, RSP_HLE_CONFIG_FALLBACK, "", + "Path to a RSP plugin which will be used when encountering an unknown ucode." + "You can disable this by letting an empty string."); + ConfigSetDefaultBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_GFX, 1, + "Send display lists to the graphics plugin"); + ConfigSetDefaultBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_AUD, 0, + "Send audio lists to the audio plugin"); + + if (bSaveConfig && ConfigAPIVersion >= 0x020100) + ConfigSaveSection(RSP_HLE_CONFIG_SECTION); + + l_CoreHandle = CoreLibHandle; + + l_PluginInit = 1; + return M64ERR_SUCCESS; +} + +EXPORT m64p_error CALL PluginShutdown(void) +{ + if (!l_PluginInit) + return M64ERR_NOT_INIT; + + /* reset some local variable */ + l_DebugCallback = NULL; + l_DebugCallContext = NULL; + l_CoreHandle = NULL; + + teardown_rsp_fallback(); + + l_PluginInit = 0; + return M64ERR_SUCCESS; +} + +EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities) +{ + /* set version info */ + if (PluginType != NULL) + *PluginType = M64PLUGIN_RSP; + + if (PluginVersion != NULL) + *PluginVersion = RSP_HLE_VERSION; + + if (APIVersion != NULL) + *APIVersion = RSP_PLUGIN_API_VERSION; + + if (PluginNamePtr != NULL) + *PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin"; + + if (Capabilities != NULL) + *Capabilities = 0; + + return M64ERR_SUCCESS; +} + +EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles) +{ + hle_execute(&g_hle); + return Cycles; +} + +EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, unsigned int* CycleCount) +{ + hle_init(&g_hle, + Rsp_Info.RDRAM, + Rsp_Info.DMEM, + Rsp_Info.IMEM, + Rsp_Info.MI_INTR_REG, + Rsp_Info.SP_MEM_ADDR_REG, + Rsp_Info.SP_DRAM_ADDR_REG, + Rsp_Info.SP_RD_LEN_REG, + Rsp_Info.SP_WR_LEN_REG, + Rsp_Info.SP_STATUS_REG, + Rsp_Info.SP_DMA_FULL_REG, + Rsp_Info.SP_DMA_BUSY_REG, + Rsp_Info.SP_PC_REG, + Rsp_Info.SP_SEMAPHORE_REG, + Rsp_Info.DPC_START_REG, + Rsp_Info.DPC_END_REG, + Rsp_Info.DPC_CURRENT_REG, + Rsp_Info.DPC_STATUS_REG, + Rsp_Info.DPC_CLOCK_REG, + Rsp_Info.DPC_BUFBUSY_REG, + Rsp_Info.DPC_PIPEBUSY_REG, + Rsp_Info.DPC_TMEM_REG, + NULL); + + l_CheckInterrupts = Rsp_Info.CheckInterrupts; + l_ProcessDlistList = Rsp_Info.ProcessDlistList; + l_ProcessAlistList = Rsp_Info.ProcessAlistList; + l_ProcessRdpList = Rsp_Info.ProcessRdpList; + l_ShowCFB = Rsp_Info.ShowCFB; + + setup_rsp_fallback(ConfigGetParamString(l_ConfigRspHle, RSP_HLE_CONFIG_FALLBACK)); + + m64p_rom_header rom_header; + CoreDoCommand(M64CMD_ROM_GET_HEADER, sizeof(rom_header), &rom_header); + + g_hle.hle_gfx = ConfigGetParamBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_GFX); + g_hle.hle_aud = ConfigGetParamBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_AUD); + + /* notify fallback plugin */ + if (l_InitiateRSP) { + l_InitiateRSP(Rsp_Info, CycleCount); + } +} + +EXPORT void CALL RomClosed(void) +{ + /* notify fallback plugin */ + if (l_RomClosed) { + l_RomClosed(); + } +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/re2.c b/libmupen64plus/mupen64plus-rsp-hle/src/re2.c new file mode 100644 index 0000000000..8ae178804d --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/re2.c @@ -0,0 +1,224 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - re2.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2016 Gilles Siberlin * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" + +#define SATURATE8(x) ((unsigned int) x <= 255 ? x : (x < 0 ? 0: 255)) + +/************************************************************************** + * Resident evil 2 ucodes + **************************************************************************/ +void resize_bilinear_task(struct hle_t* hle) +{ + int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA); + + int src_addr = *dram_u32(hle, data_ptr); + int dst_addr = *dram_u32(hle, data_ptr + 4); + int dst_width = *dram_u32(hle, data_ptr + 8); + int dst_height = *dram_u32(hle, data_ptr + 12); + int x_ratio = *dram_u32(hle, data_ptr + 16); + int y_ratio = *dram_u32(hle, data_ptr + 20); +#if 0 /* unused, but keep it for documentation purpose */ + int dst_stride = *dram_u32(hle, data_ptr + 24); +#endif + int src_offset = *dram_u32(hle, data_ptr + 36); + + int a, b, c ,d, index, y_index, xr, yr, blue, green, red, addr, i, j; + long long x, y, x_diff, y_diff, one_min_x_diff, one_min_y_diff; + unsigned short pixel; + + src_addr += (src_offset >> 16) * (320 * 3); + x = y = 0; + + for(i = 0; i < dst_height; i++) + { + yr = (int)(y >> 16); + y_diff = y - (yr << 16); + one_min_y_diff = 65536 - y_diff; + y_index = yr * 320; + x = 0; + + for(j = 0; j < dst_width; j++) + { + xr = (int)(x >> 16); + x_diff = x - (xr << 16); + one_min_x_diff = 65536 - x_diff; + index = y_index + xr; + addr = src_addr + (index * 3); + + dram_load_u8(hle, (uint8_t*)&a, addr, 3); + dram_load_u8(hle, (uint8_t*)&b, (addr + 3), 3); + dram_load_u8(hle, (uint8_t*)&c, (addr + (320 * 3)), 3); + dram_load_u8(hle, (uint8_t*)&d, (addr + (320 * 3) + 3), 3); + + blue = (int)(((a&0xff)*one_min_x_diff*one_min_y_diff + (b&0xff)*x_diff*one_min_y_diff + + (c&0xff)*y_diff*one_min_x_diff + (d&0xff)*x_diff*y_diff) >> 32); + + green = (int)((((a>>8)&0xff)*one_min_x_diff*one_min_y_diff + ((b>>8)&0xff)*x_diff*one_min_y_diff + + ((c>>8)&0xff)*y_diff*one_min_x_diff + ((d>>8)&0xff)*x_diff*y_diff) >> 32); + + red = (int)((((a>>16)&0xff)*one_min_x_diff*one_min_y_diff + ((b>>16)&0xff)*x_diff*one_min_y_diff + + ((c>>16)&0xff)*y_diff*one_min_x_diff + ((d>>16)&0xff)*x_diff*y_diff) >> 32); + + blue = (blue >> 3) & 0x001f; + green = (green >> 3) & 0x001f; + red = (red >> 3) & 0x001f; + pixel = (red << 11) | (green << 6) | (blue << 1) | 1; + + dram_store_u16(hle, &pixel, dst_addr, 1); + dst_addr += 2; + + x += x_ratio; + } + y += y_ratio; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} + +static uint32_t YCbCr_to_RGBA(uint8_t Y, uint8_t Cb, uint8_t Cr) +{ + int r, g, b; + + r = (int)(((double)Y * 0.582199097) + (0.701004028 * (double)(Cr - 128))); + g = (int)(((double)Y * 0.582199097) - (0.357070923 * (double)(Cr - 128)) - (0.172073364 * (double)(Cb - 128))); + b = (int)(((double)Y * 0.582199097) + (0.886001587 * (double)(Cb - 128))); + + r = SATURATE8(r); + g = SATURATE8(g); + b = SATURATE8(b); + + return (r << 24) | (g << 16) | (b << 8) | 0; +} + +void decode_video_frame_task(struct hle_t* hle) +{ + int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA); + + int pLuminance = *dram_u32(hle, data_ptr); + int pCb = *dram_u32(hle, data_ptr + 4); + int pCr = *dram_u32(hle, data_ptr + 8); + int pDestination = *dram_u32(hle, data_ptr + 12); + int nMovieWidth = *dram_u32(hle, data_ptr + 16); + int nMovieHeight = *dram_u32(hle, data_ptr + 20); +#if 0 /* unused, but keep it for documentation purpose */ + int nRowsPerDMEM = *dram_u32(hle, data_ptr + 24); + int nDMEMPerFrame = *dram_u32(hle, data_ptr + 28); + int nLengthSkipCount = *dram_u32(hle, data_ptr + 32); +#endif + int nScreenDMAIncrement = *dram_u32(hle, data_ptr + 36); + + int i, j; + uint8_t Y, Cb, Cr; + uint32_t pixel; + int pY_1st_row, pY_2nd_row, pDest_1st_row, pDest_2nd_row; + + for (i = 0; i < nMovieHeight; i += 2) + { + pY_1st_row = pLuminance; + pY_2nd_row = pLuminance + nMovieWidth; + pDest_1st_row = pDestination; + pDest_2nd_row = pDestination + (nScreenDMAIncrement >> 1); + + for (j = 0; j < nMovieWidth; j += 2) + { + dram_load_u8(hle, (uint8_t*)&Cb, pCb++, 1); + dram_load_u8(hle, (uint8_t*)&Cr, pCr++, 1); + + /*1st row*/ + dram_load_u8(hle, (uint8_t*)&Y, pY_1st_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_1st_row, 1); + pDest_1st_row += 4; + + dram_load_u8(hle, (uint8_t*)&Y, pY_1st_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_1st_row, 1); + pDest_1st_row += 4; + + /*2nd row*/ + dram_load_u8(hle, (uint8_t*)&Y, pY_2nd_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_2nd_row, 1); + pDest_2nd_row += 4; + + dram_load_u8(hle, (uint8_t*)&Y, pY_2nd_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_2nd_row, 1); + pDest_2nd_row += 4; + } + + pLuminance += (nMovieWidth << 1); + pDestination += nScreenDMAIncrement; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void fill_video_double_buffer_task(struct hle_t* hle) +{ + int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA); + + int pSrc = *dram_u32(hle, data_ptr); + int pDest = *dram_u32(hle, data_ptr + 0x4); + int width = *dram_u32(hle, data_ptr + 0x8) >> 1; + int height = *dram_u32(hle, data_ptr + 0x10) << 1; + int stride = *dram_u32(hle, data_ptr + 0x1c) >> 1; + + assert((*dram_u32(hle, data_ptr + 0x28) >> 16) == 0x8000); + +#if 0 /* unused, but keep it for documentation purpose */ + int arg3 = *dram_u32(hle, data_ptr + 0xc); + int arg5 = *dram_u32(hle, data_ptr + 0x14); + int arg6 = *dram_u32(hle, data_ptr + 0x18); +#endif + + int i, j; + int r, g, b; + uint32_t pixel, pixel1, pixel2; + + for(i = 0; i < height; i++) + { + for(j = 0; j < width; j=j+4) + { + pixel1 = *dram_u32(hle, pSrc+j); + pixel2 = *dram_u32(hle, pDest+j); + + r = (((pixel1 >> 24) & 0xff) + ((pixel2 >> 24) & 0xff)) >> 1; + g = (((pixel1 >> 16) & 0xff) + ((pixel2 >> 16) & 0xff)) >> 1; + b = (((pixel1 >> 8) & 0xff) + ((pixel2 >> 8) & 0xff)) >> 1; + + pixel = (r << 24) | (g << 16) | (b << 8) | 0; + + dram_store_u32(hle, &pixel, pDest+j, 1); + } + pSrc += stride; + pDest += stride; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode1.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode1.cpp deleted file mode 100644 index eb869cbb7b..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode1.cpp +++ /dev/null @@ -1,951 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode1.cpp * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include - -extern "C" { - #include "hle.h" - #include "alist_internal.h" -} - -//#include "rsp.h" -//#define SAFE_MEMORY -/* -#ifndef SAFE_MEMORY -# define wr8 (src , address); -# define rd8 (dest, address); -# define wr16 (src, address); -# define rd16 (dest, address); -# define wr32 (src, address); -# define rd32 (dest, address); -# define wr64 (src, address); -# define rd64 (dest, address); -# define dmamem (dest, src, size) memcpy (dest, src, size); -# define clrmem (dest, size) memset (dest, 0, size); -#else - void wr8 (u8 src, void *address); - void rd8 (u8 dest, void *address); - void wr16 (u16 src, void *address); - void rd16 (u16 dest, void *address); - void wr32 (u16 src, void *address); - void rd32 (u16 dest, void *address); - void wr64 (u16 src, void *address); - void rd64 (u16 dest, void *address); - void dmamem (void *dest, void *src, int size); - void clrmem (void *dest, int size); -#endif -*/ -/******** DMEM Memory Map for ABI 1 *************** -Address/Range Description -------------- ------------------------------- -0x000..0x2BF UCodeData - 0x000-0x00F Constants - 0000 0001 0002 FFFF 0020 0800 7FFF 4000 - 0x010-0x02F Function Jump Table (16 Functions * 2 bytes each = 32) 0x20 - 0x030-0x03F Constants - F000 0F00 00F0 000F 0001 0010 0100 1000 - 0x040-0x03F Used by the Envelope Mixer (But what for?) - 0x070-0x07F Used by the Envelope Mixer (But what for?) -0x2C0..0x31F -0x320..0x35F Segments -0x360 Audio In Buffer (Location) -0x362 Audio Out Buffer (Location) -0x364 Audio Buffer Size (Location) -0x366 Initial Volume for Left Channel -0x368 Initial Volume for Right Channel -0x36A Auxillary Buffer #1 (Location) -0x36C Auxillary Buffer #2 (Location) -0x36E Auxillary Buffer #3 (Location) -0x370 Loop Value (shared location) -0x370 Target Volume (Left) -0x372 Ramp?? (Left) -0x374 Rate?? (Left) -0x376 Target Volume (Right) -0x378 Ramp?? (Right) -0x37A Rate?? (Right) -0x37C Dry?? -0x37E Wet?? -0x380..0x4BF Alist data -0x4C0..0x4FF ADPCM CodeBook -0x500..0x5BF -0x5C0..0xF7F Buffers... -0xF80..0xFFF -***************************************************/ -#ifdef USE_EXPANSION - #define MEMMASK 0x7FFFFF -#else - #define MEMMASK 0x3FFFFF -#endif - -static void SPNOOP (u32 inst1, u32 inst2) { - //MessageBox (NULL, "Unknown Audio Command in ABI 1", "Audio HLE Error", MB_OK); -} - -u32 SEGMENTS[0x10]; // 0x0320 -// T8 = 0x360 -u16 AudioInBuffer; // 0x0000(T8) -u16 AudioOutBuffer; // 0x0002(T8) -u16 AudioCount; // 0x0004(T8) -s16 Vol_Left; // 0x0006(T8) -s16 Vol_Right; // 0x0008(T8) -u16 AudioAuxA; // 0x000A(T8) -u16 AudioAuxC; // 0x000C(T8) -u16 AudioAuxE; // 0x000E(T8) -u32 loopval; // 0x0010(T8) // Value set by A_SETLOOP : Possible conflict with SETVOLUME??? -s16 VolTrg_Left; // 0x0010(T8) -s32 VolRamp_Left; // m_LeftVolTarget -//u16 VolRate_Left; // m_LeftVolRate -s16 VolTrg_Right; // m_RightVol -s32 VolRamp_Right; // m_RightVolTarget -//u16 VolRate_Right; // m_RightVolRate -s16 Env_Dry; // 0x001C(T8) -s16 Env_Wet; // 0x001E(T8) - -u8 BufferSpace[0x10000]; - -short hleMixerWorkArea[256]; -u16 adpcmtable[0x88]; - -extern const u16 ResampleLUT [0x200] = { - 0x0C39, 0x66AD, 0x0D46, 0xFFDF, 0x0B39, 0x6696, 0x0E5F, 0xFFD8, - 0x0A44, 0x6669, 0x0F83, 0xFFD0, 0x095A, 0x6626, 0x10B4, 0xFFC8, - 0x087D, 0x65CD, 0x11F0, 0xFFBF, 0x07AB, 0x655E, 0x1338, 0xFFB6, - 0x06E4, 0x64D9, 0x148C, 0xFFAC, 0x0628, 0x643F, 0x15EB, 0xFFA1, - 0x0577, 0x638F, 0x1756, 0xFF96, 0x04D1, 0x62CB, 0x18CB, 0xFF8A, - 0x0435, 0x61F3, 0x1A4C, 0xFF7E, 0x03A4, 0x6106, 0x1BD7, 0xFF71, - 0x031C, 0x6007, 0x1D6C, 0xFF64, 0x029F, 0x5EF5, 0x1F0B, 0xFF56, - 0x022A, 0x5DD0, 0x20B3, 0xFF48, 0x01BE, 0x5C9A, 0x2264, 0xFF3A, - 0x015B, 0x5B53, 0x241E, 0xFF2C, 0x0101, 0x59FC, 0x25E0, 0xFF1E, - 0x00AE, 0x5896, 0x27A9, 0xFF10, 0x0063, 0x5720, 0x297A, 0xFF02, - 0x001F, 0x559D, 0x2B50, 0xFEF4, 0xFFE2, 0x540D, 0x2D2C, 0xFEE8, - 0xFFAC, 0x5270, 0x2F0D, 0xFEDB, 0xFF7C, 0x50C7, 0x30F3, 0xFED0, - 0xFF53, 0x4F14, 0x32DC, 0xFEC6, 0xFF2E, 0x4D57, 0x34C8, 0xFEBD, - 0xFF0F, 0x4B91, 0x36B6, 0xFEB6, 0xFEF5, 0x49C2, 0x38A5, 0xFEB0, - 0xFEDF, 0x47ED, 0x3A95, 0xFEAC, 0xFECE, 0x4611, 0x3C85, 0xFEAB, - 0xFEC0, 0x4430, 0x3E74, 0xFEAC, 0xFEB6, 0x424A, 0x4060, 0xFEAF, - 0xFEAF, 0x4060, 0x424A, 0xFEB6, 0xFEAC, 0x3E74, 0x4430, 0xFEC0, - 0xFEAB, 0x3C85, 0x4611, 0xFECE, 0xFEAC, 0x3A95, 0x47ED, 0xFEDF, - 0xFEB0, 0x38A5, 0x49C2, 0xFEF5, 0xFEB6, 0x36B6, 0x4B91, 0xFF0F, - 0xFEBD, 0x34C8, 0x4D57, 0xFF2E, 0xFEC6, 0x32DC, 0x4F14, 0xFF53, - 0xFED0, 0x30F3, 0x50C7, 0xFF7C, 0xFEDB, 0x2F0D, 0x5270, 0xFFAC, - 0xFEE8, 0x2D2C, 0x540D, 0xFFE2, 0xFEF4, 0x2B50, 0x559D, 0x001F, - 0xFF02, 0x297A, 0x5720, 0x0063, 0xFF10, 0x27A9, 0x5896, 0x00AE, - 0xFF1E, 0x25E0, 0x59FC, 0x0101, 0xFF2C, 0x241E, 0x5B53, 0x015B, - 0xFF3A, 0x2264, 0x5C9A, 0x01BE, 0xFF48, 0x20B3, 0x5DD0, 0x022A, - 0xFF56, 0x1F0B, 0x5EF5, 0x029F, 0xFF64, 0x1D6C, 0x6007, 0x031C, - 0xFF71, 0x1BD7, 0x6106, 0x03A4, 0xFF7E, 0x1A4C, 0x61F3, 0x0435, - 0xFF8A, 0x18CB, 0x62CB, 0x04D1, 0xFF96, 0x1756, 0x638F, 0x0577, - 0xFFA1, 0x15EB, 0x643F, 0x0628, 0xFFAC, 0x148C, 0x64D9, 0x06E4, - 0xFFB6, 0x1338, 0x655E, 0x07AB, 0xFFBF, 0x11F0, 0x65CD, 0x087D, - 0xFFC8, 0x10B4, 0x6626, 0x095A, 0xFFD0, 0x0F83, 0x6669, 0x0A44, - 0xFFD8, 0x0E5F, 0x6696, 0x0B39, 0xFFDF, 0x0D46, 0x66AD, 0x0C39 -}; - -static void CLEARBUFF (u32 inst1, u32 inst2) { - u32 addr = (u32)(inst1 & 0xffff); - u32 count = (u32)(inst2 & 0xffff); - addr &= 0xFFFC; - memset(BufferSpace+addr, 0, (count+3)&0xFFFC); -} - -//FILE *dfile = fopen ("d:\\envmix.txt", "wt"); - -static void ENVMIXER (u32 inst1, u32 inst2) { - //static int envmixcnt = 0; - u8 flags = (u8)((inst1 >> 16) & 0xff); - u32 addy = (inst2 & 0xFFFFFF);// + SEGMENTS[(inst2>>24)&0xf]; - //static -// ********* Make sure these conditions are met... *********** - /*if ((AudioInBuffer | AudioOutBuffer | AudioAuxA | AudioAuxC | AudioAuxE | AudioCount) & 0x3) { - MessageBox (NULL, "Unaligned EnvMixer... please report this to Azimer with the following information: RomTitle, Place in the rom it occurred, and any save state just before the error", "AudioHLE Error", MB_OK); - }*/ -// ------------------------------------------------------------ - short *inp=(short *)(BufferSpace+AudioInBuffer); - short *out=(short *)(BufferSpace+AudioOutBuffer); - short *aux1=(short *)(BufferSpace+AudioAuxA); - short *aux2=(short *)(BufferSpace+AudioAuxC); - short *aux3=(short *)(BufferSpace+AudioAuxE); - s32 MainR; - s32 MainL; - s32 AuxR; - s32 AuxL; - int i1,o1,a1,a2=0,a3=0; - unsigned short AuxIncRate=1; - short zero[8]; - memset(zero,0,16); - s32 LVol, RVol; - s32 LAcc, RAcc; - s32 LTrg, RTrg; - s16 Wet, Dry; - u32 ptr = 0; - s32 RRamp, LRamp; - s32 LAdderStart, RAdderStart, LAdderEnd, RAdderEnd; - s32 oMainR, oMainL, oAuxR, oAuxL; - - //envmixcnt++; - - //fprintf (dfile, "\n----------------------------------------------------\n"); - if (flags & A_INIT) { - LVol = ((Vol_Left * (s32)VolRamp_Left)); - RVol = ((Vol_Right * (s32)VolRamp_Right)); - Wet = (s16)Env_Wet; Dry = (s16)Env_Dry; // Save Wet/Dry values - LTrg = (VolTrg_Left << 16); RTrg = (VolTrg_Right << 16); // Save Current Left/Right Targets - LAdderStart = Vol_Left << 16; - RAdderStart = Vol_Right << 16; - LAdderEnd = LVol; - RAdderEnd = RVol; - RRamp = VolRamp_Right; - LRamp = VolRamp_Left; - } else { - // Load LVol, RVol, LAcc, and RAcc (all 32bit) - // Load Wet, Dry, LTrg, RTrg - memcpy((u8 *)hleMixerWorkArea, (rsp.RDRAM+addy), 80); - Wet = *(s16 *)(hleMixerWorkArea + 0); // 0-1 - Dry = *(s16 *)(hleMixerWorkArea + 2); // 2-3 - LTrg = *(s32 *)(hleMixerWorkArea + 4); // 4-5 - RTrg = *(s32 *)(hleMixerWorkArea + 6); // 6-7 - LRamp= *(s32 *)(hleMixerWorkArea + 8); // 8-9 (hleMixerWorkArea is a 16bit pointer) - RRamp= *(s32 *)(hleMixerWorkArea + 10); // 10-11 - LAdderEnd = *(s32 *)(hleMixerWorkArea + 12); // 12-13 - RAdderEnd = *(s32 *)(hleMixerWorkArea + 14); // 14-15 - LAdderStart = *(s32 *)(hleMixerWorkArea + 16); // 12-13 - RAdderStart = *(s32 *)(hleMixerWorkArea + 18); // 14-15 - } - - if(!(flags&A_AUX)) { - AuxIncRate=0; - aux2=aux3=zero; - } - - oMainL = (Dry * (LTrg>>16) + 0x4000) >> 15; - oAuxL = (Wet * (LTrg>>16) + 0x4000) >> 15; - oMainR = (Dry * (RTrg>>16) + 0x4000) >> 15; - oAuxR = (Wet * (RTrg>>16) + 0x4000) >> 15; - - for (int y = 0; y < AudioCount; y += 0x10) { - - if (LAdderStart != LTrg) { - LAcc = LAdderStart; - LVol = (LAdderEnd - LAdderStart) >> 3; - LAdderEnd = (s32) (((s64)LAdderEnd * (s64)LRamp) >> 16); - LAdderStart = (s32) (((s64)LAcc * (s64)LRamp) >> 16); - } else { - LAcc = LTrg; - LVol = 0; - } - - if (RAdderStart != RTrg) { - RAcc = RAdderStart; - RVol = (RAdderEnd - RAdderStart) >> 3; - RAdderEnd = (s32) (((s64)RAdderEnd * (s64)RRamp) >> 16); - RAdderStart = (s32) (((s64)RAcc * (s64)RRamp) >> 16); - } else { - RAcc = RTrg; - RVol = 0; - } - - for (int x = 0; x < 8; x++) { - i1=(int)inp[ptr^S]; - o1=(int)out[ptr^S]; - a1=(int)aux1[ptr^S]; - if (AuxIncRate) { - a2=(int)aux2[ptr^S]; - a3=(int)aux3[ptr^S]; - } - // TODO: here... - //LAcc = LTrg; - //RAcc = RTrg; - - LAcc += LVol; - RAcc += RVol; - - if (LVol <= 0) { // Decrementing - if (LAcc < LTrg) { - LAcc = LTrg; - LAdderStart = LTrg; - MainL = oMainL; - AuxL = oAuxL; - } else { - MainL = (Dry * ((s32)LAcc>>16) + 0x4000) >> 15; - AuxL = (Wet * ((s32)LAcc>>16) + 0x4000) >> 15; - } - } else { - if (LAcc > LTrg) { - LAcc = LTrg; - LAdderStart = LTrg; - MainL = oMainL; - AuxL = oAuxL; - } else { - MainL = (Dry * ((s32)LAcc>>16) + 0x4000) >> 15; - AuxL = (Wet * ((s32)LAcc>>16) + 0x4000) >> 15; - } - } - - if (RVol <= 0) { // Decrementing - if (RAcc < RTrg) { - RAcc = RTrg; - RAdderStart = RTrg; - MainR = oMainR; - AuxR = oAuxR; - } else { - MainR = (Dry * ((s32)RAcc>>16) + 0x4000) >> 15; - AuxR = (Wet * ((s32)RAcc>>16) + 0x4000) >> 15; - } - } else { - if (RAcc > RTrg) { - RAcc = RTrg; - RAdderStart = RTrg; - MainR = oMainR; - AuxR = oAuxR; - } else { - MainR = (Dry * ((s32)RAcc>>16) + 0x4000) >> 15; - AuxR = (Wet * ((s32)RAcc>>16) + 0x4000) >> 15; - } - } - - //fprintf (dfile, "%04X ", (LAcc>>16)); - - /*MainL = (((s64)Dry*2 * (s64)(LAcc>>16)) + 0x8000) >> 16; - MainR = (((s64)Dry*2 * (s64)(RAcc>>16)) + 0x8000) >> 16; - AuxL = (((s64)Wet*2 * (s64)(LAcc>>16)) + 0x8000) >> 16; - AuxR = (((s64)Wet*2 * (s64)(RAcc>>16)) + 0x8000) >> 16;*/ -/* - if (MainL>32767) MainL = 32767; - else if (MainL<-32768) MainL = -32768; - if (MainR>32767) MainR = 32767; - else if (MainR<-32768) MainR = -32768; - if (AuxL>32767) AuxL = 32767; - else if (AuxL<-32768) AuxR = -32768; - if (AuxR>32767) AuxR = 32767; - else if (AuxR<-32768) AuxR = -32768;*/ - /* - MainR = (Dry * RTrg + 0x10000) >> 15; - MainL = (Dry * LTrg + 0x10000) >> 15; - AuxR = (Wet * RTrg + 0x8000) >> 16; - AuxL = (Wet * LTrg + 0x8000) >> 16;*/ - - o1+=(/*(o1*0x7fff)+*/(i1*MainR)+0x4000)>>15; - a1+=(/*(a1*0x7fff)+*/(i1*MainL)+0x4000)>>15; - -/* o1=((s64)(((s64)o1*0xfffe)+((s64)i1*MainR*2)+0x8000)>>16); - - a1=((s64)(((s64)a1*0xfffe)+((s64)i1*MainL*2)+0x8000)>>16);*/ - - if(o1>32767) o1=32767; - else if(o1<-32768) o1=-32768; - - if(a1>32767) a1=32767; - else if(a1<-32768) a1=-32768; - - out[ptr^S]=o1; - aux1[ptr^S]=a1; - if (AuxIncRate) { - //a2=((s64)(((s64)a2*0xfffe)+((s64)i1*AuxR*2)+0x8000)>>16); - - //a3=((s64)(((s64)a3*0xfffe)+((s64)i1*AuxL*2)+0x8000)>>16); - a2+=(/*(a2*0x7fff)+*/(i1*AuxR)+0x4000)>>15; - a3+=(/*(a3*0x7fff)+*/(i1*AuxL)+0x4000)>>15; - - if(a2>32767) a2=32767; - else if(a2<-32768) a2=-32768; - - if(a3>32767) a3=32767; - else if(a3<-32768) a3=-32768; - - aux2[ptr^S]=a2; - aux3[ptr^S]=a3; - } - ptr++; - } - } - - /*LAcc = LAdderEnd; - RAcc = RAdderEnd;*/ - - *(s16 *)(hleMixerWorkArea + 0) = Wet; // 0-1 - *(s16 *)(hleMixerWorkArea + 2) = Dry; // 2-3 - *(s32 *)(hleMixerWorkArea + 4) = LTrg; // 4-5 - *(s32 *)(hleMixerWorkArea + 6) = RTrg; // 6-7 - *(s32 *)(hleMixerWorkArea + 8) = LRamp; // 8-9 (hleMixerWorkArea is a 16bit pointer) - *(s32 *)(hleMixerWorkArea + 10) = RRamp; // 10-11 - *(s32 *)(hleMixerWorkArea + 12) = LAdderEnd; // 12-13 - *(s32 *)(hleMixerWorkArea + 14) = RAdderEnd; // 14-15 - *(s32 *)(hleMixerWorkArea + 16) = LAdderStart; // 12-13 - *(s32 *)(hleMixerWorkArea + 18) = RAdderStart; // 14-15 - memcpy(rsp.RDRAM+addy, (u8 *)hleMixerWorkArea,80); -} - -static void RESAMPLE (u32 inst1, u32 inst2) { - unsigned char Flags=(u8)((inst1>>16)&0xff); - unsigned int Pitch=((inst1&0xffff))<<1; - u32 addy = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned int Accum=0; - unsigned int location; - s16 *lut/*, *lut2*/; - short *dst; - s16 *src; - dst=(short *)(BufferSpace); - src=(s16 *)(BufferSpace); - u32 srcPtr=(AudioInBuffer/2); - u32 dstPtr=(AudioOutBuffer/2); - s32 temp; - s32 accum; -/* - if (addy > (1024*1024*8)) - addy = (inst2 & 0xffffff); -*/ - srcPtr -= 4; - - if ((Flags & 0x1) == 0) { - //memcpy (src+srcPtr, rsp.RDRAM+addy, 0x8); - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = ((u16 *)rsp.RDRAM)[((addy/2)+x)^S]; - Accum = *(u16 *)(rsp.RDRAM+addy+10); - } else { - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = 0;//*(u16 *)(rsp.RDRAM+((addy+x)^2)); - } - - for(int i=0;i < ((AudioCount+0xf)&0xFFF0)/2;i++) { - //location = (((Accum * 0x40) >> 0x10) * 8); - // location is the fractional position between two samples - location = (Accum >> 0xa) * 4; - lut = (s16*)ResampleLUT + location; - - // mov eax, dword ptr [src+srcPtr]; - // movsx edx, word ptr [lut]; - // shl edx, 1 - // imul edx - // test eax, 08000h - // setz ecx - // shl ecx, 16 - // xor eax, 08000h - // add eax, ecx - // and edx, 0f000h - - // imul - temp = ((s32)*(s16*)(src+((srcPtr+0)^S))*((s32)((s16)lut[0]))); - accum = (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+1)^S))*((s32)((s16)lut[1]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+2)^S))*((s32)((s16)lut[2]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+3)^S))*((s32)((s16)lut[3]))); - accum += (s32)(temp >> 15); - - if (accum > 32767) accum = 32767; - if (accum < -32768) accum = -32768; - - dst[dstPtr^S] = (accum); - dstPtr++; - Accum += Pitch; - srcPtr += (Accum>>16); - Accum&=0xffff; - } - for (int x=0; x < 4; x++) - ((u16 *)rsp.RDRAM)[((addy/2)+x)^S] = src[(srcPtr+x)^S]; - //memcpy (RSWORK, src+srcPtr, 0x8); - *(u16 *)(rsp.RDRAM+addy+10) = Accum; -} - -static void SETVOL (u32 inst1, u32 inst2) { -// Might be better to unpack these depending on the flags... - u8 flags = (u8)((inst1 >> 16) & 0xff); - u16 vol = (s16)(inst1 & 0xffff); - //u16 voltarg =(u16)((inst2 >> 16)&0xffff); - u16 volrate = (u16)((inst2 & 0xffff)); - - if (flags & A_AUX) { - Env_Dry = (s16)vol; // m_MainVol - Env_Wet = (s16)volrate; // m_AuxVol - return; - } - - if(flags & A_VOL) { // Set the Source(start) Volumes - if(flags & A_LEFT) { - Vol_Left = (s16)vol; // m_LeftVolume - } else { // A_RIGHT - Vol_Right = (s16)vol; // m_RightVolume - } - return; - } - -//0x370 Loop Value (shared location) -//0x370 Target Volume (Left) -//u16 VolRamp_Left; // 0x0012(T8) - if(flags & A_LEFT) { // Set the Ramping values Target, Ramp - //loopval = (((u32)vol << 0x10) | (u32)voltarg); - VolTrg_Left = (s16)inst1; // m_LeftVol - //VolRamp_Left = (s32)inst2; - VolRamp_Left = (s32)inst2;//(u16)(inst2) | (s32)(s16)(inst2 << 0x10); - //fprintf (dfile, "Ramp Left: %f\n", (float)VolRamp_Left/65536.0); - //fprintf (dfile, "Ramp Left: %08X\n", inst2); - //VolRamp_Left = (s16)voltarg; // m_LeftVolTarget - //VolRate_Left = (s16)volrate; // m_LeftVolRate - } else { // A_RIGHT - VolTrg_Right = (s16)inst1; // m_RightVol - //VolRamp_Right = (s32)inst2; - VolRamp_Right = (s32)inst2;//(u16)(inst2 >> 0x10) | (s32)(s16)(inst2 << 0x10); - //fprintf (dfile, "Ramp Right: %f\n", (float)VolRamp_Right/65536.0); - //fprintf (dfile, "Ramp Right: %08X\n", inst2); - //VolRamp_Right = (s16)voltarg; // m_RightVolTarget - //VolRate_Right = (s16)volrate; // m_RightVolRate - } -} - -static void UNKNOWN (u32 inst1, u32 inst2) {} - -static void SETLOOP (u32 inst1, u32 inst2) { - loopval = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - //VolTrg_Left = (s16)(loopval>>16); // m_LeftVol - //VolRamp_Left = (s16)(loopval); // m_LeftVolTarget -} - -static void ADPCM (u32 inst1, u32 inst2) { // Work in progress! :) - unsigned char Flags=(u8)(inst1>>16)&0xff; - //unsigned short Gain=(u16)(inst1&0xffff); - unsigned int Address=(inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned short inPtr=0; - //short *out=(s16 *)(testbuff+(AudioOutBuffer>>2)); - short *out=(short *)(BufferSpace+AudioOutBuffer); - //unsigned char *in=(unsigned char *)(BufferSpace+AudioInBuffer); - short count=(short)AudioCount; - unsigned char icode; - unsigned char code; - int vscale; - unsigned short index; - unsigned short j; - int a[8]; - short *book1,*book2; -/* - if (Address > (1024*1024*8)) - Address = (inst2 & 0xffffff); -*/ - memset(out,0,32); - - if(!(Flags&0x1)) - { - if(Flags&0x2) { - memcpy(out,&rsp.RDRAM[loopval&MEMMASK],32); - } else { - memcpy(out,&rsp.RDRAM[Address],32); - } - } - - int l1=out[14^S]; - int l2=out[15^S]; - int inp1[8]; - int inp2[8]; - out+=16; - while(count>0) - { - // the first interation through, these values are - // either 0 in the case of A_INIT, from a special - // area of memory in the case of A_LOOP or just - // the values we calculated the last time - - code=BufferSpace[(AudioInBuffer+inPtr)^S8]; - index=code&0xf; - index<<=4; // index into the adpcm code table - book1=(short *)&adpcmtable[index]; - book2=book1+8; - code>>=4; // upper nibble is scale - vscale=(0x8000>>((12-code)-1)); // very strange. 0x8000 would be .5 in 16:16 format - // so this appears to be a fractional scale based - // on the 12 based inverse of the scale value. note - // that this could be negative, in which case we do - // not use the calculated vscale value... see the - // if(code>12) check below - - inPtr++; // coded adpcm data lies next - j=0; - while(j<8) // loop of 8, for 8 coded nibbles from 4 bytes - // which yields 8 short pcm values - { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp1[j]=(s16)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp1[j]=(s16)((icode&0xf)<<12); - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - j=0; - while(j<8) - { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp2[j]=(short)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp2[j]=(short)((icode&0xf)<<12); - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp1[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp1[0]; - a[1]+=(int)inp1[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp1[0]; - a[2]+=(int)book2[0]*inp1[1]; - a[2]+=(int)inp1[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp1[0]; - a[3]+=(int)book2[1]*inp1[1]; - a[3]+=(int)book2[0]*inp1[2]; - a[3]+=(int)inp1[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp1[0]; - a[4]+=(int)book2[2]*inp1[1]; - a[4]+=(int)book2[1]*inp1[2]; - a[4]+=(int)book2[0]*inp1[3]; - a[4]+=(int)inp1[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp1[0]; - a[5]+=(int)book2[3]*inp1[1]; - a[5]+=(int)book2[2]*inp1[2]; - a[5]+=(int)book2[1]*inp1[3]; - a[5]+=(int)book2[0]*inp1[4]; - a[5]+=(int)inp1[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp1[0]; - a[6]+=(int)book2[4]*inp1[1]; - a[6]+=(int)book2[3]*inp1[2]; - a[6]+=(int)book2[2]*inp1[3]; - a[6]+=(int)book2[1]*inp1[4]; - a[6]+=(int)book2[0]*inp1[5]; - a[6]+=(int)inp1[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp1[0]; - a[7]+=(int)book2[5]*inp1[1]; - a[7]+=(int)book2[4]*inp1[2]; - a[7]+=(int)book2[3]*inp1[3]; - a[7]+=(int)book2[2]*inp1[4]; - a[7]+=(int)book2[1]*inp1[5]; - a[7]+=(int)book2[0]*inp1[6]; - a[7]+=(int)inp1[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp2[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp2[0]; - a[1]+=(int)inp2[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp2[0]; - a[2]+=(int)book2[0]*inp2[1]; - a[2]+=(int)inp2[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp2[0]; - a[3]+=(int)book2[1]*inp2[1]; - a[3]+=(int)book2[0]*inp2[2]; - a[3]+=(int)inp2[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp2[0]; - a[4]+=(int)book2[2]*inp2[1]; - a[4]+=(int)book2[1]*inp2[2]; - a[4]+=(int)book2[0]*inp2[3]; - a[4]+=(int)inp2[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp2[0]; - a[5]+=(int)book2[3]*inp2[1]; - a[5]+=(int)book2[2]*inp2[2]; - a[5]+=(int)book2[1]*inp2[3]; - a[5]+=(int)book2[0]*inp2[4]; - a[5]+=(int)inp2[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp2[0]; - a[6]+=(int)book2[4]*inp2[1]; - a[6]+=(int)book2[3]*inp2[2]; - a[6]+=(int)book2[2]*inp2[3]; - a[6]+=(int)book2[1]*inp2[4]; - a[6]+=(int)book2[0]*inp2[5]; - a[6]+=(int)inp2[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp2[0]; - a[7]+=(int)book2[5]*inp2[1]; - a[7]+=(int)book2[4]*inp2[2]; - a[7]+=(int)book2[3]*inp2[3]; - a[7]+=(int)book2[2]*inp2[4]; - a[7]+=(int)book2[1]*inp2[5]; - a[7]+=(int)book2[0]*inp2[6]; - a[7]+=(int)inp2[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - count-=32; - } - out-=16; - memcpy(&rsp.RDRAM[Address],out,32); -} - -static void LOADBUFF (u32 inst1, u32 inst2) { // memcpy causes static... endianess issue :( - u32 v0; - //u32 cnt; - if (AudioCount == 0) - return; - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (BufferSpace+(AudioInBuffer&0xFFFC), rsp.RDRAM+v0, (AudioCount+3)&0xFFFC); -} - -static void SAVEBUFF (u32 inst1, u32 inst2) { // memcpy causes static... endianess issue :( - u32 v0; - //u32 cnt; - if (AudioCount == 0) - return; - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (rsp.RDRAM+v0, BufferSpace+(AudioOutBuffer&0xFFFC), (AudioCount+3)&0xFFFC); -} - -static void SETBUFF (u32 inst1, u32 inst2) { // Should work ;-) - if ((inst1 >> 0x10) & 0x8) { // A_AUX - Auxillary Sound Buffer Settings - AudioAuxA = u16(inst1); - AudioAuxC = u16((inst2 >> 0x10)); - AudioAuxE = u16(inst2); - } else { // A_MAIN - Main Sound Buffer Settings - AudioInBuffer = u16(inst1); // 0x00 - AudioOutBuffer = u16((inst2 >> 0x10)); // 0x02 - AudioCount = u16(inst2); // 0x04 - } -} - -static void DMEMMOVE (u32 inst1, u32 inst2) { // Doesn't sound just right?... will fix when HLE is ready - 03-11-01 - u32 v0, v1; - u32 cnt; - if ((inst2 & 0xffff)==0) - return; - v0 = (inst1 & 0xFFFF); - v1 = (inst2 >> 0x10); - //assert ((v1 & 0x3) == 0); - //assert ((v0 & 0x3) == 0); - u32 count = ((inst2+3) & 0xfffc); - //v0 = (v0) & 0xfffc; - //v1 = (v1) & 0xfffc; - - //memcpy (BufferSpace+v1, BufferSpace+v0, count-1); - for (cnt = 0; cnt < count; cnt++) { - *(u8 *)(BufferSpace+((cnt+v1)^S8)) = *(u8 *)(BufferSpace+((cnt+v0)^S8)); - } -} - -static void LOADADPCM (u32 inst1, u32 inst2) { // Loads an ADPCM table - Works 100% Now 03-13-01 - u32 v0; - v0 = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; -/* if (v0 > (1024*1024*8)) - v0 = (inst2 & 0xffffff);*/ - //memcpy (dmem+0x4c0, rsp.RDRAM+v0, inst1&0xffff); // Could prolly get away with not putting this in dmem - //assert ((inst1&0xffff) <= 0x80); - u16 *table = (u16 *)(rsp.RDRAM+v0); - for (u32 x = 0; x < ((inst1&0xffff)>>0x4); x++) { - adpcmtable[(0x0+(x<<3))^S] = table[0]; - adpcmtable[(0x1+(x<<3))^S] = table[1]; - - adpcmtable[(0x2+(x<<3))^S] = table[2]; - adpcmtable[(0x3+(x<<3))^S] = table[3]; - - adpcmtable[(0x4+(x<<3))^S] = table[4]; - adpcmtable[(0x5+(x<<3))^S] = table[5]; - - adpcmtable[(0x6+(x<<3))^S] = table[6]; - adpcmtable[(0x7+(x<<3))^S] = table[7]; - table += 8; - } -} - - -static void INTERLEAVE (u32 inst1, u32 inst2) { // Works... - 3-11-01 - u32 inL, inR; - u16 *outbuff = (u16 *)(AudioOutBuffer+BufferSpace); - u16 *inSrcR; - u16 *inSrcL; - u16 Left, Right, Left2, Right2; - - inL = inst2 & 0xFFFF; - inR = (inst2 >> 16) & 0xFFFF; - - inSrcR = (u16 *)(BufferSpace+inR); - inSrcL = (u16 *)(BufferSpace+inL); - - for (int x = 0; x < (AudioCount/4); x++) { - Left=*(inSrcL++); - Right=*(inSrcR++); - Left2=*(inSrcL++); - Right2=*(inSrcR++); - -#ifdef M64P_BIG_ENDIAN - *(outbuff++)=Right; - *(outbuff++)=Left; - *(outbuff++)=Right2; - *(outbuff++)=Left2; -#else - *(outbuff++)=Right2; - *(outbuff++)=Left2; - *(outbuff++)=Right; - *(outbuff++)=Left; -#endif - } -} - - -static void MIXER (u32 inst1, u32 inst2) { // Fixed a sign issue... 03-14-01 - u32 dmemin = (u16)(inst2 >> 0x10); - u32 dmemout = (u16)(inst2 & 0xFFFF); - //u8 flags = (u8)((inst1 >> 16) & 0xff); - s32 gain = (s16)(inst1 & 0xFFFF); - s32 temp; - - if (AudioCount == 0) - return; - - for (int x=0; x < AudioCount; x+=2) { // I think I can do this a lot easier - temp = (*(s16 *)(BufferSpace+dmemin+x) * gain) >> 15; - temp += *(s16 *)(BufferSpace+dmemout+x); - - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} - -// TOP Performance Hogs: -//Command: ADPCM - Calls: 48 - Total Time: 331226 - Avg Time: 6900.54 - Percent: 31.53% -//Command: ENVMIXER - Calls: 48 - Total Time: 408563 - Avg Time: 8511.73 - Percent: 38.90% -//Command: LOADBUFF - Calls: 56 - Total Time: 21551 - Avg Time: 384.84 - Percent: 2.05% -//Command: RESAMPLE - Calls: 48 - Total Time: 225922 - Avg Time: 4706.71 - Percent: 21.51% - -//Command: ADPCM - Calls: 48 - Total Time: 391600 - Avg Time: 8158.33 - Percent: 32.52% -//Command: ENVMIXER - Calls: 48 - Total Time: 444091 - Avg Time: 9251.90 - Percent: 36.88% -//Command: LOADBUFF - Calls: 58 - Total Time: 29945 - Avg Time: 516.29 - Percent: 2.49% -//Command: RESAMPLE - Calls: 48 - Total Time: 276354 - Avg Time: 5757.38 - Percent: 22.95% - - -extern "C" const acmd_callback_t ABI1[0x10] = { // TOP Performace Hogs: MIXER, RESAMPLE, ENVMIXER - SPNOOP , ADPCM , CLEARBUFF, ENVMIXER , LOADBUFF, RESAMPLE , SAVEBUFF, UNKNOWN, - SETBUFF, SETVOL, DMEMMOVE , LOADADPCM , MIXER , INTERLEAVE, UNKNOWN , SETLOOP -}; - -/* BACKUPS -void MIXER (u32 inst1, u32 inst2) { // Fixed a sign issue... 03-14-01 - u16 dmemin = (u16)(inst2 >> 0x10); - u16 dmemout = (u16)(inst2 & 0xFFFF); - u16 gain = (u16)(inst1 & 0xFFFF); - u8 flags = (u8)((inst1 >> 16) & 0xff); - u64 temp; - - if (AudioCount == 0) - return; - - for (int x=0; x < AudioCount; x+=2) { // I think I can do this a lot easier - temp = (s64)(*(s16 *)(BufferSpace+dmemout+x)) * (s64)((s16)(0x7FFF)*2); - - if (temp & 0x8000) - temp = (temp^0x8000) + 0x10000; - else - temp = (temp^0x8000); - - temp = (temp & 0xFFFFFFFFFFFF); - - temp += ((*(s16 *)(BufferSpace+dmemin+x) * (s64)((s16)gain*2))) & 0xFFFFFFFFFFFF; - - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} -*/ - - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode2.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode2.cpp deleted file mode 100644 index dd15689fcb..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode2.cpp +++ /dev/null @@ -1,930 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode2.cpp * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include -# include - -extern "C" { - #include "m64p_types.h" - #include "hle.h" - #include "alist_internal.h" -} - -extern u8 BufferSpace[0x10000]; - -static void SPNOOP (u32 inst1, u32 inst2) { - DebugMessage(M64MSG_ERROR, "Unknown/Unimplemented Audio Command %i in ABI 2", (int)(inst1 >> 24)); -} -extern u16 AudioInBuffer; // 0x0000(T8) -extern u16 AudioOutBuffer; // 0x0002(T8) -extern u16 AudioCount; // 0x0004(T8) -extern u32 loopval; // 0x0010(T8) -extern u32 SEGMENTS[0x10]; - -extern u16 adpcmtable[0x88]; - -extern const u16 ResampleLUT [0x200]; - -bool isMKABI = false; -bool isZeldaABI = false; - -extern "C" void init_ucode2() { isMKABI = isZeldaABI = false; } - -static void LOADADPCM2 (u32 inst1, u32 inst2) { // Loads an ADPCM table - Works 100% Now 03-13-01 - u32 v0; - v0 = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - u16 *table = (u16 *)(rsp.RDRAM+v0); // Zelda2 Specific... - - for (u32 x = 0; x < ((inst1&0xffff)>>0x4); x++) { - adpcmtable[(0x0+(x<<3))^S] = table[0]; - adpcmtable[(0x1+(x<<3))^S] = table[1]; - - adpcmtable[(0x2+(x<<3))^S] = table[2]; - adpcmtable[(0x3+(x<<3))^S] = table[3]; - - adpcmtable[(0x4+(x<<3))^S] = table[4]; - adpcmtable[(0x5+(x<<3))^S] = table[5]; - - adpcmtable[(0x6+(x<<3))^S] = table[6]; - adpcmtable[(0x7+(x<<3))^S] = table[7]; - table += 8; - } -} - -static void SETLOOP2 (u32 inst1, u32 inst2) { - loopval = inst2 & 0xffffff; // No segment? -} - -static void SETBUFF2 (u32 inst1, u32 inst2) { - AudioInBuffer = u16(inst1); // 0x00 - AudioOutBuffer = u16((inst2 >> 0x10)); // 0x02 - AudioCount = u16(inst2); // 0x04 -} - -static void ADPCM2 (u32 inst1, u32 inst2) { // Verified to be 100% Accurate... - unsigned char Flags=(u8)(inst1>>16)&0xff; - //unsigned short Gain=(u16)(inst1&0xffff); - unsigned int Address=(inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned short inPtr=0; - //short *out=(s16 *)(testbuff+(AudioOutBuffer>>2)); - short *out=(short *)(BufferSpace+AudioOutBuffer); - //unsigned char *in=(unsigned char *)(BufferSpace+AudioInBuffer); - short count=(short)AudioCount; - unsigned char icode; - unsigned char code; - int vscale; - unsigned short index; - unsigned short j; - int a[8]; - short *book1,*book2; - - u8 srange; - u8 mask1; - u8 mask2; - u8 shifter; - - memset(out,0,32); - - if (Flags & 0x4) { // Tricky lil Zelda MM and ABI2!!! hahaha I know your secrets! :DDD - srange = 0xE; - mask1 = 0xC0; - mask2 = 0x30; - shifter = 10; - } else { - srange = 0xC; - mask1 = 0xf0; - mask2 = 0x0f; - shifter = 12; - } - - if(!(Flags&0x1)) - { - if(Flags&0x2) - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(loopval+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[loopval],32); - } - else - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(Address+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[Address],32); - } - } - - int l1=out[14^S]; - int l2=out[15^S]; - int inp1[8]; - int inp2[8]; - out+=16; - while(count>0) { - code=BufferSpace[(AudioInBuffer+inPtr)^S8]; - index=code&0xf; - index<<=4; - book1=(short *)&adpcmtable[index]; - book2=book1+8; - code>>=4; - vscale=(0x8000>>((srange-code)-1)); - - inPtr++; - j=0; - - while(j<8) { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp1[j]=(s16)((icode&mask1) << 8); // this will in effect be signed - if(code>16); - //else int catchme=1; - j++; - - inp1[j]=(s16)((icode&mask2)<>16); - //else int catchme=1; - j++; - - if (Flags & 4) { - inp1[j]=(s16)((icode&0xC) << 12); // this will in effect be signed - if(code < 0xE) inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - - inp1[j]=(s16)((icode&0x3) << 14); - if(code < 0xE) inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - } // end flags - } // end while - - - - j=0; - while(j<8) { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp2[j]=(s16)((icode&mask1) << 8); - if(code>16); - //else int catchme=1; - j++; - - inp2[j]=(s16)((icode&mask2)<>16); - //else int catchme=1; - j++; - - if (Flags & 4) { - inp2[j]=(s16)((icode&0xC) << 12); - if(code < 0xE) inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - - inp2[j]=(s16)((icode&0x3) << 14); - if(code < 0xE) inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - } // end flags - } - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp1[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp1[0]; - a[1]+=(int)inp1[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp1[0]; - a[2]+=(int)book2[0]*inp1[1]; - a[2]+=(int)inp1[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp1[0]; - a[3]+=(int)book2[1]*inp1[1]; - a[3]+=(int)book2[0]*inp1[2]; - a[3]+=(int)inp1[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp1[0]; - a[4]+=(int)book2[2]*inp1[1]; - a[4]+=(int)book2[1]*inp1[2]; - a[4]+=(int)book2[0]*inp1[3]; - a[4]+=(int)inp1[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp1[0]; - a[5]+=(int)book2[3]*inp1[1]; - a[5]+=(int)book2[2]*inp1[2]; - a[5]+=(int)book2[1]*inp1[3]; - a[5]+=(int)book2[0]*inp1[4]; - a[5]+=(int)inp1[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp1[0]; - a[6]+=(int)book2[4]*inp1[1]; - a[6]+=(int)book2[3]*inp1[2]; - a[6]+=(int)book2[2]*inp1[3]; - a[6]+=(int)book2[1]*inp1[4]; - a[6]+=(int)book2[0]*inp1[5]; - a[6]+=(int)inp1[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp1[0]; - a[7]+=(int)book2[5]*inp1[1]; - a[7]+=(int)book2[4]*inp1[2]; - a[7]+=(int)book2[3]*inp1[3]; - a[7]+=(int)book2[2]*inp1[4]; - a[7]+=(int)book2[1]*inp1[5]; - a[7]+=(int)book2[0]*inp1[6]; - a[7]+=(int)inp1[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp2[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp2[0]; - a[1]+=(int)inp2[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp2[0]; - a[2]+=(int)book2[0]*inp2[1]; - a[2]+=(int)inp2[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp2[0]; - a[3]+=(int)book2[1]*inp2[1]; - a[3]+=(int)book2[0]*inp2[2]; - a[3]+=(int)inp2[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp2[0]; - a[4]+=(int)book2[2]*inp2[1]; - a[4]+=(int)book2[1]*inp2[2]; - a[4]+=(int)book2[0]*inp2[3]; - a[4]+=(int)inp2[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp2[0]; - a[5]+=(int)book2[3]*inp2[1]; - a[5]+=(int)book2[2]*inp2[2]; - a[5]+=(int)book2[1]*inp2[3]; - a[5]+=(int)book2[0]*inp2[4]; - a[5]+=(int)inp2[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp2[0]; - a[6]+=(int)book2[4]*inp2[1]; - a[6]+=(int)book2[3]*inp2[2]; - a[6]+=(int)book2[2]*inp2[3]; - a[6]+=(int)book2[1]*inp2[4]; - a[6]+=(int)book2[0]*inp2[5]; - a[6]+=(int)inp2[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp2[0]; - a[7]+=(int)book2[5]*inp2[1]; - a[7]+=(int)book2[4]*inp2[2]; - a[7]+=(int)book2[3]*inp2[3]; - a[7]+=(int)book2[2]*inp2[4]; - a[7]+=(int)book2[1]*inp2[5]; - a[7]+=(int)book2[0]*inp2[6]; - a[7]+=(int)inp2[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - count-=32; - } - out-=16; - memcpy(&rsp.RDRAM[Address],out,32); -} - -static void CLEARBUFF2 (u32 inst1, u32 inst2) { - u16 addr = (u16)(inst1 & 0xffff); - u16 count = (u16)(inst2 & 0xffff); - if (count > 0) - memset(BufferSpace+addr, 0, count); -} - -static void LOADBUFF2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (BufferSpace+(inst1&0xfffc), rsp.RDRAM+v0, (cnt+3)&0xFFFC); -} - -static void SAVEBUFF2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (rsp.RDRAM+v0, BufferSpace+(inst1&0xfffc), (cnt+3)&0xFFFC); -} - - -static void MIXER2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u16 dmemin = (u16)(inst2 >> 0x10); - u16 dmemout = (u16)(inst2 & 0xFFFF); - u32 count = ((inst1 >> 12) & 0xFF0); - s32 gain = (s16)(inst1 & 0xFFFF); - s32 temp; - - for (unsigned int x=0; x < count; x+=2) { // I think I can do this a lot easier - - temp = (*(s16 *)(BufferSpace+dmemin+x) * gain) >> 15; - temp += *(s16 *)(BufferSpace+dmemout+x); - - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} - - -static void RESAMPLE2 (u32 inst1, u32 inst2) { - unsigned char Flags=(u8)((inst1>>16)&0xff); - unsigned int Pitch=((inst1&0xffff))<<1; - u32 addy = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned int Accum=0; - unsigned int location; - s16 *lut; - short *dst; - s16 *src; - dst=(short *)(BufferSpace); - src=(s16 *)(BufferSpace); - u32 srcPtr=(AudioInBuffer/2); - u32 dstPtr=(AudioOutBuffer/2); - s32 temp; - s32 accum; - - if (addy > (1024*1024*8)) - addy = (inst2 & 0xffffff); - - srcPtr -= 4; - - if ((Flags & 0x1) == 0) { - for (int x=0; x < 4; x++) //memcpy (src+srcPtr, rsp.RDRAM+addy, 0x8); - src[(srcPtr+x)^S] = ((u16 *)rsp.RDRAM)[((addy/2)+x)^S]; - Accum = *(u16 *)(rsp.RDRAM+addy+10); - } else { - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = 0;//*(u16 *)(rsp.RDRAM+((addy+x)^2)); - } - - for(int i=0;i < ((AudioCount+0xf)&0xFFF0)/2;i++) { - location = (((Accum * 0x40) >> 0x10) * 8); - //location = (Accum >> 0xa) << 0x3; - lut = (s16 *)(((u8 *)ResampleLUT) + location); - - temp = ((s32)*(s16*)(src+((srcPtr+0)^S))*((s32)((s16)lut[0]))); - accum = (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+1)^S))*((s32)((s16)lut[1]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+2)^S))*((s32)((s16)lut[2]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+3)^S))*((s32)((s16)lut[3]))); - accum += (s32)(temp >> 15); - - if (accum > 32767) accum = 32767; - if (accum < -32768) accum = -32768; - - dst[dstPtr^S] = (s16)(accum); - dstPtr++; - Accum += Pitch; - srcPtr += (Accum>>16); - Accum&=0xffff; - } - for (int x=0; x < 4; x++) - ((u16 *)rsp.RDRAM)[((addy/2)+x)^S] = src[(srcPtr+x)^S]; - *(u16 *)(rsp.RDRAM+addy+10) = (u16)Accum; - //memcpy (RSWORK, src+srcPtr, 0x8); -} - -static void DMEMMOVE2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0, v1; - u32 cnt; - if ((inst2 & 0xffff)==0) - return; - v0 = (inst1 & 0xFFFF); - v1 = (inst2 >> 0x10); - //assert ((v1 & 0x3) == 0); - //assert ((v0 & 0x3) == 0); - u32 count = ((inst2+3) & 0xfffc); - //v0 = (v0) & 0xfffc; - //v1 = (v1) & 0xfffc; - - //memcpy (dmem+v1, dmem+v0, count-1); - for (cnt = 0; cnt < count; cnt++) { - *(u8 *)(BufferSpace+((cnt+v1)^S8)) = *(u8 *)(BufferSpace+((cnt+v0)^S8)); - } -} - -static u32 t3, s5, s6; -static u16 env[8]; - -static void ENVSETUP1 (u32 inst1, u32 inst2) { - u32 tmp; - - //fprintf (dfile, "ENVSETUP1: inst1 = %08X, inst2 = %08X\n", inst1, inst2); - t3 = inst1 & 0xFFFF; - tmp = (inst1 >> 0x8) & 0xFF00; - env[4] = (u16)tmp; - tmp += t3; - env[5] = (u16)tmp; - s5 = inst2 >> 0x10; - s6 = inst2 & 0xFFFF; - //fprintf (dfile, " t3 = %X / s5 = %X / s6 = %X / env[4] = %X / env[5] = %X\n", t3, s5, s6, env[4], env[5]); -} - -static void ENVSETUP2 (u32 inst1, u32 inst2) { - u32 tmp; - - //fprintf (dfile, "ENVSETUP2: inst1 = %08X, inst2 = %08X\n", inst1, inst2); - tmp = (inst2 >> 0x10); - env[0] = (u16)tmp; - tmp += s5; - env[1] = (u16)tmp; - tmp = inst2 & 0xffff; - env[2] = (u16)tmp; - tmp += s6; - env[3] = (u16)tmp; - //fprintf (dfile, " env[0] = %X / env[1] = %X / env[2] = %X / env[3] = %X\n", env[0], env[1], env[2], env[3]); -} - -static void ENVMIXER2 (u32 inst1, u32 inst2) { - //fprintf (dfile, "ENVMIXER: inst1 = %08X, inst2 = %08X\n", inst1, inst2); - - s16 *bufft6, *bufft7, *buffs0, *buffs1; - s16 *buffs3; - s32 count; - u32 adder; - - s16 vec9, vec10; - - s16 v2[8]; - - buffs3 = (s16 *)(BufferSpace + ((inst1 >> 0x0c)&0x0ff0)); - bufft6 = (s16 *)(BufferSpace + ((inst2 >> 0x14)&0x0ff0)); - bufft7 = (s16 *)(BufferSpace + ((inst2 >> 0x0c)&0x0ff0)); - buffs0 = (s16 *)(BufferSpace + ((inst2 >> 0x04)&0x0ff0)); - buffs1 = (s16 *)(BufferSpace + ((inst2 << 0x04)&0x0ff0)); - - - v2[0] = 0 - (s16)((inst1 & 0x2) >> 1); - v2[1] = 0 - (s16)((inst1 & 0x1)); - v2[2] = 0 - (s16)((inst1 & 0x8) >> 1); - v2[3] = 0 - (s16)((inst1 & 0x4) >> 1); - - count = (inst1 >> 8) & 0xff; - - if (!isMKABI) { - s5 *= 2; s6 *= 2; t3 *= 2; - adder = 0x10; - } else { - inst1 = 0; - adder = 0x8; - t3 = 0; - } - - - while (count > 0) { - int temp, x; - for (x=0; x < 0x8; x++) { - vec9 = (s16)(((s32)buffs3[x^S] * (u32)env[0]) >> 0x10) ^ v2[0]; - vec10 = (s16)(((s32)buffs3[x^S] * (u32)env[2]) >> 0x10) ^ v2[1]; - temp = bufft6[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft6[x^S] = temp; - temp = bufft7[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft7[x^S] = temp; - vec9 = (s16)(((s32)vec9 * (u32)env[4]) >> 0x10) ^ v2[2]; - vec10 = (s16)(((s32)vec10 * (u32)env[4]) >> 0x10) ^ v2[3]; - if (inst1 & 0x10) { - temp = buffs0[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } else { - temp = buffs0[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } - } - - if (!isMKABI) - for (x=0x8; x < 0x10; x++) { - vec9 = (s16)(((s32)buffs3[x^S] * (u32)env[1]) >> 0x10) ^ v2[0]; - vec10 = (s16)(((s32)buffs3[x^S] * (u32)env[3]) >> 0x10) ^ v2[1]; - temp = bufft6[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft6[x^S] = temp; - temp = bufft7[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft7[x^S] = temp; - vec9 = (s16)(((s32)vec9 * (u32)env[5]) >> 0x10) ^ v2[2]; - vec10 = (s16)(((s32)vec10 * (u32)env[5]) >> 0x10) ^ v2[3]; - if (inst1 & 0x10) { - temp = buffs0[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } else { - temp = buffs0[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } - } - bufft6 += adder; bufft7 += adder; - buffs0 += adder; buffs1 += adder; - buffs3 += adder; count -= adder; - env[0] += (u16)s5; env[1] += (u16)s5; - env[2] += (u16)s6; env[3] += (u16)s6; - env[4] += (u16)t3; env[5] += (u16)t3; - } -} - -static void DUPLICATE2(u32 inst1, u32 inst2) { - unsigned short Count = (inst1 >> 16) & 0xff; - unsigned short In = inst1&0xffff; - unsigned short Out = (inst2>>16); - - unsigned short buff[64]; - - memcpy(buff,BufferSpace+In,128); - - while(Count) { - memcpy(BufferSpace+Out,buff,128); - Out+=128; - Count--; - } -} -/* -static void INTERL2 (u32 inst1, u32 inst2) { // Make your own... - short Count = inst1 & 0xffff; - unsigned short Out = inst2 & 0xffff; - unsigned short In = (inst2 >> 16); - - short *src,*dst,tmp; - src=(short *)&BufferSpace[In]; - dst=(short *)&BufferSpace[Out]; - while(Count) - { - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - Count-=8; - } -} -*/ - -static void INTERL2 (u32 inst1, u32 inst2) { - short Count = inst1 & 0xffff; - unsigned short Out = inst2 & 0xffff; - unsigned short In = (inst2 >> 16); - - unsigned char *src,*dst/*,tmp*/; - src=(unsigned char *)(BufferSpace);//[In]; - dst=(unsigned char *)(BufferSpace);//[Out]; - while(Count) { - *(short *)(dst+(Out^S8)) = *(short *)(src+(In^S8)); - Out += 2; - In += 4; - Count--; - } -} - -static void INTERLEAVE2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 inL, inR; - u16 *outbuff; - u16 *inSrcR; - u16 *inSrcL; - u16 Left, Right, Left2, Right2; - u32 count; - count = ((inst1 >> 12) & 0xFF0); - if (count == 0) { - outbuff = (u16 *)(AudioOutBuffer+BufferSpace); - count = AudioCount; - } else { - outbuff = (u16 *)((inst1&0xFFFF)+BufferSpace); - } - - inR = inst2 & 0xFFFF; - inL = (inst2 >> 16) & 0xFFFF; - - inSrcR = (u16 *)(BufferSpace+inR); - inSrcL = (u16 *)(BufferSpace+inL); - - for (u32 x = 0; x < (count/4); x++) { - Left=*(inSrcL++); - Right=*(inSrcR++); - Left2=*(inSrcL++); - Right2=*(inSrcR++); - -#ifdef M64P_BIG_ENDIAN - *(outbuff++)=Right; - *(outbuff++)=Left; - *(outbuff++)=Right2; - *(outbuff++)=Left2; -#else - *(outbuff++)=Right2; - *(outbuff++)=Left2; - *(outbuff++)=Right; - *(outbuff++)=Left; -#endif - } -} - -static void ADDMIXER (u32 inst1, u32 inst2) { - short Count = (inst1 >> 12) & 0x00ff0; - u16 InBuffer = (inst2 >> 16); - u16 OutBuffer = inst2 & 0xffff; - - s16 *inp, *outp; - s32 temp; - inp = (s16 *)(BufferSpace + InBuffer); - outp = (s16 *)(BufferSpace + OutBuffer); - for (int cntr = 0; cntr < Count; cntr+=2) { - temp = *outp + *inp; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - *(outp++) = temp; - inp++; - } -} - -static void HILOGAIN (u32 inst1, u32 inst2) { - u16 cnt = inst1 & 0xffff; - u16 out = (inst2 >> 16) & 0xffff; - s16 hi = (s16)((inst1 >> 4) & 0xf000); - u16 lo = (inst1 >> 20) & 0xf; - s16 *src; - - src = (s16 *)(BufferSpace+out); - s32 tmp, val; - - while(cnt) { - val = (s32)*src; - //tmp = ((val * (s32)hi) + ((u64)(val * lo) << 16) >> 16); - tmp = ((val * (s32)hi) >> 16) + (u32)(val * lo); - if ((s32)tmp > 32767) tmp = 32767; - else if ((s32)tmp < -32768) tmp = -32768; - *src = tmp; - src++; - cnt -= 2; - } -} - -static void FILTER2 (u32 inst1, u32 inst2) { - static int cnt = 0; - static s16 *lutt6; - static s16 *lutt5; - u8 *save = (rsp.RDRAM+(inst2&0xFFFFFF)); - u8 t4 = (u8)((inst1 >> 0x10) & 0xFF); - int x; - - if (t4 > 1) { // Then set the cnt variable - cnt = (inst1 & 0xFFFF); - lutt6 = (s16 *)save; -// memcpy (dmem+0xFE0, rsp.RDRAM+(inst2&0xFFFFFF), 0x10); - return; - } - - if (t4 == 0) { -// memcpy (dmem+0xFB0, rsp.RDRAM+(inst2&0xFFFFFF), 0x20); - lutt5 = (short *)(save+0x10); - } - - lutt5 = (short *)(save+0x10); - -// lutt5 = (short *)(dmem + 0xFC0); -// lutt6 = (short *)(dmem + 0xFE0); - for (x = 0; x < 8; x++) { - s32 a; - a = (lutt5[x] + lutt6[x]) >> 1; - lutt5[x] = lutt6[x] = (short)a; - } - short *inp1, *inp2; - s32 out1[8]; - s16 outbuff[0x3c0], *outp; - u32 inPtr = (u32)(inst1&0xffff); - inp1 = (short *)(save); - outp = outbuff; - inp2 = (short *)(BufferSpace+inPtr); - for (x = 0; x < cnt; x+=0x10) { - out1[1] = inp1[0]*lutt6[6]; - out1[1] += inp1[3]*lutt6[7]; - out1[1] += inp1[2]*lutt6[4]; - out1[1] += inp1[5]*lutt6[5]; - out1[1] += inp1[4]*lutt6[2]; - out1[1] += inp1[7]*lutt6[3]; - out1[1] += inp1[6]*lutt6[0]; - out1[1] += inp2[1]*lutt6[1]; // 1 - - out1[0] = inp1[3]*lutt6[6]; - out1[0] += inp1[2]*lutt6[7]; - out1[0] += inp1[5]*lutt6[4]; - out1[0] += inp1[4]*lutt6[5]; - out1[0] += inp1[7]*lutt6[2]; - out1[0] += inp1[6]*lutt6[3]; - out1[0] += inp2[1]*lutt6[0]; - out1[0] += inp2[0]*lutt6[1]; - - out1[3] = inp1[2]*lutt6[6]; - out1[3] += inp1[5]*lutt6[7]; - out1[3] += inp1[4]*lutt6[4]; - out1[3] += inp1[7]*lutt6[5]; - out1[3] += inp1[6]*lutt6[2]; - out1[3] += inp2[1]*lutt6[3]; - out1[3] += inp2[0]*lutt6[0]; - out1[3] += inp2[3]*lutt6[1]; - - out1[2] = inp1[5]*lutt6[6]; - out1[2] += inp1[4]*lutt6[7]; - out1[2] += inp1[7]*lutt6[4]; - out1[2] += inp1[6]*lutt6[5]; - out1[2] += inp2[1]*lutt6[2]; - out1[2] += inp2[0]*lutt6[3]; - out1[2] += inp2[3]*lutt6[0]; - out1[2] += inp2[2]*lutt6[1]; - - out1[5] = inp1[4]*lutt6[6]; - out1[5] += inp1[7]*lutt6[7]; - out1[5] += inp1[6]*lutt6[4]; - out1[5] += inp2[1]*lutt6[5]; - out1[5] += inp2[0]*lutt6[2]; - out1[5] += inp2[3]*lutt6[3]; - out1[5] += inp2[2]*lutt6[0]; - out1[5] += inp2[5]*lutt6[1]; - - out1[4] = inp1[7]*lutt6[6]; - out1[4] += inp1[6]*lutt6[7]; - out1[4] += inp2[1]*lutt6[4]; - out1[4] += inp2[0]*lutt6[5]; - out1[4] += inp2[3]*lutt6[2]; - out1[4] += inp2[2]*lutt6[3]; - out1[4] += inp2[5]*lutt6[0]; - out1[4] += inp2[4]*lutt6[1]; - - out1[7] = inp1[6]*lutt6[6]; - out1[7] += inp2[1]*lutt6[7]; - out1[7] += inp2[0]*lutt6[4]; - out1[7] += inp2[3]*lutt6[5]; - out1[7] += inp2[2]*lutt6[2]; - out1[7] += inp2[5]*lutt6[3]; - out1[7] += inp2[4]*lutt6[0]; - out1[7] += inp2[7]*lutt6[1]; - - out1[6] = inp2[1]*lutt6[6]; - out1[6] += inp2[0]*lutt6[7]; - out1[6] += inp2[3]*lutt6[4]; - out1[6] += inp2[2]*lutt6[5]; - out1[6] += inp2[5]*lutt6[2]; - out1[6] += inp2[4]*lutt6[3]; - out1[6] += inp2[7]*lutt6[0]; - out1[6] += inp2[6]*lutt6[1]; - outp[1] = /*CLAMP*/((out1[1]+0x4000) >> 0xF); - outp[0] = /*CLAMP*/((out1[0]+0x4000) >> 0xF); - outp[3] = /*CLAMP*/((out1[3]+0x4000) >> 0xF); - outp[2] = /*CLAMP*/((out1[2]+0x4000) >> 0xF); - outp[5] = /*CLAMP*/((out1[5]+0x4000) >> 0xF); - outp[4] = /*CLAMP*/((out1[4]+0x4000) >> 0xF); - outp[7] = /*CLAMP*/((out1[7]+0x4000) >> 0xF); - outp[6] = /*CLAMP*/((out1[6]+0x4000) >> 0xF); - inp1 = inp2; - inp2 += 8; - outp += 8; - } -// memcpy (rsp.RDRAM+(inst2&0xFFFFFF), dmem+0xFB0, 0x20); - memcpy (save, inp2-8, 0x10); - memcpy (BufferSpace+(inst1&0xffff), outbuff, cnt); -} - -static void SEGMENT2 (u32 inst1, u32 inst2) { - if (isZeldaABI) { - FILTER2 (inst1, inst2); - return; - } - if ((inst1 & 0xffffff) == 0) { - isMKABI = true; - //SEGMENTS[(inst2>>24)&0xf] = (inst2 & 0xffffff); - } else { - isMKABI = false; - isZeldaABI = true; - FILTER2 (inst1, inst2); - } -} - -static void UNKNOWN (u32 inst1, u32 inst2) { -} -/* -void (*ABI2[0x20])(void) = { - SPNOOP, ADPCM2, CLEARBUFF2, SPNOOP, SPNOOP, RESAMPLE2, SPNOOP, SEGMENT2, - SETBUFF2, SPNOOP, DMEMMOVE2, LOADADPCM2, MIXER2, INTERLEAVE2, HILOGAIN, SETLOOP2, - SPNOOP, INTERL2, ENVSETUP1, ENVMIXER2, LOADBUFF2, SAVEBUFF2, ENVSETUP2, SPNOOP, - SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP -};*/ - -extern "C" const acmd_callback_t ABI2[0x20] = { - SPNOOP , ADPCM2, CLEARBUFF2, UNKNOWN, ADDMIXER, RESAMPLE2, UNKNOWN, SEGMENT2, - SETBUFF2 , DUPLICATE2, DMEMMOVE2, LOADADPCM2, MIXER2, INTERLEAVE2, HILOGAIN, SETLOOP2, - SPNOOP, INTERL2 , ENVSETUP1, ENVMIXER2, LOADBUFF2, SAVEBUFF2, ENVSETUP2, SPNOOP, - HILOGAIN , SPNOOP, DUPLICATE2 , UNKNOWN , SPNOOP , SPNOOP , SPNOOP , SPNOOP -}; -/* -void (*ABI2[0x20])(void) = { - SPNOOP , ADPCM2, CLEARBUFF2, SPNOOP, SPNOOP, RESAMPLE2 , SPNOOP , SEGMENT2, - SETBUFF2 , DUPLICATE2, DMEMMOVE2, LOADADPCM2, MIXER2, INTERLEAVE2, SPNOOP, SETLOOP2, - SPNOOP, INTERL2 , ENVSETUP1, ENVMIXER2, LOADBUFF2, SAVEBUFF2, ENVSETUP2, SPNOOP, - SPNOOP , SPNOOP, SPNOOP , SPNOOP , SPNOOP , SPNOOP , SPNOOP , SPNOOP -};*/ -/* NOTES: - - FILTER/SEGMENT - Still needs to be finished up... add FILTER? - UNKNOWWN #27 - Is this worth doing? Looks like a pain in the ass just for WaveRace64 -*/ - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode3.cpp deleted file mode 100644 index b63fbe27c2..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3.cpp +++ /dev/null @@ -1,834 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode3.cpp * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include -# include - -extern "C" { - #include "m64p_types.h" - #include "hle.h" - #include "alist_internal.h" -} - -/* -static void SPNOOP (u32 inst1, u32 inst2) { - DebugMessage(M64MSG_ERROR, "Unknown/Unimplemented Audio Command %i in ABI 3", (int)(inst1 >> 24)); -} -*/ - -extern const u16 ResampleLUT [0x200]; - -extern u32 loopval; - -extern s16 Env_Dry; -extern s16 Env_Wet; -extern s16 Vol_Left; -extern s16 Vol_Right; -extern s16 VolTrg_Left; -extern s32 VolRamp_Left; -//extern u16 VolRate_Left; -extern s16 VolTrg_Right; -extern s32 VolRamp_Right; -//extern u16 VolRate_Right; - - -extern short hleMixerWorkArea[256]; -extern u16 adpcmtable[0x88]; - -extern u8 BufferSpace[0x10000]; - -/* -static void SETVOL3 (u32 inst1, u32 inst2) { // Swapped Rate_Left and Vol - u8 Flags = (u8)(inst1 >> 0x10); - if (Flags & 0x4) { // 288 - if (Flags & 0x2) { // 290 - VolTrg_Left = *(s16*)&inst1; - VolRamp_Left = *(s32*)&inst2; - } else { - VolTrg_Right = *(s16*)&inst1; - VolRamp_Right = *(s32*)&inst2; - } - } else { - Vol_Left = *(s16*)&inst1; - Env_Dry = (s16)(*(s32*)&inst2 >> 0x10); - Env_Wet = *(s16*)&inst2; - } -} -*/ -static void SETVOL3 (u32 inst1, u32 inst2) { - u8 Flags = (u8)(inst1 >> 0x10); - if (Flags & 0x4) { // 288 - if (Flags & 0x2) { // 290 - Vol_Left = (s16)inst1; // 0x50 - Env_Dry = (s16)(inst2 >> 0x10); // 0x4E - Env_Wet = (s16)inst2; // 0x4C - } else { - VolTrg_Right = (s16)inst1; // 0x46 - //VolRamp_Right = (u16)(inst2 >> 0x10) | (s32)(s16)(inst2 << 0x10); - VolRamp_Right = (s32)inst2; // 0x48/0x4A - } - } else { - VolTrg_Left = (s16)inst1; // 0x40 - VolRamp_Left = (s32)inst2; // 0x42/0x44 - } -} - -static void ENVMIXER3 (u32 inst1, u32 inst2) { - u8 flags = (u8)((inst1 >> 16) & 0xff); - u32 addy = (inst2 & 0xFFFFFF); - - short *inp=(short *)(BufferSpace+0x4F0); - short *out=(short *)(BufferSpace+0x9D0); - short *aux1=(short *)(BufferSpace+0xB40); - short *aux2=(short *)(BufferSpace+0xCB0); - short *aux3=(short *)(BufferSpace+0xE20); - s32 MainR; - s32 MainL; - s32 AuxR; - s32 AuxL; - int i1,o1,a1,a2,a3; - //unsigned short AuxIncRate=1; - short zero[8]; - memset(zero,0,16); - - s32 LAdder, LAcc, LVol; - s32 RAdder, RAcc, RVol; - s16 RSig, LSig; // Most significant part of the Ramp Value - s16 Wet, Dry; - s16 LTrg, RTrg; - - Vol_Right = (s16)inst1; - - if (flags & A_INIT) { - LAdder = VolRamp_Left / 8; - LAcc = 0; - LVol = Vol_Left; - LSig = (s16)(VolRamp_Left >> 16); - - RAdder = VolRamp_Right / 8; - RAcc = 0; - RVol = Vol_Right; - RSig = (s16)(VolRamp_Right >> 16); - - Wet = (s16)Env_Wet; Dry = (s16)Env_Dry; // Save Wet/Dry values - LTrg = VolTrg_Left; RTrg = VolTrg_Right; // Save Current Left/Right Targets - } else { - memcpy((u8 *)hleMixerWorkArea, rsp.RDRAM+addy, 80); - Wet = *(s16 *)(hleMixerWorkArea + 0); // 0-1 - Dry = *(s16 *)(hleMixerWorkArea + 2); // 2-3 - LTrg = *(s16 *)(hleMixerWorkArea + 4); // 4-5 - RTrg = *(s16 *)(hleMixerWorkArea + 6); // 6-7 - LAdder = *(s32 *)(hleMixerWorkArea + 8); // 8-9 (hleMixerWorkArea is a 16bit pointer) - RAdder = *(s32 *)(hleMixerWorkArea + 10); // 10-11 - LAcc = *(s32 *)(hleMixerWorkArea + 12); // 12-13 - RAcc = *(s32 *)(hleMixerWorkArea + 14); // 14-15 - LVol = *(s32 *)(hleMixerWorkArea + 16); // 16-17 - RVol = *(s32 *)(hleMixerWorkArea + 18); // 18-19 - LSig = *(s16 *)(hleMixerWorkArea + 20); // 20-21 - RSig = *(s16 *)(hleMixerWorkArea + 22); // 22-23 - //u32 test = *(s32 *)(hleMixerWorkArea + 24); // 22-23 - //if (test != 0x13371337) - } - - - //if(!(flags&A_AUX)) { - // AuxIncRate=0; - // aux2=aux3=zero; - //} - - for (int y = 0; y < (0x170/2); y++) { - - // Left - LAcc += LAdder; - LVol += (LAcc >> 16); - LAcc &= 0xFFFF; - - // Right - RAcc += RAdder; - RVol += (RAcc >> 16); - RAcc &= 0xFFFF; -// **************************************************************** - // Clamp Left - if (LSig >= 0) { // VLT - if (LVol > LTrg) { - LVol = LTrg; - } - } else { // VGE - if (LVol < LTrg) { - LVol = LTrg; - } - } - - // Clamp Right - if (RSig >= 0) { // VLT - if (RVol > RTrg) { - RVol = RTrg; - } - } else { // VGE - if (RVol < RTrg) { - RVol = RTrg; - } - } -// **************************************************************** - MainL = ((Dry * LVol) + 0x4000) >> 15; - MainR = ((Dry * RVol) + 0x4000) >> 15; - - o1 = out [y^S]; - a1 = aux1[y^S]; - i1 = inp [y^S]; - - o1+=((i1*MainL)+0x4000)>>15; - a1+=((i1*MainR)+0x4000)>>15; - -// **************************************************************** - - if(o1>32767) o1=32767; - else if(o1<-32768) o1=-32768; - - if(a1>32767) a1=32767; - else if(a1<-32768) a1=-32768; - -// **************************************************************** - - out[y^S]=o1; - aux1[y^S]=a1; - -// **************************************************************** - //if (!(flags&A_AUX)) { - a2 = aux2[y^S]; - a3 = aux3[y^S]; - - AuxL = ((Wet * LVol) + 0x4000) >> 15; - AuxR = ((Wet * RVol) + 0x4000) >> 15; - - a2+=((i1*AuxL)+0x4000)>>15; - a3+=((i1*AuxR)+0x4000)>>15; - - if(a2>32767) a2=32767; - else if(a2<-32768) a2=-32768; - - if(a3>32767) a3=32767; - else if(a3<-32768) a3=-32768; - - aux2[y^S]=a2; - aux3[y^S]=a3; - } - //} - - *(s16 *)(hleMixerWorkArea + 0) = Wet; // 0-1 - *(s16 *)(hleMixerWorkArea + 2) = Dry; // 2-3 - *(s16 *)(hleMixerWorkArea + 4) = LTrg; // 4-5 - *(s16 *)(hleMixerWorkArea + 6) = RTrg; // 6-7 - *(s32 *)(hleMixerWorkArea + 8) = LAdder; // 8-9 (hleMixerWorkArea is a 16bit pointer) - *(s32 *)(hleMixerWorkArea + 10) = RAdder; // 10-11 - *(s32 *)(hleMixerWorkArea + 12) = LAcc; // 12-13 - *(s32 *)(hleMixerWorkArea + 14) = RAcc; // 14-15 - *(s32 *)(hleMixerWorkArea + 16) = LVol; // 16-17 - *(s32 *)(hleMixerWorkArea + 18) = RVol; // 18-19 - *(s16 *)(hleMixerWorkArea + 20) = LSig; // 20-21 - *(s16 *)(hleMixerWorkArea + 22) = RSig; // 22-23 - //*(u32 *)(hleMixerWorkArea + 24) = 0x13371337; // 22-23 - memcpy(rsp.RDRAM+addy, (u8 *)hleMixerWorkArea,80); -} - -static void CLEARBUFF3 (u32 inst1, u32 inst2) { - u16 addr = (u16)(inst1 & 0xffff); - u16 count = (u16)(inst2 & 0xffff); - memset(BufferSpace+addr+0x4f0, 0, count); -} - -static void MIXER3 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u16 dmemin = (u16)(inst2 >> 0x10) + 0x4f0; - u16 dmemout = (u16)(inst2 & 0xFFFF) + 0x4f0; - //u8 flags = (u8)((inst1 >> 16) & 0xff); - s32 gain = (s16)(inst1 & 0xFFFF); - s32 temp; - - for (int x=0; x < 0x170; x+=2) { // I think I can do this a lot easier - temp = (*(s16 *)(BufferSpace+dmemin+x) * gain) >> 15; - temp += *(s16 *)(BufferSpace+dmemout+x); - - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} - -static void LOADBUFF3 (u32 inst1, u32 inst2) { - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc); - u32 src = (inst1&0xffc)+0x4f0; - memcpy (BufferSpace+src, rsp.RDRAM+v0, cnt); -} - -static void SAVEBUFF3 (u32 inst1, u32 inst2) { - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc); - u32 src = (inst1&0xffc)+0x4f0; - memcpy (rsp.RDRAM+v0, BufferSpace+src, cnt); -} - -static void LOADADPCM3 (u32 inst1, u32 inst2) { // Loads an ADPCM table - Works 100% Now 03-13-01 - u32 v0; - v0 = (inst2 & 0xffffff); - //memcpy (dmem+0x3f0, rsp.RDRAM+v0, inst1&0xffff); - //assert ((inst1&0xffff) <= 0x80); - u16 *table = (u16 *)(rsp.RDRAM+v0); - for (u32 x = 0; x < ((inst1&0xffff)>>0x4); x++) { - adpcmtable[(0x0+(x<<3))^S] = table[0]; - adpcmtable[(0x1+(x<<3))^S] = table[1]; - - adpcmtable[(0x2+(x<<3))^S] = table[2]; - adpcmtable[(0x3+(x<<3))^S] = table[3]; - - adpcmtable[(0x4+(x<<3))^S] = table[4]; - adpcmtable[(0x5+(x<<3))^S] = table[5]; - - adpcmtable[(0x6+(x<<3))^S] = table[6]; - adpcmtable[(0x7+(x<<3))^S] = table[7]; - table += 8; - } -} - -static void DMEMMOVE3 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0, v1; - u32 cnt; - v0 = (inst1 & 0xFFFF) + 0x4f0; - v1 = (inst2 >> 0x10) + 0x4f0; - u32 count = ((inst2+3) & 0xfffc); - - //memcpy (dmem+v1, dmem+v0, count-1); - for (cnt = 0; cnt < count; cnt++) { - *(u8 *)(BufferSpace+((cnt+v1)^S8)) = *(u8 *)(BufferSpace+((cnt+v0)^S8)); - } -} - -static void SETLOOP3 (u32 inst1, u32 inst2) { - loopval = (inst2 & 0xffffff); -} - -static void ADPCM3 (u32 inst1, u32 inst2) { // Verified to be 100% Accurate... - unsigned char Flags=(u8)(inst2>>0x1c)&0xff; - //unsigned short Gain=(u16)(inst1&0xffff); - unsigned int Address=(inst1 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned short inPtr=(inst2>>12)&0xf; - //short *out=(s16 *)(testbuff+(AudioOutBuffer>>2)); - short *out=(short *)(BufferSpace+(inst2&0xfff)+0x4f0); - //unsigned char *in=(unsigned char *)(BufferSpace+((inst2>>12)&0xf)+0x4f0); - short count=(short)((inst2 >> 16)&0xfff); - unsigned char icode; - unsigned char code; - int vscale; - unsigned short index; - unsigned short j; - int a[8]; - short *book1,*book2; - - memset(out,0,32); - - if(!(Flags&0x1)) - { - if(Flags&0x2) - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(loopval+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[loopval],32); - } - else - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(Address+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[Address],32); - } - } - - int l1=out[14^S]; - int l2=out[15^S]; - int inp1[8]; - int inp2[8]; - out+=16; - while(count>0) - { - // the first interation through, these values are - // either 0 in the case of A_INIT, from a special - // area of memory in the case of A_LOOP or just - // the values we calculated the last time - - code=BufferSpace[(0x4f0+inPtr)^S8]; - index=code&0xf; - index<<=4; // index into the adpcm code table - book1=(short *)&adpcmtable[index]; - book2=book1+8; - code>>=4; // upper nibble is scale - vscale=(0x8000>>((12-code)-1)); // very strange. 0x8000 would be .5 in 16:16 format - // so this appears to be a fractional scale based - // on the 12 based inverse of the scale value. note - // that this could be negative, in which case we do - // not use the calculated vscale value... see the - // if(code>12) check below - - inPtr++; // coded adpcm data lies next - j=0; - while(j<8) // loop of 8, for 8 coded nibbles from 4 bytes - // which yields 8 short pcm values - { - icode=BufferSpace[(0x4f0+inPtr)^S8]; - inPtr++; - - inp1[j]=(s16)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp1[j]=(s16)((icode&0xf)<<12); - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - j=0; - while(j<8) - { - icode=BufferSpace[(0x4f0+inPtr)^S8]; - inPtr++; - - inp2[j]=(short)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp2[j]=(short)((icode&0xf)<<12); - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp1[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp1[0]; - a[1]+=(int)inp1[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp1[0]; - a[2]+=(int)book2[0]*inp1[1]; - a[2]+=(int)inp1[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp1[0]; - a[3]+=(int)book2[1]*inp1[1]; - a[3]+=(int)book2[0]*inp1[2]; - a[3]+=(int)inp1[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp1[0]; - a[4]+=(int)book2[2]*inp1[1]; - a[4]+=(int)book2[1]*inp1[2]; - a[4]+=(int)book2[0]*inp1[3]; - a[4]+=(int)inp1[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp1[0]; - a[5]+=(int)book2[3]*inp1[1]; - a[5]+=(int)book2[2]*inp1[2]; - a[5]+=(int)book2[1]*inp1[3]; - a[5]+=(int)book2[0]*inp1[4]; - a[5]+=(int)inp1[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp1[0]; - a[6]+=(int)book2[4]*inp1[1]; - a[6]+=(int)book2[3]*inp1[2]; - a[6]+=(int)book2[2]*inp1[3]; - a[6]+=(int)book2[1]*inp1[4]; - a[6]+=(int)book2[0]*inp1[5]; - a[6]+=(int)inp1[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp1[0]; - a[7]+=(int)book2[5]*inp1[1]; - a[7]+=(int)book2[4]*inp1[2]; - a[7]+=(int)book2[3]*inp1[3]; - a[7]+=(int)book2[2]*inp1[4]; - a[7]+=(int)book2[1]*inp1[5]; - a[7]+=(int)book2[0]*inp1[6]; - a[7]+=(int)inp1[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - //*(out+j)=a[j^S]; - } - //out += 0x10; - l1=a[6]; - l2=a[7]; - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp2[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp2[0]; - a[1]+=(int)inp2[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp2[0]; - a[2]+=(int)book2[0]*inp2[1]; - a[2]+=(int)inp2[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp2[0]; - a[3]+=(int)book2[1]*inp2[1]; - a[3]+=(int)book2[0]*inp2[2]; - a[3]+=(int)inp2[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp2[0]; - a[4]+=(int)book2[2]*inp2[1]; - a[4]+=(int)book2[1]*inp2[2]; - a[4]+=(int)book2[0]*inp2[3]; - a[4]+=(int)inp2[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp2[0]; - a[5]+=(int)book2[3]*inp2[1]; - a[5]+=(int)book2[2]*inp2[2]; - a[5]+=(int)book2[1]*inp2[3]; - a[5]+=(int)book2[0]*inp2[4]; - a[5]+=(int)inp2[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp2[0]; - a[6]+=(int)book2[4]*inp2[1]; - a[6]+=(int)book2[3]*inp2[2]; - a[6]+=(int)book2[2]*inp2[3]; - a[6]+=(int)book2[1]*inp2[4]; - a[6]+=(int)book2[0]*inp2[5]; - a[6]+=(int)inp2[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp2[0]; - a[7]+=(int)book2[5]*inp2[1]; - a[7]+=(int)book2[4]*inp2[2]; - a[7]+=(int)book2[3]*inp2[3]; - a[7]+=(int)book2[2]*inp2[4]; - a[7]+=(int)book2[1]*inp2[5]; - a[7]+=(int)book2[0]*inp2[6]; - a[7]+=(int)inp2[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - //*(out+j+0x1f8)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - count-=32; - } - out-=16; - memcpy(&rsp.RDRAM[Address],out,32); -} - -static void RESAMPLE3 (u32 inst1, u32 inst2) { - unsigned char Flags=(u8)((inst2>>0x1e)); - unsigned int Pitch=((inst2>>0xe)&0xffff)<<1; - u32 addy = (inst1 & 0xffffff); - unsigned int Accum=0; - unsigned int location; - s16 *lut; - short *dst; - s16 *src; - dst=(short *)(BufferSpace); - src=(s16 *)(BufferSpace); - u32 srcPtr=((((inst2>>2)&0xfff)+0x4f0)/2); - u32 dstPtr;//=(AudioOutBuffer/2); - s32 temp; - s32 accum; - - //if (addy > (1024*1024*8)) - // addy = (inst2 & 0xffffff); - - srcPtr -= 4; - - if (inst2 & 0x3) { - dstPtr = 0x660/2; - } else { - dstPtr = 0x4f0/2; - } - - if ((Flags & 0x1) == 0) { - for (int x=0; x < 4; x++) //memcpy (src+srcPtr, rsp.RDRAM+addy, 0x8); - src[(srcPtr+x)^S] = ((u16 *)rsp.RDRAM)[((addy/2)+x)^S]; - Accum = *(u16 *)(rsp.RDRAM+addy+10); - } else { - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = 0;//*(u16 *)(rsp.RDRAM+((addy+x)^2)); - } - - for(int i=0;i < 0x170/2;i++) { - location = (((Accum * 0x40) >> 0x10) * 8); - //location = (Accum >> 0xa) << 0x3; - lut = (s16 *)(((u8 *)ResampleLUT) + location); - - temp = ((s32)*(s16*)(src+((srcPtr+0)^S))*((s32)((s16)lut[0]))); - accum = (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+1)^S))*((s32)((s16)lut[1]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+2)^S))*((s32)((s16)lut[2]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+3)^S))*((s32)((s16)lut[3]))); - accum += (s32)(temp >> 15); -/* temp = ((s64)*(s16*)(src+((srcPtr+0)^S))*((s64)((s16)lut[0]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum = (s32)(s16)temp; - - temp = ((s64)*(s16*)(src+((srcPtr+1)^S))*((s64)((s16)lut[1]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum += (s32)(s16)temp; - - temp = ((s64)*(s16*)(src+((srcPtr+2)^S))*((s64)((s16)lut[2]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum += (s32)(s16)temp; - - temp = ((s64)*(s16*)(src+((srcPtr+3)^S))*((s64)((s16)lut[3]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum += (s32)(s16)temp;*/ - - if (accum > 32767) accum = 32767; - if (accum < -32768) accum = -32768; - - dst[dstPtr^S] = (accum); - dstPtr++; - Accum += Pitch; - srcPtr += (Accum>>16); - Accum&=0xffff; - } - for (int x=0; x < 4; x++) - ((u16 *)rsp.RDRAM)[((addy/2)+x)^S] = src[(srcPtr+x)^S]; - *(u16 *)(rsp.RDRAM+addy+10) = Accum; -} - -static void INTERLEAVE3 (u32 inst1, u32 inst2) { // Needs accuracy verification... - //u32 inL, inR; - u16 *outbuff = (u16 *)(BufferSpace + 0x4f0);//(u16 *)(AudioOutBuffer+dmem); - u16 *inSrcR; - u16 *inSrcL; - u16 Left, Right, Left2, Right2; - - //inR = inst2 & 0xFFFF; - //inL = (inst2 >> 16) & 0xFFFF; - - inSrcR = (u16 *)(BufferSpace+0xb40); - inSrcL = (u16 *)(BufferSpace+0x9d0); - - for (int x = 0; x < (0x170/4); x++) { - Left=*(inSrcL++); - Right=*(inSrcR++); - Left2=*(inSrcL++); - Right2=*(inSrcR++); - -#ifdef M64P_BIG_ENDIAN - *(outbuff++)=Right; - *(outbuff++)=Left; - *(outbuff++)=Right2; - *(outbuff++)=Left2; -#else - *(outbuff++)=Right2; - *(outbuff++)=Left2; - *(outbuff++)=Right; - *(outbuff++)=Left; -#endif -/* - Left=*(inSrcL++); - Right=*(inSrcR++); - *(outbuff++)=(u16)Left; - Left >>= 16; - *(outbuff++)=(u16)Right; - Right >>= 16; - *(outbuff++)=(u16)Left; - *(outbuff++)=(u16)Right;*/ - } -} - -//static void UNKNOWN (u32 inst1, u32 inst2); -/* -typedef struct { - unsigned char sync; - - unsigned char error_protection : 1; // 0=yes, 1=no - unsigned char lay : 2; // 4-lay = layerI, II or III - unsigned char version : 1; // 3=mpeg 1.0, 2=mpeg 2.5 0=mpeg 2.0 - unsigned char sync2 : 4; - - unsigned char extension : 1; // Unknown - unsigned char padding : 1; // padding - unsigned char sampling_freq : 2; // see table below - unsigned char bitrate_index : 4; // see table below - - unsigned char emphasis : 2; //see table below - unsigned char original : 1; // 0=no 1=yes - unsigned char copyright : 1; // 0=no 1=yes - unsigned char mode_ext : 2; // used with "joint stereo" mode - unsigned char mode : 2; // Channel Mode -} mp3struct; - -mp3struct mp3; -FILE *mp3dat; -*/ - -static void WHATISTHIS (u32 inst1, u32 inst2) { -} - -//static FILE *fp = fopen ("d:\\mp3info.txt", "wt"); -u32 setaddr; -static void MP3ADDY (u32 inst1, u32 inst2) { - setaddr = (inst2 & 0xffffff); -} - -extern "C" { - void rsp_run(void); - void mp3setup (unsigned int inst1, unsigned int inst2, unsigned int t8); -} - -extern u32 base, dmembase; -extern "C" { - extern char *pDMEM; -} -void MP3 (u32 inst1, u32 inst2); -/* - { -// return; - // Setup Registers... - mp3setup (inst1, inst2, 0xFA0); - - // Setup Memory Locations... - //u32 base = ((u32*)dmem)[0xFD0/4]; // Should be 000291A0 - memcpy (BufferSpace, dmembase+rsp.RDRAM, 0x10); - ((u32*)BufferSpace)[0x0] = base; - ((u32*)BufferSpace)[0x008/4] += base; - ((u32*)BufferSpace)[0xFFC/4] = loopval; - ((u32*)BufferSpace)[0xFF8/4] = dmembase; - - memcpy (imem+0x238, rsp.RDRAM+((u32*)BufferSpace)[0x008/4], 0x9C0); - ((u32*)BufferSpace)[0xFF4/4] = setaddr; - pDMEM = (char *)BufferSpace; - rsp_run (void); - dmembase = ((u32*)BufferSpace)[0xFF8/4]; - loopval = ((u32*)BufferSpace)[0xFFC/4]; -//0x1A98 SW S1, 0x0FF4 (R0) -//0x1A9C SW S0, 0x0FF8 (R0) -//0x1AA0 SW T7, 0x0FFC (R0) -//0x1AA4 SW T3, 0x0FF0 (R0) - //fprintf (fp, "mp3: inst1: %08X, inst2: %08X\n", inst1, inst2); -}*/ -/* -FFT = Fast Fourier Transform -DCT = Discrete Cosine Transform -MPEG-1 Layer 3 retains Layer 2's 1152-sample window, as well as the FFT polyphase filter for -backward compatibility, but adds a modified DCT filter. DCT's advantages over DFTs (discrete -Fourier transforms) include half as many multiply-accumulate operations and half the -generated coefficients because the sinusoidal portion of the calculation is absent, and DCT -generally involves simpler math. The finite lengths of a conventional DCTs' bandpass impulse -responses, however, may result in block-boundary effects. MDCTs overlap the analysis blocks -and lowpass-filter the decoded audio to remove aliases, eliminating these effects. MDCTs also -have a higher transform coding gain than the standard DCT, and their basic functions -correspond to better bandpass response. - -MPEG-1 Layer 3's DCT sub-bands are unequally sized, and correspond to the human auditory -system's critical bands. In Layer 3 decoders must support both constant- and variable-bit-rate -bit streams. (However, many Layer 1 and 2 decoders also handle variable bit rates). Finally, -Layer 3 encoders Huffman-code the quantized coefficients before archiving or transmission for -additional lossless compression. Bit streams range from 32 to 320 kbps, and 128-kbps rates -achieve near-CD quality, an important specification to enable dual-channel ISDN -(integrated-services-digital-network) to be the future high-bandwidth pipe to the home. - -*/ -static void DISABLE (u32 inst1, u32 inst2) { - //MessageBox (NULL, "Help", "ABI 3 Command 0", MB_OK); - //ChangeABI (5); -} - - -extern "C" const acmd_callback_t ABI3[0x10] = { - DISABLE , ADPCM3 , CLEARBUFF3, ENVMIXER3 , LOADBUFF3, RESAMPLE3 , SAVEBUFF3, MP3, - MP3ADDY, SETVOL3, DMEMMOVE3 , LOADADPCM3 , MIXER3 , INTERLEAVE3, WHATISTHIS , SETLOOP3 -}; - - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3mp3.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode3mp3.cpp deleted file mode 100644 index 21caa33772..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3mp3.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode3mp3.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include -# include - -extern "C" { - #include "hle.h" - #include "alist_internal.h" -} - -static const u16 DeWindowLUT [0x420] = { - 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, - 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, - 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, - 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, - 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, - 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, - 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, - 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, - 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, - 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, - 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, - 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, - 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, - 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, - 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, - 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, - 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, - 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, - 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, - 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, - 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, - 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, - 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, - 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, - 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, - 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, - 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, - 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, - 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, - 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, - 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, - 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, - 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, - 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, - 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, - 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, - 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, - 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, - 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, - 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, - 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, - 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, - 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, - 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, - 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, - 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, - 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, - 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, - 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, - 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, - 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, - 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, - 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, - 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, - 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, - 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, - 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, - 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, - 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, - 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, - 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, - 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, - 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, - 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, - 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, - 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, - 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, - 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, - 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, - 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, - 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, - 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, - 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, - 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, - 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, - 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, - 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, - 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, - 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, - 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, - 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, - 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0003, - 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, - 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0004, - 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, - 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, - 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, - 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, - 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, - 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0004, - 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, - 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0005, - 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, - 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0005, - 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, - 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0006, - 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, - 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, - 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, - 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, - 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, - 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0006, - 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, - 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0007, - 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, - 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, - 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, - 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, - 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, - 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0007, - 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, - 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0008, - 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, - 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0008, - 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, - 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0009, - 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, - 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x0009, - 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, - 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x000A, - 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, - 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000A, - 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, - 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000B, - 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, - 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000B, - 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, - 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000D, - 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, - 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x000D, - 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, - 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x0000 -}; - -//static u16 myVector[32][8]; - -static u8 mp3data[0x1000]; - -static s32 v[32]; - -static void MP3AB0 () { - // Part 2 - 100% Accurate - const u16 LUT2[8] = { 0xFEC4, 0xF4FA, 0xC5E4, 0xE1C4, - 0x1916, 0x4A50, 0xA268, 0x78AE }; - const u16 LUT3[4] = { 0xFB14, 0xD4DC, 0x31F2, 0x8E3A }; - int i; - - for (i = 0; i < 8; i++) { - v[16+i] = v[0+i] + v[8+i]; - v[24+i] = ((v[0+i] - v[8+i]) * LUT2[i]) >> 0x10; - } - - // Part 3: 4-wide butterflies - - for (i=0; i < 4; i++) { - v[0+i] = v[16+i] + v[20+i]; - v[4+i] = ((v[16+i] - v[20+i]) * LUT3[i]) >> 0x10; - - v[8+i] = v[24+i] + v[28+i]; - v[12+i] = ((v[24+i] - v[28+i]) * LUT3[i]) >> 0x10; - } - - // Part 4: 2-wide butterflies - 100% Accurate - - for (i = 0; i < 16; i+=4) { - v[16+i] = v[0+i] + v[2+i]; - v[18+i] = ((v[0+i] - v[2+i]) * 0xEC84) >> 0x10; - - v[17+i] = v[1+i] + v[3+i]; - v[19+i] = ((v[1+i] - v[3+i]) * 0x61F8) >> 0x10; - } -} - -static void InnerLoop (); - -static u32 inPtr, outPtr; - -static u32 t6;// = 0x08A0; // I think these are temporary storage buffers -static u32 t5;// = 0x0AC0; -static u32 t4;// = (inst1 & 0x1E); - -void MP3 (u32 inst1, u32 inst2) { - // Initialization Code - u32 readPtr; // s5 - u32 writePtr; // s6 - //u32 Count = 0x0480; // s4 - u32 tmp; - //u32 inPtr, outPtr; - - t6 = 0x08A0; // I think these are temporary storage buffers - t5 = 0x0AC0; - t4 = (inst1 & 0x1E); - - writePtr = inst2 & 0xFFFFFF; - readPtr = writePtr; - memcpy (mp3data+0xCE8, rsp.RDRAM+readPtr, 8); // Just do that for efficiency... may remove and use directly later anyway - readPtr += 8; // This must be a header byte or whatnot - - for (int cnt = 0; cnt < 0x480; cnt += 0x180) { - memcpy (mp3data+0xCF0, rsp.RDRAM+readPtr, 0x180); // DMA: 0xCF0 <- RDRAM[s5] : 0x180 - inPtr = 0xCF0; // s7 - outPtr = 0xE70; // s3 -// --------------- Inner Loop Start -------------------- - for (int cnt2 = 0; cnt2 < 0x180; cnt2 += 0x40) { - t6 &= 0xFFE0; - t5 &= 0xFFE0; - t6 |= t4; - t5 |= t4; - InnerLoop (); - t4 = (t4-2)&0x1E; - tmp = t6; - t6 = t5; - t5 = tmp; - //outPtr += 0x40; - inPtr += 0x40; - } -// --------------- Inner Loop End -------------------- - memcpy (rsp.RDRAM+writePtr, mp3data+0xe70, 0x180); - writePtr += 0x180; - readPtr += 0x180; - } -} - - - -static void InnerLoop () { - // Part 1: 100% Accurate - - int i; - v[0] = *(s16 *)(mp3data+inPtr+(0x00^S16)); v[31] = *(s16 *)(mp3data+inPtr+(0x3E^S16)); v[0] += v[31]; - v[1] = *(s16 *)(mp3data+inPtr+(0x02^S16)); v[30] = *(s16 *)(mp3data+inPtr+(0x3C^S16)); v[1] += v[30]; - v[2] = *(s16 *)(mp3data+inPtr+(0x06^S16)); v[28] = *(s16 *)(mp3data+inPtr+(0x38^S16)); v[2] += v[28]; - v[3] = *(s16 *)(mp3data+inPtr+(0x04^S16)); v[29] = *(s16 *)(mp3data+inPtr+(0x3A^S16)); v[3] += v[29]; - - v[4] = *(s16 *)(mp3data+inPtr+(0x0E^S16)); v[24] = *(s16 *)(mp3data+inPtr+(0x30^S16)); v[4] += v[24]; - v[5] = *(s16 *)(mp3data+inPtr+(0x0C^S16)); v[25] = *(s16 *)(mp3data+inPtr+(0x32^S16)); v[5] += v[25]; - v[6] = *(s16 *)(mp3data+inPtr+(0x08^S16)); v[27] = *(s16 *)(mp3data+inPtr+(0x36^S16)); v[6] += v[27]; - v[7] = *(s16 *)(mp3data+inPtr+(0x0A^S16)); v[26] = *(s16 *)(mp3data+inPtr+(0x34^S16)); v[7] += v[26]; - - v[8] = *(s16 *)(mp3data+inPtr+(0x1E^S16)); v[16] = *(s16 *)(mp3data+inPtr+(0x20^S16)); v[8] += v[16]; - v[9] = *(s16 *)(mp3data+inPtr+(0x1C^S16)); v[17] = *(s16 *)(mp3data+inPtr+(0x22^S16)); v[9] += v[17]; - v[10]= *(s16 *)(mp3data+inPtr+(0x18^S16)); v[19] = *(s16 *)(mp3data+inPtr+(0x26^S16)); v[10]+= v[19]; - v[11]= *(s16 *)(mp3data+inPtr+(0x1A^S16)); v[18] = *(s16 *)(mp3data+inPtr+(0x24^S16)); v[11]+= v[18]; - - v[12]= *(s16 *)(mp3data+inPtr+(0x10^S16)); v[23] = *(s16 *)(mp3data+inPtr+(0x2E^S16)); v[12]+= v[23]; - v[13]= *(s16 *)(mp3data+inPtr+(0x12^S16)); v[22] = *(s16 *)(mp3data+inPtr+(0x2C^S16)); v[13]+= v[22]; - v[14]= *(s16 *)(mp3data+inPtr+(0x16^S16)); v[20] = *(s16 *)(mp3data+inPtr+(0x28^S16)); v[14]+= v[20]; - v[15]= *(s16 *)(mp3data+inPtr+(0x14^S16)); v[21] = *(s16 *)(mp3data+inPtr+(0x2A^S16)); v[15]+= v[21]; - - // Part 2-4 - - MP3AB0 (); - - // Part 5 - 1-Wide Butterflies - 100% Accurate but need SSVs!!! - - u32 t0 = t6 + 0x100; - u32 t1 = t6 + 0x200; - u32 t2 = t5 + 0x100; - u32 t3 = t5 + 0x200; - /*RSP_GPR[0x8].W = t0; - RSP_GPR[0x9].W = t1; - RSP_GPR[0xA].W = t2; - RSP_GPR[0xB].W = t3; - - RSP_Vect[0].DW[1] = 0xB504A57E00016A09; - RSP_Vect[0].DW[0] = 0x0002D4130005A827; -*/ - - // 0x13A8 - v[1] = 0; - v[11] = ((v[16] - v[17]) * 0xB504) >> 0x10; - - v[16] = -v[16] -v[17]; - v[2] = v[18] + v[19]; - // ** Store v[11] -> (T6 + 0)** - *(s16 *)(mp3data+((t6+(short)0x0))) = (short)v[11]; - - - v[11] = -v[11]; - // ** Store v[16] -> (T3 + 0)** - *(s16 *)(mp3data+((t3+(short)0x0))) = (short)v[16]; - // ** Store v[11] -> (T5 + 0)** - *(s16 *)(mp3data+((t5+(short)0x0))) = (short)v[11]; - // 0x13E8 - Verified.... - v[2] = -v[2]; - // ** Store v[2] -> (T2 + 0)** - *(s16 *)(mp3data+((t2+(short)0x0))) = (short)v[2]; - v[3] = (((v[18] - v[19]) * 0x16A09) >> 0x10) + v[2]; - // ** Store v[3] -> (T0 + 0)** - *(s16 *)(mp3data+((t0+(short)0x0))) = (short)v[3]; - // 0x1400 - Verified - v[4] = -v[20] -v[21]; - v[6] = v[22] + v[23]; - v[5] = ((v[20] - v[21]) * 0x16A09) >> 0x10; - // ** Store v[4] -> (T3 + 0xFF80) - *(s16 *)(mp3data+((t3+(short)0xFF80))) = (short)v[4]; - v[7] = ((v[22] - v[23]) * 0x2D413) >> 0x10; - v[5] = v[5] - v[4]; - v[7] = v[7] - v[5]; - v[6] = v[6] + v[6]; - v[5] = v[5] - v[6]; - v[4] = -v[4] - v[6]; - // *** Store v[7] -> (T1 + 0xFF80) - *(s16 *)(mp3data+((t1+(short)0xFF80))) = (short)v[7]; - // *** Store v[4] -> (T2 + 0xFF80) - *(s16 *)(mp3data+((t2+(short)0xFF80))) = (short)v[4]; - // *** Store v[5] -> (T0 + 0xFF80) - *(s16 *)(mp3data+((t0+(short)0xFF80))) = (short)v[5]; - v[8] = v[24] + v[25]; - - - v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; - v[2] = v[8] + v[9]; - v[11] = ((v[26] - v[27]) * 0x2D413) >> 0x10; - v[13] = ((v[28] - v[29]) * 0x2D413) >> 0x10; - - v[10] = v[26] + v[27]; v[10] = v[10] + v[10]; - v[12] = v[28] + v[29]; v[12] = v[12] + v[12]; - v[14] = v[30] + v[31]; - v[3] = v[8] + v[10]; - v[14] = v[14] + v[14]; - v[13] = (v[13] - v[2]) + v[12]; - v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - (v[11] + v[2]); - v[14] = -(v[14] + v[14]) + v[3]; - v[17] = v[13] - v[10]; - v[9] = v[9] + v[14]; - // ** Store v[9] -> (T6 + 0x40) - *(s16 *)(mp3data+((t6+(short)0x40))) = (short)v[9]; - v[11] = v[11] - v[13]; - // ** Store v[17] -> (T0 + 0xFFC0) - *(s16 *)(mp3data+((t0+(short)0xFFC0))) = (short)v[17]; - v[12] = v[8] - v[12]; - // ** Store v[11] -> (T0 + 0x40) - *(s16 *)(mp3data+((t0+(short)0x40))) = (short)v[11]; - v[8] = -v[8]; - // ** Store v[15] -> (T1 + 0xFFC0) - *(s16 *)(mp3data+((t1+(short)0xFFC0))) = (short)v[15]; - v[10] = -v[10] -v[12]; - // ** Store v[12] -> (T2 + 0x40) - *(s16 *)(mp3data+((t2+(short)0x40))) = (short)v[12]; - // ** Store v[8] -> (T3 + 0xFFC0) - *(s16 *)(mp3data+((t3+(short)0xFFC0))) = (short)v[8]; - // ** Store v[14] -> (T5 + 0x40) - *(s16 *)(mp3data+((t5+(short)0x40))) = (short)v[14]; - // ** Store v[10] -> (T2 + 0xFFC0) - *(s16 *)(mp3data+((t2+(short)0xFFC0))) = (short)v[10]; - // 0x14FC - Verified... - - // Part 6 - 100% Accurate - - v[0] = *(s16 *)(mp3data+inPtr+(0x00^S16)); v[31] = *(s16 *)(mp3data+inPtr+(0x3E^S16)); v[0] -= v[31]; - v[1] = *(s16 *)(mp3data+inPtr+(0x02^S16)); v[30] = *(s16 *)(mp3data+inPtr+(0x3C^S16)); v[1] -= v[30]; - v[2] = *(s16 *)(mp3data+inPtr+(0x06^S16)); v[28] = *(s16 *)(mp3data+inPtr+(0x38^S16)); v[2] -= v[28]; - v[3] = *(s16 *)(mp3data+inPtr+(0x04^S16)); v[29] = *(s16 *)(mp3data+inPtr+(0x3A^S16)); v[3] -= v[29]; - - v[4] = *(s16 *)(mp3data+inPtr+(0x0E^S16)); v[24] = *(s16 *)(mp3data+inPtr+(0x30^S16)); v[4] -= v[24]; - v[5] = *(s16 *)(mp3data+inPtr+(0x0C^S16)); v[25] = *(s16 *)(mp3data+inPtr+(0x32^S16)); v[5] -= v[25]; - v[6] = *(s16 *)(mp3data+inPtr+(0x08^S16)); v[27] = *(s16 *)(mp3data+inPtr+(0x36^S16)); v[6] -= v[27]; - v[7] = *(s16 *)(mp3data+inPtr+(0x0A^S16)); v[26] = *(s16 *)(mp3data+inPtr+(0x34^S16)); v[7] -= v[26]; - - v[8] = *(s16 *)(mp3data+inPtr+(0x1E^S16)); v[16] = *(s16 *)(mp3data+inPtr+(0x20^S16)); v[8] -= v[16]; - v[9] = *(s16 *)(mp3data+inPtr+(0x1C^S16)); v[17] = *(s16 *)(mp3data+inPtr+(0x22^S16)); v[9] -= v[17]; - v[10]= *(s16 *)(mp3data+inPtr+(0x18^S16)); v[19] = *(s16 *)(mp3data+inPtr+(0x26^S16)); v[10]-= v[19]; - v[11]= *(s16 *)(mp3data+inPtr+(0x1A^S16)); v[18] = *(s16 *)(mp3data+inPtr+(0x24^S16)); v[11]-= v[18]; - - v[12]= *(s16 *)(mp3data+inPtr+(0x10^S16)); v[23] = *(s16 *)(mp3data+inPtr+(0x2E^S16)); v[12]-= v[23]; - v[13]= *(s16 *)(mp3data+inPtr+(0x12^S16)); v[22] = *(s16 *)(mp3data+inPtr+(0x2C^S16)); v[13]-= v[22]; - v[14]= *(s16 *)(mp3data+inPtr+(0x16^S16)); v[20] = *(s16 *)(mp3data+inPtr+(0x28^S16)); v[14]-= v[20]; - v[15]= *(s16 *)(mp3data+inPtr+(0x14^S16)); v[21] = *(s16 *)(mp3data+inPtr+(0x2A^S16)); v[15]-= v[21]; - - //0, 1, 3, 2, 7, 6, 4, 5, 7, 6, 4, 5, 0, 1, 3, 2 - const u16 LUT6[16] = { 0xFFB2, 0xFD3A, 0xF10A, 0xF854, - 0xBDAE, 0xCDA0, 0xE76C, 0xDB94, - 0x1920, 0x4B20, 0xAC7C, 0x7C68, - 0xABEC, 0x9880, 0xDAE8, 0x839C }; - for (i = 0; i < 16; i++) { - v[0+i] = (v[0+i] * LUT6[i]) >> 0x10; - } - v[0] = v[0] + v[0]; v[1] = v[1] + v[1]; - v[2] = v[2] + v[2]; v[3] = v[3] + v[3]; v[4] = v[4] + v[4]; - v[5] = v[5] + v[5]; v[6] = v[6] + v[6]; v[7] = v[7] + v[7]; - v[12] = v[12] + v[12]; v[13] = v[13] + v[13]; v[15] = v[15] + v[15]; - - MP3AB0 (); - - // Part 7: - 100% Accurate + SSV - Unoptimized - - v[0] = ( v[17] + v[16] ) >> 1; - v[1] = ((v[17] * (int)((short)0xA57E * 2)) + (v[16] * 0xB504)) >> 0x10; - v[2] = -v[18] -v[19]; - v[3] = ((v[18] - v[19]) * 0x16A09) >> 0x10; - v[4] = v[20] + v[21] + v[0]; - v[5] = (((v[20] - v[21]) * 0x16A09) >> 0x10) + v[1]; - v[6] = (((v[22] + v[23]) << 1) + v[0]) - v[2]; - v[7] = (((v[22] - v[23]) * 0x2D413) >> 0x10) + v[0] + v[1] + v[3]; - // 0x16A8 - // Save v[0] -> (T3 + 0xFFE0) - *(s16 *)(mp3data+((t3+(short)0xFFE0))) = (short)-v[0]; - v[8] = v[24] + v[25]; - v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; - v[10] = ((v[26] + v[27]) << 1) + v[8]; - v[11] = (((v[26] - v[27]) * 0x2D413) >> 0x10) + v[8] + v[9]; - v[12] = v[4] - ((v[28] + v[29]) << 1); - // ** Store v12 -> (T2 + 0x20) - *(s16 *)(mp3data+((t2+(short)0x20))) = (short)v[12]; - v[13] = (((v[28] - v[29]) * 0x2D413) >> 0x10) - v[12] - v[5]; - v[14] = v[30] + v[31]; - v[14] = v[14] + v[14]; - v[14] = v[14] + v[14]; - v[14] = v[6] - v[14]; - v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - v[7]; - // Store v14 -> (T5 + 0x20) - *(s16 *)(mp3data+((t5+(short)0x20))) = (short)v[14]; - v[14] = v[14] + v[1]; - // Store v[14] -> (T6 + 0x20) - *(s16 *)(mp3data+((t6+(short)0x20))) = (short)v[14]; - // Store v[15] -> (T1 + 0xFFE0) - *(s16 *)(mp3data+((t1+(short)0xFFE0))) = (short)v[15]; - v[9] = v[9] + v[10]; - v[1] = v[1] + v[6]; - v[6] = v[10] - v[6]; - v[1] = v[9] - v[1]; - // Store v[6] -> (T5 + 0x60) - *(s16 *)(mp3data+((t5+(short)0x60))) = (short)v[6]; - v[10] = v[10] + v[2]; - v[10] = v[4] - v[10]; - // Store v[10] -> (T2 + 0xFFA0) - *(s16 *)(mp3data+((t2+(short)0xFFA0))) = (short)v[10]; - v[12] = v[2] - v[12]; - // Store v[12] -> (T2 + 0xFFE0) - *(s16 *)(mp3data+((t2+(short)0xFFE0))) = (short)v[12]; - v[5] = v[4] + v[5]; - v[4] = v[8] - v[4]; - // Store v[4] -> (T2 + 0x60) - *(s16 *)(mp3data+((t2+(short)0x60))) = (short)v[4]; - v[0] = v[0] - v[8]; - // Store v[0] -> (T3 + 0xFFA0) - *(s16 *)(mp3data+((t3+(short)0xFFA0))) = (short)v[0]; - v[7] = v[7] - v[11]; - // Store v[7] -> (T1 + 0xFFA0) - *(s16 *)(mp3data+((t1+(short)0xFFA0))) = (short)v[7]; - v[11] = v[11] - v[3]; - // Store v[1] -> (T6 + 0x60) - *(s16 *)(mp3data+((t6+(short)0x60))) = (short)v[1]; - v[11] = v[11] - v[5]; - // Store v[11] -> (T0 + 0x60) - *(s16 *)(mp3data+((t0+(short)0x60))) = (short)v[11]; - v[3] = v[3] - v[13]; - // Store v[3] -> (T0 + 0x20) - *(s16 *)(mp3data+((t0+(short)0x20))) = (short)v[3]; - v[13] = v[13] + v[2]; - // Store v[13] -> (T0 + 0xFFE0) - *(s16 *)(mp3data+((t0+(short)0xFFE0))) = (short)v[13]; - //v[2] = ; - v[2] = (v[5] - v[2]) - v[9]; - // Store v[2] -> (T0 + 0xFFA0) - *(s16 *)(mp3data+((t0+(short)0xFFA0))) = (short)v[2]; - // 0x7A8 - Verified... - - // Step 8 - Dewindowing - - //u64 *DW = (u64 *)&DeWindowLUT[0x10-(t4>>1)]; - u32 offset = 0x10-(t4>>1); - - u32 addptr = t6 & 0xFFE0; - offset = 0x10-(t4>>1); - - s32 v2=0, v4=0, v6=0, v8=0; - //s32 z2=0, z4=0, z6=0, z8=0; - - offset = 0x10-(t4>>1);// + x*0x40; - int x; - for (x = 0; x < 8; x++) { - v2 = v4 = v6 = v8 = 0; - - //addptr = t1; - - for (i = 7; i >= 0; i--) { - v2 += ((int)*(s16 *)(mp3data+(addptr)+0x00) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v4 += ((int)*(s16 *)(mp3data+(addptr)+0x10) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - v6 += ((int)*(s16 *)(mp3data+(addptr)+0x20) * (short)DeWindowLUT[offset+0x20] + 0x4000) >> 0xF; - v8 += ((int)*(s16 *)(mp3data+(addptr)+0x30) * (short)DeWindowLUT[offset+0x28] + 0x4000) >> 0xF; - addptr+=2; offset++; - } - s32 v0 = v2 + v4; - s32 v18 = v6 + v8; - //Clamp(v0); - //Clamp(v18); - // clamp??? - *(s16 *)(mp3data+(outPtr^S16)) = v0; - *(s16 *)(mp3data+((outPtr+2)^S16)) = v18; - outPtr+=4; - addptr += 0x30; - offset += 0x38; - } - - offset = 0x10-(t4>>1) + 8*0x40; - v2 = v4 = 0; - for (i = 0; i < 4; i++) { - v2 += ((int)*(s16 *)(mp3data+(addptr)+0x00) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v2 += ((int)*(s16 *)(mp3data+(addptr)+0x10) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - addptr+=2; offset++; - v4 += ((int)*(s16 *)(mp3data+(addptr)+0x00) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v4 += ((int)*(s16 *)(mp3data+(addptr)+0x10) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - addptr+=2; offset++; - } - s32 mult6 = *(s32 *)(mp3data+0xCE8); - s32 mult4 = *(s32 *)(mp3data+0xCEC); - if (t4 & 0x2) { - v2 = (v2 * *(u32 *)(mp3data+0xCE8)) >> 0x10; - *(s16 *)(mp3data+(outPtr^S16)) = v2; - } else { - v4 = (v4 * *(u32 *)(mp3data+0xCE8)) >> 0x10; - *(s16 *)(mp3data+(outPtr^S16)) = v4; - mult4 = *(u32 *)(mp3data+0xCE8); - } - addptr -= 0x50; - - for (x = 0; x < 8; x++) { - v2 = v4 = v6 = v8 = 0; - - offset = (0x22F-(t4>>1) + x*0x40); - - for (i = 0; i < 4; i++) { - v2 += ((int)*(s16 *)(mp3data+(addptr )+0x20) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v2 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x20) * (short)DeWindowLUT[offset+0x01] + 0x4000) >> 0xF; - v4 += ((int)*(s16 *)(mp3data+(addptr )+0x30) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - v4 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x30) * (short)DeWindowLUT[offset+0x09] + 0x4000) >> 0xF; - v6 += ((int)*(s16 *)(mp3data+(addptr )+0x00) * (short)DeWindowLUT[offset+0x20] + 0x4000) >> 0xF; - v6 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x00) * (short)DeWindowLUT[offset+0x21] + 0x4000) >> 0xF; - v8 += ((int)*(s16 *)(mp3data+(addptr )+0x10) * (short)DeWindowLUT[offset+0x28] + 0x4000) >> 0xF; - v8 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x10) * (short)DeWindowLUT[offset+0x29] + 0x4000) >> 0xF; - addptr+=4; offset+=2; - } - s32 v0 = v2 + v4; - s32 v18 = v6 + v8; - //Clamp(v0); - //Clamp(v18); - // clamp??? - *(s16 *)(mp3data+((outPtr+2)^S16)) = v0; - *(s16 *)(mp3data+((outPtr+4)^S16)) = v18; - outPtr+=4; - addptr -= 0x50; - } - - int tmp = outPtr; - s32 hi0 = mult6; - s32 hi1 = mult4; - s32 v; - - hi0 = (int)hi0 >> 0x10; - hi1 = (int)hi1 >> 0x10; - for (i = 0; i < 8; i++) { - // v0 - v = (*(s16 *)(mp3data+((tmp-0x40)^S16)) * hi0); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0x40)^S16)) = (s16)v; - // v17 - v = (*(s16 *)(mp3data+((tmp-0x30)^S16)) * hi0); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0x30)^S16)) = v; - // v2 - v = (*(s16 *)(mp3data+((tmp-0x1E)^S16)) * hi1); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0x1E)^S16)) = v; - // v4 - v = (*(s16 *)(mp3data+((tmp-0xE)^S16)) * hi1); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0xE)^S16)) = v; - tmp += 2; - } -} - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucodes.h b/libmupen64plus/mupen64plus-rsp-hle/src/ucodes.h new file mode 100644 index 0000000000..1150cf6b18 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/ucodes.h @@ -0,0 +1,152 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - ucodes.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef UCODES_H +#define UCODES_H + +#include + +struct hle_t; + + +/* cic_x105 ucode */ +void cicx105_ucode(struct hle_t* hle); + + +/* audio list ucodes - audio */ +enum { N_SEGMENTS = 16 }; +struct alist_audio_t { + /* segments */ + uint32_t segments[N_SEGMENTS]; + + /* main buffers */ + uint16_t in; + uint16_t out; + uint16_t count; + + /* auxiliary buffers */ + uint16_t dry_right; + uint16_t wet_left; + uint16_t wet_right; + + /* gains */ + int16_t dry; + int16_t wet; + + /* envelopes (0:left, 1:right) */ + int16_t vol[2]; + int16_t target[2]; + int32_t rate[2]; + + /* ADPCM loop point address */ + uint32_t loop; + + /* storage for ADPCM table and polef coefficients */ + int16_t table[16 * 8]; +}; + +void alist_process_audio (struct hle_t* hle); +void alist_process_audio_ge(struct hle_t* hle); +void alist_process_audio_bc(struct hle_t* hle); + + +/* audio list ucodes - naudio */ +struct alist_naudio_t { + /* gains */ + int16_t dry; + int16_t wet; + + /* envelopes (0:left, 1:right) */ + int16_t vol[2]; + int16_t target[2]; + int32_t rate[2]; + + /* ADPCM loop point address */ + uint32_t loop; + + /* storage for ADPCM table and polef coefficients */ + int16_t table[16 * 8]; +}; + +void alist_process_naudio (struct hle_t* hle); +void alist_process_naudio_bk (struct hle_t* hle); +void alist_process_naudio_dk (struct hle_t* hle); +void alist_process_naudio_mp3 (struct hle_t* hle); +void alist_process_naudio_cbfd(struct hle_t* hle); + + +/* audio list ucodes - nead */ +struct alist_nead_t { + /* main buffers */ + uint16_t in; + uint16_t out; + uint16_t count; + + /* envmixer ramps */ + uint16_t env_values[3]; + uint16_t env_steps[3]; + + /* ADPCM loop point address */ + uint32_t loop; + + /* storage for ADPCM table and polef coefficients */ + int16_t table[16 * 8]; + + /* filter audio command state */ + uint16_t filter_count; + uint32_t filter_lut_address[2]; +}; + +void alist_process_nead_mk (struct hle_t* hle); +void alist_process_nead_sfj (struct hle_t* hle); +void alist_process_nead_sf (struct hle_t* hle); +void alist_process_nead_fz (struct hle_t* hle); +void alist_process_nead_wrjb(struct hle_t* hle); +void alist_process_nead_ys (struct hle_t* hle); +void alist_process_nead_1080(struct hle_t* hle); +void alist_process_nead_oot (struct hle_t* hle); +void alist_process_nead_mm (struct hle_t* hle); +void alist_process_nead_mmb (struct hle_t* hle); +void alist_process_nead_ac (struct hle_t* hle); +void alist_process_nead_mats(struct hle_t* hle); +void alist_process_nead_efz (struct hle_t* hle); + +/* mp3 ucode */ +void mp3_task(struct hle_t* hle, unsigned int index, uint32_t address); + + +/* musyx ucodes */ +void musyx_v1_task(struct hle_t* hle); +void musyx_v2_task(struct hle_t* hle); + + +/* jpeg ucodes */ +void jpeg_decode_PS0(struct hle_t* hle); +void jpeg_decode_PS(struct hle_t* hle); +void jpeg_decode_OB(struct hle_t* hle); + +/* Resident evil 2 ucode */ +void resize_bilinear_task(struct hle_t* hle); +void decode_video_frame_task(struct hle_t* hle); +void fill_video_double_buffer_task(struct hle_t* hle); + +#endif + diff --git a/output/dll/mupen64plus-rsp-hle.dll b/output/dll/mupen64plus-rsp-hle.dll index 777026c971dcaf073cbbfb2d90c0bb63052da57e..e94e85f0936e9610c2df76e22bbb138063db8d77 100644 GIT binary patch literal 76800 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 BOj7^= literal 51712 zcmeFadwdi{);Hcs&x8RIdeDrbg3Ri$q5%yCTrlL>=`aHwoY5$tD4-~qm0eWCL{R|~ zCs}BV5!PkJ3%jE0s?X{QKDq?FWhMb~;g*0(5LZA{CWK1>mz$9OeNR>Q%mh$&pZDF* z=l918pLAW$sZ*z_PF0;cb-E{b#{x-~B*}(9!;qvE2384no^wjKYi}V}2mc3BV!}1qucv$g51H$OOk9k=4hihKgf%Ila z=?f7a{+;7*cD%>KTc%99k7!ygDD+FxUZ9FXR&ZuoARBD)5`l-!ex?F$myLO`Rd|(XA}eU>Owq-WomnJy-2zxY2ww> zC*NIswCCJ;p%fs_V> zND>mFUZOYs;pvkQ=Y4{{;q=y=4e#Em4+3!wwbgCn)t!Nt-!bR^rB3ewO&{-Z-lm22 zx;19MZBL5Co^MkmiIskjrzZCriYzS}OVz?dLt5_k;^CTJ?8(-`gGMctB&D~+y0TF= zEmY(1v8Wo2^-x2{oDZkv8<}fbyK7 zKf5r&{)PVWj=J?Wb&OWFH-=RkLpBuah?cWT)5|@LBInsyisaAPht6-qyk*PyaIT=? zWY3?(Qw<00KDN=v*7)?Po@{yyq{m=Lf;%=IY*cJh%I9Lt0%7~9ze&>U0-sW~R%Pqe z@EEW9fmCW3MMX;Qa$rbOzEWjNQI)09JmX2Ac3MF}YbQ0gT2%toMY=D&PzyyA z)7I;h%C!aTGj*jKC5w)3h5Y0zm9|tZRBbETd<@u`0c7}6+mR|T>M7&Kk}oR^rFaCEzzL?XHzE9Q?HkZCO8X#uR1P4JD)AZ4viAPFTp&L>H>|pUl0TzWsKJ$(> z{l5R1B()sCsQK7jlF$HiHGPYztuOYB1#N#=O`+kYm0iR5_CTnDg#M9-HU*LZ2Z5|% z%U71CJ294&5#1?940)MO8cWLT+Qdf@qd8nz?xE~d)Pa?&P4;#Dn#0uSHx87sIelGs zfRt)*Y8>?Mk?(1dcL?%dnB;d^6R#tkw@y?MHEZbF4636OmHaR0 z_p{pI_iG zN~rcn%GYD@BYsr2QIqQp*FT`6YOGDGh?HE2t{VOpdPPe`SH;f16utBn_+n{>^|6CZF?3HMx&-T?k2U()ssP(i5h>2Yet?lbeLe2+W9xCEi)cM&qdjAJUu$fG zfz>B8(=%Es$LshCG%D8Rn z3R)!(D`R^#KpX*xDdWCTGPfyr)F|WjDVh6}JJugkZqIlP&_l}Dxo*CP%Kg}-=y5J3Z<1hHiLkD94Byl#tl3i-*`wF-vnaV z^>XVtu{t7F&xzG@V%tEYLAhfcRiA-UV9Delib4@yC_;rIyiioBj3|}$>gCm;I&U;) z?~06u=uTTxtZTGp9qE4#4HMEyil&=1Qz(n}F{{y4w9)uHZlyBSr&K;md&&mV1$9#fR%b*-{eRafMzSYO-?JsCB#n_JNrRa=q&S+RmxHP81E zzRj;xH30ArUZBP-(8vpHR+iT*%WJT~bgfgB%d3^jMNfexk*pk%s;7GZ zwsl~qnb>JAB5o{ZP${^9hPAZ_%pg0?3+hDnrZboqyfjheUBE?+vV5(@QFRhCH*glM_7AF#bn*qDp^)r+G;{rGuwT@I-z0ALZ$ppXQ0c*4T9ePd&gB@%bO) z$?Ts%INgJK)ejx0j4uJz&|5FsmSS?VT+Bak)YICZRn}c>X5)Gd`fAcS zgEd6=2(>lZ;^aVRpka;cu3G4zOy?0^wrnRIQ&eMZ{#HpJjb5#QOTrs`&cq#lrGpDH^C$UX0}wi>nHKo}KV3 zl{HG`=GYy_4Fi)m)lB1z2A#=|Oq6?>CY@4wS1J&&kn#eAa9PYFN~I-bdSZ%Miewz6 z_zIPT@blRIW4x{|Ntu0PTaih1(PToYG+IfL5Uz@?5T&{$W%i5x*(^n}i&9vB_;gRg zxjObFFUVJpr2OpIugtPERZzBzIW3cLu8j>7Wr?Yz{DHA7vn97APo0roWxh&ZM~8})^sEMx~d=(j2q=5YE<9Ni1T zJ$*PNtH$Ty5jY?ZcO&1-h@Xu8I2qHyc(p^7e+aGd(z*C!nhcQfsh9^~a3UCVt9OyY zrH;R=#ye^Z3@R2=WqVhkr!XJB{m7gT<(?(T36M4i27_#80ZoQO8rzy<1j2cD{}GGl zCu9;a^-HBnt}td~g$#N2r6I#s>(7j=4tJg;>S?>rB-_i1UIcn$I)c8 z=dvKHRaep|smyy4qv3#7?t}$IsZ1Y+thOS3tjAI7}uZPf#4;$\#lGSj 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 From 5459c92a4d61f33ff63b883e696177eb037b1b99 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 18 Jun 2018 09:00:59 +0100 Subject: [PATCH 309/339] ZXHawk: Hard/Soft client reset options implemented and working - #1158 --- .../Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs | 4 ++-- .../Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs index 6aab944eb7..b64fa125eb 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -24,8 +24,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum string EjectDisk = "Eject Current Disk"; string DiskStatus = "Get Disk Status"; - string HardResetStr = "Hard Reset"; - string SoftResetStr = "Soft Reset"; + string HardResetStr = "Power"; + string SoftResetStr = "Reset"; bool pressed_Play = false; bool pressed_Stop = false; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs index b44e1af74c..749fc03830 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -84,8 +84,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // Power functions List power = new List { - // Tape functions - "Soft Reset", "Hard Reset" + // Power functions + "Reset", "Power" }; foreach (var s in power) From ab9fb5c4e5785f3e3ce9e13a230edc05031e5454 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Mon, 18 Jun 2018 09:51:08 +0100 Subject: [PATCH 310/339] ZXHawk: TZX 'stop-the-tape' block minor improvements --- .../Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs index 2533e1fef9..891914b324 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs @@ -766,17 +766,19 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // issue stop the tape command t.Command = TapeCommand.STOP_THE_TAPE; // add 1ms period - t.DataPeriods.Add(3500); - pauseDuration = -1; + //t.DataPeriods.Add(3500); + //pauseDuration = -1; + } else { // this is actually just a pause pauseDuration = 3500 * pauseDuration; + t.DataPeriods.Add(pauseDuration); } // add end of block pause - t.DataPeriods.Add(pauseDuration); + //t.DataPeriods.Add(pauseDuration); // add to tape _datacorder.DataBlocks.Add(t); From 183983043a7b68d2c899ce722e9d947057fd09b3 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 19 Jun 2018 11:42:08 +0100 Subject: [PATCH 311/339] ZXHawk: Reset disk overrun detection counter on successful read (fixes IronLord.dsk) - #1158 --- .../SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs | 2 ++ .../Hardware/Disk/NECUPD765.IPortIODevice.cs | 12 ++++++++++-- .../SinclairSpectrum/Machine/SpectrumBase.cs | 9 ++++++--- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index 2e1a99a806..f3e946342d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -2415,6 +2415,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum switch (ActivePhase) { case Phase.Execution: + // reset overrun counter + OverrunCounter = 0; // execute read ActiveCommand.CommandDelegate(); diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs index b1ed8bce99..3c53007c0b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Text; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum @@ -23,6 +24,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n"; public bool writeDebug = false; + public List dLog = new List + { + "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN" + }; + + /* * Status read * Data write @@ -56,9 +63,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum sb.Append(ExecCounter).Append(","); sb.Append(ExecLength); - sb.Append("\r\n"); + //sb.Append("\r\n"); - outputString += sb.ToString(); + //outputString += sb.ToString(); + dLog.Add(sb.ToString()); } #endregion diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs index f2c4de0c31..75f146f2a5 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -203,9 +203,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // only write UPD log every second if (FrameCount % 10 == 0) - System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString); - } - + { + System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog); + UPDDiskDevice.dLog = new System.Collections.Generic.List(); + //System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString); + } + } } #endregion From 9bdcc81863e5bc486cf7d6ea87b17da1b0b56c21 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 19 Jun 2018 13:29:54 +0100 Subject: [PATCH 312/339] ZXHawk: Added shadow of the beast dsk detection (although it still doesnt work) --- .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index bebf139c7b..c84c739a14 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -173,6 +173,50 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { Protection = ProtectionType.Hexagon; } + else if (DetectShadowOfTheBeast()) + { + Protection = ProtectionType.ShadowOfTheBeast; + } + } + + /// + /// Detection routine for shadow of the beast game + /// Still cannot get this to work, but at least the game is detected + /// + /// + public bool DetectShadowOfTheBeast() + { + if (DiskTracks[0].Sectors.Length != 9) + return false; + + var zeroSecs = DiskTracks[0].Sectors; + if (zeroSecs[0].SectorID != 65 || + zeroSecs[1].SectorID != 66 || + zeroSecs[2].SectorID != 67 || + zeroSecs[3].SectorID != 68 || + zeroSecs[4].SectorID != 69 || + zeroSecs[5].SectorID != 70 || + zeroSecs[6].SectorID != 71 || + zeroSecs[7].SectorID != 72 || + zeroSecs[8].SectorID != 73) + return false; + + var oneSecs = DiskTracks[1].Sectors; + + if (oneSecs.Length != 8) + return false; + + if (oneSecs[0].SectorID != 17 || + oneSecs[1].SectorID != 18 || + oneSecs[2].SectorID != 19 || + oneSecs[3].SectorID != 20 || + oneSecs[4].SectorID != 21 || + oneSecs[5].SectorID != 22 || + oneSecs[6].SectorID != 23 || + oneSecs[7].SectorID != 24) + return false; + + return true; } /// @@ -676,7 +720,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Alkatraz, Hexagon, Frontier, - PaulOwens + PaulOwens, + ShadowOfTheBeast } } From 6a3377417e6e7c4f7329592ccf322f0419a6535c Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 19 Jun 2018 14:30:44 +0100 Subject: [PATCH 313/339] ZXHawk: removed all Rand() references from weak sector disk read implementation (now all speedlock protected games should load *every* time without issue) --- .../Hardware/Disk/NECUPD765.FDC.cs | 9 ++++++++ .../Hardware/Disk/NECUPD765.FDD.cs | 2 +- .../SinclairSpectrum/Media/Disk/FloppyDisk.cs | 23 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index f3e946342d..f1b2f63274 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -480,6 +480,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ExecBuffer[buffPos++] = sector.ActualData[i]; } + // mark the sector read + sector.SectorReadCompleted(); + // any CRC errors? if (Status1.Bit(SR1_DE) || Status2.Bit(SR2_DD)) { @@ -806,6 +809,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ExecBuffer[buffPos++] = sector.ActualData[i]; } + // mark the sector read + sector.SectorReadCompleted(); + if (sector.SectorID == ActiveCommandParams.EOT) { // this was the last sector to read @@ -1044,6 +1050,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ExecBuffer[buffPos++] = sec.ActualData[b]; } + // mark the sector read + sec.SectorReadCompleted(); + // end of sector - compare IDs if (sec.TrackNumber != ActiveCommandParams.Cylinder || sec.SideNumber != ActiveCommandParams.Head || diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs index 5cf84876f5..0d79ee1bed 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs @@ -857,7 +857,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - #region StateSerialization + #region StateSerialization public void SyncState(Serializer ser) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs index c84c739a14..6aa6dc8d06 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -636,6 +636,14 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum public byte[] SectorData { get; set; } public bool ContainsMultipleWeakSectors { get; set; } + public int WeakReadIndex = 0; + + public void SectorReadCompleted() + { + if (ContainsMultipleWeakSectors) + WeakReadIndex++; + } + public int DataLen { get @@ -681,6 +689,20 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } else { + // weak read neccessary + int copies = ActualDataByteLength / (0x80 << SectorSize); + + // handle index wrap-around + if (WeakReadIndex > copies - 1) + WeakReadIndex = copies - 1; + + // get the sector data based on the current weakreadindex + int step = WeakReadIndex * (0x80 << SectorSize); + byte[] res = new byte[(0x80 << SectorSize)]; + Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); + return res; + + /* int copies = ActualDataByteLength / (0x80 << SectorSize); Random rnd = new Random(); int r = rnd.Next(0, copies - 1); @@ -688,6 +710,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum byte[] res = new byte[(0x80 << SectorSize)]; Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); return res; + */ } } } From ae0543d1f371163cae4f51eb0a4987d3fdfd5fa6 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 19 Jun 2018 14:41:54 +0100 Subject: [PATCH 314/339] ZXHawk: disk drivelight now active for all operations that it should be --- .../SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs index f1b2f63274..62b42c7c09 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -1172,6 +1172,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // was that the last parameter byte? if (CommCounter == ActiveCommand.ParameterByteCount) { + DriveLight = true; + // all parameter bytes received ClearResultBuffer(); Status0 = 0; @@ -1392,6 +1394,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ExecCounter = byteCounter; ExecLength = byteCounter; ActivePhase = Phase.Execution; + DriveLight = true; break; } } @@ -1493,6 +1496,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (CommCounter == ActiveCommand.ParameterByteCount) { // all parameter bytes received - setup for execution phase + DriveLight = true; // clear exec buffer and status registers ClearExecBuffer(); @@ -1683,6 +1687,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum ExecCounter = byteCounter; ExecLength = byteCounter; ActivePhase = Phase.Execution; + DriveLight = true; break; } } @@ -1991,6 +1996,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (CommCounter == ActiveCommand.ParameterByteCount) { // all parameter bytes received + DriveLight = true; ActivePhase = Phase.Execution; ActiveCommand.CommandDelegate(); } @@ -2068,6 +2074,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (CommCounter == ActiveCommand.ParameterByteCount) { // all parameter bytes received + DriveLight = true; ActivePhase = Phase.Execution; ActiveCommand.CommandDelegate(); } From 5b0a41e31cf735e87c46a59056c5ff7044a84497 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Tue, 19 Jun 2018 15:10:56 +0100 Subject: [PATCH 315/339] ZXHawk: Throw an exception when unsupported double-sided disk images are loaded --- .../Media/Disk/CPCExtendedFloppyDisk.cs | 12 ++++++++++++ .../SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs index 3d00d74cb6..6217f2a282 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs @@ -41,6 +41,18 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; DiskData = data; int pos = 0x34; + + if (DiskHeader.NumberOfSides > 1) + { + StringBuilder sbm = new StringBuilder(); + sbm.AppendLine(); + sbm.AppendLine(); + sbm.AppendLine("The detected disk image contains multiple sides."); + sbm.AppendLine("This is NOT currently supported in ZXHawk."); + sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk)."); + throw new System.NotImplementedException(sbm.ToString()); + } + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) { DiskHeader.TrackSizes[i] = data[pos++] * 256; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs index 43f36a6a76..00dfe4f61d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs @@ -42,6 +42,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum DiskData = data; int pos = 0x32; + if (DiskHeader.NumberOfSides > 1) + { + StringBuilder sbm = new StringBuilder(); + sbm.AppendLine(); + sbm.AppendLine(); + sbm.AppendLine("The detected disk image contains multiple sides."); + sbm.AppendLine("This is NOT currently supported in ZXHawk."); + sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk)."); + throw new System.NotImplementedException(sbm.ToString()); + } + // standard CPC format all track sizes are the same in the image for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) { From 50123bf8e2514342376c706b79fdb97ba72c1849 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 20 Jun 2018 15:03:11 +0100 Subject: [PATCH 316/339] ZXHawk: Experimental PZX tape image support --- BizHawk.Client.Common/RomGame.cs | 2 +- BizHawk.Client.EmuHawk/FileLoader.cs | 2 +- BizHawk.Client.EmuHawk/MainForm.cs | 4 +- BizHawk.Emulation.Common/Database/Database.cs | 1 + .../BizHawk.Emulation.Cores.csproj | 5 +- .../Hardware/Datacorder/DatacorderDevice.cs | 36 +- .../Machine/SpectrumBase.Media.cs | 5 + .../Media/MediaConverterType.cs | 1 + .../Media/Tape/PZX/PzxConverter.cs | 403 ++++++++++++++++++ .../Media/Tape/{ => TAP}/TapConverter.cs | 0 .../Media/Tape/{ => TZX}/TzxConverter.cs | 0 .../Media/Tape/TapeDataBlock.cs | 13 +- 12 files changed, 461 insertions(+), 11 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/{ => TAP}/TapConverter.cs (100%) rename BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/{ => TZX}/TzxConverter.cs (100%) diff --git a/BizHawk.Client.Common/RomGame.cs b/BizHawk.Client.Common/RomGame.cs index 13eaf5b94f..315a3adbca 100644 --- a/BizHawk.Client.Common/RomGame.cs +++ b/BizHawk.Client.Common/RomGame.cs @@ -66,7 +66,7 @@ namespace BizHawk.Client.Common { RomData = FileData; } - else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX") + else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || file.Extension == ".PZX") { // these are not roms. unforunately if treated as such there are certain edge-cases // where a header offset is detected. This should mitigate this issue until a cleaner solution is found diff --git a/BizHawk.Client.EmuHawk/FileLoader.cs b/BizHawk.Client.EmuHawk/FileLoader.cs index db3c00d76d..a20c1a4222 100644 --- a/BizHawk.Client.EmuHawk/FileLoader.cs +++ b/BizHawk.Client.EmuHawk/FileLoader.cs @@ -51,7 +51,7 @@ namespace BizHawk.Client.EmuHawk return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", - ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX" + ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX" }; } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index d839888927..2626719f67 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -2081,7 +2081,7 @@ namespace BizHawk.Client.EmuHawk if (VersionInfo.DeveloperBuild) { return FormatFilter( - "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;%ARCH%", + "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;%ARCH%", "Music Files", "*.psf;*.minipsf;*.sid;*.nsf", "Disc Images", "*.cue;*.ccd;*.mds;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", @@ -2109,7 +2109,7 @@ namespace BizHawk.Client.EmuHawk "Apple II", "*.dsk;*.do;*.po;%ARCH%", "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", - "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;%ARCH%", + "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;%ARCH%", "All Files", "*.*"); } diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 052b1957cc..730acf9a05 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -304,6 +304,7 @@ namespace BizHawk.Emulation.Common break; case ".TZX": + case ".PZX": game.System = "ZXSpectrum"; break; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 08933f73d1..61b2dd874d 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -293,10 +293,11 @@ + - - + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 6701d0a923..ff80df1982 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -320,8 +320,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public void LoadTape(byte[] tapeData) { - // check TZX first + // instantiate converters TzxConverter tzxSer = new TzxConverter(this); + TapConverter tapSer = new TapConverter(this); + PzxConverter pzxSer = new PzxConverter(this); + + // TZX if (tzxSer.CheckType(tapeData)) { // this file has a tzx header - attempt serialization @@ -340,9 +344,30 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum "\n\nTape image file has a valid TZX header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); } } + + // PZX + else if (pzxSer.CheckType(tapeData)) + { + // this file has a pzx header - attempt serialization + try + { + pzxSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid PZX header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } + } + + // Assume TAP else { - TapConverter tapSer = new TapConverter(this); try { tapSer.Read(tapeData); @@ -383,7 +408,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { counter++; - if (counter > 50) + if (counter > 30) { counter = 0; bool state = GetEarBit(_machine.CPU.TotalExecutedCycles); @@ -421,7 +446,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // process the cycles based on the waitEdge - while (cycles >= _waitEdge) + while (cycles >= _waitEdge) { // decrement cycles cycles -= _waitEdge; @@ -433,6 +458,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { // start of block + //if (!_dataBlocks[_currentDataBlockIndex].InitialPulseLevel[_position]) + //currentState = !currentState; + // notify about the current block var bl = _dataBlocks[_currentDataBlockIndex]; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index 5d38566152..9a2a37b15c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -197,6 +197,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // spectrum .tzx tape file return SpectrumMediaType.Tape; } + if (hdr.ToUpper().StartsWith("PZXT")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } // if we get this far, assume a .tap file return SpectrumMediaType.Tape; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs index 034e45e5d0..25b3245a20 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs @@ -9,6 +9,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum NONE, TZX, TAP, + PZX, DSK } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs new file mode 100644 index 0000000000..1d913aef8c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs @@ -0,0 +1,403 @@ +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for PZX format serializaton + /// Based on the information here: http://zxds.raxoft.cz/docs/pzx.txt + /// + public class PzxConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.PZX; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + /// + /// Working list of generated tape data blocks + /// + private List _blocks = new List(); + + /// + /// Position counter + /// + private int _position = 0; + + /// + /// Object to keep track of loops - this assumes there is only one loop at a time + /// + private List> _loopCounter = new List>(); + + #region Construction + + private DatacorderDevice _datacorder; + + public PzxConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// Returns TRUE if tzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + // PZX Header + + // check whether this is a valid pzx format file by looking at the identifier in the header + // (first 4 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 4); + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident.ToUpper() != "PZXT") + { + // this is not a valid PZX format file + return false; + } + else + { + return true; + } + } + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + + /* + // PZX uniform block layout + offset type name meaning + ------ ---- ---- ------- + 0 u32 tag unique identifier for the block type. + 4 u32 size size of the block in bytes, excluding the tag and size fields themselves. + 8 u8[size] data arbitrary amount of block data. + */ + + // check whether this is a valid pzx format file by looking at the identifier in the header block + string ident = Encoding.ASCII.GetString(data, 0, 4); + + if (ident.ToUpper() != "PZXT") + { + // this is not a valid TZX format file + throw new Exception(this.GetType().ToString() + + "This is not a valid PZX format file"); + } + + _position = 0; + + // parse all blocks out into seperate byte arrays first + List bDatas = new List(); + + while (_position < data.Length) + { + int startPos = _position; + + // data size + _position += 4; + int blockSize = GetInt32(data, _position); + _position += 4; + + // block data + byte[] bd = new byte[8 + blockSize]; + Array.Copy(data, startPos, bd, 0, bd.Length); + bDatas.Add(bd); + + _position += blockSize; + } + + // process the blocks + foreach (var b in bDatas) + { + int pos = 8; + string blockId = Encoding.ASCII.GetString(b, 0, 4); + int blockSize = GetInt32(b, 4); + + TapeDataBlock t = new TapeDataBlock(); + + switch (blockId) + { + // PZXT - PZX header block + /* + offset type name meaning + 0 u8 major major version number (currently 1). + 1 u8 minor minor version number (currently 0). + 2 u8[?] info tape info, see below. + */ + case "PZXT": + + break; + + // PULS - Pulse sequence + /* + offset type name meaning + 0 u16 count bits 0-14 optional repeat count (see bit 15), always greater than zero + bit 15 repeat count present: 0 not present 1 present + 2 u16 duration1 bits 0-14 low/high (see bit 15) pulse duration bits + bit 15 duration encoding: 0 duration1 1 ((duration1<<16)+duration2) + 4 u16 duration2 optional low bits of pulse duration (see bit 15 of duration1) + 6 ... ... ditto repeated until the end of the block + */ + case "PULS": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + List pulses = new List(); + + while (pos < blockSize + 8) + { + ushort[] p = new ushort[2]; + p[0] = 1; + p[1] = GetWordValue(b, pos); + pos += 2; + + if (p[1] > 0x8000) + { + p[0] = (ushort)(p[1] & 0x7fff); + p[1] = GetWordValue(b, pos); + pos += 2; + } + + if (p[1] >= 0x8000) + { + p[1] &= 0x7fff; + p[1] <<= 16; + p[1] |= GetWordValue(b, pos); + pos += 2; + } + + pulses.Add(p); + } + + // convert to tape block + t.BlockDescription = BlockType.PULS; + t.PauseInMS = 0; + + foreach (var x in pulses) + { + for (int i = 0; i < x[0]; i++) + { + t.DataPeriods.Add(x[1]); + t.InitialPulseLevel.Add(true); + } + } + + _datacorder.DataBlocks.Add(t); + + break; + + // DATA - Data block + /* + offset type name meaning + 0 u32 count bits 0-30 number of bits in the data stream + bit 31 initial pulse level: 0 low 1 high + 4 u16 tail duration of extra pulse after last bit of the block + 6 u8 p0 number of pulses encoding bit equal to 0. + 7 u8 p1 number of pulses encoding bit equal to 1. + 8 u16[p0] s0 sequence of pulse durations encoding bit equal to 0. + 8+2*p0 u16[p1] s1 sequence of pulse durations encoding bit equal to 1. + 8+2*(p0+p1) u8[ceil(bits/8)] data data stream, see below. + */ + case "DATA": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + List s0 = new List(); + List s1 = new List(); + List dData = new List(); + + uint initPulseLevel = 1; + int dCount = 1; + ushort tail = 0; + + while (pos < blockSize + 8) + { + dCount = GetInt32(b, pos); + initPulseLevel = (uint)((dCount & 0x80000000) == 0 ? 0 : 1); + + dCount = (int)(dCount & 0x7FFFFFFF); + pos += 4; + + tail = GetWordValue(b, pos); + pos += 2; + + var p0 = b[pos++]; + var p1 = b[pos++]; + + for (int i = 0; i < p1; i++) + { + var s = GetWordValue(b, pos); + pos += 2; + s0.Add(s); + } + + for (int i = 0; i < p1; i++) + { + var s = GetWordValue(b, pos); + pos += 2; + s1.Add(s); + } + + for (int i = 0; i < Math.Ceiling((decimal)dCount / 8); i++) + { + var buff = b[pos++]; + dData.Add(buff); + } + + bool initPulse = initPulseLevel == 1 ? true : false; + + foreach (var by in dData) + { + for (int i = 7; i >= 0; i--) + { + if (by.Bit(i) == true) + { + foreach (var pu in s1) + { + t.DataPeriods.Add((int)pu); + t.InitialPulseLevel.Add(initPulse); + } + + } + else + { + foreach (var pu in s0) + { + t.DataPeriods.Add((int)pu); + t.InitialPulseLevel.Add(initPulse); + } + + } + } + } + + dData.Clear(); + } + + // convert to tape block + t.BlockDescription = BlockType.DATA; + t.PauseInMS = 0; + + // tail + t.DataPeriods.Add(tail); + + _datacorder.DataBlocks.Add(t); + + break; + + // PAUS - Pause + /* + offset type name meaning + 0 u32 duration bits 0-30 duration of the pause + bit 31 initial pulse level: 0 low 1 high + */ + case "PAUS": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + int iniPulseLevel = 1; + int pCount = 0; + + var d = GetInt32(b, pos); + iniPulseLevel = ((d & 0x80000000) == 0 ? 0 : 1); + pCount = (d & 0x7FFFFFFF); + + // convert to tape block + t.BlockDescription = BlockType.PAUS; + t.DataPeriods.Add(pCount); + + if (iniPulseLevel == 1) + t.InitialPulseLevel.Add(true); + else + t.InitialPulseLevel.Add(false); + + _datacorder.DataBlocks.Add(t); + + break; + + // BRWS - Browse point + /* + offset type name meaning + 0 u8[?] text text describing this browse point + */ + case "BRWS": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + string info = Encoding.ASCII.GetString(b, 8, blockSize); + + // convert to tape block + t.BlockDescription = BlockType.BRWS; + t.MetaData.Add(BlockDescriptorTitle.Comments, info); + t.PauseInMS = 0; + + _datacorder.DataBlocks.Add(t); + + break; + + // STOP - Stop tape command + /* + offset type name meaning + 0 u16 flags when exactly to stop the tape (1 48k only, other always). + */ + case "STOP": + + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + var flags = GetWordValue(b, pos); + if (flags == 1) + { + t.BlockDescription = BlockType.Stop_the_Tape_48K; + t.Command = TapeCommand.STOP_THE_TAPE_48K; + } + else + { + t.BlockDescription = BlockType.Pause_or_Stop_the_Tape; + t.Command = TapeCommand.STOP_THE_TAPE; + } + + _datacorder.DataBlocks.Add(t); + + break; + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapConverter.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs similarity index 100% rename from BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TzxConverter.cs rename to BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index 0b1c4e910f..ed3c046fdc 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -113,6 +113,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public List DataPeriods = new List(); + public List InitialPulseLevel = new List(); + /// /// Command that is raised by this data block /// (that may or may not need to be acted on) @@ -229,8 +231,17 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum Snapshot_Block = 0x40, // unsupported / undetected - Unsupported + Unsupported, + + // PZX blocks + PZXT, + PULS, + DATA, + BRWS, + PAUS + } + /// /// Different title possibilities From 8236d7b6452f1de7b839ebe8e11ed7d76d498025 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 21 Jun 2018 13:39:23 +0100 Subject: [PATCH 317/339] ZXHawk: Some PZX changes --- .../Hardware/Datacorder/DatacorderDevice.cs | 38 ++++++++++++++----- .../Media/Tape/PZX/PzxConverter.cs | 20 ++++------ .../Media/Tape/TapeDataBlock.cs | 2 +- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index ff80df1982..7623daae23 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -408,7 +408,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { counter++; - if (counter > 30) + if (counter > 20) { counter = 0; bool state = GetEarBit(_machine.CPU.TotalExecutedCycles); @@ -451,15 +451,27 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // decrement cycles cycles -= _waitEdge; - // flip the current state - currentState = !currentState; - if (_position == 0 && _tapeIsPlaying) { - // start of block - - //if (!_dataBlocks[_currentDataBlockIndex].InitialPulseLevel[_position]) - //currentState = !currentState; + // start of block - take care of initial pulse level for PZX + switch (_dataBlocks[_currentDataBlockIndex].BlockDescription) + { + case BlockType.PULS: + // initial pulse level is always low + if (currentState) + FlipTapeState(); + break; + case BlockType.DATA: + // initial pulse level is stored in block + if (currentState != _dataBlocks[_currentDataBlockIndex].InitialPulseLevel) + FlipTapeState(); + break; + case BlockType.PAUS: + // initial pulse level is stored in block + if (currentState != _dataBlocks[_currentDataBlockIndex].InitialPulseLevel) + FlipTapeState(); + break; + } // notify about the current block var bl = _dataBlocks[_currentDataBlockIndex]; @@ -569,7 +581,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } // update waitEdge with current position within the current block - _waitEdge = _dataBlocks[_currentDataBlockIndex].DataPeriods[_position]; + _waitEdge = _dataBlocks[_currentDataBlockIndex].DataPeriods[_position]; + + // flip the current state + FlipTapeState(); } @@ -582,6 +597,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return currentState; } + private void FlipTapeState() + { + currentState = !currentState; + } + /// /// Flash loading implementation /// (Deterministic Emulation must be FALSE) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs index 1d913aef8c..44d0c7b8c9 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs @@ -174,6 +174,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.BlockID = GetInt32(b, 0); t.DataPeriods = new List(); + t.InitialPulseLevel = false; + List pulses = new List(); while (pos < blockSize + 8) @@ -210,7 +212,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum for (int i = 0; i < x[0]; i++) { t.DataPeriods.Add(x[1]); - t.InitialPulseLevel.Add(true); } } @@ -248,6 +249,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum dCount = GetInt32(b, pos); initPulseLevel = (uint)((dCount & 0x80000000) == 0 ? 0 : 1); + t.InitialPulseLevel = initPulseLevel == 1 ? true : false; + dCount = (int)(dCount & 0x7FFFFFFF); pos += 4; @@ -277,8 +280,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum dData.Add(buff); } - bool initPulse = initPulseLevel == 1 ? true : false; - foreach (var by in dData) { for (int i = 7; i >= 0; i--) @@ -288,7 +289,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum foreach (var pu in s1) { t.DataPeriods.Add((int)pu); - t.InitialPulseLevel.Add(initPulse); } } @@ -297,13 +297,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum foreach (var pu in s0) { t.DataPeriods.Add((int)pu); - t.InitialPulseLevel.Add(initPulse); } } } } - + if (tail > 0) + t.DataPeriods.Add(tail); dData.Clear(); } @@ -312,7 +312,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum t.PauseInMS = 0; // tail - t.DataPeriods.Add(tail); + //t.DataPeriods.Add(tail); _datacorder.DataBlocks.Add(t); @@ -334,17 +334,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum var d = GetInt32(b, pos); iniPulseLevel = ((d & 0x80000000) == 0 ? 0 : 1); + t.InitialPulseLevel = iniPulseLevel == 1 ? true : false; pCount = (d & 0x7FFFFFFF); // convert to tape block t.BlockDescription = BlockType.PAUS; t.DataPeriods.Add(pCount); - if (iniPulseLevel == 1) - t.InitialPulseLevel.Add(true); - else - t.InitialPulseLevel.Add(false); - _datacorder.DataBlocks.Add(t); break; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index ed3c046fdc..c2b595f240 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -113,7 +113,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum /// public List DataPeriods = new List(); - public List InitialPulseLevel = new List(); + public bool InitialPulseLevel; /// /// Command that is raised by this data block From 84bc77e82ec73b57e1cc0dfb7e683e28a5cbb566 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 21 Jun 2018 14:03:10 +0100 Subject: [PATCH 318/339] ZXHawk: Fixed bug in datacorder 'stop-the-tape' command implementation (when this command was detected it was skipping forward an extra block) --- .../SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 7623daae23..1a4f787db2 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -531,6 +531,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum case TapeCommand.STOP_THE_TAPE: _machine.Spectrum.OSD_TapeStoppedAuto(); + shouldStop = true; if (_currentDataBlockIndex >= _dataBlocks.Count()) RTZ(); @@ -546,6 +547,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (is48k) { _machine.Spectrum.OSD_TapeStoppedAuto(); + shouldStop = true; if (_currentDataBlockIndex >= _dataBlocks.Count()) RTZ(); From 94436c307c7266f70fe4d7bcb0f6953082a1579f Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 21 Jun 2018 15:08:38 +0100 Subject: [PATCH 319/339] ZXHawk: TapeDevice - replaced embedded end of block pause values with individual PAUSE blocks --- .../SinclairSpectrum/Media/MediaConverter.cs | 15 +++++ .../Media/Tape/TAP/TapConverter.cs | 36 +++++++++- .../Media/Tape/TZX/TzxConverter.cs | 65 +++++++++++++++++-- .../Media/Tape/TapeDataBlock.cs | 4 +- 4 files changed, 111 insertions(+), 9 deletions(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs index 6f9dc4c130..631095ccd7 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs @@ -115,6 +115,21 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum buf[offsetIndex + 1] = (byte)(value >> 8); } + /// + /// Takes a PauseInMilliseconds value and returns the value in T-States + /// + /// + /// + public static int TranslatePause(int pauseInMS) + { + // t-states per millisecond + var tspms = (69888 * 50) / 1000; + // get value + int res = pauseInMS * tspms; + + return res; + } + #endregion } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs index 945585cc71..97e1b448cf 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs @@ -44,7 +44,6 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion - #region TAP Format Constants /// @@ -337,7 +336,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // add block pause int actualPause = PAUSE_MS * 1000; - dataPeriods.Add(actualPause); + //dataPeriods.Add(actualPause); // default pause for tap files tdb.PauseInMS = 1000; @@ -348,9 +347,40 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // add the raw data tdb.BlockData = blockdata; + // generate separate PAUS block + TapeDataBlock tdbPause = new TapeDataBlock(); + tdbPause.DataPeriods = new List(); + tdbPause.BlockDescription = BlockType.PAUSE_BLOCK; + tdbPause.PauseInMS = 0; + var pauseInTStates = TranslatePause(tdb.PauseInMS); + //if (pauseInTStates > 0) + //tdbPause.DataPeriods.Add(pauseInTStates); + tdb.PauseInMS = 0; + // add block to the tape _datacorder.DataBlocks.Add(tdb); - + + // PAUS block if neccessary + if (pauseInTStates > 0) + { + tdbPause.AddMetaData(BlockDescriptorTitle.Block_ID, pauseInTStates.ToString() + " cycles"); + + int by1000 = pauseInTStates / 70000; + int rem1000 = pauseInTStates % 70000; + + if (by1000 > 1) + { + tdbPause.DataPeriods.Add(35000); + tdbPause.DataPeriods.Add(pauseInTStates - 35000); + } + else + { + tdbPause.DataPeriods.Add(pauseInTStates); + tdbPause.DataPeriods.Add(0); + } + + _datacorder.DataBlocks.Add(tdbPause); + } } } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs index 891914b324..320f2593ad 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs @@ -322,6 +322,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // advance the position to the next block _position += blockLen; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); } #endregion @@ -379,6 +382,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // advance the position to the next block _position += blockLen; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); } #endregion @@ -502,6 +508,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // advance the position to the next block _position += blockLen; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); } #endregion @@ -595,7 +604,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // add end of block pause if (pauseAfterBlock > 0) { - t.DataPeriods.Add(3500 * pauseAfterBlock); + //t.DataPeriods.Add(3500 * pauseAfterBlock); } t.PauseInMS = pauseAfterBlock; @@ -605,6 +614,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // add the block _datacorder.DataBlocks.Add(t); + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); } #endregion @@ -773,8 +785,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum else { // this is actually just a pause - pauseDuration = 3500 * pauseDuration; - t.DataPeriods.Add(pauseDuration); + //pauseDuration = 3500 * pauseDuration; + //t.DataPeriods.Add(pauseDuration); } // add end of block pause @@ -785,6 +797,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // advanced position to next block _position += 2; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); + } #endregion @@ -1764,8 +1780,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // add block pause if pause is not 0 if (pauseAfterBlock != 0) { - int actualPause = pauseAfterBlock * 3500; - dataPeriods.Add(actualPause); + block.PauseInMS = pauseAfterBlock; + //int actualPause = pauseAfterBlock * 3500; + //dataPeriods.Add(actualPause); } // add to the tapedatablock object @@ -1827,6 +1844,44 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } #endregion + + #region Pause Block Creator + + /// + /// If neccessary a seperate PAUSE block will be created + /// + /// + private void CreatePauseBlock(TapeDataBlock original) + { + if (original.PauseInMS > 0) + { + TapeDataBlock pBlock = new TapeDataBlock(); + pBlock.DataPeriods = new List(); + pBlock.BlockDescription = BlockType.PAUSE_BLOCK; + pBlock.PauseInMS = 0; + var pauseInTStates = TranslatePause(original.PauseInMS); + + pBlock.AddMetaData(BlockDescriptorTitle.Block_ID, pauseInTStates.ToString() + " cycles"); + + int by1000 = pauseInTStates / 70000; + int rem1000 = pauseInTStates % 70000; + + if (by1000 > 1) + { + pBlock.DataPeriods.Add(35000); + pBlock.DataPeriods.Add(pauseInTStates - 35000); + } + else + { + pBlock.DataPeriods.Add(pauseInTStates); + pBlock.DataPeriods.Add(0); + } + + _datacorder.DataBlocks.Add(pBlock); + } + } + + #endregion } public enum DataBlockType diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index c2b595f240..de839971c6 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -238,8 +238,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum PULS, DATA, BRWS, - PAUS + PAUS, + // zxhawk proprietry + PAUSE_BLOCK, } From b90c8f0bec3c48687226e76ba23a856accea4cba Mon Sep 17 00:00:00 2001 From: Asnivor Date: Thu, 21 Jun 2018 16:16:31 +0100 Subject: [PATCH 320/339] ZXHawk: small pzx PAUS block change --- .../Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs index 44d0c7b8c9..016fd8da49 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs @@ -339,7 +339,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // convert to tape block t.BlockDescription = BlockType.PAUS; + t.DataPeriods.Add(0); t.DataPeriods.Add(pCount); + t.DataPeriods.Add(0); _datacorder.DataBlocks.Add(t); From b81a7539cfbf7bf58c4dbe543299083bfdc333cf Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 22 Jun 2018 13:23:33 +0100 Subject: [PATCH 321/339] ZXHawk: Added Compressed Square Wave (CSW) tape image support --- BizHawk.Client.Common/RomGame.cs | 2 +- BizHawk.Client.EmuHawk/FileLoader.cs | 2 +- BizHawk.Client.EmuHawk/MainForm.cs | 4 +- BizHawk.Emulation.Common/Database/Database.cs | 1 + .../BizHawk.Emulation.Cores.csproj | 1 + .../Hardware/Datacorder/DatacorderDevice.cs | 32 ++- .../Machine/SpectrumBase.Media.cs | 5 + .../SinclairSpectrum/Media/MediaConverter.cs | 18 ++ .../Media/MediaConverterType.cs | 1 + .../Media/Tape/CSW/CswConverter.cs | 268 ++++++++++++++++++ .../Media/Tape/PZX/PzxConverter.cs | 2 +- .../Media/Tape/TZX/TzxConverter.cs | 43 ++- 12 files changed, 372 insertions(+), 7 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs diff --git a/BizHawk.Client.Common/RomGame.cs b/BizHawk.Client.Common/RomGame.cs index 315a3adbca..9badb6d7da 100644 --- a/BizHawk.Client.Common/RomGame.cs +++ b/BizHawk.Client.Common/RomGame.cs @@ -66,7 +66,7 @@ namespace BizHawk.Client.Common { RomData = FileData; } - else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || file.Extension == ".PZX") + else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || file.Extension == ".PZX" || file.Extension == ".CSW") { // these are not roms. unforunately if treated as such there are certain edge-cases // where a header offset is detected. This should mitigate this issue until a cleaner solution is found diff --git a/BizHawk.Client.EmuHawk/FileLoader.cs b/BizHawk.Client.EmuHawk/FileLoader.cs index a20c1a4222..da87ee0402 100644 --- a/BizHawk.Client.EmuHawk/FileLoader.cs +++ b/BizHawk.Client.EmuHawk/FileLoader.cs @@ -51,7 +51,7 @@ namespace BizHawk.Client.EmuHawk return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", - ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX" + ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX", ".CSW" }; } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 2626719f67..b44096f7cc 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -2081,7 +2081,7 @@ namespace BizHawk.Client.EmuHawk if (VersionInfo.DeveloperBuild) { return FormatFilter( - "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;%ARCH%", + "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;*.csw;%ARCH%", "Music Files", "*.psf;*.minipsf;*.sid;*.nsf", "Disc Images", "*.cue;*.ccd;*.mds;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", @@ -2109,7 +2109,7 @@ namespace BizHawk.Client.EmuHawk "Apple II", "*.dsk;*.do;*.po;%ARCH%", "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", - "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;%ARCH%", + "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;*.csw;%ARCH%", "All Files", "*.*"); } diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 730acf9a05..6f92d4ddff 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -305,6 +305,7 @@ namespace BizHawk.Emulation.Common case ".TZX": case ".PZX": + case ".CSW": game.System = "ZXSpectrum"; break; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 61b2dd874d..13dd091df7 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -293,6 +293,7 @@ + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 1a4f787db2..8c69171110 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -324,6 +324,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TzxConverter tzxSer = new TzxConverter(this); TapConverter tapSer = new TapConverter(this); PzxConverter pzxSer = new PzxConverter(this); + CswConverter cswSer = new CswConverter(this); // TZX if (tzxSer.CheckType(tapeData)) @@ -365,6 +366,26 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + // CSW + else if (cswSer.CheckType(tapeData)) + { + // this file has a csw header - attempt serialization + try + { + cswSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid CSW header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } + } + // Assume TAP else { @@ -817,7 +838,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (_tapeIsPlaying && _autoPlay) { - _monitorTimeOut--; + if (DataBlocks.Count > 1 || _dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.CSW_Recording) + { + // we should only stop the tape when there are multiple blocks + // if we just have one big block (maybe a CSW or WAV) then auto stopping will cock things up + _monitorTimeOut--; + } if (_monitorTimeOut < 0) { @@ -843,6 +869,10 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (timeout == 0) return; + // dont autostop if there is only 1 block + if (DataBlocks.Count > 2 || _dataBlocks[_currentDataBlockIndex].BlockDescription == BlockType.CSW_Recording) + return; + if (diff >= timeout * 2) { // There have been no attempted tape reads by the CPU within the double timeout period diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index 9a2a37b15c..d2f572af1b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -202,6 +202,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // spectrum .tzx tape file return SpectrumMediaType.Tape; } + if (hdr.ToUpper().StartsWith("COMPRESSED SQ")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } // if we get this far, assume a .tap file return SpectrumMediaType.Tape; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs index 631095ccd7..f9059c7e10 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs @@ -1,4 +1,6 @@ using System; +using System.IO; +using System.IO.Compression; namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { @@ -130,6 +132,22 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return res; } + /// + /// Decompresses a byte array that is Z-RLE compressed + /// + /// + /// + public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer) + { + MemoryStream stream = new MemoryStream(); + stream.Write(sourceBuffer, 0, sourceBuffer.Length); + stream.Position = 0; + stream.ReadByte(); + stream.ReadByte(); + DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false); + ds.Read(destBuffer, 0, destBuffer.Length); + } + #endregion } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs index 25b3245a20..ed89724f30 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs @@ -10,6 +10,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TZX, TAP, PZX, + CSW, DSK } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs new file mode 100644 index 0000000000..8f0ae34787 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs @@ -0,0 +1,268 @@ +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Responsible for Compressed Square Wave conversion + /// https://web.archive.org/web/20171024182530/http://ramsoft.bbk.org.omegahg.com/csw.html + /// + public class CswConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.CSW; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Position counter + /// + private int _position = 0; + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + #region Construction + + private DatacorderDevice _datacorder; + + public CswConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// Returns TRUE if pzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + // CSW Header + + // check whether this is a valid csw format file by looking at the identifier in the header + // (first 22 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 22); + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident.ToUpper() != "COMPRESSED SQUARE WAVE") + { + // this is not a valid CSW format file + return false; + } + else + { + return true; + } + } + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + + // CSW Header + + // check whether this is a valid csw format file by looking at the identifier in the header + // (first 22 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 22); + + if (ident.ToUpper() != "COMPRESSED SQUARE WAVE") + { + // this is not a valid CSW format file + throw new Exception(this.GetType().ToString() + + "This is not a valid CSW format file"); + } + + if (data[0x16] != 0x1a) + { + // invalid terminator code + throw new Exception(this.GetType().ToString() + + "This image reports as a CSW but has an invalid terminator code"); + } + + _position = 0; + + // version info + int majorVer = data[0x17]; + int minorVer = data[0x18]; + + int sampleRate; + int totalPulses; + byte compressionType; + byte flags; + byte headerExtensionLen; + byte[] cswData; + byte[] cswDataUncompressed; + + if (majorVer == 2) + { + /* + CSW-2 Header + CSW global file header - status: required + Offset Value Type Description + 0x00 (note) ASCII[22] "Compressed Square Wave" signature + 0x16 0x1A BYTE Terminator code + 0x17 0x02 BYTE CSW major revision number + 0x18 0x00 BYTE CSW minor revision number + 0x19 - DWORD Sample rate + 0x1D - DWORD Total number of pulses (after decompression) + 0x21 - BYTE Compression type (see notes below) + 0x01: RLE + 0x02: Z-RLE + 0x22 - BYTE Flags + b0: initial polarity; if set, the signal starts at logical high + 0x23 HDR BYTE Header extension length in bytes (0x00) + For future expansions only, see note below. + 0x24 - ASCIIZ[16] Encoding application description + Information about the tool which created the file (e.g. name and version) + 0x34 - BYTE[HDR] Header extension data (if present) + 0x34+HDR - - CSW data. + */ + + _position = 0x19; + sampleRate = GetInt32(data, _position); + _position += 4; + + totalPulses = GetInt32(data, _position); + cswDataUncompressed = new byte[totalPulses + 1]; + _position += 4; + + compressionType = data[_position++]; + flags = data[_position++]; + headerExtensionLen = data[_position++]; + + _position = 0x34 + headerExtensionLen; + + cswData = new byte[data.Length - _position]; + Array.Copy(data, _position, cswData, 0, cswData.Length); + + ProcessCSWV2(cswData, ref cswDataUncompressed, compressionType, totalPulses); + } + else if (majorVer == 1) + { + /* + CSW-1 Header + CSW global file header - status: required + Offset Value Type Description + 0x00 (note) ASCII[22] "Compressed Square Wave" signature + 0x16 0x1A BYTE Terminator code + 0x17 0x01 BYTE CSW major revision number + 0x18 0x01 BYTE CSW minor revision number + 0x19 - WORD Sample rate + 0x1B 0x01 BYTE Compression type + 0x01: RLE + 0x1C - BYTE Flags + b0: initial polarity; if set, the signal starts at logical high + 0x1D 0x00 BYTE[3] Reserved. + 0x20 - - CSW data. + */ + + _position = 0x19; + sampleRate = GetWordValue(data, _position); + _position += 2; + + compressionType = data[_position++]; + flags = data[_position++]; + + _position += 3; + + cswDataUncompressed = new byte[data.Length - _position]; + + if (compressionType == 1) + Array.Copy(data, _position, cswDataUncompressed, 0, cswDataUncompressed.Length); + else + throw new Exception(this.GetType().ToString() + + "CSW Format unknown compression type"); + } + else + { + throw new Exception(this.GetType().ToString() + + "CSW Format Version " + majorVer + "." + minorVer + " is not currently supported"); + } + + // create the single tape block + // (use DATA block for now so initial signal level is handled correctly by the datacorder device) + TapeDataBlock t = new TapeDataBlock(); + t.BlockDescription = BlockType.CSW_Recording; + t.BlockID = 0x18; + t.DataPeriods = new List(); + + if (flags.Bit(0)) + t.InitialPulseLevel = true; + else + t.InitialPulseLevel = false; + + var rate = (69888 * 50) / sampleRate; + + for (int i = 0; i < cswDataUncompressed.Length;) + { + int length = cswDataUncompressed[i++] * rate; + if (length == 0) + { + length = GetInt32(cswDataUncompressed, i) / rate; + i += 4; + } + + t.DataPeriods.Add(length); + } + + // add closing period + t.DataPeriods.Add((69888 * 50) / 10); + + // add to datacorder + _datacorder.DataBlocks.Add(t); + } + + /// + /// Processes a CSW v2 data block + /// + /// + /// + /// + /// + /// + public static void ProcessCSWV2( + byte[] srcBuff, + ref byte[] destBuff, + byte compType, + int pulseCount) + { + if (compType == 1) + { + Array.Copy(srcBuff, 0, destBuff, 0, pulseCount); + } + else if (compType == 2) + { + DecompressZRLE(srcBuff, ref destBuff); + } + else + throw new Exception("CSW Format unknown compression type"); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs index 016fd8da49..6ada823be8 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs @@ -61,7 +61,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum #endregion /// - /// Returns TRUE if tzx header is detected + /// Returns TRUE if pzx header is detected /// /// public override bool CheckType(byte[] data) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs index 320f2593ad..052be1824f 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs @@ -643,10 +643,51 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum int blockLen = GetInt32(data, _position); _position += 4; - _position += blockLen; + t.PauseInMS = GetWordValue(data, _position); + + _position += 2; + + int sampleRate = data[_position++] << 16 | data[_position++] << 8 | data[_position++]; + byte compType = data[_position++]; + int pulses = GetInt32(data, _position); + _position += 4; + + int dataLen = blockLen - 10; + + // build source array + byte[] src = new byte[dataLen]; + // build destination array + byte[] dest = new byte[pulses + 1]; + + // process the CSW data + CswConverter.ProcessCSWV2(src, ref dest, compType, pulses); + + // create the periods + var rate = (69888 * 50) / sampleRate; + + for (int i = 0; i < dest.Length;) + { + int length = dest[i++] * rate; + if (length == 0) + { + length = GetInt32(dest, i) / rate; + i += 4; + } + + t.DataPeriods.Add(length); + } + + // add closing period + t.DataPeriods.Add((69888 * 50) / 10); + + _position += dataLen; + //_position += blockLen; // add the block _datacorder.DataBlocks.Add(t); + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); } #endregion From e2b58cfb982d6244106d1bc3648ce2feb529366a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 22 Jun 2018 13:43:26 +0100 Subject: [PATCH 322/339] ZXHawk: improvements to tape traps --- .../Hardware/Datacorder/DatacorderDevice.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 8c69171110..23e09b6dba 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -847,7 +847,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum if (_monitorTimeOut < 0) { - AutoStopTape(); + if (_dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.PAUSE_BLOCK && + _dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.PAUS) + { + AutoStopTape(); + } + return; } @@ -859,6 +864,13 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // get current datablock var block = DataBlocks[_currentDataBlockIndex]; + // is this a pause block? + if (block.BlockDescription == BlockType.PAUS || block.BlockDescription == BlockType.PAUSE_BLOCK) + { + // dont autostop the tape here + return; + } + // pause in ms at the end of the current block int blockPause = block.PauseInMS; From 625f063861e049f35845218bffd8ea913e65dbcc Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 22 Jun 2018 14:40:40 +0100 Subject: [PATCH 323/339] ZXHawk: Added wav tape image support --- BizHawk.Client.Common/RomGame.cs | 3 +- BizHawk.Client.EmuHawk/FileLoader.cs | 2 +- BizHawk.Client.EmuHawk/MainForm.cs | 4 +- BizHawk.Emulation.Common/Database/Database.cs | 4 +- .../BizHawk.Emulation.Cores.csproj | 4 + .../Hardware/Datacorder/DatacorderDevice.cs | 31 ++++- .../Machine/SpectrumBase.Media.cs | 5 + .../Media/MediaConverterType.cs | 1 + .../Media/Tape/TapeDataBlock.cs | 2 + .../Media/Tape/WAV/StreamHelper.cs | 104 ++++++++++++++ .../Media/Tape/WAV/WavConverter.cs | 130 ++++++++++++++++++ .../Media/Tape/WAV/WavHeader.cs | 107 ++++++++++++++ .../Media/Tape/WAV/WavStreamReader.cs | 117 ++++++++++++++++ .../Computers/SinclairSpectrum/readme.md | 2 +- 14 files changed, 507 insertions(+), 9 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/StreamHelper.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavHeader.cs create mode 100644 BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavStreamReader.cs diff --git a/BizHawk.Client.Common/RomGame.cs b/BizHawk.Client.Common/RomGame.cs index 9badb6d7da..f225c96458 100644 --- a/BizHawk.Client.Common/RomGame.cs +++ b/BizHawk.Client.Common/RomGame.cs @@ -66,7 +66,8 @@ namespace BizHawk.Client.Common { RomData = FileData; } - else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || file.Extension == ".PZX" || file.Extension == ".CSW") + else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || + file.Extension == ".PZX" || file.Extension == ".CSW" || file.Extension == ".WAV") { // these are not roms. unforunately if treated as such there are certain edge-cases // where a header offset is detected. This should mitigate this issue until a cleaner solution is found diff --git a/BizHawk.Client.EmuHawk/FileLoader.cs b/BizHawk.Client.EmuHawk/FileLoader.cs index da87ee0402..befabbc9c4 100644 --- a/BizHawk.Client.EmuHawk/FileLoader.cs +++ b/BizHawk.Client.EmuHawk/FileLoader.cs @@ -51,7 +51,7 @@ namespace BizHawk.Client.EmuHawk return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", - ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX", ".CSW" + ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX", ".CSW", ".WAV" }; } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index b44096f7cc..e7064c304c 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -2081,7 +2081,7 @@ namespace BizHawk.Client.EmuHawk if (VersionInfo.DeveloperBuild) { return FormatFilter( - "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;*.csw;%ARCH%", + "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;*.csw;*.wav;%ARCH%", "Music Files", "*.psf;*.minipsf;*.sid;*.nsf", "Disc Images", "*.cue;*.ccd;*.mds;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", @@ -2109,7 +2109,7 @@ namespace BizHawk.Client.EmuHawk "Apple II", "*.dsk;*.do;*.po;%ARCH%", "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", - "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;*.csw;%ARCH%", + "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;*.csw;*.wav;%ARCH%", "All Files", "*.*"); } diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 6f92d4ddff..5fcf3ce89f 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -306,11 +306,11 @@ namespace BizHawk.Emulation.Common case ".TZX": case ".PZX": case ".CSW": + case ".WAV": game.System = "ZXSpectrum"; break; - case ".TAP": - + case ".TAP": byte[] head = romData.Take(8).ToArray(); if (System.Text.Encoding.Default.GetString(head).Contains("C64-TAPE")) game.System = "C64"; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 13dd091df7..faf0089eb2 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -299,6 +299,10 @@ + + + + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs index 23e09b6dba..55e4a1d14c 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -325,6 +325,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TapConverter tapSer = new TapConverter(this); PzxConverter pzxSer = new PzxConverter(this); CswConverter cswSer = new CswConverter(this); + WavConverter wavSer = new WavConverter(this); // TZX if (tzxSer.CheckType(tapeData)) @@ -386,6 +387,26 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum } } + // WAV + else if (wavSer.CheckType(tapeData)) + { + // this file has a csw header - attempt serialization + try + { + wavSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid WAV header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } + } + // Assume TAP else { @@ -838,7 +859,9 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum { if (_tapeIsPlaying && _autoPlay) { - if (DataBlocks.Count > 1 || _dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.CSW_Recording) + if (DataBlocks.Count > 1 || + (_dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.CSW_Recording && + _dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.WAV_Recording)) { // we should only stop the tape when there are multiple blocks // if we just have one big block (maybe a CSW or WAV) then auto stopping will cock things up @@ -882,8 +905,12 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum return; // dont autostop if there is only 1 block - if (DataBlocks.Count > 2 || _dataBlocks[_currentDataBlockIndex].BlockDescription == BlockType.CSW_Recording) + if (DataBlocks.Count == 1 || _dataBlocks[_currentDataBlockIndex].BlockDescription == BlockType.CSW_Recording || + _dataBlocks[_currentDataBlockIndex].BlockDescription == BlockType.WAV_Recording + ) + { return; + } if (diff >= timeout * 2) { diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs index d2f572af1b..50af7674b0 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -207,6 +207,11 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // spectrum .tzx tape file return SpectrumMediaType.Tape; } + if (hdr.ToUpper().Contains("WAVE")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } // if we get this far, assume a .tap file return SpectrumMediaType.Tape; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs index ed89724f30..c82133037b 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs @@ -11,6 +11,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum TAP, PZX, CSW, + WAV, DSK } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs index de839971c6..adcc0eee97 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -242,6 +242,8 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum // zxhawk proprietry PAUSE_BLOCK, + + WAV_Recording } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/StreamHelper.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/StreamHelper.cs new file mode 100644 index 0000000000..5c24a66b61 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/StreamHelper.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// From https://archive.codeplex.com/?p=zxmak2 + /// + public static class StreamHelper + { + public static void Write(Stream stream, Int32 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, UInt32 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, Int16 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, UInt16 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, Byte value) + { + byte[] data = new byte[1]; + data[0] = value; + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, SByte value) + { + byte[] data = new byte[1]; + data[0] = (byte)value; + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, byte[] value) + { + stream.Write(value, 0, value.Length); + } + + + public static void Read(Stream stream, out Int32 value) + { + byte[] data = new byte[4]; + stream.Read(data, 0, data.Length); + //if (!BitConverter.IsLittleEndian) + // Array.Reverse(data); + value = BitConverter.ToInt32(data, 0); + } + + public static void Read(Stream stream, out UInt32 value) + { + byte[] data = new byte[4]; + stream.Read(data, 0, data.Length); + value = BitConverter.ToUInt32(data, 0); + } + + public static void Read(Stream stream, out Int16 value) + { + byte[] data = new byte[2]; + stream.Read(data, 0, data.Length); + value = BitConverter.ToInt16(data, 0); + } + + public static void Read(Stream stream, out UInt16 value) + { + byte[] data = new byte[2]; + stream.Read(data, 0, data.Length); + value = BitConverter.ToUInt16(data, 0); + } + + public static void Read(Stream stream, out Byte value) + { + byte[] data = new byte[1]; + stream.Read(data, 0, data.Length); + value = data[0]; + } + + public static void Read(Stream stream, out SByte value) + { + byte[] data = new byte[1]; + stream.Read(data, 0, data.Length); + value = (sbyte)data[0]; + } + + public static void Read(Stream stream, byte[] value) + { + stream.Read(value, 0, value.Length); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs new file mode 100644 index 0000000000..07c8d3e300 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for WAV format conversion + /// Based heavily on code from zxmak2: https://archive.codeplex.com/?p=zxmak2 + /// + public class WavConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.WAV; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + /// + /// Position counter + /// + private int _position = 0; + + #region Construction + + private DatacorderDevice _datacorder; + + public WavConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// Returns TRUE if pzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + // WAV Header + + // check whether this is a valid wav format file by looking at the identifier in the header + string ident = Encoding.ASCII.GetString(data, 8, 4); + + if (ident.ToUpper() != "WAVE") + { + // this is not a valid WAV format file + return false; + } + else + { + return true; + } + } + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + + // check whether this is a valid pzx format file by looking at the identifier in the header block + string ident = Encoding.ASCII.GetString(data, 8, 4); + + if (ident.ToUpper() != "WAVE") + { + // this is not a valid TZX format file + throw new Exception(this.GetType().ToString() + + "This is not a valid WAV format file"); + } + + _position = 0; + + MemoryStream stream = new MemoryStream(); + stream.Write(data, 0, data.Length); + stream.Position = 0; + + WavStreamReader reader = new WavStreamReader(stream); + + int rate = (69888 * 50) / reader.Header.sampleRate; + int smpCounter = 0; + int state = reader.ReadNext(); + + // create the single tape block + TapeDataBlock t = new TapeDataBlock(); + t.BlockDescription = BlockType.WAV_Recording; + t.BlockID = 0; + t.DataPeriods = new List(); + + for (int i = 0; i < reader.Count; i++) + { + int sample = reader.ReadNext(); + smpCounter++; + if ((state < 0 && sample < 0) || (state >= 0 && sample >= 0)) + continue; + t.DataPeriods.Add(smpCounter * rate); + smpCounter = 0; + state = sample; + } + + // add closing period + t.DataPeriods.Add((69888 * 50) / 10); + + // add to datacorder + _datacorder.DataBlocks.Add(t); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavHeader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavHeader.cs new file mode 100644 index 0000000000..8a19b53ed9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavHeader.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// From https://archive.codeplex.com/?p=zxmak2 + /// + public class WavHeader + { + // RIFF chunk (12 bytes) + public Int32 chunkID; // "RIFF" + public Int32 fileSize; + public Int32 riffType; // "WAVE" + + // Format chunk (24 bytes) + public Int32 fmtID; // "fmt " + public Int32 fmtSize; + public Int16 fmtCode; + public Int16 channels; + public Int32 sampleRate; + public Int32 fmtAvgBPS; + public Int16 fmtBlockAlign; + public Int16 bitDepth; + public Int16 fmtExtraSize; + + // Data chunk + public Int32 dataID; // "data" + public Int32 dataSize; // The data size should be file size - 36 bytes. + + + public void Deserialize(Stream stream) + { + StreamHelper.Read(stream, out chunkID); + StreamHelper.Read(stream, out fileSize); + StreamHelper.Read(stream, out riffType); + if (chunkID != BitConverter.ToInt32(Encoding.ASCII.GetBytes("RIFF"), 0)) + { + throw new FormatException("Invalid WAV file header"); + } + if (riffType != BitConverter.ToInt32(Encoding.ASCII.GetBytes("WAVE"), 0)) + { + throw new FormatException(string.Format( + "Not supported RIFF type: '{0}'", + Encoding.ASCII.GetString(BitConverter.GetBytes(riffType)))); + } + Int32 chunkId; + Int32 chunkSize; + while (stream.Position < stream.Length) + { + StreamHelper.Read(stream, out chunkId); + StreamHelper.Read(stream, out chunkSize); + string strChunkId = Encoding.ASCII.GetString( + BitConverter.GetBytes(chunkId)); + if (strChunkId == "fmt ") + { + read_fmt(stream, chunkId, chunkSize); + } + else if (strChunkId == "data") + { + read_data(stream, chunkId, chunkSize); + break; + } + else + { + stream.Seek(chunkSize, SeekOrigin.Current); + } + } + if (fmtID != BitConverter.ToInt32(Encoding.ASCII.GetBytes("fmt "), 0)) + { + throw new FormatException("WAV format chunk not found"); + } + if (dataID != BitConverter.ToInt32(Encoding.ASCII.GetBytes("data"), 0)) + { + throw new FormatException("WAV data chunk not found"); + } + } + + private void read_data(Stream stream, int chunkId, int chunkSize) + { + dataID = chunkId; + dataSize = chunkSize; + } + + private void read_fmt(Stream stream, int chunkId, int chunkSize) + { + fmtID = chunkId; + fmtSize = chunkSize; + StreamHelper.Read(stream, out fmtCode); + StreamHelper.Read(stream, out channels); + StreamHelper.Read(stream, out sampleRate); + StreamHelper.Read(stream, out fmtAvgBPS); + StreamHelper.Read(stream, out fmtBlockAlign); + StreamHelper.Read(stream, out bitDepth); + if (fmtSize == 18) + { + // Read any extra values + StreamHelper.Read(stream, out fmtExtraSize); + stream.Seek(fmtExtraSize, SeekOrigin.Current); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavStreamReader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavStreamReader.cs new file mode 100644 index 0000000000..042b276c7e --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavStreamReader.cs @@ -0,0 +1,117 @@ +using System; +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// From https://archive.codeplex.com/?p=zxmak2 + /// + public class WavStreamReader + { + private Stream m_stream; + private WavHeader m_header = new WavHeader(); + + public WavStreamReader(Stream stream) + { + m_stream = stream; + m_header.Deserialize(stream); + } + + public WavHeader Header { get { return m_header; } } + + public int Count { get { return m_header.dataSize / m_header.fmtBlockAlign; } } + + public Int32 ReadNext() + { + // check - sample should be in PCM format + if (m_header.fmtCode != WAVE_FORMAT_PCM && + m_header.fmtCode != WAVE_FORMAT_IEEE_FLOAT) + { + throw new FormatException(string.Format( + "Not supported audio format: fmtCode={0}, bitDepth={1}", + m_header.fmtCode, + m_header.bitDepth)); + } + byte[] data = new byte[m_header.fmtBlockAlign]; + m_stream.Read(data, 0, data.Length); + if (m_header.fmtCode == WAVE_FORMAT_PCM) + { + // use first channel only + if (m_header.bitDepth == 8) + return getSamplePcm8(data, 0, 0); + if (m_header.bitDepth == 16) + return getSamplePcm16(data, 0, 0); + if (m_header.bitDepth == 24) + return getSamplePcm24(data, 0, 0); + if (m_header.bitDepth == 32) + return getSamplePcm32(data, 0, 0); + } + else if (m_header.fmtCode == WAVE_FORMAT_IEEE_FLOAT) + { + // use first channel only + if (m_header.bitDepth == 32) + return getSampleFloat32(data, 0, 0); + if (m_header.bitDepth == 64) + return getSampleFloat64(data, 0, 0); + } + throw new NotSupportedException(string.Format( + "Not supported audio format ({0}/{1} bit)", + m_header.fmtCode == WAVE_FORMAT_PCM ? "PCM" : "FLOAT", + m_header.bitDepth)); + } + + private Int32 getSamplePcm8(byte[] bufferRaw, int offset, int channel) + { + return bufferRaw[offset + channel] - 128; + } + + private Int32 getSamplePcm16(byte[] bufferRaw, int offset, int channel) + { + return BitConverter.ToInt16(bufferRaw, offset + 2 * channel); + } + + private Int32 getSamplePcm24(byte[] bufferRaw, int offset, int channel) + { + Int32 result; + int subOffset = offset + channel * 3; + if (BitConverter.IsLittleEndian) + { + result = ((sbyte)bufferRaw[2 + subOffset]) * 0x10000; + result |= bufferRaw[1 + subOffset] * 0x100; + result |= bufferRaw[0 + subOffset]; + } + else + { + result = ((sbyte)bufferRaw[0 + subOffset]) * 0x10000; + result |= bufferRaw[1 + subOffset] * 0x100; + result |= bufferRaw[2 + subOffset]; + } + return result; + } + + private Int32 getSamplePcm32(byte[] bufferRaw, int offset, int channel) + { + return BitConverter.ToInt32(bufferRaw, offset + 4 * channel); + } + + private Int32 getSampleFloat32(byte[] data, int offset, int channel) + { + float fSample = BitConverter.ToSingle(data, offset + 4 * channel); + // convert to 32 bit integer + return (Int32)(fSample * Int32.MaxValue); + } + + private Int32 getSampleFloat64(byte[] data, int offset, int channel) + { + double fSample = BitConverter.ToDouble(data, offset + 8 * channel); + // convert to 32 bit integer + return (Int32)(fSample * Int32.MaxValue); + } + + private const int WAVE_FORMAT_PCM = 1; /* PCM */ + private const int WAVE_FORMAT_IEEE_FLOAT = 3; /* IEEE float */ + private const int WAVE_FORMAT_ALAW = 6; /* 8-bit ITU-T G.711 A-law */ + private const int WAVE_FORMAT_MULAW = 7; /* 8-bit ITU-T G.711 µ-law */ + private const int WAVE_FORMAT_EXTENSIBLE = 0xFFFE; /* Determined by SubFormat */ + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 300abfd881..39c06a7c02 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -11,7 +11,7 @@ ZXHawk is still in dev but is potentially nearing a release state. * Full beeper and AY-3-3912 sound emulation * Tape device (datacorder) emulation * Internal 3" disk drive emulation (found in the +3 model) -* Currently supports the following tape image formats: *.tzx, *.tap +* Currently supports the following tape image formats: *.tzx, *.tap, *.pzx, *.csw, *.wav * Currently supports the following disk image formats (+3 only): *.dsk * Fully integrated into the Bizhawk ecosystem * See the ZXSpectrum menu for all available configuration options From 1ade5d0b49d5b9ac1e095f356e7e1083dcd0db52 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 22 Jun 2018 16:53:32 +0100 Subject: [PATCH 324/339] ZXHawk: Prep for release --- BizHawk.Client.EmuHawk/FileLoader.cs | 2 +- BizHawk.Client.EmuHawk/MainForm.cs | 5 +++-- .../Computers/SinclairSpectrum/ZXSpectrum.cs | 2 +- BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md | 2 -- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/BizHawk.Client.EmuHawk/FileLoader.cs b/BizHawk.Client.EmuHawk/FileLoader.cs index befabbc9c4..bbe8b9d62e 100644 --- a/BizHawk.Client.EmuHawk/FileLoader.cs +++ b/BizHawk.Client.EmuHawk/FileLoader.cs @@ -55,7 +55,7 @@ namespace BizHawk.Client.EmuHawk }; } - return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", ".32X", ".MDS" }; + return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", ".32X", ".MDS", ".TZX", ".PZX", ".CSW", ".WAV" }; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index fd25d9f7ee..508a6840b0 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -2119,7 +2119,7 @@ namespace BizHawk.Client.EmuHawk } return FormatFilter( - "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.32x;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.int;*.rom;*.m3u;*.cue;*.ccd;*.mds;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;*.dsk;*.do;*.po;*.psf;*.ngp;*.ngc;*.prg;*.d64;*.g64;*.minipsf;*.nsf;%ARCH%", + "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.32x;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.int;*.rom;*.m3u;*.cue;*.ccd;*.mds;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;*.dsk;*.do;*.po;*.psf;*.ngp;*.ngc;*.prg;*.d64;*.g64;*.minipsf;*.nsf;*.tzx;*.pzx;*.csw;*.wav;%ARCH%", "Disc Images", "*.cue;*.ccd;*.mds;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", "Super NES", "*.smc;*.sfc;*.xml;%ARCH%", @@ -2144,7 +2144,8 @@ namespace BizHawk.Client.EmuHawk "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", "Commodore 64", "*.prg; *.d64, *.g64; *.crt; *.tap;%ARCH%", - "All Files", "*.*"); + "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;*.csw;*.wav;%ARCH%", + "All Files", "*.*"); } } diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs index 811ede903c..7a50f1e7d3 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum "ZXHawk", "Asnivor, Alyosha", isPorted: false, - isReleased: false)] + isReleased: true)] public partial class ZXSpectrum : IRegionable, IDriveLight { public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md index 39c06a7c02..58280ac42d 100644 --- a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -1,7 +1,5 @@ ## ZXHawk -ZXHawk is still in dev but is potentially nearing a release state. - ### Whats in the box? * Emulates the Sinclair ZX Spectrum 16k, 48k, 128k, +2, +2A & +3 * Accurate Z80A implementation From b397cb58fb65e9a00d55daa990431c0650554565 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 22 Jun 2018 17:11:25 +0100 Subject: [PATCH 325/339] ZXHawk: small update to defctrl.json --- Assets/defctrl.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Assets/defctrl.json b/Assets/defctrl.json index dace0123e0..64ab6bdc4a 100644 --- a/Assets/defctrl.json +++ b/Assets/defctrl.json @@ -533,7 +533,10 @@ "Insert Previous Tape": "F5", "Next Tape Block": "F8", "Prev Tape Block": "F7", - "Get Tape Status": "F10" + "Get Tape Status": "F9", + "Insert Next Disk": "F11", + "Insert Previous Disk": "F10", + "Get Disk Status": "F12" }, "Intellivision Controller": { "P1 Up": "UpArrow, J1 POV1U, X1 DpadUp, X1 LStickUp", From 8979b0abe15be3304498fb245c024a8b010f5f61 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Fri, 22 Jun 2018 17:42:17 +0100 Subject: [PATCH 326/339] EmuHawk: fix exception on multidiskbundler xml check when file is within an archive --- BizHawk.Client.EmuHawk/MainForm.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 508a6840b0..6c7bcfb5e4 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -3640,7 +3640,7 @@ namespace BizHawk.Client.EmuHawk CoreFileProvider.SyncCoreCommInputSignals(nextComm); InputManager.SyncControls(); - if (Path.GetExtension(loaderName).ToLower() == ".xml") + if (Path.GetExtension(loaderName.Replace("*OpenRom*", "").Replace("|", "")).ToLower() == ".xml") { // this is a multi-disk bundler file // determine the xml assets and create RomStatusDetails for all of them From 8a2ee98beac80de110f50de6d79fa1e1abb26b76 Mon Sep 17 00:00:00 2001 From: feos Date: Fri, 22 Jun 2018 20:26:38 +0300 Subject: [PATCH 327/339] fix #1221 can this also close #1222 I wonder? --- BizHawk.Client.Common/lua/LuaLibraryBase.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/BizHawk.Client.Common/lua/LuaLibraryBase.cs b/BizHawk.Client.Common/lua/LuaLibraryBase.cs index 0a2b1a7463..391ce4330d 100644 --- a/BizHawk.Client.Common/lua/LuaLibraryBase.cs +++ b/BizHawk.Client.Common/lua/LuaLibraryBase.cs @@ -70,13 +70,12 @@ namespace BizHawk.Client.Common return null; } - double tryNum; - var result = double.TryParse(color.ToString(), out tryNum); + int tryNum; + var result = int.TryParse(color.ToString(), out tryNum); if (result) { - var stringResult = ((int)tryNum).ToString(); - return ColorTranslator.FromHtml(stringResult); + return Color.FromArgb(tryNum); } if (!string.IsNullOrWhiteSpace(color.ToString())) From 03b3ea0b2f881112f21951bb39460fbd49b58283 Mon Sep 17 00:00:00 2001 From: feos Date: Fri, 22 Jun 2018 23:15:35 +0300 Subject: [PATCH 328/339] triple cast, but we don't have to cast to string to fetch ARGB --- BizHawk.Client.Common/lua/LuaLibraryBase.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/BizHawk.Client.Common/lua/LuaLibraryBase.cs b/BizHawk.Client.Common/lua/LuaLibraryBase.cs index 391ce4330d..bcf0a6845e 100644 --- a/BizHawk.Client.Common/lua/LuaLibraryBase.cs +++ b/BizHawk.Client.Common/lua/LuaLibraryBase.cs @@ -63,24 +63,21 @@ namespace BizHawk.Client.Common return (uint)(double)luaArg; } - protected static Color? ToColor(object color) + protected static Color? ToColor(object o) { - if (color == null) + if (o == null) { return null; } - int tryNum; - var result = int.TryParse(color.ToString(), out tryNum); - - if (result) + if (o.GetType() == typeof(double)) { - return Color.FromArgb(tryNum); + return Color.FromArgb((int)(long)(double)o); } - if (!string.IsNullOrWhiteSpace(color.ToString())) + if (o.GetType() == typeof(string)) { - return Color.FromName(color.ToString()); + return Color.FromName(o.ToString()); } return null; From 9ddeaed84cbd06d4db9742bbf1da3ae0ce6fb35b Mon Sep 17 00:00:00 2001 From: feos Date: Fri, 22 Jun 2018 23:47:24 +0300 Subject: [PATCH 329/339] update gliden64 --- libmupen64plus/GLideN64 | 2 +- output/dll/mupen64plus-video-GLideN64.dll | Bin 2034176 -> 2051584 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libmupen64plus/GLideN64 b/libmupen64plus/GLideN64 index 3d09b7719e..fec7309dc9 160000 --- a/libmupen64plus/GLideN64 +++ b/libmupen64plus/GLideN64 @@ -1 +1 @@ -Subproject commit 3d09b7719e0181ace91c7f2c7c966cfae410561c +Subproject commit fec7309dc945e7c37f707a1f8e96d6e4bff9c63d diff --git a/output/dll/mupen64plus-video-GLideN64.dll b/output/dll/mupen64plus-video-GLideN64.dll index dc59326a6826b16f4d93da18fbdc2c60286774aa..2d4981fdc4e028bc7cd9e94bf72c9d1a1a1f697f 100644 GIT binary patch delta 671979 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�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%0Ag4 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 z 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�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 z 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 delta 654627 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%0AVab1yt}!H4F%Pc@XxrB96S3NF29 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 z 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 I;+EO}2YBAeVgLXD From fc03be879fdb6103135a0549843172beb0a2bb8e Mon Sep 17 00:00:00 2001 From: feos Date: Sat, 23 Jun 2018 00:00:08 +0300 Subject: [PATCH 330/339] gliden64: disable texture cache option, since internally it's hardcoded to 8000 now --- .../N64/N64VideoPluginconfig.Designer.cs | 7952 +++++++++-------- .../Nintendo/N64/N64SyncSettings.GLideN64.cs | 2 +- 2 files changed, 3978 insertions(+), 3976 deletions(-) diff --git a/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs b/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs index ebc228d4f9..4dee0b94c6 100644 --- a/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs @@ -28,514 +28,514 @@ ///

    private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(N64VideoPluginconfig)); - this.N64plugintabcontrol = new System.Windows.Forms.TabControl(); - this.N64vpluginglobaltab = new System.Windows.Forms.TabPage(); - this.VideoResolutionYTextBox = new System.Windows.Forms.TextBox(); - this.LabelVideoResolutionY = new System.Windows.Forms.Label(); - this.VideoResolutionXTextBox = new System.Windows.Forms.TextBox(); - this.LabelVideoResolutionX = new System.Windows.Forms.Label(); - this.label49 = new System.Windows.Forms.Label(); - this.RspTypeDropdown = new System.Windows.Forms.ComboBox(); - this.label48 = new System.Windows.Forms.Label(); - this.CoreTypeDropdown = new System.Windows.Forms.ComboBox(); - this.label47 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.VideoResolutionComboBox = new System.Windows.Forms.ComboBox(); - this.PluginComboBox = new System.Windows.Forms.ComboBox(); - this.label1 = new System.Windows.Forms.Label(); - this.GLideN64Tab = new System.Windows.Forms.TabPage(); - this.tabControl3 = new System.Windows.Forms.TabControl(); - this.tabPage5 = new System.Windows.Forms.TabPage(); - this.label88 = new System.Windows.Forms.Label(); - this.GLideN64_GammaCorrectionLevel = new System.Windows.Forms.TextBox(); - this.GLideN64_ForceGammaCorrection = new System.Windows.Forms.CheckBox(); - this.label81 = new System.Windows.Forms.Label(); - this.GLideN64_MultiSampling = new System.Windows.Forms.ComboBox(); - this.label80 = new System.Windows.Forms.Label(); - this.GLideN64_blurStrength = new System.Windows.Forms.ComboBox(); - this.label79 = new System.Windows.Forms.Label(); - this.GLideN64_blurAmount = new System.Windows.Forms.ComboBox(); - this.label78 = new System.Windows.Forms.Label(); - this.GLideN64_bloomBlendMode = new System.Windows.Forms.ComboBox(); - this.label77 = new System.Windows.Forms.Label(); - this.GLideN64_bloomThresholdLevel = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableBloom = new System.Windows.Forms.CheckBox(); - this.GLideN64_FragmentDepthWrite = new System.Windows.Forms.CheckBox(); - this.GLideN64_LegacyBlending = new System.Windows.Forms.CheckBox(); - this.GLideN64_NativeResTexrects = new System.Windows.Forms.CheckBox(); - this.label82 = new System.Windows.Forms.Label(); - this.GLideN64_CorrectTexrectCoords = new System.Windows.Forms.ComboBox(); - this.GLideN64_ShadersStorage = new System.Windows.Forms.CheckBox(); - this.GLideN64_HWLighting = new System.Windows.Forms.CheckBox(); - this.GLideN64_EnableNoise = new System.Windows.Forms.CheckBox(); - this.GLideN64_EnableLOD = new System.Windows.Forms.CheckBox(); - this.tabPage6 = new System.Windows.Forms.TabPage(); - this.label87 = new System.Windows.Forms.Label(); - this.GLideN64_txPath = new System.Windows.Forms.TextBox(); - this.GLideN64_txSaveCache = new System.Windows.Forms.CheckBox(); - this.GLideN64_txCacheCompression = new System.Windows.Forms.CheckBox(); - this.GLideN64_txForce16bpp = new System.Windows.Forms.CheckBox(); - this.GLideN64_txDump = new System.Windows.Forms.CheckBox(); - this.GLideN64_txHresAltCRC = new System.Windows.Forms.CheckBox(); - this.GLideN64_txHiresFullAlphaChannel = new System.Windows.Forms.CheckBox(); - this.GLideN64_txHiresEnable = new System.Windows.Forms.CheckBox(); - this.label86 = new System.Windows.Forms.Label(); - this.GLideN64_txCacheSize = new System.Windows.Forms.TextBox(); - this.GLideN64_txFilterIgnoreBG = new System.Windows.Forms.CheckBox(); - this.GLideN64_txDeposterize = new System.Windows.Forms.CheckBox(); - this.label85 = new System.Windows.Forms.Label(); - this.GLideN64_txEnhancementMode = new System.Windows.Forms.ComboBox(); - this.label84 = new System.Windows.Forms.Label(); - this.GLideN64_txFilterMode = new System.Windows.Forms.ComboBox(); - this.label83 = new System.Windows.Forms.Label(); - this.GLideN64_CacheSize = new System.Windows.Forms.TextBox(); - this.label72 = new System.Windows.Forms.Label(); - this.GLideN64_bilinearMode = new System.Windows.Forms.ComboBox(); - this.GLideN64_MaxAnisotropy = new System.Windows.Forms.CheckBox(); - this.tabPage7 = new System.Windows.Forms.TabPage(); - this.label89 = new System.Windows.Forms.Label(); - this.GLideN64_UseNativeResolutionFactor = new System.Windows.Forms.TextBox(); - this.GLideN64_DisableFBInfo = new System.Windows.Forms.CheckBox(); - this.GLideN64_FBInfoReadDepthChunk = new System.Windows.Forms.CheckBox(); - this.GLideN64_FBInfoReadColorChunk = new System.Windows.Forms.CheckBox(); - this.label76 = new System.Windows.Forms.Label(); - this.GLideN64_BufferSwapMode = new System.Windows.Forms.ComboBox(); - this.label75 = new System.Windows.Forms.Label(); - this.GLideN64_AspectRatio = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableN64DepthCompare = new System.Windows.Forms.CheckBox(); - this.label74 = new System.Windows.Forms.Label(); - this.GLideN64_EnableCopyColorToRDRAM = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableCopyAuxiliaryToRDRAM = new System.Windows.Forms.CheckBox(); - this.GLideN64_EnableCopyColorFromRDRAM = new System.Windows.Forms.CheckBox(); - this.label73 = new System.Windows.Forms.Label(); - this.GLideN64_EnableCopyDepthToRDRAM = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableFBEmulation = new System.Windows.Forms.CheckBox(); - this.GLideN64_UseDefaultHacks = new System.Windows.Forms.CheckBox(); - this.Glide64mk2TabPage = new System.Windows.Forms.TabPage(); - this.tabControl2 = new System.Windows.Forms.TabControl(); - this.tabPage1 = new System.Windows.Forms.TabPage(); - this.Glide64mk2_fb_get_info = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_render = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_wrpAnisotropic = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_wrpFBO = new System.Windows.Forms.CheckBox(); - this.label50 = new System.Windows.Forms.Label(); - this.Glide64mk2_card_id = new System.Windows.Forms.ComboBox(); - this.tabPage2 = new System.Windows.Forms.TabPage(); - this.Glide64mk2_fb_read_always = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_useless_is_useless = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_n64_z_scale = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_old_style_adither = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_zmode_compare_less = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_adjust_aspect = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fast_crc = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_clip_zmax = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_clip_zmin = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_force_quad3d = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_pal230 = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_texture_correction = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_correct_viewport = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_force_calc_sphere = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_use_sts1_only = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_optimize_texrect = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_increase_texrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_ignore_aux_copy = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_hires_buf_clear = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_force_microcheck = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fog = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_smart = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_read_alpha = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_hires = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_detect_cpu_write = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_buff_clear = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_alt_tex_size = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_UseDefaultHacks1 = new System.Windows.Forms.CheckBox(); - this.tabPage3 = new System.Windows.Forms.TabPage(); - this.label46 = new System.Windows.Forms.Label(); - this.Glide64mk2_read_back_to_screen = new System.Windows.Forms.ComboBox(); - this.Glide64mk2_aspectmode = new System.Windows.Forms.ComboBox(); - this.Glide64mk2_fb_crc_mode = new System.Windows.Forms.ComboBox(); - this.label45 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.label52 = new System.Windows.Forms.Label(); - this.Glide64mk2_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); - this.label53 = new System.Windows.Forms.Label(); - this.Glide64mk2_swapmode = new System.Windows.Forms.ComboBox(); - this.label54 = new System.Windows.Forms.Label(); - this.Glide64mk2_stipple_pattern = new System.Windows.Forms.TextBox(); - this.label55 = new System.Windows.Forms.Label(); - this.Glide64mk2_stipple_mode = new System.Windows.Forms.TextBox(); - this.label56 = new System.Windows.Forms.Label(); - this.Glide64mk2_lodmode = new System.Windows.Forms.ComboBox(); - this.label58 = new System.Windows.Forms.Label(); - this.Glide64mk2_filtering = new System.Windows.Forms.ComboBox(); - this.Glide64mk2_UseDefaultHacks2 = new System.Windows.Forms.CheckBox(); - this.Glide64TabPage = new System.Windows.Forms.TabPage(); - this.tabControl1 = new System.Windows.Forms.TabControl(); - this.Glide64General = new System.Windows.Forms.TabPage(); - this.label39 = new System.Windows.Forms.Label(); - this.Glide_scale_y = new System.Windows.Forms.TextBox(); - this.label40 = new System.Windows.Forms.Label(); - this.Glide_scale_x = new System.Windows.Forms.TextBox(); - this.label38 = new System.Windows.Forms.Label(); - this.Glide_offset_y = new System.Windows.Forms.TextBox(); - this.label37 = new System.Windows.Forms.Label(); - this.Glide_offset_x = new System.Windows.Forms.TextBox(); - this.Glide_fb_get_info = new System.Windows.Forms.CheckBox(); - this.Glide_disable_auxbuf = new System.Windows.Forms.CheckBox(); - this.Glide_fbo = new System.Windows.Forms.CheckBox(); - this.Glide_noglsl = new System.Windows.Forms.CheckBox(); - this.Glide_noditheredalpha = new System.Windows.Forms.CheckBox(); - this.label32 = new System.Windows.Forms.Label(); - this.Glide_tex_filter = new System.Windows.Forms.ComboBox(); - this.Glide_fb_render = new System.Windows.Forms.CheckBox(); - this.Glide_motionblur = new System.Windows.Forms.CheckBox(); - this.Glide_fb_read_always = new System.Windows.Forms.CheckBox(); - this.Glide_unk_as_red = new System.Windows.Forms.CheckBox(); - this.Glide_filter_cache = new System.Windows.Forms.CheckBox(); - this.Glide_fast_crc = new System.Windows.Forms.CheckBox(); - this.label31 = new System.Windows.Forms.Label(); - this.Glide_wfmode = new System.Windows.Forms.ComboBox(); - this.Glide_wireframe = new System.Windows.Forms.CheckBox(); - this.label30 = new System.Windows.Forms.Label(); - this.Glide_card_id = new System.Windows.Forms.ComboBox(); - this.Glide_flame_corona = new System.Windows.Forms.CheckBox(); - this.label29 = new System.Windows.Forms.Label(); - this.Glide_ucode = new System.Windows.Forms.ComboBox(); - this.Glide_autodetect_ucode = new System.Windows.Forms.CheckBox(); - this.GlidePerGameHacks1 = new System.Windows.Forms.TabPage(); - this.Glide_wrap_big_tex = new System.Windows.Forms.CheckBox(); - this.Glide_use_sts1_only = new System.Windows.Forms.CheckBox(); - this.Glide_soft_depth_compare = new System.Windows.Forms.CheckBox(); - this.Glide_PPL = new System.Windows.Forms.CheckBox(); - this.Glide_fb_optimize_write = new System.Windows.Forms.CheckBox(); - this.Glide_fb_optimize_texrect = new System.Windows.Forms.CheckBox(); - this.Glide_increase_texrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide_increase_primdepth = new System.Windows.Forms.CheckBox(); - this.Glide_fb_ignore_previous = new System.Windows.Forms.CheckBox(); - this.Glide_fb_ignore_aux_copy = new System.Windows.Forms.CheckBox(); - this.Glide_fb_hires_buf_clear = new System.Windows.Forms.CheckBox(); - this.Glide_force_microcheck = new System.Windows.Forms.CheckBox(); - this.Glide_force_depth_compare = new System.Windows.Forms.CheckBox(); - this.Glide_fog = new System.Windows.Forms.CheckBox(); - this.Glide_fillcolor_fix = new System.Windows.Forms.CheckBox(); - this.Glide_fb_smart = new System.Windows.Forms.CheckBox(); - this.Glide_fb_read_alpha = new System.Windows.Forms.CheckBox(); - this.Glide_fb_hires = new System.Windows.Forms.CheckBox(); - this.Glide_fb_clear = new System.Windows.Forms.CheckBox(); - this.Glide_detect_cpu_write = new System.Windows.Forms.CheckBox(); - this.Glide_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide_buff_clear = new System.Windows.Forms.CheckBox(); - this.Glide_alt_tex_size = new System.Windows.Forms.CheckBox(); - this.GlideUseDefaultHacks1 = new System.Windows.Forms.CheckBox(); - this.GlidePerGameHacks2 = new System.Windows.Forms.TabPage(); - this.label44 = new System.Windows.Forms.Label(); - this.Glide_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); - this.label43 = new System.Windows.Forms.Label(); - this.Glide_swapmode = new System.Windows.Forms.ComboBox(); - this.label42 = new System.Windows.Forms.Label(); - this.Glide_stipple_pattern = new System.Windows.Forms.TextBox(); - this.label41 = new System.Windows.Forms.Label(); - this.Glide_stipple_mode = new System.Windows.Forms.TextBox(); - this.label36 = new System.Windows.Forms.Label(); - this.Glide_lodmode = new System.Windows.Forms.ComboBox(); - this.label35 = new System.Windows.Forms.Label(); - this.Glide_fix_tex_coord = new System.Windows.Forms.TextBox(); - this.label34 = new System.Windows.Forms.Label(); - this.Glide_filtering = new System.Windows.Forms.ComboBox(); - this.label33 = new System.Windows.Forms.Label(); - this.Glide_depth_bias = new System.Windows.Forms.TextBox(); - this.GlideUseDefaultHacks2 = new System.Windows.Forms.CheckBox(); - this.RiceTabPage = new System.Windows.Forms.TabPage(); - this.RiceTabControl = new System.Windows.Forms.TabControl(); - this.RiceGeneral = new System.Windows.Forms.TabPage(); - this.label15 = new System.Windows.Forms.Label(); - this.RiceScreenUpdateSetting_Combo = new System.Windows.Forms.ComboBox(); - this.label14 = new System.Windows.Forms.Label(); - this.RiceMultiSampling_Combo = new System.Windows.Forms.ComboBox(); - this.label13 = new System.Windows.Forms.Label(); - this.RiceOpenGLRenderSetting_Combo = new System.Windows.Forms.ComboBox(); - this.label11 = new System.Windows.Forms.Label(); - this.RiceColorQuality_Combo = new System.Windows.Forms.ComboBox(); - this.label10 = new System.Windows.Forms.Label(); - this.RiceOpenGLDepthBufferSetting_Combo = new System.Windows.Forms.ComboBox(); - this.label9 = new System.Windows.Forms.Label(); - this.RiceTextureQuality_Combo = new System.Windows.Forms.ComboBox(); - this.RiceEnableVertexShader_CB = new System.Windows.Forms.CheckBox(); - this.RiceSkipFrame_CB = new System.Windows.Forms.CheckBox(); - this.RiceEnableHacks_CB = new System.Windows.Forms.CheckBox(); - this.RiceFullTMEMEmulation_CB = new System.Windows.Forms.CheckBox(); - this.RiceOpenGLVertexClipper_CB = new System.Windows.Forms.CheckBox(); - this.AnisotropicFiltering_LB = new System.Windows.Forms.Label(); - this.RiceAnisotropicFiltering_TB = new System.Windows.Forms.TrackBar(); - this.label6 = new System.Windows.Forms.Label(); - this.RiceFogMethod_Combo = new System.Windows.Forms.ComboBox(); - this.label5 = new System.Windows.Forms.Label(); - this.RiceMipmapping_Combo = new System.Windows.Forms.ComboBox(); - this.RiceWinFrameMode_CB = new System.Windows.Forms.CheckBox(); - this.RiceInN64Resolution_CB = new System.Windows.Forms.CheckBox(); - this.RiceFastTextureLoading_CB = new System.Windows.Forms.CheckBox(); - this.RiceAccurateTextureMapping_CB = new System.Windows.Forms.CheckBox(); - this.RiceSaveVRAM_CB = new System.Windows.Forms.CheckBox(); - this.RiceEnableSSE_CB = new System.Windows.Forms.CheckBox(); - this.RiceGameDefaultTab = new System.Windows.Forms.TabPage(); - this.RiceNormalAlphaBlender_CB = new System.Windows.Forms.CheckBox(); - this.RiceDefaultCombinerDisable_CB = new System.Windows.Forms.CheckBox(); - this.RiceFrameBuffer_GroupBox = new System.Windows.Forms.GroupBox(); - this.label16 = new System.Windows.Forms.Label(); - this.label17 = new System.Windows.Forms.Label(); - this.label18 = new System.Windows.Forms.Label(); - this.RiceFrameBufferWriteBackControl_Combo = new System.Windows.Forms.ComboBox(); - this.RiceFrameBufferSetting_Combo = new System.Windows.Forms.ComboBox(); - this.RiceDoubleSizeForSmallTxtrBuf_CB = new System.Windows.Forms.CheckBox(); - this.RiceRenderToTexture_Combo = new System.Windows.Forms.ComboBox(); - this.RiceTextureEnhancementTab = new System.Windows.Forms.TabPage(); - this.label12 = new System.Windows.Forms.Label(); - this.RiceForceTextureFilter_Combo = new System.Windows.Forms.ComboBox(); - this.label8 = new System.Windows.Forms.Label(); - this.RiceTextureEnhancementControl_Combo = new System.Windows.Forms.ComboBox(); - this.label7 = new System.Windows.Forms.Label(); - this.RiceTextureEnhancement_Combo = new System.Windows.Forms.ComboBox(); - this.RiceTexRectOnly_CB = new System.Windows.Forms.CheckBox(); - this.RiceSmallTextureOnly_CB = new System.Windows.Forms.CheckBox(); - this.RiceLoadHiResCRCOnly_CB = new System.Windows.Forms.CheckBox(); - this.RiceLoadHiResTextures_CB = new System.Windows.Forms.CheckBox(); - this.RiceDumpTexturesToFiles_CB = new System.Windows.Forms.CheckBox(); - this.RiceGameSpecificTab = new System.Windows.Forms.TabPage(); - this.label27 = new System.Windows.Forms.Label(); - this.RiceEnableHacksForGame_Combo = new System.Windows.Forms.ComboBox(); - this.RiceForceDepthBuffer_CB = new System.Windows.Forms.CheckBox(); - this.label28 = new System.Windows.Forms.Label(); - this.RiceUseCIWidthAndRatio_Combo = new System.Windows.Forms.ComboBox(); - this.label26 = new System.Windows.Forms.Label(); - this.RiceRenderToTextureOption_Combo = new System.Windows.Forms.ComboBox(); - this.label25 = new System.Windows.Forms.Label(); - this.RiceFrameBufferOption_Combo = new System.Windows.Forms.ComboBox(); - this.label24 = new System.Windows.Forms.Label(); - this.RiceNormalBlender_Combo = new System.Windows.Forms.ComboBox(); - this.label23 = new System.Windows.Forms.Label(); - this.RiceAccurateTextureMappingHack_Combo = new System.Windows.Forms.ComboBox(); - this.label22 = new System.Windows.Forms.Label(); - this.RiceFastTextureCRC_Combo = new System.Windows.Forms.ComboBox(); - this.label21 = new System.Windows.Forms.Label(); - this.RiceFullTMEM_Combo = new System.Windows.Forms.ComboBox(); - this.label20 = new System.Windows.Forms.Label(); - this.RiceScreenUpdateSettingHack_Combo = new System.Windows.Forms.ComboBox(); - this.label19 = new System.Windows.Forms.Label(); - this.RiceVIHeight_Text = new System.Windows.Forms.TextBox(); - this.label4 = new System.Windows.Forms.Label(); - this.RiceVIWidth_Text = new System.Windows.Forms.TextBox(); - this.RiceTextureScaleHack_CB = new System.Windows.Forms.CheckBox(); - this.RiceFastLoadTile_CB = new System.Windows.Forms.CheckBox(); - this.RiceUseSmallerTexture_CB = new System.Windows.Forms.CheckBox(); - this.RiceEnableTxtLOD_CB = new System.Windows.Forms.CheckBox(); - this.RiceZHack_CB = new System.Windows.Forms.CheckBox(); - this.RicePrimaryDepthHack_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableObjBG_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableBlender_CB = new System.Windows.Forms.CheckBox(); - this.RiceForceScreenClear_CB = new System.Windows.Forms.CheckBox(); - this.RiceEmulateClear_CB = new System.Windows.Forms.CheckBox(); - this.RiceTxtSizeMethod2_CB = new System.Windows.Forms.CheckBox(); - this.RiceIncTexRectEdge_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableCulling_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableTextureCRC_CB = new System.Windows.Forms.CheckBox(); - this.RiceTexture1Hack_CB = new System.Windows.Forms.CheckBox(); - this.RiceUseDefaultHacks_CB = new System.Windows.Forms.CheckBox(); - this.label71 = new System.Windows.Forms.Label(); - this.label70 = new System.Windows.Forms.Label(); - this.label69 = new System.Windows.Forms.Label(); - this.label68 = new System.Windows.Forms.Label(); - this.label67 = new System.Windows.Forms.Label(); - this.label66 = new System.Windows.Forms.Label(); - this.label65 = new System.Windows.Forms.Label(); - this.label64 = new System.Windows.Forms.Label(); - this.label63 = new System.Windows.Forms.Label(); - this.label62 = new System.Windows.Forms.Label(); - this.label61 = new System.Windows.Forms.Label(); - this.label60 = new System.Windows.Forms.Label(); - this.label59 = new System.Windows.Forms.Label(); - this.label57 = new System.Windows.Forms.Label(); - this.label51 = new System.Windows.Forms.Label(); - this.SaveButton = new System.Windows.Forms.Button(); - this.CancelBT = new System.Windows.Forms.Button(); - this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); - this.N64plugintabcontrol.SuspendLayout(); - this.N64vpluginglobaltab.SuspendLayout(); - this.GLideN64Tab.SuspendLayout(); - this.tabControl3.SuspendLayout(); - this.tabPage5.SuspendLayout(); - this.tabPage6.SuspendLayout(); - this.tabPage7.SuspendLayout(); - this.Glide64mk2TabPage.SuspendLayout(); - this.tabControl2.SuspendLayout(); - this.tabPage1.SuspendLayout(); - this.tabPage2.SuspendLayout(); - this.tabPage3.SuspendLayout(); - this.Glide64TabPage.SuspendLayout(); - this.tabControl1.SuspendLayout(); - this.Glide64General.SuspendLayout(); - this.GlidePerGameHacks1.SuspendLayout(); - this.GlidePerGameHacks2.SuspendLayout(); - this.RiceTabPage.SuspendLayout(); - this.RiceTabControl.SuspendLayout(); - this.RiceGeneral.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).BeginInit(); - this.RiceGameDefaultTab.SuspendLayout(); - this.RiceFrameBuffer_GroupBox.SuspendLayout(); - this.RiceTextureEnhancementTab.SuspendLayout(); - this.RiceGameSpecificTab.SuspendLayout(); - this.SuspendLayout(); - // - // N64plugintabcontrol - // - this.N64plugintabcontrol.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(N64VideoPluginconfig)); + this.N64plugintabcontrol = new System.Windows.Forms.TabControl(); + this.N64vpluginglobaltab = new System.Windows.Forms.TabPage(); + this.VideoResolutionYTextBox = new System.Windows.Forms.TextBox(); + this.LabelVideoResolutionY = new System.Windows.Forms.Label(); + this.VideoResolutionXTextBox = new System.Windows.Forms.TextBox(); + this.LabelVideoResolutionX = new System.Windows.Forms.Label(); + this.label49 = new System.Windows.Forms.Label(); + this.RspTypeDropdown = new System.Windows.Forms.ComboBox(); + this.label48 = new System.Windows.Forms.Label(); + this.CoreTypeDropdown = new System.Windows.Forms.ComboBox(); + this.label47 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.VideoResolutionComboBox = new System.Windows.Forms.ComboBox(); + this.PluginComboBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.GLideN64Tab = new System.Windows.Forms.TabPage(); + this.tabControl3 = new System.Windows.Forms.TabControl(); + this.tabPage5 = new System.Windows.Forms.TabPage(); + this.label88 = new System.Windows.Forms.Label(); + this.GLideN64_GammaCorrectionLevel = new System.Windows.Forms.TextBox(); + this.GLideN64_ForceGammaCorrection = new System.Windows.Forms.CheckBox(); + this.label81 = new System.Windows.Forms.Label(); + this.GLideN64_MultiSampling = new System.Windows.Forms.ComboBox(); + this.label80 = new System.Windows.Forms.Label(); + this.GLideN64_blurStrength = new System.Windows.Forms.ComboBox(); + this.label79 = new System.Windows.Forms.Label(); + this.GLideN64_blurAmount = new System.Windows.Forms.ComboBox(); + this.label78 = new System.Windows.Forms.Label(); + this.GLideN64_bloomBlendMode = new System.Windows.Forms.ComboBox(); + this.label77 = new System.Windows.Forms.Label(); + this.GLideN64_bloomThresholdLevel = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableBloom = new System.Windows.Forms.CheckBox(); + this.GLideN64_FragmentDepthWrite = new System.Windows.Forms.CheckBox(); + this.GLideN64_LegacyBlending = new System.Windows.Forms.CheckBox(); + this.GLideN64_NativeResTexrects = new System.Windows.Forms.CheckBox(); + this.label82 = new System.Windows.Forms.Label(); + this.GLideN64_CorrectTexrectCoords = new System.Windows.Forms.ComboBox(); + this.GLideN64_ShadersStorage = new System.Windows.Forms.CheckBox(); + this.GLideN64_HWLighting = new System.Windows.Forms.CheckBox(); + this.GLideN64_EnableNoise = new System.Windows.Forms.CheckBox(); + this.GLideN64_EnableLOD = new System.Windows.Forms.CheckBox(); + this.tabPage6 = new System.Windows.Forms.TabPage(); + this.label87 = new System.Windows.Forms.Label(); + this.GLideN64_txPath = new System.Windows.Forms.TextBox(); + this.GLideN64_txSaveCache = new System.Windows.Forms.CheckBox(); + this.GLideN64_txCacheCompression = new System.Windows.Forms.CheckBox(); + this.GLideN64_txForce16bpp = new System.Windows.Forms.CheckBox(); + this.GLideN64_txDump = new System.Windows.Forms.CheckBox(); + this.GLideN64_txHresAltCRC = new System.Windows.Forms.CheckBox(); + this.GLideN64_txHiresFullAlphaChannel = new System.Windows.Forms.CheckBox(); + this.GLideN64_txHiresEnable = new System.Windows.Forms.CheckBox(); + this.label86 = new System.Windows.Forms.Label(); + this.GLideN64_txCacheSize = new System.Windows.Forms.TextBox(); + this.GLideN64_txFilterIgnoreBG = new System.Windows.Forms.CheckBox(); + this.GLideN64_txDeposterize = new System.Windows.Forms.CheckBox(); + this.label85 = new System.Windows.Forms.Label(); + this.GLideN64_txEnhancementMode = new System.Windows.Forms.ComboBox(); + this.label84 = new System.Windows.Forms.Label(); + this.GLideN64_txFilterMode = new System.Windows.Forms.ComboBox(); + this.label83 = new System.Windows.Forms.Label(); + this.GLideN64_CacheSize = new System.Windows.Forms.TextBox(); + this.label72 = new System.Windows.Forms.Label(); + this.GLideN64_bilinearMode = new System.Windows.Forms.ComboBox(); + this.GLideN64_MaxAnisotropy = new System.Windows.Forms.CheckBox(); + this.tabPage7 = new System.Windows.Forms.TabPage(); + this.label89 = new System.Windows.Forms.Label(); + this.GLideN64_UseNativeResolutionFactor = new System.Windows.Forms.TextBox(); + this.GLideN64_DisableFBInfo = new System.Windows.Forms.CheckBox(); + this.GLideN64_FBInfoReadDepthChunk = new System.Windows.Forms.CheckBox(); + this.GLideN64_FBInfoReadColorChunk = new System.Windows.Forms.CheckBox(); + this.label76 = new System.Windows.Forms.Label(); + this.GLideN64_BufferSwapMode = new System.Windows.Forms.ComboBox(); + this.label75 = new System.Windows.Forms.Label(); + this.GLideN64_AspectRatio = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableN64DepthCompare = new System.Windows.Forms.CheckBox(); + this.label74 = new System.Windows.Forms.Label(); + this.GLideN64_EnableCopyColorToRDRAM = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableCopyAuxiliaryToRDRAM = new System.Windows.Forms.CheckBox(); + this.GLideN64_EnableCopyColorFromRDRAM = new System.Windows.Forms.CheckBox(); + this.label73 = new System.Windows.Forms.Label(); + this.GLideN64_EnableCopyDepthToRDRAM = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableFBEmulation = new System.Windows.Forms.CheckBox(); + this.GLideN64_UseDefaultHacks = new System.Windows.Forms.CheckBox(); + this.Glide64mk2TabPage = new System.Windows.Forms.TabPage(); + this.tabControl2 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.Glide64mk2_fb_get_info = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_render = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_wrpAnisotropic = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_wrpFBO = new System.Windows.Forms.CheckBox(); + this.label50 = new System.Windows.Forms.Label(); + this.Glide64mk2_card_id = new System.Windows.Forms.ComboBox(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.Glide64mk2_fb_read_always = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_useless_is_useless = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_n64_z_scale = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_old_style_adither = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_zmode_compare_less = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_adjust_aspect = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fast_crc = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_clip_zmax = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_clip_zmin = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_force_quad3d = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_pal230 = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_texture_correction = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_correct_viewport = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_force_calc_sphere = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_use_sts1_only = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_optimize_texrect = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_increase_texrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_ignore_aux_copy = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_hires_buf_clear = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_force_microcheck = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fog = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_smart = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_read_alpha = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_hires = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_detect_cpu_write = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_buff_clear = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_alt_tex_size = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_UseDefaultHacks1 = new System.Windows.Forms.CheckBox(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.label46 = new System.Windows.Forms.Label(); + this.Glide64mk2_read_back_to_screen = new System.Windows.Forms.ComboBox(); + this.Glide64mk2_aspectmode = new System.Windows.Forms.ComboBox(); + this.Glide64mk2_fb_crc_mode = new System.Windows.Forms.ComboBox(); + this.label45 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label52 = new System.Windows.Forms.Label(); + this.Glide64mk2_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); + this.label53 = new System.Windows.Forms.Label(); + this.Glide64mk2_swapmode = new System.Windows.Forms.ComboBox(); + this.label54 = new System.Windows.Forms.Label(); + this.Glide64mk2_stipple_pattern = new System.Windows.Forms.TextBox(); + this.label55 = new System.Windows.Forms.Label(); + this.Glide64mk2_stipple_mode = new System.Windows.Forms.TextBox(); + this.label56 = new System.Windows.Forms.Label(); + this.Glide64mk2_lodmode = new System.Windows.Forms.ComboBox(); + this.label58 = new System.Windows.Forms.Label(); + this.Glide64mk2_filtering = new System.Windows.Forms.ComboBox(); + this.Glide64mk2_UseDefaultHacks2 = new System.Windows.Forms.CheckBox(); + this.Glide64TabPage = new System.Windows.Forms.TabPage(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.Glide64General = new System.Windows.Forms.TabPage(); + this.label39 = new System.Windows.Forms.Label(); + this.Glide_scale_y = new System.Windows.Forms.TextBox(); + this.label40 = new System.Windows.Forms.Label(); + this.Glide_scale_x = new System.Windows.Forms.TextBox(); + this.label38 = new System.Windows.Forms.Label(); + this.Glide_offset_y = new System.Windows.Forms.TextBox(); + this.label37 = new System.Windows.Forms.Label(); + this.Glide_offset_x = new System.Windows.Forms.TextBox(); + this.Glide_fb_get_info = new System.Windows.Forms.CheckBox(); + this.Glide_disable_auxbuf = new System.Windows.Forms.CheckBox(); + this.Glide_fbo = new System.Windows.Forms.CheckBox(); + this.Glide_noglsl = new System.Windows.Forms.CheckBox(); + this.Glide_noditheredalpha = new System.Windows.Forms.CheckBox(); + this.label32 = new System.Windows.Forms.Label(); + this.Glide_tex_filter = new System.Windows.Forms.ComboBox(); + this.Glide_fb_render = new System.Windows.Forms.CheckBox(); + this.Glide_motionblur = new System.Windows.Forms.CheckBox(); + this.Glide_fb_read_always = new System.Windows.Forms.CheckBox(); + this.Glide_unk_as_red = new System.Windows.Forms.CheckBox(); + this.Glide_filter_cache = new System.Windows.Forms.CheckBox(); + this.Glide_fast_crc = new System.Windows.Forms.CheckBox(); + this.label31 = new System.Windows.Forms.Label(); + this.Glide_wfmode = new System.Windows.Forms.ComboBox(); + this.Glide_wireframe = new System.Windows.Forms.CheckBox(); + this.label30 = new System.Windows.Forms.Label(); + this.Glide_card_id = new System.Windows.Forms.ComboBox(); + this.Glide_flame_corona = new System.Windows.Forms.CheckBox(); + this.label29 = new System.Windows.Forms.Label(); + this.Glide_ucode = new System.Windows.Forms.ComboBox(); + this.Glide_autodetect_ucode = new System.Windows.Forms.CheckBox(); + this.GlidePerGameHacks1 = new System.Windows.Forms.TabPage(); + this.Glide_wrap_big_tex = new System.Windows.Forms.CheckBox(); + this.Glide_use_sts1_only = new System.Windows.Forms.CheckBox(); + this.Glide_soft_depth_compare = new System.Windows.Forms.CheckBox(); + this.Glide_PPL = new System.Windows.Forms.CheckBox(); + this.Glide_fb_optimize_write = new System.Windows.Forms.CheckBox(); + this.Glide_fb_optimize_texrect = new System.Windows.Forms.CheckBox(); + this.Glide_increase_texrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide_increase_primdepth = new System.Windows.Forms.CheckBox(); + this.Glide_fb_ignore_previous = new System.Windows.Forms.CheckBox(); + this.Glide_fb_ignore_aux_copy = new System.Windows.Forms.CheckBox(); + this.Glide_fb_hires_buf_clear = new System.Windows.Forms.CheckBox(); + this.Glide_force_microcheck = new System.Windows.Forms.CheckBox(); + this.Glide_force_depth_compare = new System.Windows.Forms.CheckBox(); + this.Glide_fog = new System.Windows.Forms.CheckBox(); + this.Glide_fillcolor_fix = new System.Windows.Forms.CheckBox(); + this.Glide_fb_smart = new System.Windows.Forms.CheckBox(); + this.Glide_fb_read_alpha = new System.Windows.Forms.CheckBox(); + this.Glide_fb_hires = new System.Windows.Forms.CheckBox(); + this.Glide_fb_clear = new System.Windows.Forms.CheckBox(); + this.Glide_detect_cpu_write = new System.Windows.Forms.CheckBox(); + this.Glide_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide_buff_clear = new System.Windows.Forms.CheckBox(); + this.Glide_alt_tex_size = new System.Windows.Forms.CheckBox(); + this.GlideUseDefaultHacks1 = new System.Windows.Forms.CheckBox(); + this.GlidePerGameHacks2 = new System.Windows.Forms.TabPage(); + this.label44 = new System.Windows.Forms.Label(); + this.Glide_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); + this.label43 = new System.Windows.Forms.Label(); + this.Glide_swapmode = new System.Windows.Forms.ComboBox(); + this.label42 = new System.Windows.Forms.Label(); + this.Glide_stipple_pattern = new System.Windows.Forms.TextBox(); + this.label41 = new System.Windows.Forms.Label(); + this.Glide_stipple_mode = new System.Windows.Forms.TextBox(); + this.label36 = new System.Windows.Forms.Label(); + this.Glide_lodmode = new System.Windows.Forms.ComboBox(); + this.label35 = new System.Windows.Forms.Label(); + this.Glide_fix_tex_coord = new System.Windows.Forms.TextBox(); + this.label34 = new System.Windows.Forms.Label(); + this.Glide_filtering = new System.Windows.Forms.ComboBox(); + this.label33 = new System.Windows.Forms.Label(); + this.Glide_depth_bias = new System.Windows.Forms.TextBox(); + this.GlideUseDefaultHacks2 = new System.Windows.Forms.CheckBox(); + this.RiceTabPage = new System.Windows.Forms.TabPage(); + this.RiceTabControl = new System.Windows.Forms.TabControl(); + this.RiceGeneral = new System.Windows.Forms.TabPage(); + this.label15 = new System.Windows.Forms.Label(); + this.RiceScreenUpdateSetting_Combo = new System.Windows.Forms.ComboBox(); + this.label14 = new System.Windows.Forms.Label(); + this.RiceMultiSampling_Combo = new System.Windows.Forms.ComboBox(); + this.label13 = new System.Windows.Forms.Label(); + this.RiceOpenGLRenderSetting_Combo = new System.Windows.Forms.ComboBox(); + this.label11 = new System.Windows.Forms.Label(); + this.RiceColorQuality_Combo = new System.Windows.Forms.ComboBox(); + this.label10 = new System.Windows.Forms.Label(); + this.RiceOpenGLDepthBufferSetting_Combo = new System.Windows.Forms.ComboBox(); + this.label9 = new System.Windows.Forms.Label(); + this.RiceTextureQuality_Combo = new System.Windows.Forms.ComboBox(); + this.RiceEnableVertexShader_CB = new System.Windows.Forms.CheckBox(); + this.RiceSkipFrame_CB = new System.Windows.Forms.CheckBox(); + this.RiceEnableHacks_CB = new System.Windows.Forms.CheckBox(); + this.RiceFullTMEMEmulation_CB = new System.Windows.Forms.CheckBox(); + this.RiceOpenGLVertexClipper_CB = new System.Windows.Forms.CheckBox(); + this.AnisotropicFiltering_LB = new System.Windows.Forms.Label(); + this.RiceAnisotropicFiltering_TB = new System.Windows.Forms.TrackBar(); + this.label6 = new System.Windows.Forms.Label(); + this.RiceFogMethod_Combo = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); + this.RiceMipmapping_Combo = new System.Windows.Forms.ComboBox(); + this.RiceWinFrameMode_CB = new System.Windows.Forms.CheckBox(); + this.RiceInN64Resolution_CB = new System.Windows.Forms.CheckBox(); + this.RiceFastTextureLoading_CB = new System.Windows.Forms.CheckBox(); + this.RiceAccurateTextureMapping_CB = new System.Windows.Forms.CheckBox(); + this.RiceSaveVRAM_CB = new System.Windows.Forms.CheckBox(); + this.RiceEnableSSE_CB = new System.Windows.Forms.CheckBox(); + this.RiceGameDefaultTab = new System.Windows.Forms.TabPage(); + this.RiceNormalAlphaBlender_CB = new System.Windows.Forms.CheckBox(); + this.RiceDefaultCombinerDisable_CB = new System.Windows.Forms.CheckBox(); + this.RiceFrameBuffer_GroupBox = new System.Windows.Forms.GroupBox(); + this.label16 = new System.Windows.Forms.Label(); + this.label17 = new System.Windows.Forms.Label(); + this.label18 = new System.Windows.Forms.Label(); + this.RiceFrameBufferWriteBackControl_Combo = new System.Windows.Forms.ComboBox(); + this.RiceFrameBufferSetting_Combo = new System.Windows.Forms.ComboBox(); + this.RiceDoubleSizeForSmallTxtrBuf_CB = new System.Windows.Forms.CheckBox(); + this.RiceRenderToTexture_Combo = new System.Windows.Forms.ComboBox(); + this.RiceTextureEnhancementTab = new System.Windows.Forms.TabPage(); + this.label12 = new System.Windows.Forms.Label(); + this.RiceForceTextureFilter_Combo = new System.Windows.Forms.ComboBox(); + this.label8 = new System.Windows.Forms.Label(); + this.RiceTextureEnhancementControl_Combo = new System.Windows.Forms.ComboBox(); + this.label7 = new System.Windows.Forms.Label(); + this.RiceTextureEnhancement_Combo = new System.Windows.Forms.ComboBox(); + this.RiceTexRectOnly_CB = new System.Windows.Forms.CheckBox(); + this.RiceSmallTextureOnly_CB = new System.Windows.Forms.CheckBox(); + this.RiceLoadHiResCRCOnly_CB = new System.Windows.Forms.CheckBox(); + this.RiceLoadHiResTextures_CB = new System.Windows.Forms.CheckBox(); + this.RiceDumpTexturesToFiles_CB = new System.Windows.Forms.CheckBox(); + this.RiceGameSpecificTab = new System.Windows.Forms.TabPage(); + this.label27 = new System.Windows.Forms.Label(); + this.RiceEnableHacksForGame_Combo = new System.Windows.Forms.ComboBox(); + this.RiceForceDepthBuffer_CB = new System.Windows.Forms.CheckBox(); + this.label28 = new System.Windows.Forms.Label(); + this.RiceUseCIWidthAndRatio_Combo = new System.Windows.Forms.ComboBox(); + this.label26 = new System.Windows.Forms.Label(); + this.RiceRenderToTextureOption_Combo = new System.Windows.Forms.ComboBox(); + this.label25 = new System.Windows.Forms.Label(); + this.RiceFrameBufferOption_Combo = new System.Windows.Forms.ComboBox(); + this.label24 = new System.Windows.Forms.Label(); + this.RiceNormalBlender_Combo = new System.Windows.Forms.ComboBox(); + this.label23 = new System.Windows.Forms.Label(); + this.RiceAccurateTextureMappingHack_Combo = new System.Windows.Forms.ComboBox(); + this.label22 = new System.Windows.Forms.Label(); + this.RiceFastTextureCRC_Combo = new System.Windows.Forms.ComboBox(); + this.label21 = new System.Windows.Forms.Label(); + this.RiceFullTMEM_Combo = new System.Windows.Forms.ComboBox(); + this.label20 = new System.Windows.Forms.Label(); + this.RiceScreenUpdateSettingHack_Combo = new System.Windows.Forms.ComboBox(); + this.label19 = new System.Windows.Forms.Label(); + this.RiceVIHeight_Text = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.RiceVIWidth_Text = new System.Windows.Forms.TextBox(); + this.RiceTextureScaleHack_CB = new System.Windows.Forms.CheckBox(); + this.RiceFastLoadTile_CB = new System.Windows.Forms.CheckBox(); + this.RiceUseSmallerTexture_CB = new System.Windows.Forms.CheckBox(); + this.RiceEnableTxtLOD_CB = new System.Windows.Forms.CheckBox(); + this.RiceZHack_CB = new System.Windows.Forms.CheckBox(); + this.RicePrimaryDepthHack_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableObjBG_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableBlender_CB = new System.Windows.Forms.CheckBox(); + this.RiceForceScreenClear_CB = new System.Windows.Forms.CheckBox(); + this.RiceEmulateClear_CB = new System.Windows.Forms.CheckBox(); + this.RiceTxtSizeMethod2_CB = new System.Windows.Forms.CheckBox(); + this.RiceIncTexRectEdge_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableCulling_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableTextureCRC_CB = new System.Windows.Forms.CheckBox(); + this.RiceTexture1Hack_CB = new System.Windows.Forms.CheckBox(); + this.RiceUseDefaultHacks_CB = new System.Windows.Forms.CheckBox(); + this.label71 = new System.Windows.Forms.Label(); + this.label70 = new System.Windows.Forms.Label(); + this.label69 = new System.Windows.Forms.Label(); + this.label68 = new System.Windows.Forms.Label(); + this.label67 = new System.Windows.Forms.Label(); + this.label66 = new System.Windows.Forms.Label(); + this.label65 = new System.Windows.Forms.Label(); + this.label64 = new System.Windows.Forms.Label(); + this.label63 = new System.Windows.Forms.Label(); + this.label62 = new System.Windows.Forms.Label(); + this.label61 = new System.Windows.Forms.Label(); + this.label60 = new System.Windows.Forms.Label(); + this.label59 = new System.Windows.Forms.Label(); + this.label57 = new System.Windows.Forms.Label(); + this.label51 = new System.Windows.Forms.Label(); + this.SaveButton = new System.Windows.Forms.Button(); + this.CancelBT = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.N64plugintabcontrol.SuspendLayout(); + this.N64vpluginglobaltab.SuspendLayout(); + this.GLideN64Tab.SuspendLayout(); + this.tabControl3.SuspendLayout(); + this.tabPage5.SuspendLayout(); + this.tabPage6.SuspendLayout(); + this.tabPage7.SuspendLayout(); + this.Glide64mk2TabPage.SuspendLayout(); + this.tabControl2.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.Glide64TabPage.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.Glide64General.SuspendLayout(); + this.GlidePerGameHacks1.SuspendLayout(); + this.GlidePerGameHacks2.SuspendLayout(); + this.RiceTabPage.SuspendLayout(); + this.RiceTabControl.SuspendLayout(); + this.RiceGeneral.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).BeginInit(); + this.RiceGameDefaultTab.SuspendLayout(); + this.RiceFrameBuffer_GroupBox.SuspendLayout(); + this.RiceTextureEnhancementTab.SuspendLayout(); + this.RiceGameSpecificTab.SuspendLayout(); + this.SuspendLayout(); + // + // N64plugintabcontrol + // + this.N64plugintabcontrol.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.N64plugintabcontrol.Controls.Add(this.N64vpluginglobaltab); - this.N64plugintabcontrol.Controls.Add(this.GLideN64Tab); - this.N64plugintabcontrol.Controls.Add(this.Glide64mk2TabPage); - this.N64plugintabcontrol.Controls.Add(this.Glide64TabPage); - this.N64plugintabcontrol.Controls.Add(this.RiceTabPage); - this.N64plugintabcontrol.Location = new System.Drawing.Point(-2, -2); - this.N64plugintabcontrol.Name = "N64plugintabcontrol"; - this.N64plugintabcontrol.SelectedIndex = 0; - this.N64plugintabcontrol.Size = new System.Drawing.Size(580, 369); - this.N64plugintabcontrol.TabIndex = 0; - // - // N64vpluginglobaltab - // - this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionYTextBox); - this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionY); - this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionXTextBox); - this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionX); - this.N64vpluginglobaltab.Controls.Add(this.label49); - this.N64vpluginglobaltab.Controls.Add(this.RspTypeDropdown); - this.N64vpluginglobaltab.Controls.Add(this.label48); - this.N64vpluginglobaltab.Controls.Add(this.CoreTypeDropdown); - this.N64vpluginglobaltab.Controls.Add(this.label47); - this.N64vpluginglobaltab.Controls.Add(this.label2); - this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionComboBox); - this.N64vpluginglobaltab.Controls.Add(this.PluginComboBox); - this.N64vpluginglobaltab.Controls.Add(this.label1); - this.N64vpluginglobaltab.Location = new System.Drawing.Point(4, 22); - this.N64vpluginglobaltab.Name = "N64vpluginglobaltab"; - this.N64vpluginglobaltab.Padding = new System.Windows.Forms.Padding(3); - this.N64vpluginglobaltab.Size = new System.Drawing.Size(572, 343); - this.N64vpluginglobaltab.TabIndex = 0; - this.N64vpluginglobaltab.Text = "Global"; - this.N64vpluginglobaltab.UseVisualStyleBackColor = true; - // - // VideoResolutionYTextBox - // - this.VideoResolutionYTextBox.Location = new System.Drawing.Point(66, 270); - this.VideoResolutionYTextBox.MaxLength = 5; - this.VideoResolutionYTextBox.Name = "VideoResolutionYTextBox"; - this.VideoResolutionYTextBox.Size = new System.Drawing.Size(35, 20); - this.VideoResolutionYTextBox.TabIndex = 22; - this.VideoResolutionYTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.VideoResolutionYTextBox.Visible = false; - // - // LabelVideoResolutionY - // - this.LabelVideoResolutionY.AutoSize = true; - this.LabelVideoResolutionY.Location = new System.Drawing.Point(22, 273); - this.LabelVideoResolutionY.Name = "LabelVideoResolutionY"; - this.LabelVideoResolutionY.Size = new System.Drawing.Size(41, 13); - this.LabelVideoResolutionY.TabIndex = 21; - this.LabelVideoResolutionY.Text = "Height:"; - this.LabelVideoResolutionY.Visible = false; - // - // VideoResolutionXTextBox - // - this.VideoResolutionXTextBox.Location = new System.Drawing.Point(66, 244); - this.VideoResolutionXTextBox.MaxLength = 5; - this.VideoResolutionXTextBox.Name = "VideoResolutionXTextBox"; - this.VideoResolutionXTextBox.Size = new System.Drawing.Size(35, 20); - this.VideoResolutionXTextBox.TabIndex = 20; - this.VideoResolutionXTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.VideoResolutionXTextBox.Visible = false; - // - // LabelVideoResolutionX - // - this.LabelVideoResolutionX.AutoSize = true; - this.LabelVideoResolutionX.Location = new System.Drawing.Point(22, 247); - this.LabelVideoResolutionX.Name = "LabelVideoResolutionX"; - this.LabelVideoResolutionX.Size = new System.Drawing.Size(38, 13); - this.LabelVideoResolutionX.TabIndex = 19; - this.LabelVideoResolutionX.Text = "Width:"; - this.LabelVideoResolutionX.Visible = false; - // - // label49 - // - this.label49.AutoSize = true; - this.label49.Location = new System.Drawing.Point(206, 13); - this.label49.Name = "label49"; - this.label49.Size = new System.Drawing.Size(58, 13); - this.label49.TabIndex = 18; - this.label49.Text = "Rsp Plugin"; - this.label49.Visible = false; - // - // RspTypeDropdown - // - this.RspTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RspTypeDropdown.FormattingEnabled = true; - this.RspTypeDropdown.Items.AddRange(new object[] { + this.N64plugintabcontrol.Controls.Add(this.N64vpluginglobaltab); + this.N64plugintabcontrol.Controls.Add(this.GLideN64Tab); + this.N64plugintabcontrol.Controls.Add(this.Glide64mk2TabPage); + this.N64plugintabcontrol.Controls.Add(this.Glide64TabPage); + this.N64plugintabcontrol.Controls.Add(this.RiceTabPage); + this.N64plugintabcontrol.Location = new System.Drawing.Point(-2, -2); + this.N64plugintabcontrol.Name = "N64plugintabcontrol"; + this.N64plugintabcontrol.SelectedIndex = 0; + this.N64plugintabcontrol.Size = new System.Drawing.Size(580, 369); + this.N64plugintabcontrol.TabIndex = 0; + // + // N64vpluginglobaltab + // + this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionYTextBox); + this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionY); + this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionXTextBox); + this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionX); + this.N64vpluginglobaltab.Controls.Add(this.label49); + this.N64vpluginglobaltab.Controls.Add(this.RspTypeDropdown); + this.N64vpluginglobaltab.Controls.Add(this.label48); + this.N64vpluginglobaltab.Controls.Add(this.CoreTypeDropdown); + this.N64vpluginglobaltab.Controls.Add(this.label47); + this.N64vpluginglobaltab.Controls.Add(this.label2); + this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionComboBox); + this.N64vpluginglobaltab.Controls.Add(this.PluginComboBox); + this.N64vpluginglobaltab.Controls.Add(this.label1); + this.N64vpluginglobaltab.Location = new System.Drawing.Point(4, 22); + this.N64vpluginglobaltab.Name = "N64vpluginglobaltab"; + this.N64vpluginglobaltab.Padding = new System.Windows.Forms.Padding(3); + this.N64vpluginglobaltab.Size = new System.Drawing.Size(572, 343); + this.N64vpluginglobaltab.TabIndex = 0; + this.N64vpluginglobaltab.Text = "Global"; + this.N64vpluginglobaltab.UseVisualStyleBackColor = true; + // + // VideoResolutionYTextBox + // + this.VideoResolutionYTextBox.Location = new System.Drawing.Point(66, 270); + this.VideoResolutionYTextBox.MaxLength = 5; + this.VideoResolutionYTextBox.Name = "VideoResolutionYTextBox"; + this.VideoResolutionYTextBox.Size = new System.Drawing.Size(35, 20); + this.VideoResolutionYTextBox.TabIndex = 22; + this.VideoResolutionYTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.VideoResolutionYTextBox.Visible = false; + // + // LabelVideoResolutionY + // + this.LabelVideoResolutionY.AutoSize = true; + this.LabelVideoResolutionY.Location = new System.Drawing.Point(22, 273); + this.LabelVideoResolutionY.Name = "LabelVideoResolutionY"; + this.LabelVideoResolutionY.Size = new System.Drawing.Size(41, 13); + this.LabelVideoResolutionY.TabIndex = 21; + this.LabelVideoResolutionY.Text = "Height:"; + this.LabelVideoResolutionY.Visible = false; + // + // VideoResolutionXTextBox + // + this.VideoResolutionXTextBox.Location = new System.Drawing.Point(66, 244); + this.VideoResolutionXTextBox.MaxLength = 5; + this.VideoResolutionXTextBox.Name = "VideoResolutionXTextBox"; + this.VideoResolutionXTextBox.Size = new System.Drawing.Size(35, 20); + this.VideoResolutionXTextBox.TabIndex = 20; + this.VideoResolutionXTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.VideoResolutionXTextBox.Visible = false; + // + // LabelVideoResolutionX + // + this.LabelVideoResolutionX.AutoSize = true; + this.LabelVideoResolutionX.Location = new System.Drawing.Point(22, 247); + this.LabelVideoResolutionX.Name = "LabelVideoResolutionX"; + this.LabelVideoResolutionX.Size = new System.Drawing.Size(38, 13); + this.LabelVideoResolutionX.TabIndex = 19; + this.LabelVideoResolutionX.Text = "Width:"; + this.LabelVideoResolutionX.Visible = false; + // + // label49 + // + this.label49.AutoSize = true; + this.label49.Location = new System.Drawing.Point(206, 13); + this.label49.Name = "label49"; + this.label49.Size = new System.Drawing.Size(58, 13); + this.label49.TabIndex = 18; + this.label49.Text = "Rsp Plugin"; + this.label49.Visible = false; + // + // RspTypeDropdown + // + this.RspTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RspTypeDropdown.FormattingEnabled = true; + this.RspTypeDropdown.Items.AddRange(new object[] { "Pure Interpreter", "Interpreter", "DynaRec"}); - this.RspTypeDropdown.Location = new System.Drawing.Point(209, 29); - this.RspTypeDropdown.Name = "RspTypeDropdown"; - this.RspTypeDropdown.Size = new System.Drawing.Size(136, 21); - this.RspTypeDropdown.TabIndex = 17; - this.RspTypeDropdown.Visible = false; - // - // label48 - // - this.label48.AutoSize = true; - this.label48.Location = new System.Drawing.Point(10, 13); - this.label48.Name = "label48"; - this.label48.Size = new System.Drawing.Size(56, 13); - this.label48.TabIndex = 16; - this.label48.Text = "Core Type"; - // - // CoreTypeDropdown - // - this.CoreTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.CoreTypeDropdown.FormattingEnabled = true; - this.CoreTypeDropdown.Items.AddRange(new object[] { + this.RspTypeDropdown.Location = new System.Drawing.Point(209, 29); + this.RspTypeDropdown.Name = "RspTypeDropdown"; + this.RspTypeDropdown.Size = new System.Drawing.Size(136, 21); + this.RspTypeDropdown.TabIndex = 17; + this.RspTypeDropdown.Visible = false; + // + // label48 + // + this.label48.AutoSize = true; + this.label48.Location = new System.Drawing.Point(10, 13); + this.label48.Name = "label48"; + this.label48.Size = new System.Drawing.Size(56, 13); + this.label48.TabIndex = 16; + this.label48.Text = "Core Type"; + // + // CoreTypeDropdown + // + this.CoreTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.CoreTypeDropdown.FormattingEnabled = true; + this.CoreTypeDropdown.Items.AddRange(new object[] { "Pure Interpreter", "Interpreter", "DynaRec"}); - this.CoreTypeDropdown.Location = new System.Drawing.Point(13, 29); - this.CoreTypeDropdown.Name = "CoreTypeDropdown"; - this.CoreTypeDropdown.Size = new System.Drawing.Size(136, 21); - this.CoreTypeDropdown.TabIndex = 1; - // - // label47 - // - this.label47.AutoSize = true; - this.label47.Location = new System.Drawing.Point(10, 115); - this.label47.Name = "label47"; + this.CoreTypeDropdown.Location = new System.Drawing.Point(13, 29); + this.CoreTypeDropdown.Name = "CoreTypeDropdown"; + this.CoreTypeDropdown.Size = new System.Drawing.Size(136, 21); + this.CoreTypeDropdown.TabIndex = 1; + // + // label47 + // + this.label47.AutoSize = true; + this.label47.Location = new System.Drawing.Point(10, 115); + this.label47.Name = "label47"; this.label47.Size = new System.Drawing.Size(275, 13); - this.label47.TabIndex = 14; - this.label47.Text = "(GLideN64 is the newest pluging and has the highest compatibility)"; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(10, 201); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(87, 13); - this.label2.TabIndex = 13; - this.label2.Text = "Video Resolution"; - // - // VideoResolutionComboBox - // - this.VideoResolutionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.VideoResolutionComboBox.FormattingEnabled = true; - this.VideoResolutionComboBox.Items.AddRange(new object[] { + this.label47.TabIndex = 14; + this.label47.Text = "(GLideN64 is the newest pluging and has the highest compatibility)"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(10, 201); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(87, 13); + this.label2.TabIndex = 13; + this.label2.Text = "Video Resolution"; + // + // VideoResolutionComboBox + // + this.VideoResolutionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.VideoResolutionComboBox.FormattingEnabled = true; + this.VideoResolutionComboBox.Items.AddRange(new object[] { "320 x 240", "400 x 300", "480 x 360", @@ -550,155 +550,155 @@ "1920 x 1440", "2048 x 1536", "Custom"}); - this.VideoResolutionComboBox.Location = new System.Drawing.Point(13, 217); - this.VideoResolutionComboBox.Name = "VideoResolutionComboBox"; - this.VideoResolutionComboBox.Size = new System.Drawing.Size(136, 21); - this.VideoResolutionComboBox.TabIndex = 10; - this.VideoResolutionComboBox.SelectedIndexChanged += new System.EventHandler(this.VideoResolutionComboBox_SelectedIndexChanged); - // - // PluginComboBox - // - this.PluginComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.PluginComboBox.FormattingEnabled = true; - this.PluginComboBox.Items.AddRange(new object[] { + this.VideoResolutionComboBox.Location = new System.Drawing.Point(13, 217); + this.VideoResolutionComboBox.Name = "VideoResolutionComboBox"; + this.VideoResolutionComboBox.Size = new System.Drawing.Size(136, 21); + this.VideoResolutionComboBox.TabIndex = 10; + this.VideoResolutionComboBox.SelectedIndexChanged += new System.EventHandler(this.VideoResolutionComboBox_SelectedIndexChanged); + // + // PluginComboBox + // + this.PluginComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.PluginComboBox.FormattingEnabled = true; + this.PluginComboBox.Items.AddRange(new object[] { "Rice", "Glide64", "Glide64mk2", "GLideN64"}); - this.PluginComboBox.Location = new System.Drawing.Point(13, 91); - this.PluginComboBox.Name = "PluginComboBox"; - this.PluginComboBox.Size = new System.Drawing.Size(173, 21); - this.PluginComboBox.TabIndex = 5; - this.PluginComboBox.SelectedIndexChanged += new System.EventHandler(this.PluginComboBox_SelectedIndexChanged); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(10, 75); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(99, 13); - this.label1.TabIndex = 1; - this.label1.Text = "Active Video Plugin"; - // - // GLideN64Tab - // - this.GLideN64Tab.Controls.Add(this.tabControl3); - this.GLideN64Tab.Location = new System.Drawing.Point(4, 22); - this.GLideN64Tab.Name = "GLideN64Tab"; - this.GLideN64Tab.Padding = new System.Windows.Forms.Padding(3); - this.GLideN64Tab.Size = new System.Drawing.Size(572, 343); - this.GLideN64Tab.TabIndex = 8; - this.GLideN64Tab.Text = "GLideN64"; - this.GLideN64Tab.UseVisualStyleBackColor = true; - // - // tabControl3 - // - this.tabControl3.Controls.Add(this.tabPage5); - this.tabControl3.Controls.Add(this.tabPage6); - this.tabControl3.Controls.Add(this.tabPage7); - this.tabControl3.Location = new System.Drawing.Point(0, 3); - this.tabControl3.Name = "tabControl3"; - this.tabControl3.SelectedIndex = 0; - this.tabControl3.Size = new System.Drawing.Size(572, 336); - this.tabControl3.TabIndex = 2; - // - // tabPage5 - // - this.tabPage5.Controls.Add(this.label88); - this.tabPage5.Controls.Add(this.GLideN64_GammaCorrectionLevel); - this.tabPage5.Controls.Add(this.GLideN64_ForceGammaCorrection); - this.tabPage5.Controls.Add(this.label81); - this.tabPage5.Controls.Add(this.GLideN64_MultiSampling); - this.tabPage5.Controls.Add(this.label80); - this.tabPage5.Controls.Add(this.GLideN64_blurStrength); - this.tabPage5.Controls.Add(this.label79); - this.tabPage5.Controls.Add(this.GLideN64_blurAmount); - this.tabPage5.Controls.Add(this.label78); - this.tabPage5.Controls.Add(this.GLideN64_bloomBlendMode); - this.tabPage5.Controls.Add(this.label77); - this.tabPage5.Controls.Add(this.GLideN64_bloomThresholdLevel); - this.tabPage5.Controls.Add(this.GLideN64_EnableBloom); - this.tabPage5.Controls.Add(this.GLideN64_FragmentDepthWrite); - this.tabPage5.Controls.Add(this.GLideN64_LegacyBlending); - this.tabPage5.Controls.Add(this.GLideN64_NativeResTexrects); - this.tabPage5.Controls.Add(this.label82); - this.tabPage5.Controls.Add(this.GLideN64_CorrectTexrectCoords); - this.tabPage5.Controls.Add(this.GLideN64_ShadersStorage); - this.tabPage5.Controls.Add(this.GLideN64_HWLighting); - this.tabPage5.Controls.Add(this.GLideN64_EnableNoise); - this.tabPage5.Controls.Add(this.GLideN64_EnableLOD); - this.tabPage5.Location = new System.Drawing.Point(4, 22); - this.tabPage5.Name = "tabPage5"; - this.tabPage5.Padding = new System.Windows.Forms.Padding(3); - this.tabPage5.Size = new System.Drawing.Size(564, 310); - this.tabPage5.TabIndex = 0; - this.tabPage5.Text = "General"; - this.tabPage5.UseVisualStyleBackColor = true; - // - // label88 - // - this.label88.AutoSize = true; - this.label88.Location = new System.Drawing.Point(306, 187); - this.label88.Name = "label88"; - this.label88.Size = new System.Drawing.Size(123, 13); - this.label88.TabIndex = 78; - this.label88.Text = "Gamma Correction Level"; - // - // GLideN64_GammaCorrectionLevel - // - this.GLideN64_GammaCorrectionLevel.Location = new System.Drawing.Point(435, 184); - this.GLideN64_GammaCorrectionLevel.Name = "GLideN64_GammaCorrectionLevel"; - this.GLideN64_GammaCorrectionLevel.Size = new System.Drawing.Size(78, 20); - this.GLideN64_GammaCorrectionLevel.TabIndex = 77; - // - // GLideN64_ForceGammaCorrection - // - this.GLideN64_ForceGammaCorrection.AutoSize = true; - this.GLideN64_ForceGammaCorrection.Location = new System.Drawing.Point(309, 161); - this.GLideN64_ForceGammaCorrection.Name = "GLideN64_ForceGammaCorrection"; - this.GLideN64_ForceGammaCorrection.Size = new System.Drawing.Size(143, 17); - this.GLideN64_ForceGammaCorrection.TabIndex = 70; - this.GLideN64_ForceGammaCorrection.Text = "Force Gamma Correction"; - this.GLideN64_ForceGammaCorrection.UseVisualStyleBackColor = true; - // - // label81 - // - this.label81.AutoSize = true; - this.label81.Location = new System.Drawing.Point(3, 210); - this.label81.Name = "label81"; - this.label81.Size = new System.Drawing.Size(75, 13); - this.label81.TabIndex = 69; - this.label81.Text = "MultiSampling:"; - // - // GLideN64_MultiSampling - // - this.GLideN64_MultiSampling.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_MultiSampling.FormattingEnabled = true; - this.GLideN64_MultiSampling.Items.AddRange(new object[] { + this.PluginComboBox.Location = new System.Drawing.Point(13, 91); + this.PluginComboBox.Name = "PluginComboBox"; + this.PluginComboBox.Size = new System.Drawing.Size(173, 21); + this.PluginComboBox.TabIndex = 5; + this.PluginComboBox.SelectedIndexChanged += new System.EventHandler(this.PluginComboBox_SelectedIndexChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(10, 75); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(99, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Active Video Plugin"; + // + // GLideN64Tab + // + this.GLideN64Tab.Controls.Add(this.tabControl3); + this.GLideN64Tab.Location = new System.Drawing.Point(4, 22); + this.GLideN64Tab.Name = "GLideN64Tab"; + this.GLideN64Tab.Padding = new System.Windows.Forms.Padding(3); + this.GLideN64Tab.Size = new System.Drawing.Size(572, 343); + this.GLideN64Tab.TabIndex = 8; + this.GLideN64Tab.Text = "GLideN64"; + this.GLideN64Tab.UseVisualStyleBackColor = true; + // + // tabControl3 + // + this.tabControl3.Controls.Add(this.tabPage5); + this.tabControl3.Controls.Add(this.tabPage6); + this.tabControl3.Controls.Add(this.tabPage7); + this.tabControl3.Location = new System.Drawing.Point(0, 3); + this.tabControl3.Name = "tabControl3"; + this.tabControl3.SelectedIndex = 0; + this.tabControl3.Size = new System.Drawing.Size(572, 336); + this.tabControl3.TabIndex = 2; + // + // tabPage5 + // + this.tabPage5.Controls.Add(this.label88); + this.tabPage5.Controls.Add(this.GLideN64_GammaCorrectionLevel); + this.tabPage5.Controls.Add(this.GLideN64_ForceGammaCorrection); + this.tabPage5.Controls.Add(this.label81); + this.tabPage5.Controls.Add(this.GLideN64_MultiSampling); + this.tabPage5.Controls.Add(this.label80); + this.tabPage5.Controls.Add(this.GLideN64_blurStrength); + this.tabPage5.Controls.Add(this.label79); + this.tabPage5.Controls.Add(this.GLideN64_blurAmount); + this.tabPage5.Controls.Add(this.label78); + this.tabPage5.Controls.Add(this.GLideN64_bloomBlendMode); + this.tabPage5.Controls.Add(this.label77); + this.tabPage5.Controls.Add(this.GLideN64_bloomThresholdLevel); + this.tabPage5.Controls.Add(this.GLideN64_EnableBloom); + this.tabPage5.Controls.Add(this.GLideN64_FragmentDepthWrite); + this.tabPage5.Controls.Add(this.GLideN64_LegacyBlending); + this.tabPage5.Controls.Add(this.GLideN64_NativeResTexrects); + this.tabPage5.Controls.Add(this.label82); + this.tabPage5.Controls.Add(this.GLideN64_CorrectTexrectCoords); + this.tabPage5.Controls.Add(this.GLideN64_ShadersStorage); + this.tabPage5.Controls.Add(this.GLideN64_HWLighting); + this.tabPage5.Controls.Add(this.GLideN64_EnableNoise); + this.tabPage5.Controls.Add(this.GLideN64_EnableLOD); + this.tabPage5.Location = new System.Drawing.Point(4, 22); + this.tabPage5.Name = "tabPage5"; + this.tabPage5.Padding = new System.Windows.Forms.Padding(3); + this.tabPage5.Size = new System.Drawing.Size(564, 310); + this.tabPage5.TabIndex = 0; + this.tabPage5.Text = "General"; + this.tabPage5.UseVisualStyleBackColor = true; + // + // label88 + // + this.label88.AutoSize = true; + this.label88.Location = new System.Drawing.Point(306, 187); + this.label88.Name = "label88"; + this.label88.Size = new System.Drawing.Size(123, 13); + this.label88.TabIndex = 78; + this.label88.Text = "Gamma Correction Level"; + // + // GLideN64_GammaCorrectionLevel + // + this.GLideN64_GammaCorrectionLevel.Location = new System.Drawing.Point(435, 184); + this.GLideN64_GammaCorrectionLevel.Name = "GLideN64_GammaCorrectionLevel"; + this.GLideN64_GammaCorrectionLevel.Size = new System.Drawing.Size(78, 20); + this.GLideN64_GammaCorrectionLevel.TabIndex = 77; + // + // GLideN64_ForceGammaCorrection + // + this.GLideN64_ForceGammaCorrection.AutoSize = true; + this.GLideN64_ForceGammaCorrection.Location = new System.Drawing.Point(309, 161); + this.GLideN64_ForceGammaCorrection.Name = "GLideN64_ForceGammaCorrection"; + this.GLideN64_ForceGammaCorrection.Size = new System.Drawing.Size(143, 17); + this.GLideN64_ForceGammaCorrection.TabIndex = 70; + this.GLideN64_ForceGammaCorrection.Text = "Force Gamma Correction"; + this.GLideN64_ForceGammaCorrection.UseVisualStyleBackColor = true; + // + // label81 + // + this.label81.AutoSize = true; + this.label81.Location = new System.Drawing.Point(3, 210); + this.label81.Name = "label81"; + this.label81.Size = new System.Drawing.Size(75, 13); + this.label81.TabIndex = 69; + this.label81.Text = "MultiSampling:"; + // + // GLideN64_MultiSampling + // + this.GLideN64_MultiSampling.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_MultiSampling.FormattingEnabled = true; + this.GLideN64_MultiSampling.Items.AddRange(new object[] { "0", "2", "4", "8", "16"}); - this.GLideN64_MultiSampling.Location = new System.Drawing.Point(128, 207); - this.GLideN64_MultiSampling.Name = "GLideN64_MultiSampling"; - this.GLideN64_MultiSampling.Size = new System.Drawing.Size(138, 21); - this.GLideN64_MultiSampling.TabIndex = 68; - // - // label80 - // - this.label80.AutoSize = true; - this.label80.Location = new System.Drawing.Point(306, 122); - this.label80.Name = "label80"; - this.label80.Size = new System.Drawing.Size(71, 13); - this.label80.TabIndex = 67; - this.label80.Text = "Blur Strength:"; - // - // GLideN64_blurStrength - // - this.GLideN64_blurStrength.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_blurStrength.FormattingEnabled = true; - this.GLideN64_blurStrength.Items.AddRange(new object[] { + this.GLideN64_MultiSampling.Location = new System.Drawing.Point(128, 207); + this.GLideN64_MultiSampling.Name = "GLideN64_MultiSampling"; + this.GLideN64_MultiSampling.Size = new System.Drawing.Size(138, 21); + this.GLideN64_MultiSampling.TabIndex = 68; + // + // label80 + // + this.label80.AutoSize = true; + this.label80.Location = new System.Drawing.Point(306, 122); + this.label80.Name = "label80"; + this.label80.Size = new System.Drawing.Size(71, 13); + this.label80.TabIndex = 67; + this.label80.Text = "Blur Strength:"; + // + // GLideN64_blurStrength + // + this.GLideN64_blurStrength.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_blurStrength.FormattingEnabled = true; + this.GLideN64_blurStrength.Items.AddRange(new object[] { "10", "11", "12", @@ -790,25 +790,25 @@ "98", "99", "100"}); - this.GLideN64_blurStrength.Location = new System.Drawing.Point(430, 119); - this.GLideN64_blurStrength.Name = "GLideN64_blurStrength"; - this.GLideN64_blurStrength.Size = new System.Drawing.Size(60, 21); - this.GLideN64_blurStrength.TabIndex = 66; - // - // label79 - // - this.label79.AutoSize = true; - this.label79.Location = new System.Drawing.Point(306, 95); - this.label79.Name = "label79"; - this.label79.Size = new System.Drawing.Size(67, 13); - this.label79.TabIndex = 65; - this.label79.Text = "Blur Amount:"; - // - // GLideN64_blurAmount - // - this.GLideN64_blurAmount.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_blurAmount.FormattingEnabled = true; - this.GLideN64_blurAmount.Items.AddRange(new object[] { + this.GLideN64_blurStrength.Location = new System.Drawing.Point(430, 119); + this.GLideN64_blurStrength.Name = "GLideN64_blurStrength"; + this.GLideN64_blurStrength.Size = new System.Drawing.Size(60, 21); + this.GLideN64_blurStrength.TabIndex = 66; + // + // label79 + // + this.label79.AutoSize = true; + this.label79.Location = new System.Drawing.Point(306, 95); + this.label79.Name = "label79"; + this.label79.Size = new System.Drawing.Size(67, 13); + this.label79.TabIndex = 65; + this.label79.Text = "Blur Amount:"; + // + // GLideN64_blurAmount + // + this.GLideN64_blurAmount.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_blurAmount.FormattingEnabled = true; + this.GLideN64_blurAmount.Items.AddRange(new object[] { "2", "3", "4", @@ -818,327 +818,327 @@ "8", "9", "10"}); - this.GLideN64_blurAmount.Location = new System.Drawing.Point(430, 92); - this.GLideN64_blurAmount.Name = "GLideN64_blurAmount"; - this.GLideN64_blurAmount.Size = new System.Drawing.Size(60, 21); - this.GLideN64_blurAmount.TabIndex = 64; - // - // label78 - // - this.label78.AutoSize = true; - this.label78.Location = new System.Drawing.Point(306, 68); - this.label78.Name = "label78"; - this.label78.Size = new System.Drawing.Size(99, 13); - this.label78.TabIndex = 63; - this.label78.Text = "Bloom Blend Mode:"; - // - // GLideN64_bloomBlendMode - // - this.GLideN64_bloomBlendMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_bloomBlendMode.FormattingEnabled = true; - this.GLideN64_bloomBlendMode.Items.AddRange(new object[] { + this.GLideN64_blurAmount.Location = new System.Drawing.Point(430, 92); + this.GLideN64_blurAmount.Name = "GLideN64_blurAmount"; + this.GLideN64_blurAmount.Size = new System.Drawing.Size(60, 21); + this.GLideN64_blurAmount.TabIndex = 64; + // + // label78 + // + this.label78.AutoSize = true; + this.label78.Location = new System.Drawing.Point(306, 68); + this.label78.Name = "label78"; + this.label78.Size = new System.Drawing.Size(99, 13); + this.label78.TabIndex = 63; + this.label78.Text = "Bloom Blend Mode:"; + // + // GLideN64_bloomBlendMode + // + this.GLideN64_bloomBlendMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_bloomBlendMode.FormattingEnabled = true; + this.GLideN64_bloomBlendMode.Items.AddRange(new object[] { "Strong", "Mild", "Light"}); - this.GLideN64_bloomBlendMode.Location = new System.Drawing.Point(430, 65); - this.GLideN64_bloomBlendMode.Name = "GLideN64_bloomBlendMode"; - this.GLideN64_bloomBlendMode.Size = new System.Drawing.Size(128, 21); - this.GLideN64_bloomBlendMode.TabIndex = 62; - // - // label77 - // - this.label77.AutoSize = true; - this.label77.Location = new System.Drawing.Point(306, 41); - this.label77.Name = "label77"; - this.label77.Size = new System.Drawing.Size(118, 13); - this.label77.TabIndex = 61; - this.label77.Text = "Bloom Threshold Level:"; - // - // GLideN64_bloomThresholdLevel - // - this.GLideN64_bloomThresholdLevel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_bloomThresholdLevel.FormattingEnabled = true; - this.GLideN64_bloomThresholdLevel.Items.AddRange(new object[] { + this.GLideN64_bloomBlendMode.Location = new System.Drawing.Point(430, 65); + this.GLideN64_bloomBlendMode.Name = "GLideN64_bloomBlendMode"; + this.GLideN64_bloomBlendMode.Size = new System.Drawing.Size(128, 21); + this.GLideN64_bloomBlendMode.TabIndex = 62; + // + // label77 + // + this.label77.AutoSize = true; + this.label77.Location = new System.Drawing.Point(306, 41); + this.label77.Name = "label77"; + this.label77.Size = new System.Drawing.Size(118, 13); + this.label77.TabIndex = 61; + this.label77.Text = "Bloom Threshold Level:"; + // + // GLideN64_bloomThresholdLevel + // + this.GLideN64_bloomThresholdLevel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_bloomThresholdLevel.FormattingEnabled = true; + this.GLideN64_bloomThresholdLevel.Items.AddRange(new object[] { "2", "3", "4", "5", "6"}); - this.GLideN64_bloomThresholdLevel.Location = new System.Drawing.Point(430, 38); - this.GLideN64_bloomThresholdLevel.Name = "GLideN64_bloomThresholdLevel"; - this.GLideN64_bloomThresholdLevel.Size = new System.Drawing.Size(60, 21); - this.GLideN64_bloomThresholdLevel.TabIndex = 60; - // - // GLideN64_EnableBloom - // - this.GLideN64_EnableBloom.AutoSize = true; - this.GLideN64_EnableBloom.Location = new System.Drawing.Point(309, 19); - this.GLideN64_EnableBloom.Name = "GLideN64_EnableBloom"; - this.GLideN64_EnableBloom.Size = new System.Drawing.Size(91, 17); - this.GLideN64_EnableBloom.TabIndex = 59; - this.GLideN64_EnableBloom.Text = "Enable Bloom"; - this.GLideN64_EnableBloom.UseVisualStyleBackColor = true; - // - // GLideN64_FragmentDepthWrite - // - this.GLideN64_FragmentDepthWrite.AutoSize = true; - this.GLideN64_FragmentDepthWrite.Location = new System.Drawing.Point(6, 184); - this.GLideN64_FragmentDepthWrite.Name = "GLideN64_FragmentDepthWrite"; - this.GLideN64_FragmentDepthWrite.Size = new System.Drawing.Size(130, 17); - this.GLideN64_FragmentDepthWrite.TabIndex = 58; - this.GLideN64_FragmentDepthWrite.Text = "Fragment Depth Write"; - this.GLideN64_FragmentDepthWrite.UseVisualStyleBackColor = true; - // - // GLideN64_LegacyBlending - // - this.GLideN64_LegacyBlending.AutoSize = true; - this.GLideN64_LegacyBlending.Location = new System.Drawing.Point(6, 161); - this.GLideN64_LegacyBlending.Name = "GLideN64_LegacyBlending"; - this.GLideN64_LegacyBlending.Size = new System.Drawing.Size(105, 17); - this.GLideN64_LegacyBlending.TabIndex = 57; - this.GLideN64_LegacyBlending.Text = "Legacy Blending"; - this.GLideN64_LegacyBlending.UseVisualStyleBackColor = true; - // - // GLideN64_NativeResTexrects - // - this.GLideN64_NativeResTexrects.AutoSize = true; - this.GLideN64_NativeResTexrects.Location = new System.Drawing.Point(6, 138); - this.GLideN64_NativeResTexrects.Name = "GLideN64_NativeResTexrects"; - this.GLideN64_NativeResTexrects.Size = new System.Drawing.Size(123, 17); - this.GLideN64_NativeResTexrects.TabIndex = 56; - this.GLideN64_NativeResTexrects.Text = "Native Res Texrects"; - this.GLideN64_NativeResTexrects.UseVisualStyleBackColor = true; - // - // label82 - // - this.label82.AutoSize = true; - this.label82.Location = new System.Drawing.Point(3, 114); - this.label82.Name = "label82"; - this.label82.Size = new System.Drawing.Size(119, 13); - this.label82.TabIndex = 55; - this.label82.Text = "Correct Texrect Coords:"; - // - // GLideN64_CorrectTexrectCoords - // - this.GLideN64_CorrectTexrectCoords.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_CorrectTexrectCoords.FormattingEnabled = true; - this.GLideN64_CorrectTexrectCoords.Items.AddRange(new object[] { + this.GLideN64_bloomThresholdLevel.Location = new System.Drawing.Point(430, 38); + this.GLideN64_bloomThresholdLevel.Name = "GLideN64_bloomThresholdLevel"; + this.GLideN64_bloomThresholdLevel.Size = new System.Drawing.Size(60, 21); + this.GLideN64_bloomThresholdLevel.TabIndex = 60; + // + // GLideN64_EnableBloom + // + this.GLideN64_EnableBloom.AutoSize = true; + this.GLideN64_EnableBloom.Location = new System.Drawing.Point(309, 19); + this.GLideN64_EnableBloom.Name = "GLideN64_EnableBloom"; + this.GLideN64_EnableBloom.Size = new System.Drawing.Size(91, 17); + this.GLideN64_EnableBloom.TabIndex = 59; + this.GLideN64_EnableBloom.Text = "Enable Bloom"; + this.GLideN64_EnableBloom.UseVisualStyleBackColor = true; + // + // GLideN64_FragmentDepthWrite + // + this.GLideN64_FragmentDepthWrite.AutoSize = true; + this.GLideN64_FragmentDepthWrite.Location = new System.Drawing.Point(6, 184); + this.GLideN64_FragmentDepthWrite.Name = "GLideN64_FragmentDepthWrite"; + this.GLideN64_FragmentDepthWrite.Size = new System.Drawing.Size(130, 17); + this.GLideN64_FragmentDepthWrite.TabIndex = 58; + this.GLideN64_FragmentDepthWrite.Text = "Fragment Depth Write"; + this.GLideN64_FragmentDepthWrite.UseVisualStyleBackColor = true; + // + // GLideN64_LegacyBlending + // + this.GLideN64_LegacyBlending.AutoSize = true; + this.GLideN64_LegacyBlending.Location = new System.Drawing.Point(6, 161); + this.GLideN64_LegacyBlending.Name = "GLideN64_LegacyBlending"; + this.GLideN64_LegacyBlending.Size = new System.Drawing.Size(105, 17); + this.GLideN64_LegacyBlending.TabIndex = 57; + this.GLideN64_LegacyBlending.Text = "Legacy Blending"; + this.GLideN64_LegacyBlending.UseVisualStyleBackColor = true; + // + // GLideN64_NativeResTexrects + // + this.GLideN64_NativeResTexrects.AutoSize = true; + this.GLideN64_NativeResTexrects.Location = new System.Drawing.Point(6, 138); + this.GLideN64_NativeResTexrects.Name = "GLideN64_NativeResTexrects"; + this.GLideN64_NativeResTexrects.Size = new System.Drawing.Size(123, 17); + this.GLideN64_NativeResTexrects.TabIndex = 56; + this.GLideN64_NativeResTexrects.Text = "Native Res Texrects"; + this.GLideN64_NativeResTexrects.UseVisualStyleBackColor = true; + // + // label82 + // + this.label82.AutoSize = true; + this.label82.Location = new System.Drawing.Point(3, 114); + this.label82.Name = "label82"; + this.label82.Size = new System.Drawing.Size(119, 13); + this.label82.TabIndex = 55; + this.label82.Text = "Correct Texrect Coords:"; + // + // GLideN64_CorrectTexrectCoords + // + this.GLideN64_CorrectTexrectCoords.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_CorrectTexrectCoords.FormattingEnabled = true; + this.GLideN64_CorrectTexrectCoords.Items.AddRange(new object[] { "Off", "Auto", "Force"}); - this.GLideN64_CorrectTexrectCoords.Location = new System.Drawing.Point(128, 111); - this.GLideN64_CorrectTexrectCoords.Name = "GLideN64_CorrectTexrectCoords"; - this.GLideN64_CorrectTexrectCoords.Size = new System.Drawing.Size(138, 21); - this.GLideN64_CorrectTexrectCoords.TabIndex = 54; - // - // GLideN64_ShadersStorage - // - this.GLideN64_ShadersStorage.AutoSize = true; - this.GLideN64_ShadersStorage.Location = new System.Drawing.Point(6, 88); - this.GLideN64_ShadersStorage.Name = "GLideN64_ShadersStorage"; - this.GLideN64_ShadersStorage.Size = new System.Drawing.Size(103, 17); - this.GLideN64_ShadersStorage.TabIndex = 53; - this.GLideN64_ShadersStorage.Text = "Shaders storage"; - this.GLideN64_ShadersStorage.UseVisualStyleBackColor = true; - // - // GLideN64_HWLighting - // - this.GLideN64_HWLighting.AutoSize = true; - this.GLideN64_HWLighting.Location = new System.Drawing.Point(6, 65); - this.GLideN64_HWLighting.Name = "GLideN64_HWLighting"; - this.GLideN64_HWLighting.Size = new System.Drawing.Size(85, 17); - this.GLideN64_HWLighting.TabIndex = 52; - this.GLideN64_HWLighting.Text = "HW Lighting"; - this.GLideN64_HWLighting.UseVisualStyleBackColor = true; - // - // GLideN64_EnableNoise - // - this.GLideN64_EnableNoise.AutoSize = true; - this.GLideN64_EnableNoise.Location = new System.Drawing.Point(6, 42); - this.GLideN64_EnableNoise.Name = "GLideN64_EnableNoise"; - this.GLideN64_EnableNoise.Size = new System.Drawing.Size(126, 17); - this.GLideN64_EnableNoise.TabIndex = 25; - this.GLideN64_EnableNoise.Text = "Color noise emulation"; - this.GLideN64_EnableNoise.UseVisualStyleBackColor = true; - // - // GLideN64_EnableLOD - // - this.GLideN64_EnableLOD.AutoSize = true; - this.GLideN64_EnableLOD.Location = new System.Drawing.Point(6, 19); - this.GLideN64_EnableLOD.Name = "GLideN64_EnableLOD"; - this.GLideN64_EnableLOD.Size = new System.Drawing.Size(97, 17); - this.GLideN64_EnableLOD.TabIndex = 21; - this.GLideN64_EnableLOD.Text = "LOD Emulation"; - this.GLideN64_EnableLOD.UseVisualStyleBackColor = true; - // - // tabPage6 - // - this.tabPage6.Controls.Add(this.label87); - this.tabPage6.Controls.Add(this.GLideN64_txPath); - this.tabPage6.Controls.Add(this.GLideN64_txSaveCache); - this.tabPage6.Controls.Add(this.GLideN64_txCacheCompression); - this.tabPage6.Controls.Add(this.GLideN64_txForce16bpp); - this.tabPage6.Controls.Add(this.GLideN64_txDump); - this.tabPage6.Controls.Add(this.GLideN64_txHresAltCRC); - this.tabPage6.Controls.Add(this.GLideN64_txHiresFullAlphaChannel); - this.tabPage6.Controls.Add(this.GLideN64_txHiresEnable); - this.tabPage6.Controls.Add(this.label86); - this.tabPage6.Controls.Add(this.GLideN64_txCacheSize); - this.tabPage6.Controls.Add(this.GLideN64_txFilterIgnoreBG); - this.tabPage6.Controls.Add(this.GLideN64_txDeposterize); - this.tabPage6.Controls.Add(this.label85); - this.tabPage6.Controls.Add(this.GLideN64_txEnhancementMode); - this.tabPage6.Controls.Add(this.label84); - this.tabPage6.Controls.Add(this.GLideN64_txFilterMode); - this.tabPage6.Controls.Add(this.label83); - this.tabPage6.Controls.Add(this.GLideN64_CacheSize); - this.tabPage6.Controls.Add(this.label72); - this.tabPage6.Controls.Add(this.GLideN64_bilinearMode); - this.tabPage6.Controls.Add(this.GLideN64_MaxAnisotropy); - this.tabPage6.Location = new System.Drawing.Point(4, 22); - this.tabPage6.Name = "tabPage6"; - this.tabPage6.Padding = new System.Windows.Forms.Padding(3); - this.tabPage6.Size = new System.Drawing.Size(564, 310); - this.tabPage6.TabIndex = 1; - this.tabPage6.Text = "Texture settings"; - this.tabPage6.UseVisualStyleBackColor = true; - // - // label87 - // - this.label87.AutoSize = true; - this.label87.Location = new System.Drawing.Point(3, 232); - this.label87.Name = "label87"; - this.label87.Size = new System.Drawing.Size(70, 13); - this.label87.TabIndex = 76; - this.label87.Text = "Texture path:"; - // - // GLideN64_txPath - // - this.GLideN64_txPath.Location = new System.Drawing.Point(123, 229); - this.GLideN64_txPath.Name = "GLideN64_txPath"; - this.GLideN64_txPath.Size = new System.Drawing.Size(78, 20); - this.GLideN64_txPath.TabIndex = 75; - // - // GLideN64_txSaveCache - // - this.GLideN64_txSaveCache.AutoSize = true; - this.GLideN64_txSaveCache.Location = new System.Drawing.Point(328, 157); - this.GLideN64_txSaveCache.Name = "GLideN64_txSaveCache"; - this.GLideN64_txSaveCache.Size = new System.Drawing.Size(119, 17); - this.GLideN64_txSaveCache.TabIndex = 74; - this.GLideN64_txSaveCache.Text = "Save texture cache"; - this.GLideN64_txSaveCache.UseVisualStyleBackColor = true; - // - // GLideN64_txCacheCompression - // - this.GLideN64_txCacheCompression.AutoSize = true; - this.GLideN64_txCacheCompression.Location = new System.Drawing.Point(328, 134); - this.GLideN64_txCacheCompression.Name = "GLideN64_txCacheCompression"; - this.GLideN64_txCacheCompression.Size = new System.Drawing.Size(159, 17); - this.GLideN64_txCacheCompression.TabIndex = 73; - this.GLideN64_txCacheCompression.Text = "Texture Cache Compression"; - this.GLideN64_txCacheCompression.UseVisualStyleBackColor = true; - // - // GLideN64_txForce16bpp - // - this.GLideN64_txForce16bpp.AutoSize = true; - this.GLideN64_txForce16bpp.Location = new System.Drawing.Point(328, 111); - this.GLideN64_txForce16bpp.Name = "GLideN64_txForce16bpp"; - this.GLideN64_txForce16bpp.Size = new System.Drawing.Size(125, 17); - this.GLideN64_txForce16bpp.TabIndex = 72; - this.GLideN64_txForce16bpp.Text = "Texture Force 16bpp"; - this.GLideN64_txForce16bpp.UseVisualStyleBackColor = true; - // - // GLideN64_txDump - // - this.GLideN64_txDump.AutoSize = true; - this.GLideN64_txDump.Location = new System.Drawing.Point(328, 88); - this.GLideN64_txDump.Name = "GLideN64_txDump"; - this.GLideN64_txDump.Size = new System.Drawing.Size(91, 17); - this.GLideN64_txDump.TabIndex = 71; - this.GLideN64_txDump.Text = "Texture dump"; - this.GLideN64_txDump.UseVisualStyleBackColor = true; - // - // GLideN64_txHresAltCRC - // - this.GLideN64_txHresAltCRC.AutoSize = true; - this.GLideN64_txHresAltCRC.Location = new System.Drawing.Point(328, 65); - this.GLideN64_txHresAltCRC.Name = "GLideN64_txHresAltCRC"; - this.GLideN64_txHresAltCRC.Size = new System.Drawing.Size(88, 17); - this.GLideN64_txHresAltCRC.TabIndex = 70; - this.GLideN64_txHresAltCRC.Text = "Hres Alt CRC"; - this.GLideN64_txHresAltCRC.UseVisualStyleBackColor = true; - // - // GLideN64_txHiresFullAlphaChannel - // - this.GLideN64_txHiresFullAlphaChannel.AutoSize = true; - this.GLideN64_txHiresFullAlphaChannel.Location = new System.Drawing.Point(328, 42); - this.GLideN64_txHiresFullAlphaChannel.Name = "GLideN64_txHiresFullAlphaChannel"; - this.GLideN64_txHiresFullAlphaChannel.Size = new System.Drawing.Size(141, 17); - this.GLideN64_txHiresFullAlphaChannel.TabIndex = 69; - this.GLideN64_txHiresFullAlphaChannel.Text = "Hires Full Alpha Channel"; - this.GLideN64_txHiresFullAlphaChannel.UseVisualStyleBackColor = true; - // - // GLideN64_txHiresEnable - // - this.GLideN64_txHiresEnable.AutoSize = true; - this.GLideN64_txHiresEnable.Location = new System.Drawing.Point(328, 19); - this.GLideN64_txHiresEnable.Name = "GLideN64_txHiresEnable"; - this.GLideN64_txHiresEnable.Size = new System.Drawing.Size(94, 17); - this.GLideN64_txHiresEnable.TabIndex = 68; - this.GLideN64_txHiresEnable.Text = "Hires Textures"; - this.GLideN64_txHiresEnable.UseVisualStyleBackColor = true; - // - // label86 - // - this.label86.AutoSize = true; - this.label86.Location = new System.Drawing.Point(3, 198); - this.label86.Name = "label86"; - this.label86.Size = new System.Drawing.Size(98, 13); - this.label86.TabIndex = 67; - this.label86.Text = "Filtered cache size:"; - // - // GLideN64_txCacheSize - // - this.GLideN64_txCacheSize.Location = new System.Drawing.Point(123, 195); - this.GLideN64_txCacheSize.Name = "GLideN64_txCacheSize"; - this.GLideN64_txCacheSize.Size = new System.Drawing.Size(78, 20); - this.GLideN64_txCacheSize.TabIndex = 66; - // - // GLideN64_txFilterIgnoreBG - // - this.GLideN64_txFilterIgnoreBG.AutoSize = true; - this.GLideN64_txFilterIgnoreBG.Location = new System.Drawing.Point(6, 172); - this.GLideN64_txFilterIgnoreBG.Name = "GLideN64_txFilterIgnoreBG"; - this.GLideN64_txFilterIgnoreBG.Size = new System.Drawing.Size(99, 17); - this.GLideN64_txFilterIgnoreBG.TabIndex = 65; - this.GLideN64_txFilterIgnoreBG.Text = "Filter Ignore BG"; - this.GLideN64_txFilterIgnoreBG.UseVisualStyleBackColor = true; - // - // GLideN64_txDeposterize - // - this.GLideN64_txDeposterize.AutoSize = true; - this.GLideN64_txDeposterize.Location = new System.Drawing.Point(6, 149); - this.GLideN64_txDeposterize.Name = "GLideN64_txDeposterize"; - this.GLideN64_txDeposterize.Size = new System.Drawing.Size(82, 17); - this.GLideN64_txDeposterize.TabIndex = 64; - this.GLideN64_txDeposterize.Text = "Deposterize"; - this.GLideN64_txDeposterize.UseVisualStyleBackColor = true; - // - // label85 - // - this.label85.AutoSize = true; - this.label85.Location = new System.Drawing.Point(3, 125); - this.label85.Name = "label85"; - this.label85.Size = new System.Drawing.Size(114, 13); - this.label85.TabIndex = 63; - this.label85.Text = "Texture enhancement:"; - // - // GLideN64_txEnhancementMode - // - this.GLideN64_txEnhancementMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_txEnhancementMode.FormattingEnabled = true; - this.GLideN64_txEnhancementMode.Items.AddRange(new object[] { + this.GLideN64_CorrectTexrectCoords.Location = new System.Drawing.Point(128, 111); + this.GLideN64_CorrectTexrectCoords.Name = "GLideN64_CorrectTexrectCoords"; + this.GLideN64_CorrectTexrectCoords.Size = new System.Drawing.Size(138, 21); + this.GLideN64_CorrectTexrectCoords.TabIndex = 54; + // + // GLideN64_ShadersStorage + // + this.GLideN64_ShadersStorage.AutoSize = true; + this.GLideN64_ShadersStorage.Location = new System.Drawing.Point(6, 88); + this.GLideN64_ShadersStorage.Name = "GLideN64_ShadersStorage"; + this.GLideN64_ShadersStorage.Size = new System.Drawing.Size(103, 17); + this.GLideN64_ShadersStorage.TabIndex = 53; + this.GLideN64_ShadersStorage.Text = "Shaders storage"; + this.GLideN64_ShadersStorage.UseVisualStyleBackColor = true; + // + // GLideN64_HWLighting + // + this.GLideN64_HWLighting.AutoSize = true; + this.GLideN64_HWLighting.Location = new System.Drawing.Point(6, 65); + this.GLideN64_HWLighting.Name = "GLideN64_HWLighting"; + this.GLideN64_HWLighting.Size = new System.Drawing.Size(85, 17); + this.GLideN64_HWLighting.TabIndex = 52; + this.GLideN64_HWLighting.Text = "HW Lighting"; + this.GLideN64_HWLighting.UseVisualStyleBackColor = true; + // + // GLideN64_EnableNoise + // + this.GLideN64_EnableNoise.AutoSize = true; + this.GLideN64_EnableNoise.Location = new System.Drawing.Point(6, 42); + this.GLideN64_EnableNoise.Name = "GLideN64_EnableNoise"; + this.GLideN64_EnableNoise.Size = new System.Drawing.Size(126, 17); + this.GLideN64_EnableNoise.TabIndex = 25; + this.GLideN64_EnableNoise.Text = "Color noise emulation"; + this.GLideN64_EnableNoise.UseVisualStyleBackColor = true; + // + // GLideN64_EnableLOD + // + this.GLideN64_EnableLOD.AutoSize = true; + this.GLideN64_EnableLOD.Location = new System.Drawing.Point(6, 19); + this.GLideN64_EnableLOD.Name = "GLideN64_EnableLOD"; + this.GLideN64_EnableLOD.Size = new System.Drawing.Size(97, 17); + this.GLideN64_EnableLOD.TabIndex = 21; + this.GLideN64_EnableLOD.Text = "LOD Emulation"; + this.GLideN64_EnableLOD.UseVisualStyleBackColor = true; + // + // tabPage6 + // + this.tabPage6.Controls.Add(this.label87); + this.tabPage6.Controls.Add(this.GLideN64_txPath); + this.tabPage6.Controls.Add(this.GLideN64_txSaveCache); + this.tabPage6.Controls.Add(this.GLideN64_txCacheCompression); + this.tabPage6.Controls.Add(this.GLideN64_txForce16bpp); + this.tabPage6.Controls.Add(this.GLideN64_txDump); + this.tabPage6.Controls.Add(this.GLideN64_txHresAltCRC); + this.tabPage6.Controls.Add(this.GLideN64_txHiresFullAlphaChannel); + this.tabPage6.Controls.Add(this.GLideN64_txHiresEnable); + this.tabPage6.Controls.Add(this.label86); + this.tabPage6.Controls.Add(this.GLideN64_txCacheSize); + this.tabPage6.Controls.Add(this.GLideN64_txFilterIgnoreBG); + this.tabPage6.Controls.Add(this.GLideN64_txDeposterize); + this.tabPage6.Controls.Add(this.label85); + this.tabPage6.Controls.Add(this.GLideN64_txEnhancementMode); + this.tabPage6.Controls.Add(this.label84); + this.tabPage6.Controls.Add(this.GLideN64_txFilterMode); + this.tabPage6.Controls.Add(this.label83); + this.tabPage6.Controls.Add(this.GLideN64_CacheSize); + this.tabPage6.Controls.Add(this.label72); + this.tabPage6.Controls.Add(this.GLideN64_bilinearMode); + this.tabPage6.Controls.Add(this.GLideN64_MaxAnisotropy); + this.tabPage6.Location = new System.Drawing.Point(4, 22); + this.tabPage6.Name = "tabPage6"; + this.tabPage6.Padding = new System.Windows.Forms.Padding(3); + this.tabPage6.Size = new System.Drawing.Size(564, 310); + this.tabPage6.TabIndex = 1; + this.tabPage6.Text = "Texture settings"; + this.tabPage6.UseVisualStyleBackColor = true; + // + // label87 + // + this.label87.AutoSize = true; + this.label87.Location = new System.Drawing.Point(3, 232); + this.label87.Name = "label87"; + this.label87.Size = new System.Drawing.Size(70, 13); + this.label87.TabIndex = 76; + this.label87.Text = "Texture path:"; + // + // GLideN64_txPath + // + this.GLideN64_txPath.Location = new System.Drawing.Point(123, 229); + this.GLideN64_txPath.Name = "GLideN64_txPath"; + this.GLideN64_txPath.Size = new System.Drawing.Size(78, 20); + this.GLideN64_txPath.TabIndex = 75; + // + // GLideN64_txSaveCache + // + this.GLideN64_txSaveCache.AutoSize = true; + this.GLideN64_txSaveCache.Location = new System.Drawing.Point(328, 157); + this.GLideN64_txSaveCache.Name = "GLideN64_txSaveCache"; + this.GLideN64_txSaveCache.Size = new System.Drawing.Size(119, 17); + this.GLideN64_txSaveCache.TabIndex = 74; + this.GLideN64_txSaveCache.Text = "Save texture cache"; + this.GLideN64_txSaveCache.UseVisualStyleBackColor = true; + // + // GLideN64_txCacheCompression + // + this.GLideN64_txCacheCompression.AutoSize = true; + this.GLideN64_txCacheCompression.Location = new System.Drawing.Point(328, 134); + this.GLideN64_txCacheCompression.Name = "GLideN64_txCacheCompression"; + this.GLideN64_txCacheCompression.Size = new System.Drawing.Size(159, 17); + this.GLideN64_txCacheCompression.TabIndex = 73; + this.GLideN64_txCacheCompression.Text = "Texture Cache Compression"; + this.GLideN64_txCacheCompression.UseVisualStyleBackColor = true; + // + // GLideN64_txForce16bpp + // + this.GLideN64_txForce16bpp.AutoSize = true; + this.GLideN64_txForce16bpp.Location = new System.Drawing.Point(328, 111); + this.GLideN64_txForce16bpp.Name = "GLideN64_txForce16bpp"; + this.GLideN64_txForce16bpp.Size = new System.Drawing.Size(125, 17); + this.GLideN64_txForce16bpp.TabIndex = 72; + this.GLideN64_txForce16bpp.Text = "Texture Force 16bpp"; + this.GLideN64_txForce16bpp.UseVisualStyleBackColor = true; + // + // GLideN64_txDump + // + this.GLideN64_txDump.AutoSize = true; + this.GLideN64_txDump.Location = new System.Drawing.Point(328, 88); + this.GLideN64_txDump.Name = "GLideN64_txDump"; + this.GLideN64_txDump.Size = new System.Drawing.Size(91, 17); + this.GLideN64_txDump.TabIndex = 71; + this.GLideN64_txDump.Text = "Texture dump"; + this.GLideN64_txDump.UseVisualStyleBackColor = true; + // + // GLideN64_txHresAltCRC + // + this.GLideN64_txHresAltCRC.AutoSize = true; + this.GLideN64_txHresAltCRC.Location = new System.Drawing.Point(328, 65); + this.GLideN64_txHresAltCRC.Name = "GLideN64_txHresAltCRC"; + this.GLideN64_txHresAltCRC.Size = new System.Drawing.Size(88, 17); + this.GLideN64_txHresAltCRC.TabIndex = 70; + this.GLideN64_txHresAltCRC.Text = "Hres Alt CRC"; + this.GLideN64_txHresAltCRC.UseVisualStyleBackColor = true; + // + // GLideN64_txHiresFullAlphaChannel + // + this.GLideN64_txHiresFullAlphaChannel.AutoSize = true; + this.GLideN64_txHiresFullAlphaChannel.Location = new System.Drawing.Point(328, 42); + this.GLideN64_txHiresFullAlphaChannel.Name = "GLideN64_txHiresFullAlphaChannel"; + this.GLideN64_txHiresFullAlphaChannel.Size = new System.Drawing.Size(141, 17); + this.GLideN64_txHiresFullAlphaChannel.TabIndex = 69; + this.GLideN64_txHiresFullAlphaChannel.Text = "Hires Full Alpha Channel"; + this.GLideN64_txHiresFullAlphaChannel.UseVisualStyleBackColor = true; + // + // GLideN64_txHiresEnable + // + this.GLideN64_txHiresEnable.AutoSize = true; + this.GLideN64_txHiresEnable.Location = new System.Drawing.Point(328, 19); + this.GLideN64_txHiresEnable.Name = "GLideN64_txHiresEnable"; + this.GLideN64_txHiresEnable.Size = new System.Drawing.Size(94, 17); + this.GLideN64_txHiresEnable.TabIndex = 68; + this.GLideN64_txHiresEnable.Text = "Hires Textures"; + this.GLideN64_txHiresEnable.UseVisualStyleBackColor = true; + // + // label86 + // + this.label86.AutoSize = true; + this.label86.Location = new System.Drawing.Point(3, 198); + this.label86.Name = "label86"; + this.label86.Size = new System.Drawing.Size(98, 13); + this.label86.TabIndex = 67; + this.label86.Text = "Filtered cache size:"; + // + // GLideN64_txCacheSize + // + this.GLideN64_txCacheSize.Location = new System.Drawing.Point(123, 195); + this.GLideN64_txCacheSize.Name = "GLideN64_txCacheSize"; + this.GLideN64_txCacheSize.Size = new System.Drawing.Size(78, 20); + this.GLideN64_txCacheSize.TabIndex = 66; + // + // GLideN64_txFilterIgnoreBG + // + this.GLideN64_txFilterIgnoreBG.AutoSize = true; + this.GLideN64_txFilterIgnoreBG.Location = new System.Drawing.Point(6, 172); + this.GLideN64_txFilterIgnoreBG.Name = "GLideN64_txFilterIgnoreBG"; + this.GLideN64_txFilterIgnoreBG.Size = new System.Drawing.Size(99, 17); + this.GLideN64_txFilterIgnoreBG.TabIndex = 65; + this.GLideN64_txFilterIgnoreBG.Text = "Filter Ignore BG"; + this.GLideN64_txFilterIgnoreBG.UseVisualStyleBackColor = true; + // + // GLideN64_txDeposterize + // + this.GLideN64_txDeposterize.AutoSize = true; + this.GLideN64_txDeposterize.Location = new System.Drawing.Point(6, 149); + this.GLideN64_txDeposterize.Name = "GLideN64_txDeposterize"; + this.GLideN64_txDeposterize.Size = new System.Drawing.Size(82, 17); + this.GLideN64_txDeposterize.TabIndex = 64; + this.GLideN64_txDeposterize.Text = "Deposterize"; + this.GLideN64_txDeposterize.UseVisualStyleBackColor = true; + // + // label85 + // + this.label85.AutoSize = true; + this.label85.Location = new System.Drawing.Point(3, 125); + this.label85.Name = "label85"; + this.label85.Size = new System.Drawing.Size(114, 13); + this.label85.TabIndex = 63; + this.label85.Text = "Texture enhancement:"; + // + // GLideN64_txEnhancementMode + // + this.GLideN64_txEnhancementMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_txEnhancementMode.FormattingEnabled = true; + this.GLideN64_txEnhancementMode.Items.AddRange(new object[] { "None", "Store as is", "X2", @@ -1153,25 +1153,25 @@ "4xBRZ", "5xBRZ", "6xBRZ"}); - this.GLideN64_txEnhancementMode.Location = new System.Drawing.Point(123, 122); - this.GLideN64_txEnhancementMode.Name = "GLideN64_txEnhancementMode"; - this.GLideN64_txEnhancementMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_txEnhancementMode.TabIndex = 62; - // - // label84 - // - this.label84.AutoSize = true; - this.label84.Location = new System.Drawing.Point(3, 98); - this.label84.Name = "label84"; - this.label84.Size = new System.Drawing.Size(68, 13); - this.label84.TabIndex = 61; - this.label84.Text = "Texture filter:"; - // - // GLideN64_txFilterMode - // - this.GLideN64_txFilterMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_txFilterMode.FormattingEnabled = true; - this.GLideN64_txFilterMode.Items.AddRange(new object[] { + this.GLideN64_txEnhancementMode.Location = new System.Drawing.Point(123, 122); + this.GLideN64_txEnhancementMode.Name = "GLideN64_txEnhancementMode"; + this.GLideN64_txEnhancementMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_txEnhancementMode.TabIndex = 62; + // + // label84 + // + this.label84.AutoSize = true; + this.label84.Location = new System.Drawing.Point(3, 98); + this.label84.Name = "label84"; + this.label84.Size = new System.Drawing.Size(68, 13); + this.label84.TabIndex = 61; + this.label84.Text = "Texture filter:"; + // + // GLideN64_txFilterMode + // + this.GLideN64_txFilterMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_txFilterMode.FormattingEnabled = true; + this.GLideN64_txFilterMode.Items.AddRange(new object[] { "None", "Smooth filtering 1", "Smooth filtering 2", @@ -1179,810 +1179,812 @@ "Smooth filtering 4", "Sharp filtering 1", "Sharp filtering 2"}); - this.GLideN64_txFilterMode.Location = new System.Drawing.Point(123, 95); - this.GLideN64_txFilterMode.Name = "GLideN64_txFilterMode"; - this.GLideN64_txFilterMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_txFilterMode.TabIndex = 60; - // - // label83 - // - this.label83.AutoSize = true; - this.label83.Location = new System.Drawing.Point(3, 72); - this.label83.Name = "label83"; - this.label83.Size = new System.Drawing.Size(62, 13); - this.label83.TabIndex = 59; - this.label83.Text = "Cache size:"; - // - // GLideN64_CacheSize - // - this.GLideN64_CacheSize.Location = new System.Drawing.Point(123, 69); - this.GLideN64_CacheSize.Name = "GLideN64_CacheSize"; - this.GLideN64_CacheSize.Size = new System.Drawing.Size(78, 20); - this.GLideN64_CacheSize.TabIndex = 58; - // - // label72 - // - this.label72.AutoSize = true; - this.label72.Location = new System.Drawing.Point(3, 45); - this.label72.Name = "label72"; - this.label72.Size = new System.Drawing.Size(83, 13); - this.label72.TabIndex = 57; - this.label72.Text = "Bilinear Filtering:"; - // - // GLideN64_bilinearMode - // - this.GLideN64_bilinearMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_bilinearMode.FormattingEnabled = true; - this.GLideN64_bilinearMode.Items.AddRange(new object[] { + this.GLideN64_txFilterMode.Location = new System.Drawing.Point(123, 95); + this.GLideN64_txFilterMode.Name = "GLideN64_txFilterMode"; + this.GLideN64_txFilterMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_txFilterMode.TabIndex = 60; + // + // label83 + // + this.label83.AutoSize = true; + this.label83.Enabled = false; + this.label83.Location = new System.Drawing.Point(3, 72); + this.label83.Name = "label83"; + this.label83.Size = new System.Drawing.Size(62, 13); + this.label83.TabIndex = 59; + this.label83.Text = "Cache size:"; + // + // GLideN64_CacheSize + // + this.GLideN64_CacheSize.Enabled = false; + this.GLideN64_CacheSize.Location = new System.Drawing.Point(123, 69); + this.GLideN64_CacheSize.Name = "GLideN64_CacheSize"; + this.GLideN64_CacheSize.Size = new System.Drawing.Size(78, 20); + this.GLideN64_CacheSize.TabIndex = 58; + // + // label72 + // + this.label72.AutoSize = true; + this.label72.Location = new System.Drawing.Point(3, 45); + this.label72.Name = "label72"; + this.label72.Size = new System.Drawing.Size(83, 13); + this.label72.TabIndex = 57; + this.label72.Text = "Bilinear Filtering:"; + // + // GLideN64_bilinearMode + // + this.GLideN64_bilinearMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_bilinearMode.FormattingEnabled = true; + this.GLideN64_bilinearMode.Items.AddRange(new object[] { "N64 3point", "Standard"}); - this.GLideN64_bilinearMode.Location = new System.Drawing.Point(123, 42); - this.GLideN64_bilinearMode.Name = "GLideN64_bilinearMode"; - this.GLideN64_bilinearMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_bilinearMode.TabIndex = 56; - // - // GLideN64_MaxAnisotropy - // - this.GLideN64_MaxAnisotropy.AutoSize = true; - this.GLideN64_MaxAnisotropy.Location = new System.Drawing.Point(6, 19); - this.GLideN64_MaxAnisotropy.Name = "GLideN64_MaxAnisotropy"; - this.GLideN64_MaxAnisotropy.Size = new System.Drawing.Size(98, 17); - this.GLideN64_MaxAnisotropy.TabIndex = 3; - this.GLideN64_MaxAnisotropy.Text = "Max Anisotropy"; - this.GLideN64_MaxAnisotropy.UseVisualStyleBackColor = true; - // - // tabPage7 - // - this.tabPage7.Controls.Add(this.label89); - this.tabPage7.Controls.Add(this.GLideN64_UseNativeResolutionFactor); - this.tabPage7.Controls.Add(this.GLideN64_DisableFBInfo); - this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadDepthChunk); - this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadColorChunk); - this.tabPage7.Controls.Add(this.label76); - this.tabPage7.Controls.Add(this.GLideN64_BufferSwapMode); - this.tabPage7.Controls.Add(this.label75); - this.tabPage7.Controls.Add(this.GLideN64_AspectRatio); - this.tabPage7.Controls.Add(this.GLideN64_EnableN64DepthCompare); - this.tabPage7.Controls.Add(this.label74); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorToRDRAM); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyAuxiliaryToRDRAM); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorFromRDRAM); - this.tabPage7.Controls.Add(this.label73); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyDepthToRDRAM); - this.tabPage7.Controls.Add(this.GLideN64_EnableFBEmulation); - this.tabPage7.Controls.Add(this.GLideN64_UseDefaultHacks); - this.tabPage7.Location = new System.Drawing.Point(4, 22); - this.tabPage7.Name = "tabPage7"; - this.tabPage7.Size = new System.Drawing.Size(564, 310); - this.tabPage7.TabIndex = 2; - this.tabPage7.Text = "Frame buffer settings"; - this.tabPage7.UseVisualStyleBackColor = true; - // - // label89 - // - this.label89.AutoSize = true; - this.label89.Location = new System.Drawing.Point(3, 45); - this.label89.Name = "label89"; - this.label89.Size = new System.Drawing.Size(146, 13); - this.label89.TabIndex = 78; - this.label89.Text = "Use Native Resolution Factor"; - // - // GLideN64_UseNativeResolutionFactor - // - this.GLideN64_UseNativeResolutionFactor.Location = new System.Drawing.Point(155, 42); - this.GLideN64_UseNativeResolutionFactor.Name = "GLideN64_UseNativeResolutionFactor"; - this.GLideN64_UseNativeResolutionFactor.Size = new System.Drawing.Size(32, 20); - this.GLideN64_UseNativeResolutionFactor.TabIndex = 77; - // - // GLideN64_DisableFBInfo - // - this.GLideN64_DisableFBInfo.AutoSize = true; - this.GLideN64_DisableFBInfo.Location = new System.Drawing.Point(6, 115); - this.GLideN64_DisableFBInfo.Name = "GLideN64_DisableFBInfo"; - this.GLideN64_DisableFBInfo.Size = new System.Drawing.Size(98, 17); - this.GLideN64_DisableFBInfo.TabIndex = 76; - this.GLideN64_DisableFBInfo.Text = "Disable FB Info"; - this.GLideN64_DisableFBInfo.UseVisualStyleBackColor = true; - // - // GLideN64_FBInfoReadDepthChunk - // - this.GLideN64_FBInfoReadDepthChunk.AutoSize = true; - this.GLideN64_FBInfoReadDepthChunk.Location = new System.Drawing.Point(6, 92); - this.GLideN64_FBInfoReadDepthChunk.Name = "GLideN64_FBInfoReadDepthChunk"; - this.GLideN64_FBInfoReadDepthChunk.Size = new System.Drawing.Size(155, 17); - this.GLideN64_FBInfoReadDepthChunk.TabIndex = 75; - this.GLideN64_FBInfoReadDepthChunk.Text = "FB Info Read Depth Chunk"; - this.GLideN64_FBInfoReadDepthChunk.UseVisualStyleBackColor = true; - // - // GLideN64_FBInfoReadColorChunk - // - this.GLideN64_FBInfoReadColorChunk.AutoSize = true; - this.GLideN64_FBInfoReadColorChunk.Location = new System.Drawing.Point(6, 69); - this.GLideN64_FBInfoReadColorChunk.Name = "GLideN64_FBInfoReadColorChunk"; - this.GLideN64_FBInfoReadColorChunk.Size = new System.Drawing.Size(150, 17); - this.GLideN64_FBInfoReadColorChunk.TabIndex = 74; - this.GLideN64_FBInfoReadColorChunk.Text = "FB Info Read Color Chunk"; - this.GLideN64_FBInfoReadColorChunk.UseVisualStyleBackColor = true; - // - // label76 - // - this.label76.AutoSize = true; - this.label76.Location = new System.Drawing.Point(3, 171); - this.label76.Name = "label76"; - this.label76.Size = new System.Drawing.Size(95, 13); - this.label76.TabIndex = 72; - this.label76.Text = "Buffer Swap Mode"; - // - // GLideN64_BufferSwapMode - // - this.GLideN64_BufferSwapMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_BufferSwapMode.FormattingEnabled = true; - this.GLideN64_BufferSwapMode.Items.AddRange(new object[] { + this.GLideN64_bilinearMode.Location = new System.Drawing.Point(123, 42); + this.GLideN64_bilinearMode.Name = "GLideN64_bilinearMode"; + this.GLideN64_bilinearMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_bilinearMode.TabIndex = 56; + // + // GLideN64_MaxAnisotropy + // + this.GLideN64_MaxAnisotropy.AutoSize = true; + this.GLideN64_MaxAnisotropy.Location = new System.Drawing.Point(6, 19); + this.GLideN64_MaxAnisotropy.Name = "GLideN64_MaxAnisotropy"; + this.GLideN64_MaxAnisotropy.Size = new System.Drawing.Size(98, 17); + this.GLideN64_MaxAnisotropy.TabIndex = 3; + this.GLideN64_MaxAnisotropy.Text = "Max Anisotropy"; + this.GLideN64_MaxAnisotropy.UseVisualStyleBackColor = true; + // + // tabPage7 + // + this.tabPage7.Controls.Add(this.label89); + this.tabPage7.Controls.Add(this.GLideN64_UseNativeResolutionFactor); + this.tabPage7.Controls.Add(this.GLideN64_DisableFBInfo); + this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadDepthChunk); + this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadColorChunk); + this.tabPage7.Controls.Add(this.label76); + this.tabPage7.Controls.Add(this.GLideN64_BufferSwapMode); + this.tabPage7.Controls.Add(this.label75); + this.tabPage7.Controls.Add(this.GLideN64_AspectRatio); + this.tabPage7.Controls.Add(this.GLideN64_EnableN64DepthCompare); + this.tabPage7.Controls.Add(this.label74); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorToRDRAM); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyAuxiliaryToRDRAM); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorFromRDRAM); + this.tabPage7.Controls.Add(this.label73); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyDepthToRDRAM); + this.tabPage7.Controls.Add(this.GLideN64_EnableFBEmulation); + this.tabPage7.Controls.Add(this.GLideN64_UseDefaultHacks); + this.tabPage7.Location = new System.Drawing.Point(4, 22); + this.tabPage7.Name = "tabPage7"; + this.tabPage7.Size = new System.Drawing.Size(564, 310); + this.tabPage7.TabIndex = 2; + this.tabPage7.Text = "Frame buffer settings"; + this.tabPage7.UseVisualStyleBackColor = true; + // + // label89 + // + this.label89.AutoSize = true; + this.label89.Location = new System.Drawing.Point(3, 45); + this.label89.Name = "label89"; + this.label89.Size = new System.Drawing.Size(146, 13); + this.label89.TabIndex = 78; + this.label89.Text = "Use Native Resolution Factor"; + // + // GLideN64_UseNativeResolutionFactor + // + this.GLideN64_UseNativeResolutionFactor.Location = new System.Drawing.Point(155, 42); + this.GLideN64_UseNativeResolutionFactor.Name = "GLideN64_UseNativeResolutionFactor"; + this.GLideN64_UseNativeResolutionFactor.Size = new System.Drawing.Size(32, 20); + this.GLideN64_UseNativeResolutionFactor.TabIndex = 77; + // + // GLideN64_DisableFBInfo + // + this.GLideN64_DisableFBInfo.AutoSize = true; + this.GLideN64_DisableFBInfo.Location = new System.Drawing.Point(6, 115); + this.GLideN64_DisableFBInfo.Name = "GLideN64_DisableFBInfo"; + this.GLideN64_DisableFBInfo.Size = new System.Drawing.Size(98, 17); + this.GLideN64_DisableFBInfo.TabIndex = 76; + this.GLideN64_DisableFBInfo.Text = "Disable FB Info"; + this.GLideN64_DisableFBInfo.UseVisualStyleBackColor = true; + // + // GLideN64_FBInfoReadDepthChunk + // + this.GLideN64_FBInfoReadDepthChunk.AutoSize = true; + this.GLideN64_FBInfoReadDepthChunk.Location = new System.Drawing.Point(6, 92); + this.GLideN64_FBInfoReadDepthChunk.Name = "GLideN64_FBInfoReadDepthChunk"; + this.GLideN64_FBInfoReadDepthChunk.Size = new System.Drawing.Size(155, 17); + this.GLideN64_FBInfoReadDepthChunk.TabIndex = 75; + this.GLideN64_FBInfoReadDepthChunk.Text = "FB Info Read Depth Chunk"; + this.GLideN64_FBInfoReadDepthChunk.UseVisualStyleBackColor = true; + // + // GLideN64_FBInfoReadColorChunk + // + this.GLideN64_FBInfoReadColorChunk.AutoSize = true; + this.GLideN64_FBInfoReadColorChunk.Location = new System.Drawing.Point(6, 69); + this.GLideN64_FBInfoReadColorChunk.Name = "GLideN64_FBInfoReadColorChunk"; + this.GLideN64_FBInfoReadColorChunk.Size = new System.Drawing.Size(150, 17); + this.GLideN64_FBInfoReadColorChunk.TabIndex = 74; + this.GLideN64_FBInfoReadColorChunk.Text = "FB Info Read Color Chunk"; + this.GLideN64_FBInfoReadColorChunk.UseVisualStyleBackColor = true; + // + // label76 + // + this.label76.AutoSize = true; + this.label76.Location = new System.Drawing.Point(3, 171); + this.label76.Name = "label76"; + this.label76.Size = new System.Drawing.Size(95, 13); + this.label76.TabIndex = 72; + this.label76.Text = "Buffer Swap Mode"; + // + // GLideN64_BufferSwapMode + // + this.GLideN64_BufferSwapMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_BufferSwapMode.FormattingEnabled = true; + this.GLideN64_BufferSwapMode.Items.AddRange(new object[] { "On VI update call", "On VI origin change", "On buffer update"}); - this.GLideN64_BufferSwapMode.Location = new System.Drawing.Point(134, 168); - this.GLideN64_BufferSwapMode.Name = "GLideN64_BufferSwapMode"; - this.GLideN64_BufferSwapMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_BufferSwapMode.TabIndex = 71; - // - // label75 - // - this.label75.AutoSize = true; - this.label75.Location = new System.Drawing.Point(3, 144); - this.label75.Name = "label75"; - this.label75.Size = new System.Drawing.Size(68, 13); - this.label75.TabIndex = 70; - this.label75.Text = "Aspect Ratio"; - // - // GLideN64_AspectRatio - // - this.GLideN64_AspectRatio.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_AspectRatio.FormattingEnabled = true; - this.GLideN64_AspectRatio.Items.AddRange(new object[] { + this.GLideN64_BufferSwapMode.Location = new System.Drawing.Point(134, 168); + this.GLideN64_BufferSwapMode.Name = "GLideN64_BufferSwapMode"; + this.GLideN64_BufferSwapMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_BufferSwapMode.TabIndex = 71; + // + // label75 + // + this.label75.AutoSize = true; + this.label75.Location = new System.Drawing.Point(3, 144); + this.label75.Name = "label75"; + this.label75.Size = new System.Drawing.Size(68, 13); + this.label75.TabIndex = 70; + this.label75.Text = "Aspect Ratio"; + // + // GLideN64_AspectRatio + // + this.GLideN64_AspectRatio.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_AspectRatio.FormattingEnabled = true; + this.GLideN64_AspectRatio.Items.AddRange(new object[] { "Stretch", "Force 4:3", "Force 16:9", "Adjust"}); - this.GLideN64_AspectRatio.Location = new System.Drawing.Point(134, 141); - this.GLideN64_AspectRatio.Name = "GLideN64_AspectRatio"; - this.GLideN64_AspectRatio.Size = new System.Drawing.Size(138, 21); - this.GLideN64_AspectRatio.TabIndex = 69; - // - // GLideN64_EnableN64DepthCompare - // - this.GLideN64_EnableN64DepthCompare.AutoSize = true; - this.GLideN64_EnableN64DepthCompare.Location = new System.Drawing.Point(293, 142); - this.GLideN64_EnableN64DepthCompare.Name = "GLideN64_EnableN64DepthCompare"; - this.GLideN64_EnableN64DepthCompare.Size = new System.Drawing.Size(123, 17); - this.GLideN64_EnableN64DepthCompare.TabIndex = 68; - this.GLideN64_EnableN64DepthCompare.Text = "N64 Depth Compare"; - this.GLideN64_EnableN64DepthCompare.UseVisualStyleBackColor = true; - // - // label74 - // - this.label74.AutoSize = true; - this.label74.Location = new System.Drawing.Point(290, 118); - this.label74.Name = "label74"; - this.label74.Size = new System.Drawing.Size(120, 13); - this.label74.TabIndex = 67; - this.label74.Text = "Copy Color To RDRAM:"; - // - // GLideN64_EnableCopyColorToRDRAM - // - this.GLideN64_EnableCopyColorToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_EnableCopyColorToRDRAM.FormattingEnabled = true; - this.GLideN64_EnableCopyColorToRDRAM.Items.AddRange(new object[] { + this.GLideN64_AspectRatio.Location = new System.Drawing.Point(134, 141); + this.GLideN64_AspectRatio.Name = "GLideN64_AspectRatio"; + this.GLideN64_AspectRatio.Size = new System.Drawing.Size(138, 21); + this.GLideN64_AspectRatio.TabIndex = 69; + // + // GLideN64_EnableN64DepthCompare + // + this.GLideN64_EnableN64DepthCompare.AutoSize = true; + this.GLideN64_EnableN64DepthCompare.Location = new System.Drawing.Point(293, 142); + this.GLideN64_EnableN64DepthCompare.Name = "GLideN64_EnableN64DepthCompare"; + this.GLideN64_EnableN64DepthCompare.Size = new System.Drawing.Size(123, 17); + this.GLideN64_EnableN64DepthCompare.TabIndex = 68; + this.GLideN64_EnableN64DepthCompare.Text = "N64 Depth Compare"; + this.GLideN64_EnableN64DepthCompare.UseVisualStyleBackColor = true; + // + // label74 + // + this.label74.AutoSize = true; + this.label74.Location = new System.Drawing.Point(290, 118); + this.label74.Name = "label74"; + this.label74.Size = new System.Drawing.Size(120, 13); + this.label74.TabIndex = 67; + this.label74.Text = "Copy Color To RDRAM:"; + // + // GLideN64_EnableCopyColorToRDRAM + // + this.GLideN64_EnableCopyColorToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_EnableCopyColorToRDRAM.FormattingEnabled = true; + this.GLideN64_EnableCopyColorToRDRAM.Items.AddRange(new object[] { "Do not copy", "Copy in sync mode", "Copy in async mode"}); - this.GLideN64_EnableCopyColorToRDRAM.Location = new System.Drawing.Point(421, 115); - this.GLideN64_EnableCopyColorToRDRAM.Name = "GLideN64_EnableCopyColorToRDRAM"; - this.GLideN64_EnableCopyColorToRDRAM.Size = new System.Drawing.Size(138, 21); - this.GLideN64_EnableCopyColorToRDRAM.TabIndex = 66; - // - // GLideN64_EnableCopyAuxiliaryToRDRAM - // - this.GLideN64_EnableCopyAuxiliaryToRDRAM.AutoSize = true; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Location = new System.Drawing.Point(293, 92); - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Name = "GLideN64_EnableCopyAuxiliaryToRDRAM"; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Size = new System.Drawing.Size(150, 17); - this.GLideN64_EnableCopyAuxiliaryToRDRAM.TabIndex = 65; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Text = "Copy Auxiliary To RDRAM"; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.UseVisualStyleBackColor = true; - // - // GLideN64_EnableCopyColorFromRDRAM - // - this.GLideN64_EnableCopyColorFromRDRAM.AutoSize = true; - this.GLideN64_EnableCopyColorFromRDRAM.Location = new System.Drawing.Point(293, 69); - this.GLideN64_EnableCopyColorFromRDRAM.Name = "GLideN64_EnableCopyColorFromRDRAM"; - this.GLideN64_EnableCopyColorFromRDRAM.Size = new System.Drawing.Size(146, 17); - this.GLideN64_EnableCopyColorFromRDRAM.TabIndex = 64; - this.GLideN64_EnableCopyColorFromRDRAM.Text = "Copy Color From RDRAM"; - this.GLideN64_EnableCopyColorFromRDRAM.UseVisualStyleBackColor = true; - // - // label73 - // - this.label73.AutoSize = true; - this.label73.Location = new System.Drawing.Point(290, 45); - this.label73.Name = "label73"; - this.label73.Size = new System.Drawing.Size(125, 13); - this.label73.TabIndex = 63; - this.label73.Text = "Copy Depth To RDRAM:"; - // - // GLideN64_EnableCopyDepthToRDRAM - // - this.GLideN64_EnableCopyDepthToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_EnableCopyDepthToRDRAM.FormattingEnabled = true; - this.GLideN64_EnableCopyDepthToRDRAM.Items.AddRange(new object[] { + this.GLideN64_EnableCopyColorToRDRAM.Location = new System.Drawing.Point(421, 115); + this.GLideN64_EnableCopyColorToRDRAM.Name = "GLideN64_EnableCopyColorToRDRAM"; + this.GLideN64_EnableCopyColorToRDRAM.Size = new System.Drawing.Size(138, 21); + this.GLideN64_EnableCopyColorToRDRAM.TabIndex = 66; + // + // GLideN64_EnableCopyAuxiliaryToRDRAM + // + this.GLideN64_EnableCopyAuxiliaryToRDRAM.AutoSize = true; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Location = new System.Drawing.Point(293, 92); + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Name = "GLideN64_EnableCopyAuxiliaryToRDRAM"; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Size = new System.Drawing.Size(150, 17); + this.GLideN64_EnableCopyAuxiliaryToRDRAM.TabIndex = 65; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Text = "Copy Auxiliary To RDRAM"; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.UseVisualStyleBackColor = true; + // + // GLideN64_EnableCopyColorFromRDRAM + // + this.GLideN64_EnableCopyColorFromRDRAM.AutoSize = true; + this.GLideN64_EnableCopyColorFromRDRAM.Location = new System.Drawing.Point(293, 69); + this.GLideN64_EnableCopyColorFromRDRAM.Name = "GLideN64_EnableCopyColorFromRDRAM"; + this.GLideN64_EnableCopyColorFromRDRAM.Size = new System.Drawing.Size(146, 17); + this.GLideN64_EnableCopyColorFromRDRAM.TabIndex = 64; + this.GLideN64_EnableCopyColorFromRDRAM.Text = "Copy Color From RDRAM"; + this.GLideN64_EnableCopyColorFromRDRAM.UseVisualStyleBackColor = true; + // + // label73 + // + this.label73.AutoSize = true; + this.label73.Location = new System.Drawing.Point(290, 45); + this.label73.Name = "label73"; + this.label73.Size = new System.Drawing.Size(125, 13); + this.label73.TabIndex = 63; + this.label73.Text = "Copy Depth To RDRAM:"; + // + // GLideN64_EnableCopyDepthToRDRAM + // + this.GLideN64_EnableCopyDepthToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_EnableCopyDepthToRDRAM.FormattingEnabled = true; + this.GLideN64_EnableCopyDepthToRDRAM.Items.AddRange(new object[] { "Do not copy", "Copy from video memory", "Use software render"}); - this.GLideN64_EnableCopyDepthToRDRAM.Location = new System.Drawing.Point(421, 42); - this.GLideN64_EnableCopyDepthToRDRAM.Name = "GLideN64_EnableCopyDepthToRDRAM"; - this.GLideN64_EnableCopyDepthToRDRAM.Size = new System.Drawing.Size(138, 21); - this.GLideN64_EnableCopyDepthToRDRAM.TabIndex = 62; - // - // GLideN64_EnableFBEmulation - // - this.GLideN64_EnableFBEmulation.AutoSize = true; - this.GLideN64_EnableFBEmulation.Location = new System.Drawing.Point(6, 19); - this.GLideN64_EnableFBEmulation.Name = "GLideN64_EnableFBEmulation"; - this.GLideN64_EnableFBEmulation.Size = new System.Drawing.Size(124, 17); - this.GLideN64_EnableFBEmulation.TabIndex = 61; - this.GLideN64_EnableFBEmulation.Text = "Enable FB Emulation"; - this.GLideN64_EnableFBEmulation.UseVisualStyleBackColor = true; - // - // GLideN64_UseDefaultHacks - // - this.GLideN64_UseDefaultHacks.AutoSize = true; - this.GLideN64_UseDefaultHacks.Location = new System.Drawing.Point(293, 19); - this.GLideN64_UseDefaultHacks.Name = "GLideN64_UseDefaultHacks"; - this.GLideN64_UseDefaultHacks.Size = new System.Drawing.Size(165, 17); - this.GLideN64_UseDefaultHacks.TabIndex = 3; - this.GLideN64_UseDefaultHacks.Text = "Use defaults for current game"; - this.GLideN64_UseDefaultHacks.UseVisualStyleBackColor = true; - this.GLideN64_UseDefaultHacks.CheckedChanged += new System.EventHandler(this.GLideN64_UseDefaultHacks_CheckedChanged); - // - // Glide64mk2TabPage - // - this.Glide64mk2TabPage.Controls.Add(this.tabControl2); - this.Glide64mk2TabPage.Location = new System.Drawing.Point(4, 22); - this.Glide64mk2TabPage.Name = "Glide64mk2TabPage"; - this.Glide64mk2TabPage.Size = new System.Drawing.Size(572, 343); - this.Glide64mk2TabPage.TabIndex = 6; - this.Glide64mk2TabPage.Text = "Glide64mk2"; - this.Glide64mk2TabPage.UseVisualStyleBackColor = true; - // - // tabControl2 - // - this.tabControl2.Controls.Add(this.tabPage1); - this.tabControl2.Controls.Add(this.tabPage2); - this.tabControl2.Controls.Add(this.tabPage3); - this.tabControl2.Location = new System.Drawing.Point(0, 3); - this.tabControl2.Name = "tabControl2"; - this.tabControl2.SelectedIndex = 0; - this.tabControl2.Size = new System.Drawing.Size(572, 336); - this.tabControl2.TabIndex = 1; - // - // tabPage1 - // - this.tabPage1.Controls.Add(this.Glide64mk2_fb_get_info); - this.tabPage1.Controls.Add(this.Glide64mk2_fb_render); - this.tabPage1.Controls.Add(this.Glide64mk2_wrpAnisotropic); - this.tabPage1.Controls.Add(this.Glide64mk2_wrpFBO); - this.tabPage1.Controls.Add(this.label50); - this.tabPage1.Controls.Add(this.Glide64mk2_card_id); - this.tabPage1.Location = new System.Drawing.Point(4, 22); - this.tabPage1.Name = "tabPage1"; - this.tabPage1.Padding = new System.Windows.Forms.Padding(3); - this.tabPage1.Size = new System.Drawing.Size(564, 310); - this.tabPage1.TabIndex = 0; - this.tabPage1.Text = "General"; - this.tabPage1.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_get_info - // - this.Glide64mk2_fb_get_info.AutoSize = true; - this.Glide64mk2_fb_get_info.Location = new System.Drawing.Point(9, 132); - this.Glide64mk2_fb_get_info.Name = "Glide64mk2_fb_get_info"; - this.Glide64mk2_fb_get_info.Size = new System.Drawing.Size(119, 17); - this.Glide64mk2_fb_get_info.TabIndex = 53; - this.Glide64mk2_fb_get_info.Text = "Get framebuffer info"; - this.Glide64mk2_fb_get_info.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_render - // - this.Glide64mk2_fb_render.AutoSize = true; - this.Glide64mk2_fb_render.Location = new System.Drawing.Point(9, 109); - this.Glide64mk2_fb_render.Name = "Glide64mk2_fb_render"; - this.Glide64mk2_fb_render.Size = new System.Drawing.Size(118, 17); - this.Glide64mk2_fb_render.TabIndex = 52; - this.Glide64mk2_fb_render.Text = "Depth buffer render"; - this.Glide64mk2_fb_render.UseVisualStyleBackColor = true; - // - // Glide64mk2_wrpAnisotropic - // - this.Glide64mk2_wrpAnisotropic.AutoSize = true; - this.Glide64mk2_wrpAnisotropic.Location = new System.Drawing.Point(9, 86); - this.Glide64mk2_wrpAnisotropic.Name = "Glide64mk2_wrpAnisotropic"; - this.Glide64mk2_wrpAnisotropic.Size = new System.Drawing.Size(161, 17); - this.Glide64mk2_wrpAnisotropic.TabIndex = 25; - this.Glide64mk2_wrpAnisotropic.Text = "Wrapper Anisotropic Filtering"; - this.Glide64mk2_wrpAnisotropic.UseVisualStyleBackColor = true; - // - // Glide64mk2_wrpFBO - // - this.Glide64mk2_wrpFBO.AutoSize = true; - this.Glide64mk2_wrpFBO.Location = new System.Drawing.Point(9, 63); - this.Glide64mk2_wrpFBO.Name = "Glide64mk2_wrpFBO"; - this.Glide64mk2_wrpFBO.Size = new System.Drawing.Size(91, 17); - this.Glide64mk2_wrpFBO.TabIndex = 21; - this.Glide64mk2_wrpFBO.Text = "Wrapper FBO"; - this.Glide64mk2_wrpFBO.UseVisualStyleBackColor = true; - // - // label50 - // - this.label50.AutoSize = true; - this.label50.Location = new System.Drawing.Point(6, 30); - this.label50.Name = "label50"; - this.label50.Size = new System.Drawing.Size(46, 13); - this.label50.TabIndex = 5; - this.label50.Text = "Card ID:"; - // - // Glide64mk2_card_id - // - this.Glide64mk2_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_card_id.FormattingEnabled = true; - this.Glide64mk2_card_id.Items.AddRange(new object[] { + this.GLideN64_EnableCopyDepthToRDRAM.Location = new System.Drawing.Point(421, 42); + this.GLideN64_EnableCopyDepthToRDRAM.Name = "GLideN64_EnableCopyDepthToRDRAM"; + this.GLideN64_EnableCopyDepthToRDRAM.Size = new System.Drawing.Size(138, 21); + this.GLideN64_EnableCopyDepthToRDRAM.TabIndex = 62; + // + // GLideN64_EnableFBEmulation + // + this.GLideN64_EnableFBEmulation.AutoSize = true; + this.GLideN64_EnableFBEmulation.Location = new System.Drawing.Point(6, 19); + this.GLideN64_EnableFBEmulation.Name = "GLideN64_EnableFBEmulation"; + this.GLideN64_EnableFBEmulation.Size = new System.Drawing.Size(124, 17); + this.GLideN64_EnableFBEmulation.TabIndex = 61; + this.GLideN64_EnableFBEmulation.Text = "Enable FB Emulation"; + this.GLideN64_EnableFBEmulation.UseVisualStyleBackColor = true; + // + // GLideN64_UseDefaultHacks + // + this.GLideN64_UseDefaultHacks.AutoSize = true; + this.GLideN64_UseDefaultHacks.Location = new System.Drawing.Point(293, 19); + this.GLideN64_UseDefaultHacks.Name = "GLideN64_UseDefaultHacks"; + this.GLideN64_UseDefaultHacks.Size = new System.Drawing.Size(165, 17); + this.GLideN64_UseDefaultHacks.TabIndex = 3; + this.GLideN64_UseDefaultHacks.Text = "Use defaults for current game"; + this.GLideN64_UseDefaultHacks.UseVisualStyleBackColor = true; + this.GLideN64_UseDefaultHacks.CheckedChanged += new System.EventHandler(this.GLideN64_UseDefaultHacks_CheckedChanged); + // + // Glide64mk2TabPage + // + this.Glide64mk2TabPage.Controls.Add(this.tabControl2); + this.Glide64mk2TabPage.Location = new System.Drawing.Point(4, 22); + this.Glide64mk2TabPage.Name = "Glide64mk2TabPage"; + this.Glide64mk2TabPage.Size = new System.Drawing.Size(572, 343); + this.Glide64mk2TabPage.TabIndex = 6; + this.Glide64mk2TabPage.Text = "Glide64mk2"; + this.Glide64mk2TabPage.UseVisualStyleBackColor = true; + // + // tabControl2 + // + this.tabControl2.Controls.Add(this.tabPage1); + this.tabControl2.Controls.Add(this.tabPage2); + this.tabControl2.Controls.Add(this.tabPage3); + this.tabControl2.Location = new System.Drawing.Point(0, 3); + this.tabControl2.Name = "tabControl2"; + this.tabControl2.SelectedIndex = 0; + this.tabControl2.Size = new System.Drawing.Size(572, 336); + this.tabControl2.TabIndex = 1; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.Glide64mk2_fb_get_info); + this.tabPage1.Controls.Add(this.Glide64mk2_fb_render); + this.tabPage1.Controls.Add(this.Glide64mk2_wrpAnisotropic); + this.tabPage1.Controls.Add(this.Glide64mk2_wrpFBO); + this.tabPage1.Controls.Add(this.label50); + this.tabPage1.Controls.Add(this.Glide64mk2_card_id); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(564, 310); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "General"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_get_info + // + this.Glide64mk2_fb_get_info.AutoSize = true; + this.Glide64mk2_fb_get_info.Location = new System.Drawing.Point(9, 132); + this.Glide64mk2_fb_get_info.Name = "Glide64mk2_fb_get_info"; + this.Glide64mk2_fb_get_info.Size = new System.Drawing.Size(119, 17); + this.Glide64mk2_fb_get_info.TabIndex = 53; + this.Glide64mk2_fb_get_info.Text = "Get framebuffer info"; + this.Glide64mk2_fb_get_info.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_render + // + this.Glide64mk2_fb_render.AutoSize = true; + this.Glide64mk2_fb_render.Location = new System.Drawing.Point(9, 109); + this.Glide64mk2_fb_render.Name = "Glide64mk2_fb_render"; + this.Glide64mk2_fb_render.Size = new System.Drawing.Size(118, 17); + this.Glide64mk2_fb_render.TabIndex = 52; + this.Glide64mk2_fb_render.Text = "Depth buffer render"; + this.Glide64mk2_fb_render.UseVisualStyleBackColor = true; + // + // Glide64mk2_wrpAnisotropic + // + this.Glide64mk2_wrpAnisotropic.AutoSize = true; + this.Glide64mk2_wrpAnisotropic.Location = new System.Drawing.Point(9, 86); + this.Glide64mk2_wrpAnisotropic.Name = "Glide64mk2_wrpAnisotropic"; + this.Glide64mk2_wrpAnisotropic.Size = new System.Drawing.Size(161, 17); + this.Glide64mk2_wrpAnisotropic.TabIndex = 25; + this.Glide64mk2_wrpAnisotropic.Text = "Wrapper Anisotropic Filtering"; + this.Glide64mk2_wrpAnisotropic.UseVisualStyleBackColor = true; + // + // Glide64mk2_wrpFBO + // + this.Glide64mk2_wrpFBO.AutoSize = true; + this.Glide64mk2_wrpFBO.Location = new System.Drawing.Point(9, 63); + this.Glide64mk2_wrpFBO.Name = "Glide64mk2_wrpFBO"; + this.Glide64mk2_wrpFBO.Size = new System.Drawing.Size(91, 17); + this.Glide64mk2_wrpFBO.TabIndex = 21; + this.Glide64mk2_wrpFBO.Text = "Wrapper FBO"; + this.Glide64mk2_wrpFBO.UseVisualStyleBackColor = true; + // + // label50 + // + this.label50.AutoSize = true; + this.label50.Location = new System.Drawing.Point(6, 30); + this.label50.Name = "label50"; + this.label50.Size = new System.Drawing.Size(46, 13); + this.label50.TabIndex = 5; + this.label50.Text = "Card ID:"; + // + // Glide64mk2_card_id + // + this.Glide64mk2_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_card_id.FormattingEnabled = true; + this.Glide64mk2_card_id.Items.AddRange(new object[] { "0", "1", "2", "3"}); - this.Glide64mk2_card_id.Location = new System.Drawing.Point(58, 27); - this.Glide64mk2_card_id.Name = "Glide64mk2_card_id"; - this.Glide64mk2_card_id.Size = new System.Drawing.Size(36, 21); - this.Glide64mk2_card_id.TabIndex = 4; - // - // tabPage2 - // - this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_always); - this.tabPage2.Controls.Add(this.Glide64mk2_useless_is_useless); - this.tabPage2.Controls.Add(this.Glide64mk2_n64_z_scale); - this.tabPage2.Controls.Add(this.Glide64mk2_old_style_adither); - this.tabPage2.Controls.Add(this.Glide64mk2_zmode_compare_less); - this.tabPage2.Controls.Add(this.Glide64mk2_adjust_aspect); - this.tabPage2.Controls.Add(this.Glide64mk2_fast_crc); - this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmax); - this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmin); - this.tabPage2.Controls.Add(this.Glide64mk2_force_quad3d); - this.tabPage2.Controls.Add(this.Glide64mk2_pal230); - this.tabPage2.Controls.Add(this.Glide64mk2_texture_correction); - this.tabPage2.Controls.Add(this.Glide64mk2_correct_viewport); - this.tabPage2.Controls.Add(this.Glide64mk2_force_calc_sphere); - this.tabPage2.Controls.Add(this.Glide64mk2_use_sts1_only); - this.tabPage2.Controls.Add(this.Glide64mk2_optimize_texrect); - this.tabPage2.Controls.Add(this.Glide64mk2_increase_texrect_edge); - this.tabPage2.Controls.Add(this.Glide64mk2_ignore_aux_copy); - this.tabPage2.Controls.Add(this.Glide64mk2_hires_buf_clear); - this.tabPage2.Controls.Add(this.Glide64mk2_force_microcheck); - this.tabPage2.Controls.Add(this.Glide64mk2_fog); - this.tabPage2.Controls.Add(this.Glide64mk2_fb_smart); - this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_alpha); - this.tabPage2.Controls.Add(this.Glide64mk2_fb_hires); - this.tabPage2.Controls.Add(this.Glide64mk2_detect_cpu_write); - this.tabPage2.Controls.Add(this.Glide64mk2_decrease_fillrect_edge); - this.tabPage2.Controls.Add(this.Glide64mk2_buff_clear); - this.tabPage2.Controls.Add(this.Glide64mk2_alt_tex_size); - this.tabPage2.Controls.Add(this.Glide64mk2_UseDefaultHacks1); - this.tabPage2.Location = new System.Drawing.Point(4, 22); - this.tabPage2.Name = "tabPage2"; - this.tabPage2.Padding = new System.Windows.Forms.Padding(3); - this.tabPage2.Size = new System.Drawing.Size(564, 310); - this.tabPage2.TabIndex = 1; - this.tabPage2.Text = "Per Game Settings"; - this.tabPage2.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_read_always - // - this.Glide64mk2_fb_read_always.AutoSize = true; - this.Glide64mk2_fb_read_always.Location = new System.Drawing.Point(362, 39); - this.Glide64mk2_fb_read_always.Name = "Glide64mk2_fb_read_always"; - this.Glide64mk2_fb_read_always.Size = new System.Drawing.Size(164, 17); - this.Glide64mk2_fb_read_always.TabIndex = 48; - this.Glide64mk2_fb_read_always.Text = "Framebuffer read every frame"; - this.Glide64mk2_fb_read_always.UseVisualStyleBackColor = true; - // - // Glide64mk2_useless_is_useless - // - this.Glide64mk2_useless_is_useless.AutoSize = true; - this.Glide64mk2_useless_is_useless.Location = new System.Drawing.Point(362, 62); - this.Glide64mk2_useless_is_useless.Name = "Glide64mk2_useless_is_useless"; - this.Glide64mk2_useless_is_useless.Size = new System.Drawing.Size(115, 17); - this.Glide64mk2_useless_is_useless.TabIndex = 46; - this.Glide64mk2_useless_is_useless.Text = "useless_is_useless"; - this.Glide64mk2_useless_is_useless.UseVisualStyleBackColor = true; - // - // Glide64mk2_n64_z_scale - // - this.Glide64mk2_n64_z_scale.AutoSize = true; - this.Glide64mk2_n64_z_scale.Location = new System.Drawing.Point(362, 154); - this.Glide64mk2_n64_z_scale.Name = "Glide64mk2_n64_z_scale"; - this.Glide64mk2_n64_z_scale.Size = new System.Drawing.Size(86, 17); - this.Glide64mk2_n64_z_scale.TabIndex = 45; - this.Glide64mk2_n64_z_scale.Text = "N64 Z Scale"; - this.Glide64mk2_n64_z_scale.UseVisualStyleBackColor = true; - // - // Glide64mk2_old_style_adither - // - this.Glide64mk2_old_style_adither.AutoSize = true; - this.Glide64mk2_old_style_adither.Location = new System.Drawing.Point(362, 131); - this.Glide64mk2_old_style_adither.Name = "Glide64mk2_old_style_adither"; - this.Glide64mk2_old_style_adither.Size = new System.Drawing.Size(104, 17); - this.Glide64mk2_old_style_adither.TabIndex = 44; - this.Glide64mk2_old_style_adither.Text = "Old Style Adither"; - this.Glide64mk2_old_style_adither.UseVisualStyleBackColor = true; - // - // Glide64mk2_zmode_compare_less - // - this.Glide64mk2_zmode_compare_less.AutoSize = true; - this.Glide64mk2_zmode_compare_less.Location = new System.Drawing.Point(362, 108); - this.Glide64mk2_zmode_compare_less.Name = "Glide64mk2_zmode_compare_less"; - this.Glide64mk2_zmode_compare_less.Size = new System.Drawing.Size(127, 17); - this.Glide64mk2_zmode_compare_less.TabIndex = 43; - this.Glide64mk2_zmode_compare_less.Text = "Z mode compare less"; - this.Glide64mk2_zmode_compare_less.UseVisualStyleBackColor = true; - // - // Glide64mk2_adjust_aspect - // - this.Glide64mk2_adjust_aspect.AutoSize = true; - this.Glide64mk2_adjust_aspect.Location = new System.Drawing.Point(362, 85); - this.Glide64mk2_adjust_aspect.Name = "Glide64mk2_adjust_aspect"; - this.Glide64mk2_adjust_aspect.Size = new System.Drawing.Size(91, 17); - this.Glide64mk2_adjust_aspect.TabIndex = 42; - this.Glide64mk2_adjust_aspect.Text = "Adjust Aspect"; - this.Glide64mk2_adjust_aspect.UseVisualStyleBackColor = true; - // - // Glide64mk2_fast_crc - // - this.Glide64mk2_fast_crc.AutoSize = true; - this.Glide64mk2_fast_crc.Location = new System.Drawing.Point(192, 269); - this.Glide64mk2_fast_crc.Name = "Glide64mk2_fast_crc"; - this.Glide64mk2_fast_crc.Size = new System.Drawing.Size(71, 17); - this.Glide64mk2_fast_crc.TabIndex = 41; - this.Glide64mk2_fast_crc.Text = "Fast CRC"; - this.Glide64mk2_fast_crc.UseVisualStyleBackColor = true; - // - // Glide64mk2_clip_zmax - // - this.Glide64mk2_clip_zmax.AutoSize = true; - this.Glide64mk2_clip_zmax.Location = new System.Drawing.Point(192, 246); - this.Glide64mk2_clip_zmax.Name = "Glide64mk2_clip_zmax"; - this.Glide64mk2_clip_zmax.Size = new System.Drawing.Size(75, 17); - this.Glide64mk2_clip_zmax.TabIndex = 40; - this.Glide64mk2_clip_zmax.Text = "Clip Z max"; - this.Glide64mk2_clip_zmax.UseVisualStyleBackColor = true; - // - // Glide64mk2_clip_zmin - // - this.Glide64mk2_clip_zmin.AutoSize = true; - this.Glide64mk2_clip_zmin.Location = new System.Drawing.Point(192, 223); - this.Glide64mk2_clip_zmin.Name = "Glide64mk2_clip_zmin"; - this.Glide64mk2_clip_zmin.Size = new System.Drawing.Size(72, 17); - this.Glide64mk2_clip_zmin.TabIndex = 39; - this.Glide64mk2_clip_zmin.Text = "Clip Z min"; - this.Glide64mk2_clip_zmin.UseVisualStyleBackColor = true; - // - // Glide64mk2_force_quad3d - // - this.Glide64mk2_force_quad3d.AutoSize = true; - this.Glide64mk2_force_quad3d.Location = new System.Drawing.Point(192, 200); - this.Glide64mk2_force_quad3d.Name = "Glide64mk2_force_quad3d"; - this.Glide64mk2_force_quad3d.Size = new System.Drawing.Size(94, 17); - this.Glide64mk2_force_quad3d.TabIndex = 38; - this.Glide64mk2_force_quad3d.Text = "Force Quad3d"; - this.Glide64mk2_force_quad3d.UseVisualStyleBackColor = true; - // - // Glide64mk2_pal230 - // - this.Glide64mk2_pal230.AutoSize = true; - this.Glide64mk2_pal230.Location = new System.Drawing.Point(192, 177); - this.Glide64mk2_pal230.Name = "Glide64mk2_pal230"; - this.Glide64mk2_pal230.Size = new System.Drawing.Size(58, 17); - this.Glide64mk2_pal230.TabIndex = 37; - this.Glide64mk2_pal230.Text = "pal230"; - this.Glide64mk2_pal230.UseVisualStyleBackColor = true; - // - // Glide64mk2_texture_correction - // - this.Glide64mk2_texture_correction.AutoSize = true; - this.Glide64mk2_texture_correction.Location = new System.Drawing.Point(192, 154); - this.Glide64mk2_texture_correction.Name = "Glide64mk2_texture_correction"; - this.Glide64mk2_texture_correction.Size = new System.Drawing.Size(113, 17); - this.Glide64mk2_texture_correction.TabIndex = 36; - this.Glide64mk2_texture_correction.Text = "Texture Correction"; - this.Glide64mk2_texture_correction.UseVisualStyleBackColor = true; - // - // Glide64mk2_correct_viewport - // - this.Glide64mk2_correct_viewport.AutoSize = true; - this.Glide64mk2_correct_viewport.Location = new System.Drawing.Point(192, 131); - this.Glide64mk2_correct_viewport.Name = "Glide64mk2_correct_viewport"; - this.Glide64mk2_correct_viewport.Size = new System.Drawing.Size(104, 17); - this.Glide64mk2_correct_viewport.TabIndex = 35; - this.Glide64mk2_correct_viewport.Text = "Correct Viewport"; - this.Glide64mk2_correct_viewport.UseVisualStyleBackColor = true; - // - // Glide64mk2_force_calc_sphere - // - this.Glide64mk2_force_calc_sphere.AutoSize = true; - this.Glide64mk2_force_calc_sphere.Location = new System.Drawing.Point(192, 108); - this.Glide64mk2_force_calc_sphere.Name = "Glide64mk2_force_calc_sphere"; - this.Glide64mk2_force_calc_sphere.Size = new System.Drawing.Size(114, 17); - this.Glide64mk2_force_calc_sphere.TabIndex = 34; - this.Glide64mk2_force_calc_sphere.Text = "Force Calc Sphere"; - this.Glide64mk2_force_calc_sphere.UseVisualStyleBackColor = true; - // - // Glide64mk2_use_sts1_only - // - this.Glide64mk2_use_sts1_only.AutoSize = true; - this.Glide64mk2_use_sts1_only.Location = new System.Drawing.Point(192, 85); - this.Glide64mk2_use_sts1_only.Name = "Glide64mk2_use_sts1_only"; - this.Glide64mk2_use_sts1_only.Size = new System.Drawing.Size(89, 17); - this.Glide64mk2_use_sts1_only.TabIndex = 32; - this.Glide64mk2_use_sts1_only.Text = "Use sts1 only"; - this.Glide64mk2_use_sts1_only.UseVisualStyleBackColor = true; - // - // Glide64mk2_optimize_texrect - // - this.Glide64mk2_optimize_texrect.AutoSize = true; - this.Glide64mk2_optimize_texrect.Location = new System.Drawing.Point(192, 62); - this.Glide64mk2_optimize_texrect.Name = "Glide64mk2_optimize_texrect"; - this.Glide64mk2_optimize_texrect.Size = new System.Drawing.Size(101, 17); - this.Glide64mk2_optimize_texrect.TabIndex = 28; - this.Glide64mk2_optimize_texrect.Text = "Optimize texrect"; - this.Glide64mk2_optimize_texrect.UseVisualStyleBackColor = true; - // - // Glide64mk2_increase_texrect_edge - // - this.Glide64mk2_increase_texrect_edge.AutoSize = true; - this.Glide64mk2_increase_texrect_edge.Location = new System.Drawing.Point(192, 39); - this.Glide64mk2_increase_texrect_edge.Name = "Glide64mk2_increase_texrect_edge"; - this.Glide64mk2_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); - this.Glide64mk2_increase_texrect_edge.TabIndex = 27; - this.Glide64mk2_increase_texrect_edge.Text = "Increase texrect edge"; - this.Glide64mk2_increase_texrect_edge.UseVisualStyleBackColor = true; - // - // Glide64mk2_ignore_aux_copy - // - this.Glide64mk2_ignore_aux_copy.AutoSize = true; - this.Glide64mk2_ignore_aux_copy.Location = new System.Drawing.Point(6, 269); - this.Glide64mk2_ignore_aux_copy.Name = "Glide64mk2_ignore_aux_copy"; - this.Glide64mk2_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); - this.Glide64mk2_ignore_aux_copy.TabIndex = 24; - this.Glide64mk2_ignore_aux_copy.Text = "Ignore aux copy"; - this.Glide64mk2_ignore_aux_copy.UseVisualStyleBackColor = true; - // - // Glide64mk2_hires_buf_clear - // - this.Glide64mk2_hires_buf_clear.AutoSize = true; - this.Glide64mk2_hires_buf_clear.Location = new System.Drawing.Point(6, 246); - this.Glide64mk2_hires_buf_clear.Name = "Glide64mk2_hires_buf_clear"; - this.Glide64mk2_hires_buf_clear.Size = new System.Drawing.Size(109, 17); - this.Glide64mk2_hires_buf_clear.TabIndex = 23; - this.Glide64mk2_hires_buf_clear.Text = "Hi-res buffer clear"; - this.Glide64mk2_hires_buf_clear.UseVisualStyleBackColor = true; - // - // Glide64mk2_force_microcheck - // - this.Glide64mk2_force_microcheck.AutoSize = true; - this.Glide64mk2_force_microcheck.Location = new System.Drawing.Point(6, 223); - this.Glide64mk2_force_microcheck.Name = "Glide64mk2_force_microcheck"; - this.Glide64mk2_force_microcheck.Size = new System.Drawing.Size(165, 17); - this.Glide64mk2_force_microcheck.TabIndex = 22; - this.Glide64mk2_force_microcheck.Text = "Check microcode each frame"; - this.Glide64mk2_force_microcheck.UseVisualStyleBackColor = true; - // - // Glide64mk2_fog - // - this.Glide64mk2_fog.AutoSize = true; - this.Glide64mk2_fog.Location = new System.Drawing.Point(6, 200); - this.Glide64mk2_fog.Name = "Glide64mk2_fog"; - this.Glide64mk2_fog.Size = new System.Drawing.Size(86, 17); - this.Glide64mk2_fog.TabIndex = 20; - this.Glide64mk2_fog.Text = "Fog Enabled"; - this.Glide64mk2_fog.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_smart - // - this.Glide64mk2_fb_smart.AutoSize = true; - this.Glide64mk2_fb_smart.Location = new System.Drawing.Point(6, 177); - this.Glide64mk2_fb_smart.Name = "Glide64mk2_fb_smart"; - this.Glide64mk2_fb_smart.Size = new System.Drawing.Size(109, 17); - this.Glide64mk2_fb_smart.TabIndex = 18; - this.Glide64mk2_fb_smart.Text = "Smart framebuffer"; - this.Glide64mk2_fb_smart.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_read_alpha - // - this.Glide64mk2_fb_read_alpha.AutoSize = true; - this.Glide64mk2_fb_read_alpha.Location = new System.Drawing.Point(6, 154); - this.Glide64mk2_fb_read_alpha.Name = "Glide64mk2_fb_read_alpha"; - this.Glide64mk2_fb_read_alpha.Size = new System.Drawing.Size(135, 17); - this.Glide64mk2_fb_read_alpha.TabIndex = 17; - this.Glide64mk2_fb_read_alpha.Text = "Framebuffer read alpha"; - this.Glide64mk2_fb_read_alpha.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_hires - // - this.Glide64mk2_fb_hires.AutoSize = true; - this.Glide64mk2_fb_hires.Location = new System.Drawing.Point(6, 131); - this.Glide64mk2_fb_hires.Name = "Glide64mk2_fb_hires"; - this.Glide64mk2_fb_hires.Size = new System.Drawing.Size(109, 17); - this.Glide64mk2_fb_hires.TabIndex = 15; - this.Glide64mk2_fb_hires.Text = "Hi-res framebuffer"; - this.Glide64mk2_fb_hires.UseVisualStyleBackColor = true; - // - // Glide64mk2_detect_cpu_write - // - this.Glide64mk2_detect_cpu_write.AutoSize = true; - this.Glide64mk2_detect_cpu_write.Location = new System.Drawing.Point(6, 108); - this.Glide64mk2_detect_cpu_write.Name = "Glide64mk2_detect_cpu_write"; - this.Glide64mk2_detect_cpu_write.Size = new System.Drawing.Size(113, 17); - this.Glide64mk2_detect_cpu_write.TabIndex = 6; - this.Glide64mk2_detect_cpu_write.Text = "Detect CPU writes"; - this.Glide64mk2_detect_cpu_write.UseVisualStyleBackColor = true; - // - // Glide64mk2_decrease_fillrect_edge - // - this.Glide64mk2_decrease_fillrect_edge.AutoSize = true; - this.Glide64mk2_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); - this.Glide64mk2_decrease_fillrect_edge.Name = "Glide64mk2_decrease_fillrect_edge"; - this.Glide64mk2_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); - this.Glide64mk2_decrease_fillrect_edge.TabIndex = 5; - this.Glide64mk2_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; - this.Glide64mk2_decrease_fillrect_edge.UseVisualStyleBackColor = true; - // - // Glide64mk2_buff_clear - // - this.Glide64mk2_buff_clear.AutoSize = true; - this.Glide64mk2_buff_clear.Location = new System.Drawing.Point(6, 62); - this.Glide64mk2_buff_clear.Name = "Glide64mk2_buff_clear"; - this.Glide64mk2_buff_clear.Size = new System.Drawing.Size(72, 17); - this.Glide64mk2_buff_clear.TabIndex = 4; - this.Glide64mk2_buff_clear.Text = "Buff Clear"; - this.Glide64mk2_buff_clear.UseVisualStyleBackColor = true; - // - // Glide64mk2_alt_tex_size - // - this.Glide64mk2_alt_tex_size.AutoSize = true; - this.Glide64mk2_alt_tex_size.Location = new System.Drawing.Point(6, 39); - this.Glide64mk2_alt_tex_size.Name = "Glide64mk2_alt_tex_size"; - this.Glide64mk2_alt_tex_size.Size = new System.Drawing.Size(169, 17); - this.Glide64mk2_alt_tex_size.TabIndex = 3; - this.Glide64mk2_alt_tex_size.Text = "Alternate Texture Size Method"; - this.Glide64mk2_alt_tex_size.UseVisualStyleBackColor = true; - // - // Glide64mk2_UseDefaultHacks1 - // - this.Glide64mk2_UseDefaultHacks1.AutoSize = true; - this.Glide64mk2_UseDefaultHacks1.Location = new System.Drawing.Point(6, 6); - this.Glide64mk2_UseDefaultHacks1.Name = "Glide64mk2_UseDefaultHacks1"; - this.Glide64mk2_UseDefaultHacks1.Size = new System.Drawing.Size(165, 17); - this.Glide64mk2_UseDefaultHacks1.TabIndex = 2; - this.Glide64mk2_UseDefaultHacks1.Text = "Use defaults for current game"; - this.Glide64mk2_UseDefaultHacks1.UseVisualStyleBackColor = true; - this.Glide64mk2_UseDefaultHacks1.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks1_CheckedChanged); - // - // tabPage3 - // - this.tabPage3.Controls.Add(this.label46); - this.tabPage3.Controls.Add(this.Glide64mk2_read_back_to_screen); - this.tabPage3.Controls.Add(this.Glide64mk2_aspectmode); - this.tabPage3.Controls.Add(this.Glide64mk2_fb_crc_mode); - this.tabPage3.Controls.Add(this.label45); - this.tabPage3.Controls.Add(this.label3); - this.tabPage3.Controls.Add(this.label52); - this.tabPage3.Controls.Add(this.Glide64mk2_enable_hacks_for_game); - this.tabPage3.Controls.Add(this.label53); - this.tabPage3.Controls.Add(this.Glide64mk2_swapmode); - this.tabPage3.Controls.Add(this.label54); - this.tabPage3.Controls.Add(this.Glide64mk2_stipple_pattern); - this.tabPage3.Controls.Add(this.label55); - this.tabPage3.Controls.Add(this.Glide64mk2_stipple_mode); - this.tabPage3.Controls.Add(this.label56); - this.tabPage3.Controls.Add(this.Glide64mk2_lodmode); - this.tabPage3.Controls.Add(this.label58); - this.tabPage3.Controls.Add(this.Glide64mk2_filtering); - this.tabPage3.Controls.Add(this.Glide64mk2_UseDefaultHacks2); - this.tabPage3.Location = new System.Drawing.Point(4, 22); - this.tabPage3.Name = "tabPage3"; - this.tabPage3.Size = new System.Drawing.Size(564, 310); - this.tabPage3.TabIndex = 2; - this.tabPage3.Text = "More Per Game Settings"; - this.tabPage3.UseVisualStyleBackColor = true; - // - // label46 - // - this.label46.AutoSize = true; - this.label46.Location = new System.Drawing.Point(280, 145); - this.label46.Name = "label46"; - this.label46.Size = new System.Drawing.Size(110, 13); - this.label46.TabIndex = 60; - this.label46.Text = "Read back to screen:"; - // - // Glide64mk2_read_back_to_screen - // - this.Glide64mk2_read_back_to_screen.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_read_back_to_screen.FormattingEnabled = true; - this.Glide64mk2_read_back_to_screen.Items.AddRange(new object[] { + this.Glide64mk2_card_id.Location = new System.Drawing.Point(58, 27); + this.Glide64mk2_card_id.Name = "Glide64mk2_card_id"; + this.Glide64mk2_card_id.Size = new System.Drawing.Size(36, 21); + this.Glide64mk2_card_id.TabIndex = 4; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_always); + this.tabPage2.Controls.Add(this.Glide64mk2_useless_is_useless); + this.tabPage2.Controls.Add(this.Glide64mk2_n64_z_scale); + this.tabPage2.Controls.Add(this.Glide64mk2_old_style_adither); + this.tabPage2.Controls.Add(this.Glide64mk2_zmode_compare_less); + this.tabPage2.Controls.Add(this.Glide64mk2_adjust_aspect); + this.tabPage2.Controls.Add(this.Glide64mk2_fast_crc); + this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmax); + this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmin); + this.tabPage2.Controls.Add(this.Glide64mk2_force_quad3d); + this.tabPage2.Controls.Add(this.Glide64mk2_pal230); + this.tabPage2.Controls.Add(this.Glide64mk2_texture_correction); + this.tabPage2.Controls.Add(this.Glide64mk2_correct_viewport); + this.tabPage2.Controls.Add(this.Glide64mk2_force_calc_sphere); + this.tabPage2.Controls.Add(this.Glide64mk2_use_sts1_only); + this.tabPage2.Controls.Add(this.Glide64mk2_optimize_texrect); + this.tabPage2.Controls.Add(this.Glide64mk2_increase_texrect_edge); + this.tabPage2.Controls.Add(this.Glide64mk2_ignore_aux_copy); + this.tabPage2.Controls.Add(this.Glide64mk2_hires_buf_clear); + this.tabPage2.Controls.Add(this.Glide64mk2_force_microcheck); + this.tabPage2.Controls.Add(this.Glide64mk2_fog); + this.tabPage2.Controls.Add(this.Glide64mk2_fb_smart); + this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_alpha); + this.tabPage2.Controls.Add(this.Glide64mk2_fb_hires); + this.tabPage2.Controls.Add(this.Glide64mk2_detect_cpu_write); + this.tabPage2.Controls.Add(this.Glide64mk2_decrease_fillrect_edge); + this.tabPage2.Controls.Add(this.Glide64mk2_buff_clear); + this.tabPage2.Controls.Add(this.Glide64mk2_alt_tex_size); + this.tabPage2.Controls.Add(this.Glide64mk2_UseDefaultHacks1); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(564, 310); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Per Game Settings"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_read_always + // + this.Glide64mk2_fb_read_always.AutoSize = true; + this.Glide64mk2_fb_read_always.Location = new System.Drawing.Point(362, 39); + this.Glide64mk2_fb_read_always.Name = "Glide64mk2_fb_read_always"; + this.Glide64mk2_fb_read_always.Size = new System.Drawing.Size(164, 17); + this.Glide64mk2_fb_read_always.TabIndex = 48; + this.Glide64mk2_fb_read_always.Text = "Framebuffer read every frame"; + this.Glide64mk2_fb_read_always.UseVisualStyleBackColor = true; + // + // Glide64mk2_useless_is_useless + // + this.Glide64mk2_useless_is_useless.AutoSize = true; + this.Glide64mk2_useless_is_useless.Location = new System.Drawing.Point(362, 62); + this.Glide64mk2_useless_is_useless.Name = "Glide64mk2_useless_is_useless"; + this.Glide64mk2_useless_is_useless.Size = new System.Drawing.Size(115, 17); + this.Glide64mk2_useless_is_useless.TabIndex = 46; + this.Glide64mk2_useless_is_useless.Text = "useless_is_useless"; + this.Glide64mk2_useless_is_useless.UseVisualStyleBackColor = true; + // + // Glide64mk2_n64_z_scale + // + this.Glide64mk2_n64_z_scale.AutoSize = true; + this.Glide64mk2_n64_z_scale.Location = new System.Drawing.Point(362, 154); + this.Glide64mk2_n64_z_scale.Name = "Glide64mk2_n64_z_scale"; + this.Glide64mk2_n64_z_scale.Size = new System.Drawing.Size(86, 17); + this.Glide64mk2_n64_z_scale.TabIndex = 45; + this.Glide64mk2_n64_z_scale.Text = "N64 Z Scale"; + this.Glide64mk2_n64_z_scale.UseVisualStyleBackColor = true; + // + // Glide64mk2_old_style_adither + // + this.Glide64mk2_old_style_adither.AutoSize = true; + this.Glide64mk2_old_style_adither.Location = new System.Drawing.Point(362, 131); + this.Glide64mk2_old_style_adither.Name = "Glide64mk2_old_style_adither"; + this.Glide64mk2_old_style_adither.Size = new System.Drawing.Size(104, 17); + this.Glide64mk2_old_style_adither.TabIndex = 44; + this.Glide64mk2_old_style_adither.Text = "Old Style Adither"; + this.Glide64mk2_old_style_adither.UseVisualStyleBackColor = true; + // + // Glide64mk2_zmode_compare_less + // + this.Glide64mk2_zmode_compare_less.AutoSize = true; + this.Glide64mk2_zmode_compare_less.Location = new System.Drawing.Point(362, 108); + this.Glide64mk2_zmode_compare_less.Name = "Glide64mk2_zmode_compare_less"; + this.Glide64mk2_zmode_compare_less.Size = new System.Drawing.Size(127, 17); + this.Glide64mk2_zmode_compare_less.TabIndex = 43; + this.Glide64mk2_zmode_compare_less.Text = "Z mode compare less"; + this.Glide64mk2_zmode_compare_less.UseVisualStyleBackColor = true; + // + // Glide64mk2_adjust_aspect + // + this.Glide64mk2_adjust_aspect.AutoSize = true; + this.Glide64mk2_adjust_aspect.Location = new System.Drawing.Point(362, 85); + this.Glide64mk2_adjust_aspect.Name = "Glide64mk2_adjust_aspect"; + this.Glide64mk2_adjust_aspect.Size = new System.Drawing.Size(91, 17); + this.Glide64mk2_adjust_aspect.TabIndex = 42; + this.Glide64mk2_adjust_aspect.Text = "Adjust Aspect"; + this.Glide64mk2_adjust_aspect.UseVisualStyleBackColor = true; + // + // Glide64mk2_fast_crc + // + this.Glide64mk2_fast_crc.AutoSize = true; + this.Glide64mk2_fast_crc.Location = new System.Drawing.Point(192, 269); + this.Glide64mk2_fast_crc.Name = "Glide64mk2_fast_crc"; + this.Glide64mk2_fast_crc.Size = new System.Drawing.Size(71, 17); + this.Glide64mk2_fast_crc.TabIndex = 41; + this.Glide64mk2_fast_crc.Text = "Fast CRC"; + this.Glide64mk2_fast_crc.UseVisualStyleBackColor = true; + // + // Glide64mk2_clip_zmax + // + this.Glide64mk2_clip_zmax.AutoSize = true; + this.Glide64mk2_clip_zmax.Location = new System.Drawing.Point(192, 246); + this.Glide64mk2_clip_zmax.Name = "Glide64mk2_clip_zmax"; + this.Glide64mk2_clip_zmax.Size = new System.Drawing.Size(75, 17); + this.Glide64mk2_clip_zmax.TabIndex = 40; + this.Glide64mk2_clip_zmax.Text = "Clip Z max"; + this.Glide64mk2_clip_zmax.UseVisualStyleBackColor = true; + // + // Glide64mk2_clip_zmin + // + this.Glide64mk2_clip_zmin.AutoSize = true; + this.Glide64mk2_clip_zmin.Location = new System.Drawing.Point(192, 223); + this.Glide64mk2_clip_zmin.Name = "Glide64mk2_clip_zmin"; + this.Glide64mk2_clip_zmin.Size = new System.Drawing.Size(72, 17); + this.Glide64mk2_clip_zmin.TabIndex = 39; + this.Glide64mk2_clip_zmin.Text = "Clip Z min"; + this.Glide64mk2_clip_zmin.UseVisualStyleBackColor = true; + // + // Glide64mk2_force_quad3d + // + this.Glide64mk2_force_quad3d.AutoSize = true; + this.Glide64mk2_force_quad3d.Location = new System.Drawing.Point(192, 200); + this.Glide64mk2_force_quad3d.Name = "Glide64mk2_force_quad3d"; + this.Glide64mk2_force_quad3d.Size = new System.Drawing.Size(94, 17); + this.Glide64mk2_force_quad3d.TabIndex = 38; + this.Glide64mk2_force_quad3d.Text = "Force Quad3d"; + this.Glide64mk2_force_quad3d.UseVisualStyleBackColor = true; + // + // Glide64mk2_pal230 + // + this.Glide64mk2_pal230.AutoSize = true; + this.Glide64mk2_pal230.Location = new System.Drawing.Point(192, 177); + this.Glide64mk2_pal230.Name = "Glide64mk2_pal230"; + this.Glide64mk2_pal230.Size = new System.Drawing.Size(58, 17); + this.Glide64mk2_pal230.TabIndex = 37; + this.Glide64mk2_pal230.Text = "pal230"; + this.Glide64mk2_pal230.UseVisualStyleBackColor = true; + // + // Glide64mk2_texture_correction + // + this.Glide64mk2_texture_correction.AutoSize = true; + this.Glide64mk2_texture_correction.Location = new System.Drawing.Point(192, 154); + this.Glide64mk2_texture_correction.Name = "Glide64mk2_texture_correction"; + this.Glide64mk2_texture_correction.Size = new System.Drawing.Size(113, 17); + this.Glide64mk2_texture_correction.TabIndex = 36; + this.Glide64mk2_texture_correction.Text = "Texture Correction"; + this.Glide64mk2_texture_correction.UseVisualStyleBackColor = true; + // + // Glide64mk2_correct_viewport + // + this.Glide64mk2_correct_viewport.AutoSize = true; + this.Glide64mk2_correct_viewport.Location = new System.Drawing.Point(192, 131); + this.Glide64mk2_correct_viewport.Name = "Glide64mk2_correct_viewport"; + this.Glide64mk2_correct_viewport.Size = new System.Drawing.Size(104, 17); + this.Glide64mk2_correct_viewport.TabIndex = 35; + this.Glide64mk2_correct_viewport.Text = "Correct Viewport"; + this.Glide64mk2_correct_viewport.UseVisualStyleBackColor = true; + // + // Glide64mk2_force_calc_sphere + // + this.Glide64mk2_force_calc_sphere.AutoSize = true; + this.Glide64mk2_force_calc_sphere.Location = new System.Drawing.Point(192, 108); + this.Glide64mk2_force_calc_sphere.Name = "Glide64mk2_force_calc_sphere"; + this.Glide64mk2_force_calc_sphere.Size = new System.Drawing.Size(114, 17); + this.Glide64mk2_force_calc_sphere.TabIndex = 34; + this.Glide64mk2_force_calc_sphere.Text = "Force Calc Sphere"; + this.Glide64mk2_force_calc_sphere.UseVisualStyleBackColor = true; + // + // Glide64mk2_use_sts1_only + // + this.Glide64mk2_use_sts1_only.AutoSize = true; + this.Glide64mk2_use_sts1_only.Location = new System.Drawing.Point(192, 85); + this.Glide64mk2_use_sts1_only.Name = "Glide64mk2_use_sts1_only"; + this.Glide64mk2_use_sts1_only.Size = new System.Drawing.Size(89, 17); + this.Glide64mk2_use_sts1_only.TabIndex = 32; + this.Glide64mk2_use_sts1_only.Text = "Use sts1 only"; + this.Glide64mk2_use_sts1_only.UseVisualStyleBackColor = true; + // + // Glide64mk2_optimize_texrect + // + this.Glide64mk2_optimize_texrect.AutoSize = true; + this.Glide64mk2_optimize_texrect.Location = new System.Drawing.Point(192, 62); + this.Glide64mk2_optimize_texrect.Name = "Glide64mk2_optimize_texrect"; + this.Glide64mk2_optimize_texrect.Size = new System.Drawing.Size(101, 17); + this.Glide64mk2_optimize_texrect.TabIndex = 28; + this.Glide64mk2_optimize_texrect.Text = "Optimize texrect"; + this.Glide64mk2_optimize_texrect.UseVisualStyleBackColor = true; + // + // Glide64mk2_increase_texrect_edge + // + this.Glide64mk2_increase_texrect_edge.AutoSize = true; + this.Glide64mk2_increase_texrect_edge.Location = new System.Drawing.Point(192, 39); + this.Glide64mk2_increase_texrect_edge.Name = "Glide64mk2_increase_texrect_edge"; + this.Glide64mk2_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); + this.Glide64mk2_increase_texrect_edge.TabIndex = 27; + this.Glide64mk2_increase_texrect_edge.Text = "Increase texrect edge"; + this.Glide64mk2_increase_texrect_edge.UseVisualStyleBackColor = true; + // + // Glide64mk2_ignore_aux_copy + // + this.Glide64mk2_ignore_aux_copy.AutoSize = true; + this.Glide64mk2_ignore_aux_copy.Location = new System.Drawing.Point(6, 269); + this.Glide64mk2_ignore_aux_copy.Name = "Glide64mk2_ignore_aux_copy"; + this.Glide64mk2_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); + this.Glide64mk2_ignore_aux_copy.TabIndex = 24; + this.Glide64mk2_ignore_aux_copy.Text = "Ignore aux copy"; + this.Glide64mk2_ignore_aux_copy.UseVisualStyleBackColor = true; + // + // Glide64mk2_hires_buf_clear + // + this.Glide64mk2_hires_buf_clear.AutoSize = true; + this.Glide64mk2_hires_buf_clear.Location = new System.Drawing.Point(6, 246); + this.Glide64mk2_hires_buf_clear.Name = "Glide64mk2_hires_buf_clear"; + this.Glide64mk2_hires_buf_clear.Size = new System.Drawing.Size(109, 17); + this.Glide64mk2_hires_buf_clear.TabIndex = 23; + this.Glide64mk2_hires_buf_clear.Text = "Hi-res buffer clear"; + this.Glide64mk2_hires_buf_clear.UseVisualStyleBackColor = true; + // + // Glide64mk2_force_microcheck + // + this.Glide64mk2_force_microcheck.AutoSize = true; + this.Glide64mk2_force_microcheck.Location = new System.Drawing.Point(6, 223); + this.Glide64mk2_force_microcheck.Name = "Glide64mk2_force_microcheck"; + this.Glide64mk2_force_microcheck.Size = new System.Drawing.Size(165, 17); + this.Glide64mk2_force_microcheck.TabIndex = 22; + this.Glide64mk2_force_microcheck.Text = "Check microcode each frame"; + this.Glide64mk2_force_microcheck.UseVisualStyleBackColor = true; + // + // Glide64mk2_fog + // + this.Glide64mk2_fog.AutoSize = true; + this.Glide64mk2_fog.Location = new System.Drawing.Point(6, 200); + this.Glide64mk2_fog.Name = "Glide64mk2_fog"; + this.Glide64mk2_fog.Size = new System.Drawing.Size(86, 17); + this.Glide64mk2_fog.TabIndex = 20; + this.Glide64mk2_fog.Text = "Fog Enabled"; + this.Glide64mk2_fog.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_smart + // + this.Glide64mk2_fb_smart.AutoSize = true; + this.Glide64mk2_fb_smart.Location = new System.Drawing.Point(6, 177); + this.Glide64mk2_fb_smart.Name = "Glide64mk2_fb_smart"; + this.Glide64mk2_fb_smart.Size = new System.Drawing.Size(109, 17); + this.Glide64mk2_fb_smart.TabIndex = 18; + this.Glide64mk2_fb_smart.Text = "Smart framebuffer"; + this.Glide64mk2_fb_smart.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_read_alpha + // + this.Glide64mk2_fb_read_alpha.AutoSize = true; + this.Glide64mk2_fb_read_alpha.Location = new System.Drawing.Point(6, 154); + this.Glide64mk2_fb_read_alpha.Name = "Glide64mk2_fb_read_alpha"; + this.Glide64mk2_fb_read_alpha.Size = new System.Drawing.Size(135, 17); + this.Glide64mk2_fb_read_alpha.TabIndex = 17; + this.Glide64mk2_fb_read_alpha.Text = "Framebuffer read alpha"; + this.Glide64mk2_fb_read_alpha.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_hires + // + this.Glide64mk2_fb_hires.AutoSize = true; + this.Glide64mk2_fb_hires.Location = new System.Drawing.Point(6, 131); + this.Glide64mk2_fb_hires.Name = "Glide64mk2_fb_hires"; + this.Glide64mk2_fb_hires.Size = new System.Drawing.Size(109, 17); + this.Glide64mk2_fb_hires.TabIndex = 15; + this.Glide64mk2_fb_hires.Text = "Hi-res framebuffer"; + this.Glide64mk2_fb_hires.UseVisualStyleBackColor = true; + // + // Glide64mk2_detect_cpu_write + // + this.Glide64mk2_detect_cpu_write.AutoSize = true; + this.Glide64mk2_detect_cpu_write.Location = new System.Drawing.Point(6, 108); + this.Glide64mk2_detect_cpu_write.Name = "Glide64mk2_detect_cpu_write"; + this.Glide64mk2_detect_cpu_write.Size = new System.Drawing.Size(113, 17); + this.Glide64mk2_detect_cpu_write.TabIndex = 6; + this.Glide64mk2_detect_cpu_write.Text = "Detect CPU writes"; + this.Glide64mk2_detect_cpu_write.UseVisualStyleBackColor = true; + // + // Glide64mk2_decrease_fillrect_edge + // + this.Glide64mk2_decrease_fillrect_edge.AutoSize = true; + this.Glide64mk2_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); + this.Glide64mk2_decrease_fillrect_edge.Name = "Glide64mk2_decrease_fillrect_edge"; + this.Glide64mk2_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); + this.Glide64mk2_decrease_fillrect_edge.TabIndex = 5; + this.Glide64mk2_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; + this.Glide64mk2_decrease_fillrect_edge.UseVisualStyleBackColor = true; + // + // Glide64mk2_buff_clear + // + this.Glide64mk2_buff_clear.AutoSize = true; + this.Glide64mk2_buff_clear.Location = new System.Drawing.Point(6, 62); + this.Glide64mk2_buff_clear.Name = "Glide64mk2_buff_clear"; + this.Glide64mk2_buff_clear.Size = new System.Drawing.Size(72, 17); + this.Glide64mk2_buff_clear.TabIndex = 4; + this.Glide64mk2_buff_clear.Text = "Buff Clear"; + this.Glide64mk2_buff_clear.UseVisualStyleBackColor = true; + // + // Glide64mk2_alt_tex_size + // + this.Glide64mk2_alt_tex_size.AutoSize = true; + this.Glide64mk2_alt_tex_size.Location = new System.Drawing.Point(6, 39); + this.Glide64mk2_alt_tex_size.Name = "Glide64mk2_alt_tex_size"; + this.Glide64mk2_alt_tex_size.Size = new System.Drawing.Size(169, 17); + this.Glide64mk2_alt_tex_size.TabIndex = 3; + this.Glide64mk2_alt_tex_size.Text = "Alternate Texture Size Method"; + this.Glide64mk2_alt_tex_size.UseVisualStyleBackColor = true; + // + // Glide64mk2_UseDefaultHacks1 + // + this.Glide64mk2_UseDefaultHacks1.AutoSize = true; + this.Glide64mk2_UseDefaultHacks1.Location = new System.Drawing.Point(6, 6); + this.Glide64mk2_UseDefaultHacks1.Name = "Glide64mk2_UseDefaultHacks1"; + this.Glide64mk2_UseDefaultHacks1.Size = new System.Drawing.Size(165, 17); + this.Glide64mk2_UseDefaultHacks1.TabIndex = 2; + this.Glide64mk2_UseDefaultHacks1.Text = "Use defaults for current game"; + this.Glide64mk2_UseDefaultHacks1.UseVisualStyleBackColor = true; + this.Glide64mk2_UseDefaultHacks1.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks1_CheckedChanged); + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.label46); + this.tabPage3.Controls.Add(this.Glide64mk2_read_back_to_screen); + this.tabPage3.Controls.Add(this.Glide64mk2_aspectmode); + this.tabPage3.Controls.Add(this.Glide64mk2_fb_crc_mode); + this.tabPage3.Controls.Add(this.label45); + this.tabPage3.Controls.Add(this.label3); + this.tabPage3.Controls.Add(this.label52); + this.tabPage3.Controls.Add(this.Glide64mk2_enable_hacks_for_game); + this.tabPage3.Controls.Add(this.label53); + this.tabPage3.Controls.Add(this.Glide64mk2_swapmode); + this.tabPage3.Controls.Add(this.label54); + this.tabPage3.Controls.Add(this.Glide64mk2_stipple_pattern); + this.tabPage3.Controls.Add(this.label55); + this.tabPage3.Controls.Add(this.Glide64mk2_stipple_mode); + this.tabPage3.Controls.Add(this.label56); + this.tabPage3.Controls.Add(this.Glide64mk2_lodmode); + this.tabPage3.Controls.Add(this.label58); + this.tabPage3.Controls.Add(this.Glide64mk2_filtering); + this.tabPage3.Controls.Add(this.Glide64mk2_UseDefaultHacks2); + this.tabPage3.Location = new System.Drawing.Point(4, 22); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Size = new System.Drawing.Size(564, 310); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "More Per Game Settings"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // label46 + // + this.label46.AutoSize = true; + this.label46.Location = new System.Drawing.Point(280, 145); + this.label46.Name = "label46"; + this.label46.Size = new System.Drawing.Size(110, 13); + this.label46.TabIndex = 60; + this.label46.Text = "Read back to screen:"; + // + // Glide64mk2_read_back_to_screen + // + this.Glide64mk2_read_back_to_screen.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_read_back_to_screen.FormattingEnabled = true; + this.Glide64mk2_read_back_to_screen.Items.AddRange(new object[] { "0", "1", "2"}); - this.Glide64mk2_read_back_to_screen.Location = new System.Drawing.Point(396, 142); - this.Glide64mk2_read_back_to_screen.Name = "Glide64mk2_read_back_to_screen"; - this.Glide64mk2_read_back_to_screen.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_read_back_to_screen.TabIndex = 59; - // - // Glide64mk2_aspectmode - // - this.Glide64mk2_aspectmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_aspectmode.FormattingEnabled = true; - this.Glide64mk2_aspectmode.Items.AddRange(new object[] { + this.Glide64mk2_read_back_to_screen.Location = new System.Drawing.Point(396, 142); + this.Glide64mk2_read_back_to_screen.Name = "Glide64mk2_read_back_to_screen"; + this.Glide64mk2_read_back_to_screen.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_read_back_to_screen.TabIndex = 59; + // + // Glide64mk2_aspectmode + // + this.Glide64mk2_aspectmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_aspectmode.FormattingEnabled = true; + this.Glide64mk2_aspectmode.Items.AddRange(new object[] { "4:3", "16:9", "?????", "Original"}); - this.Glide64mk2_aspectmode.Location = new System.Drawing.Point(396, 87); - this.Glide64mk2_aspectmode.Name = "Glide64mk2_aspectmode"; - this.Glide64mk2_aspectmode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_aspectmode.TabIndex = 58; - // - // Glide64mk2_fb_crc_mode - // - this.Glide64mk2_fb_crc_mode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_fb_crc_mode.FormattingEnabled = true; - this.Glide64mk2_fb_crc_mode.Items.AddRange(new object[] { + this.Glide64mk2_aspectmode.Location = new System.Drawing.Point(396, 87); + this.Glide64mk2_aspectmode.Name = "Glide64mk2_aspectmode"; + this.Glide64mk2_aspectmode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_aspectmode.TabIndex = 58; + // + // Glide64mk2_fb_crc_mode + // + this.Glide64mk2_fb_crc_mode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_fb_crc_mode.FormattingEnabled = true; + this.Glide64mk2_fb_crc_mode.Items.AddRange(new object[] { "None", "Fast", "Safe"}); - this.Glide64mk2_fb_crc_mode.Location = new System.Drawing.Point(396, 115); - this.Glide64mk2_fb_crc_mode.Name = "Glide64mk2_fb_crc_mode"; - this.Glide64mk2_fb_crc_mode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_fb_crc_mode.TabIndex = 57; - // - // label45 - // - this.label45.AutoSize = true; - this.label45.Location = new System.Drawing.Point(269, 118); - this.label45.Name = "label45"; - this.label45.Size = new System.Drawing.Size(121, 13); - this.label45.TabIndex = 56; - this.label45.Text = "Framebuffer CRC Mode:"; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(317, 90); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(73, 13); - this.label3.TabIndex = 55; - this.label3.Text = "Aspect Mode:"; - // - // label52 - // - this.label52.AutoSize = true; - this.label52.Location = new System.Drawing.Point(271, 62); - this.label52.Name = "label52"; - this.label52.Size = new System.Drawing.Size(119, 13); - this.label52.TabIndex = 54; - this.label52.Text = "Enable hacks for game:"; - // - // Glide64mk2_enable_hacks_for_game - // - this.Glide64mk2_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_enable_hacks_for_game.FormattingEnabled = true; - this.Glide64mk2_enable_hacks_for_game.Items.AddRange(new object[] { + this.Glide64mk2_fb_crc_mode.Location = new System.Drawing.Point(396, 115); + this.Glide64mk2_fb_crc_mode.Name = "Glide64mk2_fb_crc_mode"; + this.Glide64mk2_fb_crc_mode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_fb_crc_mode.TabIndex = 57; + // + // label45 + // + this.label45.AutoSize = true; + this.label45.Location = new System.Drawing.Point(269, 118); + this.label45.Name = "label45"; + this.label45.Size = new System.Drawing.Size(121, 13); + this.label45.TabIndex = 56; + this.label45.Text = "Framebuffer CRC Mode:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(317, 90); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(73, 13); + this.label3.TabIndex = 55; + this.label3.Text = "Aspect Mode:"; + // + // label52 + // + this.label52.AutoSize = true; + this.label52.Location = new System.Drawing.Point(271, 62); + this.label52.Name = "label52"; + this.label52.Size = new System.Drawing.Size(119, 13); + this.label52.TabIndex = 54; + this.label52.Text = "Enable hacks for game:"; + // + // Glide64mk2_enable_hacks_for_game + // + this.Glide64mk2_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_enable_hacks_for_game.FormattingEnabled = true; + this.Glide64mk2_enable_hacks_for_game.Items.AddRange(new object[] { "None", "All-Star Baseball", "Banjo Tooie", @@ -2013,461 +2015,461 @@ "Tonic Trouble", "Yoshi Story", "Zelda"}); - this.Glide64mk2_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); - this.Glide64mk2_enable_hacks_for_game.Name = "Glide64mk2_enable_hacks_for_game"; - this.Glide64mk2_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); - this.Glide64mk2_enable_hacks_for_game.TabIndex = 53; - // - // label53 - // - this.label53.AutoSize = true; - this.label53.Location = new System.Drawing.Point(266, 36); - this.label53.Name = "label53"; - this.label53.Size = new System.Drawing.Size(124, 13); - this.label53.TabIndex = 40; - this.label53.Text = "Buffer swapping method:"; - // - // Glide64mk2_swapmode - // - this.Glide64mk2_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_swapmode.FormattingEnabled = true; - this.Glide64mk2_swapmode.Items.AddRange(new object[] { + this.Glide64mk2_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); + this.Glide64mk2_enable_hacks_for_game.Name = "Glide64mk2_enable_hacks_for_game"; + this.Glide64mk2_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); + this.Glide64mk2_enable_hacks_for_game.TabIndex = 53; + // + // label53 + // + this.label53.AutoSize = true; + this.label53.Location = new System.Drawing.Point(266, 36); + this.label53.Name = "label53"; + this.label53.Size = new System.Drawing.Size(124, 13); + this.label53.TabIndex = 40; + this.label53.Text = "Buffer swapping method:"; + // + // Glide64mk2_swapmode + // + this.Glide64mk2_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_swapmode.FormattingEnabled = true; + this.Glide64mk2_swapmode.Items.AddRange(new object[] { "Old", "New", "Hybrid"}); - this.Glide64mk2_swapmode.Location = new System.Drawing.Point(396, 32); - this.Glide64mk2_swapmode.Name = "Glide64mk2_swapmode"; - this.Glide64mk2_swapmode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_swapmode.TabIndex = 39; - // - // label54 - // - this.label54.AutoSize = true; - this.label54.Location = new System.Drawing.Point(3, 275); - this.label54.Name = "label54"; - this.label54.Size = new System.Drawing.Size(78, 13); - this.label54.TabIndex = 38; - this.label54.Text = "Stipple pattern:"; - // - // Glide64mk2_stipple_pattern - // - this.Glide64mk2_stipple_pattern.Location = new System.Drawing.Point(92, 272); - this.Glide64mk2_stipple_pattern.Name = "Glide64mk2_stipple_pattern"; - this.Glide64mk2_stipple_pattern.Size = new System.Drawing.Size(78, 20); - this.Glide64mk2_stipple_pattern.TabIndex = 37; - // - // label55 - // - this.label55.AutoSize = true; - this.label55.Location = new System.Drawing.Point(3, 249); - this.label55.Name = "label55"; - this.label55.Size = new System.Drawing.Size(71, 13); - this.label55.TabIndex = 36; - this.label55.Text = "Stipple mode:"; - // - // Glide64mk2_stipple_mode - // - this.Glide64mk2_stipple_mode.Location = new System.Drawing.Point(92, 246); - this.Glide64mk2_stipple_mode.Name = "Glide64mk2_stipple_mode"; - this.Glide64mk2_stipple_mode.Size = new System.Drawing.Size(36, 20); - this.Glide64mk2_stipple_mode.TabIndex = 35; - // - // label56 - // - this.label56.AutoSize = true; - this.label56.Location = new System.Drawing.Point(3, 118); - this.label56.Name = "label56"; - this.label56.Size = new System.Drawing.Size(86, 13); - this.label56.TabIndex = 26; - this.label56.Text = "LOD calculation:"; - // - // Glide64mk2_lodmode - // - this.Glide64mk2_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_lodmode.FormattingEnabled = true; - this.Glide64mk2_lodmode.Items.AddRange(new object[] { + this.Glide64mk2_swapmode.Location = new System.Drawing.Point(396, 32); + this.Glide64mk2_swapmode.Name = "Glide64mk2_swapmode"; + this.Glide64mk2_swapmode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_swapmode.TabIndex = 39; + // + // label54 + // + this.label54.AutoSize = true; + this.label54.Location = new System.Drawing.Point(3, 275); + this.label54.Name = "label54"; + this.label54.Size = new System.Drawing.Size(78, 13); + this.label54.TabIndex = 38; + this.label54.Text = "Stipple pattern:"; + // + // Glide64mk2_stipple_pattern + // + this.Glide64mk2_stipple_pattern.Location = new System.Drawing.Point(92, 272); + this.Glide64mk2_stipple_pattern.Name = "Glide64mk2_stipple_pattern"; + this.Glide64mk2_stipple_pattern.Size = new System.Drawing.Size(78, 20); + this.Glide64mk2_stipple_pattern.TabIndex = 37; + // + // label55 + // + this.label55.AutoSize = true; + this.label55.Location = new System.Drawing.Point(3, 249); + this.label55.Name = "label55"; + this.label55.Size = new System.Drawing.Size(71, 13); + this.label55.TabIndex = 36; + this.label55.Text = "Stipple mode:"; + // + // Glide64mk2_stipple_mode + // + this.Glide64mk2_stipple_mode.Location = new System.Drawing.Point(92, 246); + this.Glide64mk2_stipple_mode.Name = "Glide64mk2_stipple_mode"; + this.Glide64mk2_stipple_mode.Size = new System.Drawing.Size(36, 20); + this.Glide64mk2_stipple_mode.TabIndex = 35; + // + // label56 + // + this.label56.AutoSize = true; + this.label56.Location = new System.Drawing.Point(3, 118); + this.label56.Name = "label56"; + this.label56.Size = new System.Drawing.Size(86, 13); + this.label56.TabIndex = 26; + this.label56.Text = "LOD calculation:"; + // + // Glide64mk2_lodmode + // + this.Glide64mk2_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_lodmode.FormattingEnabled = true; + this.Glide64mk2_lodmode.Items.AddRange(new object[] { "Off", "Fast", "Precise"}); - this.Glide64mk2_lodmode.Location = new System.Drawing.Point(92, 115); - this.Glide64mk2_lodmode.Name = "Glide64mk2_lodmode"; - this.Glide64mk2_lodmode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_lodmode.TabIndex = 25; - // - // label58 - // - this.label58.AutoSize = true; - this.label58.Location = new System.Drawing.Point(3, 65); - this.label58.Name = "label58"; - this.label58.Size = new System.Drawing.Size(75, 13); - this.label58.TabIndex = 22; - this.label58.Text = "Filtering mode:"; - // - // Glide64mk2_filtering - // - this.Glide64mk2_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_filtering.FormattingEnabled = true; - this.Glide64mk2_filtering.Items.AddRange(new object[] { + this.Glide64mk2_lodmode.Location = new System.Drawing.Point(92, 115); + this.Glide64mk2_lodmode.Name = "Glide64mk2_lodmode"; + this.Glide64mk2_lodmode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_lodmode.TabIndex = 25; + // + // label58 + // + this.label58.AutoSize = true; + this.label58.Location = new System.Drawing.Point(3, 65); + this.label58.Name = "label58"; + this.label58.Size = new System.Drawing.Size(75, 13); + this.label58.TabIndex = 22; + this.label58.Text = "Filtering mode:"; + // + // Glide64mk2_filtering + // + this.Glide64mk2_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_filtering.FormattingEnabled = true; + this.Glide64mk2_filtering.Items.AddRange(new object[] { "None", "Force bilinear", "Force point-sampled"}); - this.Glide64mk2_filtering.Location = new System.Drawing.Point(92, 62); - this.Glide64mk2_filtering.Name = "Glide64mk2_filtering"; - this.Glide64mk2_filtering.Size = new System.Drawing.Size(138, 21); - this.Glide64mk2_filtering.TabIndex = 21; - // - // Glide64mk2_UseDefaultHacks2 - // - this.Glide64mk2_UseDefaultHacks2.AutoSize = true; - this.Glide64mk2_UseDefaultHacks2.Location = new System.Drawing.Point(6, 6); - this.Glide64mk2_UseDefaultHacks2.Name = "Glide64mk2_UseDefaultHacks2"; - this.Glide64mk2_UseDefaultHacks2.Size = new System.Drawing.Size(165, 17); - this.Glide64mk2_UseDefaultHacks2.TabIndex = 3; - this.Glide64mk2_UseDefaultHacks2.Text = "Use defaults for current game"; - this.Glide64mk2_UseDefaultHacks2.UseVisualStyleBackColor = true; - this.Glide64mk2_UseDefaultHacks2.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks2_CheckedChanged); - // - // Glide64TabPage - // - this.Glide64TabPage.Controls.Add(this.tabControl1); - this.Glide64TabPage.Location = new System.Drawing.Point(4, 22); - this.Glide64TabPage.Name = "Glide64TabPage"; - this.Glide64TabPage.Padding = new System.Windows.Forms.Padding(3); - this.Glide64TabPage.Size = new System.Drawing.Size(572, 343); - this.Glide64TabPage.TabIndex = 5; - this.Glide64TabPage.Text = "Glide64"; - this.Glide64TabPage.UseVisualStyleBackColor = true; - // - // tabControl1 - // - this.tabControl1.Controls.Add(this.Glide64General); - this.tabControl1.Controls.Add(this.GlidePerGameHacks1); - this.tabControl1.Controls.Add(this.GlidePerGameHacks2); - this.tabControl1.Location = new System.Drawing.Point(0, 3); - this.tabControl1.Name = "tabControl1"; - this.tabControl1.SelectedIndex = 0; - this.tabControl1.Size = new System.Drawing.Size(572, 336); - this.tabControl1.TabIndex = 0; - // - // Glide64General - // - this.Glide64General.Controls.Add(this.label39); - this.Glide64General.Controls.Add(this.Glide_scale_y); - this.Glide64General.Controls.Add(this.label40); - this.Glide64General.Controls.Add(this.Glide_scale_x); - this.Glide64General.Controls.Add(this.label38); - this.Glide64General.Controls.Add(this.Glide_offset_y); - this.Glide64General.Controls.Add(this.label37); - this.Glide64General.Controls.Add(this.Glide_offset_x); - this.Glide64General.Controls.Add(this.Glide_fb_get_info); - this.Glide64General.Controls.Add(this.Glide_disable_auxbuf); - this.Glide64General.Controls.Add(this.Glide_fbo); - this.Glide64General.Controls.Add(this.Glide_noglsl); - this.Glide64General.Controls.Add(this.Glide_noditheredalpha); - this.Glide64General.Controls.Add(this.label32); - this.Glide64General.Controls.Add(this.Glide_tex_filter); - this.Glide64General.Controls.Add(this.Glide_fb_render); - this.Glide64General.Controls.Add(this.Glide_motionblur); - this.Glide64General.Controls.Add(this.Glide_fb_read_always); - this.Glide64General.Controls.Add(this.Glide_unk_as_red); - this.Glide64General.Controls.Add(this.Glide_filter_cache); - this.Glide64General.Controls.Add(this.Glide_fast_crc); - this.Glide64General.Controls.Add(this.label31); - this.Glide64General.Controls.Add(this.Glide_wfmode); - this.Glide64General.Controls.Add(this.Glide_wireframe); - this.Glide64General.Controls.Add(this.label30); - this.Glide64General.Controls.Add(this.Glide_card_id); - this.Glide64General.Controls.Add(this.Glide_flame_corona); - this.Glide64General.Controls.Add(this.label29); - this.Glide64General.Controls.Add(this.Glide_ucode); - this.Glide64General.Controls.Add(this.Glide_autodetect_ucode); - this.Glide64General.Location = new System.Drawing.Point(4, 22); - this.Glide64General.Name = "Glide64General"; - this.Glide64General.Padding = new System.Windows.Forms.Padding(3); - this.Glide64General.Size = new System.Drawing.Size(564, 310); - this.Glide64General.TabIndex = 0; - this.Glide64General.Text = "General"; - this.Glide64General.UseVisualStyleBackColor = true; - // - // label39 - // - this.label39.AutoSize = true; - this.label39.Location = new System.Drawing.Point(386, 265); - this.label39.Name = "label39"; - this.label39.Size = new System.Drawing.Size(47, 13); - this.label39.TabIndex = 42; - this.label39.Text = "Scale Y:"; - // - // Glide_scale_y - // - this.Glide_scale_y.Location = new System.Drawing.Point(455, 262); - this.Glide_scale_y.Name = "Glide_scale_y"; - this.Glide_scale_y.Size = new System.Drawing.Size(56, 20); - this.Glide_scale_y.TabIndex = 41; - // - // label40 - // - this.label40.AutoSize = true; - this.label40.ForeColor = System.Drawing.SystemColors.ControlText; - this.label40.Location = new System.Drawing.Point(386, 239); - this.label40.Name = "label40"; - this.label40.Size = new System.Drawing.Size(47, 13); - this.label40.TabIndex = 40; - this.label40.Text = "Scale X:"; - // - // Glide_scale_x - // - this.Glide_scale_x.Location = new System.Drawing.Point(455, 236); - this.Glide_scale_x.Name = "Glide_scale_x"; - this.Glide_scale_x.Size = new System.Drawing.Size(56, 20); - this.Glide_scale_x.TabIndex = 39; - // - // label38 - // - this.label38.AutoSize = true; - this.label38.Location = new System.Drawing.Point(386, 213); - this.label38.Name = "label38"; - this.label38.Size = new System.Drawing.Size(48, 13); - this.label38.TabIndex = 38; - this.label38.Text = "Offset Y:"; - // - // Glide_offset_y - // - this.Glide_offset_y.Location = new System.Drawing.Point(455, 210); - this.Glide_offset_y.Name = "Glide_offset_y"; - this.Glide_offset_y.Size = new System.Drawing.Size(56, 20); - this.Glide_offset_y.TabIndex = 37; - // - // label37 - // - this.label37.AutoSize = true; - this.label37.Location = new System.Drawing.Point(386, 187); - this.label37.Name = "label37"; - this.label37.Size = new System.Drawing.Size(48, 13); - this.label37.TabIndex = 36; - this.label37.Text = "Offset X:"; - // - // Glide_offset_x - // - this.Glide_offset_x.Location = new System.Drawing.Point(455, 184); - this.Glide_offset_x.Name = "Glide_offset_x"; - this.Glide_offset_x.Size = new System.Drawing.Size(56, 20); - this.Glide_offset_x.TabIndex = 35; - // - // Glide_fb_get_info - // - this.Glide_fb_get_info.AutoSize = true; - this.Glide_fb_get_info.Location = new System.Drawing.Point(9, 278); - this.Glide_fb_get_info.Name = "Glide_fb_get_info"; - this.Glide_fb_get_info.Size = new System.Drawing.Size(119, 17); - this.Glide_fb_get_info.TabIndex = 23; - this.Glide_fb_get_info.Text = "Get framebuffer info"; - this.Glide_fb_get_info.UseVisualStyleBackColor = true; - // - // Glide_disable_auxbuf - // - this.Glide_disable_auxbuf.AutoSize = true; - this.Glide_disable_auxbuf.Location = new System.Drawing.Point(199, 278); - this.Glide_disable_auxbuf.Name = "Glide_disable_auxbuf"; - this.Glide_disable_auxbuf.Size = new System.Drawing.Size(111, 17); - this.Glide_disable_auxbuf.TabIndex = 22; - this.Glide_disable_auxbuf.Text = "Disable aux buffer"; - this.Glide_disable_auxbuf.UseVisualStyleBackColor = true; - // - // Glide_fbo - // - this.Glide_fbo.AutoSize = true; - this.Glide_fbo.Location = new System.Drawing.Point(199, 255); - this.Glide_fbo.Name = "Glide_fbo"; - this.Glide_fbo.Size = new System.Drawing.Size(138, 17); - this.Glide_fbo.TabIndex = 21; - this.Glide_fbo.Text = "Use framebuffer objects"; - this.Glide_fbo.UseVisualStyleBackColor = true; - // - // Glide_noglsl - // - this.Glide_noglsl.AutoSize = true; - this.Glide_noglsl.Location = new System.Drawing.Point(199, 232); - this.Glide_noglsl.Name = "Glide_noglsl"; - this.Glide_noglsl.Size = new System.Drawing.Size(142, 17); - this.Glide_noglsl.TabIndex = 20; - this.Glide_noglsl.Text = "Disable GLSL combiners"; - this.Glide_noglsl.UseVisualStyleBackColor = true; - // - // Glide_noditheredalpha - // - this.Glide_noditheredalpha.AutoSize = true; - this.Glide_noditheredalpha.Location = new System.Drawing.Point(199, 209); - this.Glide_noditheredalpha.Name = "Glide_noditheredalpha"; - this.Glide_noditheredalpha.Size = new System.Drawing.Size(131, 17); - this.Glide_noditheredalpha.TabIndex = 19; - this.Glide_noditheredalpha.Text = "Disable dithered alpha"; - this.Glide_noditheredalpha.UseVisualStyleBackColor = true; - // - // label32 - // - this.label32.AutoSize = true; - this.label32.Location = new System.Drawing.Point(206, 89); - this.label32.Name = "label32"; - this.label32.Size = new System.Drawing.Size(85, 13); - this.label32.TabIndex = 18; - this.label32.Text = "Texture Filtering:"; - // - // Glide_tex_filter - // - this.Glide_tex_filter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_tex_filter.FormattingEnabled = true; - this.Glide_tex_filter.Items.AddRange(new object[] { + this.Glide64mk2_filtering.Location = new System.Drawing.Point(92, 62); + this.Glide64mk2_filtering.Name = "Glide64mk2_filtering"; + this.Glide64mk2_filtering.Size = new System.Drawing.Size(138, 21); + this.Glide64mk2_filtering.TabIndex = 21; + // + // Glide64mk2_UseDefaultHacks2 + // + this.Glide64mk2_UseDefaultHacks2.AutoSize = true; + this.Glide64mk2_UseDefaultHacks2.Location = new System.Drawing.Point(6, 6); + this.Glide64mk2_UseDefaultHacks2.Name = "Glide64mk2_UseDefaultHacks2"; + this.Glide64mk2_UseDefaultHacks2.Size = new System.Drawing.Size(165, 17); + this.Glide64mk2_UseDefaultHacks2.TabIndex = 3; + this.Glide64mk2_UseDefaultHacks2.Text = "Use defaults for current game"; + this.Glide64mk2_UseDefaultHacks2.UseVisualStyleBackColor = true; + this.Glide64mk2_UseDefaultHacks2.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks2_CheckedChanged); + // + // Glide64TabPage + // + this.Glide64TabPage.Controls.Add(this.tabControl1); + this.Glide64TabPage.Location = new System.Drawing.Point(4, 22); + this.Glide64TabPage.Name = "Glide64TabPage"; + this.Glide64TabPage.Padding = new System.Windows.Forms.Padding(3); + this.Glide64TabPage.Size = new System.Drawing.Size(572, 343); + this.Glide64TabPage.TabIndex = 5; + this.Glide64TabPage.Text = "Glide64"; + this.Glide64TabPage.UseVisualStyleBackColor = true; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.Glide64General); + this.tabControl1.Controls.Add(this.GlidePerGameHacks1); + this.tabControl1.Controls.Add(this.GlidePerGameHacks2); + this.tabControl1.Location = new System.Drawing.Point(0, 3); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(572, 336); + this.tabControl1.TabIndex = 0; + // + // Glide64General + // + this.Glide64General.Controls.Add(this.label39); + this.Glide64General.Controls.Add(this.Glide_scale_y); + this.Glide64General.Controls.Add(this.label40); + this.Glide64General.Controls.Add(this.Glide_scale_x); + this.Glide64General.Controls.Add(this.label38); + this.Glide64General.Controls.Add(this.Glide_offset_y); + this.Glide64General.Controls.Add(this.label37); + this.Glide64General.Controls.Add(this.Glide_offset_x); + this.Glide64General.Controls.Add(this.Glide_fb_get_info); + this.Glide64General.Controls.Add(this.Glide_disable_auxbuf); + this.Glide64General.Controls.Add(this.Glide_fbo); + this.Glide64General.Controls.Add(this.Glide_noglsl); + this.Glide64General.Controls.Add(this.Glide_noditheredalpha); + this.Glide64General.Controls.Add(this.label32); + this.Glide64General.Controls.Add(this.Glide_tex_filter); + this.Glide64General.Controls.Add(this.Glide_fb_render); + this.Glide64General.Controls.Add(this.Glide_motionblur); + this.Glide64General.Controls.Add(this.Glide_fb_read_always); + this.Glide64General.Controls.Add(this.Glide_unk_as_red); + this.Glide64General.Controls.Add(this.Glide_filter_cache); + this.Glide64General.Controls.Add(this.Glide_fast_crc); + this.Glide64General.Controls.Add(this.label31); + this.Glide64General.Controls.Add(this.Glide_wfmode); + this.Glide64General.Controls.Add(this.Glide_wireframe); + this.Glide64General.Controls.Add(this.label30); + this.Glide64General.Controls.Add(this.Glide_card_id); + this.Glide64General.Controls.Add(this.Glide_flame_corona); + this.Glide64General.Controls.Add(this.label29); + this.Glide64General.Controls.Add(this.Glide_ucode); + this.Glide64General.Controls.Add(this.Glide_autodetect_ucode); + this.Glide64General.Location = new System.Drawing.Point(4, 22); + this.Glide64General.Name = "Glide64General"; + this.Glide64General.Padding = new System.Windows.Forms.Padding(3); + this.Glide64General.Size = new System.Drawing.Size(564, 310); + this.Glide64General.TabIndex = 0; + this.Glide64General.Text = "General"; + this.Glide64General.UseVisualStyleBackColor = true; + // + // label39 + // + this.label39.AutoSize = true; + this.label39.Location = new System.Drawing.Point(386, 265); + this.label39.Name = "label39"; + this.label39.Size = new System.Drawing.Size(47, 13); + this.label39.TabIndex = 42; + this.label39.Text = "Scale Y:"; + // + // Glide_scale_y + // + this.Glide_scale_y.Location = new System.Drawing.Point(455, 262); + this.Glide_scale_y.Name = "Glide_scale_y"; + this.Glide_scale_y.Size = new System.Drawing.Size(56, 20); + this.Glide_scale_y.TabIndex = 41; + // + // label40 + // + this.label40.AutoSize = true; + this.label40.ForeColor = System.Drawing.SystemColors.ControlText; + this.label40.Location = new System.Drawing.Point(386, 239); + this.label40.Name = "label40"; + this.label40.Size = new System.Drawing.Size(47, 13); + this.label40.TabIndex = 40; + this.label40.Text = "Scale X:"; + // + // Glide_scale_x + // + this.Glide_scale_x.Location = new System.Drawing.Point(455, 236); + this.Glide_scale_x.Name = "Glide_scale_x"; + this.Glide_scale_x.Size = new System.Drawing.Size(56, 20); + this.Glide_scale_x.TabIndex = 39; + // + // label38 + // + this.label38.AutoSize = true; + this.label38.Location = new System.Drawing.Point(386, 213); + this.label38.Name = "label38"; + this.label38.Size = new System.Drawing.Size(48, 13); + this.label38.TabIndex = 38; + this.label38.Text = "Offset Y:"; + // + // Glide_offset_y + // + this.Glide_offset_y.Location = new System.Drawing.Point(455, 210); + this.Glide_offset_y.Name = "Glide_offset_y"; + this.Glide_offset_y.Size = new System.Drawing.Size(56, 20); + this.Glide_offset_y.TabIndex = 37; + // + // label37 + // + this.label37.AutoSize = true; + this.label37.Location = new System.Drawing.Point(386, 187); + this.label37.Name = "label37"; + this.label37.Size = new System.Drawing.Size(48, 13); + this.label37.TabIndex = 36; + this.label37.Text = "Offset X:"; + // + // Glide_offset_x + // + this.Glide_offset_x.Location = new System.Drawing.Point(455, 184); + this.Glide_offset_x.Name = "Glide_offset_x"; + this.Glide_offset_x.Size = new System.Drawing.Size(56, 20); + this.Glide_offset_x.TabIndex = 35; + // + // Glide_fb_get_info + // + this.Glide_fb_get_info.AutoSize = true; + this.Glide_fb_get_info.Location = new System.Drawing.Point(9, 278); + this.Glide_fb_get_info.Name = "Glide_fb_get_info"; + this.Glide_fb_get_info.Size = new System.Drawing.Size(119, 17); + this.Glide_fb_get_info.TabIndex = 23; + this.Glide_fb_get_info.Text = "Get framebuffer info"; + this.Glide_fb_get_info.UseVisualStyleBackColor = true; + // + // Glide_disable_auxbuf + // + this.Glide_disable_auxbuf.AutoSize = true; + this.Glide_disable_auxbuf.Location = new System.Drawing.Point(199, 278); + this.Glide_disable_auxbuf.Name = "Glide_disable_auxbuf"; + this.Glide_disable_auxbuf.Size = new System.Drawing.Size(111, 17); + this.Glide_disable_auxbuf.TabIndex = 22; + this.Glide_disable_auxbuf.Text = "Disable aux buffer"; + this.Glide_disable_auxbuf.UseVisualStyleBackColor = true; + // + // Glide_fbo + // + this.Glide_fbo.AutoSize = true; + this.Glide_fbo.Location = new System.Drawing.Point(199, 255); + this.Glide_fbo.Name = "Glide_fbo"; + this.Glide_fbo.Size = new System.Drawing.Size(138, 17); + this.Glide_fbo.TabIndex = 21; + this.Glide_fbo.Text = "Use framebuffer objects"; + this.Glide_fbo.UseVisualStyleBackColor = true; + // + // Glide_noglsl + // + this.Glide_noglsl.AutoSize = true; + this.Glide_noglsl.Location = new System.Drawing.Point(199, 232); + this.Glide_noglsl.Name = "Glide_noglsl"; + this.Glide_noglsl.Size = new System.Drawing.Size(142, 17); + this.Glide_noglsl.TabIndex = 20; + this.Glide_noglsl.Text = "Disable GLSL combiners"; + this.Glide_noglsl.UseVisualStyleBackColor = true; + // + // Glide_noditheredalpha + // + this.Glide_noditheredalpha.AutoSize = true; + this.Glide_noditheredalpha.Location = new System.Drawing.Point(199, 209); + this.Glide_noditheredalpha.Name = "Glide_noditheredalpha"; + this.Glide_noditheredalpha.Size = new System.Drawing.Size(131, 17); + this.Glide_noditheredalpha.TabIndex = 19; + this.Glide_noditheredalpha.Text = "Disable dithered alpha"; + this.Glide_noditheredalpha.UseVisualStyleBackColor = true; + // + // label32 + // + this.label32.AutoSize = true; + this.label32.Location = new System.Drawing.Point(206, 89); + this.label32.Name = "label32"; + this.label32.Size = new System.Drawing.Size(85, 13); + this.label32.TabIndex = 18; + this.label32.Text = "Texture Filtering:"; + // + // Glide_tex_filter + // + this.Glide_tex_filter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_tex_filter.FormattingEnabled = true; + this.Glide_tex_filter.Items.AddRange(new object[] { "None", "Blur edges", "Super 2xSai", "Hq2x", "Hq4x"}); - this.Glide_tex_filter.Location = new System.Drawing.Point(297, 86); - this.Glide_tex_filter.Name = "Glide_tex_filter"; - this.Glide_tex_filter.Size = new System.Drawing.Size(95, 21); - this.Glide_tex_filter.TabIndex = 17; - // - // Glide_fb_render - // - this.Glide_fb_render.AutoSize = true; - this.Glide_fb_render.Location = new System.Drawing.Point(199, 186); - this.Glide_fb_render.Name = "Glide_fb_render"; - this.Glide_fb_render.Size = new System.Drawing.Size(118, 17); - this.Glide_fb_render.TabIndex = 16; - this.Glide_fb_render.Text = "Depth buffer render"; - this.Glide_fb_render.UseVisualStyleBackColor = true; - // - // Glide_motionblur - // - this.Glide_motionblur.AutoSize = true; - this.Glide_motionblur.Location = new System.Drawing.Point(9, 255); - this.Glide_motionblur.Name = "Glide_motionblur"; - this.Glide_motionblur.Size = new System.Drawing.Size(79, 17); - this.Glide_motionblur.TabIndex = 13; - this.Glide_motionblur.Text = "Motion Blur"; - this.Glide_motionblur.UseVisualStyleBackColor = true; - // - // Glide_fb_read_always - // - this.Glide_fb_read_always.AutoSize = true; - this.Glide_fb_read_always.Location = new System.Drawing.Point(9, 232); - this.Glide_fb_read_always.Name = "Glide_fb_read_always"; - this.Glide_fb_read_always.Size = new System.Drawing.Size(164, 17); - this.Glide_fb_read_always.TabIndex = 12; - this.Glide_fb_read_always.Text = "Framebuffer read every frame"; - this.Glide_fb_read_always.UseVisualStyleBackColor = true; - // - // Glide_unk_as_red - // - this.Glide_unk_as_red.AutoSize = true; - this.Glide_unk_as_red.Location = new System.Drawing.Point(9, 209); - this.Glide_unk_as_red.Name = "Glide_unk_as_red"; - this.Glide_unk_as_red.Size = new System.Drawing.Size(187, 17); - this.Glide_unk_as_red.TabIndex = 11; - this.Glide_unk_as_red.Text = "Display unknown combines as red"; - this.Glide_unk_as_red.UseVisualStyleBackColor = true; - // - // Glide_filter_cache - // - this.Glide_filter_cache.AutoSize = true; - this.Glide_filter_cache.Location = new System.Drawing.Point(9, 186); - this.Glide_filter_cache.Name = "Glide_filter_cache"; - this.Glide_filter_cache.Size = new System.Drawing.Size(82, 17); - this.Glide_filter_cache.TabIndex = 10; - this.Glide_filter_cache.Text = "Filter Cache"; - this.Glide_filter_cache.UseVisualStyleBackColor = true; - // - // Glide_fast_crc - // - this.Glide_fast_crc.AutoSize = true; - this.Glide_fast_crc.Location = new System.Drawing.Point(9, 163); - this.Glide_fast_crc.Name = "Glide_fast_crc"; - this.Glide_fast_crc.Size = new System.Drawing.Size(71, 17); - this.Glide_fast_crc.TabIndex = 9; - this.Glide_fast_crc.Text = "Fast CRC"; - this.Glide_fast_crc.UseVisualStyleBackColor = true; - // - // label31 - // - this.label31.AutoSize = true; - this.label31.Location = new System.Drawing.Point(6, 139); - this.label31.Name = "label31"; - this.label31.Size = new System.Drawing.Size(88, 13); - this.label31.TabIndex = 8; - this.label31.Text = "Wireframe Mode:"; - // - // Glide_wfmode - // - this.Glide_wfmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_wfmode.FormattingEnabled = true; - this.Glide_wfmode.Items.AddRange(new object[] { + this.Glide_tex_filter.Location = new System.Drawing.Point(297, 86); + this.Glide_tex_filter.Name = "Glide_tex_filter"; + this.Glide_tex_filter.Size = new System.Drawing.Size(95, 21); + this.Glide_tex_filter.TabIndex = 17; + // + // Glide_fb_render + // + this.Glide_fb_render.AutoSize = true; + this.Glide_fb_render.Location = new System.Drawing.Point(199, 186); + this.Glide_fb_render.Name = "Glide_fb_render"; + this.Glide_fb_render.Size = new System.Drawing.Size(118, 17); + this.Glide_fb_render.TabIndex = 16; + this.Glide_fb_render.Text = "Depth buffer render"; + this.Glide_fb_render.UseVisualStyleBackColor = true; + // + // Glide_motionblur + // + this.Glide_motionblur.AutoSize = true; + this.Glide_motionblur.Location = new System.Drawing.Point(9, 255); + this.Glide_motionblur.Name = "Glide_motionblur"; + this.Glide_motionblur.Size = new System.Drawing.Size(79, 17); + this.Glide_motionblur.TabIndex = 13; + this.Glide_motionblur.Text = "Motion Blur"; + this.Glide_motionblur.UseVisualStyleBackColor = true; + // + // Glide_fb_read_always + // + this.Glide_fb_read_always.AutoSize = true; + this.Glide_fb_read_always.Location = new System.Drawing.Point(9, 232); + this.Glide_fb_read_always.Name = "Glide_fb_read_always"; + this.Glide_fb_read_always.Size = new System.Drawing.Size(164, 17); + this.Glide_fb_read_always.TabIndex = 12; + this.Glide_fb_read_always.Text = "Framebuffer read every frame"; + this.Glide_fb_read_always.UseVisualStyleBackColor = true; + // + // Glide_unk_as_red + // + this.Glide_unk_as_red.AutoSize = true; + this.Glide_unk_as_red.Location = new System.Drawing.Point(9, 209); + this.Glide_unk_as_red.Name = "Glide_unk_as_red"; + this.Glide_unk_as_red.Size = new System.Drawing.Size(187, 17); + this.Glide_unk_as_red.TabIndex = 11; + this.Glide_unk_as_red.Text = "Display unknown combines as red"; + this.Glide_unk_as_red.UseVisualStyleBackColor = true; + // + // Glide_filter_cache + // + this.Glide_filter_cache.AutoSize = true; + this.Glide_filter_cache.Location = new System.Drawing.Point(9, 186); + this.Glide_filter_cache.Name = "Glide_filter_cache"; + this.Glide_filter_cache.Size = new System.Drawing.Size(82, 17); + this.Glide_filter_cache.TabIndex = 10; + this.Glide_filter_cache.Text = "Filter Cache"; + this.Glide_filter_cache.UseVisualStyleBackColor = true; + // + // Glide_fast_crc + // + this.Glide_fast_crc.AutoSize = true; + this.Glide_fast_crc.Location = new System.Drawing.Point(9, 163); + this.Glide_fast_crc.Name = "Glide_fast_crc"; + this.Glide_fast_crc.Size = new System.Drawing.Size(71, 17); + this.Glide_fast_crc.TabIndex = 9; + this.Glide_fast_crc.Text = "Fast CRC"; + this.Glide_fast_crc.UseVisualStyleBackColor = true; + // + // label31 + // + this.label31.AutoSize = true; + this.label31.Location = new System.Drawing.Point(6, 139); + this.label31.Name = "label31"; + this.label31.Size = new System.Drawing.Size(88, 13); + this.label31.TabIndex = 8; + this.label31.Text = "Wireframe Mode:"; + // + // Glide_wfmode + // + this.Glide_wfmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_wfmode.FormattingEnabled = true; + this.Glide_wfmode.Items.AddRange(new object[] { "Normal colors", "Vertex colors", "Red only"}); - this.Glide_wfmode.Location = new System.Drawing.Point(100, 136); - this.Glide_wfmode.Name = "Glide_wfmode"; - this.Glide_wfmode.Size = new System.Drawing.Size(104, 21); - this.Glide_wfmode.TabIndex = 7; - // - // Glide_wireframe - // - this.Glide_wireframe.AutoSize = true; - this.Glide_wireframe.ForeColor = System.Drawing.SystemColors.ControlText; - this.Glide_wireframe.Location = new System.Drawing.Point(9, 113); - this.Glide_wireframe.Name = "Glide_wireframe"; - this.Glide_wireframe.Size = new System.Drawing.Size(110, 17); - this.Glide_wireframe.TabIndex = 6; - this.Glide_wireframe.Text = "Enable Wireframe"; - this.Glide_wireframe.UseVisualStyleBackColor = true; - // - // label30 - // - this.label30.AutoSize = true; - this.label30.Location = new System.Drawing.Point(6, 89); - this.label30.Name = "label30"; - this.label30.Size = new System.Drawing.Size(46, 13); - this.label30.TabIndex = 5; - this.label30.Text = "Card ID:"; - // - // Glide_card_id - // - this.Glide_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_card_id.FormattingEnabled = true; - this.Glide_card_id.Items.AddRange(new object[] { + this.Glide_wfmode.Location = new System.Drawing.Point(100, 136); + this.Glide_wfmode.Name = "Glide_wfmode"; + this.Glide_wfmode.Size = new System.Drawing.Size(104, 21); + this.Glide_wfmode.TabIndex = 7; + // + // Glide_wireframe + // + this.Glide_wireframe.AutoSize = true; + this.Glide_wireframe.ForeColor = System.Drawing.SystemColors.ControlText; + this.Glide_wireframe.Location = new System.Drawing.Point(9, 113); + this.Glide_wireframe.Name = "Glide_wireframe"; + this.Glide_wireframe.Size = new System.Drawing.Size(110, 17); + this.Glide_wireframe.TabIndex = 6; + this.Glide_wireframe.Text = "Enable Wireframe"; + this.Glide_wireframe.UseVisualStyleBackColor = true; + // + // label30 + // + this.label30.AutoSize = true; + this.label30.Location = new System.Drawing.Point(6, 89); + this.label30.Name = "label30"; + this.label30.Size = new System.Drawing.Size(46, 13); + this.label30.TabIndex = 5; + this.label30.Text = "Card ID:"; + // + // Glide_card_id + // + this.Glide_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_card_id.FormattingEnabled = true; + this.Glide_card_id.Items.AddRange(new object[] { "0", "1", "2", "3"}); - this.Glide_card_id.Location = new System.Drawing.Point(58, 86); - this.Glide_card_id.Name = "Glide_card_id"; - this.Glide_card_id.Size = new System.Drawing.Size(36, 21); - this.Glide_card_id.TabIndex = 4; - // - // Glide_flame_corona - // - this.Glide_flame_corona.AutoSize = true; - this.Glide_flame_corona.Location = new System.Drawing.Point(9, 63); - this.Glide_flame_corona.Name = "Glide_flame_corona"; - this.Glide_flame_corona.Size = new System.Drawing.Size(103, 17); - this.Glide_flame_corona.TabIndex = 3; - this.Glide_flame_corona.Text = "Zelda Corona fix"; - this.Glide_flame_corona.UseVisualStyleBackColor = true; - // - // label29 - // - this.label29.AutoSize = true; - this.label29.Location = new System.Drawing.Point(6, 39); - this.label29.Name = "label29"; - this.label29.Size = new System.Drawing.Size(90, 13); - this.label29.TabIndex = 2; - this.label29.Text = "Force Microcode:"; - // - // Glide_ucode - // - this.Glide_ucode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_ucode.FormattingEnabled = true; - this.Glide_ucode.Items.AddRange(new object[] { + this.Glide_card_id.Location = new System.Drawing.Point(58, 86); + this.Glide_card_id.Name = "Glide_card_id"; + this.Glide_card_id.Size = new System.Drawing.Size(36, 21); + this.Glide_card_id.TabIndex = 4; + // + // Glide_flame_corona + // + this.Glide_flame_corona.AutoSize = true; + this.Glide_flame_corona.Location = new System.Drawing.Point(9, 63); + this.Glide_flame_corona.Name = "Glide_flame_corona"; + this.Glide_flame_corona.Size = new System.Drawing.Size(103, 17); + this.Glide_flame_corona.TabIndex = 3; + this.Glide_flame_corona.Text = "Zelda Corona fix"; + this.Glide_flame_corona.UseVisualStyleBackColor = true; + // + // label29 + // + this.label29.AutoSize = true; + this.label29.Location = new System.Drawing.Point(6, 39); + this.label29.Name = "label29"; + this.label29.Size = new System.Drawing.Size(90, 13); + this.label29.TabIndex = 2; + this.label29.Text = "Force Microcode:"; + // + // Glide_ucode + // + this.Glide_ucode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_ucode.FormattingEnabled = true; + this.Glide_ucode.Items.AddRange(new object[] { "0: RSP SW 2.0X (ex. Super Mario 64)", "1: F3DEX 1.XX (ex. Star Fox 64)", "2: F3DEX 2.XX (ex. Zelda: OOT)", @@ -2477,336 +2479,336 @@ "6: S2DEX 1.XX (ex. Yoshi\'s Story)", "7: RSP SW PD (ex. Perfect Dark)", "8: F3DEXBG 2.08 (ex. Conker\'s Bad Fur Day)"}); - this.Glide_ucode.Location = new System.Drawing.Point(102, 36); - this.Glide_ucode.Name = "Glide_ucode"; - this.Glide_ucode.Size = new System.Drawing.Size(273, 21); - this.Glide_ucode.TabIndex = 1; - // - // Glide_autodetect_ucode - // - this.Glide_autodetect_ucode.AutoSize = true; - this.Glide_autodetect_ucode.Location = new System.Drawing.Point(9, 13); - this.Glide_autodetect_ucode.Name = "Glide_autodetect_ucode"; - this.Glide_autodetect_ucode.Size = new System.Drawing.Size(131, 17); - this.Glide_autodetect_ucode.TabIndex = 0; - this.Glide_autodetect_ucode.Text = "Autodetect Microcode"; - this.Glide_autodetect_ucode.UseVisualStyleBackColor = true; - // - // GlidePerGameHacks1 - // - this.GlidePerGameHacks1.Controls.Add(this.Glide_wrap_big_tex); - this.GlidePerGameHacks1.Controls.Add(this.Glide_use_sts1_only); - this.GlidePerGameHacks1.Controls.Add(this.Glide_soft_depth_compare); - this.GlidePerGameHacks1.Controls.Add(this.Glide_PPL); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_write); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_texrect); - this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_texrect_edge); - this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_primdepth); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_previous); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_aux_copy); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires_buf_clear); - this.GlidePerGameHacks1.Controls.Add(this.Glide_force_microcheck); - this.GlidePerGameHacks1.Controls.Add(this.Glide_force_depth_compare); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fog); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fillcolor_fix); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_smart); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_read_alpha); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_clear); - this.GlidePerGameHacks1.Controls.Add(this.Glide_detect_cpu_write); - this.GlidePerGameHacks1.Controls.Add(this.Glide_decrease_fillrect_edge); - this.GlidePerGameHacks1.Controls.Add(this.Glide_buff_clear); - this.GlidePerGameHacks1.Controls.Add(this.Glide_alt_tex_size); - this.GlidePerGameHacks1.Controls.Add(this.GlideUseDefaultHacks1); - this.GlidePerGameHacks1.Location = new System.Drawing.Point(4, 22); - this.GlidePerGameHacks1.Name = "GlidePerGameHacks1"; - this.GlidePerGameHacks1.Padding = new System.Windows.Forms.Padding(3); - this.GlidePerGameHacks1.Size = new System.Drawing.Size(564, 310); - this.GlidePerGameHacks1.TabIndex = 1; - this.GlidePerGameHacks1.Text = "Per Game Settings"; - this.GlidePerGameHacks1.UseVisualStyleBackColor = true; - // - // Glide_wrap_big_tex - // - this.Glide_wrap_big_tex.AutoSize = true; - this.Glide_wrap_big_tex.Location = new System.Drawing.Point(375, 62); - this.Glide_wrap_big_tex.Name = "Glide_wrap_big_tex"; - this.Glide_wrap_big_tex.Size = new System.Drawing.Size(86, 17); - this.Glide_wrap_big_tex.TabIndex = 33; - this.Glide_wrap_big_tex.Text = "Wrap big tex"; - this.Glide_wrap_big_tex.UseVisualStyleBackColor = true; - // - // Glide_use_sts1_only - // - this.Glide_use_sts1_only.AutoSize = true; - this.Glide_use_sts1_only.Location = new System.Drawing.Point(375, 39); - this.Glide_use_sts1_only.Name = "Glide_use_sts1_only"; - this.Glide_use_sts1_only.Size = new System.Drawing.Size(89, 17); - this.Glide_use_sts1_only.TabIndex = 32; - this.Glide_use_sts1_only.Text = "Use sts1 only"; - this.Glide_use_sts1_only.UseVisualStyleBackColor = true; - // - // Glide_soft_depth_compare - // - this.Glide_soft_depth_compare.AutoSize = true; - this.Glide_soft_depth_compare.Location = new System.Drawing.Point(192, 269); - this.Glide_soft_depth_compare.Name = "Glide_soft_depth_compare"; - this.Glide_soft_depth_compare.Size = new System.Drawing.Size(119, 17); - this.Glide_soft_depth_compare.TabIndex = 31; - this.Glide_soft_depth_compare.Text = "Soft depth compare"; - this.Glide_soft_depth_compare.UseVisualStyleBackColor = true; - // - // Glide_PPL - // - this.Glide_PPL.AutoSize = true; - this.Glide_PPL.Location = new System.Drawing.Point(192, 246); - this.Glide_PPL.Name = "Glide_PPL"; - this.Glide_PPL.Size = new System.Drawing.Size(46, 17); - this.Glide_PPL.TabIndex = 30; - this.Glide_PPL.Text = "PPL"; - this.Glide_PPL.UseVisualStyleBackColor = true; - // - // Glide_fb_optimize_write - // - this.Glide_fb_optimize_write.AutoSize = true; - this.Glide_fb_optimize_write.Location = new System.Drawing.Point(192, 223); - this.Glide_fb_optimize_write.Name = "Glide_fb_optimize_write"; - this.Glide_fb_optimize_write.Size = new System.Drawing.Size(91, 17); - this.Glide_fb_optimize_write.TabIndex = 29; - this.Glide_fb_optimize_write.Text = "Optimize write"; - this.Glide_fb_optimize_write.UseVisualStyleBackColor = true; - // - // Glide_fb_optimize_texrect - // - this.Glide_fb_optimize_texrect.AutoSize = true; - this.Glide_fb_optimize_texrect.Location = new System.Drawing.Point(192, 200); - this.Glide_fb_optimize_texrect.Name = "Glide_fb_optimize_texrect"; - this.Glide_fb_optimize_texrect.Size = new System.Drawing.Size(101, 17); - this.Glide_fb_optimize_texrect.TabIndex = 28; - this.Glide_fb_optimize_texrect.Text = "Optimize texrect"; - this.Glide_fb_optimize_texrect.UseVisualStyleBackColor = true; - // - // Glide_increase_texrect_edge - // - this.Glide_increase_texrect_edge.AutoSize = true; - this.Glide_increase_texrect_edge.Location = new System.Drawing.Point(192, 177); - this.Glide_increase_texrect_edge.Name = "Glide_increase_texrect_edge"; - this.Glide_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); - this.Glide_increase_texrect_edge.TabIndex = 27; - this.Glide_increase_texrect_edge.Text = "Increase texrect edge"; - this.Glide_increase_texrect_edge.UseVisualStyleBackColor = true; - // - // Glide_increase_primdepth - // - this.Glide_increase_primdepth.AutoSize = true; - this.Glide_increase_primdepth.Location = new System.Drawing.Point(192, 154); - this.Glide_increase_primdepth.Name = "Glide_increase_primdepth"; - this.Glide_increase_primdepth.Size = new System.Drawing.Size(116, 17); - this.Glide_increase_primdepth.TabIndex = 26; - this.Glide_increase_primdepth.Text = "Increase primdepth"; - this.Glide_increase_primdepth.UseVisualStyleBackColor = true; - // - // Glide_fb_ignore_previous - // - this.Glide_fb_ignore_previous.AutoSize = true; - this.Glide_fb_ignore_previous.Location = new System.Drawing.Point(192, 131); - this.Glide_fb_ignore_previous.Name = "Glide_fb_ignore_previous"; - this.Glide_fb_ignore_previous.Size = new System.Drawing.Size(100, 17); - this.Glide_fb_ignore_previous.TabIndex = 25; - this.Glide_fb_ignore_previous.Text = "Ignore Previous"; - this.Glide_fb_ignore_previous.UseVisualStyleBackColor = true; - // - // Glide_fb_ignore_aux_copy - // - this.Glide_fb_ignore_aux_copy.AutoSize = true; - this.Glide_fb_ignore_aux_copy.Location = new System.Drawing.Point(192, 108); - this.Glide_fb_ignore_aux_copy.Name = "Glide_fb_ignore_aux_copy"; - this.Glide_fb_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); - this.Glide_fb_ignore_aux_copy.TabIndex = 24; - this.Glide_fb_ignore_aux_copy.Text = "Ignore aux copy"; - this.Glide_fb_ignore_aux_copy.UseVisualStyleBackColor = true; - // - // Glide_fb_hires_buf_clear - // - this.Glide_fb_hires_buf_clear.AutoSize = true; - this.Glide_fb_hires_buf_clear.Location = new System.Drawing.Point(192, 85); - this.Glide_fb_hires_buf_clear.Name = "Glide_fb_hires_buf_clear"; - this.Glide_fb_hires_buf_clear.Size = new System.Drawing.Size(109, 17); - this.Glide_fb_hires_buf_clear.TabIndex = 23; - this.Glide_fb_hires_buf_clear.Text = "Hi-res buffer clear"; - this.Glide_fb_hires_buf_clear.UseVisualStyleBackColor = true; - // - // Glide_force_microcheck - // - this.Glide_force_microcheck.AutoSize = true; - this.Glide_force_microcheck.Location = new System.Drawing.Point(192, 62); - this.Glide_force_microcheck.Name = "Glide_force_microcheck"; - this.Glide_force_microcheck.Size = new System.Drawing.Size(165, 17); - this.Glide_force_microcheck.TabIndex = 22; - this.Glide_force_microcheck.Text = "Check microcode each frame"; - this.Glide_force_microcheck.UseVisualStyleBackColor = true; - // - // Glide_force_depth_compare - // - this.Glide_force_depth_compare.AutoSize = true; - this.Glide_force_depth_compare.Location = new System.Drawing.Point(192, 39); - this.Glide_force_depth_compare.Name = "Glide_force_depth_compare"; - this.Glide_force_depth_compare.Size = new System.Drawing.Size(130, 17); - this.Glide_force_depth_compare.TabIndex = 21; - this.Glide_force_depth_compare.Text = "Force Depth Compare"; - this.Glide_force_depth_compare.UseVisualStyleBackColor = true; - // - // Glide_fog - // - this.Glide_fog.AutoSize = true; - this.Glide_fog.Location = new System.Drawing.Point(6, 269); - this.Glide_fog.Name = "Glide_fog"; - this.Glide_fog.Size = new System.Drawing.Size(86, 17); - this.Glide_fog.TabIndex = 20; - this.Glide_fog.Text = "Fog Enabled"; - this.Glide_fog.UseVisualStyleBackColor = true; - // - // Glide_fillcolor_fix - // - this.Glide_fillcolor_fix.AutoSize = true; - this.Glide_fillcolor_fix.Location = new System.Drawing.Point(6, 246); - this.Glide_fillcolor_fix.Name = "Glide_fillcolor_fix"; - this.Glide_fillcolor_fix.Size = new System.Drawing.Size(77, 17); - this.Glide_fillcolor_fix.TabIndex = 19; - this.Glide_fillcolor_fix.Text = "Fillcolor Fix"; - this.Glide_fillcolor_fix.UseVisualStyleBackColor = true; - // - // Glide_fb_smart - // - this.Glide_fb_smart.AutoSize = true; - this.Glide_fb_smart.Location = new System.Drawing.Point(6, 223); - this.Glide_fb_smart.Name = "Glide_fb_smart"; - this.Glide_fb_smart.Size = new System.Drawing.Size(109, 17); - this.Glide_fb_smart.TabIndex = 18; - this.Glide_fb_smart.Text = "Smart framebuffer"; - this.Glide_fb_smart.UseVisualStyleBackColor = true; - // - // Glide_fb_read_alpha - // - this.Glide_fb_read_alpha.AutoSize = true; - this.Glide_fb_read_alpha.Location = new System.Drawing.Point(6, 200); - this.Glide_fb_read_alpha.Name = "Glide_fb_read_alpha"; - this.Glide_fb_read_alpha.Size = new System.Drawing.Size(135, 17); - this.Glide_fb_read_alpha.TabIndex = 17; - this.Glide_fb_read_alpha.Text = "Framebuffer read alpha"; - this.Glide_fb_read_alpha.UseVisualStyleBackColor = true; - // - // Glide_fb_hires - // - this.Glide_fb_hires.AutoSize = true; - this.Glide_fb_hires.Location = new System.Drawing.Point(6, 154); - this.Glide_fb_hires.Name = "Glide_fb_hires"; - this.Glide_fb_hires.Size = new System.Drawing.Size(109, 17); - this.Glide_fb_hires.TabIndex = 15; - this.Glide_fb_hires.Text = "Hi-res framebuffer"; - this.Glide_fb_hires.UseVisualStyleBackColor = true; - // - // Glide_fb_clear - // - this.Glide_fb_clear.AutoSize = true; - this.Glide_fb_clear.Location = new System.Drawing.Point(6, 131); - this.Glide_fb_clear.Name = "Glide_fb_clear"; - this.Glide_fb_clear.Size = new System.Drawing.Size(106, 17); - this.Glide_fb_clear.TabIndex = 7; - this.Glide_fb_clear.Text = "Clear framebuffer"; - this.Glide_fb_clear.UseVisualStyleBackColor = true; - // - // Glide_detect_cpu_write - // - this.Glide_detect_cpu_write.AutoSize = true; - this.Glide_detect_cpu_write.Location = new System.Drawing.Point(6, 108); - this.Glide_detect_cpu_write.Name = "Glide_detect_cpu_write"; - this.Glide_detect_cpu_write.Size = new System.Drawing.Size(113, 17); - this.Glide_detect_cpu_write.TabIndex = 6; - this.Glide_detect_cpu_write.Text = "Detect CPU writes"; - this.Glide_detect_cpu_write.UseVisualStyleBackColor = true; - // - // Glide_decrease_fillrect_edge - // - this.Glide_decrease_fillrect_edge.AutoSize = true; - this.Glide_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); - this.Glide_decrease_fillrect_edge.Name = "Glide_decrease_fillrect_edge"; - this.Glide_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); - this.Glide_decrease_fillrect_edge.TabIndex = 5; - this.Glide_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; - this.Glide_decrease_fillrect_edge.UseVisualStyleBackColor = true; - // - // Glide_buff_clear - // - this.Glide_buff_clear.AutoSize = true; - this.Glide_buff_clear.Location = new System.Drawing.Point(6, 62); - this.Glide_buff_clear.Name = "Glide_buff_clear"; - this.Glide_buff_clear.Size = new System.Drawing.Size(153, 17); - this.Glide_buff_clear.TabIndex = 4; - this.Glide_buff_clear.Text = "Buffer clear on every frame"; - this.Glide_buff_clear.UseVisualStyleBackColor = true; - // - // Glide_alt_tex_size - // - this.Glide_alt_tex_size.AutoSize = true; - this.Glide_alt_tex_size.Location = new System.Drawing.Point(6, 39); - this.Glide_alt_tex_size.Name = "Glide_alt_tex_size"; - this.Glide_alt_tex_size.Size = new System.Drawing.Size(169, 17); - this.Glide_alt_tex_size.TabIndex = 3; - this.Glide_alt_tex_size.Text = "Alternate Texture Size Method"; - this.Glide_alt_tex_size.UseVisualStyleBackColor = true; - // - // GlideUseDefaultHacks1 - // - this.GlideUseDefaultHacks1.AutoSize = true; - this.GlideUseDefaultHacks1.Location = new System.Drawing.Point(6, 6); - this.GlideUseDefaultHacks1.Name = "GlideUseDefaultHacks1"; - this.GlideUseDefaultHacks1.Size = new System.Drawing.Size(165, 17); - this.GlideUseDefaultHacks1.TabIndex = 2; - this.GlideUseDefaultHacks1.Text = "Use defaults for current game"; - this.GlideUseDefaultHacks1.UseVisualStyleBackColor = true; - this.GlideUseDefaultHacks1.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks1_CheckedChanged); - // - // GlidePerGameHacks2 - // - this.GlidePerGameHacks2.Controls.Add(this.label44); - this.GlidePerGameHacks2.Controls.Add(this.Glide_enable_hacks_for_game); - this.GlidePerGameHacks2.Controls.Add(this.label43); - this.GlidePerGameHacks2.Controls.Add(this.Glide_swapmode); - this.GlidePerGameHacks2.Controls.Add(this.label42); - this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_pattern); - this.GlidePerGameHacks2.Controls.Add(this.label41); - this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_mode); - this.GlidePerGameHacks2.Controls.Add(this.label36); - this.GlidePerGameHacks2.Controls.Add(this.Glide_lodmode); - this.GlidePerGameHacks2.Controls.Add(this.label35); - this.GlidePerGameHacks2.Controls.Add(this.Glide_fix_tex_coord); - this.GlidePerGameHacks2.Controls.Add(this.label34); - this.GlidePerGameHacks2.Controls.Add(this.Glide_filtering); - this.GlidePerGameHacks2.Controls.Add(this.label33); - this.GlidePerGameHacks2.Controls.Add(this.Glide_depth_bias); - this.GlidePerGameHacks2.Controls.Add(this.GlideUseDefaultHacks2); - this.GlidePerGameHacks2.Location = new System.Drawing.Point(4, 22); - this.GlidePerGameHacks2.Name = "GlidePerGameHacks2"; - this.GlidePerGameHacks2.Size = new System.Drawing.Size(564, 310); - this.GlidePerGameHacks2.TabIndex = 2; - this.GlidePerGameHacks2.Text = "More Per Game Settings"; - this.GlidePerGameHacks2.UseVisualStyleBackColor = true; - // - // label44 - // - this.label44.AutoSize = true; - this.label44.Location = new System.Drawing.Point(271, 62); - this.label44.Name = "label44"; - this.label44.Size = new System.Drawing.Size(119, 13); - this.label44.TabIndex = 54; - this.label44.Text = "Enable hacks for game:"; - // - // Glide_enable_hacks_for_game - // - this.Glide_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_enable_hacks_for_game.FormattingEnabled = true; - this.Glide_enable_hacks_for_game.Items.AddRange(new object[] { + this.Glide_ucode.Location = new System.Drawing.Point(102, 36); + this.Glide_ucode.Name = "Glide_ucode"; + this.Glide_ucode.Size = new System.Drawing.Size(273, 21); + this.Glide_ucode.TabIndex = 1; + // + // Glide_autodetect_ucode + // + this.Glide_autodetect_ucode.AutoSize = true; + this.Glide_autodetect_ucode.Location = new System.Drawing.Point(9, 13); + this.Glide_autodetect_ucode.Name = "Glide_autodetect_ucode"; + this.Glide_autodetect_ucode.Size = new System.Drawing.Size(131, 17); + this.Glide_autodetect_ucode.TabIndex = 0; + this.Glide_autodetect_ucode.Text = "Autodetect Microcode"; + this.Glide_autodetect_ucode.UseVisualStyleBackColor = true; + // + // GlidePerGameHacks1 + // + this.GlidePerGameHacks1.Controls.Add(this.Glide_wrap_big_tex); + this.GlidePerGameHacks1.Controls.Add(this.Glide_use_sts1_only); + this.GlidePerGameHacks1.Controls.Add(this.Glide_soft_depth_compare); + this.GlidePerGameHacks1.Controls.Add(this.Glide_PPL); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_write); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_texrect); + this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_texrect_edge); + this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_primdepth); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_previous); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_aux_copy); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires_buf_clear); + this.GlidePerGameHacks1.Controls.Add(this.Glide_force_microcheck); + this.GlidePerGameHacks1.Controls.Add(this.Glide_force_depth_compare); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fog); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fillcolor_fix); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_smart); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_read_alpha); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_clear); + this.GlidePerGameHacks1.Controls.Add(this.Glide_detect_cpu_write); + this.GlidePerGameHacks1.Controls.Add(this.Glide_decrease_fillrect_edge); + this.GlidePerGameHacks1.Controls.Add(this.Glide_buff_clear); + this.GlidePerGameHacks1.Controls.Add(this.Glide_alt_tex_size); + this.GlidePerGameHacks1.Controls.Add(this.GlideUseDefaultHacks1); + this.GlidePerGameHacks1.Location = new System.Drawing.Point(4, 22); + this.GlidePerGameHacks1.Name = "GlidePerGameHacks1"; + this.GlidePerGameHacks1.Padding = new System.Windows.Forms.Padding(3); + this.GlidePerGameHacks1.Size = new System.Drawing.Size(564, 310); + this.GlidePerGameHacks1.TabIndex = 1; + this.GlidePerGameHacks1.Text = "Per Game Settings"; + this.GlidePerGameHacks1.UseVisualStyleBackColor = true; + // + // Glide_wrap_big_tex + // + this.Glide_wrap_big_tex.AutoSize = true; + this.Glide_wrap_big_tex.Location = new System.Drawing.Point(375, 62); + this.Glide_wrap_big_tex.Name = "Glide_wrap_big_tex"; + this.Glide_wrap_big_tex.Size = new System.Drawing.Size(86, 17); + this.Glide_wrap_big_tex.TabIndex = 33; + this.Glide_wrap_big_tex.Text = "Wrap big tex"; + this.Glide_wrap_big_tex.UseVisualStyleBackColor = true; + // + // Glide_use_sts1_only + // + this.Glide_use_sts1_only.AutoSize = true; + this.Glide_use_sts1_only.Location = new System.Drawing.Point(375, 39); + this.Glide_use_sts1_only.Name = "Glide_use_sts1_only"; + this.Glide_use_sts1_only.Size = new System.Drawing.Size(89, 17); + this.Glide_use_sts1_only.TabIndex = 32; + this.Glide_use_sts1_only.Text = "Use sts1 only"; + this.Glide_use_sts1_only.UseVisualStyleBackColor = true; + // + // Glide_soft_depth_compare + // + this.Glide_soft_depth_compare.AutoSize = true; + this.Glide_soft_depth_compare.Location = new System.Drawing.Point(192, 269); + this.Glide_soft_depth_compare.Name = "Glide_soft_depth_compare"; + this.Glide_soft_depth_compare.Size = new System.Drawing.Size(119, 17); + this.Glide_soft_depth_compare.TabIndex = 31; + this.Glide_soft_depth_compare.Text = "Soft depth compare"; + this.Glide_soft_depth_compare.UseVisualStyleBackColor = true; + // + // Glide_PPL + // + this.Glide_PPL.AutoSize = true; + this.Glide_PPL.Location = new System.Drawing.Point(192, 246); + this.Glide_PPL.Name = "Glide_PPL"; + this.Glide_PPL.Size = new System.Drawing.Size(46, 17); + this.Glide_PPL.TabIndex = 30; + this.Glide_PPL.Text = "PPL"; + this.Glide_PPL.UseVisualStyleBackColor = true; + // + // Glide_fb_optimize_write + // + this.Glide_fb_optimize_write.AutoSize = true; + this.Glide_fb_optimize_write.Location = new System.Drawing.Point(192, 223); + this.Glide_fb_optimize_write.Name = "Glide_fb_optimize_write"; + this.Glide_fb_optimize_write.Size = new System.Drawing.Size(91, 17); + this.Glide_fb_optimize_write.TabIndex = 29; + this.Glide_fb_optimize_write.Text = "Optimize write"; + this.Glide_fb_optimize_write.UseVisualStyleBackColor = true; + // + // Glide_fb_optimize_texrect + // + this.Glide_fb_optimize_texrect.AutoSize = true; + this.Glide_fb_optimize_texrect.Location = new System.Drawing.Point(192, 200); + this.Glide_fb_optimize_texrect.Name = "Glide_fb_optimize_texrect"; + this.Glide_fb_optimize_texrect.Size = new System.Drawing.Size(101, 17); + this.Glide_fb_optimize_texrect.TabIndex = 28; + this.Glide_fb_optimize_texrect.Text = "Optimize texrect"; + this.Glide_fb_optimize_texrect.UseVisualStyleBackColor = true; + // + // Glide_increase_texrect_edge + // + this.Glide_increase_texrect_edge.AutoSize = true; + this.Glide_increase_texrect_edge.Location = new System.Drawing.Point(192, 177); + this.Glide_increase_texrect_edge.Name = "Glide_increase_texrect_edge"; + this.Glide_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); + this.Glide_increase_texrect_edge.TabIndex = 27; + this.Glide_increase_texrect_edge.Text = "Increase texrect edge"; + this.Glide_increase_texrect_edge.UseVisualStyleBackColor = true; + // + // Glide_increase_primdepth + // + this.Glide_increase_primdepth.AutoSize = true; + this.Glide_increase_primdepth.Location = new System.Drawing.Point(192, 154); + this.Glide_increase_primdepth.Name = "Glide_increase_primdepth"; + this.Glide_increase_primdepth.Size = new System.Drawing.Size(116, 17); + this.Glide_increase_primdepth.TabIndex = 26; + this.Glide_increase_primdepth.Text = "Increase primdepth"; + this.Glide_increase_primdepth.UseVisualStyleBackColor = true; + // + // Glide_fb_ignore_previous + // + this.Glide_fb_ignore_previous.AutoSize = true; + this.Glide_fb_ignore_previous.Location = new System.Drawing.Point(192, 131); + this.Glide_fb_ignore_previous.Name = "Glide_fb_ignore_previous"; + this.Glide_fb_ignore_previous.Size = new System.Drawing.Size(100, 17); + this.Glide_fb_ignore_previous.TabIndex = 25; + this.Glide_fb_ignore_previous.Text = "Ignore Previous"; + this.Glide_fb_ignore_previous.UseVisualStyleBackColor = true; + // + // Glide_fb_ignore_aux_copy + // + this.Glide_fb_ignore_aux_copy.AutoSize = true; + this.Glide_fb_ignore_aux_copy.Location = new System.Drawing.Point(192, 108); + this.Glide_fb_ignore_aux_copy.Name = "Glide_fb_ignore_aux_copy"; + this.Glide_fb_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); + this.Glide_fb_ignore_aux_copy.TabIndex = 24; + this.Glide_fb_ignore_aux_copy.Text = "Ignore aux copy"; + this.Glide_fb_ignore_aux_copy.UseVisualStyleBackColor = true; + // + // Glide_fb_hires_buf_clear + // + this.Glide_fb_hires_buf_clear.AutoSize = true; + this.Glide_fb_hires_buf_clear.Location = new System.Drawing.Point(192, 85); + this.Glide_fb_hires_buf_clear.Name = "Glide_fb_hires_buf_clear"; + this.Glide_fb_hires_buf_clear.Size = new System.Drawing.Size(109, 17); + this.Glide_fb_hires_buf_clear.TabIndex = 23; + this.Glide_fb_hires_buf_clear.Text = "Hi-res buffer clear"; + this.Glide_fb_hires_buf_clear.UseVisualStyleBackColor = true; + // + // Glide_force_microcheck + // + this.Glide_force_microcheck.AutoSize = true; + this.Glide_force_microcheck.Location = new System.Drawing.Point(192, 62); + this.Glide_force_microcheck.Name = "Glide_force_microcheck"; + this.Glide_force_microcheck.Size = new System.Drawing.Size(165, 17); + this.Glide_force_microcheck.TabIndex = 22; + this.Glide_force_microcheck.Text = "Check microcode each frame"; + this.Glide_force_microcheck.UseVisualStyleBackColor = true; + // + // Glide_force_depth_compare + // + this.Glide_force_depth_compare.AutoSize = true; + this.Glide_force_depth_compare.Location = new System.Drawing.Point(192, 39); + this.Glide_force_depth_compare.Name = "Glide_force_depth_compare"; + this.Glide_force_depth_compare.Size = new System.Drawing.Size(130, 17); + this.Glide_force_depth_compare.TabIndex = 21; + this.Glide_force_depth_compare.Text = "Force Depth Compare"; + this.Glide_force_depth_compare.UseVisualStyleBackColor = true; + // + // Glide_fog + // + this.Glide_fog.AutoSize = true; + this.Glide_fog.Location = new System.Drawing.Point(6, 269); + this.Glide_fog.Name = "Glide_fog"; + this.Glide_fog.Size = new System.Drawing.Size(86, 17); + this.Glide_fog.TabIndex = 20; + this.Glide_fog.Text = "Fog Enabled"; + this.Glide_fog.UseVisualStyleBackColor = true; + // + // Glide_fillcolor_fix + // + this.Glide_fillcolor_fix.AutoSize = true; + this.Glide_fillcolor_fix.Location = new System.Drawing.Point(6, 246); + this.Glide_fillcolor_fix.Name = "Glide_fillcolor_fix"; + this.Glide_fillcolor_fix.Size = new System.Drawing.Size(77, 17); + this.Glide_fillcolor_fix.TabIndex = 19; + this.Glide_fillcolor_fix.Text = "Fillcolor Fix"; + this.Glide_fillcolor_fix.UseVisualStyleBackColor = true; + // + // Glide_fb_smart + // + this.Glide_fb_smart.AutoSize = true; + this.Glide_fb_smart.Location = new System.Drawing.Point(6, 223); + this.Glide_fb_smart.Name = "Glide_fb_smart"; + this.Glide_fb_smart.Size = new System.Drawing.Size(109, 17); + this.Glide_fb_smart.TabIndex = 18; + this.Glide_fb_smart.Text = "Smart framebuffer"; + this.Glide_fb_smart.UseVisualStyleBackColor = true; + // + // Glide_fb_read_alpha + // + this.Glide_fb_read_alpha.AutoSize = true; + this.Glide_fb_read_alpha.Location = new System.Drawing.Point(6, 200); + this.Glide_fb_read_alpha.Name = "Glide_fb_read_alpha"; + this.Glide_fb_read_alpha.Size = new System.Drawing.Size(135, 17); + this.Glide_fb_read_alpha.TabIndex = 17; + this.Glide_fb_read_alpha.Text = "Framebuffer read alpha"; + this.Glide_fb_read_alpha.UseVisualStyleBackColor = true; + // + // Glide_fb_hires + // + this.Glide_fb_hires.AutoSize = true; + this.Glide_fb_hires.Location = new System.Drawing.Point(6, 154); + this.Glide_fb_hires.Name = "Glide_fb_hires"; + this.Glide_fb_hires.Size = new System.Drawing.Size(109, 17); + this.Glide_fb_hires.TabIndex = 15; + this.Glide_fb_hires.Text = "Hi-res framebuffer"; + this.Glide_fb_hires.UseVisualStyleBackColor = true; + // + // Glide_fb_clear + // + this.Glide_fb_clear.AutoSize = true; + this.Glide_fb_clear.Location = new System.Drawing.Point(6, 131); + this.Glide_fb_clear.Name = "Glide_fb_clear"; + this.Glide_fb_clear.Size = new System.Drawing.Size(106, 17); + this.Glide_fb_clear.TabIndex = 7; + this.Glide_fb_clear.Text = "Clear framebuffer"; + this.Glide_fb_clear.UseVisualStyleBackColor = true; + // + // Glide_detect_cpu_write + // + this.Glide_detect_cpu_write.AutoSize = true; + this.Glide_detect_cpu_write.Location = new System.Drawing.Point(6, 108); + this.Glide_detect_cpu_write.Name = "Glide_detect_cpu_write"; + this.Glide_detect_cpu_write.Size = new System.Drawing.Size(113, 17); + this.Glide_detect_cpu_write.TabIndex = 6; + this.Glide_detect_cpu_write.Text = "Detect CPU writes"; + this.Glide_detect_cpu_write.UseVisualStyleBackColor = true; + // + // Glide_decrease_fillrect_edge + // + this.Glide_decrease_fillrect_edge.AutoSize = true; + this.Glide_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); + this.Glide_decrease_fillrect_edge.Name = "Glide_decrease_fillrect_edge"; + this.Glide_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); + this.Glide_decrease_fillrect_edge.TabIndex = 5; + this.Glide_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; + this.Glide_decrease_fillrect_edge.UseVisualStyleBackColor = true; + // + // Glide_buff_clear + // + this.Glide_buff_clear.AutoSize = true; + this.Glide_buff_clear.Location = new System.Drawing.Point(6, 62); + this.Glide_buff_clear.Name = "Glide_buff_clear"; + this.Glide_buff_clear.Size = new System.Drawing.Size(153, 17); + this.Glide_buff_clear.TabIndex = 4; + this.Glide_buff_clear.Text = "Buffer clear on every frame"; + this.Glide_buff_clear.UseVisualStyleBackColor = true; + // + // Glide_alt_tex_size + // + this.Glide_alt_tex_size.AutoSize = true; + this.Glide_alt_tex_size.Location = new System.Drawing.Point(6, 39); + this.Glide_alt_tex_size.Name = "Glide_alt_tex_size"; + this.Glide_alt_tex_size.Size = new System.Drawing.Size(169, 17); + this.Glide_alt_tex_size.TabIndex = 3; + this.Glide_alt_tex_size.Text = "Alternate Texture Size Method"; + this.Glide_alt_tex_size.UseVisualStyleBackColor = true; + // + // GlideUseDefaultHacks1 + // + this.GlideUseDefaultHacks1.AutoSize = true; + this.GlideUseDefaultHacks1.Location = new System.Drawing.Point(6, 6); + this.GlideUseDefaultHacks1.Name = "GlideUseDefaultHacks1"; + this.GlideUseDefaultHacks1.Size = new System.Drawing.Size(165, 17); + this.GlideUseDefaultHacks1.TabIndex = 2; + this.GlideUseDefaultHacks1.Text = "Use defaults for current game"; + this.GlideUseDefaultHacks1.UseVisualStyleBackColor = true; + this.GlideUseDefaultHacks1.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks1_CheckedChanged); + // + // GlidePerGameHacks2 + // + this.GlidePerGameHacks2.Controls.Add(this.label44); + this.GlidePerGameHacks2.Controls.Add(this.Glide_enable_hacks_for_game); + this.GlidePerGameHacks2.Controls.Add(this.label43); + this.GlidePerGameHacks2.Controls.Add(this.Glide_swapmode); + this.GlidePerGameHacks2.Controls.Add(this.label42); + this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_pattern); + this.GlidePerGameHacks2.Controls.Add(this.label41); + this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_mode); + this.GlidePerGameHacks2.Controls.Add(this.label36); + this.GlidePerGameHacks2.Controls.Add(this.Glide_lodmode); + this.GlidePerGameHacks2.Controls.Add(this.label35); + this.GlidePerGameHacks2.Controls.Add(this.Glide_fix_tex_coord); + this.GlidePerGameHacks2.Controls.Add(this.label34); + this.GlidePerGameHacks2.Controls.Add(this.Glide_filtering); + this.GlidePerGameHacks2.Controls.Add(this.label33); + this.GlidePerGameHacks2.Controls.Add(this.Glide_depth_bias); + this.GlidePerGameHacks2.Controls.Add(this.GlideUseDefaultHacks2); + this.GlidePerGameHacks2.Location = new System.Drawing.Point(4, 22); + this.GlidePerGameHacks2.Name = "GlidePerGameHacks2"; + this.GlidePerGameHacks2.Size = new System.Drawing.Size(564, 310); + this.GlidePerGameHacks2.TabIndex = 2; + this.GlidePerGameHacks2.Text = "More Per Game Settings"; + this.GlidePerGameHacks2.UseVisualStyleBackColor = true; + // + // label44 + // + this.label44.AutoSize = true; + this.label44.Location = new System.Drawing.Point(271, 62); + this.label44.Name = "label44"; + this.label44.Size = new System.Drawing.Size(119, 13); + this.label44.TabIndex = 54; + this.label44.Text = "Enable hacks for game:"; + // + // Glide_enable_hacks_for_game + // + this.Glide_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_enable_hacks_for_game.FormattingEnabled = true; + this.Glide_enable_hacks_for_game.Items.AddRange(new object[] { "None", "Zelda", "Bomberman64", @@ -2827,230 +2829,230 @@ "Top Gear Rally 2", "Killer Instinct", "LEGO Racers"}); - this.Glide_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); - this.Glide_enable_hacks_for_game.Name = "Glide_enable_hacks_for_game"; - this.Glide_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); - this.Glide_enable_hacks_for_game.TabIndex = 53; - // - // label43 - // - this.label43.AutoSize = true; - this.label43.Location = new System.Drawing.Point(266, 36); - this.label43.Name = "label43"; - this.label43.Size = new System.Drawing.Size(124, 13); - this.label43.TabIndex = 40; - this.label43.Text = "Buffer swapping method:"; - // - // Glide_swapmode - // - this.Glide_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_swapmode.FormattingEnabled = true; - this.Glide_swapmode.Items.AddRange(new object[] { + this.Glide_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); + this.Glide_enable_hacks_for_game.Name = "Glide_enable_hacks_for_game"; + this.Glide_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); + this.Glide_enable_hacks_for_game.TabIndex = 53; + // + // label43 + // + this.label43.AutoSize = true; + this.label43.Location = new System.Drawing.Point(266, 36); + this.label43.Name = "label43"; + this.label43.Size = new System.Drawing.Size(124, 13); + this.label43.TabIndex = 40; + this.label43.Text = "Buffer swapping method:"; + // + // Glide_swapmode + // + this.Glide_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_swapmode.FormattingEnabled = true; + this.Glide_swapmode.Items.AddRange(new object[] { "Old", "New", "Hybrid"}); - this.Glide_swapmode.Location = new System.Drawing.Point(396, 32); - this.Glide_swapmode.Name = "Glide_swapmode"; - this.Glide_swapmode.Size = new System.Drawing.Size(78, 21); - this.Glide_swapmode.TabIndex = 39; - // - // label42 - // - this.label42.AutoSize = true; - this.label42.Location = new System.Drawing.Point(3, 275); - this.label42.Name = "label42"; - this.label42.Size = new System.Drawing.Size(78, 13); - this.label42.TabIndex = 38; - this.label42.Text = "Stipple pattern:"; - // - // Glide_stipple_pattern - // - this.Glide_stipple_pattern.Location = new System.Drawing.Point(92, 272); - this.Glide_stipple_pattern.Name = "Glide_stipple_pattern"; - this.Glide_stipple_pattern.Size = new System.Drawing.Size(78, 20); - this.Glide_stipple_pattern.TabIndex = 37; - // - // label41 - // - this.label41.AutoSize = true; - this.label41.Location = new System.Drawing.Point(3, 249); - this.label41.Name = "label41"; - this.label41.Size = new System.Drawing.Size(71, 13); - this.label41.TabIndex = 36; - this.label41.Text = "Stipple mode:"; - // - // Glide_stipple_mode - // - this.Glide_stipple_mode.Location = new System.Drawing.Point(92, 246); - this.Glide_stipple_mode.Name = "Glide_stipple_mode"; - this.Glide_stipple_mode.Size = new System.Drawing.Size(36, 20); - this.Glide_stipple_mode.TabIndex = 35; - // - // label36 - // - this.label36.AutoSize = true; - this.label36.Location = new System.Drawing.Point(3, 118); - this.label36.Name = "label36"; - this.label36.Size = new System.Drawing.Size(86, 13); - this.label36.TabIndex = 26; - this.label36.Text = "LOD calculation:"; - // - // Glide_lodmode - // - this.Glide_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_lodmode.FormattingEnabled = true; - this.Glide_lodmode.Items.AddRange(new object[] { + this.Glide_swapmode.Location = new System.Drawing.Point(396, 32); + this.Glide_swapmode.Name = "Glide_swapmode"; + this.Glide_swapmode.Size = new System.Drawing.Size(78, 21); + this.Glide_swapmode.TabIndex = 39; + // + // label42 + // + this.label42.AutoSize = true; + this.label42.Location = new System.Drawing.Point(3, 275); + this.label42.Name = "label42"; + this.label42.Size = new System.Drawing.Size(78, 13); + this.label42.TabIndex = 38; + this.label42.Text = "Stipple pattern:"; + // + // Glide_stipple_pattern + // + this.Glide_stipple_pattern.Location = new System.Drawing.Point(92, 272); + this.Glide_stipple_pattern.Name = "Glide_stipple_pattern"; + this.Glide_stipple_pattern.Size = new System.Drawing.Size(78, 20); + this.Glide_stipple_pattern.TabIndex = 37; + // + // label41 + // + this.label41.AutoSize = true; + this.label41.Location = new System.Drawing.Point(3, 249); + this.label41.Name = "label41"; + this.label41.Size = new System.Drawing.Size(71, 13); + this.label41.TabIndex = 36; + this.label41.Text = "Stipple mode:"; + // + // Glide_stipple_mode + // + this.Glide_stipple_mode.Location = new System.Drawing.Point(92, 246); + this.Glide_stipple_mode.Name = "Glide_stipple_mode"; + this.Glide_stipple_mode.Size = new System.Drawing.Size(36, 20); + this.Glide_stipple_mode.TabIndex = 35; + // + // label36 + // + this.label36.AutoSize = true; + this.label36.Location = new System.Drawing.Point(3, 118); + this.label36.Name = "label36"; + this.label36.Size = new System.Drawing.Size(86, 13); + this.label36.TabIndex = 26; + this.label36.Text = "LOD calculation:"; + // + // Glide_lodmode + // + this.Glide_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_lodmode.FormattingEnabled = true; + this.Glide_lodmode.Items.AddRange(new object[] { "Off", "Fast", "Precise"}); - this.Glide_lodmode.Location = new System.Drawing.Point(92, 115); - this.Glide_lodmode.Name = "Glide_lodmode"; - this.Glide_lodmode.Size = new System.Drawing.Size(78, 21); - this.Glide_lodmode.TabIndex = 25; - // - // label35 - // - this.label35.AutoSize = true; - this.label35.Location = new System.Drawing.Point(3, 92); - this.label35.Name = "label35"; - this.label35.Size = new System.Drawing.Size(70, 13); - this.label35.TabIndex = 24; - this.label35.Text = "Fix tex coord:"; - // - // Glide_fix_tex_coord - // - this.Glide_fix_tex_coord.Location = new System.Drawing.Point(92, 89); - this.Glide_fix_tex_coord.Name = "Glide_fix_tex_coord"; - this.Glide_fix_tex_coord.Size = new System.Drawing.Size(36, 20); - this.Glide_fix_tex_coord.TabIndex = 23; - // - // label34 - // - this.label34.AutoSize = true; - this.label34.Location = new System.Drawing.Point(3, 65); - this.label34.Name = "label34"; - this.label34.Size = new System.Drawing.Size(75, 13); - this.label34.TabIndex = 22; - this.label34.Text = "Filtering mode:"; - // - // Glide_filtering - // - this.Glide_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_filtering.FormattingEnabled = true; - this.Glide_filtering.Items.AddRange(new object[] { + this.Glide_lodmode.Location = new System.Drawing.Point(92, 115); + this.Glide_lodmode.Name = "Glide_lodmode"; + this.Glide_lodmode.Size = new System.Drawing.Size(78, 21); + this.Glide_lodmode.TabIndex = 25; + // + // label35 + // + this.label35.AutoSize = true; + this.label35.Location = new System.Drawing.Point(3, 92); + this.label35.Name = "label35"; + this.label35.Size = new System.Drawing.Size(70, 13); + this.label35.TabIndex = 24; + this.label35.Text = "Fix tex coord:"; + // + // Glide_fix_tex_coord + // + this.Glide_fix_tex_coord.Location = new System.Drawing.Point(92, 89); + this.Glide_fix_tex_coord.Name = "Glide_fix_tex_coord"; + this.Glide_fix_tex_coord.Size = new System.Drawing.Size(36, 20); + this.Glide_fix_tex_coord.TabIndex = 23; + // + // label34 + // + this.label34.AutoSize = true; + this.label34.Location = new System.Drawing.Point(3, 65); + this.label34.Name = "label34"; + this.label34.Size = new System.Drawing.Size(75, 13); + this.label34.TabIndex = 22; + this.label34.Text = "Filtering mode:"; + // + // Glide_filtering + // + this.Glide_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_filtering.FormattingEnabled = true; + this.Glide_filtering.Items.AddRange(new object[] { "None", "Force bilinear", "Force point-sampled"}); - this.Glide_filtering.Location = new System.Drawing.Point(92, 62); - this.Glide_filtering.Name = "Glide_filtering"; - this.Glide_filtering.Size = new System.Drawing.Size(138, 21); - this.Glide_filtering.TabIndex = 21; - // - // label33 - // - this.label33.AutoSize = true; - this.label33.Location = new System.Drawing.Point(3, 39); - this.label33.Name = "label33"; - this.label33.Size = new System.Drawing.Size(86, 13); - this.label33.TabIndex = 20; - this.label33.Text = "Depth bias level:"; - // - // Glide_depth_bias - // - this.Glide_depth_bias.Location = new System.Drawing.Point(92, 36); - this.Glide_depth_bias.Name = "Glide_depth_bias"; - this.Glide_depth_bias.Size = new System.Drawing.Size(36, 20); - this.Glide_depth_bias.TabIndex = 19; - // - // GlideUseDefaultHacks2 - // - this.GlideUseDefaultHacks2.AutoSize = true; - this.GlideUseDefaultHacks2.Location = new System.Drawing.Point(6, 6); - this.GlideUseDefaultHacks2.Name = "GlideUseDefaultHacks2"; - this.GlideUseDefaultHacks2.Size = new System.Drawing.Size(165, 17); - this.GlideUseDefaultHacks2.TabIndex = 3; - this.GlideUseDefaultHacks2.Text = "Use defaults for current game"; - this.GlideUseDefaultHacks2.UseVisualStyleBackColor = true; - this.GlideUseDefaultHacks2.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks2_CheckedChanged); - // - // RiceTabPage - // - this.RiceTabPage.Controls.Add(this.RiceTabControl); - this.RiceTabPage.Location = new System.Drawing.Point(4, 22); - this.RiceTabPage.Name = "RiceTabPage"; - this.RiceTabPage.Size = new System.Drawing.Size(572, 343); - this.RiceTabPage.TabIndex = 4; - this.RiceTabPage.Text = "Rice"; - this.RiceTabPage.UseVisualStyleBackColor = true; - // - // RiceTabControl - // - this.RiceTabControl.Controls.Add(this.RiceGeneral); - this.RiceTabControl.Controls.Add(this.RiceGameDefaultTab); - this.RiceTabControl.Controls.Add(this.RiceTextureEnhancementTab); - this.RiceTabControl.Controls.Add(this.RiceGameSpecificTab); - this.RiceTabControl.Location = new System.Drawing.Point(0, 3); - this.RiceTabControl.Name = "RiceTabControl"; - this.RiceTabControl.SelectedIndex = 0; - this.RiceTabControl.Size = new System.Drawing.Size(572, 336); - this.RiceTabControl.TabIndex = 0; - // - // RiceGeneral - // - this.RiceGeneral.Controls.Add(this.label15); - this.RiceGeneral.Controls.Add(this.RiceScreenUpdateSetting_Combo); - this.RiceGeneral.Controls.Add(this.label14); - this.RiceGeneral.Controls.Add(this.RiceMultiSampling_Combo); - this.RiceGeneral.Controls.Add(this.label13); - this.RiceGeneral.Controls.Add(this.RiceOpenGLRenderSetting_Combo); - this.RiceGeneral.Controls.Add(this.label11); - this.RiceGeneral.Controls.Add(this.RiceColorQuality_Combo); - this.RiceGeneral.Controls.Add(this.label10); - this.RiceGeneral.Controls.Add(this.RiceOpenGLDepthBufferSetting_Combo); - this.RiceGeneral.Controls.Add(this.label9); - this.RiceGeneral.Controls.Add(this.RiceTextureQuality_Combo); - this.RiceGeneral.Controls.Add(this.RiceEnableVertexShader_CB); - this.RiceGeneral.Controls.Add(this.RiceSkipFrame_CB); - this.RiceGeneral.Controls.Add(this.RiceEnableHacks_CB); - this.RiceGeneral.Controls.Add(this.RiceFullTMEMEmulation_CB); - this.RiceGeneral.Controls.Add(this.RiceOpenGLVertexClipper_CB); - this.RiceGeneral.Controls.Add(this.AnisotropicFiltering_LB); - this.RiceGeneral.Controls.Add(this.RiceAnisotropicFiltering_TB); - this.RiceGeneral.Controls.Add(this.label6); - this.RiceGeneral.Controls.Add(this.RiceFogMethod_Combo); - this.RiceGeneral.Controls.Add(this.label5); - this.RiceGeneral.Controls.Add(this.RiceMipmapping_Combo); - this.RiceGeneral.Controls.Add(this.RiceWinFrameMode_CB); - this.RiceGeneral.Controls.Add(this.RiceInN64Resolution_CB); - this.RiceGeneral.Controls.Add(this.RiceFastTextureLoading_CB); - this.RiceGeneral.Controls.Add(this.RiceAccurateTextureMapping_CB); - this.RiceGeneral.Controls.Add(this.RiceSaveVRAM_CB); - this.RiceGeneral.Controls.Add(this.RiceEnableSSE_CB); - this.RiceGeneral.Location = new System.Drawing.Point(4, 22); - this.RiceGeneral.Name = "RiceGeneral"; - this.RiceGeneral.Padding = new System.Windows.Forms.Padding(3); - this.RiceGeneral.Size = new System.Drawing.Size(564, 310); - this.RiceGeneral.TabIndex = 0; - this.RiceGeneral.Text = "General"; - this.RiceGeneral.UseVisualStyleBackColor = true; - // - // label15 - // - this.label15.AutoSize = true; - this.label15.Location = new System.Drawing.Point(287, 204); - this.label15.Name = "label15"; - this.label15.Size = new System.Drawing.Size(118, 13); - this.label15.TabIndex = 36; - this.label15.Text = "Screen Update Setting:"; - this.label15.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label15, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); - // - // RiceScreenUpdateSetting_Combo - // - this.RiceScreenUpdateSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceScreenUpdateSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceScreenUpdateSetting_Combo.FormattingEnabled = true; - this.RiceScreenUpdateSetting_Combo.Items.AddRange(new object[] { + this.Glide_filtering.Location = new System.Drawing.Point(92, 62); + this.Glide_filtering.Name = "Glide_filtering"; + this.Glide_filtering.Size = new System.Drawing.Size(138, 21); + this.Glide_filtering.TabIndex = 21; + // + // label33 + // + this.label33.AutoSize = true; + this.label33.Location = new System.Drawing.Point(3, 39); + this.label33.Name = "label33"; + this.label33.Size = new System.Drawing.Size(86, 13); + this.label33.TabIndex = 20; + this.label33.Text = "Depth bias level:"; + // + // Glide_depth_bias + // + this.Glide_depth_bias.Location = new System.Drawing.Point(92, 36); + this.Glide_depth_bias.Name = "Glide_depth_bias"; + this.Glide_depth_bias.Size = new System.Drawing.Size(36, 20); + this.Glide_depth_bias.TabIndex = 19; + // + // GlideUseDefaultHacks2 + // + this.GlideUseDefaultHacks2.AutoSize = true; + this.GlideUseDefaultHacks2.Location = new System.Drawing.Point(6, 6); + this.GlideUseDefaultHacks2.Name = "GlideUseDefaultHacks2"; + this.GlideUseDefaultHacks2.Size = new System.Drawing.Size(165, 17); + this.GlideUseDefaultHacks2.TabIndex = 3; + this.GlideUseDefaultHacks2.Text = "Use defaults for current game"; + this.GlideUseDefaultHacks2.UseVisualStyleBackColor = true; + this.GlideUseDefaultHacks2.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks2_CheckedChanged); + // + // RiceTabPage + // + this.RiceTabPage.Controls.Add(this.RiceTabControl); + this.RiceTabPage.Location = new System.Drawing.Point(4, 22); + this.RiceTabPage.Name = "RiceTabPage"; + this.RiceTabPage.Size = new System.Drawing.Size(572, 343); + this.RiceTabPage.TabIndex = 4; + this.RiceTabPage.Text = "Rice"; + this.RiceTabPage.UseVisualStyleBackColor = true; + // + // RiceTabControl + // + this.RiceTabControl.Controls.Add(this.RiceGeneral); + this.RiceTabControl.Controls.Add(this.RiceGameDefaultTab); + this.RiceTabControl.Controls.Add(this.RiceTextureEnhancementTab); + this.RiceTabControl.Controls.Add(this.RiceGameSpecificTab); + this.RiceTabControl.Location = new System.Drawing.Point(0, 3); + this.RiceTabControl.Name = "RiceTabControl"; + this.RiceTabControl.SelectedIndex = 0; + this.RiceTabControl.Size = new System.Drawing.Size(572, 336); + this.RiceTabControl.TabIndex = 0; + // + // RiceGeneral + // + this.RiceGeneral.Controls.Add(this.label15); + this.RiceGeneral.Controls.Add(this.RiceScreenUpdateSetting_Combo); + this.RiceGeneral.Controls.Add(this.label14); + this.RiceGeneral.Controls.Add(this.RiceMultiSampling_Combo); + this.RiceGeneral.Controls.Add(this.label13); + this.RiceGeneral.Controls.Add(this.RiceOpenGLRenderSetting_Combo); + this.RiceGeneral.Controls.Add(this.label11); + this.RiceGeneral.Controls.Add(this.RiceColorQuality_Combo); + this.RiceGeneral.Controls.Add(this.label10); + this.RiceGeneral.Controls.Add(this.RiceOpenGLDepthBufferSetting_Combo); + this.RiceGeneral.Controls.Add(this.label9); + this.RiceGeneral.Controls.Add(this.RiceTextureQuality_Combo); + this.RiceGeneral.Controls.Add(this.RiceEnableVertexShader_CB); + this.RiceGeneral.Controls.Add(this.RiceSkipFrame_CB); + this.RiceGeneral.Controls.Add(this.RiceEnableHacks_CB); + this.RiceGeneral.Controls.Add(this.RiceFullTMEMEmulation_CB); + this.RiceGeneral.Controls.Add(this.RiceOpenGLVertexClipper_CB); + this.RiceGeneral.Controls.Add(this.AnisotropicFiltering_LB); + this.RiceGeneral.Controls.Add(this.RiceAnisotropicFiltering_TB); + this.RiceGeneral.Controls.Add(this.label6); + this.RiceGeneral.Controls.Add(this.RiceFogMethod_Combo); + this.RiceGeneral.Controls.Add(this.label5); + this.RiceGeneral.Controls.Add(this.RiceMipmapping_Combo); + this.RiceGeneral.Controls.Add(this.RiceWinFrameMode_CB); + this.RiceGeneral.Controls.Add(this.RiceInN64Resolution_CB); + this.RiceGeneral.Controls.Add(this.RiceFastTextureLoading_CB); + this.RiceGeneral.Controls.Add(this.RiceAccurateTextureMapping_CB); + this.RiceGeneral.Controls.Add(this.RiceSaveVRAM_CB); + this.RiceGeneral.Controls.Add(this.RiceEnableSSE_CB); + this.RiceGeneral.Location = new System.Drawing.Point(4, 22); + this.RiceGeneral.Name = "RiceGeneral"; + this.RiceGeneral.Padding = new System.Windows.Forms.Padding(3); + this.RiceGeneral.Size = new System.Drawing.Size(564, 310); + this.RiceGeneral.TabIndex = 0; + this.RiceGeneral.Text = "General"; + this.RiceGeneral.UseVisualStyleBackColor = true; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Location = new System.Drawing.Point(287, 204); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(118, 13); + this.label15.TabIndex = 36; + this.label15.Text = "Screen Update Setting:"; + this.label15.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label15, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); + // + // RiceScreenUpdateSetting_Combo + // + this.RiceScreenUpdateSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceScreenUpdateSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceScreenUpdateSetting_Combo.FormattingEnabled = true; + this.RiceScreenUpdateSetting_Combo.Items.AddRange(new object[] { "ROM default", "VI origin update", "VI origin change", @@ -3059,57 +3061,57 @@ "First primitive draw", "Before screen clear", "After screen drawn"}); - this.RiceScreenUpdateSetting_Combo.Location = new System.Drawing.Point(409, 201); - this.RiceScreenUpdateSetting_Combo.Name = "RiceScreenUpdateSetting_Combo"; - this.RiceScreenUpdateSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceScreenUpdateSetting_Combo.TabIndex = 35; - this.toolTip1.SetToolTip(this.RiceScreenUpdateSetting_Combo, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); - // - // label14 - // - this.label14.AutoSize = true; - this.label14.Location = new System.Drawing.Point(326, 177); - this.label14.Name = "label14"; - this.label14.Size = new System.Drawing.Size(75, 13); - this.label14.TabIndex = 34; - this.label14.Text = "MultiSampling:"; - this.label14.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label14, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); - // - // RiceMultiSampling_Combo - // - this.RiceMultiSampling_Combo.DisplayMember = "0,1,2,3"; - this.RiceMultiSampling_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceMultiSampling_Combo.FormattingEnabled = true; - this.RiceMultiSampling_Combo.Items.AddRange(new object[] { + this.RiceScreenUpdateSetting_Combo.Location = new System.Drawing.Point(409, 201); + this.RiceScreenUpdateSetting_Combo.Name = "RiceScreenUpdateSetting_Combo"; + this.RiceScreenUpdateSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceScreenUpdateSetting_Combo.TabIndex = 35; + this.toolTip1.SetToolTip(this.RiceScreenUpdateSetting_Combo, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(326, 177); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(75, 13); + this.label14.TabIndex = 34; + this.label14.Text = "MultiSampling:"; + this.label14.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label14, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); + // + // RiceMultiSampling_Combo + // + this.RiceMultiSampling_Combo.DisplayMember = "0,1,2,3"; + this.RiceMultiSampling_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceMultiSampling_Combo.FormattingEnabled = true; + this.RiceMultiSampling_Combo.Items.AddRange(new object[] { "Off", "2x", "4x", "8x", "16x"}); - this.RiceMultiSampling_Combo.Location = new System.Drawing.Point(409, 174); - this.RiceMultiSampling_Combo.Name = "RiceMultiSampling_Combo"; - this.RiceMultiSampling_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceMultiSampling_Combo.TabIndex = 33; - this.toolTip1.SetToolTip(this.RiceMultiSampling_Combo, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); - // - // label13 - // - this.label13.AutoSize = true; - this.label13.Location = new System.Drawing.Point(287, 150); - this.label13.Name = "label13"; - this.label13.Size = new System.Drawing.Size(118, 13); - this.label13.TabIndex = 32; - this.label13.Text = "OpenGLRenderSetting:"; - this.label13.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label13, "OpenGL level to support.\r\n\r\nDefault: Auto"); - // - // RiceOpenGLRenderSetting_Combo - // - this.RiceOpenGLRenderSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceOpenGLRenderSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceOpenGLRenderSetting_Combo.FormattingEnabled = true; - this.RiceOpenGLRenderSetting_Combo.Items.AddRange(new object[] { + this.RiceMultiSampling_Combo.Location = new System.Drawing.Point(409, 174); + this.RiceMultiSampling_Combo.Name = "RiceMultiSampling_Combo"; + this.RiceMultiSampling_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceMultiSampling_Combo.TabIndex = 33; + this.toolTip1.SetToolTip(this.RiceMultiSampling_Combo, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(287, 150); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(118, 13); + this.label13.TabIndex = 32; + this.label13.Text = "OpenGLRenderSetting:"; + this.label13.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label13, "OpenGL level to support.\r\n\r\nDefault: Auto"); + // + // RiceOpenGLRenderSetting_Combo + // + this.RiceOpenGLRenderSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceOpenGLRenderSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceOpenGLRenderSetting_Combo.FormattingEnabled = true; + this.RiceOpenGLRenderSetting_Combo.Items.AddRange(new object[] { "Auto", "OGL_1.1", "OGL_1.2", @@ -3119,379 +3121,379 @@ "OGL_TNT2", "NVIDIA_OGL", "OpenGL Fragment Program Extension"}); - this.RiceOpenGLRenderSetting_Combo.Location = new System.Drawing.Point(409, 147); - this.RiceOpenGLRenderSetting_Combo.Name = "RiceOpenGLRenderSetting_Combo"; - this.RiceOpenGLRenderSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceOpenGLRenderSetting_Combo.TabIndex = 31; - this.toolTip1.SetToolTip(this.RiceOpenGLRenderSetting_Combo, "OpenGL level to support.\r\n\r\nDefault: Auto"); - // - // label11 - // - this.label11.AutoSize = true; - this.label11.Location = new System.Drawing.Point(326, 123); - this.label11.Name = "label11"; - this.label11.Size = new System.Drawing.Size(69, 13); - this.label11.TabIndex = 30; - this.label11.Text = "Color Quality:"; - this.label11.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label11, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); - // - // RiceColorQuality_Combo - // - this.RiceColorQuality_Combo.DisplayMember = "0,1,2,3"; - this.RiceColorQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceColorQuality_Combo.FormattingEnabled = true; - this.RiceColorQuality_Combo.Items.AddRange(new object[] { + this.RiceOpenGLRenderSetting_Combo.Location = new System.Drawing.Point(409, 147); + this.RiceOpenGLRenderSetting_Combo.Name = "RiceOpenGLRenderSetting_Combo"; + this.RiceOpenGLRenderSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceOpenGLRenderSetting_Combo.TabIndex = 31; + this.toolTip1.SetToolTip(this.RiceOpenGLRenderSetting_Combo, "OpenGL level to support.\r\n\r\nDefault: Auto"); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(326, 123); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(69, 13); + this.label11.TabIndex = 30; + this.label11.Text = "Color Quality:"; + this.label11.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label11, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); + // + // RiceColorQuality_Combo + // + this.RiceColorQuality_Combo.DisplayMember = "0,1,2,3"; + this.RiceColorQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceColorQuality_Combo.FormattingEnabled = true; + this.RiceColorQuality_Combo.Items.AddRange(new object[] { "32-bits", "16-bits"}); - this.RiceColorQuality_Combo.Location = new System.Drawing.Point(409, 120); - this.RiceColorQuality_Combo.Name = "RiceColorQuality_Combo"; - this.RiceColorQuality_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceColorQuality_Combo.TabIndex = 29; - this.toolTip1.SetToolTip(this.RiceColorQuality_Combo, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); - // - // label10 - // - this.label10.AutoSize = true; - this.label10.Location = new System.Drawing.Point(326, 96); - this.label10.Name = "label10"; - this.label10.Size = new System.Drawing.Size(77, 13); - this.label10.TabIndex = 28; - this.label10.Text = "Z-buffer depth:"; - this.label10.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label10, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); - // - // RiceOpenGLDepthBufferSetting_Combo - // - this.RiceOpenGLDepthBufferSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceOpenGLDepthBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceOpenGLDepthBufferSetting_Combo.FormattingEnabled = true; - this.RiceOpenGLDepthBufferSetting_Combo.Items.AddRange(new object[] { + this.RiceColorQuality_Combo.Location = new System.Drawing.Point(409, 120); + this.RiceColorQuality_Combo.Name = "RiceColorQuality_Combo"; + this.RiceColorQuality_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceColorQuality_Combo.TabIndex = 29; + this.toolTip1.SetToolTip(this.RiceColorQuality_Combo, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(326, 96); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(77, 13); + this.label10.TabIndex = 28; + this.label10.Text = "Z-buffer depth:"; + this.label10.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label10, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); + // + // RiceOpenGLDepthBufferSetting_Combo + // + this.RiceOpenGLDepthBufferSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceOpenGLDepthBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceOpenGLDepthBufferSetting_Combo.FormattingEnabled = true; + this.RiceOpenGLDepthBufferSetting_Combo.Items.AddRange(new object[] { "16-bits", "32-bits"}); - this.RiceOpenGLDepthBufferSetting_Combo.Location = new System.Drawing.Point(409, 93); - this.RiceOpenGLDepthBufferSetting_Combo.Name = "RiceOpenGLDepthBufferSetting_Combo"; - this.RiceOpenGLDepthBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceOpenGLDepthBufferSetting_Combo.TabIndex = 27; - this.toolTip1.SetToolTip(this.RiceOpenGLDepthBufferSetting_Combo, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); - // - // label9 - // - this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(298, 70); - this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(105, 13); - this.label9.TabIndex = 26; - this.label9.Text = "Texture Color Depth:"; - this.label9.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label9, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); - // - // RiceTextureQuality_Combo - // - this.RiceTextureQuality_Combo.DisplayMember = "0,1,2,3"; - this.RiceTextureQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceTextureQuality_Combo.FormattingEnabled = true; - this.RiceTextureQuality_Combo.Items.AddRange(new object[] { + this.RiceOpenGLDepthBufferSetting_Combo.Location = new System.Drawing.Point(409, 93); + this.RiceOpenGLDepthBufferSetting_Combo.Name = "RiceOpenGLDepthBufferSetting_Combo"; + this.RiceOpenGLDepthBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceOpenGLDepthBufferSetting_Combo.TabIndex = 27; + this.toolTip1.SetToolTip(this.RiceOpenGLDepthBufferSetting_Combo, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(298, 70); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(105, 13); + this.label9.TabIndex = 26; + this.label9.Text = "Texture Color Depth:"; + this.label9.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label9, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); + // + // RiceTextureQuality_Combo + // + this.RiceTextureQuality_Combo.DisplayMember = "0,1,2,3"; + this.RiceTextureQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceTextureQuality_Combo.FormattingEnabled = true; + this.RiceTextureQuality_Combo.Items.AddRange(new object[] { "Default", "32-bits", "16-bits"}); - this.RiceTextureQuality_Combo.Location = new System.Drawing.Point(409, 66); - this.RiceTextureQuality_Combo.Name = "RiceTextureQuality_Combo"; - this.RiceTextureQuality_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceTextureQuality_Combo.TabIndex = 25; - this.toolTip1.SetToolTip(this.RiceTextureQuality_Combo, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); - // - // RiceEnableVertexShader_CB - // - this.RiceEnableVertexShader_CB.AutoSize = true; - this.RiceEnableVertexShader_CB.Location = new System.Drawing.Point(23, 223); - this.RiceEnableVertexShader_CB.Name = "RiceEnableVertexShader_CB"; - this.RiceEnableVertexShader_CB.Size = new System.Drawing.Size(93, 17); - this.RiceEnableVertexShader_CB.TabIndex = 24; - this.RiceEnableVertexShader_CB.Text = "Vertex Shader"; - this.toolTip1.SetToolTip(this.RiceEnableVertexShader_CB, "Use GPU vertex shader.\r\n\r\nDefault: Off"); - this.RiceEnableVertexShader_CB.UseVisualStyleBackColor = true; - // - // RiceSkipFrame_CB - // - this.RiceSkipFrame_CB.AutoSize = true; - this.RiceSkipFrame_CB.Location = new System.Drawing.Point(23, 246); - this.RiceSkipFrame_CB.Name = "RiceSkipFrame_CB"; - this.RiceSkipFrame_CB.Size = new System.Drawing.Size(79, 17); - this.RiceSkipFrame_CB.TabIndex = 23; - this.RiceSkipFrame_CB.Text = "Skip Frame"; - this.toolTip1.SetToolTip(this.RiceSkipFrame_CB, "If this option is enabled, the plugin will skip every other frame.\r\n\r\nDefault: Of" + + this.RiceTextureQuality_Combo.Location = new System.Drawing.Point(409, 66); + this.RiceTextureQuality_Combo.Name = "RiceTextureQuality_Combo"; + this.RiceTextureQuality_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceTextureQuality_Combo.TabIndex = 25; + this.toolTip1.SetToolTip(this.RiceTextureQuality_Combo, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); + // + // RiceEnableVertexShader_CB + // + this.RiceEnableVertexShader_CB.AutoSize = true; + this.RiceEnableVertexShader_CB.Location = new System.Drawing.Point(23, 223); + this.RiceEnableVertexShader_CB.Name = "RiceEnableVertexShader_CB"; + this.RiceEnableVertexShader_CB.Size = new System.Drawing.Size(93, 17); + this.RiceEnableVertexShader_CB.TabIndex = 24; + this.RiceEnableVertexShader_CB.Text = "Vertex Shader"; + this.toolTip1.SetToolTip(this.RiceEnableVertexShader_CB, "Use GPU vertex shader.\r\n\r\nDefault: Off"); + this.RiceEnableVertexShader_CB.UseVisualStyleBackColor = true; + // + // RiceSkipFrame_CB + // + this.RiceSkipFrame_CB.AutoSize = true; + this.RiceSkipFrame_CB.Location = new System.Drawing.Point(23, 246); + this.RiceSkipFrame_CB.Name = "RiceSkipFrame_CB"; + this.RiceSkipFrame_CB.Size = new System.Drawing.Size(79, 17); + this.RiceSkipFrame_CB.TabIndex = 23; + this.RiceSkipFrame_CB.Text = "Skip Frame"; + this.toolTip1.SetToolTip(this.RiceSkipFrame_CB, "If this option is enabled, the plugin will skip every other frame.\r\n\r\nDefault: Of" + "f"); - this.RiceSkipFrame_CB.UseVisualStyleBackColor = true; - // - // RiceEnableHacks_CB - // - this.RiceEnableHacks_CB.AutoSize = true; - this.RiceEnableHacks_CB.Location = new System.Drawing.Point(23, 154); - this.RiceEnableHacks_CB.Name = "RiceEnableHacks_CB"; - this.RiceEnableHacks_CB.Size = new System.Drawing.Size(166, 17); - this.RiceEnableHacks_CB.TabIndex = 17; - this.RiceEnableHacks_CB.Text = "Enable game-specific settings"; - this.toolTip1.SetToolTip(this.RiceEnableHacks_CB, "Enable game-specific settings from INI file.\r\n\r\nDefault: On"); - this.RiceEnableHacks_CB.UseVisualStyleBackColor = true; - // - // RiceFullTMEMEmulation_CB - // - this.RiceFullTMEMEmulation_CB.AutoSize = true; - this.RiceFullTMEMEmulation_CB.Location = new System.Drawing.Point(23, 177); - this.RiceFullTMEMEmulation_CB.Name = "RiceFullTMEMEmulation_CB"; - this.RiceFullTMEMEmulation_CB.Size = new System.Drawing.Size(170, 17); - this.RiceFullTMEMEmulation_CB.TabIndex = 16; - this.RiceFullTMEMEmulation_CB.Text = "Full Texture Memory Emulation"; - this.toolTip1.SetToolTip(this.RiceFullTMEMEmulation_CB, "N64 Texture Memory Full Emulation.\r\nMay fix some games, may break others.\r\n\r\nDefa" + + this.RiceSkipFrame_CB.UseVisualStyleBackColor = true; + // + // RiceEnableHacks_CB + // + this.RiceEnableHacks_CB.AutoSize = true; + this.RiceEnableHacks_CB.Location = new System.Drawing.Point(23, 154); + this.RiceEnableHacks_CB.Name = "RiceEnableHacks_CB"; + this.RiceEnableHacks_CB.Size = new System.Drawing.Size(166, 17); + this.RiceEnableHacks_CB.TabIndex = 17; + this.RiceEnableHacks_CB.Text = "Enable game-specific settings"; + this.toolTip1.SetToolTip(this.RiceEnableHacks_CB, "Enable game-specific settings from INI file.\r\n\r\nDefault: On"); + this.RiceEnableHacks_CB.UseVisualStyleBackColor = true; + // + // RiceFullTMEMEmulation_CB + // + this.RiceFullTMEMEmulation_CB.AutoSize = true; + this.RiceFullTMEMEmulation_CB.Location = new System.Drawing.Point(23, 177); + this.RiceFullTMEMEmulation_CB.Name = "RiceFullTMEMEmulation_CB"; + this.RiceFullTMEMEmulation_CB.Size = new System.Drawing.Size(170, 17); + this.RiceFullTMEMEmulation_CB.TabIndex = 16; + this.RiceFullTMEMEmulation_CB.Text = "Full Texture Memory Emulation"; + this.toolTip1.SetToolTip(this.RiceFullTMEMEmulation_CB, "N64 Texture Memory Full Emulation.\r\nMay fix some games, may break others.\r\n\r\nDefa" + "ult: Off"); - this.RiceFullTMEMEmulation_CB.UseVisualStyleBackColor = true; - // - // RiceOpenGLVertexClipper_CB - // - this.RiceOpenGLVertexClipper_CB.AutoSize = true; - this.RiceOpenGLVertexClipper_CB.Location = new System.Drawing.Point(23, 200); - this.RiceOpenGLVertexClipper_CB.Name = "RiceOpenGLVertexClipper_CB"; - this.RiceOpenGLVertexClipper_CB.Size = new System.Drawing.Size(91, 17); - this.RiceOpenGLVertexClipper_CB.TabIndex = 15; - this.RiceOpenGLVertexClipper_CB.Text = "Vertex Clipper"; - this.toolTip1.SetToolTip(this.RiceOpenGLVertexClipper_CB, "Enable vertex clipper for fog operations.\r\n\r\nDefault: Off"); - this.RiceOpenGLVertexClipper_CB.UseVisualStyleBackColor = true; - // - // AnisotropicFiltering_LB - // - this.AnisotropicFiltering_LB.AutoSize = true; - this.AnisotropicFiltering_LB.Location = new System.Drawing.Point(287, 247); - this.AnisotropicFiltering_LB.Name = "AnisotropicFiltering_LB"; - this.AnisotropicFiltering_LB.Size = new System.Drawing.Size(116, 13); - this.AnisotropicFiltering_LB.TabIndex = 14; - this.AnisotropicFiltering_LB.Text = "Anisotropic Filtering: 00"; - this.toolTip1.SetToolTip(this.AnisotropicFiltering_LB, resources.GetString("AnisotropicFiltering_LB.ToolTip")); - // - // RiceAnisotropicFiltering_TB - // - this.RiceAnisotropicFiltering_TB.AutoSize = false; - this.RiceAnisotropicFiltering_TB.Location = new System.Drawing.Point(409, 235); - this.RiceAnisotropicFiltering_TB.Margin = new System.Windows.Forms.Padding(0); - this.RiceAnisotropicFiltering_TB.Maximum = 16; - this.RiceAnisotropicFiltering_TB.Name = "RiceAnisotropicFiltering_TB"; - this.RiceAnisotropicFiltering_TB.Size = new System.Drawing.Size(121, 25); - this.RiceAnisotropicFiltering_TB.TabIndex = 13; - this.toolTip1.SetToolTip(this.RiceAnisotropicFiltering_TB, resources.GetString("RiceAnisotropicFiltering_TB.ToolTip")); - this.RiceAnisotropicFiltering_TB.Scroll += new System.EventHandler(this.RiceAnisotropicFiltering_Tb_Scroll_1); - // - // label6 - // - this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(336, 44); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(67, 13); - this.label6.TabIndex = 12; - this.label6.Text = "Fog Method:"; - this.label6.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label6, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); - // - // RiceFogMethod_Combo - // - this.RiceFogMethod_Combo.DisplayMember = "0,1,2,3"; - this.RiceFogMethod_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFogMethod_Combo.FormattingEnabled = true; - this.RiceFogMethod_Combo.Items.AddRange(new object[] { + this.RiceFullTMEMEmulation_CB.UseVisualStyleBackColor = true; + // + // RiceOpenGLVertexClipper_CB + // + this.RiceOpenGLVertexClipper_CB.AutoSize = true; + this.RiceOpenGLVertexClipper_CB.Location = new System.Drawing.Point(23, 200); + this.RiceOpenGLVertexClipper_CB.Name = "RiceOpenGLVertexClipper_CB"; + this.RiceOpenGLVertexClipper_CB.Size = new System.Drawing.Size(91, 17); + this.RiceOpenGLVertexClipper_CB.TabIndex = 15; + this.RiceOpenGLVertexClipper_CB.Text = "Vertex Clipper"; + this.toolTip1.SetToolTip(this.RiceOpenGLVertexClipper_CB, "Enable vertex clipper for fog operations.\r\n\r\nDefault: Off"); + this.RiceOpenGLVertexClipper_CB.UseVisualStyleBackColor = true; + // + // AnisotropicFiltering_LB + // + this.AnisotropicFiltering_LB.AutoSize = true; + this.AnisotropicFiltering_LB.Location = new System.Drawing.Point(287, 247); + this.AnisotropicFiltering_LB.Name = "AnisotropicFiltering_LB"; + this.AnisotropicFiltering_LB.Size = new System.Drawing.Size(116, 13); + this.AnisotropicFiltering_LB.TabIndex = 14; + this.AnisotropicFiltering_LB.Text = "Anisotropic Filtering: 00"; + this.toolTip1.SetToolTip(this.AnisotropicFiltering_LB, resources.GetString("AnisotropicFiltering_LB.ToolTip")); + // + // RiceAnisotropicFiltering_TB + // + this.RiceAnisotropicFiltering_TB.AutoSize = false; + this.RiceAnisotropicFiltering_TB.Location = new System.Drawing.Point(409, 235); + this.RiceAnisotropicFiltering_TB.Margin = new System.Windows.Forms.Padding(0); + this.RiceAnisotropicFiltering_TB.Maximum = 16; + this.RiceAnisotropicFiltering_TB.Name = "RiceAnisotropicFiltering_TB"; + this.RiceAnisotropicFiltering_TB.Size = new System.Drawing.Size(121, 25); + this.RiceAnisotropicFiltering_TB.TabIndex = 13; + this.toolTip1.SetToolTip(this.RiceAnisotropicFiltering_TB, resources.GetString("RiceAnisotropicFiltering_TB.ToolTip")); + this.RiceAnisotropicFiltering_TB.Scroll += new System.EventHandler(this.RiceAnisotropicFiltering_Tb_Scroll_1); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(336, 44); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(67, 13); + this.label6.TabIndex = 12; + this.label6.Text = "Fog Method:"; + this.label6.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label6, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); + // + // RiceFogMethod_Combo + // + this.RiceFogMethod_Combo.DisplayMember = "0,1,2,3"; + this.RiceFogMethod_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFogMethod_Combo.FormattingEnabled = true; + this.RiceFogMethod_Combo.Items.AddRange(new object[] { "Disabled", "Enabled (N64 choose)", "Force Fog"}); - this.RiceFogMethod_Combo.Location = new System.Drawing.Point(409, 39); - this.RiceFogMethod_Combo.Name = "RiceFogMethod_Combo"; - this.RiceFogMethod_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceFogMethod_Combo.TabIndex = 11; - this.toolTip1.SetToolTip(this.RiceFogMethod_Combo, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(314, 21); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(89, 13); - this.label5.TabIndex = 10; - this.label5.Text = "Use Mipmapping:"; - this.label5.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label5, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + + this.RiceFogMethod_Combo.Location = new System.Drawing.Point(409, 39); + this.RiceFogMethod_Combo.Name = "RiceFogMethod_Combo"; + this.RiceFogMethod_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceFogMethod_Combo.TabIndex = 11; + this.toolTip1.SetToolTip(this.RiceFogMethod_Combo, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(314, 21); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(89, 13); + this.label5.TabIndex = 10; + this.label5.Text = "Use Mipmapping:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label5, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + "e in some case.\r\n\r\nDefault: Bilinear\r\n"); - // - // RiceMipmapping_Combo - // - this.RiceMipmapping_Combo.DisplayMember = "0,1,2,3"; - this.RiceMipmapping_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceMipmapping_Combo.FormattingEnabled = true; - this.RiceMipmapping_Combo.Items.AddRange(new object[] { + // + // RiceMipmapping_Combo + // + this.RiceMipmapping_Combo.DisplayMember = "0,1,2,3"; + this.RiceMipmapping_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceMipmapping_Combo.FormattingEnabled = true; + this.RiceMipmapping_Combo.Items.AddRange(new object[] { "None", "Nearest", "Bilinear", "Trilinear"}); - this.RiceMipmapping_Combo.Location = new System.Drawing.Point(409, 16); - this.RiceMipmapping_Combo.Name = "RiceMipmapping_Combo"; - this.RiceMipmapping_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceMipmapping_Combo.TabIndex = 9; - this.toolTip1.SetToolTip(this.RiceMipmapping_Combo, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + + this.RiceMipmapping_Combo.Location = new System.Drawing.Point(409, 16); + this.RiceMipmapping_Combo.Name = "RiceMipmapping_Combo"; + this.RiceMipmapping_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceMipmapping_Combo.TabIndex = 9; + this.toolTip1.SetToolTip(this.RiceMipmapping_Combo, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + "e in some case.\r\n\r\nDefault: Bilinear"); - // - // RiceWinFrameMode_CB - // - this.RiceWinFrameMode_CB.AutoSize = true; - this.RiceWinFrameMode_CB.Location = new System.Drawing.Point(23, 33); - this.RiceWinFrameMode_CB.Name = "RiceWinFrameMode_CB"; - this.RiceWinFrameMode_CB.Size = new System.Drawing.Size(140, 17); - this.RiceWinFrameMode_CB.TabIndex = 8; - this.RiceWinFrameMode_CB.Text = "Enable WinFrame Mode"; - this.toolTip1.SetToolTip(this.RiceWinFrameMode_CB, "If enabled, graphics will be drawn in WinFrame mode instead of solid and texture " + + // + // RiceWinFrameMode_CB + // + this.RiceWinFrameMode_CB.AutoSize = true; + this.RiceWinFrameMode_CB.Location = new System.Drawing.Point(23, 33); + this.RiceWinFrameMode_CB.Name = "RiceWinFrameMode_CB"; + this.RiceWinFrameMode_CB.Size = new System.Drawing.Size(140, 17); + this.RiceWinFrameMode_CB.TabIndex = 8; + this.RiceWinFrameMode_CB.Text = "Enable WinFrame Mode"; + this.toolTip1.SetToolTip(this.RiceWinFrameMode_CB, "If enabled, graphics will be drawn in WinFrame mode instead of solid and texture " + "mode.\r\n\r\nDefault: Off"); - this.RiceWinFrameMode_CB.UseVisualStyleBackColor = true; - // - // RiceInN64Resolution_CB - // - this.RiceInN64Resolution_CB.AutoSize = true; - this.RiceInN64Resolution_CB.Location = new System.Drawing.Point(23, 56); - this.RiceInN64Resolution_CB.Name = "RiceInN64Resolution_CB"; - this.RiceInN64Resolution_CB.Size = new System.Drawing.Size(166, 17); - this.RiceInN64Resolution_CB.TabIndex = 7; - this.RiceInN64Resolution_CB.Text = "Force Native N64 Resolution"; - this.toolTip1.SetToolTip(this.RiceInN64Resolution_CB, "Force emulated frame buffers to be in N64 native resolution.\r\n\r\nDefault: Off"); - this.RiceInN64Resolution_CB.UseVisualStyleBackColor = true; - // - // RiceFastTextureLoading_CB - // - this.RiceFastTextureLoading_CB.AutoSize = true; - this.RiceFastTextureLoading_CB.Location = new System.Drawing.Point(23, 82); - this.RiceFastTextureLoading_CB.Name = "RiceFastTextureLoading_CB"; - this.RiceFastTextureLoading_CB.Size = new System.Drawing.Size(126, 17); - this.RiceFastTextureLoading_CB.TabIndex = 5; - this.RiceFastTextureLoading_CB.Text = "Fast Texture Loading"; - this.toolTip1.SetToolTip(this.RiceFastTextureLoading_CB, "Use a faster algorithm to speed up texture loading and CRC computation.\r\n\r\nDefaul" + + this.RiceWinFrameMode_CB.UseVisualStyleBackColor = true; + // + // RiceInN64Resolution_CB + // + this.RiceInN64Resolution_CB.AutoSize = true; + this.RiceInN64Resolution_CB.Location = new System.Drawing.Point(23, 56); + this.RiceInN64Resolution_CB.Name = "RiceInN64Resolution_CB"; + this.RiceInN64Resolution_CB.Size = new System.Drawing.Size(166, 17); + this.RiceInN64Resolution_CB.TabIndex = 7; + this.RiceInN64Resolution_CB.Text = "Force Native N64 Resolution"; + this.toolTip1.SetToolTip(this.RiceInN64Resolution_CB, "Force emulated frame buffers to be in N64 native resolution.\r\n\r\nDefault: Off"); + this.RiceInN64Resolution_CB.UseVisualStyleBackColor = true; + // + // RiceFastTextureLoading_CB + // + this.RiceFastTextureLoading_CB.AutoSize = true; + this.RiceFastTextureLoading_CB.Location = new System.Drawing.Point(23, 82); + this.RiceFastTextureLoading_CB.Name = "RiceFastTextureLoading_CB"; + this.RiceFastTextureLoading_CB.Size = new System.Drawing.Size(126, 17); + this.RiceFastTextureLoading_CB.TabIndex = 5; + this.RiceFastTextureLoading_CB.Text = "Fast Texture Loading"; + this.toolTip1.SetToolTip(this.RiceFastTextureLoading_CB, "Use a faster algorithm to speed up texture loading and CRC computation.\r\n\r\nDefaul" + "t: Off"); - this.RiceFastTextureLoading_CB.UseVisualStyleBackColor = true; - // - // RiceAccurateTextureMapping_CB - // - this.RiceAccurateTextureMapping_CB.AutoSize = true; - this.RiceAccurateTextureMapping_CB.Location = new System.Drawing.Point(23, 105); - this.RiceAccurateTextureMapping_CB.Name = "RiceAccurateTextureMapping_CB"; - this.RiceAccurateTextureMapping_CB.Size = new System.Drawing.Size(152, 17); - this.RiceAccurateTextureMapping_CB.TabIndex = 4; - this.RiceAccurateTextureMapping_CB.Text = "Accurate Texture Mapping"; - this.toolTip1.SetToolTip(this.RiceAccurateTextureMapping_CB, "Use different texture coordinate clamping code.\r\n\r\nDefault: On"); - this.RiceAccurateTextureMapping_CB.UseVisualStyleBackColor = true; - // - // RiceSaveVRAM_CB - // - this.RiceSaveVRAM_CB.AutoSize = true; - this.RiceSaveVRAM_CB.Location = new System.Drawing.Point(23, 128); - this.RiceSaveVRAM_CB.Name = "RiceSaveVRAM_CB"; - this.RiceSaveVRAM_CB.Size = new System.Drawing.Size(85, 17); - this.RiceSaveVRAM_CB.TabIndex = 3; - this.RiceSaveVRAM_CB.Text = "Save VRAM"; - this.toolTip1.SetToolTip(this.RiceSaveVRAM_CB, "Try to reduce Video RAM usage (should never be used).\r\n\r\nDefault: Off"); - this.RiceSaveVRAM_CB.UseVisualStyleBackColor = true; - // - // RiceEnableSSE_CB - // - this.RiceEnableSSE_CB.AutoSize = true; - this.RiceEnableSSE_CB.Location = new System.Drawing.Point(23, 10); - this.RiceEnableSSE_CB.Name = "RiceEnableSSE_CB"; - this.RiceEnableSSE_CB.Size = new System.Drawing.Size(83, 17); - this.RiceEnableSSE_CB.TabIndex = 0; - this.RiceEnableSSE_CB.Text = "Enable SSE"; - this.toolTip1.SetToolTip(this.RiceEnableSSE_CB, "Enable/Disable SSE optimizations for capable CPUs.\r\n\r\nDefault: On"); - this.RiceEnableSSE_CB.UseVisualStyleBackColor = true; - // - // RiceGameDefaultTab - // - this.RiceGameDefaultTab.Controls.Add(this.RiceNormalAlphaBlender_CB); - this.RiceGameDefaultTab.Controls.Add(this.RiceDefaultCombinerDisable_CB); - this.RiceGameDefaultTab.Controls.Add(this.RiceFrameBuffer_GroupBox); - this.RiceGameDefaultTab.Location = new System.Drawing.Point(4, 22); - this.RiceGameDefaultTab.Name = "RiceGameDefaultTab"; - this.RiceGameDefaultTab.Size = new System.Drawing.Size(564, 310); - this.RiceGameDefaultTab.TabIndex = 3; - this.RiceGameDefaultTab.Text = "Game Default Options"; - this.RiceGameDefaultTab.UseVisualStyleBackColor = true; - // - // RiceNormalAlphaBlender_CB - // - this.RiceNormalAlphaBlender_CB.AutoSize = true; - this.RiceNormalAlphaBlender_CB.Location = new System.Drawing.Point(63, 19); - this.RiceNormalAlphaBlender_CB.Name = "RiceNormalAlphaBlender_CB"; - this.RiceNormalAlphaBlender_CB.Size = new System.Drawing.Size(128, 17); - this.RiceNormalAlphaBlender_CB.TabIndex = 8; - this.RiceNormalAlphaBlender_CB.Text = "Normal Alpha Blender"; - this.toolTip1.SetToolTip(this.RiceNormalAlphaBlender_CB, "Force to use normal alpha blender.\r\n\r\nDefault: Off"); - this.RiceNormalAlphaBlender_CB.UseVisualStyleBackColor = true; - // - // RiceDefaultCombinerDisable_CB - // - this.RiceDefaultCombinerDisable_CB.AutoSize = true; - this.RiceDefaultCombinerDisable_CB.Location = new System.Drawing.Point(63, 42); - this.RiceDefaultCombinerDisable_CB.Name = "RiceDefaultCombinerDisable_CB"; - this.RiceDefaultCombinerDisable_CB.Size = new System.Drawing.Size(133, 17); - this.RiceDefaultCombinerDisable_CB.TabIndex = 7; - this.RiceDefaultCombinerDisable_CB.Text = "Normal Color Combiner"; - this.toolTip1.SetToolTip(this.RiceDefaultCombinerDisable_CB, "Force to use normal color combiner.\r\n\r\nDefault: Off"); - this.RiceDefaultCombinerDisable_CB.UseVisualStyleBackColor = true; - // - // RiceFrameBuffer_GroupBox - // - this.RiceFrameBuffer_GroupBox.Controls.Add(this.label16); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.label17); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.label18); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferWriteBackControl_Combo); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferSetting_Combo); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceDoubleSizeForSmallTxtrBuf_CB); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceRenderToTexture_Combo); - this.RiceFrameBuffer_GroupBox.Location = new System.Drawing.Point(87, 78); - this.RiceFrameBuffer_GroupBox.Name = "RiceFrameBuffer_GroupBox"; - this.RiceFrameBuffer_GroupBox.Size = new System.Drawing.Size(385, 191); - this.RiceFrameBuffer_GroupBox.TabIndex = 0; - this.RiceFrameBuffer_GroupBox.TabStop = false; - this.RiceFrameBuffer_GroupBox.Text = "N64 Frame Buffer Effect Emulation Options"; - // - // label16 - // - this.label16.AutoSize = true; - this.label16.Location = new System.Drawing.Point(8, 100); - this.label16.Name = "label16"; - this.label16.Size = new System.Drawing.Size(162, 13); - this.label16.TabIndex = 32; - this.label16.Text = "Frame Buffer Write Back Control:"; - this.label16.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label16, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); - // - // label17 - // - this.label17.AutoSize = true; - this.label17.Location = new System.Drawing.Point(8, 46); - this.label17.Name = "label17"; - this.label17.Size = new System.Drawing.Size(106, 13); - this.label17.TabIndex = 34; - this.label17.Text = "Frame Buffer Setting:"; - this.label17.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label17, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); - // - // label18 - // - this.label18.AutoSize = true; - this.label18.Location = new System.Drawing.Point(8, 73); - this.label18.Name = "label18"; - this.label18.Size = new System.Drawing.Size(159, 13); - this.label18.TabIndex = 36; - this.label18.Text = "Rendering to Texture Emulation:"; - this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label18, "Render-to-texture emulation.\r\n\r\nDefault: None"); - // - // RiceFrameBufferWriteBackControl_Combo - // - this.RiceFrameBufferWriteBackControl_Combo.DisplayMember = "0,1,2,3"; - this.RiceFrameBufferWriteBackControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFrameBufferWriteBackControl_Combo.FormattingEnabled = true; - this.RiceFrameBufferWriteBackControl_Combo.Items.AddRange(new object[] { + this.RiceFastTextureLoading_CB.UseVisualStyleBackColor = true; + // + // RiceAccurateTextureMapping_CB + // + this.RiceAccurateTextureMapping_CB.AutoSize = true; + this.RiceAccurateTextureMapping_CB.Location = new System.Drawing.Point(23, 105); + this.RiceAccurateTextureMapping_CB.Name = "RiceAccurateTextureMapping_CB"; + this.RiceAccurateTextureMapping_CB.Size = new System.Drawing.Size(152, 17); + this.RiceAccurateTextureMapping_CB.TabIndex = 4; + this.RiceAccurateTextureMapping_CB.Text = "Accurate Texture Mapping"; + this.toolTip1.SetToolTip(this.RiceAccurateTextureMapping_CB, "Use different texture coordinate clamping code.\r\n\r\nDefault: On"); + this.RiceAccurateTextureMapping_CB.UseVisualStyleBackColor = true; + // + // RiceSaveVRAM_CB + // + this.RiceSaveVRAM_CB.AutoSize = true; + this.RiceSaveVRAM_CB.Location = new System.Drawing.Point(23, 128); + this.RiceSaveVRAM_CB.Name = "RiceSaveVRAM_CB"; + this.RiceSaveVRAM_CB.Size = new System.Drawing.Size(85, 17); + this.RiceSaveVRAM_CB.TabIndex = 3; + this.RiceSaveVRAM_CB.Text = "Save VRAM"; + this.toolTip1.SetToolTip(this.RiceSaveVRAM_CB, "Try to reduce Video RAM usage (should never be used).\r\n\r\nDefault: Off"); + this.RiceSaveVRAM_CB.UseVisualStyleBackColor = true; + // + // RiceEnableSSE_CB + // + this.RiceEnableSSE_CB.AutoSize = true; + this.RiceEnableSSE_CB.Location = new System.Drawing.Point(23, 10); + this.RiceEnableSSE_CB.Name = "RiceEnableSSE_CB"; + this.RiceEnableSSE_CB.Size = new System.Drawing.Size(83, 17); + this.RiceEnableSSE_CB.TabIndex = 0; + this.RiceEnableSSE_CB.Text = "Enable SSE"; + this.toolTip1.SetToolTip(this.RiceEnableSSE_CB, "Enable/Disable SSE optimizations for capable CPUs.\r\n\r\nDefault: On"); + this.RiceEnableSSE_CB.UseVisualStyleBackColor = true; + // + // RiceGameDefaultTab + // + this.RiceGameDefaultTab.Controls.Add(this.RiceNormalAlphaBlender_CB); + this.RiceGameDefaultTab.Controls.Add(this.RiceDefaultCombinerDisable_CB); + this.RiceGameDefaultTab.Controls.Add(this.RiceFrameBuffer_GroupBox); + this.RiceGameDefaultTab.Location = new System.Drawing.Point(4, 22); + this.RiceGameDefaultTab.Name = "RiceGameDefaultTab"; + this.RiceGameDefaultTab.Size = new System.Drawing.Size(564, 310); + this.RiceGameDefaultTab.TabIndex = 3; + this.RiceGameDefaultTab.Text = "Game Default Options"; + this.RiceGameDefaultTab.UseVisualStyleBackColor = true; + // + // RiceNormalAlphaBlender_CB + // + this.RiceNormalAlphaBlender_CB.AutoSize = true; + this.RiceNormalAlphaBlender_CB.Location = new System.Drawing.Point(63, 19); + this.RiceNormalAlphaBlender_CB.Name = "RiceNormalAlphaBlender_CB"; + this.RiceNormalAlphaBlender_CB.Size = new System.Drawing.Size(128, 17); + this.RiceNormalAlphaBlender_CB.TabIndex = 8; + this.RiceNormalAlphaBlender_CB.Text = "Normal Alpha Blender"; + this.toolTip1.SetToolTip(this.RiceNormalAlphaBlender_CB, "Force to use normal alpha blender.\r\n\r\nDefault: Off"); + this.RiceNormalAlphaBlender_CB.UseVisualStyleBackColor = true; + // + // RiceDefaultCombinerDisable_CB + // + this.RiceDefaultCombinerDisable_CB.AutoSize = true; + this.RiceDefaultCombinerDisable_CB.Location = new System.Drawing.Point(63, 42); + this.RiceDefaultCombinerDisable_CB.Name = "RiceDefaultCombinerDisable_CB"; + this.RiceDefaultCombinerDisable_CB.Size = new System.Drawing.Size(133, 17); + this.RiceDefaultCombinerDisable_CB.TabIndex = 7; + this.RiceDefaultCombinerDisable_CB.Text = "Normal Color Combiner"; + this.toolTip1.SetToolTip(this.RiceDefaultCombinerDisable_CB, "Force to use normal color combiner.\r\n\r\nDefault: Off"); + this.RiceDefaultCombinerDisable_CB.UseVisualStyleBackColor = true; + // + // RiceFrameBuffer_GroupBox + // + this.RiceFrameBuffer_GroupBox.Controls.Add(this.label16); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.label17); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.label18); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferWriteBackControl_Combo); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferSetting_Combo); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceDoubleSizeForSmallTxtrBuf_CB); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceRenderToTexture_Combo); + this.RiceFrameBuffer_GroupBox.Location = new System.Drawing.Point(87, 78); + this.RiceFrameBuffer_GroupBox.Name = "RiceFrameBuffer_GroupBox"; + this.RiceFrameBuffer_GroupBox.Size = new System.Drawing.Size(385, 191); + this.RiceFrameBuffer_GroupBox.TabIndex = 0; + this.RiceFrameBuffer_GroupBox.TabStop = false; + this.RiceFrameBuffer_GroupBox.Text = "N64 Frame Buffer Effect Emulation Options"; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Location = new System.Drawing.Point(8, 100); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(162, 13); + this.label16.TabIndex = 32; + this.label16.Text = "Frame Buffer Write Back Control:"; + this.label16.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label16, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Location = new System.Drawing.Point(8, 46); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(106, 13); + this.label17.TabIndex = 34; + this.label17.Text = "Frame Buffer Setting:"; + this.label17.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label17, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Location = new System.Drawing.Point(8, 73); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(159, 13); + this.label18.TabIndex = 36; + this.label18.Text = "Rendering to Texture Emulation:"; + this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label18, "Render-to-texture emulation.\r\n\r\nDefault: None"); + // + // RiceFrameBufferWriteBackControl_Combo + // + this.RiceFrameBufferWriteBackControl_Combo.DisplayMember = "0,1,2,3"; + this.RiceFrameBufferWriteBackControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFrameBufferWriteBackControl_Combo.FormattingEnabled = true; + this.RiceFrameBufferWriteBackControl_Combo.Items.AddRange(new object[] { "Every Frame", "Every 2 Frames", "Every 3 Frames", @@ -3500,140 +3502,140 @@ "Every 6 Frames", "Every 7 Frames", "Every 8 Frames"}); - this.RiceFrameBufferWriteBackControl_Combo.Location = new System.Drawing.Point(226, 97); - this.RiceFrameBufferWriteBackControl_Combo.Name = "RiceFrameBufferWriteBackControl_Combo"; - this.RiceFrameBufferWriteBackControl_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceFrameBufferWriteBackControl_Combo.TabIndex = 33; - this.toolTip1.SetToolTip(this.RiceFrameBufferWriteBackControl_Combo, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); - // - // RiceFrameBufferSetting_Combo - // - this.RiceFrameBufferSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceFrameBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFrameBufferSetting_Combo.FormattingEnabled = true; - this.RiceFrameBufferSetting_Combo.Items.AddRange(new object[] { + this.RiceFrameBufferWriteBackControl_Combo.Location = new System.Drawing.Point(226, 97); + this.RiceFrameBufferWriteBackControl_Combo.Name = "RiceFrameBufferWriteBackControl_Combo"; + this.RiceFrameBufferWriteBackControl_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceFrameBufferWriteBackControl_Combo.TabIndex = 33; + this.toolTip1.SetToolTip(this.RiceFrameBufferWriteBackControl_Combo, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); + // + // RiceFrameBufferSetting_Combo + // + this.RiceFrameBufferSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceFrameBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFrameBufferSetting_Combo.FormattingEnabled = true; + this.RiceFrameBufferSetting_Combo.Items.AddRange(new object[] { "Rom Default", "Disable"}); - this.RiceFrameBufferSetting_Combo.Location = new System.Drawing.Point(226, 43); - this.RiceFrameBufferSetting_Combo.Name = "RiceFrameBufferSetting_Combo"; - this.RiceFrameBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceFrameBufferSetting_Combo.TabIndex = 31; - this.toolTip1.SetToolTip(this.RiceFrameBufferSetting_Combo, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); - // - // RiceDoubleSizeForSmallTxtrBuf_CB - // - this.RiceDoubleSizeForSmallTxtrBuf_CB.AutoSize = true; - this.RiceDoubleSizeForSmallTxtrBuf_CB.Location = new System.Drawing.Point(61, 159); - this.RiceDoubleSizeForSmallTxtrBuf_CB.Name = "RiceDoubleSizeForSmallTxtrBuf_CB"; - this.RiceDoubleSizeForSmallTxtrBuf_CB.Size = new System.Drawing.Size(253, 17); - this.RiceDoubleSizeForSmallTxtrBuf_CB.TabIndex = 3; - this.RiceDoubleSizeForSmallTxtrBuf_CB.Text = "Double Resolution For Small Render-to-Textures"; - this.toolTip1.SetToolTip(this.RiceDoubleSizeForSmallTxtrBuf_CB, "Enable this option to have better render-to-texture quality.\r\nThis requires more " + + this.RiceFrameBufferSetting_Combo.Location = new System.Drawing.Point(226, 43); + this.RiceFrameBufferSetting_Combo.Name = "RiceFrameBufferSetting_Combo"; + this.RiceFrameBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceFrameBufferSetting_Combo.TabIndex = 31; + this.toolTip1.SetToolTip(this.RiceFrameBufferSetting_Combo, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); + // + // RiceDoubleSizeForSmallTxtrBuf_CB + // + this.RiceDoubleSizeForSmallTxtrBuf_CB.AutoSize = true; + this.RiceDoubleSizeForSmallTxtrBuf_CB.Location = new System.Drawing.Point(61, 159); + this.RiceDoubleSizeForSmallTxtrBuf_CB.Name = "RiceDoubleSizeForSmallTxtrBuf_CB"; + this.RiceDoubleSizeForSmallTxtrBuf_CB.Size = new System.Drawing.Size(253, 17); + this.RiceDoubleSizeForSmallTxtrBuf_CB.TabIndex = 3; + this.RiceDoubleSizeForSmallTxtrBuf_CB.Text = "Double Resolution For Small Render-to-Textures"; + this.toolTip1.SetToolTip(this.RiceDoubleSizeForSmallTxtrBuf_CB, "Enable this option to have better render-to-texture quality.\r\nThis requires more " + "VRAM.\r\n\r\nDefault: Off"); - this.RiceDoubleSizeForSmallTxtrBuf_CB.UseVisualStyleBackColor = true; - // - // RiceRenderToTexture_Combo - // - this.RiceRenderToTexture_Combo.DisplayMember = "0,1,2,3"; - this.RiceRenderToTexture_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceRenderToTexture_Combo.FormattingEnabled = true; - this.RiceRenderToTexture_Combo.Items.AddRange(new object[] { + this.RiceDoubleSizeForSmallTxtrBuf_CB.UseVisualStyleBackColor = true; + // + // RiceRenderToTexture_Combo + // + this.RiceRenderToTexture_Combo.DisplayMember = "0,1,2,3"; + this.RiceRenderToTexture_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceRenderToTexture_Combo.FormattingEnabled = true; + this.RiceRenderToTexture_Combo.Items.AddRange(new object[] { "None", "Hide Render-to-texture Effects", "Basic Render-to-texture", "Basic & Write Back", "Write Back & Reload"}); - this.RiceRenderToTexture_Combo.Location = new System.Drawing.Point(226, 70); - this.RiceRenderToTexture_Combo.Name = "RiceRenderToTexture_Combo"; - this.RiceRenderToTexture_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceRenderToTexture_Combo.TabIndex = 35; - this.toolTip1.SetToolTip(this.RiceRenderToTexture_Combo, "Render-to-texture emulation.\r\n\r\nDefault: None"); - // - // RiceTextureEnhancementTab - // - this.RiceTextureEnhancementTab.Controls.Add(this.label12); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceForceTextureFilter_Combo); - this.RiceTextureEnhancementTab.Controls.Add(this.label8); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancementControl_Combo); - this.RiceTextureEnhancementTab.Controls.Add(this.label7); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancement_Combo); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceTexRectOnly_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceSmallTextureOnly_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResCRCOnly_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResTextures_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceDumpTexturesToFiles_CB); - this.RiceTextureEnhancementTab.Location = new System.Drawing.Point(4, 22); - this.RiceTextureEnhancementTab.Name = "RiceTextureEnhancementTab"; - this.RiceTextureEnhancementTab.Size = new System.Drawing.Size(564, 310); - this.RiceTextureEnhancementTab.TabIndex = 2; - this.RiceTextureEnhancementTab.Text = "Texture Enhancement"; - this.RiceTextureEnhancementTab.UseVisualStyleBackColor = true; - // - // label12 - // - this.label12.AutoSize = true; - this.label12.Location = new System.Drawing.Point(134, 115); - this.label12.Name = "label12"; - this.label12.Size = new System.Drawing.Size(115, 13); - this.label12.TabIndex = 33; - this.label12.Text = "Force Texture Filtering:"; - this.toolTip1.SetToolTip(this.label12, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); - // - // RiceForceTextureFilter_Combo - // - this.RiceForceTextureFilter_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceForceTextureFilter_Combo.FormattingEnabled = true; - this.RiceForceTextureFilter_Combo.Items.AddRange(new object[] { + this.RiceRenderToTexture_Combo.Location = new System.Drawing.Point(226, 70); + this.RiceRenderToTexture_Combo.Name = "RiceRenderToTexture_Combo"; + this.RiceRenderToTexture_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceRenderToTexture_Combo.TabIndex = 35; + this.toolTip1.SetToolTip(this.RiceRenderToTexture_Combo, "Render-to-texture emulation.\r\n\r\nDefault: None"); + // + // RiceTextureEnhancementTab + // + this.RiceTextureEnhancementTab.Controls.Add(this.label12); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceForceTextureFilter_Combo); + this.RiceTextureEnhancementTab.Controls.Add(this.label8); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancementControl_Combo); + this.RiceTextureEnhancementTab.Controls.Add(this.label7); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancement_Combo); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceTexRectOnly_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceSmallTextureOnly_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResCRCOnly_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResTextures_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceDumpTexturesToFiles_CB); + this.RiceTextureEnhancementTab.Location = new System.Drawing.Point(4, 22); + this.RiceTextureEnhancementTab.Name = "RiceTextureEnhancementTab"; + this.RiceTextureEnhancementTab.Size = new System.Drawing.Size(564, 310); + this.RiceTextureEnhancementTab.TabIndex = 2; + this.RiceTextureEnhancementTab.Text = "Texture Enhancement"; + this.RiceTextureEnhancementTab.UseVisualStyleBackColor = true; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(134, 115); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(115, 13); + this.label12.TabIndex = 33; + this.label12.Text = "Force Texture Filtering:"; + this.toolTip1.SetToolTip(this.label12, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); + // + // RiceForceTextureFilter_Combo + // + this.RiceForceTextureFilter_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceForceTextureFilter_Combo.FormattingEnabled = true; + this.RiceForceTextureFilter_Combo.Items.AddRange(new object[] { "Let N64 choose", "Force No Filtering", "Force Filtering"}); - this.RiceForceTextureFilter_Combo.Location = new System.Drawing.Point(287, 112); - this.RiceForceTextureFilter_Combo.Name = "RiceForceTextureFilter_Combo"; - this.RiceForceTextureFilter_Combo.Size = new System.Drawing.Size(120, 21); - this.RiceForceTextureFilter_Combo.TabIndex = 32; - this.toolTip1.SetToolTip(this.RiceForceTextureFilter_Combo, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); - // - // label8 - // - this.label8.AutoSize = true; - this.label8.Location = new System.Drawing.Point(134, 169); - this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(151, 13); - this.label8.TabIndex = 31; - this.label8.Text = "Texture Enhancement Control:"; - this.toolTip1.SetToolTip(this.label8, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); - // - // RiceTextureEnhancementControl_Combo - // - this.RiceTextureEnhancementControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceTextureEnhancementControl_Combo.FormattingEnabled = true; - this.RiceTextureEnhancementControl_Combo.Items.AddRange(new object[] { + this.RiceForceTextureFilter_Combo.Location = new System.Drawing.Point(287, 112); + this.RiceForceTextureFilter_Combo.Name = "RiceForceTextureFilter_Combo"; + this.RiceForceTextureFilter_Combo.Size = new System.Drawing.Size(120, 21); + this.RiceForceTextureFilter_Combo.TabIndex = 32; + this.toolTip1.SetToolTip(this.RiceForceTextureFilter_Combo, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(134, 169); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(151, 13); + this.label8.TabIndex = 31; + this.label8.Text = "Texture Enhancement Control:"; + this.toolTip1.SetToolTip(this.label8, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); + // + // RiceTextureEnhancementControl_Combo + // + this.RiceTextureEnhancementControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceTextureEnhancementControl_Combo.FormattingEnabled = true; + this.RiceTextureEnhancementControl_Combo.Items.AddRange(new object[] { "Normal", "Smooth", "Less smooth", "2xSaI smooth", "Less 2xSaI smooth"}); - this.RiceTextureEnhancementControl_Combo.Location = new System.Drawing.Point(287, 166); - this.RiceTextureEnhancementControl_Combo.Name = "RiceTextureEnhancementControl_Combo"; - this.RiceTextureEnhancementControl_Combo.Size = new System.Drawing.Size(120, 21); - this.RiceTextureEnhancementControl_Combo.TabIndex = 30; - this.toolTip1.SetToolTip(this.RiceTextureEnhancementControl_Combo, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); - // - // label7 - // - this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(134, 142); - this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(140, 13); - this.label7.TabIndex = 29; - this.label7.Text = "Texture Enhancement Filter:"; - this.toolTip1.SetToolTip(this.label7, "Primary texture enhancement filter.\r\n\r\nDefault: None\r\n"); - // - // RiceTextureEnhancement_Combo - // - this.RiceTextureEnhancement_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceTextureEnhancement_Combo.FormattingEnabled = true; - this.RiceTextureEnhancement_Combo.Items.AddRange(new object[] { + this.RiceTextureEnhancementControl_Combo.Location = new System.Drawing.Point(287, 166); + this.RiceTextureEnhancementControl_Combo.Name = "RiceTextureEnhancementControl_Combo"; + this.RiceTextureEnhancementControl_Combo.Size = new System.Drawing.Size(120, 21); + this.RiceTextureEnhancementControl_Combo.TabIndex = 30; + this.toolTip1.SetToolTip(this.RiceTextureEnhancementControl_Combo, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(134, 142); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(140, 13); + this.label7.TabIndex = 29; + this.label7.Text = "Texture Enhancement Filter:"; + this.toolTip1.SetToolTip(this.label7, "Primary texture enhancement filter.\r\n\r\nDefault: None\r\n"); + // + // RiceTextureEnhancement_Combo + // + this.RiceTextureEnhancement_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceTextureEnhancement_Combo.FormattingEnabled = true; + this.RiceTextureEnhancement_Combo.Items.AddRange(new object[] { "None", "2X", "2XSAI", @@ -3644,133 +3646,133 @@ "Sharpen More", "External", "Mirrored"}); - this.RiceTextureEnhancement_Combo.Location = new System.Drawing.Point(287, 139); - this.RiceTextureEnhancement_Combo.Name = "RiceTextureEnhancement_Combo"; - this.RiceTextureEnhancement_Combo.Size = new System.Drawing.Size(120, 21); - this.RiceTextureEnhancement_Combo.TabIndex = 28; - this.toolTip1.SetToolTip(this.RiceTextureEnhancement_Combo, "Primary texture enhancement filter.\r\n\r\nDefault: None"); - // - // RiceTexRectOnly_CB - // - this.RiceTexRectOnly_CB.AutoSize = true; - this.RiceTexRectOnly_CB.Location = new System.Drawing.Point(316, 66); - this.RiceTexRectOnly_CB.Name = "RiceTexRectOnly_CB"; - this.RiceTexRectOnly_CB.Size = new System.Drawing.Size(91, 17); - this.RiceTexRectOnly_CB.TabIndex = 27; - this.RiceTexRectOnly_CB.Text = "TexRect Only"; - this.toolTip1.SetToolTip(this.RiceTexRectOnly_CB, "If enabled, texture enhancement will be done only for TxtRect ucode.\r\n\r\nDefault: " + + this.RiceTextureEnhancement_Combo.Location = new System.Drawing.Point(287, 139); + this.RiceTextureEnhancement_Combo.Name = "RiceTextureEnhancement_Combo"; + this.RiceTextureEnhancement_Combo.Size = new System.Drawing.Size(120, 21); + this.RiceTextureEnhancement_Combo.TabIndex = 28; + this.toolTip1.SetToolTip(this.RiceTextureEnhancement_Combo, "Primary texture enhancement filter.\r\n\r\nDefault: None"); + // + // RiceTexRectOnly_CB + // + this.RiceTexRectOnly_CB.AutoSize = true; + this.RiceTexRectOnly_CB.Location = new System.Drawing.Point(316, 66); + this.RiceTexRectOnly_CB.Name = "RiceTexRectOnly_CB"; + this.RiceTexRectOnly_CB.Size = new System.Drawing.Size(91, 17); + this.RiceTexRectOnly_CB.TabIndex = 27; + this.RiceTexRectOnly_CB.Text = "TexRect Only"; + this.toolTip1.SetToolTip(this.RiceTexRectOnly_CB, "If enabled, texture enhancement will be done only for TxtRect ucode.\r\n\r\nDefault: " + "Off"); - this.RiceTexRectOnly_CB.UseVisualStyleBackColor = true; - // - // RiceSmallTextureOnly_CB - // - this.RiceSmallTextureOnly_CB.AutoSize = true; - this.RiceSmallTextureOnly_CB.Location = new System.Drawing.Point(36, 66); - this.RiceSmallTextureOnly_CB.Name = "RiceSmallTextureOnly_CB"; - this.RiceSmallTextureOnly_CB.Size = new System.Drawing.Size(160, 17); - this.RiceSmallTextureOnly_CB.TabIndex = 26; - this.RiceSmallTextureOnly_CB.Text = "Enhance Small Texture Only"; - this.toolTip1.SetToolTip(this.RiceSmallTextureOnly_CB, "If enabled, texture enhancement will be done \r\nonly for textures width+height<=12" + + this.RiceTexRectOnly_CB.UseVisualStyleBackColor = true; + // + // RiceSmallTextureOnly_CB + // + this.RiceSmallTextureOnly_CB.AutoSize = true; + this.RiceSmallTextureOnly_CB.Location = new System.Drawing.Point(36, 66); + this.RiceSmallTextureOnly_CB.Name = "RiceSmallTextureOnly_CB"; + this.RiceSmallTextureOnly_CB.Size = new System.Drawing.Size(160, 17); + this.RiceSmallTextureOnly_CB.TabIndex = 26; + this.RiceSmallTextureOnly_CB.Text = "Enhance Small Texture Only"; + this.toolTip1.SetToolTip(this.RiceSmallTextureOnly_CB, "If enabled, texture enhancement will be done \r\nonly for textures width+height<=12" + "8.\r\n\r\nDefault: Off"); - this.RiceSmallTextureOnly_CB.UseVisualStyleBackColor = true; - // - // RiceLoadHiResCRCOnly_CB - // - this.RiceLoadHiResCRCOnly_CB.AutoSize = true; - this.RiceLoadHiResCRCOnly_CB.Location = new System.Drawing.Point(316, 43); - this.RiceLoadHiResCRCOnly_CB.Name = "RiceLoadHiResCRCOnly_CB"; - this.RiceLoadHiResCRCOnly_CB.Size = new System.Drawing.Size(201, 17); - this.RiceLoadHiResCRCOnly_CB.TabIndex = 25; - this.RiceLoadHiResCRCOnly_CB.Text = "Use CRC only to load Hi-Res Texture"; - this.toolTip1.SetToolTip(this.RiceLoadHiResCRCOnly_CB, "Select hi-resolution textures based only on the CRC and \r\nignore format+size info" + + this.RiceSmallTextureOnly_CB.UseVisualStyleBackColor = true; + // + // RiceLoadHiResCRCOnly_CB + // + this.RiceLoadHiResCRCOnly_CB.AutoSize = true; + this.RiceLoadHiResCRCOnly_CB.Location = new System.Drawing.Point(316, 43); + this.RiceLoadHiResCRCOnly_CB.Name = "RiceLoadHiResCRCOnly_CB"; + this.RiceLoadHiResCRCOnly_CB.Size = new System.Drawing.Size(201, 17); + this.RiceLoadHiResCRCOnly_CB.TabIndex = 25; + this.RiceLoadHiResCRCOnly_CB.Text = "Use CRC only to load Hi-Res Texture"; + this.toolTip1.SetToolTip(this.RiceLoadHiResCRCOnly_CB, "Select hi-resolution textures based only on the CRC and \r\nignore format+size info" + "rmation (Glide64 compatibility).\r\n\r\nDefault: On"); - this.RiceLoadHiResCRCOnly_CB.UseVisualStyleBackColor = true; - // - // RiceLoadHiResTextures_CB - // - this.RiceLoadHiResTextures_CB.AutoSize = true; - this.RiceLoadHiResTextures_CB.Location = new System.Drawing.Point(36, 43); - this.RiceLoadHiResTextures_CB.Name = "RiceLoadHiResTextures_CB"; - this.RiceLoadHiResTextures_CB.Size = new System.Drawing.Size(178, 17); - this.RiceLoadHiResTextures_CB.TabIndex = 24; - this.RiceLoadHiResTextures_CB.Text = "Load Hi-Res textures if available"; - this.toolTip1.SetToolTip(this.RiceLoadHiResTextures_CB, "Enable hi-resolution texture file loading.\r\n\r\nDefault: Off"); - this.RiceLoadHiResTextures_CB.UseVisualStyleBackColor = true; - // - // RiceDumpTexturesToFiles_CB - // - this.RiceDumpTexturesToFiles_CB.AutoSize = true; - this.RiceDumpTexturesToFiles_CB.Location = new System.Drawing.Point(273, 248); - this.RiceDumpTexturesToFiles_CB.Name = "RiceDumpTexturesToFiles_CB"; - this.RiceDumpTexturesToFiles_CB.Size = new System.Drawing.Size(134, 17); - this.RiceDumpTexturesToFiles_CB.TabIndex = 23; - this.RiceDumpTexturesToFiles_CB.Text = "Dump Textures to Files"; - this.toolTip1.SetToolTip(this.RiceDumpTexturesToFiles_CB, "Enable texture dumping.\r\n\r\nDefault: Off"); - this.RiceDumpTexturesToFiles_CB.UseVisualStyleBackColor = true; - // - // RiceGameSpecificTab - // - this.RiceGameSpecificTab.Controls.Add(this.label27); - this.RiceGameSpecificTab.Controls.Add(this.RiceEnableHacksForGame_Combo); - this.RiceGameSpecificTab.Controls.Add(this.RiceForceDepthBuffer_CB); - this.RiceGameSpecificTab.Controls.Add(this.label28); - this.RiceGameSpecificTab.Controls.Add(this.RiceUseCIWidthAndRatio_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label26); - this.RiceGameSpecificTab.Controls.Add(this.RiceRenderToTextureOption_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label25); - this.RiceGameSpecificTab.Controls.Add(this.RiceFrameBufferOption_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label24); - this.RiceGameSpecificTab.Controls.Add(this.RiceNormalBlender_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label23); - this.RiceGameSpecificTab.Controls.Add(this.RiceAccurateTextureMappingHack_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label22); - this.RiceGameSpecificTab.Controls.Add(this.RiceFastTextureCRC_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label21); - this.RiceGameSpecificTab.Controls.Add(this.RiceFullTMEM_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label20); - this.RiceGameSpecificTab.Controls.Add(this.RiceScreenUpdateSettingHack_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label19); - this.RiceGameSpecificTab.Controls.Add(this.RiceVIHeight_Text); - this.RiceGameSpecificTab.Controls.Add(this.label4); - this.RiceGameSpecificTab.Controls.Add(this.RiceVIWidth_Text); - this.RiceGameSpecificTab.Controls.Add(this.RiceTextureScaleHack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceFastLoadTile_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceUseSmallerTexture_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceEnableTxtLOD_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceZHack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RicePrimaryDepthHack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableObjBG_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableBlender_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceForceScreenClear_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceEmulateClear_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceTxtSizeMethod2_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceIncTexRectEdge_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableCulling_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableTextureCRC_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceTexture1Hack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceUseDefaultHacks_CB); - this.RiceGameSpecificTab.Location = new System.Drawing.Point(4, 22); - this.RiceGameSpecificTab.Name = "RiceGameSpecificTab"; - this.RiceGameSpecificTab.Padding = new System.Windows.Forms.Padding(3); - this.RiceGameSpecificTab.Size = new System.Drawing.Size(564, 310); - this.RiceGameSpecificTab.TabIndex = 1; - this.RiceGameSpecificTab.Text = "Per-Game Hacks"; - this.RiceGameSpecificTab.UseVisualStyleBackColor = true; - // - // label27 - // - this.label27.AutoSize = true; - this.label27.Location = new System.Drawing.Point(310, 178); - this.label27.Name = "label27"; - this.label27.Size = new System.Drawing.Size(119, 13); - this.label27.TabIndex = 52; - this.label27.Text = "Enable hacks for game:"; - // - // RiceEnableHacksForGame_Combo - // - this.RiceEnableHacksForGame_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceEnableHacksForGame_Combo.FormattingEnabled = true; - this.RiceEnableHacksForGame_Combo.Items.AddRange(new object[] { + this.RiceLoadHiResCRCOnly_CB.UseVisualStyleBackColor = true; + // + // RiceLoadHiResTextures_CB + // + this.RiceLoadHiResTextures_CB.AutoSize = true; + this.RiceLoadHiResTextures_CB.Location = new System.Drawing.Point(36, 43); + this.RiceLoadHiResTextures_CB.Name = "RiceLoadHiResTextures_CB"; + this.RiceLoadHiResTextures_CB.Size = new System.Drawing.Size(178, 17); + this.RiceLoadHiResTextures_CB.TabIndex = 24; + this.RiceLoadHiResTextures_CB.Text = "Load Hi-Res textures if available"; + this.toolTip1.SetToolTip(this.RiceLoadHiResTextures_CB, "Enable hi-resolution texture file loading.\r\n\r\nDefault: Off"); + this.RiceLoadHiResTextures_CB.UseVisualStyleBackColor = true; + // + // RiceDumpTexturesToFiles_CB + // + this.RiceDumpTexturesToFiles_CB.AutoSize = true; + this.RiceDumpTexturesToFiles_CB.Location = new System.Drawing.Point(273, 248); + this.RiceDumpTexturesToFiles_CB.Name = "RiceDumpTexturesToFiles_CB"; + this.RiceDumpTexturesToFiles_CB.Size = new System.Drawing.Size(134, 17); + this.RiceDumpTexturesToFiles_CB.TabIndex = 23; + this.RiceDumpTexturesToFiles_CB.Text = "Dump Textures to Files"; + this.toolTip1.SetToolTip(this.RiceDumpTexturesToFiles_CB, "Enable texture dumping.\r\n\r\nDefault: Off"); + this.RiceDumpTexturesToFiles_CB.UseVisualStyleBackColor = true; + // + // RiceGameSpecificTab + // + this.RiceGameSpecificTab.Controls.Add(this.label27); + this.RiceGameSpecificTab.Controls.Add(this.RiceEnableHacksForGame_Combo); + this.RiceGameSpecificTab.Controls.Add(this.RiceForceDepthBuffer_CB); + this.RiceGameSpecificTab.Controls.Add(this.label28); + this.RiceGameSpecificTab.Controls.Add(this.RiceUseCIWidthAndRatio_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label26); + this.RiceGameSpecificTab.Controls.Add(this.RiceRenderToTextureOption_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label25); + this.RiceGameSpecificTab.Controls.Add(this.RiceFrameBufferOption_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label24); + this.RiceGameSpecificTab.Controls.Add(this.RiceNormalBlender_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label23); + this.RiceGameSpecificTab.Controls.Add(this.RiceAccurateTextureMappingHack_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label22); + this.RiceGameSpecificTab.Controls.Add(this.RiceFastTextureCRC_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label21); + this.RiceGameSpecificTab.Controls.Add(this.RiceFullTMEM_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label20); + this.RiceGameSpecificTab.Controls.Add(this.RiceScreenUpdateSettingHack_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label19); + this.RiceGameSpecificTab.Controls.Add(this.RiceVIHeight_Text); + this.RiceGameSpecificTab.Controls.Add(this.label4); + this.RiceGameSpecificTab.Controls.Add(this.RiceVIWidth_Text); + this.RiceGameSpecificTab.Controls.Add(this.RiceTextureScaleHack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceFastLoadTile_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceUseSmallerTexture_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceEnableTxtLOD_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceZHack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RicePrimaryDepthHack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableObjBG_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableBlender_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceForceScreenClear_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceEmulateClear_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceTxtSizeMethod2_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceIncTexRectEdge_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableCulling_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableTextureCRC_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceTexture1Hack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceUseDefaultHacks_CB); + this.RiceGameSpecificTab.Location = new System.Drawing.Point(4, 22); + this.RiceGameSpecificTab.Name = "RiceGameSpecificTab"; + this.RiceGameSpecificTab.Padding = new System.Windows.Forms.Padding(3); + this.RiceGameSpecificTab.Size = new System.Drawing.Size(564, 310); + this.RiceGameSpecificTab.TabIndex = 1; + this.RiceGameSpecificTab.Text = "Per-Game Hacks"; + this.RiceGameSpecificTab.UseVisualStyleBackColor = true; + // + // label27 + // + this.label27.AutoSize = true; + this.label27.Location = new System.Drawing.Point(310, 178); + this.label27.Name = "label27"; + this.label27.Size = new System.Drawing.Size(119, 13); + this.label27.TabIndex = 52; + this.label27.Text = "Enable hacks for game:"; + // + // RiceEnableHacksForGame_Combo + // + this.RiceEnableHacksForGame_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceEnableHacksForGame_Combo.FormattingEnabled = true; + this.RiceEnableHacksForGame_Combo.Items.AddRange(new object[] { "NO_HACK_FOR_GAME", "HACK_FOR_BANJO_TOOIE", "HACK_FOR_DR_MARIO", @@ -3807,81 +3809,81 @@ "HACK_FOR_DUKE_NUKEM", "HACK_FOR_ZELDA_MM", "HACK_FOR_MARIO_KART"}); - this.RiceEnableHacksForGame_Combo.Location = new System.Drawing.Point(313, 194); - this.RiceEnableHacksForGame_Combo.Name = "RiceEnableHacksForGame_Combo"; - this.RiceEnableHacksForGame_Combo.Size = new System.Drawing.Size(240, 21); - this.RiceEnableHacksForGame_Combo.TabIndex = 51; - // - // RiceForceDepthBuffer_CB - // - this.RiceForceDepthBuffer_CB.AutoSize = true; - this.RiceForceDepthBuffer_CB.Location = new System.Drawing.Point(173, 186); - this.RiceForceDepthBuffer_CB.Name = "RiceForceDepthBuffer_CB"; - this.RiceForceDepthBuffer_CB.Size = new System.Drawing.Size(116, 17); - this.RiceForceDepthBuffer_CB.TabIndex = 50; - this.RiceForceDepthBuffer_CB.Text = "Force Depth Buffer"; - this.RiceForceDepthBuffer_CB.UseVisualStyleBackColor = true; - // - // label28 - // - this.label28.AutoSize = true; - this.label28.Location = new System.Drawing.Point(12, 275); - this.label28.Name = "label28"; - this.label28.Size = new System.Drawing.Size(115, 13); - this.label28.TabIndex = 49; - this.label28.Text = "Screen Update Setting"; - // - // RiceUseCIWidthAndRatio_Combo - // - this.RiceUseCIWidthAndRatio_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceUseCIWidthAndRatio_Combo.FormattingEnabled = true; - this.RiceUseCIWidthAndRatio_Combo.Items.AddRange(new object[] { + this.RiceEnableHacksForGame_Combo.Location = new System.Drawing.Point(313, 194); + this.RiceEnableHacksForGame_Combo.Name = "RiceEnableHacksForGame_Combo"; + this.RiceEnableHacksForGame_Combo.Size = new System.Drawing.Size(240, 21); + this.RiceEnableHacksForGame_Combo.TabIndex = 51; + // + // RiceForceDepthBuffer_CB + // + this.RiceForceDepthBuffer_CB.AutoSize = true; + this.RiceForceDepthBuffer_CB.Location = new System.Drawing.Point(173, 186); + this.RiceForceDepthBuffer_CB.Name = "RiceForceDepthBuffer_CB"; + this.RiceForceDepthBuffer_CB.Size = new System.Drawing.Size(116, 17); + this.RiceForceDepthBuffer_CB.TabIndex = 50; + this.RiceForceDepthBuffer_CB.Text = "Force Depth Buffer"; + this.RiceForceDepthBuffer_CB.UseVisualStyleBackColor = true; + // + // label28 + // + this.label28.AutoSize = true; + this.label28.Location = new System.Drawing.Point(12, 275); + this.label28.Name = "label28"; + this.label28.Size = new System.Drawing.Size(115, 13); + this.label28.TabIndex = 49; + this.label28.Text = "Screen Update Setting"; + // + // RiceUseCIWidthAndRatio_Combo + // + this.RiceUseCIWidthAndRatio_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceUseCIWidthAndRatio_Combo.FormattingEnabled = true; + this.RiceUseCIWidthAndRatio_Combo.Items.AddRange(new object[] { "NOT_USE_CI_WIDTH_AND_RATIO", "USE_CI_WIDTH_AND_RATIO_FOR_NTSC", "USE_CI_WIDTH_AND_RATIO_FOR_PAL"}); - this.RiceUseCIWidthAndRatio_Combo.Location = new System.Drawing.Point(133, 241); - this.RiceUseCIWidthAndRatio_Combo.Name = "RiceUseCIWidthAndRatio_Combo"; - this.RiceUseCIWidthAndRatio_Combo.Size = new System.Drawing.Size(274, 21); - this.RiceUseCIWidthAndRatio_Combo.TabIndex = 48; - // - // label26 - // - this.label26.AutoSize = true; - this.label26.Location = new System.Drawing.Point(310, 138); - this.label26.Name = "label26"; - this.label26.Size = new System.Drawing.Size(121, 13); - this.label26.TabIndex = 47; - this.label26.Text = "Render to texture option"; - // - // RiceRenderToTextureOption_Combo - // - this.RiceRenderToTextureOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceRenderToTextureOption_Combo.FormattingEnabled = true; - this.RiceRenderToTextureOption_Combo.Items.AddRange(new object[] { + this.RiceUseCIWidthAndRatio_Combo.Location = new System.Drawing.Point(133, 241); + this.RiceUseCIWidthAndRatio_Combo.Name = "RiceUseCIWidthAndRatio_Combo"; + this.RiceUseCIWidthAndRatio_Combo.Size = new System.Drawing.Size(274, 21); + this.RiceUseCIWidthAndRatio_Combo.TabIndex = 48; + // + // label26 + // + this.label26.AutoSize = true; + this.label26.Location = new System.Drawing.Point(310, 138); + this.label26.Name = "label26"; + this.label26.Size = new System.Drawing.Size(121, 13); + this.label26.TabIndex = 47; + this.label26.Text = "Render to texture option"; + // + // RiceRenderToTextureOption_Combo + // + this.RiceRenderToTextureOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceRenderToTextureOption_Combo.FormattingEnabled = true; + this.RiceRenderToTextureOption_Combo.Items.AddRange(new object[] { "TXT_BUF_NONE", "TXT_BUF_IGNORE", "TXT_BUF_NORMAL", "TXT_BUF_WRITE_BACK", "TXT_BUF_WRITE_BACK_AND_RELOAD"}); - this.RiceRenderToTextureOption_Combo.Location = new System.Drawing.Point(313, 154); - this.RiceRenderToTextureOption_Combo.Name = "RiceRenderToTextureOption_Combo"; - this.RiceRenderToTextureOption_Combo.Size = new System.Drawing.Size(240, 21); - this.RiceRenderToTextureOption_Combo.TabIndex = 46; - // - // label25 - // - this.label25.AutoSize = true; - this.label25.Location = new System.Drawing.Point(310, 95); - this.label25.Name = "label25"; - this.label25.Size = new System.Drawing.Size(97, 13); - this.label25.TabIndex = 45; - this.label25.Text = "Framebuffer Option"; - // - // RiceFrameBufferOption_Combo - // - this.RiceFrameBufferOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFrameBufferOption_Combo.FormattingEnabled = true; - this.RiceFrameBufferOption_Combo.Items.AddRange(new object[] { + this.RiceRenderToTextureOption_Combo.Location = new System.Drawing.Point(313, 154); + this.RiceRenderToTextureOption_Combo.Name = "RiceRenderToTextureOption_Combo"; + this.RiceRenderToTextureOption_Combo.Size = new System.Drawing.Size(240, 21); + this.RiceRenderToTextureOption_Combo.TabIndex = 46; + // + // label25 + // + this.label25.AutoSize = true; + this.label25.Location = new System.Drawing.Point(310, 95); + this.label25.Name = "label25"; + this.label25.Size = new System.Drawing.Size(97, 13); + this.label25.TabIndex = 45; + this.label25.Text = "Framebuffer Option"; + // + // RiceFrameBufferOption_Combo + // + this.RiceFrameBufferOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFrameBufferOption_Combo.FormattingEnabled = true; + this.RiceFrameBufferOption_Combo.Items.AddRange(new object[] { "FRM_BUF_NONE", "FRM_BUF_IGNORE", "FRM_BUF_BASIC", @@ -3892,121 +3894,121 @@ "FRM_BUF_BASIC_AND_WITH_EMULATOR", "FRM_BUF_WITH_EMULATOR_READ_ONLY", "FRM_BUF_WITH_EMULATOR_WRITE_ONLY"}); - this.RiceFrameBufferOption_Combo.Location = new System.Drawing.Point(313, 114); - this.RiceFrameBufferOption_Combo.Name = "RiceFrameBufferOption_Combo"; - this.RiceFrameBufferOption_Combo.Size = new System.Drawing.Size(239, 21); - this.RiceFrameBufferOption_Combo.TabIndex = 44; - // - // label24 - // - this.label24.AutoSize = true; - this.label24.Location = new System.Drawing.Point(430, 34); - this.label24.Name = "label24"; - this.label24.Size = new System.Drawing.Size(79, 13); - this.label24.TabIndex = 43; - this.label24.Text = "Normal Blender"; - this.label24.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // RiceNormalBlender_Combo - // - this.RiceNormalBlender_Combo.DisplayMember = "0,1,2,3"; - this.RiceNormalBlender_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceNormalBlender_Combo.FormattingEnabled = true; - this.RiceNormalBlender_Combo.Items.AddRange(new object[] { + this.RiceFrameBufferOption_Combo.Location = new System.Drawing.Point(313, 114); + this.RiceFrameBufferOption_Combo.Name = "RiceFrameBufferOption_Combo"; + this.RiceFrameBufferOption_Combo.Size = new System.Drawing.Size(239, 21); + this.RiceFrameBufferOption_Combo.TabIndex = 44; + // + // label24 + // + this.label24.AutoSize = true; + this.label24.Location = new System.Drawing.Point(430, 34); + this.label24.Name = "label24"; + this.label24.Size = new System.Drawing.Size(79, 13); + this.label24.TabIndex = 43; + this.label24.Text = "Normal Blender"; + this.label24.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // RiceNormalBlender_Combo + // + this.RiceNormalBlender_Combo.DisplayMember = "0,1,2,3"; + this.RiceNormalBlender_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceNormalBlender_Combo.FormattingEnabled = true; + this.RiceNormalBlender_Combo.Items.AddRange(new object[] { "0", "1", "2"}); - this.RiceNormalBlender_Combo.Location = new System.Drawing.Point(515, 31); - this.RiceNormalBlender_Combo.Name = "RiceNormalBlender_Combo"; - this.RiceNormalBlender_Combo.Size = new System.Drawing.Size(37, 21); - this.RiceNormalBlender_Combo.TabIndex = 42; - // - // label23 - // - this.label23.AutoSize = true; - this.label23.Location = new System.Drawing.Point(376, 7); - this.label23.Name = "label23"; - this.label23.Size = new System.Drawing.Size(133, 13); - this.label23.TabIndex = 41; - this.label23.Text = "Accurate Texture Mapping"; - this.label23.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label23, "\\"); - // - // RiceAccurateTextureMappingHack_Combo - // - this.RiceAccurateTextureMappingHack_Combo.DisplayMember = "0,1,2,3"; - this.RiceAccurateTextureMappingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceAccurateTextureMappingHack_Combo.FormattingEnabled = true; - this.RiceAccurateTextureMappingHack_Combo.Items.AddRange(new object[] { + this.RiceNormalBlender_Combo.Location = new System.Drawing.Point(515, 31); + this.RiceNormalBlender_Combo.Name = "RiceNormalBlender_Combo"; + this.RiceNormalBlender_Combo.Size = new System.Drawing.Size(37, 21); + this.RiceNormalBlender_Combo.TabIndex = 42; + // + // label23 + // + this.label23.AutoSize = true; + this.label23.Location = new System.Drawing.Point(376, 7); + this.label23.Name = "label23"; + this.label23.Size = new System.Drawing.Size(133, 13); + this.label23.TabIndex = 41; + this.label23.Text = "Accurate Texture Mapping"; + this.label23.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label23, "\\"); + // + // RiceAccurateTextureMappingHack_Combo + // + this.RiceAccurateTextureMappingHack_Combo.DisplayMember = "0,1,2,3"; + this.RiceAccurateTextureMappingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceAccurateTextureMappingHack_Combo.FormattingEnabled = true; + this.RiceAccurateTextureMappingHack_Combo.Items.AddRange(new object[] { "0", "1"}); - this.RiceAccurateTextureMappingHack_Combo.Location = new System.Drawing.Point(515, 4); - this.RiceAccurateTextureMappingHack_Combo.Name = "RiceAccurateTextureMappingHack_Combo"; - this.RiceAccurateTextureMappingHack_Combo.Size = new System.Drawing.Size(37, 21); - this.RiceAccurateTextureMappingHack_Combo.TabIndex = 40; - // - // label22 - // - this.label22.AutoSize = true; - this.label22.Location = new System.Drawing.Point(418, 61); - this.label22.Name = "label22"; - this.label22.Size = new System.Drawing.Size(91, 13); - this.label22.TabIndex = 39; - this.label22.Text = "Fast Texture CRC"; - this.label22.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // RiceFastTextureCRC_Combo - // - this.RiceFastTextureCRC_Combo.DisplayMember = "0,1,2,3"; - this.RiceFastTextureCRC_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFastTextureCRC_Combo.FormattingEnabled = true; - this.RiceFastTextureCRC_Combo.Items.AddRange(new object[] { + this.RiceAccurateTextureMappingHack_Combo.Location = new System.Drawing.Point(515, 4); + this.RiceAccurateTextureMappingHack_Combo.Name = "RiceAccurateTextureMappingHack_Combo"; + this.RiceAccurateTextureMappingHack_Combo.Size = new System.Drawing.Size(37, 21); + this.RiceAccurateTextureMappingHack_Combo.TabIndex = 40; + // + // label22 + // + this.label22.AutoSize = true; + this.label22.Location = new System.Drawing.Point(418, 61); + this.label22.Name = "label22"; + this.label22.Size = new System.Drawing.Size(91, 13); + this.label22.TabIndex = 39; + this.label22.Text = "Fast Texture CRC"; + this.label22.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // RiceFastTextureCRC_Combo + // + this.RiceFastTextureCRC_Combo.DisplayMember = "0,1,2,3"; + this.RiceFastTextureCRC_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFastTextureCRC_Combo.FormattingEnabled = true; + this.RiceFastTextureCRC_Combo.Items.AddRange(new object[] { "0", "1", "2"}); - this.RiceFastTextureCRC_Combo.Location = new System.Drawing.Point(515, 58); - this.RiceFastTextureCRC_Combo.Name = "RiceFastTextureCRC_Combo"; - this.RiceFastTextureCRC_Combo.Size = new System.Drawing.Size(37, 21); - this.RiceFastTextureCRC_Combo.TabIndex = 38; - // - // label21 - // - this.label21.AutoSize = true; - this.label21.Location = new System.Drawing.Point(454, 88); - this.label21.Name = "label21"; - this.label21.Size = new System.Drawing.Size(55, 13); - this.label21.TabIndex = 37; - this.label21.Text = "FullTMEM"; - this.label21.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // RiceFullTMEM_Combo - // - this.RiceFullTMEM_Combo.DisplayMember = "0,1,2,3"; - this.RiceFullTMEM_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFullTMEM_Combo.FormattingEnabled = true; - this.RiceFullTMEM_Combo.Items.AddRange(new object[] { + this.RiceFastTextureCRC_Combo.Location = new System.Drawing.Point(515, 58); + this.RiceFastTextureCRC_Combo.Name = "RiceFastTextureCRC_Combo"; + this.RiceFastTextureCRC_Combo.Size = new System.Drawing.Size(37, 21); + this.RiceFastTextureCRC_Combo.TabIndex = 38; + // + // label21 + // + this.label21.AutoSize = true; + this.label21.Location = new System.Drawing.Point(454, 88); + this.label21.Name = "label21"; + this.label21.Size = new System.Drawing.Size(55, 13); + this.label21.TabIndex = 37; + this.label21.Text = "FullTMEM"; + this.label21.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // RiceFullTMEM_Combo + // + this.RiceFullTMEM_Combo.DisplayMember = "0,1,2,3"; + this.RiceFullTMEM_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFullTMEM_Combo.FormattingEnabled = true; + this.RiceFullTMEM_Combo.Items.AddRange(new object[] { "0", "1", "2"}); - this.RiceFullTMEM_Combo.Location = new System.Drawing.Point(515, 85); - this.RiceFullTMEM_Combo.Name = "RiceFullTMEM_Combo"; - this.RiceFullTMEM_Combo.Size = new System.Drawing.Size(36, 21); - this.RiceFullTMEM_Combo.TabIndex = 36; - // - // label20 - // - this.label20.AutoSize = true; - this.label20.Location = new System.Drawing.Point(8, 244); - this.label20.Name = "label20"; - this.label20.Size = new System.Drawing.Size(119, 13); - this.label20.TabIndex = 35; - this.label20.Text = "Use CI Width and Ratio"; - // - // RiceScreenUpdateSettingHack_Combo - // - this.RiceScreenUpdateSettingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceScreenUpdateSettingHack_Combo.FormattingEnabled = true; - this.RiceScreenUpdateSettingHack_Combo.Items.AddRange(new object[] { + this.RiceFullTMEM_Combo.Location = new System.Drawing.Point(515, 85); + this.RiceFullTMEM_Combo.Name = "RiceFullTMEM_Combo"; + this.RiceFullTMEM_Combo.Size = new System.Drawing.Size(36, 21); + this.RiceFullTMEM_Combo.TabIndex = 36; + // + // label20 + // + this.label20.AutoSize = true; + this.label20.Location = new System.Drawing.Point(8, 244); + this.label20.Name = "label20"; + this.label20.Size = new System.Drawing.Size(119, 13); + this.label20.TabIndex = 35; + this.label20.Text = "Use CI Width and Ratio"; + // + // RiceScreenUpdateSettingHack_Combo + // + this.RiceScreenUpdateSettingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceScreenUpdateSettingHack_Combo.FormattingEnabled = true; + this.RiceScreenUpdateSettingHack_Combo.Items.AddRange(new object[] { "SCREEN_UPDATE_DEFAULT", "SCREEN_UPDATE_AT_VI_UPDATE", "SCREEN_UPDATE_AT_VI_CHANGE", @@ -4015,428 +4017,428 @@ "SCREEN_UPDATE_AT_1ST_PRIMITIVE", "SCREEN_UPDATE_BEFORE_SCREEN_CLEAR", "SCREEN_UPDATE_AT_VI_UPDATE_AND_DRAWN"}); - this.RiceScreenUpdateSettingHack_Combo.Location = new System.Drawing.Point(133, 272); - this.RiceScreenUpdateSettingHack_Combo.Name = "RiceScreenUpdateSettingHack_Combo"; - this.RiceScreenUpdateSettingHack_Combo.Size = new System.Drawing.Size(274, 21); - this.RiceScreenUpdateSettingHack_Combo.TabIndex = 34; - // - // label19 - // - this.label19.AutoSize = true; - this.label19.Location = new System.Drawing.Point(306, 62); - this.label19.Name = "label19"; - this.label19.Size = new System.Drawing.Size(51, 13); - this.label19.TabIndex = 20; - this.label19.Text = "VI Height"; - // - // RiceVIHeight_Text - // - this.RiceVIHeight_Text.Location = new System.Drawing.Point(363, 59); - this.RiceVIHeight_Text.Name = "RiceVIHeight_Text"; - this.RiceVIHeight_Text.Size = new System.Drawing.Size(36, 20); - this.RiceVIHeight_Text.TabIndex = 19; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(309, 34); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(48, 13); - this.label4.TabIndex = 18; - this.label4.Text = "VI Width"; - // - // RiceVIWidth_Text - // - this.RiceVIWidth_Text.Location = new System.Drawing.Point(363, 31); - this.RiceVIWidth_Text.Name = "RiceVIWidth_Text"; - this.RiceVIWidth_Text.Size = new System.Drawing.Size(36, 20); - this.RiceVIWidth_Text.TabIndex = 17; - // - // RiceTextureScaleHack_CB - // - this.RiceTextureScaleHack_CB.AutoSize = true; - this.RiceTextureScaleHack_CB.Location = new System.Drawing.Point(6, 140); - this.RiceTextureScaleHack_CB.Name = "RiceTextureScaleHack_CB"; - this.RiceTextureScaleHack_CB.Size = new System.Drawing.Size(121, 17); - this.RiceTextureScaleHack_CB.TabIndex = 16; - this.RiceTextureScaleHack_CB.Text = "Texture Scale Hack"; - this.RiceTextureScaleHack_CB.UseVisualStyleBackColor = true; - // - // RiceFastLoadTile_CB - // - this.RiceFastLoadTile_CB.AutoSize = true; - this.RiceFastLoadTile_CB.Location = new System.Drawing.Point(6, 209); - this.RiceFastLoadTile_CB.Name = "RiceFastLoadTile_CB"; - this.RiceFastLoadTile_CB.Size = new System.Drawing.Size(93, 17); - this.RiceFastLoadTile_CB.TabIndex = 15; - this.RiceFastLoadTile_CB.Text = "Fast Load Tile"; - this.RiceFastLoadTile_CB.UseVisualStyleBackColor = true; - // - // RiceUseSmallerTexture_CB - // - this.RiceUseSmallerTexture_CB.AutoSize = true; - this.RiceUseSmallerTexture_CB.ForeColor = System.Drawing.SystemColors.ControlText; - this.RiceUseSmallerTexture_CB.Location = new System.Drawing.Point(173, 209); - this.RiceUseSmallerTexture_CB.Name = "RiceUseSmallerTexture_CB"; - this.RiceUseSmallerTexture_CB.Size = new System.Drawing.Size(121, 17); - this.RiceUseSmallerTexture_CB.TabIndex = 14; - this.RiceUseSmallerTexture_CB.Text = "Use Smaller Texture"; - this.RiceUseSmallerTexture_CB.UseVisualStyleBackColor = true; - // - // RiceEnableTxtLOD_CB - // - this.RiceEnableTxtLOD_CB.AutoSize = true; - this.RiceEnableTxtLOD_CB.Location = new System.Drawing.Point(173, 71); - this.RiceEnableTxtLOD_CB.Name = "RiceEnableTxtLOD_CB"; - this.RiceEnableTxtLOD_CB.Size = new System.Drawing.Size(102, 17); - this.RiceEnableTxtLOD_CB.TabIndex = 13; - this.RiceEnableTxtLOD_CB.Text = "Enable Txt LOD"; - this.RiceEnableTxtLOD_CB.UseVisualStyleBackColor = true; - // - // RiceZHack_CB - // - this.RiceZHack_CB.AutoSize = true; - this.RiceZHack_CB.Location = new System.Drawing.Point(6, 117); - this.RiceZHack_CB.Name = "RiceZHack_CB"; - this.RiceZHack_CB.Size = new System.Drawing.Size(62, 17); - this.RiceZHack_CB.TabIndex = 12; - this.RiceZHack_CB.Text = "Z Hack"; - this.RiceZHack_CB.UseVisualStyleBackColor = true; - // - // RicePrimaryDepthHack_CB - // - this.RicePrimaryDepthHack_CB.AutoSize = true; - this.RicePrimaryDepthHack_CB.Location = new System.Drawing.Point(6, 163); - this.RicePrimaryDepthHack_CB.Name = "RicePrimaryDepthHack_CB"; - this.RicePrimaryDepthHack_CB.Size = new System.Drawing.Size(121, 17); - this.RicePrimaryDepthHack_CB.TabIndex = 11; - this.RicePrimaryDepthHack_CB.Text = "Primary Depth Hack"; - this.RicePrimaryDepthHack_CB.UseVisualStyleBackColor = true; - // - // RiceDisableObjBG_CB - // - this.RiceDisableObjBG_CB.AutoSize = true; - this.RiceDisableObjBG_CB.Location = new System.Drawing.Point(173, 163); - this.RiceDisableObjBG_CB.Name = "RiceDisableObjBG_CB"; - this.RiceDisableObjBG_CB.Size = new System.Drawing.Size(98, 17); - this.RiceDisableObjBG_CB.TabIndex = 10; - this.RiceDisableObjBG_CB.Text = "Disable Obj BG"; - this.RiceDisableObjBG_CB.UseVisualStyleBackColor = true; - // - // RiceDisableBlender_CB - // - this.RiceDisableBlender_CB.AutoSize = true; - this.RiceDisableBlender_CB.Location = new System.Drawing.Point(173, 140); - this.RiceDisableBlender_CB.Name = "RiceDisableBlender_CB"; - this.RiceDisableBlender_CB.Size = new System.Drawing.Size(100, 17); - this.RiceDisableBlender_CB.TabIndex = 9; - this.RiceDisableBlender_CB.Text = "Disable Blender"; - this.RiceDisableBlender_CB.UseVisualStyleBackColor = true; - // - // RiceForceScreenClear_CB - // - this.RiceForceScreenClear_CB.AutoSize = true; - this.RiceForceScreenClear_CB.Location = new System.Drawing.Point(173, 117); - this.RiceForceScreenClear_CB.Name = "RiceForceScreenClear_CB"; - this.RiceForceScreenClear_CB.Size = new System.Drawing.Size(117, 17); - this.RiceForceScreenClear_CB.TabIndex = 8; - this.RiceForceScreenClear_CB.Text = "Force Screen Clear"; - this.RiceForceScreenClear_CB.UseVisualStyleBackColor = true; - // - // RiceEmulateClear_CB - // - this.RiceEmulateClear_CB.AutoSize = true; - this.RiceEmulateClear_CB.Location = new System.Drawing.Point(173, 94); - this.RiceEmulateClear_CB.Name = "RiceEmulateClear_CB"; - this.RiceEmulateClear_CB.Size = new System.Drawing.Size(91, 17); - this.RiceEmulateClear_CB.TabIndex = 7; - this.RiceEmulateClear_CB.Text = "Emulate Clear"; - this.RiceEmulateClear_CB.UseVisualStyleBackColor = true; - // - // RiceTxtSizeMethod2_CB - // - this.RiceTxtSizeMethod2_CB.AutoSize = true; - this.RiceTxtSizeMethod2_CB.Location = new System.Drawing.Point(173, 48); - this.RiceTxtSizeMethod2_CB.Name = "RiceTxtSizeMethod2_CB"; - this.RiceTxtSizeMethod2_CB.Size = new System.Drawing.Size(112, 17); - this.RiceTxtSizeMethod2_CB.TabIndex = 6; - this.RiceTxtSizeMethod2_CB.Text = "Txt Size Method 2"; - this.RiceTxtSizeMethod2_CB.UseVisualStyleBackColor = true; - // - // RiceIncTexRectEdge_CB - // - this.RiceIncTexRectEdge_CB.AutoSize = true; - this.RiceIncTexRectEdge_CB.Location = new System.Drawing.Point(6, 94); - this.RiceIncTexRectEdge_CB.Name = "RiceIncTexRectEdge_CB"; - this.RiceIncTexRectEdge_CB.Size = new System.Drawing.Size(116, 17); - this.RiceIncTexRectEdge_CB.TabIndex = 5; - this.RiceIncTexRectEdge_CB.Text = "Inc Tex Rect Edge"; - this.RiceIncTexRectEdge_CB.UseVisualStyleBackColor = true; - // - // RiceDisableCulling_CB - // - this.RiceDisableCulling_CB.AutoSize = true; - this.RiceDisableCulling_CB.Location = new System.Drawing.Point(6, 71); - this.RiceDisableCulling_CB.Name = "RiceDisableCulling_CB"; - this.RiceDisableCulling_CB.Size = new System.Drawing.Size(95, 17); - this.RiceDisableCulling_CB.TabIndex = 4; - this.RiceDisableCulling_CB.Text = "Disable Culling"; - this.RiceDisableCulling_CB.UseVisualStyleBackColor = true; - // - // RiceDisableTextureCRC_CB - // - this.RiceDisableTextureCRC_CB.AutoSize = true; - this.RiceDisableTextureCRC_CB.Location = new System.Drawing.Point(6, 48); - this.RiceDisableTextureCRC_CB.Name = "RiceDisableTextureCRC_CB"; - this.RiceDisableTextureCRC_CB.Size = new System.Drawing.Size(125, 17); - this.RiceDisableTextureCRC_CB.TabIndex = 3; - this.RiceDisableTextureCRC_CB.Text = "Disable Texture CRC"; - this.RiceDisableTextureCRC_CB.UseVisualStyleBackColor = true; - // - // RiceTexture1Hack_CB - // - this.RiceTexture1Hack_CB.AutoSize = true; - this.RiceTexture1Hack_CB.Location = new System.Drawing.Point(6, 186); - this.RiceTexture1Hack_CB.Name = "RiceTexture1Hack_CB"; - this.RiceTexture1Hack_CB.Size = new System.Drawing.Size(97, 17); - this.RiceTexture1Hack_CB.TabIndex = 2; - this.RiceTexture1Hack_CB.Text = "Texture1 Hack"; - this.RiceTexture1Hack_CB.UseVisualStyleBackColor = true; - // - // RiceUseDefaultHacks_CB - // - this.RiceUseDefaultHacks_CB.AutoSize = true; - this.RiceUseDefaultHacks_CB.Location = new System.Drawing.Point(6, 6); - this.RiceUseDefaultHacks_CB.Name = "RiceUseDefaultHacks_CB"; - this.RiceUseDefaultHacks_CB.Size = new System.Drawing.Size(165, 17); - this.RiceUseDefaultHacks_CB.TabIndex = 1; - this.RiceUseDefaultHacks_CB.Text = "Use defaults for current game"; - this.RiceUseDefaultHacks_CB.UseVisualStyleBackColor = true; - this.RiceUseDefaultHacks_CB.CheckedChanged += new System.EventHandler(this.RiceUseDefaultHacks_Cb_CheckedChanged); - // - // label71 - // - this.label71.AutoSize = true; - this.label71.Location = new System.Drawing.Point(15, 94); - this.label71.Name = "label71"; - this.label71.Size = new System.Drawing.Size(91, 13); - this.label71.TabIndex = 32; - this.label71.Text = "Resolution Height"; - // - // label70 - // - this.label70.AutoSize = true; - this.label70.Location = new System.Drawing.Point(15, 64); - this.label70.Name = "label70"; - this.label70.Size = new System.Drawing.Size(88, 13); - this.label70.TabIndex = 31; - this.label70.Text = "Resolution Width"; - // - // label69 - // - this.label69.AutoSize = true; - this.label69.Location = new System.Drawing.Point(15, 34); - this.label69.Name = "label69"; - this.label69.Size = new System.Drawing.Size(106, 13); - this.label69.TabIndex = 0; - this.label69.Text = "Direct3D Clear Mode"; - // - // label68 - // - this.label68.AutoSize = true; - this.label68.Location = new System.Drawing.Point(51, 316); - this.label68.Name = "label68"; - this.label68.Size = new System.Drawing.Size(87, 13); - this.label68.TabIndex = 27; - this.label68.Text = "Copy framebuffer"; - // - // label67 - // - this.label67.AutoSize = true; - this.label67.Location = new System.Drawing.Point(49, 286); - this.label67.Name = "label67"; - this.label67.Size = new System.Drawing.Size(89, 13); - this.label67.TabIndex = 25; - this.label67.Text = "Force Z Compare"; - // - // label66 - // - this.label66.AutoSize = true; - this.label66.Location = new System.Drawing.Point(24, 256); - this.label66.Name = "label66"; - this.label66.Size = new System.Drawing.Size(114, 13); - this.label66.TabIndex = 23; - this.label66.Text = "Direct3D trans pipeline"; - // - // label65 - // - this.label65.AutoSize = true; - this.label65.Location = new System.Drawing.Point(32, 226); - this.label65.Name = "label65"; - this.label65.Size = new System.Drawing.Size(106, 13); - this.label65.TabIndex = 21; - this.label65.Text = "Force alpha blending"; - // - // label64 - // - this.label64.AutoSize = true; - this.label64.Location = new System.Drawing.Point(15, 195); - this.label64.Name = "label64"; - this.label64.Size = new System.Drawing.Size(123, 13); - this.label64.TabIndex = 19; - this.label64.Text = "Use legacy pixel pipeline"; - // - // label63 - // - this.label63.AutoSize = true; - this.label63.Location = new System.Drawing.Point(15, 177); - this.label63.Name = "label63"; - this.label63.Size = new System.Drawing.Size(76, 13); - this.label63.TabIndex = 17; - this.label63.Text = "to match yours"; - // - // label62 - // - this.label62.AutoSize = true; - this.label62.Location = new System.Drawing.Point(15, 164); - this.label62.Name = "label62"; - this.label62.Size = new System.Drawing.Size(123, 13); - this.label62.TabIndex = 16; - this.label62.Text = "Adjust game aspect ratio"; - // - // label61 - // - this.label61.AutoSize = true; - this.label61.Location = new System.Drawing.Point(49, 49); - this.label61.Name = "label61"; - this.label61.Size = new System.Drawing.Size(89, 13); - this.label61.TabIndex = 14; - this.label61.Text = "Antialiasing Level"; - // - // label60 - // - this.label60.AutoSize = true; - this.label60.Location = new System.Drawing.Point(21, 136); - this.label60.Name = "label60"; - this.label60.Size = new System.Drawing.Size(117, 13); - this.label60.TabIndex = 12; - this.label60.Text = "Always use texture filter"; - // - // label59 - // - this.label59.AutoSize = true; - this.label59.Location = new System.Drawing.Point(76, 107); - this.label59.Name = "label59"; - this.label59.Size = new System.Drawing.Size(62, 13); - this.label59.TabIndex = 10; - this.label59.Text = "Super 2xsal"; - // - // label57 - // - this.label57.AutoSize = true; - this.label57.Location = new System.Drawing.Point(82, 79); - this.label57.Name = "label57"; - this.label57.Size = new System.Drawing.Size(56, 13); - this.label57.TabIndex = 7; - this.label57.Text = "Brightness"; - // - // label51 - // - this.label51.AutoSize = true; - this.label51.Location = new System.Drawing.Point(18, 15); - this.label51.Name = "label51"; - this.label51.Size = new System.Drawing.Size(120, 13); - this.label51.TabIndex = 5; - this.label51.Text = "Anisotropic filtering level"; - // - // SaveButton - // - this.SaveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.SaveButton.Location = new System.Drawing.Point(409, 373); - this.SaveButton.Name = "SaveButton"; - this.SaveButton.Size = new System.Drawing.Size(75, 23); - this.SaveButton.TabIndex = 100; - this.SaveButton.Text = "Save"; - this.SaveButton.UseVisualStyleBackColor = true; - this.SaveButton.Click += new System.EventHandler(this.Button1_Click); - // - // CancelBT - // - this.CancelBT.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelBT.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBT.Location = new System.Drawing.Point(490, 373); - this.CancelBT.Name = "CancelBT"; - this.CancelBT.Size = new System.Drawing.Size(75, 23); - this.CancelBT.TabIndex = 105; - this.CancelBT.Text = "Cancel"; - this.CancelBT.UseVisualStyleBackColor = true; - this.CancelBT.Click += new System.EventHandler(this.CancelBtn_Click); - // - // toolTip1 - // - this.toolTip1.AutoPopDelay = 10000; - this.toolTip1.InitialDelay = 500; - this.toolTip1.IsBalloon = true; - this.toolTip1.ReshowDelay = 100; - this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; - // - // N64VideoPluginconfig - // - this.AcceptButton = this.SaveButton; - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.CancelButton = this.CancelBT; - this.ClientSize = new System.Drawing.Size(577, 401); - this.Controls.Add(this.CancelBT); - this.Controls.Add(this.SaveButton); - this.Controls.Add(this.N64plugintabcontrol); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Name = "N64VideoPluginconfig"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "Plugin Settings"; - this.Load += new System.EventHandler(this.N64VideoPluginconfig_Load); - this.N64plugintabcontrol.ResumeLayout(false); - this.N64vpluginglobaltab.ResumeLayout(false); - this.N64vpluginglobaltab.PerformLayout(); - this.GLideN64Tab.ResumeLayout(false); - this.tabControl3.ResumeLayout(false); - this.tabPage5.ResumeLayout(false); - this.tabPage5.PerformLayout(); - this.tabPage6.ResumeLayout(false); - this.tabPage6.PerformLayout(); - this.tabPage7.ResumeLayout(false); - this.tabPage7.PerformLayout(); - this.Glide64mk2TabPage.ResumeLayout(false); - this.tabControl2.ResumeLayout(false); - this.tabPage1.ResumeLayout(false); - this.tabPage1.PerformLayout(); - this.tabPage2.ResumeLayout(false); - this.tabPage2.PerformLayout(); - this.tabPage3.ResumeLayout(false); - this.tabPage3.PerformLayout(); - this.Glide64TabPage.ResumeLayout(false); - this.tabControl1.ResumeLayout(false); - this.Glide64General.ResumeLayout(false); - this.Glide64General.PerformLayout(); - this.GlidePerGameHacks1.ResumeLayout(false); - this.GlidePerGameHacks1.PerformLayout(); - this.GlidePerGameHacks2.ResumeLayout(false); - this.GlidePerGameHacks2.PerformLayout(); - this.RiceTabPage.ResumeLayout(false); - this.RiceTabControl.ResumeLayout(false); - this.RiceGeneral.ResumeLayout(false); - this.RiceGeneral.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).EndInit(); - this.RiceGameDefaultTab.ResumeLayout(false); - this.RiceGameDefaultTab.PerformLayout(); - this.RiceFrameBuffer_GroupBox.ResumeLayout(false); - this.RiceFrameBuffer_GroupBox.PerformLayout(); - this.RiceTextureEnhancementTab.ResumeLayout(false); - this.RiceTextureEnhancementTab.PerformLayout(); - this.RiceGameSpecificTab.ResumeLayout(false); - this.RiceGameSpecificTab.PerformLayout(); - this.ResumeLayout(false); + this.RiceScreenUpdateSettingHack_Combo.Location = new System.Drawing.Point(133, 272); + this.RiceScreenUpdateSettingHack_Combo.Name = "RiceScreenUpdateSettingHack_Combo"; + this.RiceScreenUpdateSettingHack_Combo.Size = new System.Drawing.Size(274, 21); + this.RiceScreenUpdateSettingHack_Combo.TabIndex = 34; + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Location = new System.Drawing.Point(306, 62); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(51, 13); + this.label19.TabIndex = 20; + this.label19.Text = "VI Height"; + // + // RiceVIHeight_Text + // + this.RiceVIHeight_Text.Location = new System.Drawing.Point(363, 59); + this.RiceVIHeight_Text.Name = "RiceVIHeight_Text"; + this.RiceVIHeight_Text.Size = new System.Drawing.Size(36, 20); + this.RiceVIHeight_Text.TabIndex = 19; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(309, 34); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(48, 13); + this.label4.TabIndex = 18; + this.label4.Text = "VI Width"; + // + // RiceVIWidth_Text + // + this.RiceVIWidth_Text.Location = new System.Drawing.Point(363, 31); + this.RiceVIWidth_Text.Name = "RiceVIWidth_Text"; + this.RiceVIWidth_Text.Size = new System.Drawing.Size(36, 20); + this.RiceVIWidth_Text.TabIndex = 17; + // + // RiceTextureScaleHack_CB + // + this.RiceTextureScaleHack_CB.AutoSize = true; + this.RiceTextureScaleHack_CB.Location = new System.Drawing.Point(6, 140); + this.RiceTextureScaleHack_CB.Name = "RiceTextureScaleHack_CB"; + this.RiceTextureScaleHack_CB.Size = new System.Drawing.Size(121, 17); + this.RiceTextureScaleHack_CB.TabIndex = 16; + this.RiceTextureScaleHack_CB.Text = "Texture Scale Hack"; + this.RiceTextureScaleHack_CB.UseVisualStyleBackColor = true; + // + // RiceFastLoadTile_CB + // + this.RiceFastLoadTile_CB.AutoSize = true; + this.RiceFastLoadTile_CB.Location = new System.Drawing.Point(6, 209); + this.RiceFastLoadTile_CB.Name = "RiceFastLoadTile_CB"; + this.RiceFastLoadTile_CB.Size = new System.Drawing.Size(93, 17); + this.RiceFastLoadTile_CB.TabIndex = 15; + this.RiceFastLoadTile_CB.Text = "Fast Load Tile"; + this.RiceFastLoadTile_CB.UseVisualStyleBackColor = true; + // + // RiceUseSmallerTexture_CB + // + this.RiceUseSmallerTexture_CB.AutoSize = true; + this.RiceUseSmallerTexture_CB.ForeColor = System.Drawing.SystemColors.ControlText; + this.RiceUseSmallerTexture_CB.Location = new System.Drawing.Point(173, 209); + this.RiceUseSmallerTexture_CB.Name = "RiceUseSmallerTexture_CB"; + this.RiceUseSmallerTexture_CB.Size = new System.Drawing.Size(121, 17); + this.RiceUseSmallerTexture_CB.TabIndex = 14; + this.RiceUseSmallerTexture_CB.Text = "Use Smaller Texture"; + this.RiceUseSmallerTexture_CB.UseVisualStyleBackColor = true; + // + // RiceEnableTxtLOD_CB + // + this.RiceEnableTxtLOD_CB.AutoSize = true; + this.RiceEnableTxtLOD_CB.Location = new System.Drawing.Point(173, 71); + this.RiceEnableTxtLOD_CB.Name = "RiceEnableTxtLOD_CB"; + this.RiceEnableTxtLOD_CB.Size = new System.Drawing.Size(102, 17); + this.RiceEnableTxtLOD_CB.TabIndex = 13; + this.RiceEnableTxtLOD_CB.Text = "Enable Txt LOD"; + this.RiceEnableTxtLOD_CB.UseVisualStyleBackColor = true; + // + // RiceZHack_CB + // + this.RiceZHack_CB.AutoSize = true; + this.RiceZHack_CB.Location = new System.Drawing.Point(6, 117); + this.RiceZHack_CB.Name = "RiceZHack_CB"; + this.RiceZHack_CB.Size = new System.Drawing.Size(62, 17); + this.RiceZHack_CB.TabIndex = 12; + this.RiceZHack_CB.Text = "Z Hack"; + this.RiceZHack_CB.UseVisualStyleBackColor = true; + // + // RicePrimaryDepthHack_CB + // + this.RicePrimaryDepthHack_CB.AutoSize = true; + this.RicePrimaryDepthHack_CB.Location = new System.Drawing.Point(6, 163); + this.RicePrimaryDepthHack_CB.Name = "RicePrimaryDepthHack_CB"; + this.RicePrimaryDepthHack_CB.Size = new System.Drawing.Size(121, 17); + this.RicePrimaryDepthHack_CB.TabIndex = 11; + this.RicePrimaryDepthHack_CB.Text = "Primary Depth Hack"; + this.RicePrimaryDepthHack_CB.UseVisualStyleBackColor = true; + // + // RiceDisableObjBG_CB + // + this.RiceDisableObjBG_CB.AutoSize = true; + this.RiceDisableObjBG_CB.Location = new System.Drawing.Point(173, 163); + this.RiceDisableObjBG_CB.Name = "RiceDisableObjBG_CB"; + this.RiceDisableObjBG_CB.Size = new System.Drawing.Size(98, 17); + this.RiceDisableObjBG_CB.TabIndex = 10; + this.RiceDisableObjBG_CB.Text = "Disable Obj BG"; + this.RiceDisableObjBG_CB.UseVisualStyleBackColor = true; + // + // RiceDisableBlender_CB + // + this.RiceDisableBlender_CB.AutoSize = true; + this.RiceDisableBlender_CB.Location = new System.Drawing.Point(173, 140); + this.RiceDisableBlender_CB.Name = "RiceDisableBlender_CB"; + this.RiceDisableBlender_CB.Size = new System.Drawing.Size(100, 17); + this.RiceDisableBlender_CB.TabIndex = 9; + this.RiceDisableBlender_CB.Text = "Disable Blender"; + this.RiceDisableBlender_CB.UseVisualStyleBackColor = true; + // + // RiceForceScreenClear_CB + // + this.RiceForceScreenClear_CB.AutoSize = true; + this.RiceForceScreenClear_CB.Location = new System.Drawing.Point(173, 117); + this.RiceForceScreenClear_CB.Name = "RiceForceScreenClear_CB"; + this.RiceForceScreenClear_CB.Size = new System.Drawing.Size(117, 17); + this.RiceForceScreenClear_CB.TabIndex = 8; + this.RiceForceScreenClear_CB.Text = "Force Screen Clear"; + this.RiceForceScreenClear_CB.UseVisualStyleBackColor = true; + // + // RiceEmulateClear_CB + // + this.RiceEmulateClear_CB.AutoSize = true; + this.RiceEmulateClear_CB.Location = new System.Drawing.Point(173, 94); + this.RiceEmulateClear_CB.Name = "RiceEmulateClear_CB"; + this.RiceEmulateClear_CB.Size = new System.Drawing.Size(91, 17); + this.RiceEmulateClear_CB.TabIndex = 7; + this.RiceEmulateClear_CB.Text = "Emulate Clear"; + this.RiceEmulateClear_CB.UseVisualStyleBackColor = true; + // + // RiceTxtSizeMethod2_CB + // + this.RiceTxtSizeMethod2_CB.AutoSize = true; + this.RiceTxtSizeMethod2_CB.Location = new System.Drawing.Point(173, 48); + this.RiceTxtSizeMethod2_CB.Name = "RiceTxtSizeMethod2_CB"; + this.RiceTxtSizeMethod2_CB.Size = new System.Drawing.Size(112, 17); + this.RiceTxtSizeMethod2_CB.TabIndex = 6; + this.RiceTxtSizeMethod2_CB.Text = "Txt Size Method 2"; + this.RiceTxtSizeMethod2_CB.UseVisualStyleBackColor = true; + // + // RiceIncTexRectEdge_CB + // + this.RiceIncTexRectEdge_CB.AutoSize = true; + this.RiceIncTexRectEdge_CB.Location = new System.Drawing.Point(6, 94); + this.RiceIncTexRectEdge_CB.Name = "RiceIncTexRectEdge_CB"; + this.RiceIncTexRectEdge_CB.Size = new System.Drawing.Size(116, 17); + this.RiceIncTexRectEdge_CB.TabIndex = 5; + this.RiceIncTexRectEdge_CB.Text = "Inc Tex Rect Edge"; + this.RiceIncTexRectEdge_CB.UseVisualStyleBackColor = true; + // + // RiceDisableCulling_CB + // + this.RiceDisableCulling_CB.AutoSize = true; + this.RiceDisableCulling_CB.Location = new System.Drawing.Point(6, 71); + this.RiceDisableCulling_CB.Name = "RiceDisableCulling_CB"; + this.RiceDisableCulling_CB.Size = new System.Drawing.Size(95, 17); + this.RiceDisableCulling_CB.TabIndex = 4; + this.RiceDisableCulling_CB.Text = "Disable Culling"; + this.RiceDisableCulling_CB.UseVisualStyleBackColor = true; + // + // RiceDisableTextureCRC_CB + // + this.RiceDisableTextureCRC_CB.AutoSize = true; + this.RiceDisableTextureCRC_CB.Location = new System.Drawing.Point(6, 48); + this.RiceDisableTextureCRC_CB.Name = "RiceDisableTextureCRC_CB"; + this.RiceDisableTextureCRC_CB.Size = new System.Drawing.Size(125, 17); + this.RiceDisableTextureCRC_CB.TabIndex = 3; + this.RiceDisableTextureCRC_CB.Text = "Disable Texture CRC"; + this.RiceDisableTextureCRC_CB.UseVisualStyleBackColor = true; + // + // RiceTexture1Hack_CB + // + this.RiceTexture1Hack_CB.AutoSize = true; + this.RiceTexture1Hack_CB.Location = new System.Drawing.Point(6, 186); + this.RiceTexture1Hack_CB.Name = "RiceTexture1Hack_CB"; + this.RiceTexture1Hack_CB.Size = new System.Drawing.Size(97, 17); + this.RiceTexture1Hack_CB.TabIndex = 2; + this.RiceTexture1Hack_CB.Text = "Texture1 Hack"; + this.RiceTexture1Hack_CB.UseVisualStyleBackColor = true; + // + // RiceUseDefaultHacks_CB + // + this.RiceUseDefaultHacks_CB.AutoSize = true; + this.RiceUseDefaultHacks_CB.Location = new System.Drawing.Point(6, 6); + this.RiceUseDefaultHacks_CB.Name = "RiceUseDefaultHacks_CB"; + this.RiceUseDefaultHacks_CB.Size = new System.Drawing.Size(165, 17); + this.RiceUseDefaultHacks_CB.TabIndex = 1; + this.RiceUseDefaultHacks_CB.Text = "Use defaults for current game"; + this.RiceUseDefaultHacks_CB.UseVisualStyleBackColor = true; + this.RiceUseDefaultHacks_CB.CheckedChanged += new System.EventHandler(this.RiceUseDefaultHacks_Cb_CheckedChanged); + // + // label71 + // + this.label71.AutoSize = true; + this.label71.Location = new System.Drawing.Point(15, 94); + this.label71.Name = "label71"; + this.label71.Size = new System.Drawing.Size(91, 13); + this.label71.TabIndex = 32; + this.label71.Text = "Resolution Height"; + // + // label70 + // + this.label70.AutoSize = true; + this.label70.Location = new System.Drawing.Point(15, 64); + this.label70.Name = "label70"; + this.label70.Size = new System.Drawing.Size(88, 13); + this.label70.TabIndex = 31; + this.label70.Text = "Resolution Width"; + // + // label69 + // + this.label69.AutoSize = true; + this.label69.Location = new System.Drawing.Point(15, 34); + this.label69.Name = "label69"; + this.label69.Size = new System.Drawing.Size(106, 13); + this.label69.TabIndex = 0; + this.label69.Text = "Direct3D Clear Mode"; + // + // label68 + // + this.label68.AutoSize = true; + this.label68.Location = new System.Drawing.Point(51, 316); + this.label68.Name = "label68"; + this.label68.Size = new System.Drawing.Size(87, 13); + this.label68.TabIndex = 27; + this.label68.Text = "Copy framebuffer"; + // + // label67 + // + this.label67.AutoSize = true; + this.label67.Location = new System.Drawing.Point(49, 286); + this.label67.Name = "label67"; + this.label67.Size = new System.Drawing.Size(89, 13); + this.label67.TabIndex = 25; + this.label67.Text = "Force Z Compare"; + // + // label66 + // + this.label66.AutoSize = true; + this.label66.Location = new System.Drawing.Point(24, 256); + this.label66.Name = "label66"; + this.label66.Size = new System.Drawing.Size(114, 13); + this.label66.TabIndex = 23; + this.label66.Text = "Direct3D trans pipeline"; + // + // label65 + // + this.label65.AutoSize = true; + this.label65.Location = new System.Drawing.Point(32, 226); + this.label65.Name = "label65"; + this.label65.Size = new System.Drawing.Size(106, 13); + this.label65.TabIndex = 21; + this.label65.Text = "Force alpha blending"; + // + // label64 + // + this.label64.AutoSize = true; + this.label64.Location = new System.Drawing.Point(15, 195); + this.label64.Name = "label64"; + this.label64.Size = new System.Drawing.Size(123, 13); + this.label64.TabIndex = 19; + this.label64.Text = "Use legacy pixel pipeline"; + // + // label63 + // + this.label63.AutoSize = true; + this.label63.Location = new System.Drawing.Point(15, 177); + this.label63.Name = "label63"; + this.label63.Size = new System.Drawing.Size(76, 13); + this.label63.TabIndex = 17; + this.label63.Text = "to match yours"; + // + // label62 + // + this.label62.AutoSize = true; + this.label62.Location = new System.Drawing.Point(15, 164); + this.label62.Name = "label62"; + this.label62.Size = new System.Drawing.Size(123, 13); + this.label62.TabIndex = 16; + this.label62.Text = "Adjust game aspect ratio"; + // + // label61 + // + this.label61.AutoSize = true; + this.label61.Location = new System.Drawing.Point(49, 49); + this.label61.Name = "label61"; + this.label61.Size = new System.Drawing.Size(89, 13); + this.label61.TabIndex = 14; + this.label61.Text = "Antialiasing Level"; + // + // label60 + // + this.label60.AutoSize = true; + this.label60.Location = new System.Drawing.Point(21, 136); + this.label60.Name = "label60"; + this.label60.Size = new System.Drawing.Size(117, 13); + this.label60.TabIndex = 12; + this.label60.Text = "Always use texture filter"; + // + // label59 + // + this.label59.AutoSize = true; + this.label59.Location = new System.Drawing.Point(76, 107); + this.label59.Name = "label59"; + this.label59.Size = new System.Drawing.Size(62, 13); + this.label59.TabIndex = 10; + this.label59.Text = "Super 2xsal"; + // + // label57 + // + this.label57.AutoSize = true; + this.label57.Location = new System.Drawing.Point(82, 79); + this.label57.Name = "label57"; + this.label57.Size = new System.Drawing.Size(56, 13); + this.label57.TabIndex = 7; + this.label57.Text = "Brightness"; + // + // label51 + // + this.label51.AutoSize = true; + this.label51.Location = new System.Drawing.Point(18, 15); + this.label51.Name = "label51"; + this.label51.Size = new System.Drawing.Size(120, 13); + this.label51.TabIndex = 5; + this.label51.Text = "Anisotropic filtering level"; + // + // SaveButton + // + this.SaveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.SaveButton.Location = new System.Drawing.Point(409, 373); + this.SaveButton.Name = "SaveButton"; + this.SaveButton.Size = new System.Drawing.Size(75, 23); + this.SaveButton.TabIndex = 100; + this.SaveButton.Text = "Save"; + this.SaveButton.UseVisualStyleBackColor = true; + this.SaveButton.Click += new System.EventHandler(this.Button1_Click); + // + // CancelBT + // + this.CancelBT.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBT.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBT.Location = new System.Drawing.Point(490, 373); + this.CancelBT.Name = "CancelBT"; + this.CancelBT.Size = new System.Drawing.Size(75, 23); + this.CancelBT.TabIndex = 105; + this.CancelBT.Text = "Cancel"; + this.CancelBT.UseVisualStyleBackColor = true; + this.CancelBT.Click += new System.EventHandler(this.CancelBtn_Click); + // + // toolTip1 + // + this.toolTip1.AutoPopDelay = 10000; + this.toolTip1.InitialDelay = 500; + this.toolTip1.IsBalloon = true; + this.toolTip1.ReshowDelay = 100; + this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; + // + // N64VideoPluginconfig + // + this.AcceptButton = this.SaveButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBT; + this.ClientSize = new System.Drawing.Size(577, 401); + this.Controls.Add(this.CancelBT); + this.Controls.Add(this.SaveButton); + this.Controls.Add(this.N64plugintabcontrol); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "N64VideoPluginconfig"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Plugin Settings"; + this.Load += new System.EventHandler(this.N64VideoPluginconfig_Load); + this.N64plugintabcontrol.ResumeLayout(false); + this.N64vpluginglobaltab.ResumeLayout(false); + this.N64vpluginglobaltab.PerformLayout(); + this.GLideN64Tab.ResumeLayout(false); + this.tabControl3.ResumeLayout(false); + this.tabPage5.ResumeLayout(false); + this.tabPage5.PerformLayout(); + this.tabPage6.ResumeLayout(false); + this.tabPage6.PerformLayout(); + this.tabPage7.ResumeLayout(false); + this.tabPage7.PerformLayout(); + this.Glide64mk2TabPage.ResumeLayout(false); + this.tabControl2.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.tabPage3.ResumeLayout(false); + this.tabPage3.PerformLayout(); + this.Glide64TabPage.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.Glide64General.ResumeLayout(false); + this.Glide64General.PerformLayout(); + this.GlidePerGameHacks1.ResumeLayout(false); + this.GlidePerGameHacks1.PerformLayout(); + this.GlidePerGameHacks2.ResumeLayout(false); + this.GlidePerGameHacks2.PerformLayout(); + this.RiceTabPage.ResumeLayout(false); + this.RiceTabControl.ResumeLayout(false); + this.RiceGeneral.ResumeLayout(false); + this.RiceGeneral.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).EndInit(); + this.RiceGameDefaultTab.ResumeLayout(false); + this.RiceGameDefaultTab.PerformLayout(); + this.RiceFrameBuffer_GroupBox.ResumeLayout(false); + this.RiceFrameBuffer_GroupBox.PerformLayout(); + this.RiceTextureEnhancementTab.ResumeLayout(false); + this.RiceTextureEnhancementTab.PerformLayout(); + this.RiceGameSpecificTab.ResumeLayout(false); + this.RiceGameSpecificTab.PerformLayout(); + this.ResumeLayout(false); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs index 92abebab5d..9a0cd9c61e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs @@ -20,7 +20,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 UseNativeResolutionFactor = 0; bilinearMode = bilinearFilteringMode.Standard; MaxAnisotropy = false; - CacheSize = 500; + CacheSize = 8000; EnableNoise = true; EnableLOD = true; EnableHWLighting = false; From 28de5cc84cce401797869601e7773d6083c03a6f Mon Sep 17 00:00:00 2001 From: feos Date: Sat, 23 Jun 2018 00:15:19 +0300 Subject: [PATCH 331/339] update mgba to latest 0.6 branch (past 0.6.3 release) --- output/dll/mgba.dll | Bin 837120 -> 816128 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/output/dll/mgba.dll b/output/dll/mgba.dll index 7592cbb0476e48365a414522c0205cc4b1c7f501..95e09ff6f806a7809c6f1dff8f150510d0dada1a 100644 GIT binary patch literal 816128 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 }E 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 literal 837120 zcmdSC3wRXO`9Ho(HjAsUJgetUlwiYD|cdjSXbW6b}V(`S53C#@?Lb+_}9|QQF)8` z1D|c#j#sa<{Z9Fs+mh`VlyZ%JwqPFB28d~HI|hgDoEpN7*IvY&2}GJZEsE`5siU}b z@Ex~>Zo{>C1nZl4hWh*iK5hRB9i=sc9}*9io`eT#a6bp1^SeA(IC!#nj^~KEI!e*e z+jayHE#wyto+_SuTfzN8tjX2*%)zJaA3t~d!w=J?mVWqCoqXSXw36VvQ}o9;2kv>c52kG~zzt<=)ii3Tp<3?q&EU(Q_@vbN8n{ zNBFz*-UrGYVy&^}j_dLKO z%0J)iRT|)suan1LH{p8fc2Kk_ zpnhDWE~$7dmO1x{Y)7<4nmqtdhvQBqcIMB~UqPpu`55ke$bG~v`sQERj@F*<#?gJ} zXNub1rbusHqC{&db}F$ePDF!P&v1JWDY6|yzB>Sa;&%XO6M{iE-E!;A@icVw1B9I+fT(kJ6xhr2&=nyyC5wB0T|48X&e7 z>;o9TL0baDDMil2bFrS7R^)p$H7@qW#ppgM(nP(*G7r8%LjjF7q25Jm{5_(}pt`xU zF`W6t^lXQK4(Lk&Ur?i8+}YV#E)DSE(bzx_1`Ncq^WrV}Se0mvqAm%I3dSxvo39r+ z;!EkeTCaq^$x^)EPdQnM9Y@1`OBXsDap{p=imJ!|7I8ZQ4Uo6^7ie&?1TLs)=t;ko z85v!%C=DnSE2C#x@L=(0WaxR;ZxF^0Jj32w@X7Bz@PR&Q7sLHS}{6e&XTR z#lz3xBVLW`4Lu1V`U>3T`C8PItHgfM`HxEmq}+)b^IQ5tBvS1JHR?mc{zSAw+bycug3q3Zlg8mu;Q%nL1)EV79brRot>?J zw$UH%2IG%h$WXjT*deGMF`G-#M*x7;`W6ALn3HV(@hbY&&lJxx4N*52;|ajP%VW}S zc&7rYTLNAI={I0Jha+@6W~k^d1IN`t)t5I^9iKNuQGU#w`S{2NywOFB>a zz_{qfW4UMmdenONB{V>I+^qlpd_1RRK97mDenIpm#;dme$rI%3EtyiaGme(Ax8|Pa za7YV>U+9ZHU5&2+bye)@J^}SJS>5MzHu^nlqRTK%B{aYDw5d<6d9hrs)m?IU zvm}RWo$~%4WKW|kE%Xoe$BJ@&&Kk82O-ie_v||K0`~!r{W^e|+Wk`{)kH@!|zn?$m z&srUqYg^9JT-9>yItQ8*s*SZR{eA8l+#Cek!;yvi{lxD)+|T!eS|62TGlpbA8j;la z;W+fRZ$>fT%8fmqm#;paH(81d!e~CN@Q_T;%s3st0@{qBXj3kSKbnVrfln+EU4#~* z?~^3;tY30hW2h?#A+QoZq_^UmkRNDF13U3N1{sWrSp6COa|>zaztIHiuV6X{GJMXq z+V%`jBU%*)VxvmwnUejO48U*(%Ic)(r=b&pV+;5B|-JxykfCF_UVN;r=6Y;&y>>if|(AQSSDSVq{Kpb z#fj-;jLgPZ!?htZ)pjYegeI;oBa+8#He)uM#cYB+8<0w=!%+qPrui4epzR1^p9eGP z(RxyObwYlD9D6?TXtb$fklI$Hu88?Ejk(v=ZgIIAReWTLk3Q~3pL#%Tkkz{I4yPR6 zB84vU#mrJ*f#EZKy~cw*yj%Rzh|hfL$+dBp5?%xr zt#O8lQ@?RmRE0q=S(um<*>^J`5A^6S02wK{dL6(ouVzMoq?01gjKO4{4B~J20Jt}U zzkqoUfaZy?8$iN<#DUML^qEBHCT**cyh_|NX}*|g{alc@9GjLC?UbUQUrMB2q`hj8 z`mCSfUJ|K4hASqiV=8Kc(dXSYc8V7Sf6^0xqusAAf-LeZl4kz!`)(6_@v#&9OVf!u zmJ`8G0*UO%hL~)Uv7R7wpE@_M8W_N8U9DX{K~X=*n~9(FY%w#zALcq7!?kPxYH|UH z#s%^>xBwr0+)HJ(fmf|?A|?a|KyxLizMcmTf~o=agF7JiN_A`sXqp^?9rq{mDWe> z@K$||hn=95$Z&AGuh&bAO=8xeMP(aDN(=kng71@M_jl?_pZm+I?aTNK{F-=XxW!r9 zavEeP)RT-`q!bgE8=$?0;z}QoO1>1C4#~|{s!fG`L@H9j{h^Y2#SegPA9tTiC>ToD zA$Pw3io)?8q*$N2@Gs)=NqPgWmRAoXW#X*U&uk|E3&H328j z5Lh7YCQ(8&nVP^r#t33%(yQIh;5pptc zRs$R;=(U;vVZ$M)-jP=-dv+A5I{|VDco7)KY~vQ z+~~fFp94=NK2-;YObUV?OXXO9S95E4tE8^0ZE?95(MOi}=z|Zyt!{wYBh{H7lLV86 z&Q;3bN4G;pXHS5<$@TRL=H)9yKm;!V5g%2R<*03i_H1y4mq7LIV%Z@@9{}qWsb4{f zP8Le^EkrSB^`;i=VjVi2*|N})fS>t6%Ned`e@_g7MG+h*MHk~j)g5{H=(7W#lj#$- ztrYn?Zs>QwKOz1JXc6X^=rUSpn4B??L$ZQ=6jz9GFkNDt20Ujl4w-^bj2qRp((K~^ zD$$w0V4q1~OmakJ^#*a_KAa4+F9te_HQ)Mn+#rm+!!+22UqmGa4f;vWLs$`DrDlBK zNVGTCpgo{J@3?X@W{CRc8q@~?2QSj69;|J_&8lq<7uk1{2i8C&!y+r#=hH51lr_H# zLzXD9h!Dn^L`2L7ty9B?GNjovXs+Y)O0?|nTYy%O$*Ik)Aec$gf{n8JC5Yl%C)rue z(t_q_6KJEzVGzeaTFs&GAAKN?;w17YB~MO?U+_ehBV6ySZOu}`OI+bYSyH3~@ylpu z#cOiakHP}{8hl+&mVD$s49_490Z2Y|2Z5sQ*}p-{{%j~P+1#q%i6?CKq1_g0=^`Un z8ZweNa;j6<{N^G{v(FhXme_}>Dh0dPDh9a*V|V10h-C(4$eQmx#HDrZc_&r%z&+Cd*omQ@DtHUL~N2uqOgN2F3(}pZn-gy=y+9FT!LA1eiTI#v<(SLW9G4c@j|I6EU$Yw7=|KAxk4y%Bou^|Exf`77SdMg|V<&zZ}Q9{#DalKYjuhE4opN zJV>O%;PeC{)wXy0VH^U-=ULqx*YC#d@@i75q;E~@H{6mHIs+G9U`@LtLtPvG+6gsT zV#E(3HPYM~nhk^m$(U|i$&n3vJ3IZco8b{|l%oD$koqjLJiAoWQ> zC}qnOtv}4amki?=^v(M8x!gOcumeM+^ zD@=P?=+)AMUai6XgkJre0D`1z#AkUm)~wV-%VG2>Da~T24(f)S2v^3w#X_ZWS23Zg z)w-%J>?&``%os;%)u(RsX_x;-im1f|ULA>-N&h85%RYqX$MS(Nn&C-6=KU8}9kpNe zpt%RiniMVj8n%^5W6|skMM_$9K7Q)|z#k)CQPqO)PF5@}p!{CK9SLHvp-_ZipPXyR z|D8;-PIkk}5nU*d$%KI-6327%-oaPkTbA4Q_(`vF07}Hwd*_8HJ*A!8`(oa^J+Y=Aodb;t|Rp~Nr?2NSSXsT zLDEFamH4S&(gPVJb&njooILMH4_I@B(t@>6fJ1z-5iRom8a#ColOnAUg?;X%2*DO$$%VC;lt6P0*)yy-SV_$)SjdQy^pw5JEF* zI|_)};yDD-H-r(h{obQ!zc>ErBdP7%kD`5he6DC8-50tdHm<|K$j_CseF=YRXt%9EU(>>kug56kV27D zog`4Qb115o<%3KhGE|q>ZaJ=M3z=|o#`W0+vII!v^6gB?5K6VO*O@g5!V_3hIB=!k#q z!#qCC<8e?!Y+Rc_1?*@1Od{g8!x8bb=tI8}kCWGw;BT9JzDCIB$045&cMBnI3*ewOwg-%I4wk4(C|n$Hu1Bjv{Y}tzs(-s(3Jr=q*0R3=0~`vSiOI_9 zA!*@|2Muf1=ict!4|{f>`cMl3S!#V)wAj|27TfGC;@xQxx3}2YofbRoE!w)%qRrl7 zZ+BYkwYO;RPK$Q5K%yu=_D~1nmxS7Z$~@R+vbwu!Gt_sc&)tI5^vGIuc!xdSsD$fs z;WlIiysM?iM@6*gHHJ~_hv(2xufD(#`_iRh@D;m=0!&zAfHxU+VY?eRAaXd*d^@ zv`^@>Be!plkLuDsp-+$8z9Zh#-u@DQ%#~-V<0B%!CBFP#o;*q~M#xwimthrLs+4Us z5MKl&yC^_)i_N;lBF5*SX^MW4VbBG_i*tR$?j?iniXhS2jv_6T7Z)boK0~Y>#-y9< zY{Ec&EsL%aG^H+2xU$#5pv!r(>Hy2PXJY;P0`8c)Qr%Eq`wb%H+cT@SQ;b;2K&*z% zy?aQwnPlZM@zM>la>d`g$3_-Mi`IwjjhUv8ZcQ)g(*EdZ>bqarfamd#j;8@_SQk!IYq(q6y?8+od2>44yNA zCfDadbra&iLBpaW<4paGY_lTgW=<+n>@;6r1Cll3Cu-v;C~c0PG-4El`r2Wx5AwxI z;W3L;{29{1%cuL*?Uu#XDSJC$vK>;Vw#c>dEL$763rB3TEu{@4_YvEmh$fDdL`~@8 z*i=*3<`tFI$Z7|2q^^;OrIXR)cenez4Ss3lVxL;LlPu*h@}aSulc`!{H6c-2P)FSi zicPhYyy!sfNY_VO?T##48_SgVsWy9)+gRq@Pqk@B8~Eh~zF48fws{c6Sad|NuCMx9 zm~;nZZ=*DFtyOTZSbHlYT=W3-$sb9#hc3MZ4dnY2)L*X4PH>!+K!%_!t)~Bv@ zZg4ii(QuuN3`Gqx6g|{$eU#R<{MhTVwVVTn)O(2^f+yPu;`V97T^7NtN+uXE+z}UY z=dmvSu@D)p^x)W3)A=~(2n5r@{-z+7S79*O7T&T`e`*msj&b2_<8+(Ah@c$=W314W zlTzecmQ#l%nl^nUi>Jf*k0%aJw)Z~BtHk;u7|AAIyQi(I$@fMv1lJ)+>xFbYt!zdcx*9ArTMt1Ww#k2&M@Nk5VVEbg>53X*Ny@LeF}2vCVeCM8M;Z1 zO*Smq&{!Yhgot472Ng!;b(KrH(bkxcRDZmI-jF4#AM#O6R-$Kt%_PM`9t)J}v z+z?^jO^^}W{c35#G{|-_2weEm3K|4CVfPyHX5q_* z=y}v=?Db&~brW1#Q&f7ll4~ogleZc963dmL@#1O6+i_p=c#ljuhVkO*#@m^8yx#+9 z_W3uu7@KBkw)A4wkXz!T>DOro5~^x|`G1s%)oO@`tjq8vp;*>1~M3OD}-5KD(K zxgx;;pHnnx2uell^uHKGv6^=`f~_XA$2StGqYbiqF>G3BNbe$_G;#|D=@9y#bO%(3 zS4X}8_K8_K#oD7c8N&856C^5}jUqMbq|_)>$pLkpSx+KD0o7Nqsr*-j0-O-Pe?%oU z07hDD2hw6qR5bRrOI;K!+b~A!e~T39k2(NC=UqUj8OgXQiN|-}kvzu^u*XsHcvAc$ zi9XxY(#uQb$?NB3p>mY`-fp884rvj;r>DA=@W)Zh7dw4g7QCLg&eNX5>?6Pl|npe>|AFu%H%GJ*4PQGXzl<2_R3ulVJQ`OT1z6nUO6Ahn6` zC8aha`%6);yct)W^y?mQ7^O>xy7zD{7=|_Fg%M?H#3NK^L*Q|CcrFiRBm&zg7rYd= zf`F@r9@S=2vM~OXKZ{br)Y^xb7(ar1B0Z84pP(?)dTb()BCp_P>^dWvCq+<_fVOPQ zG_ozD-$rZNg3BVC{WWkZrpTjlRP|ZP7z2 zyjG?{z{}ZVzzd|_c4soYC*6OH@E#V+xyJzSUejmqg5O(MtS9hW46$@f_=T01o+>7f z=pKG4>?r5~L~KS`q{!1)KF-gZMBI6|oro`h8FTbR40>ok9K1Edj{#oOJvHG41v9*` zMqUA%C75nD)XFiz+i^H}`y30r4kH9-!fO)}rQomxyuUprc&U``FywHXe0cO)`whWw z6mp7U?PJ%OvCzj@FsQ3pF8mJaB?09RfYSY)5z3*$w6YZlMzK^NDP+pBu2KP&`Y$EP zu)0DjZxPoZ%cwQ{-*5xf{pETafUlvj2M+|b3L}y#LJGa@C3BP<_hSPN#S_wH#;rK2 zoRJhreGzqnIf$e_t*#8hP=$5)4Xnd`wcBY+fl{`nNXxla-5^CyK-`s?<K7Qe@*=+lQFO^+VGKP5jauQ)t{?eHeadowDYMjrheo=$>8YdSLJ7>xVtqips6?^vd1-S(LL}{N+k`7rwEI$CC5@>~ z#zxNi-Sm|=aUKFHW*42yzC$Tjbt~$R*lA61L_`aXZC_NqPtChjc8lC}ay?9U>bqxg zcg`9p&i*|S++M^r`l5o8G;6U>T^A6Q-q_ZOBGrN#vqE3%zZ3=h+QTlC$|(BVfGWk_ z7X{W&i8(3Q__l<~#IX~OYrcIbPgEDDR(;>(kDVwBIY#l#d�&y18O~-NS%lhaVihLX&{w{ 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 xii^VgBrS!rv(z)#tp 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 oO=jalz_j@ zZOV(cT~d;;*d*_-YJ8j$>c<~H3(ctUSxRUo_wFA2MaVAnUkRPZIU_iymU6-)vuTGp zvsKW;g?PebeF_IP3IJA!Dn`qh2wqejj9VRWIjr$tyM=M&JICV?FA5t6LK5=SRQi^)muL#`R;w`uR1A z(Y32&*Gi~sr=V*huvi@#OZ0hU0S8?V&&OBQrA)*1r8H?uxE_>1_m9=q#&#e!K3oJ#O7b+q0Pgmv!squKe5?aKMEr6LChc^9u zdS~v{)NC9=7DdNU<9*RS65;oi@F5tkIg%SL0c4hA1WM=}YNKX8&&ZpcGmdjE!bgJf zcQ}VJYcB0h!FUVjd=BChjQ^R_2cUj1-bCr}611-8-PW9dzeE1YTa2Rz0C>&F0)43?j0mlw^0`0d zIh;p1;d^*Idfs_&N%&x10GtB>99r`ws04KQU_8)aJ~v*C96*}PS#zQ8Kr#@>SK~JX zURLARVg9+j;?C;jtEi$9WT!!CDg(B zM=PN(IdKFIuTjr7;i(evSxub{J&vdV%G?KdwbgSDAxJPzV^KoSVr;>9l+xh~=;i-K zA=6`JF78P|S%~Xhf>wg@8csjP`E&T4qv;)DeJS4H`A_j$yZ}GO!zYWi*!%|A=u6Nj z%14XxQ>pxRS^ftuM*}E7M3fJr@{48p-Bf-ig*r$eq7qty(I_ttG~tba&QcEvz-KC< z7oovd<_=&0k5@wLFgi8vS3-{>Uyb{e@C|6mj3FEFd4fg|dI1kg%`->`<4ZW_G0yn` z8VSb9D@+MZ$LqoPE1V9YG#FpX>Fa3pM-qT>A4>c^&bxx&rmF&i@eeqs0=0tizjFE@ zv}A6cKc6xDp!9pU&d+ z5KgncF#Hds&HXro_Ae#WhtnhpLSETU4IDw~@LH<)F*4M6t`hoy6ZOlla3cct zMcN#U8o~H6-0TR-*q1ZYuWculs8P?PL)gR?o23K}wnyuWqb=>SA zq|Mjxk{Ul18wEYRh$m^kwBnaX#$h*kQ)~5Uuc__XH{_^0E>&va7=h)mikm5g!c3{T z9cekz&=xRmO3g&B!x$3!D>iMVW;8Eafw9N&TR5mgZwY)x8vY5)24;ZSQ$mOAOQpp4 zDdBI>LZvs@{@18-Zw>K)=DYZ!gZ~f41+aY7%-qRnk~Wvnebkq^)L%7zxe|H@oVHTa zltqs-mCz|jC^f~*2!LG7L@6=8;k)oPXvX0#Qd@H;Tr`oceu5I7sL{8!CSqk#0$KgE zdUca06s=u29~o7+r=w<60Uendfxj8mg>e9H#V8~zgCB3mXgielTR+0UfXUZBq^Q!( zU+~U6PhRx<_Jd=So}|H1ei3;_Eb zL2&LR48fa}P#J&f4m7F8K~Ue}?^ILG@R`U2;Are6$bf;Pr269HAd33|W}m3Yh>uwf?Y+k=1#Il^P)rJ%&LBhAK7W#t@7@PA~|OyMo*a!{DAB{HXRJd=2s=Q@^FT z-x8TXMwIa--khlM9MHo23R{qiUlGzE_hSd zKnc;F7>tVz;U?y^R#SeM98`FWCifnY$d;^c-dn_s5&+Bly61s#FFgw16S~!A8$7SHxf;;BM`LODMW?1{l7#EB|oZ71S7=J;ZXs$wA z@mDe}9Cw*!+q=D~(syLdI23JfTH(Q$53eC#L=4mH!0t=@buG0N@)3cVJDX`)i2Jz} zf4e|y95Riie}b~(#hO(}GtjVMY^Vm|QQ+Z&h8I)AHa!#kWPql&rY~^>7b0FTs_!glR+&@dCrsjAapWrVy@tg?$k_eHQ--w#APe$iyWCB&@La#x@ zJJgqSY33W)y20-Ts@~TZF2V)`J3|xzqM(dEMx|5n5-dp=V@{J+Lscqp0$|CXje`+& z)j}^?GZW0)&=%tt^h=nWs#-#DEQb}evlyo=1e0(+s(?R@U!jEm9~QE{a73zap5Xc> zRgvF({>J2Q_NV>>L+}M9T!1El1uTxDOAQ$-f@q0O6a+UkAZ1*iiFNiict5v3($Wu* zX+WQUr(Ao}@S9@o)zaEqh&F8e#WyXrg81xQ_h-NEgU`}6Vv;|iITbQ<%QB$E#i$LG zIE~t5_*rnn&YEE;ivYL7UEk9e-joV#C!-yEs;G(t+u%Sl*qXrr+WmK|h!w>Ir+q;> zOk&eTe`v3#$e1fYJ2K4skne^ct(8tV!fv68*P+uE6JlQe5!$GuRqLYBGik&%g|t~s zKx+g)APg-M0lzRF9MkrS2i^Grf3_MWgrrTZ{KbpIn()dQ8&-@)zE)F*L4!Xah5!s0 za9OLl7g^>;FsJr-?fwA5v}at+FPBh%h>dl?Lo5O>%8gH6a6wVI(bSpcEg{NdDNRNH zRqCXTc3)@E{Bz}Mln@1?1xCXV?T>~3^w%S?xaKMwJ^*uPiMj7T1t!|_iDA=xUe2w9=w+Cne#Q7j zWQd3ay?80@?n|d`}3BMH^{{U_4h8FT@zQWK-bMkV$G%L4HN;Aeiw z9~HtOt0CS*hNM6V3_24B@hvS2-7jg9KV~p4l*T`&W)^YAIv(5CSgPh+&iFHDv{A+l zobeoIoJz0!6d8=)(q0moGYbS99@*MEK5eDi?iacf40i4V81sy*Iet%l=O*m$9D7K6 zU@Rt>C{gq0+Lx8uMH5OQ<2|u9IG`eluCpHjl`BhvByG{n$MDfT>T7Js*Z}7~iM%$l zm)qq)L)-+23CQp-N>o{#Tn?Bz%d3;idBab z#c2L$KXdOA`c}AuuY?PW)}g-H2l!ZL%W9TLyX{>ZcXBk?)7!vjnpht$*I z%z-5ARp@PmE<+OB*bM_vkVPx>B@|dFTj#3{d1(ArMp-C&^M!t$k0}&)!bKMx;wcV# zPG?Rs7o<9`po{Ygu(GNRIIr*~omVK``FREF!wG(63Gw{F5*khl{~Xw7wI=UN#9tzU z8rIm!>ZWgF8#Dbe;}mb}M(_4!FzgkjCB$5|Zh(6pjxEA7p4cGSoVn&R$9xL^iQtZ* zScp&{ff!&1fr|#4tl=5t-4?A5+<^NbN2s@)gDA;;^xEmDI=iSsuU-oFj9i`usvA#) zD?rB)xb^@Z>t2esblwQmMV)GpbHuC1VX;JTaT&k%9L5JaU+9XL2U9b|79(pA$nCny zqvAKou1NT};scMzNBZM<_cUBjKp$$WrvP~yocpgBAMUnX4oU+2&|(0aHZfOuaUPj> z5EFPHl5{iz$wA7Jd2~2Jn~30MYxH?^K0=!~Sf96u6NUOb5hqrD#0VHCIWf|w-^0fv5NIAqeGi?Ez{E}+ivtUMJi=F7dOAK$CnNCb5}Z$NHftx2 ztDZ5=gKT2b@LH)4Wqv%<@d)DmGC+MNo{4_aQ3f29&?W-t;KBOfgmIP+mM3i@J}@te z(%RAq)iWk|YA24z7pZ#G=!0Xr6fVbl&cbPnLgEKX&%xO_u`b}M;PB6egPi`{D!dGj z0J1bm&rETO-I%;T#ggB5TW}_DqB;=sN2XeH;bkv!@_QIP5G;|kUd4Rqe}EN|rlVxr z>OwF&>Ps9MKnZ>0=!3wIh{i#_A_I;XV4ct~8XB@PfOkjY|Uf2FwT2}+!~~Jm1u;bn^7wfo%BcQo6_kXG3Aj<|Fq`?jQJ}9 zDCQ#yxfzr=_A@$&pkCI$1Sc_Ye9wc0M6=uwybZmgR&csxo_};L^@;BXOp0G%R-nbM z>kX$u(57EIE1zVq*BiW0BU4-WM&x<=0GfpnHb>s_qJ&5a!O@d9R})k6AgL5Ew~O#Y z|BzhF2~Iv~B{j7gNxU@*dlu?En<#iNN(I{W(*Wb8qRyZ1M%=5?Ks6VX&cs`fy-nXL zot}g*LB8ljpm}kNaTQF|e3h7_KfvGM-?@JjFdj#RfQQqAJ65AzB}Dsaa7QEl27VIU zv5NC_{MGZ`_=Dj4Z^t?T_n3VTynbk&jEjbmvt20;y_M!ewH7#5d2#B%N)4AUPaOnu zfiR`ols@p!p-*CA))67ck+fXYt@<-XIe~*Js#j|MK3v_dnvf^GgM7L-x9tsHPdIHg zNL%YYym5O=<&jeddUKlesT2aVbl|pnTp-n~gm>Ue+v@u(VfHWC)Q7H_hUJ>6J&m@2 zdI&u@PW|J^zo!$CUqLR_jc|oH5lBO&+v=gxqC2&=)x!^b4aN*oMs%rkJZRX)PDN|3 z1Q`w-S$*q-jKE<`=e)R5NE8Y(hfRyR<@4!NbJZ;#jC)P(L?&n)yE+T|BW(ZA_Jai! zzDG)EC1?~NduJV-E(7q^%qQr6`$HSLCb=MzeYWvV=IUGUV|)YJL~f49p&@3W@VP<} zah!x}q+s5A5-I-Z$7liSxopHFr|9PI;*wO8>>??_*f4Ec+H>!0u!9$YfD?8d3 zzL6zVx%$>|8G-Uj9IC4t4@!`bo`iHZLmB~01f(aRg!mQA%xs>!4M53*k-~l0PgX18J1Kd6 zQDm}5?dXq995OlHP=7YqquzQf2re0;^y`OeUy^@uI~;m0LWX+U{?9-mS9J(Pqs8+G zaq?>MW2`y|h?BkTcW~9q`x1YFdRjy-YWV#bp`#$lPwlVXIv8YUC~y%!Mf9VDL+D3C z>9Hbj1pNTq?0y3Hqy3@$nE@x@hy0nx8q@GC3h2xZxhg(p!bV`}uSO$!>BM~H;V9q~ zDR_;VLonMDJv_TjJi8h~iqP`I%4Aq$(;?fx>`+7LV0y{jqlVH#^fJRd3?EuuI-Xbc zQrZwGQo-qX8U3u*cpc5dkMYL=VjX?_Tl9|nF!YHS>K0SvnL)IiQxt zG!8W%1jz>7IF_zZxI;bdcX&;}cq})XS453Yr$)2yL7AR+C;kG)KgFV5R)T&QM?xM4 zfbktfHvs`9f_NTih?rYA|CSC`4Nn-X&EPxsOCB801VQ~k^n*{-^i$ij0_B+Yz==3Y zA2=Q-TqYK^wxjE9zpMa&JyEqW8@zO789?j9%JC81<)!0kt6hrjXACbL>|ccYzA{Ha z(p^$Ivh7u?<;=kOXkH0_PJk&Ls+Ep3KSB~5QY3G9Zr=>;`VpckJ}^$t4Tjf%>r@_% zp(nWHNt<|VU?TNwJcvY?4(8DHLilGsg0ysUIq7KOOm+;*V4m`Hjr;zos)hSG)v% z1niV*T(NzNS8FP6*}jGT#x`X6JkeU*h~z7pj>8x!3rC~yqLd}4qGYC{L^QXpbYw|w z=@^tvtJ-Hd@emJe|5kicewusZ{Qg)}KF86J-LunBGR08>EHm5Us#H z-CMw{jB=gVzygB5+>&SoCh6G9yo}}417r2KWMP3l%8!6B)3rYQ8uu6dIqFL+WUh~u zsQ?DvVB@Te!i{7ydf4+V&2rLv4t~+{H_-2Hf?CC|y#W@L;*uEZC=vz2ma|=7wv=P2 zWne3-X<)RSP4eqE;b=f-z~@)*7?BY;t#}*v%DF#I7)mm>KKM&MTQ;To*6AGT;XU#_ zz8dlY!U1^vV{RNl@^!eKu{aty$*t`?L}d5h z7A6*XdesGfJwnnuM=1b6Pj>W&&ePl{<$b;($cj(EZ*VvZ)rwD$!XJPFRP_G`+}a*O zFcAE+y+dbuF!*9*fyf<) z^-B~92LM6$z!zA05yZgqQ}|_IrKA5shd{y7xx|X+fv7O*%_H!&Hv%_1`F9G-flv+l z$acTJpV60kq0XKK795r@z`kKTZSWa^Ar^kaEqA70&qYl8l^)~WCsBROC9|$We74Ec zNOh8JtfV}QNp0|So=04a@dvJT`-tlnAZo`@#Ao3>(D(&qHM{28hA8qC!*ouqKS|gz{n7F$-`) zAveZ=e~}s*cD?g^a{h&EHoED09E5PM2Tv3#CovzAT|x))jSAT(o_q&B0d@Kl3^5zk z^lP<`Ug3&MOeiFL0x04MU_cY0jq){$XiH-LYH-GfSi~#zuEabiznzTl z;`{XN9AoDHqMZf^LbNbTd0~ns9Q_yd#XQNOR1If@XZe$G(#W88s4oKX-SCR!zD*x! zdt2;BJ6{jk#*>fJ_z1rfs9>+(F6$xNX!>I>)cv**e38fuTO$H#)F7RA7xL%y&9+ijLHTGXr)|=4$1k({82NG z^^z^t%c0z9v0nI_ll!+VtgJ(lmJE8(R=a1`OSbVW4y*KPy=cw6UVw>+>nPWZ-xH%{ zgF*8EJ@rPAnscw3F7z|8eva#5{b1cd0S#c*8+o#ej6yDYwyWz0J;#FRzd>Sh*Y%Ta zEPkHwE3tms8XWj8W*5}SBk_f2+kSSgpP`STonEaUtT#ChoS7%!g7Q`E^HMbyhP{kl z`lXmAxs1gAf%9BBZ}rX4`+)n&M5G`(|Aa`+UOhu?$L-)a>hu&Ialk9ngAHp&BMzix zAcX^7xCbt8Hp+0oYYnLp`{2HsmDYJ^vHt0U`b58RsYpyQ&J&4g#w3xLZumrErZI*R ziNX45O@nAmk-yXA?{xV)lYdjuBdyWJfBJXwEbKEQX!DE=I3a^KW#cE1Ai)kI0()Q} zZA8o{{9-YIufteYjY6$2s`uyW>y4pybtnxd`$hf#k@e?s{qZ<4J3bdLz1Inb9g#MZWRA$eM+$dYRQIvY?wButaA4QDhB9)>4`E7_#vAf_z*6A2MQT z2Vn~b!)mCP(qw=hRFBH2%KnG4|M)`Gu9LNYDr+B(s@|m}Lg6*z$B)uGU{$M6&8X^2 zjNbU6cxk45X@q>~2d-y55N6r4XE%s4I{c?;D?k@?m!Q`^n`u zp!`6){N?2GDwKcoZKC}LMLF(3yr^g>6ixU-32L<2HEvGSz{p5%cHej}YJ6bV_(`Hh z1=U!C8sku-(XMe)q6UTm$qF?}QR5}M#(|>7KG2H{Y-Xvrq>JWITro$Qp&LnKJ89k%a@(11^+VcOnaqQ|06BV;Cz4vk6tF z2vlA1ut4n(Z%3&%ehOark$j=T{eong=gSwK5ih`cHXv8NaH#u*>Ea6)%NK4JFHFY^ zo4*m`*nFz&63LPJ% zHpIhYCk40AlxM=R)0sI$eljXr_)`1;+?ej{b||KoBlvWDf&Ye2!G_RYWe}3 z0&^0Gcp2j65sU&rqpJu$p8-Ucd{Uu(?$100G9JJ)y!ib(+?Q3Z83I1o+B5s&ssg~r z-`^OA{y?|`e5gIZ2Jm@pVEO1(fRD=Y5M&VMl@^@L51=K1fHt!tawYH-%Bu^JVU?m4 zi0Ic8a=|;YfQN$+02qZ?1`P~VraZoyjrb``e)a3iqR1x&gw|RCSpk=pg;r2bWL`BF z>@a2p83utdEI`MM>GFBFU%xMkI^~3`UqV7h|6!=S^kVY|O@zo!eDz24#oXLh<8bkM z6_I-)5c|vK@@4ZLS^i6ZWFk;`tMMi6Opw^?kOAb~VSFeu0u$sLZ;59m{4)%-t(d-y z?$h@wz*GIfDxJitDADFaR^}*?Ih{T<*UCJSGUZ?d|6}QE{Q4iFJPwF!?^eJK8w!W1 z(A`vX=2pWenh4y%gNqp>GS#LZn@&X02n-Qzd zxd|BUFmAFkOGGAMztxx_->jBzPPHl>ib^JV$a4RC3$Fs%oCU8e`7GdNZ}C^%@7UtY z5LuT2ml!a{E8w+ib`rdvml+9oJti}{g;!8iaKY>6A};~2i>*3Nc=@eNC%jISZ(8s= z(yHW!S3;i!SB#gUn0eMyygf-82g)>zf?;2hD?67 zp|lOqUEy&sde=*MJhRn?$F0+i z!+s@zfq6s@aSuqYM|xsJ^eVHTC?FHuG8x9Ca0p{Qo8 z6?G0p9o|f~7&oB~oTVVNMJ_5Df%lL7oZeq=Jo*TAXT5R1c;#99l`{DXDSN)o&%-oGb$v-vu%oR7+n%gP7K%8IBw%dY$?RYunf zQ2Pd1+mxz?jaM-+GVOlcC7LG1Te+we#%S zBT>6j`2u$F>@CQZM|C8-^bh5N358pg{h@L&CQe4L&{U~t;_bG?z_);o14^WkdlF4h zr60)N9V9A0XIEY&D${J;Dl2b!NX(Y`GamOncIEr2GN44Nw<)srN?H45QTs-__LZ_W zDdLWhweOR)Bck?HyY?8-5z=J+6S>sunX>Y9QTZ;r@_wQ+>9`tYwM$U@09iXMYu_$vPqJ$dm$gX+`8Q+}Kz<@?|5()ikzM;6&}ZrisUw%j z+QVe+!$s`^yY^eMHmN3SW$pMc#3=u9C6DqeGcn3vqBc>t638kL&ZT_-;p0$+1c_@w z3oX(R{uqu)Xf<1wor^NlK%R?#D`5<`dD;=c_Dt@_>)>u3I1hw}YrMtcO3XiN;@6!i z7`syeFu8r7KwH)^7>Ld2et?cxgRmG~r@cq)SI^X@rq2z=2Pgr3zVC%3`n-Gm?tE&^ zrq33?dkR5|gxRG1Diyq14T64m@Va%n1Xj_R0$5H)Cm$0kAqAn;G%{v7A@v-HV^>HC z_{^W%9eiFv3E)%p{2qbN)zn&7_#BL`@zw3UfY0-v5`5sMk`kY8w8&^yxqy$w_>j@}nOd{qWAQuN zLC(zaz@NH0VPDpRw?W?p8x{_bG{-*AMf44LWT~GhbZ(n(Y=9?Ka8tDcPiH+#4V2>o zJzR>WXX#pVYa<-JKy}C+>DXcQoeM^gR(QrcTGj&cv>L8b)0rxHg{t3jgMni_rG5|! zn4-y%uoR8qgsoPO=3<+MG#fchZNIhXhdy;I+EzDxjguvv3l^afg!oStvR@n@IR2JZc<`qKfPc<>t?MvQsA)$-kXWfw}&nKP4+r|@8bA|!D7EF^Qzbb zWX#^1<74Wjy(8ucw zLlNM`G?IgBVrYUsBS&iH&+}tn^s^r_WGf$^`s{zCA^a#boBq-*ydcCpKpmke4a0c@U0S}JJGi?y?;ey@XHR+MhbGgIB69#hj?R~hj_L8GEXxl zV8gf{erM=?=Dy0377r%jKtO+DDL9LcA+e2s{YJnZr=0-%e4M9MH?D|n>YLNdqeX6y zqjik0_xK{l)-Ar2!J#cu@O)xy=t*uq78wSGAvW^}%^_u;M(^lG<^GsIwi#nP04En{ zYzK($a_0r!beW-U@TJ@Ss}kR5TXVx!d@KLhA8!-4S~dmQ5v@wOfC?+)M3C48Ua z?z@Fo65r8dLU?rAh4CG|!}xCC`EGak{(B4GpV(!5cY$^%7?0kk1LM10-@V75-r>Jv ztUFx+J9cpeq`)6b=CG(f2vlUIM{5F=LdYH3*oZ{UCbG1sB7*yt^{gVTenbnx96)jj zoQ#_mPQcY9#gu`Zz)z8J*-@z8c^dr*0ZbQ@clwx1oE-lS_0ov%gc%HR4v*KhO`G5snI?ju>a=iSaNCS=Xs?36frz9tEk=>aOK)kEMgr_7+1rHhcu|8I) zO7E9pLBt6xzcNylR z1Ly4$%#{!^r4G1T^jPR?tX8X@N)Py_0Q~9Km+TbZllBAN3RqW!%~N1KHUk}7tk@6y zi--taOxr4ih<>f@2{Iixe;1)o@k1QYx9KBE`Rzjz4Ojg-Jrj`d&)SXVzax5+0{`9g ze^*yPFli3Nen=!AT7?9$PvZ(8N#MJOe@X=+pX{MsRw&+Tj-^S$-PwxJ+;fw z-q!)`-5Bq`wBC2)d%b`!`IGKh_Q^FQz7#;8hb#C1xS>#bT?zPJV zd5{CbN%HY-jQ`(|K7W=P-bws+7wrqU?I6@$?3VzX)|$Z$gXJca-jHC4zo#Fh%9IWm zJ<|^mkKM*^twXZF*b-mdWegwa!f>ZQKpOo&QptO}v7UOtf6!&Jf}Ys=VyXXrTj;+( z%P0LescT`)l=|;)p#P2mlqvP!z=g@yEpf)lDBlm5c+YIcs4x?Ma0$ z^yl*d4B;PW!=pRkT-%hJz50>8) z`u1j!DNE&U=CW}fG#^_8=?B7VFsabHS5=bc7H&?6xHf44q~fo%hxw>MdS^i4?IYpb*b#*6OuJb;E}_O||fyN+{5#=u8%~E`g;&G|Q*>)`j32|2W>kyeJ_G z9_iEG^J(jV7OiVl)6?3H7D(ZyU8o z<{hur=$ctAc0G^0f*4dBGL09!ZAQ~$&6 z0`=%>^JX!8*s5Y1hvcjk>jsb_5J{MX+Cg2rCsfxXo=IOcm9rKjw(!|nHg#IPgt=%r z0q?d41-+(K>(d@2uw$0Q`W0O=Zy^0U=FKj~a*|~iSo-3_WmVZ`gR z;t_SBR}OsMJ$j{t{M51Z;j#|`%Q$wlI-KYS0KyolYX~8 zj201adI!^yWSrJ2>olGKfz>T8bf)9EDDUQ7cBjTj_Z~i9_aN|DTr=&U&jNwa)lPvx zgiVBR8Otcoe0;Cre-`8a6p(n}Kkk<$93$j^ofCyNqAQ_ksn!8u1N~FN6reOg54+%h z9Hsx#evR)_m-ZU|OZQ7Awz0GlGc*6i0{SqaQPzQSCa7?UTVcHa?^Plg>HJ^!u(4!9P}jr zLQE;~FdGyH|1uQ#(F6YyBhJI7_r4~=_j?Ja2;ZOPgbUyO0^boz8H6AEv`y-Lhp!=K zNC|&Sm_J0=&r4GAFQvj+Gnsea05$+_n$|Y%Vp&8y_q2JS2!22)8z!-n2PCjrKsJro z#279o;j=Hpnths1n;ZvM7BeIH6>IdNehFkJht#Aog2l%eUEH_iR*2FoOT4Ay^<2%T zY4j0^vO%II6u=VYfE+vVfO~Zz^o{svW!=(3KpsWI8qr}yhR3a8$-R}q7=%2r_vrnc z`+(oWlkrA$Z$CTX0_oY|Z7f2%wuLGryk#_qnBe{VuxwkZO9{*RN# z66@f5d5pN!#2WZs9_!+OUJcF}ckNjEV_|J8mTp8 z2BtU>v*vDe7N~7)# z_J?L)@fsXaG>4)SmFbh$#Pu#BUc}JE@fe6?6KtuJzR7HOD}ZXwGU#lK|B*#6LIkjU4%hBSFQq@n^>@(ERX z+vzkPq;Fqi*^k)U2KJ*htT5G@Q5P4FvEtne%<{s7$P_yCzZTX+|qV@ zLVxbS2Vs9Xot?1U_7|FZI_+kkbMrfA-WeYmx;7nFaW<%UTRSB1;p_k`Vx`;jZu^T1 zU+zdhzK9OdDWvFTSU2U`K#$FX(@PH|zKIFJryTYeVT!pQtWo7I(~EBKB?Y}8lmW>g zW%Lg%T%myEYKKc1P5NnDwuLgWZLHINZh5RTvM6CX8V`bEqZwfjUP>77Qc#Ut8+R(} zT=V}{`sW{=r7kj^G*i+^vz!ni;csIRkYtoGk6?VU8`_*qa7{XQn{N7o2m%1NpxGv0 z7)n+Gdq3Vo^k?_+iTpL(_~duuliNO^gkAv(jMocbBOv}I*+$@P-2r?>%S}>FadSHKhW-a+@FRx$nh}P=m_z|c~ zjNkGqBiNXy&_cEL2gW|hVYXQ4EZNNcP*H0VO#rY7ZzpbLi_nCmdw zOqPrPR6-{c4v}?x5)#aO{uv3ID{yWAj9_gi%lYksPcH+XNP=ZF1REh~eJ>HHXZrsk z+D@}PV&RW)C`ySxbW=yK@JB(##De4oyDUhvjk!j9zfU(lrQ%<`%)l~MUnH>F?$4gW zza$1kqM+nwPqEN%GACU8Ecq98MZZ(6^_#@cz6uQSFR~H0thfa;>RP!wqhWbY8s^-O zl+a$lKmQ^N1BE|NN(>wg#FFcemzmZgh?G|E6pG9PocPDhfyZ@k2qvGba>RE_H~uld z%OphdygvuVIljMF@NI&HZG)4p<OYD$I`{%yEKk0wd zLjE_KG&tNdWaurZ7%cIfFF6581!tOW{9}IDiGP*dGN!Y+JZgyi_O|9+9wr0F$kqTy6gHi>83$P`{~B7RQ$8eK9{32ZhEq(@G-UgMkJ3E!U_59 zVxoO+h46cfo0hwW#K_SWKeH45xhM8H1(Dl{{J$6Qk@;uKiM?Gcq=Y*yW8@ehnY~^F zeQU8uoYZGY1welH_(<%u7Xh)E)H!sTd`IS9#7BxnE9O*gXeQmsX^+p1e{dCUVf!VW z@7rwYpM;aV)IW8z-?IGXB?D%oFETDGA-}P6{I2lRQG$3|FZk)Gw{=5;pXM_QUHEA_ z&;$GG_~BU#gfhxIEJ|8N9~n0X*c8$+A1?a17vwpFBa{4eaF_Y%rtbOaCgP_z5I>zb z&G8-b(>$kAp6h|17KbzTwEg@RB6-B_k-e17Iq9;WOUDe-=?{Y+5mcV=%eaL7GMsK1 zZ{QWNYN$*SOTc??a4(uI&f?r$cyPOj){p)Z> zWQ%TdEq|ST zyW+RJpWBY#O{X)lD6N1wa2$o;0qOQWm6e9wvKQvh#4MKtjGn-08nzPNXSM;G=L8=KY@-0UIy13tV^^NSD7b zcM$wGUD<*_Fy=_~B0c&?{*tg?xaOayUI^SY?+JmMW`2{U;Qo1PgFREYauT@L_#$VL zz>Q=xISj9-O_~IrFOdLh(KL7KG(0JZ{&vwnUWi8A{vR>h|0CIl7|{kGaswHqye&kb z1QwD!baev%>KXqw5&lW^LqHQozcK`EWK-o<57twK=RN=Eca;Sxq@+~)yQF~v=5;o{ z1!@@AZg=6@Vbff=ri4CV8j+Sm^V}m}G2Nd7cT~B*vo#lodnXRjUGz!aI>f^##Zs`ZCKM-sl+TbL z1$$NZ;uJALZKEvsb(N>iL=z-WQN2_$6^uBvZL3>hnTxVvh{Stw@`4ch=X(;N$=^0v z;QEo1o;vV(F5~lT7d}rqHwmA=-dp%g*Jtir9wZ=}PckZo&@{$rW;B@i7 zgDZk-u=SOw#1qv@@+J&+a>Y0`tfXX5te3R#vkrVurSBK=84vZ|EYsywa?d;ga`~ zmd_Qa)Puao4VBPhap-5#yz zVSPvkq&+7;f?5#o$wtdEus8|M-6cQnRD3MknfU0HXHS;{MJdGU7og`k2|LF)2a-Dc z|9dMxmRGp&HNEnqkF6bAvw4!h&vetzG}6a^8~G8!xI=^-)||k?g#7px$u>R7kF7hC zAJ?*#?O&E3{{m++7i~@{HE9{ zYgx6OBDTuuZyPSp3^#BXuB>s!t#?sz{33i)LAb?hz}#*^(dR27*#BXJOqOpms&Sr-chJ!#5Dk8VV%{uojc%MZ*KZS&O?k}IIjVJA-NynGDg1BlH$#!z^@eHLTE=2?C(zFMUg^n3m>LsJK10Y%1T^>Eo`B+$2y%~(nP!A-x@bKV42JSCKN{?UO5 z-}8@Izz7oGbk)9GHs$>PH}a1TO!=OF-1Ifww>foe=YmCqFH^_r`b|hw=qiPy(3%cM z>@>or-J~zy^N;&}&p)OSzt}%zSNKPve6$shxw!lnq6(r?kF=Ra!7NJU#Ei!6Es#ej z{LSHA_D@US_=en19r}m=#CVWWd5};vibMEH=xu0PcJ*9<2lR#YjKuqEO|6Z5VpB*X z+nX@4%-6AGfJoptBCmw$;&KOsl@J-r)2QJg{ibDK*9%k`gm?M6V|1QgaKH)r%qQ@NmiRqUAd*lyE z#)nW3Y=Hkl5(Xq)vYd2DAzezVgoY3XklQ@P10lD0I?uTj!0H72X!cX77#R_Mi?6XJ z#DxvqUV7udchP4eetDK|&klz-*xn@j&$EAi((JqZ$4MIs+;30DeIXIIy=eOjY;SUE z7~38e_`x?D@>%CDjuJRowY|x_4qxAk_>zjBERLFgw=`FK7N3$XdHs=vPw@Ci4lA>= z3u+PBp?{1}bb66A$u4~2_R@_{4&zCcH_5l%fmzOF!Dw}M5?Y0LFw!0mlrp+Osh3^) z`EK#+UI%{h`CLQVIW*i--U#Zv9g$`Ugy#Q4M-uj$bkif;-y`nqWO#5mQrCFWieFNO zRptwQw@c0atDfe?N}%h^ytJO!wn-^--SO4S%s4_v1W(ZPY;q)8?;CR?NDD zzw$6K6X~A+uJi#Rrucpw9CM~quGTr{#Qn>Gi=2#Y5?g~v;yV=hEJpaEak8H4w5G{= z{umNHg!ZP8Qs4x-+RsnLd*BKd;1oVw1(^@V#@~z!u8G=;_~szN>=XfN4-G!f0kRFs z4)SyU&W`Y}O?iBk?CZ%VnT~TJR*B<$M&LpCS_T%{ewLRV=E55#6h(7sC2xSAw|Fh7 z7+_K}#{q-tK#vxfVdgff@p<+{5EKq`;bfr9C@NLfJ&-7ryV-A$y%+hVF8G+x$9Hr5 zy36@g5pUn=&>@XyF8AUW2rY@#eiJ}p_u?bEcQ0-mnb^;5`9}&rI^M^j7q#orR5*gb zS&Wep*z0g8?p?ee_jzjJ5C1tnmW+_@ct65l7<(6SJ6%{{lTPxx=)2QjmeCG3>=pIWh6tODFpW-{m=j3=lEp4Sv zd`>#=N&YsyiRa!OVo%>aMqx<`x;)Zm6x{e=#RI&T_=US{I$J#8JHT(8Z`L>-AbIa$ zX8?chj2|yCHpuAFKBeu?hgeq4N)xnc zZ4V~=FDI6Ood)X?$Wc4;m2e!Whc8S7l|gux_90%rTy!IU1mQ(-c+-RHMSVH-_)gAR zb<>AA^|ez=9dtBsQ5tDzdgAps{5|dEvlaJmnnePOZTxc6FA>ZM@{O2RA>W9J-39r^ zrw>-Wky3IHUPmI%BH|@U#Q8lEKJ7i9wocsNlpMdf?Nq0KFtO&6i6nh5i#LQ5a1B;z zy@9)dcW{n$4hE7r(F=&>>uK77QexWCV9r|1y*2X*)DrqJV2&G~enw;1DaN|d)qzb) zh~)7!VpP)p)Lk%qlY^5DA^8mcg%1+J!pyh`)>EJc@K;`E7bn!#CIye=FQuD4+4y?` zv|r|P{9OnCyBqj>)lTJey4`6_WL%a)W(UZDO6Y$99~OsRL4qs~=X4_t6-VK*>WYV- z?C*XIS|E8n;iW2Qqt?_FgU9V0(Yhy%-Qe%7G}HeIJyb&L(v886`6VLIT>yS_t%Zd) zJrm-&z-ssY`!FiQK39*umP9`Rb*z3tOiW@XvA^#KBtnOmSV~>gp>BV zg#BmW9cxYXDcybg#Q(?7=kp8T~*p2C8#?R3R0Oy+SMo@sj5HE{aB zvIa>1ltjP(kKsRnC6@qSaMcBEUuUd#;D5cn0$kLb!lweit*Zl|9weG?uPJVkOY#dKyBoh-peJWOZZn6OB!<)CQEdLCC;HC8Bw?AC^xend{h2=@T^I2u zNw-TR<9Atcw9V&`S1fypSY_=kj5K4UGJbHRI(R+xBUZ%>pt!LqnG z)n4)|-SWR^OfW(znO`MD_XDMqUBO*k;{h+rKV3*nLY`VrDCv-=np_C|GGo4!r>y$| z=ysmuyA;g38MVcJxcBJ$oW8(s5eeS0pJ`3NAbZtq>xrIswI@Na0!K_}4=D>SNS6_K zBvhn?1?MC7gJE*I=j5x!-^*7i{b6nX^qwrt_fF)iB!6j|NXsAQL=cW17*qC#A>*JI zd?^I0CK9ZeUXft68ZYxHp(dALg`=-PwIqa4!eFFfTkeKGj8jv*>UxsMdysDs{Wh%x z!+n3a!@mY(S7&*#>Xx0zirZW4L%~X@1LUDsbQiEpEh;+KcfD%{5-Yt;7xrqwnetl2 zgt-V>CF$=|-!EpK$#T1oAe&zQTN|J2;csUi!KBkMKKFhs4sRK`UHtn_0unf}c`Y`$ zo(xZjYQur;9wy|Wt(fdjY<9X33}$hZxnDVHHuGqoBL$&T!rQS`b>a6UK6ktSVTO9v z|0tnp>BeXWd~N#~EL8%VuOhJ$H?@oWVYn`?> zUh}jHEaA{ew6p#tq_^=s)qQG7Nbg!7-QxeH4*YlBPtgVcDbn6|f&b8in+3`EuQf54 z6K-;h^bYyPjsL<}Yg+Vvqm1#cgpLBrq^9?b|J_}z7PRpHzOMLxf)oF79aRUsdwYTZ z@1v!j@PB9W&KT-R`g}N(>~Xha4X2V8;jkmc7zZ*!zfEAE#UGOR-|e>6qp;|hUp8lz zN|$msNpHIg`U05}y;h zmPtlAJG@Mtj6dnuy(9eETmEp=9~t>tJ38*yRt65Mtn=#=hWfP~em!f1U;VtlUyF54 z8R}QtJXIe<(&lh&uJ4KUQT)DNy=6p(UwbDojCM)8KYo2Y+S`iu*7?>&$u}k^;KO&?`qhmelY4hy5VbT3Ep2IgA?bV2WOTyCEbx#Pr?0er zgv&UZ4JGUYh}-ILyV9p__*CfUVOYEL}+Ucbj_D}} zAT;UmkB+72|36k1M^`%5hxaGm^StMJ&pB5h&QQO87LcN%XcoSzgoa^){CZAV@n>bl z+u91KKk8~vx%!&FeDpgYj+nkPBU!_IdP#rGX<(CI@BjbV`xf}9s%!n3WCDo|oT#Aj z{!xOC8ho^gkD3(JoMZxLU;~U^TiaWR z&zngg6Y!M;A0V{`ta1)h6tsnakNJOV@AI5YLK2d2+mhc8=A5%1Ywxwcz4qE`@4a?F z{;0b2ucbTGOaCVuX?@5kyG#n-QRoU@iu3{b11bBK0s_?XTvxDEQbzgZcm45gIWv?% zS+443sen;rcbKG%#n{pqSi)v8)?j#Qs>JO}#6poxK za@j9~_f=Pjmy1`3SBh7)fKkZ~DM6Si!rV?cfhu#`mQf+oQ5 zMgo?m0}axS^Xie3gny6B8v;8*wjC< zMDkwsou@%Hx?7U7g9U!M``Hv_Cdbv&QJQC|p`W4rr9@RB{i?euy4)(sRp>-H_3!3T zP!U30Y(MrhR3-!#ONnU(>^U)w6;piy3GGovAx5ztgQZs=L5ybzVv;sC`NNuZt{l<8}JBOq6z$f^o~iE3giX z5Nu4M_@GCh9sFr(BJ_YaJEd%J)Z!|7mXZHU0fJXYKtTj08*A?#|KxTlHtr0`9rsV( zNtI?5bT_N_Q+AVq=}hAz^Pb3rJqlXhe$(k`o-&Qr^Ni+mE+IT%rB1sTC_fKOW(&Sep`2p_}peiMN-U=d554uD`S7BeG z=&C-iBt)>S*9Cp>a&#dj!jr|{%aO7ReW;&LxhY?{iAHrHa%moHRi!(S6&oq5OQ8X& ztv>I`?rQlT80GlYb1be`t$^S$`%GhPftH6Q<1%U;(jVo=ci0l@kE))d|PrmG%3VqqvOo%qs-b=%L z8Mq}xN|0JAM?Hn;iFmAz{#8l{I!?A0y(hqAI%=zLX=EDKKbK-92Zj!T^M?NQB`;lG}+~4guK4f zyLzNASA~Zt{omO{0O{v~e-{;o_kn-&M}hu6@b6?iVAW-xyY@IA+O~uhp+ijlfQq1} z@S%PsKDSEYw*Do?DpD-7KO!p2jP9R7R^T$7K6MGv`#>5qPNYNHvv}St2n4!`0KOu& z_8r7)Q0Ah$R2Nb)&hoiGrujg_PF@O8Zl$98y<8u#egesD7Hfv-9gw zhziEYl8ZZ%l0%qZSc4q0r9WILur#(zSiV7 zffM|?ijng<`AzN4%V(tjPnO=!@1ODX6OHr?Hw>!OjXDgR<)UH+&QhP+>`Ojjbw*&i zYzbaP0%JTNylYRh43Q6!;}UmAsTN_GhDTYkLnrD(i_s?gMFFP z8{w_CaI?>%yRu9ssd}d)QNTK)w%bO}9-j)8eAO<$&GJ%K+MPPetoec&PD!<^Glbjo zC;6mZgI;gSdow)?`T^@k_VL;bXY`l!9?s~0z4vgIe=}>YjQO+hKGHM#XY`&wqkly2 z;gmn&fdqzj)LyGSsZkjA_4YC(By|cc$qMYimpwiab;N(J@Ay;+NBqqwh-1F!98Y@W z|76ej`sl~_vpGZi>*0()&-ETo=}rFZ-ed8{mfj41vu8M?e?0nuQ{OZ3!{}%A9?sf- zE301b_~;`&qu)n*M!y;PTl&jQsD-Y-ETTuWxTU<1(pId^!FRpEwCZliYh(Vgfz7|x z!*@|??wrtV^IgI5m~KI%*cd{pfV@jgU0fJi z=ADlOnBsmMyi_?1<_Ax#F7?G1U+n3a)OKPBe?d)-&JUj9SE@^-*c=qNx{M}{<#;Ie%NLcPY8cwuy94res$x+<-pasJMy1B2 zdXr!Nb80ScQSGZJgfa8cQK*sethfsg7k^}k@e@GBRU;03yLvpe}Hi`sHaw_gc( z7g=$^GBwB|U+7)z2WSv>DNM%A?CdGnP~z6v@Sm@6Hv9!N$W*ut5DXr}JA{(YEY0e^ zp87cPa!}J zYOLM{x2Y4(QAaUL5X%91`ejT`(LRrrb~-TQm)Ee4OTZ!2XelO^L#T_(vJ7_3O`+U> z?{*}1j5o~Q@Fv+eW+EB#iEqtT&;K3CnwiL>3f*jt6uZ%#e3I)=T>ilvijGDGC|II` zr%Q^zSekNp@LHJkk9Nv`seD2l_HkITC07_ef4ALwzgz&vZpLt{LEfyFkGHsD$bjj~f>?v0+UZP3Lta(H_N1aRGU^uzB(5#tuKECPct@0WMo`hIH zqs%iQA}7D8e*6e4`(nJ}J)4b7sLN7{q`pCcWCyNV6!cQ1@=>YQs`-#AX2qQN(z(Fm zgpE^HR(l!iAf+$>e4kdlUI~8RuK!|kTMX_fX91yO)tiZYCiYA;<{M~Ii$BvTdK!Pu zq;J&b2{-4FI*G-)y=2KsDN)TVF1&@Vx&oFxgI{k-vHMA~v0k}LJ#inbR?12mjA*$C zrld`&l{@K8fyJJpIk0NgSI;kYW05n%*RgACAQ>9$b*(%4Y;lngAxj3Dq`&%)oFEOT!j)h^c{`hwlgS^e?Z7&MjLd17h=vRr9U!?T zbz>~3utLFMPkTU_pUZTdAEN{TrGykCtO{HFCH(@cXhk*U+k{9hB8gMZtKIHbX64o> z)6iret6zIK8!Afen>EVKg*DOLLS!~ohU%@}x0LFN#=}SheGlu*>Qsn)VmZ?)z?jST zdQkJd5*1eQ6tJ}wKX{6Q7pt=`Md#I9>cH9a^lhKwdfirw15W<^EBdAPdUq5tC6My4*9%{MMpoxShRt@7D5cdg2FIQum(>G z(QW|*6@wr1yLF_3DeCNR3~BvurTX{4M=?rS41D-90z|KW@Df({-N^}Q_~$FERR1rq zG(Tmkw|0lK?*H$HtnP2s!1PM^2#%qqhkCh7Ekc8EEX*mu8Zs6V-w^IRl{6T%K9E`i zNK`|9!4?nYC3yOjdAUMl1#vFiBw;A0m2RfPG)Ysa|1Dra&9r_|yA7Hi2^)u|hm{~^ z7@iLr@CehOU4aM2c|%Za*=-1l4VPHF*XUw%4(aD>dqDcTW;LKFqX}i?p+h>LcsZnN zy7YbG)EvWK@yU`iRe+y+)K@CUkvg_|?7v<|DXPWJDMpCz3WVJOG zJMuC0h*M-V#mm0{M@XtYmXGZEFQUyUDh=hPg#Pj^kkD&I6h4JUS_ah;4K0?GPUv|phXMd0;e~&>dD*E9!kyrt@x%3S{(!2l>Ka7A3wgIWa5kKLGm}IHA9TrMFhWumLF9rF zO(;Y_DW&VLQ#BErqphy)mbb> zGxprw!0{p}mtib|S!5k6@LqMxVhb;9y17ri7YzkOytIqtlg2s?p=I^gEwsFdupVgn zAtDR{Etz?-7dXbaZSjJQ(*{}s7@%utfL?;7K*oz5$^U29U)C~y)Rd$D%tjR7lrx?D zxb?m?eoUr}P|J=REVzIlY8?~(kVUjWSi~miMbebYT;WdO2*83=WFirv^FR}Vv5?R< z50iM|POljyytK?EM6O_#K3xq|&OPxtl9WI3hS0rWZ^HmrM2c<&{U_1zhe=|D>BIB| z6FCgyptrCgAis|WIvz_u)$$f8{=P@bUQ}WXBtEQFV3v5Tw@CF|KuuPI;UVXPkI-VI zi&CZjHV2g?*iI(#dZ6-c7J8T{uT596N>J7kreZLOX9Y7I)a>s^V${%eP+NcJ`e&g0 zb0LDRhW?%||6+#xTaNAyAiT_KvPnJsdQ?r`$2$97wFM|52}dKrSmHnt{$@#Nne{K!|IqQC>?M6Hc)P-4WD#1p{BNswnAo;%*^EHf*bjHrd#z#`7F}Y;}fP6P&2=u$_ zFaThj#ld`H2)0nV)jxmFUgozDfN9#Z;~(%VQI| z8AZQ}Mvz#}?~+^^$$J?S)%pM*Sg4167h#6*sa9rt)N2q(*)4Do9?x@HoR-shh>TRZ z$f(Gck$RGz$#ITM+QC=EJUcCuy)#)R`3Ty)-71Z`1>f1e3a)M6EoaiUC3&sdh!AME zH~xvV?WF~Q*zZZ>mSoIT64fMF@qAICJ|1&@+uuXqMn^7GuSZUP=-V~rUs(FKUseGr z^$3(+`?jh4e_EfZIDR>yPG-fSfhM-dD2!ho4U()Ze)*C@Ydk~=U3>yi-D3iYFFRzb4f(H_uVV$!Nv{U$aYgEpHhP$H61}EtsR&&A@7{)t zNyL#pw@tDrZl{2B?Y|WfOo~rK0GRe=&igA@Wa2&6p458~LReS?Ll?mL1n()?=Kel; zZlS{->rad#cw+9F22TjKLiIf4=9j0Tzx<>24L|nKvL!in8zi~r-%Xt7<#{#QTX{Z1 z2$g5Cqv!Tj9yVX@rT_jGxtaafD$gZUo?j80^zxjSu{<<$zm}C}{E1H+vV?KSF?+wb z(AK{GKIO@AW_doZeS^>2v96~kAQrWKA6w=5Z6p(as62m02=Qkr?<+lR2^TxtYd`%r zM%giW@=@*UR)M}s1)8@D$W5~kKTK^ezsgz>t2Z>?FT*D`@u%%|F7=ZzSRP(Zrg$io zLS!WkURZ0!s^+!cIj|Os&sR_agcg&1h|Z&+KE*SXsSvAsQ&oa^iO8!o@rTSAiLv=477g&J)yPb`f-4!*$^fjruMG}p= zQ-a>ICI2fnVkU_ajoWFvRAvvEz9^tf#rE4)H+9w5@5J70rquGh&E!iga#LX9&w z#Hr~dR)i1d z=_j$`_xPc|e$l($r1xi5zcwUHqc@X9`Rzu8IIyvziM}LO5Owh zWUSKBfuI)SNFHBbQ|jp2Ijq&u^}a3vHxn*)!>^-;i{AUVZwXF+r3G!e`!smZIRS?r zt;O01(Io$n!iT#Jxc{6e=C$c{K)F!|R`f_U(pUg8^k4Fn#}Nh&*zd

    {IZx)3>);oZk-{||a?f4d)eYJ9py8IIt&KMrKb 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 From cea1a04425c67fdc09c708c251bbdaa49f5ca71e Mon Sep 17 00:00:00 2001 From: feos Date: Sat, 23 Jun 2018 00:31:16 +0300 Subject: [PATCH 332/339] tastudio: select newly created branch makes button action experience smoother --- BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs index aed7b087ef..7d36eff285 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs @@ -157,6 +157,7 @@ namespace BizHawk.Client.EmuHawk BranchView.RowCount = Movie.BranchCount; Movie.CurrentBranch = Movie.BranchCount - 1; BranchView.ScrollToIndex(Movie.CurrentBranch); + BranchView.SelectRow(Movie.CurrentBranch, true); BranchView.Refresh(); Tastudio.RefreshDialog(); } From 08e33f7ec15ff22a85cc334dbc56f1f713736768 Mon Sep 17 00:00:00 2001 From: feos Date: Sat, 23 Jun 2018 00:47:46 +0300 Subject: [PATCH 333/339] tastudio: make current branch track its new pos upon reordering --- BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs index 7d36eff285..3f5e344729 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs @@ -627,7 +627,11 @@ namespace BizHawk.Client.EmuHawk { if (e.NewCell != null && e.NewCell.IsDataCell && e.OldCell.RowIndex.Value < Movie.BranchCount) { + int currenthash = Movie.BranchHashByIndex(Movie.CurrentBranch); Movie.SwapBranches(e.OldCell.RowIndex.Value, e.NewCell.RowIndex.Value); + int newindex = Movie.BranchIndexByHash(currenthash); + Movie.CurrentBranch = newindex; + BranchView.SelectRow(newindex, true); } } From b6acb998e1ac371307e317b54115211bb37710da Mon Sep 17 00:00:00 2001 From: feos Date: Sat, 23 Jun 2018 15:22:23 +0300 Subject: [PATCH 334/339] actually update mgba --- output/dll/mgba.dll | Bin 816128 -> 816640 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/output/dll/mgba.dll b/output/dll/mgba.dll index 95e09ff6f806a7809c6f1dff8f150510d0dada1a..77d932ca8719b4402037fca142df2a285c02c255 100644 GIT binary patch delta 206303 zcmaG}cR&=!_rIOnrKotQG^IB?3Mz^U0s?vn*lRSFV6b=8Bo2D>HT!N*^We~%LKd}(of_ELXBGe%a_8{>0)%DG4;Msv0oVhdIWeIr2FI+q-Di} z0t)SiRi~p20E#z!5On5186(fa1@>5_Z$pKOlIo3iRA;s4lG`PIZXm;v| zq|lCuR@y~5>a0=4#l>l}jb_(cD!eihB|J*LA-Q@qW(XC4Fgvz$mu_7<8ME&B8O`Cd zIXo+0@*alb2acIpVA%K#HRa3S57qL8%cTP0D+EL84E>J!<%A+c{8oDyD>44D`=L|95%X2H#Cf<$Zf(m|qZ^IpR+gW@W zO|3kPg>ezc>X628kIW611*c##JY25n)L!or?npN!*}KZKom@$cCh|(Bb!0{>xogE5 zq+MeZ_c1K}pr$ADa^tkJq_w4@YXjEdKn(cd}El`_h2D*II1rxi?l4LL+b;zHJ* zmK#(FC-3Uz4XRR4AmO3%4=!f1s(xN|*YEWDH?&C%ME;k$hcs+yKgVON3bJ}O|f^AkN7qw7aGZuUq>=FNbcd+i8utv z>-}1hrT+2@zy4&8B=_=Y*&hK0hQ>ODgoC=he)$>+{2m%Rm zlh@R%PoBEUm+GaGaaH7)`opW(RbeXk3NntqZ8Rqb#VF4!%LVmY>0RgB%jH7r6WK*> z9$JeGbdl3S{Ybsa@{G`4M5!X*3LWp|UJ10hMgg8w>LAZWa)$>8%3p>xCmvPgtzkK2 zS%4hXpf8CIke4+0oE)qq+cgXen`8iSVXN*uCUY*O*EX6iC^cCn=UAvjB9C}Cvc6oP zH1?MVHVh^;4Dzgo^=iMY$h7V~aIKNG{8y-|naOgD*s+O8<&-gGLR!oEeTLItY0#8r`u#@$r*08zl>hJhP#P z{C6Xdnii*zrH3j%vN|TWP*62c<~Yg0;k8JbliWT$QmAR8j(%C;W9s(GO;%Ti`UI2ug1yA{8(;#xTocyY3FZ~zgG!Da>H6~4~$t#+5BxfAu*Ufs8FC68r&07%Q6t&xl!Q`pu50-S zM_qDiesZq219I(5E(L)3Hj>*#de=AF!4T#oqY|xV+AEFKOm`(j&Gc1*)XYG|(@tIx zxrxkPBe#w6CZpEK!=n62+ckN!qbiWfF{{*3skzoKLRZP_qy0#WZSwJG&y2?_Idp6R zACfL8H`H>vvxa>_gD*4YCSj;}qU388vr!Sl2BHlbg^Nb9L@gU~hVTI zRq`n9^XXoLuRJ)Wl6yZJFhr%`a*|id-^Mhq?4oh^<@yyU;!62qjDMFKKdWkA;X*Gc zC)6@i8zXChQlP zH8z!)FvV1mXu?8ZPCg82t+?si){#L=m#P7oq}gY-R&H+0?Hp8-d9UqGxo_*{8L=v0 z>?Y00VH%>%$^IIAj?Kx=T1BSK$;C@lQHV_*77x|3A*W|aY`#%0+A3S2GAtj_8PT&h zqEoJBtmv`D2J|cC`z1^4EUkZ^#rAS$VvEW?8hhJw|#cEZ$ZP77vk-glZjcc88C8&m+{@hUNBBl37_-%ndr;V$=G1Ap- z>U)Z%=$^diiyL(QJIcL<_VUvxmHqi<@ANB;cSCI|Al-Ih*hh{+6I9z4b5g> ze+^zT2smpMC4)fmg0dcYxIi5Q&Y}LJL13SXT6PfF0Q}J8_N37Zd1`WfXZ;HYU2^kO z2euF@uRMP)7bHhl-0~c~#~c@dm7yVfggi4juu>MPfLD{@CHX1o&*hdWZ4E&xBIb1< zE+O))%F|pfprL}bw^$|xIH;%%_|-pO~}04c`?I!h@|;V zdBLdVg#MWqJ=%kiN!R5b>Fr3}>v?O^uL&gSs%#!Nl@wmdGmL*FkRz8kbmFDFPrf`X zko6bjniKYu+voEhPLK$>epY@l=`%9%Y~Cl6FAHSxX*nWuEU9riZ$l=aekbLvQ#+Ew zC-TZ=WlN;h;k?;19zeaKyi9XjJ+V8GcYNk}Z*NT%%0>oV^iA@a_=xF3EI z$*rwg$c?4)^SohX^wPZkTjmJl z+G6?TuM0`+;=H+Aj|ybeLJlPh^G%hK+E zfE)+i4i6Sbg)t5eNVl{9jHdm{KY(C4GatO`{?>}*PT|7r zKsKnLiI*1`GN)G28d$fwHkcQtr^`1UMhE_oSz5mQEt7wz#I>)L4ng*^&mND!-oX1W z`Gv|ifIz;qLGH1q6^Y7}m+f)YZw<7Ux9@4-A zy#JOT2?(?GIwoJZFE8EWrcVoit@kbS>sV|DeylUUD?@f-Ikukf9260|4LO}M zYNGsnf2QB`cV+oMoKTAY4sUyT?txFqR}DMAW@q4vajU* zg*Dv_|1jQIuUJdezYLA_2ixl3dfLmK4}L;+e<^P~Ses1!Qoej}gvX{gTp7jx%Th|* zt7b2!7WE-(zL577b=EgR(eqHZz&GPRQsPTVRidWWz!T%;xrgd|M7&mct^$6E;l@<8 zmoFdcN}7$A8~xE)zYjYg^B>&;zaIAy&sx}FvWD4s_8TWlhwFPBe5vw`0DcKiJ(wgP z?n(}gm47M^+UVeMHTVS=ZAMrHd2(L8H$LaFeBlSJHzfgG|OfSV#c4H3iN{T`aLt&HcwS(mR4F~DjFJC7nn4DR%}YCWj=22(*iM&Xy1`f2{Df*Z zI#-92VO78S2yh@iXp~M#2Xw*7EHp| zqLNP3@Z2@Lp}-q+YM8$l@RD<-%F7*eu_i0F$~*j^GQwgG5}oqyp)8zs(Z57j@i;v& zYT`qVJ$f0(=BOYk!TuCreys3XX;4Mg%bV+2pafL0mv^1+T;=Wq8w&L5r&D%f#t1s| zMU}q19yYM5`i7!@!r@=G>7!32_GdZoDf+skjx2K!yP z2er5!OSskvLo`>RQgL>&m!r?6`W_l=^G%Y6(VQL>%1$@RG3WRxi-A*c&ciS8BSvP> z@Hk?Ox>m54>tFGf-Ofk)R{Mxq%~H%B0B_X!?}bisxeHrJi#u}ug{mGm(KXq8CJ!u$ zBCFE7oW1<`!YER1pxpoBr_QGaU_>i9m^t#LeBz>8r=0Tkx@2~M1TQV0kCDm`O-#lZ z^N(VuiwD3DfENsaLuiP&0B@*PxCDem(*@3vO}Q}a1f6om3-jNl*%=K01?A2kK&c*O zUWhlR=#*+IRif#UYGlK|i4sy>b=xkobZ2iYJuMl}tba zixT>+x9u_*0|&sGmu9f|V+M;<2A#?>V244{;PDd623sSPPd?_+13Zl8csRWI z7ZHZ3cd!evzL5`JZbWpq<@cAXdaS?*#C+4`^HRR?vuEB;B^Ue4-(Be*yQ)9)%_Hs5 zY&pgk7K=4o9uPy-rONAMoK$?`@WEN*vG{435s5S$U!ksDk~A)ltoQ?6ChpRmK}(zQ6^`Imh2+LvVMO}Y2=f#hgE z`QY_jvhq*4+l^#$qOZK-MiR;HD?ho>&230uaKVvg1h*#6H8I|F>nr#9vv0p&`vAnx z3=Swo$Rh+aQ|%qaAg<#u{eG~OGD**T@~P37bIFiBlFfkF{f{igB)bfg$)jYyn=U>P zpo%waO>i*gvi_Uv+L|va9ve+hm7$~Lg1=fgi=583o_s%2UV78r?N_4GIfA5!uLB+{ zk4G~6NK^09i-fF^T-n95nX=j%^flw&jBzk0Q~-l^8iVniL32YH297F&gR?$nkS}ls z&R}o`+%ak@@8k|Qqn+n|T3UfwBV@zh?yjvEHX7;z^iMSOsSLe&wKC`~Be=ec{w3}Y z9T(>T`s)BPaSn_=a%r#9;^q(M^kZgztZoNJ4+Xry;B$>ZuAKi@v~zG72EN1Pj9c#H zY_a_Ptr{fiid=B3h4b8=rDe<-CL3;}Nmwtzf;GXgJH#=KvW9UNc$PtT9mZAi$o*KQ z!|yo*=Ju-qG6s&ELDaHOO3Pa?lr#9{n~xcEWDL-zao~`qahN%jM@{uI9Q=mLlNEQj zb69~f{IS5l3vNte6^TY=7dH7j?x(XFod@`tPF!XDuaT5 zG58Y;H86+(gO>m@4SYERXG{0e8f+RY-?zHE4ph-2fj&t?Uon`=iz!IJj#ZqB#fWb6ra7aAJUbMJbYa`Lh9PM^Rwd7mHeSX#S7m?g_D9 zCM%CZ94+nH3LrQpL-u(bD<6C0AP;!#Q6UAjMsvu4$>ns(vi~zzdFkW&0_Yz53RmPu zkE4Y}a_AFZGO3;1;YkY;)lQ!C#Gh1aCvSO@BpmtnX@Ias4t$zSK5Z+HdfG5LKLy-! zjrPhpmN8)>nwAfQ2Q#y^{4x3F(*}+fh6p_;%0ACx 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 CAblkO delta 206262 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 ND&g5NU9Xhq5&-^qAz=Uj From 0dc8187cb5331962c788e8794472cbf6f63e035a Mon Sep 17 00:00:00 2001 From: feos Date: Sat, 23 Jun 2018 16:23:53 +0300 Subject: [PATCH 335/339] update mgba info --- BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs index 7720dc4595..0028c7b4e9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs @@ -3,7 +3,7 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Nintendo.GBA { - [Core("mGBA", "endrift", true, true, "0.6.1 (c3e9258bc3a1e8a0e68ff73d5ff9674b56257eb7)", "https://mgba.io/", false)] + [Core("mGBA", "endrift", true, true, "0.6.3 (c4dfb265c35ecba4050d646bd65a02d8b3704948)", "https://mgba.io/", false)] [ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))] public partial class MGBAHawk : IEmulator, IVideoProvider, ISoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable, ISettable, From 51e4a13384ecbee674ce12f1ad045fe642b50be6 Mon Sep 17 00:00:00 2001 From: adelikat Date: Sat, 23 Jun 2018 11:29:06 -0500 Subject: [PATCH 336/339] Hex Editor - fix exception if autoload is set on table files when no files are in the recent menu --- BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs index 82b3f53de1..1b6c98b9e4 100644 --- a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs +++ b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs @@ -1271,6 +1271,11 @@ namespace BizHawk.Client.EmuHawk private bool LoadTable(string path) { + if (string.IsNullOrWhiteSpace(path)) + { + return false; + } + var file = new FileInfo(path); if (!file.Exists) { From 23a061e1ba9625492ed2fd99a5022fd1c81ef14e Mon Sep 17 00:00:00 2001 From: adelikat Date: Sat, 23 Jun 2018 12:25:23 -0500 Subject: [PATCH 337/339] Turn off a lot of resharper defaults styling that we don't want in our code, things like over indented object initializers, using this. everywhere, and other clutter --- BizHawk.sln.DotSettings | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/BizHawk.sln.DotSettings b/BizHawk.sln.DotSettings index c6e4f57315..25f57108a2 100644 --- a/BizHawk.sln.DotSettings +++ b/BizHawk.sln.DotSettings @@ -48,6 +48,19 @@ DO_NOT_SHOW HINT DO_NOT_SHOW + 0 + None + False + False + False + USE_TABS_ONLY + Tab + NEXT_LINE + False + True + CHOP_ALWAYS + CHOP_ALWAYS + CHOP_ALWAYS AF ARGB AV @@ -98,5 +111,6 @@ VBA ROM <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> - <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + C:\Users\Andy Delikat\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_5c6a09eb\SolutionCaches \ No newline at end of file From ce70b8b32ade083344b8f6a8a600c1f3dbc40e6a Mon Sep 17 00:00:00 2001 From: adelikat Date: Sat, 23 Jun 2018 12:50:07 -0500 Subject: [PATCH 338/339] more resharper configuring --- BizHawk.sln.DotSettings | 58 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/BizHawk.sln.DotSettings b/BizHawk.sln.DotSettings index 25f57108a2..33207db7ed 100644 --- a/BizHawk.sln.DotSettings +++ b/BizHawk.sln.DotSettings @@ -56,7 +56,9 @@ USE_TABS_ONLY Tab NEXT_LINE + True False + True True CHOP_ALWAYS CHOP_ALWAYS @@ -112,5 +114,59 @@ ROM <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> - C:\Users\Andy Delikat\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_5c6a09eb\SolutionCaches + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + C:\Users\Andy Delikat\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_5c6a09eb\SolutionCaches + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR \ No newline at end of file From 22d18186414d06176ad1240d5c24d2b09fe52891 Mon Sep 17 00:00:00 2001 From: adelikat Date: Sat, 23 Jun 2018 13:06:13 -0500 Subject: [PATCH 339/339] DoSettings file for all project to explicitely set language level to C#6 --- .../BizHawk.Client.ApiHawk.csproj.DotSettings | 2 ++ BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings | 2 ++ BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings | 2 ++ .../BizHawk.Client.DiscoHawk.csproj.DotSettings | 2 ++ .../BizHawk.Client.EmuHawk.csproj.DotSettings | 2 ++ .../BizHawk.Client.MultiHawk.csproj.DotSettings | 2 ++ BizHawk.Common/BizHawk.Common.csproj.DotSettings | 2 ++ .../BizHawk.Emulation.Common.csproj.DotSettings | 2 ++ .../BizHawk.Emulation.Cores.csproj.DotSettings | 1 + .../BizHawk.Emulation.DiscSystem.csproj.DotSettings | 2 ++ .../BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings | 2 ++ .../BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings | 2 ++ .../BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings | 2 ++ .../BizHawk.Bizware.BizwareGL.csproj.DotSettings | 2 ++ Version/Version.csproj.DotSettings | 2 ++ 15 files changed, 29 insertions(+) create mode 100644 BizHawk.Client.ApiHawk/BizHawk.Client.ApiHawk.csproj.DotSettings create mode 100644 BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings create mode 100644 BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings create mode 100644 BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj.DotSettings create mode 100644 BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj.DotSettings create mode 100644 BizHawk.Client.MultiHawk/BizHawk.Client.MultiHawk.csproj.DotSettings create mode 100644 BizHawk.Common/BizHawk.Common.csproj.DotSettings create mode 100644 BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj.DotSettings create mode 100644 BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj.DotSettings create mode 100644 Bizware/BizHawk.Bizware.BizwareGL.GdiPlus/BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings create mode 100644 Bizware/BizHawk.Bizware.BizwareGL.OpenTK/BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings create mode 100644 Bizware/BizHawk.Bizware.BizwareGL.SlimDX/BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings create mode 100644 Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj.DotSettings create mode 100644 Version/Version.csproj.DotSettings diff --git a/BizHawk.Client.ApiHawk/BizHawk.Client.ApiHawk.csproj.DotSettings b/BizHawk.Client.ApiHawk/BizHawk.Client.ApiHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.ApiHawk/BizHawk.Client.ApiHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings b/BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings b/BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj.DotSettings b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj.DotSettings b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.MultiHawk/BizHawk.Client.MultiHawk.csproj.DotSettings b/BizHawk.Client.MultiHawk/BizHawk.Client.MultiHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.MultiHawk/BizHawk.Client.MultiHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Common/BizHawk.Common.csproj.DotSettings b/BizHawk.Common/BizHawk.Common.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Common/BizHawk.Common.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj.DotSettings b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings index 235b742b90..6e05d923f6 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings @@ -1,4 +1,5 @@  + CSharp60 DO_NOT_SHOW \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj.DotSettings b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL.GdiPlus/BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL.GdiPlus/BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL.GdiPlus/BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL.SlimDX/BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL.SlimDX/BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL.SlimDX/BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Version/Version.csproj.DotSettings b/Version/Version.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Version/Version.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file

    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�ag$P}#V-&@%^U|W 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 LFQ4z;*%SW*d&I-M diff --git a/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp b/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp index 638e8d202c..0bca14ea6e 100644 --- a/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp +++ b/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp @@ -441,7 +441,7 @@ void CPUcore::disassemble_opcode(char *output, uint32 addr) { strcat(s, t); strcat(s, " "); - sprintf(t, "V:%3d H:%4d", cpu.vcounter(), cpu.hcounter()); + sprintf(t, "V:%3d H:%4d", cpu.vcounter(), cpu.hdot()); strcat(s, t); } diff --git a/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp b/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp index 55d01886f0..b8ea3d64fc 100644 --- a/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp +++ b/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp @@ -113,6 +113,7 @@ struct CPURegsComm { u8 p, nothing; u32 aa, rd; u8 sp, dp, db, mdr; + u16 v, h; } #ifndef _MSC_VER __attribute__((__packed__)) @@ -174,8 +175,6 @@ struct CommStruct uint32 region; uint32 mapper; - int32 padding4; - //=========================================================== //private stuff @@ -537,6 +536,8 @@ void QUERY_peek_cpu_regs() { comm.cpuregs.vector = SNES::cpu.regs.vector; comm.cpuregs.p = SNES::cpu.regs.p; comm.cpuregs.nothing = 0; + comm.cpuregs.v = SNES::cpu.vcounter(); + comm.cpuregs.h = SNES::cpu.hdot(); } void QUERY_peek_set_cdl() { for (int i = 0; i Date: Sat, 19 May 2018 16:28:15 -0500 Subject: [PATCH 232/339] Lua functions docs to wiki - escape braces on optional params --- BizHawk.Client.Common/lua/LuaDocumentation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BizHawk.Client.Common/lua/LuaDocumentation.cs b/BizHawk.Client.Common/lua/LuaDocumentation.cs index 9487336989..c52c4316fb 100644 --- a/BizHawk.Client.Common/lua/LuaDocumentation.cs +++ b/BizHawk.Client.Common/lua/LuaDocumentation.cs @@ -59,7 +59,7 @@ __Types and notation__ sb .AppendFormat("__{0}.{1}__%%%", func.Library, func.Name) .AppendLine().AppendLine() - .AppendFormat("* {0} {1}.{2}{3}", func.ReturnType, func.Library, func.Name, func.ParameterList) + .AppendFormat("* {0} {1}.{2}{3}", func.ReturnType, func.Library, func.Name, func.ParameterList.Replace("[", "[[").Replace("]", "]]")) .AppendLine().AppendLine() .AppendFormat("* {0}", func.Description) .AppendLine().AppendLine(); From f3ea6fe0254302cf9312d2eefe11eb822f6f8bc2 Mon Sep 17 00:00:00 2001 From: Scepheo Date: Sun, 20 May 2018 22:18:53 +0200 Subject: [PATCH 233/339] Use generic interface type on MOS 6052X for talking to the emulator core (#1189) * Use generic interface type on MOS 6052X for talking to the emulator core * Change CpuLink constructors to not use expression-bodies, to get the AppVeyor build to pass. * Add comment explaining why IMOS6502XLink exists. --- .../BizHawk.Emulation.Cores.csproj | 2 + .../CPUs/MOS 6502X/Disassembler.cs | 66 +-- .../CPUs/MOS 6502X/Execute.cs | 382 +++++++++--------- .../CPUs/MOS 6502X/IMOS6502XLink.cs | 18 + .../CPUs/MOS 6502X/MOS6502X.cs | 45 +-- .../Computers/Commodore64/MOS/Chip6510.cs | 30 +- .../Computers/Commodore64/Serial/Drive1541.cs | 28 +- .../Consoles/Atari/2600/Atari2600.Core.cs | 40 +- .../Consoles/Atari/A7800Hawk/A7800Hawk.cs | 32 +- .../Consoles/Nintendo/NES/NES.Core.cs | 10 +- .../Consoles/Nintendo/NES/NES.CpuLink.cs | 27 ++ .../Nintendo/NES/NES.ICodeDataLogger.cs | 12 - 12 files changed, 383 insertions(+), 309 deletions(-) create mode 100644 BizHawk.Emulation.Cores/CPUs/MOS 6502X/IMOS6502XLink.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.CpuLink.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 75b1906e1a..6aa7c1e3c1 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -1301,6 +1301,7 @@ + @@ -1441,6 +1442,7 @@ + diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs index 23075ee8f0..25d28f85c3 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Components.M6502 { - public partial class MOS6502X : IDisassemblable + public partial class MOS6502X : IDisassemblable { private static ushort peeker_word(ushort address, Func peeker) { @@ -15,7 +15,43 @@ namespace BizHawk.Emulation.Cores.Components.M6502 public string Disassemble(ushort pc, out int bytesToAdvance) { - return Disassemble(pc, out bytesToAdvance, PeekMemory); + return MOS6502X.Disassemble(pc, out bytesToAdvance, _link.PeekMemory); + } + + public string Cpu + { + get + { + return "6502"; + } + set + { + } + } + + public string PCRegisterName + { + get { return "PC"; } + } + + public IEnumerable AvailableCpus + { + get { yield return "6502"; } + } + + public string Disassemble(MemoryDomain m, uint addr, out int length) + { + return MOS6502X.Disassemble((ushort)addr, out length, a => m.PeekByte((int)a)); + } + } + + public static class MOS6502X + { + private static ushort peeker_word(ushort address, Func peeker) + { + byte l = peeker(address); + byte h = peeker(++address); + return (ushort)((h << 8) | l); } ///

  • GaquoM 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ⅆrAl~Bg)iy*@VncRS^u`Dg65A`rBI62EswTnqTh| zV~WDXKl{!e$O$($0`f%^XH}UGiK*?G@qpa$C}nM0!*R_rsepBe_nmTtbZ~{tA;O(P zY5Z8p^;qL!=Hoy-@@H16Wg7HKkYn2y_%KX4$X59ykQk@#P<^Mi8%!;NRsb}&9qh{x zxmvX;O)`}Y{LV};{}ls3QGR)3Nv)AbO1MRobedl4j%Bz?m(ka zwx@7&_>%{z>z;8dc2M2D|ExV4Dfn3)>RA3bR|LJBDtcl6QXKYh3&Ei)%hyR~1Rn4g zo)DC18A|h{Q8Q9R*D`3(!Wn#IIpB8u`17Xo3dE6Z2UN{{HlWh!<%V7>w9x~ck95_W zlgs3!&#o?>#E?;PF)TYAU>8U4hX~Bou+nqRNAXTJDMMMYXN>2haW!FlnbbqH8W}nc z1RguFd=$U#{JqzPyJClQWZ6xo4=t4E1`+L)H}Xt(X5=13-!L0@IMKfcrR>^!VOCf- zADQCznTB5~!+8#jrw7+$IEdg2MV;&~Ir7<$5iw2t#B!42&;WvF+xH*dgj0BHJ5zBx zv9#H-ylWOPMf2226f98G*`xySJ)$wV$D`IOm5VuEk~y%{X|7uAzMcE@tDBqcbL2aD4gbQ*4Z&;dN?xB=Un6QP!z^#Saa)HF+zQ=h<%Wg+beW%od;=JY5s!sIZ9J1cY4DZ(@Jd)r`@8ts}^a>f=1q#4U z`y_Afh6_L{_{GRFQ&b;h#oro%}0|#@#{_p|8w0iT8`z7;^ z5ijhwu@3AEcANl}2Zn|_!CK3^Sp9xXPB$?^ z|CH!D<8{MB>JYlx{!EY9ES87S^PRorRWwn!?H{(=J3@TxXpR6Tjp}!Kt)E+a^{TK0 zUeV{sVOv3m-<*m8?^}?L{^6p^z;bu9v@PRWIoDHtz(%K@P9URn&n=^+F_M8d(#4m+ z@$P5Zy+GTeuL96^(2~vL+k$X(=uC=meX?kU8ipu9R&a;f4m}O2ABE^H)kjX_ZZ@1x zBX-OWobfe6cMhmL=11hB$jCVh+=(V~ff=!T59zFzQqPk*M|0~pKzv2dexR{5-m^~( z7d&**w>x8TwTfp)1MOaU#?_hnI$xLhAe9vJIZC6f@xuMp3PZHb{LA(*7?{+Z1 z-_8Xq`aM`UY0SqDx)aH1`eBK86#Nggnm-n;m1tO|B5`mL4(lg#2xwiRbBxMCjEOarPru=xo-d6mS9 zhqabC<1$#nwEoOhn`@yGY%ye%9?lEio5UtSzZ%#lP7`A*V%14C%>vIA1fpNTyXs*uSTes3osP_=zp?L{s;isoGWSX71+|1a3chcoRy+iG?TR{Ekht52 zuG0a!m)3nk9vhOnmj4#}qM8~*j%j7s&^ex!a|)<+#c5o`fV8~)k~>!W_`9ZJn#bvT zum}>kg`@3B0rDH3073qE6cG>gKiFi^}ItA^6AP!U`C&}gwIlQS*y zrqpEHTPV|G9E?Sj;oI-&O;25|l~DbJp5*i)ihODKNNK6sOyUHuL?5Zzr&OOY9!w#M zLS@sTFbKRT3T%vvDC#JJIU^WM@R$e!xUzPx%)i1MiSKjGgHV^$gKWLnA)zhr9INv$!4#3#2r#f zS#*S=?B^*rqV!kX;Zp^R_Zu9S@Q=LW&e47$yu5TumV$Ct-oVD6gTZu~QfSLUQ-@L@Rao1@zdHyv(7I#&Xh`Yr-L5^rq-5H=Ta+nX8}xXz4Zo)lY8-w9%E*c#%e@b)r8A z927qo!p7nIUfyUq7#M6<3ldz*4l^?_h*j{ejln|jH?;Am;{4-VXqVnk= z^a#r_KEi>g>~j$wJt1*G=|e{V-x|Aqlz6Og(cY|CMDtXBs1vsN>yI{@NV9?=%D8vC zCJw~)i)6fDolfdt0#t7B*+MOPBoD0gvkP`XOv{`m9>{ahQ#<%L@cCHmWM>vCOz)tG z>R5-la4#!W`Wo*OEBX=e=my4-AGj~X`yVv{=yfp*C|sG7sLnS06Tz`MdG50MX8}-u z9tS)5v}|Wlk>V4NJem7tWH;NaO8B@M?3$u6@DN;zW+nRm?c=ulHG4%|_4J3q;o=d? zTr^=wvO)hT{9R$&P=P{vRx1A2F6b#LR>PFq$pHENptoL^m2xxOJv{HZNV_mqwhC?@ z-e`%_+F(@ZIGD02$xgbyo&&&_8QJplq!4Zpzfg^b<0klA!7%SawP`x^KI*8gE{Pf# zYAW_ei=<<3$mkv@l)EIEL%r0Y)x)Wf$z5z{ktPpm?_^x9j>~P8@>^}KjN=E;zOYGZ zUgx08TNX|Z)hpwHR}k>(QbXjY;`%qv^K6*T?Esaa!IW-0o#4-8(^i~(=FzHh^(ib} z=E&+?sW~FUUc;hDtG$+e2`t`|jyO%Dw1j16Cxpj_T~%x0Q2wWe9edeyl>X9BO3hMf z=}<|6!`UZ?PzF-XCK$@ae1IsROhp|Qs6OOx$STrAByW>30ND?llvz_Jh4T&6v%F&c z3(oFj#}2(O4b~_fNMT zmJJJ1W~ZMbq=y>Ggb%eC>k%l$wQttya2Bu9x;0OQ?>jg+D!#Iim=r9 zUO=sthZk^9g1X-PSMXt9hR0O6Y#Kj;vwws66Ziu?^mGa=0gDktyUl?xA_}0-f?PyO z!5g_?4pvX8qKFsqseIQbcG~az8>brY-PomRQmBdXBz|J<%|h_wY%w9_)l6`~^Rc`~ zg*rfOQ`WnHS|k2Cv`Y=n(*hLSSD=$7Zsw@NC?9-4F0)0ica}YncCC9oJ871-|9;5opG)z zh0}s8ZzK13(Wg$Q>7Sx%1u$AN6?1{657>1pa`uQ-?0>oaW0o@^89=^d#ZF=(b-5Q+683|J=MZEOG;BcJq;>Bnvn@A~hK{&y(F?i>Xvln+ zx<}Rc!rJC?rXMbbydsW%hWK!9{gBXFK&7j7eWKAo-};P1|eS(~qCrWKUjY8V@{CwV3fzG)zcBCeF+)V>iJQ>@O zUq0-Lpr^dg?x|1swjHY56V$Otc<9EAcM{>5e(z~<0K=7?1_X!8VFd8eCG4}upNuuS zZh|~GhQbLhIb3AJ{cRGu=TQSdlUSw#ccT_D#<9;5gpM)^!Y9@C`oO20GdRyKMme$l zIbslEFomQ0^W=+mensaV#PiXa{#K8Fl#DcLOwqyDbBEu$?z?u|cgQ^q19?9P@E1D%Hki z*o~Hz{w^G8iQF@g`4~F z$z0pdB;o`&-HI}caIqL6q&(_@(inuKnd4OQ1!n;4rI5Lfe<(lpcpp=xTES1u z*jtwEh5;^K<o_ z_CxV~%isXm=(g-SB}~QWlsK{7Z1vPf+(TJq$e?x2-&LM_LDTJNuQ*=#%^Wx7o^Pt*@jvC+u*SS0KffrsgFlcDuz8oVPT z)rk_%Bb8xS>Az~Z8QL_>&54hDkkYfRRC#cy&xyUvp_<9TCVU5MeQksxF)5*$WR&$OcMbHi5&@HUdHTH<-F65ys(a!Wq0#+)k)#6HdOp1V3xecqr^Hy%F%xT z^xE0DNsCpk2ZFqc@Xhh37wq&Gcbbm+3wN$DULPUZdVVU^3WMpEAyfiIl*L6~8O*19 z+WZ-v-VRCFVZCKh^L6LqyI=$9Pl-3#X(3r5PTJU&hM0`r+T6z&yH5TME*30|;>P&j z+t&{Z2G#T4kw(yDVc{jv6y2=_HxMC78FlUNgoTCDpBuY^1vLyQz#S zixcCxX(^azXyJa6T9vquMd(myxq9Zr#Qr)FY2d95T_;anu>$QvC(~O*-&`sS0fhLq z<(ZpT;=TFKw78$*ip`(!<7*j}5fsn~+4C8DPgvGyr`FAByoypks|j@C zf#bqk(B$has9%UDK&Z8;`0a>hvtXgiTh~($|C-XfD{uk?=~VI==}`we;Wl`__}9Xdl~jtSgH@{##vk! zNQ&pOA`y*HRnO+wq}ta9m$bcNT@Y9n{Mr=Ua<+1UUWqWtQkAE_(_bA>KDEtgHpcSr z%@Xxixdf9|O0q znWo7ZS)|-f3Bcl^8OBk+)A*X8YZyyC^*F@ykOrn3Abko(rk0%;72cn!1>L(R|54A^ryh|!L6!7=e9XEKj4ER=-IabqL_mlt zD%{*Bvh6c5Oo;IhVCWKBF-JHjNeI0y8ZA93GFv1us-6v7T>%O2TQLQBPz!5NPjfUo z9xi@N3vz=_=my*9b3$0iAAfde@r5Ng!QAK9wr2;vHnwCv6AV8!#bh()=yg>JYda&{ zdl(91zD9Rs{z>*-n(_n4tn@&%pATdY67ea;ZYgb%v1Rx)G&}IN=|k8y;WDV$!e9nS z+*t7*eV9kH5O6h5DfIpEt^5?y+#WTNuKGNW9N$ylhD7EL1lAj5fN8>(m_*wZ#xi3N z&bO`H9RMJ|wU-GW|3EyCXhDufTV8ufZVW|}IYePasX$;_jw@4nHUJfK>`%gc02bYE zy=!KQAY`ALKA4~E3mc`$%0mRsWi3l7>!&2~^zcI{!Ew~-Nj~e6AB?h_Lij8v5yap` ze1HK&1P-icF%)g=2r@_l6u?zpXoh<~;sg@i>;M(GMt|og&USleqZU>nGvZF3@AmWO zj*a7-o7d%{4MRO2bp0@@%^D%EoaM;mLMk!}YL^!269Xv;stKII5Mx(FjwC9Q7%6z{ zq4CEb7si^+O0r&;qinzJjMK@}lLN}-zEuMTWdf4p_w5`M}%%WqPD zuV$M?uZ0?gsk5d0jz?P+gpZGET)#TzZiO71NX%d5?v-9cI?I1J?QxYF-4M!+-zC&) zO-(EQmcuNb%_rM5Sf~~0x>Rft@G#u|ocpY%*4fGw;a^Xvb=N&ZwUfLQxhz-jL|mxy zm9*LDLE|`?#c^>h=*YodJl(*!cP+})?-a#3Ity&~QR}Y4lz&Xq#KS|J1(Z`E#61-D zV06JEc!6Tbx`k zj_62;xWO;tFYK$D#4_fYO}~cg)IhErj;dh?o* zM)>PhGk!4akr4W7tfneEMq>e@${0e_Z%^)&?>bYWXz-03=`C9cg_f7rA~?^Hr70_R z4$hQAmxCaiKW2kFIxXV`G9eAF?|6TX;|Eal5bo+4I9V-*Y5~JITDcIs;68jK9`4)bNoL}GK zId1K7@fon5BLvxYN98ew0UNmhwTMc?+C z*&{_Jzswy=z{$6#f#E}p9+tlZcXqc&wxonhM`WF}>x z^w)wP-$ra;Pi5pvBc{RpV&x%?1O8?bHz&w(<={(+6y zXhdii5Db8E7;xd<_`dEEwcGC(2O>$QL-J_BWElzKhr8S~e&AtWqto%yRs{wFAg_X$ z3|z0s0UI}s8?x9$U`fJZeDr~fbtrCwu9GZKa$1PzMsOG5MK`D&h%~$`*a$c;ZbZkk zjxnBIB6QjN)Q_beIK!Q3D5ggN)GXuJsW=oaMhgTuA!{*GCA=yXbTYs4R5n}Ho}&}h z$59~Q0t<%fw6qLn_6jSbM$G@t9M~-#q+&d4O7#o46n;`j!y@+Okw1igXOqhIKM0n> z14N4sLPrL)LbObK88QAkO$nop4B91tgJ}ei+XeU%!vJ?cH$Wl%Uww-!U&e`aaO^d2YwJb5_8#2imJV29Lm$p!o(;Triy!0g z1)T#h5&Jo{z@kZ@RE^o;L!7PPe(in|9t@JTAzJPtUj#g-3uYf{izDi~sPGx~mCWqC zdD9J69`8NnE~SN3Q>0Ga7s8m=v7=8N9pUo~YA6AbbGxnkF7tykXgF=rP8dR@S zei)(FD$W?uo0c^Kg@oahL>>~+vSZi~ku|=6xW^i0cT?7+7;?si>%6R~@ zeL%cm_Jlive@lJv`#{x>8YVBrzf{@|ZJYN5JqPuY_<=d@a&)1dDWdfTJMP-P5WmoU znBIPieGcH2f0YZWpCS3d){cPAd{yuMe1Y78`2z8WGgSU&=t?~!`i9&Z@P3iF(Y$$M z-S0&f^ko`W{KOuCdMSN?=n;_pyz{Bu`>4+*GT;`x0O{=|+=A|6e4O!!?kyq$-@?D~ zsSjOF^e^95t@^w0c&JB~N`rXJDt5EX>(3ci;8toPF|H#Giy$k2?L**~jYxK_F1 zIv@DJx(#>S<@Z7M(EM=hyS6mgp7iadtTMa}zux(m|IHNWm-%<&H|XmO9D?A9#Fkd`NO`nCf*1(CASM4XVXqpA5grIx?>UNK)gWtqoygf zdkYBOI8VUba7o{Vb79t4l5)|&T-H{`wj_8~ied$<1f z5p+NJIYxJT`SmGVj?{5K{>r9})pIZFyYlG&)>fU={Pd8=`$i=DJ^t5!bIRS*FYqC6 z{Hy-Y_sH*=^_~TuyBu5`-5o?4MyHT(tS#f2k+aBpr1V{+v&9u}wG+jg5q@f9A>%iP z3uw^;>>XSf%u%`;(?E))6JKoaFmRr*bdyNKs<|v2?-;rbBcYtkBs^rYP>C>5ijguU z(@;o;AVpJAYOx5EX(absaRwDDvk0a9W^HbW6@>=L(`6VW@N~|>kYEb ztSMi1`J#ybb0p6{`&X7S%?7@52tt};&0b1`ctVO8fs-gnkXd2m2s@So(D{agRCZXM z74%Z{EUjs+Z7*>zb=h1kE-u+utV6)|L|p%U!pJUkNb$6J0*`N>fuX0vMIGe?sKl4h zP)7wb#Jj;T)Rr%=E_={IxlrTsjwn!S_Wybx_utRrXCm7DHZnR zwK{fsn-N)FuPtrugabpvvO0QllO9o4rzK?tz=oz~URVcC9G}16r)7ml|EcV3)YO%f zwY2qi!NP-rqoVuCw!AhnesuPBmzo(CRa()}prIonV`1avjEM^gOG(Siy0khxc6j=F zo01V4SyJ9!udXdFZEo%9gn`;bnX1%%B)zN zDMLkwtIS4uo!(l+tDB0lK)U@1OW7zDSRK&+-@aqGoe|03e-@QZ$003gXwXApTdBB7 z@EAYr>E+Q7xK-6$V9h&WZQK~g1_DFrof8XAdJAPOaLWMEvI9lRcK`l4Pn;u%%vcKL zKzsn?+aP217vn(NaS_V7OHnm<)v-6i=F!5&vmndxYmuPAnuUpCa+`&Ua8jE=i6Sx|fQvFRA3%x{ z5+6W_2oN7YiGmP=Gh1PCB7^=w?&}!n?CcZ_3He;!o99@IYEf`4!txEq9vNLBFSTH4 zt&2%%jrCi18)vXd_{cZi5uYl;ydOe@}8lqAx%ah zB{RQXZEk!X-O9Qch5gM?lqJvAVIG3a%El@pA$5YQ@7MnF8s@6{HQT9q?E1z1IQs)( z4*K8xaB3ExVGh=Q7V|P8(q zCCv$e3jpU1AVRvup1A?7y@9lT32+@s2)rg9u9_U4Z<^glp-Hhy`FtbD~%VfVBdCCbfq{r4OhP?n~$H zLKqzV+B)JFI8g1`u`RtNGyRa!sl!iQhSdp5sZ-IIrq#lzbZD-w;b^j0T1%`-`qj9_ z*%!6~jc1!=*D=-d+Om?mZli0NW#37$Zt&Ag@}exKsnKn&W=+1+7U?3gyKebudj2wc zSUz|2-a}aNMvUq5u&Yq>5!V2C?)`5)=(4NG{V{iI2I?I^w2$UfF>~kHGn)1$V|b3| zTY3Gk*|V+6w;2CQBY2GUS+RZd^Q({O-NpaV@iW-}4nDqA5dqFXnU5$)gVxL3%FT1QulTdV%8eNl&ZL>jEllpo})#lot zV3|3TlCv^}ljP}>Y{rwsnv)KPla{NKT*8yzgy$K#=OqTG&0Dnc*N@7lZ^h|7)S}zy zSV0g-u-YO^2@D6&JfbxTKM!C$MSBtiD}<=AVZ|{J#S5dp=yw%|3uS(c(%&-a?&a-& zlYJ9qIDY4bB1cn6jEh%@*+{Z}(+z6*$Q3)1(};Y`?>v76$9HH;6UPj2`<10)xIuLL zi3df5VKhNch1!$O$w&<`>1cD8!C^6Ug#d|B?3zW|Y8LM?0u{F2a^GS4Q}~VDEdDs^4zGU*+$)6N zYLr<~q@b(|^Z;UE6+_PbwZ?eeGaS*)2O&ezcpwNe5M$3}*L@m(x>A^c6fE$t@fs*Z zFgG-}BSvHGwk{J&HaFBSRIN9E%9*^DC%+erEq!<`r>;YA*^71$#$S?3_10aQmBirU zzMaM8_5AgovYLN3=9@d3Q>;lDO-1_D6;E|&{o#I^w#u~sD`A})Jw*70@3&9MDt2QX z1R^M%*W~GOngm0%CsC4J^Z>hjWWu?~eK#6bU6O$O)v zOBFRf#v+E=oTHlGdYAf?Wf?EnVz5S} zxpLO#oU^XTe4)j(vF7qIt7M4EP+9MlmTtp*5hX3<9rpX=2ZpL;=~Z2$ zcX(oTiG|Bo1EZ#5D@z6hfXAucv{6$@*UQ_2)!SpD*rI5&uB`aF2lCXpb{2cN<{Z*& zPCBEwcJb7V@Gl?xy&L8Atwq%yPr4p=0UJ0;d+Uwokea>3Cqsk3_JW=!JcU%4)2M^% zr%a(YX7S#R;Bgp%P;u$ zgf{5cJ_~Iz?(f&trvLiQge(4A1c>-0-fFBWJ|P}^*)($0>#XTd=TKEY4T$wy9;WIQ z9K38Cx%rkmzIi8dOi>djhr zahN8(9ORFrzh~68=J3jSroOJn4feN!3gy?eKkf$Jv@Us64<9LjTx)jpBpUL#D-wSUJI<$px$MbFbXd&sIFDuDouA;86yRVw(G)e)%&02EfBY zaV>>|TkTK}` z;dD}X6uc1Y$|2w#F%|{@aHP2iTZ@8kz*>+kpiP0YWp7dKD7B?IeV~nuX!#a_;B^xf ze~@^s2Zl?@x9~*iV4vnp5tSg4FMCk(g|e|!Ie<0hOx`oOP=RQ!ekB-{dj4|~rDX?I zpx~F`90Yd^YtsqvV-MRP-`9UmojY{diZ$T2&kda<&=0537J#A7dJ4sv58VvK$~Zc# zkh}xc0gw9233^7^k$HJofKOzQr0}m&0Fo7lnk%H+u7C}KsZTvB)-3_Y$+JURa};D% ze=L6nRJ4+iwk@ z^1w#E7V>8%`w82mu5a$<-b(g%LDJ@4>YKFpWnTPdukufQ_o}bBg|9mL>o;4qhe=rh zEVRNuy^&9^la^!NM?{O26iw#GAxpz)3NQ?kEJ0mYYb*sBW_%;tISHyL#&FA|CMeAj z+D)+|Ypcq$k-*3V@s)w*2rV%N6HE;b2X)h_avPKu00SweGF%0!Mhcm?CaKwf3(?rW zt865NOK-8Jp5pK%(M6R3qKF^QUi67Sz$8?5F0*jm#9I^Hc}dWr(|g|^C>`!f9o{Si zj&pOLPT!~3RdP8casB$sb&qSP z|JnUL=JR<62?7Iw0G3DMr?SoYNK9OiZ!BcP@aU_G1d0la3t7CRBKW^#m;aG_|Cj9j zKeGS-le^2G-#j)kz{%0s;k#5U%e(2IrKqj26Le%R^@W8*ga-o_bZo9+|8F(kn#c)C zv&_WJscW4<0U<$QAmz;qTmQLTs=DN<=0CZeU7ZzOUH^ZGDoEuHmt=+C=Y${BFT{+! zGK~=A0+}2h9*(7WbtSCKGo0qM!H!J0Bb+A>#;XR}FGP$D4GH$Is2;H%Gd3eKBQ`u_ zaK8~VGJJ?XV8$0^W7e}XYvu_D>EgBb4 z-1tiZgGbCrl8tiGkUJ<484}P+JjsSH5**~WF$vcghq|;AgrR8|>KR3XraRi}| z@bAI!NJp~(1Fi%`32)56-s5AOKo5ki;~0O5;YdrJ4E_rF;1&Wc^2nwXw4o-3q|#A6 zko-?Aa(?*zLeOAss$=n@R!Xz+Sxg6#Ft&jHsDJL$k|=c8VJ~8S*~IbGVK>O6f^qc_ z7{P6MVA2_sbm zZGpztPmJEaPC;o5JX{M(1iaAN0wyjF;Fa{7={Wy7^;Z^4-Wm@YEj~O}mIIYUTz8zE?YEEdsyB=W40H z70!C8A!7PhSQ9awIB()St6(R3X0vp}A9}|te8&oT_*j}7wrTHVnSYN++amhPFrian z<0UMzi5@pT!K|f!gDg+F;v|+)no&7j9d}|JVE@hK++odH`O<%A`61HEP8!T89$j!N zIhdXFL!1dOwTql7+p|O4&2X+LR^(LeGzcKM2;2Mvx}33IAy66XPR&uY*&te}u`8Ao zI0LT$Pj5kGeB9Il3*8X$xd;t ziI=$fHe!6wYdvxch?Dd?W0O{4EE!el9JJAi?A! zB>#qNdZ(1Rz3g6QK7)Ta`{7FS-O1Roydv*wW!3&py_+3W3kItF-0#Y>uXyW5^Rbky z5#@^Y%{+E$MWsw{xL*@`p;^PxQ#+RG(b}4nigH1%+r}DVD&l!<#aq%_IIxOdD*44gRitV|$G?@!{-li?b(L-wS zF33RqaKXQR4I53C{qcS2E)?@AVq-Dtm>(r08reC0-m4(NYL`%I9r8g5O{H@sSgzu#}WubmmM(XR2m-;c< z_V7SRnsh@(D#k;(Qktb83w8T@}rBi z^1idPCe~l!3!a8xcY6?ZKQBGb+B}}bTst9iv%aAbm3=(qkkqFl?j7!!!*J?5qBIRBK?#DrAoc8@ z-WsUrkUH?GeIt*Oqh*+bly1*ZTNTp-m*TzQ(iszj7p@};Eiw8uO$7awIC`6Q)$%c> zrybdeF8aVV6AE*5oF!_(@br_s+S<`DVqP-G81}z3ejhtg zuTP=}!wwKJdL+L`^1JkO8y0ML_oa#O;m2*r@S@s(7z_!FJ3{L(tY#E^LmuLaAzQQb zf}$tBo^wx`JtWMD8H6}o&c7)l<0GMpCBC`(qc58v7A&vFzz6W~{_Hdg4P6~uZ zXd*-5x!U6J!Inq11^6cb<8Khf-cupukqb}sr3%;&BuX0}0+e(1UHdwUcIj@;9Nl%9 zs)|eK3(jn)~yb zyPGAP|g(;Cje_+H|;y8V*&X4U^lN{QZS~Z%}H1Q!6E3a>Fq&LjRA2CUJ z+jAZ-Y3`FaOLvUeZ??pRMYpxNoS@{K30I*J{aEWU2jTgKd+)n@BQQz8p_I>;YUq0L zWby4*84EQWE}8u_m9Nu;BhV)CkNix=-Fj7+wb z)}x`^)Lkt>GK}b9ySL?NYPo-=(`V2;--QEv)DHygw=z4IH3It$<>)mD=4y%LSNZMZ zj%-0|JU1(CSWYb_9k|Z&CNu4ue+Rabc4TTK#c*qRgq&7+8L^)gqm1J>m=N6UhGNgD z=4wqNMRF4~AM-yn{4*`Hgm^Lq3Oeq4NwF8ROOgFe^uiFG9blRtz>>J~_g*RUTJ+{4 zYk%dkp_K^pyGvX-_NS2(XCUDF!jM5dH}mIcyoEc8=M3gjDueXai|_X-UxWW6pk53o zow|0j<#>j#wzLWs_@HomW-{|@`mR(~dP!#PM{Fv=CH97gJTK31p#JSK)l7b)iJM~M zJKmcQGeuYHW^Ah#K>K>+-mJ?4mp`w(4)<@f52HS!BKM@U6^Q&J)c6qaOM1$DITwg|IXAsQU&Z*C&hBl7g-Gt7z$UMv( zAl@?83z*`m)}rUJ;i+Pr z5j(15J_!xzTg$}It3)G>%rfwTN-ppdq8TB z5wH4B9Y@wKICoDwG<;XqJ%nLbW+>E_QX&%JB4MDJ=aFLlZG=GnAswTitre?BsJ-yF z(-<@DorD1r*-MMD46!R?bJm$Q3ionidpE6Hq*v0>jV!e5KOLE&Z4OJvlsC0)AMTL7 zqXrS#B)<(xEC|mee3fO9(w45Nw73Wvq(rfAHsvMqLBpRdVG`~NJqT5^hMv}7O(2K| zQ(M&%WbXv)&I1YAgwb>m1mvUQkyRy4Io)W%???e{uq2_xqxr)m<<0o@=jyS#mQrz> z*q7V$@~N)P+-SGUjhPmLi9>Cd^zbNn{nFZzdP^tJvbl5P;oh%GTQBA6T!u#nUgpXY zIDd;L?kID#AI*Qs865!7z$LsA8*Qf}TN8N3KU;si;ow?H`NY+w+w~FS7yR8GJQS$P zvUagFd?E2S)L#dm&8$c%?!!QkPC&=?;lFEOKsU!5mD(U4IEX-_C6))2jxkvE;Vge> z;iSn$Rq#SoaSV^e5-9;-X88SQK!b)B#RqX|4f{DkhlrB6S8_T~i6_o#(>GjEr2J~P zkzS7s-ub#Tr$GwlZmDZEWt6$vuI+5;zUBi7*TaXnPlHaWwIU+jF2^opABZ|?{#}Jf zUUP^LwFvS^n0g2}TUhzvI|TWCv^tyn z35cQa>m4$K;J<)fuFYW2RON>h`%ikl@XtUqiN~~9vlY(1Sbxuv)s|NeeX`*t@90~78rr$*Egq^63=eY%h9Pz6DGp} z3)Mb#6TddIDL>!Q7vCe#al0Gr(W|#k@Lwn-lTBE|EW}+DO>4)cr837J?3zHb1Gv1u; zPX)6Jyf%s&npTOU&*x?Yjvh%3{%3DCzUmP~xpo2$t~P!WtLi8bBv}Nu=`B0%i0nQP zK>S&|?tpi5QA6Y+0}rdlf34(o5aWD2`df7smawn`iO4iNMuVw`g+K4)Q&x}TA&zD;($zO`~gtI;{@ zD^@`Pwrh;C3H4_>Q%c$k?;UcEpNhsx_m-AcGWwa|)VxavZL60M`n*G-Lm~C4OQ>3l zXy9s*dVnN5b9C69&sRf_4d?i?x?pl}Lcy2nk7BWTLOOcvrzZWmX=`dK#{Jhiz=tFF5%UNcuim};#8 z{wC|_xI?j5?=)`eI+By-XJtnu*OfKvDbQ-u0twjrM&Rd@N#+mgd4 zE^Mqy=6Od+AP5NQXXI!mBs5N8xi; z8nlk6ArXP4_q{R4w|9Jh$V}Zk*kS)Mc-jx~>ewrNp~Q)LXVA*8k6kSSqwzWwUpC_r!0H?zxOqZFhHk6X>(; zGT+Nda>lttYS+E0ppllROW)jGO6X>~K>UPz4OH{0;YMn^#P!MZ8%HOGaW&OMEa*)= zk}C8L<6Yb^rL%}BmV?Tkl=*_2t6jm&9+Ig1{99#M7{=_^rVqu>WlenFGV4Dtj&!U^ zK6O^^ep}}DGzQ5|VJxm2h|N?~a<0$|zuD9b)?6CKx0nVt-8NFu^f!FTN^76OEbeN} zZAD~!7l~|Ezk)tUdyG~W5#YjstzxfVL1xh-#%9}`D7Ef|;<|>=NjaOzjJ0qX6;|g; z(bSAmya&wVnQuKR7(w;neR_i#U-mwoF!SSf2ZkenP0Zrd!Qd z-PzvC?2{q!n5lgDI}(7gA?k{(@mC6w0SgPQJDd`Igs#b6uOZBZ^8LJRTi%|<<|0qLS&zl>7ztDG@k$%nAc?iiboYZ*F z{>GzUdmD3JV zgFp`NMR73EvwRq{vk7+P|3-9(^3~akWFEf7G8bj&w~95cv|;B0YI}OR?2ec2ZXsBR zuQfj^TwDxFnaS_(T^7{30jXcxb#9#SY-D-s*kD}xvee5pHQf}GyTVFVy=he2Yn@GhV|A8O=;-+&#R;(-<$aC>|HsoX zJ5)Cuy*J1|lz?fteyuyzEOq{ZB+cmA@w3So-3$9Im+Jvkpf$Al*}3q~JM-6%4IB2( zp{o=V_}w8!^lkUs-nA~?@-2i1hVLT3J7T_!ilErOvLs-?PUu^;-UcKg3wpcK2YmBsRe1-+HE(BeCC8cSb;HFKTQrd5mB_Myc6Dd@ zKyJq-V5(mJ_@#-H^rovTy0?9lGo?Yhq_B||nSZ)H1!ZZ1NpDjJXtzSc?q}Q3s$|iR zNMdi2%HqN-(2drZACKNg0zx*7yF_H;`2l$e3xT}Qyvo~Gp$LY&6ue4{C*)=ERSdi! zFCDM4`YI;goy$P>x0<-9=I$8Sn%`vdsS31WWooWbL=nS2Q)?#bFXpN8OC<&(jWjb? zInxQp$ZIgp3lA_=*Cc-XHc$U_Fm64$zHYQ(=Df%5@@<|*;%4$7q;n9}C?d(l+o&cT&-%Q#HS03S9+8x33Wv#%ge%b#j&SqS?iuU;pv2ub(7l zVXgZ|I;Wy(_2U{C&Y&5r3^)eex=g|DQi4*0y`P=(K=r8Fm^UGt16)A~PJhVXZCTxs zC(hXZO<3w1wC`atz*-bSc`7hOCa+sWVj6sNKg~ye_93x#BcWda`T*$Kd3lm6{fZmJ z@)vf9)A*Isuixt7DJjXsw{0U2_2LI>Y&0TH3HE(U1Pp0;E>lU^+cXS`ihd8cwrY0bIyYbMSAh^6F^O6mKox*ewz5 z$Y6zmE=;|ak~hv|r)V9$`0*E){kKUw>}t#rNSM7UBXb9hWorMJBh;Cr)b zZt&QQjJ@J9$yRSy#}hbgjU=9oM8fv()y@vgpQM>wWES_(TeKcLJQ@F_^pWm7Jow@5 z>na^(56}+gPLT3Xfeb#G>408dqobUhJ5ezC~G10@-pPzaRIm0e|wYUGs~7LF5-p z*Mf@YVwseGTA4+ZJ92G=`zXZD>W!@b$VtvlNj6fH2!1|%11%N(`To&$`uljMxymQ{ z2#TZ%ztmG&6^}%(`)3?v=Q$S)N{kN#4973rLwR?LcTP)FLhh*`8;sE zO$zR2Jn~mQ==yy=V(j4G`jQ{eKsLJSp*$(BDXbZFG+A1k@fHKSt-DB~X0VdXVQO{cx;nAUda*q2D-dC0imJs>gd>@Q0#PN6T`UrV1= zgj6mxLT()maU4&@GV6WTkH`?b-U{2^epIPL;tfMB2hz3P-Zb_#z}OIEwdgFdl_L}Y^SbSVJ5TJ}pX*-^>{)n}) zl~hhGmoJdm*`?!rtpzc%jz~M5xck7Xi5iO0WlF$I=g2HpUbcd(Yd~S#vPv zFwLWY9ZAAlD2ml=mi~>2HVs_)X1kAB-REAGBEW4s?qk(oCSPOn*jqazt|F_)1;yFD^xvXftAYIZd9jAZhKeV`=oB_q^72JsjUieo}K&pMdUW_ z8|_+$+(qvTd(|hIr*gUw)mclG){nx{_fwKFZ`S_C=H31_{hga#ucPTZ-hd@ua3kbC z5&hE*)>d3i`!RVml{pwN-I*$?wVmSh8GJbIeiWy}EXdNUsb}H^u~rPlEf)?S_AN*@)&&X~ zxWDC~eRNVpN3VF-U9=lagZ+JQwi7?&vU|j=uH?Lyia6Ia@JI##-blNjH0{V=X&hZ_ z&#n0DV+erO7W(I@;l%DAE90GtiZuG{zHtP{st!c}tS;3Gv&#zJ#7mIi-H|q-^t5|v zXjbA%4}`VzzDFrf9xKTOJunB8ROb6B!-Z zs{8g~`zdd4M{#I+o+HRj5ZDGjnE3rbub3r}l|P@O&3SyB4ou^Bijx)-G%7>j?JSrSe~F1^j6m((S{rTNvL=Fn!v4Os^Oey7VSH z?d&yZ^G-NtKn-nJqnB8?pC2D$XfYA$Xj)iVobv_LR-H$k$7G6-*iGaRo~5MIR_X`q#;RBuRNM19_AefnJo#!C z&&Spg_|SB1u6_|PIOHq#z&Wf#XR-AcII|isQdSr4k6v|M=D+qEy79@`H3`Vk(k^k7 zZ_pLDTl5us_?6lb@R!^a6yav@q*$ySMR%#rHkxn_{w*z#_Mq!nKqDgEP?`P3WtWx% z66`jF(nJF>x*RSM-A(MObmiSIi9Pd)6>6U1Y@SgCsT8j(*oIg}9o3#>jol;18x7mt z@DDt={I0+mnlrYLT_AL~7Dzt+?%yew2g~MY<8>D(<#Fp-XYjk6*$q1<_Gd0RKrBA| z`gO6$uC{2mug|c{M$4fDZdWEO&GlSa9VtTZw zUQd3b6YTpD3F zI*~4oDCN`G%>9vYM!cQsQS-mXgps0tHGif5rrV*l3Qek>aXI!9+7SK){(ZktPW*Ac z?4bMrsS>OHvn=&bdWO$O(0-JtXBp4VbQ987BoRSnE}nR+#TRPNI!h2MJ}z!q&LbgE zT#!ldsgsi6ZKv&>Rq2)BpxR#-6KMO_jqZ}}iiC0(Nt_dns2N6?k6#*8Id+Kq=H0J( zf-E*V168UrdqY*pq;Sn7_p?`nNeUTJBPXu+Utk1=l}p+7QMK#wjKTm+rx&xAE}O!! zv9d^x$6aL`ng~BR-1H2*w49x~w64^Y?gCZTymlLl%qA=jUnor#=jg9A^WmsPEiH8= zIYtQ>`oBL3AFkmv!JC!dMyHWoFAsU&X`;P6D8o5Tg%;=CuNW@|R9g{qdw%oPI#W+d zzF#N&*}cr;0Qs#C-zC%r#HjbQr*0Nr@QEfce1G}U+!c4Xv}KvIHUJ;g5ct3la`S~u zJ&^okujw)}(PP%suHUy6XS}gXD{Rw$-^v@l+Q7~;!^__}*>t#8N{|g)?I`S-O9<9= z?K$-lvkoANhDLCa%}qyAnzC6K>AU{Pdq6T8RZAC9D}1pZ3T-8tD3gKMYiE4g_@5VJ z+_{Ltoj={vPDtK?rO!~$&V9!jYwjzolYx^#J`xrii~dC+Y9IY8JstC-bP=t4t#$SY zB3LHNBsl7gQ5D-#vz>p(z3B>sRet_A(^LG{Kqnh$*0B#&zk-84cFWr_=o2$+?=sD& zn|~_}fcY?Ue!a}TziiN7{Y>=~{wmK>;!`YueEWqP9sZ3EWMbKPQl_BX?T#)j}$u;zSg?6 zKS)-)Q9#3v{H|$`04DH|x=P*!&68QvWmOHycm{MopE>uS*)u<;JO*C8*5%;}L)5PM zN0ON!YJ4AE{_rmkEOcqHI#$yC2F;ZFt_J+*kLG*T<#Zwd;=YUV`IPW4U~b-QhfMSi z#kH>7f2(v!6((eBP8!YSm{bl<3S~X}p=`=?_cjylwY13A_=jO?Az`*DUsFlmP?h<& zYpz{l(VrbNkxIDRgnQ=dg0?$8nJ1Yv-tGe z9=JR9q_K3$J~f+McgnzP@~wBIv4M3@dd~@TKJOd83zTdl?wRj$lvD>F+O5iS+y=Kr z`3Yh#4?ki$Nn|~X8z;!A^pXpDBdu+XQK)vI*K9kiN6YCHpqrxq3^A$e3_V4NeGUl< zn#x2=*j8JQv;!83Rl?F?JDg_`zpoDkO5i9O1%k1&?|m?@1PyVsM3)k#3!yDY-^fN^816A{k0r`=mk2{UlV)gjhK zOXAOhWJ`gQ-4eU*3FMJMQsN(roLh8r9whiY#r|r9e`eJmnfeq?L-AE&Td)>uC|?4X z+_0xkTz-vXmfw(zjKplKA@p^0nCl(5L>yE3@Foy}xh`@2%dpKh6OD2g-G1)lfK93H zrdwMjROI=bAmq2w{F9QhvJyD1?lqFO35@JWL%}52S1a4gCMo_*mhJh5ymcC_nu7my zED96eYLFM<{vFY4hQ9U_8bVHZP0n!D+fBm1x%0Ymr zV+P=@L_k2nhHX^Sr#aQ|JydIdpp2dXxDUx%bU*!?t!@rNb61DxYZf_jhft0=?AP~& zBJDYTmkzYZ(K6qnV5)DqS8^QXcJW*0zJXQqqBSn_L@@e7@z$)MLqs3R9!KalUFIw=^5m)ic^NHgSzLWJn`P~gzS+Ht2}oeAN!?xq zs2iBQ;Pi#jhRZAX{&Wg(8lF!XUViJ2o`#0p_zKmY?uuwM zd)Ect@JKj6`3^5jc?zAgKLy>StktBs`RC0qQCO6h{&v3ONS>FFcYAS!F>k&g6~6*K zz2Zb`yJTybVvfKu?o;e|Eq+tD8~P?$+PL8w%_EynbaQkvB- zraqP@fYR*zf!`qBYxh%Lzwar;8Owp?;uL!f8A!g(Q!!P6{y_~+kng5TmwL3SujPJ$ z^1tBLJFgh&2S8zqvB)Z6h8o5 zyrytJQv7pidH|ya8wI2O7<*EPav5riJSB4#j}k0ysWkwCno%Q)n@{wx;HCJlMswS-KOGKAkp+2^@J$ELgQ zW%dpQ1@6omr-Zsaho)ajsU?QQq#-)5?T;!?c~MZ(?0XTMe?0%bPCbChx883NZ8edX zVfrBYp!l34|F1&#U!{tpRgl8JAMH-CS>=v4W9iU<%Pe8He_9;-oF^VYa&nkFcB&i& zsfg5$cX7`tFOcY5V0@m~>%OX)t*I4nv%6wRR+iw$3i?(nk%#Ox_bZ{;DVzj=MixAH?A0Nosb5a?+G1~&6&gE9@3_EqRd z5KO9=wE#Cc?M06udag(QxgYWDnBB&nIl&o4d{=-Mr?uRt5cd7JZi=6975OAHm0I7e zlIGV`q}`w5B(rd^n)m@`6c9R$q2$8_8eWZG>w_C;JMByko5>kE?O^gdg}CfoC~!cp zBHE+MU3kf;m;{6NSv|ILUJ}YpLte6TdrH&KEo2gnIot}M= zv2tY$T9=o@pgmIun?08(;q8x?>sJIUWWr{R+}BIO#;tW(2}~XFmO?6;HM>9a!B+O? z=2E-PLuwz0hwI z`{SwaY_E;kc1-8f+}2CW9~~AT$se@{2j9AXhf4(xJiY$qywp3ws*d@;n`@W{ebcBV zC&8^8LP0)V`J*#yG&KJowN9$X&*9ZW^nX47X}(@)N{m~H`d{yV`(?Z~dl2Jp0V^U2 z`eX&1=n2GV7LAec)88}7)I?o8?HF>mL1D-wSqhQ(^nL=LYboE7(8YEq$2M!=r$p9k z*jmucba8;=`FoWt`KP=m^(MV1b3}kIJcpZM$EQr^rBm4gr>GP{tw9HQJh1g^_t~kt zJh%u>W;_ve_=RKX^6;g*K>YAJhvY?l@z9Fva#u{hwe=-&sD;iLbUZ|defl;T-N3;POwqWavOT*{BoS}bO&$h-l(|^^}lU&yLEa!-EVz& zy&uE6`wqiLBj5->HiIDl5r3Yb1q|qmWH<2LfvK(7BSrSa1Hsaj*pO*Q z&={x~=!*c~bl?<6Fbu#HqTSdS27iU$ayW*&zV2duI54>;0IUwFN)n6%Px?UlfwP3X ziZuxOGVK@*uIC)^3h!P35g|JQm^}fmXx4wX7EJCiE|WtY^E9#bR9-xe20g}GoVviY zB0F4yJY2Y_0EWT=*dT2TfIC1RV-L1x90Zg&Ls@}_HVuQGC!jG$E_!>NBSnar-cPrf zM|v{C<`|f18#B%|d z#N6B0U5-MN;`jEm+2{$*N!-#Y7cj=4%fa>K>?hzRZnHpK2rbZuuEM)aU{dPRWa1>I zOu~!UZYAeS(I~QO`R6B|#5&{arU3zbh{l;y9Y5YLJ^2Rd4UdV1}#7aZ531&J!#9$Fe>fUCAE)SZmO!j#~d9 zo|&1o_MXOB-*Y)BU*ft9|EVH8xuN2e75ssBs(qG&KX+OG6R13SRF)b-O`+XJd7s?C z;l_F`osiCew{Gs!GP%vqD#zLn)=)LG;=<&R1IT(5Jj>tFho zGTOa&#-Av9T3k;Di&|A6)!V_1I*T%%78LfbcG^#H)7P_RNsC)7CtXsvF|uyST~-E` z-hUd}YIR``G?wmI*IPM)E5f~N{!oh(q$vGSq{owt@SDRBpLZdVqR#iSsSVsR9uhOvSnUVLvO}U zY5k*XobM2}H}hZv1J?!5-nNPiP`2GVUj&L2l*ii=0uK(|q7F(RY!>M&d%9N<8$1nf zp2255c@sVlS@MRrR=Y=>a)m$!kb#T-N?>s>naqVd3JN|lPW5cTHul#eHu-Lcz-?2gWNCi* zrzVx|4rDBpeP8wrmr*gENF7k|4RZ8Ik|BR3B_8PWkl0qbhuT-X8dWJ=VMd)40)#Ed zU`G`4(}^eiOm&Rut*M02JD8F`9*p_Nb;z4tc92s2%fkCuY2ucZgXcEcp_yD%{9HFP zv;C>XP+Mv+PKxfRIFERlsv1^tArL<>Xu6(Ux58P8?NjEdpphAx0 zfjWUSHzj#OSq~9Tr8H*~0@>yQZroCvvCP5b~rU|9m(I&ryHdc3{;o5emHh z-Mm%D|7U?1-6%v8XSI&3@R2h?%cgYjRQv-J=<#b&$B_b z{zN{ux!k}r5i3w~T@0t0zd(f2vaSa{!vhN&teB^q@{2TPI9lT~8{8YtVP4A>`OLq*^{B=s? z{W})_3bN(U9uH_o53=bFEM71wf!P?R1(FZ>wbWsjP6rV?|E3&C@K*xndHhY(+k$bq z{1uJxJ=EvQfgKd(#Q#=;dSfBrQlDvpn5e2(@5#6=_3*+i z>%y*UYBZn`JvT)SXHb<2AaG4L-xljb;W?;bhwC}0RmwHBBr#{x+Xgf^zXzrG0Fqq5 z%GFtN|zjQn0BtfsY7F!91OfNWU>z0*3a|JQGQWdjj$A(ye{WytZvg@2*QH^ z(;jAIs@Js6Cke&9gKUEaiaa9&iKZS8Dy>lSc3FIaruKxML)~#L*m8Fd`PkRhVLU}I zB8OgY7R_@Rd~z}FIEsQo+6!x`?n2l62Yl2_*aW9s2{Dk=P1@wQ`_tgTENSlb{s57YVVk^g)in853P{$J)NxSX^W|Qr(>_RmN9aYK2pD$f zouo)Kd5o3Yp?W4g0|oAP%^09mu9=ivKjE#HhK>uQ-?Q!r&GPOVt9&tSjsZ&sc?1WN zGxTNkb7&<%p|y+q?3N}&21}U6r5!4xu3gunOKU=)GwDaN-S*K|R!Doop&uQXRb|Kj z=B+OageTNJK%s7BLTI?m9}VJhgT5J))1b?3#uoZCUo56SBKVx*JA2or?oR~ZI=r-6 zG>L{x>l|R%G)BNW73yHv*Cw%FsnKd`G0~0z2EvV{Xx7|%wj5kRVdIC>K;w3A&b%6- z49H_pWBk+RV0WM_Of09RMM!|pXR<;$0tA7^LjiQ4(0U_lPUZ9M4@73wUZA9>1@4~O z*2Wnj2kO%_A$qViXC55+*6KC<%+SQ#RW9?dyX8op3FBs=N8;5}Ktq#Xd9j+6UMC7u@1(Q15?Tegr+#`u?5>4Cxu*%~&Vd(~8Yf;E_>gS9!<~6?5?c+%;t^nv+&1yhE|! zn*KwTETZT&Mm1?7?{q1GW3RL?0$CPnrnBkS=S6e5K`O$-wS~9BnjEPPG=nXPCB?CZ ziZ2K!&FRRofkeFN&NwHV96Tz&Mr*Tfe<^357MFrWaBMp%;4crVRCNii?{ixkO0GM3 z@r<>5Soo#Q372X4XyJMh_#7bI@B_L4cIcZ7HkMJSM*M$fpX~q25Tu`ATsgaA2apH{w zo-gs#IoGE55samK4tK!zvc5R!~jHcB`iG(At=15u~mRPHmh9Xwj=H z)D*^w40m>Ua5*wGG|%?b#;LTKTHxGq!o&T{v@r|g=AV*;B{UBG9jvZ?^BNU}!CSaj zRJ!GZbZ@xIJ_p&{aFw&6H5Em;s%9-Evdb;ld{GrXQqSMCK2a?oAH*pa2odJcutV|W z%weB9ERO5pbtqA)P}mgNZsY!{u;Dj5RLRa|>lqotI@*yy*nrZXxxg@ZW@_oNwDWbg zl1i@O7vY3azx^hy92kY`*HYBX_un~Ft}W5yoR!Ql<ikT#T85d{>n$JF^P*>Uk_~JO%BY>x zpR3U?scUe3y;Jmlb#m`*&}_KvWn?(;b(>v@aqItsnx$G$r>t;i)3JHjjrw$}9FFv| z%u2o35KW{V&}B`%(hjJ*NeGWnrdrq$c_z8)WqG75YCrOdhdR|(HLfX^c6o>>)X>}? zh&WQ9L?viKi-xj_!!~zN~61as` zHmMx)y*1=ek3i}Yevl&&%kTQ}nphQOWs=a2VkSD65_dTx8s6(S^UcagVV8Rtjy?6I zA(HrU-A9x#!#RkVwhai_(2HG{AsxC-I*Q~j(6gZQK$N?B`0qmn*0n^Yolv@;6im<|J_F$t zr|2@}Ts#(}_XRQ%0LUVx6sh!18*t{{F!?2BE!k4a`jEV`Im?}A(Erq->42Z&AaI#n zIIpTK2PP-N7#owbzK&NRP=b_vtIHaDY_S$srw7|az?}>@Bqxpbl*e${yp%_RG16U0 zOYG~c0{)?VWv$!yXJQY!v}1XCI30xhlIdrQRh_$WCOkCeiaz2uVKR2OKjq-2!bDex z{+fxGt#`AYQ?xijegEp2B|i{NKVCbw^f^wEFSbtaKJ^4|*5}0?Uj7z0Fz<*NABNA6 z&<&o>^!9Mm2<+jL4W6zz*2`B*@3uDrtK}<}I>euA9(c-Y`)PwP?ZF6fyAY{!7qx^_ zZ|EV+6aLW6H}JvG)H~9xclTuqG7kmAd0srta$nj(lP{YQ^j$xvdUh9_y{#n-(jREL z)erQv)M^bi@sT>oU2Ne-*81@;U%g3zi*``@(QsIi>AZIC@FBby#b-m&ojC!5d(63E zfS-E~%NaLKJXgB~n69W7O?Ga)uiy6XKEED?2RM{P%q??#*#GJEK=R_!9=Xdobx7E} zWSTj29+Xqx5Sqky(#t6RXKl1?UNj%f_ z>Ry5LlPdZQFb`OU@q}mytcL=Q32mgs@-Ga6vS;$zOHDPs=W$L9auWQcHWG2%xZ*om zcKL0AYYOQVV*$GLX%o!cu}KRk+=@j*wZB;TQ{!K^{=qEML5&BmtJ5~t^5RLbsoE~) z`-Jqv2C9rRdA42UK@vCF$>%H=35UTE_6t;)Lf5(c3EcjKm4j-DtDzh3oah|6jml1x zeCU}0HjnxFn}Gx6;Mc;Jd#^}B#CuiV% zD&Tz^J&Bv*{dh(kRRc8DPxfL5KJq9jR=BC*G9u(D&0g-mC_rZ?8q`s7woH+YVN>7E zTajGF`XyRQ>l^q~hikEV{C|K=6YYN$R`+$rGEgxo*U6JRg~JW1!Zi=5XN zw`%wS&6>(s{57fjjdF^A(pDGJp8DT;h5k1KM*4Iy4sP4$ECsx>8$qf6>lv4p_#_z@AwR=7?z>s@^Zz54VnQS*-gT|C z#RxGS$$YF7D@wvZcx}IIKuVLPh#CQDz#?{7=?^6sbD!SFVc^sMM#0#8@>e4u#J$2L z#vhU+G@>n@Yw}U@Vu<%l8BGKusWdwJJDb^%GifWGkT~xmZm;^W@?Q1q25xEa@64cY zrHk~pP<@jc5p&KFjbQfNK97A9r@t{~7au(o1HuIU8C-E3-_qj-suFaw>Tfn(Si#$xmuO(NH={|KrG$ zE{03GG%4i;*#ZMpnnhkC>l>;59$N>;z`~E{TyP(FW(T(eo!m2y?VX4|24+_BHn52m z@F{IuG#l%ttr?(73piPZ%LgeBoF*xUK7GMT*hy;<3EE4ryV9SiCY}YaPF2DQCS75n zfLKJKilM6bxLfRxKFL3a;Vk}^kT|4M4uto}#$;*iEFnMSv5tTerop-}W$FgE$0V*q z6M*r_d({w5?kJNc#B<^N&WR`>xaPG)W}-ba69s0;`>LEtW?UGdlB4+Qwy!=!O*%yr zDU0#P{+|6P#y8@^0(r}A$)JO}Qhp38LNX1$wT}FI-6bBHq7DAOq-~fC(U?hHt^(ph>W;UrgC~Fy z^~Ecluk3@*?&Vz8n)$K1UVXeRh8@4(<|P(9p770vU5;7HxTd5blmdho3GCE$#mc*i zfSS!PXYk?2JtrBtOFH*>hzx(r*W_7`W>TgDHanFrsQ1&*lL(lCQ5!0~>=xR@8FTN> zKKJ9h)YK~9{>be}ZVHZ23)Y2e=q80zXlw4Jd497B?7j2i&70QmA4zCgjWqnKLqc4s za8G)0mH=loJ8lkGgLBvzQ-~EgR>_C3xOb^;i)2+gcUeQgp@aAgzv)4;^)AW-JhEs%X>Vx(LSZZVGigj47C}n03dHr zLtOym{F1}c0phqCX#%L=!rXp5)KivAdCY#PKW!SCbD}(`j$kCV1oQ(%7+YrGelpiU zQup9w@Ri)b&Yocm7U3H3B`!0``T$65Zzv6Q-S@CskEd}EMi14RD^aqxt@r1U;XFqu zF`qxK$LG+H?Oa0Z0#_J)_tObzB*+9<`h*d3qGAoT^g#R|VUSlcRcjrBtP4GVKJ*YXv~ST+Iz z=%t}#=fjoc3|+){7^x^B(8nEMZx}Q5AJS+u$AqbRP@s!>wJAPzg{_3!&~<~rtt(i2Xnm$y+Hmt2#G`XYY*e*+R`8LmOLi!-t%?Vc|aNw z&nfql@8v@h;nm3rwGClvWb_vgbr1CocC*L_Zbh7=a(twLjA|Z~UJnwBiegz6{of2f zX$^B^d-?X^AYfTaZ0l>R1{LYZi8grpAQZE2$3vbf`1;#Zfwb`Z=fRpr;#@gdg0I^f zPF~?qdu30z4$!3nVE41BkX46|J3eh-Q;Wik&c`NS6}baGoacYEn9R)QlhYRGku&rB z{_(wM)TZ&@FCV#u5sJ%tM&fU}1W(3pa)3JydtIQr0il}p>h+Qu+p5YU-l-p7){7&O zLFd@1QK14Lgd3Rk7&N;(c>7q`9D|dq!*xTjX)`?ocLf;gh++ff)Q51`hbKtGb$+R@ z4^l>=3(Ax^3s+(Pj$IV-IS`Y0eOO$hZ=rp<9qwkV#j-WWK9{g!x`vYe~gY4OjbYF|6%Nw&aXeodaWthnf+ z{Ub{Q+oXEb^)q%_qwbA=v}gKTh5`&d6A`enktg6XXy?z-1MWnLtm`&$y^I@5#KY|$ z;xY$)0Z8~u_!yUky;N?C&2}I(x(xs~<0WedP(jFZInPpgIX^jjYF8MS^Qf`m+A?S^ zj_C7BZB@b-!0$F@cM@H~qebO!ZB@NH^fVB|&`()G5BFn2WOUH_Z6gJ4fjdfHdgQd? z7-TFEJ%LhVg0Y(2f=-eejdJAav0_zqoWlt}bY`-%c}0KOZ6j{(cu_;Wm5YyeMY4UR z4D*b*5bxh`VR-`^1{6AN~%KR=M<~EAwdz z(EA){6*}_}sqce&#Rd#fvp#o@bH|AphNKS4zwVP`TCW*o!&e=o>@%2o#9=5rJ+K^+ z*IIa8=Q*|dJ$#edqC2ypzbmuhpV>q0MWXfZ$M@|}$o|0jS7rtXAHB;9wd~luFEYPE zF+4nLmlvP1yXn^(#ZUI3qv+$kdoC^lXQCZvo%P>Vb5uIiFTI^d+;1Do(+iZDXpO>e z8w+H=?;jf>hdXdxweH7{v6a;~T_3pKX0Pk6XES?gha7*J?R-$?%5Zkqz`-u$_}id? zKZa`2uO(oZbNFC+1e$@3y#dG086wcC|8DN+FKcybow)wieUN@|ECCubCUygOsQqL5 zzW?1iKR>{7Nba*70_;mrmM;Nm&afI~6NNOtw=QHucg%hRcZtDcvD;1Rq1bSUM}H^M z-rAxk`)cK$Hm-DA38Ab_0&R5oX6?o*agxI(nupN2(l&t++)jrgZ#q_Vx|`W$uBvkz zx}<<<{tXr!`(TU6z0mmURPn6-?}}4I`cYz4x1_^uQzNq>l3^YZyD1X=yqjpfg|pLc znq_M1mc>A`{^!9JY$#3f-CIU+pKt!EMv&r(v4eBmEzuIJ<%j8Aln=PcmNEBV70FVD77D5Cxb zyaM0S%soLog)9EuPuZCXL7_SUa5zAD%!>tkc-Wik{N`+o8}UyZo&>l(jt0k^%Z5c6 z-<+iXRA{Rb{+9Q9&@6AMhT3@OU0h-Ia5Imttif5bP33aLO?9eIWYxbv&0((p{#->I zO?1}>Y0veZN8!eIe=LQ(@`s!W(SgY5*MH#?hoYey#9kl7y)a9ZG!S@VxR z&96_1lkFxH@zpqX*`1Rr!KcLPUd7WuRwEo~b=N$`zS5{I6g|>}d?Biu@~ROuC1>dK z(wT54P`hN-%MAS6GI%xc-eAL;&a*C+&q8{9W4WWUMry)Z`9tmcxA=;)Hb(g zZuW~$drzw+^#OT=7+7#7=gtNz?ZIXp0+8(|yRB2L_qzLf_VtBwa$CCXk`;aB&^XR2 z_+au9qKNB>>NyQ4^qNL+m<@ducW<=J_u}0KRKP3NJ(ZM$TRc-<*r(Zcnv+d3{4=PdmK9I@LD@6m4?<;=|Lu*~LHe*A_`2dLX+U0LJym zuDmey8deI%MUr%K7q;kF@xZBTxRgb&zP4JZh?YL_=;E~cq|v{XbAN+H(~_Nds@ME6 zIrPs+1D5bRfZw;*rjq!uJ1{P$)pI~ZoHsAecGqy(u(rwFd5R)Th~rl9=B_?;+4{q~O|s2fq3b;zYS&e-q~B(Q zw%oNpK+dKdhlcnXevMUBZnY}4Qf4fM9y3NEXA zR@lu!uLyZP?v`RFjeK4(R4Lav>&r7@=$v>h*1a*wKxD4Wy(#IRaBYD8I?|26%D%kc z@7zPQ6t3BrGu4(j6&GQ0+NFkQLydaM`?BDJwEhq$f)=^HTOHAL>CUhq<3tybRlEOr6cWpqP0#gcCE@TD|WIO zz}*uAqwnO$G?g5FPa7R!*)!DiEq!mCge+XVO`ORUie}@jU-+9oc*n(?i!iIi?k?`G zM1^LIfy#F3f!+;(S{ygxW5vkAZ&A#+)W>5g>S*LrPDj*uVIwZ7yakv?OlvQVlB~-&-9yJqkbhJDf$~+VZde zu%I@j&w{Q+Bz``~x`kEEQD-Ui-LUe5j)TYe5*tCLq_>iXDnF9*Q6?d5L;8Q$3%d!Dx zgdE!`BN^#U5>BLr>*d?C$6SXUG*y21SjYaF+_&iL8d|89Ah(J3?2Aduh<%>)xIgeZ z`bq8G*DP-iBF#$_2N`e&i+DgHh{DX+9V=b~|SfZeYar-|>8*a9Q?&D(9}_6^H7s+lOrs zRd00Yka)#f(1WZ5Q{tPqip?)7-C@*$DJsVHuPmE;pC)t|{{m>~yDZUuc>M3da`%fD zz)^dOpY^)?!WK>@*5+=AlTMd#YdU7QkPJ9Rs^&GNWf^e)twv$*EhP#aHMzV_Hh6whQ>)#|HdZ)*~iL?ha zqjbH7jaaAdK{ubghh<_!st-d&C9GE7W`!8fL!Xdcr;~(m>#$ILcii9$ey#gtdJyh@ zgFI6x!sv2Krr-)=At~YW_w1|M^9cr}-R><~q=Uy8HwdP@nTuHn;jf8%XbW_A@p({u zBF@(1!gB64)Ceys8PyDRZb28idgE_AiHftzoRD60N29^HYZ9r^R25oxl+~M}RzT+F zxfDT;a?1H1tG^;rn;fW78M|iQ!1yCJ0UbHBrBkXNU}Q1}@hK^{Yj7(Epc{*#mlxzeI@)Ft?fJ`3);_dLg@@+ch-k3RCldo z1b1G9TjL_Fj2?3zmk$WG~4^3zX^rg`WUp#OA5ZFk2- zcgR*FqN*`z{1RHZj`EH{D;cEOU_xA(687s^xVNjZPD$>=FzZhQjp=M$W^#Ece0s3&= zxB@EB;|(5cC!r`u1gIYAw-5}Hf#?pgXNe{OILCJ@IoWy)!Oz$qdq>@P{|^4jE&?8Zt(zzK|tF8iB=h1_f-3f)PosZ zWtzN&D!W1NRP#HNYObC04v)J1;jQ0cP~(((v%EG>r;)$DFKp3I@QI?`D^NX>fXa{U zZAdX&EBrxSc?m0&VVqC=95S=Zrhrs5orzH-yIErr_AI)MKzpJz3OgX?8u#P}Q?Y&mHe3Aw;%rRW5Z-~O4nSsuc^WKaTH8bG3Ms}Ow*GY>@e$K)PU^9@)Y-Mr zZ04V1CFm3)HJwL9<)*vPUxjRtMOkEi6o{!hP=AqB@9G@N*gX2USSDd~a`yYEkW8OU zeyKYk*wxt+Q4r(qstZ0x=E|W0*A(u;oz=lfze0jmP0{bJJj}bfPF=&?O$$ZG2*L&4 zK{mo!ruc=Q%WbQaKa$@-6iM7+8yZ@yrio5=X7{Ggtp$%o>hgL=jJG|(C$N0uKrU~( zU970SXuCw^){)F0e~BoXA}%-%7*8#tDI_lNWUWkcZZR)slc#fT>4Iw%Pl;1))zt-W zEACFKIDBIF4lK5{Y&nvj8vj*5%vOH?c1>}&@da zvYojUJmfJ(y1Bb}5AK)@COEw$-`eiD5AITJJKCTWfO=7S9Cfp-nqoiwK2f#t15u(w zX(50Vh4jMAKgjy)5Ua$RH4A}wKz93~{^?~Ok}dV*%_q3cCY_e$*hmXIxK9p+S~FG1 z&pVL~F88`x!WE7}HY3{;VOS9S1 zB(5M8SN0EZFKA;K_d_a*PkBX3Pu;ci!hE4&GX%kR@D)AaS`MEQ#TtJlYS9)mZB0uG z$6ev>fp`dY67{D@MjlH#I6LYZPLRLQ$8mz}Npl~T$8^@r_x&PK)!&?cT6{)-Eh*0iUm#a$5DuoF*N5i& z=@}ucHK8JGO@hG-t9@e0?~Q_9)NFklaBE&^{FrhCwR5Yrgao&rwP)_L8?)+lY_-T- zBp;Y+hJ1rcYugAmZQl6M+m+SVKgk#gL3K5n906?al=69nV7nR*BRV%&?k?NjsWSc( zv`u3P6frl_ySk-PycIAoMMfR_1iVtJ6JERkoiNOvoT~m6Tfv+-c!j4CtFhn6IlwG) zGv{;pWCfBjw35~8{xGRD`>xBq{5DhB_N;v7+aH1gBcUtOrA&I@MfMP13!j9vLIUE zq+nX#PgWxw)|sNs)#JzI*SfHjANPlUF%P@p6y>EJfhn}UMV<_Z!+KJzNOB36J4t+w z?!v2AO(g@UuK6jk4^MTZP&c&K!jz#8(6Yn;iWTz;kl)ucLP}-A(Co;QJ8+0l%&9r?LJG#@z6uIE!xNL9xK^$NKQE?S$T!~I{x-DXp{8Vz2dlNWL)H(9#3yN&PQHRXK+9b}xZa&)Jpx^OoksQpnUb&c z&~pAk1n8_SM;ry7o2DN=I)VL9;7fECeiJS|o>2L8VoPoTa5g(Igz;F1L_97qSm4l@ ze`PZNa4?rS)2@_nC?k{Zt-a}66Y1FcI>`ZWe0~TRpd**3&z#^o0w1y^3d9y`}aQ^tXCW)(i&tvzg-wLxW8vHxK!e0gDiiIO#T#I%LId;HF&~c1acN&4vk%z)z5A{baip z`v^$6(n*M({Wo^ot><}K)elrkzQ(KP`rer(hzm=Dzuoi{t;Bl9fqqI1dWvQF+z(tI z>tTq)#ju_M`SJaXtd_EPH2#Nl_yWl)#r((O5{ZMd9{tAxe~p$|bHw2vhsZ;m&VLHs zSvA+{gTp4UJ$QMV>yR9F3#>)Tb>wV&42?aj_TsiTw2}OnfH9Fq+mlRwiX1wl=3rwL z$fQl07^EdHamW5^PhvQUi@L*hccLgGF_hv%bD6|lQFWpzQl8^-3G<;e_^vh}TbM|K z6bi0-BMo0x7jIZrlprf(88&D3vU*fDk&JTJa6nttlk<>58Hyw{MFWV^@6#+4jwXgN zIp7zXAN-0WuC}_xvUx<=6ee?2yNz@Aw0W~7BmahZ9IH9R^jt_}JBjKjOffB6B`3C$DFRWQkZ)) zo`}j3(GYKtP31^t-z7>X*qArX%i+M?OTS-Q&rvOalTJFF$vg#eU^+R1zeT!pVQ=?$ zg8nn^7icb2z| zf+MdF*0*jDUvS|O@82}uz%r45YYC{8{aHEUvHVmkX)?0M8%kxL-=o&^l&To7%N5l< zZTextNT$0}K8yu9cQ+LBN9T;xaS=U|;hO5v^(U+B21^d?_saGj$WM`!{jn0aYEStt z(PxF$n8K=-5aK5 z)Jq&SE|^Ophmw_D%F5#dMnJ^^C*mnB#=+q7E~{MKB8dP|Tk^68JS#691S?9hypv2+ z*@D@=_i6O=8LUOO(|5J+X=n6YgfZEOs#-DYl}V&qQIkVEiK_F+p%hS>a=fn1$$2R8 zgPgRwSDpcZUFuEp_HK4xh-Z_CdXblf>NL*k?Z@e*=<@NM;YfEI)X0I}N+uzY9B0n-t4F3g49~};ti|Ub` zd7)s74=rB#y4r(rWIQXQ6S(_;z8;1zI1^TkCH_CwF%yHEN8LQQW};NqM31fQ;;4a? z;8$pc3&fSr>R69VWD2MPr1uc#C5M9a;}nyzB6uSkak~c9Kp=+%00QhC^*p$`M`3f= znfGK4rKlLu6fsYq6{!^u6JDdR``*xY;bxZM2~wV?5(;Ay7NSfWZHiF z7Gl-jz3XbkwJOB0x1)2=1Byh3wLHB<#6AR4DyySKX2z)S@08HoYs(UlUnLbDNx0(q z>pwg4I>q!uv~U4RD)_f=1u-^jZwIs5;eB?Fe9({c41^T1Xvg%BN06AQI6!-}MH#1v zWU2IX6St9%bkosQ)0y|AkomVYH-y#HgPu7qSSq92BF-8$R;?0KzM-AlLBMq}ZZmUt zVA3b)`3P4QOx(W;e>Hk~(4||nomp$eR^0e}L}2ai2H2u#yOb=FiJ&wyWmI#YiYGX- zdzecjd0;8&ot_$l-$+20hBm~V@N z1U#@)lE|F?$txmn5Rm?ES=9Yc*eu#?e$m0;Rrs@84J$LMZ?{Wje|jW| z?1{*Qa6_z&u(0PNxEI^b^@@U`2#93lL8zM5HqePc=JIFvFfNTK3#g6%&2*0;_(jpE zsv-$Ai{|Hz$DGxEvMdBN2;ssT4{WMD&atX+G!#(bk@_c+-z|#4FSx_OH1kGx`NCs^9xhAEQ z9kz!2I)~vtf01K_`_T1Oyq5=UC5HRMaK?^vU=F(k%ilpueq?}=2XKvegFS}I?ij+C zy)LR`&(q@#d7YADB04iWC+0=Fw0dxuEk`_xAX2hVL+2jL?1??QV^2T4M+%aT))UA+ z{rOm^(n_1Oj4To78o0DeJatYP67;gCb%pMGRi@XwXTkP8giyjgujsK@QV>+V76LDY zsRFVeGkyy75!DoJ4CL;)=+aAxH4$~?sNGoOYyA^@=r60Wk4$~@jY`PS6CPNz&gv}_ zhGRT?BQ-Wr9q%Q2PZ~|4#NHgahk38S-oAB{Ym**v@uNhT1!ulwi6Mw)%7(>Ukv1$fru>DBYnriuw>)+UhRlp(# z5nREDoy#)o%$#;-M{o}9e!-{-PRYiaE4*yqI^n8+cXQLGl0<&=9-QR5YAaQ;X;6uj z`xms&Udz_RDdk2P?lBve>!Du(JjuL8@;mNGZo38i5?p4IJImedx>;Fh zRW{RFZ}JF{xk!z9wolbH^iCb4iR&Z{VMO(o7)*1ZE^PjTGXoVrflBKsP4OsIcZ(Mp zANT?lOWuk-KYkdXY(JX@y(}G<5wSCKHw_D}u{Ps~QEv05yNp$CAXkNeb3<j8z73jHFCA zI9a1jN>PEnz2&V?NQ1db8xYX34KdQ=MfGeq{jPB!SDtdl1YHjIj5Utp(PVp&j*1!w zM&*WMU`=7jsYDI(O`*vwA3-3_NTI3~6wxF61 zloos+L1fW~re7vuD0Ga;<#3<-!%&*xK(KCAdGxT+Qhyze>Cz<}egWCs30J2N{UxM6 zaucifCI{$$FV{mi$(6r9p-OGfjABIdS+2dKyqwh0xc28ysA8vuTs;XAom_p}mNn&Y zeVQnq&7H+rw(*PSQ@@Jk)YT>Kj0j31ogLi_CTNS}+)FLf@65DHE{I6#&F+o@ErP_k zFgI5eKFQ4THn0C6#DZFGfiteAM6(^;xQ1ienSw2RVwAI*l35HmfMOy~&H7 zX=o60eYBt(`C-SCQBU-z65$7=Ftkx=-1o2-nAqD5D`JcG4N8`7X-;lFG+{>$^#I{A zBye;!4tzow?A3wk01F%67(QoRFwfBa9eje^omAY4N{Wz*y{O}_6_TH4oeM2+dE|m& zM;V)Pp%~;%0CSbP(nK?j<$MM2{z6~R(^KQRV|v^S`#SwB)i7oB0epUMadub~G4RBq zB%`Fjbmun`(kW2V_2QNg5vrhiB-7Lfqpe=yr2-KKv)iGTe(i1a+L(|a6;9>A=hv(s zLIp#tx`GaDORZ?+=V|9y^VBBK+Cu7t(?GsWfiG- z^6k|vWrLH@l16*;`#=+ZNWyRn4fy`$uZxy%=Of~vfpuBqjEGF^Y}WbWAG9nz&bp-W z4L7`O+x=HK?vbNW=3+xxE|BILTPo`IW)Jjs!*-Q7`O%N9XJ}{Oo7<~WV)QHR{tj9? z^j8~%T_~g3zW>{WU!J#YpDqiy7U4Xvi4D(9f4V|gmijeIyhFHwFZdzp!!2QC1)Q(z zUWYnJ;eGWzb*Hb*B8vvWc`TGQjTi}O1u?J-KqJG=0h%X0G8f!!!=Bw?bsW~lwmst-TX%0A zPJ%PM2J~M*%=$Pe!q)*LxO-@}L))@8jDjPpj!4{JMxd_mm&qSZzg?@`YI5t#n#?0V zto%yiz#V~t-}cYO+3hO6ZND>yJOJ#YbV>mFiymT69sGti)~+)6mQ4b{zCX=n{@xxt zQLd1!g#HLsevNlB+}44ap~`yb9=Q2mkY%qsjsc*Gu9+EA7{R+KeXB>ys2g4UvX|;1 z&iMk7j&~zlgvfEumx#NkPL#^J=$ff919cSV8pfCbA~Ws zFC|DdTYEFhwGYAgjoap|j*7$nS`Z|CH~-pFw8*&!H?4zicSENv4cD+hfiNL-dmtHV zh{#)K{1SP7us+W1wl!;4jDSOH@}fqA6(H4*JRAty?LJNW06y*Zj#FHn9kdr6bVXWb z#R>=TLfk0Zn>)riCdM(9#R%b;tDLpjfg%Kaq~;)U)Q1Ym1@o858H5}jxvnq zkLTpuXPf@{mgncB4g6!TL{W{J*Y^%@)Ly$|;s`{&kshHVb2rW2OJ(?#b6z?6`3 zX4gu{e9+B$a$`{SBVi0UsG3dqv^dC(vD&Ve&B!HKgb#{L;~3ei^nwA~T1Bl_r{ucy zJ`W9KJ1;wU1$G=^%!e7w@4DoYA4X9ad9SvR z=xmn#hXJHuI~e1>t(SOFs@x3o!b%>~y>!t;QUA;E1ALbkgct|tPe=$aS|<99rjOOU zRF)8Uy@{|-ozDGnqox_8_&lU}@Te*#PSvGsP~4GiU#MY0+yASO}mxb8ei+R?BN9cbnwQMak zuD7tvf!R$3wFsk~)yn2M z_SIkdn!2r<3(n$q%OBTWlCr}=?HGR1AOuR;vA8DgG6q?}6@c zls_`6tOEKEPtROG$e`LbF0DGKDXJQfxxauwGPSuYJ^bxU2mTaf$tYwR!vIghf8tS4 z9`eVg`eN3^vY%x49l}HuDoy2K!@b(AR-gJ8aP#H^SEE?~qhQsPq99*^YS_3cwNe~= z>Ms99wwhikFLxll68D6X^5oauCl{4!{}E+Pb0|HXNZ)-FSNI}D^FlK4U5UelKK1ej zFGU>ti?P=i58555G%*wZza1TXjZvd2cHj;qTy7};j@^9{SE!cq>Vh_qJVd5}UFIK0 zmrk$4M9|arSn&Cmr>U9ZMF8xV89t7VLj>M`f65qsZQS8$|(dXxD z>z5>@EprFc#1vgZX2!>aXKd7N+<_2^EYgQhw={_#s9)4NrU;^l(MKF@ugoF`Y-!eM zjCIy)&}JzUfMwGuBihj_kaGY}%^sX1$vAeEXB8`XE}z_4>5><>WicCQKo#%4=yO!M zcbkI}=3jsLrNKoc#s6yl57kj-gr|jjz7lEkdGGANm=Il<@ji(V`jOnLDTv8fs3KMl zsKTq*a&>n}JG~5UbF=+#@8)EC0HMldb7X`~5}vs55eijupuUAk7NYERI0TkbEJagD zV)6Z^`&-tAI*g%QWfXbUq%i6UJ2{=aFCBZ*2O1{ig<+SWh%7fd5ADP>4dg_kCL#Uc zS`gp!N8ipQBIy-1r)b=ubRj}3@8nYOO8O@}b>RzX@OWYAHBXclPHwNJJGa_5NMtYH zJM2Au7Hsil2l1N<6R~pvMmr?0w^E~Eo*`@4SkJM-vONW%C7cTklQOQD88syp(sy6z z>(uWDQjW|E-(`9I;Wgxh17k7`sVo}{z7j7t2$fKf0kRrKDqIwXwM-Su#K~-pDBMDlT2x)j3ep^^s@X$aPK-~xaF@Mr`S94mtam_@mA)OcZB#f{rfLL}2<& z@1{8T&WHCX60V~A)UlDD3k0@q^wx8FyVGUdB%$4>E;8xI)Nhg;GQ^BWt_Bdc zsp8rk{o0eO$ES^7~!aS6gv7&pYC#;7cU!k;QDs1xRTE)`N|Eo>Hd~*46cRrDGk(34aCg~`)!04kE7y?kc$5HXkQm%m-V-%&0~t< zZm{Iu(O$6mpQ|jJ+3segGU)MW?oBRdUf9vAdACvxonx+}IhDD1nCm>R_A$4w3%&IH zig(+&M{QW;VyJK=TZF+ccM+G3^yWj`?KlbrX@b1B9|e3@gQb2#Us~@R&BJt#?G>8I zns51=oL@6bUo-`Ul5oz%ICJACv9w$zG?dcxGJ=_9E(bwZIVjag z(tM_lgt^}fqLwBI>cHG87}1hn_>YMM*KK#ISKF6ViVcs{>HN_O(wtHSzk^>LBj}l^ zyH+K%Vtb?$f7f-@62!X@%^-4_P3r`9 zo~cWdD*XE6iy2wCfi+7&J%UXWY>N5fOBmy$Q&VGXipv;Z$JiJ%(m#obON}2$`S=Jx zf2JWOHlg;nw!imPl<9QZ-vJlUf-cu^)kPdIUi4p{{mBT_lsNtjg)9k$N_dcoOhLMT0Xi32&8BJ`U-smH^ai}h&Ien~&So8L zR}+Ccr6B~*HazJpW)s~U&{_S?DYC+|y_NQdyVG!CJBYaO>>*1hf$u)VV?XDQ-?h#x zrXzDv)>UcO%j+WH^Zue*$ZN(d0jweBviwUZ%GC(t>$cVQ2?}fp4e8fFBh3zWn5r*1 zFuk45nBdte<&P8P%Ac!1S#2}Gi|G3!jz0SvZYXtqT&gn$-Cu>3%!9{%@U8+GNi9fs z|Hkcladga#F%aHpW};F;uV_Z*R(^4KRJhyjmS^>2wv^o9tf*^;>dT{tZ{bdRTDuI& zeo2s0(|}w|mV~7vv}qJw^UAYD8V5AvZ@kencExIdrxQWOD4Bzd#j7|!-(atamDaHf zq}UH|JsQATzNxqNSK_{*BzQI+r-~NF7+ZK=AohH{vC_6xVo4#c1A((jGec>J)@OCp z{f@UY@t51clz(nb|3Z8WB$R)ivrHzW3D{w-Xjc+ZsDgHpB$11*c6UO{Gr+a|=_RJ< z*c3L~X}G*2Xc=s1y8H{bdH|b6l(xIGf^fV(@O$7m+$x!y8zo!CvDTUZKk7p&<;IXR z8hX$<+d1M|IjVW_q$?CdjWVUXP+4gFM2e!k2m#F2a6AX3D9^ruOIfjooM)jfPl`bA z#$`@5h5|7tX>6;J3VetIYk4r6at^aH4?MWuNwK?lW-H!g67qOEbSiQ7^aiy1aG@5y zG3k?wv2Xf!B;o|6PrfC%HGahF@-|@qM=`Xi7k4dqQe(q8`3cvsAez#kiv#6))Iy*Q zG=E7ds@#5R%6tnx_ots!YB;Ipxm~DqkV4$P$=hXNM$9LsKhZrXV@aTKVCK~=lr4m} z2M*4&ipK;$a(9tf-u0UK(SQ?*_Y5U4_2^RVFqVM02PWfP!eqdkpn@{#1ZldTBEb8N zeuUD65UKS>7Yj=)r>Q=CVkR9zyFk*&G6LuGNHd+zf^aT8YP{TZzkkHKkhEPR^yemY zDEB}RXPD3&cJ-{Lees~O6^}04YPXQTt6a8r z)Fs8GoyyiJMWa0>C3zz1bFy1Ckv+qj+xcS;ZAw!{e*7TFncC4tA;39sqVt2Qmory; zL6_8hN$%L~;z73O(}9qT#R1cXm@=jHDiW!;$x@OYl36GFReLHlb0ek_i9D97=^9gY z9WD0sBQ_Kx&vJ`@w(#uQ-Xc$D8cTE35Un~AhWH%+Iz&1)dhR;(BQ6vpvbo1yol-R= zCgp>EZ_~5@Yh_gndiT z4Yrq{8h!YvWSnwiikqU*)(3;nih3Ed4H53-@P^UL&FIUgxW7jJ`rmHfmFJ^wmz%kw zL+@d-^?|W@p$|fyma)_*T9NQZjz#awLv zvgtdKxhJQ9A#R6{lcVoBhsRw$CP^R_9Lxw;w^BdLriJ!?B6gV2E)8fN1PAwfz^#*l z{vWR1JRHjJjURuvND`9lgG#8Zk!7r(5)~pz2*VJPb&O@KlPr@kin4^6N@&51?2LUG zTQgaQ>;{9e&sfI#efs^b>-)#|y5{1Z``qW=&g-0Kndi9=8b|$)vRr93o^~tGg{d;H zD7Zo3jxSwoJ3Q{e?Q7RueNrHXtEuU@2a2oN>9}!Yn`0(N0efl+;RIfL>0;#AnM_^+ zM;@r)^vJb`o8zU;;X`*kjxt&9l*AJ)R0%b%ktdE^uE-bGISse*!f~yR219Qf&f|wZ zA1x}IxLb2B$&u?g4s0tAU$@tO>Ee>he2mtC7Og6cA#%Tu-=Log6Dw;gpvE{iEpqA# zP!id&#F7T80vm~Ud;5@xy*JNBvQ}F`F-`)cBLaXLT5!X%IcyZ8b<1KwHTCl;K5-hN0B5rd)COz_qNBV z&!B|HQ*~uDb=PR*=exdWLr$c_(OTJ5Nvw~2MGUH}$(#8%^_ZJ!Z@d2QB^g3cXfV!q zxSU$CI*xTjuZV)IRR zF$?@;admM?gDaFbz0xZlujC1jENr}u_c}SJzGvIqvL6HGjVo(vI!}KYb7hc=vq`!z z(O6}(Svq__0G%#(OD}6`@v6_VG3w<~p{M$9D~rDMXLGf=Z}luS#eY?)%edOb zRVCRwdpPS`Nn5zXJAd4m@0MvLX@3v$Q#0(9_|UqxR^NsSfaA@|dw>=hQI9VL^! zPX*$>W}+S#Rr6b$_kM8XOjf9rpOsp8>d56@qrmME8&|DhZPL5h^jB)^q8t|={mdj&iKABN7! zT`tyggvKQ{-#(xDicLz5(<620vfRaD)pQ4^!*eQeG0>0)nS9odEc zJm1yy3c$mZkThND*e{X?hz?=Nr@{pR`rY=KD_FXby6TQ*Ku2& zgQhpMzfcV;ZAiw)sw|D4E$H`hAkN|g;<%b`#*J~>@>z3Q94kN5x3sP0e{*O1*3oJ) z?oX`|qa}Yj3Pe6#i{C2feES}iB$>i_j-`8tYgv;k9o50- z5r!8iDlcVl^BvR-bEe#+&12?G)OYL~47c{@l78(dXe#1yXZ6c`W0+1nVwYd1F}qS+ zHY6F*#;>D~c=HCsq0RXQBc=V($p#ge2U31Pzdz+}$$Yl{^@2_cV)ueh7b2El2MSq4 zUkj;TGjZ$h3=tv|rbF`93a*7bS-XiXoQu0tS#Qq#x_*UUr($}+l4(Nim^Qygx6}r= zZr%yXCGhKvA;!&_36nOK%nR!$N#=}#DR*;bsjvP-Ct%Nur?Gdnxt|s%Y0h<@tzhL1AEhA!;;vb(x$Y-58J5zylx>m zu!6eD9!@oJw|5$nqHmmQkA~uR!y*r=l(}UajhZTJ?eQNVV;WT(=)Rywl8W;t;d<`x zIG1ny!l$*x}Vti^stw7L9i677+BP!#Hx5?E-;;CuGhJ!mq3r=_+tV_W&t|uM zwBv3?z&9+NbnkW7k5ub8)mi7pU*vq6W3!6xi7JKPvT1Ws3cH{Q0Bhg|4dfp^JQ*X zd?mk)aQp4o8Io!drx8j2Nt67gNKCmK@`{;$H-u*amc*1>xR%7c(W@f9&|05fvpcVm zcNLQ{d?oYdrN5W^iI-i(91M)lowQ9A;5UxCXnpe9t#c=V&s4O4L73RBEWXFB=5ntz z{7(OP`s(7}$Ho@P*}{Gy2d>*L%Uy(w>`S_b2%UxTiJI-Yz46R#*Olk+jpXhK*#9GDYv{!Y5v5*tHuerjH&4aCM`n3Kx(HUC@V%p<= zl_#DG;hm?94Cg)d%v{iN4jXCbjvRN=$DOJjiyc23(h^&Dnkg>2`{>V}=e+(Wo~J{u zeoKcUYdDF|R?hK}2jFLG`f>Dk0V5xpNpIAlQnnzdD6>{Dtu2+If{3 zyU;RIcOrQmhc0p3Hot{>Dm9%p{qQwrDEpR&Pg~VFI?Ebz-9%P?0)=h- z&F;;5o!Oy3nm}1hpk3O#vTVNZnRLFI&AG2KZE10|MLgH^k=j+~mDg)m=dmNaecmv2 zXsRc7U1pn8rex6BzQR*UU(AKq+x-N1+PtE}8Z58WWRp>+tqX62G`dEY+b(T>WVcVsf-`6z8= zSc+yQib*@YJhzp6>%D<^f^ol1mei6Wi??ss#--1shjmLp=I>UwlB$m+iH!hH{qD4{ zb8J((p6mo4D)Ci2OhHZP-rzQSz4ukY0&?}NxbmEl*F%4}h z!`Z$Elk{zajH1-NeP(S+n@xIR$<6*y0VG1aI}l#s5TD;N{6I7|%kaJ9MI*)^Z!`Z_ zqTr~`VbaWFJmVOA&Ts*uPqF}e&oHlcrUj!{-CSrV7RgDyRaI{T?iGpJ@VXS`)|^56W8D8UY#B5*$KZ( zm)4jHjpuS8d>Ap7SDr&9Z1c2-l{UB@g=PEN$1RLH&fsmx{<^zZN>#$Fh5O5HUj4a` zp2VQ6AgxL&;azsn`q6nQ+{r_p$oly3uluPvwqYksX&s>Vmn7>?GpjBI=zbZJ%aR`+ z`7#mZ@(Qovggdu%5A?iaphX({poznGCY7Z&#HR< z-t>Xj&z(Yx^78X5td$9=g<;D^jD6q0)o@L=@~qi_5=?36`$2zpsw{8PcyzC(d9K!l z_K;E8aR(83Eyxb&VL9LO37ez$Uvz3Sf0TwA{s4B4c;k|c+!_r(PPlCGShHE7{NOoM zyX4m+f%(@JIhVJzZR!$@Vv>LBc1v=IN_6GQ<{Xw^7kdZQB=S!6(%OHI(k+(uE-e;i z|L86AGt+P^{z1*p$5J*cTB2J z8xCaBRS66EH{1Fj1Ug9@$gDxM5ylkjw8+}Gh?T#o`V5B=CD&{G+`{gua&iXpEX(P^H{UH;OMb= z=-rp(Bh%cH?j0w1o&1GKqdm(PYE+LqTh~q$dE^?UeOxETx0y-$vVK~AbT6mHJFj!e zN!U$2{eVffk8tL^hnD`LtnW8-;>RtH>(5uoLR!gvC68x2O|H!vKIHx@*()*>JeC@8 z#G}={{y*&IQKDG&rI9^4(g*dQ(RWx~kaccJ?&H10^Igl1FUQ6}Z+`gO8R@dyY;Y7a zsz3ZN%CDMF)4vj$!#*_jd*Gwd&qsB1UuqlT9VE2X?2uG|fNIxA7AJ`2;yZ1g#|KL_mO+7gNJXr$EK%B|=FEeyNn=QrZo z<0Ig$Ov1pRJ{cqm>U zNO|S(S$TZfl@_2QVvx1KDlNlw^TZq&p4`{{#aK4%)>`_ni(Sx&^87(3jjG&5b3UER zA7=0IN*WVSJryQ?ivk}?M?_aeT87e%s zwjj=~+vit2?s62at2$XSa56E%s3V`E=3sX+OtX;&ttk#h1$&AF+Ly2QDXt>nF*_V&TymxlSAfNb>#d0s-#BvT&n52ezYQQv8$Q9_1wT*D!Tp{N z#Nw+yjXj{b-&EzR_FRF@rlq_;$Ugb7#m^6AU88R)BL6$8VD@=M)as8qjv}BiL{&xu zvFfX7PN;h1XpQ}F&0U2c8WmJ+n^RJ9iRPTHkogMq9`)l`UJ3sRet=1Vjd<}2@f;uUA>AxJ{P1hCE%mi2CZc;N0|B1!oOOY=z_G< z(UVEd|8G-^G~k|n1$}KP4bj^4GMIlp0Aor+*mc|yY918N-&<0O*DK6sAKMtMvoQE2 z*3Oa8_FMr4BA03Ey!}g79AUBItuej5A=%ztcwsG3*`~_&bgamxtxw;=Ec5G*=gem} zSnd;#h6ls1g4i7RF!6ty;jOW}etl*ewrI$Gi&-y)db51Z^~&>QQ&hP|&2ry5Q<^Bv zJm|Kl-4ZPRz4-7#iB9+U!czfye>%I3s9lH1XycUDt73@zRr}RNW>?!=xLDP@zV}6* zMH=ztr4G0=)<+i<`xbPr1!rZ;NhPXG4h7-K0Uyaz9|T3 z;ke^XN64&FyoSlS^f_F>k4YsL4hvQWwvH$LE?W=={kx<7zo9jCO&zbc@+;2aOWA(^ z4V7pN%fNTJ{!^(n%jeWENpGHM*HC2dhIOQ0A3T{LbV7wIUCQ}};EnDA4e65!r@Do+ z3YvwlDm4Ei^G2Jmp3?ZL|MeLq*gah>#VPji z{AZdaaXh7$|Cr7&wbh{NQON(&MI5aW^{_+)W0# zUQ=pR)eP`Ur)U7tom27x&E4lNvDBPvewtl0r!)Y{6H13Tde118fuzrxtjS~-O)Jqh zw$J>>IH@EEPv@%w_nk8NOLo3Ngv0aL)0O#L6=Xb4r*reUnmRcF=he#{bbrmvX3+0h zxZ?3VP`V1Z+Z+V^S99D6j<|yQ&Tgmzb@iiG^QSj_GUcl(PHjyjdON<;J~u^ylG?vj(l_ zTbtUiNb!F#c`J9JM(FyRm@ijjfzSQx(t*YCMj^#53aQ`HTd+pgV}Uy&f+@@|arScN z9gn}$<{L*UcDIb_X@mtI@-`R?Cxwn@V{&y?%?F3ue@s2FEZc5BoB(-c$!Uj1&>bR* zaH=lZ2X2G2NN?n6#SPhdvj^km2-2iKL1>)hqepT3NQin`?j^1`WL4-eQtCBvRQ7bl zruF`cMWJ3#z9x^lSeI9pSl0Sn@vH`k@QPh|eDCK2#;p)XNFrDBK0f7wz{|wuk8}Ld zF*gNb*i_X{a6b3sR?91v^L*?aJoLJ4&@m#>t2q{$+H_q>V#zwDiu=Wk*k}AL>;E}N z3{|$h!Yj$G;PGRQZ0YnlU5$D`rMDNncF|MOYcci3fqj7ZoxhjN%k{?~AB~-% zAK5%UvcDT-*Ln=S|8#lKHTCmyW4{Z$^vse}OkuCnOroZok!`+w1GnBJjuo3GYN5?^ z7)?h<<{w}0%#X*ZN;fap_eIpsIMQ;DI%Bu2`;3p0NFD|2ox7qFvRtE)n+CH;!DJwyw6n{qjSG%8Eg@A$SqmRgQe z41KF|pXANs_iHA4#bqg|JGei+M4to#bBe(+>T?O!Ae{J2_glW#L0F56ZguQn)Dm%h z@fPVM`14*h+2*3#yR0fSI7t|8H1obA_=mIUXu3*w8RqD41wK7oZj)Sj{9erL zNGz$L+BO}k*})|5P{*+@e4e`UAUR#4NW2@Yd#wU+iFBKQ*A!9rV%E3jF%a zS0JQ7CE`zZ@HIa}w;!9sXfRJE4%vP`nemL66$*5MJHF9r&$!|?`ECUc#0~V-Cpq!> z8SXge0=W_P+fRBz=LzVp`mKSri3I8b(!L>6Cf2vLO{5mx^~WN(O-K*7A1R6~-dY4| zH{TxHwB8GOQ=PPH^Fn7gNXIz5A0HMOGW1Z<+)QXO^&pI2f6Q3N=NtQXACX1g#n9LT zWAh-X5k+k7ib_9czkX~;TuA@Jic>-ovDIlIq=!6@6s1R29in&hpsWj&0+uGhk z$ien$7*#dqV0c7074330Sw;v__i^&~` zNyqJ5{x?CcO}WiqpCO-CjLIhJ&j*-w`tFZ0lRmHHimUZU_4CDsMDG$lW4>V2`%C)O zN0zWWk3T{3A|krnYWh0w){x+HFpu+;VD9b)3uX0aI)626)ckYJYb^zTif_K}-KV1? zPe*k|E0rjrH>tMskeHf_{%*c$G2Yx27yZ?J)nmLTZrBN)+4B^tSarAY7NW@eTU6?P zu1V06_OI~8ov&8lsn5S1bW%N1cA@0jsExUT_{x_*zo|v1$Ig43&vS!}>p8}5jM{$| zZ7svR+q5}FiIVLI=Lxc&Uzt1=U4(MWH#t3a(fjdyo3C5+%JG#fJo!zf4b+=WUC|$f zrV_6E6JZrG-p4C0`rRq1>?h;#e;XS!w#<3iC_vY{_u(SQG)BiV}g3rRs(OR!%t75-T?HVX7`=d+#WH)eej$K(P{bS$s;LO;A z(X-z9^b+2Wt;fNm^2CAF?5-E;o%Yk+zxCSutZJbjLg0{GP-oju|JrK<=3OqkKh= z!tI|AiboIkJ#|?3mmPlF9^{6hV{JZq6g{oSESDNCZ?>-{>OP-fY^RJ{2z*-ZdBJ*d zz@<*#4P2fmkR4w2qzfJJ9+W<rDSm2u`KfKt`G2l4x zp^OkHnvsu37&DY+@0p`$u2NpjcpB$ZX3m4OJq5w zh=@kTs0Q+T-gf5Y$p5pq=_>)WgZWo8j{_EcT3%BhIgIa@nJ_ z(`sNhm+(|C?D5Z|&4NGmtB~?+m$*3jM#UA`US^`p|Y(^?$Lt*9Q%8kW!nxn~Z#u>nCX#O{%L~5*9F8)8c z6IwP^>MqC2&+a`^o0!dv4RW8m_rhVHx=*YoOsLf>dagB}GtqJQ0F6>gYkHx?W3oJP z?4-Jh*j>obf89!G$ey+E-AituBme&}3f2`a0va5P9EJu(jO8} z3-*eq^3hoTVG-4 z0na@2y>-P2c8o{d@AUkwhqrp7jCj77=LjUoD)4eX`F+dof0x)Bf5aI6{_H*Z8TAAF zxKKp(#ehbfZhimflp`~h@2HGChq_?K+(Glq?C%*Q-n;d$W9=KAn9Xa0Cw_)rvJqE% zQd~@ZUE>6?u5~(=?oezv6xP~=H*76$dLk3qZ<-(1w&y&r&^yhmR~F97BDa15U`Lt6)!t@#`tP#^w>i=E)RVNV;9e*$jRH2xXG&0x?(K8W5AJ_zHpir>`KPGQ2pyX&rjD5o_%Ay z+a0Re>Dh6QmqT}r|auqL6! zNjvyobiIP@GM7g#2kocj*4vcfo#YHvk_xtvfSn0P8camg&X2I$Xu^D^ybFTuK3{h( z3EW?pbh|2Zv%6!N7L`lme=u0KG?j77=wO>g-WskT1)WvwGPM>Dyv2;4zRaU9Yg$VA z@iC>9nB25pUt&_AOzY9jW?V&uo~Ov?qlx-Rs?J1a4E(kRmxh-{T6Ev#EG1Jad)sJC ziIoO#))msNsipI#$BCH%I5Uy_+}M=XjIEwpUB3K3;~L(Z9FsG?$xT()v(A|=rMP@i z0ILN~&%o@cNqh&M8N{aGtJEYa*=Ek)WuHfDy@F=b_Fz){yC2VUZ1wtN8J4qX&nYAN zTQfL=$SD)scRfp`X*gPC@u!RU8CsDqdg=;HoP!qWo2=nce`9OaZzW*r8Elk9zHDkC z;dLqNplRJS_vjVeQbRC?uVr}GUc##8bDKNEV6iRGkJ@{a0QI+Z%V70MmX z%k(mh(`=bxCGqXLXHc8mQ)x#xciA<_(J2C^|Iw_+m3gA!W+0!_Zik9pOM^t?%}v#7 zPp|WA|GK(L7^1mk*`$URXu>|qZ(Gm$YO`%y2j}%o$e)#e67NfoKN9@u<;e%lto+t& zZcf%P<)wY~<>tue%3r7mEl}2o)LSVQ0iWQ>e^OvnB%$sYyJf@ImPmzM*wdBW5^Ma9 zlkNAUCx>!X2ygr5)>Mpgtpy5t3v3(YhQdoF6~*|bvD`o2Uye~;Y{|&oQ)Gi1%$S8} zzNLB~d@tS|*Mt-=e4BMv8^H-!JU!yvt9sbk$)lJa^dX>K!xCSAv@f-+pkHvhS?O%1 z%n&KKxQT`fyO$}$1bNrZ?N05`Olk6d$^TMWxr>7mFYIQ7o7n8w~CV}B+ zZS{yF+oH(uq{5lD3O(IsIHE;;mEDzdrMU1gpJJP1s4Xz7hl#IsZ8l4F@h7RT{%pg` zy&F92`c;4yx)IJivQEvKP%w@ql@Kh!Kk_d~zVBxijc$Ns{ugICe|R!wLI^u@uX z`l<(V*H%10?Od1rocaWh#OIHs`LTG*Q(1&{M>@D4XcUk=PR?yy(!{O<#j@>VdoB10} z8{MG6NKf7oY%qJ4U|8EbT${TH_C0DXp)G%kvhJu&ycTgrqOHPgjc6?&89)@BGPeAP zGJdX!=Km0U+V*sj+UiU5UbWH7aC7({?6h4 zjkaqFNX&(a)VF#(R=otH4lJ=H=m$Xm)qQMuNI30nP7M|+l9NT&iTI?Y=phr#y1KBa zpXU_ph;KG%2gUYMEC#m*@bGDHgWKXc9xH>`#S1*pv>!m*`fXJfgPx9$-4A}0@YMKF zTUyN=7tB72Yidp9H~k@x1i}fJkGs@udHX+z%Eg7=L(3;1z!qymJlI zd+#tz+tgD|TD-~n!7MptdVg+%_VY&3iBJ{Hh0Kan@ajw2-^H4p0Fypbk~ZhoLuxQm zyC9EYk&ds;3V!67o4UqbX;3%^t*ne<*wl$g`uFFrrw>_+QN?^AGQ{I`BKZMQ&wD!#WY{ zW{M&YW#ShX-eWzzGW-+J0$piD8xT&xw_WYY;dg%A zAh*O`!L@Uy;$ER=K=h<{V4vs#VzuiT5MNTCE9QdM_YQ&{=1Iv;z>+c~6oDlCNCmR! z^{OsGu#w}17SB|{ePDb?Oy23OzRb*f`b0c7B|k%eXR3B#V>SskahAof5JKkd2lu{Q zl;=_JD*t@B^~PB>j68DunB?zw04Tg3JB=TMqh`qQlQC5rG)i%oHj8nHu;ibltWSmk z-8EpqD4ZUVe=oNB5+>O3UZxfGLl~K0A&{xg109o^YZRT59mqs69M^xmQ1XfP=zlIA z>zvjZlSItwy7@t{G491No+-YvER=0gt?UfhdD^Bd+krSDJKHWn-L>4HK`VXArgpAn z5ezefPooM|xh0Y;DIHk#raSamVDdEsmGX1njZW4LZ2a8NmRs`zl2hZErnj18AK0$v+g)q{pm=3` z7P)_6{Omsfw+41$t=if{0mqzzR5P}Tn|m|lW~JFS02P7l%!=aklw zMT#p8fB}6&GlczM_JV+0q6kU5pUP`W-J#~82;L?C`-cR*@=7^iOhO$#0KRVG{>9%& zIUo_K%c2{!q~bw&7Q@hbx_m#_MJ|X~dYq^nlqjKLS}g-io8axT3|!kT&kR{~Du(C6 zSdjlSXCdNxkgLW|QPpa5zLPnIHVGnQ-)eTh*__N-ak}xsKc~o>c~MBtTp-6#ebG=t zwb7~ns=4l}L_OnRrKDqSd?I`$JF7${W0gr3-C5b*O+vdJd_zLoL^F43{Bj3hk>adw z@-8hPj461qA!2%9dDH{j@F7K}`5sPel2tTpA`;>I7KXt>je5+9rX%KR2Z#`fJz5S( zRfmlu38am)-Wxc23j^^731J|3dCtVWO++)UoQKMhCl@;I3?-)(2Vw5CGp=KLl23=W zg1L>=eWu}@QcYJN#O(d)9ZtkCy)-(4F9~vmcNf8t5-;6!6VqlqYYAzL5j2iYLY(qR z6(+nnYQM@8FLOJ;yYwx*@lAK5B@kVXHeskn-7J!h^smOVGPo#2V+Cdg_T_am9Dukj6{kRQ2_&Tt29>A~m3E5oP6z zdqQjAM0B5tTRW{dbzF8K+HK91_1Y?@g02&yV6UWgE!W9lnli-uki96PF7q>5vr)5zZ4iI8tk}pI% z>4d?yaTaxuDAkk!L+3y6>n0>rWX}&F_$R;v#p|bZA%5C~SU0ST1VY$W;XYz=@tk07 z|N0$bbAF{Dg4gW5OE8BFE9rOnJfdNsNKA2>M7h$WNqD(TsTM>CIHYRhsQi>+dq|_b zk8dL47^QFsiD|Cz_NzslNULi-WXS2le==qRvbF<5+f{bZb|levLGzJTzG)B&TG01U z%U%o981(GA&*W@SFlOz{ieE)7jcqi?%%^8>M@Lsia|MRWMx#SV^U3p-_Xbo@Zfhr4 zAJo@k_w0ipjYaIEobmKCNAJN1#6zrwn1mv9q*;?@v$RYBF=*}zeG>BH> z0EZgXH>ya8L+ZUd4HhSGZ1I{^2@qZr2bcI2r|N&n^O(srUHfH~MXm4s-K;XF3?{8R zRJ}yG@l9z|DFE~WbqXvY+cyomJ8LP9QBO+(D3?>fsb$ymth=2c<8+=YO&q_36sNwG zGSN8on$ikJ8%glsK6aIA5Y2->peWt5IRKT> z*i1)T7*aueJ{pHowQ5sRzl3m;j{=C~5=4E%tm^2_10tERQ@zwmucX?dv4TqL<+$H{ zh{~+y_{u>ojFmR3q>f(pGJie%`^Kqp7UXh2g5NBCC75VjIpBEtCPG{=RFC(gc9hDM zXG=V$dlz|o39*7<^ESV`Dk*m{=D^5GjNHJqns+eP?qANj!Uk!o&&@=c{dRejMV;`I z=Hb$kp4A{CXEnujRno=*%H=u_Hq%%hNDvT(Vz-MDY2AmFj8Y8P`~aT;Yp(!XOAw-Y zxWE&+S&|CcS*J_Z&PFJ>kGH@s2@6cKY2RyOTo#uJT|ck;(IoabZi;D>JoUfvhB&x% z^D{^ji)Iwt6?`hcGk_4=%qi7eNQlc+NIu(#5GQY(`nI~JJSbomdkY;nOj`qH$%FBh zs5FyUJp^xkD!vpfo^?Og<{#~!<-GlfBuj$DKAQ&Bq?yK+YY>H#6X8n`Y2m>*c-AH0 z8Rr$36lm8!b`rogwSu8lzMi8E)rKjXyoP&vJczwap2nNZJy`NGj)f!mWePs2Lt4uD zQ$9D9Xvl>V;%a53nk5Nu`2MC$fq{k(DW6F|gT%V@R?##W;6Hf~>k!O4@+-u)jY!3J zH)amid@TB>2XnS-<1*y34WzNg<}v?9&^JLs=x;)PvBL%-*4|@S0@9LNc2J`o zbplv7s4eJcSJ%&%7p0sPT$3|R`-wOaI#{MhND!k->3^X4-pgB;Hnq_chRpCUfjn>s z@w;Cen@@H4qPiz7K^33vr`bRy=E~ir1yo|heTGQjY%=r)pg%EysG@PvV?)z)v<0(v zzTErc*^v!E{W>91C$q`{A*`iV!K01RSI$s}bqZ`nTH#Cjbl0@&r1>Gl<105Kb z6{_RuO)5KY+^~OpctG=flWhK901MU+lu6A|Ef3Yc$#l$SJx0M z`d|hON_Hp2S&6w%AlNG8{@%b4^`5;&GpghUJ8ml4dHK;# zDlrDlfN4i58hxa|de;xpX?U+MMF;`*+(h-Ytowm+X|Qa*Xqs>J3;-8X|4{%gG7E13 zw6rm#O*1^x5NujzfnnN7l8P}CuU|)NSym8UIvb&*033D#ILu!_3l`7%lg|MD zF90h#B9;`)VN34kB~QQXM~KN_as`2KO2#PK@qM|CqC4) zkjFXFf-zXYPH$T?i)21HV+jh1Jgcm&w4;(bo>)u+OnJb|IAcHAh=O8;PIAgRzvq8jU-u} z2>|-f(oxq)#u2lCl@DXKjq8)?KWjw$*mJu@heG4YG|MdipZ3#I zqZ?{SBBS!pa$uaB?>+IU2TS$M3hN?UOigJ>8!TusQ4qhW;E?17yC*`33mPrdhqM;y zZYwJ~(RD~E0JrIluJ~#t1-y1WaLjE2!E5^y{ZCl=pRmt1g1<)mj|w0h6*6T5N(T8% zi&D=v_hLDcdUvJ>_Kf`4Q$K#Vpd)2cz-y2}&-sN8?lrUXC%%k~zL`H%YW zj{=$9^WwS*+Qn8uHKWgD0^%0;{4~XEc`_>(x z5$vEIVD2jwpn8y5&RAesO+!98f}tyn)krWF4X}c!nfb?r0L(rqvgi=w6-5%#hCY_f z0C9H|6XGl&{VD(&j4>wwH2CrJDAv^Z$>OFft(YC08grJoyV{8d8<%lViU$TocrA~k zC%XCm&vi}HG)&)$5G~PZvWym{;A*ymxp5y@!E8XWE(D@)`}Qw6#Yt)}$)mE#7L%eK zZ`EZDX|$Qx3IPt0uRObqhj!7l6Tp1CwsvZZK;IJpJlbBm0?g#Bj!X@gYMp(aXF0EWaISZ%c+ zF^AnJRz2&Bt4sX2$rmc4UD_z;h(DrcR5883Hz2K)OkjkGhgko1kIEj!twfqH%3-Wa zJ1PGt&kWKQS3jZyc>{teGTLPi|H$Je$s3n>kkZXXk7#UR|2q}Tg*@ys-B_z&e1ML( z^dS97&Giu6VF{KH$&j(QR51N?GUNN>^q4eQi|Aoqa5|X3?tKh`PmB2Vb}S9{z{hc- z4L;BAcsn(1y?6K#R1DJSMY|URB2yc(zT>5ER0cf%wyvhQy4m#vniK&s{`l&evH;CE zhC_R!gLRNWS!-l_1byV6Rqc5F{TJ#?>SGO5+S+`~qt75Qime8J(KI;h?sY<>v*7(0 z;9R0OKKHh&CaL(&b?v4;4G(Gl7d^1F+(%OekOr+((-=+MbVk?Lri+SH|N8%ZX%y86 zsCq0kr>Y&qdi;DtSz+Mg8RVKAx^w|)se^la#^Oo89&R#4b^%LSLkAu(t{36o90m(e z>@AP9QFq-o$;Lxv9}k0hS8eT&2yd*4J|iKG6IaO1AHU;M5@>7$fB6H(nJu6m3#5%c z2%wxgv&keIBk0zEDpLLD!{Ae)YQ1`j)MD@f7m4?0jylk@O7NBNV7)MX5(1{(R7RJ%PYv5@&B@kzF|u!2|uSB}1|Xw8CgPGZkzsI{hh zS?dE-o_x)EZjUeZtL4wJ#nRZRHbLh;g>VipVQ%T1+!X2fT+DXTHg!|ZZ94*)y{av) zBM$rOJePQO1b8OYVN0v>TZ#g+D#{MN%z8tH6_m*85?!*lEwR)$TaX#J-9R0V1(Nxz zH$kh+JK?YmF7lup$5QXnd@JbkA^5w<_tkC8nHMh7^4r_!p`cK3w` zeJ3peiICv|jF;)n2Zpf$1yi+@5DD0=`rQ-c>sDoT-8V{NgX~w+d0^ckb38hsc0GUn zRviY{$fAa^9UyOzuqmOVq{T(7Z&jn}lWjqo=-(qrVuxyWc*#+>ix@nfG4;y3_?Gu` zKxF}aa|!pMntmI7*!CB<@~spOQqeLz(;WZG>jE2Bs~ZZY$%(H9p9FvWT+G#{S9#C zq8zk^zVrw&7`q{{D8ArbACL|1{aKL77ry#p!b4#`$xE5C`Ducs0KdOlGMu##=Mjdi zDj<)9c`wFPuteugoa-Mrr=OPQ%zNu_tvnoa^hEy~%npMLWpc8!Fi=Rca_0s&yW@UElL)P9p&m|X? zAI}e2lU!i~xDRF?^F;L5P6Ety3ms+>z+`4-z^8r~CV>S9@ja=&dBze!3R(00dSF5H zrnqg_ck!KQI@zFhSh;Ay+iVx&`!z^=wi^C}=mq>zZ!PEvL-UD6tuV6SGhepqCP7s{ z;vM|ZKz}dNJ7=+-O!!Q-?tT!hJ!723Lu>zDUcUkY_bRTg5nu)*6HEf^YwgiG0cJT8 z1a?3(`E45?WbZm0tuz%!tcguXD$M(My|&OfLXy&CcbDICj#U0IOK6YD9{n>b3BQD_ zq~~BsvrW&@0ndyT5~%m$3#*jgsg7ooW8AYA-}B5goked>sHHWt!>8QTu=J45dAo0Z zHn!XCF}i4Gpe^Y;rov63VDin%`e(au@LLk)w@A!PXFuo(tAGg}Ep~-PR}UcF+wbg# zNO9ta=CpRH@K(ROmEIxxmjo{tEx;tYVs3yu$PiM{(BG0Eg#?N6t%Tn(z4$U3Rc&c1 z;vD}mt6M<%mTKGYwtTu7jju_R^ev7=>+np|BVRi~27l{R;XjCK(xdI3``M@?6$Ar;rtEnzcBY>4TV}TDs`q?>aMi=fU*5W4!Ro5d zC3TY-v|rws49=9Q&-QLJx4YjRj=K8V6EO?HlV8N0;TnT|UJ7Xe6(x2OXyz|P7TWhq z2^kh%S-_5^4^Jl7g<#yGGI#&5ia6YMOE~TLis-ToI{@o-f+6CS-)P~jL6Nf6WGj1M z%&_%yEWOUEd7k=}4BB5`oG-5GTiUjkqABy2Er@~N{4Sy8>OH?wzfn8)s;D^HqPBPi zk_&Fo18{n_t5BFP-`fLIC)h52pYMVzgu2NUAv>O zVHHV-D z;O-pPL6d_WQi1;6ER=*QqzwN@@y{6m_jOh5?4kL>M+RXCX~8-teY+lEa@CyOV5WnA zc2%BP{Ze*d!o<&?M^pA~-y`O*MI6MCPI?hXS^!Y~lTTq@QUIQpwz9@e`vEzSSmiP; z4ZUM*-ELry@!+s5TKKk=icjF&%|NH(lbv;HOQia%3aHgjo1Q8xvZeYN_>=@>z38g3 zz1{Zia7{-Z%1QChR@La+zAF!v6=ZsP{FAWHT6C|eh~6)bz#aA*!t5vT$5Gp5(bool z7en0ms`4Bp2F^>vEhS2YvljLyCeC--{VDMhw_>WOGi0}?Wm<7++2+Qw24)1Ns zyr0?5b+BVi>oPc~ZnYa=_fm7)Jxr>IuCn{%;iXGqX6R>Wso&J?t}2Q3kS>jm_Smf+ z&@l1j*b^H+|HRjPRr8C~S>GR_W_}MUztwAZ9?Vs*!2OL8ui~r6%YE}`VV$Z|Z|XcB zcrS9~rs7ka$DTodIrr6|Vc{ABT1f%nv4vIbP@m}M{R`r2$~PVf4fPyFft>GqvJ zbiCmfLVZj@d4wF^q@ZL&T3|&yX{A)e1TUJY_;1{2{U;=f=&d?M99Ek<of>6x= zsp-4p+5W!&-yLXc6>p;iRmHniv{hpJw5Y1p)+|9$l!z6vLyMY4wMC6kqtuF3L2NN< zC1zspnIL9UBm7?dJ|4e+@<`6>p65CDyiV>t=iK|cuK`ZRx^HC@jBaGBEV#bHsHFWY zv%&rdmt~ueG$mM~jtw$Wa|hh96Ed@7*`7M1kSq3KJ}on(k}6@gA7y0G<|pmk#cS~> zT9h7p;Gq$sSS8lCgtb;F7VDcY5(;tA{qa`_ku%`05KcfyRimWBD6;|0)kBM>jN5C` zLXHx>jLeXOdo75@ZyWR$?&&iUISO{4@1jTfq=!snahx2Bbn{Sxys-- znN$YeeP`X;Y%p^C_#I%S_cQE}LD42)SP(-_zF zrT<#k3bax$o}5KaTZ^-yoxt^JmUD-zIi0+Lr!R4i>FunIWX@G!zMG~@)#as^_Oa4e zYBH3~5DB^{W!`-Q%l1P25eB|mdD?ZPXl&WWiunW$_~U>_=_=t~NK@2O0vT(6(14Vl zkcw|^qoY=69`kxJak$8rEdi+t}LY)SgiQMbyBQW zL3R1Q5GqJ<^l~psG{u|3icd_(u80GX1O6}(z7!LW?NIc3NE!`_m!;1cW{C27Z(gCL z7;PTb9rsrJK5e7U6~$XFpL#uy3j**)?VG5XDi7@P6=OTJWOCU+7zu#@X7ENZ59;@(h8* zuCf8JG~zlvifsObLAxA~!=3Q4wb^?m?Q*EQOIWxLcwEnItxbDM7foK%23WsAOH+(c zhuT!IG{wNOQv}Xh(ka(Szef-*2jGeVtaRo>F5aipM#LW%vDt`5QSfWP#BMFJ@k5BZ@sS=@7>hDQsh=$H#L%7dV0-C z=~RrMnBx6j3_@yZ4q#Il6qIk7UrydOvFtTLw#a+rXU(n%;vJCA_0WF(&9i#ouFY(x zWy!g!Hms}E<;nFPYzU2s zb#bZOhm|MwFc#kGE?_-dMWB~^v&u7L?JWXsMXkP3aP%{7HNrZ>ot3MuC=gQ<(G3}B zHV6JOw%X9I{c@^JRdRjH<~4sa13|0O?4H8RW4EqWZe&j~T!WWN-Yz5jjEY1s>_Q|>$u}@mG9VYWkmgn+H8pawwRKBdX%OS3Q!pVP zk3hp#V`l91(dz^l6X{@HbsCD_hxKO7@u`jQ`_xEFuXWv7$Ps#-Tbl=kJtzpsiksJ? z9hM*e(xQ{Jr$yNqRx4@(TUn7^jvjoI{J$$xAtRzN`4n$#k|L1{#+GuRm)^^5^=7ZO z$SJ5n&bYQ@^SCwvv~EgS&NmQP74j&NnDmmS$Px*x^q!_IK{M}ntX?L*<~_E}L=TJu zWRL;#zBs_N=D3X;<^6WHUrZH;@q8&$vzf7`Ph3hvR|-N%vl9je2i%h#`qT9P&9)Ni zWCFsMYx9hx2o?BBX=#d?)IyPT^>cthJ`IZP>GZIo0?CZUts}O|2wk>a!KET=M=gVW zfyh5Io+f2#eO14r#ko-G(Av(Ykl{%-8ZbWzE49(*LWN+ekfEwOD>2q~vb9CJFmVZN zpCeqqOnnG`-&L@@4nnGj2^AcbnAB#Aiu=ne3|)YU@E+WxELU2)n4rm}m?+B852<-Q zn;JDX?dpgG^EL)mVzOrm?}Qh6t(r)XYwU8#ATwh^)3K9-Ga{h%R{0j7zhnrp7se`a z1hoXlyD7%HY^r(Byit=4CO$SG6o}|#Dn-!b{$x*mYK?@#Fv*n*jG%p5&0xkh%hmqS zo-mbO_nz#m$aI{nE8#W&EEy2JIS1GS1=s@<3)th)M#H{auV;~f6d_d&W;Hd?{IXpi zIr-RAAz#jW;!XU%hI|Bo#r+q{9KaIj0Py>aFdh?O@n>$^maKb2Qf5d zT0Yfi`pvMya6xILz!A0s6n`M=%9yf$GrilPu^TW~h^qsdLUm;;2q6DUP)|8XF3b!; zuN#)MA?T(3tH#u-_ZjQ<#9mh9OwrUx>9EQfj;PZGi~Z??qgV&lQQXL@RO>yD(8w^xkF0+YzOpp<$=6NSi4Qkz3)jizm zfoCsVL#D1B-ey9tj+@7-LGA0Z>kk4<6^3$QDk=Mq(%ZQk$Hn3LU<-DqfTJ1}bqKN8 zl}#cb0kDBh)#21g0)+f>DKzs{tO8NDG@%&V16ViV1VbT+H4ZS==71;p(d)VLzfzm* zDGJ7PlpBy9$o~KIpow;$Dkr^m853sTIO5}J5(2CHESR7M1>AR9nnq*R4)_^2|H=B5 zeO(S|LKD4iWV#SBvu{O^AngZB!t=GQ*AJIIq04WR4y=%whVeg-Z_g@AlMgT%&?9%t z)_F2<``LnpXZ`_H#js?)jO5>0TWDjsMaP-Inp@@OpOiHZ%FU<6|4CYn6K!nq@zhfH zjD&i{*bg8K1K8pDh7){lYh=}lNAHXp7v5r?Eoqijagw}`bYQmkyN-^lCfi&MfP?HX z?++owgIO3e5-0%0PPScoCY1NCJk4LFR4T zRwrI+cNR74ZTchmd8?^Zd@R={bOkqIOBo4S#wo=kwrBB)Mo1SYHP`tFwe94+WxZM2 zY|vOM108?1E7G?M_uh#5JCj*0wYL_b{ny?v2%fKr{r znX(DHu+Q=h3jr=8bLo|+G?E8NEM}j?q7{K zeeK@WiJ)(HO=_s~?rRmpjY4SAzsuu|R9`^?$J(`E6Fx)_YSZSa+Ws}G>MZ+3Z2XoR zJA?Ea@Banz5%`5v%T@JO^hiOKs(nkwX8%s&AZSWu*aYqodY#7ZlaaCZF7jvOGGs&> zCgv0{|L1pkvLtpgd(-xU_Jn`9y$~joR`|Od=<@iU;GQet15e1D`Ab|k_=o@@?i5ei zNUwhm+mZo$_XLnW+N=HtWk*7W0>O$*1_#OnOyom@2eh2Pp2YYuGM3fy*xHMw;P`{} zsae-uSLF8c?}7eQOv8}SQ_wofUb&6M;NXO=+@^wlGcwGIn#I*9vvo;QVVIgV z%t7?X0cTEfMAYWj<0+Rc6*6WyW?;$+L+|_rZ6!#Exkt^5EJS6Q%}a|4qpM|Wm$>Q* zvn(e#@;ysv)}tJbUPV?q+Fx;r!OhL0#tMV(vr`;LwM8L~)uMM+^VNVF@iQO{3Nq6k zYcbiZEfaNJctTPi;lOz2&xYiQK2RK7OAr(})5;o*wV__;HszlOH=HDU`ci7A5uo|(N|UvjRMdZP(m*$hJ7@mk4adC zNR#E{fyvm)o31!&$?iq3Lw5;o9KlNpVNR^{MJr&k-N5}Vc%*Yd&Sa;%qEYixIDq0WsV88*Ruqy*F)cok7fdQzM0VrvVj zX+zhNJ@SOB)TGJxl08Z&uGgHf&JIrIc%T@6oAw2Y9U?bQO%r$)$kH3od zvc?YH!0(4v&&WUq@yf~?iK71(r|j1&Y4la7IXSq!p-!XK%@BRxpuG%&Kf{esn`aFT zoet9u2sWCZn$&U{4;5iuGA|GHC^CA{MW|47p4SZqUKo|NxEj-B%fLqSjgTz)744%s z_eXJvn+I+sQ3gK&QA~?HjCfVo{wNMsuqy^jla-1+^JPiO!9XIWtLVCY$GlSx^{0Gs z^^;qrIM{n$7<2|AJ=J#iK7kCJlk~7+2OgJ&0PfnJ8hpypMQ}h4_DqeGsgnQH6^Wh! zs6&!c^P1EeekY|Hdmp^=x-3xkjZ;Lg9GYcMYP?_HURPpp&wc52ElhMVc7i{HOu@wA z7E$xBpk6vMl_<#QC=dR(KRt4}t}u|^EjOQ&>5yBOQ)BUtaugc3n7?%p4(-rV?G*<~ zO%em~dIa9Vb$T zC=l7gOHwT3xEg0oz4Fhhlp^)fNu|r5tY6fo#125Dn{3AtN#t?|K7D;}+A@+_Zr5t0myYYdWS2 zfI7pq`3aVNtVn|F--_XA4~Sxy-W83md7t4tS=E#US~u2JAa)Q5(deoqfCQgu{7VA< zwesyiEBy>-OI-b1QIv~*JR)&(L6c;83dtg2R_RvVi3Zi@yRc(e#lZvlL|Is1YhYyV zyhUGyG-faCR&91p#W10W}1cG)a?;_@A3%XVR?z z7$X3Nf#*E{`2^3=>0_u>D;IeXGR2asj(Cg0uyiY-t85V+SSS!y5fuo}9LgC1k2-s-)O8MYW_RmelBc_!m?6khLgV&rG}Cwe62H4! z9bq~DF?L{?zEgs4a&rs#FBq8GJ$ndAP?)IQ{TOp4+VinFON^bE_2~spb(dZIabtI% z@9Hs9ZEr&y#>9;1wvwc$XBST;q#5|J1d>14w7!+DqgbBt4w-Ce7jX&U%JF_zbb)5G zipo~Y!LOC(pWh)}Gn}`z@*>ezY|QF}-vf;9iiUqh^x)x5pi?9IQ+X0}Z@ z1*rJaU!5Y7jycxDyUKF`tVZk-gSv$oz!(iM>Uwt?@ePUj(qI2=G&piOVcrq7Bl&ee z%(p)kzzY5eVEGw1sl0av1QPyFAPlwrpXpzLWx88O9YXkBzb$PO36%&frNvvWfD%We zCzRVX5#nW=gbt-elE+VJ|5E?Gvf`lY{7Ul_&pP}cxjVyRWjpDIBopgsh_7G4Y=1L) zr^?TQx1me;H`+eA_g9CYxzyP7QM=b4Bf?~G_otWAJ*x12pj7;!+6M|xNc83D`TpO9 z;m3$}?VRsMO_vGlA0+OU!h`>DC-XE%zNqn!tL1d`P6Bt7#QQ;m@}N_(nD|45AheNC zn;EthZ2KMWcU};+SM_z6!O-41<%HFAosA%xL8sB2Hkp~d+b8W{R*PZJ1~0Sc6bY!r z#Cdo6+Gpea8FCMz77rsUq-_ zS1CN|v1(S^0(=8vAyFOJ{@Lq;f~OY{g1^hc5uQKMyN>2LqBG|T(L1qxBY+l9QceUT z0Km24@Q8RnrVnfU)OX*NR@m9^W?}p5h2N+l`kD`vjRYApB$L;pvQ&Nrkwa&-@+VK2 z#ecFJT(=_~I8fW3Uq^0by)XD{JUF(yEN4K>J*XT>jw*XxC_A%t6gUaIA)klKJUktk* zP0ulxKLPt+yMfx-9DBOjaDI{ubW#w}N&-2~cz4!_5*5{>)TS4@?tWtqz7~tGnM02(p0MZ)T+bY4AbcBuW#S~2Kn_SfN>t!>etcd0 zivpFSzK@ux`5w@kB*KyJ(oVQz#nqY%{)&)1(}~ZC>9cvwza+K zG@gL>_ZZ7qV=Q}=#pYD6Eix#Ug@~u11sdk-(ADvN3=!V3zR=oyz(p&JYYr>-HuS;% z(|cAjR?EtX>lqZ@T4|+aLL!B2R&C(~fnSBJ^&x;)b^k&>Haiq*$boOwkByVVmA{UH zGA+ob-bu}FJZxt&oklZ~ZuuD#Eh=^5XrWvYO;PBsmv0Dm%CC!vWRhP4 z#;$EEzEQ=pDqCMZlF8{sb_$zt)pOrB>tAHrhq2wh-v$)bqn)j`xu5&4)wMupa+L2h zv@U`Ng03{}2I_>JTwU>wW!~@$U){xneIwK24--~Pb8*bmwz|e8&D=D5QPmbF+eD$2 zT$>Fx%k?kdTPkMSCtC&6*(0!17MpdpGV^k4^Fm9IZ|Q1P1ii5Ve?)oz^ zo|c7Mk~3p3-sE&SI}SOMucVFA20 zF$+vY-Hs3+gJ(0zqW%JIHebdgS8O5@JEQ_bZV5F}Fw2;g3VQ9qj}WlWRqtXnyxfc9G_^Q>vAPLMYuU2)zrqi;36J@jizq6CC<7@b7L!2KirUTGhPuF3_+W-Y^`#zdx? z3r~+92Yi!j$)b*z5y!v@Vle-o=c-J1C7k@r3{$Py_f#RJ!-lb@+gA#+P5UuF)A2QiK^W&2tN8mz zuWl(s2eP$Fbog>O@|}To`9z4mQeBSSI1%b2>sBV}{EUXrid{%|^5m6cpKsbmeJZPh z)3YiR#tr=#?r}Dktq6;For+_FJ(fQP7Lvc>7Y?}!BMZJvr0Z)>o+#0>FW<9tMjxTu zF02{uWEAh0Y8eSFX%(}M-Aj$>zcy0)}Wk6MNd@XWKW0 zRHL=k-)h^)%S^|{#pD8f~F})aGby%k-W=mcK?;>u=-*rpWn!0rOc-O*|Aa<0rbS>b{eMtf+ETI2+O$YM=on;?3IBsq& z8#0lWGE*NE!r_k^Dr5G`8AjF4GH={p4i*kl*$8t+K2%XJyC5O)ij$6F*!LB}rZbZ+ zuGxP&Xt1I`7<*{o-7GuDGSwVrm&t`ymuCxfIo-?*u7RiRe8^o%dAp}GUQ+ori34}h z^+plm-0$D{t_dGEg4zjPM!Q+~?YZ_)$Mrj??^WLXLxD@PuYM~#k`{nRBNmWXOIL=! zaapQ6KGH>H#hmd*Sw*%cOr!NH?4`-~>-s+PYx8GupMcmV$4PqCM2SLOvG% z^sSXzl2?*;eO$}qCADPS<+~8VaVpjqvTpm%BW9ar{=qy$n}3^Dn?qZAc+F+WaA-Is zoN%e;k_{K;lKNGj(<-N}>xLH}f`qxawc?&@zA(RZTl3=cotKy7+azKx&R(jl+ko{g zG&W4Ds6DIT;Ffw36IUM?_dV+U`}gK=t}s_Tbr$2{;kj|+52`0oi3c&{Ckwje=qe{<;o(zav;Qg z7WZ{)dHo|I^kWi|{U7TTCuIy>0av+-NQhQ3+#}ku9h)8PAuX0F^v+8$ZgZo!B2UJr zO@0^%PB-<3Y~+3uWtv2C2+cpctD1Fr<6F8=cwqC)9z)W}!R@cJQ`7jd>!-k(ipAsO zci*u7^VKZTOwd^8YS=cY)5BJ-KQF|3BP*JNH#OP${KtWp+8HUAaYi#2v@)MO@{&&c zd;o7{yicNEKVPfoCf!40ML#=H#YR6@NI*V`fBA*6_C5Y^a4m&!X z<&1J}!pDOqGtl4!&)3`u1OJ7{4jfwwz$FEDr7Z8#h|~W_?K`Fi=oY#~-GfnMYiD(n z@^Q@-WI6NrH|A#ymSJ3%?jnZoQu^hqNT9p$e1{2fS+jmtS@}+ty9xb9dp&yn0(ZeEQ#`wuHaG{u1T$YEn^{;nay5S$7{eo;Gci#GN$zsubf=7T?n% zjwz251kGL5R_MGOJSQRT5q-;gI|kgactN23CQn7Q#(B<9QSCQH*mrL-+`25r{IdJ8 zkJr^)d1x^PTEIpwk0;j}khSS0vDgyl#t~@aXdrUrvAw*hJ=lQjfi0Ju=jR6rE93v_ zj}JIH6CI(ySKhF=yoj9`$v(&GtZAP%z=RwMP5v_U9_^j0toox>&+%Tm;+;`+Q!8xj ze!oP8_QH+ScB6AoB(W7fubEWFL;ojt-AM48@M72gy^uTi3-=a}{wJd$zTDK1a(3nQ zi*?Ds<@+*6{5HeTU@71#qVbu-ue_n2j8#tFS~0~%$B+d zEVfs4$J#jFUDCQ~mlzf47xU;#Xv}jxJDync4sjpPD5G!m)w7dHo2w1F`OFPYvC6e^AJqv0*9erXY2+pTINHP$nhL!!XGU6t?nmmjeOmjj+NUSm#Drvno)hZAw0?HA&HY6e-goFPV;q?utpodd(#i@ZVR5oXC?$Ku+J3nPoaOa82 zW8!oUWq~|AcePL@~18$-)V7`gq-(04|Ifu6i8z z+9Z)If2v<>CkNY2H`Ahw^?@s5NL3Gw?-xfQOyC-$)y4=~;e`Cqp}3i6;EVQ~^)ZkX z|31=w{vFcA1XsH~{R+E3uX#1n>}w9>dY#j&5BsZf{9RYm-*oJa-;ho<)vO0XTvNRQ zl~wlBrSCa`E0v{1#S+?&ew$Q}a-U9{kCKd+imGDnU1#}24n~kR8}N~*;#L#O*baLz zLiWMo8h(rX%>9>|a$_04AWbLhcb(h8m?52||8ZD$+9vxuV0*1H5*!d=kcE?*VpMSqHXaS|!ht>@7@UU`}N*{5_T`D4NTr`NNT zQwvchw|STB3Lz?6#d`NM!(dKH9UX~1tz1n#*SQX7Aq8HwuQ}s;{->xZ_VMBQeS?1> zIWxjR3;S-=cSc(m?>~9_=zQ~r*Z!sKKLVA5GEU~rFEvh8=@B1fj`q&Vdv-Ch(x_SNCn z^M6c#l-NZ)#r&grnK7(r;caea8_Bw_l2P<^Qpk(*kVHHD)Fh4O1}uX^{d4- z6IBRI^koJ2GvC(!t5?8129jsZ^@Ee;V)sj;&j&7qYds&fVSi`274ywQifQRB=*i5x zNYKX%Uf2DZ_!X}+E&XqoEeIfgywI(@JzWddAiRFD%|y zm6)JW)S`G`iwZqrt8K9@ct1@|Q6g63`MgD+$Mdcic$-iaN?TvRtNOGS^l1@~dZJZ{ ze|9$6ESt&h>@KjO@?M*3=I{Jt{`~oW=KoV>1eq}m!;FQ0(I~^b!r;GRM)}VN|E#;d zW@TQ=TDRLP?8)nPJ7eDbrH+f2)LgKndZA->^`b>JwT@YH9ZTvKIp!~NxTlvp7S_y} zTbPqGVRr#WSqa0;xh{ucF19j^Cy)PbGgFu`F(YFVQwZO=;k$iu8OFxof6T2c!wBy) z(75rR1wP=;Q{YR4$2RZ{!otDC+sEI;U&Oa7r!e=S*U%K^jjRMDgSpK5B?+G+Q<#@_ zW~l$3Zk@t7GRVId*3P}G7GD3{---u>a33peQ^k~370#)ytwzH{u!@B?2U;cVikYfV z;llZI=3hMjvJg*X_#L%Bba1<=p~58?pyzk=0Qmh6cs*POuek7nnp*U}f*%Nn;T3X& z;T0Fon?GmH+(nG<3TGb`+A z-1fy6r!eqc!Al(D477U!Vfb+U3sabABy1_L=GIKB$;4{~PglbHAMVn=hgE1-0sb8q^C82rDK^RSbvqZP@}#h7;gC*H0Ck6&?$krT|5^nVh~XCIF#**M6L@kTF5ZY ziIWZhl>zw#8G^6dENiA>P=Jt>?7MDw5cab)&R zftFr@-fG$(2JM3e@yEfY;LjTeB;n8BwEVf`02zP2CBZ;E0UqIR5p#I99DD$QEx?jg z?EI%N)(_TdG%waSf9mGOhlxZUiGG{q zy8Z9~(FCZqfV|h1CY5!xV9@E5zFWLOWqIm*Oy~N71`ar)nOgr0e1lO7wOO9t7h@*O z;>Y+|%%0phF#)rPGkThYf0!tX_Z5laZ)FqoNh4XG(63jbGUJ+Eydvv8CdyCR_$b+S zOf;@J!cz>5x3?6lV7IFII-6#`(#y~B`>2+m{PdEa1$%3jpQ61r%1{32lb=NT-jqn+ z>;egWZ&9Q##iOrW>`wrhyA&5+t`408I3b0fp|g zVT$vF^h7tx2zV}_E7DhylvO;v8350=?vX&fR^d^x=L8YGb<^+PMEb4WQ$jx{OQIhl z1rV#va?hS5vHT>3SjI`z0!M|=Yt}@R}Ks(zcbhbet*8Zgx^I9 z{DwX0le?4n{SJxWxbHb_mPL}j83}#AAoWf32EcANu|b<<7XhC3O^V3$-T?~zwGU#h z>iHZ}XEf*X+ofW?{M|p1zyC~@@b@%j{&q|z@wZozzc)=L@%P`<{Ka1ae|ff^Fj?;Q zAPd1Hp*0Lv)80lt|KC!_FAL_EGXI;@!TWhY#(%&!hPZ=Yl<#Ep9K7OAmFdOwL-Xn`r+y00{WmOeIjqShd0`=*&7EKGk!-L-{&V0 z@Rjly(Bzw6JBh?*>Hep*67}bUhW=hQ&<>-hM1dJ^{c5gx9ZxBj2Y^72dPlI^hAdC0R?P)CDy|&kB@sjKZ{r-a+x0CEQc2nMO%-D{! z-*}a@XE5aaLC>~?^9Ph-<|X(Klx8PA{OH*Y?b@viR%X#590L-`+J68WDe;eRKi%>O*<`Kn+K zR^(~72TAPM5kRP>uOmt`U-i;ICvpB&=1S;4PI>XNs{cKD;bg4`V(9}l`oHk2gjyU((RxuuOic!%~Khke1 z;-imvy@s8SdNV->~ z=lxTiCtHw}guXLbGJTVtC)+bicAl(PWv~^LUv=_eD>^VN1Y6O=1eGiv!I(z~x{^36 zMqi&w5q(XZKkp6j{B>gn^AzrICh`|Pqws6Js^AQbJ$~DOc6l+>9z1zGZ9H~|@g#e2 zwPL((!#ExBSiGzzJGOlNIL%HQSbqripfVHp!1lS>;<3{b`Mba(;cvAve~+|~`1_Y4 ze+R~r_?yF$`AcH!N&yr1-22?St?@&~nMaDhn%`uwEWsdx)=qjSo}W)GBRSH_7XV zw)kQ2uCG15ejym*{w`ha51!7Xo4o3~KR7FD*Li=?0T7In*Aa;i+X$$Y4A@D+K)vlK z#~gr`K>LX>iy$%v98)T?=yx+h7D2RwO5{ZbV5Qzl(p>=Y_ z88+69Nb1DYBt#{(kw2LB_Y>>?#&kEQ{xa--;saakl=l-CKo7kZp`P<)jx;!5=AiVS z8-M@ey+7@0?_W65>;A>}{dTSQFP?|4n(kjXwxatNH~wMQeE%Z!|NRTg$-mnv?_cZy zJq);i5&HlB1s%VY&+~?c_&jeYP0#br`^8R-_dC+){O8YhZRbC6KXUxsktXLqHxu!f ztM<`w{_`h0)%j0oPJ8|{Y}WIis^f<@{%F@1KMegJKTH)r-0ugw0rA6czPIa&ABO&q z9}Z3YFzzOmnpBYgqmSnpcKsH4{S1q|!%}YL!AJ-ehmT?V-cR{F{Frs$iI1th)JJ*> zJY&%KZcpe~cdFMkN=R2M|;wpz~dMGFoc#`@6h>^2)k0q>tTz>~yZB zwFEGA7j?MVP=A-3jV$Y7d@!P-kh{JWEZq~Q0U(BZ&^2AOd=X#00Tv|_oq66W9M5Il z2vi_k5?JN*cXR%YoEu017svMY$Ld zE(T;Hvl#*5;y@+|K(c`_AGnJ?a|b@|?^^Z^Y9`Qz-CV+k0jjg8)m`7UBFHpFqavwJ zZS8GGXIshgNbitXnRsB~^{*ukiH*t!76MGUBceAv!PH5@oPV#`mMVs>u~3(i;?Dtw$`v|#6;*68G`!W*bt+YoXZ=noD zd7IQjlDCSCp}dgtP`6%vdVV)apNcZeuYl$Gbu(a)ZnOsSQj19I&KCioHoh$+>OM5#)FWFotrPc^0q-kcKdeN&u2vbi<= zUsdOY?)+Ef`J*Ce7#t$~{r3i1{k?j_$ms9WHYn+Dm{LfA*PUvd_D1|s(%<9!Tae%z zr@yQ4$k2QMu)P%{r@xi`&0tM43Hw$=FYqlRal{Xh{6^Ye%1<+u4LZ#~z!eEL1mL+knU^PsPB=hJNf zjqZH8t1E)g1SE zmqzS{-RIpaLMLBxb-p#o7Z82meMJ%8-b|IBSuelVN$P*J zN%-}ia=xr9+DQC*Nn^gOg0?sVXe61uU)@E`{epdI?iU!Rp(>+c$|Mc_EHnjY+>EoaNdQod%u~XKIx(9k_trvA3^fjPf z)b_8?ScAPSdaOb4*P6Iql>YjE&9wWgdzz{DS0ewLN&H`@sUDoQna27ZzWn{_;O%CN zzv})(e*W{4gr9d(i6O3m_8y^=~^+J zD=)`CN0b+GMAc04k}Cf9tev*r{bA1%1A!EZ#He});wYtv-SUhCF^V~Dg8i6Ae;M85 z)GUYk&$sQ8Du2TFPbJBp@Hg4mtVo(a;jyP=`4hgS3%<^IN}Ka#zo!fgzV?2E!a6|} z`tDcCc+q$R>lOTvD&NRXPbT5#8d`pS{Dh33EgFNfjZf%s21@Un^^Q}0K4=Q%`JlrP ze}-S*==KlNJx-nf&TR26ppcc{<(B4;DaYWO+2kpIpe1VNZiHcoNe`SXK$T}LFn2w~ z_?n6wWC$_99U%@K%#{BEI>Obf7+YLDj&_CNu^cPGR=oFWGyu7K%L$(c|fb>#QB(Oa`9P}m) zFON)N%KL=YA&w7N`S^e-?-NSrr;T!c+R|x$+NkCy{e9Hzp}miqO)Ee8{Tr8vqEXJf zWccgLgHMdlBqfJKl!@8vs3(UcM9liQ0l377zsyhbPS7 zw1x+g5R~VO?t2vLQwn?x=C3uz-)tKGS`+!3Giv#3aNJp*z*1`I#Gg{Gj7zIb_E-0}f4D1?orDjXt@2gjvXYk92; z280+CM|Q+XBs)TWeyMK-4t6IAbKClEA8vceDJu?Qm6matO2SNLAH7Ur9~sZTYzqG6 zjAs62rzihx6#UB_&HS^OpMOT@C;z=sTYmC48>#Y>^9dx8r!deF-pFo1%VP+5OraD51SKyCenTx4r$sJ}p0uVQ(F`g#>MLcI!u&W}t>bsp&R z>yw-xxrnx&%AMEC&X4@1x}FLaZZ0m|AlZ`IDy7vzaTO@D5-Y9(byh~8zDl!!^;LeS z%5UqZt*>Hiz9F*w+*hAOe(ojkUb|w`SO17U-%z=Dp2!sb<~+^*=50o|zd3JI`kRv; znJl`Ns{UGr*c|Ce4OSb1OR(CYJHR!l9@|LW|Md~*sZC4KVy(&^n&wk!4`mOsKgRmh z+Cw?$Zs(BOL$%kaw}-N6>>>L58##&hJwLyi>b@t`n|<gHJd!%054Ib+Oz0U z!&!fJgAw&-$E&YD+cYqo^@b5GME+kT;r~OV`#ZEsL6BZ(Z$$m(Q|Rh9m(kR3Hq5`2 zN8w-EW;FYk@SOMP(xYR{f>g{Eeqvs@mU(ELOfCA~NXSb9oqdAmr@))4!1|sSkPG0heH| zPIrbkWnwQQvV-HtF^zLCntjGeAX1QVRc7V;B1ROKgh z5b_iD)T+);NFaJsEm2tII?9~&J9N~md3Y&7q>b-2U!?ZFzuZJere$8Z<-&R%s$E%_K&r}oE{}JukY8Cb@UVnweJlGN2P+kU06q3b1`kQTG5&X6e&WK054?ycnHNYKJ*$>&a}Mt*<`M%Go;W^VH0y*dUHn zeV&e4YU$ZD%b@x^XV0S8Iu37ro;Rv#>+{@DZBTt4XSKEzQCVU?#1o=`jmr8w*;Tal zc|JSe==wYj=ci5qHFXr%=ehAb+WI^*&KsHfJU!>?_4hTMOQFuUlKMP1R?_B=o>4h6 z^?7<`s#$dYug{~YK2QA&nvCpc3$20u$tTa~sn4_E9D?i4eD!(46;$UfZ>^A-9i*r= z0-zMN1~7cYdIF0aV^k+B&2ry<5Iq;49Vd|X@H7?Lc)vAL_%1t3oBev?S?cW9w5bnt z*_pcR!C}nLshhrZkE6h!5Wmh*AHUAl7{4~QzRH4f+WIO-mTRxCGH|*f^;KFjGe>kR85f8Yb@l)3 z@lOk$W@muP*8#XAw*3%31iCJP6IhnJ;ca8iMex6ejbvuRH(x$A83s4jwkkfKpg|9O zS--lr2?n$)1CW%YyEGqqKOgIjpWL?|?;kBk@Xr|&drsenfv&z= zWANeqJ1XxZzWE<_R|DTvb;a|NreCza;#x#3P->Z}indO+cGB5LlafMUAc%m7OkAeh zbWBn~W!mx@%riu*fFd%#hv?jN&J_`+NeXE}#I`61Y^>$8p+ifNwiIagKli?Tl~U2U zPk%P=-gD1A_v?Sox$oZlE)&DY*2RhZx)vqm9%>^@EF_fO%a#7 z+r9CKSlYcYj-qi0jc@mA@t6PySK)?Rv$1tWU!g!H8R)5=|Botvzow7!DFZv8o)DGi zxxVrMmOaktZ~b9JX8T(R_4Vz*EN9Wo;RJEYtQ5tU+R7dntFcYM3yQiQVRp%LQz%c- z9U**^ME}~*dJ2!<9Egrz6poX$(s3lIkW1?M{^2T`8$qrtL-w)A9!f{fjL4is$cYk# z|GwJy3@=ZJ?BqxK>SVb#$5%8~@D+^{A~8Y(PB;leBN`V#MNd4G-pIQ5YU&W%^s~Db z^$+#m+#daR-TC+5zoNfv<0JdasrmORQmKV?+_zcBO+2gPScm;WT|fE{^_Tknvn8^d zYQ7SzYV@L-I|Q)2z5qrgQjg8;!#vFFF^s<(7x5X-n7&NIE%c&eg-wX);_om_^6!aU z(ccrjQyGp=r4BOWwkzAY|5QGOjVaWO(4dnG0@rIa_g)SYZ@pwF_IC6?!9%P{N-?|i zGZB=WsrX*NQj2~S$b+`JiUh^O^1CWEje$UU6N!W)>td3whNN`lQ?@|XC1F`9y~uKV z8sp_^ZGtb?t?Vw}Pcq9}s8YO|<)@g$5s`m48p z5O06DjG4WZ(JEI9eO8x5UBX4Bheh>2OBC83uIu^V7ht=s$Kgy&YUsnlNlaTEMUzDP zuT&Ft4%MB+RQC$*QtEqiDSR__bx1!Mt9>m(XX!BX`)vKil9(CQD)BVMc^YE{55i+f zwLFb_78wT}LnKD11*gRoLyz&U7zZ8`TrpW5UE=cHb4inG$> zFky?Mu5A5O1E?;eM!-~EwD0XlqLS}inNA*wH!&qYPM0=iX_J7dJf>(yYaSdn=F z84qX9cQ6gB^K{0TSNLKCL8${R4>V7w-qRIpUSXcoKyR!l=&J~Xt@@y8PeN@?&kCL(9MXP-WyE}_+`$ep0K*g^9Z zx2DPlwFF>zAK#l({T2k;p{s? z**~G|T4YyRM7svkPp1uDEtBse@NlwJiL{{3J-rk;a?#fg@cV82U~(TcqfR1_kuX64&q>Q z5C?06IQTP(yu7DKFBe%G6j>D%xgjXh6BJn!6#1v1$n_+0=jk3I-CU$EDDv%~$ajMx z-w%r16cqW#pvZM3vH~JuMBYRgmZ04$h!s~RkhVDA0*Q3jKo+T6pfP*Mk(9!jk+PIh zhEqbJ!=@Efy6hIL!vlgMU!SFIMj6W4t@-+n8=lMvy5bWF+iR&_O^? z@v5QT8~0^~T5zkTnwjatdS<4*%ea}|uuL`6Pw7KudZRvMrk_MHsz3eb?6p{7=?Vn= z)A0j)%Q~ek*eUyXr#KKsS_K|Rp$8*BLA@{x`M3tp^)x1X4kaL=3<*HQB1#u}Nngm* zAEflAlPH&WA3-U{o!~=J&u|W+jzXUfi^UUNnnzF8qdQzNpRgY7(#!ke07_J;(BTu> zgFR{=g&7U_F!gA@WoAE(Ybqw;orqbL^@-V()X;1Kod&)Wbt0O7%E_<_DqdI~GjAd;`L-=NcINWtp0%(CS+07km9 z^|O%<@UhIcc=#WqMbsPeZeo)Z%IBZn&TY9* zUQn!I-kkcqE9~x1h1|Vcb@iHT>T?X*X$}k_o1{_U{(kMM+m{;`BIN-FEK>b@sjQK~$07lcyhQtHwZVJmh_g|?@Opa)XOc-6}LNaYuESUVZ^ z;xKEh1@RFQV}Tf0${YhTA!6026qpZK3~oEvdZxb`=HCcUiuRJdu9wPA3Kwsdn~s%x zsJEs3E#i{fVf+v%6;=B^1#!Oq>EyEAg^p_le?ixmi@Ng*E-FYP@1X22IVJT|7aLDRJ=!Aau^2A0*}H<@uDjv zN0LCc^+z}M({N3rBA`aR6t&b~VG#v|@tM5S6tK zG0z~48tPcuPI3Qn(sv&eP}5Bl$5?sa^%zsnJ{^YD=RbHdlzf_!>X17q{u5SWopm@Q)_c0*;u` zYB#0E%1zftq{ejimA9a+ue?K9s(b^VrBO#o3b{4(l2%;1l<3VLP7e3frKgCW8LV8l z&=9gQOSySd>R{mH#)lj-GjENjdP;XB1;x@B%!6cqayNske)o0h5y&$-Vg!HnF`o4P z*-6tn!tc}4lpDjZ$lLzJUy((JAtwBO9K+s^ue&!@tQz@CNK;zXm&jYBnRxxypPUf& zzMS@mGaXQmg?8c*M-N>6QC4u~Z3q_~cR{ke1@=uYVM_nUlCgx>3|K-o-x!N1)(;YV z2|F<0#MAO9xXcu=WBuWMHZc_6Tlxw;-ta0uhiqmp6z_w*q~kz9h-EZbdSOTjF$@Dk zS;1shQzaP@r~bN}VKf)4dL&(AcbBG?6}*a&rnH~6{A4SpAM@DUM*lR3)`BEGO&=Fe zqb45FS__8g#l_Qf)R3x-$k3pB#^0!T(a^c}WHb1zwVTptZbdsJ4A0 zwAK@vPK|@Ku!+#8qv$9TRV%s-U}4}1>u?pU(NK{2ln$xugHyUYb$m+KpPqtprgX5l zw#IekCJ3WBE40&Eeh{;|O-d)`gqt9jEU!9e4R)Pvj{g8Ǔgf4M6D3GI1_hk$-( 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 z

    WJT>}9Cl)KI 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%0A-uO*)OElZ$ zG*Cny;c9hV*|rA)9!|d-C|8~>Jh}be^k256LExnw z^9vR8oFU1OF7XksY)E%vL%P0*E8&G=mH`AlmVLNJ-YU5TXTM~>aOO7?vF z)on!YDd8VN$UR=UlN>Ulf3eXh&!N7maOd7bWV=GBU8$TxBuPIPB8+!M8pQqqHW@2 zdAbywM80t7p5?v&(VcxG+1jlhBQ|=e`h61v-Mk2?w z_!ONe_-?6SW)SU!&Znq_|5wdxcwCydCaxT|Q|ST5)93kai53Z~^KX?Qw=ITeE?6Q& zEv$7$7B;##x~372%^>MRMi{9X!5zaxlj~HeRVF`#ev$MFW{=~)pyg|RXAkTbv_p*p zq-+@q)(}Ghc$A?rugVoejZjRGFj>b4*(os!E=l|ARHpd9Ai#5#)UACVw+&Isu^t^(s&;RuVKgQ)t*53|r096_G(zPZ*yF}5B_7_R}-O_$Ug@RBk zIW!e`vDetOqHs6|W(XcyHmD``&ov2ETia}U(k`{y4Z~Gy*2E23Bbz|RFS$x=zjOlR z`kHiS&eZZ_>6yMp7euijU_z#69#>8q#(EWuZJpw5+A8>$F8J318~@T3{>2?UlU)s& zEe!@skb#VeS`4fa*+NOdz_c|BJy7=kzhKRt@*8_H1Ua!6sFOG zBYqzi*8VA=N;w>QHc(ynt90C>ag`tey%f770+NP*#2QFpZToqjD_(lMT=K2apwMYo z$liFf8hRFWrbTi|?2V7bmP!6kK{L~mq{ly^>OX0`@IU%S_AMV@IuCz2K6yU{fe?G< zAANMg+_pL@aITjvFB|{4>%W zX#yz8%Xpvh5Wk{pewiHX6kRqY7iIh-S&pb=PRVlIrM@;=wLC?Z!$^}Tnn{?{o#dl3 zdcATmwj76fof^GQ(?{>~W%lUZi;M!JSCtq&pkkNRbdHRkW;vzW=*7|b{bf5|RGAOh zXc1$0GTDyKrT?E~JANPyh$#6Z4m(#uKCHLjwm7{AQ)$b~opYiH$1g7*z>`gCD=5K| zQ0hr}Q9X}4rg)CcxBmE=xYdbG2d$h`WWB*+K;+7{2H9EVWvym(R{7*%-sVLP9Z{Ay zkb)H~&3fW78LO(&=&|MI*}a?7ryaa;VT8l@ET@Rz*1j)U_!mM0o~~(99xsEj>MbSTEUz2>`9)A zq~70#2yORTTOUr=-M{T#;wM{AePulU15@#-{b2JRA%b`6UNz3K5w49~!=w>R`M6#1 zy?uqjIj5TtZjPd}h^Zw8}G)3e4i(_OQR$%#!W zEnW?tbd%5|XIW3Z@jVPPmMxeJ9-F=XxO04j;>+ zy`nM@^oI%}hqLO2Tv4v$xYs%QweIZaIFvG4SH>R&qeQc!+{w@Q0s6rn z!CarXUP0MHAY}3FL|~~7ZONvac-3P%7REY|IFJwgXVsi$R*v%6?ny&K;QtQ1v@JeP zBC#n|qDZ_x&$_xxt7E>BED^%(;O5pfr)*)#@$jjn$(R%MU27?Z*6EsuE<*dW>Z9hPu>x zH2RkC>G!;=Ejovd!9r51?Vfi-ZaeiIeN*4aCy8RI(x&u{FX!{g%FgW%_n)fBbM46Y`I5IpyLZ}=1IR5Jy^X76k0&7j2wyK8j#ay@ zr?#ZS;J⁡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