diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 096632fec1..9827265f2d 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -672,7 +672,7 @@ namespace BizHawk.Client.Common nextEmulator = new Atari7800(nextComm, game, rom.RomData, gamedbpath); break; case "C64": - var c64 = new C64(nextComm, game, rom.RomData, rom.Extension); + var c64 = new C64(nextComm, game, rom.RomData, rom.Extension, GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = c64; break; case "GBA": diff --git a/BizHawk.Client.Common/inputAdapters/InputAdapters.cs b/BizHawk.Client.Common/inputAdapters/InputAdapters.cs index 70bf6ebdb3..46a31fb849 100644 --- a/BizHawk.Client.Common/inputAdapters/InputAdapters.cs +++ b/BizHawk.Client.Common/inputAdapters/InputAdapters.cs @@ -383,238 +383,139 @@ namespace BizHawk.Client.Common private List _justPressed = new List(); } - /// SuuperW: I'm leaving the old class in case I accidentally screwed something up - /// adelikat: You did, the autofire feature this was controlling, putting it back, fix your class - public class AutoFireStickyXorAdapter : IController, ISticky - { - public int On { get; set; } - public int Off { get; set; } - public WorkingDictionary buttonStarts = new WorkingDictionary(); - public WorkingDictionary lagStarts = new WorkingDictionary(); // TODO: need a data structure not misc dictionaries - - private readonly HashSet _stickySet = new HashSet(); - - public IController Source { get; set; } - - public void SetOnOffPatternFromConfig() - { - On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn; - Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff; - } - - public AutoFireStickyXorAdapter() - { - //On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn; - //Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff; - On = 1; - Off = 1; - } - - public bool IsPressed(string button) - { - return this[button]; - } - - public bool this[string button] - { - get - { - var source = Source[button]; - - if (_stickySet.Contains(button)) - { - var lagcount = 0; - if (Global.Emulator.CanPollInput() && Global.Config.AutofireLagFrames) - { - lagcount = Global.Emulator.AsInputPollable().LagCount; - } - - var a = ((Global.Emulator.Frame - lagcount) - (buttonStarts[button] - lagStarts[button])) % (On + Off); - if (a < On) - { - return source ^= true; - } - else - { - return source ^= false; - } - } - - return source; - } - - set - { - throw new InvalidOperationException(); - } - } - - public ControllerDefinition Type { get { return Source.Type; } set { throw new InvalidOperationException(); } } - public bool Locked { get; set; } // Pretty much a hack, - - // dumb passthrough for floats, because autofire doesn't care about them - public float GetFloat(string name) - { - return Source.GetFloat(name); - } - - public void SetSticky(string button, bool isSticky) - { - if (isSticky) - { - _stickySet.Add(button); - buttonStarts.Add(button, Global.Emulator.Frame); - - if (Global.Emulator.CanPollInput()) - { - lagStarts.Add(button, Global.Emulator.AsInputPollable().LagCount); - } - else - { - lagStarts.Add(button, 0); - } - } - else - { - _stickySet.Remove(button); - buttonStarts.Remove(button); - lagStarts.Remove(button); - } - } - - public bool IsSticky(string button) - { - return this._stickySet.Contains(button); - } - - public HashSet CurrentStickies - { - get - { - return this._stickySet; - } - } - - public void ClearStickies() - { - _stickySet.Clear(); - buttonStarts.Clear(); - lagStarts.Clear(); - } - - public void MassToggleStickyState(List buttons) - { - foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) - { - if (_stickySet.Contains(button)) - { - _stickySet.Remove(button); - } - else - { - _stickySet.Add(button); - } - } - - _justPressed = buttons; - } - - /// - /// Determines if a sticky is current mashing the button itself, - /// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false - /// - public bool StickyIsInEffect(string button) - { - if (Source.IsPressed(button)) - { - return false; - } - - return (IsPressed(button)); // Shortcut logic since we know the Source isn't pressed, Ispressed can only return true if the autofire sticky is in effect for this frame - } - - private List _justPressed = new List(); - } - - // commenting this out, it breaks the autofire hotkey + ///// SuuperW: I'm leaving the old class in case I accidentally screwed something up //public class AutoFireStickyXorAdapter : IController, ISticky //{ - // // TODO: Change the AutoHold adapter to be one of these, with an 'Off' value of 0? - // // Probably would have slightly lower performance, but it seems weird to have such a similar class that is only used once. - // private int On; - // private int Off; + // public int On { get; set; } + // public int Off { get; set; } + // public WorkingDictionary buttonStarts = new WorkingDictionary(); + // public WorkingDictionary lagStarts = new WorkingDictionary(); // TODO: need a data structure not misc dictionaries + + // private readonly HashSet _stickySet = new HashSet(); + + // public IController Source { get; set; } + // public void SetOnOffPatternFromConfig() // { // On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn; // Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff; // } - // private WorkingDictionary _boolPatterns = new WorkingDictionary(); - // private WorkingDictionary _floatPatterns = new WorkingDictionary(); - // public AutoFireStickyXorAdapter() // { - // On = 1; Off = 1; + // //On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn; + // //Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff; + // On = 1; + // Off = 1; // } - // public IController Source { get; set; } - - // public ControllerDefinition Type - // { - // get { return Source.Type; } - // } - - // public bool Locked { get; set; } // Pretty much a hack, - // public bool IsPressed(string button) // { // return this[button]; // } - // public void SetFloat(string name, float? value, AutoPatternFloat pattern = null) - // { - // if (value.HasValue) - // { - // if (pattern == null) - // pattern = new AutoPatternFloat(value.Value, On, 0, Off); - // _floatPatterns[name] = pattern; - // } - // else - // { - // _floatPatterns.Remove(name); - // } - // } - - // public float GetFloat(string name) - // { - // if (_floatPatterns.ContainsKey(name)) - // return _floatPatterns[name].PeekNextValue(); - - // if (Source == null) - // return 0; - - // return Source.GetFloat(name); - // } - - // public void ClearStickyFloats() - // { - // _floatPatterns.Clear(); - // } - // public bool this[string button] // { // get // { // var source = Source[button]; - // bool patternValue = false; - // if (_boolPatterns.ContainsKey(button)) - // { // I can't figure a way to determine right here if it should Peek or Get. - // patternValue = _boolPatterns[button].PeekNextValue(); + + // if (_stickySet.Contains(button)) + // { + // var lagcount = 0; + // if (Global.Emulator.CanPollInput() && Global.Config.AutofireLagFrames) + // { + // lagcount = Global.Emulator.AsInputPollable().LagCount; + // } + + // var a = ((Global.Emulator.Frame - lagcount) - (buttonStarts[button] - lagStarts[button])) % (On + Off); + // if (a < On) + // { + // return source ^= true; + // } + // else + // { + // return source ^= false; + // } // } - // source ^= patternValue; // return source; // } + + // set + // { + // throw new InvalidOperationException(); + // } + // } + + // public ControllerDefinition Type { get { return Source.Type; } set { throw new InvalidOperationException(); } } + // public bool Locked { get; set; } // Pretty much a hack, + + // // dumb passthrough for floats, because autofire doesn't care about them + // public float GetFloat(string name) + // { + // return Source.GetFloat(name); + // } + + // public void SetSticky(string button, bool isSticky) + // { + // if (isSticky) + // { + // _stickySet.Add(button); + // buttonStarts.Add(button, Global.Emulator.Frame); + + // if (Global.Emulator.CanPollInput()) + // { + // lagStarts.Add(button, Global.Emulator.AsInputPollable().LagCount); + // } + // else + // { + // lagStarts.Add(button, 0); + // } + // } + // else + // { + // _stickySet.Remove(button); + // buttonStarts.Remove(button); + // lagStarts.Remove(button); + // } + // } + + // public bool IsSticky(string button) + // { + // return this._stickySet.Contains(button); + // } + + // public HashSet CurrentStickies + // { + // get + // { + // return this._stickySet; + // } + // } + + // public void ClearStickies() + // { + // _stickySet.Clear(); + // buttonStarts.Clear(); + // lagStarts.Clear(); + // } + + // public void MassToggleStickyState(List buttons) + // { + // foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) + // { + // if (_stickySet.Contains(button)) + // { + // _stickySet.Remove(button); + // } + // else + // { + // _stickySet.Add(button); + // } + // } + + // _justPressed = buttons; // } // /// @@ -623,84 +524,174 @@ namespace BizHawk.Client.Common // /// // public bool StickyIsInEffect(string button) // { - // if (IsSticky(button)) + // if (Source.IsPressed(button)) // { - // return !Source.IsPressed(button); + // return false; // } - // return false; + // return (IsPressed(button)); // Shortcut logic since we know the Source isn't pressed, Ispressed can only return true if the autofire sticky is in effect for this frame // } - // public void SetSticky(string button, bool isSticky, AutoPatternBool pattern = null) - // { - // if (isSticky) - // { - // if (pattern == null) - // pattern = new AutoPatternBool(On, Off); - // _boolPatterns[button] = pattern; - // } - // else - // { - // _boolPatterns.Remove(button); - // } - // } - - // public void Unset(string button) - // { - // _boolPatterns.Remove(button); - // _floatPatterns.Remove(button); - // } - - // public bool IsSticky(string button) - // { - // return _boolPatterns.ContainsKey(button) || _floatPatterns.ContainsKey(button); - // } - - // public HashSet CurrentStickies - // { - // get - // { - // return new HashSet(_boolPatterns.Keys); - // } - // } - - // public void ClearStickies() - // { - // _boolPatterns.Clear(); - // _floatPatterns.Clear(); - // } - - // public void IncrementLoops(bool lagged) - // { - // for (int i = 0; i < _boolPatterns.Count; i++) - // _boolPatterns.ElementAt(i).Value.GetNextValue(lagged); - // for (int i = 0; i < _floatPatterns.Count; i++) - // _floatPatterns.ElementAt(i).Value.GetNextValue(lagged); - // } - - // // SuuperW: What does this even do? I set a breakpoint inside the loop and it wasn't reached. - // private WorkingDictionary _toggledButtons = new WorkingDictionary(); // private List _justPressed = new List(); - // public void MassToggleStickyState(List buttons) - // { - // foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) - // { - // if (_boolPatterns.ContainsKey(button)) - // { - // _toggledButtons[button] = _boolPatterns[button]; - // SetSticky(button, false); - // } - // else - // { - // _boolPatterns[button] = _toggledButtons[button]; - // _toggledButtons.Remove(button); - // } - // } - - // _justPressed = buttons; - // } //} + // commenting this out, it breaks the autofire hotkey + public class AutoFireStickyXorAdapter : IController, ISticky + { + // TODO: Change the AutoHold adapter to be one of these, with an 'Off' value of 0? + // Probably would have slightly lower performance, but it seems weird to have such a similar class that is only used once. + private int On; + private int Off; + public void SetOnOffPatternFromConfig() + { + On = Global.Config.AutofireOn < 1 ? 0 : Global.Config.AutofireOn; + Off = Global.Config.AutofireOff < 1 ? 0 : Global.Config.AutofireOff; + } + + private WorkingDictionary _boolPatterns = new WorkingDictionary(); + private WorkingDictionary _floatPatterns = new WorkingDictionary(); + + public AutoFireStickyXorAdapter() + { + On = 1; Off = 1; + } + + public IController Source { get; set; } + + public ControllerDefinition Type + { + get { return Source.Type; } + } + + public bool Locked { get; set; } // Pretty much a hack, + + public bool IsPressed(string button) + { + return this[button]; + } + + public void SetFloat(string name, float? value, AutoPatternFloat pattern = null) + { + if (value.HasValue) + { + if (pattern == null) + pattern = new AutoPatternFloat(value.Value, On, 0, Off); + _floatPatterns[name] = pattern; + } + else + { + _floatPatterns.Remove(name); + } + } + + public float GetFloat(string name) + { + if (_floatPatterns.ContainsKey(name)) + return _floatPatterns[name].PeekNextValue(); + + if (Source == null) + return 0; + + return Source.GetFloat(name); + } + + public void ClearStickyFloats() + { + _floatPatterns.Clear(); + } + + public bool this[string button] + { + get + { + var source = Source[button]; + bool patternValue = false; + if (_boolPatterns.ContainsKey(button)) + { // I can't figure a way to determine right here if it should Peek or Get. + patternValue = _boolPatterns[button].PeekNextValue(); + } + source ^= patternValue; + + return source; + } + } + + /// + /// Determines if a sticky is current mashing the button itself, + /// If sticky is not set then false, if set, it returns true if the Source is not pressed, else false + /// + public bool StickyIsInEffect(string button) + { + if (IsSticky(button)) + { + return !Source.IsPressed(button); + } + + return false; + } + + public void SetSticky(string button, bool isSticky, AutoPatternBool pattern = null) + { + if (isSticky) + { + if (pattern == null) + pattern = new AutoPatternBool(On, Off); + _boolPatterns[button] = pattern; + } + else + { + _boolPatterns.Remove(button); + } + } + + public void Unset(string button) + { + _boolPatterns.Remove(button); + _floatPatterns.Remove(button); + } + + public bool IsSticky(string button) + { + return _boolPatterns.ContainsKey(button) || _floatPatterns.ContainsKey(button); + } + + public HashSet CurrentStickies + { + get + { + return new HashSet(_boolPatterns.Keys); + } + } + + public void ClearStickies() + { + _boolPatterns.Clear(); + _floatPatterns.Clear(); + } + + public void IncrementLoops(bool lagged) + { + for (int i = 0; i < _boolPatterns.Count; i++) + _boolPatterns.ElementAt(i).Value.GetNextValue(lagged); + for (int i = 0; i < _floatPatterns.Count; i++) + _floatPatterns.ElementAt(i).Value.GetNextValue(lagged); + } + + private List _justPressed = new List(); + public void MassToggleStickyState(List buttons) + { + foreach (var button in buttons.Where(button => !_justPressed.Contains(button))) + { + if (_boolPatterns.ContainsKey(button)) + SetSticky(button, false); + else + SetSticky(button, true); + } + + _justPressed = buttons; + } + } + /// /// Just copies source to sink, or returns whatever a NullController would if it is disconnected. useful for immovable hardpoints. /// diff --git a/BizHawk.Client.Common/movie/PlatformFrameRates.cs b/BizHawk.Client.Common/movie/PlatformFrameRates.cs index b093fc2e27..726c5ad73b 100644 --- a/BizHawk.Client.Common/movie/PlatformFrameRates.cs +++ b/BizHawk.Client.Common/movie/PlatformFrameRates.cs @@ -8,6 +8,12 @@ namespace BizHawk.Client.Common // 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) // are we collecting them anywhere else? for avi-writing code perhaps? + + // just some constants, according to specs + private static readonly double PAL_CARRIER = 15625 * 283.75 + 25; // 4.43361875 MHz + private static readonly double NTSC_CARRIER = 4500000 * 227.5 / 286; // 3.579545454... MHz + private static readonly double PAL_N_CARRIER = 15625 * 229.25 + 25; // 3.58205625 MHz + private static readonly Dictionary _rates = new Dictionary { { "NES", 60.098813897440515532 }, // discussion here: http://forums.nesdev.com/viewtopic.php?t=492 ; a rational expression would be (19687500 / 11) / ((341*262-0.529780.5)/3) -> (118125000 / 1965513) -> 60.098813897440515529533511098629 (so our chosen number is very close) @@ -48,6 +54,11 @@ namespace BizHawk.Client.Common {"PSX", 44100.0*768*11/7/263/3413}, //59.292862562 {"PSX_PAL", 44100.0*768*11/7/314/3406}, //49.7645593576 + {"C64_PAL", PAL_CARRIER*2/9/312/63}, + {"C64_NTSC", NTSC_CARRIER*2/7/263/65}, + {"C64_NTSC_OLD", NTSC_CARRIER*2/7/262/64}, + {"C64_DREAN", PAL_N_CARRIER*2/7/312/65}, + //according to ryphecha, using //clocks[2] = { 53.693182e06, 53.203425e06 }; //ntsc console, pal console //lpf[2][2] = { { 263, 262.5 }, { 314, 312.5 } }; //ntsc,pal; noninterlaced, interlaced diff --git a/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs b/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs index c4012ac9d7..c1a6801f47 100644 --- a/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs +++ b/BizHawk.Client.Common/movie/tasproj/StateManagerState.cs @@ -1,4 +1,5 @@ using System; +using System.IO; namespace BizHawk.Client.Common { @@ -15,6 +16,20 @@ namespace BizHawk.Client.Common public int Frame { get; set; } + public void Write(BinaryWriter w) + { + w.Write(Frame); + w.Write(_state.Length); + w.Write(_state); + } + + public static StateManagerState Read(BinaryReader r, TasStateManager m) + { + int frame = r.ReadInt32(); + byte[] data = r.ReadBytes(r.ReadInt32()); + return new StateManagerState(m, data, frame); + } + public byte[] State { get diff --git a/BizHawk.Client.Common/movie/tasproj/TasBranch.cs b/BizHawk.Client.Common/movie/tasproj/TasBranch.cs index 47e416a9e4..b290831ff5 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasBranch.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasBranch.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using Newtonsoft.Json; using BizHawk.Bizware.BizwareGL; @@ -22,6 +23,19 @@ namespace BizHawk.Client.Common public class TasBranchCollection : List { + public new void Add(TasBranch item) + { + if (item.UniqueIdentifier == Guid.Empty) + { + var currentHashes = this.Select(b => b.UniqueIdentifier.GetHashCode()).ToList(); + + do item.UniqueIdentifier = Guid.NewGuid(); + while (currentHashes.Contains(item.UniqueIdentifier.GetHashCode())); + } + + base.Add(item); + } + public void Save(BinaryStateSaver bs) { var nheader = new IndexedStateLump(BinaryStateLump.BranchHeader); diff --git a/BizHawk.Client.Common/movie/tasproj/TasMovie.cs b/BizHawk.Client.Common/movie/tasproj/TasMovie.cs index 960723ccd6..a5a99e3894 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasMovie.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasMovie.cs @@ -22,7 +22,7 @@ namespace BizHawk.Client.Common private readonly Dictionary InputStateCache = new Dictionary(); public readonly List VerificationLog = new List(); // For movies that do not begin with power-on, this is the input required to get into the initial state - private readonly TasBranchCollection Branches = new TasBranchCollection(); + public readonly TasBranchCollection Branches = new TasBranchCollection(); private BackgroundWorker _progressReportWorker = null; public void NewBGWorker(BackgroundWorker newWorker) @@ -82,8 +82,21 @@ namespace BizHawk.Client.Common public bool BindMarkersToInput { get; set; } public bool UseInputCache { get; set; } public int BranchCount { get { return Branches.Count; } } - public TasBranch GetBranch(int index) { return Branches[index]; } - public int BranchHashByIndex(int index) { return Branches[index].UniqueIdentifier.GetHashCode(); } + public TasBranch GetBranch(int index) + { + if (index >= Branches.Count) + return null; // are we allowed? + else + return Branches[index]; + } + + public int BranchHashByIndex(int index) + { + if (index >= Branches.Count) + return -1; + else + return Branches[index].UniqueIdentifier.GetHashCode(); + } public int BranchIndexByHash(int hash) { @@ -524,12 +537,6 @@ namespace BizHawk.Client.Common public void AddBranch(TasBranch branch) { - // before adding, make sure guid hash is unique too, we can't afford branch id clashes - do - { - branch.UniqueIdentifier = Guid.NewGuid(); - } while (BranchIndexByHash(branch.UniqueIdentifier.GetHashCode()) != -1); - Branches.Add(branch); TasStateManager.AddBranch(); Changes = true; @@ -545,6 +552,7 @@ namespace BizHawk.Client.Common public void UpdateBranch(TasBranch old, TasBranch newBranch) { int index = Branches.IndexOf(old); + newBranch.UniqueIdentifier = old.UniqueIdentifier; Branches[index] = newBranch; TasStateManager.UpdateBranch(index); Changes = true; diff --git a/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs b/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs index 1ecce08ea7..41fca12525 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasMovieMarker.cs @@ -218,6 +218,8 @@ namespace BizHawk.Client.Common { if (this[i].Frame >= startFrame) { + if (i == 0) + continue; _movie.ChangeLog.AddMarkerChange(null, this[i].Frame, this[i].Message); RemoveAt(i); deletedCount++; diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index 1858794af2..062b27ec4d 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -221,11 +221,20 @@ namespace BizHawk.Client.Common /// private Point StateToRemove() { - int markerSkips = maxStates / 2; - // X is frame, Y is branch Point shouldRemove = new Point(-1, -1); + + if (BranchStates.Any() && Settings.EraseBranchStatesFirst) + { + var kvp = BranchStates.Count() > 1 ? BranchStates.ElementAt(1) : BranchStates.ElementAt(0); + shouldRemove.X = kvp.Key; + shouldRemove.Y = kvp.Value.Keys[0]; + + return shouldRemove; + } + int i = 0; + int markerSkips = maxStates / 2; // lowPrioritySates (e.g. states with only lag frames between them) do { @@ -263,9 +272,9 @@ namespace BizHawk.Client.Common if (shouldRemove.X < 1) // only found marker states above { - if (BranchStates.Any()) + if (BranchStates.Any() && !Settings.EraseBranchStatesFirst) { - var kvp = BranchStates.ElementAt(1); + var kvp = BranchStates.Count() > 1 ? BranchStates.ElementAt(1) : BranchStates.ElementAt(0); shouldRemove.X = kvp.Key; shouldRemove.Y = kvp.Value.Keys[0]; } @@ -289,7 +298,7 @@ namespace BizHawk.Client.Common return _movie.Markers.IsMarker(States[frame].Frame + 1); else { - if (_movie.GetBranch(branch).Markers == null) + if (_movie.GetBranch(_movie.BranchIndexByHash(branch)).Markers == null) return _movie.Markers.IsMarker(States[frame].Frame + 1); else return _movie.GetBranch(branch).Markers.Any(m => m.Frame + 1 == frame); @@ -356,7 +365,7 @@ namespace BizHawk.Client.Common { if (branch == -1) accessed.Remove(States[frame]); - else + else if (accessed.Contains(BranchStates[frame][branch]) && !Settings.EraseBranchStatesFirst) accessed.Remove(BranchStates[frame][branch]); StateManagerState state; @@ -503,25 +512,6 @@ namespace BizHawk.Client.Common MaybeRemoveStates(); } - // TODO: save/load BranchStates - public void Save(BinaryWriter bw) - { - List noSave = ExcludeStates(); - - bw.Write(States.Count - noSave.Count); - for (int i = 0; i < States.Count; i++) - { - if (noSave.Contains(i)) - continue; - - StateAccessed(States.ElementAt(i).Key); - KeyValuePair kvp = States.ElementAt(i); - bw.Write(kvp.Key); - bw.Write(kvp.Value.Length); - bw.Write(kvp.Value.State); - } - } - private List ExcludeStates() { List ret = new List(); @@ -556,6 +546,41 @@ namespace BizHawk.Client.Common return ret; } + public void Save(BinaryWriter bw) + { + List noSave = ExcludeStates(); + + bw.Write(States.Count - noSave.Count); + for (int i = 0; i < States.Count; i++) + { + if (noSave.Contains(i)) + continue; + + StateAccessed(States.ElementAt(i).Key); + KeyValuePair kvp = States.ElementAt(i); + bw.Write(kvp.Key); + bw.Write(kvp.Value.Length); + bw.Write(kvp.Value.State); + } + + bw.Write(currentBranch); + + if (Settings.BranchStatesInTasproj) + { + bw.Write(BranchStates.Count); + foreach (var s in BranchStates) + { + bw.Write(s.Key); + bw.Write(s.Value.Count); + foreach (var t in s.Value) + { + bw.Write(t.Key); + t.Value.Write(bw); + } + } + } + } + public void Load(BinaryReader br) { States.Clear(); @@ -574,6 +599,32 @@ namespace BizHawk.Client.Common //Used += len; } //} + + try + { + currentBranch = br.ReadInt32(); + if (Settings.BranchStatesInTasproj) + { + int c = br.ReadInt32(); + BranchStates = new SortedList>(c); + while (c > 0) + { + int key = br.ReadInt32(); + int c2 = br.ReadInt32(); + var list = new SortedList(c2); + while (c2 > 0) + { + int key2 = br.ReadInt32(); + var state = StateManagerState.Read(br, this); + list.Add(key2, state); + c2--; + } + BranchStates.Add(key, list); + c--; + } + } + } + catch (EndOfStreamException) { } } public KeyValuePair GetStateClosestToFrame(int frame) @@ -666,8 +717,8 @@ namespace BizHawk.Client.Common if (!BranchStates[frame].ContainsKey(branchHash)) return -2; stateToMatch = BranchStates[frame][branchHash]; - if (States.ContainsKey(frame) && States[frame] == stateToMatch) - return -1; + //if (States.ContainsKey(frame) && States[frame] == stateToMatch) + // return -1; } // there's no state for that frame at all @@ -695,7 +746,10 @@ namespace BizHawk.Client.Common if (!States.ContainsValue(s)) { if (BranchStates.ContainsKey(s.Frame)) - ret.Y = BranchStates[s.Frame].Values.IndexOf(s); + { + int index = BranchStates[s.Frame].Values.IndexOf(s); + ret.Y = BranchStates[s.Frame].Keys.ElementAt(index); + } if (ret.Y == -1) return new Point(-1, -2); } diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs index 2ccd8ba8fa..a575f21e5d 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManagerSettings.cs @@ -13,6 +13,8 @@ namespace BizHawk.Client.Common DiskSaveCapacitymb = 512; Capacitymb = 512; DiskCapacitymb = 512; + BranchStatesInTasproj = false; + EraseBranchStatesFirst = true; } public TasStateManagerSettings(TasStateManagerSettings settings) @@ -20,6 +22,8 @@ namespace BizHawk.Client.Common DiskSaveCapacitymb = settings.DiskSaveCapacitymb; Capacitymb = settings.Capacitymb; DiskCapacitymb = settings.DiskCapacitymb; + BranchStatesInTasproj = settings.BranchStatesInTasproj; + EraseBranchStatesFirst = settings.EraseBranchStatesFirst; } /// @@ -50,6 +54,20 @@ namespace BizHawk.Client.Common [Description("The size limit of the state history buffer on the disk. When this limit is reached it will start removing previous savestates")] public int DiskCapacitymb { get; set; } + /// + /// Put branch states to .tasproj + /// + [DisplayName("Put branch states to .tasproj")] + [Description("Put branch states to .tasproj")] + public bool BranchStatesInTasproj { get; set; } + + /// + /// Erase branch states before greenzone states when capacity is met + /// + [DisplayName("Erase branch states first")] + [Description("Erase branch states before greenzone states when capacity is met")] + public bool EraseBranchStatesFirst { get; set; } + /// /// The total state capacity in bytes. /// @@ -77,6 +95,8 @@ namespace BizHawk.Client.Common sb.AppendLine(DiskSaveCapacitymb.ToString()); sb.AppendLine(Capacitymb.ToString()); sb.AppendLine(DiskCapacitymb.ToString()); + sb.AppendLine(BranchStatesInTasproj.ToString()); + sb.AppendLine(EraseBranchStatesFirst.ToString()); return sb.ToString(); } @@ -88,6 +108,7 @@ namespace BizHawk.Client.Common string[] lines = settings.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); Capacitymb = int.Parse(lines[1]); int refCapacity; + if (!int.TryParse(lines[0], out refCapacity)) { if (bool.Parse(lines[0])) @@ -97,10 +118,21 @@ namespace BizHawk.Client.Common } else DiskSaveCapacitymb = refCapacity; + if (lines.Length > 2) DiskCapacitymb = int.Parse(lines[2]); else DiskCapacitymb = 512; + + if (lines.Length > 3) + BranchStatesInTasproj = bool.Parse(lines[3]); + else + BranchStatesInTasproj = false; + + if (lines.Length > 4) + EraseBranchStatesFirst = bool.Parse(lines[4]); + else + EraseBranchStatesFirst = true; } } } diff --git a/BizHawk.Client.EmuHawk/AboutBox.Designer.cs b/BizHawk.Client.EmuHawk/AboutBox.Designer.cs index 6ce6402235..702733471c 100644 --- a/BizHawk.Client.EmuHawk/AboutBox.Designer.cs +++ b/BizHawk.Client.EmuHawk/AboutBox.Designer.cs @@ -35,7 +35,6 @@ this.timer1 = new System.Windows.Forms.Timer(this.components); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); - this.HR = new BizHawk.Client.EmuHawk.HorizontalLine(); this.label5 = new System.Windows.Forms.Label(); this.mom2 = new System.Windows.Forms.PictureBox(); this.pictureBox2 = new System.Windows.Forms.PictureBox(); @@ -43,9 +42,14 @@ this.pictureBox4 = new System.Windows.Forms.PictureBox(); this.pictureBox3 = new System.Windows.Forms.PictureBox(); this.pictureBox1 = new System.Windows.Forms.PictureBox(); - this.pictureBox5 = new BizHawk.Client.EmuHawk.MyViewportPanel(); this.CloseBtn = new System.Windows.Forms.Button(); this.btnBizBox = new System.Windows.Forms.Button(); + this.tbBranch = new System.Windows.Forms.TextBox(); + this.tbCommit = new System.Windows.Forms.TextBox(); + this.label6 = new System.Windows.Forms.Label(); + this.label7 = new System.Windows.Forms.Label(); + this.pictureBox5 = new BizHawk.Client.EmuHawk.MyViewportPanel(); + this.HR = new BizHawk.Client.EmuHawk.HorizontalLine(); ((System.ComponentModel.ISupportInitialize)(this.mom2)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox2)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.mom1)).BeginInit(); @@ -100,15 +104,6 @@ this.label4.TabIndex = 5; this.label4.Text = "(LEVAR BURTON\r\nCAMEO)"; // - // HR - // - this.HR.Font = new System.Drawing.Font("Microsoft Sans Serif", 26.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.HR.Location = new System.Drawing.Point(349, 213); - this.HR.Name = "HR"; - this.HR.Size = new System.Drawing.Size(158, 2); - this.HR.TabIndex = 4; - this.HR.Text = "COPYRITE 2001"; - // // label5 // this.label5.AutoSize = true; @@ -178,17 +173,9 @@ this.pictureBox1.TabIndex = 0; this.pictureBox1.TabStop = false; // - // pictureBox5 - // - this.pictureBox5.Enabled = false; - this.pictureBox5.Location = new System.Drawing.Point(71, 223); - this.pictureBox5.Name = "pictureBox5"; - this.pictureBox5.Size = new System.Drawing.Size(376, 48); - this.pictureBox5.TabIndex = 15; - this.pictureBox5.TabStop = false; - // // CloseBtn // + this.CloseBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.CloseBtn.Location = new System.Drawing.Point(424, 462); this.CloseBtn.Name = "CloseBtn"; this.CloseBtn.Size = new System.Drawing.Size(75, 23); @@ -200,6 +187,7 @@ // // btnBizBox // + this.btnBizBox.DialogResult = System.Windows.Forms.DialogResult.Cancel; this.btnBizBox.Location = new System.Drawing.Point(-4, -3); this.btnBizBox.Name = "btnBizBox"; this.btnBizBox.Size = new System.Drawing.Size(75, 23); @@ -208,12 +196,69 @@ this.btnBizBox.UseVisualStyleBackColor = true; this.btnBizBox.Click += new System.EventHandler(this.btnBizBox_Click); // + // tbBranch + // + this.tbBranch.Location = new System.Drawing.Point(49, 476); + this.tbBranch.Name = "tbBranch"; + this.tbBranch.ReadOnly = true; + this.tbBranch.Size = new System.Drawing.Size(100, 20); + this.tbBranch.TabIndex = 20; + // + // tbCommit + // + this.tbCommit.Location = new System.Drawing.Point(203, 476); + this.tbCommit.Name = "tbCommit"; + this.tbCommit.ReadOnly = true; + this.tbCommit.Size = new System.Drawing.Size(100, 20); + this.tbCommit.TabIndex = 20; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(2, 479); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(44, 13); + this.label6.TabIndex = 21; + this.label6.Text = "Branch:"; + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(155, 479); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(44, 13); + this.label7.TabIndex = 22; + this.label7.Text = "Commit:"; + // + // pictureBox5 + // + this.pictureBox5.Enabled = false; + this.pictureBox5.Location = new System.Drawing.Point(71, 223); + this.pictureBox5.Name = "pictureBox5"; + this.pictureBox5.Size = new System.Drawing.Size(376, 48); + this.pictureBox5.TabIndex = 15; + this.pictureBox5.TabStop = false; + // + // HR + // + this.HR.Font = new System.Drawing.Font("Microsoft Sans Serif", 26.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.HR.Location = new System.Drawing.Point(349, 213); + this.HR.Name = "HR"; + this.HR.Size = new System.Drawing.Size(158, 2); + this.HR.TabIndex = 4; + this.HR.Text = "COPYRITE 2001"; + // // AboutBox // this.AcceptButton = this.CloseBtn; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CloseBtn; this.ClientSize = new System.Drawing.Size(519, 497); + this.Controls.Add(this.label7); + this.Controls.Add(this.label6); + this.Controls.Add(this.tbCommit); + this.Controls.Add(this.tbBranch); this.Controls.Add(this.btnBizBox); this.Controls.Add(this.CloseBtn); this.Controls.Add(this.pictureBox5); @@ -268,5 +313,9 @@ private System.Windows.Forms.PictureBox pictureBox1; private System.Windows.Forms.Button CloseBtn; private System.Windows.Forms.Button btnBizBox; + private System.Windows.Forms.TextBox tbBranch; + private System.Windows.Forms.TextBox tbCommit; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Label label7; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/AboutBox.cs b/BizHawk.Client.EmuHawk/AboutBox.cs index 0aec2ff897..1c6b3be5de 100644 --- a/BizHawk.Client.EmuHawk/AboutBox.cs +++ b/BizHawk.Client.EmuHawk/AboutBox.cs @@ -37,6 +37,9 @@ namespace BizHawk.Client.EmuHawk pictureBox2.BringToFront(); pictureBox1.BringToFront(); pictureBox5.Visible = false; + + tbBranch.Text = SubWCRev.GIT_BRANCH; + tbCommit.Text = SubWCRev.GIT_SHORTHASH; } protected override void OnClosed(EventArgs e) @@ -160,9 +163,9 @@ namespace BizHawk.Client.EmuHawk private void AboutBox_Load(object sender, EventArgs e) { #if DEBUG - Text = "BizHawk Developer Build (DEBUG MODE) GIT " + SubWCRev.GIT_BRANCH + "-" + SubWCRev.SVN_REV + "#" + SubWCRev.GIT_SHORTHASH; + Text = "BizHawk Developer Build (DEBUG MODE) GIT " + SubWCRev.GIT_BRANCH + "#" + SubWCRev.GIT_SHORTHASH; #else - Text = "BizHawk Developer Build (RELEASE MODE) GIT " + SubWCRev.GIT_BRANCH + "-"+SubWCRev.SVN_REV + "#" + SubWCRev.GIT_SHORTHASH; + Text = "BizHawk Developer Build (RELEASE MODE) GIT " + SubWCRev.GIT_BRANCH + "#" + SubWCRev.GIT_SHORTHASH; #endif if (DateTime.Now.Month == 12) if (DateTime.Now.Day > 17 && DateTime.Now.Day <= 25) diff --git a/BizHawk.Client.EmuHawk/BizBox.Designer.cs b/BizHawk.Client.EmuHawk/BizBox.Designer.cs index 7484442a31..2cc054603d 100644 --- a/BizHawk.Client.EmuHawk/BizBox.Designer.cs +++ b/BizHawk.Client.EmuHawk/BizBox.Designer.cs @@ -36,7 +36,6 @@ this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); - this.VersionLabel = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label7 = new System.Windows.Forms.Label(); @@ -44,6 +43,9 @@ this.label37 = new System.Windows.Forms.Label(); this.CoreInfoPanel = new System.Windows.Forms.Panel(); this.textBox1 = new System.Windows.Forms.TextBox(); + this.VersionLabel = new System.Windows.Forms.Label(); + this.btnCopyHash = new System.Windows.Forms.Button(); + this.linkLabel2 = new System.Windows.Forms.LinkLabel(); ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); this.SuspendLayout(); // @@ -119,14 +121,6 @@ this.label4.TabIndex = 6; this.label4.Text = "A multi-Platform Emulator"; // - // VersionLabel - // - this.VersionLabel.AutoSize = true; - this.VersionLabel.Location = new System.Drawing.Point(198, 52); - this.VersionLabel.Name = "VersionLabel"; - this.VersionLabel.Size = new System.Drawing.Size(0, 13); - this.VersionLabel.TabIndex = 7; - // // label5 // this.label5.AutoSize = true; @@ -196,6 +190,39 @@ this.textBox1.TabIndex = 16; this.textBox1.Text = "jabo_direct3d8_patched.dll is distributed with the special permission of the auth" + "or."; + // + // VersionLabel + // + this.VersionLabel.AutoSize = true; + this.VersionLabel.Location = new System.Drawing.Point(198, 52); + this.VersionLabel.Name = "VersionLabel"; + this.VersionLabel.Size = new System.Drawing.Size(0, 13); + this.VersionLabel.TabIndex = 7; + // + // btnCopyHash + // + this.btnCopyHash.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnCopyHash.AutoSize = true; + this.btnCopyHash.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.btnCopyHash.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Duplicate; + this.btnCopyHash.Location = new System.Drawing.Point(12, 505); + this.btnCopyHash.Name = "btnCopyHash"; + this.btnCopyHash.Size = new System.Drawing.Size(22, 22); + this.btnCopyHash.TabIndex = 18; + this.btnCopyHash.UseVisualStyleBackColor = true; + this.btnCopyHash.Click += new System.EventHandler(this.btnCopyHash_Click); + // + // linkLabel2 + // + this.linkLabel2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.linkLabel2.AutoSize = true; + this.linkLabel2.Location = new System.Drawing.Point(40, 509); + this.linkLabel2.Name = "linkLabel2"; + this.linkLabel2.Size = new System.Drawing.Size(100, 13); + this.linkLabel2.TabIndex = 19; + this.linkLabel2.TabStop = true; + this.linkLabel2.Text = "Commit #XXXXXXX"; + this.linkLabel2.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel2_LinkClicked); // // BizBox // @@ -204,6 +231,8 @@ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.OK; this.ClientSize = new System.Drawing.Size(448, 536); + this.Controls.Add(this.linkLabel2); + this.Controls.Add(this.btnCopyHash); this.Controls.Add(this.textBox1); this.Controls.Add(this.CoreInfoPanel); this.Controls.Add(this.label37); @@ -240,7 +269,6 @@ private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label VersionLabel; private System.Windows.Forms.Label label5; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label7; @@ -248,5 +276,8 @@ private System.Windows.Forms.Label label37; private System.Windows.Forms.Panel CoreInfoPanel; private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label VersionLabel; + private System.Windows.Forms.Button btnCopyHash; + private System.Windows.Forms.LinkLabel linkLabel2; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/BizBox.cs b/BizHawk.Client.EmuHawk/BizBox.cs index cbef7d3edc..d32b3a65ed 100644 --- a/BizHawk.Client.EmuHawk/BizBox.cs +++ b/BizHawk.Client.EmuHawk/BizBox.cs @@ -28,11 +28,11 @@ namespace BizHawk.Client.EmuHawk { if (VersionInfo.DeveloperBuild) { - Text = " BizHawk (GIT " + SubWCRev.GIT_BRANCH + "-" + SubWCRev.SVN_REV + "#" + SubWCRev.GIT_SHORTHASH + ")"; + Text = " BizHawk (GIT " + SubWCRev.GIT_BRANCH + "#" + SubWCRev.GIT_SHORTHASH + ")"; } else { - Text = "Version " + VersionInfo.MAINVERSION + " (GIT " + SubWCRev.GIT_BRANCH + "-" + SubWCRev.SVN_REV + "#" + SubWCRev.GIT_SHORTHASH + ")"; + Text = "Version " + VersionInfo.MAINVERSION + " (GIT " + SubWCRev.GIT_BRANCH + "#" + SubWCRev.GIT_SHORTHASH + ")"; } VersionLabel.Text = "Version " + VersionInfo.MAINVERSION + " " + VersionInfo.RELEASEDATE; @@ -55,6 +55,18 @@ namespace BizHawk.Client.EmuHawk }); } + + linkLabel2.Text = "Commit # " + SubWCRev.GIT_SHORTHASH; + } + + private void linkLabel2_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start("https://github.com/TASVideos/BizHawk/commit/" + SubWCRev.GIT_SHORTHASH); + } + + private void btnCopyHash_Click(object sender, EventArgs e) + { + System.Windows.Forms.Clipboard.SetText(SubWCRev.GIT_SHORTHASH); } } } diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 5e3798dc2c..ad085d840b 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -412,12 +412,6 @@ ProfileConfig.cs - - Form - - - PSXControllerConfig.cs - Form @@ -1271,9 +1265,6 @@ ProfileConfig.cs - - PSXControllerConfig.cs - PSXControllerConfigNew.cs diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index c1002ff667..b348c2cc33 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -330,6 +330,8 @@ this.ForumsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.FeaturesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.C64SubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.C64SettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton(); this.EmuStatus = new System.Windows.Forms.ToolStripStatusLabel(); @@ -433,6 +435,7 @@ this.GenesisSubMenu, this.wonderSwanToolStripMenuItem, this.AppleSubMenu, + this.C64SubMenu, this.HelpSubMenu}); this.MainformMenu.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Flow; this.MainformMenu.Location = new System.Drawing.Point(0, 0); @@ -2552,28 +2555,28 @@ // this.PSXControllerSettingsMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.GameController; this.PSXControllerSettingsMenuItem.Name = "PSXControllerSettingsMenuItem"; - this.PSXControllerSettingsMenuItem.Size = new System.Drawing.Size(172, 22); - this.PSXControllerSettingsMenuItem.Text = "Controller Settings"; + this.PSXControllerSettingsMenuItem.Size = new System.Drawing.Size(234, 22); + this.PSXControllerSettingsMenuItem.Text = "Controller / Memcard Settings"; this.PSXControllerSettingsMenuItem.Click += new System.EventHandler(this.PSXControllerSettingsMenuItem_Click); // // PSXOptionsMenuItem // this.PSXOptionsMenuItem.Name = "PSXOptionsMenuItem"; - this.PSXOptionsMenuItem.Size = new System.Drawing.Size(172, 22); + this.PSXOptionsMenuItem.Size = new System.Drawing.Size(234, 22); this.PSXOptionsMenuItem.Text = "&Options"; this.PSXOptionsMenuItem.Click += new System.EventHandler(this.PSXOptionsMenuItem_Click); // // PSXDiscControlsMenuItem // this.PSXDiscControlsMenuItem.Name = "PSXDiscControlsMenuItem"; - this.PSXDiscControlsMenuItem.Size = new System.Drawing.Size(172, 22); + this.PSXDiscControlsMenuItem.Size = new System.Drawing.Size(234, 22); this.PSXDiscControlsMenuItem.Text = "&Disc Controls"; this.PSXDiscControlsMenuItem.Click += new System.EventHandler(this.PSXDiscControlsMenuItem_Click); // // PSXHashDiscsToolStripMenuItem // this.PSXHashDiscsToolStripMenuItem.Name = "PSXHashDiscsToolStripMenuItem"; - this.PSXHashDiscsToolStripMenuItem.Size = new System.Drawing.Size(172, 22); + this.PSXHashDiscsToolStripMenuItem.Size = new System.Drawing.Size(234, 22); this.PSXHashDiscsToolStripMenuItem.Text = "&Hash Discs"; this.PSXHashDiscsToolStripMenuItem.Click += new System.EventHandler(this.PSXHashDiscsToolStripMenuItem_Click); // @@ -2926,6 +2929,21 @@ this.AboutMenuItem.Text = "&About"; this.AboutMenuItem.Click += new System.EventHandler(this.AboutMenuItem_Click); // + // C64SubMenu + // + this.C64SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.C64SettingsMenuItem}); + this.C64SubMenu.Name = "C64SubMenu"; + this.C64SubMenu.Size = new System.Drawing.Size(39, 19); + this.C64SubMenu.Text = "&C64"; + // + // C64SettingsMenuItem + // + this.C64SettingsMenuItem.Name = "C64SettingsMenuItem"; + this.C64SettingsMenuItem.Size = new System.Drawing.Size(152, 22); + this.C64SettingsMenuItem.Text = "&Settings..."; + this.C64SettingsMenuItem.Click += new System.EventHandler(this.C64SettingsMenuItem_Click); + // // MainStatusBar // this.MainStatusBar.ClickThrough = true; @@ -3973,6 +3991,8 @@ private System.Windows.Forms.ToolStripMenuItem Speed400MenuItem; private System.Windows.Forms.ToolStripMenuItem BasicBotMenuItem; private System.Windows.Forms.ToolStripMenuItem DisplayMessagesMenuItem; + private System.Windows.Forms.ToolStripMenuItem C64SubMenu; + private System.Windows.Forms.ToolStripMenuItem C64SettingsMenuItem; } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 2d68d2746c..60cd748d54 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -1746,7 +1746,7 @@ namespace BizHawk.Client.EmuHawk private void PSXControllerSettingsMenuItem_Click(object sender, EventArgs e) { - new PSXControllerConfig().ShowDialog(); + new PSXControllerConfigNew().ShowDialog(); } #endregion @@ -2063,6 +2063,15 @@ namespace BizHawk.Client.EmuHawk #endregion + #region C64 + + private void C64SettingsMenuItem_Click(object sender, EventArgs e) + { + GenericCoreConfig.DoDialog(this, "C64 Settings"); + } + + #endregion + #region Help private void OnlineHelpMenuItem_Click(object sender, EventArgs e) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index a289f4d627..19af808367 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -1595,6 +1595,7 @@ namespace BizHawk.Client.EmuHawk GenesisSubMenu.Visible = false; wonderSwanToolStripMenuItem.Visible = false; AppleSubMenu.Visible = false; + C64SubMenu.Visible = false; switch (system) { @@ -1675,6 +1676,9 @@ namespace BizHawk.Client.EmuHawk case "AppleII": AppleSubMenu.Visible = true; break; + case "C64": + C64SubMenu.Visible = true; + break; } } @@ -1965,7 +1969,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;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.col;.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.psf;*.minipsf;*.nsf;%ARCH%", + "Rom Files", "*.nes;*.fds;*unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.col;.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.psf;*.minipsf;*.nsf;%ARCH%", "Music Files", "*.psf;*.minipsf;*.sid;*.nsf", "Disc Images", "*.cue;*.ccd;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", @@ -1986,7 +1990,7 @@ namespace BizHawk.Client.EmuHawk "PlayStation", "*.cue;*.ccd;*.m3u", "PSX Executables (experimental)", "*.exe", "PSF Playstation Sound File", "*.psf;*.minipsf", - "Commodore 64 (experimental)", "*.prg; *.d64, *.g64; *.crt;%ARCH%", + "Commodore 64 (experimental)", "*.prg; *.d64, *.g64; *.crt; *.tap;%ARCH%", "SID Commodore 64 Music File", "*.sid;%ARCH%", "Nintendo 64", "*.z64;*.v64;*.n64", "WonderSwan", "*.ws;*.wsc;%ARCH%", @@ -2956,7 +2960,7 @@ namespace BizHawk.Client.EmuHawk { Global.AutoFireController.IncrementStarts(); } - //Global.AutofireStickyXORAdapter.IncrementLoops(IsLagFrame); + Global.AutofireStickyXORAdapter.IncrementLoops(IsLagFrame); PressFrameAdvance = false; diff --git a/BizHawk.Client.EmuHawk/config/GenericCoreConfig.Designer.cs b/BizHawk.Client.EmuHawk/config/GenericCoreConfig.Designer.cs index 359cc23605..0232504ff4 100644 --- a/BizHawk.Client.EmuHawk/config/GenericCoreConfig.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/GenericCoreConfig.Designer.cs @@ -67,6 +67,7 @@ // // propertyGrid1 // + this.propertyGrid1.CategoryForeColor = System.Drawing.SystemColors.InactiveCaptionText; this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill; this.propertyGrid1.Location = new System.Drawing.Point(3, 3); this.propertyGrid1.Name = "propertyGrid1"; @@ -88,6 +89,7 @@ // // propertyGrid2 // + this.propertyGrid2.CategoryForeColor = System.Drawing.SystemColors.InactiveCaptionText; this.propertyGrid2.Dock = System.Windows.Forms.DockStyle.Fill; this.propertyGrid2.Location = new System.Drawing.Point(3, 3); this.propertyGrid2.Name = "propertyGrid2"; @@ -143,6 +145,7 @@ this.Controls.Add(this.tabControl1); this.Name = "GenericCoreConfig"; this.ShowIcon = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "GenericCoreConfig"; this.Load += new System.EventHandler(this.GenericCoreConfig_Load); this.tabControl1.ResumeLayout(false); diff --git a/BizHawk.Client.EmuHawk/config/NES/NesControllerSettings.cs b/BizHawk.Client.EmuHawk/config/NES/NesControllerSettings.cs index c6d641bbeb..94e7d82c6b 100644 --- a/BizHawk.Client.EmuHawk/config/NES/NesControllerSettings.cs +++ b/BizHawk.Client.EmuHawk/config/NES/NesControllerSettings.cs @@ -58,8 +58,6 @@ namespace BizHawk.Client.EmuHawk SyncSettings.Controls = ctrls; - SyncSettings.Controls = ctrls; - if (changed) { GlobalWin.MainForm.PutCoreSyncSettings(SyncSettings); diff --git a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.Designer.cs b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.Designer.cs deleted file mode 100644 index f55c730785..0000000000 --- a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.Designer.cs +++ /dev/null @@ -1,96 +0,0 @@ -namespace BizHawk.Client.EmuHawk -{ - partial class PSXControllerConfig - { - /// - /// 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(PSXControllerConfig)); - this.CancelBtn = new System.Windows.Forms.Button(); - this.OkBtn = new System.Windows.Forms.Button(); - this.btnTest = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // 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(294, 227); - this.CancelBtn.Name = "CancelBtn"; - this.CancelBtn.Size = new System.Drawing.Size(60, 23); - this.CancelBtn.TabIndex = 5; - this.CancelBtn.Text = "&Cancel"; - this.CancelBtn.UseVisualStyleBackColor = true; - this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); - // - // 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(228, 227); - this.OkBtn.Name = "OkBtn"; - this.OkBtn.Size = new System.Drawing.Size(60, 23); - this.OkBtn.TabIndex = 4; - this.OkBtn.Text = "&Ok"; - this.OkBtn.UseVisualStyleBackColor = true; - this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); - // - // btnTest - // - this.btnTest.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.btnTest.Location = new System.Drawing.Point(12, 227); - this.btnTest.Name = "btnTest"; - this.btnTest.Size = new System.Drawing.Size(60, 23); - this.btnTest.TabIndex = 6; - this.btnTest.Text = "Test"; - this.btnTest.UseVisualStyleBackColor = true; - this.btnTest.Click += new System.EventHandler(this.btnTest_Click); - // - // PSXControllerConfig - // - 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(366, 262); - this.Controls.Add(this.btnTest); - this.Controls.Add(this.CancelBtn); - this.Controls.Add(this.OkBtn); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Name = "PSXControllerConfig"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "Controller Settings"; - this.Load += new System.EventHandler(this.PSXControllerConfig_Load); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.Button CancelBtn; - private System.Windows.Forms.Button OkBtn; - private System.Windows.Forms.Button btnTest; - } -} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.cs b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.cs deleted file mode 100644 index edafbf1e66..0000000000 --- a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Windows.Forms; - -using BizHawk.Common; -using BizHawk.Emulation.Cores.Sony.PSX; -using BizHawk.Client.Common; -using BizHawk.Client.EmuHawk.WinFormExtensions; -using BizHawk.Common.ReflectionExtensions; - -namespace BizHawk.Client.EmuHawk -{ - public partial class PSXControllerConfig : Form - { - public PSXControllerConfig() - { - InitializeComponent(); - } - - private void PSXControllerConfig_Load(object sender, EventArgs e) - { - var psxSettings = ((Octoshock)Global.Emulator).GetSyncSettings(); - for (int i = 0; i < psxSettings.Controllers.Length; i++) - { - Controls.Add(new Label - { - Text = "Controller " + (i + 1), - Location = new Point(15, 19 + (i * 25)), - Width = 85 - }); - Controls.Add(new CheckBox - { - Text = "Connected", - Name = "Controller" + i, - Location = new Point(105, 15 + (i * 25)), - Checked = psxSettings.Controllers[i].IsConnected, - Width = 90 - }); - - var dropdown = new ComboBox - { - Name = "Controller" + i, - DropDownStyle = ComboBoxStyle.DropDownList, - Location = new Point(200, 15 + (i * 25)) - }; - - dropdown.PopulateFromEnum(psxSettings.Controllers[i].Type); - - Controls.Add(dropdown); - } - } - - private void OkBtn_Click(object sender, EventArgs e) - { - var psxSettings = ((Octoshock)Global.Emulator).GetSyncSettings(); - - Controls - .OfType() - .OrderBy(c => c.Name) - .ToList() - .ForEach(c => - { - var index = int.Parse(c.Name.Replace("Controller", "")); - psxSettings.Controllers[index].IsConnected = c.Checked; - }); - - Controls - .OfType() - .OrderBy(c => c.Name) - .ToList() - .ForEach(c => - { - var index = int.Parse(c.Name.Replace("Controller", "")); - psxSettings.Controllers[index].Type = c.SelectedItem.ToString().GetEnumFromDescription(); - }); - - GlobalWin.MainForm.PutCoreSyncSettings(psxSettings); - DialogResult = DialogResult.OK; - Close(); - } - - private void CancelBtn_Click(object sender, EventArgs e) - { - DialogResult = DialogResult.Cancel; - Close(); - } - - private void btnTest_Click(object sender, EventArgs e) - { - new PSXControllerConfigNew().ShowDialog(); - } - } -} diff --git a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.resx b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.resx deleted file mode 100644 index 91f294b89b..0000000000 --- a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfig.resx +++ /dev/null @@ -1,624 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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/PSX/PSXControllerConfigNew.Designer.cs b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.Designer.cs index 36740d6774..2082194199 100644 --- a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.Designer.cs @@ -28,413 +28,431 @@ /// private void InitializeComponent() { - this.cbMultitap_1 = new System.Windows.Forms.CheckBox(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.lbl_p_1_4 = new System.Windows.Forms.Label(); - this.lbl_p_1_3 = new System.Windows.Forms.Label(); - this.lbl_p_1_2 = new System.Windows.Forms.Label(); - this.lbl_p_1_1 = new System.Windows.Forms.Label(); - this.lbl_1_4 = new System.Windows.Forms.Label(); - this.lbl_1_3 = new System.Windows.Forms.Label(); - this.lbl_1_2 = new System.Windows.Forms.Label(); - this.lbl_1_1 = new System.Windows.Forms.Label(); - this.combo_1_4 = new System.Windows.Forms.ComboBox(); - this.combo_1_3 = new System.Windows.Forms.ComboBox(); - this.combo_1_2 = new System.Windows.Forms.ComboBox(); - this.combo_1_1 = new System.Windows.Forms.ComboBox(); - this.cbMemcard_1 = new System.Windows.Forms.CheckBox(); - this.btnOK = new System.Windows.Forms.Button(); - this.btnCancel = new System.Windows.Forms.Button(); - this.groupBox2 = new System.Windows.Forms.GroupBox(); - this.lbl_p_2_4 = new System.Windows.Forms.Label(); - this.lbl_p_2_3 = new System.Windows.Forms.Label(); - this.lbl_p_2_2 = new System.Windows.Forms.Label(); - this.lbl_p_2_1 = new System.Windows.Forms.Label(); - this.lbl_2_4 = new System.Windows.Forms.Label(); - this.lbl_2_3 = new System.Windows.Forms.Label(); - this.lbl_2_2 = new System.Windows.Forms.Label(); - this.lbl_2_1 = new System.Windows.Forms.Label(); - this.combo_2_4 = new System.Windows.Forms.ComboBox(); - this.combo_2_3 = new System.Windows.Forms.ComboBox(); - this.combo_2_2 = new System.Windows.Forms.ComboBox(); - this.combo_2_1 = new System.Windows.Forms.ComboBox(); - this.cbMemcard_2 = new System.Windows.Forms.CheckBox(); - this.cbMultitap_2 = new System.Windows.Forms.CheckBox(); - this.groupBox1.SuspendLayout(); - this.groupBox2.SuspendLayout(); - this.SuspendLayout(); - // - // cbMultitap_1 - // - this.cbMultitap_1.AutoSize = true; - this.cbMultitap_1.Location = new System.Drawing.Point(18, 43); - this.cbMultitap_1.Name = "cbMultitap_1"; - this.cbMultitap_1.Size = new System.Drawing.Size(63, 17); - this.cbMultitap_1.TabIndex = 0; - this.cbMultitap_1.Text = "Multitap"; - this.cbMultitap_1.UseVisualStyleBackColor = true; - this.cbMultitap_1.CheckedChanged += new System.EventHandler(this.cb_changed); - // - // groupBox1 - // - this.groupBox1.Controls.Add(this.lbl_p_1_4); - this.groupBox1.Controls.Add(this.lbl_p_1_3); - this.groupBox1.Controls.Add(this.lbl_p_1_2); - this.groupBox1.Controls.Add(this.lbl_p_1_1); - this.groupBox1.Controls.Add(this.lbl_1_4); - this.groupBox1.Controls.Add(this.lbl_1_3); - this.groupBox1.Controls.Add(this.lbl_1_2); - this.groupBox1.Controls.Add(this.lbl_1_1); - this.groupBox1.Controls.Add(this.combo_1_4); - this.groupBox1.Controls.Add(this.combo_1_3); - this.groupBox1.Controls.Add(this.combo_1_2); - this.groupBox1.Controls.Add(this.combo_1_1); - this.groupBox1.Controls.Add(this.cbMemcard_1); - this.groupBox1.Controls.Add(this.cbMultitap_1); - this.groupBox1.Location = new System.Drawing.Point(12, 12); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(273, 136); - this.groupBox1.TabIndex = 1; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Port 1"; - // - // lbl_p_1_4 - // - this.lbl_p_1_4.AutoSize = true; - this.lbl_p_1_4.Location = new System.Drawing.Point(241, 105); - this.lbl_p_1_4.Name = "lbl_p_1_4"; - this.lbl_p_1_4.Size = new System.Drawing.Size(20, 13); - this.lbl_p_1_4.TabIndex = 12; - this.lbl_p_1_4.Text = "P1"; - this.lbl_p_1_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_p_1_3 - // - this.lbl_p_1_3.AutoSize = true; - this.lbl_p_1_3.Location = new System.Drawing.Point(241, 78); - this.lbl_p_1_3.Name = "lbl_p_1_3"; - this.lbl_p_1_3.Size = new System.Drawing.Size(20, 13); - this.lbl_p_1_3.TabIndex = 11; - this.lbl_p_1_3.Text = "P1"; - this.lbl_p_1_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_p_1_2 - // - this.lbl_p_1_2.AutoSize = true; - this.lbl_p_1_2.Location = new System.Drawing.Point(241, 50); - this.lbl_p_1_2.Name = "lbl_p_1_2"; - this.lbl_p_1_2.Size = new System.Drawing.Size(20, 13); - this.lbl_p_1_2.TabIndex = 10; - this.lbl_p_1_2.Text = "P1"; - this.lbl_p_1_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_p_1_1 - // - this.lbl_p_1_1.AutoSize = true; - this.lbl_p_1_1.Location = new System.Drawing.Point(241, 24); - this.lbl_p_1_1.Name = "lbl_p_1_1"; - this.lbl_p_1_1.Size = new System.Drawing.Size(20, 13); - this.lbl_p_1_1.TabIndex = 9; - this.lbl_p_1_1.Text = "P1"; - this.lbl_p_1_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_1_4 - // - this.lbl_1_4.AutoSize = true; - this.lbl_1_4.Location = new System.Drawing.Point(94, 105); - this.lbl_1_4.Name = "lbl_1_4"; - this.lbl_1_4.Size = new System.Drawing.Size(15, 13); - this.lbl_1_4.TabIndex = 8; - this.lbl_1_4.Text = "D"; - this.lbl_1_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_1_3 - // - this.lbl_1_3.AutoSize = true; - this.lbl_1_3.Location = new System.Drawing.Point(94, 78); - this.lbl_1_3.Name = "lbl_1_3"; - this.lbl_1_3.Size = new System.Drawing.Size(14, 13); - this.lbl_1_3.TabIndex = 7; - this.lbl_1_3.Text = "C"; - this.lbl_1_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_1_2 - // - this.lbl_1_2.AutoSize = true; - this.lbl_1_2.Location = new System.Drawing.Point(94, 51); - this.lbl_1_2.Name = "lbl_1_2"; - this.lbl_1_2.Size = new System.Drawing.Size(14, 13); - this.lbl_1_2.TabIndex = 6; - this.lbl_1_2.Text = "B"; - this.lbl_1_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_1_1 - // - this.lbl_1_1.AutoSize = true; - this.lbl_1_1.Location = new System.Drawing.Point(94, 24); - this.lbl_1_1.Name = "lbl_1_1"; - this.lbl_1_1.Size = new System.Drawing.Size(14, 13); - this.lbl_1_1.TabIndex = 2; - this.lbl_1_1.Text = "A"; - this.lbl_1_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // combo_1_4 - // - this.combo_1_4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_1_4.FormattingEnabled = true; - this.combo_1_4.Location = new System.Drawing.Point(114, 102); - this.combo_1_4.Name = "combo_1_4"; - this.combo_1_4.Size = new System.Drawing.Size(121, 21); - this.combo_1_4.TabIndex = 5; - this.combo_1_4.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // combo_1_3 - // - this.combo_1_3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_1_3.FormattingEnabled = true; - this.combo_1_3.Location = new System.Drawing.Point(114, 75); - this.combo_1_3.Name = "combo_1_3"; - this.combo_1_3.Size = new System.Drawing.Size(121, 21); - this.combo_1_3.TabIndex = 4; - this.combo_1_3.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // combo_1_2 - // - this.combo_1_2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_1_2.FormattingEnabled = true; - this.combo_1_2.Location = new System.Drawing.Point(114, 48); - this.combo_1_2.Name = "combo_1_2"; - this.combo_1_2.Size = new System.Drawing.Size(121, 21); - this.combo_1_2.TabIndex = 3; - this.combo_1_2.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // combo_1_1 - // - this.combo_1_1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_1_1.FormattingEnabled = true; - this.combo_1_1.Location = new System.Drawing.Point(114, 21); - this.combo_1_1.Name = "combo_1_1"; - this.combo_1_1.Size = new System.Drawing.Size(121, 21); - this.combo_1_1.TabIndex = 2; - this.combo_1_1.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // cbMemcard_1 - // - this.cbMemcard_1.AutoSize = true; - this.cbMemcard_1.Location = new System.Drawing.Point(18, 21); - this.cbMemcard_1.Name = "cbMemcard_1"; - this.cbMemcard_1.Size = new System.Drawing.Size(70, 17); - this.cbMemcard_1.TabIndex = 1; - this.cbMemcard_1.Text = "Memcard"; - this.cbMemcard_1.UseVisualStyleBackColor = true; - this.cbMemcard_1.CheckedChanged += new System.EventHandler(this.cb_changed); - // - // btnOK - // - this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; - this.btnOK.Location = new System.Drawing.Point(408, 163); - this.btnOK.Name = "btnOK"; - this.btnOK.Size = new System.Drawing.Size(75, 23); - this.btnOK.TabIndex = 2; - this.btnOK.Text = "OK"; - this.btnOK.UseVisualStyleBackColor = true; - // - // btnCancel - // - this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.btnCancel.Location = new System.Drawing.Point(489, 163); - this.btnCancel.Name = "btnCancel"; - this.btnCancel.Size = new System.Drawing.Size(75, 23); - this.btnCancel.TabIndex = 3; - this.btnCancel.Text = "Cancel"; - this.btnCancel.UseVisualStyleBackColor = true; - // - // groupBox2 - // - this.groupBox2.Controls.Add(this.lbl_p_2_4); - this.groupBox2.Controls.Add(this.lbl_p_2_3); - this.groupBox2.Controls.Add(this.lbl_p_2_2); - this.groupBox2.Controls.Add(this.lbl_p_2_1); - this.groupBox2.Controls.Add(this.lbl_2_4); - this.groupBox2.Controls.Add(this.lbl_2_3); - this.groupBox2.Controls.Add(this.lbl_2_2); - this.groupBox2.Controls.Add(this.lbl_2_1); - this.groupBox2.Controls.Add(this.combo_2_4); - this.groupBox2.Controls.Add(this.combo_2_3); - this.groupBox2.Controls.Add(this.combo_2_2); - this.groupBox2.Controls.Add(this.combo_2_1); - this.groupBox2.Controls.Add(this.cbMemcard_2); - this.groupBox2.Controls.Add(this.cbMultitap_2); - this.groupBox2.Location = new System.Drawing.Point(291, 12); - this.groupBox2.Name = "groupBox2"; - this.groupBox2.Size = new System.Drawing.Size(273, 136); - this.groupBox2.TabIndex = 13; - this.groupBox2.TabStop = false; - this.groupBox2.Text = "Port 2"; - // - // lbl_p_2_4 - // - this.lbl_p_2_4.AutoSize = true; - this.lbl_p_2_4.Location = new System.Drawing.Point(241, 105); - this.lbl_p_2_4.Name = "lbl_p_2_4"; - this.lbl_p_2_4.Size = new System.Drawing.Size(20, 13); - this.lbl_p_2_4.TabIndex = 12; - this.lbl_p_2_4.Text = "P1"; - this.lbl_p_2_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_p_2_3 - // - this.lbl_p_2_3.AutoSize = true; - this.lbl_p_2_3.Location = new System.Drawing.Point(241, 78); - this.lbl_p_2_3.Name = "lbl_p_2_3"; - this.lbl_p_2_3.Size = new System.Drawing.Size(20, 13); - this.lbl_p_2_3.TabIndex = 11; - this.lbl_p_2_3.Text = "P1"; - this.lbl_p_2_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_p_2_2 - // - this.lbl_p_2_2.AutoSize = true; - this.lbl_p_2_2.Location = new System.Drawing.Point(241, 50); - this.lbl_p_2_2.Name = "lbl_p_2_2"; - this.lbl_p_2_2.Size = new System.Drawing.Size(20, 13); - this.lbl_p_2_2.TabIndex = 10; - this.lbl_p_2_2.Text = "P1"; - this.lbl_p_2_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_p_2_1 - // - this.lbl_p_2_1.AutoSize = true; - this.lbl_p_2_1.Location = new System.Drawing.Point(241, 24); - this.lbl_p_2_1.Name = "lbl_p_2_1"; - this.lbl_p_2_1.Size = new System.Drawing.Size(20, 13); - this.lbl_p_2_1.TabIndex = 9; - this.lbl_p_2_1.Text = "P1"; - this.lbl_p_2_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_2_4 - // - this.lbl_2_4.AutoSize = true; - this.lbl_2_4.Location = new System.Drawing.Point(94, 105); - this.lbl_2_4.Name = "lbl_2_4"; - this.lbl_2_4.Size = new System.Drawing.Size(15, 13); - this.lbl_2_4.TabIndex = 8; - this.lbl_2_4.Text = "D"; - this.lbl_2_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_2_3 - // - this.lbl_2_3.AutoSize = true; - this.lbl_2_3.Location = new System.Drawing.Point(94, 78); - this.lbl_2_3.Name = "lbl_2_3"; - this.lbl_2_3.Size = new System.Drawing.Size(14, 13); - this.lbl_2_3.TabIndex = 7; - this.lbl_2_3.Text = "C"; - this.lbl_2_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_2_2 - // - this.lbl_2_2.AutoSize = true; - this.lbl_2_2.Location = new System.Drawing.Point(94, 51); - this.lbl_2_2.Name = "lbl_2_2"; - this.lbl_2_2.Size = new System.Drawing.Size(14, 13); - this.lbl_2_2.TabIndex = 6; - this.lbl_2_2.Text = "B"; - this.lbl_2_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // lbl_2_1 - // - this.lbl_2_1.AutoSize = true; - this.lbl_2_1.Location = new System.Drawing.Point(94, 24); - this.lbl_2_1.Name = "lbl_2_1"; - this.lbl_2_1.Size = new System.Drawing.Size(14, 13); - this.lbl_2_1.TabIndex = 2; - this.lbl_2_1.Text = "A"; - this.lbl_2_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; - // - // combo_2_4 - // - this.combo_2_4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_2_4.FormattingEnabled = true; - this.combo_2_4.Location = new System.Drawing.Point(114, 102); - this.combo_2_4.Name = "combo_2_4"; - this.combo_2_4.Size = new System.Drawing.Size(121, 21); - this.combo_2_4.TabIndex = 5; - this.combo_2_4.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // combo_2_3 - // - this.combo_2_3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_2_3.FormattingEnabled = true; - this.combo_2_3.Location = new System.Drawing.Point(114, 75); - this.combo_2_3.Name = "combo_2_3"; - this.combo_2_3.Size = new System.Drawing.Size(121, 21); - this.combo_2_3.TabIndex = 4; - this.combo_2_3.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // combo_2_2 - // - this.combo_2_2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_2_2.FormattingEnabled = true; - this.combo_2_2.Location = new System.Drawing.Point(114, 48); - this.combo_2_2.Name = "combo_2_2"; - this.combo_2_2.Size = new System.Drawing.Size(121, 21); - this.combo_2_2.TabIndex = 3; - this.combo_2_2.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // combo_2_1 - // - this.combo_2_1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.combo_2_1.FormattingEnabled = true; - this.combo_2_1.Location = new System.Drawing.Point(114, 21); - this.combo_2_1.Name = "combo_2_1"; - this.combo_2_1.Size = new System.Drawing.Size(121, 21); - this.combo_2_1.TabIndex = 2; - this.combo_2_1.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); - // - // cbMemcard_2 - // - this.cbMemcard_2.AutoSize = true; - this.cbMemcard_2.Location = new System.Drawing.Point(18, 21); - this.cbMemcard_2.Name = "cbMemcard_2"; - this.cbMemcard_2.Size = new System.Drawing.Size(70, 17); - this.cbMemcard_2.TabIndex = 1; - this.cbMemcard_2.Text = "Memcard"; - this.cbMemcard_2.UseVisualStyleBackColor = true; - this.cbMemcard_2.CheckedChanged += new System.EventHandler(this.cb_changed); - // - // cbMultitap_2 - // - this.cbMultitap_2.AutoSize = true; - this.cbMultitap_2.Location = new System.Drawing.Point(18, 43); - this.cbMultitap_2.Name = "cbMultitap_2"; - this.cbMultitap_2.Size = new System.Drawing.Size(63, 17); - this.cbMultitap_2.TabIndex = 0; - this.cbMultitap_2.Text = "Multitap"; - this.cbMultitap_2.UseVisualStyleBackColor = true; - this.cbMultitap_2.CheckedChanged += new System.EventHandler(this.cb_changed); - // - // PSXControllerConfigNew - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(586, 201); - this.Controls.Add(this.groupBox2); - this.Controls.Add(this.btnCancel); - this.Controls.Add(this.btnOK); - this.Controls.Add(this.groupBox1); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "PSXControllerConfigNew"; - this.Text = "PSX FrontIO Configuration"; - this.Load += new System.EventHandler(this.PSXControllerConfigNew_Load); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); - this.groupBox2.ResumeLayout(false); - this.groupBox2.PerformLayout(); - this.ResumeLayout(false); - + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(PSXControllerConfigNew)); + this.cbMultitap_1 = new System.Windows.Forms.CheckBox(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.lbl_p_1_4 = new System.Windows.Forms.Label(); + this.lbl_p_1_3 = new System.Windows.Forms.Label(); + this.lbl_p_1_2 = new System.Windows.Forms.Label(); + this.lbl_p_1_1 = new System.Windows.Forms.Label(); + this.lbl_1_4 = new System.Windows.Forms.Label(); + this.lbl_1_3 = new System.Windows.Forms.Label(); + this.lbl_1_2 = new System.Windows.Forms.Label(); + this.lbl_1_1 = new System.Windows.Forms.Label(); + this.combo_1_4 = new System.Windows.Forms.ComboBox(); + this.combo_1_3 = new System.Windows.Forms.ComboBox(); + this.combo_1_2 = new System.Windows.Forms.ComboBox(); + this.combo_1_1 = new System.Windows.Forms.ComboBox(); + this.cbMemcard_1 = new System.Windows.Forms.CheckBox(); + this.btnOK = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.lbl_p_2_4 = new System.Windows.Forms.Label(); + this.lbl_p_2_3 = new System.Windows.Forms.Label(); + this.lbl_p_2_2 = new System.Windows.Forms.Label(); + this.lbl_p_2_1 = new System.Windows.Forms.Label(); + this.lbl_2_4 = new System.Windows.Forms.Label(); + this.lbl_2_3 = new System.Windows.Forms.Label(); + this.lbl_2_2 = new System.Windows.Forms.Label(); + this.lbl_2_1 = new System.Windows.Forms.Label(); + this.combo_2_4 = new System.Windows.Forms.ComboBox(); + this.combo_2_3 = new System.Windows.Forms.ComboBox(); + this.combo_2_2 = new System.Windows.Forms.ComboBox(); + this.combo_2_1 = new System.Windows.Forms.ComboBox(); + this.cbMemcard_2 = new System.Windows.Forms.CheckBox(); + this.cbMultitap_2 = new System.Windows.Forms.CheckBox(); + this.label1 = new System.Windows.Forms.Label(); + this.groupBox1.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.SuspendLayout(); + // + // cbMultitap_1 + // + this.cbMultitap_1.AutoSize = true; + this.cbMultitap_1.Enabled = false; + this.cbMultitap_1.Location = new System.Drawing.Point(18, 43); + this.cbMultitap_1.Name = "cbMultitap_1"; + this.cbMultitap_1.Size = new System.Drawing.Size(63, 17); + this.cbMultitap_1.TabIndex = 0; + this.cbMultitap_1.Text = "Multitap"; + this.cbMultitap_1.UseVisualStyleBackColor = true; + this.cbMultitap_1.CheckedChanged += new System.EventHandler(this.cb_changed); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.lbl_p_1_4); + this.groupBox1.Controls.Add(this.lbl_p_1_3); + this.groupBox1.Controls.Add(this.lbl_p_1_2); + this.groupBox1.Controls.Add(this.lbl_p_1_1); + this.groupBox1.Controls.Add(this.lbl_1_4); + this.groupBox1.Controls.Add(this.lbl_1_3); + this.groupBox1.Controls.Add(this.lbl_1_2); + this.groupBox1.Controls.Add(this.lbl_1_1); + this.groupBox1.Controls.Add(this.combo_1_4); + this.groupBox1.Controls.Add(this.combo_1_3); + this.groupBox1.Controls.Add(this.combo_1_2); + this.groupBox1.Controls.Add(this.combo_1_1); + this.groupBox1.Controls.Add(this.cbMemcard_1); + this.groupBox1.Controls.Add(this.cbMultitap_1); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(273, 136); + this.groupBox1.TabIndex = 1; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Port 1"; + // + // lbl_p_1_4 + // + this.lbl_p_1_4.AutoSize = true; + this.lbl_p_1_4.Location = new System.Drawing.Point(241, 105); + this.lbl_p_1_4.Name = "lbl_p_1_4"; + this.lbl_p_1_4.Size = new System.Drawing.Size(20, 13); + this.lbl_p_1_4.TabIndex = 12; + this.lbl_p_1_4.Text = "P1"; + this.lbl_p_1_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_p_1_3 + // + this.lbl_p_1_3.AutoSize = true; + this.lbl_p_1_3.Location = new System.Drawing.Point(241, 78); + this.lbl_p_1_3.Name = "lbl_p_1_3"; + this.lbl_p_1_3.Size = new System.Drawing.Size(20, 13); + this.lbl_p_1_3.TabIndex = 11; + this.lbl_p_1_3.Text = "P1"; + this.lbl_p_1_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_p_1_2 + // + this.lbl_p_1_2.AutoSize = true; + this.lbl_p_1_2.Location = new System.Drawing.Point(241, 50); + this.lbl_p_1_2.Name = "lbl_p_1_2"; + this.lbl_p_1_2.Size = new System.Drawing.Size(20, 13); + this.lbl_p_1_2.TabIndex = 10; + this.lbl_p_1_2.Text = "P1"; + this.lbl_p_1_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_p_1_1 + // + this.lbl_p_1_1.AutoSize = true; + this.lbl_p_1_1.Location = new System.Drawing.Point(241, 24); + this.lbl_p_1_1.Name = "lbl_p_1_1"; + this.lbl_p_1_1.Size = new System.Drawing.Size(20, 13); + this.lbl_p_1_1.TabIndex = 9; + this.lbl_p_1_1.Text = "P1"; + this.lbl_p_1_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_1_4 + // + this.lbl_1_4.AutoSize = true; + this.lbl_1_4.Location = new System.Drawing.Point(94, 105); + this.lbl_1_4.Name = "lbl_1_4"; + this.lbl_1_4.Size = new System.Drawing.Size(15, 13); + this.lbl_1_4.TabIndex = 8; + this.lbl_1_4.Text = "D"; + this.lbl_1_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_1_3 + // + this.lbl_1_3.AutoSize = true; + this.lbl_1_3.Location = new System.Drawing.Point(94, 78); + this.lbl_1_3.Name = "lbl_1_3"; + this.lbl_1_3.Size = new System.Drawing.Size(14, 13); + this.lbl_1_3.TabIndex = 7; + this.lbl_1_3.Text = "C"; + this.lbl_1_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_1_2 + // + this.lbl_1_2.AutoSize = true; + this.lbl_1_2.Location = new System.Drawing.Point(94, 51); + this.lbl_1_2.Name = "lbl_1_2"; + this.lbl_1_2.Size = new System.Drawing.Size(14, 13); + this.lbl_1_2.TabIndex = 6; + this.lbl_1_2.Text = "B"; + this.lbl_1_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_1_1 + // + this.lbl_1_1.AutoSize = true; + this.lbl_1_1.Location = new System.Drawing.Point(94, 24); + this.lbl_1_1.Name = "lbl_1_1"; + this.lbl_1_1.Size = new System.Drawing.Size(14, 13); + this.lbl_1_1.TabIndex = 2; + this.lbl_1_1.Text = "A"; + this.lbl_1_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // combo_1_4 + // + this.combo_1_4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_1_4.FormattingEnabled = true; + this.combo_1_4.Location = new System.Drawing.Point(114, 102); + this.combo_1_4.Name = "combo_1_4"; + this.combo_1_4.Size = new System.Drawing.Size(121, 21); + this.combo_1_4.TabIndex = 5; + this.combo_1_4.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // combo_1_3 + // + this.combo_1_3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_1_3.FormattingEnabled = true; + this.combo_1_3.Location = new System.Drawing.Point(114, 75); + this.combo_1_3.Name = "combo_1_3"; + this.combo_1_3.Size = new System.Drawing.Size(121, 21); + this.combo_1_3.TabIndex = 4; + this.combo_1_3.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // combo_1_2 + // + this.combo_1_2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_1_2.FormattingEnabled = true; + this.combo_1_2.Location = new System.Drawing.Point(114, 48); + this.combo_1_2.Name = "combo_1_2"; + this.combo_1_2.Size = new System.Drawing.Size(121, 21); + this.combo_1_2.TabIndex = 3; + this.combo_1_2.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // combo_1_1 + // + this.combo_1_1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_1_1.FormattingEnabled = true; + this.combo_1_1.Location = new System.Drawing.Point(114, 21); + this.combo_1_1.Name = "combo_1_1"; + this.combo_1_1.Size = new System.Drawing.Size(121, 21); + this.combo_1_1.TabIndex = 2; + this.combo_1_1.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // cbMemcard_1 + // + this.cbMemcard_1.AutoSize = true; + this.cbMemcard_1.Location = new System.Drawing.Point(18, 21); + this.cbMemcard_1.Name = "cbMemcard_1"; + this.cbMemcard_1.Size = new System.Drawing.Size(70, 17); + this.cbMemcard_1.TabIndex = 1; + this.cbMemcard_1.Text = "Memcard"; + this.cbMemcard_1.UseVisualStyleBackColor = true; + this.cbMemcard_1.CheckedChanged += new System.EventHandler(this.cb_changed); + // + // btnOK + // + this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK; + this.btnOK.Location = new System.Drawing.Point(408, 163); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(75, 23); + this.btnOK.TabIndex = 2; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // btnCancel + // + this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.btnCancel.Location = new System.Drawing.Point(489, 163); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 3; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.lbl_p_2_4); + this.groupBox2.Controls.Add(this.lbl_p_2_3); + this.groupBox2.Controls.Add(this.lbl_p_2_2); + this.groupBox2.Controls.Add(this.lbl_p_2_1); + this.groupBox2.Controls.Add(this.lbl_2_4); + this.groupBox2.Controls.Add(this.lbl_2_3); + this.groupBox2.Controls.Add(this.lbl_2_2); + this.groupBox2.Controls.Add(this.lbl_2_1); + this.groupBox2.Controls.Add(this.combo_2_4); + this.groupBox2.Controls.Add(this.combo_2_3); + this.groupBox2.Controls.Add(this.combo_2_2); + this.groupBox2.Controls.Add(this.combo_2_1); + this.groupBox2.Controls.Add(this.cbMemcard_2); + this.groupBox2.Controls.Add(this.cbMultitap_2); + this.groupBox2.Location = new System.Drawing.Point(291, 12); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(273, 136); + this.groupBox2.TabIndex = 13; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Port 2"; + // + // lbl_p_2_4 + // + this.lbl_p_2_4.AutoSize = true; + this.lbl_p_2_4.Location = new System.Drawing.Point(241, 105); + this.lbl_p_2_4.Name = "lbl_p_2_4"; + this.lbl_p_2_4.Size = new System.Drawing.Size(20, 13); + this.lbl_p_2_4.TabIndex = 12; + this.lbl_p_2_4.Text = "P1"; + this.lbl_p_2_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_p_2_3 + // + this.lbl_p_2_3.AutoSize = true; + this.lbl_p_2_3.Location = new System.Drawing.Point(241, 78); + this.lbl_p_2_3.Name = "lbl_p_2_3"; + this.lbl_p_2_3.Size = new System.Drawing.Size(20, 13); + this.lbl_p_2_3.TabIndex = 11; + this.lbl_p_2_3.Text = "P1"; + this.lbl_p_2_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_p_2_2 + // + this.lbl_p_2_2.AutoSize = true; + this.lbl_p_2_2.Location = new System.Drawing.Point(241, 50); + this.lbl_p_2_2.Name = "lbl_p_2_2"; + this.lbl_p_2_2.Size = new System.Drawing.Size(20, 13); + this.lbl_p_2_2.TabIndex = 10; + this.lbl_p_2_2.Text = "P1"; + this.lbl_p_2_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_p_2_1 + // + this.lbl_p_2_1.AutoSize = true; + this.lbl_p_2_1.Location = new System.Drawing.Point(241, 24); + this.lbl_p_2_1.Name = "lbl_p_2_1"; + this.lbl_p_2_1.Size = new System.Drawing.Size(20, 13); + this.lbl_p_2_1.TabIndex = 9; + this.lbl_p_2_1.Text = "P1"; + this.lbl_p_2_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_2_4 + // + this.lbl_2_4.AutoSize = true; + this.lbl_2_4.Location = new System.Drawing.Point(94, 105); + this.lbl_2_4.Name = "lbl_2_4"; + this.lbl_2_4.Size = new System.Drawing.Size(15, 13); + this.lbl_2_4.TabIndex = 8; + this.lbl_2_4.Text = "D"; + this.lbl_2_4.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_2_3 + // + this.lbl_2_3.AutoSize = true; + this.lbl_2_3.Location = new System.Drawing.Point(94, 78); + this.lbl_2_3.Name = "lbl_2_3"; + this.lbl_2_3.Size = new System.Drawing.Size(14, 13); + this.lbl_2_3.TabIndex = 7; + this.lbl_2_3.Text = "C"; + this.lbl_2_3.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_2_2 + // + this.lbl_2_2.AutoSize = true; + this.lbl_2_2.Location = new System.Drawing.Point(94, 51); + this.lbl_2_2.Name = "lbl_2_2"; + this.lbl_2_2.Size = new System.Drawing.Size(14, 13); + this.lbl_2_2.TabIndex = 6; + this.lbl_2_2.Text = "B"; + this.lbl_2_2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // lbl_2_1 + // + this.lbl_2_1.AutoSize = true; + this.lbl_2_1.Location = new System.Drawing.Point(94, 24); + this.lbl_2_1.Name = "lbl_2_1"; + this.lbl_2_1.Size = new System.Drawing.Size(14, 13); + this.lbl_2_1.TabIndex = 2; + this.lbl_2_1.Text = "A"; + this.lbl_2_1.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // + // combo_2_4 + // + this.combo_2_4.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_2_4.FormattingEnabled = true; + this.combo_2_4.Location = new System.Drawing.Point(114, 102); + this.combo_2_4.Name = "combo_2_4"; + this.combo_2_4.Size = new System.Drawing.Size(121, 21); + this.combo_2_4.TabIndex = 5; + this.combo_2_4.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // combo_2_3 + // + this.combo_2_3.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_2_3.FormattingEnabled = true; + this.combo_2_3.Location = new System.Drawing.Point(114, 75); + this.combo_2_3.Name = "combo_2_3"; + this.combo_2_3.Size = new System.Drawing.Size(121, 21); + this.combo_2_3.TabIndex = 4; + this.combo_2_3.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // combo_2_2 + // + this.combo_2_2.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_2_2.FormattingEnabled = true; + this.combo_2_2.Location = new System.Drawing.Point(114, 48); + this.combo_2_2.Name = "combo_2_2"; + this.combo_2_2.Size = new System.Drawing.Size(121, 21); + this.combo_2_2.TabIndex = 3; + this.combo_2_2.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // combo_2_1 + // + this.combo_2_1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.combo_2_1.FormattingEnabled = true; + this.combo_2_1.Location = new System.Drawing.Point(114, 21); + this.combo_2_1.Name = "combo_2_1"; + this.combo_2_1.Size = new System.Drawing.Size(121, 21); + this.combo_2_1.TabIndex = 2; + this.combo_2_1.SelectedIndexChanged += new System.EventHandler(this.combo_SelectedIndexChanged); + // + // cbMemcard_2 + // + this.cbMemcard_2.AutoSize = true; + this.cbMemcard_2.Location = new System.Drawing.Point(18, 21); + this.cbMemcard_2.Name = "cbMemcard_2"; + this.cbMemcard_2.Size = new System.Drawing.Size(70, 17); + this.cbMemcard_2.TabIndex = 1; + this.cbMemcard_2.Text = "Memcard"; + this.cbMemcard_2.UseVisualStyleBackColor = true; + this.cbMemcard_2.CheckedChanged += new System.EventHandler(this.cb_changed); + // + // cbMultitap_2 + // + this.cbMultitap_2.AutoSize = true; + this.cbMultitap_2.Enabled = false; + this.cbMultitap_2.Location = new System.Drawing.Point(18, 43); + this.cbMultitap_2.Name = "cbMultitap_2"; + this.cbMultitap_2.Size = new System.Drawing.Size(63, 17); + this.cbMultitap_2.TabIndex = 0; + this.cbMultitap_2.Text = "Multitap"; + this.cbMultitap_2.UseVisualStyleBackColor = true; + this.cbMultitap_2.CheckedChanged += new System.EventHandler(this.cb_changed); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(13, 172); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(177, 13); + this.label1.TabIndex = 14; + this.label1.Text = "Sorry, multitap not supported just yet"; + // + // PSXControllerConfigNew + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.btnCancel; + this.ClientSize = new System.Drawing.Size(586, 201); + this.Controls.Add(this.label1); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOK); + this.Controls.Add(this.groupBox1); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PSXControllerConfigNew"; + this.Text = "Controller / Memcard Configuration"; + this.Load += new System.EventHandler(this.PSXControllerConfigNew_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + } #endregion @@ -470,6 +488,7 @@ private System.Windows.Forms.ComboBox combo_2_2; private System.Windows.Forms.ComboBox combo_2_1; private System.Windows.Forms.CheckBox cbMemcard_2; - private System.Windows.Forms.CheckBox cbMultitap_2; + private System.Windows.Forms.CheckBox cbMultitap_2; + private System.Windows.Forms.Label label1; } } \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.cs b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.cs index 2f7758a097..0c4ab7d700 100644 --- a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.cs +++ b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.cs @@ -34,15 +34,56 @@ namespace BizHawk.Client.EmuHawk combo.SelectedIndex = 0; } + var psxSettings = ((Octoshock)Global.Emulator).GetSyncSettings(); + GuiFromUserConfig(psxSettings.FIOConfig); + RefreshLabels(); + } + + void GuiFromUserConfig(OctoshockFIOConfigUser user) + { + cbMemcard_1.Checked = user.Memcards[0]; + cbMemcard_2.Checked = user.Memcards[1]; + cbMultitap_1.Checked = user.Multitaps[0]; + cbMultitap_2.Checked = user.Multitaps[1]; + + var combos = new[] { combo_1_1, combo_1_2, combo_1_3, combo_1_4, combo_2_1, combo_2_2, combo_2_3, combo_2_4 }; + for (int i = 0; i < 8; i++) + { + var combo = combos[i]; + if (user.Devices8[i] == OctoshockDll.ePeripheralType.None) combo.SelectedIndex = 0; + if (user.Devices8[i] == OctoshockDll.ePeripheralType.DualAnalog) combo.SelectedIndex = 1; + if (user.Devices8[i] == OctoshockDll.ePeripheralType.DualShock) combo.SelectedIndex = 2; + } + } + + OctoshockFIOConfigUser UserConfigFromGui() + { + OctoshockFIOConfigUser uc = new OctoshockFIOConfigUser(); + + uc.Memcards[0] = cbMemcard_1.Checked; + uc.Memcards[1] = cbMemcard_2.Checked; + + uc.Multitaps[0] = cbMultitap_1.Checked; + uc.Multitaps[1] = cbMultitap_2.Checked; + + var combos = new[] { combo_1_1, combo_1_2, combo_1_3, combo_1_4, combo_2_1, combo_2_2, combo_2_3, combo_2_4 }; + for (int i = 0; i < 8; i++) + { + var combo = combos[i]; + if (combo.SelectedIndex == 0) uc.Devices8[i] = OctoshockDll.ePeripheralType.None; + if (combo.SelectedIndex == 1) uc.Devices8[i] = OctoshockDll.ePeripheralType.DualAnalog; + if (combo.SelectedIndex == 2) uc.Devices8[i] = OctoshockDll.ePeripheralType.DualShock; + } + + return uc; } void RefreshLabels() - { - bool multitap_1 = cbMultitap_1.Checked; - bool multitap_2 = cbMultitap_2.Checked; + { + var uc = UserConfigFromGui(); - bool b1 = multitap_1; + bool b1 = uc.Multitaps[0]; lbl_1_1.Visible = b1; lbl_1_2.Visible = b1; lbl_1_3.Visible = b1; @@ -52,9 +93,9 @@ namespace BizHawk.Client.EmuHawk combo_1_4.Enabled = b1; lbl_p_1_2.Visible = b1; lbl_p_1_3.Visible = b1; - lbl_p_1_4.Visible = b1; - - bool b2 = multitap_2; + lbl_p_1_4.Visible = b1; + + bool b2 = uc.Multitaps[1]; lbl_2_1.Visible = b2; lbl_2_2.Visible = b2; lbl_2_3.Visible = b2; @@ -66,21 +107,7 @@ namespace BizHawk.Client.EmuHawk lbl_p_2_3.Visible = b2; lbl_p_2_4.Visible = b2; - OctoshockControlUserConfig uc = new OctoshockControlUserConfig(); - - uc.Multitaps[0] = multitap_1; - uc.Multitaps[1] = multitap_2; - - var combos = new[] { combo_1_1, combo_1_2, combo_1_3, combo_1_4, combo_2_1, combo_2_2, combo_2_3, combo_2_4}; - for (int i = 0; i < 8; i++) - { - var combo = combos[i]; - if (combo.SelectedIndex == 0) uc.Devices8[i] = OctoshockDll.ePeripheralType.None; - if (combo.SelectedIndex == 1) uc.Devices8[i] = OctoshockDll.ePeripheralType.DualAnalog; - if (combo.SelectedIndex == 2) uc.Devices8[i] = OctoshockDll.ePeripheralType.DualShock; - } - - var LC = uc.ToLogicalConfig(); + var LC = uc.ToLogical(); var p_labels = new[] { lbl_p_1_1,lbl_p_1_2,lbl_p_1_3,lbl_p_1_4,lbl_p_2_1,lbl_p_2_2,lbl_p_2_3,lbl_p_2_4}; for (int i = 0; i < 8; i++) @@ -94,7 +121,6 @@ namespace BizHawk.Client.EmuHawk lbl.Visible = true; } } - } private void cb_changed(object sender, EventArgs e) @@ -105,6 +131,18 @@ namespace BizHawk.Client.EmuHawk private void combo_SelectedIndexChanged(object sender, EventArgs e) { RefreshLabels(); + } + + private void btnOK_Click(object sender, EventArgs e) + { + var psxSettings = ((Octoshock)Global.Emulator).GetSyncSettings(); + + psxSettings.FIOConfig = UserConfigFromGui(); + GlobalWin.MainForm.PutCoreSyncSettings(psxSettings); + + DialogResult = DialogResult.OK; + + Close(); } } } diff --git a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.resx b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.resx index 1af7de150c..91f294b89b 100644 --- a/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.resx +++ b/BizHawk.Client.EmuHawk/config/PSX/PSXControllerConfigNew.resx @@ -1,120 +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 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs index 98394e1793..f7692ea626 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs @@ -98,19 +98,22 @@ namespace BizHawk.Client.EmuHawk private void QueryItemBkColor(int index, InputRoll.RollColumn column, ref Color color) { - var record = Tastudio.CurrentTasMovie[GetBranch(index).Frame]; - - if (index == CurrentBranch) - color = TAStudio.CurrentFrame_InputLog; // SystemColors.HotTrack; - else if (record.Lagged.HasValue) + TasBranch branch = GetBranch(index); + if (branch != null) { - if (record.Lagged.Value) + var record = Tastudio.CurrentTasMovie[branch.Frame]; + if (index == CurrentBranch) + color = TAStudio.CurrentFrame_InputLog; // SystemColors.HotTrack; + else if (record.Lagged.HasValue) { - color = TAStudio.LagZone_InputLog; - } - else - { - color = TAStudio.GreenZone_InputLog; + if (record.Lagged.Value) + { + color = TAStudio.LagZone_InputLog; + } + else + { + color = TAStudio.GreenZone_InputLog; + } } } @@ -141,12 +144,11 @@ namespace BizHawk.Client.EmuHawk private void LoadSelectedBranch() { - int index = BranchView.SelectedRows.First(); - //if (CurrentBranch == index) // if the current branch was edited, we should allow loading it. some day there might be a proper check - // return; - if (SelectedBranch != null) { + int index = BranchView.SelectedRows.First(); + //if (CurrentBranch == index) // if the current branch was edited, we should allow loading it. some day there might be a proper check + // return; CurrentBranch = index; LoadBranch(SelectedBranch); BranchView.Refresh(); diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.Designer.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.Designer.cs index 8488b81e49..54fa987917 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.Designer.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.Designer.cs @@ -53,6 +53,8 @@ namespace BizHawk.Client.EmuHawk this.label8 = new System.Windows.Forms.Label(); this.label9 = new System.Windows.Forms.Label(); this.NumSaveStatesLabel = new System.Windows.Forms.Label(); + this.BranchStatesInTasproj = new System.Windows.Forms.CheckBox(); + this.EraseBranchStatesFirst = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.MemCapacityNumeric)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.DiskCapacityNumeric)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.SaveCapacityNumeric)).BeginInit(); @@ -62,7 +64,7 @@ namespace BizHawk.Client.EmuHawk // 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, 113); + this.CancelBtn.Location = new System.Drawing.Point(216, 163); this.CancelBtn.Name = "CancelBtn"; this.CancelBtn.Size = new System.Drawing.Size(60, 23); this.CancelBtn.TabIndex = 0; @@ -73,7 +75,7 @@ namespace BizHawk.Client.EmuHawk // 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, 113); + this.OkBtn.Location = new System.Drawing.Point(150, 163); this.OkBtn.Name = "OkBtn"; this.OkBtn.Size = new System.Drawing.Size(60, 23); this.OkBtn.TabIndex = 1; @@ -116,7 +118,7 @@ namespace BizHawk.Client.EmuHawk // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(12, 9); + this.label2.Location = new System.Drawing.Point(9, 9); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(88, 13); this.label2.TabIndex = 5; @@ -125,7 +127,7 @@ namespace BizHawk.Client.EmuHawk // label3 // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(126, 9); + this.label3.Location = new System.Drawing.Point(147, 9); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(79, 13); this.label3.TabIndex = 6; @@ -134,7 +136,7 @@ namespace BizHawk.Client.EmuHawk // SavestateSizeLabel // this.SavestateSizeLabel.AutoSize = true; - this.SavestateSizeLabel.Location = new System.Drawing.Point(208, 9); + this.SavestateSizeLabel.Location = new System.Drawing.Point(229, 9); this.SavestateSizeLabel.Name = "SavestateSizeLabel"; this.SavestateSizeLabel.Size = new System.Drawing.Size(25, 13); this.SavestateSizeLabel.TabIndex = 7; @@ -188,7 +190,7 @@ namespace BizHawk.Client.EmuHawk // label6 // this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(12, 49); + this.label6.Location = new System.Drawing.Point(9, 49); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(72, 13); this.label6.TabIndex = 5; @@ -196,7 +198,7 @@ namespace BizHawk.Client.EmuHawk // // SaveCapacityNumeric // - this.SaveCapacityNumeric.Location = new System.Drawing.Point(158, 66); + this.SaveCapacityNumeric.Location = new System.Drawing.Point(150, 66); this.SaveCapacityNumeric.Maximum = new decimal(new int[] { 65536, 0, @@ -215,7 +217,7 @@ namespace BizHawk.Client.EmuHawk // label7 // this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(213, 69); + this.label7.Location = new System.Drawing.Point(205, 69); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(21, 13); this.label7.TabIndex = 4; @@ -224,7 +226,7 @@ namespace BizHawk.Client.EmuHawk // label8 // this.label8.AutoSize = true; - this.label8.Location = new System.Drawing.Point(158, 49); + this.label8.Location = new System.Drawing.Point(147, 49); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(112, 13); this.label8.TabIndex = 5; @@ -233,7 +235,7 @@ namespace BizHawk.Client.EmuHawk // label9 // this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(155, 89); + this.label9.Location = new System.Drawing.Point(147, 89); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(84, 13); this.label9.TabIndex = 8; @@ -242,19 +244,45 @@ namespace BizHawk.Client.EmuHawk // NumSaveStatesLabel // this.NumSaveStatesLabel.AutoSize = true; - this.NumSaveStatesLabel.Location = new System.Drawing.Point(242, 89); + this.NumSaveStatesLabel.Location = new System.Drawing.Point(234, 89); this.NumSaveStatesLabel.Name = "NumSaveStatesLabel"; this.NumSaveStatesLabel.Size = new System.Drawing.Size(25, 13); this.NumSaveStatesLabel.TabIndex = 9; this.NumSaveStatesLabel.Text = "1kb"; // + // BranchStatesInTasproj + // + this.BranchStatesInTasproj.AutoSize = true; + this.BranchStatesInTasproj.Location = new System.Drawing.Point(12, 115); + this.BranchStatesInTasproj.Name = "BranchStatesInTasproj"; + this.BranchStatesInTasproj.Size = new System.Drawing.Size(158, 17); + this.BranchStatesInTasproj.TabIndex = 10; + this.BranchStatesInTasproj.Text = "Put branch states to .tasproj"; + this.BranchStatesInTasproj.UseVisualStyleBackColor = true; + this.BranchStatesInTasproj.CheckedChanged += new System.EventHandler(this.BranchStatesInTasproj_CheckedChanged); + // + // EraseBranchStatesFirst + // + this.EraseBranchStatesFirst.AutoSize = true; + this.EraseBranchStatesFirst.Checked = true; + this.EraseBranchStatesFirst.CheckState = System.Windows.Forms.CheckState.Checked; + this.EraseBranchStatesFirst.Location = new System.Drawing.Point(12, 140); + this.EraseBranchStatesFirst.Name = "EraseBranchStatesFirst"; + this.EraseBranchStatesFirst.Size = new System.Drawing.Size(139, 17); + this.EraseBranchStatesFirst.TabIndex = 11; + this.EraseBranchStatesFirst.Text = "Erase branch states first"; + this.EraseBranchStatesFirst.UseVisualStyleBackColor = true; + this.EraseBranchStatesFirst.CheckedChanged += new System.EventHandler(this.EraseBranchStatesFIrst_CheckedChanged); + // // StateHistorySettingsForm // 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, 148); + this.ClientSize = new System.Drawing.Size(288, 198); + this.Controls.Add(this.EraseBranchStatesFirst); + this.Controls.Add(this.BranchStatesInTasproj); this.Controls.Add(this.NumSaveStatesLabel); this.Controls.Add(this.NumStatesLabel); this.Controls.Add(this.label9); @@ -303,5 +331,7 @@ namespace BizHawk.Client.EmuHawk private Label label8; private Label label9; private Label NumSaveStatesLabel; + private CheckBox BranchStatesInTasproj; + private CheckBox EraseBranchStatesFirst; } } diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.cs index a9293a0f9c..e60cc15c7c 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/GreenzoneSettings.cs @@ -44,6 +44,8 @@ namespace BizHawk.Client.EmuHawk SavestateSizeLabel.Text = Math.Round(_stateSizeMb, 2).ToString() + " mb"; CapacityNumeric_ValueChanged(null, null); SaveCapacityNumeric_ValueChanged(null, null); + BranchStatesInTasproj.Checked = Settings.BranchStatesInTasproj; + EraseBranchStatesFirst.Checked = Settings.EraseBranchStatesFirst; } private int MaxStatesInCapacity @@ -79,5 +81,15 @@ namespace BizHawk.Client.EmuHawk { NumSaveStatesLabel.Text = ((int)Math.Floor(SaveCapacityNumeric.Value / _stateSizeMb)).ToString(); } + + private void BranchStatesInTasproj_CheckedChanged(object sender, EventArgs e) + { + Settings.BranchStatesInTasproj = BranchStatesInTasproj.Checked; + } + + private void EraseBranchStatesFIrst_CheckedChanged(object sender, EventArgs e) + { + Settings.EraseBranchStatesFirst = EraseBranchStatesFirst.Checked; + } } } diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs index f1a1eb98a4..aef3c4b631 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.ListView.cs @@ -271,7 +271,6 @@ namespace BizHawk.Client.EmuHawk { CurrentTasMovie.Markers.Add(TasView.LastSelectedIndex.Value, ""); RefreshDialog(); - } else if (columnName != CursorColumnName) // TODO: what about float? { @@ -315,9 +314,7 @@ namespace BizHawk.Client.EmuHawk else index += controllerType.BoolButtons.Count - 1; AutoPatternBool p = BoolPatterns[index]; - // adelikat: I broke it - //Global.AutofireStickyXORAdapter.SetSticky(button, isOn.Value, p); - Global.StickyXORAdapter.SetSticky(button, true); + Global.AutofireStickyXORAdapter.SetSticky(button, isOn.Value, p); } else { @@ -328,9 +325,7 @@ namespace BizHawk.Client.EmuHawk float? value = null; if (isOn.Value) value = 0f; AutoPatternFloat p = FloatPatterns[index]; - // adelikat: I broke it - //Global.AutofireStickyXORAdapter.SetFloat(button, value, p); - Global.StickyXORAdapter.SetFloat(button, value); + Global.AutofireStickyXORAdapter.SetFloat(button, value, p); } } @@ -543,9 +538,20 @@ namespace BizHawk.Client.EmuHawk private void TasView_MouseUp(object sender, MouseEventArgs e) { - if (e.Button == MouseButtons.Right && !TasView.IsPointingAtColumnHeader && !_supressContextMenu) + if (e.Button == MouseButtons.Right && !TasView.IsPointingAtColumnHeader && !_supressContextMenu && TasView.SelectedRows.Any()) { - RightClickMenu.Show(TasView, e.X, e.Y); + if (Global.MovieSession.Movie.FrameCount < TasView.SelectedRows.Max()) + { + // trying to be smart here + // if a loaded branch log is shorter than selection, keep selection until you attempt to call context menu + // you might need it when you load again the branch where this frame exists + TasView.DeselectAll(); + RefreshTasView(); + } + else + { + RightClickMenu.Show(TasView, e.X, e.Y); + } } else if (e.Button == MouseButtons.Left) { diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Navigation.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Navigation.cs index 88899f8814..ddeb65f5ce 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Navigation.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.Navigation.cs @@ -39,7 +39,7 @@ namespace BizHawk.Client.EmuHawk MaybeFollowCursor(); - return; + //return; seriously? well, maybe it's for some insane speedup, but it skipped updating when putting playback to frame zero. } else // Emulate to a future frame { diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs index 85ced34cad..444af2c4fc 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/TAStudio.cs @@ -432,8 +432,12 @@ namespace BizHawk.Client.EmuHawk if (!HandleMovieLoadStuff(newMovie)) return false; + + // clear all selections + TasView.DeselectAll(); + BookMarkControl.Restart(); + MarkerControl.Restart(); - BookMarkControl.UpdateValues(); RefreshDialog(); return true; } @@ -594,6 +598,9 @@ namespace BizHawk.Client.EmuHawk if (MarkerControl != null) MarkerControl.UpdateValues(); + if (BookMarkControl != null) + BookMarkControl.UpdateValues(); + if (undoForm != null && !undoForm.IsDisposed) undoForm.UpdateValues(); } diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/PSXSchema.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/PSXSchema.cs index e2fc87c47c..71082ec872 100644 --- a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/PSXSchema.cs +++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/PSXSchema.cs @@ -14,19 +14,14 @@ namespace BizHawk.Client.EmuHawk var psx = ((Octoshock)Global.Emulator); var settings = (Octoshock.SyncSettings)psx.GetSyncSettings(); - for (int i = 0; i < settings.Controllers.Length; i++) + var fioConfig = settings.FIOConfig.ToLogical(); + for (int i = 0; i < 2; i++) { - if (settings.Controllers[i].IsConnected) - { - if (settings.Controllers[i].Type == Octoshock.ControllerSetting.ControllerType.Gamepad) - { - yield return GamePadController(i + 1); - } - else - { - yield return DualShockController(i + 1); - } - } + int pnum = i + 1; + if (fioConfig.DevicesPlayer[i] == OctoshockDll.ePeripheralType.DualAnalog || fioConfig.DevicesPlayer[i] == OctoshockDll.ePeripheralType.DualShock) + yield return DualShockController(pnum); + if (fioConfig.DevicesPlayer[i] == OctoshockDll.ePeripheralType.Pad) + yield return GamePadController(pnum); } yield return ConsoleButtons(psx); diff --git a/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs b/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs index 7ee58c663b..d1bfdfe875 100644 --- a/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs +++ b/BizHawk.Client.EmuHawk/tools/Watch/RamSearch.cs @@ -1024,7 +1024,7 @@ namespace BizHawk.Client.EmuHawk if (result) { MessageLabel.Text = Path.GetFileName(_currentFileName) + " saved"; - Global.Config.RecentWatches.Add(watches.CurrentFileName); + Settings.RecentSearches.Add(watches.CurrentFileName); } } } diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index 16d6b65e85..ee30a95d48 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -315,6 +315,7 @@ namespace BizHawk.Emulation.Common case ".T64": case ".G64": case ".CRT": + case ".TAP": game.System = "C64"; break; diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 0876ed3bcb..4bc13fcbf3 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -151,6 +151,7 @@ C64.cs + C64.cs @@ -160,6 +161,7 @@ C64.cs + C64.cs @@ -176,14 +178,15 @@ + - + @@ -193,7 +196,8 @@ - + + @@ -797,8 +801,8 @@ Code - + diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs index 30e0bec3fd..a79d0f8881 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs @@ -2931,5 +2931,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (!rdy_freeze) mi++; } //ExecuteOne + + public bool AtInstructionStart() + { + return Microcode[opcode][mi] >= Uop.End; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs index f01f915c14..8e29874b02 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs @@ -51,15 +51,110 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 } } - public IMemoryCallbackSystem MemoryCallbacks + public bool CanStep(StepType type) { - [FeatureNotImplemented] - get { throw new NotImplementedException(); } + switch (type) + { + case StepType.Into: + case StepType.Over: + case StepType.Out: + return true; + default: + return false; + } } - [FeatureNotImplemented] - public void Step(StepType type) { throw new NotImplementedException(); } - public bool CanStep(StepType type) { return false; } + public void Step(StepType type) + { + switch (type) + { + case StepType.Into: + StepInto(); + break; + case StepType.Out: + StepOut(); + break; + case StepType.Over: + StepOver(); + break; + } + } + + private void StepInto() + { + while (board.cpu.AtInstructionStart()) + { + DoCycle(); + } + while (!board.cpu.AtInstructionStart()) + { + DoCycle(); + } + } + + private void StepOver() + { + var instruction = board.cpu.Peek(board.cpu.PC); + + if (instruction == JSR) + { + var destination = board.cpu.PC + JSRSize; + while (board.cpu.PC != destination) + { + StepInto(); + } + } + else + { + StepInto(); + } + } + + private void StepOut() + { + var instr = board.cpu.Peek(board.cpu.PC); + + JSRCount = instr == JSR ? 1 : 0; + + var bailOutFrame = Frame + 1; + + while (true) + { + StepInto(); + instr = board.cpu.Peek(board.cpu.PC); + if (instr == JSR) + { + JSRCount++; + } + else if ((instr == RTS || instr == RTI) && JSRCount <= 0) + { + StepInto(); + JSRCount = 0; + break; + } + else if (instr == RTS || instr == RTI) + { + JSRCount--; + } + else //Emergency Bailout Logic + { + if (Frame == bailOutFrame) + { + break; + } + } + } + } + + private int JSRCount = 0; + + private const byte JSR = 0x20; + private const byte RTI = 0x40; + private const byte RTS = 0x60; + + private const byte JSRSize = 3; + + public IMemoryCallbackSystem MemoryCallbacks { get; private set; } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDisassemblable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDisassemblable.cs new file mode 100644 index 0000000000..9afa0b3220 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDisassemblable.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.Commodore64 +{ + public partial class C64 : IDisassemblable + { + public string Cpu + { + get { return "6510"; } set { } + } + + public string PCRegisterName + { + get { return "PC"; } + } + + public IEnumerable AvailableCpus + { + get { yield return "6510"; } + } + + public string Disassemble(MemoryDomain m, uint addr, out int length) + { + return Components.M6502.MOS6502X.Disassemble((ushort)addr, out length, (a) => m.PeekByte(a)); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.ISettable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.ISettable.cs new file mode 100644 index 0000000000..b6e382a294 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.ISettable.cs @@ -0,0 +1,77 @@ +using BizHawk.Emulation.Common; + +using Newtonsoft.Json; + +using System; +using System.ComponentModel; +using System.Drawing; + + +namespace BizHawk.Emulation.Cores.Computers.Commodore64 +{ + // adelikat: changing settings to default object untl there are actually settings, as the ui depends on it to know if there are any settings avaialable + public partial class C64 : ISettable + { + public object /*C64Settings*/ GetSettings() + { + //return Settings.Clone(); + return null; + } + + public C64SyncSettings GetSyncSettings() + { + return SyncSettings.Clone(); + } + + public bool PutSettings(object /*C64Settings*/ o) + { + //Settings = o; + return false; + } + + public bool PutSyncSettings(C64SyncSettings o) + { + SyncSettings = o; + return false; + } + + internal C64Settings Settings { get; private set; } + internal C64SyncSettings SyncSettings { get; private set; } + + public class C64Settings + { + public C64Settings Clone() + { + return (C64Settings)MemberwiseClone(); + } + + public C64Settings() + { + BizHawk.Common.SettingsUtil.SetDefaultValues(this); + } + } + + public class C64SyncSettings + { + [DisplayName("VIC type")] + [Description("Set the type of video chip to use")] + [DefaultValue(VicType.PAL)] + public VicType vicType { get; set; } + + public C64SyncSettings Clone() + { + return (C64SyncSettings)MemberwiseClone(); + } + + public C64SyncSettings() + { + BizHawk.Common.SettingsUtil.SetDefaultValues(this); + } + } + + public enum VicType + { + PAL, NTSC, NTSC_OLD, DREAN + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs index 99633d578a..7fc07731ba 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs @@ -42,25 +42,48 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 private C64 _c64; - public Motherboard(C64 c64, Region initRegion) + public Motherboard(C64 c64, C64.VicType initRegion) { // note: roms need to be added on their own externally _c64 = c64; - + int clockNum, clockDen, mainsFrq; + switch (initRegion) + { + case C64.VicType.PAL: + clockNum = 17734475; + clockDen = 18; + mainsFrq = 50; + break; + case C64.VicType.NTSC: + case C64.VicType.NTSC_OLD: + clockNum = 11250000; + clockDen = 11; + mainsFrq = 60; + break; + case C64.VicType.DREAN: + clockNum = 14328225; + clockDen = 14; + mainsFrq = 50; + break; + default: + throw new System.Exception(); + } cartPort = new CartridgePort(); cassPort = new CassettePortDevice(); - cia0 = new MOS6526(initRegion); - cia1 = new MOS6526(initRegion); + cia0 = new MOS6526(clockNum, clockDen*mainsFrq); + cia1 = new MOS6526(clockNum, clockDen*mainsFrq); colorRam = new Chip2114(); cpu = new MOS6510(); pla = new MOSPLA(); ram = new Chip4864(); serPort = new SerialPort(); - sid = MOS6581.Create(44100, initRegion); + sid = MOS6581.Create(44100, clockNum, clockDen); switch (initRegion) { - case Region.NTSC: vic = MOS6567.Create(); break; - case Region.PAL: vic = MOS6569.Create(); break; + case C64.VicType.NTSC: vic = MOS6567R8.Create(); break; + case C64.VicType.PAL: vic = MOS6569.Create(); break; + case C64.VicType.NTSC_OLD: vic = MOS6567R56A.Create(); break; + case C64.VicType.DREAN: vic = MOS6572.Create(); break; } userPort = new UserPortDevice(); } @@ -102,6 +125,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 sid.HardReset(); vic.HardReset(); userPort.HardReset(); + cassPort.HardReset(); // because of how mapping works, the cpu needs to be hard reset twice cpu.HardReset(); diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs index 3d0028a017..2294bb131f 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.cs @@ -4,28 +4,25 @@ using System.IO; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Computers.Commodore64.MOS; +using System.Windows.Forms; namespace BizHawk.Emulation.Cores.Computers.Commodore64 { - // TODO: use the EMulation.Common Region enum - public enum Region - { - NTSC, - PAL - } - [CoreAttributes( "C64Hawk", "SaxxonPIke", isPorted: false, isReleased: false )] - [ServiceNotApplicable(typeof(IRegionable), typeof(ISettable<,>))] - sealed public partial class C64 : IEmulator, IStatable, IInputPollable, IDriveLight, IDebuggable + [ServiceNotApplicable(typeof(ISettable<,>))] + sealed public partial class C64 : IEmulator, IStatable, IInputPollable, IDriveLight, IDebuggable, IDisassemblable, IRegionable, ISettable { // framework - public C64(CoreComm comm, GameInfo game, byte[] rom, string romextension) + public C64(CoreComm comm, GameInfo game, byte[] rom, string romextension, object Settings, object SyncSettings) { + PutSyncSettings((C64SyncSettings)SyncSettings ?? new C64SyncSettings()); + PutSettings((C64Settings)Settings ?? new C64Settings()); + ServiceProvider = new BasicServiceProvider(this); InputCallbacks = new InputCallbackSystem(); @@ -33,14 +30,37 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 inputFileInfo.Data = rom; inputFileInfo.Extension = romextension; CoreComm = comm; - Init(Region.PAL); + Init(this.SyncSettings.vicType); cyclesPerFrame = board.vic.CyclesPerFrame; SetupMemoryDomains(); + MemoryCallbacks = new MemoryCallbackSystem(); HardReset(); (ServiceProvider as BasicServiceProvider).Register(board.vic); } + + /*private DisplayType queryUserForRegion() + { + Form prompt = new Form() { Width = 160, Height = 120, FormBorderStyle = FormBorderStyle.FixedDialog, Text = "Region selector", StartPosition = FormStartPosition.CenterScreen }; + Label textLabel = new Label() { Left = 10, Top = 10, Width = 260, Text = "Please choose a region:" }; + RadioButton palButton = new RadioButton() { Left = 10, Top = 30, Width = 70, Text = "PAL", Checked = true }; + RadioButton ntscButton = new RadioButton() { Left = 80, Top = 30, Width = 70, Text = "NTSC" }; + Button confirmation = new Button() { Text = "Ok", Left = 40, Width = 80, Top = 60, DialogResult = DialogResult.OK }; + confirmation.Click += (sender, e) => { prompt.Close(); }; + prompt.Controls.Add(textLabel); + prompt.Controls.Add(palButton); + prompt.Controls.Add(ntscButton); + prompt.Controls.Add(confirmation); + prompt.AcceptButton = confirmation; + + if (prompt.ShowDialog() != DialogResult.OK || !palButton.Checked && !ntscButton.Checked) + { + throw new Exception("Can't construct new C64 because you didn't choose anything"); + } + return palButton.Checked ? DisplayType.PAL : DisplayType.NTSC; + }*/ + // internal variables private int _frame = 0; private int cyclesPerFrame; @@ -63,6 +83,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 _frame = 0; _lagcount = 0; _islag = false; + frameCycles = 0; } // audio/video @@ -92,6 +113,12 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 public IEmulatorServiceProvider ServiceProvider { get; private set; } + public DisplayType Region + { + get; + private set; + } + public void Dispose() { if (board.sid != null) @@ -101,76 +128,66 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 } } + int frameCycles; + // process frame public void FrameAdvance(bool render, bool rendersound) { - board.inputRead = false; - board.PollInput(); - board.cpu.LagCycles = 0; - - for (int count = 0; count < cyclesPerFrame; count++) + do { - //disk.Execute(); - board.Execute(); + DoCycle(); + } + while (frameCycles != 0); + } -#if false - if (board.cpu.PC == 0xE16F && (board.cpu.ReadPort() & 0x7) == 7) + private void DoCycle() + { + if (frameCycles == 0) { + board.inputRead = false; + board.PollInput(); + board.cpu.LagCycles = 0; + } + + //disk.Execute(); + board.Execute(); + frameCycles++; + + // load PRG file if needed + if (loadPrg) + { + // check to see if cpu PC is at the BASIC warm start vector + if (board.cpu.PC == ((board.ram.Peek(0x0303) << 8) | board.ram.Peek(0x0302))) { - // HUGE kernal hack to load files - // the only purpose for this is to be able to run the Lorenz - // test suite! + //board.ram.Poke(0x0302, 0xAE); + //board.ram.Poke(0x0303, 0xA7); + ////board.ram.Poke(0x0302, board.ram.Peek(0x0308)); + ////board.ram.Poke(0x0303, board.ram.Peek(0x0309)); - int fileNameLength = board.ram.Peek(0xB7); - int fileNameOffset = board.ram.Peek(0xBB) | ((int)board.ram.Peek(0xBC) << 8); - byte[] fileNameRaw = new byte[fileNameLength]; - for (int i = 0; i < fileNameLength; i++) - { - fileNameRaw[i] = board.ram.Peek(fileNameOffset + i); - } - var enc = System.Text.Encoding.ASCII; - string fileName = enc.GetString(fileNameRaw); - string filePath = Path.Combine(@"E:\Programming\Visual Studio 2013\Vice\testprogs\general\Lorenz-2.15\src\", fileName + ".prg"); - if (File.Exists(filePath)) - { - PRG.Load(board.pla, File.ReadAllBytes(filePath)); - } - board.cpu.PC = 0xE1B5; - } -#endif - - // load PRG file if needed - if (loadPrg) - { - // check to see if cpu PC is at the BASIC warm start vector - if (board.cpu.PC == ((board.ram.Peek(0x0303) << 8) | board.ram.Peek(0x0302))) - { - //board.ram.Poke(0x0302, 0xAE); - //board.ram.Poke(0x0303, 0xA7); - ////board.ram.Poke(0x0302, board.ram.Peek(0x0308)); - ////board.ram.Poke(0x0303, board.ram.Peek(0x0309)); - - //if (inputFileInfo.Data.Length >= 6) - //{ - // board.ram.Poke(0x0039, inputFileInfo.Data[4]); - // board.ram.Poke(0x003A, inputFileInfo.Data[5]); - //} - PRG.Load(board.pla, inputFileInfo.Data); - loadPrg = false; - } + //if (inputFileInfo.Data.Length >= 6) + //{ + // board.ram.Poke(0x0039, inputFileInfo.Data[4]); + // board.ram.Poke(0x003A, inputFileInfo.Data[5]); + //} + PRG.Load(board.pla, inputFileInfo.Data); + loadPrg = false; } } - board.Flush(); - _islag = !board.inputRead; + if (frameCycles == cyclesPerFrame) + { + board.Flush(); + _islag = !board.inputRead; - if (_islag) - _lagcount++; - _frame++; + if (_islag) + _lagcount++; + frameCycles -= cyclesPerFrame; + _frame++; - //Console.WriteLine("CPUPC: " + C64Util.ToHex(board.cpu.PC, 4) + " 1541PC: " + C64Util.ToHex(disk.PC, 4)); + //Console.WriteLine("CPUPC: " + C64Util.ToHex(board.cpu.PC, 4) + " 1541PC: " + C64Util.ToHex(disk.PC, 4)); - int test = board.cpu.LagCycles; - DriveLightOn = DriveLED; + int test = board.cpu.LagCycles; + DriveLightOn = DriveLED; + } } private void HandleFirmwareError(string file) @@ -190,7 +207,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 return result; } - private void Init(Region initRegion) + private void Init(VicType initRegion) { board = new Motherboard(this, initRegion); InitRoms(); @@ -213,6 +230,13 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 board.cartPort.Connect(cart); } break; + case @".TAP": + CassettePort.Tape tape = CassettePort.Tape.Load(inputFileInfo.Data); + if (tape != null) + { + board.cassPort.Connect(tape); + } + break; case @".PRG": if (inputFileInfo.Data.Length > 2) loadPrg = true; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs index d8f567713c..44aa71e893 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/CassettePortDevice.cs @@ -11,24 +11,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort { public Func ReadDataOutput; public Func ReadMotor; + Commodore64.CassettePort.Tape tape; public void HardReset() { + if (tape != null) tape.rewind(); } virtual public bool ReadDataInputBuffer() { - return true; + return tape != null && !ReadMotor() ? tape.read() : true; } virtual public bool ReadSenseBuffer() { - return true; + return tape == null; // Just assume that "play" is constantly pressed as long as a tape is inserted } public void SyncState(Serializer ser) { SaveState.SyncObject(ser, this); } + + internal void Connect(Tape tape) + { + this.tape = tape; + } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/Tape.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/Tape.cs new file mode 100644 index 0000000000..2c04d1bf09 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/CassettePort/Tape.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +using BizHawk.Common; + + +namespace BizHawk.Emulation.Cores.Computers.Commodore64.CassettePort +{ + /** + * This class represents a tape. Only TAP-style tapes are supported for now. + */ + class Tape + { + private readonly byte[] tapeData; + private readonly byte version; + private uint pos, cycle; + private readonly uint start, end; + + public Tape(byte version, byte[] tapeData, uint start, uint end) + { + this.version = version; + this.tapeData = tapeData; + this.start = start; + this.end = end; + rewind(); + } + + // Rewinds the tape back to start + public void rewind() + { + pos = start; + cycle = 0; + } + + // Reads from tape, this will tell the caller if the flag pin should be raised + public bool read() + { + if (cycle == 0) + { + if (pos >= end) + { + return true; + } + else + { + cycle = ((uint)tapeData[pos++])*8; + if (cycle == 0) + { + if (version == 0) + { + cycle = 256 * 8; // unspecified overflow condition + } + else + { + cycle = BitConverter.ToUInt32(tapeData, (int)pos-1)>>8; + pos += 3; + if (cycle == 0) + { + throw new Exception("Bad tape data"); + } + } + } + } + } + + // Send a single negative pulse at the end of a cycle + return --cycle != 0; + } + + // Try to construct a tape file from file data. Returns null if not a tape file, throws exceptions for bad tape files. + // (Note that some error conditions aren't caught right here.) + static public Tape Load(byte[] tapeFile) + { + Tape result = null; + + if (System.Text.Encoding.ASCII.GetString(tapeFile, 0, 12) == "C64-TAPE-RAW") + { + byte version = tapeFile[12]; + if (version > 1) throw new Exception("This tape has an unsupported version"); + uint size = BitConverter.ToUInt32(tapeFile, 16); + if (size + 20 != tapeFile.Length) + { + throw new Exception("Tape file header specifies a length that doesn't match the file size"); + } + result = new Tape(version, tapeFile, 20, (uint)tapeFile.Length); + } + return result; + } + + public void SyncState(Serializer ser) + { + ser.BeginSection("tape"); + ser.Sync("pos", ref pos); + ser.Sync("cycle", ref cycle); + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/CassettePort.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/CassettePort.cs deleted file mode 100644 index 26ddeb1b49..0000000000 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/CassettePort.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using BizHawk.Common; - -namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS -{ - public class CassettePort - { - public Func ReadDataOutput; - public Func ReadMotor; - - public void HardReset() - { - } - - virtual public bool ReadDataInputBuffer() - { - return true; - } - - virtual public bool ReadSenseBuffer() - { - return true; - } - - public void SyncState(Serializer ser) - { - SaveState.SyncObject(ser, this); - } - } -} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs index 557312b0a9..51cb927531 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6510.cs @@ -99,9 +99,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } set { - lagCycles = value; - } - } + lagCycles = value; + } + } + + internal bool AtInstructionStart() + { + return cpu.AtInstructionStart(); + } // ------------------------------------ diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs index bc0d060fb1..30a3bdfd9d 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526-2.cs @@ -233,7 +233,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS LatchedPort portA; LatchedPort portB; - public MOS6526_2(Region region) + public MOS6526_2(Common.DisplayType region) { a = new CiaTimer(serialPortA, underFlowA); b = new CiaTimer(serialPortB, underFlowB); @@ -241,10 +241,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS portB = new LatchedPort(); switch (region) { - case Region.NTSC: + case Common.DisplayType.NTSC: tod_period = 14318181 / 140; break; - case Region.PAL: + case Common.DisplayType.PAL: tod_period = 17734472 / 180; break; } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs index 97a8ab2185..cec480ae07 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6526.cs @@ -51,7 +51,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS // ------------------------------------ bool alarmSelect; - Region chipRegion; bool cntPos; bool enableIntAlarm; bool enableIntFlag; @@ -77,16 +76,22 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS byte[] todAlarm; bool todAlarmPM; int todCounter; - int todCounterLatch; bool todIn; bool todPM; - + bool oldFlag; // ------------------------------------ - public MOS6526(Region region) + int todStepsNum; + int todStepsDen; + + // todStepsNum/todStepsDen is the number of clock cycles it takes the external clock source to advance one cycle + // (50 or 60 Hz depending on AC frequency in use). + // By default the CIA assumes 60 Hz and will thus count incorrectly when fed with 50 Hz. + public MOS6526(int todStepsNum, int todStepsDen) { - chipRegion = region; - enableIntTimer = new bool[2]; + this.todStepsNum = todStepsNum; + this.todStepsDen = todStepsDen; + enableIntTimer = new bool[2]; intTimer = new bool[2]; timerDelay = new int[2]; timerInMode = new InMode[2]; @@ -96,7 +101,6 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS timerRunMode = new RunMode[2]; tod = new byte[4]; todAlarm = new byte[4]; - SetTodIn(chipRegion); portA = new LatchedPort(); portB = new LatchedPort(); @@ -161,6 +165,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS cntPos = false; underflow[0] = false; underflow[1] = false; + + bool newFlag = ReadFlag(); + intFlag |= oldFlag && !newFlag; + oldFlag = newFlag; } } @@ -203,29 +211,14 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS todAlarm[1] = 0; todAlarm[2] = 0; todAlarm[3] = 0; - todCounter = todCounterLatch; - todIn = (chipRegion == Region.PAL); + todCounter = 0; + todIn = false; todPM = false; pinCnt = false; pinPC = true; } - private void SetTodIn(Region region) - { - switch (region) - { - case Region.NTSC: - todCounterLatch = 14318181 / 140; - todIn = false; - break; - case Region.PAL: - todCounterLatch = 17734472 / 180; - todIn = true; - break; - } - } - // ------------------------------------ private byte BCDAdd(byte i, byte j, out bool overflow) @@ -338,9 +331,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS { bool todV; - if (todCounter == 0) + if (todCounter <= 0) { - todCounter = todCounterLatch; + todCounter += todStepsNum*(todIn ? 6 : 5); tod[0] = BCDAdd(tod[0], 1, out todV); if (tod[0] >= 10) { @@ -366,7 +359,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } } } - todCounter--; + todCounter -= todStepsDen; } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R56A.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R56A.cs new file mode 100644 index 0000000000..49570065e4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R56A.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS +{ + // vic ntsc old + // TODO is everything right? it's mostly a copy from the other NTSC chip with tweaks wherever it was neccessary to fix something + static public class MOS6567R56A + { + static int cycles = 64; + static int scanwidth = cycles * 8; + static int lines = 262; + static int vblankstart = 0x00D % lines; + static int vblankend = 0x018 % lines; + static int hblankoffset = 20; + static int hblankstart = (0x18C + hblankoffset) % scanwidth; + static int hblankend = (0x1F0 + hblankoffset) % scanwidth; + + static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, -1, -1); + static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174); + static int[] ba = Vic.TimingBuilder_BA(fetch); + static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); + + static int[][] pipeline = new int[][] + { + timing, + fetch, + ba, + act + }; + + static public Vic Create() + { + return new Vic( + cycles, lines, + pipeline, + 14318181 / 14, + hblankstart, hblankend, + vblankstart, vblankend + ); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R8.cs similarity index 79% rename from BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567.cs rename to BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R8.cs index 939e8211f8..f6470b0d6a 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6567R8.cs @@ -1,41 +1,41 @@ -using System.Drawing; - -namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS -{ - // vic ntsc - static public class MOS6567 - { - static int cycles = 65; - static int scanwidth = cycles * 8; - static int lines = 263; - static int vblankstart = 0x00D % lines; - static int vblankend = 0x018 % lines; - static int hblankoffset = 20; - static int hblankstart = (0x18C + hblankoffset) % scanwidth; - static int hblankend = (0x1F0 + hblankoffset) % scanwidth; - - static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, 0x18C, 8); - static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174); - static int[] ba = Vic.TimingBuilder_BA(fetch); - static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); - - static int[][] pipeline = new int[][] - { - timing, - fetch, - ba, - act - }; - - static public Vic Create() - { - return new Vic( - cycles, lines, - pipeline, - 14318181 / 14, - hblankstart, hblankend, - vblankstart, vblankend - ); - } - } -} +using System.Drawing; + +namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS +{ + // vic ntsc + static public class MOS6567R8 + { + static int cycles = 65; + static int scanwidth = cycles * 8; + static int lines = 263; + static int vblankstart = 0x00D % lines; + static int vblankend = 0x018 % lines; + static int hblankoffset = 20; + static int hblankstart = (0x18C + hblankoffset) % scanwidth - 8; // -8 because the VIC repeats internal pixel cycles around 0x18C + static int hblankend = (0x1F0 + hblankoffset) % scanwidth - 8; + + static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, 0x18C, 8); + static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174); + static int[] ba = Vic.TimingBuilder_BA(fetch); + static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); + + static int[][] pipeline = new int[][] + { + timing, + fetch, + ba, + act + }; + + static public Vic Create() + { + return new Vic( + cycles, lines, + pipeline, + 14318181 / 14, + hblankstart, hblankend, + vblankstart, vblankend + ); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6572.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6572.cs new file mode 100644 index 0000000000..ad11e5737c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6572.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS +{ + // pal n / drean - TODO correct? + class MOS6572 + { + static int cycles = 65; + static int scanwidth = cycles * 8; + static int lines = 312; + static int vblankstart = 0x12C % lines; + static int vblankend = 0x00F % lines; + static int hblankoffset = 20; + static int hblankstart = (0x18C + hblankoffset) % scanwidth - 8; // -8 because the VIC repeats internal pixel cycles around 0x18C + static int hblankend = (0x1F0 + hblankoffset) % scanwidth - 8; + + static int[] timing = Vic.TimingBuilder_XRaster(0x19C, 0x200, scanwidth, 0x18C, 8); + static int[] fetch = Vic.TimingBuilder_Fetch(timing, 0x174); + static int[] ba = Vic.TimingBuilder_BA(fetch); + static int[] act = Vic.TimingBuilder_Act(timing, 0x004, 0x14C, hblankstart, hblankend); + + static int[][] pipeline = new int[][] + { + timing, + fetch, + ba, + act + }; + + static public Vic Create() + { + return new Vic( + cycles, lines, + pipeline, + 14328225 / 14, + hblankstart, hblankend, + vblankstart, vblankend + ); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs index 9485937697..d19800e88c 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/MOS6581.cs @@ -4119,9 +4119,9 @@ } }; - static public Sid Create(int newSampleRate, Region newRegion) + static public Sid Create(int newSampleRate, int clockFrqNum, int clockFrqDen) { - return new Sid(waveTable, newSampleRate, newRegion); + return new Sid(waveTable, (uint)newSampleRate, (uint)clockFrqNum, (uint)clockFrqDen); } } } diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs index d74e8fc828..873dcfacb5 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Sid.cs @@ -42,20 +42,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS public Func ReadPotX; public Func ReadPotY; - public Sid(int[][] newWaveformTable, int newSampleRate, Region newRegion) + public Sid(int[][] newWaveformTable, uint sampleRate, uint cyclesNum, uint cyclesDen) { - uint cyclesPerSec = 0; - uint cyclesNum; - uint cyclesDen; - uint sampleRate = 44100; - - switch (newRegion) - { - case Region.NTSC: cyclesNum = 14318181; cyclesDen = 14; break; - case Region.PAL: cyclesNum = 17734472; cyclesDen = 18; break; - default: return; - } - waveformTable = newWaveformTable; envelopes = new Envelope[3]; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs index f510f09159..ac5d441678 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.Parse.cs @@ -145,8 +145,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } else if (parseba == 0x1000) { - pinBA = !badline; - } + pinBA = !badline; + } else { parsecycleBAsprite0 = (parseba & 0x000F); diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs index ba587c6360..c1bbf6a699 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Vic.cs @@ -75,6 +75,9 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } // display enable compare + if (rasterLine == 0) + badlineEnable = false; + if (rasterLine == 0x030) badlineEnable |= displayEnable; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs index e07f6c7f45..cec1c68c4e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs @@ -10,7 +10,7 @@ using System.ComponentModel; namespace BizHawk.Emulation.Cores.Nintendo.GBA { - [CoreAttributes("mGBA", "endrift", true, false, "NOT DONE", "NOT DONE", false)] + [CoreAttributes("mGBA", "endrift", true, true, "0.2.0", "https://mgba.io/", false)] [ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))] public class MGBAHawk : IEmulator, IVideoProvider, ISyncSoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable, ISettable { diff --git a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs index 2552eac888..ca16d2ce99 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs @@ -39,47 +39,49 @@ namespace BizHawk.Emulation.Cores.Sony.PSX private void SetControllerButtons() { ControllerDefinition = new ControllerDefinition(); - ControllerDefinition.Name = _SyncSettings.Controllers.All(c => c.Type == ControllerSetting.ControllerType.Gamepad) - ? "PSX Gamepad Controller" - : "PSX DualShock Controller"; // Meh, more nuanced logic doesn't really work with a simple property + ControllerDefinition.Name = "PSX DualShock Controller"; // <-- for compatibility + //ControllerDefinition.Name = "PSX FrontIO"; // TODO - later rename to this, I guess, so it's less misleading. don't want to wreck keybindings yet. ControllerDefinition.BoolButtons.Clear(); ControllerDefinition.FloatControls.Clear(); - for (int i = 0; i < _SyncSettings.Controllers.Length; i++) + var cfg = _SyncSettings.FIOConfig.ToLogical(); + + for (int i = 0; i < cfg.NumPlayers; i++) { - if (_SyncSettings.Controllers[i].IsConnected) - { + int pnum = i + 1; ControllerDefinition.BoolButtons.AddRange(new[] { - "P" + (i + 1) + " Up", - "P" + (i + 1) + " Down", - "P" + (i + 1) + " Left", - "P" + (i + 1) + " Right", - "P" + (i + 1) + " Select", - "P" + (i + 1) + " Start", - "P" + (i + 1) + " Square", - "P" + (i + 1) + " Triangle", - "P" + (i + 1) + " Circle", - "P" + (i + 1) + " Cross", - "P" + (i + 1) + " L1", - "P" + (i + 1) + " R1", - "P" + (i + 1) + " L2", - "P" + (i + 1) + " R2", + "P" + pnum + " Up", + "P" + pnum + " Down", + "P" + pnum + " Left", + "P" + pnum + " Right", + "P" + pnum + " Select", + "P" + pnum + " Start", + "P" + pnum + " Square", + "P" + pnum + " Triangle", + "P" + pnum + " Circle", + "P" + pnum + " Cross", + "P" + pnum + " L1", + "P" + pnum + " R1", + "P" + pnum + " L2", + "P" + pnum + " R2", }); - if (_SyncSettings.Controllers[i].Type != ControllerSetting.ControllerType.Gamepad) + var type = cfg.DevicesPlayer[i]; + + if (type == OctoshockDll.ePeripheralType.DualShock || type == OctoshockDll.ePeripheralType.DualAnalog) { - ControllerDefinition.BoolButtons.Add("P" + (i + 1) + " L3"); - ControllerDefinition.BoolButtons.Add("P" + (i + 1) + " R3"); - ControllerDefinition.BoolButtons.Add("P" + (i + 1) + " MODE"); + ControllerDefinition.BoolButtons.Add("P" + pnum + " L3"); + ControllerDefinition.BoolButtons.Add("P" + pnum + " R3"); + ControllerDefinition.BoolButtons.Add("P" + pnum + " MODE"); ControllerDefinition.FloatControls.AddRange(new[] { - "P" + (i + 1) + " LStick X", - "P" + (i + 1) + " LStick Y", - "P" + (i + 1) + " RStick X", - "P" + (i + 1) + " RStick Y" + "P" + pnum + " LStick X", + "P" + pnum + " LStick Y", + "P" + pnum + " RStick X", + "P" + pnum + " RStick Y" }); ControllerDefinition.FloatRanges.Add(new[] { 0.0f, 128.0f, 255.0f }); @@ -88,7 +90,6 @@ namespace BizHawk.Emulation.Cores.Sony.PSX ControllerDefinition.FloatRanges.Add(new[] { 255.0f, 128.0f, 0.0f }); } } - } ControllerDefinition.BoolButtons.AddRange(new[] { @@ -374,21 +375,18 @@ namespace BizHawk.Emulation.Cores.Sony.PSX //setup the controller based on sync settings SetControllerButtons(); - var lookup = new Dictionary { - { ControllerSetting.ControllerType.Gamepad, OctoshockDll.ePeripheralType.Pad }, - { ControllerSetting.ControllerType.DualAnalog, OctoshockDll.ePeripheralType.DualAnalog }, - { ControllerSetting.ControllerType.DualShock, OctoshockDll.ePeripheralType.DualShock }, + var fioCfg = _SyncSettings.FIOConfig; + if(fioCfg.Devices8[0] != OctoshockDll.ePeripheralType.None) + OctoshockDll.shock_Peripheral_Connect(psx, 0x01, fioCfg.Devices8[0]); + if (fioCfg.Devices8[4] != OctoshockDll.ePeripheralType.None) + OctoshockDll.shock_Peripheral_Connect(psx, 0x02, fioCfg.Devices8[4]); + + var memcardTransaction = new OctoshockDll.ShockMemcardTransaction() + { + transaction = OctoshockDll.eShockMemcardTransaction.Connect }; - - if (_SyncSettings.Controllers[0].IsConnected) - { - OctoshockDll.shock_Peripheral_Connect(psx, 0x01, lookup[_SyncSettings.Controllers[0].Type]); - } - - if (_SyncSettings.Controllers[1].IsConnected) - { - OctoshockDll.shock_Peripheral_Connect(psx, 0x02, lookup[_SyncSettings.Controllers[1].Type]); - } + if (fioCfg.Memcards[0]) OctoshockDll.shock_Peripheral_MemcardTransact(psx, 0x01, ref memcardTransaction); + if (fioCfg.Memcards[1]) OctoshockDll.shock_Peripheral_MemcardTransact(psx, 0x02, ref memcardTransaction); //do this after framebuffers and peripherals and whatever crap are setup. kind of lame, but thats how it is for now StudySaveBufferSize(); @@ -423,65 +421,48 @@ namespace BizHawk.Emulation.Cores.Sony.PSX void SetInput() { - uint buttons = 0; + var fioCfg = _SyncSettings.FIOConfig.ToLogical(); - if (_SyncSettings.Controllers[0].IsConnected) + int portNum = 0x01; + foreach (int slot in new[] { 0, 4 }) { - //dualshock style - if (Controller["P1 Select"]) buttons |= 1; - if (Controller["P1 L3"]) buttons |= 2; - if (Controller["P1 R3"]) buttons |= 4; - if (Controller["P1 Start"]) buttons |= 8; - if (Controller["P1 Up"]) buttons |= 16; - if (Controller["P1 Right"]) buttons |= 32; - if (Controller["P1 Down"]) buttons |= 64; - if (Controller["P1 Left"]) buttons |= 128; - if (Controller["P1 L2"]) buttons |= 256; - if (Controller["P1 R2"]) buttons |= 512; - if (Controller["P1 L1"]) buttons |= 1024; - if (Controller["P1 R1"]) buttons |= 2048; - if (Controller["P1 Triangle"]) buttons |= 4096; - if (Controller["P1 Circle"]) buttons |= 8192; - if (Controller["P1 Cross"]) buttons |= 16384; - if (Controller["P1 Square"]) buttons |= 32768; - if (Controller["P1 MODE"]) buttons |= 65536; + //no input to set + if (fioCfg.Devices8[slot] == OctoshockDll.ePeripheralType.None) + continue; - byte left_x = (byte)Controller.GetFloat("P1 LStick X"); - byte left_y = (byte)Controller.GetFloat("P1 LStick Y"); - byte right_x = (byte)Controller.GetFloat("P1 RStick X"); - byte right_y = (byte)Controller.GetFloat("P1 RStick Y"); + uint buttons = 0; + string pstring = "P" + fioCfg.PlayerAssignments[slot] + " "; - OctoshockDll.shock_Peripheral_SetPadInput(psx, 0x01, buttons, left_x, left_y, right_x, right_y); - } + if (Controller[pstring + "Select"]) buttons |= 1; + if (Controller[pstring + "Start"]) buttons |= 8; + if (Controller[pstring + "Up"]) buttons |= 16; + if (Controller[pstring + "Right"]) buttons |= 32; + if (Controller[pstring + "Down"]) buttons |= 64; + if (Controller[pstring + "Left"]) buttons |= 128; + if (Controller[pstring + "L2"]) buttons |= 256; + if (Controller[pstring + "R2"]) buttons |= 512; + if (Controller[pstring + "L1"]) buttons |= 1024; + if (Controller[pstring + "R1"]) buttons |= 2048; + if (Controller[pstring + "Triangle"]) buttons |= 4096; + if (Controller[pstring + "Circle"]) buttons |= 8192; + if (Controller[pstring + "Cross"]) buttons |= 16384; + if (Controller[pstring + "Square"]) buttons |= 32768; - if (_SyncSettings.Controllers[1].IsConnected) - { - //dualshock style - buttons = 0; - if (Controller["P2 Select"]) buttons |= 1; - if (Controller["P2 L3"]) buttons |= 2; - if (Controller["P2 R3"]) buttons |= 4; - if (Controller["P2 Start"]) buttons |= 8; - if (Controller["P2 Up"]) buttons |= 16; - if (Controller["P2 Right"]) buttons |= 32; - if (Controller["P2 Down"]) buttons |= 64; - if (Controller["P2 Left"]) buttons |= 128; - if (Controller["P2 L2"]) buttons |= 256; - if (Controller["P2 R2"]) buttons |= 512; - if (Controller["P2 L1"]) buttons |= 1024; - if (Controller["P2 R1"]) buttons |= 2048; - if (Controller["P2 Triangle"]) buttons |= 4096; - if (Controller["P2 Circle"]) buttons |= 8192; - if (Controller["P2 Cross"]) buttons |= 16384; - if (Controller["P2 Square"]) buttons |= 32768; - if (Controller["P2 MODE"]) buttons |= 65536; + byte left_x = 0, left_y = 0, right_x = 0, right_y = 0; + if (fioCfg.Devices8[slot] == OctoshockDll.ePeripheralType.DualShock || fioCfg.Devices8[slot] == OctoshockDll.ePeripheralType.DualAnalog) + { + if (Controller[pstring + "L3"]) buttons |= 2; + if (Controller[pstring + "R3"]) buttons |= 4; + if (Controller[pstring + "MODE"]) buttons |= 65536; - byte left_x = (byte)Controller.GetFloat("P2 LStick X"); - byte left_y = (byte)Controller.GetFloat("P2 LStick Y"); - byte right_x = (byte)Controller.GetFloat("P2 RStick X"); - byte right_y = (byte)Controller.GetFloat("P2 RStick Y"); + left_x = (byte)Controller.GetFloat(pstring + "LStick X"); + left_y = (byte)Controller.GetFloat(pstring + "LStick Y"); + right_x = (byte)Controller.GetFloat(pstring + "RStick X"); + right_y = (byte)Controller.GetFloat(pstring + "RStick Y"); + } - OctoshockDll.shock_Peripheral_SetPadInput(psx, 0x02, buttons, left_x, left_y, right_x, right_y); + OctoshockDll.shock_Peripheral_SetPadInput(psx, portNum, buttons, left_x, left_y, right_x, right_y); + portNum <<= 1; } } @@ -858,25 +839,42 @@ namespace BizHawk.Emulation.Cores.Sony.PSX public byte[] CloneSaveRam() { - var buf = new byte[128 * 1024]; - fixed (byte* pbuf = buf) + var cfg = _SyncSettings.FIOConfig.ToLogical(); + int nMemcards = cfg.NumMemcards; + var buf = new byte[128 * 1024 * nMemcards]; + for (int i = 0, idx = 0, addr=0x01; i < 2; i++, addr<<=1) { - var transaction = new OctoshockDll.ShockMemcardTransaction(); - transaction.buffer128k = pbuf; - transaction.transaction = OctoshockDll.eShockMemcardTransaction.Read; - OctoshockDll.shock_Peripheral_MemcardTransact(psx, 0x01, ref transaction); + if (cfg.Memcards[i]) + { + fixed (byte* pbuf = buf) + { + var transaction = new OctoshockDll.ShockMemcardTransaction(); + transaction.buffer128k = pbuf + idx * 128 * 1024; + transaction.transaction = OctoshockDll.eShockMemcardTransaction.Read; + OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction); + idx++; + } + } } return buf; } public void StoreSaveRam(byte[] data) { - fixed (byte* pbuf = data) + var cfg = _SyncSettings.FIOConfig.ToLogical(); + for (int i = 0, idx = 0, addr = 0x01; i < 2; i++, addr <<= 1) { - var transaction = new OctoshockDll.ShockMemcardTransaction(); - transaction.buffer128k = pbuf; - transaction.transaction = OctoshockDll.eShockMemcardTransaction.Write; - OctoshockDll.shock_Peripheral_MemcardTransact(psx, 0x01, ref transaction); + if (cfg.Memcards[i]) + { + fixed (byte* pbuf = data) + { + var transaction = new OctoshockDll.ShockMemcardTransaction(); + transaction.buffer128k = pbuf + idx * 128 * 1024; + transaction.transaction = OctoshockDll.eShockMemcardTransaction.Write; + OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction); + idx++; + } + } } } @@ -884,9 +882,20 @@ namespace BizHawk.Emulation.Cores.Sony.PSX { get { - var transaction = new OctoshockDll.ShockMemcardTransaction(); - transaction.transaction = OctoshockDll.eShockMemcardTransaction.CheckDirty; - return OctoshockDll.shock_Peripheral_MemcardTransact(psx, 0x01, ref transaction) == OctoshockDll.SHOCK_TRUE; + var cfg = _SyncSettings.FIOConfig.ToLogical(); + for (int i = 0, addr = 0x01; i < 2; i++, addr <<= 1) + { + if (cfg.Memcards[i]) + { + var transaction = new OctoshockDll.ShockMemcardTransaction(); + transaction.transaction = OctoshockDll.eShockMemcardTransaction.CheckDirty; + OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction); + if (OctoshockDll.shock_Peripheral_MemcardTransact(psx, addr, ref transaction) == OctoshockDll.SHOCK_TRUE) + return true; + } + } + + return false; } } @@ -1055,49 +1064,23 @@ namespace BizHawk.Emulation.Cores.Sony.PSX { public SyncSettings Clone() { - var ret = (SyncSettings)MemberwiseClone(); - ret.Controllers = Controllers.Select(x => x.Clone()).ToArray(); - - return ret; + return JsonConvert.DeserializeObject(JsonConvert.SerializeObject(this)); } public bool EnableLEC; - public ControllerSetting[] Controllers = + public SyncSettings() { - new ControllerSetting - { - IsConnected = true, - Type = ControllerSetting.ControllerType.DualShock - }, - new ControllerSetting - { - IsConnected = false, - Type = ControllerSetting.ControllerType.DualShock - } - }; - } - - public class ControllerSetting - { - public ControllerSetting Clone() - { - return (ControllerSetting)this.MemberwiseClone(); + //initialize with historical default settings + var user = new OctoshockFIOConfigUser(); + user.Memcards[0] = user.Memcards[1] = true; + user.Multitaps[0] = user.Multitaps[0] = false; + user.Devices8[0] = OctoshockDll.ePeripheralType.DualShock; + user.Devices8[4] = OctoshockDll.ePeripheralType.DualShock; + FIOConfig = user; } - public bool IsConnected { get; set; } - public ControllerType Type { get; set; } - - public enum ControllerType - { - Gamepad, - - [Description("Dual Analog")] - DualAnalog, - - [Description("Dual Shock")] - DualShock - } + public OctoshockFIOConfigUser FIOConfig; } public enum eHorizontalClipping @@ -1200,12 +1183,15 @@ namespace BizHawk.Emulation.Cores.Sony.PSX public bool PutSyncSettings(SyncSettings o) { - //check for reboot-required options (well, none right now) - bool reboot = false; + //currently LEC and pad settings changes both require reboot + bool reboot = true; + + //we could do it this way roughly if we need to + //if(JsonConvert.SerializeObject(o.FIOConfig) != JsonConvert.SerializeObject(_SyncSettings.FIOConfig) + _SyncSettings = o; - - //TODO - store settings into core? or we can just keep doing it before frameadvance + return reboot; } diff --git a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/OctoshockControlConfig.cs b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/OctoshockFIOConfig.cs similarity index 54% rename from BizHawk.Emulation.Cores/Consoles/Sony/PSX/OctoshockControlConfig.cs rename to BizHawk.Emulation.Cores/Consoles/Sony/PSX/OctoshockFIOConfig.cs index 6cbf28bf3f..c4e0845434 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/OctoshockControlConfig.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/OctoshockFIOConfig.cs @@ -3,28 +3,57 @@ using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Sony.PSX { - public class OctoshockControlUserConfig + /// + /// Represents a user's view of what equipment is plugged into the PSX FIO + /// + public class OctoshockFIOConfigUser { public bool[] Multitaps = new bool[2]; + public bool[] Memcards = new bool[2]; public OctoshockDll.ePeripheralType[] Devices8 = new OctoshockDll.ePeripheralType[8]; - public OctoshockControlLogicalConfig ToLogicalConfig() + public OctoshockFIOConfigLogical ToLogical() { - var lc = new OctoshockControlLogicalConfig(); + var lc = new OctoshockFIOConfigLogical(); lc.PopulateFrom(this); return lc; } } - public class OctoshockControlLogicalConfig + /// + /// Represents a baked-down view of what's plugged into the PSX FIO. + /// But really, users are interested in it too (its what produces the player number assignments) + /// + public class OctoshockFIOConfigLogical { - public int[] PlayerAssignments = new int[8]; public bool[] Multitaps; + public bool[] Memcards; public OctoshockDll.ePeripheralType[] Devices8; - internal void PopulateFrom(OctoshockControlUserConfig userConfig) + /// + /// Total number of players defined + /// + public int NumPlayers; + + /// + /// The player number on each of the input slots + /// + public int[] PlayerAssignments = new int[8]; + + /// + /// The device type associated with each player + /// + public OctoshockDll.ePeripheralType[] DevicesPlayer = new OctoshockDll.ePeripheralType[8]; + + /// + /// Total number of connected memcards + /// + public int NumMemcards { get { return (Memcards[0] ? 1 : 0) + (Memcards[1] ? 1 : 0); } } + + internal void PopulateFrom(OctoshockFIOConfigUser userConfig) { Multitaps = (bool[])userConfig.Multitaps.Clone(); + Memcards = (bool[])userConfig.Memcards.Clone(); Devices8 = (OctoshockDll.ePeripheralType[])userConfig.Devices8.Clone(); int id = 1; @@ -38,6 +67,18 @@ namespace BizHawk.Emulation.Cores.Sony.PSX if (userConfig.Devices8[5] == OctoshockDll.ePeripheralType.None || !userConfig.Multitaps[1]) PlayerAssignments[5] = -1; else PlayerAssignments[5] = id++; if (userConfig.Devices8[6] == OctoshockDll.ePeripheralType.None || !userConfig.Multitaps[1]) PlayerAssignments[6] = -1; else PlayerAssignments[6] = id++; if (userConfig.Devices8[7] == OctoshockDll.ePeripheralType.None || !userConfig.Multitaps[1]) PlayerAssignments[7] = -1; else PlayerAssignments[7] = id++; + + NumPlayers = id - 1; + + for (int i = 0; i < 8; i++) + { + int pnum = i+1; + for (int j = 0; j < 8; j++) + { + if(PlayerAssignments[j] == pnum) + DevicesPlayer[i] = userConfig.Devices8[j]; + } + } } } diff --git a/Dist/changelog.txt b/Dist/changelog.txt index 9aff7cb33e..78bad25545 100644 --- a/Dist/changelog.txt +++ b/Dist/changelog.txt @@ -13,6 +13,7 @@ **Sequentially number screenshots taken in the same second **Finetune hotkey config dialog **Leniency fixes to cue loading +**Make on-screen watches position editable **Fix #461 - Repair autofire patterns **Fix #477 - Support key input over IPC for automation **Fix #490 - Ampersands in hex editor flakeout @@ -38,6 +39,7 @@ **Add movie.startsfromsavestate() and movie.startsfromsaveram(), movie.getheader(), movie.getcomments(), and movie.getsubtitles() **Add memorysavestate library **Fix bizstring.split() +**Fix crashes removing callbacks **Fix #469 - forms.newform() : add an onclosed callback optional parameter **Fix #463 - memory.readbyterange off-by-one **Fix #498 - fix quicknes lua rendering being offset incorrectly sometimes @@ -49,8 +51,9 @@ **Issue #481 - keep scroll bar position when resetting the current rom as opposed to changing to a new one *PSXHawk -**Update to mednafen 0.9.38.6 -**Fix loading of some .psf filess +**Update to mednafen 0.9.38.7 +**Support 0-2 pads/shocks and 0-2 memcards +**Fix loading of some .psf files **Add overscan clipping and deinterlacer options **Fix resolution management and PAR stuff for some PAL modes **Support .xml disc bundling tool as alternative to .m3u diff --git a/Version/VersionInfo.cs b/Version/VersionInfo.cs index e08f42ee47..50bd4eb4e5 100644 --- a/Version/VersionInfo.cs +++ b/Version/VersionInfo.cs @@ -1,12 +1,12 @@ static class VersionInfo { - public const string MAINVERSION = "1.10.0"; // Use numbers only or the new version notification won't work - public static readonly string RELEASEDATE = "June 15, 2015"; + public const string MAINVERSION = "1.11.2"; // Use numbers only or the new version notification won't work + public static readonly string RELEASEDATE = "October 9, 2015"; public static readonly bool DeveloperBuild = true; public static readonly string HomePage = "http://tasvideos.org/BizHawk.html"; public static string GetEmuVersion() { - return DeveloperBuild ? "SVN " + SubWCRev.SVN_REV : ("Version " + MAINVERSION); + return DeveloperBuild ? ("GIT " + SubWCRev.GIT_BRANCH + "#" + SubWCRev.GIT_SHORTHASH) : ("Version " + MAINVERSION); } } diff --git a/output/dll/octoshock.dll b/output/dll/octoshock.dll index e8a0a325dc..1dadf2281d 100644 Binary files a/output/dll/octoshock.dll and b/output/dll/octoshock.dll differ diff --git a/psx/octoshock/bizhawk/octoshock.sln b/psx/octoshock/bizhawk/octoshock.sln index e19efdc80b..78e1a0d864 100644 --- a/psx/octoshock/bizhawk/octoshock.sln +++ b/psx/octoshock/bizhawk/octoshock.sln @@ -1,6 +1,6 @@  Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +# Visual Studio 2015 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "octoshock", "octoshock.vcxproj", "{5F35CAFC-6208-4FBE-AD17-0E69BA3F70EC}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniclient", "..\test\miniclient\miniclient.vcxproj", "{5A0DAC84-1170-4B1A-B9A9-F566A1D97790}" diff --git a/psx/octoshock/docs/upstreaminfo.txt b/psx/octoshock/docs/upstreaminfo.txt index cb21989a7b..ac6a0b6e72 100644 --- a/psx/octoshock/docs/upstreaminfo.txt +++ b/psx/octoshock/docs/upstreaminfo.txt @@ -80,4 +80,12 @@ [OK] psx/gpu : change to comments [OK] psx/memcard : change to debug output 0.9.38.4 -> 0.9.38.6 -[OK] psx/gpu & gpu_sprite : Fixed GPU emulation timing bugs that caused graphical glitches in "Mr. Driller G". \ No newline at end of file +[OK] psx/gpu & gpu_sprite : Fixed GPU emulation timing bugs that caused graphical glitches in "Mr. Driller G". +0.9.38.5 -> 0.9.38.7 +[OK] psx/cpu : Revisions to exception handling +[OK] psx/cpu : Many revisions and cleanups to branch and exception handling in opcode implementations +[OK] psx/dis : Just some basic disassembly changes +[OK] psx/gte : Cleanup +[OK] psx/psx : Cleanup +[OK] psx/timer : Major functional changes +[NO] psx/timer : Added loadstate sanity checks \ No newline at end of file diff --git a/psx/octoshock/psx/cpu.cpp b/psx/octoshock/psx/cpu.cpp index c7c20ec732..4be2a6699e 100644 --- a/psx/octoshock/psx/cpu.cpp +++ b/psx/octoshock/psx/cpu.cpp @@ -20,6 +20,12 @@ #include "cpu.h" #include "math_ops.h" +#if 0 +#define EXP_ILL_CHECK(n) {n;} +#else +#define EXP_ILL_CHECK(n) {} +#endif + //not very organized, is it void* g_ShockTraceCallbackOpaque = NULL; ShockCallback_Trace g_ShockTraceCallback = NULL; @@ -360,43 +366,54 @@ INLINE void PS_CPU::WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, ui } } -uint32 PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NPM) -{ - const bool InBDSlot = !(NPM & 0x3); - uint32 handler = 0x80000080; - - assert(code < 16); - - if(code != EXCEPTION_INT && code != EXCEPTION_BP && code != EXCEPTION_SYSCALL) - { - PSX_DBG(PSX_DBG_WARNING, "Exception: %08x @ PC=0x%08x(IBDS=%d) -- IPCache=0x%02x -- IPEND=0x%02x -- SR=0x%08x ; IRQC_Status=0x%04x -- IRQC_Mask=0x%04x\n", code, PC, InBDSlot, IPCache, (CP0.CAUSE >> 8) & 0xFF, CP0.SR, - IRQ_GetRegister(IRQ_GSREG_STATUS, NULL, 0), IRQ_GetRegister(IRQ_GSREG_MASK, NULL, 0)); - } - - if(CP0.SR & (1 << 22)) // BEV - handler = 0xBFC00180; - - CP0.EPC = PC; - if(InBDSlot) - CP0.EPC -= 4; - - if(ADDBT) - ADDBT(PC, handler, true); - - // "Push" IEc and KUc(so that the new IEc and KUc are 0) - CP0.SR = (CP0.SR & ~0x3F) | ((CP0.SR << 2) & 0x3F); - - // Setup cause register - CP0.CAUSE &= 0x0000FF00; - CP0.CAUSE |= code << 2; - - // If EPC was adjusted -= 4 because we were in a branch delay slot, set the bit. - if(InBDSlot) - CP0.CAUSE |= 0x80000000; - - RecalcIPCache(); - - return(handler); + +uint32 NO_INLINE PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 NPM, const uint32 instr) +{ + const bool AfterBranchInstr = !(NPM & 0x1); + const bool BranchTaken = !(NPM & 0x3); + uint32 handler = 0x80000080; + + assert(code < 16); + + if(code != EXCEPTION_INT && code != EXCEPTION_BP && code != EXCEPTION_SYSCALL) + { + static const char* exmne[16] = + { + "INT", "MOD", "TLBL", "TLBS", "ADEL", "ADES", "IBE", "DBE", "SYSCALL", "BP", "RI", "COPU", "OV", NULL, NULL, NULL + }; + + PSX_DBG(PSX_DBG_WARNING, "[CPU] Exception %s(0x%02x) @ PC=0x%08x(NP=0x%08x, NPM=0x%08x), Instr=0x%08x, IPCache=0x%02x, CAUSE=0x%08x, SR=0x%08x, IRQC_Status=0x%04x, IRQC_Mask=0x%04x\n", + exmne[code], code, PC, NP, NPM, instr, IPCache, CP0.CAUSE, CP0.SR, IRQ_GetRegister(IRQ_GSREG_STATUS, NULL, 0), IRQ_GetRegister(IRQ_GSREG_MASK, NULL, 0)); + } + + if(CP0.SR & (1 << 22)) // BEV + handler = 0xBFC00180; + + CP0.EPC = PC; + if(AfterBranchInstr) + { + CP0.EPC -= 4; + CP0.TAR = (PC & (NPM | 3)) + NP; + } + + if(ADDBT) + ADDBT(PC, handler, true); + + // "Push" IEc and KUc(so that the new IEc and KUc are 0) + CP0.SR = (CP0.SR & ~0x3F) | ((CP0.SR << 2) & 0x3F); + + // Setup cause register + CP0.CAUSE &= 0x0000FF00; + CP0.CAUSE |= code << 2; + + // If EPC was adjusted -= 4 because we are after a branch instruction, set bit 31. + CP0.CAUSE |= AfterBranchInstr << 31; + CP0.CAUSE |= BranchTaken << 30; + CP0.CAUSE |= (instr << 2) & (0x3 << 28); // CE + + RecalcIPCache(); + + return(handler); } #define BACKING_TO_ACTIVE \ @@ -413,9 +430,12 @@ uint32 PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NPM) BACKED_LDWhich = LDWhich; \ BACKED_LDValue = LDValue; -#define GPR_DEPRES_BEGIN { uint8 back = ReadAbsorb[0]; -#define GPR_DEP(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; } -#define GPR_RES(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; } +// +// Should come before DO_LDS() so the EXP_ILL_CHECK() emulator debugging macro in GPR_DEP() will work properly. +// +#define GPR_DEPRES_BEGIN { uint8 back = ReadAbsorb[0]; +#define GPR_DEP(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; EXP_ILL_CHECK(if(LDWhich > 0 && LDWhich < 0x20 && LDWhich == tn) { PSX_DBG(PSX_DBG_WARNING, "[CPU] Instruction at PC=0x%08x in load delay slot has dependency on load target register(0x%02x): SR=0x%08x\n", PC, LDWhich, CP0.SR); }) } +#define GPR_RES(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; } #define GPR_DEPRES_END ReadAbsorb[0] = back; } template @@ -481,7 +501,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) { // This will block interrupt processing, but since we're going more for keeping broken homebrew/hacks from working // than super-duper-accurate pipeline emulation, it shouldn't be a problem. - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); + CP0.BADVA = PC; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, 0); new_PC_mask = 0; goto OpDone; } @@ -576,37 +597,54 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) #endif #define DO_LDS() { GPR[LDWhich] = LDValue; ReadAbsorb[LDWhich] = LDAbsorb; ReadFudge = LDWhich; ReadAbsorbWhich |= LDWhich & 0x1F; LDWhich = 0x20; } - #define BEGIN_OPF(name, arg_op, arg_funct) { op_##name: /*assert( ((arg_op) ? (0x40 | (arg_op)) : (arg_funct)) == opf); */ + #define BEGIN_OPF(name) { op_##name: #define END_OPF goto OpDone; } - #define DO_BRANCH(offset, mask) \ - { \ - if(ILHMode) \ - { \ - uint32 old_PC = PC; \ - PC = (PC & new_PC_mask) + new_PC; \ - if(old_PC == ((PC & (mask)) + (offset))) \ - { \ - if(MDFN_densb(&FastMap[PC >> FAST_MAP_SHIFT][PC]) == 0) \ - { \ - if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */ \ - { \ - timestamp = next_event_ts; \ - } \ - } \ - } \ - } \ - else \ - PC = (PC & new_PC_mask) + new_PC; \ - new_PC = (offset); \ - new_PC_mask = (mask) & ~3; \ - /* Lower bits of new_PC_mask being clear signifies being in a branch delay slot. (overloaded behavior for performance) */ \ - \ - if(DebugMode && ADDBT) \ - { \ - ADDBT(PC, (PC & new_PC_mask) + new_PC, false); \ - } \ - goto SkipNPCStuff; \ + +#define DO_BRANCH(arg_cond, arg_offset, arg_mask, arg_dolink, arg_linkreg)\ + { \ + const bool cond = (arg_cond); \ + const uint32 offset = (arg_offset); \ + const uint32 mask = (arg_mask); \ + const uint32 old_PC = PC; \ + \ + EXP_ILL_CHECK(if(!(new_PC_mask & 0x03)) { PSX_DBG(PSX_DBG_WARNING, "[CPU] Branch instruction at PC=0x%08x in branch delay slot: SR=0x%08x\n", PC, CP0.SR);}) \ + \ + PC = (PC & new_PC_mask) + new_PC; \ + \ + /* Clear lower bit to signify being after a branch instruction (overloaded behavior for performance). */ \ + new_PC_mask = ~1U; \ + new_PC = 4; \ + \ + if(arg_dolink) \ + GPR[(arg_linkreg)] = PC + 4; \ + \ + if(cond) \ + { \ + if(ILHMode) \ + { \ + if(old_PC == ((PC & mask) + offset)) \ + { \ + if(MDFN_densb(&FastMap[PC >> FAST_MAP_SHIFT][PC]) == 0) \ + { \ + if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */ \ + { \ + timestamp = next_event_ts; \ + } \ + } \ + } \ + } \ + \ + /* Lower bits of new_PC_mask being clear signifies being in a branch delay slot. (overloaded behavior for performance) */ \ + new_PC = offset; \ + new_PC_mask = mask & ~3; \ + \ + if(DebugMode && ADDBT) \ + { \ + ADDBT(PC, (PC & new_PC_mask) + new_PC, false); \ + } \ + } \ + goto SkipNPCStuff; \ } #define ITYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = (int32)(int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/ @@ -626,1642 +664,1713 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) #define CGE(l) case __COUNTER__ - CGESB: goto l; #define CGEND } } #endif - - CGBEGIN - CGE(op_SLL) CGE(op_ILL) CGE(op_SRL) CGE(op_SRA) CGE(op_SLLV) CGE(op_ILL) CGE(op_SRLV) CGE(op_SRAV) - CGE(op_JR) CGE(op_JALR) CGE(op_ILL) CGE(op_ILL) CGE(op_SYSCALL) CGE(op_BREAK) CGE(op_ILL) CGE(op_ILL) - CGE(op_MFHI) CGE(op_MTHI) CGE(op_MFLO) CGE(op_MTLO) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - CGE(op_MULT) CGE(op_MULTU) CGE(op_DIV) CGE(op_DIVU) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - CGE(op_ADD) CGE(op_ADDU) CGE(op_SUB) CGE(op_SUBU) CGE(op_AND) CGE(op_OR) CGE(op_XOR) CGE(op_NOR) - CGE(op_ILL) CGE(op_ILL) CGE(op_SLT) CGE(op_SLTU) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - - CGE(op_ILL) CGE(op_BCOND) CGE(op_J) CGE(op_JAL) CGE(op_BEQ) CGE(op_BNE) CGE(op_BLEZ) CGE(op_BGTZ) - CGE(op_ADDI) CGE(op_ADDIU) CGE(op_SLTI) CGE(op_SLTIU) CGE(op_ANDI) CGE(op_ORI) CGE(op_XORI) CGE(op_LUI) - CGE(op_COP0) CGE(op_COP1) CGE(op_COP2) CGE(op_COP3) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - CGE(op_LB) CGE(op_LH) CGE(op_LWL) CGE(op_LW) CGE(op_LBU) CGE(op_LHU) CGE(op_LWR) CGE(op_ILL) - CGE(op_SB) CGE(op_SH) CGE(op_SWL) CGE(op_SW) CGE(op_ILL) CGE(op_ILL) CGE(op_SWR) CGE(op_ILL) - CGE(op_LWC0) CGE(op_LWC1) CGE(op_LWC2) CGE(op_LWC3) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - CGE(op_SWC0) CGE(op_SWC1) CGE(op_SWC2) CGE(op_SWC3) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) - - // Interrupt portion of this table is constructed so that an interrupt won't be taken when the PC is pointing to a GTE instruction, - // to avoid problems caused by pipeline vs coprocessor nuances that aren't emulated. - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - - CGE(op_ILL) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_COP2) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) - CGEND - - { - BEGIN_OPF(ILL, 0, 0); - PSX_WARNING("[CPU] Unknown instruction @%08x = %08x, op=%02x, funct=%02x", PC, instr, instr >> 26, (instr & 0x3F)); - DO_LDS(); - new_PC = Exception(EXCEPTION_RI, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // - // ADD - Add Word - // - BEGIN_OPF(ADD, 0, 0x20); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rs] + GPR[rt]; - bool ep = ((~(GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000; - - DO_LDS(); - - if(MDFN_UNLIKELY(ep)) - { - new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask); - new_PC_mask = 0; - } - else - GPR[rd] = result; - - END_OPF; - - // - // ADDI - Add Immediate Word - // - BEGIN_OPF(ADDI, 0x08, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rt); - GPR_DEPRES_END - - uint32 result = GPR[rs] + immediate; - bool ep = ((~(GPR[rs] ^ immediate)) & (GPR[rs] ^ result)) & 0x80000000; - - DO_LDS(); - - if(MDFN_UNLIKELY(ep)) - { - new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask); - new_PC_mask = 0; - } - else - GPR[rt] = result; - - END_OPF; - - // - // ADDIU - Add Immediate Unsigned Word - // - BEGIN_OPF(ADDIU, 0x09, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rt); - GPR_DEPRES_END - - uint32 result = GPR[rs] + immediate; - - DO_LDS(); - - GPR[rt] = result; - - END_OPF; - - // - // ADDU - Add Unsigned Word - // - BEGIN_OPF(ADDU, 0, 0x21); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rs] + GPR[rt]; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - // - // AND - And - // - BEGIN_OPF(AND, 0, 0x24); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rs] & GPR[rt]; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - // - // ANDI - And Immediate - // - BEGIN_OPF(ANDI, 0x0C, 0); - ITYPE_ZE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rt); - GPR_DEPRES_END - - uint32 result = GPR[rs] & immediate; - - DO_LDS(); - - GPR[rt] = result; - - END_OPF; - - // - // BEQ - Branch on Equal - // - BEGIN_OPF(BEQ, 0x04, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - bool result = (GPR[rs] == GPR[rt]); - - DO_LDS(); - - if(result) - { - DO_BRANCH((immediate << 2), ~0U); - } - END_OPF; - - // Bah, why does MIPS encoding have to be funky like this. :( - // Handles BGEZ, BGEZAL, BLTZ, BLTZAL - BEGIN_OPF(BCOND, 0x01, 0); - const uint32 tv = GPR[(instr >> 21) & 0x1F]; - uint32 riv = (instr >> 16) & 0x1F; - uint32 immediate = (int32)(int16)(instr & 0xFFFF); - bool result = (int32)(tv ^ (riv << 31)) < 0; - - GPR_DEPRES_BEGIN - GPR_DEP((instr >> 21) & 0x1F); - - if(riv & 0x10) - GPR_RES(31); - - GPR_DEPRES_END - - - DO_LDS(); - - if(riv & 0x10) // Unconditional link reg setting. - GPR[31] = PC + 8; - - if(result) - { - DO_BRANCH((immediate << 2), ~0U); - } - - END_OPF; - - - // - // BGTZ - Branch on Greater than Zero - // - BEGIN_OPF(BGTZ, 0x07, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - bool result = (int32)GPR[rs] > 0; - - DO_LDS(); - - if(result) - { - DO_BRANCH((immediate << 2), ~0U); - } - END_OPF; - - // - // BLEZ - Branch on Less Than or Equal to Zero - // - BEGIN_OPF(BLEZ, 0x06, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - bool result = (int32)GPR[rs] <= 0; - - DO_LDS(); - - if(result) - { - DO_BRANCH((immediate << 2), ~0U); - } - - END_OPF; - - // - // BNE - Branch on Not Equal - // - BEGIN_OPF(BNE, 0x05, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - bool result = GPR[rs] != GPR[rt]; - - DO_LDS(); - - if(result) - { - DO_BRANCH((immediate << 2), ~0U); - } - - END_OPF; - - // - // BREAK - Breakpoint - // - BEGIN_OPF(BREAK, 0, 0x0D); - PSX_WARNING("[CPU] BREAK BREAK BREAK BREAK DAAANCE -- PC=0x%08x", PC); - - DO_LDS(); - new_PC = Exception(EXCEPTION_BP, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // Cop "instructions": CFCz(no CP0), COPz, CTCz(no CP0), LWCz(no CP0), MFCz, MTCz, SWCz(no CP0) - // - // COP0 instructions - BEGIN_OPF(COP0, 0x10, 0); - uint32 sub_op = (instr >> 21) & 0x1F; - - if(sub_op & 0x10) - sub_op = 0x10 + (instr & 0x3F); - - //printf("COP0 thing: %02x\n", sub_op); - switch(sub_op) - { - default: - DO_LDS(); - break; - - case 0x00: // MFC0 - Move from Coprocessor - { - uint32 rt = (instr >> 16) & 0x1F; - uint32 rd = (instr >> 11) & 0x1F; - - //printf("MFC0: rt=%d <- rd=%d(%08x)\n", rt, rd, CP0.Regs[rd]); - DO_LDS(); - - LDAbsorb = 0; - LDWhich = rt; - LDValue = CP0.Regs[rd]; - } - break; - - case 0x04: // MTC0 - Move to Coprocessor - { - uint32 rt = (instr >> 16) & 0x1F; - uint32 rd = (instr >> 11) & 0x1F; - uint32 val = GPR[rt]; - - if(rd != CP0REG_PRID && rd != CP0REG_CAUSE && rd != CP0REG_SR && val) - { - PSX_WARNING("[CPU] Unimplemented MTC0: rt=%d(%08x) -> rd=%d", rt, GPR[rt], rd); - } - - switch(rd) - { - case CP0REG_BPC: - CP0.BPC = val; - break; - - case CP0REG_BDA: - CP0.BDA = val; - break; - - case CP0REG_TAR: - CP0.TAR = val; - break; - - case CP0REG_DCIC: - CP0.DCIC = val & 0xFF80003F; - break; - - case CP0REG_BDAM: - CP0.BDAM = val; - break; - - case CP0REG_BPCM: - CP0.BPCM = val; - break; - - case CP0REG_CAUSE: - CP0.CAUSE &= ~(0x3 << 8); - CP0.CAUSE |= val & (0x3 << 8); - RecalcIPCache(); - break; - - case CP0REG_SR: - if((CP0.SR ^ val) & 0x10000) - PSX_DBG(PSX_DBG_SPARSE, "[CPU] IsC %u->%u\n", (bool)(CP0.SR & (1U << 16)), (bool)(val & (1U << 16))); - - CP0.SR = val & ~( (0x3 << 26) | (0x3 << 23) | (0x3 << 6)); - RecalcIPCache(); - break; - } - } - DO_LDS(); - break; - - case (0x10 + 0x10): // RFE - // "Pop" - DO_LDS(); - CP0.SR = (CP0.SR & ~0x0F) | ((CP0.SR >> 2) & 0x0F); - RecalcIPCache(); - break; - } - END_OPF; - - // - // COP1 - // - BEGIN_OPF(COP1, 0x11, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // - // COP2 - // - BEGIN_OPF(COP2, 0x12, 0); - uint32 sub_op = (instr >> 21) & 0x1F; - - switch(sub_op) - { - default: - DO_LDS(); - break; - - case 0x00: // MFC2 - Move from Coprocessor - { - uint32 rt = (instr >> 16) & 0x1F; - uint32 rd = (instr >> 11) & 0x1F; - - DO_LDS(); - - if(timestamp < gte_ts_done) - { - LDAbsorb = gte_ts_done - timestamp; - timestamp = gte_ts_done; - } - else - LDAbsorb = 0; - - LDWhich = rt; - LDValue = GTE_ReadDR(rd); - } - break; - - case 0x04: // MTC2 - Move to Coprocessor - { - uint32 rt = (instr >> 16) & 0x1F; - uint32 rd = (instr >> 11) & 0x1F; - uint32 val = GPR[rt]; - - if(timestamp < gte_ts_done) - timestamp = gte_ts_done; - - //printf("GTE WriteDR: %d %d\n", rd, val); - GTE_WriteDR(rd, val); - DO_LDS(); - } - break; - - case 0x02: // CFC2 - { - uint32 rt = (instr >> 16) & 0x1F; - uint32 rd = (instr >> 11) & 0x1F; - - DO_LDS(); - - if(timestamp < gte_ts_done) - { - LDAbsorb = gte_ts_done - timestamp; - timestamp = gte_ts_done; - } - else - LDAbsorb = 0; - - LDWhich = rt; - LDValue = GTE_ReadCR(rd); - - //printf("GTE ReadCR: %d %d\n", rd, GPR[rt]); - } - break; - - case 0x06: // CTC2 - { - uint32 rt = (instr >> 16) & 0x1F; - uint32 rd = (instr >> 11) & 0x1F; - uint32 val = GPR[rt]; - - //printf("GTE WriteCR: %d %d\n", rd, val); - - if(timestamp < gte_ts_done) - timestamp = gte_ts_done; - - GTE_WriteCR(rd, val); - DO_LDS(); - } - break; - - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: - case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: - //printf("%08x\n", PC); - if(timestamp < gte_ts_done) - timestamp = gte_ts_done; - gte_ts_done = timestamp + GTE_Instruction(instr); - DO_LDS(); - break; - } - END_OPF; - - // - // COP3 - // - BEGIN_OPF(COP3, 0x13, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // - // LWC0 - // - BEGIN_OPF(LWC0, 0x30, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // - // LWC1 - // - BEGIN_OPF(LWC1, 0x31, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // - // LWC2 - // - BEGIN_OPF(LWC2, 0x32, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - DO_LDS(); - - if(MDFN_UNLIKELY(address & 3)) - { - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); - new_PC_mask = 0; - } - else - { - if(timestamp < gte_ts_done) - timestamp = gte_ts_done; - - GTE_WriteDR(rt, ReadMemory(timestamp, address, false, true)); - } - // GTE stuff here - END_OPF; - - // - // LWC3 - // - BEGIN_OPF(LWC3, 0x33, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - - // - // SWC0 - // - BEGIN_OPF(SWC0, 0x38, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // - // SWC1 - // - BEGIN_OPF(SWC1, 0x39, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - // - // SWC2 - // - BEGIN_OPF(SWC2, 0x3A, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - if(MDFN_UNLIKELY(address & 0x3)) - { - new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask); - new_PC_mask = 0; - } - else - { - if(timestamp < gte_ts_done) - timestamp = gte_ts_done; - - WriteMemory(timestamp, address, GTE_ReadDR(rt)); - } - DO_LDS(); - END_OPF; - - // - // SWC3 - /// - BEGIN_OPF(SWC3, 0x3B, 0); - DO_LDS(); - new_PC = Exception(EXCEPTION_RI, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - - // - // DIV - Divide Word - // - BEGIN_OPF(DIV, 0, 0x1A); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - if(!GPR[rt]) - { - if(GPR[rs] & 0x80000000) - LO = 1; - else - LO = 0xFFFFFFFF; - - HI = GPR[rs]; - } - else if(GPR[rs] == 0x80000000 && GPR[rt] == 0xFFFFFFFF) - { - LO = 0x80000000; - HI = 0; - } - else - { - LO = (int32)GPR[rs] / (int32)GPR[rt]; - HI = (int32)GPR[rs] % (int32)GPR[rt]; - } - muldiv_ts_done = timestamp + 37; - - DO_LDS(); - - END_OPF; - - - // - // DIVU - Divide Unsigned Word - // - BEGIN_OPF(DIVU, 0, 0x1B); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - if(!GPR[rt]) - { - LO = 0xFFFFFFFF; - HI = GPR[rs]; - } - else - { - LO = GPR[rs] / GPR[rt]; - HI = GPR[rs] % GPR[rt]; - } - muldiv_ts_done = timestamp + 37; - - DO_LDS(); - END_OPF; - - // - // J - Jump - // - BEGIN_OPF(J, 0x02, 0); - JTYPE; - - DO_LDS(); - - DO_BRANCH(target << 2, 0xF0000000); - END_OPF; - - // - // JAL - Jump and Link - // - BEGIN_OPF(JAL, 0x03, 0); - JTYPE; - - //GPR_DEPRES_BEGIN - GPR_RES(31); - //GPR_DEPRES_END - - DO_LDS(); - - GPR[31] = PC + 8; - - DO_BRANCH(target << 2, 0xF0000000); - END_OPF; - - // - // JALR - Jump and Link Register - // - BEGIN_OPF(JALR, 0, 0x09); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 tmp = GPR[rs]; - - DO_LDS(); - - GPR[rd] = PC + 8; - - DO_BRANCH(tmp, 0); - - END_OPF; - - // - // JR - Jump Register - // - BEGIN_OPF(JR, 0, 0x08); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 bt = GPR[rs]; - - DO_LDS(); - - DO_BRANCH(bt, 0); - - END_OPF; - - // - // LUI - Load Upper Immediate - // - BEGIN_OPF(LUI, 0x0F, 0); - ITYPE_ZE; // Actually, probably would be sign-extending...if we were emulating a 64-bit MIPS chip :b - - GPR_DEPRES_BEGIN - GPR_RES(rt); - GPR_DEPRES_END - - DO_LDS(); - - GPR[rt] = immediate << 16; - - END_OPF; - - // - // MFHI - Move from HI - // - BEGIN_OPF(MFHI, 0, 0x10); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_RES(rd); - GPR_DEPRES_END - - DO_LDS(); - - if(timestamp < muldiv_ts_done) - { - if(timestamp == muldiv_ts_done - 1) - muldiv_ts_done--; - else - { - do - { - if(ReadAbsorb[ReadAbsorbWhich]) - ReadAbsorb[ReadAbsorbWhich]--; - timestamp++; - } while(timestamp < muldiv_ts_done); - } - } - - GPR[rd] = HI; - - END_OPF; - - - // - // MFLO - Move from LO - // - BEGIN_OPF(MFLO, 0, 0x12); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_RES(rd); - GPR_DEPRES_END - - DO_LDS(); - - if(timestamp < muldiv_ts_done) - { - if(timestamp == muldiv_ts_done - 1) - muldiv_ts_done--; - else - { - do - { - if(ReadAbsorb[ReadAbsorbWhich]) - ReadAbsorb[ReadAbsorbWhich]--; - timestamp++; - } while(timestamp < muldiv_ts_done); - } - } - - GPR[rd] = LO; - - END_OPF; - - - // - // MTHI - Move to HI - // - BEGIN_OPF(MTHI, 0, 0x11); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - HI = GPR[rs]; - - DO_LDS(); - - END_OPF; - - // - // MTLO - Move to LO - // - BEGIN_OPF(MTLO, 0, 0x13); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - LO = GPR[rs]; - - DO_LDS(); - - END_OPF; - - - // - // MULT - Multiply Word - // - BEGIN_OPF(MULT, 0, 0x18); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - uint64 result; - - result = (int64)(int32)GPR[rs] * (int32)GPR[rt]; - muldiv_ts_done = timestamp + MULT_Tab24[MDFN_lzcount32((GPR[rs] ^ ((int32)GPR[rs] >> 31)) | 0x400)]; - DO_LDS(); - - LO = result; - HI = result >> 32; - - END_OPF; - - // - // MULTU - Multiply Unsigned Word - // - BEGIN_OPF(MULTU, 0, 0x19); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - uint64 result; - - result = (uint64)GPR[rs] * GPR[rt]; - muldiv_ts_done = timestamp + MULT_Tab24[MDFN_lzcount32(GPR[rs] | 0x400)]; - DO_LDS(); - - LO = result; - HI = result >> 32; - - END_OPF; - - - // - // NOR - NOR - // - BEGIN_OPF(NOR, 0, 0x27); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = ~(GPR[rs] | GPR[rt]); - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - // - // OR - OR - // - BEGIN_OPF(OR, 0, 0x25); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rs] | GPR[rt]; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // ORI - OR Immediate - // - BEGIN_OPF(ORI, 0x0D, 0); - ITYPE_ZE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rt); - GPR_DEPRES_END - - uint32 result = GPR[rs] | immediate; - - DO_LDS(); - - GPR[rt] = result; - - END_OPF; - - - // - // SLL - Shift Word Left Logical - // - BEGIN_OPF(SLL, 0, 0x00); // SLL - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rt] << shamt; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // SLLV - Shift Word Left Logical Variable - // - BEGIN_OPF(SLLV, 0, 0x04); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rt] << (GPR[rs] & 0x1F); - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - // - // SLT - Set on Less Than - // - BEGIN_OPF(SLT, 0, 0x2A); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = (bool)((int32)GPR[rs] < (int32)GPR[rt]); - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // SLTI - Set on Less Than Immediate - // - BEGIN_OPF(SLTI, 0x0A, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rt); - GPR_DEPRES_END - - uint32 result = (bool)((int32)GPR[rs] < (int32)immediate); - - DO_LDS(); - - GPR[rt] = result; - - END_OPF; - - - // - // SLTIU - Set on Less Than Immediate, Unsigned - // - BEGIN_OPF(SLTIU, 0x0B, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rt); - GPR_DEPRES_END - - uint32 result = (bool)(GPR[rs] < (uint32)immediate); - - DO_LDS(); - - GPR[rt] = result; - - END_OPF; - - - // - // SLTU - Set on Less Than, Unsigned - // - BEGIN_OPF(SLTU, 0, 0x2B); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = (bool)(GPR[rs] < GPR[rt]); - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // SRA - Shift Word Right Arithmetic - // - BEGIN_OPF(SRA, 0, 0x03); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = ((int32)GPR[rt]) >> shamt; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // SRAV - Shift Word Right Arithmetic Variable - // - BEGIN_OPF(SRAV, 0, 0x07); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = ((int32)GPR[rt]) >> (GPR[rs] & 0x1F); - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // SRL - Shift Word Right Logical - // - BEGIN_OPF(SRL, 0, 0x02); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rt] >> shamt; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - // - // SRLV - Shift Word Right Logical Variable - // - BEGIN_OPF(SRLV, 0, 0x06); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rt] >> (GPR[rs] & 0x1F); - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // SUB - Subtract Word - // - BEGIN_OPF(SUB, 0, 0x22); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rs] - GPR[rt]; - bool ep = (((GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000; - - DO_LDS(); - - if(MDFN_UNLIKELY(ep)) - { - new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask); - new_PC_mask = 0; - } - else - GPR[rd] = result; - - END_OPF; - - - // - // SUBU - Subtract Unsigned Word - // - BEGIN_OPF(SUBU, 0, 0x23); // SUBU - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rs] - GPR[rt]; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - - // - // SYSCALL - // - BEGIN_OPF(SYSCALL, 0, 0x0C); - DO_LDS(); - - new_PC = Exception(EXCEPTION_SYSCALL, PC, new_PC_mask); - new_PC_mask = 0; - END_OPF; - - - // - // XOR - // - BEGIN_OPF(XOR, 0, 0x26); - RTYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_RES(rd); - GPR_DEPRES_END - - uint32 result = GPR[rs] ^ GPR[rt]; - - DO_LDS(); - - GPR[rd] = result; - - END_OPF; - - // - // XORI - Exclusive OR Immediate - // - BEGIN_OPF(XORI, 0x0E, 0); - ITYPE_ZE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_RES(rt); - GPR_DEPRES_END - - uint32 result = GPR[rs] ^ immediate; - - DO_LDS(); - - GPR[rt] = result; - END_OPF; - - // - // Memory access instructions(besides the coprocessor ones) follow: - // - - // - // LB - Load Byte - // - BEGIN_OPF(LB, 0x20, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - DO_LDS(); - - LDWhich = rt; - LDValue = (int32)ReadMemory(timestamp, address); - END_OPF; - - // - // LBU - Load Byte Unsigned - // - BEGIN_OPF(LBU, 0x24, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - DO_LDS(); - - LDWhich = rt; - LDValue = ReadMemory(timestamp, address); - END_OPF; - - // - // LH - Load Halfword - // - BEGIN_OPF(LH, 0x21, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - DO_LDS(); - - if(MDFN_UNLIKELY(address & 1)) - { - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); - new_PC_mask = 0; - } - else - { - LDWhich = rt; - LDValue = (int32)ReadMemory(timestamp, address); - } - END_OPF; - - // - // LHU - Load Halfword Unsigned - // - BEGIN_OPF(LHU, 0x25, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - DO_LDS(); - - if(MDFN_UNLIKELY(address & 1)) - { - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); - new_PC_mask = 0; - } - else - { - LDWhich = rt; - LDValue = ReadMemory(timestamp, address); - } - END_OPF; - - - // - // LW - Load Word - // - BEGIN_OPF(LW, 0x23, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - DO_LDS(); - - if(MDFN_UNLIKELY(address & 3)) - { - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); - new_PC_mask = 0; - } - else - { - LDWhich = rt; - LDValue = ReadMemory(timestamp, address); - } - END_OPF; - - // - // SB - Store Byte - // - BEGIN_OPF(SB, 0x28, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - WriteMemory(timestamp, address, GPR[rt]); - - DO_LDS(); - END_OPF; - - // - // SH - Store Halfword - // - BEGIN_OPF(SH, 0x29, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - if(MDFN_UNLIKELY(address & 0x1)) - { - new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask); - new_PC_mask = 0; - } - else - WriteMemory(timestamp, address, GPR[rt]); - - DO_LDS(); - END_OPF; - - // - // SW - Store Word - // - BEGIN_OPF(SW, 0x2B, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - if(MDFN_UNLIKELY(address & 0x3)) - { - new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask); - new_PC_mask = 0; - } - else - WriteMemory(timestamp, address, GPR[rt]); - - DO_LDS(); - END_OPF; - - // LWL and LWR load delay slot tomfoolery appears to apply even to MFC0! (and probably MFCn and CFCn as well, though they weren't explicitly tested) - - // - // LWL - Load Word Left - // - BEGIN_OPF(LWL, 0x22, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - //GPR_DEP(rt); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - uint32 v = GPR[rt]; - - if(LDWhich == rt) - { - v = LDValue; - ReadFudge = 0; - } - else - { - DO_LDS(); - } - - LDWhich = rt; - switch(address & 0x3) - { - case 0: LDValue = (v & ~(0xFF << 24)) | (ReadMemory(timestamp, address & ~3) << 24); - break; - - case 1: LDValue = (v & ~(0xFFFF << 16)) | (ReadMemory(timestamp, address & ~3) << 16); - break; - - case 2: LDValue = (v & ~(0xFFFFFF << 8)) | (ReadMemory(timestamp, address & ~3, true) << 8); - break; - - case 3: LDValue = (v & ~(0xFFFFFFFF << 0)) | (ReadMemory(timestamp, address & ~3) << 0); - break; - } - END_OPF; - - // - // SWL - Store Word Left - // - BEGIN_OPF(SWL, 0x2A, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - switch(address & 0x3) - { - case 0: WriteMemory(timestamp, address & ~3, GPR[rt] >> 24); - break; - - case 1: WriteMemory(timestamp, address & ~3, GPR[rt] >> 16); - break; - - case 2: WriteMemory(timestamp, address & ~3, GPR[rt] >> 8, true); - break; - - case 3: WriteMemory(timestamp, address & ~3, GPR[rt] >> 0); - break; - } - DO_LDS(); - - END_OPF; - - // - // LWR - Load Word Right - // - BEGIN_OPF(LWR, 0x26, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - //GPR_DEP(rt); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - uint32 v = GPR[rt]; - - if(LDWhich == rt) - { - v = LDValue; - ReadFudge = 0; - } - else - { - DO_LDS(); - } - - LDWhich = rt; - switch(address & 0x3) - { - case 0: LDValue = (v & ~(0xFFFFFFFF)) | ReadMemory(timestamp, address); - break; - - case 1: LDValue = (v & ~(0xFFFFFF)) | ReadMemory(timestamp, address, true); - break; - - case 2: LDValue = (v & ~(0xFFFF)) | ReadMemory(timestamp, address); - break; - - case 3: LDValue = (v & ~(0xFF)) | ReadMemory(timestamp, address); - break; - } - END_OPF; - - // - // SWR - Store Word Right - // - BEGIN_OPF(SWR, 0x2E, 0); - ITYPE; - - GPR_DEPRES_BEGIN - GPR_DEP(rs); - GPR_DEP(rt); - GPR_DEPRES_END - - uint32 address = GPR[rs] + immediate; - - switch(address & 0x3) - { - case 0: WriteMemory(timestamp, address, GPR[rt]); - break; - - case 1: WriteMemory(timestamp, address, GPR[rt], true); - break; - - case 2: WriteMemory(timestamp, address, GPR[rt]); - break; - - case 3: WriteMemory(timestamp, address, GPR[rt]); - break; - } - - DO_LDS(); - - END_OPF; - - // - // Mednafen special instruction - // - BEGIN_OPF(INTERRUPT, 0x3F, 0); - if(Halted) - { - goto SkipNPCStuff; - } - else - { - DO_LDS(); - - new_PC = Exception(EXCEPTION_INT, PC, new_PC_mask); - new_PC_mask = 0; - } - END_OPF; - } - - OpDone: ; - - PC = (PC & new_PC_mask) + new_PC; - new_PC_mask = ~0U; - new_PC = 4; - - SkipNPCStuff: ; - - //printf("\n"); - } - } while(MDFN_LIKELY(PSX_EventHandler(timestamp))); - - if(gte_ts_done > 0) - gte_ts_done -= timestamp; - - if(muldiv_ts_done > 0) - muldiv_ts_done -= timestamp; - + + CGBEGIN + CGE(op_SLL) CGE(op_ILL) CGE(op_SRL) CGE(op_SRA) CGE(op_SLLV) CGE(op_ILL) CGE(op_SRLV) CGE(op_SRAV) + CGE(op_JR) CGE(op_JALR) CGE(op_ILL) CGE(op_ILL) CGE(op_SYSCALL) CGE(op_BREAK) CGE(op_ILL) CGE(op_ILL) + CGE(op_MFHI) CGE(op_MTHI) CGE(op_MFLO) CGE(op_MTLO) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_MULT) CGE(op_MULTU) CGE(op_DIV) CGE(op_DIVU) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ADD) CGE(op_ADDU) CGE(op_SUB) CGE(op_SUBU) CGE(op_AND) CGE(op_OR) CGE(op_XOR) CGE(op_NOR) + CGE(op_ILL) CGE(op_ILL) CGE(op_SLT) CGE(op_SLTU) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + + CGE(op_ILL) CGE(op_BCOND) CGE(op_J) CGE(op_JAL) CGE(op_BEQ) CGE(op_BNE) CGE(op_BLEZ) CGE(op_BGTZ) + CGE(op_ADDI) CGE(op_ADDIU) CGE(op_SLTI) CGE(op_SLTIU) CGE(op_ANDI) CGE(op_ORI) CGE(op_XORI) CGE(op_LUI) + CGE(op_COP0) CGE(op_COP13) CGE(op_COP2) CGE(op_COP13) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_LB) CGE(op_LH) CGE(op_LWL) CGE(op_LW) CGE(op_LBU) CGE(op_LHU) CGE(op_LWR) CGE(op_ILL) + CGE(op_SB) CGE(op_SH) CGE(op_SWL) CGE(op_SW) CGE(op_ILL) CGE(op_ILL) CGE(op_SWR) CGE(op_ILL) + CGE(op_LWC013) CGE(op_LWC013) CGE(op_LWC2) CGE(op_LWC013) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_SWC013) CGE(op_SWC013) CGE(op_SWC2) CGE(op_SWC013) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + + // Interrupt portion of this table is constructed so that an interrupt won't be taken when the PC is pointing to a GTE instruction, + // to avoid problems caused by pipeline vs coprocessor nuances that aren't emulated. + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + + CGE(op_ILL) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_COP2) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGEND + + { + BEGIN_OPF(ILL); + PSX_WARNING("[CPU] Unknown instruction @%08x = %08x, op=%02x, funct=%02x", PC, instr, instr >> 26, (instr & 0x3F)); + DO_LDS(); + new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + END_OPF; + + // + // ADD - Add Word + // + BEGIN_OPF(ADD); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] + GPR[rt]; + bool ep = ((~(GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000; + + DO_LDS(); + + if(MDFN_UNLIKELY(ep)) + { + new_PC = Exception(EXCEPTION_OV, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + GPR[rd] = result; + + END_OPF; + + // + // ADDI - Add Immediate Word + // + BEGIN_OPF(ADDI); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] + immediate; + bool ep = ((~(GPR[rs] ^ immediate)) & (GPR[rs] ^ result)) & 0x80000000; + + DO_LDS(); + + if(MDFN_UNLIKELY(ep)) + { + new_PC = Exception(EXCEPTION_OV, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + GPR[rt] = result; + + END_OPF; + + // + // ADDIU - Add Immediate Unsigned Word + // + BEGIN_OPF(ADDIU); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] + immediate; + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + // + // ADDU - Add Unsigned Word + // + BEGIN_OPF(ADDU); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] + GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // AND - And + // + BEGIN_OPF(AND); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] & GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // ANDI - And Immediate + // + BEGIN_OPF(ANDI); + ITYPE_ZE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] & immediate; + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + // + // BEQ - Branch on Equal + // + BEGIN_OPF(BEQ); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + const bool result = (GPR[rs] == GPR[rt]); + + DO_LDS(); + + DO_BRANCH(result, (immediate << 2), ~0U, false, 0); + END_OPF; + + // Bah, why does MIPS encoding have to be funky like this. :( + // Handles BGEZ, BGEZAL, BLTZ, BLTZAL + BEGIN_OPF(BCOND); + const uint32 tv = GPR[(instr >> 21) & 0x1F]; + const uint32 riv = (instr >> 16) & 0x1F; + const uint32 immediate = (int32)(int16)(instr & 0xFFFF); + const bool result = (int32)(tv ^ (riv << 31)) < 0; + const uint32 link = ((riv & 0x1E) == 0x10) ? 31 : 0; + + GPR_DEPRES_BEGIN + GPR_DEP((instr >> 21) & 0x1F); + GPR_RES(link); + GPR_DEPRES_END + + DO_LDS(); + + DO_BRANCH(result, (immediate << 2), ~0U, true, link); + END_OPF; + + + // + // BGTZ - Branch on Greater than Zero + // + BEGIN_OPF(BGTZ); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + const bool result = (int32)GPR[rs] > 0; + + DO_LDS(); + + DO_BRANCH(result, (immediate << 2), ~0U, false, 0); + END_OPF; + + // + // BLEZ - Branch on Less Than or Equal to Zero + // + BEGIN_OPF(BLEZ); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + const bool result = (int32)GPR[rs] <= 0; + + DO_LDS(); + + DO_BRANCH(result, (immediate << 2), ~0U, false, 0); + END_OPF; + + // + // BNE - Branch on Not Equal + // + BEGIN_OPF(BNE); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + const bool result = GPR[rs] != GPR[rt]; + + DO_LDS(); + + DO_BRANCH(result, (immediate << 2), ~0U, false, 0); + END_OPF; + + // + // BREAK - Breakpoint + // + BEGIN_OPF(BREAK); + DO_LDS(); + new_PC = Exception(EXCEPTION_BP, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + END_OPF; + + // Cop "instructions": CFCz(no CP0), COPz, CTCz(no CP0), LWCz(no CP0), MFCz, MTCz, SWCz(no CP0) + // + // COP0 instructions + // + BEGIN_OPF(COP0); + const uint32 sub_op = (instr >> 21) & 0x1F; + const uint32 rt = (instr >> 16) & 0x1F; + const uint32 rd = (instr >> 11) & 0x1F; + const uint32 val = GPR[rt]; + + switch(sub_op) + { + default: + DO_LDS(); + break; + + case 0x02: + case 0x06: + DO_LDS(); + new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + break; + + case 0x00: // MFC0 - Move from Coprocessor + switch(rd) + { + case 0x00: + case 0x01: + case 0x02: + case 0x04: + case 0x0A: + DO_LDS(); + new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + break; + + case 0x03: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + LDAbsorb = 0; + LDWhich = rt; + LDValue = CP0.Regs[rd]; + break; + + default: + // Tested to be rather NOPish + DO_LDS(); + PSX_DBG(PSX_DBG_WARNING, "[CPU] MFC0 from unmapped CP0 register %u.\n", rd); + break; + } + break; + + case 0x04: // MTC0 - Move to Coprocessor + DO_LDS(); + switch(rd) + { + case 0x00: + case 0x01: + case 0x02: + case 0x04: + case 0x0A: + new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + break; + + case CP0REG_BPC: + CP0.BPC = val; + break; + + case CP0REG_BDA: + CP0.BDA = val; + break; + + case CP0REG_DCIC: + if(val) + { + PSX_DBG(PSX_DBG_WARNING, "[CPU] Non-zero write to DCIC: 0x%08x\n", val); + } + CP0.DCIC = val & 0xFF80003F; + break; + + case CP0REG_BDAM: + CP0.BDAM = val; + break; + + case CP0REG_BPCM: + CP0.BPCM = val; + break; + + case CP0REG_CAUSE: + CP0.CAUSE &= ~(0x3 << 8); + CP0.CAUSE |= val & (0x3 << 8); + RecalcIPCache(); + break; + + case CP0REG_SR: + if((CP0.SR ^ val) & 0x10000) + PSX_DBG(PSX_DBG_SPARSE, "[CPU] IsC %u->%u\n", (bool)(CP0.SR & (1U << 16)), (bool)(val & (1U << 16))); + + CP0.SR = val & ~( (0x3 << 26) | (0x3 << 23) | (0x3 << 6)); + RecalcIPCache(); + break; + } + break; + + case 0x08: // BC + case 0x0C: + DO_LDS(); + { + const uint32 immediate = (int32)(int16)(instr & 0xFFFF); + const bool result = (false == (bool)(instr & (1U << 16))); + + PSX_DBG(PSX_DBG_WARNING, "[CPU] BC0x instruction(0x%08x) @ PC=0x%08x\n", instr, PC); + + DO_BRANCH(result, (immediate << 2), ~0U, false, 0); + } + break; + + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: + DO_LDS(); + { + const uint32 cp0_op = instr & 0x1F; // Not 0x3F + + if(MDFN_LIKELY(cp0_op == 0x10)) // RFE + { + // "Pop" + CP0.SR = (CP0.SR & ~0x0F) | ((CP0.SR >> 2) & 0x0F); + RecalcIPCache(); + } + else if(cp0_op == 0x01 || cp0_op == 0x02 || cp0_op == 0x06 || cp0_op == 0x08) // TLBR, TLBWI, TLBWR, TLBP + { + new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + } + break; + } + END_OPF; + + // + // COP2 + // + BEGIN_OPF(COP2); + const uint32 sub_op = (instr >> 21) & 0x1F; + const uint32 rt = (instr >> 16) & 0x1F; + const uint32 rd = (instr >> 11) & 0x1F; + const uint32 val = GPR[rt]; + + if(MDFN_UNLIKELY(!(CP0.SR & (1U << (28 + 2))))) + { + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else switch(sub_op) + { + default: + DO_LDS(); + break; + + case 0x00: // MFC2 - Move from Coprocessor + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + if(timestamp < gte_ts_done) + { + LDAbsorb = gte_ts_done - timestamp; + timestamp = gte_ts_done; + } + else + LDAbsorb = 0; + + LDWhich = rt; + LDValue = GTE_ReadDR(rd); + break; + + case 0x04: // MTC2 - Move to Coprocessor + DO_LDS(); + + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + GTE_WriteDR(rd, val); + break; + + case 0x02: // CFC2 + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + if(timestamp < gte_ts_done) + { + LDAbsorb = gte_ts_done - timestamp; + timestamp = gte_ts_done; + } + else + LDAbsorb = 0; + + LDWhich = rt; + LDValue = GTE_ReadCR(rd); + break; + + case 0x06: // CTC2 + DO_LDS(); + + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + GTE_WriteCR(rd, val); + break; + + case 0x08: + case 0x0C: + DO_LDS(); + { + const uint32 immediate = (int32)(int16)(instr & 0xFFFF); + const bool result = (false == (bool)(instr & (1U << 16))); + + PSX_DBG(PSX_DBG_WARNING, "[CPU] BC2x instruction(0x%08x) @ PC=0x%08x\n", instr, PC); + + DO_BRANCH(result, (immediate << 2), ~0U, false, 0); + } + break; + + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: + DO_LDS(); + + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + gte_ts_done = timestamp + GTE_Instruction(instr); + break; + } + END_OPF; + + // + // COP1, COP3 + // + BEGIN_OPF(COP13); + DO_LDS(); + + if(!(CP0.SR & (1U << (28 + ((instr >> 26) & 0x3))))) + { + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + const uint32 sub_op = (instr >> 21) & 0x1F; + + PSX_DBG(PSX_DBG_WARNING, "[CPU] COP%u instruction(0x%08x) @ PC=0x%08x\n", (instr >> 26) & 0x3, instr, PC); + + if(sub_op == 0x08 || sub_op == 0x0C) + { + const uint32 immediate = (int32)(int16)(instr & 0xFFFF); + const bool result = (false == (bool)(instr & (1U << 16))); + + DO_BRANCH(result, (immediate << 2), ~0U, false, 0); + } + } + END_OPF; + + // + // LWC0, LWC1, LWC3 + // + BEGIN_OPF(LWC013); + ITYPE; + const uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + if(!(CP0.SR & (1U << (28 + ((instr >> 26) & 0x3))))) + { + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + if(MDFN_UNLIKELY(address & 3)) + { + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + PSX_DBG(PSX_DBG_WARNING, "[CPU] LWC%u instruction(0x%08x) @ PC=0x%08x\n", (instr >> 26) & 0x3, instr, PC); + + ReadMemory(timestamp, address, false, true); + } + } + END_OPF; + + // + // LWC2 + // + BEGIN_OPF(LWC2); + ITYPE; + const uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + if(MDFN_UNLIKELY(address & 3)) + { + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + GTE_WriteDR(rt, ReadMemory(timestamp, address, false, true)); + } + // GTE stuff here + END_OPF; + + // + // SWC0, SWC1, SCW3 + // + BEGIN_OPF(SWC013); + ITYPE; + const uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + if(!(CP0.SR & (1U << (28 + ((instr >> 26) & 0x3))))) + { + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + if(MDFN_UNLIKELY(address & 0x3)) + { + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + PSX_DBG(PSX_DBG_WARNING, "[CPU] SWC%u instruction(0x%08x) @ PC=0x%08x\n", (instr >> 26) & 0x3, instr, PC); + //WriteMemory(timestamp, address, SOMETHING); + } + } + END_OPF; + + // + // SWC2 + // + BEGIN_OPF(SWC2); + ITYPE; + const uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 0x3)) + { + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + WriteMemory(timestamp, address, GTE_ReadDR(rt)); + } + DO_LDS(); + END_OPF; + + // + // DIV - Divide Word + // + BEGIN_OPF(DIV); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + if(!GPR[rt]) + { + if(GPR[rs] & 0x80000000) + LO = 1; + else + LO = 0xFFFFFFFF; + + HI = GPR[rs]; + } + else if(GPR[rs] == 0x80000000 && GPR[rt] == 0xFFFFFFFF) + { + LO = 0x80000000; + HI = 0; + } + else + { + LO = (int32)GPR[rs] / (int32)GPR[rt]; + HI = (int32)GPR[rs] % (int32)GPR[rt]; + } + muldiv_ts_done = timestamp + 37; + + DO_LDS(); + + END_OPF; + + + // + // DIVU - Divide Unsigned Word + // + BEGIN_OPF(DIVU); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + if(!GPR[rt]) + { + LO = 0xFFFFFFFF; + HI = GPR[rs]; + } + else + { + LO = GPR[rs] / GPR[rt]; + HI = GPR[rs] % GPR[rt]; + } + muldiv_ts_done = timestamp + 37; + + DO_LDS(); + END_OPF; + + // + // J - Jump + // + BEGIN_OPF(J); + JTYPE; + + DO_LDS(); + + DO_BRANCH(true, target << 2, 0xF0000000, false, 0); + END_OPF; + + // + // JAL - Jump and Link + // + BEGIN_OPF(JAL); + JTYPE; + + //GPR_DEPRES_BEGIN + GPR_RES(31); + //GPR_DEPRES_END + + DO_LDS(); + + DO_BRANCH(true, target << 2, 0xF0000000, true, 31); + END_OPF; + + // + // JALR - Jump and Link Register + // + BEGIN_OPF(JALR); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 tmp = GPR[rs]; + + DO_LDS(); + + DO_BRANCH(true, tmp, 0, true, rd); + END_OPF; + + // + // JR - Jump Register + // + BEGIN_OPF(JR); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 bt = GPR[rs]; + + DO_LDS(); + + DO_BRANCH(true, bt, 0, false, 0); + END_OPF; + + // + // LUI - Load Upper Immediate + // + BEGIN_OPF(LUI); + ITYPE_ZE; // Actually, probably would be sign-extending...if we were emulating a 64-bit MIPS chip :b + + GPR_DEPRES_BEGIN + GPR_RES(rt); + GPR_DEPRES_END + + DO_LDS(); + + GPR[rt] = immediate << 16; + + END_OPF; + + // + // MFHI - Move from HI + // + BEGIN_OPF(MFHI); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_RES(rd); + GPR_DEPRES_END + + DO_LDS(); + + if(timestamp < muldiv_ts_done) + { + if(timestamp == muldiv_ts_done - 1) + muldiv_ts_done--; + else + { + do + { + if(ReadAbsorb[ReadAbsorbWhich]) + ReadAbsorb[ReadAbsorbWhich]--; + timestamp++; + } while(timestamp < muldiv_ts_done); + } + } + + GPR[rd] = HI; + + END_OPF; + + + // + // MFLO - Move from LO + // + BEGIN_OPF(MFLO); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_RES(rd); + GPR_DEPRES_END + + DO_LDS(); + + if(timestamp < muldiv_ts_done) + { + if(timestamp == muldiv_ts_done - 1) + muldiv_ts_done--; + else + { + do + { + if(ReadAbsorb[ReadAbsorbWhich]) + ReadAbsorb[ReadAbsorbWhich]--; + timestamp++; + } while(timestamp < muldiv_ts_done); + } + } + + GPR[rd] = LO; + + END_OPF; + + + // + // MTHI - Move to HI + // + BEGIN_OPF(MTHI); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + HI = GPR[rs]; + + DO_LDS(); + + END_OPF; + + // + // MTLO - Move to LO + // + BEGIN_OPF(MTLO); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + LO = GPR[rs]; + + DO_LDS(); + + END_OPF; + + + // + // MULT - Multiply Word + // + BEGIN_OPF(MULT); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint64 result; + + result = (int64)(int32)GPR[rs] * (int32)GPR[rt]; + muldiv_ts_done = timestamp + MULT_Tab24[MDFN_lzcount32((GPR[rs] ^ ((int32)GPR[rs] >> 31)) | 0x400)]; + DO_LDS(); + + LO = result; + HI = result >> 32; + + END_OPF; + + // + // MULTU - Multiply Unsigned Word + // + BEGIN_OPF(MULTU); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint64 result; + + result = (uint64)GPR[rs] * GPR[rt]; + muldiv_ts_done = timestamp + MULT_Tab24[MDFN_lzcount32(GPR[rs] | 0x400)]; + DO_LDS(); + + LO = result; + HI = result >> 32; + + END_OPF; + + + // + // NOR - NOR + // + BEGIN_OPF(NOR); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = ~(GPR[rs] | GPR[rt]); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // OR - OR + // + BEGIN_OPF(OR); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] | GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // ORI - OR Immediate + // + BEGIN_OPF(ORI); + ITYPE_ZE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] | immediate; + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + + // + // SLL - Shift Word Left Logical + // + BEGIN_OPF(SLL); // SLL + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] << shamt; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SLLV - Shift Word Left Logical Variable + // + BEGIN_OPF(SLLV); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] << (GPR[rs] & 0x1F); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // SLT - Set on Less Than + // + BEGIN_OPF(SLT); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = (bool)((int32)GPR[rs] < (int32)GPR[rt]); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SLTI - Set on Less Than Immediate + // + BEGIN_OPF(SLTI); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = (bool)((int32)GPR[rs] < (int32)immediate); + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + + // + // SLTIU - Set on Less Than Immediate, Unsigned + // + BEGIN_OPF(SLTIU); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = (bool)(GPR[rs] < (uint32)immediate); + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + + // + // SLTU - Set on Less Than, Unsigned + // + BEGIN_OPF(SLTU); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = (bool)(GPR[rs] < GPR[rt]); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SRA - Shift Word Right Arithmetic + // + BEGIN_OPF(SRA); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = ((int32)GPR[rt]) >> shamt; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SRAV - Shift Word Right Arithmetic Variable + // + BEGIN_OPF(SRAV); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = ((int32)GPR[rt]) >> (GPR[rs] & 0x1F); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SRL - Shift Word Right Logical + // + BEGIN_OPF(SRL); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] >> shamt; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // SRLV - Shift Word Right Logical Variable + // + BEGIN_OPF(SRLV); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] >> (GPR[rs] & 0x1F); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SUB - Subtract Word + // + BEGIN_OPF(SUB); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] - GPR[rt]; + bool ep = (((GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000; + + DO_LDS(); + + if(MDFN_UNLIKELY(ep)) + { + new_PC = Exception(EXCEPTION_OV, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + GPR[rd] = result; + + END_OPF; + + + // + // SUBU - Subtract Unsigned Word + // + BEGIN_OPF(SUBU); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] - GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SYSCALL + // + BEGIN_OPF(SYSCALL); + DO_LDS(); + + new_PC = Exception(EXCEPTION_SYSCALL, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + END_OPF; + + + // + // XOR + // + BEGIN_OPF(XOR); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] ^ GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // XORI - Exclusive OR Immediate + // + BEGIN_OPF(XORI); + ITYPE_ZE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] ^ immediate; + + DO_LDS(); + + GPR[rt] = result; + END_OPF; + + // + // Memory access instructions(besides the coprocessor ones) follow: + // + + // + // LB - Load Byte + // + BEGIN_OPF(LB); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + LDWhich = rt; + LDValue = (int32)ReadMemory(timestamp, address); + END_OPF; + + // + // LBU - Load Byte Unsigned + // + BEGIN_OPF(LBU); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + LDWhich = rt; + LDValue = ReadMemory(timestamp, address); + END_OPF; + + // + // LH - Load Halfword + // + BEGIN_OPF(LH); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 1)) + { + DO_LDS(); + + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + LDWhich = rt; + LDValue = (int32)ReadMemory(timestamp, address); + } + END_OPF; + + // + // LHU - Load Halfword Unsigned + // + BEGIN_OPF(LHU); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 1)) + { + DO_LDS(); + + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + LDWhich = rt; + LDValue = ReadMemory(timestamp, address); + } + END_OPF; + + + // + // LW - Load Word + // + BEGIN_OPF(LW); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 3)) + { + DO_LDS(); + + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + { + if(MDFN_UNLIKELY(LDWhich == rt)) + LDWhich = 0; + + DO_LDS(); + + LDWhich = rt; + LDValue = ReadMemory(timestamp, address); + } + END_OPF; + + // + // SB - Store Byte + // + BEGIN_OPF(SB); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + WriteMemory(timestamp, address, GPR[rt]); + + DO_LDS(); + END_OPF; + + // + // SH - Store Halfword + // + BEGIN_OPF(SH); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 0x1)) + { + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + WriteMemory(timestamp, address, GPR[rt]); + + DO_LDS(); + END_OPF; + + // + // SW - Store Word + // + BEGIN_OPF(SW); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 0x3)) + { + CP0.BADVA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + else + WriteMemory(timestamp, address, GPR[rt]); + + DO_LDS(); + END_OPF; + + // LWL and LWR load delay slot tomfoolery appears to apply even to MFC0! (and probably MFCn and CFCn as well, though they weren't explicitly tested) + + // + // LWL - Load Word Left + // + BEGIN_OPF(LWL); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + //GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + uint32 v = GPR[rt]; + + if(LDWhich == rt) + { + v = LDValue; + ReadFudge = 0; + } + else + { + DO_LDS(); + } + + LDWhich = rt; + switch(address & 0x3) + { + case 0: LDValue = (v & ~(0xFF << 24)) | (ReadMemory(timestamp, address & ~3) << 24); + break; + + case 1: LDValue = (v & ~(0xFFFF << 16)) | (ReadMemory(timestamp, address & ~3) << 16); + break; + + case 2: LDValue = (v & ~(0xFFFFFF << 8)) | (ReadMemory(timestamp, address & ~3, true) << 8); + break; + + case 3: LDValue = (v & ~(0xFFFFFFFF << 0)) | (ReadMemory(timestamp, address & ~3) << 0); + break; + } + END_OPF; + + // + // SWL - Store Word Left + // + BEGIN_OPF(SWL); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + switch(address & 0x3) + { + case 0: WriteMemory(timestamp, address & ~3, GPR[rt] >> 24); + break; + + case 1: WriteMemory(timestamp, address & ~3, GPR[rt] >> 16); + break; + + case 2: WriteMemory(timestamp, address & ~3, GPR[rt] >> 8, true); + break; + + case 3: WriteMemory(timestamp, address & ~3, GPR[rt] >> 0); + break; + } + DO_LDS(); + + END_OPF; + + // + // LWR - Load Word Right + // + BEGIN_OPF(LWR); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + //GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + uint32 v = GPR[rt]; + + if(LDWhich == rt) + { + v = LDValue; + ReadFudge = 0; + } + else + { + DO_LDS(); + } + + LDWhich = rt; + switch(address & 0x3) + { + case 0: LDValue = (v & ~(0xFFFFFFFF)) | ReadMemory(timestamp, address); + break; + + case 1: LDValue = (v & ~(0xFFFFFF)) | ReadMemory(timestamp, address, true); + break; + + case 2: LDValue = (v & ~(0xFFFF)) | ReadMemory(timestamp, address); + break; + + case 3: LDValue = (v & ~(0xFF)) | ReadMemory(timestamp, address); + break; + } + END_OPF; + + // + // SWR - Store Word Right + // + BEGIN_OPF(SWR); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + switch(address & 0x3) + { + case 0: WriteMemory(timestamp, address, GPR[rt]); + break; + + case 1: WriteMemory(timestamp, address, GPR[rt], true); + break; + + case 2: WriteMemory(timestamp, address, GPR[rt]); + break; + + case 3: WriteMemory(timestamp, address, GPR[rt]); + break; + } + + DO_LDS(); + + END_OPF; + + // + // Mednafen special instruction + // + BEGIN_OPF(INTERRUPT); + if(Halted) + { + goto SkipNPCStuff; + } + else + { + DO_LDS(); + + new_PC = Exception(EXCEPTION_INT, PC, new_PC, new_PC_mask, instr); + new_PC_mask = 0; + } + END_OPF; + } + + OpDone: ; + + PC = (PC & new_PC_mask) + new_PC; + new_PC_mask = ~0U; + new_PC = 4; + + SkipNPCStuff: ; + + //printf("\n"); + } + } while(MDFN_LIKELY(PSX_EventHandler(timestamp))); + + if(gte_ts_done > 0) + gte_ts_done -= timestamp; + + if(muldiv_ts_done > 0) + muldiv_ts_done -= timestamp; + ACTIVE_TO_BACKING; - return(timestamp); } diff --git a/psx/octoshock/psx/cpu.h b/psx/octoshock/psx/cpu.h index e6bfe3b5a1..619f8c27f2 100644 --- a/psx/octoshock/psx/cpu.h +++ b/psx/octoshock/psx/cpu.h @@ -1,263 +1,262 @@ -#ifndef __MDFN_PSX_CPU_H -#define __MDFN_PSX_CPU_H - -/* - Load delay notes: - - // Takes 1 less - ".set noreorder\n\t" - ".set nomacro\n\t" - "lw %0, 0(%2)\n\t" - "nop\n\t" - "nop\n\t" - "or %0, %1, %1\n\t" - - // cycle than this: - ".set noreorder\n\t" - ".set nomacro\n\t" - "lw %0, 0(%2)\n\t" - "nop\n\t" - "or %0, %1, %1\n\t" - "nop\n\t" - - - // Both of these - ".set noreorder\n\t" - ".set nomacro\n\t" - "lw %0, 0(%2)\n\t" - "nop\n\t" - "nop\n\t" - "or %1, %0, %0\n\t" - - // take same...(which is kind of odd). - ".set noreorder\n\t" - ".set nomacro\n\t" - "lw %0, 0(%2)\n\t" - "nop\n\t" - "or %1, %0, %0\n\t" - "nop\n\t" -*/ - -#include "gte.h" - -namespace MDFN_IEN_PSX -{ - -#define PS_CPU_EMULATE_ICACHE 1 - -class PS_CPU -{ - public: - - PS_CPU() MDFN_COLD; - ~PS_CPU() MDFN_COLD; - - templatevoid SyncState(EW::NewState *ns); - - // FAST_MAP_* enums are in BYTES(8-bit), not in 32-bit units("words" in MIPS context), but the sizes - // will always be multiples of 4. - enum { FAST_MAP_SHIFT = 16 }; - enum { FAST_MAP_PSIZE = 1 << FAST_MAP_SHIFT }; - - void SetFastMap(void *region_mem, uint32 region_address, uint32 region_size); - - INLINE void SetEventNT(const pscpu_timestamp_t next_event_ts_arg) - { - next_event_ts = next_event_ts_arg; - } - - pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode); - - void Power(void) MDFN_COLD; - - // which ranges 0-5, inclusive - void AssertIRQ(unsigned which, bool asserted); - - void SetHalt(bool status); - - // TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed. - void SetBIU(uint32 val); - uint32 GetBIU(void); - - private: - - uint32 GPR[32 + 1]; // GPR[32] Used as dummy in load delay simulation(indexing past the end of real GPR) - - uint32 LO; - uint32 HI; - - - uint32 BACKED_PC; - uint32 BACKED_new_PC; - uint32 BACKED_new_PC_mask; - - uint32 IPCache; - void RecalcIPCache(void); - bool Halted; - - uint32 BACKED_LDWhich; - uint32 BACKED_LDValue; - uint32 LDAbsorb; - - pscpu_timestamp_t next_event_ts; - pscpu_timestamp_t gte_ts_done; - pscpu_timestamp_t muldiv_ts_done; - - uint32 BIU; - - struct __ICache - { - uint32 TV; - uint32 Data; - }; - - union - { - __ICache ICache[1024]; - uint32 ICache_Bulk[2048]; - }; - - enum - { - CP0REG_BPC = 3, // PC breakpoint address. - CP0REG_BDA = 5, // Data load/store breakpoint address. - CP0REG_TAR = 6, // Target address(???) - CP0REG_DCIC = 7, // Cache control - CP0REG_BDAM = 9, // Data load/store address mask. - CP0REG_BPCM = 11, // PC breakpoint address mask. - CP0REG_SR = 12, - CP0REG_CAUSE = 13, - CP0REG_EPC = 14, - CP0REG_PRID = 15, // Product ID - CP0REG_ERREG = 16 - }; - - struct - { - union - { - uint32 Regs[32]; - struct - { - uint32 Unused00; - uint32 Unused01; - uint32 Unused02; - uint32 BPC; // RW - uint32 Unused04; - uint32 BDA; // RW - uint32 TAR; - uint32 DCIC; // RW - uint32 Unused08; - uint32 BDAM; // R/W - uint32 Unused0A; - uint32 BPCM; // R/W - uint32 SR; // R/W - uint32 CAUSE; // R/W(partial) - uint32 EPC; // R - uint32 PRID; // R - uint32 ERREG; // ?(may not exist, test) - }; - }; - } CP0; - -#if 1 - //uint32 WrAbsorb; - //uint8 WrAbsorbShift; - - // On read: - //WrAbsorb = 0; - //WrAbsorbShift = 0; - - // On write: - //WrAbsorb >>= (WrAbsorbShift >> 2) & 8; - //WrAbsorbShift -= (WrAbsorbShift >> 2) & 8; - - //WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift; - //WrAbsorbShift += 8; -#endif - - uint8 ReadAbsorb[0x20 + 1]; - uint8 ReadAbsorbWhich; - uint8 ReadFudge; - - //uint32 WriteAbsorb; - //uint8 WriteAbsorbCount; - //uint8 WriteAbsorbMonkey; - uint8 MULT_Tab24[24]; - - MultiAccessSizeMem<1024, false> ScratchRAM; - - //PS_GTE GTE; - - uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)]; - uint8 DummyPage[FAST_MAP_PSIZE]; - - enum - { - EXCEPTION_INT = 0, - EXCEPTION_MOD = 1, - EXCEPTION_TLBL = 2, - EXCEPTION_TLBS = 3, - EXCEPTION_ADEL = 4, // Address error on load - EXCEPTION_ADES = 5, // Address error on store - EXCEPTION_IBE = 6, // Instruction bus error - EXCEPTION_DBE = 7, // Data bus error - EXCEPTION_SYSCALL = 8, // System call - EXCEPTION_BP = 9, // Breakpoint - EXCEPTION_RI = 10, // Reserved instruction - EXCEPTION_COPU = 11, // Coprocessor unusable - EXCEPTION_OV = 12 // Arithmetic overflow - }; - - uint32 Exception(uint32 code, uint32 PC, const uint32 NPM) MDFN_WARN_UNUSED_RESULT; - - template pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE; - - template T PeekMemory(uint32 address) MDFN_COLD; - template void PokeMemory(uint32 address, T value) MDFN_COLD; - template T ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24 = false, bool LWC_timing = false); - template void WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24 = false); - - - // - // Mednafen debugger stuff follows: - // - public: - void SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32 pc), void (*addbt)(uint32 from, uint32 to, bool exception)); - void CheckBreakpoints(void (*callback)(bool write, uint32 address, unsigned int len), uint32 instr); - void* debug_GetScratchRAMPtr() { return ScratchRAM.data8; } - void* debug_GetGPRPtr() { return GPR; } - - enum - { - GSREG_GPR = 0, - GSREG_PC = 32, - GSREG_PC_NEXT, - GSREG_IN_BD_SLOT, - GSREG_LO, - GSREG_HI, - GSREG_SR, - GSREG_CAUSE, - GSREG_EPC, - }; - - uint32 GetRegister(unsigned int which, char *special, const uint32 special_len); - void SetRegister(unsigned int which, uint32 value); - bool PeekCheckICache(uint32 PC, uint32 *iw); - - uint8 PeekMem8(uint32 A); - uint16 PeekMem16(uint32 A); - uint32 PeekMem32(uint32 A); - - void PokeMem8(uint32 A, uint8 V); - void PokeMem16(uint32 A, uint16 V); - void PokeMem32(uint32 A, uint32 V); - - private: - void (*CPUHook)(const pscpu_timestamp_t timestamp, uint32 pc); - void (*ADDBT)(uint32 from, uint32 to, bool exception); -}; - -} - -#endif +#ifndef __MDFN_PSX_CPU_H +#define __MDFN_PSX_CPU_H + +/* +Load delay notes: + +// Takes 1 less +".set noreorder\n\t" +".set nomacro\n\t" +"lw %0, 0(%2)\n\t" +"nop\n\t" +"nop\n\t" +"or %0, %1, %1\n\t" + +// cycle than this: +".set noreorder\n\t" +".set nomacro\n\t" +"lw %0, 0(%2)\n\t" +"nop\n\t" +"or %0, %1, %1\n\t" +"nop\n\t" + + +// Both of these +".set noreorder\n\t" +".set nomacro\n\t" +"lw %0, 0(%2)\n\t" +"nop\n\t" +"nop\n\t" +"or %1, %0, %0\n\t" + +// take same...(which is kind of odd). +".set noreorder\n\t" +".set nomacro\n\t" +"lw %0, 0(%2)\n\t" +"nop\n\t" +"or %1, %0, %0\n\t" +"nop\n\t" +*/ + +#include "gte.h" + +namespace MDFN_IEN_PSX +{ + +#define PS_CPU_EMULATE_ICACHE 1 + + class PS_CPU + { + public: + + PS_CPU() MDFN_COLD; + ~PS_CPU() MDFN_COLD; + + templatevoid SyncState(EW::NewState *ns); + + // FAST_MAP_* enums are in BYTES(8-bit), not in 32-bit units("words" in MIPS context), but the sizes + // will always be multiples of 4. + enum { FAST_MAP_SHIFT = 16 }; + enum { FAST_MAP_PSIZE = 1 << FAST_MAP_SHIFT }; + + void SetFastMap(void *region_mem, uint32 region_address, uint32 region_size); + + INLINE void SetEventNT(const pscpu_timestamp_t next_event_ts_arg) + { + next_event_ts = next_event_ts_arg; + } + + pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode); + + void Power(void) MDFN_COLD; + + // which ranges 0-5, inclusive + void AssertIRQ(unsigned which, bool asserted); + + void SetHalt(bool status); + + // TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed. + void SetBIU(uint32 val); + uint32 GetBIU(void); + + private: + + uint32 GPR[32 + 1]; // GPR[32] Used as dummy in load delay simulation(indexing past the end of real GPR) + + uint32 LO; + uint32 HI; + + + uint32 BACKED_PC; + uint32 BACKED_new_PC; + uint32 BACKED_new_PC_mask; + + uint32 IPCache; + void RecalcIPCache(void); + bool Halted; + + uint32 BACKED_LDWhich; + uint32 BACKED_LDValue; + uint32 LDAbsorb; + + pscpu_timestamp_t next_event_ts; + pscpu_timestamp_t gte_ts_done; + pscpu_timestamp_t muldiv_ts_done; + + uint32 BIU; + + struct __ICache + { + uint32 TV; + uint32 Data; + }; + + union + { + __ICache ICache[1024]; + uint32 ICache_Bulk[2048]; + }; + + enum + { + CP0REG_BPC = 3, // PC breakpoint address. + CP0REG_BDA = 5, // Data load/store breakpoint address. + CP0REG_TAR = 6, // Target address(???) + CP0REG_DCIC = 7, // Cache control + CP0REG_BADVA = 8, + CP0REG_BDAM = 9, // Data load/store address mask. + CP0REG_BPCM = 11, // PC breakpoint address mask. + CP0REG_SR = 12, + CP0REG_CAUSE = 13, + CP0REG_EPC = 14, + CP0REG_PRID = 15 // Product ID + }; + + struct + { + union + { + uint32 Regs[32]; + struct + { + uint32 Unused00; + uint32 Unused01; + uint32 Unused02; + uint32 BPC; // RW + uint32 Unused04; + uint32 BDA; // RW + uint32 TAR; // R + uint32 DCIC; // RW + uint32 BADVA; // R + uint32 BDAM; // R/W + uint32 Unused0A; + uint32 BPCM; // R/W + uint32 SR; // R/W + uint32 CAUSE; // R/W(partial) + uint32 EPC; // R + uint32 PRID; // R + }; + }; + } CP0; + +#if 1 + //uint32 WrAbsorb; + //uint8 WrAbsorbShift; + + // On read: + //WrAbsorb = 0; + //WrAbsorbShift = 0; + + // On write: + //WrAbsorb >>= (WrAbsorbShift >> 2) & 8; + //WrAbsorbShift -= (WrAbsorbShift >> 2) & 8; + + //WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift; + //WrAbsorbShift += 8; +#endif + + uint8 ReadAbsorb[0x20 + 1]; + uint8 ReadAbsorbWhich; + uint8 ReadFudge; + + //uint32 WriteAbsorb; + //uint8 WriteAbsorbCount; + //uint8 WriteAbsorbMonkey; + uint8 MULT_Tab24[24]; + + MultiAccessSizeMem<1024, false> ScratchRAM; + + //PS_GTE GTE; + + uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)]; + uint8 DummyPage[FAST_MAP_PSIZE]; + + enum + { + EXCEPTION_INT = 0, + EXCEPTION_MOD = 1, + EXCEPTION_TLBL = 2, + EXCEPTION_TLBS = 3, + EXCEPTION_ADEL = 4, // Address error on load + EXCEPTION_ADES = 5, // Address error on store + EXCEPTION_IBE = 6, // Instruction bus error + EXCEPTION_DBE = 7, // Data bus error + EXCEPTION_SYSCALL = 8, // System call + EXCEPTION_BP = 9, // Breakpoint + EXCEPTION_RI = 10, // Reserved instruction + EXCEPTION_COPU = 11, // Coprocessor unusable + EXCEPTION_OV = 12 // Arithmetic overflow + }; + + uint32 Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 NPM, const uint32 instr) MDFN_WARN_UNUSED_RESULT; + + template pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE; + + template T PeekMemory(uint32 address) MDFN_COLD; + template void PokeMemory(uint32 address, T value) MDFN_COLD; + template T ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24 = false, bool LWC_timing = false); + template void WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24 = false); + + + // + // Mednafen debugger stuff follows: + // + public: + void SetCPUHook(void(*cpuh)(const pscpu_timestamp_t timestamp, uint32 pc), void(*addbt)(uint32 from, uint32 to, bool exception)); + void CheckBreakpoints(void(*callback)(bool write, uint32 address, unsigned int len), uint32 instr); + void* debug_GetScratchRAMPtr() { return ScratchRAM.data8; } + void* debug_GetGPRPtr() { return GPR; } + + enum + { + GSREG_GPR = 0, + GSREG_PC = 32, + GSREG_PC_NEXT, + GSREG_IN_BD_SLOT, + GSREG_LO, + GSREG_HI, + GSREG_SR, + GSREG_CAUSE, + GSREG_EPC, + }; + + uint32 GetRegister(unsigned int which, char *special, const uint32 special_len); + void SetRegister(unsigned int which, uint32 value); + bool PeekCheckICache(uint32 PC, uint32 *iw); + + uint8 PeekMem8(uint32 A); + uint16 PeekMem16(uint32 A); + uint32 PeekMem32(uint32 A); + + void PokeMem8(uint32 A, uint8 V); + void PokeMem16(uint32 A, uint16 V); + void PokeMem32(uint32 A, uint32 V); + + private: + void(*CPUHook)(const pscpu_timestamp_t timestamp, uint32 pc); + void(*ADDBT)(uint32 from, uint32 to, bool exception); + }; + +} + +#endif diff --git a/psx/octoshock/psx/dis.cpp b/psx/octoshock/psx/dis.cpp index 8ecb217d8c..9ebe5fc15a 100644 --- a/psx/octoshock/psx/dis.cpp +++ b/psx/octoshock/psx/dis.cpp @@ -128,13 +128,15 @@ struct OpEntry #define MK_OP(mnemonic, format, op, func, extra_mask) { MASK_OP | (op ? 0 : MASK_FUNC) | extra_mask, ((unsigned)op << 26) | func, mnemonic, format } -#define MK_OP_REGIMM(mnemonic, regop) { MASK_OP | MASK_RT, (0x01U << 26) | (regop << 16), mnemonic, "s, p" } +#define MK_OP_REGIMM(mnemonic, regop_mask, regop) { MASK_OP | (regop_mask << 16), (0x01U << 26) | (regop << 16), mnemonic, "s, p" } #define MK_COPZ(z) { MASK_OP | (0x1U << 25), (0x1U << 25) | ((0x10U | z) << 26), "cop" #z, "F" } #define MK_COP0_FUNC(mnemonic, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x10U << 26) | (0x1U << 25) | func, mnemonic, "" } #define MK_COPZ_XFER(z, mnemonic, format, xf) { MASK_OP | (0x1FU << 21), ((0x10U | z) << 26) | (xf << 21), mnemonic, format } +#define MK_COPZ_BCzx(z, x) { MASK_OP | (0x1BU << 21) | (0x01 << 16), ((0x10U | z) << 26) | (0x08 << 21) | (x << 16), (x ? "bc" #z "t" : "bc" #z "f"), "p" } +#define MK_COPZ_BC(z) MK_COPZ_BCzx(z, 0), MK_COPZ_BCzx(z, 1) #define MK_GTE(mnemonic, format, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x1U << 25) | (0x12U << 26) | func, mnemonic, format } @@ -180,10 +182,12 @@ static OpEntry ops[] = MK_OP("slt", "d, s, t", 0, 42, 0), MK_OP("sltu", "d, s, t", 0, 43, 0), - MK_OP_REGIMM("bgez", 0x01), - MK_OP_REGIMM("bgezal", 0x11), - MK_OP_REGIMM("bltz", 0x00), - MK_OP_REGIMM("bltzal", 0x10), + // keep *al before the non-linking versions, due to mask setup. + MK_OP_REGIMM("bgezal", 0x1F, 0x11), + MK_OP_REGIMM("bltzal", 0x1F, 0x10), + + MK_OP_REGIMM("bgez", 0x01, 0x01), + MK_OP_REGIMM("bltz", 0x00, 0x00), MK_OP("j", "P", 2, 0, 0), @@ -225,6 +229,11 @@ static OpEntry ops[] = MK_COPZ_XFER(2, "ctc2", "t, G", 0x06), MK_COPZ_XFER(3, "ctc3", "t, ?", 0x06), + MK_COPZ_BC(0), + MK_COPZ_BC(1), + MK_COPZ_BC(2), + MK_COPZ_BC(3), + // COP0 stuff here MK_COP0_FUNC("rfe", 0x10), @@ -316,8 +325,8 @@ EW_EXPORT s32 shock_Util_DisassembleMIPS(u32 PC, u32 instr, void* outbuf, s32 bu static const char *cop0_names[32] = { - "CPR0", "CPR1", "CPR2", "BPC", "CPR4", "BDA", "TAR", "DCIC", "CPR8", "BDAM", "CPR10", "BPCM", "SR", "CAUSE", "EPC", "PRID", - "ERREG", "CPR17", "CPR18", "CPR19", "CPR20", "CPR21", "CPR22", "CPR23", "CPR24", "CPR25", "CPR26", "CPR27", "CPR28", "CPR29", "CPR30", "CPR31" + "CPR0", "CPR1", "CPR2", "BPC", "CPR4", "BDA", "TAR", "DCIC", "BADVA", "BDAM", "CPR10", "BPCM", "SR", "CAUSE", "EPC", "PRID", + "CPR16", "CPR17", "CPR18", "CPR19", "CPR20", "CPR21", "CPR22", "CPR23", "CPR24", "CPR25", "CPR26", "CPR27", "CPR28", "CPR29", "CPR30", "CPR31" }; static const char *gte_cr_names[32] = diff --git a/psx/octoshock/psx/frontio.cpp b/psx/octoshock/psx/frontio.cpp index 7d119db4a0..458a8a0be9 100644 --- a/psx/octoshock/psx/frontio.cpp +++ b/psx/octoshock/psx/frontio.cpp @@ -136,10 +136,6 @@ FrontIO::FrontIO() PortData[i] = NULL; MCPorts[i] = new InputDevice(); } - - //always add one memory device for now - delete MCPorts[0]; - MCPorts[0] = Device_Memcard_Create(); } diff --git a/psx/octoshock/psx/frontio.h b/psx/octoshock/psx/frontio.h index 900997eb98..08770d4168 100644 --- a/psx/octoshock/psx/frontio.h +++ b/psx/octoshock/psx/frontio.h @@ -26,6 +26,8 @@ class InputDevice virtual void Update(const pscpu_timestamp_t timestamp); // Partially-implemented, don't rely on for timing any more fine-grained than a video frame for now. virtual void ResetTS(void); + virtual const char* GetName() const { return "InputDevice"; } + // // diff --git a/psx/octoshock/psx/gte.cpp b/psx/octoshock/psx/gte.cpp index 2f87f94078..0f38aefae8 100644 --- a/psx/octoshock/psx/gte.cpp +++ b/psx/octoshock/psx/gte.cpp @@ -796,22 +796,6 @@ static INLINE int32 Lm_G(unsigned int which, int32 value) // limit to 4096, not 4095 static INLINE int32 Lm_H(int32 value) { -#if 0 - if(FLAGS & (1 << 15)) - { - value = 0; - FLAGS |= 1 << 12; - return value; - } - - if(FLAGS & (1 << 16)) - { - value = 4096; - FLAGS |= 1 << 12; - return value; - } -#endif - if(value < 0) { value = 0; diff --git a/psx/octoshock/psx/input/memcard.cpp b/psx/octoshock/psx/input/memcard.cpp index 1e7a405512..814e68d117 100644 --- a/psx/octoshock/psx/input/memcard.cpp +++ b/psx/octoshock/psx/input/memcard.cpp @@ -40,6 +40,8 @@ class InputDevice_Memcard : public InputDevice InputDevice_Memcard(); virtual ~InputDevice_Memcard(); + virtual const char* GetName() const { return "InputDevice_Memcard"; } + virtual void Power(void); virtual void SyncState(bool isReader, EW::NewState *ns); // diff --git a/psx/octoshock/psx/psx.cpp b/psx/octoshock/psx/psx.cpp index 7a30774eb9..e57a3afe10 100644 --- a/psx/octoshock/psx/psx.cpp +++ b/psx/octoshock/psx/psx.cpp @@ -32,6 +32,8 @@ #include "input/dualshock.h" #include "input/dualanalog.h" #include "input/gamepad.h" +#include "input/memcard.h" + #include #include @@ -272,7 +274,6 @@ static void RebaseTS(const pscpu_timestamp_t timestamp) void PSX_SetEventNT(const int type, const pscpu_timestamp_t next_timestamp) { - assert(type > PSX_EVENT__SYNFIRST && type < PSX_EVENT__SYNLAST); event_list_entry *e = &events[type]; if(next_timestamp < e->event_time) @@ -1198,8 +1199,15 @@ struct { //TODO - once we get flexible here, do some extra condition checks.. whether memcards exist, etc. much like devices. switch(transaction->transaction) { - case eShockMemcardTransaction_Connect: return SHOCK_ERROR; //not supported yet - case eShockMemcardTransaction_Disconnect: return SHOCK_ERROR; //not supported yet + case eShockMemcardTransaction_Connect: + //cant connect when a memcard is already connected + if(!strcmp(FIO->MCPorts[portnum]->GetName(),"InputDevice_Memcard")) + return SHOCK_NOCANDO; + delete FIO->MCPorts[portnum]; //delete dummy + FIO->MCPorts[portnum] = Device_Memcard_Create(); + + case eShockMemcardTransaction_Disconnect: + return SHOCK_ERROR; //not supported yet case eShockMemcardTransaction_Write: FIO->MCPorts[portnum]->WriteNV((uint8*)transaction->buffer128k,0,128*1024); diff --git a/psx/octoshock/psx/timer.cpp b/psx/octoshock/psx/timer.cpp index 88a94ed2e4..573561ed02 100644 --- a/psx/octoshock/psx/timer.cpp +++ b/psx/octoshock/psx/timer.cpp @@ -18,433 +18,441 @@ #include #include "psx.h" #include "timer.h" - -/* - Notes(some of it may be incomplete or wrong in subtle ways) - - Control bits: - Lower 3 bits of mode, for timer1(when mode is | 0x100): - 0x1 = don't count while in vblank(except that the first count while in vblank does go through) - 0x3 = vblank going inactive triggers timer reset, then some interesting behavior where counting again is delayed... - 0x5 = vblank going inactive triggers timer reset, and only count within vblank. - 0x7 = Wait until vblank goes active then inactive, then start counting? - For timer2: - 0x1 = timer stopped(TODO: confirm on real system) - - Target mode enabled 0x008 - IRQ enable 0x010 - --?Affects 0x400 status flag?-- 0x020 - IRQ evaluation auto-reset 0x040 - --unknown-- 0x080 - Clock selection 0x100 - Divide by 8(timer 2 only?) 0x200 - - Counter: - Reset to 0 on writes to the mode/status register. - - Status flags: - Unknown flag 0x0400 - Compare flag 0x0800 - Cleared on mode/status read. - Set when: //ever Counter == 0(proooobably, need to investigate lower 3 bits in relation to this). - - - Overflow/Carry flag 0x1000 - Cleared on mode/status read. - Set when counter overflows from 0xFFFF->0. - - Hidden flags: - IRQ done - Cleared on writes to the mode/status register, on writes to the count register, and apparently automatically when the counter - increments if (Mode & 0x40) [Note: If target mode is enabled, and target is 0, IRQ done flag won't be automatically reset] - - There seems to be a brief period(edge condition?) where, if count to target is enabled, you can (sometimes?) read the target value in the count - register before it's reset to 0. I doubt any games rely on this, but who knows. Maybe a PSX equivalent of the PC Engine "Battle Royale"? ;) - - When the counter == 0, the compare flag is set. An IRQ will be generated if (Mode & 0x10), and the hidden IRQ done flag will be set. -*/ - -/* - Dec. 26, 2011 Note - Due to problems I've had with my GPU timing test program, timer2 appears to be unreliable(clocks are skipped?) when target mode is enabled and the full - 33MHz clock is used(rather than 33MHz / 8). TODO: Investigate further and confirm(or not). - - Jan. 15, 2013 Note: - Counters using GPU clock sources(hretrace,dot clock) reportedly will with a low probability return wrong count values on an actual PS1, so keep this in mind - when writing test programs(IE keep reading the count value until two consecutive reads return the same value). -*/ - -/* - FIXME: Clock appropriately(and update events) when using SetRegister() via the debugger. - - TODO: If we ever return randomish values to "simulate" open bus, remember to change the return type and such of the TIMER_Read() function to full 32-bit too. -*/ - -namespace MDFN_IEN_PSX -{ - -struct Timer -{ - uint32 Mode; - int32 Counter; // Only 16-bit, but 32-bit here for detecting counting past target. - int32 Target; - - int32 Div8Counter; - - bool IRQDone; - int32 DoZeCounting; -}; - -static bool vblank; -static bool hretrace; -static Timer Timers[3]; -static pscpu_timestamp_t lastts; - -static int32 CalcNextEvent(int32 next_event) -{ - for(int i = 0; i < 3; i++) - { - int32 target; - int32 count_delta; - - if((i == 0 || i == 1) && (Timers[i].Mode & 0x100)) // If clocked by GPU, abort for this timer(will result in poor granularity for pixel-clock-derived timer IRQs, but whatever). - continue; - - if(!(Timers[i].Mode & 0x10)) // If IRQ is disabled, abort for this timer. - continue; - - if((Timers[i].Mode & 0x8) && (Timers[i].Counter == 0) && (Timers[i].Target == 0) && !Timers[i].IRQDone) - { - next_event = 1; - continue; - } - - target = ((Timers[i].Mode & 0x8) && (Timers[i].Counter < Timers[i].Target)) ? Timers[i].Target : 0x10000; - - count_delta = target - Timers[i].Counter; - if(count_delta <= 0) - { - PSX_DBG(PSX_DBG_ERROR, "timer %d count_delta <= 0!!! %d %d\n", i, target, Timers[i].Counter); - continue; - } - - { - int32 tmp_clocks; - - if(Timers[i].DoZeCounting <= 0) - continue; - - if((i == 0x2) && (Timers[i].Mode & 0x1)) - continue; - - if((i == 0x2) && (Timers[i].Mode & 0x200)) - { - assert(Timers[i].Div8Counter >= 0 && Timers[i].Div8Counter < 8); - tmp_clocks = ((count_delta - 1) * 8) + (8 - Timers[i].Div8Counter); - } - else - tmp_clocks = count_delta; - - assert(tmp_clocks > 0); - - if(next_event > tmp_clocks) - next_event = tmp_clocks; - } - } - - return(next_event); -} - -static void ClockTimer(int i, uint32 clocks) -{ - int32 before = Timers[i].Counter; - int32 target = 0x10000; - bool zero_tm = false; - - if(Timers[i].DoZeCounting <= 0) - clocks = 0; - - if(i == 0x2) - { - uint32 d8_clocks; - - Timers[i].Div8Counter += clocks; - d8_clocks = Timers[i].Div8Counter >> 3; - Timers[i].Div8Counter -= d8_clocks << 3; - - if(Timers[i].Mode & 0x200) // Divide by 8, at least for timer 0x2 - clocks = d8_clocks; - - if(Timers[i].Mode & 1) - clocks = 0; - } - - if(Timers[i].Mode & 0x008) - target = Timers[i].Target; - - if(target == 0 && Timers[i].Counter == 0) - zero_tm = true; - else - Timers[i].Counter += clocks; - - if(clocks && (Timers[i].Mode & 0x40)) - Timers[i].IRQDone = false; - - if((before < target && Timers[i].Counter >= target) || zero_tm || Timers[i].Counter > 0xFFFF) - { -#if 1 - if(Timers[i].Mode & 0x10) - { - if((Timers[i].Counter - target) > 3) - PSX_WARNING("Timer %d IRQ trigger error: %d", i, Timers[i].Counter - target); - } - -#endif - - - Timers[i].Mode |= 0x0800; - - if(Timers[i].Counter > 0xFFFF) - { - Timers[i].Counter -= 0x10000; - - if(target == 0x10000) - Timers[i].Mode |= 0x1000; - - if(!target) - Timers[i].Counter = 0; - } - - if(target) - Timers[i].Counter -= (Timers[i].Counter / target) * target; - - if((Timers[i].Mode & 0x10) && !Timers[i].IRQDone) - { - Timers[i].IRQDone = true; - - IRQ_Assert(IRQ_TIMER_0 + i, true); - IRQ_Assert(IRQ_TIMER_0 + i, false); - } - - if(Timers[i].Counter && (Timers[i].Mode & 0x40)) - Timers[i].IRQDone = false; - } - -} - -void TIMER_SetVBlank(bool status) -{ - switch(Timers[1].Mode & 0x7) - { - case 0x1: - Timers[1].DoZeCounting = !status; - break; - - case 0x3: - if(vblank && !status) - Timers[1].Counter = 0; - break; - - case 0x5: - Timers[1].DoZeCounting = status; - if(vblank && !status) - Timers[1].Counter = 0; - break; - - case 0x7: - if(Timers[1].DoZeCounting == -1) - { - if(!vblank && status) - Timers[1].DoZeCounting = 0; - } - else if(Timers[1].DoZeCounting == 0) - { - if(vblank && !status) - Timers[1].DoZeCounting = 1; - } - break; - } - vblank = status; -} - -void TIMER_SetHRetrace(bool status) -{ - if(hretrace && !status) - { - if((Timers[0].Mode & 0x7) == 0x3) - Timers[0].Counter = 0; - } - - hretrace = status; -} - -void TIMER_AddDotClocks(uint32 count) -{ - if(Timers[0].Mode & 0x100) - ClockTimer(0, count); -} - -void TIMER_ClockHRetrace(void) -{ - if(Timers[1].Mode & 0x100) - ClockTimer(1, 1); -} - -pscpu_timestamp_t TIMER_Update(const pscpu_timestamp_t timestamp) -{ - int32 cpu_clocks = timestamp - lastts; - - for(int i = 0; i < 3; i++) - { - uint32 timer_clocks = cpu_clocks; - - if(Timers[i].Mode & 0x100) - continue; - - ClockTimer(i, timer_clocks); - } - - lastts = timestamp; - - return(timestamp + CalcNextEvent(1024)); -} - -static void CalcCountingStart(unsigned which) -{ - Timers[which].DoZeCounting = true; - - switch(which) - { - case 1: - switch(Timers[which].Mode & 0x07) - { - case 0x1: - Timers[which].DoZeCounting = !vblank; - break; - - case 0x5: - Timers[which].DoZeCounting = vblank; - break; - - case 0x7: - Timers[which].DoZeCounting = -1; - break; - } - break; - - - } -} - -void TIMER_Write(const pscpu_timestamp_t timestamp, uint32 A, uint16 V) -{ - TIMER_Update(timestamp); - - int which = (A >> 4) & 0x3; - - V <<= (A & 3) * 8; - - PSX_DBGINFO("[TIMER] Write: %08x %04x\n", A, V); - - if(which >= 3) - return; - - // TODO: See if the "Timers[which].Counter" part of the IRQ if() statements below is what a real PSX does. - switch(A & 0xC) - { - case 0x0: Timers[which].IRQDone = false; -#if 1 - if(Timers[which].Counter && (V & 0xFFFF) == 0) - { - Timers[which].Mode |= 0x0800; - if((Timers[which].Mode & 0x10) && !Timers[which].IRQDone) - { - Timers[which].IRQDone = true; - IRQ_Assert(IRQ_TIMER_0 + which, true); - IRQ_Assert(IRQ_TIMER_0 + which, false); - } - } -#endif - Timers[which].Counter = V & 0xFFFF; - break; - - case 0x4: Timers[which].Mode = (V & 0x3FF) | (Timers[which].Mode & 0x1C00); - Timers[which].IRQDone = false; -#if 1 - if(Timers[which].Counter) - { - Timers[which].Mode |= 0x0800; - if((Timers[which].Mode & 0x10) && !Timers[which].IRQDone) - { - Timers[which].IRQDone = true; - IRQ_Assert(IRQ_TIMER_0 + which, true); - IRQ_Assert(IRQ_TIMER_0 + which, false); - } - } - Timers[which].Counter = 0; -#endif - CalcCountingStart(which); // Call after setting .Mode - break; - - case 0x8: Timers[which].Target = V & 0xFFFF; - break; - - case 0xC: // Open bus - break; - } - - // TIMER_Update(timestamp); - - PSX_SetEventNT(PSX_EVENT_TIMER, timestamp + CalcNextEvent(1024)); -} - -uint16 TIMER_Read(const pscpu_timestamp_t timestamp, uint32 A) -{ - uint16 ret = 0; - int which = (A >> 4) & 0x3; - - if(which >= 3) - { - PSX_WARNING("[TIMER] Open Bus Read: 0x%08x", A); - - return(ret >> ((A & 3) * 8)); - } - - TIMER_Update(timestamp); - - switch(A & 0xC) - { - case 0x0: ret = Timers[which].Counter; - break; - - case 0x4: ret = Timers[which].Mode; - Timers[which].Mode &= ~0x1800; - break; - - case 0x8: ret = Timers[which].Target; - break; - - case 0xC: PSX_WARNING("[TIMER] Open Bus Read: 0x%08x", A); - break; - } - - return(ret >> ((A & 3) * 8)); -} - - -void TIMER_ResetTS(void) -{ - lastts = 0; -} - - -void TIMER_Power(void) -{ - lastts = 0; - - hretrace = false; - vblank = false; - memset(Timers, 0, sizeof(Timers)); + +/* + Notes(some of it may be incomplete or wrong in subtle ways) + + Control bits: + Lower 3 bits of mode, for timer1(when mode is | 0x100): + 0x1 = don't count while in vblank(except that the first count while in vblank does go through) + 0x3 = vblank going inactive triggers timer reset, then some interesting behavior where counting again is delayed... + 0x5 = vblank going inactive triggers timer reset, and only count within vblank. + 0x7 = Wait until vblank goes active then inactive, then start counting? + For timer2: + 0x1 = timer stopped(TODO: confirm on real system) + + Target match counter reset enable 0x008 + Target match IRQ enable 0x010 + Overflow IRQ enable 0x020 + IRQ evaluation auto-reset 0x040 + --unknown-- 0x080 + Clock selection 0x100 + Divide by 8(timer 2 only?) 0x200 + + Counter: + Reset to 0 on writes to the mode/status register. + + Status flags: + Current IRQ line status? 0x0400 + Compare flag 0x0800 + Cleared on mode/status read. + Set repeatedly while counter == target. + + Overflow/Carry flag 0x1000 + Cleared on mode/status read. + Set when counter overflows from 0xFFFF->0. + + Hidden flags: + IRQ done + Cleared on writes to the mode/status register, on writes to the count register, and apparently automatically when the counter + increments if (Mode & 0x40) [Note: If target mode is enabled, and target is 0, IRQ done flag won't be automatically reset] + + There seems to be a brief period(edge condition?) where, if target match reset mode is enabled, you can (sometimes?) read the target value in the count + register before it's reset to 0. Currently not emulated; I doubt any games rely on this, but who knows. Maybe a PSX equivalent + of the PC Engine "Battle Royale"? ;) + + A timer is somewhat unreliable when target match reset mode is enabled and the 33MHz clock is used. Average 2.4 counts seem to be + skipped for that timer every target match reset, but oddly subtracting only 2 from the desired target match value seems to effectively + negate the loss...wonder if my test program is faulty in some way. Currently not emulated. + + Counters using GPU clock sources(hretrace,dot clock) reportedly will with a low probability return wrong count values on an actual PS1, + so keep this in mind when writing test programs(IE keep reading the count value until two consecutive reads return the same value). + Currently not emulated. +*/ + +/* + FIXME: Clock appropriately(and update events) when using SetRegister() via the debugger. + + TODO: If we ever return randomish values to "simulate" open bus, remember to change the return type and such of the TIMER_Read() function to full 32-bit too. +*/ + +namespace MDFN_IEN_PSX +{ + +struct Timer +{ + uint32 Mode; + uint32 Counter; // Only 16-bit, but 32-bit here for detecting counting past target. + uint32 Target; + + uint32 Div8Counter; + + bool IRQDone; + int32 DoZeCounting; +}; + +static bool vblank; +static bool hretrace; +static Timer Timers[3]; +static pscpu_timestamp_t lastts; + +static uint32 CalcNextEvent(void) +{ + uint32 next_event = 1024; // + + for(unsigned i = 0; i < 3; i++) + { + if(!(Timers[i].Mode & 0x30)) // If IRQ is disabled, abort for this timer(don't look at IRQDone for this test, or things will break since its resetting is deferred!). + continue; + + if((Timers[i].Mode & 0x8) && (Timers[i].Counter == 0) && (Timers[i].Target == 0) && !Timers[i].IRQDone) + { + next_event = 1; + continue; + } + + // + // + if((i == 0 || i == 1) && (Timers[i].Mode & 0x100)) // If clocked by GPU, abort for this timer(will result in poor granularity for pixel-clock-derived timer IRQs, but whatever). + continue; + + if(Timers[i].DoZeCounting <= 0) + continue; + + if((i == 0x2) && (Timers[i].Mode & 0x1)) + continue; + + // + // + // + const uint32 target = ((Timers[i].Mode & 0x18) && (Timers[i].Counter < Timers[i].Target)) ? Timers[i].Target : 0x10000; + const uint32 count_delta = target - Timers[i].Counter; + uint32 tmp_clocks; + + if((i == 0x2) && (Timers[i].Mode & 0x200)) + tmp_clocks = (count_delta * 8) - Timers[i].Div8Counter; + else + tmp_clocks = count_delta; + + if(next_event > tmp_clocks) + next_event = tmp_clocks; + } + + return(next_event); +} + +static bool TimerMatch(unsigned i) +{ + bool irq_exact = false; + + Timers[i].Mode |= 0x0800; + + if(Timers[i].Mode & 0x008) + Timers[i].Counter %= std::max(1, Timers[i].Target); + + if((Timers[i].Mode & 0x10) && !Timers[i].IRQDone) + { + if(Timers[i].Counter == 0 || Timers[i].Counter == Timers[i].Target) + irq_exact = true; + +#if 1 + { + const uint16 lateness = (Timers[i].Mode & 0x008) ? Timers[i].Counter : (Timers[i].Counter - Timers[i].Target); + + if(lateness > ((i == 1 && (Timers[i].Mode & 0x100)) ? 0 : 3)) + PSX_DBG(PSX_DBG_WARNING, "[TIMER] Timer %d match IRQ trigger late: %u\n", i, lateness); + } +#endif + + Timers[i].IRQDone = true; + IRQ_Assert(IRQ_TIMER_0 + i, true); + IRQ_Assert(IRQ_TIMER_0 + i, false); + } + + return irq_exact; +} + +static bool TimerOverflow(unsigned i) +{ + bool irq_exact = false; + + Timers[i].Mode |= 0x1000; + Timers[i].Counter &= 0xFFFF; + + if((Timers[i].Mode & 0x20) && !Timers[i].IRQDone) + { + if(Timers[i].Counter == 0) + irq_exact = true; + +#if 1 + if(Timers[i].Counter > ((i == 1 && (Timers[i].Mode & 0x100)) ? 0 : 3)) + PSX_DBG(PSX_DBG_WARNING, "[TIMER] Timer %d overflow IRQ trigger late: %u\n", i, Timers[i].Counter); +#endif + + Timers[i].IRQDone = true; + IRQ_Assert(IRQ_TIMER_0 + i, true); + IRQ_Assert(IRQ_TIMER_0 + i, false); + } + + return irq_exact; +} + +static void ClockTimer(int i, uint32 clocks) +{ + if(Timers[i].DoZeCounting <= 0) + clocks = 0; + + if(i == 0x2) + { + uint32 d8_clocks; + + Timers[i].Div8Counter += clocks; + d8_clocks = Timers[i].Div8Counter >> 3; + Timers[i].Div8Counter &= 0x7; + + if(Timers[i].Mode & 0x200) // Divide by 8, at least for timer 0x2 + clocks = d8_clocks; + + if(Timers[i].Mode & 1) + clocks = 0; + } + + if((Timers[i].Mode & 0x008) && Timers[i].Target == 0 && Timers[i].Counter == 0) + TimerMatch(i); + else if(clocks) + { + uint32 before = Timers[i].Counter; + + Timers[i].Counter += clocks; + + if(Timers[i].Mode & 0x40) + Timers[i].IRQDone = false; + + bool irq_exact = false; + + // + // Target match handling + // + if((before < Timers[i].Target && Timers[i].Counter >= Timers[i].Target) || (Timers[i].Counter >= Timers[i].Target + 0x10000)) + irq_exact |= TimerMatch(i); + + // + // Overflow handling + // + if(Timers[i].Counter >= 0x10000) + irq_exact |= TimerOverflow(i); + + // + if((Timers[i].Mode & 0x40) && !irq_exact) + Timers[i].IRQDone = false; + } +} + +void TIMER_SetVBlank(bool status) +{ + switch(Timers[1].Mode & 0x7) + { + case 0x1: + Timers[1].DoZeCounting = !status; + break; + + case 0x3: + if(vblank && !status) + { + Timers[1].Counter = 0; + if(Timers[1].Counter == Timers[1].Target) + TimerMatch(1); + } + break; + + case 0x5: + Timers[1].DoZeCounting = status; + if(vblank && !status) + { + Timers[1].Counter = 0; + if(Timers[1].Counter == Timers[1].Target) + TimerMatch(1); + } + break; + + case 0x7: + if(Timers[1].DoZeCounting == -1) + { + if(!vblank && status) + Timers[1].DoZeCounting = 0; + } + else if(Timers[1].DoZeCounting == 0) + { + if(vblank && !status) + Timers[1].DoZeCounting = 1; + } + break; + } + vblank = status; +} + +void TIMER_SetHRetrace(bool status) +{ + if(hretrace && !status) + { + if((Timers[0].Mode & 0x7) == 0x3) + { + Timers[0].Counter = 0; + + if(Timers[0].Counter == Timers[0].Target) + TimerMatch(0); + } + } + + hretrace = status; +} + +void TIMER_AddDotClocks(uint32 count) +{ + if(Timers[0].Mode & 0x100) + ClockTimer(0, count); +} + +void TIMER_ClockHRetrace(void) +{ + if(Timers[1].Mode & 0x100) + ClockTimer(1, 1); +} + +pscpu_timestamp_t TIMER_Update(const pscpu_timestamp_t timestamp) +{ + int32 cpu_clocks = timestamp - lastts; + + for(int i = 0; i < 3; i++) + { + uint32 timer_clocks = cpu_clocks; + + if(Timers[i].Mode & 0x100) + continue; + + ClockTimer(i, timer_clocks); + } + + lastts = timestamp; + + return(timestamp + CalcNextEvent()); +} + +static void CalcCountingStart(unsigned which) +{ + Timers[which].DoZeCounting = true; + + switch(which) + { + case 1: + switch(Timers[which].Mode & 0x07) + { + case 0x1: + Timers[which].DoZeCounting = !vblank; + break; + + case 0x5: + Timers[which].DoZeCounting = vblank; + break; + + case 0x7: + Timers[which].DoZeCounting = -1; + break; + } + break; + + + } +} + +void TIMER_Write(const pscpu_timestamp_t timestamp, uint32 A, uint16 V) +{ + TIMER_Update(timestamp); + + int which = (A >> 4) & 0x3; + + V <<= (A & 3) * 8; + + PSX_DBGINFO("[TIMER] Write: %08x %04x\n", A, V); + + if(which >= 3) + return; + + switch(A & 0xC) + { + case 0x0: Timers[which].IRQDone = false; + Timers[which].Counter = V & 0xFFFF; + break; + + case 0x4: Timers[which].Mode = (V & 0x3FF) | (Timers[which].Mode & 0x1C00); + Timers[which].IRQDone = false; + Timers[which].Counter = 0; + + CalcCountingStart(which); // Call after setting .Mode + break; + + case 0x8: Timers[which].Target = V & 0xFFFF; + break; + + case 0xC: // Open bus + break; + } + + if(Timers[which].Counter == Timers[which].Target) + TimerMatch(which); + + PSX_SetEventNT(PSX_EVENT_TIMER, timestamp + CalcNextEvent()); +} + +uint16 TIMER_Read(const pscpu_timestamp_t timestamp, uint32 A) +{ + uint16 ret = 0; + int which = (A >> 4) & 0x3; + + if(which >= 3) + { + PSX_WARNING("[TIMER] Open Bus Read: 0x%08x", A); + + return(ret >> ((A & 3) * 8)); + } + + TIMER_Update(timestamp); + + switch(A & 0xC) + { + case 0x0: ret = Timers[which].Counter; + break; + + case 0x4: ret = Timers[which].Mode; + Timers[which].Mode &= ~0x1000; + if(Timers[which].Counter != Timers[which].Target) + Timers[which].Mode &= ~0x0800; + break; + + case 0x8: ret = Timers[which].Target; + break; + + case 0xC: PSX_WARNING("[TIMER] Open Bus Read: 0x%08x", A); + break; + } + + return(ret >> ((A & 3) * 8)); +} + + +void TIMER_ResetTS(void) +{ + lastts = 0; +} + + +void TIMER_Power(void) +{ + lastts = 0; + + hretrace = false; + vblank = false; + memset(Timers, 0, sizeof(Timers)); } void TIMER_SyncState(bool isReader, EW::NewState *ns) @@ -496,6 +504,9 @@ void TIMER_SetRegister(unsigned int which, uint32 value) break; } + if (Timers[tw].Counter == Timers[tw].Target) + TimerMatch(tw); + }