diff --git a/Assets/defctrl.json b/Assets/defctrl.json index 42f8b4cee1..64ab6bdc4a 100644 --- a/Assets/defctrl.json +++ b/Assets/defctrl.json @@ -461,6 +461,82 @@ "Key Cursor Up/Down": "DownArrow", "Key Cursor Left/Right": "RightArrow", "Key Space": "Space" + }, + "ZXSpectrum Controller": { + "P1 Up": "NumberPad8, J1 POV1U, X1 DpadUp, X1 LStickUp", + "P1 Down": "NumberPad2, J1 POV1D, X1 DpadDown, X1 LStickDown", + "P1 Left": "NumberPad4, J1 POV1L, X1 DpadLeft, X1 LStickLeft", + "P1 Right": "NumberPad6, J1 POV1R, X1 DpadRight, X1 LStickRight", + "P1 Button": "NumberPad1, J1 B1, X1 X", + "Key True Video": "", + "Key Inv Video": "", + "Key 1": "D1", + "Key 2": "D2", + "Key 3": "D3", + "Key 4": "D4", + "Key 5": "D5", + "Key 6": "D6", + "Key 7": "D7", + "Key 8": "D8", + "Key 9": "D9", + "Key 0": "D0", + "Key Break": "Delete", + "Key Delete": "Backspace", + "Key Graph": "", + "Key Q": "Q", + "Key W": "W", + "Key E": "E", + "Key R": "R", + "Key T": "T", + "Key Y": "Y", + "Key U": "U", + "Key I": "I", + "Key O": "O", + "Key P": "P", + "Key Extend Mode": "", + "Key Edit": "", + "Key A": "A", + "Key S": "S", + "Key D": "D", + "Key F": "F", + "Key G": "G", + "Key H": "H", + "Key J": "J", + "Key K": "K", + "Key L": "L", + "Key Return": "Return", + "Key Caps Shift": "LeftShift, RightShift", + "Key Caps Lock": "", + "Key Z": "Z", + "Key X": "X", + "Key C": "C", + "Key V": "V", + "Key B": "B", + "Key N": "N", + "Key M": "M", + "Key Period": "Period", + "Key Symbol Shift": "LeftControl, RightControl", + "Key Semi-Colon": "Semicolon", + "Key Inverted-Comma": "", + "Key Left Cursor": "LeftArrow", + "Key Right Cursor": "RightArrow", + "Key Space": "Space", + "Key Up Cursor": "UpArrow", + "Key Down Cursor": "DownArrow", + "Key Comma": "Comma", + "Play Tape": "F1", + "Stop Tape": "F2", + "RTZ Tape": "F3", + "Record Tape": "", + "Key Quote": "Shift+D2", + "Insert Next Tape": "F6", + "Insert Previous Tape": "F5", + "Next Tape Block": "F8", + "Prev Tape Block": "F7", + "Get Tape Status": "F9", + "Insert Next Disk": "F11", + "Insert Previous Disk": "F10", + "Get Disk Status": "F12" }, "Intellivision Controller": { "P1 Up": "UpArrow, J1 POV1U, X1 DpadUp, X1 LStickUp", diff --git a/Assets/gamedb/gamedb_a2600.txt b/Assets/gamedb/gamedb_a2600.txt index a3a6adc0d7..505744f0ff 100644 --- a/Assets/gamedb/gamedb_a2600.txt +++ b/Assets/gamedb/gamedb_a2600.txt @@ -27,11 +27,11 @@ sha1:b13080675116318af2b8cda7e71bee2a8f39be63 3-D Corridor (Green) (30-03-2003) sha1:76f9020ace43a20a11e3d540ebabc793e6fa4a40 3-D Corridor (Pink Spiral) (31-03-2003) (AD) A26 m=4K;NTSC=true sha1:136b7d61ca887a8687b48cf8b1ee6244d5090cde 3-D Corridor (Spiral Words) (31-03-2003) (AD) A26 m=4K;NTSC=true sha1:77466b40034e5d7603be2e1a46fc69803de5b685 3-D Corridor Demo (27-03-2003) (MP) A26 m=4K;NTSC=true -sha1:6b163aa967e4204a5bd98a59bd8e80f159004e34 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [!] A26 m=2K;PAL=true -sha1:e5a370e4f9531836e5b12951e44ea4a4afe9f5d7 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [p1][o1] A26 m=2K;PAL=true -sha1:21d983f2f52b84c22ecae84b0943678ae2c31c10 3-D Tic-Tac-Toe (1978) (Atari) [!] A26 m=2K;NTSC=true -sha1:f93a1067958a17c14ee72c6b611cffc16901f58b 3-D Tic-Tac-Toe (1978) (Atari) [o1] A26 m=2K;NTSC=true -sha1:fd8f2a6eb9248227edca6ae60de93ca38c0ed2ea 3-D Tic-Tac-Toe (32-in-1) (Atari) (PAL) [!] A26 m=2K;PAL=true +sha1:6b163aa967e4204a5bd98a59bd8e80f159004e34 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [!] A26 SP_FRAME=true;m=2K;PAL=true +sha1:e5a370e4f9531836e5b12951e44ea4a4afe9f5d7 3-D Tic-Tac-Toe (1978) (Atari) (PAL) [p1][o1] A26 SP_FRAME=true;m=2K;PAL=true +sha1:21d983f2f52b84c22ecae84b0943678ae2c31c10 3-D Tic-Tac-Toe (1978) (Atari) [!] A26 SP_FRAME=true;m=2K;NTSC=true +sha1:f93a1067958a17c14ee72c6b611cffc16901f58b 3-D Tic-Tac-Toe (1978) (Atari) [o1] A26 SP_FRAME=true;m=2K;NTSC=true +sha1:fd8f2a6eb9248227edca6ae60de93ca38c0ed2ea 3-D Tic-Tac-Toe (32-in-1) (Atari) (PAL) [!] A26 SP_FRAME=true;m=2K;PAL=true sha1:a100ecfae6ef7f825648f7c9c1f1a35029f4fb5b 2600 Collison Demo 1 (Piero Cavina) (PD) A26 m=4K;NTSC=true sha1:f750d96d811c6cea9dcddf752ba3716ba3dd5dde 2600 Collison Demo 2 (Piero Cavina) (PD) A26 m=4K;NTSC=true sha1:5bb83bdbd49bf0c9cbbd0381e99dbb5b7f58d074 2600 Digital Clock (Demo 1) (PD) A26 m=4K;NTSC=true @@ -93,10 +93,10 @@ sha1:a1f660827ce291f19719a5672f2c5d277d903b03 Alpha Beam with Ernie (1983) (Ata sha1:f22872b1a1d6156f81335e605b83c180faae6209 Alpha Demo - The Beta Demo 2 (2000) (MP) A26 m=4K;NTSC=true sha1:ff8c849db0e963d9e3962d887fb389ef90f968c8 Amanda Invaders (PD) [o1] A26 m=4K;NTSC=true sha1:316c851551956c8e73956ee073f918380b9fa778 Amanda Invaders (PD) A26 m=4K;NTSC=true -sha1:fb9bba6556fc45904dac8750fa18155e6196b2c0 Amidar (1983) (Parker Bros) (PAL) [!] A26 m=4K;PAL=true -sha1:b89a5ac6593e83fbebee1fe7d4cec81a7032c544 Amidar (1983) (Parker Bros) A26 m=4K;NTSC=true -sha1:59657177bc7e2eaf7736ac121defdd891328ae00 Amidar DS (2003) (TJ) (Amidar Hack) A26 m=4K;NTSC=true -sha1:f4688fc8278a2c2040b3eaf90ef7323f6523555a Amidar DS (Fast Enemies) (2003) (TJ) (Amidar Hack) A26 m=4K;NTSC=true +sha1:fb9bba6556fc45904dac8750fa18155e6196b2c0 Amidar (1983) (Parker Bros) (PAL) [!] A26 SP_RESET=true;m=4K;PAL=true +sha1:b89a5ac6593e83fbebee1fe7d4cec81a7032c544 Amidar (1983) (Parker Bros) A26 SP_RESET=true;m=4K;NTSC=true +sha1:59657177bc7e2eaf7736ac121defdd891328ae00 Amidar DS (2003) (TJ) (Amidar Hack) A26 SP_RESET=true;m=4K;NTSC=true +sha1:f4688fc8278a2c2040b3eaf90ef7323f6523555a Amidar DS (Fast Enemies) (2003) (TJ) (Amidar Hack) A26 SP_RESET=true;m=4K;NTSC=true sha1:2d29ce1ff161b1cdae935bbbd84fc330254696b6 An Exercise In Minimalism (V1) (1999) (Marc de Smet) (PD) A26 m=4K;NTSC=true sha1:50e383e0e2e652e0b067f56bc3964cf6641139f1 An Exercise In Minimalism (V2) (1999) (Eckhard Stolberg) A26 m=4K;NTSC=true sha1:3ad5c5a35f5e198f35de6066df6c0d15e7b89e02 Analog Clock (Additional Frame Info) (V0.0) (20-01-2003) (AD) A26 m=3F;NTSC=true @@ -328,7 +328,7 @@ sha1:1819ef408c1216c83dcfeceec28d13f6ea5ca477 Bump 'N' Jump (1983) (Mattel) A26 sha1:35bc4048f58bb170313872a0bce44fb1ca3217cc Bump 'N' Jump (Telegames) (PAL) [!] A26 m=F8;PAL=true sha1:ad48f4952e867a2b97900d965b69f50fddf8ba2d Bumper Bash (1983) (Spectravideo) (PAL) [!] A26 m=4K;PAL=true sha1:6c199782c79686dc0cbce6d5fe805f276a86a3f5 Bumper Bash (1983) (Spectravideo) A26 m=4K;NTSC=true -sha1:49e01b8048ae344cb65838f6b1c1de0e1f416f29 Burgertime (1982) (Mattel) A26 m=E7;NTSC=true +sha1:49e01b8048ae344cb65838f6b1c1de0e1f416f29 Burgertime (1982) (Mattel) A26 SP_RESET=true;m=E7;NTSC=true sha1:3f1f17cf620f462355009f5302cddffa730fa2fa Cakewalk (CommaVid) A26 m=4K;NTSC=true sha1:4ca390f8dc4d0f8dce889dfc21ddd675c5860095 Cakewalk (PAL Conversion) (Fabrizio Zavagli) A26 m=4K;NTSC=true sha1:def9502c5a37700ae03461b2d7cf2f73e91b4cec California Games (1988) (Epyx) (PAL) [!] A26 m=F6;PAL=true diff --git a/Assets/gamedb/gamedb_nes.txt b/Assets/gamedb/gamedb_nes.txt index ea70a263de..5ccc1128a5 100644 --- a/Assets/gamedb/gamedb_nes.txt +++ b/Assets/gamedb/gamedb_nes.txt @@ -276,6 +276,7 @@ sha1:4EBC1ED9665C36913D0F05129E6A54787BAD3165 Dragon Ball 3 - Gokuu Den (Japan) sha1:5A6DFDD8A2D62EBE313A6FDB986C3585077BB348 Final Combat (Asia) (NTSC) (Unl) NES board=MAPPER139 sha1:DFAF6D81280ADBEB2ADF3DAB38E536B0F2FDFC76 Final Combat (Asia) (PAL) (Unl) NES board=MAPPER139;system=NES-PAL sha1:433CEC30E71DCA31E32B8A44A0D534DBFE7039CA BoogerMan II (RexSoft) [!] NES board=UNIF_UNL-KOF97 +sha1:1903F3C614E54C0AA4EDE740909D7B3C791FA514 Toy Story NES board=MAPPER219 ;;;;;;;;;;;;;;;;;;;----------------------------------------------------------------------- @@ -303,6 +304,8 @@ sha1:FFB4706E49B826C6CDD12E502E8AE94FC9810B7F Monty no Doki Doki Daisassou (FDS sha1:17473C223453D2D80FCB9DCFA317947287DC5C52 Xing He Zhan Shi (China) (Unl) NES board=WAIXINGMAPPER176 sha1:B1C74236FD17FAB4AB9AA6AB28E38864C66D6255 Pocahontus (UNL) NES board=MAPPER182;PRG=256;CHR=256;WRAM=8;PAD_H=1 sha1:5FA23F88432006DCF6874EA36E9E7DA8934427BE Super Donkey Kong (Unl) NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 +sha1:8A7DAB8B78DA1C5EA159BA9EEC00FF97742245F1 B Super Donkey Kong (Unl) [b1] NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 +sha1:8A7DAB8B78DA1C5EA159BA9EEC00FF97742245F1 O Super Donkey Kong (Unl) [o1] NES board=MAPPER182;PRG=128;CHR=128;WRAM=8;PAD_H=1 ;wrong vram info sha1:32D71DD6C5A8D78A918FE1B9D6D6C4A570D9652D Oeka Kids Anpanman no Hiragana Daisuki (J) NES board=MAPPER096;VRAM=32 diff --git a/Assets/gamedb/gamedb_sega_gg.txt b/Assets/gamedb/gamedb_sega_gg.txt index a8a1d105bb..83b92ad5f6 100644 --- a/Assets/gamedb/gamedb_sega_gg.txt +++ b/Assets/gamedb/gamedb_sega_gg.txt @@ -432,7 +432,7 @@ A438F5C42A849E8224F8D3AFE010655E Tatakae! Pro Yakyuu Twin League (J) GG Sports; D5D52E216698B34A3DBE95D5990C52C8 Tempo Jr. (W) GG World C7570D82D91DC353D7DF445453FC0646 Tengen World Cup Soccer (UE) GG Sports;Soccer GGLink USA;Europe 67388B1C9758E7F0C0957EB9A788611A Terminator 2 - Judgment Day (W) GG World -B9190F130F728BA8C05D82D5F01FDE21 The Terminator (UE) GG USA;Europe +B9190F130F728BA8C05D82D5F01FDE21 The Terminator (UE) GG SRAM=8192 USA;Europe 5E203CD85B4BC7D81B54D8B407E0F307 Tesserae (UE) GG USA;Europe 082DBE19BCFB1A9D16423BC79E940826 Tintin in Tibet (E) (En,Fr,De,Es,Nl,Sv) GG Europe C0246EED0C11EBE7A9B144A00ED61DF1 Tom and Jerry - The Movie (UE) GG USA;Europe diff --git a/Assets/gamedb/gamedb_sega_sms.txt b/Assets/gamedb/gamedb_sega_sms.txt index 6701daef48..d185513659 100644 --- a/Assets/gamedb/gamedb_sega_sms.txt +++ b/Assets/gamedb/gamedb_sega_sms.txt @@ -137,7 +137,7 @@ C28AA80489A467BF54FC1403029A83D3 Champions of Europe (E) SMS Sports;Soccer Eur 1F706162F833F1DCE9513988601D314B Championship Hockey (E) SMS Sports;Hockey Europe 2C709BA8CDC3A1B7ADC5BC838EABF6F3 Chapolim x Dracula - Um Duelo Assustador (B) SMS Brazil 08511C5CF0DF0941B10EBF28050AFE51 Chase H.Q. (E) SMS Racing Europe -3CD1A4C27E330BBA7703EE22AC83B856 Chase H.Q. (J) (SMSGG) SMS Racing Japan +3CD1A4C27E330BBA7703EE22AC83B856 Chase H.Q. (J) (SMSGG) SMS Racing GG_in_SMS Japan F90B86478445D220D386460E55F3B74F Cheese Cat-astrophe Starring Speedy Gonzales (E) (En,Fr,De,Es) SMS Europe 747A206EAAADF48714695E8B4CCEAD7E Choplifter (UE) SMS StereoByte=203 USA;Europe B556297E5BA2B95ED3F3A76A47402E1A Choplifter (U) (Beta) SMS StereoByte=203 USA @@ -368,7 +368,7 @@ A31CBBDED45F66633FB38B1C1BEF9B08 Opa Opa (J) SMS Japan 2CA2064302F51F724E1F2593369A0696 Operation Wolf (E) SMS Light Gun;Arcade Europe F64EA159120C018E05FB95AC8533E9EB The Ottifants (E) (En,Fr,De,Es,It) SMS Europe 558C793AAB09B46BED78431348191060 Out Run 3-D (E) SMS Racing;PaddleSupported 3D;FM;PaddleOptional Europe -2A3BDD1A6C35EEEDBF1A794ABFB57B87 Out Run Europa (U) (SMSGG) SMS Racing USA +2A3BDD1A6C35EEEDBF1A794ABFB57B87 Out Run Europa (U) (SMSGG) SMS Racing GG_in_SMS USA 458FC29765865FDAAF3F56808C94D8A6 Out Run Europa (E) SMS Racing Europe 029EE92155247F8A282D63B8A6DD23C4 Out Run (W) SMS Racing;Arcade FM;WhenFMDisablePSG;PaddleOptional World 946F3E6C2F0F546A8EBE55C8170ECC78 Pac-Mania (E) SMS Arcade Europe @@ -409,7 +409,7 @@ FC40576778DE28CC254B588A3A46D2A6 Putt & Putter (E) (Beta) SMS Europe 58B89D62438407F242BBE713F1D945CA Quest for the Shaven Yak Starring Ren Hoek & Stimpy (B) SMS Brazil B30E60B91960A0F2A89FC133410770DF R.C. Grand Prix (UE) SMS Racing USA;Europe 61AA7404E23836C79E2A689322FFE190 R.C. Grand Prix (UE) (Beta) SMS Racing USA;Europe -D087B25D96F3F3E9338B0B5EC4FC2AA5 R.C. Grand Prix (UE) (SMSGG) SMS Racing USA;Europe +D087B25D96F3F3E9338B0B5EC4FC2AA5 R.C. Grand Prix (UE) (SMSGG) SMS Racing GG_in_SMS USA;Europe FFBA9869948E2CEF2BDA6F805CC72087 Rainbow Islands - Story of the Bubble Bobble 2 (E) SMS Europe E80AE00D8924F2BADA5949BF75995D87 Rainbow Islands - The Story of Bubble Bobble 2 (B) SMS Brazil 7A080A155AD6A806DA88AA1D3576D78C Rambo - First Blood Part II (U) SMS USA diff --git a/BizHawk.Client.ApiHawk/BizHawk.Client.ApiHawk.csproj.DotSettings b/BizHawk.Client.ApiHawk/BizHawk.Client.ApiHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.ApiHawk/BizHawk.Client.ApiHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs index 31b65c5df5..fcce1118cf 100644 --- a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs +++ b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs @@ -96,6 +96,9 @@ namespace BizHawk.Client.ApiHawk case "WSWAN": return CoreSystem.WonderSwan; + case "ZXSpectrum": + return CoreSystem.ZXSpectrum; + case "VB": case "NGP": case "DNGP": @@ -205,6 +208,9 @@ namespace BizHawk.Client.ApiHawk case CoreSystem.WonderSwan: return "WSWAN"; + case CoreSystem.ZXSpectrum: + return "ZXSpectrum"; + default: throw new IndexOutOfRangeException(string.Format("{0} is missing in convert list", value.ToString())); } diff --git a/BizHawk.Client.ApiHawk/Classes/ClientApi.cs b/BizHawk.Client.ApiHawk/Classes/ClientApi.cs index 12ee67277c..767d755105 100644 --- a/BizHawk.Client.ApiHawk/Classes/ClientApi.cs +++ b/BizHawk.Client.ApiHawk/Classes/ClientApi.cs @@ -427,11 +427,11 @@ namespace BizHawk.Client.ApiHawk } else { - return SystemInfo.DualGB; + return SystemInfo.DualGB; } default: - return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(Global.Emulator.SystemId)); + return SystemInfo.FindByCoreSystem(SystemIdConverter.Convert(Global.Emulator.SystemId)); } } } diff --git a/BizHawk.Client.Common/Api/CoreSystem.cs b/BizHawk.Client.Common/Api/CoreSystem.cs index 132a832b98..9bdc7d8232 100644 --- a/BizHawk.Client.Common/Api/CoreSystem.cs +++ b/BizHawk.Client.Common/Api/CoreSystem.cs @@ -29,6 +29,7 @@ WonderSwan, Libretro, VirtualBoy, - NeoGeoPocket + NeoGeoPocket, + ZXSpectrum } } diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings b/BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.Common/Global.cs b/BizHawk.Client.Common/Global.cs index b592f1696b..edd287ef49 100644 --- a/BizHawk.Client.Common/Global.cs +++ b/BizHawk.Client.Common/Global.cs @@ -149,6 +149,8 @@ namespace BizHawk.Client.Common return SystemInfo.VirtualBoy; case "NGP": return SystemInfo.NeoGeoPocket; + case "ZXSpectrum": + return SystemInfo.ZXSpectrum; } } } diff --git a/BizHawk.Client.Common/PathManager.cs b/BizHawk.Client.Common/PathManager.cs index 51826e93f5..a84563484a 100644 --- a/BizHawk.Client.Common/PathManager.cs +++ b/BizHawk.Client.Common/PathManager.cs @@ -231,7 +231,8 @@ namespace BizHawk.Client.Common var filesystemSafeName = game.Name .Replace("|", "+") .Replace(":", " -") // adelikat - Path.GetFileName scraps everything to the left of a colon unfortunately, so we need this hack here - .Replace("\"", ""); // adelikat - Ivan Ironman Stewart's Super Off-Road has quotes in game name + .Replace("\"", "") // adelikat - Ivan Ironman Stewart's Super Off-Road has quotes in game name + .Replace("/", "+"); // Narry - Mario Bros / Duck hunt has a slash in the name which GetDirectoryName and GetFileName treat as if it were a folder // zero 06-nov-2015 - regarding the below, i changed my mind. for libretro i want subdirectories here. var filesystemDir = Path.GetDirectoryName(filesystemSafeName); diff --git a/BizHawk.Client.Common/RomGame.cs b/BizHawk.Client.Common/RomGame.cs index a30e3b64c4..f225c96458 100644 --- a/BizHawk.Client.Common/RomGame.cs +++ b/BizHawk.Client.Common/RomGame.cs @@ -66,6 +66,14 @@ namespace BizHawk.Client.Common { RomData = FileData; } + else if (file.Extension == ".DSK" || file.Extension == ".TAP" || file.Extension == ".TZX" || + file.Extension == ".PZX" || file.Extension == ".CSW" || file.Extension == ".WAV") + { + // these are not roms. unforunately if treated as such there are certain edge-cases + // where a header offset is detected. This should mitigate this issue until a cleaner solution is found + // (-Asnivor) + RomData = FileData; + } else { // if there was a header offset, read the whole file into FileData and then copy it into RomData (this is unfortunate, in case RomData isnt needed) diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 6c7cac044f..84a91bbf7d 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -19,6 +19,7 @@ using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.Saturn; using BizHawk.Emulation.Cores.Sony.PSP; using BizHawk.Emulation.Cores.Sony.PSX; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; using BizHawk.Emulation.DiscSystem; using GPGX64 = BizHawk.Emulation.Cores.Consoles.Sega.gpgx; @@ -657,6 +658,21 @@ namespace BizHawk.Client.Common (C64.C64Settings)GetCoreSettings(), (C64.C64SyncSettings)GetCoreSyncSettings()); break; + case "ZXSpectrum": + + List zxGI = new List(); + foreach (var a in xmlGame.Assets) + { + zxGI.Add(new GameInfo { Name = Path.GetFileNameWithoutExtension(a.Key) }); + } + + nextEmulator = new ZXSpectrum( + nextComm, + xmlGame.Assets.Select(a => a.Value), //.First(), + zxGI, // GameInfo.NullInstance, + (ZXSpectrum.ZXSpectrumSettings)GetCoreSettings(), + (ZXSpectrum.ZXSpectrumSyncSettings)GetCoreSyncSettings()); + break; case "PSX": var entries = xmlGame.AssetFullPaths; var discs = new List(); @@ -964,8 +980,15 @@ namespace BizHawk.Client.Common break; case "GBC": if (!Global.Config.GB_AsSGB) - { - core = CoreInventory.Instance["GBC", "Gambatte"]; + { + if (Global.Config.GB_UseGBHawk) + { + core = CoreInventory.Instance["GBC", "GBHawk"]; + } + else + { + core = CoreInventory.Instance["GBC", "Gambatte"]; + } } else { @@ -990,6 +1013,10 @@ namespace BizHawk.Client.Common var c64 = new C64(nextComm, Enumerable.Repeat(rom.FileData, 1), rom.GameInfo, GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = c64; break; + case "ZXSpectrum": + var zx = new ZXSpectrum(nextComm, Enumerable.Repeat(rom.RomData, 1), Enumerable.Repeat(rom.GameInfo, 1).ToList(), GetCoreSettings(), GetCoreSyncSettings()); + nextEmulator = zx; + break; case "GBA": if (Global.Config.GBA_UsemGBA) { diff --git a/BizHawk.Client.Common/SystemInfo.cs b/BizHawk.Client.Common/SystemInfo.cs index 82414adc50..2d65f1729c 100644 --- a/BizHawk.Client.Common/SystemInfo.cs +++ b/BizHawk.Client.Common/SystemInfo.cs @@ -188,6 +188,11 @@ namespace BizHawk.Client.Common /// public static SystemInfo NeoGeoPocket { get; } = new SystemInfo("Neo-Geo Pocket", CoreSystem.NeoGeoPocket, 1); + /// + /// Gets the instance for ZXSpectrum + /// + public static SystemInfo ZXSpectrum { get; } = new SystemInfo("ZX Spectrum", CoreSystem.ZXSpectrum, 2); + #endregion Get SystemInfo /// diff --git a/BizHawk.Client.Common/config/PathEntry.cs b/BizHawk.Client.Common/config/PathEntry.cs index ae05acc25e..d8e070f950 100644 --- a/BizHawk.Client.Common/config/PathEntry.cs +++ b/BizHawk.Client.Common/config/PathEntry.cs @@ -292,7 +292,13 @@ namespace BizHawk.Client.Common new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, new PathEntry { System = "C64", SystemDisplayName = "Commodore 64", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, - new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Base", Path = Path.Combine(".", "PSX"), Ordinal = 0 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Base", Path = Path.Combine(".", "ZXSpectrum"), Ordinal = 0 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "ROM", Path = ".", Ordinal = 1 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, + new PathEntry { System = "ZXSpectrum", SystemDisplayName = "Sinclair ZX Spectrum", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, + + new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Base", Path = Path.Combine(".", "PSX"), Ordinal = 0 }, new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "ROM", Path = ".", Ordinal = 1 }, new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, new PathEntry { System = "PSX", SystemDisplayName = "Playstation", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 }, diff --git a/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs b/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs index 60c0eb6164..2ce9d776f3 100644 --- a/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs +++ b/BizHawk.Client.Common/lua/EmuLuaLibrary.Emu.cs @@ -169,7 +169,7 @@ namespace BizHawk.Client.Common [LuaMethodExample("local inemutot = emu.totalexecutedcycles( );")] [LuaMethod("totalexecutedcycles", "gets the total number of executed cpu cycles")] - public int TotalExecutedycles() + public long TotalExecutedycles() { try { diff --git a/BizHawk.Client.Common/lua/LuaDocumentation.cs b/BizHawk.Client.Common/lua/LuaDocumentation.cs index 9487336989..c52c4316fb 100644 --- a/BizHawk.Client.Common/lua/LuaDocumentation.cs +++ b/BizHawk.Client.Common/lua/LuaDocumentation.cs @@ -59,7 +59,7 @@ __Types and notation__ sb .AppendFormat("__{0}.{1}__%%%", func.Library, func.Name) .AppendLine().AppendLine() - .AppendFormat("* {0} {1}.{2}{3}", func.ReturnType, func.Library, func.Name, func.ParameterList) + .AppendFormat("* {0} {1}.{2}{3}", func.ReturnType, func.Library, func.Name, func.ParameterList.Replace("[", "[[").Replace("]", "]]")) .AppendLine().AppendLine() .AppendFormat("* {0}", func.Description) .AppendLine().AppendLine(); diff --git a/BizHawk.Client.Common/lua/LuaLibraryBase.cs b/BizHawk.Client.Common/lua/LuaLibraryBase.cs index 0a2b1a7463..bcf0a6845e 100644 --- a/BizHawk.Client.Common/lua/LuaLibraryBase.cs +++ b/BizHawk.Client.Common/lua/LuaLibraryBase.cs @@ -63,25 +63,21 @@ namespace BizHawk.Client.Common return (uint)(double)luaArg; } - protected static Color? ToColor(object color) + protected static Color? ToColor(object o) { - if (color == null) + if (o == null) { return null; } - double tryNum; - var result = double.TryParse(color.ToString(), out tryNum); - - if (result) + if (o.GetType() == typeof(double)) { - var stringResult = ((int)tryNum).ToString(); - return ColorTranslator.FromHtml(stringResult); + return Color.FromArgb((int)(long)(double)o); } - if (!string.IsNullOrWhiteSpace(color.ToString())) + if (o.GetType() == typeof(string)) { - return Color.FromName(color.ToString()); + return Color.FromName(o.ToString()); } return null; diff --git a/BizHawk.Client.Common/movie/PlatformFrameRates.cs b/BizHawk.Client.Common/movie/PlatformFrameRates.cs index 63fa342b55..40ad9b22ff 100644 --- a/BizHawk.Client.Common/movie/PlatformFrameRates.cs +++ b/BizHawk.Client.Common/movie/PlatformFrameRates.cs @@ -6,7 +6,7 @@ namespace BizHawk.Client.Common public class PlatformFrameRates { // these are political numbers, designed to be in accord with tasvideos.org tradition. theyre not necessarily mathematical factualities (although they may be in some cases) - // it would be nice if we could turn this into a rational expression natively, and also, to write some comments about the derivation and ideal valees (since this seems to be where theyre all collected) + // it would be nice if we could turn this into a rational expression natively, and also, to write some comments about the derivation and ideal values (since this seems to be where theyre all collected) // are we collecting them anywhere else? for avi-writing code perhaps? // just some constants, according to specs @@ -59,7 +59,9 @@ namespace BizHawk.Client.Common ["C64_NTSC"] = NTSCCarrier * 2 / 7 / 263 / 65, ["C64_NTSC_OLD"] = NTSCCarrier * 2 / 7 / 262 / 64, ["C64_DREAN"] = PALNCarrier * 2 / 7 / 312 / 65, - ["INTV"] = 59.92 + ["INTV"] = 59.92, + + ["ZXSpectrum_PAL"] = 50.080128205 // according to ryphecha, using // clocks[2] = { 53.693182e06, 53.203425e06 }; //ntsc console, pal console diff --git a/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs b/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs index 026bfbcfb7..0fd295a010 100644 --- a/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs +++ b/BizHawk.Client.Common/movie/bk2/Bk2ControllerAdapter.cs @@ -172,11 +172,12 @@ namespace BizHawk.Client.Common } else if (def.FloatControls.Contains(key)) { - var temp = trimmed.Substring(iterator, 5); + var commaIndex = trimmed.Substring(iterator).IndexOf(','); + var temp = trimmed.Substring(iterator, commaIndex); var val = int.Parse(temp.Trim()); _myFloatControls[key] = val; - iterator += 6; + iterator += commaIndex + 1; } } } diff --git a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs index 7cf5fbdccd..4219f31eb0 100644 --- a/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs +++ b/BizHawk.Client.Common/movie/tasproj/StateManagerDecay.cs @@ -9,8 +9,14 @@ means that we lower the priority of a state that goes at that index. Priority changes depending on current frame and amount of states. States with biggest priority get erased first. With a 4-bit battern and no initial gap between states, total frame coverage is - about 5 times state count. Initial state gap can screw up our patterns, so do all - calculations like gap isn't there, and take it back into account afterwards. + about 5 times state count. + + Initial state gap can screw up our patterns, so do all the calculations like the gap + isn't there, and take it back into account afterwards. The algo only works with integral + greenzone, so we make it think it is integral by reducing the frame numbers. Before any + decay logic starts for each state, we check if it has a marker on it (in which case we + don't drop it) or appears inside the state gap (in which case we forcibly drop it). This + step doesn't involve numbers reduction. _zeros values are essentialy the values of rshiftby here: bitwise view frame rshiftby priority @@ -59,7 +65,7 @@ namespace BizHawk.Client.Common for (; decayStates > 0 && _tsm.StateCount > 1;) { int baseStateIndex = _tsm.GetStateIndexByFrame(Global.Emulator.Frame); - int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; + int baseStateFrame = _tsm.GetStateFrameByIndex(baseStateIndex) / _step; // reduce right away int forwardPriority = -1000000; int backwardPriority = -1000000; int forwardFrame = -1; @@ -73,17 +79,21 @@ namespace BizHawk.Client.Common { continue; } - else if (currentFrame % _step > 0) + else if ((currentFrame % _step > 0) && (currentFrame + 1 != _tsm.LastEditedFrame)) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything - _tsm.RemoveState(currentFrame); - decayStates--; + if (_tsm.RemoveState(currentFrame)) + { + // decrementing this if no state was removed is BAD + decayStates--; - // this is the kind of highly complex loops that might justify goto - goto next_state; + // this is the kind of highly complex loops that might justify goto + goto next_state; + } } else { + // reduce to imaginary integral greenzone for all the decay logic currentFrame /= _step; } @@ -104,23 +114,27 @@ namespace BizHawk.Client.Common for (int currentStateIndex = _tsm.StateCount - 1; currentStateIndex > baseStateIndex; currentStateIndex--) { - int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex) / _step; + int currentFrame = _tsm.GetStateFrameByIndex(currentStateIndex); if (_tsm.StateIsMarker(currentFrame)) { continue; } - else if (currentFrame % _step > 0) + else if ((currentFrame % _step > 0) && (currentFrame + 1 != _tsm.LastEditedFrame)) { // ignore the pattern if the state doesn't belong already, drop it blindly and skip everything - _tsm.RemoveState(currentFrame); - decayStates--; + if (_tsm.RemoveState(currentFrame)) + { + // decrementing this if no state was removed is BAD + decayStates--; - // this is the kind of highly complex loops that might justify goto - goto next_state; + // this is the kind of highly complex loops that might justify goto + goto next_state; + } } else { + // reduce to imaginary integral greenzone for all the decay logic currentFrame /= _step; } @@ -143,30 +157,46 @@ namespace BizHawk.Client.Common { if (baseStateFrame - forwardFrame > backwardFrame - baseStateFrame) { - _tsm.RemoveState(forwardFrame * _step); + if (_tsm.RemoveState(forwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else { - _tsm.RemoveState(backwardFrame * _step); + if (_tsm.RemoveState(backwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } - - decayStates--; } else if (forwardFrame > -1) { - _tsm.RemoveState(forwardFrame * _step); - decayStates--; + if (_tsm.RemoveState(forwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else if (backwardFrame > -1) { - _tsm.RemoveState(backwardFrame * _step); - decayStates--; + if (_tsm.RemoveState(backwardFrame * _step)) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } else { // we're very sorry about failing to find states to remove, but we can't go beyond capacity, so remove at least something // this shouldn't happen, but if we don't do it here, nothing good will happen either - _tsm.RemoveState(_tsm.GetStateFrameByIndex(1)); + if (_tsm.RemoveState(_tsm.GetStateFrameByIndex(1))) + { + // decrementing this if no state was removed is BAD + decayStates--; + } } // this is the kind of highly complex loops that might justify goto diff --git a/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs b/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs index bd70407858..c7eb292890 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasMovie.Editing.cs @@ -101,7 +101,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; if (m.Frame == frame) { Markers.Remove(m); @@ -152,7 +152,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; if (m.Frame == frame) { Markers.Remove(m); @@ -198,7 +198,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; if (m.Frame < removeUpTo) { Markers.Remove(m); @@ -241,7 +241,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; Markers.Move(m.Frame, m.Frame + 1); } } @@ -274,7 +274,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; Markers.Move(m.Frame, m.Frame + inputLog.Count()); } } @@ -363,7 +363,7 @@ namespace BizHawk.Client.Common { for (int i = firstIndex; i < Markers.Count; i++) { - TasMovieMarker m = Markers.ElementAt(i); + TasMovieMarker m = Markers[i]; Markers.Move(m.Frame, m.Frame + count, fromHistory); } } diff --git a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs index cd1ff55c1c..3b2920814c 100644 --- a/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs +++ b/BizHawk.Client.Common/movie/tasproj/TasStateManager.cs @@ -262,16 +262,16 @@ namespace BizHawk.Client.Common return _movie.Markers.IsMarker(frame + 1); } - public void RemoveState(int frame) + public bool RemoveState(int frame) { int index = _states.IndexOfKey(frame); if (frame < 1 || index < 1) { - return; + return false; } - StateManagerState state = _states.ElementAt(index).Value; + StateManagerState state = _states.Values[index]; if (state.IsOnDisk) { @@ -283,6 +283,8 @@ namespace BizHawk.Client.Common } _states.RemoveAt(index); + + return true; } /// @@ -407,11 +409,10 @@ namespace BizHawk.Client.Common { continue; } - - KeyValuePair kvp = _states.ElementAt(i); - bw.Write(kvp.Key); - bw.Write(kvp.Value.Length); - bw.Write(kvp.Value.State); + + bw.Write(_states.Keys[i]); + bw.Write(_states.Values[i].Length); + bw.Write(_states.Values[i].State); } } @@ -504,6 +505,7 @@ namespace BizHawk.Client.Common } public int StateCount => _states.Count; + public int LastEditedFrame => _movie.LastEditedFrame; public bool Any() { diff --git a/BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings b/BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.DBMan/BizHawk.Client.DBMan.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj.DotSettings b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 0fa83153e4..a5b9eaf9d6 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -51,7 +51,7 @@ AnyCPU false prompt - + AllRules.ruleset @@ -503,6 +503,36 @@ TI83PaletteConfig.cs + + Form + + + ZXSpectrumAudioSettings.cs + + + Form + + + ZXSpectrumPokeMemory.cs + + + Form + + + ZXSpectrumNonSyncSettings.cs + + + Form + + + ZXSpectrumCoreEmulationSettings.cs + + + Form + + + ZXSpectrumJoystickSettings.cs + Form @@ -1206,6 +1236,7 @@ + UserControl @@ -1403,6 +1434,21 @@ TI83PaletteConfig.cs + + ZXSpectrumAudioSettings.cs + + + ZXSpectrumPokeMemory.cs + + + ZXSpectrumNonSyncSettings.cs + + + ZXSpectrumCoreEmulationSettings.cs + + + ZXSpectrumJoystickSettings.cs + CoreFeatureAnalysis.cs @@ -1794,6 +1840,7 @@ + @@ -2118,6 +2165,7 @@ + diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj.DotSettings b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/FileLoader.cs b/BizHawk.Client.EmuHawk/FileLoader.cs index 0009646fb4..bbe8b9d62e 100644 --- a/BizHawk.Client.EmuHawk/FileLoader.cs +++ b/BizHawk.Client.EmuHawk/FileLoader.cs @@ -51,11 +51,11 @@ namespace BizHawk.Client.EmuHawk return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", - ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS" + ".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS", ".TZX", ".PZX", ".CSW", ".WAV" }; } - return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", ".32X", ".MDS" }; + return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", ".32X", ".MDS", ".TZX", ".PZX", ".CSW", ".WAV" }; } } diff --git a/BizHawk.Client.EmuHawk/Input/GamePad360.cs b/BizHawk.Client.EmuHawk/Input/GamePad360.cs index 00078cdeac..2d77a44485 100644 --- a/BizHawk.Client.EmuHawk/Input/GamePad360.cs +++ b/BizHawk.Client.EmuHawk/Input/GamePad360.cs @@ -169,15 +169,18 @@ namespace BizHawk.Client.EmuHawk //constant for adapting a +/- 32768 range to a +/-10000-based range const float f = 32768 / 10000.0f; - //constant for adapting a 0-255 range to a 0-10000-based range - const float f255 = 255 / 10000.0f; + //since our whole input framework really only understands whole axes, let's make the triggers look like an axis + float ltrig = g.bLeftTrigger / 255.0f * 2 - 1; + float rtrig = g.bRightTrigger / 255.0f * 2 - 1; + ltrig *= 10000; + rtrig *= 10000; yield return new Tuple("LeftThumbX", g.sThumbLX / f); yield return new Tuple("LeftThumbY", g.sThumbLY / f); yield return new Tuple("RightThumbX", g.sThumbRX / f); yield return new Tuple("RightThumbY", g.sThumbRY / f); - yield return new Tuple("LeftTrigger", g.bLeftTrigger / f255); - yield return new Tuple("RightTrigger", g.bRightTrigger / f255); + yield return new Tuple("LeftTrigger", ltrig); + yield return new Tuple("RightTrigger", rtrig); yield break; } diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 5f47cb66d0..57df31fabc 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -73,7 +73,7 @@ this.LoadCurrentSlotMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SaveRAMSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.FlushSaveRAMMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); + this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.MovieSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ReadonlyMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator15 = new System.Windows.Forms.ToolStripSeparator(); @@ -192,14 +192,13 @@ this.GbaCoreSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.VbaNextCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MgbaCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SGBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); - this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SgbBsnesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SgbSameBoyMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); - this.GBGambatteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBGBHawkMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBCoreSubmenu = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGambatteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGBHawkMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripMenuItem16 = new System.Windows.Forms.ToolStripSeparator(); this.allowGameDBCoreOverridesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator8 = new System.Windows.Forms.ToolStripSeparator(); @@ -239,9 +238,8 @@ this.coreToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.quickNESToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.nesHawkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator34 = new System.Windows.Forms.ToolStripSeparator(); - this.toolStripSeparator35 = new System.Windows.Forms.ToolStripSeparator(); - this.NESPPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator34 = new System.Windows.Forms.ToolStripSeparator(); + this.NESPPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.NESNametableViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.NESGameGenieCodesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.musicRipperToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -274,12 +272,18 @@ this.SMSregionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSregionExportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSregionJapanToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSregionKoreaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSregionAutoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSregionKoreaToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSregionAutoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayNtscToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayPalToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSdisplayAutoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerStandardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerPaddleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerLightPhaserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerSportsPadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.SMSControllerKeyboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMStoolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator(); this.SMSenableBIOSToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.SMSEnableFMChipMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -304,14 +308,14 @@ this.AtariSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.A7800SubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.A7800ControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.A7800FilterSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBSubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.A7800FilterSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.GBcoreSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.LoadGBInSGBMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator28 = new System.Windows.Forms.ToolStripSeparator(); this.GBGPUViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBPrinterViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBGameGenieMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GBPrinterViewerMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.GBASubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.GBACoreSelectionSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.GBAmGBAMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -333,9 +337,10 @@ this.SnesOptionsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.ColecoSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.ColecoControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator35 = new System.Windows.Forms.ToolStripSeparator(); this.ColecoSkipBiosMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.ColecoUseSGMMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.N64SubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.ColecoUseSGMMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.N64SubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.N64PluginSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.N64ControllerSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolStripSeparator23 = new System.Windows.Forms.ToolStripSeparator(); @@ -376,6 +381,16 @@ this.ForumsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.FeaturesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.AboutMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zXSpectrumToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumCoreEmulationSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumControllerConfigurationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumAudioSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumNonSyncSettingsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumPokeMemoryMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumMediaMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumTapesSubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.ZXSpectrumDisksSubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.Atari7800HawkCoreMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.MainStatusBar = new StatusStripEx(); this.DumpStatusButton = new System.Windows.Forms.ToolStripDropDownButton(); this.EmuStatus = new System.Windows.Forms.ToolStripStatusLabel(); @@ -447,13 +462,9 @@ this.ShowMenuContextMenuSeparator = new System.Windows.Forms.ToolStripSeparator(); this.ShowMenuContextMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.timerMouseIdle = new System.Windows.Forms.Timer(this.components); - this.SMSControllerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerStandardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerPaddleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerLightPhaserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerSportsPadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.SMSControllerKeyboardToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.MainformMenu.SuspendLayout(); + this.zxt1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.zxt2ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.MainformMenu.SuspendLayout(); this.MainStatusBar.SuspendLayout(); this.MainFormContextMenu.SuspendLayout(); this.SuspendLayout(); @@ -490,7 +501,8 @@ this.pCFXToolStripMenuItem, this.virtualBoyToolStripMenuItem, this.neoGeoPocketToolStripMenuItem, - this.HelpSubMenu}); + this.HelpSubMenu, + this.zXSpectrumToolStripMenuItem}); this.MainformMenu.LayoutStyle = System.Windows.Forms.ToolStripLayoutStyle.Flow; this.MainformMenu.Location = new System.Drawing.Point(0, 0); this.MainformMenu.Name = "MainformMenu"; @@ -912,26 +924,26 @@ this.LoadCurrentSlotMenuItem.Size = new System.Drawing.Size(178, 22); this.LoadCurrentSlotMenuItem.Text = "Load Current Slot"; this.LoadCurrentSlotMenuItem.Click += new System.EventHandler(this.LoadCurrentSlotMenuItem_Click); - - // - // SaveRAMSubMenu - // - this.SaveRAMSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.FlushSaveRAMMenuItem}); - this.SaveRAMSubMenu.Name = "SaveRAMSubMenu"; - this.SaveRAMSubMenu.Size = new System.Drawing.Size(159, 22); - this.SaveRAMSubMenu.Text = "Save &RAM"; - this.SaveRAMSubMenu.DropDownOpened += new System.EventHandler(this.SaveRAMSubMenu_DropDownOpened); - // - // FlushSaveRAMMenuItem - // - this.FlushSaveRAMMenuItem.Name = "FlushSaveRAMMenuItem"; - this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(156, 22); - this.FlushSaveRAMMenuItem.Text = "&Flush Save Ram"; - this.FlushSaveRAMMenuItem.Click += new System.EventHandler(this.FlushSaveRAMMenuItem_Click); - // toolStripMenuItem2 - // - this.toolStripMenuItem2.Name = "toolStripMenuItem2"; + // + // SaveRAMSubMenu + // + this.SaveRAMSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.FlushSaveRAMMenuItem}); + this.SaveRAMSubMenu.Name = "SaveRAMSubMenu"; + this.SaveRAMSubMenu.Size = new System.Drawing.Size(159, 22); + this.SaveRAMSubMenu.Text = "Save &RAM"; + this.SaveRAMSubMenu.DropDownOpened += new System.EventHandler(this.SaveRAMSubMenu_DropDownOpened); + // + // FlushSaveRAMMenuItem + // + this.FlushSaveRAMMenuItem.Name = "FlushSaveRAMMenuItem"; + this.FlushSaveRAMMenuItem.Size = new System.Drawing.Size(156, 22); + this.FlushSaveRAMMenuItem.Text = "&Flush Save Ram"; + this.FlushSaveRAMMenuItem.Click += new System.EventHandler(this.FlushSaveRAMMenuItem_Click); + // + // toolStripMenuItem2 + // + this.toolStripMenuItem2.Name = "toolStripMenuItem2"; this.toolStripMenuItem2.Size = new System.Drawing.Size(156, 6); // // MovieSubMenu @@ -1126,7 +1138,7 @@ // this.RecordAVMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.RecordHS; this.RecordAVMenuItem.Name = "RecordAVMenuItem"; - this.RecordAVMenuItem.Size = new System.Drawing.Size(223, 22); + this.RecordAVMenuItem.Size = new System.Drawing.Size(225, 22); this.RecordAVMenuItem.Text = "&Record AVI/WAV"; this.RecordAVMenuItem.Click += new System.EventHandler(this.RecordAVMenuItem_Click); // @@ -1134,7 +1146,7 @@ // this.ConfigAndRecordAVMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.AVI; this.ConfigAndRecordAVMenuItem.Name = "ConfigAndRecordAVMenuItem"; - this.ConfigAndRecordAVMenuItem.Size = new System.Drawing.Size(223, 22); + this.ConfigAndRecordAVMenuItem.Size = new System.Drawing.Size(225, 22); this.ConfigAndRecordAVMenuItem.Text = "Config and Record AVI/WAV"; this.ConfigAndRecordAVMenuItem.Click += new System.EventHandler(this.ConfigAndRecordAVMenuItem_Click); // @@ -1142,26 +1154,26 @@ // this.StopAVIMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.Stop; this.StopAVIMenuItem.Name = "StopAVIMenuItem"; - this.StopAVIMenuItem.Size = new System.Drawing.Size(223, 22); + this.StopAVIMenuItem.Size = new System.Drawing.Size(225, 22); this.StopAVIMenuItem.Text = "&Stop AVI/WAV"; this.StopAVIMenuItem.Click += new System.EventHandler(this.StopAVMenuItem_Click); // // toolStripSeparator19 // this.toolStripSeparator19.Name = "toolStripSeparator19"; - this.toolStripSeparator19.Size = new System.Drawing.Size(220, 6); + this.toolStripSeparator19.Size = new System.Drawing.Size(222, 6); // // CaptureOSDMenuItem // this.CaptureOSDMenuItem.Name = "CaptureOSDMenuItem"; - this.CaptureOSDMenuItem.Size = new System.Drawing.Size(223, 22); + this.CaptureOSDMenuItem.Size = new System.Drawing.Size(225, 22); this.CaptureOSDMenuItem.Text = "Capture OSD"; this.CaptureOSDMenuItem.Click += new System.EventHandler(this.CaptureOSDMenuItem_Click); // // SynclessRecordingMenuItem // this.SynclessRecordingMenuItem.Name = "SynclessRecordingMenuItem"; - this.SynclessRecordingMenuItem.Size = new System.Drawing.Size(223, 22); + this.SynclessRecordingMenuItem.Size = new System.Drawing.Size(225, 22); this.SynclessRecordingMenuItem.Text = "S&yncless Recording Tools"; this.SynclessRecordingMenuItem.Click += new System.EventHandler(this.SynclessRecordingMenuItem_Click); // @@ -1816,8 +1828,8 @@ this.CoreSNESSubMenu, this.GbaCoreSubMenu, this.SGBCoreSubmenu, - this.GBCoreSubmenu, - this.GBInSGBMenuItem, + this.GBCoreSubmenu, + this.GBInSGBMenuItem, this.toolStripMenuItem16, this.allowGameDBCoreOverridesToolStripMenuItem, this.toolStripSeparator8, @@ -1900,12 +1912,6 @@ this.MgbaCoreMenuItem.Text = "mGBA"; this.MgbaCoreMenuItem.Click += new System.EventHandler(this.GbaCorePick_Click); // - // Atari7800HawkCoreMenuItem - // - this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem"; - this.Atari7800HawkCoreMenuItem.Size = new System.Drawing.Size(153, 22); - this.Atari7800HawkCoreMenuItem.Text = "Atari7800Hawk"; - // // SGBCoreSubmenu // this.SGBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -1915,48 +1921,48 @@ this.SGBCoreSubmenu.Size = new System.Drawing.Size(239, 22); this.SGBCoreSubmenu.Text = "SGB"; this.SGBCoreSubmenu.DropDownOpened += new System.EventHandler(this.SGBCoreSubmenu_DropDownOpened); - // - // GBCoreSubmenu - // - this.GBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.GBGambatteMenuItem, - this.GBGBHawkMenuItem}); - this.GBCoreSubmenu.Name = "GBCoreSubmenu"; - this.GBCoreSubmenu.Size = new System.Drawing.Size(239, 22); - this.GBCoreSubmenu.Text = "GB"; - this.GBCoreSubmenu.DropDownOpened += new System.EventHandler(this.GBCoreSubmenu_DropDownOpened); - // - // SgbBsnesMenuItem - // - this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; - this.SgbBsnesMenuItem.Size = new System.Drawing.Size(152, 22); + // + // SgbBsnesMenuItem + // + this.SgbBsnesMenuItem.Name = "SgbBsnesMenuItem"; + this.SgbBsnesMenuItem.Size = new System.Drawing.Size(123, 22); this.SgbBsnesMenuItem.Text = "BSNES"; this.SgbBsnesMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); // // SgbSameBoyMenuItem // this.SgbSameBoyMenuItem.Name = "SgbSameBoyMenuItem"; - this.SgbSameBoyMenuItem.Size = new System.Drawing.Size(152, 22); + this.SgbSameBoyMenuItem.Size = new System.Drawing.Size(123, 22); this.SgbSameBoyMenuItem.Text = "SameBoy"; this.SgbSameBoyMenuItem.Click += new System.EventHandler(this.SgbCorePick_Click); - // - // GBGambatteMenuItem - // - this.GBGambatteMenuItem.Name = "GBGambatteMenuItem"; - this.GBGambatteMenuItem.Size = new System.Drawing.Size(152, 22); - this.GBGambatteMenuItem.Text = "Gambatte"; - this.GBGambatteMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); - // - // GBGBHawkMenuItem - // - this.GBGBHawkMenuItem.Name = "GBGBHawkMenuItem"; - this.GBGBHawkMenuItem.Size = new System.Drawing.Size(152, 22); - this.GBGBHawkMenuItem.Text = "GBHawk"; - this.GBGBHawkMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); - // - // GBInSGBMenuItem - // - this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; + // + // GBCoreSubmenu + // + this.GBCoreSubmenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.GBGambatteMenuItem, + this.GBGBHawkMenuItem}); + this.GBCoreSubmenu.Name = "GBCoreSubmenu"; + this.GBCoreSubmenu.Size = new System.Drawing.Size(239, 22); + this.GBCoreSubmenu.Text = "GB"; + this.GBCoreSubmenu.DropDownOpened += new System.EventHandler(this.GBCoreSubmenu_DropDownOpened); + // + // GBGambatteMenuItem + // + this.GBGambatteMenuItem.Name = "GBGambatteMenuItem"; + this.GBGambatteMenuItem.Size = new System.Drawing.Size(126, 22); + this.GBGambatteMenuItem.Text = "Gambatte"; + this.GBGambatteMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBGBHawkMenuItem + // + this.GBGBHawkMenuItem.Name = "GBGBHawkMenuItem"; + this.GBGBHawkMenuItem.Size = new System.Drawing.Size(126, 22); + this.GBGBHawkMenuItem.Text = "GBHawk"; + this.GBGBHawkMenuItem.Click += new System.EventHandler(this.GBCorePick_Click); + // + // GBInSGBMenuItem + // + this.GBInSGBMenuItem.Name = "GBInSGBMenuItem"; this.GBInSGBMenuItem.Size = new System.Drawing.Size(239, 22); this.GBInSGBMenuItem.Text = "GB in SGB"; this.GBInSGBMenuItem.Click += new System.EventHandler(this.GbInSgbMenuItem_Click); @@ -2053,7 +2059,7 @@ this.batchRunnerToolStripMenuItem, this.ExperimentalToolsSubMenu}); this.ToolsSubMenu.Name = "ToolsSubMenu"; - this.ToolsSubMenu.Size = new System.Drawing.Size(47, 19); + this.ToolsSubMenu.Size = new System.Drawing.Size(48, 19); this.ToolsSubMenu.Text = "&Tools"; this.ToolsSubMenu.DropDownOpened += new System.EventHandler(this.ToolsSubMenu_DropDownOpened); // @@ -2442,7 +2448,7 @@ // this.PceControllerSettingsMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.GameController; this.PceControllerSettingsMenuItem.Name = "PceControllerSettingsMenuItem"; - this.PceControllerSettingsMenuItem.Size = new System.Drawing.Size(258, 22); + this.PceControllerSettingsMenuItem.Size = new System.Drawing.Size(259, 22); this.PceControllerSettingsMenuItem.Text = "Controller Settings"; this.PceControllerSettingsMenuItem.Click += new System.EventHandler(this.PceControllerSettingsMenuItem_Click); // @@ -2450,59 +2456,59 @@ // this.PCEGraphicsSettingsMenuItem.Image = global::BizHawk.Client.EmuHawk.Properties.Resources.tvIcon; this.PCEGraphicsSettingsMenuItem.Name = "PCEGraphicsSettingsMenuItem"; - this.PCEGraphicsSettingsMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEGraphicsSettingsMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEGraphicsSettingsMenuItem.Text = "Graphics Settings"; this.PCEGraphicsSettingsMenuItem.Click += new System.EventHandler(this.PceGraphicsSettingsMenuItem_Click); // // toolStripSeparator32 // this.toolStripSeparator32.Name = "toolStripSeparator32"; - this.toolStripSeparator32.Size = new System.Drawing.Size(255, 6); + this.toolStripSeparator32.Size = new System.Drawing.Size(256, 6); // // PCEBGViewerMenuItem // this.PCEBGViewerMenuItem.Name = "PCEBGViewerMenuItem"; - this.PCEBGViewerMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEBGViewerMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEBGViewerMenuItem.Text = "&BG Viewer"; this.PCEBGViewerMenuItem.Click += new System.EventHandler(this.PceBgViewerMenuItem_Click); // // PCEtileViewerToolStripMenuItem // this.PCEtileViewerToolStripMenuItem.Name = "PCEtileViewerToolStripMenuItem"; - this.PCEtileViewerToolStripMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEtileViewerToolStripMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEtileViewerToolStripMenuItem.Text = "&Tile Viewer"; this.PCEtileViewerToolStripMenuItem.Click += new System.EventHandler(this.PceTileViewerMenuItem_Click); // // PceSoundDebuggerToolStripMenuItem // this.PceSoundDebuggerToolStripMenuItem.Name = "PceSoundDebuggerToolStripMenuItem"; - this.PceSoundDebuggerToolStripMenuItem.Size = new System.Drawing.Size(258, 22); + this.PceSoundDebuggerToolStripMenuItem.Size = new System.Drawing.Size(259, 22); this.PceSoundDebuggerToolStripMenuItem.Text = "&Sound Debugger"; this.PceSoundDebuggerToolStripMenuItem.Click += new System.EventHandler(this.PceSoundDebuggerMenuItem_Click); // // toolStripSeparator25 // this.toolStripSeparator25.Name = "toolStripSeparator25"; - this.toolStripSeparator25.Size = new System.Drawing.Size(255, 6); + this.toolStripSeparator25.Size = new System.Drawing.Size(256, 6); // // PCEAlwaysPerformSpriteLimitMenuItem // this.PCEAlwaysPerformSpriteLimitMenuItem.Name = "PCEAlwaysPerformSpriteLimitMenuItem"; - this.PCEAlwaysPerformSpriteLimitMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEAlwaysPerformSpriteLimitMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEAlwaysPerformSpriteLimitMenuItem.Text = "Always Perform Sprite Limit"; this.PCEAlwaysPerformSpriteLimitMenuItem.Click += new System.EventHandler(this.PCEAlwaysPerformSpriteLimitMenuItem_Click); // // PCEAlwaysEqualizeVolumesMenuItem // this.PCEAlwaysEqualizeVolumesMenuItem.Name = "PCEAlwaysEqualizeVolumesMenuItem"; - this.PCEAlwaysEqualizeVolumesMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEAlwaysEqualizeVolumesMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEAlwaysEqualizeVolumesMenuItem.Text = "Always Equalize Volumes (PCE-CD)"; this.PCEAlwaysEqualizeVolumesMenuItem.Click += new System.EventHandler(this.PCEAlwaysEqualizeVolumesMenuItem_Click); // // PCEArcadeCardRewindEnableMenuItem // this.PCEArcadeCardRewindEnableMenuItem.Name = "PCEArcadeCardRewindEnableMenuItem"; - this.PCEArcadeCardRewindEnableMenuItem.Size = new System.Drawing.Size(258, 22); + this.PCEArcadeCardRewindEnableMenuItem.Size = new System.Drawing.Size(259, 22); this.PCEArcadeCardRewindEnableMenuItem.Text = "Arcade Card Rewind-Enable Hack"; this.PCEArcadeCardRewindEnableMenuItem.Click += new System.EventHandler(this.PCEArcadeCardRewindEnableMenuItem_Click); // @@ -2512,7 +2518,7 @@ this.SMSregionToolStripMenuItem, this.SMSdisplayToolStripMenuItem, this.SMSControllerToolStripMenuItem, - this.SMStoolStripMenuItem2, + this.SMStoolStripMenuItem2, this.SMSenableBIOSToolStripMenuItem, this.SMSEnableFMChipMenuItem, this.SMSOverclockMenuItem, @@ -2536,10 +2542,10 @@ this.SMSregionToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.SMSregionExportToolStripMenuItem, this.SMSregionJapanToolStripMenuItem, - this.SMSregionKoreaToolStripMenuItem, - this.SMSregionAutoToolStripMenuItem}); + this.SMSregionKoreaToolStripMenuItem, + this.SMSregionAutoToolStripMenuItem}); this.SMSregionToolStripMenuItem.Name = "SMSregionToolStripMenuItem"; - this.SMSregionToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSregionToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSregionToolStripMenuItem.Text = "Region"; // // SMSregionExportToolStripMenuItem @@ -2555,17 +2561,17 @@ this.SMSregionJapanToolStripMenuItem.Size = new System.Drawing.Size(107, 22); this.SMSregionJapanToolStripMenuItem.Text = "Japan"; this.SMSregionJapanToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionJapan_Click); - // - // SMSregionKoreaToolStripMenuItem - // - this.SMSregionKoreaToolStripMenuItem.Name = "SMSregionKoreaToolStripMenuItem"; - this.SMSregionKoreaToolStripMenuItem.Size = new System.Drawing.Size(107, 22); - this.SMSregionKoreaToolStripMenuItem.Text = "Korea"; - this.SMSregionKoreaToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionKorea_Click); - // - // SMSregionAutoToolStripMenuItem - // - this.SMSregionAutoToolStripMenuItem.Name = "SMSregionAutoToolStripMenuItem"; + // + // SMSregionKoreaToolStripMenuItem + // + this.SMSregionKoreaToolStripMenuItem.Name = "SMSregionKoreaToolStripMenuItem"; + this.SMSregionKoreaToolStripMenuItem.Size = new System.Drawing.Size(107, 22); + this.SMSregionKoreaToolStripMenuItem.Text = "Korea"; + this.SMSregionKoreaToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionKorea_Click); + // + // SMSregionAutoToolStripMenuItem + // + this.SMSregionAutoToolStripMenuItem.Name = "SMSregionAutoToolStripMenuItem"; this.SMSregionAutoToolStripMenuItem.Size = new System.Drawing.Size(107, 22); this.SMSregionAutoToolStripMenuItem.Text = "Auto"; this.SMSregionAutoToolStripMenuItem.Click += new System.EventHandler(this.SMS_RegionAuto_Click); @@ -2577,7 +2583,7 @@ this.SMSdisplayPalToolStripMenuItem, this.SMSdisplayAutoToolStripMenuItem}); this.SMSdisplayToolStripMenuItem.Name = "SMSdisplayToolStripMenuItem"; - this.SMSdisplayToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSdisplayToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSdisplayToolStripMenuItem.Text = "Display Type"; // // SMSdisplayNtscToolStripMenuItem @@ -2587,51 +2593,9 @@ this.SMSdisplayNtscToolStripMenuItem.Text = "NTSC"; this.SMSdisplayNtscToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayNTSC_Click); // - // SMSControllerToolStripMenuItem + // SMSdisplayPalToolStripMenuItem // - this.SMSControllerToolStripMenuItem.Name = "SMSControllerToolStripMenuItem"; - this.SMSControllerToolStripMenuItem.Text = "&Controller Type"; - this.SMSControllerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.SMSControllerStandardToolStripMenuItem, - this.SMSControllerPaddleToolStripMenuItem, - this.SMSControllerLightPhaserToolStripMenuItem, - this.SMSControllerSportsPadToolStripMenuItem, - this.SMSControllerKeyboardToolStripMenuItem}); - // - // SMSControllerStandardToolStripMenuItem - // - this.SMSControllerStandardToolStripMenuItem.Name = "SMSControllerStandardToolStripMenuItem"; - this.SMSControllerStandardToolStripMenuItem.Text = "Standard"; - this.SMSControllerStandardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerStandardToolStripMenuItem_Click); - // - // SMSControllerPaddleToolStripMenuItem - // - this.SMSControllerPaddleToolStripMenuItem.Name = "SMSControllerPaddleToolStripMenuItem"; - this.SMSControllerPaddleToolStripMenuItem.Text = "Paddle"; - this.SMSControllerPaddleToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerPaddleToolStripMenuItem_Click); - // - // SMSControllerLightPhaserToolStripMenuItem - // - this.SMSControllerLightPhaserToolStripMenuItem.Name = "SMSControllerLightPhaserToolStripMenuItem"; - this.SMSControllerLightPhaserToolStripMenuItem.Text = "Light Phaser"; - this.SMSControllerLightPhaserToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerLightPhaserToolStripMenuItem_Click); - // - // SMSControllerSportsPadToolStripMenuItem - // - this.SMSControllerSportsPadToolStripMenuItem.Name = "SMSControllerSportsPadToolStripMenuItem"; - this.SMSControllerSportsPadToolStripMenuItem.Text = "Sports Pad"; - this.SMSControllerSportsPadToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerSportsPadToolStripMenuItem_Click); - // - // SMSControllerKeyboardToolStripMenuItem - // - this.SMSControllerKeyboardToolStripMenuItem.Name = "SMSControllerKeyboardToolStripMenuItem"; - this.SMSControllerKeyboardToolStripMenuItem.Text = "Keyboard"; - this.SMSControllerKeyboardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerKeyboardToolStripMenuItem_Click); - - // - // SMSdisplayPalToolStripMenuItem - // - this.SMSdisplayPalToolStripMenuItem.Name = "SMSdisplayPalToolStripMenuItem"; + this.SMSdisplayPalToolStripMenuItem.Name = "SMSdisplayPalToolStripMenuItem"; this.SMSdisplayPalToolStripMenuItem.Size = new System.Drawing.Size(104, 22); this.SMSdisplayPalToolStripMenuItem.Text = "PAL"; this.SMSdisplayPalToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayPAL_Click); @@ -2643,97 +2607,144 @@ this.SMSdisplayAutoToolStripMenuItem.Text = "Auto"; this.SMSdisplayAutoToolStripMenuItem.Click += new System.EventHandler(this.SMS_DisplayAuto_Click); // + // SMSControllerToolStripMenuItem + // + this.SMSControllerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.SMSControllerStandardToolStripMenuItem, + this.SMSControllerPaddleToolStripMenuItem, + this.SMSControllerLightPhaserToolStripMenuItem, + this.SMSControllerSportsPadToolStripMenuItem, + this.SMSControllerKeyboardToolStripMenuItem}); + this.SMSControllerToolStripMenuItem.Name = "SMSControllerToolStripMenuItem"; + this.SMSControllerToolStripMenuItem.Size = new System.Drawing.Size(278, 22); + this.SMSControllerToolStripMenuItem.Text = "&Controller Type"; + // + // SMSControllerStandardToolStripMenuItem + // + this.SMSControllerStandardToolStripMenuItem.Name = "SMSControllerStandardToolStripMenuItem"; + this.SMSControllerStandardToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerStandardToolStripMenuItem.Text = "Standard"; + this.SMSControllerStandardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerStandardToolStripMenuItem_Click); + // + // SMSControllerPaddleToolStripMenuItem + // + this.SMSControllerPaddleToolStripMenuItem.Name = "SMSControllerPaddleToolStripMenuItem"; + this.SMSControllerPaddleToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerPaddleToolStripMenuItem.Text = "Paddle"; + this.SMSControllerPaddleToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerPaddleToolStripMenuItem_Click); + // + // SMSControllerLightPhaserToolStripMenuItem + // + this.SMSControllerLightPhaserToolStripMenuItem.Name = "SMSControllerLightPhaserToolStripMenuItem"; + this.SMSControllerLightPhaserToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerLightPhaserToolStripMenuItem.Text = "Light Phaser"; + this.SMSControllerLightPhaserToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerLightPhaserToolStripMenuItem_Click); + // + // SMSControllerSportsPadToolStripMenuItem + // + this.SMSControllerSportsPadToolStripMenuItem.Name = "SMSControllerSportsPadToolStripMenuItem"; + this.SMSControllerSportsPadToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerSportsPadToolStripMenuItem.Text = "Sports Pad"; + this.SMSControllerSportsPadToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerSportsPadToolStripMenuItem_Click); + // + // SMSControllerKeyboardToolStripMenuItem + // + this.SMSControllerKeyboardToolStripMenuItem.Name = "SMSControllerKeyboardToolStripMenuItem"; + this.SMSControllerKeyboardToolStripMenuItem.Size = new System.Drawing.Size(139, 22); + this.SMSControllerKeyboardToolStripMenuItem.Text = "Keyboard"; + this.SMSControllerKeyboardToolStripMenuItem.Click += new System.EventHandler(this.SMSControllerKeyboardToolStripMenuItem_Click); + // // SMStoolStripMenuItem2 // this.SMStoolStripMenuItem2.Name = "SMStoolStripMenuItem2"; - this.SMStoolStripMenuItem2.Size = new System.Drawing.Size(274, 6); + this.SMStoolStripMenuItem2.Size = new System.Drawing.Size(275, 6); // // SMSenableBIOSToolStripMenuItem // this.SMSenableBIOSToolStripMenuItem.Name = "SMSenableBIOSToolStripMenuItem"; - this.SMSenableBIOSToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSenableBIOSToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSenableBIOSToolStripMenuItem.Text = "Enable BIOS (Must be Enabled for TAS)"; this.SMSenableBIOSToolStripMenuItem.Click += new System.EventHandler(this.SmsBiosMenuItem_Click); // // SMSEnableFMChipMenuItem // this.SMSEnableFMChipMenuItem.Name = "SMSEnableFMChipMenuItem"; - this.SMSEnableFMChipMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSEnableFMChipMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSEnableFMChipMenuItem.Text = "&Enable FM Chip"; this.SMSEnableFMChipMenuItem.Click += new System.EventHandler(this.SmsEnableFmChipMenuItem_Click); // // SMSOverclockMenuItem // this.SMSOverclockMenuItem.Name = "SMSOverclockMenuItem"; - this.SMSOverclockMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSOverclockMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSOverclockMenuItem.Text = "&Overclock when Known Safe"; this.SMSOverclockMenuItem.Click += new System.EventHandler(this.SMSOverclockMenuItem_Click); // // SMSForceStereoMenuItem // this.SMSForceStereoMenuItem.Name = "SMSForceStereoMenuItem"; - this.SMSForceStereoMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSForceStereoMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSForceStereoMenuItem.Text = "&Force Stereo Separation"; this.SMSForceStereoMenuItem.Click += new System.EventHandler(this.SMSForceStereoMenuItem_Click); // // SMSSpriteLimitMenuItem // this.SMSSpriteLimitMenuItem.Name = "SMSSpriteLimitMenuItem"; - this.SMSSpriteLimitMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSSpriteLimitMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSSpriteLimitMenuItem.Text = "Sprite &Limit"; this.SMSSpriteLimitMenuItem.Click += new System.EventHandler(this.SMSSpriteLimitMenuItem_Click); // // SMSDisplayOverscanMenuItem // this.SMSDisplayOverscanMenuItem.Name = "SMSDisplayOverscanMenuItem"; - this.SMSDisplayOverscanMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSDisplayOverscanMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSDisplayOverscanMenuItem.Text = "Display Overscan"; this.SMSDisplayOverscanMenuItem.Click += new System.EventHandler(this.SMSDisplayOverscanMenuItem_Click); // // SMSFix3DGameDisplayToolStripMenuItem // this.SMSFix3DGameDisplayToolStripMenuItem.Name = "SMSFix3DGameDisplayToolStripMenuItem"; - this.SMSFix3DGameDisplayToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSFix3DGameDisplayToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSFix3DGameDisplayToolStripMenuItem.Text = "Fix 3D Game Display"; this.SMSFix3DGameDisplayToolStripMenuItem.Click += new System.EventHandler(this.SMSFix3DDisplayMenuItem_Click); // // ShowClippedRegionsMenuItem // this.ShowClippedRegionsMenuItem.Name = "ShowClippedRegionsMenuItem"; - this.ShowClippedRegionsMenuItem.Size = new System.Drawing.Size(277, 22); + this.ShowClippedRegionsMenuItem.Size = new System.Drawing.Size(278, 22); this.ShowClippedRegionsMenuItem.Text = "&Show Clipped Regions"; this.ShowClippedRegionsMenuItem.Click += new System.EventHandler(this.ShowClippedRegionsMenuItem_Click); // // HighlightActiveDisplayRegionMenuItem // this.HighlightActiveDisplayRegionMenuItem.Name = "HighlightActiveDisplayRegionMenuItem"; - this.HighlightActiveDisplayRegionMenuItem.Size = new System.Drawing.Size(277, 22); + this.HighlightActiveDisplayRegionMenuItem.Size = new System.Drawing.Size(278, 22); this.HighlightActiveDisplayRegionMenuItem.Text = "&Highlight Active Display Region"; this.HighlightActiveDisplayRegionMenuItem.Click += new System.EventHandler(this.HighlightActiveDisplayRegionMenuItem_Click); // // SMSGraphicsSettingsMenuItem // this.SMSGraphicsSettingsMenuItem.Name = "SMSGraphicsSettingsMenuItem"; - this.SMSGraphicsSettingsMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSGraphicsSettingsMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSGraphicsSettingsMenuItem.Text = "&Graphics Settings..."; this.SMSGraphicsSettingsMenuItem.Click += new System.EventHandler(this.SMSGraphicsSettingsMenuItem_Click); // // toolStripSeparator24 // this.toolStripSeparator24.Name = "toolStripSeparator24"; - this.toolStripSeparator24.Size = new System.Drawing.Size(274, 6); + this.toolStripSeparator24.Size = new System.Drawing.Size(275, 6); // // SMSVDPViewerToolStripMenuItem // this.SMSVDPViewerToolStripMenuItem.Name = "SMSVDPViewerToolStripMenuItem"; - this.SMSVDPViewerToolStripMenuItem.Size = new System.Drawing.Size(277, 22); + this.SMSVDPViewerToolStripMenuItem.Size = new System.Drawing.Size(278, 22); this.SMSVDPViewerToolStripMenuItem.Text = "&VDP Viewer"; this.SMSVDPViewerToolStripMenuItem.Click += new System.EventHandler(this.SmsVdpViewerMenuItem_Click); // // GGGameGenieMenuItem // this.GGGameGenieMenuItem.Name = "GGGameGenieMenuItem"; - this.GGGameGenieMenuItem.Size = new System.Drawing.Size(277, 22); + this.GGGameGenieMenuItem.Size = new System.Drawing.Size(278, 22); this.GGGameGenieMenuItem.Text = "&Game Genie Encoder/Decoder"; this.GGGameGenieMenuItem.Click += new System.EventHandler(this.GGGameGenieMenuItem_Click); // @@ -2805,7 +2816,7 @@ // this.A7800SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.A7800ControllerSettingsMenuItem, - this.A7800FilterSettingsMenuItem}); + this.A7800FilterSettingsMenuItem}); this.A7800SubMenu.Name = "A7800SubMenu"; this.A7800SubMenu.Size = new System.Drawing.Size(51, 19); this.A7800SubMenu.Text = "&A7800"; @@ -2814,26 +2825,26 @@ // A7800ControllerSettingsMenuItem // this.A7800ControllerSettingsMenuItem.Name = "A7800ControllerSettingsMenuItem"; - this.A7800ControllerSettingsMenuItem.Size = new System.Drawing.Size(125, 22); + this.A7800ControllerSettingsMenuItem.Size = new System.Drawing.Size(172, 22); this.A7800ControllerSettingsMenuItem.Text = "Controller Settings"; this.A7800ControllerSettingsMenuItem.Click += new System.EventHandler(this.A7800ControllerSettingsToolStripMenuItem_Click); - // - // A7800FilterSettingsMenuItem - // - this.A7800FilterSettingsMenuItem.Name = "A7800FilterSettingsMenuItem"; - this.A7800FilterSettingsMenuItem.Size = new System.Drawing.Size(125, 22); - this.A7800FilterSettingsMenuItem.Text = "Filter Settings"; - this.A7800FilterSettingsMenuItem.Click += new System.EventHandler(this.A7800FilterSettingsToolStripMenuItem_Click); - // - // GBSubMenu - // - this.GBSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + // + // A7800FilterSettingsMenuItem + // + this.A7800FilterSettingsMenuItem.Name = "A7800FilterSettingsMenuItem"; + this.A7800FilterSettingsMenuItem.Size = new System.Drawing.Size(172, 22); + this.A7800FilterSettingsMenuItem.Text = "Filter Settings"; + this.A7800FilterSettingsMenuItem.Click += new System.EventHandler(this.A7800FilterSettingsToolStripMenuItem_Click); + // + // GBSubMenu + // + this.GBSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.GBcoreSettingsToolStripMenuItem, this.LoadGBInSGBMenuItem, this.toolStripSeparator28, this.GBGPUViewerMenuItem, this.GBGameGenieMenuItem, - this.GBPrinterViewerMenuItem}); + this.GBPrinterViewerMenuItem}); this.GBSubMenu.Name = "GBSubMenu"; this.GBSubMenu.Size = new System.Drawing.Size(34, 19); this.GBSubMenu.Text = "&GB"; @@ -2871,17 +2882,17 @@ this.GBGameGenieMenuItem.Size = new System.Drawing.Size(233, 22); this.GBGameGenieMenuItem.Text = "&Game Genie Encoder/Decoder"; this.GBGameGenieMenuItem.Click += new System.EventHandler(this.GBGameGenieMenuItem_Click); - // - // GBPrinterViewerMenuItem - // - this.GBPrinterViewerMenuItem.Name = "GBPrinterViewerMenuItem"; - this.GBPrinterViewerMenuItem.Size = new System.Drawing.Size(233, 22); - this.GBPrinterViewerMenuItem.Text = "&Printer Viewer"; - this.GBPrinterViewerMenuItem.Click += new System.EventHandler(this.GBPrinterViewerMenuItem_Click); - // - // GBASubMenu - // - this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + // + // GBPrinterViewerMenuItem + // + this.GBPrinterViewerMenuItem.Name = "GBPrinterViewerMenuItem"; + this.GBPrinterViewerMenuItem.Size = new System.Drawing.Size(233, 22); + this.GBPrinterViewerMenuItem.Text = "&Printer Viewer"; + this.GBPrinterViewerMenuItem.Click += new System.EventHandler(this.GBPrinterViewerMenuItem_Click); + // + // GBASubMenu + // + this.GBASubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.GBACoreSelectionSubMenu, this.GBAcoresettingsToolStripMenuItem1, this.toolStripSeparator33, @@ -3036,7 +3047,7 @@ this.ColecoControllerSettingsMenuItem, this.toolStripSeparator35, this.ColecoSkipBiosMenuItem, - this.ColecoUseSGMMenuItem}); + this.ColecoUseSGMMenuItem}); this.ColecoSubMenu.Name = "ColecoSubMenu"; this.ColecoSubMenu.Size = new System.Drawing.Size(56, 19); this.ColecoSubMenu.Text = "&Coleco"; @@ -3061,17 +3072,17 @@ this.ColecoSkipBiosMenuItem.Size = new System.Drawing.Size(253, 22); this.ColecoSkipBiosMenuItem.Text = "&Skip BIOS intro (When Applicable)"; this.ColecoSkipBiosMenuItem.Click += new System.EventHandler(this.ColecoSkipBiosMenuItem_Click); - // - // ColecoUseSGMMenuItem - // - this.ColecoUseSGMMenuItem.Name = "ColecoUseSGMMenuItem"; - this.ColecoUseSGMMenuItem.Size = new System.Drawing.Size(253, 22); - this.ColecoUseSGMMenuItem.Text = "&Use the Super Game Module"; - this.ColecoUseSGMMenuItem.Click += new System.EventHandler(this.ColecoUseSGMMenuItem_Click); - // - // N64SubMenu - // - this.N64SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + // + // ColecoUseSGMMenuItem + // + this.ColecoUseSGMMenuItem.Name = "ColecoUseSGMMenuItem"; + this.ColecoUseSGMMenuItem.Size = new System.Drawing.Size(253, 22); + this.ColecoUseSGMMenuItem.Text = "&Use the Super Game Module"; + this.ColecoUseSGMMenuItem.Click += new System.EventHandler(this.ColecoUseSGMMenuItem_Click); + // + // N64SubMenu + // + this.N64SubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.N64PluginSettingsMenuItem, this.N64ControllerSettingsMenuItem, this.toolStripSeparator23, @@ -3390,6 +3401,89 @@ this.AboutMenuItem.Text = "&About"; this.AboutMenuItem.Click += new System.EventHandler(this.AboutMenuItem_Click); // + // zXSpectrumToolStripMenuItem + // + this.zXSpectrumToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ZXSpectrumCoreEmulationSettingsMenuItem, + this.ZXSpectrumControllerConfigurationMenuItem, + this.ZXSpectrumAudioSettingsMenuItem, + this.ZXSpectrumNonSyncSettingsMenuItem, + this.ZXSpectrumPokeMemoryMenuItem, + this.ZXSpectrumMediaMenuItem}); + this.zXSpectrumToolStripMenuItem.Name = "zXSpectrumToolStripMenuItem"; + this.zXSpectrumToolStripMenuItem.Size = new System.Drawing.Size(87, 19); + this.zXSpectrumToolStripMenuItem.Text = "ZX Spectrum"; + this.zXSpectrumToolStripMenuItem.DropDownOpened += new System.EventHandler(this.zXSpectrumToolStripMenuItem_DropDownOpened); + // + // ZXSpectrumCoreEmulationSettingsMenuItem + // + this.ZXSpectrumCoreEmulationSettingsMenuItem.Name = "ZXSpectrumCoreEmulationSettingsMenuItem"; + this.ZXSpectrumCoreEmulationSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumCoreEmulationSettingsMenuItem.Text = "Core Emulation Settings"; + this.ZXSpectrumCoreEmulationSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumCoreEmulationSettingsMenuItem_Click); + // + // ZXSpectrumControllerConfigurationMenuItem + // + this.ZXSpectrumControllerConfigurationMenuItem.Name = "ZXSpectrumControllerConfigurationMenuItem"; + this.ZXSpectrumControllerConfigurationMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumControllerConfigurationMenuItem.Text = "Joystick Configuration"; + this.ZXSpectrumControllerConfigurationMenuItem.Click += new System.EventHandler(this.ZXSpectrumControllerConfigurationMenuItem_Click); + // + // ZXSpectrumAudioSettingsMenuItem + // + this.ZXSpectrumAudioSettingsMenuItem.Name = "ZXSpectrumAudioSettingsMenuItem"; + this.ZXSpectrumAudioSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumAudioSettingsMenuItem.Text = "Audio Settings"; + this.ZXSpectrumAudioSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumAudioSettingsMenuItem_Click); + // + // ZXSpectrumNonSyncSettingsMenuItem + // + this.ZXSpectrumNonSyncSettingsMenuItem.Name = "ZXSpectrumNonSyncSettingsMenuItem"; + this.ZXSpectrumNonSyncSettingsMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumNonSyncSettingsMenuItem.Text = "Non-Sync Settings"; + this.ZXSpectrumNonSyncSettingsMenuItem.Click += new System.EventHandler(this.ZXSpectrumNonSyncSettingsMenuItem_Click); + // + // ZXSpectrumPokeMemoryMenuItem + // + this.ZXSpectrumPokeMemoryMenuItem.Name = "ZXSpectrumPokeMemoryMenuItem"; + this.ZXSpectrumPokeMemoryMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumPokeMemoryMenuItem.Text = "POKE Memory"; + this.ZXSpectrumPokeMemoryMenuItem.Click += new System.EventHandler(this.ZXSpectrumPokeMemoryMenuItem_Click); + // + // ZXSpectrumMediaMenuItem + // + this.ZXSpectrumMediaMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.ZXSpectrumTapesSubMenu, + this.ZXSpectrumDisksSubMenu}); + this.ZXSpectrumMediaMenuItem.Name = "ZXSpectrumMediaMenuItem"; + this.ZXSpectrumMediaMenuItem.Size = new System.Drawing.Size(201, 22); + this.ZXSpectrumMediaMenuItem.Text = "Media"; + this.ZXSpectrumMediaMenuItem.DropDownOpened += new System.EventHandler(this.ZXSpectrumMediaMenuItem_DropDownOpened); + // + // ZXSpectrumTapesSubMenu + // + this.ZXSpectrumTapesSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.zxt1ToolStripMenuItem}); + this.ZXSpectrumTapesSubMenu.Name = "ZXSpectrumTapesSubMenu"; + this.ZXSpectrumTapesSubMenu.Size = new System.Drawing.Size(152, 22); + this.ZXSpectrumTapesSubMenu.Text = "Tapes"; + this.ZXSpectrumTapesSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumTapesSubMenu_DropDownOpened); + // + // ZXSpectrumDisksSubMenu + // + this.ZXSpectrumDisksSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.zxt2ToolStripMenuItem}); + this.ZXSpectrumDisksSubMenu.Name = "ZXSpectrumDisksSubMenu"; + this.ZXSpectrumDisksSubMenu.Size = new System.Drawing.Size(152, 22); + this.ZXSpectrumDisksSubMenu.Text = "Disks"; + this.ZXSpectrumDisksSubMenu.DropDownOpened += new System.EventHandler(this.ZXSpectrumDisksSubMenu_DropDownOpened); + // + // Atari7800HawkCoreMenuItem + // + this.Atari7800HawkCoreMenuItem.Name = "Atari7800HawkCoreMenuItem"; + this.Atari7800HawkCoreMenuItem.Size = new System.Drawing.Size(153, 22); + this.Atari7800HawkCoreMenuItem.Text = "Atari7800Hawk"; + // // MainStatusBar // this.MainStatusBar.ClickThrough = true; @@ -4010,6 +4104,18 @@ this.timerMouseIdle.Interval = 2000; this.timerMouseIdle.Tick += new System.EventHandler(this.TimerMouseIdle_Tick); // + // zxt1ToolStripMenuItem + // + this.zxt1ToolStripMenuItem.Name = "zxt1ToolStripMenuItem"; + this.zxt1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.zxt1ToolStripMenuItem.Text = "zxt1"; + // + // zxt2ToolStripMenuItem + // + this.zxt2ToolStripMenuItem.Name = "zxt2ToolStripMenuItem"; + this.zxt2ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.zxt2ToolStripMenuItem.Text = "zxt2"; + // // MainForm // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; @@ -4474,5 +4580,16 @@ private System.Windows.Forms.ToolStripMenuItem SMSControllerLightPhaserToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem SMSControllerSportsPadToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem SMSControllerKeyboardToolStripMenuItem; - } + private System.Windows.Forms.ToolStripMenuItem zXSpectrumToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumControllerConfigurationMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumCoreEmulationSettingsMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumNonSyncSettingsMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumAudioSettingsMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumPokeMemoryMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumMediaMenuItem; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumTapesSubMenu; + private System.Windows.Forms.ToolStripMenuItem ZXSpectrumDisksSubMenu; + private System.Windows.Forms.ToolStripMenuItem zxt1ToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem zxt2ToolStripMenuItem; + } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index aafb6e0eaf..e9048cab55 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -25,6 +25,7 @@ using BizHawk.Emulation.Cores.Computers.AppleII; using BizHawk.Client.ApiHawk; using BizHawk.Emulation.Cores.Computers.Commodore64; using BizHawk.Emulation.Cores.Nintendo.Gameboy; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; namespace BizHawk.Client.EmuHawk { @@ -695,12 +696,12 @@ namespace BizHawk.Client.EmuHawk public void CloseEmulator() { - _exit = true; + _exitRequestPending = true; } public void CloseEmulator(int exitCode) { - _exit = true; + _exitRequestPending = true; _exitCode = exitCode; } @@ -2451,11 +2452,123 @@ namespace BizHawk.Client.EmuHawk new IntvControllerSettings().ShowDialog(); } - #endregion + #endregion - #region Help + #region ZXSpectrum - private void HelpSubMenu_DropDownOpened(object sender, EventArgs e) + private void zXSpectrumToolStripMenuItem_DropDownOpened(object sender, EventArgs e) + { + + } + + + private void preferencesToolStripMenuItem4_Click(object sender, EventArgs e) + { + GenericCoreConfig.DoDialog(this, "ZXSpectrum Settings"); + } + + + private void ZXSpectrumControllerConfigurationMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumJoystickSettings().ShowDialog(); + } + + private void ZXSpectrumCoreEmulationSettingsMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumCoreEmulationSettings().ShowDialog(); + } + + private void ZXSpectrumNonSyncSettingsMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumNonSyncSettings().ShowDialog(); + } + + private void ZXSpectrumAudioSettingsMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumAudioSettings().ShowDialog(); + } + + private void ZXSpectrumPokeMemoryMenuItem_Click(object sender, EventArgs e) + { + new ZXSpectrumPokeMemory().ShowDialog(); + } + + private void ZXSpectrumMediaMenuItem_DropDownOpened(object sender, EventArgs e) + { + if (Emulator is ZXSpectrum) + { + ZXSpectrumTapesSubMenu.Enabled = ((ZXSpectrum)Emulator)._tapeInfo.Count > 0; + ZXSpectrumDisksSubMenu.Enabled = ((ZXSpectrum)Emulator)._diskInfo.Count > 0; + } + } + + private void ZXSpectrumTapesSubMenu_DropDownOpened(object sender, EventArgs e) + { + ZXSpectrumTapesSubMenu.DropDownItems.Clear(); + + if (Emulator is ZXSpectrum) + { + var speccy = (ZXSpectrum)Emulator; + var currSel = speccy._machine.TapeMediaIndex; + + for (int i = 0; i < speccy._tapeInfo.Count; i++) + { + string name = speccy._tapeInfo[i].Name; + + var menuItem = new ToolStripMenuItem + { + Name = i + "_" + name, + Text = i + ": " + name, + Checked = currSel == i + }; + + int dummy = i; + menuItem.Click += (o, ev) => + { + speccy._machine.TapeMediaIndex = dummy; + }; + + ZXSpectrumTapesSubMenu.DropDownItems.Add(menuItem); + } + } + } + + private void ZXSpectrumDisksSubMenu_DropDownOpened(object sender, EventArgs e) + { + ZXSpectrumDisksSubMenu.DropDownItems.Clear(); + + if (Emulator is ZXSpectrum) + { + var speccy = (ZXSpectrum)Emulator; + var currSel = speccy._machine.DiskMediaIndex; + + for (int i = 0; i < speccy._diskInfo.Count; i++) + { + string name = speccy._diskInfo[i].Name; + + var menuItem = new ToolStripMenuItem + { + Name = i + "_" + name, + Text = i + ": " + name, + Checked = currSel == i + }; + + int dummy = i; + menuItem.Click += (o, ev) => + { + speccy._machine.DiskMediaIndex = dummy; + }; + + ZXSpectrumDisksSubMenu.DropDownItems.Add(menuItem); + } + } + } + + #endregion + + #region Help + + private void HelpSubMenu_DropDownOpened(object sender, EventArgs e) { FeaturesMenuItem.Visible = VersionInfo.DeveloperBuild; } @@ -2911,7 +3024,7 @@ namespace BizHawk.Client.EmuHawk protected override void OnClosed(EventArgs e) { - _exit = true; + _windowClosedAndSafeToExitProcess = true; base.OnClosed(e); } diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index d653e06691..6c7bcfb5e4 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -493,7 +493,7 @@ namespace BizHawk.Client.EmuHawk Close(); } - if (_exit) + if (_windowClosedAndSafeToExitProcess) { break; } @@ -1370,7 +1370,7 @@ namespace BizHawk.Client.EmuHawk private int _avwriterResizeh; private bool _avwriterpad; - private bool _exit; + private bool _windowClosedAndSafeToExitProcess; private int _exitCode; private bool _exitRequestPending; private bool _runloopFrameProgress; @@ -1559,6 +1559,11 @@ namespace BizHawk.Client.EmuHawk if (!string.IsNullOrEmpty(Emulator.CoreComm.RomStatusAnnotation)) { annotation = Emulator.CoreComm.RomStatusAnnotation; + + if (annotation == "Multi-disk bundler") + { + DumpStatusButton.Image = Properties.Resources.RetroQuestion; + } } DumpStatusButton.ToolTipText = annotation; @@ -1719,6 +1724,7 @@ namespace BizHawk.Client.EmuHawk sNESToolStripMenuItem.Visible = false; neoGeoPocketToolStripMenuItem.Visible = false; pCFXToolStripMenuItem.Visible = false; + zXSpectrumToolStripMenuItem.Visible = false; switch (system) { @@ -1816,6 +1822,9 @@ namespace BizHawk.Client.EmuHawk case "PCFX": pCFXToolStripMenuItem.Visible = true; break; + case "ZXSpectrum": + zXSpectrumToolStripMenuItem.Visible = true; + break; } } @@ -2077,7 +2086,7 @@ namespace BizHawk.Client.EmuHawk if (VersionInfo.DeveloperBuild) { return FormatFilter( - "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;%ARCH%", + "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.pce;*.sgx;*.bin;*.smd;*.rom;*.a26;*.a78;*.lnx;*.m3u;*.cue;*.ccd;*.mds;*.exe;*.gb;*.gbc;*.gba;*.gen;*.md;*.32x;*.col;*.int;*.smc;*.sfc;*.prg;*.d64;*.g64;*.crt;*.tap;*.sgb;*.xml;*.z64;*.v64;*.n64;*.ws;*.wsc;*.dsk;*.do;*.po;*.vb;*.ngp;*.ngc;*.psf;*.minipsf;*.nsf;*.tzx;*.pzx;*.csw;*.wav;%ARCH%", "Music Files", "*.psf;*.minipsf;*.sid;*.nsf", "Disc Images", "*.cue;*.ccd;*.mds;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", @@ -2105,11 +2114,12 @@ namespace BizHawk.Client.EmuHawk "Apple II", "*.dsk;*.do;*.po;%ARCH%", "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", + "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;*.csw;*.wav;%ARCH%", "All Files", "*.*"); } return FormatFilter( - "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.32x;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.int;*.rom;*.m3u;*.cue;*.ccd;*.mds;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;*.dsk;*.do;*.po;*.psf;*.ngp;*.ngc;*.prg;*.d64;*.g64;*.minipsf;*.nsf;%ARCH%", + "Rom Files", "*.nes;*.fds;*.unf;*.sms;*.gg;*.sg;*.gb;*.gbc;*.gba;*.pce;*.sgx;*.bin;*.smd;*.gen;*.md;*.32x;*.smc;*.sfc;*.a26;*.a78;*.lnx;*.col;*.int;*.rom;*.m3u;*.cue;*.ccd;*.mds;*.sgb;*.z64;*.v64;*.n64;*.ws;*.wsc;*.xml;*.dsk;*.do;*.po;*.psf;*.ngp;*.ngc;*.prg;*.d64;*.g64;*.minipsf;*.nsf;*.tzx;*.pzx;*.csw;*.wav;%ARCH%", "Disc Images", "*.cue;*.ccd;*.mds;*.m3u", "NES", "*.nes;*.fds;*.unf;*.nsf;%ARCH%", "Super NES", "*.smc;*.sfc;*.xml;%ARCH%", @@ -2134,7 +2144,8 @@ namespace BizHawk.Client.EmuHawk "Virtual Boy", "*.vb;%ARCH%", "Neo Geo Pocket", "*.ngp;*.ngc;%ARCH%", "Commodore 64", "*.prg; *.d64, *.g64; *.crt; *.tap;%ARCH%", - "All Files", "*.*"); + "Sinclair ZX Spectrum", "*.tzx;*.tap;*.dsk;*.pzx;*.csw;*.wav;%ARCH%", + "All Files", "*.*"); } } @@ -2747,8 +2758,16 @@ namespace BizHawk.Client.EmuHawk var attributes = Emulator.Attributes(); CoreNameStatusBarButton.Text = Emulator.DisplayName(); - CoreNameStatusBarButton.Image = Emulator.Icon(); - CoreNameStatusBarButton.ToolTipText = attributes.Ported ? "(ported) " : ""; + CoreNameStatusBarButton.Image = Emulator.Icon(); + CoreNameStatusBarButton.ToolTipText = attributes.Ported ? "(ported) " : ""; + + + if (Emulator.SystemId == "ZXSpectrum") + { + var core = (Emulation.Cores.Computers.SinclairSpectrum.ZXSpectrum)Emulator as Emulation.Cores.Computers.SinclairSpectrum.ZXSpectrum; + CoreNameStatusBarButton.ToolTipText = core.GetMachineType(); + + } } private void ToggleKeyPriority() @@ -3155,6 +3174,12 @@ namespace BizHawk.Client.EmuHawk aw = new AudioStretcher(aw); } + if (unattended && Global.Config.TargetZoomFactor > 1) + { + _avwriterResizew = Global.Config.TargetZoomFactor * _currentVideoProvider.BufferWidth; + _avwriterResizeh = Global.Config.TargetZoomFactor * _currentVideoProvider.BufferHeight; + } + aw.SetMovieParameters(Emulator.VsyncNumerator(), Emulator.VsyncDenominator()); if (_avwriterResizew > 0 && _avwriterResizeh > 0) { @@ -3422,7 +3447,7 @@ namespace BizHawk.Client.EmuHawk StopAv(); if (argParse._autoCloseOnDump) { - _exit = true; + _exitRequestPending = true; } } } @@ -3615,6 +3640,38 @@ namespace BizHawk.Client.EmuHawk CoreFileProvider.SyncCoreCommInputSignals(nextComm); InputManager.SyncControls(); + if (Path.GetExtension(loaderName.Replace("*OpenRom*", "").Replace("|", "")).ToLower() == ".xml") + { + // this is a multi-disk bundler file + // determine the xml assets and create RomStatusDetails for all of them + var xmlGame = XmlGame.Create(new HawkFile(loaderName.Replace("*OpenRom*", ""))); + + StringWriter xSw = new StringWriter(); + + for (int xg = 0; xg < xmlGame.Assets.Count; xg++) + { + var ext = Path.GetExtension(xmlGame.AssetFullPaths[xg]).ToLower(); + + if (ext == ".cue" || ext == ".ccd" || ext == ".toc" || ext == ".mds") + { + xSw.WriteLine(Path.GetFileNameWithoutExtension(xmlGame.Assets[xg].Key)); + xSw.WriteLine("SHA1:N/A"); + xSw.WriteLine("MD5:N/A"); + xSw.WriteLine(); + } + else + { + xSw.WriteLine(xmlGame.Assets[xg].Key); + xSw.WriteLine("SHA1:" + xmlGame.Assets[xg].Value.HashSHA1()); + xSw.WriteLine("MD5:" + xmlGame.Assets[xg].Value.HashMD5()); + xSw.WriteLine(); + } + } + + Emulator.CoreComm.RomStatusDetails = xSw.ToString(); + Emulator.CoreComm.RomStatusAnnotation = "Multi-disk bundler"; + } + if (Emulator is TI83 && Global.Config.TI83autoloadKeyPad) { GlobalWin.Tools.Load(); @@ -3644,10 +3701,15 @@ namespace BizHawk.Client.EmuHawk } } - if (Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null) - { - Emulator.CoreComm.RomStatusDetails = $"{loader.Game.Name}\r\nSHA1:{loader.Rom.RomData.HashSHA1()}\r\nMD5:{loader.Rom.RomData.HashMD5()}\r\n"; - } + if (Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null) + { + Emulator.CoreComm.RomStatusDetails = $"{loader.Game.Name}\r\nSHA1:{loader.Rom.RomData.HashSHA1()}\r\nMD5:{loader.Rom.RomData.HashMD5()}\r\n"; + } + else if (Emulator.CoreComm.RomStatusDetails == null && loader.Rom == null) + { + // single disc game + Emulator.CoreComm.RomStatusDetails = $"{loader.Game.Name}\r\nSHA1:N/A\r\nMD5:N/A\r\n"; + } if (Emulator.HasBoardInfo()) { @@ -4293,8 +4355,9 @@ namespace BizHawk.Client.EmuHawk { GenericCoreConfig.DoDialog(this, "PC-FX Settings"); } + - private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording) + private bool Rewind(ref bool runFrame, long currentTimestamp, out bool returnToRecording) { var isRewinding = false; diff --git a/BizHawk.Client.EmuHawk/Program.cs b/BizHawk.Client.EmuHawk/Program.cs index a6a5e5a538..47d97f0f1b 100644 --- a/BizHawk.Client.EmuHawk/Program.cs +++ b/BizHawk.Client.EmuHawk/Program.cs @@ -121,8 +121,19 @@ namespace BizHawk.Client.EmuHawk HawkFile.ArchiveHandlerFactory = new SevenZipSharpArchiveHandler(); string iniPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "config.ini"); - Global.Config = ConfigService.Load(iniPath); + + try + { + Global.Config = ConfigService.Load(iniPath); + } catch (Exception e) { + new ExceptionBox(e).ShowDialog(); + new ExceptionBox("Since your config file is corrupted, we're going to recreate it. Back it up before proceeding if you want to investigate further.").ShowDialog(); + File.Delete(iniPath); + Global.Config = ConfigService.Load(iniPath); + } + Global.Config.ResolveDefaults(); + BizHawk.Client.Common.StringLogUtil.DefaultToDisk = Global.Config.MoviesOnDisk; BizHawk.Client.Common.StringLogUtil.DefaultToAWE = Global.Config.MoviesInAWE; diff --git a/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs b/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs index 72c965441f..7504818e28 100644 --- a/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs +++ b/BizHawk.Client.EmuHawk/Properties/Resources.Designer.cs @@ -489,7 +489,7 @@ namespace BizHawk.Client.EmuHawk.Properties { return ((System.Drawing.Bitmap)(obj)); } } - + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -3438,5 +3438,15 @@ namespace BizHawk.Client.EmuHawk.Properties { return ((System.Drawing.Bitmap)(obj)); } } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ZXSpectrumKeyboards { + get { + object obj = ResourceManager.GetObject("ZXSpectrumKeyboards", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } } } diff --git a/BizHawk.Client.EmuHawk/Properties/Resources.resx b/BizHawk.Client.EmuHawk/Properties/Resources.resx index b77dae3d68..7e21e9ffc9 100644 --- a/BizHawk.Client.EmuHawk/Properties/Resources.resx +++ b/BizHawk.Client.EmuHawk/Properties/Resources.resx @@ -1557,4 +1557,7 @@ ..\images\ControllerImages\NGPController.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\config\ControllerImages\ZXSpectrumKeyboards.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + diff --git a/BizHawk.Client.EmuHawk/Resources/ZXSpectrumKeyboard.bmp b/BizHawk.Client.EmuHawk/Resources/ZXSpectrumKeyboard.bmp new file mode 100644 index 0000000000..55516c7aaa Binary files /dev/null and b/BizHawk.Client.EmuHawk/Resources/ZXSpectrumKeyboard.bmp differ diff --git a/BizHawk.Client.EmuHawk/Sound/Sound.cs b/BizHawk.Client.EmuHawk/Sound/Sound.cs index 84362490e7..88a94e44c9 100644 --- a/BizHawk.Client.EmuHawk/Sound/Sound.cs +++ b/BizHawk.Client.EmuHawk/Sound/Sound.cs @@ -15,6 +15,8 @@ namespace BizHawk.Client.EmuHawk public const int BlockAlign = BytesPerSample * ChannelCount; private bool _disposed; + private bool _unjamSoundThrottle; + private readonly ISoundOutput _outputDevice; private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(); // Buffer for Sync sources private readonly BufferedAsync _bufferedAsync = new BufferedAsync(); // Buffer for Async sources @@ -117,6 +119,7 @@ namespace BizHawk.Client.EmuHawk int silenceSamples = Math.Max(samplesNeeded - samplesPerFrame, 0); _outputDevice.WriteSamples(new short[silenceSamples * 2], silenceSamples); samplesNeeded -= silenceSamples; + _unjamSoundThrottle = isUnderrun; if (isUnderrun) { @@ -163,7 +166,14 @@ namespace BizHawk.Client.EmuHawk { Thread.Sleep((samplesProvided - samplesNeeded) / (SampleRate / 1000)); // Let the audio clock control sleep time samplesNeeded = _outputDevice.CalculateSamplesNeeded(); + if (_unjamSoundThrottle) + { + //may be garbage, but what can we do? + samplesProvided = samplesNeeded; + break; + } } + _unjamSoundThrottle = false; } else { diff --git a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs index ab488b1724..f17a7e1afe 100644 --- a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs +++ b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs @@ -172,15 +172,24 @@ namespace BizHawk.Client.EmuHawk string tabname = cat.Key; tt.TabPages.Add(tabname); tt.TabPages[pageidx].Controls.Add(createpanel(settings, cat.Value, tt.Size)); - } + + // zxhawk hack - it uses multiple categoryLabels + if (Global.Emulator.SystemId == "ZXSpectrum") + pageidx++; + + } if (buckets[0].Count > 0) { - string tabname = Global.Emulator.SystemId == "C64" ? "Keyboard" : "Console"; // hack - tt.TabPages.Add(tabname); - tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[0], tt.Size)); - } - } + // ZXHawk needs to skip this bit + if (Global.Emulator.SystemId == "ZXSpectrum") + return; + + string tabname = (Global.Emulator.SystemId == "C64") ? "Keyboard" : "Console"; // hack + tt.TabPages.Add(tabname); + tt.TabPages[pageidx].Controls.Add(createpanel(settings, buckets[0], tt.Size)); + } + } } public ControllerConfig(ControllerDefinition def) @@ -256,6 +265,13 @@ namespace BizHawk.Client.EmuHawk pictureBox2.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom; } + + if (controlName == "ZXSpectrum Controller") + { + pictureBox1.Image = Properties.Resources.ZXSpectrumKeyboards; + pictureBox1.Size = Properties.Resources.ZXSpectrumKeyboards.Size; + tableLayoutPanel1.ColumnStyles[1].Width = Properties.Resources.ZXSpectrumKeyboards.Width; + } } // lazy methods, but they're not called often and actually diff --git a/BizHawk.Client.EmuHawk/config/ControllerImages/ZXSpectrumKeyboards.png b/BizHawk.Client.EmuHawk/config/ControllerImages/ZXSpectrumKeyboards.png new file mode 100644 index 0000000000..1af85ebea9 Binary files /dev/null and b/BizHawk.Client.EmuHawk/config/ControllerImages/ZXSpectrumKeyboards.png differ diff --git a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs index 5b2e5eca92..7b016a8cab 100644 --- a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs +++ b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs @@ -52,6 +52,7 @@ namespace BizHawk.Client.EmuHawk { "GBC", "Game Boy Color" }, { "PCFX", "PC-FX" }, { "32X", "32X" }, + { "ZXSpectrum", "ZX Spectrum" } }; public string TargetSystem = null; diff --git a/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs b/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs index 94c24420ba..ba9a874de5 100644 --- a/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/GB/ColorChooserForm.Designer.cs @@ -194,7 +194,7 @@ // this.OK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.OK.DialogResult = System.Windows.Forms.DialogResult.OK; - this.OK.Location = new System.Drawing.Point(143, 221); + this.OK.Location = new System.Drawing.Point(183, 221); this.OK.Name = "OK"; this.OK.Size = new System.Drawing.Size(75, 23); this.OK.TabIndex = 22; @@ -206,7 +206,7 @@ // this.Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.Cancel.Location = new System.Drawing.Point(224, 221); + this.Cancel.Location = new System.Drawing.Point(264, 221); this.Cancel.Name = "Cancel"; this.Cancel.Size = new System.Drawing.Size(75, 23); this.Cancel.TabIndex = 23; @@ -267,19 +267,19 @@ // this.DefaultButton.Location = new System.Drawing.Point(149, 137); this.DefaultButton.Name = "DefaultButton"; - this.DefaultButton.Size = new System.Drawing.Size(60, 23); + this.DefaultButton.Size = new System.Drawing.Size(90, 23); this.DefaultButton.TabIndex = 32; - this.DefaultButton.Text = "&Default"; + this.DefaultButton.Text = "&Default Green"; this.DefaultButton.UseVisualStyleBackColor = true; this.DefaultButton.Click += new System.EventHandler(this.DefaultButton_Click); // // DefaultButtonCGB // - this.DefaultButtonCGB.Location = new System.Drawing.Point(215, 137); + this.DefaultButtonCGB.Location = new System.Drawing.Point(245, 137); this.DefaultButtonCGB.Name = "DefaultButtonCGB"; - this.DefaultButtonCGB.Size = new System.Drawing.Size(75, 23); + this.DefaultButtonCGB.Size = new System.Drawing.Size(94, 23); this.DefaultButtonCGB.TabIndex = 33; - this.DefaultButtonCGB.Text = "Default &CGB"; + this.DefaultButtonCGB.Text = "Default &Grey"; this.DefaultButtonCGB.UseVisualStyleBackColor = true; this.DefaultButtonCGB.Click += new System.EventHandler(this.DefaultButtonCGB_Click); // @@ -290,7 +290,7 @@ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.CancelButton = this.Cancel; - this.ClientSize = new System.Drawing.Size(313, 254); + this.ClientSize = new System.Drawing.Size(353, 254); this.Controls.Add(this.DefaultButtonCGB); this.Controls.Add(this.DefaultButton); this.Controls.Add(this.buttonSave); diff --git a/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs b/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs index ebc228d4f9..4dee0b94c6 100644 --- a/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs +++ b/BizHawk.Client.EmuHawk/config/N64/N64VideoPluginconfig.Designer.cs @@ -28,514 +28,514 @@ /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(N64VideoPluginconfig)); - this.N64plugintabcontrol = new System.Windows.Forms.TabControl(); - this.N64vpluginglobaltab = new System.Windows.Forms.TabPage(); - this.VideoResolutionYTextBox = new System.Windows.Forms.TextBox(); - this.LabelVideoResolutionY = new System.Windows.Forms.Label(); - this.VideoResolutionXTextBox = new System.Windows.Forms.TextBox(); - this.LabelVideoResolutionX = new System.Windows.Forms.Label(); - this.label49 = new System.Windows.Forms.Label(); - this.RspTypeDropdown = new System.Windows.Forms.ComboBox(); - this.label48 = new System.Windows.Forms.Label(); - this.CoreTypeDropdown = new System.Windows.Forms.ComboBox(); - this.label47 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.VideoResolutionComboBox = new System.Windows.Forms.ComboBox(); - this.PluginComboBox = new System.Windows.Forms.ComboBox(); - this.label1 = new System.Windows.Forms.Label(); - this.GLideN64Tab = new System.Windows.Forms.TabPage(); - this.tabControl3 = new System.Windows.Forms.TabControl(); - this.tabPage5 = new System.Windows.Forms.TabPage(); - this.label88 = new System.Windows.Forms.Label(); - this.GLideN64_GammaCorrectionLevel = new System.Windows.Forms.TextBox(); - this.GLideN64_ForceGammaCorrection = new System.Windows.Forms.CheckBox(); - this.label81 = new System.Windows.Forms.Label(); - this.GLideN64_MultiSampling = new System.Windows.Forms.ComboBox(); - this.label80 = new System.Windows.Forms.Label(); - this.GLideN64_blurStrength = new System.Windows.Forms.ComboBox(); - this.label79 = new System.Windows.Forms.Label(); - this.GLideN64_blurAmount = new System.Windows.Forms.ComboBox(); - this.label78 = new System.Windows.Forms.Label(); - this.GLideN64_bloomBlendMode = new System.Windows.Forms.ComboBox(); - this.label77 = new System.Windows.Forms.Label(); - this.GLideN64_bloomThresholdLevel = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableBloom = new System.Windows.Forms.CheckBox(); - this.GLideN64_FragmentDepthWrite = new System.Windows.Forms.CheckBox(); - this.GLideN64_LegacyBlending = new System.Windows.Forms.CheckBox(); - this.GLideN64_NativeResTexrects = new System.Windows.Forms.CheckBox(); - this.label82 = new System.Windows.Forms.Label(); - this.GLideN64_CorrectTexrectCoords = new System.Windows.Forms.ComboBox(); - this.GLideN64_ShadersStorage = new System.Windows.Forms.CheckBox(); - this.GLideN64_HWLighting = new System.Windows.Forms.CheckBox(); - this.GLideN64_EnableNoise = new System.Windows.Forms.CheckBox(); - this.GLideN64_EnableLOD = new System.Windows.Forms.CheckBox(); - this.tabPage6 = new System.Windows.Forms.TabPage(); - this.label87 = new System.Windows.Forms.Label(); - this.GLideN64_txPath = new System.Windows.Forms.TextBox(); - this.GLideN64_txSaveCache = new System.Windows.Forms.CheckBox(); - this.GLideN64_txCacheCompression = new System.Windows.Forms.CheckBox(); - this.GLideN64_txForce16bpp = new System.Windows.Forms.CheckBox(); - this.GLideN64_txDump = new System.Windows.Forms.CheckBox(); - this.GLideN64_txHresAltCRC = new System.Windows.Forms.CheckBox(); - this.GLideN64_txHiresFullAlphaChannel = new System.Windows.Forms.CheckBox(); - this.GLideN64_txHiresEnable = new System.Windows.Forms.CheckBox(); - this.label86 = new System.Windows.Forms.Label(); - this.GLideN64_txCacheSize = new System.Windows.Forms.TextBox(); - this.GLideN64_txFilterIgnoreBG = new System.Windows.Forms.CheckBox(); - this.GLideN64_txDeposterize = new System.Windows.Forms.CheckBox(); - this.label85 = new System.Windows.Forms.Label(); - this.GLideN64_txEnhancementMode = new System.Windows.Forms.ComboBox(); - this.label84 = new System.Windows.Forms.Label(); - this.GLideN64_txFilterMode = new System.Windows.Forms.ComboBox(); - this.label83 = new System.Windows.Forms.Label(); - this.GLideN64_CacheSize = new System.Windows.Forms.TextBox(); - this.label72 = new System.Windows.Forms.Label(); - this.GLideN64_bilinearMode = new System.Windows.Forms.ComboBox(); - this.GLideN64_MaxAnisotropy = new System.Windows.Forms.CheckBox(); - this.tabPage7 = new System.Windows.Forms.TabPage(); - this.label89 = new System.Windows.Forms.Label(); - this.GLideN64_UseNativeResolutionFactor = new System.Windows.Forms.TextBox(); - this.GLideN64_DisableFBInfo = new System.Windows.Forms.CheckBox(); - this.GLideN64_FBInfoReadDepthChunk = new System.Windows.Forms.CheckBox(); - this.GLideN64_FBInfoReadColorChunk = new System.Windows.Forms.CheckBox(); - this.label76 = new System.Windows.Forms.Label(); - this.GLideN64_BufferSwapMode = new System.Windows.Forms.ComboBox(); - this.label75 = new System.Windows.Forms.Label(); - this.GLideN64_AspectRatio = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableN64DepthCompare = new System.Windows.Forms.CheckBox(); - this.label74 = new System.Windows.Forms.Label(); - this.GLideN64_EnableCopyColorToRDRAM = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableCopyAuxiliaryToRDRAM = new System.Windows.Forms.CheckBox(); - this.GLideN64_EnableCopyColorFromRDRAM = new System.Windows.Forms.CheckBox(); - this.label73 = new System.Windows.Forms.Label(); - this.GLideN64_EnableCopyDepthToRDRAM = new System.Windows.Forms.ComboBox(); - this.GLideN64_EnableFBEmulation = new System.Windows.Forms.CheckBox(); - this.GLideN64_UseDefaultHacks = new System.Windows.Forms.CheckBox(); - this.Glide64mk2TabPage = new System.Windows.Forms.TabPage(); - this.tabControl2 = new System.Windows.Forms.TabControl(); - this.tabPage1 = new System.Windows.Forms.TabPage(); - this.Glide64mk2_fb_get_info = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_render = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_wrpAnisotropic = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_wrpFBO = new System.Windows.Forms.CheckBox(); - this.label50 = new System.Windows.Forms.Label(); - this.Glide64mk2_card_id = new System.Windows.Forms.ComboBox(); - this.tabPage2 = new System.Windows.Forms.TabPage(); - this.Glide64mk2_fb_read_always = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_useless_is_useless = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_n64_z_scale = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_old_style_adither = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_zmode_compare_less = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_adjust_aspect = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fast_crc = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_clip_zmax = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_clip_zmin = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_force_quad3d = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_pal230 = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_texture_correction = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_correct_viewport = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_force_calc_sphere = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_use_sts1_only = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_optimize_texrect = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_increase_texrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_ignore_aux_copy = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_hires_buf_clear = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_force_microcheck = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fog = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_smart = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_read_alpha = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_fb_hires = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_detect_cpu_write = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_buff_clear = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_alt_tex_size = new System.Windows.Forms.CheckBox(); - this.Glide64mk2_UseDefaultHacks1 = new System.Windows.Forms.CheckBox(); - this.tabPage3 = new System.Windows.Forms.TabPage(); - this.label46 = new System.Windows.Forms.Label(); - this.Glide64mk2_read_back_to_screen = new System.Windows.Forms.ComboBox(); - this.Glide64mk2_aspectmode = new System.Windows.Forms.ComboBox(); - this.Glide64mk2_fb_crc_mode = new System.Windows.Forms.ComboBox(); - this.label45 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.label52 = new System.Windows.Forms.Label(); - this.Glide64mk2_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); - this.label53 = new System.Windows.Forms.Label(); - this.Glide64mk2_swapmode = new System.Windows.Forms.ComboBox(); - this.label54 = new System.Windows.Forms.Label(); - this.Glide64mk2_stipple_pattern = new System.Windows.Forms.TextBox(); - this.label55 = new System.Windows.Forms.Label(); - this.Glide64mk2_stipple_mode = new System.Windows.Forms.TextBox(); - this.label56 = new System.Windows.Forms.Label(); - this.Glide64mk2_lodmode = new System.Windows.Forms.ComboBox(); - this.label58 = new System.Windows.Forms.Label(); - this.Glide64mk2_filtering = new System.Windows.Forms.ComboBox(); - this.Glide64mk2_UseDefaultHacks2 = new System.Windows.Forms.CheckBox(); - this.Glide64TabPage = new System.Windows.Forms.TabPage(); - this.tabControl1 = new System.Windows.Forms.TabControl(); - this.Glide64General = new System.Windows.Forms.TabPage(); - this.label39 = new System.Windows.Forms.Label(); - this.Glide_scale_y = new System.Windows.Forms.TextBox(); - this.label40 = new System.Windows.Forms.Label(); - this.Glide_scale_x = new System.Windows.Forms.TextBox(); - this.label38 = new System.Windows.Forms.Label(); - this.Glide_offset_y = new System.Windows.Forms.TextBox(); - this.label37 = new System.Windows.Forms.Label(); - this.Glide_offset_x = new System.Windows.Forms.TextBox(); - this.Glide_fb_get_info = new System.Windows.Forms.CheckBox(); - this.Glide_disable_auxbuf = new System.Windows.Forms.CheckBox(); - this.Glide_fbo = new System.Windows.Forms.CheckBox(); - this.Glide_noglsl = new System.Windows.Forms.CheckBox(); - this.Glide_noditheredalpha = new System.Windows.Forms.CheckBox(); - this.label32 = new System.Windows.Forms.Label(); - this.Glide_tex_filter = new System.Windows.Forms.ComboBox(); - this.Glide_fb_render = new System.Windows.Forms.CheckBox(); - this.Glide_motionblur = new System.Windows.Forms.CheckBox(); - this.Glide_fb_read_always = new System.Windows.Forms.CheckBox(); - this.Glide_unk_as_red = new System.Windows.Forms.CheckBox(); - this.Glide_filter_cache = new System.Windows.Forms.CheckBox(); - this.Glide_fast_crc = new System.Windows.Forms.CheckBox(); - this.label31 = new System.Windows.Forms.Label(); - this.Glide_wfmode = new System.Windows.Forms.ComboBox(); - this.Glide_wireframe = new System.Windows.Forms.CheckBox(); - this.label30 = new System.Windows.Forms.Label(); - this.Glide_card_id = new System.Windows.Forms.ComboBox(); - this.Glide_flame_corona = new System.Windows.Forms.CheckBox(); - this.label29 = new System.Windows.Forms.Label(); - this.Glide_ucode = new System.Windows.Forms.ComboBox(); - this.Glide_autodetect_ucode = new System.Windows.Forms.CheckBox(); - this.GlidePerGameHacks1 = new System.Windows.Forms.TabPage(); - this.Glide_wrap_big_tex = new System.Windows.Forms.CheckBox(); - this.Glide_use_sts1_only = new System.Windows.Forms.CheckBox(); - this.Glide_soft_depth_compare = new System.Windows.Forms.CheckBox(); - this.Glide_PPL = new System.Windows.Forms.CheckBox(); - this.Glide_fb_optimize_write = new System.Windows.Forms.CheckBox(); - this.Glide_fb_optimize_texrect = new System.Windows.Forms.CheckBox(); - this.Glide_increase_texrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide_increase_primdepth = new System.Windows.Forms.CheckBox(); - this.Glide_fb_ignore_previous = new System.Windows.Forms.CheckBox(); - this.Glide_fb_ignore_aux_copy = new System.Windows.Forms.CheckBox(); - this.Glide_fb_hires_buf_clear = new System.Windows.Forms.CheckBox(); - this.Glide_force_microcheck = new System.Windows.Forms.CheckBox(); - this.Glide_force_depth_compare = new System.Windows.Forms.CheckBox(); - this.Glide_fog = new System.Windows.Forms.CheckBox(); - this.Glide_fillcolor_fix = new System.Windows.Forms.CheckBox(); - this.Glide_fb_smart = new System.Windows.Forms.CheckBox(); - this.Glide_fb_read_alpha = new System.Windows.Forms.CheckBox(); - this.Glide_fb_hires = new System.Windows.Forms.CheckBox(); - this.Glide_fb_clear = new System.Windows.Forms.CheckBox(); - this.Glide_detect_cpu_write = new System.Windows.Forms.CheckBox(); - this.Glide_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); - this.Glide_buff_clear = new System.Windows.Forms.CheckBox(); - this.Glide_alt_tex_size = new System.Windows.Forms.CheckBox(); - this.GlideUseDefaultHacks1 = new System.Windows.Forms.CheckBox(); - this.GlidePerGameHacks2 = new System.Windows.Forms.TabPage(); - this.label44 = new System.Windows.Forms.Label(); - this.Glide_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); - this.label43 = new System.Windows.Forms.Label(); - this.Glide_swapmode = new System.Windows.Forms.ComboBox(); - this.label42 = new System.Windows.Forms.Label(); - this.Glide_stipple_pattern = new System.Windows.Forms.TextBox(); - this.label41 = new System.Windows.Forms.Label(); - this.Glide_stipple_mode = new System.Windows.Forms.TextBox(); - this.label36 = new System.Windows.Forms.Label(); - this.Glide_lodmode = new System.Windows.Forms.ComboBox(); - this.label35 = new System.Windows.Forms.Label(); - this.Glide_fix_tex_coord = new System.Windows.Forms.TextBox(); - this.label34 = new System.Windows.Forms.Label(); - this.Glide_filtering = new System.Windows.Forms.ComboBox(); - this.label33 = new System.Windows.Forms.Label(); - this.Glide_depth_bias = new System.Windows.Forms.TextBox(); - this.GlideUseDefaultHacks2 = new System.Windows.Forms.CheckBox(); - this.RiceTabPage = new System.Windows.Forms.TabPage(); - this.RiceTabControl = new System.Windows.Forms.TabControl(); - this.RiceGeneral = new System.Windows.Forms.TabPage(); - this.label15 = new System.Windows.Forms.Label(); - this.RiceScreenUpdateSetting_Combo = new System.Windows.Forms.ComboBox(); - this.label14 = new System.Windows.Forms.Label(); - this.RiceMultiSampling_Combo = new System.Windows.Forms.ComboBox(); - this.label13 = new System.Windows.Forms.Label(); - this.RiceOpenGLRenderSetting_Combo = new System.Windows.Forms.ComboBox(); - this.label11 = new System.Windows.Forms.Label(); - this.RiceColorQuality_Combo = new System.Windows.Forms.ComboBox(); - this.label10 = new System.Windows.Forms.Label(); - this.RiceOpenGLDepthBufferSetting_Combo = new System.Windows.Forms.ComboBox(); - this.label9 = new System.Windows.Forms.Label(); - this.RiceTextureQuality_Combo = new System.Windows.Forms.ComboBox(); - this.RiceEnableVertexShader_CB = new System.Windows.Forms.CheckBox(); - this.RiceSkipFrame_CB = new System.Windows.Forms.CheckBox(); - this.RiceEnableHacks_CB = new System.Windows.Forms.CheckBox(); - this.RiceFullTMEMEmulation_CB = new System.Windows.Forms.CheckBox(); - this.RiceOpenGLVertexClipper_CB = new System.Windows.Forms.CheckBox(); - this.AnisotropicFiltering_LB = new System.Windows.Forms.Label(); - this.RiceAnisotropicFiltering_TB = new System.Windows.Forms.TrackBar(); - this.label6 = new System.Windows.Forms.Label(); - this.RiceFogMethod_Combo = new System.Windows.Forms.ComboBox(); - this.label5 = new System.Windows.Forms.Label(); - this.RiceMipmapping_Combo = new System.Windows.Forms.ComboBox(); - this.RiceWinFrameMode_CB = new System.Windows.Forms.CheckBox(); - this.RiceInN64Resolution_CB = new System.Windows.Forms.CheckBox(); - this.RiceFastTextureLoading_CB = new System.Windows.Forms.CheckBox(); - this.RiceAccurateTextureMapping_CB = new System.Windows.Forms.CheckBox(); - this.RiceSaveVRAM_CB = new System.Windows.Forms.CheckBox(); - this.RiceEnableSSE_CB = new System.Windows.Forms.CheckBox(); - this.RiceGameDefaultTab = new System.Windows.Forms.TabPage(); - this.RiceNormalAlphaBlender_CB = new System.Windows.Forms.CheckBox(); - this.RiceDefaultCombinerDisable_CB = new System.Windows.Forms.CheckBox(); - this.RiceFrameBuffer_GroupBox = new System.Windows.Forms.GroupBox(); - this.label16 = new System.Windows.Forms.Label(); - this.label17 = new System.Windows.Forms.Label(); - this.label18 = new System.Windows.Forms.Label(); - this.RiceFrameBufferWriteBackControl_Combo = new System.Windows.Forms.ComboBox(); - this.RiceFrameBufferSetting_Combo = new System.Windows.Forms.ComboBox(); - this.RiceDoubleSizeForSmallTxtrBuf_CB = new System.Windows.Forms.CheckBox(); - this.RiceRenderToTexture_Combo = new System.Windows.Forms.ComboBox(); - this.RiceTextureEnhancementTab = new System.Windows.Forms.TabPage(); - this.label12 = new System.Windows.Forms.Label(); - this.RiceForceTextureFilter_Combo = new System.Windows.Forms.ComboBox(); - this.label8 = new System.Windows.Forms.Label(); - this.RiceTextureEnhancementControl_Combo = new System.Windows.Forms.ComboBox(); - this.label7 = new System.Windows.Forms.Label(); - this.RiceTextureEnhancement_Combo = new System.Windows.Forms.ComboBox(); - this.RiceTexRectOnly_CB = new System.Windows.Forms.CheckBox(); - this.RiceSmallTextureOnly_CB = new System.Windows.Forms.CheckBox(); - this.RiceLoadHiResCRCOnly_CB = new System.Windows.Forms.CheckBox(); - this.RiceLoadHiResTextures_CB = new System.Windows.Forms.CheckBox(); - this.RiceDumpTexturesToFiles_CB = new System.Windows.Forms.CheckBox(); - this.RiceGameSpecificTab = new System.Windows.Forms.TabPage(); - this.label27 = new System.Windows.Forms.Label(); - this.RiceEnableHacksForGame_Combo = new System.Windows.Forms.ComboBox(); - this.RiceForceDepthBuffer_CB = new System.Windows.Forms.CheckBox(); - this.label28 = new System.Windows.Forms.Label(); - this.RiceUseCIWidthAndRatio_Combo = new System.Windows.Forms.ComboBox(); - this.label26 = new System.Windows.Forms.Label(); - this.RiceRenderToTextureOption_Combo = new System.Windows.Forms.ComboBox(); - this.label25 = new System.Windows.Forms.Label(); - this.RiceFrameBufferOption_Combo = new System.Windows.Forms.ComboBox(); - this.label24 = new System.Windows.Forms.Label(); - this.RiceNormalBlender_Combo = new System.Windows.Forms.ComboBox(); - this.label23 = new System.Windows.Forms.Label(); - this.RiceAccurateTextureMappingHack_Combo = new System.Windows.Forms.ComboBox(); - this.label22 = new System.Windows.Forms.Label(); - this.RiceFastTextureCRC_Combo = new System.Windows.Forms.ComboBox(); - this.label21 = new System.Windows.Forms.Label(); - this.RiceFullTMEM_Combo = new System.Windows.Forms.ComboBox(); - this.label20 = new System.Windows.Forms.Label(); - this.RiceScreenUpdateSettingHack_Combo = new System.Windows.Forms.ComboBox(); - this.label19 = new System.Windows.Forms.Label(); - this.RiceVIHeight_Text = new System.Windows.Forms.TextBox(); - this.label4 = new System.Windows.Forms.Label(); - this.RiceVIWidth_Text = new System.Windows.Forms.TextBox(); - this.RiceTextureScaleHack_CB = new System.Windows.Forms.CheckBox(); - this.RiceFastLoadTile_CB = new System.Windows.Forms.CheckBox(); - this.RiceUseSmallerTexture_CB = new System.Windows.Forms.CheckBox(); - this.RiceEnableTxtLOD_CB = new System.Windows.Forms.CheckBox(); - this.RiceZHack_CB = new System.Windows.Forms.CheckBox(); - this.RicePrimaryDepthHack_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableObjBG_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableBlender_CB = new System.Windows.Forms.CheckBox(); - this.RiceForceScreenClear_CB = new System.Windows.Forms.CheckBox(); - this.RiceEmulateClear_CB = new System.Windows.Forms.CheckBox(); - this.RiceTxtSizeMethod2_CB = new System.Windows.Forms.CheckBox(); - this.RiceIncTexRectEdge_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableCulling_CB = new System.Windows.Forms.CheckBox(); - this.RiceDisableTextureCRC_CB = new System.Windows.Forms.CheckBox(); - this.RiceTexture1Hack_CB = new System.Windows.Forms.CheckBox(); - this.RiceUseDefaultHacks_CB = new System.Windows.Forms.CheckBox(); - this.label71 = new System.Windows.Forms.Label(); - this.label70 = new System.Windows.Forms.Label(); - this.label69 = new System.Windows.Forms.Label(); - this.label68 = new System.Windows.Forms.Label(); - this.label67 = new System.Windows.Forms.Label(); - this.label66 = new System.Windows.Forms.Label(); - this.label65 = new System.Windows.Forms.Label(); - this.label64 = new System.Windows.Forms.Label(); - this.label63 = new System.Windows.Forms.Label(); - this.label62 = new System.Windows.Forms.Label(); - this.label61 = new System.Windows.Forms.Label(); - this.label60 = new System.Windows.Forms.Label(); - this.label59 = new System.Windows.Forms.Label(); - this.label57 = new System.Windows.Forms.Label(); - this.label51 = new System.Windows.Forms.Label(); - this.SaveButton = new System.Windows.Forms.Button(); - this.CancelBT = new System.Windows.Forms.Button(); - this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); - this.N64plugintabcontrol.SuspendLayout(); - this.N64vpluginglobaltab.SuspendLayout(); - this.GLideN64Tab.SuspendLayout(); - this.tabControl3.SuspendLayout(); - this.tabPage5.SuspendLayout(); - this.tabPage6.SuspendLayout(); - this.tabPage7.SuspendLayout(); - this.Glide64mk2TabPage.SuspendLayout(); - this.tabControl2.SuspendLayout(); - this.tabPage1.SuspendLayout(); - this.tabPage2.SuspendLayout(); - this.tabPage3.SuspendLayout(); - this.Glide64TabPage.SuspendLayout(); - this.tabControl1.SuspendLayout(); - this.Glide64General.SuspendLayout(); - this.GlidePerGameHacks1.SuspendLayout(); - this.GlidePerGameHacks2.SuspendLayout(); - this.RiceTabPage.SuspendLayout(); - this.RiceTabControl.SuspendLayout(); - this.RiceGeneral.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).BeginInit(); - this.RiceGameDefaultTab.SuspendLayout(); - this.RiceFrameBuffer_GroupBox.SuspendLayout(); - this.RiceTextureEnhancementTab.SuspendLayout(); - this.RiceGameSpecificTab.SuspendLayout(); - this.SuspendLayout(); - // - // N64plugintabcontrol - // - this.N64plugintabcontrol.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(N64VideoPluginconfig)); + this.N64plugintabcontrol = new System.Windows.Forms.TabControl(); + this.N64vpluginglobaltab = new System.Windows.Forms.TabPage(); + this.VideoResolutionYTextBox = new System.Windows.Forms.TextBox(); + this.LabelVideoResolutionY = new System.Windows.Forms.Label(); + this.VideoResolutionXTextBox = new System.Windows.Forms.TextBox(); + this.LabelVideoResolutionX = new System.Windows.Forms.Label(); + this.label49 = new System.Windows.Forms.Label(); + this.RspTypeDropdown = new System.Windows.Forms.ComboBox(); + this.label48 = new System.Windows.Forms.Label(); + this.CoreTypeDropdown = new System.Windows.Forms.ComboBox(); + this.label47 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.VideoResolutionComboBox = new System.Windows.Forms.ComboBox(); + this.PluginComboBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.GLideN64Tab = new System.Windows.Forms.TabPage(); + this.tabControl3 = new System.Windows.Forms.TabControl(); + this.tabPage5 = new System.Windows.Forms.TabPage(); + this.label88 = new System.Windows.Forms.Label(); + this.GLideN64_GammaCorrectionLevel = new System.Windows.Forms.TextBox(); + this.GLideN64_ForceGammaCorrection = new System.Windows.Forms.CheckBox(); + this.label81 = new System.Windows.Forms.Label(); + this.GLideN64_MultiSampling = new System.Windows.Forms.ComboBox(); + this.label80 = new System.Windows.Forms.Label(); + this.GLideN64_blurStrength = new System.Windows.Forms.ComboBox(); + this.label79 = new System.Windows.Forms.Label(); + this.GLideN64_blurAmount = new System.Windows.Forms.ComboBox(); + this.label78 = new System.Windows.Forms.Label(); + this.GLideN64_bloomBlendMode = new System.Windows.Forms.ComboBox(); + this.label77 = new System.Windows.Forms.Label(); + this.GLideN64_bloomThresholdLevel = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableBloom = new System.Windows.Forms.CheckBox(); + this.GLideN64_FragmentDepthWrite = new System.Windows.Forms.CheckBox(); + this.GLideN64_LegacyBlending = new System.Windows.Forms.CheckBox(); + this.GLideN64_NativeResTexrects = new System.Windows.Forms.CheckBox(); + this.label82 = new System.Windows.Forms.Label(); + this.GLideN64_CorrectTexrectCoords = new System.Windows.Forms.ComboBox(); + this.GLideN64_ShadersStorage = new System.Windows.Forms.CheckBox(); + this.GLideN64_HWLighting = new System.Windows.Forms.CheckBox(); + this.GLideN64_EnableNoise = new System.Windows.Forms.CheckBox(); + this.GLideN64_EnableLOD = new System.Windows.Forms.CheckBox(); + this.tabPage6 = new System.Windows.Forms.TabPage(); + this.label87 = new System.Windows.Forms.Label(); + this.GLideN64_txPath = new System.Windows.Forms.TextBox(); + this.GLideN64_txSaveCache = new System.Windows.Forms.CheckBox(); + this.GLideN64_txCacheCompression = new System.Windows.Forms.CheckBox(); + this.GLideN64_txForce16bpp = new System.Windows.Forms.CheckBox(); + this.GLideN64_txDump = new System.Windows.Forms.CheckBox(); + this.GLideN64_txHresAltCRC = new System.Windows.Forms.CheckBox(); + this.GLideN64_txHiresFullAlphaChannel = new System.Windows.Forms.CheckBox(); + this.GLideN64_txHiresEnable = new System.Windows.Forms.CheckBox(); + this.label86 = new System.Windows.Forms.Label(); + this.GLideN64_txCacheSize = new System.Windows.Forms.TextBox(); + this.GLideN64_txFilterIgnoreBG = new System.Windows.Forms.CheckBox(); + this.GLideN64_txDeposterize = new System.Windows.Forms.CheckBox(); + this.label85 = new System.Windows.Forms.Label(); + this.GLideN64_txEnhancementMode = new System.Windows.Forms.ComboBox(); + this.label84 = new System.Windows.Forms.Label(); + this.GLideN64_txFilterMode = new System.Windows.Forms.ComboBox(); + this.label83 = new System.Windows.Forms.Label(); + this.GLideN64_CacheSize = new System.Windows.Forms.TextBox(); + this.label72 = new System.Windows.Forms.Label(); + this.GLideN64_bilinearMode = new System.Windows.Forms.ComboBox(); + this.GLideN64_MaxAnisotropy = new System.Windows.Forms.CheckBox(); + this.tabPage7 = new System.Windows.Forms.TabPage(); + this.label89 = new System.Windows.Forms.Label(); + this.GLideN64_UseNativeResolutionFactor = new System.Windows.Forms.TextBox(); + this.GLideN64_DisableFBInfo = new System.Windows.Forms.CheckBox(); + this.GLideN64_FBInfoReadDepthChunk = new System.Windows.Forms.CheckBox(); + this.GLideN64_FBInfoReadColorChunk = new System.Windows.Forms.CheckBox(); + this.label76 = new System.Windows.Forms.Label(); + this.GLideN64_BufferSwapMode = new System.Windows.Forms.ComboBox(); + this.label75 = new System.Windows.Forms.Label(); + this.GLideN64_AspectRatio = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableN64DepthCompare = new System.Windows.Forms.CheckBox(); + this.label74 = new System.Windows.Forms.Label(); + this.GLideN64_EnableCopyColorToRDRAM = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableCopyAuxiliaryToRDRAM = new System.Windows.Forms.CheckBox(); + this.GLideN64_EnableCopyColorFromRDRAM = new System.Windows.Forms.CheckBox(); + this.label73 = new System.Windows.Forms.Label(); + this.GLideN64_EnableCopyDepthToRDRAM = new System.Windows.Forms.ComboBox(); + this.GLideN64_EnableFBEmulation = new System.Windows.Forms.CheckBox(); + this.GLideN64_UseDefaultHacks = new System.Windows.Forms.CheckBox(); + this.Glide64mk2TabPage = new System.Windows.Forms.TabPage(); + this.tabControl2 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.Glide64mk2_fb_get_info = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_render = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_wrpAnisotropic = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_wrpFBO = new System.Windows.Forms.CheckBox(); + this.label50 = new System.Windows.Forms.Label(); + this.Glide64mk2_card_id = new System.Windows.Forms.ComboBox(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.Glide64mk2_fb_read_always = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_useless_is_useless = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_n64_z_scale = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_old_style_adither = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_zmode_compare_less = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_adjust_aspect = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fast_crc = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_clip_zmax = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_clip_zmin = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_force_quad3d = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_pal230 = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_texture_correction = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_correct_viewport = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_force_calc_sphere = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_use_sts1_only = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_optimize_texrect = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_increase_texrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_ignore_aux_copy = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_hires_buf_clear = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_force_microcheck = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fog = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_smart = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_read_alpha = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_fb_hires = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_detect_cpu_write = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_buff_clear = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_alt_tex_size = new System.Windows.Forms.CheckBox(); + this.Glide64mk2_UseDefaultHacks1 = new System.Windows.Forms.CheckBox(); + this.tabPage3 = new System.Windows.Forms.TabPage(); + this.label46 = new System.Windows.Forms.Label(); + this.Glide64mk2_read_back_to_screen = new System.Windows.Forms.ComboBox(); + this.Glide64mk2_aspectmode = new System.Windows.Forms.ComboBox(); + this.Glide64mk2_fb_crc_mode = new System.Windows.Forms.ComboBox(); + this.label45 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.label52 = new System.Windows.Forms.Label(); + this.Glide64mk2_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); + this.label53 = new System.Windows.Forms.Label(); + this.Glide64mk2_swapmode = new System.Windows.Forms.ComboBox(); + this.label54 = new System.Windows.Forms.Label(); + this.Glide64mk2_stipple_pattern = new System.Windows.Forms.TextBox(); + this.label55 = new System.Windows.Forms.Label(); + this.Glide64mk2_stipple_mode = new System.Windows.Forms.TextBox(); + this.label56 = new System.Windows.Forms.Label(); + this.Glide64mk2_lodmode = new System.Windows.Forms.ComboBox(); + this.label58 = new System.Windows.Forms.Label(); + this.Glide64mk2_filtering = new System.Windows.Forms.ComboBox(); + this.Glide64mk2_UseDefaultHacks2 = new System.Windows.Forms.CheckBox(); + this.Glide64TabPage = new System.Windows.Forms.TabPage(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.Glide64General = new System.Windows.Forms.TabPage(); + this.label39 = new System.Windows.Forms.Label(); + this.Glide_scale_y = new System.Windows.Forms.TextBox(); + this.label40 = new System.Windows.Forms.Label(); + this.Glide_scale_x = new System.Windows.Forms.TextBox(); + this.label38 = new System.Windows.Forms.Label(); + this.Glide_offset_y = new System.Windows.Forms.TextBox(); + this.label37 = new System.Windows.Forms.Label(); + this.Glide_offset_x = new System.Windows.Forms.TextBox(); + this.Glide_fb_get_info = new System.Windows.Forms.CheckBox(); + this.Glide_disable_auxbuf = new System.Windows.Forms.CheckBox(); + this.Glide_fbo = new System.Windows.Forms.CheckBox(); + this.Glide_noglsl = new System.Windows.Forms.CheckBox(); + this.Glide_noditheredalpha = new System.Windows.Forms.CheckBox(); + this.label32 = new System.Windows.Forms.Label(); + this.Glide_tex_filter = new System.Windows.Forms.ComboBox(); + this.Glide_fb_render = new System.Windows.Forms.CheckBox(); + this.Glide_motionblur = new System.Windows.Forms.CheckBox(); + this.Glide_fb_read_always = new System.Windows.Forms.CheckBox(); + this.Glide_unk_as_red = new System.Windows.Forms.CheckBox(); + this.Glide_filter_cache = new System.Windows.Forms.CheckBox(); + this.Glide_fast_crc = new System.Windows.Forms.CheckBox(); + this.label31 = new System.Windows.Forms.Label(); + this.Glide_wfmode = new System.Windows.Forms.ComboBox(); + this.Glide_wireframe = new System.Windows.Forms.CheckBox(); + this.label30 = new System.Windows.Forms.Label(); + this.Glide_card_id = new System.Windows.Forms.ComboBox(); + this.Glide_flame_corona = new System.Windows.Forms.CheckBox(); + this.label29 = new System.Windows.Forms.Label(); + this.Glide_ucode = new System.Windows.Forms.ComboBox(); + this.Glide_autodetect_ucode = new System.Windows.Forms.CheckBox(); + this.GlidePerGameHacks1 = new System.Windows.Forms.TabPage(); + this.Glide_wrap_big_tex = new System.Windows.Forms.CheckBox(); + this.Glide_use_sts1_only = new System.Windows.Forms.CheckBox(); + this.Glide_soft_depth_compare = new System.Windows.Forms.CheckBox(); + this.Glide_PPL = new System.Windows.Forms.CheckBox(); + this.Glide_fb_optimize_write = new System.Windows.Forms.CheckBox(); + this.Glide_fb_optimize_texrect = new System.Windows.Forms.CheckBox(); + this.Glide_increase_texrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide_increase_primdepth = new System.Windows.Forms.CheckBox(); + this.Glide_fb_ignore_previous = new System.Windows.Forms.CheckBox(); + this.Glide_fb_ignore_aux_copy = new System.Windows.Forms.CheckBox(); + this.Glide_fb_hires_buf_clear = new System.Windows.Forms.CheckBox(); + this.Glide_force_microcheck = new System.Windows.Forms.CheckBox(); + this.Glide_force_depth_compare = new System.Windows.Forms.CheckBox(); + this.Glide_fog = new System.Windows.Forms.CheckBox(); + this.Glide_fillcolor_fix = new System.Windows.Forms.CheckBox(); + this.Glide_fb_smart = new System.Windows.Forms.CheckBox(); + this.Glide_fb_read_alpha = new System.Windows.Forms.CheckBox(); + this.Glide_fb_hires = new System.Windows.Forms.CheckBox(); + this.Glide_fb_clear = new System.Windows.Forms.CheckBox(); + this.Glide_detect_cpu_write = new System.Windows.Forms.CheckBox(); + this.Glide_decrease_fillrect_edge = new System.Windows.Forms.CheckBox(); + this.Glide_buff_clear = new System.Windows.Forms.CheckBox(); + this.Glide_alt_tex_size = new System.Windows.Forms.CheckBox(); + this.GlideUseDefaultHacks1 = new System.Windows.Forms.CheckBox(); + this.GlidePerGameHacks2 = new System.Windows.Forms.TabPage(); + this.label44 = new System.Windows.Forms.Label(); + this.Glide_enable_hacks_for_game = new System.Windows.Forms.ComboBox(); + this.label43 = new System.Windows.Forms.Label(); + this.Glide_swapmode = new System.Windows.Forms.ComboBox(); + this.label42 = new System.Windows.Forms.Label(); + this.Glide_stipple_pattern = new System.Windows.Forms.TextBox(); + this.label41 = new System.Windows.Forms.Label(); + this.Glide_stipple_mode = new System.Windows.Forms.TextBox(); + this.label36 = new System.Windows.Forms.Label(); + this.Glide_lodmode = new System.Windows.Forms.ComboBox(); + this.label35 = new System.Windows.Forms.Label(); + this.Glide_fix_tex_coord = new System.Windows.Forms.TextBox(); + this.label34 = new System.Windows.Forms.Label(); + this.Glide_filtering = new System.Windows.Forms.ComboBox(); + this.label33 = new System.Windows.Forms.Label(); + this.Glide_depth_bias = new System.Windows.Forms.TextBox(); + this.GlideUseDefaultHacks2 = new System.Windows.Forms.CheckBox(); + this.RiceTabPage = new System.Windows.Forms.TabPage(); + this.RiceTabControl = new System.Windows.Forms.TabControl(); + this.RiceGeneral = new System.Windows.Forms.TabPage(); + this.label15 = new System.Windows.Forms.Label(); + this.RiceScreenUpdateSetting_Combo = new System.Windows.Forms.ComboBox(); + this.label14 = new System.Windows.Forms.Label(); + this.RiceMultiSampling_Combo = new System.Windows.Forms.ComboBox(); + this.label13 = new System.Windows.Forms.Label(); + this.RiceOpenGLRenderSetting_Combo = new System.Windows.Forms.ComboBox(); + this.label11 = new System.Windows.Forms.Label(); + this.RiceColorQuality_Combo = new System.Windows.Forms.ComboBox(); + this.label10 = new System.Windows.Forms.Label(); + this.RiceOpenGLDepthBufferSetting_Combo = new System.Windows.Forms.ComboBox(); + this.label9 = new System.Windows.Forms.Label(); + this.RiceTextureQuality_Combo = new System.Windows.Forms.ComboBox(); + this.RiceEnableVertexShader_CB = new System.Windows.Forms.CheckBox(); + this.RiceSkipFrame_CB = new System.Windows.Forms.CheckBox(); + this.RiceEnableHacks_CB = new System.Windows.Forms.CheckBox(); + this.RiceFullTMEMEmulation_CB = new System.Windows.Forms.CheckBox(); + this.RiceOpenGLVertexClipper_CB = new System.Windows.Forms.CheckBox(); + this.AnisotropicFiltering_LB = new System.Windows.Forms.Label(); + this.RiceAnisotropicFiltering_TB = new System.Windows.Forms.TrackBar(); + this.label6 = new System.Windows.Forms.Label(); + this.RiceFogMethod_Combo = new System.Windows.Forms.ComboBox(); + this.label5 = new System.Windows.Forms.Label(); + this.RiceMipmapping_Combo = new System.Windows.Forms.ComboBox(); + this.RiceWinFrameMode_CB = new System.Windows.Forms.CheckBox(); + this.RiceInN64Resolution_CB = new System.Windows.Forms.CheckBox(); + this.RiceFastTextureLoading_CB = new System.Windows.Forms.CheckBox(); + this.RiceAccurateTextureMapping_CB = new System.Windows.Forms.CheckBox(); + this.RiceSaveVRAM_CB = new System.Windows.Forms.CheckBox(); + this.RiceEnableSSE_CB = new System.Windows.Forms.CheckBox(); + this.RiceGameDefaultTab = new System.Windows.Forms.TabPage(); + this.RiceNormalAlphaBlender_CB = new System.Windows.Forms.CheckBox(); + this.RiceDefaultCombinerDisable_CB = new System.Windows.Forms.CheckBox(); + this.RiceFrameBuffer_GroupBox = new System.Windows.Forms.GroupBox(); + this.label16 = new System.Windows.Forms.Label(); + this.label17 = new System.Windows.Forms.Label(); + this.label18 = new System.Windows.Forms.Label(); + this.RiceFrameBufferWriteBackControl_Combo = new System.Windows.Forms.ComboBox(); + this.RiceFrameBufferSetting_Combo = new System.Windows.Forms.ComboBox(); + this.RiceDoubleSizeForSmallTxtrBuf_CB = new System.Windows.Forms.CheckBox(); + this.RiceRenderToTexture_Combo = new System.Windows.Forms.ComboBox(); + this.RiceTextureEnhancementTab = new System.Windows.Forms.TabPage(); + this.label12 = new System.Windows.Forms.Label(); + this.RiceForceTextureFilter_Combo = new System.Windows.Forms.ComboBox(); + this.label8 = new System.Windows.Forms.Label(); + this.RiceTextureEnhancementControl_Combo = new System.Windows.Forms.ComboBox(); + this.label7 = new System.Windows.Forms.Label(); + this.RiceTextureEnhancement_Combo = new System.Windows.Forms.ComboBox(); + this.RiceTexRectOnly_CB = new System.Windows.Forms.CheckBox(); + this.RiceSmallTextureOnly_CB = new System.Windows.Forms.CheckBox(); + this.RiceLoadHiResCRCOnly_CB = new System.Windows.Forms.CheckBox(); + this.RiceLoadHiResTextures_CB = new System.Windows.Forms.CheckBox(); + this.RiceDumpTexturesToFiles_CB = new System.Windows.Forms.CheckBox(); + this.RiceGameSpecificTab = new System.Windows.Forms.TabPage(); + this.label27 = new System.Windows.Forms.Label(); + this.RiceEnableHacksForGame_Combo = new System.Windows.Forms.ComboBox(); + this.RiceForceDepthBuffer_CB = new System.Windows.Forms.CheckBox(); + this.label28 = new System.Windows.Forms.Label(); + this.RiceUseCIWidthAndRatio_Combo = new System.Windows.Forms.ComboBox(); + this.label26 = new System.Windows.Forms.Label(); + this.RiceRenderToTextureOption_Combo = new System.Windows.Forms.ComboBox(); + this.label25 = new System.Windows.Forms.Label(); + this.RiceFrameBufferOption_Combo = new System.Windows.Forms.ComboBox(); + this.label24 = new System.Windows.Forms.Label(); + this.RiceNormalBlender_Combo = new System.Windows.Forms.ComboBox(); + this.label23 = new System.Windows.Forms.Label(); + this.RiceAccurateTextureMappingHack_Combo = new System.Windows.Forms.ComboBox(); + this.label22 = new System.Windows.Forms.Label(); + this.RiceFastTextureCRC_Combo = new System.Windows.Forms.ComboBox(); + this.label21 = new System.Windows.Forms.Label(); + this.RiceFullTMEM_Combo = new System.Windows.Forms.ComboBox(); + this.label20 = new System.Windows.Forms.Label(); + this.RiceScreenUpdateSettingHack_Combo = new System.Windows.Forms.ComboBox(); + this.label19 = new System.Windows.Forms.Label(); + this.RiceVIHeight_Text = new System.Windows.Forms.TextBox(); + this.label4 = new System.Windows.Forms.Label(); + this.RiceVIWidth_Text = new System.Windows.Forms.TextBox(); + this.RiceTextureScaleHack_CB = new System.Windows.Forms.CheckBox(); + this.RiceFastLoadTile_CB = new System.Windows.Forms.CheckBox(); + this.RiceUseSmallerTexture_CB = new System.Windows.Forms.CheckBox(); + this.RiceEnableTxtLOD_CB = new System.Windows.Forms.CheckBox(); + this.RiceZHack_CB = new System.Windows.Forms.CheckBox(); + this.RicePrimaryDepthHack_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableObjBG_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableBlender_CB = new System.Windows.Forms.CheckBox(); + this.RiceForceScreenClear_CB = new System.Windows.Forms.CheckBox(); + this.RiceEmulateClear_CB = new System.Windows.Forms.CheckBox(); + this.RiceTxtSizeMethod2_CB = new System.Windows.Forms.CheckBox(); + this.RiceIncTexRectEdge_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableCulling_CB = new System.Windows.Forms.CheckBox(); + this.RiceDisableTextureCRC_CB = new System.Windows.Forms.CheckBox(); + this.RiceTexture1Hack_CB = new System.Windows.Forms.CheckBox(); + this.RiceUseDefaultHacks_CB = new System.Windows.Forms.CheckBox(); + this.label71 = new System.Windows.Forms.Label(); + this.label70 = new System.Windows.Forms.Label(); + this.label69 = new System.Windows.Forms.Label(); + this.label68 = new System.Windows.Forms.Label(); + this.label67 = new System.Windows.Forms.Label(); + this.label66 = new System.Windows.Forms.Label(); + this.label65 = new System.Windows.Forms.Label(); + this.label64 = new System.Windows.Forms.Label(); + this.label63 = new System.Windows.Forms.Label(); + this.label62 = new System.Windows.Forms.Label(); + this.label61 = new System.Windows.Forms.Label(); + this.label60 = new System.Windows.Forms.Label(); + this.label59 = new System.Windows.Forms.Label(); + this.label57 = new System.Windows.Forms.Label(); + this.label51 = new System.Windows.Forms.Label(); + this.SaveButton = new System.Windows.Forms.Button(); + this.CancelBT = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.N64plugintabcontrol.SuspendLayout(); + this.N64vpluginglobaltab.SuspendLayout(); + this.GLideN64Tab.SuspendLayout(); + this.tabControl3.SuspendLayout(); + this.tabPage5.SuspendLayout(); + this.tabPage6.SuspendLayout(); + this.tabPage7.SuspendLayout(); + this.Glide64mk2TabPage.SuspendLayout(); + this.tabControl2.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + this.tabPage3.SuspendLayout(); + this.Glide64TabPage.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.Glide64General.SuspendLayout(); + this.GlidePerGameHacks1.SuspendLayout(); + this.GlidePerGameHacks2.SuspendLayout(); + this.RiceTabPage.SuspendLayout(); + this.RiceTabControl.SuspendLayout(); + this.RiceGeneral.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).BeginInit(); + this.RiceGameDefaultTab.SuspendLayout(); + this.RiceFrameBuffer_GroupBox.SuspendLayout(); + this.RiceTextureEnhancementTab.SuspendLayout(); + this.RiceGameSpecificTab.SuspendLayout(); + this.SuspendLayout(); + // + // N64plugintabcontrol + // + this.N64plugintabcontrol.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.N64plugintabcontrol.Controls.Add(this.N64vpluginglobaltab); - this.N64plugintabcontrol.Controls.Add(this.GLideN64Tab); - this.N64plugintabcontrol.Controls.Add(this.Glide64mk2TabPage); - this.N64plugintabcontrol.Controls.Add(this.Glide64TabPage); - this.N64plugintabcontrol.Controls.Add(this.RiceTabPage); - this.N64plugintabcontrol.Location = new System.Drawing.Point(-2, -2); - this.N64plugintabcontrol.Name = "N64plugintabcontrol"; - this.N64plugintabcontrol.SelectedIndex = 0; - this.N64plugintabcontrol.Size = new System.Drawing.Size(580, 369); - this.N64plugintabcontrol.TabIndex = 0; - // - // N64vpluginglobaltab - // - this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionYTextBox); - this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionY); - this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionXTextBox); - this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionX); - this.N64vpluginglobaltab.Controls.Add(this.label49); - this.N64vpluginglobaltab.Controls.Add(this.RspTypeDropdown); - this.N64vpluginglobaltab.Controls.Add(this.label48); - this.N64vpluginglobaltab.Controls.Add(this.CoreTypeDropdown); - this.N64vpluginglobaltab.Controls.Add(this.label47); - this.N64vpluginglobaltab.Controls.Add(this.label2); - this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionComboBox); - this.N64vpluginglobaltab.Controls.Add(this.PluginComboBox); - this.N64vpluginglobaltab.Controls.Add(this.label1); - this.N64vpluginglobaltab.Location = new System.Drawing.Point(4, 22); - this.N64vpluginglobaltab.Name = "N64vpluginglobaltab"; - this.N64vpluginglobaltab.Padding = new System.Windows.Forms.Padding(3); - this.N64vpluginglobaltab.Size = new System.Drawing.Size(572, 343); - this.N64vpluginglobaltab.TabIndex = 0; - this.N64vpluginglobaltab.Text = "Global"; - this.N64vpluginglobaltab.UseVisualStyleBackColor = true; - // - // VideoResolutionYTextBox - // - this.VideoResolutionYTextBox.Location = new System.Drawing.Point(66, 270); - this.VideoResolutionYTextBox.MaxLength = 5; - this.VideoResolutionYTextBox.Name = "VideoResolutionYTextBox"; - this.VideoResolutionYTextBox.Size = new System.Drawing.Size(35, 20); - this.VideoResolutionYTextBox.TabIndex = 22; - this.VideoResolutionYTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.VideoResolutionYTextBox.Visible = false; - // - // LabelVideoResolutionY - // - this.LabelVideoResolutionY.AutoSize = true; - this.LabelVideoResolutionY.Location = new System.Drawing.Point(22, 273); - this.LabelVideoResolutionY.Name = "LabelVideoResolutionY"; - this.LabelVideoResolutionY.Size = new System.Drawing.Size(41, 13); - this.LabelVideoResolutionY.TabIndex = 21; - this.LabelVideoResolutionY.Text = "Height:"; - this.LabelVideoResolutionY.Visible = false; - // - // VideoResolutionXTextBox - // - this.VideoResolutionXTextBox.Location = new System.Drawing.Point(66, 244); - this.VideoResolutionXTextBox.MaxLength = 5; - this.VideoResolutionXTextBox.Name = "VideoResolutionXTextBox"; - this.VideoResolutionXTextBox.Size = new System.Drawing.Size(35, 20); - this.VideoResolutionXTextBox.TabIndex = 20; - this.VideoResolutionXTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; - this.VideoResolutionXTextBox.Visible = false; - // - // LabelVideoResolutionX - // - this.LabelVideoResolutionX.AutoSize = true; - this.LabelVideoResolutionX.Location = new System.Drawing.Point(22, 247); - this.LabelVideoResolutionX.Name = "LabelVideoResolutionX"; - this.LabelVideoResolutionX.Size = new System.Drawing.Size(38, 13); - this.LabelVideoResolutionX.TabIndex = 19; - this.LabelVideoResolutionX.Text = "Width:"; - this.LabelVideoResolutionX.Visible = false; - // - // label49 - // - this.label49.AutoSize = true; - this.label49.Location = new System.Drawing.Point(206, 13); - this.label49.Name = "label49"; - this.label49.Size = new System.Drawing.Size(58, 13); - this.label49.TabIndex = 18; - this.label49.Text = "Rsp Plugin"; - this.label49.Visible = false; - // - // RspTypeDropdown - // - this.RspTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RspTypeDropdown.FormattingEnabled = true; - this.RspTypeDropdown.Items.AddRange(new object[] { + this.N64plugintabcontrol.Controls.Add(this.N64vpluginglobaltab); + this.N64plugintabcontrol.Controls.Add(this.GLideN64Tab); + this.N64plugintabcontrol.Controls.Add(this.Glide64mk2TabPage); + this.N64plugintabcontrol.Controls.Add(this.Glide64TabPage); + this.N64plugintabcontrol.Controls.Add(this.RiceTabPage); + this.N64plugintabcontrol.Location = new System.Drawing.Point(-2, -2); + this.N64plugintabcontrol.Name = "N64plugintabcontrol"; + this.N64plugintabcontrol.SelectedIndex = 0; + this.N64plugintabcontrol.Size = new System.Drawing.Size(580, 369); + this.N64plugintabcontrol.TabIndex = 0; + // + // N64vpluginglobaltab + // + this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionYTextBox); + this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionY); + this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionXTextBox); + this.N64vpluginglobaltab.Controls.Add(this.LabelVideoResolutionX); + this.N64vpluginglobaltab.Controls.Add(this.label49); + this.N64vpluginglobaltab.Controls.Add(this.RspTypeDropdown); + this.N64vpluginglobaltab.Controls.Add(this.label48); + this.N64vpluginglobaltab.Controls.Add(this.CoreTypeDropdown); + this.N64vpluginglobaltab.Controls.Add(this.label47); + this.N64vpluginglobaltab.Controls.Add(this.label2); + this.N64vpluginglobaltab.Controls.Add(this.VideoResolutionComboBox); + this.N64vpluginglobaltab.Controls.Add(this.PluginComboBox); + this.N64vpluginglobaltab.Controls.Add(this.label1); + this.N64vpluginglobaltab.Location = new System.Drawing.Point(4, 22); + this.N64vpluginglobaltab.Name = "N64vpluginglobaltab"; + this.N64vpluginglobaltab.Padding = new System.Windows.Forms.Padding(3); + this.N64vpluginglobaltab.Size = new System.Drawing.Size(572, 343); + this.N64vpluginglobaltab.TabIndex = 0; + this.N64vpluginglobaltab.Text = "Global"; + this.N64vpluginglobaltab.UseVisualStyleBackColor = true; + // + // VideoResolutionYTextBox + // + this.VideoResolutionYTextBox.Location = new System.Drawing.Point(66, 270); + this.VideoResolutionYTextBox.MaxLength = 5; + this.VideoResolutionYTextBox.Name = "VideoResolutionYTextBox"; + this.VideoResolutionYTextBox.Size = new System.Drawing.Size(35, 20); + this.VideoResolutionYTextBox.TabIndex = 22; + this.VideoResolutionYTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.VideoResolutionYTextBox.Visible = false; + // + // LabelVideoResolutionY + // + this.LabelVideoResolutionY.AutoSize = true; + this.LabelVideoResolutionY.Location = new System.Drawing.Point(22, 273); + this.LabelVideoResolutionY.Name = "LabelVideoResolutionY"; + this.LabelVideoResolutionY.Size = new System.Drawing.Size(41, 13); + this.LabelVideoResolutionY.TabIndex = 21; + this.LabelVideoResolutionY.Text = "Height:"; + this.LabelVideoResolutionY.Visible = false; + // + // VideoResolutionXTextBox + // + this.VideoResolutionXTextBox.Location = new System.Drawing.Point(66, 244); + this.VideoResolutionXTextBox.MaxLength = 5; + this.VideoResolutionXTextBox.Name = "VideoResolutionXTextBox"; + this.VideoResolutionXTextBox.Size = new System.Drawing.Size(35, 20); + this.VideoResolutionXTextBox.TabIndex = 20; + this.VideoResolutionXTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.VideoResolutionXTextBox.Visible = false; + // + // LabelVideoResolutionX + // + this.LabelVideoResolutionX.AutoSize = true; + this.LabelVideoResolutionX.Location = new System.Drawing.Point(22, 247); + this.LabelVideoResolutionX.Name = "LabelVideoResolutionX"; + this.LabelVideoResolutionX.Size = new System.Drawing.Size(38, 13); + this.LabelVideoResolutionX.TabIndex = 19; + this.LabelVideoResolutionX.Text = "Width:"; + this.LabelVideoResolutionX.Visible = false; + // + // label49 + // + this.label49.AutoSize = true; + this.label49.Location = new System.Drawing.Point(206, 13); + this.label49.Name = "label49"; + this.label49.Size = new System.Drawing.Size(58, 13); + this.label49.TabIndex = 18; + this.label49.Text = "Rsp Plugin"; + this.label49.Visible = false; + // + // RspTypeDropdown + // + this.RspTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RspTypeDropdown.FormattingEnabled = true; + this.RspTypeDropdown.Items.AddRange(new object[] { "Pure Interpreter", "Interpreter", "DynaRec"}); - this.RspTypeDropdown.Location = new System.Drawing.Point(209, 29); - this.RspTypeDropdown.Name = "RspTypeDropdown"; - this.RspTypeDropdown.Size = new System.Drawing.Size(136, 21); - this.RspTypeDropdown.TabIndex = 17; - this.RspTypeDropdown.Visible = false; - // - // label48 - // - this.label48.AutoSize = true; - this.label48.Location = new System.Drawing.Point(10, 13); - this.label48.Name = "label48"; - this.label48.Size = new System.Drawing.Size(56, 13); - this.label48.TabIndex = 16; - this.label48.Text = "Core Type"; - // - // CoreTypeDropdown - // - this.CoreTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.CoreTypeDropdown.FormattingEnabled = true; - this.CoreTypeDropdown.Items.AddRange(new object[] { + this.RspTypeDropdown.Location = new System.Drawing.Point(209, 29); + this.RspTypeDropdown.Name = "RspTypeDropdown"; + this.RspTypeDropdown.Size = new System.Drawing.Size(136, 21); + this.RspTypeDropdown.TabIndex = 17; + this.RspTypeDropdown.Visible = false; + // + // label48 + // + this.label48.AutoSize = true; + this.label48.Location = new System.Drawing.Point(10, 13); + this.label48.Name = "label48"; + this.label48.Size = new System.Drawing.Size(56, 13); + this.label48.TabIndex = 16; + this.label48.Text = "Core Type"; + // + // CoreTypeDropdown + // + this.CoreTypeDropdown.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.CoreTypeDropdown.FormattingEnabled = true; + this.CoreTypeDropdown.Items.AddRange(new object[] { "Pure Interpreter", "Interpreter", "DynaRec"}); - this.CoreTypeDropdown.Location = new System.Drawing.Point(13, 29); - this.CoreTypeDropdown.Name = "CoreTypeDropdown"; - this.CoreTypeDropdown.Size = new System.Drawing.Size(136, 21); - this.CoreTypeDropdown.TabIndex = 1; - // - // label47 - // - this.label47.AutoSize = true; - this.label47.Location = new System.Drawing.Point(10, 115); - this.label47.Name = "label47"; + this.CoreTypeDropdown.Location = new System.Drawing.Point(13, 29); + this.CoreTypeDropdown.Name = "CoreTypeDropdown"; + this.CoreTypeDropdown.Size = new System.Drawing.Size(136, 21); + this.CoreTypeDropdown.TabIndex = 1; + // + // label47 + // + this.label47.AutoSize = true; + this.label47.Location = new System.Drawing.Point(10, 115); + this.label47.Name = "label47"; this.label47.Size = new System.Drawing.Size(275, 13); - this.label47.TabIndex = 14; - this.label47.Text = "(GLideN64 is the newest pluging and has the highest compatibility)"; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(10, 201); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(87, 13); - this.label2.TabIndex = 13; - this.label2.Text = "Video Resolution"; - // - // VideoResolutionComboBox - // - this.VideoResolutionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.VideoResolutionComboBox.FormattingEnabled = true; - this.VideoResolutionComboBox.Items.AddRange(new object[] { + this.label47.TabIndex = 14; + this.label47.Text = "(GLideN64 is the newest pluging and has the highest compatibility)"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(10, 201); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(87, 13); + this.label2.TabIndex = 13; + this.label2.Text = "Video Resolution"; + // + // VideoResolutionComboBox + // + this.VideoResolutionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.VideoResolutionComboBox.FormattingEnabled = true; + this.VideoResolutionComboBox.Items.AddRange(new object[] { "320 x 240", "400 x 300", "480 x 360", @@ -550,155 +550,155 @@ "1920 x 1440", "2048 x 1536", "Custom"}); - this.VideoResolutionComboBox.Location = new System.Drawing.Point(13, 217); - this.VideoResolutionComboBox.Name = "VideoResolutionComboBox"; - this.VideoResolutionComboBox.Size = new System.Drawing.Size(136, 21); - this.VideoResolutionComboBox.TabIndex = 10; - this.VideoResolutionComboBox.SelectedIndexChanged += new System.EventHandler(this.VideoResolutionComboBox_SelectedIndexChanged); - // - // PluginComboBox - // - this.PluginComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.PluginComboBox.FormattingEnabled = true; - this.PluginComboBox.Items.AddRange(new object[] { + this.VideoResolutionComboBox.Location = new System.Drawing.Point(13, 217); + this.VideoResolutionComboBox.Name = "VideoResolutionComboBox"; + this.VideoResolutionComboBox.Size = new System.Drawing.Size(136, 21); + this.VideoResolutionComboBox.TabIndex = 10; + this.VideoResolutionComboBox.SelectedIndexChanged += new System.EventHandler(this.VideoResolutionComboBox_SelectedIndexChanged); + // + // PluginComboBox + // + this.PluginComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.PluginComboBox.FormattingEnabled = true; + this.PluginComboBox.Items.AddRange(new object[] { "Rice", "Glide64", "Glide64mk2", "GLideN64"}); - this.PluginComboBox.Location = new System.Drawing.Point(13, 91); - this.PluginComboBox.Name = "PluginComboBox"; - this.PluginComboBox.Size = new System.Drawing.Size(173, 21); - this.PluginComboBox.TabIndex = 5; - this.PluginComboBox.SelectedIndexChanged += new System.EventHandler(this.PluginComboBox_SelectedIndexChanged); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(10, 75); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(99, 13); - this.label1.TabIndex = 1; - this.label1.Text = "Active Video Plugin"; - // - // GLideN64Tab - // - this.GLideN64Tab.Controls.Add(this.tabControl3); - this.GLideN64Tab.Location = new System.Drawing.Point(4, 22); - this.GLideN64Tab.Name = "GLideN64Tab"; - this.GLideN64Tab.Padding = new System.Windows.Forms.Padding(3); - this.GLideN64Tab.Size = new System.Drawing.Size(572, 343); - this.GLideN64Tab.TabIndex = 8; - this.GLideN64Tab.Text = "GLideN64"; - this.GLideN64Tab.UseVisualStyleBackColor = true; - // - // tabControl3 - // - this.tabControl3.Controls.Add(this.tabPage5); - this.tabControl3.Controls.Add(this.tabPage6); - this.tabControl3.Controls.Add(this.tabPage7); - this.tabControl3.Location = new System.Drawing.Point(0, 3); - this.tabControl3.Name = "tabControl3"; - this.tabControl3.SelectedIndex = 0; - this.tabControl3.Size = new System.Drawing.Size(572, 336); - this.tabControl3.TabIndex = 2; - // - // tabPage5 - // - this.tabPage5.Controls.Add(this.label88); - this.tabPage5.Controls.Add(this.GLideN64_GammaCorrectionLevel); - this.tabPage5.Controls.Add(this.GLideN64_ForceGammaCorrection); - this.tabPage5.Controls.Add(this.label81); - this.tabPage5.Controls.Add(this.GLideN64_MultiSampling); - this.tabPage5.Controls.Add(this.label80); - this.tabPage5.Controls.Add(this.GLideN64_blurStrength); - this.tabPage5.Controls.Add(this.label79); - this.tabPage5.Controls.Add(this.GLideN64_blurAmount); - this.tabPage5.Controls.Add(this.label78); - this.tabPage5.Controls.Add(this.GLideN64_bloomBlendMode); - this.tabPage5.Controls.Add(this.label77); - this.tabPage5.Controls.Add(this.GLideN64_bloomThresholdLevel); - this.tabPage5.Controls.Add(this.GLideN64_EnableBloom); - this.tabPage5.Controls.Add(this.GLideN64_FragmentDepthWrite); - this.tabPage5.Controls.Add(this.GLideN64_LegacyBlending); - this.tabPage5.Controls.Add(this.GLideN64_NativeResTexrects); - this.tabPage5.Controls.Add(this.label82); - this.tabPage5.Controls.Add(this.GLideN64_CorrectTexrectCoords); - this.tabPage5.Controls.Add(this.GLideN64_ShadersStorage); - this.tabPage5.Controls.Add(this.GLideN64_HWLighting); - this.tabPage5.Controls.Add(this.GLideN64_EnableNoise); - this.tabPage5.Controls.Add(this.GLideN64_EnableLOD); - this.tabPage5.Location = new System.Drawing.Point(4, 22); - this.tabPage5.Name = "tabPage5"; - this.tabPage5.Padding = new System.Windows.Forms.Padding(3); - this.tabPage5.Size = new System.Drawing.Size(564, 310); - this.tabPage5.TabIndex = 0; - this.tabPage5.Text = "General"; - this.tabPage5.UseVisualStyleBackColor = true; - // - // label88 - // - this.label88.AutoSize = true; - this.label88.Location = new System.Drawing.Point(306, 187); - this.label88.Name = "label88"; - this.label88.Size = new System.Drawing.Size(123, 13); - this.label88.TabIndex = 78; - this.label88.Text = "Gamma Correction Level"; - // - // GLideN64_GammaCorrectionLevel - // - this.GLideN64_GammaCorrectionLevel.Location = new System.Drawing.Point(435, 184); - this.GLideN64_GammaCorrectionLevel.Name = "GLideN64_GammaCorrectionLevel"; - this.GLideN64_GammaCorrectionLevel.Size = new System.Drawing.Size(78, 20); - this.GLideN64_GammaCorrectionLevel.TabIndex = 77; - // - // GLideN64_ForceGammaCorrection - // - this.GLideN64_ForceGammaCorrection.AutoSize = true; - this.GLideN64_ForceGammaCorrection.Location = new System.Drawing.Point(309, 161); - this.GLideN64_ForceGammaCorrection.Name = "GLideN64_ForceGammaCorrection"; - this.GLideN64_ForceGammaCorrection.Size = new System.Drawing.Size(143, 17); - this.GLideN64_ForceGammaCorrection.TabIndex = 70; - this.GLideN64_ForceGammaCorrection.Text = "Force Gamma Correction"; - this.GLideN64_ForceGammaCorrection.UseVisualStyleBackColor = true; - // - // label81 - // - this.label81.AutoSize = true; - this.label81.Location = new System.Drawing.Point(3, 210); - this.label81.Name = "label81"; - this.label81.Size = new System.Drawing.Size(75, 13); - this.label81.TabIndex = 69; - this.label81.Text = "MultiSampling:"; - // - // GLideN64_MultiSampling - // - this.GLideN64_MultiSampling.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_MultiSampling.FormattingEnabled = true; - this.GLideN64_MultiSampling.Items.AddRange(new object[] { + this.PluginComboBox.Location = new System.Drawing.Point(13, 91); + this.PluginComboBox.Name = "PluginComboBox"; + this.PluginComboBox.Size = new System.Drawing.Size(173, 21); + this.PluginComboBox.TabIndex = 5; + this.PluginComboBox.SelectedIndexChanged += new System.EventHandler(this.PluginComboBox_SelectedIndexChanged); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(10, 75); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(99, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Active Video Plugin"; + // + // GLideN64Tab + // + this.GLideN64Tab.Controls.Add(this.tabControl3); + this.GLideN64Tab.Location = new System.Drawing.Point(4, 22); + this.GLideN64Tab.Name = "GLideN64Tab"; + this.GLideN64Tab.Padding = new System.Windows.Forms.Padding(3); + this.GLideN64Tab.Size = new System.Drawing.Size(572, 343); + this.GLideN64Tab.TabIndex = 8; + this.GLideN64Tab.Text = "GLideN64"; + this.GLideN64Tab.UseVisualStyleBackColor = true; + // + // tabControl3 + // + this.tabControl3.Controls.Add(this.tabPage5); + this.tabControl3.Controls.Add(this.tabPage6); + this.tabControl3.Controls.Add(this.tabPage7); + this.tabControl3.Location = new System.Drawing.Point(0, 3); + this.tabControl3.Name = "tabControl3"; + this.tabControl3.SelectedIndex = 0; + this.tabControl3.Size = new System.Drawing.Size(572, 336); + this.tabControl3.TabIndex = 2; + // + // tabPage5 + // + this.tabPage5.Controls.Add(this.label88); + this.tabPage5.Controls.Add(this.GLideN64_GammaCorrectionLevel); + this.tabPage5.Controls.Add(this.GLideN64_ForceGammaCorrection); + this.tabPage5.Controls.Add(this.label81); + this.tabPage5.Controls.Add(this.GLideN64_MultiSampling); + this.tabPage5.Controls.Add(this.label80); + this.tabPage5.Controls.Add(this.GLideN64_blurStrength); + this.tabPage5.Controls.Add(this.label79); + this.tabPage5.Controls.Add(this.GLideN64_blurAmount); + this.tabPage5.Controls.Add(this.label78); + this.tabPage5.Controls.Add(this.GLideN64_bloomBlendMode); + this.tabPage5.Controls.Add(this.label77); + this.tabPage5.Controls.Add(this.GLideN64_bloomThresholdLevel); + this.tabPage5.Controls.Add(this.GLideN64_EnableBloom); + this.tabPage5.Controls.Add(this.GLideN64_FragmentDepthWrite); + this.tabPage5.Controls.Add(this.GLideN64_LegacyBlending); + this.tabPage5.Controls.Add(this.GLideN64_NativeResTexrects); + this.tabPage5.Controls.Add(this.label82); + this.tabPage5.Controls.Add(this.GLideN64_CorrectTexrectCoords); + this.tabPage5.Controls.Add(this.GLideN64_ShadersStorage); + this.tabPage5.Controls.Add(this.GLideN64_HWLighting); + this.tabPage5.Controls.Add(this.GLideN64_EnableNoise); + this.tabPage5.Controls.Add(this.GLideN64_EnableLOD); + this.tabPage5.Location = new System.Drawing.Point(4, 22); + this.tabPage5.Name = "tabPage5"; + this.tabPage5.Padding = new System.Windows.Forms.Padding(3); + this.tabPage5.Size = new System.Drawing.Size(564, 310); + this.tabPage5.TabIndex = 0; + this.tabPage5.Text = "General"; + this.tabPage5.UseVisualStyleBackColor = true; + // + // label88 + // + this.label88.AutoSize = true; + this.label88.Location = new System.Drawing.Point(306, 187); + this.label88.Name = "label88"; + this.label88.Size = new System.Drawing.Size(123, 13); + this.label88.TabIndex = 78; + this.label88.Text = "Gamma Correction Level"; + // + // GLideN64_GammaCorrectionLevel + // + this.GLideN64_GammaCorrectionLevel.Location = new System.Drawing.Point(435, 184); + this.GLideN64_GammaCorrectionLevel.Name = "GLideN64_GammaCorrectionLevel"; + this.GLideN64_GammaCorrectionLevel.Size = new System.Drawing.Size(78, 20); + this.GLideN64_GammaCorrectionLevel.TabIndex = 77; + // + // GLideN64_ForceGammaCorrection + // + this.GLideN64_ForceGammaCorrection.AutoSize = true; + this.GLideN64_ForceGammaCorrection.Location = new System.Drawing.Point(309, 161); + this.GLideN64_ForceGammaCorrection.Name = "GLideN64_ForceGammaCorrection"; + this.GLideN64_ForceGammaCorrection.Size = new System.Drawing.Size(143, 17); + this.GLideN64_ForceGammaCorrection.TabIndex = 70; + this.GLideN64_ForceGammaCorrection.Text = "Force Gamma Correction"; + this.GLideN64_ForceGammaCorrection.UseVisualStyleBackColor = true; + // + // label81 + // + this.label81.AutoSize = true; + this.label81.Location = new System.Drawing.Point(3, 210); + this.label81.Name = "label81"; + this.label81.Size = new System.Drawing.Size(75, 13); + this.label81.TabIndex = 69; + this.label81.Text = "MultiSampling:"; + // + // GLideN64_MultiSampling + // + this.GLideN64_MultiSampling.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_MultiSampling.FormattingEnabled = true; + this.GLideN64_MultiSampling.Items.AddRange(new object[] { "0", "2", "4", "8", "16"}); - this.GLideN64_MultiSampling.Location = new System.Drawing.Point(128, 207); - this.GLideN64_MultiSampling.Name = "GLideN64_MultiSampling"; - this.GLideN64_MultiSampling.Size = new System.Drawing.Size(138, 21); - this.GLideN64_MultiSampling.TabIndex = 68; - // - // label80 - // - this.label80.AutoSize = true; - this.label80.Location = new System.Drawing.Point(306, 122); - this.label80.Name = "label80"; - this.label80.Size = new System.Drawing.Size(71, 13); - this.label80.TabIndex = 67; - this.label80.Text = "Blur Strength:"; - // - // GLideN64_blurStrength - // - this.GLideN64_blurStrength.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_blurStrength.FormattingEnabled = true; - this.GLideN64_blurStrength.Items.AddRange(new object[] { + this.GLideN64_MultiSampling.Location = new System.Drawing.Point(128, 207); + this.GLideN64_MultiSampling.Name = "GLideN64_MultiSampling"; + this.GLideN64_MultiSampling.Size = new System.Drawing.Size(138, 21); + this.GLideN64_MultiSampling.TabIndex = 68; + // + // label80 + // + this.label80.AutoSize = true; + this.label80.Location = new System.Drawing.Point(306, 122); + this.label80.Name = "label80"; + this.label80.Size = new System.Drawing.Size(71, 13); + this.label80.TabIndex = 67; + this.label80.Text = "Blur Strength:"; + // + // GLideN64_blurStrength + // + this.GLideN64_blurStrength.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_blurStrength.FormattingEnabled = true; + this.GLideN64_blurStrength.Items.AddRange(new object[] { "10", "11", "12", @@ -790,25 +790,25 @@ "98", "99", "100"}); - this.GLideN64_blurStrength.Location = new System.Drawing.Point(430, 119); - this.GLideN64_blurStrength.Name = "GLideN64_blurStrength"; - this.GLideN64_blurStrength.Size = new System.Drawing.Size(60, 21); - this.GLideN64_blurStrength.TabIndex = 66; - // - // label79 - // - this.label79.AutoSize = true; - this.label79.Location = new System.Drawing.Point(306, 95); - this.label79.Name = "label79"; - this.label79.Size = new System.Drawing.Size(67, 13); - this.label79.TabIndex = 65; - this.label79.Text = "Blur Amount:"; - // - // GLideN64_blurAmount - // - this.GLideN64_blurAmount.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_blurAmount.FormattingEnabled = true; - this.GLideN64_blurAmount.Items.AddRange(new object[] { + this.GLideN64_blurStrength.Location = new System.Drawing.Point(430, 119); + this.GLideN64_blurStrength.Name = "GLideN64_blurStrength"; + this.GLideN64_blurStrength.Size = new System.Drawing.Size(60, 21); + this.GLideN64_blurStrength.TabIndex = 66; + // + // label79 + // + this.label79.AutoSize = true; + this.label79.Location = new System.Drawing.Point(306, 95); + this.label79.Name = "label79"; + this.label79.Size = new System.Drawing.Size(67, 13); + this.label79.TabIndex = 65; + this.label79.Text = "Blur Amount:"; + // + // GLideN64_blurAmount + // + this.GLideN64_blurAmount.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_blurAmount.FormattingEnabled = true; + this.GLideN64_blurAmount.Items.AddRange(new object[] { "2", "3", "4", @@ -818,327 +818,327 @@ "8", "9", "10"}); - this.GLideN64_blurAmount.Location = new System.Drawing.Point(430, 92); - this.GLideN64_blurAmount.Name = "GLideN64_blurAmount"; - this.GLideN64_blurAmount.Size = new System.Drawing.Size(60, 21); - this.GLideN64_blurAmount.TabIndex = 64; - // - // label78 - // - this.label78.AutoSize = true; - this.label78.Location = new System.Drawing.Point(306, 68); - this.label78.Name = "label78"; - this.label78.Size = new System.Drawing.Size(99, 13); - this.label78.TabIndex = 63; - this.label78.Text = "Bloom Blend Mode:"; - // - // GLideN64_bloomBlendMode - // - this.GLideN64_bloomBlendMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_bloomBlendMode.FormattingEnabled = true; - this.GLideN64_bloomBlendMode.Items.AddRange(new object[] { + this.GLideN64_blurAmount.Location = new System.Drawing.Point(430, 92); + this.GLideN64_blurAmount.Name = "GLideN64_blurAmount"; + this.GLideN64_blurAmount.Size = new System.Drawing.Size(60, 21); + this.GLideN64_blurAmount.TabIndex = 64; + // + // label78 + // + this.label78.AutoSize = true; + this.label78.Location = new System.Drawing.Point(306, 68); + this.label78.Name = "label78"; + this.label78.Size = new System.Drawing.Size(99, 13); + this.label78.TabIndex = 63; + this.label78.Text = "Bloom Blend Mode:"; + // + // GLideN64_bloomBlendMode + // + this.GLideN64_bloomBlendMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_bloomBlendMode.FormattingEnabled = true; + this.GLideN64_bloomBlendMode.Items.AddRange(new object[] { "Strong", "Mild", "Light"}); - this.GLideN64_bloomBlendMode.Location = new System.Drawing.Point(430, 65); - this.GLideN64_bloomBlendMode.Name = "GLideN64_bloomBlendMode"; - this.GLideN64_bloomBlendMode.Size = new System.Drawing.Size(128, 21); - this.GLideN64_bloomBlendMode.TabIndex = 62; - // - // label77 - // - this.label77.AutoSize = true; - this.label77.Location = new System.Drawing.Point(306, 41); - this.label77.Name = "label77"; - this.label77.Size = new System.Drawing.Size(118, 13); - this.label77.TabIndex = 61; - this.label77.Text = "Bloom Threshold Level:"; - // - // GLideN64_bloomThresholdLevel - // - this.GLideN64_bloomThresholdLevel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_bloomThresholdLevel.FormattingEnabled = true; - this.GLideN64_bloomThresholdLevel.Items.AddRange(new object[] { + this.GLideN64_bloomBlendMode.Location = new System.Drawing.Point(430, 65); + this.GLideN64_bloomBlendMode.Name = "GLideN64_bloomBlendMode"; + this.GLideN64_bloomBlendMode.Size = new System.Drawing.Size(128, 21); + this.GLideN64_bloomBlendMode.TabIndex = 62; + // + // label77 + // + this.label77.AutoSize = true; + this.label77.Location = new System.Drawing.Point(306, 41); + this.label77.Name = "label77"; + this.label77.Size = new System.Drawing.Size(118, 13); + this.label77.TabIndex = 61; + this.label77.Text = "Bloom Threshold Level:"; + // + // GLideN64_bloomThresholdLevel + // + this.GLideN64_bloomThresholdLevel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_bloomThresholdLevel.FormattingEnabled = true; + this.GLideN64_bloomThresholdLevel.Items.AddRange(new object[] { "2", "3", "4", "5", "6"}); - this.GLideN64_bloomThresholdLevel.Location = new System.Drawing.Point(430, 38); - this.GLideN64_bloomThresholdLevel.Name = "GLideN64_bloomThresholdLevel"; - this.GLideN64_bloomThresholdLevel.Size = new System.Drawing.Size(60, 21); - this.GLideN64_bloomThresholdLevel.TabIndex = 60; - // - // GLideN64_EnableBloom - // - this.GLideN64_EnableBloom.AutoSize = true; - this.GLideN64_EnableBloom.Location = new System.Drawing.Point(309, 19); - this.GLideN64_EnableBloom.Name = "GLideN64_EnableBloom"; - this.GLideN64_EnableBloom.Size = new System.Drawing.Size(91, 17); - this.GLideN64_EnableBloom.TabIndex = 59; - this.GLideN64_EnableBloom.Text = "Enable Bloom"; - this.GLideN64_EnableBloom.UseVisualStyleBackColor = true; - // - // GLideN64_FragmentDepthWrite - // - this.GLideN64_FragmentDepthWrite.AutoSize = true; - this.GLideN64_FragmentDepthWrite.Location = new System.Drawing.Point(6, 184); - this.GLideN64_FragmentDepthWrite.Name = "GLideN64_FragmentDepthWrite"; - this.GLideN64_FragmentDepthWrite.Size = new System.Drawing.Size(130, 17); - this.GLideN64_FragmentDepthWrite.TabIndex = 58; - this.GLideN64_FragmentDepthWrite.Text = "Fragment Depth Write"; - this.GLideN64_FragmentDepthWrite.UseVisualStyleBackColor = true; - // - // GLideN64_LegacyBlending - // - this.GLideN64_LegacyBlending.AutoSize = true; - this.GLideN64_LegacyBlending.Location = new System.Drawing.Point(6, 161); - this.GLideN64_LegacyBlending.Name = "GLideN64_LegacyBlending"; - this.GLideN64_LegacyBlending.Size = new System.Drawing.Size(105, 17); - this.GLideN64_LegacyBlending.TabIndex = 57; - this.GLideN64_LegacyBlending.Text = "Legacy Blending"; - this.GLideN64_LegacyBlending.UseVisualStyleBackColor = true; - // - // GLideN64_NativeResTexrects - // - this.GLideN64_NativeResTexrects.AutoSize = true; - this.GLideN64_NativeResTexrects.Location = new System.Drawing.Point(6, 138); - this.GLideN64_NativeResTexrects.Name = "GLideN64_NativeResTexrects"; - this.GLideN64_NativeResTexrects.Size = new System.Drawing.Size(123, 17); - this.GLideN64_NativeResTexrects.TabIndex = 56; - this.GLideN64_NativeResTexrects.Text = "Native Res Texrects"; - this.GLideN64_NativeResTexrects.UseVisualStyleBackColor = true; - // - // label82 - // - this.label82.AutoSize = true; - this.label82.Location = new System.Drawing.Point(3, 114); - this.label82.Name = "label82"; - this.label82.Size = new System.Drawing.Size(119, 13); - this.label82.TabIndex = 55; - this.label82.Text = "Correct Texrect Coords:"; - // - // GLideN64_CorrectTexrectCoords - // - this.GLideN64_CorrectTexrectCoords.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_CorrectTexrectCoords.FormattingEnabled = true; - this.GLideN64_CorrectTexrectCoords.Items.AddRange(new object[] { + this.GLideN64_bloomThresholdLevel.Location = new System.Drawing.Point(430, 38); + this.GLideN64_bloomThresholdLevel.Name = "GLideN64_bloomThresholdLevel"; + this.GLideN64_bloomThresholdLevel.Size = new System.Drawing.Size(60, 21); + this.GLideN64_bloomThresholdLevel.TabIndex = 60; + // + // GLideN64_EnableBloom + // + this.GLideN64_EnableBloom.AutoSize = true; + this.GLideN64_EnableBloom.Location = new System.Drawing.Point(309, 19); + this.GLideN64_EnableBloom.Name = "GLideN64_EnableBloom"; + this.GLideN64_EnableBloom.Size = new System.Drawing.Size(91, 17); + this.GLideN64_EnableBloom.TabIndex = 59; + this.GLideN64_EnableBloom.Text = "Enable Bloom"; + this.GLideN64_EnableBloom.UseVisualStyleBackColor = true; + // + // GLideN64_FragmentDepthWrite + // + this.GLideN64_FragmentDepthWrite.AutoSize = true; + this.GLideN64_FragmentDepthWrite.Location = new System.Drawing.Point(6, 184); + this.GLideN64_FragmentDepthWrite.Name = "GLideN64_FragmentDepthWrite"; + this.GLideN64_FragmentDepthWrite.Size = new System.Drawing.Size(130, 17); + this.GLideN64_FragmentDepthWrite.TabIndex = 58; + this.GLideN64_FragmentDepthWrite.Text = "Fragment Depth Write"; + this.GLideN64_FragmentDepthWrite.UseVisualStyleBackColor = true; + // + // GLideN64_LegacyBlending + // + this.GLideN64_LegacyBlending.AutoSize = true; + this.GLideN64_LegacyBlending.Location = new System.Drawing.Point(6, 161); + this.GLideN64_LegacyBlending.Name = "GLideN64_LegacyBlending"; + this.GLideN64_LegacyBlending.Size = new System.Drawing.Size(105, 17); + this.GLideN64_LegacyBlending.TabIndex = 57; + this.GLideN64_LegacyBlending.Text = "Legacy Blending"; + this.GLideN64_LegacyBlending.UseVisualStyleBackColor = true; + // + // GLideN64_NativeResTexrects + // + this.GLideN64_NativeResTexrects.AutoSize = true; + this.GLideN64_NativeResTexrects.Location = new System.Drawing.Point(6, 138); + this.GLideN64_NativeResTexrects.Name = "GLideN64_NativeResTexrects"; + this.GLideN64_NativeResTexrects.Size = new System.Drawing.Size(123, 17); + this.GLideN64_NativeResTexrects.TabIndex = 56; + this.GLideN64_NativeResTexrects.Text = "Native Res Texrects"; + this.GLideN64_NativeResTexrects.UseVisualStyleBackColor = true; + // + // label82 + // + this.label82.AutoSize = true; + this.label82.Location = new System.Drawing.Point(3, 114); + this.label82.Name = "label82"; + this.label82.Size = new System.Drawing.Size(119, 13); + this.label82.TabIndex = 55; + this.label82.Text = "Correct Texrect Coords:"; + // + // GLideN64_CorrectTexrectCoords + // + this.GLideN64_CorrectTexrectCoords.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_CorrectTexrectCoords.FormattingEnabled = true; + this.GLideN64_CorrectTexrectCoords.Items.AddRange(new object[] { "Off", "Auto", "Force"}); - this.GLideN64_CorrectTexrectCoords.Location = new System.Drawing.Point(128, 111); - this.GLideN64_CorrectTexrectCoords.Name = "GLideN64_CorrectTexrectCoords"; - this.GLideN64_CorrectTexrectCoords.Size = new System.Drawing.Size(138, 21); - this.GLideN64_CorrectTexrectCoords.TabIndex = 54; - // - // GLideN64_ShadersStorage - // - this.GLideN64_ShadersStorage.AutoSize = true; - this.GLideN64_ShadersStorage.Location = new System.Drawing.Point(6, 88); - this.GLideN64_ShadersStorage.Name = "GLideN64_ShadersStorage"; - this.GLideN64_ShadersStorage.Size = new System.Drawing.Size(103, 17); - this.GLideN64_ShadersStorage.TabIndex = 53; - this.GLideN64_ShadersStorage.Text = "Shaders storage"; - this.GLideN64_ShadersStorage.UseVisualStyleBackColor = true; - // - // GLideN64_HWLighting - // - this.GLideN64_HWLighting.AutoSize = true; - this.GLideN64_HWLighting.Location = new System.Drawing.Point(6, 65); - this.GLideN64_HWLighting.Name = "GLideN64_HWLighting"; - this.GLideN64_HWLighting.Size = new System.Drawing.Size(85, 17); - this.GLideN64_HWLighting.TabIndex = 52; - this.GLideN64_HWLighting.Text = "HW Lighting"; - this.GLideN64_HWLighting.UseVisualStyleBackColor = true; - // - // GLideN64_EnableNoise - // - this.GLideN64_EnableNoise.AutoSize = true; - this.GLideN64_EnableNoise.Location = new System.Drawing.Point(6, 42); - this.GLideN64_EnableNoise.Name = "GLideN64_EnableNoise"; - this.GLideN64_EnableNoise.Size = new System.Drawing.Size(126, 17); - this.GLideN64_EnableNoise.TabIndex = 25; - this.GLideN64_EnableNoise.Text = "Color noise emulation"; - this.GLideN64_EnableNoise.UseVisualStyleBackColor = true; - // - // GLideN64_EnableLOD - // - this.GLideN64_EnableLOD.AutoSize = true; - this.GLideN64_EnableLOD.Location = new System.Drawing.Point(6, 19); - this.GLideN64_EnableLOD.Name = "GLideN64_EnableLOD"; - this.GLideN64_EnableLOD.Size = new System.Drawing.Size(97, 17); - this.GLideN64_EnableLOD.TabIndex = 21; - this.GLideN64_EnableLOD.Text = "LOD Emulation"; - this.GLideN64_EnableLOD.UseVisualStyleBackColor = true; - // - // tabPage6 - // - this.tabPage6.Controls.Add(this.label87); - this.tabPage6.Controls.Add(this.GLideN64_txPath); - this.tabPage6.Controls.Add(this.GLideN64_txSaveCache); - this.tabPage6.Controls.Add(this.GLideN64_txCacheCompression); - this.tabPage6.Controls.Add(this.GLideN64_txForce16bpp); - this.tabPage6.Controls.Add(this.GLideN64_txDump); - this.tabPage6.Controls.Add(this.GLideN64_txHresAltCRC); - this.tabPage6.Controls.Add(this.GLideN64_txHiresFullAlphaChannel); - this.tabPage6.Controls.Add(this.GLideN64_txHiresEnable); - this.tabPage6.Controls.Add(this.label86); - this.tabPage6.Controls.Add(this.GLideN64_txCacheSize); - this.tabPage6.Controls.Add(this.GLideN64_txFilterIgnoreBG); - this.tabPage6.Controls.Add(this.GLideN64_txDeposterize); - this.tabPage6.Controls.Add(this.label85); - this.tabPage6.Controls.Add(this.GLideN64_txEnhancementMode); - this.tabPage6.Controls.Add(this.label84); - this.tabPage6.Controls.Add(this.GLideN64_txFilterMode); - this.tabPage6.Controls.Add(this.label83); - this.tabPage6.Controls.Add(this.GLideN64_CacheSize); - this.tabPage6.Controls.Add(this.label72); - this.tabPage6.Controls.Add(this.GLideN64_bilinearMode); - this.tabPage6.Controls.Add(this.GLideN64_MaxAnisotropy); - this.tabPage6.Location = new System.Drawing.Point(4, 22); - this.tabPage6.Name = "tabPage6"; - this.tabPage6.Padding = new System.Windows.Forms.Padding(3); - this.tabPage6.Size = new System.Drawing.Size(564, 310); - this.tabPage6.TabIndex = 1; - this.tabPage6.Text = "Texture settings"; - this.tabPage6.UseVisualStyleBackColor = true; - // - // label87 - // - this.label87.AutoSize = true; - this.label87.Location = new System.Drawing.Point(3, 232); - this.label87.Name = "label87"; - this.label87.Size = new System.Drawing.Size(70, 13); - this.label87.TabIndex = 76; - this.label87.Text = "Texture path:"; - // - // GLideN64_txPath - // - this.GLideN64_txPath.Location = new System.Drawing.Point(123, 229); - this.GLideN64_txPath.Name = "GLideN64_txPath"; - this.GLideN64_txPath.Size = new System.Drawing.Size(78, 20); - this.GLideN64_txPath.TabIndex = 75; - // - // GLideN64_txSaveCache - // - this.GLideN64_txSaveCache.AutoSize = true; - this.GLideN64_txSaveCache.Location = new System.Drawing.Point(328, 157); - this.GLideN64_txSaveCache.Name = "GLideN64_txSaveCache"; - this.GLideN64_txSaveCache.Size = new System.Drawing.Size(119, 17); - this.GLideN64_txSaveCache.TabIndex = 74; - this.GLideN64_txSaveCache.Text = "Save texture cache"; - this.GLideN64_txSaveCache.UseVisualStyleBackColor = true; - // - // GLideN64_txCacheCompression - // - this.GLideN64_txCacheCompression.AutoSize = true; - this.GLideN64_txCacheCompression.Location = new System.Drawing.Point(328, 134); - this.GLideN64_txCacheCompression.Name = "GLideN64_txCacheCompression"; - this.GLideN64_txCacheCompression.Size = new System.Drawing.Size(159, 17); - this.GLideN64_txCacheCompression.TabIndex = 73; - this.GLideN64_txCacheCompression.Text = "Texture Cache Compression"; - this.GLideN64_txCacheCompression.UseVisualStyleBackColor = true; - // - // GLideN64_txForce16bpp - // - this.GLideN64_txForce16bpp.AutoSize = true; - this.GLideN64_txForce16bpp.Location = new System.Drawing.Point(328, 111); - this.GLideN64_txForce16bpp.Name = "GLideN64_txForce16bpp"; - this.GLideN64_txForce16bpp.Size = new System.Drawing.Size(125, 17); - this.GLideN64_txForce16bpp.TabIndex = 72; - this.GLideN64_txForce16bpp.Text = "Texture Force 16bpp"; - this.GLideN64_txForce16bpp.UseVisualStyleBackColor = true; - // - // GLideN64_txDump - // - this.GLideN64_txDump.AutoSize = true; - this.GLideN64_txDump.Location = new System.Drawing.Point(328, 88); - this.GLideN64_txDump.Name = "GLideN64_txDump"; - this.GLideN64_txDump.Size = new System.Drawing.Size(91, 17); - this.GLideN64_txDump.TabIndex = 71; - this.GLideN64_txDump.Text = "Texture dump"; - this.GLideN64_txDump.UseVisualStyleBackColor = true; - // - // GLideN64_txHresAltCRC - // - this.GLideN64_txHresAltCRC.AutoSize = true; - this.GLideN64_txHresAltCRC.Location = new System.Drawing.Point(328, 65); - this.GLideN64_txHresAltCRC.Name = "GLideN64_txHresAltCRC"; - this.GLideN64_txHresAltCRC.Size = new System.Drawing.Size(88, 17); - this.GLideN64_txHresAltCRC.TabIndex = 70; - this.GLideN64_txHresAltCRC.Text = "Hres Alt CRC"; - this.GLideN64_txHresAltCRC.UseVisualStyleBackColor = true; - // - // GLideN64_txHiresFullAlphaChannel - // - this.GLideN64_txHiresFullAlphaChannel.AutoSize = true; - this.GLideN64_txHiresFullAlphaChannel.Location = new System.Drawing.Point(328, 42); - this.GLideN64_txHiresFullAlphaChannel.Name = "GLideN64_txHiresFullAlphaChannel"; - this.GLideN64_txHiresFullAlphaChannel.Size = new System.Drawing.Size(141, 17); - this.GLideN64_txHiresFullAlphaChannel.TabIndex = 69; - this.GLideN64_txHiresFullAlphaChannel.Text = "Hires Full Alpha Channel"; - this.GLideN64_txHiresFullAlphaChannel.UseVisualStyleBackColor = true; - // - // GLideN64_txHiresEnable - // - this.GLideN64_txHiresEnable.AutoSize = true; - this.GLideN64_txHiresEnable.Location = new System.Drawing.Point(328, 19); - this.GLideN64_txHiresEnable.Name = "GLideN64_txHiresEnable"; - this.GLideN64_txHiresEnable.Size = new System.Drawing.Size(94, 17); - this.GLideN64_txHiresEnable.TabIndex = 68; - this.GLideN64_txHiresEnable.Text = "Hires Textures"; - this.GLideN64_txHiresEnable.UseVisualStyleBackColor = true; - // - // label86 - // - this.label86.AutoSize = true; - this.label86.Location = new System.Drawing.Point(3, 198); - this.label86.Name = "label86"; - this.label86.Size = new System.Drawing.Size(98, 13); - this.label86.TabIndex = 67; - this.label86.Text = "Filtered cache size:"; - // - // GLideN64_txCacheSize - // - this.GLideN64_txCacheSize.Location = new System.Drawing.Point(123, 195); - this.GLideN64_txCacheSize.Name = "GLideN64_txCacheSize"; - this.GLideN64_txCacheSize.Size = new System.Drawing.Size(78, 20); - this.GLideN64_txCacheSize.TabIndex = 66; - // - // GLideN64_txFilterIgnoreBG - // - this.GLideN64_txFilterIgnoreBG.AutoSize = true; - this.GLideN64_txFilterIgnoreBG.Location = new System.Drawing.Point(6, 172); - this.GLideN64_txFilterIgnoreBG.Name = "GLideN64_txFilterIgnoreBG"; - this.GLideN64_txFilterIgnoreBG.Size = new System.Drawing.Size(99, 17); - this.GLideN64_txFilterIgnoreBG.TabIndex = 65; - this.GLideN64_txFilterIgnoreBG.Text = "Filter Ignore BG"; - this.GLideN64_txFilterIgnoreBG.UseVisualStyleBackColor = true; - // - // GLideN64_txDeposterize - // - this.GLideN64_txDeposterize.AutoSize = true; - this.GLideN64_txDeposterize.Location = new System.Drawing.Point(6, 149); - this.GLideN64_txDeposterize.Name = "GLideN64_txDeposterize"; - this.GLideN64_txDeposterize.Size = new System.Drawing.Size(82, 17); - this.GLideN64_txDeposterize.TabIndex = 64; - this.GLideN64_txDeposterize.Text = "Deposterize"; - this.GLideN64_txDeposterize.UseVisualStyleBackColor = true; - // - // label85 - // - this.label85.AutoSize = true; - this.label85.Location = new System.Drawing.Point(3, 125); - this.label85.Name = "label85"; - this.label85.Size = new System.Drawing.Size(114, 13); - this.label85.TabIndex = 63; - this.label85.Text = "Texture enhancement:"; - // - // GLideN64_txEnhancementMode - // - this.GLideN64_txEnhancementMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_txEnhancementMode.FormattingEnabled = true; - this.GLideN64_txEnhancementMode.Items.AddRange(new object[] { + this.GLideN64_CorrectTexrectCoords.Location = new System.Drawing.Point(128, 111); + this.GLideN64_CorrectTexrectCoords.Name = "GLideN64_CorrectTexrectCoords"; + this.GLideN64_CorrectTexrectCoords.Size = new System.Drawing.Size(138, 21); + this.GLideN64_CorrectTexrectCoords.TabIndex = 54; + // + // GLideN64_ShadersStorage + // + this.GLideN64_ShadersStorage.AutoSize = true; + this.GLideN64_ShadersStorage.Location = new System.Drawing.Point(6, 88); + this.GLideN64_ShadersStorage.Name = "GLideN64_ShadersStorage"; + this.GLideN64_ShadersStorage.Size = new System.Drawing.Size(103, 17); + this.GLideN64_ShadersStorage.TabIndex = 53; + this.GLideN64_ShadersStorage.Text = "Shaders storage"; + this.GLideN64_ShadersStorage.UseVisualStyleBackColor = true; + // + // GLideN64_HWLighting + // + this.GLideN64_HWLighting.AutoSize = true; + this.GLideN64_HWLighting.Location = new System.Drawing.Point(6, 65); + this.GLideN64_HWLighting.Name = "GLideN64_HWLighting"; + this.GLideN64_HWLighting.Size = new System.Drawing.Size(85, 17); + this.GLideN64_HWLighting.TabIndex = 52; + this.GLideN64_HWLighting.Text = "HW Lighting"; + this.GLideN64_HWLighting.UseVisualStyleBackColor = true; + // + // GLideN64_EnableNoise + // + this.GLideN64_EnableNoise.AutoSize = true; + this.GLideN64_EnableNoise.Location = new System.Drawing.Point(6, 42); + this.GLideN64_EnableNoise.Name = "GLideN64_EnableNoise"; + this.GLideN64_EnableNoise.Size = new System.Drawing.Size(126, 17); + this.GLideN64_EnableNoise.TabIndex = 25; + this.GLideN64_EnableNoise.Text = "Color noise emulation"; + this.GLideN64_EnableNoise.UseVisualStyleBackColor = true; + // + // GLideN64_EnableLOD + // + this.GLideN64_EnableLOD.AutoSize = true; + this.GLideN64_EnableLOD.Location = new System.Drawing.Point(6, 19); + this.GLideN64_EnableLOD.Name = "GLideN64_EnableLOD"; + this.GLideN64_EnableLOD.Size = new System.Drawing.Size(97, 17); + this.GLideN64_EnableLOD.TabIndex = 21; + this.GLideN64_EnableLOD.Text = "LOD Emulation"; + this.GLideN64_EnableLOD.UseVisualStyleBackColor = true; + // + // tabPage6 + // + this.tabPage6.Controls.Add(this.label87); + this.tabPage6.Controls.Add(this.GLideN64_txPath); + this.tabPage6.Controls.Add(this.GLideN64_txSaveCache); + this.tabPage6.Controls.Add(this.GLideN64_txCacheCompression); + this.tabPage6.Controls.Add(this.GLideN64_txForce16bpp); + this.tabPage6.Controls.Add(this.GLideN64_txDump); + this.tabPage6.Controls.Add(this.GLideN64_txHresAltCRC); + this.tabPage6.Controls.Add(this.GLideN64_txHiresFullAlphaChannel); + this.tabPage6.Controls.Add(this.GLideN64_txHiresEnable); + this.tabPage6.Controls.Add(this.label86); + this.tabPage6.Controls.Add(this.GLideN64_txCacheSize); + this.tabPage6.Controls.Add(this.GLideN64_txFilterIgnoreBG); + this.tabPage6.Controls.Add(this.GLideN64_txDeposterize); + this.tabPage6.Controls.Add(this.label85); + this.tabPage6.Controls.Add(this.GLideN64_txEnhancementMode); + this.tabPage6.Controls.Add(this.label84); + this.tabPage6.Controls.Add(this.GLideN64_txFilterMode); + this.tabPage6.Controls.Add(this.label83); + this.tabPage6.Controls.Add(this.GLideN64_CacheSize); + this.tabPage6.Controls.Add(this.label72); + this.tabPage6.Controls.Add(this.GLideN64_bilinearMode); + this.tabPage6.Controls.Add(this.GLideN64_MaxAnisotropy); + this.tabPage6.Location = new System.Drawing.Point(4, 22); + this.tabPage6.Name = "tabPage6"; + this.tabPage6.Padding = new System.Windows.Forms.Padding(3); + this.tabPage6.Size = new System.Drawing.Size(564, 310); + this.tabPage6.TabIndex = 1; + this.tabPage6.Text = "Texture settings"; + this.tabPage6.UseVisualStyleBackColor = true; + // + // label87 + // + this.label87.AutoSize = true; + this.label87.Location = new System.Drawing.Point(3, 232); + this.label87.Name = "label87"; + this.label87.Size = new System.Drawing.Size(70, 13); + this.label87.TabIndex = 76; + this.label87.Text = "Texture path:"; + // + // GLideN64_txPath + // + this.GLideN64_txPath.Location = new System.Drawing.Point(123, 229); + this.GLideN64_txPath.Name = "GLideN64_txPath"; + this.GLideN64_txPath.Size = new System.Drawing.Size(78, 20); + this.GLideN64_txPath.TabIndex = 75; + // + // GLideN64_txSaveCache + // + this.GLideN64_txSaveCache.AutoSize = true; + this.GLideN64_txSaveCache.Location = new System.Drawing.Point(328, 157); + this.GLideN64_txSaveCache.Name = "GLideN64_txSaveCache"; + this.GLideN64_txSaveCache.Size = new System.Drawing.Size(119, 17); + this.GLideN64_txSaveCache.TabIndex = 74; + this.GLideN64_txSaveCache.Text = "Save texture cache"; + this.GLideN64_txSaveCache.UseVisualStyleBackColor = true; + // + // GLideN64_txCacheCompression + // + this.GLideN64_txCacheCompression.AutoSize = true; + this.GLideN64_txCacheCompression.Location = new System.Drawing.Point(328, 134); + this.GLideN64_txCacheCompression.Name = "GLideN64_txCacheCompression"; + this.GLideN64_txCacheCompression.Size = new System.Drawing.Size(159, 17); + this.GLideN64_txCacheCompression.TabIndex = 73; + this.GLideN64_txCacheCompression.Text = "Texture Cache Compression"; + this.GLideN64_txCacheCompression.UseVisualStyleBackColor = true; + // + // GLideN64_txForce16bpp + // + this.GLideN64_txForce16bpp.AutoSize = true; + this.GLideN64_txForce16bpp.Location = new System.Drawing.Point(328, 111); + this.GLideN64_txForce16bpp.Name = "GLideN64_txForce16bpp"; + this.GLideN64_txForce16bpp.Size = new System.Drawing.Size(125, 17); + this.GLideN64_txForce16bpp.TabIndex = 72; + this.GLideN64_txForce16bpp.Text = "Texture Force 16bpp"; + this.GLideN64_txForce16bpp.UseVisualStyleBackColor = true; + // + // GLideN64_txDump + // + this.GLideN64_txDump.AutoSize = true; + this.GLideN64_txDump.Location = new System.Drawing.Point(328, 88); + this.GLideN64_txDump.Name = "GLideN64_txDump"; + this.GLideN64_txDump.Size = new System.Drawing.Size(91, 17); + this.GLideN64_txDump.TabIndex = 71; + this.GLideN64_txDump.Text = "Texture dump"; + this.GLideN64_txDump.UseVisualStyleBackColor = true; + // + // GLideN64_txHresAltCRC + // + this.GLideN64_txHresAltCRC.AutoSize = true; + this.GLideN64_txHresAltCRC.Location = new System.Drawing.Point(328, 65); + this.GLideN64_txHresAltCRC.Name = "GLideN64_txHresAltCRC"; + this.GLideN64_txHresAltCRC.Size = new System.Drawing.Size(88, 17); + this.GLideN64_txHresAltCRC.TabIndex = 70; + this.GLideN64_txHresAltCRC.Text = "Hres Alt CRC"; + this.GLideN64_txHresAltCRC.UseVisualStyleBackColor = true; + // + // GLideN64_txHiresFullAlphaChannel + // + this.GLideN64_txHiresFullAlphaChannel.AutoSize = true; + this.GLideN64_txHiresFullAlphaChannel.Location = new System.Drawing.Point(328, 42); + this.GLideN64_txHiresFullAlphaChannel.Name = "GLideN64_txHiresFullAlphaChannel"; + this.GLideN64_txHiresFullAlphaChannel.Size = new System.Drawing.Size(141, 17); + this.GLideN64_txHiresFullAlphaChannel.TabIndex = 69; + this.GLideN64_txHiresFullAlphaChannel.Text = "Hires Full Alpha Channel"; + this.GLideN64_txHiresFullAlphaChannel.UseVisualStyleBackColor = true; + // + // GLideN64_txHiresEnable + // + this.GLideN64_txHiresEnable.AutoSize = true; + this.GLideN64_txHiresEnable.Location = new System.Drawing.Point(328, 19); + this.GLideN64_txHiresEnable.Name = "GLideN64_txHiresEnable"; + this.GLideN64_txHiresEnable.Size = new System.Drawing.Size(94, 17); + this.GLideN64_txHiresEnable.TabIndex = 68; + this.GLideN64_txHiresEnable.Text = "Hires Textures"; + this.GLideN64_txHiresEnable.UseVisualStyleBackColor = true; + // + // label86 + // + this.label86.AutoSize = true; + this.label86.Location = new System.Drawing.Point(3, 198); + this.label86.Name = "label86"; + this.label86.Size = new System.Drawing.Size(98, 13); + this.label86.TabIndex = 67; + this.label86.Text = "Filtered cache size:"; + // + // GLideN64_txCacheSize + // + this.GLideN64_txCacheSize.Location = new System.Drawing.Point(123, 195); + this.GLideN64_txCacheSize.Name = "GLideN64_txCacheSize"; + this.GLideN64_txCacheSize.Size = new System.Drawing.Size(78, 20); + this.GLideN64_txCacheSize.TabIndex = 66; + // + // GLideN64_txFilterIgnoreBG + // + this.GLideN64_txFilterIgnoreBG.AutoSize = true; + this.GLideN64_txFilterIgnoreBG.Location = new System.Drawing.Point(6, 172); + this.GLideN64_txFilterIgnoreBG.Name = "GLideN64_txFilterIgnoreBG"; + this.GLideN64_txFilterIgnoreBG.Size = new System.Drawing.Size(99, 17); + this.GLideN64_txFilterIgnoreBG.TabIndex = 65; + this.GLideN64_txFilterIgnoreBG.Text = "Filter Ignore BG"; + this.GLideN64_txFilterIgnoreBG.UseVisualStyleBackColor = true; + // + // GLideN64_txDeposterize + // + this.GLideN64_txDeposterize.AutoSize = true; + this.GLideN64_txDeposterize.Location = new System.Drawing.Point(6, 149); + this.GLideN64_txDeposterize.Name = "GLideN64_txDeposterize"; + this.GLideN64_txDeposterize.Size = new System.Drawing.Size(82, 17); + this.GLideN64_txDeposterize.TabIndex = 64; + this.GLideN64_txDeposterize.Text = "Deposterize"; + this.GLideN64_txDeposterize.UseVisualStyleBackColor = true; + // + // label85 + // + this.label85.AutoSize = true; + this.label85.Location = new System.Drawing.Point(3, 125); + this.label85.Name = "label85"; + this.label85.Size = new System.Drawing.Size(114, 13); + this.label85.TabIndex = 63; + this.label85.Text = "Texture enhancement:"; + // + // GLideN64_txEnhancementMode + // + this.GLideN64_txEnhancementMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_txEnhancementMode.FormattingEnabled = true; + this.GLideN64_txEnhancementMode.Items.AddRange(new object[] { "None", "Store as is", "X2", @@ -1153,25 +1153,25 @@ "4xBRZ", "5xBRZ", "6xBRZ"}); - this.GLideN64_txEnhancementMode.Location = new System.Drawing.Point(123, 122); - this.GLideN64_txEnhancementMode.Name = "GLideN64_txEnhancementMode"; - this.GLideN64_txEnhancementMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_txEnhancementMode.TabIndex = 62; - // - // label84 - // - this.label84.AutoSize = true; - this.label84.Location = new System.Drawing.Point(3, 98); - this.label84.Name = "label84"; - this.label84.Size = new System.Drawing.Size(68, 13); - this.label84.TabIndex = 61; - this.label84.Text = "Texture filter:"; - // - // GLideN64_txFilterMode - // - this.GLideN64_txFilterMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_txFilterMode.FormattingEnabled = true; - this.GLideN64_txFilterMode.Items.AddRange(new object[] { + this.GLideN64_txEnhancementMode.Location = new System.Drawing.Point(123, 122); + this.GLideN64_txEnhancementMode.Name = "GLideN64_txEnhancementMode"; + this.GLideN64_txEnhancementMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_txEnhancementMode.TabIndex = 62; + // + // label84 + // + this.label84.AutoSize = true; + this.label84.Location = new System.Drawing.Point(3, 98); + this.label84.Name = "label84"; + this.label84.Size = new System.Drawing.Size(68, 13); + this.label84.TabIndex = 61; + this.label84.Text = "Texture filter:"; + // + // GLideN64_txFilterMode + // + this.GLideN64_txFilterMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_txFilterMode.FormattingEnabled = true; + this.GLideN64_txFilterMode.Items.AddRange(new object[] { "None", "Smooth filtering 1", "Smooth filtering 2", @@ -1179,810 +1179,812 @@ "Smooth filtering 4", "Sharp filtering 1", "Sharp filtering 2"}); - this.GLideN64_txFilterMode.Location = new System.Drawing.Point(123, 95); - this.GLideN64_txFilterMode.Name = "GLideN64_txFilterMode"; - this.GLideN64_txFilterMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_txFilterMode.TabIndex = 60; - // - // label83 - // - this.label83.AutoSize = true; - this.label83.Location = new System.Drawing.Point(3, 72); - this.label83.Name = "label83"; - this.label83.Size = new System.Drawing.Size(62, 13); - this.label83.TabIndex = 59; - this.label83.Text = "Cache size:"; - // - // GLideN64_CacheSize - // - this.GLideN64_CacheSize.Location = new System.Drawing.Point(123, 69); - this.GLideN64_CacheSize.Name = "GLideN64_CacheSize"; - this.GLideN64_CacheSize.Size = new System.Drawing.Size(78, 20); - this.GLideN64_CacheSize.TabIndex = 58; - // - // label72 - // - this.label72.AutoSize = true; - this.label72.Location = new System.Drawing.Point(3, 45); - this.label72.Name = "label72"; - this.label72.Size = new System.Drawing.Size(83, 13); - this.label72.TabIndex = 57; - this.label72.Text = "Bilinear Filtering:"; - // - // GLideN64_bilinearMode - // - this.GLideN64_bilinearMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_bilinearMode.FormattingEnabled = true; - this.GLideN64_bilinearMode.Items.AddRange(new object[] { + this.GLideN64_txFilterMode.Location = new System.Drawing.Point(123, 95); + this.GLideN64_txFilterMode.Name = "GLideN64_txFilterMode"; + this.GLideN64_txFilterMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_txFilterMode.TabIndex = 60; + // + // label83 + // + this.label83.AutoSize = true; + this.label83.Enabled = false; + this.label83.Location = new System.Drawing.Point(3, 72); + this.label83.Name = "label83"; + this.label83.Size = new System.Drawing.Size(62, 13); + this.label83.TabIndex = 59; + this.label83.Text = "Cache size:"; + // + // GLideN64_CacheSize + // + this.GLideN64_CacheSize.Enabled = false; + this.GLideN64_CacheSize.Location = new System.Drawing.Point(123, 69); + this.GLideN64_CacheSize.Name = "GLideN64_CacheSize"; + this.GLideN64_CacheSize.Size = new System.Drawing.Size(78, 20); + this.GLideN64_CacheSize.TabIndex = 58; + // + // label72 + // + this.label72.AutoSize = true; + this.label72.Location = new System.Drawing.Point(3, 45); + this.label72.Name = "label72"; + this.label72.Size = new System.Drawing.Size(83, 13); + this.label72.TabIndex = 57; + this.label72.Text = "Bilinear Filtering:"; + // + // GLideN64_bilinearMode + // + this.GLideN64_bilinearMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_bilinearMode.FormattingEnabled = true; + this.GLideN64_bilinearMode.Items.AddRange(new object[] { "N64 3point", "Standard"}); - this.GLideN64_bilinearMode.Location = new System.Drawing.Point(123, 42); - this.GLideN64_bilinearMode.Name = "GLideN64_bilinearMode"; - this.GLideN64_bilinearMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_bilinearMode.TabIndex = 56; - // - // GLideN64_MaxAnisotropy - // - this.GLideN64_MaxAnisotropy.AutoSize = true; - this.GLideN64_MaxAnisotropy.Location = new System.Drawing.Point(6, 19); - this.GLideN64_MaxAnisotropy.Name = "GLideN64_MaxAnisotropy"; - this.GLideN64_MaxAnisotropy.Size = new System.Drawing.Size(98, 17); - this.GLideN64_MaxAnisotropy.TabIndex = 3; - this.GLideN64_MaxAnisotropy.Text = "Max Anisotropy"; - this.GLideN64_MaxAnisotropy.UseVisualStyleBackColor = true; - // - // tabPage7 - // - this.tabPage7.Controls.Add(this.label89); - this.tabPage7.Controls.Add(this.GLideN64_UseNativeResolutionFactor); - this.tabPage7.Controls.Add(this.GLideN64_DisableFBInfo); - this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadDepthChunk); - this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadColorChunk); - this.tabPage7.Controls.Add(this.label76); - this.tabPage7.Controls.Add(this.GLideN64_BufferSwapMode); - this.tabPage7.Controls.Add(this.label75); - this.tabPage7.Controls.Add(this.GLideN64_AspectRatio); - this.tabPage7.Controls.Add(this.GLideN64_EnableN64DepthCompare); - this.tabPage7.Controls.Add(this.label74); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorToRDRAM); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyAuxiliaryToRDRAM); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorFromRDRAM); - this.tabPage7.Controls.Add(this.label73); - this.tabPage7.Controls.Add(this.GLideN64_EnableCopyDepthToRDRAM); - this.tabPage7.Controls.Add(this.GLideN64_EnableFBEmulation); - this.tabPage7.Controls.Add(this.GLideN64_UseDefaultHacks); - this.tabPage7.Location = new System.Drawing.Point(4, 22); - this.tabPage7.Name = "tabPage7"; - this.tabPage7.Size = new System.Drawing.Size(564, 310); - this.tabPage7.TabIndex = 2; - this.tabPage7.Text = "Frame buffer settings"; - this.tabPage7.UseVisualStyleBackColor = true; - // - // label89 - // - this.label89.AutoSize = true; - this.label89.Location = new System.Drawing.Point(3, 45); - this.label89.Name = "label89"; - this.label89.Size = new System.Drawing.Size(146, 13); - this.label89.TabIndex = 78; - this.label89.Text = "Use Native Resolution Factor"; - // - // GLideN64_UseNativeResolutionFactor - // - this.GLideN64_UseNativeResolutionFactor.Location = new System.Drawing.Point(155, 42); - this.GLideN64_UseNativeResolutionFactor.Name = "GLideN64_UseNativeResolutionFactor"; - this.GLideN64_UseNativeResolutionFactor.Size = new System.Drawing.Size(32, 20); - this.GLideN64_UseNativeResolutionFactor.TabIndex = 77; - // - // GLideN64_DisableFBInfo - // - this.GLideN64_DisableFBInfo.AutoSize = true; - this.GLideN64_DisableFBInfo.Location = new System.Drawing.Point(6, 115); - this.GLideN64_DisableFBInfo.Name = "GLideN64_DisableFBInfo"; - this.GLideN64_DisableFBInfo.Size = new System.Drawing.Size(98, 17); - this.GLideN64_DisableFBInfo.TabIndex = 76; - this.GLideN64_DisableFBInfo.Text = "Disable FB Info"; - this.GLideN64_DisableFBInfo.UseVisualStyleBackColor = true; - // - // GLideN64_FBInfoReadDepthChunk - // - this.GLideN64_FBInfoReadDepthChunk.AutoSize = true; - this.GLideN64_FBInfoReadDepthChunk.Location = new System.Drawing.Point(6, 92); - this.GLideN64_FBInfoReadDepthChunk.Name = "GLideN64_FBInfoReadDepthChunk"; - this.GLideN64_FBInfoReadDepthChunk.Size = new System.Drawing.Size(155, 17); - this.GLideN64_FBInfoReadDepthChunk.TabIndex = 75; - this.GLideN64_FBInfoReadDepthChunk.Text = "FB Info Read Depth Chunk"; - this.GLideN64_FBInfoReadDepthChunk.UseVisualStyleBackColor = true; - // - // GLideN64_FBInfoReadColorChunk - // - this.GLideN64_FBInfoReadColorChunk.AutoSize = true; - this.GLideN64_FBInfoReadColorChunk.Location = new System.Drawing.Point(6, 69); - this.GLideN64_FBInfoReadColorChunk.Name = "GLideN64_FBInfoReadColorChunk"; - this.GLideN64_FBInfoReadColorChunk.Size = new System.Drawing.Size(150, 17); - this.GLideN64_FBInfoReadColorChunk.TabIndex = 74; - this.GLideN64_FBInfoReadColorChunk.Text = "FB Info Read Color Chunk"; - this.GLideN64_FBInfoReadColorChunk.UseVisualStyleBackColor = true; - // - // label76 - // - this.label76.AutoSize = true; - this.label76.Location = new System.Drawing.Point(3, 171); - this.label76.Name = "label76"; - this.label76.Size = new System.Drawing.Size(95, 13); - this.label76.TabIndex = 72; - this.label76.Text = "Buffer Swap Mode"; - // - // GLideN64_BufferSwapMode - // - this.GLideN64_BufferSwapMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_BufferSwapMode.FormattingEnabled = true; - this.GLideN64_BufferSwapMode.Items.AddRange(new object[] { + this.GLideN64_bilinearMode.Location = new System.Drawing.Point(123, 42); + this.GLideN64_bilinearMode.Name = "GLideN64_bilinearMode"; + this.GLideN64_bilinearMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_bilinearMode.TabIndex = 56; + // + // GLideN64_MaxAnisotropy + // + this.GLideN64_MaxAnisotropy.AutoSize = true; + this.GLideN64_MaxAnisotropy.Location = new System.Drawing.Point(6, 19); + this.GLideN64_MaxAnisotropy.Name = "GLideN64_MaxAnisotropy"; + this.GLideN64_MaxAnisotropy.Size = new System.Drawing.Size(98, 17); + this.GLideN64_MaxAnisotropy.TabIndex = 3; + this.GLideN64_MaxAnisotropy.Text = "Max Anisotropy"; + this.GLideN64_MaxAnisotropy.UseVisualStyleBackColor = true; + // + // tabPage7 + // + this.tabPage7.Controls.Add(this.label89); + this.tabPage7.Controls.Add(this.GLideN64_UseNativeResolutionFactor); + this.tabPage7.Controls.Add(this.GLideN64_DisableFBInfo); + this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadDepthChunk); + this.tabPage7.Controls.Add(this.GLideN64_FBInfoReadColorChunk); + this.tabPage7.Controls.Add(this.label76); + this.tabPage7.Controls.Add(this.GLideN64_BufferSwapMode); + this.tabPage7.Controls.Add(this.label75); + this.tabPage7.Controls.Add(this.GLideN64_AspectRatio); + this.tabPage7.Controls.Add(this.GLideN64_EnableN64DepthCompare); + this.tabPage7.Controls.Add(this.label74); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorToRDRAM); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyAuxiliaryToRDRAM); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyColorFromRDRAM); + this.tabPage7.Controls.Add(this.label73); + this.tabPage7.Controls.Add(this.GLideN64_EnableCopyDepthToRDRAM); + this.tabPage7.Controls.Add(this.GLideN64_EnableFBEmulation); + this.tabPage7.Controls.Add(this.GLideN64_UseDefaultHacks); + this.tabPage7.Location = new System.Drawing.Point(4, 22); + this.tabPage7.Name = "tabPage7"; + this.tabPage7.Size = new System.Drawing.Size(564, 310); + this.tabPage7.TabIndex = 2; + this.tabPage7.Text = "Frame buffer settings"; + this.tabPage7.UseVisualStyleBackColor = true; + // + // label89 + // + this.label89.AutoSize = true; + this.label89.Location = new System.Drawing.Point(3, 45); + this.label89.Name = "label89"; + this.label89.Size = new System.Drawing.Size(146, 13); + this.label89.TabIndex = 78; + this.label89.Text = "Use Native Resolution Factor"; + // + // GLideN64_UseNativeResolutionFactor + // + this.GLideN64_UseNativeResolutionFactor.Location = new System.Drawing.Point(155, 42); + this.GLideN64_UseNativeResolutionFactor.Name = "GLideN64_UseNativeResolutionFactor"; + this.GLideN64_UseNativeResolutionFactor.Size = new System.Drawing.Size(32, 20); + this.GLideN64_UseNativeResolutionFactor.TabIndex = 77; + // + // GLideN64_DisableFBInfo + // + this.GLideN64_DisableFBInfo.AutoSize = true; + this.GLideN64_DisableFBInfo.Location = new System.Drawing.Point(6, 115); + this.GLideN64_DisableFBInfo.Name = "GLideN64_DisableFBInfo"; + this.GLideN64_DisableFBInfo.Size = new System.Drawing.Size(98, 17); + this.GLideN64_DisableFBInfo.TabIndex = 76; + this.GLideN64_DisableFBInfo.Text = "Disable FB Info"; + this.GLideN64_DisableFBInfo.UseVisualStyleBackColor = true; + // + // GLideN64_FBInfoReadDepthChunk + // + this.GLideN64_FBInfoReadDepthChunk.AutoSize = true; + this.GLideN64_FBInfoReadDepthChunk.Location = new System.Drawing.Point(6, 92); + this.GLideN64_FBInfoReadDepthChunk.Name = "GLideN64_FBInfoReadDepthChunk"; + this.GLideN64_FBInfoReadDepthChunk.Size = new System.Drawing.Size(155, 17); + this.GLideN64_FBInfoReadDepthChunk.TabIndex = 75; + this.GLideN64_FBInfoReadDepthChunk.Text = "FB Info Read Depth Chunk"; + this.GLideN64_FBInfoReadDepthChunk.UseVisualStyleBackColor = true; + // + // GLideN64_FBInfoReadColorChunk + // + this.GLideN64_FBInfoReadColorChunk.AutoSize = true; + this.GLideN64_FBInfoReadColorChunk.Location = new System.Drawing.Point(6, 69); + this.GLideN64_FBInfoReadColorChunk.Name = "GLideN64_FBInfoReadColorChunk"; + this.GLideN64_FBInfoReadColorChunk.Size = new System.Drawing.Size(150, 17); + this.GLideN64_FBInfoReadColorChunk.TabIndex = 74; + this.GLideN64_FBInfoReadColorChunk.Text = "FB Info Read Color Chunk"; + this.GLideN64_FBInfoReadColorChunk.UseVisualStyleBackColor = true; + // + // label76 + // + this.label76.AutoSize = true; + this.label76.Location = new System.Drawing.Point(3, 171); + this.label76.Name = "label76"; + this.label76.Size = new System.Drawing.Size(95, 13); + this.label76.TabIndex = 72; + this.label76.Text = "Buffer Swap Mode"; + // + // GLideN64_BufferSwapMode + // + this.GLideN64_BufferSwapMode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_BufferSwapMode.FormattingEnabled = true; + this.GLideN64_BufferSwapMode.Items.AddRange(new object[] { "On VI update call", "On VI origin change", "On buffer update"}); - this.GLideN64_BufferSwapMode.Location = new System.Drawing.Point(134, 168); - this.GLideN64_BufferSwapMode.Name = "GLideN64_BufferSwapMode"; - this.GLideN64_BufferSwapMode.Size = new System.Drawing.Size(138, 21); - this.GLideN64_BufferSwapMode.TabIndex = 71; - // - // label75 - // - this.label75.AutoSize = true; - this.label75.Location = new System.Drawing.Point(3, 144); - this.label75.Name = "label75"; - this.label75.Size = new System.Drawing.Size(68, 13); - this.label75.TabIndex = 70; - this.label75.Text = "Aspect Ratio"; - // - // GLideN64_AspectRatio - // - this.GLideN64_AspectRatio.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_AspectRatio.FormattingEnabled = true; - this.GLideN64_AspectRatio.Items.AddRange(new object[] { + this.GLideN64_BufferSwapMode.Location = new System.Drawing.Point(134, 168); + this.GLideN64_BufferSwapMode.Name = "GLideN64_BufferSwapMode"; + this.GLideN64_BufferSwapMode.Size = new System.Drawing.Size(138, 21); + this.GLideN64_BufferSwapMode.TabIndex = 71; + // + // label75 + // + this.label75.AutoSize = true; + this.label75.Location = new System.Drawing.Point(3, 144); + this.label75.Name = "label75"; + this.label75.Size = new System.Drawing.Size(68, 13); + this.label75.TabIndex = 70; + this.label75.Text = "Aspect Ratio"; + // + // GLideN64_AspectRatio + // + this.GLideN64_AspectRatio.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_AspectRatio.FormattingEnabled = true; + this.GLideN64_AspectRatio.Items.AddRange(new object[] { "Stretch", "Force 4:3", "Force 16:9", "Adjust"}); - this.GLideN64_AspectRatio.Location = new System.Drawing.Point(134, 141); - this.GLideN64_AspectRatio.Name = "GLideN64_AspectRatio"; - this.GLideN64_AspectRatio.Size = new System.Drawing.Size(138, 21); - this.GLideN64_AspectRatio.TabIndex = 69; - // - // GLideN64_EnableN64DepthCompare - // - this.GLideN64_EnableN64DepthCompare.AutoSize = true; - this.GLideN64_EnableN64DepthCompare.Location = new System.Drawing.Point(293, 142); - this.GLideN64_EnableN64DepthCompare.Name = "GLideN64_EnableN64DepthCompare"; - this.GLideN64_EnableN64DepthCompare.Size = new System.Drawing.Size(123, 17); - this.GLideN64_EnableN64DepthCompare.TabIndex = 68; - this.GLideN64_EnableN64DepthCompare.Text = "N64 Depth Compare"; - this.GLideN64_EnableN64DepthCompare.UseVisualStyleBackColor = true; - // - // label74 - // - this.label74.AutoSize = true; - this.label74.Location = new System.Drawing.Point(290, 118); - this.label74.Name = "label74"; - this.label74.Size = new System.Drawing.Size(120, 13); - this.label74.TabIndex = 67; - this.label74.Text = "Copy Color To RDRAM:"; - // - // GLideN64_EnableCopyColorToRDRAM - // - this.GLideN64_EnableCopyColorToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_EnableCopyColorToRDRAM.FormattingEnabled = true; - this.GLideN64_EnableCopyColorToRDRAM.Items.AddRange(new object[] { + this.GLideN64_AspectRatio.Location = new System.Drawing.Point(134, 141); + this.GLideN64_AspectRatio.Name = "GLideN64_AspectRatio"; + this.GLideN64_AspectRatio.Size = new System.Drawing.Size(138, 21); + this.GLideN64_AspectRatio.TabIndex = 69; + // + // GLideN64_EnableN64DepthCompare + // + this.GLideN64_EnableN64DepthCompare.AutoSize = true; + this.GLideN64_EnableN64DepthCompare.Location = new System.Drawing.Point(293, 142); + this.GLideN64_EnableN64DepthCompare.Name = "GLideN64_EnableN64DepthCompare"; + this.GLideN64_EnableN64DepthCompare.Size = new System.Drawing.Size(123, 17); + this.GLideN64_EnableN64DepthCompare.TabIndex = 68; + this.GLideN64_EnableN64DepthCompare.Text = "N64 Depth Compare"; + this.GLideN64_EnableN64DepthCompare.UseVisualStyleBackColor = true; + // + // label74 + // + this.label74.AutoSize = true; + this.label74.Location = new System.Drawing.Point(290, 118); + this.label74.Name = "label74"; + this.label74.Size = new System.Drawing.Size(120, 13); + this.label74.TabIndex = 67; + this.label74.Text = "Copy Color To RDRAM:"; + // + // GLideN64_EnableCopyColorToRDRAM + // + this.GLideN64_EnableCopyColorToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_EnableCopyColorToRDRAM.FormattingEnabled = true; + this.GLideN64_EnableCopyColorToRDRAM.Items.AddRange(new object[] { "Do not copy", "Copy in sync mode", "Copy in async mode"}); - this.GLideN64_EnableCopyColorToRDRAM.Location = new System.Drawing.Point(421, 115); - this.GLideN64_EnableCopyColorToRDRAM.Name = "GLideN64_EnableCopyColorToRDRAM"; - this.GLideN64_EnableCopyColorToRDRAM.Size = new System.Drawing.Size(138, 21); - this.GLideN64_EnableCopyColorToRDRAM.TabIndex = 66; - // - // GLideN64_EnableCopyAuxiliaryToRDRAM - // - this.GLideN64_EnableCopyAuxiliaryToRDRAM.AutoSize = true; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Location = new System.Drawing.Point(293, 92); - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Name = "GLideN64_EnableCopyAuxiliaryToRDRAM"; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Size = new System.Drawing.Size(150, 17); - this.GLideN64_EnableCopyAuxiliaryToRDRAM.TabIndex = 65; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.Text = "Copy Auxiliary To RDRAM"; - this.GLideN64_EnableCopyAuxiliaryToRDRAM.UseVisualStyleBackColor = true; - // - // GLideN64_EnableCopyColorFromRDRAM - // - this.GLideN64_EnableCopyColorFromRDRAM.AutoSize = true; - this.GLideN64_EnableCopyColorFromRDRAM.Location = new System.Drawing.Point(293, 69); - this.GLideN64_EnableCopyColorFromRDRAM.Name = "GLideN64_EnableCopyColorFromRDRAM"; - this.GLideN64_EnableCopyColorFromRDRAM.Size = new System.Drawing.Size(146, 17); - this.GLideN64_EnableCopyColorFromRDRAM.TabIndex = 64; - this.GLideN64_EnableCopyColorFromRDRAM.Text = "Copy Color From RDRAM"; - this.GLideN64_EnableCopyColorFromRDRAM.UseVisualStyleBackColor = true; - // - // label73 - // - this.label73.AutoSize = true; - this.label73.Location = new System.Drawing.Point(290, 45); - this.label73.Name = "label73"; - this.label73.Size = new System.Drawing.Size(125, 13); - this.label73.TabIndex = 63; - this.label73.Text = "Copy Depth To RDRAM:"; - // - // GLideN64_EnableCopyDepthToRDRAM - // - this.GLideN64_EnableCopyDepthToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.GLideN64_EnableCopyDepthToRDRAM.FormattingEnabled = true; - this.GLideN64_EnableCopyDepthToRDRAM.Items.AddRange(new object[] { + this.GLideN64_EnableCopyColorToRDRAM.Location = new System.Drawing.Point(421, 115); + this.GLideN64_EnableCopyColorToRDRAM.Name = "GLideN64_EnableCopyColorToRDRAM"; + this.GLideN64_EnableCopyColorToRDRAM.Size = new System.Drawing.Size(138, 21); + this.GLideN64_EnableCopyColorToRDRAM.TabIndex = 66; + // + // GLideN64_EnableCopyAuxiliaryToRDRAM + // + this.GLideN64_EnableCopyAuxiliaryToRDRAM.AutoSize = true; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Location = new System.Drawing.Point(293, 92); + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Name = "GLideN64_EnableCopyAuxiliaryToRDRAM"; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Size = new System.Drawing.Size(150, 17); + this.GLideN64_EnableCopyAuxiliaryToRDRAM.TabIndex = 65; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.Text = "Copy Auxiliary To RDRAM"; + this.GLideN64_EnableCopyAuxiliaryToRDRAM.UseVisualStyleBackColor = true; + // + // GLideN64_EnableCopyColorFromRDRAM + // + this.GLideN64_EnableCopyColorFromRDRAM.AutoSize = true; + this.GLideN64_EnableCopyColorFromRDRAM.Location = new System.Drawing.Point(293, 69); + this.GLideN64_EnableCopyColorFromRDRAM.Name = "GLideN64_EnableCopyColorFromRDRAM"; + this.GLideN64_EnableCopyColorFromRDRAM.Size = new System.Drawing.Size(146, 17); + this.GLideN64_EnableCopyColorFromRDRAM.TabIndex = 64; + this.GLideN64_EnableCopyColorFromRDRAM.Text = "Copy Color From RDRAM"; + this.GLideN64_EnableCopyColorFromRDRAM.UseVisualStyleBackColor = true; + // + // label73 + // + this.label73.AutoSize = true; + this.label73.Location = new System.Drawing.Point(290, 45); + this.label73.Name = "label73"; + this.label73.Size = new System.Drawing.Size(125, 13); + this.label73.TabIndex = 63; + this.label73.Text = "Copy Depth To RDRAM:"; + // + // GLideN64_EnableCopyDepthToRDRAM + // + this.GLideN64_EnableCopyDepthToRDRAM.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.GLideN64_EnableCopyDepthToRDRAM.FormattingEnabled = true; + this.GLideN64_EnableCopyDepthToRDRAM.Items.AddRange(new object[] { "Do not copy", "Copy from video memory", "Use software render"}); - this.GLideN64_EnableCopyDepthToRDRAM.Location = new System.Drawing.Point(421, 42); - this.GLideN64_EnableCopyDepthToRDRAM.Name = "GLideN64_EnableCopyDepthToRDRAM"; - this.GLideN64_EnableCopyDepthToRDRAM.Size = new System.Drawing.Size(138, 21); - this.GLideN64_EnableCopyDepthToRDRAM.TabIndex = 62; - // - // GLideN64_EnableFBEmulation - // - this.GLideN64_EnableFBEmulation.AutoSize = true; - this.GLideN64_EnableFBEmulation.Location = new System.Drawing.Point(6, 19); - this.GLideN64_EnableFBEmulation.Name = "GLideN64_EnableFBEmulation"; - this.GLideN64_EnableFBEmulation.Size = new System.Drawing.Size(124, 17); - this.GLideN64_EnableFBEmulation.TabIndex = 61; - this.GLideN64_EnableFBEmulation.Text = "Enable FB Emulation"; - this.GLideN64_EnableFBEmulation.UseVisualStyleBackColor = true; - // - // GLideN64_UseDefaultHacks - // - this.GLideN64_UseDefaultHacks.AutoSize = true; - this.GLideN64_UseDefaultHacks.Location = new System.Drawing.Point(293, 19); - this.GLideN64_UseDefaultHacks.Name = "GLideN64_UseDefaultHacks"; - this.GLideN64_UseDefaultHacks.Size = new System.Drawing.Size(165, 17); - this.GLideN64_UseDefaultHacks.TabIndex = 3; - this.GLideN64_UseDefaultHacks.Text = "Use defaults for current game"; - this.GLideN64_UseDefaultHacks.UseVisualStyleBackColor = true; - this.GLideN64_UseDefaultHacks.CheckedChanged += new System.EventHandler(this.GLideN64_UseDefaultHacks_CheckedChanged); - // - // Glide64mk2TabPage - // - this.Glide64mk2TabPage.Controls.Add(this.tabControl2); - this.Glide64mk2TabPage.Location = new System.Drawing.Point(4, 22); - this.Glide64mk2TabPage.Name = "Glide64mk2TabPage"; - this.Glide64mk2TabPage.Size = new System.Drawing.Size(572, 343); - this.Glide64mk2TabPage.TabIndex = 6; - this.Glide64mk2TabPage.Text = "Glide64mk2"; - this.Glide64mk2TabPage.UseVisualStyleBackColor = true; - // - // tabControl2 - // - this.tabControl2.Controls.Add(this.tabPage1); - this.tabControl2.Controls.Add(this.tabPage2); - this.tabControl2.Controls.Add(this.tabPage3); - this.tabControl2.Location = new System.Drawing.Point(0, 3); - this.tabControl2.Name = "tabControl2"; - this.tabControl2.SelectedIndex = 0; - this.tabControl2.Size = new System.Drawing.Size(572, 336); - this.tabControl2.TabIndex = 1; - // - // tabPage1 - // - this.tabPage1.Controls.Add(this.Glide64mk2_fb_get_info); - this.tabPage1.Controls.Add(this.Glide64mk2_fb_render); - this.tabPage1.Controls.Add(this.Glide64mk2_wrpAnisotropic); - this.tabPage1.Controls.Add(this.Glide64mk2_wrpFBO); - this.tabPage1.Controls.Add(this.label50); - this.tabPage1.Controls.Add(this.Glide64mk2_card_id); - this.tabPage1.Location = new System.Drawing.Point(4, 22); - this.tabPage1.Name = "tabPage1"; - this.tabPage1.Padding = new System.Windows.Forms.Padding(3); - this.tabPage1.Size = new System.Drawing.Size(564, 310); - this.tabPage1.TabIndex = 0; - this.tabPage1.Text = "General"; - this.tabPage1.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_get_info - // - this.Glide64mk2_fb_get_info.AutoSize = true; - this.Glide64mk2_fb_get_info.Location = new System.Drawing.Point(9, 132); - this.Glide64mk2_fb_get_info.Name = "Glide64mk2_fb_get_info"; - this.Glide64mk2_fb_get_info.Size = new System.Drawing.Size(119, 17); - this.Glide64mk2_fb_get_info.TabIndex = 53; - this.Glide64mk2_fb_get_info.Text = "Get framebuffer info"; - this.Glide64mk2_fb_get_info.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_render - // - this.Glide64mk2_fb_render.AutoSize = true; - this.Glide64mk2_fb_render.Location = new System.Drawing.Point(9, 109); - this.Glide64mk2_fb_render.Name = "Glide64mk2_fb_render"; - this.Glide64mk2_fb_render.Size = new System.Drawing.Size(118, 17); - this.Glide64mk2_fb_render.TabIndex = 52; - this.Glide64mk2_fb_render.Text = "Depth buffer render"; - this.Glide64mk2_fb_render.UseVisualStyleBackColor = true; - // - // Glide64mk2_wrpAnisotropic - // - this.Glide64mk2_wrpAnisotropic.AutoSize = true; - this.Glide64mk2_wrpAnisotropic.Location = new System.Drawing.Point(9, 86); - this.Glide64mk2_wrpAnisotropic.Name = "Glide64mk2_wrpAnisotropic"; - this.Glide64mk2_wrpAnisotropic.Size = new System.Drawing.Size(161, 17); - this.Glide64mk2_wrpAnisotropic.TabIndex = 25; - this.Glide64mk2_wrpAnisotropic.Text = "Wrapper Anisotropic Filtering"; - this.Glide64mk2_wrpAnisotropic.UseVisualStyleBackColor = true; - // - // Glide64mk2_wrpFBO - // - this.Glide64mk2_wrpFBO.AutoSize = true; - this.Glide64mk2_wrpFBO.Location = new System.Drawing.Point(9, 63); - this.Glide64mk2_wrpFBO.Name = "Glide64mk2_wrpFBO"; - this.Glide64mk2_wrpFBO.Size = new System.Drawing.Size(91, 17); - this.Glide64mk2_wrpFBO.TabIndex = 21; - this.Glide64mk2_wrpFBO.Text = "Wrapper FBO"; - this.Glide64mk2_wrpFBO.UseVisualStyleBackColor = true; - // - // label50 - // - this.label50.AutoSize = true; - this.label50.Location = new System.Drawing.Point(6, 30); - this.label50.Name = "label50"; - this.label50.Size = new System.Drawing.Size(46, 13); - this.label50.TabIndex = 5; - this.label50.Text = "Card ID:"; - // - // Glide64mk2_card_id - // - this.Glide64mk2_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_card_id.FormattingEnabled = true; - this.Glide64mk2_card_id.Items.AddRange(new object[] { + this.GLideN64_EnableCopyDepthToRDRAM.Location = new System.Drawing.Point(421, 42); + this.GLideN64_EnableCopyDepthToRDRAM.Name = "GLideN64_EnableCopyDepthToRDRAM"; + this.GLideN64_EnableCopyDepthToRDRAM.Size = new System.Drawing.Size(138, 21); + this.GLideN64_EnableCopyDepthToRDRAM.TabIndex = 62; + // + // GLideN64_EnableFBEmulation + // + this.GLideN64_EnableFBEmulation.AutoSize = true; + this.GLideN64_EnableFBEmulation.Location = new System.Drawing.Point(6, 19); + this.GLideN64_EnableFBEmulation.Name = "GLideN64_EnableFBEmulation"; + this.GLideN64_EnableFBEmulation.Size = new System.Drawing.Size(124, 17); + this.GLideN64_EnableFBEmulation.TabIndex = 61; + this.GLideN64_EnableFBEmulation.Text = "Enable FB Emulation"; + this.GLideN64_EnableFBEmulation.UseVisualStyleBackColor = true; + // + // GLideN64_UseDefaultHacks + // + this.GLideN64_UseDefaultHacks.AutoSize = true; + this.GLideN64_UseDefaultHacks.Location = new System.Drawing.Point(293, 19); + this.GLideN64_UseDefaultHacks.Name = "GLideN64_UseDefaultHacks"; + this.GLideN64_UseDefaultHacks.Size = new System.Drawing.Size(165, 17); + this.GLideN64_UseDefaultHacks.TabIndex = 3; + this.GLideN64_UseDefaultHacks.Text = "Use defaults for current game"; + this.GLideN64_UseDefaultHacks.UseVisualStyleBackColor = true; + this.GLideN64_UseDefaultHacks.CheckedChanged += new System.EventHandler(this.GLideN64_UseDefaultHacks_CheckedChanged); + // + // Glide64mk2TabPage + // + this.Glide64mk2TabPage.Controls.Add(this.tabControl2); + this.Glide64mk2TabPage.Location = new System.Drawing.Point(4, 22); + this.Glide64mk2TabPage.Name = "Glide64mk2TabPage"; + this.Glide64mk2TabPage.Size = new System.Drawing.Size(572, 343); + this.Glide64mk2TabPage.TabIndex = 6; + this.Glide64mk2TabPage.Text = "Glide64mk2"; + this.Glide64mk2TabPage.UseVisualStyleBackColor = true; + // + // tabControl2 + // + this.tabControl2.Controls.Add(this.tabPage1); + this.tabControl2.Controls.Add(this.tabPage2); + this.tabControl2.Controls.Add(this.tabPage3); + this.tabControl2.Location = new System.Drawing.Point(0, 3); + this.tabControl2.Name = "tabControl2"; + this.tabControl2.SelectedIndex = 0; + this.tabControl2.Size = new System.Drawing.Size(572, 336); + this.tabControl2.TabIndex = 1; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.Glide64mk2_fb_get_info); + this.tabPage1.Controls.Add(this.Glide64mk2_fb_render); + this.tabPage1.Controls.Add(this.Glide64mk2_wrpAnisotropic); + this.tabPage1.Controls.Add(this.Glide64mk2_wrpFBO); + this.tabPage1.Controls.Add(this.label50); + this.tabPage1.Controls.Add(this.Glide64mk2_card_id); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(564, 310); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "General"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_get_info + // + this.Glide64mk2_fb_get_info.AutoSize = true; + this.Glide64mk2_fb_get_info.Location = new System.Drawing.Point(9, 132); + this.Glide64mk2_fb_get_info.Name = "Glide64mk2_fb_get_info"; + this.Glide64mk2_fb_get_info.Size = new System.Drawing.Size(119, 17); + this.Glide64mk2_fb_get_info.TabIndex = 53; + this.Glide64mk2_fb_get_info.Text = "Get framebuffer info"; + this.Glide64mk2_fb_get_info.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_render + // + this.Glide64mk2_fb_render.AutoSize = true; + this.Glide64mk2_fb_render.Location = new System.Drawing.Point(9, 109); + this.Glide64mk2_fb_render.Name = "Glide64mk2_fb_render"; + this.Glide64mk2_fb_render.Size = new System.Drawing.Size(118, 17); + this.Glide64mk2_fb_render.TabIndex = 52; + this.Glide64mk2_fb_render.Text = "Depth buffer render"; + this.Glide64mk2_fb_render.UseVisualStyleBackColor = true; + // + // Glide64mk2_wrpAnisotropic + // + this.Glide64mk2_wrpAnisotropic.AutoSize = true; + this.Glide64mk2_wrpAnisotropic.Location = new System.Drawing.Point(9, 86); + this.Glide64mk2_wrpAnisotropic.Name = "Glide64mk2_wrpAnisotropic"; + this.Glide64mk2_wrpAnisotropic.Size = new System.Drawing.Size(161, 17); + this.Glide64mk2_wrpAnisotropic.TabIndex = 25; + this.Glide64mk2_wrpAnisotropic.Text = "Wrapper Anisotropic Filtering"; + this.Glide64mk2_wrpAnisotropic.UseVisualStyleBackColor = true; + // + // Glide64mk2_wrpFBO + // + this.Glide64mk2_wrpFBO.AutoSize = true; + this.Glide64mk2_wrpFBO.Location = new System.Drawing.Point(9, 63); + this.Glide64mk2_wrpFBO.Name = "Glide64mk2_wrpFBO"; + this.Glide64mk2_wrpFBO.Size = new System.Drawing.Size(91, 17); + this.Glide64mk2_wrpFBO.TabIndex = 21; + this.Glide64mk2_wrpFBO.Text = "Wrapper FBO"; + this.Glide64mk2_wrpFBO.UseVisualStyleBackColor = true; + // + // label50 + // + this.label50.AutoSize = true; + this.label50.Location = new System.Drawing.Point(6, 30); + this.label50.Name = "label50"; + this.label50.Size = new System.Drawing.Size(46, 13); + this.label50.TabIndex = 5; + this.label50.Text = "Card ID:"; + // + // Glide64mk2_card_id + // + this.Glide64mk2_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_card_id.FormattingEnabled = true; + this.Glide64mk2_card_id.Items.AddRange(new object[] { "0", "1", "2", "3"}); - this.Glide64mk2_card_id.Location = new System.Drawing.Point(58, 27); - this.Glide64mk2_card_id.Name = "Glide64mk2_card_id"; - this.Glide64mk2_card_id.Size = new System.Drawing.Size(36, 21); - this.Glide64mk2_card_id.TabIndex = 4; - // - // tabPage2 - // - this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_always); - this.tabPage2.Controls.Add(this.Glide64mk2_useless_is_useless); - this.tabPage2.Controls.Add(this.Glide64mk2_n64_z_scale); - this.tabPage2.Controls.Add(this.Glide64mk2_old_style_adither); - this.tabPage2.Controls.Add(this.Glide64mk2_zmode_compare_less); - this.tabPage2.Controls.Add(this.Glide64mk2_adjust_aspect); - this.tabPage2.Controls.Add(this.Glide64mk2_fast_crc); - this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmax); - this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmin); - this.tabPage2.Controls.Add(this.Glide64mk2_force_quad3d); - this.tabPage2.Controls.Add(this.Glide64mk2_pal230); - this.tabPage2.Controls.Add(this.Glide64mk2_texture_correction); - this.tabPage2.Controls.Add(this.Glide64mk2_correct_viewport); - this.tabPage2.Controls.Add(this.Glide64mk2_force_calc_sphere); - this.tabPage2.Controls.Add(this.Glide64mk2_use_sts1_only); - this.tabPage2.Controls.Add(this.Glide64mk2_optimize_texrect); - this.tabPage2.Controls.Add(this.Glide64mk2_increase_texrect_edge); - this.tabPage2.Controls.Add(this.Glide64mk2_ignore_aux_copy); - this.tabPage2.Controls.Add(this.Glide64mk2_hires_buf_clear); - this.tabPage2.Controls.Add(this.Glide64mk2_force_microcheck); - this.tabPage2.Controls.Add(this.Glide64mk2_fog); - this.tabPage2.Controls.Add(this.Glide64mk2_fb_smart); - this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_alpha); - this.tabPage2.Controls.Add(this.Glide64mk2_fb_hires); - this.tabPage2.Controls.Add(this.Glide64mk2_detect_cpu_write); - this.tabPage2.Controls.Add(this.Glide64mk2_decrease_fillrect_edge); - this.tabPage2.Controls.Add(this.Glide64mk2_buff_clear); - this.tabPage2.Controls.Add(this.Glide64mk2_alt_tex_size); - this.tabPage2.Controls.Add(this.Glide64mk2_UseDefaultHacks1); - this.tabPage2.Location = new System.Drawing.Point(4, 22); - this.tabPage2.Name = "tabPage2"; - this.tabPage2.Padding = new System.Windows.Forms.Padding(3); - this.tabPage2.Size = new System.Drawing.Size(564, 310); - this.tabPage2.TabIndex = 1; - this.tabPage2.Text = "Per Game Settings"; - this.tabPage2.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_read_always - // - this.Glide64mk2_fb_read_always.AutoSize = true; - this.Glide64mk2_fb_read_always.Location = new System.Drawing.Point(362, 39); - this.Glide64mk2_fb_read_always.Name = "Glide64mk2_fb_read_always"; - this.Glide64mk2_fb_read_always.Size = new System.Drawing.Size(164, 17); - this.Glide64mk2_fb_read_always.TabIndex = 48; - this.Glide64mk2_fb_read_always.Text = "Framebuffer read every frame"; - this.Glide64mk2_fb_read_always.UseVisualStyleBackColor = true; - // - // Glide64mk2_useless_is_useless - // - this.Glide64mk2_useless_is_useless.AutoSize = true; - this.Glide64mk2_useless_is_useless.Location = new System.Drawing.Point(362, 62); - this.Glide64mk2_useless_is_useless.Name = "Glide64mk2_useless_is_useless"; - this.Glide64mk2_useless_is_useless.Size = new System.Drawing.Size(115, 17); - this.Glide64mk2_useless_is_useless.TabIndex = 46; - this.Glide64mk2_useless_is_useless.Text = "useless_is_useless"; - this.Glide64mk2_useless_is_useless.UseVisualStyleBackColor = true; - // - // Glide64mk2_n64_z_scale - // - this.Glide64mk2_n64_z_scale.AutoSize = true; - this.Glide64mk2_n64_z_scale.Location = new System.Drawing.Point(362, 154); - this.Glide64mk2_n64_z_scale.Name = "Glide64mk2_n64_z_scale"; - this.Glide64mk2_n64_z_scale.Size = new System.Drawing.Size(86, 17); - this.Glide64mk2_n64_z_scale.TabIndex = 45; - this.Glide64mk2_n64_z_scale.Text = "N64 Z Scale"; - this.Glide64mk2_n64_z_scale.UseVisualStyleBackColor = true; - // - // Glide64mk2_old_style_adither - // - this.Glide64mk2_old_style_adither.AutoSize = true; - this.Glide64mk2_old_style_adither.Location = new System.Drawing.Point(362, 131); - this.Glide64mk2_old_style_adither.Name = "Glide64mk2_old_style_adither"; - this.Glide64mk2_old_style_adither.Size = new System.Drawing.Size(104, 17); - this.Glide64mk2_old_style_adither.TabIndex = 44; - this.Glide64mk2_old_style_adither.Text = "Old Style Adither"; - this.Glide64mk2_old_style_adither.UseVisualStyleBackColor = true; - // - // Glide64mk2_zmode_compare_less - // - this.Glide64mk2_zmode_compare_less.AutoSize = true; - this.Glide64mk2_zmode_compare_less.Location = new System.Drawing.Point(362, 108); - this.Glide64mk2_zmode_compare_less.Name = "Glide64mk2_zmode_compare_less"; - this.Glide64mk2_zmode_compare_less.Size = new System.Drawing.Size(127, 17); - this.Glide64mk2_zmode_compare_less.TabIndex = 43; - this.Glide64mk2_zmode_compare_less.Text = "Z mode compare less"; - this.Glide64mk2_zmode_compare_less.UseVisualStyleBackColor = true; - // - // Glide64mk2_adjust_aspect - // - this.Glide64mk2_adjust_aspect.AutoSize = true; - this.Glide64mk2_adjust_aspect.Location = new System.Drawing.Point(362, 85); - this.Glide64mk2_adjust_aspect.Name = "Glide64mk2_adjust_aspect"; - this.Glide64mk2_adjust_aspect.Size = new System.Drawing.Size(91, 17); - this.Glide64mk2_adjust_aspect.TabIndex = 42; - this.Glide64mk2_adjust_aspect.Text = "Adjust Aspect"; - this.Glide64mk2_adjust_aspect.UseVisualStyleBackColor = true; - // - // Glide64mk2_fast_crc - // - this.Glide64mk2_fast_crc.AutoSize = true; - this.Glide64mk2_fast_crc.Location = new System.Drawing.Point(192, 269); - this.Glide64mk2_fast_crc.Name = "Glide64mk2_fast_crc"; - this.Glide64mk2_fast_crc.Size = new System.Drawing.Size(71, 17); - this.Glide64mk2_fast_crc.TabIndex = 41; - this.Glide64mk2_fast_crc.Text = "Fast CRC"; - this.Glide64mk2_fast_crc.UseVisualStyleBackColor = true; - // - // Glide64mk2_clip_zmax - // - this.Glide64mk2_clip_zmax.AutoSize = true; - this.Glide64mk2_clip_zmax.Location = new System.Drawing.Point(192, 246); - this.Glide64mk2_clip_zmax.Name = "Glide64mk2_clip_zmax"; - this.Glide64mk2_clip_zmax.Size = new System.Drawing.Size(75, 17); - this.Glide64mk2_clip_zmax.TabIndex = 40; - this.Glide64mk2_clip_zmax.Text = "Clip Z max"; - this.Glide64mk2_clip_zmax.UseVisualStyleBackColor = true; - // - // Glide64mk2_clip_zmin - // - this.Glide64mk2_clip_zmin.AutoSize = true; - this.Glide64mk2_clip_zmin.Location = new System.Drawing.Point(192, 223); - this.Glide64mk2_clip_zmin.Name = "Glide64mk2_clip_zmin"; - this.Glide64mk2_clip_zmin.Size = new System.Drawing.Size(72, 17); - this.Glide64mk2_clip_zmin.TabIndex = 39; - this.Glide64mk2_clip_zmin.Text = "Clip Z min"; - this.Glide64mk2_clip_zmin.UseVisualStyleBackColor = true; - // - // Glide64mk2_force_quad3d - // - this.Glide64mk2_force_quad3d.AutoSize = true; - this.Glide64mk2_force_quad3d.Location = new System.Drawing.Point(192, 200); - this.Glide64mk2_force_quad3d.Name = "Glide64mk2_force_quad3d"; - this.Glide64mk2_force_quad3d.Size = new System.Drawing.Size(94, 17); - this.Glide64mk2_force_quad3d.TabIndex = 38; - this.Glide64mk2_force_quad3d.Text = "Force Quad3d"; - this.Glide64mk2_force_quad3d.UseVisualStyleBackColor = true; - // - // Glide64mk2_pal230 - // - this.Glide64mk2_pal230.AutoSize = true; - this.Glide64mk2_pal230.Location = new System.Drawing.Point(192, 177); - this.Glide64mk2_pal230.Name = "Glide64mk2_pal230"; - this.Glide64mk2_pal230.Size = new System.Drawing.Size(58, 17); - this.Glide64mk2_pal230.TabIndex = 37; - this.Glide64mk2_pal230.Text = "pal230"; - this.Glide64mk2_pal230.UseVisualStyleBackColor = true; - // - // Glide64mk2_texture_correction - // - this.Glide64mk2_texture_correction.AutoSize = true; - this.Glide64mk2_texture_correction.Location = new System.Drawing.Point(192, 154); - this.Glide64mk2_texture_correction.Name = "Glide64mk2_texture_correction"; - this.Glide64mk2_texture_correction.Size = new System.Drawing.Size(113, 17); - this.Glide64mk2_texture_correction.TabIndex = 36; - this.Glide64mk2_texture_correction.Text = "Texture Correction"; - this.Glide64mk2_texture_correction.UseVisualStyleBackColor = true; - // - // Glide64mk2_correct_viewport - // - this.Glide64mk2_correct_viewport.AutoSize = true; - this.Glide64mk2_correct_viewport.Location = new System.Drawing.Point(192, 131); - this.Glide64mk2_correct_viewport.Name = "Glide64mk2_correct_viewport"; - this.Glide64mk2_correct_viewport.Size = new System.Drawing.Size(104, 17); - this.Glide64mk2_correct_viewport.TabIndex = 35; - this.Glide64mk2_correct_viewport.Text = "Correct Viewport"; - this.Glide64mk2_correct_viewport.UseVisualStyleBackColor = true; - // - // Glide64mk2_force_calc_sphere - // - this.Glide64mk2_force_calc_sphere.AutoSize = true; - this.Glide64mk2_force_calc_sphere.Location = new System.Drawing.Point(192, 108); - this.Glide64mk2_force_calc_sphere.Name = "Glide64mk2_force_calc_sphere"; - this.Glide64mk2_force_calc_sphere.Size = new System.Drawing.Size(114, 17); - this.Glide64mk2_force_calc_sphere.TabIndex = 34; - this.Glide64mk2_force_calc_sphere.Text = "Force Calc Sphere"; - this.Glide64mk2_force_calc_sphere.UseVisualStyleBackColor = true; - // - // Glide64mk2_use_sts1_only - // - this.Glide64mk2_use_sts1_only.AutoSize = true; - this.Glide64mk2_use_sts1_only.Location = new System.Drawing.Point(192, 85); - this.Glide64mk2_use_sts1_only.Name = "Glide64mk2_use_sts1_only"; - this.Glide64mk2_use_sts1_only.Size = new System.Drawing.Size(89, 17); - this.Glide64mk2_use_sts1_only.TabIndex = 32; - this.Glide64mk2_use_sts1_only.Text = "Use sts1 only"; - this.Glide64mk2_use_sts1_only.UseVisualStyleBackColor = true; - // - // Glide64mk2_optimize_texrect - // - this.Glide64mk2_optimize_texrect.AutoSize = true; - this.Glide64mk2_optimize_texrect.Location = new System.Drawing.Point(192, 62); - this.Glide64mk2_optimize_texrect.Name = "Glide64mk2_optimize_texrect"; - this.Glide64mk2_optimize_texrect.Size = new System.Drawing.Size(101, 17); - this.Glide64mk2_optimize_texrect.TabIndex = 28; - this.Glide64mk2_optimize_texrect.Text = "Optimize texrect"; - this.Glide64mk2_optimize_texrect.UseVisualStyleBackColor = true; - // - // Glide64mk2_increase_texrect_edge - // - this.Glide64mk2_increase_texrect_edge.AutoSize = true; - this.Glide64mk2_increase_texrect_edge.Location = new System.Drawing.Point(192, 39); - this.Glide64mk2_increase_texrect_edge.Name = "Glide64mk2_increase_texrect_edge"; - this.Glide64mk2_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); - this.Glide64mk2_increase_texrect_edge.TabIndex = 27; - this.Glide64mk2_increase_texrect_edge.Text = "Increase texrect edge"; - this.Glide64mk2_increase_texrect_edge.UseVisualStyleBackColor = true; - // - // Glide64mk2_ignore_aux_copy - // - this.Glide64mk2_ignore_aux_copy.AutoSize = true; - this.Glide64mk2_ignore_aux_copy.Location = new System.Drawing.Point(6, 269); - this.Glide64mk2_ignore_aux_copy.Name = "Glide64mk2_ignore_aux_copy"; - this.Glide64mk2_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); - this.Glide64mk2_ignore_aux_copy.TabIndex = 24; - this.Glide64mk2_ignore_aux_copy.Text = "Ignore aux copy"; - this.Glide64mk2_ignore_aux_copy.UseVisualStyleBackColor = true; - // - // Glide64mk2_hires_buf_clear - // - this.Glide64mk2_hires_buf_clear.AutoSize = true; - this.Glide64mk2_hires_buf_clear.Location = new System.Drawing.Point(6, 246); - this.Glide64mk2_hires_buf_clear.Name = "Glide64mk2_hires_buf_clear"; - this.Glide64mk2_hires_buf_clear.Size = new System.Drawing.Size(109, 17); - this.Glide64mk2_hires_buf_clear.TabIndex = 23; - this.Glide64mk2_hires_buf_clear.Text = "Hi-res buffer clear"; - this.Glide64mk2_hires_buf_clear.UseVisualStyleBackColor = true; - // - // Glide64mk2_force_microcheck - // - this.Glide64mk2_force_microcheck.AutoSize = true; - this.Glide64mk2_force_microcheck.Location = new System.Drawing.Point(6, 223); - this.Glide64mk2_force_microcheck.Name = "Glide64mk2_force_microcheck"; - this.Glide64mk2_force_microcheck.Size = new System.Drawing.Size(165, 17); - this.Glide64mk2_force_microcheck.TabIndex = 22; - this.Glide64mk2_force_microcheck.Text = "Check microcode each frame"; - this.Glide64mk2_force_microcheck.UseVisualStyleBackColor = true; - // - // Glide64mk2_fog - // - this.Glide64mk2_fog.AutoSize = true; - this.Glide64mk2_fog.Location = new System.Drawing.Point(6, 200); - this.Glide64mk2_fog.Name = "Glide64mk2_fog"; - this.Glide64mk2_fog.Size = new System.Drawing.Size(86, 17); - this.Glide64mk2_fog.TabIndex = 20; - this.Glide64mk2_fog.Text = "Fog Enabled"; - this.Glide64mk2_fog.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_smart - // - this.Glide64mk2_fb_smart.AutoSize = true; - this.Glide64mk2_fb_smart.Location = new System.Drawing.Point(6, 177); - this.Glide64mk2_fb_smart.Name = "Glide64mk2_fb_smart"; - this.Glide64mk2_fb_smart.Size = new System.Drawing.Size(109, 17); - this.Glide64mk2_fb_smart.TabIndex = 18; - this.Glide64mk2_fb_smart.Text = "Smart framebuffer"; - this.Glide64mk2_fb_smart.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_read_alpha - // - this.Glide64mk2_fb_read_alpha.AutoSize = true; - this.Glide64mk2_fb_read_alpha.Location = new System.Drawing.Point(6, 154); - this.Glide64mk2_fb_read_alpha.Name = "Glide64mk2_fb_read_alpha"; - this.Glide64mk2_fb_read_alpha.Size = new System.Drawing.Size(135, 17); - this.Glide64mk2_fb_read_alpha.TabIndex = 17; - this.Glide64mk2_fb_read_alpha.Text = "Framebuffer read alpha"; - this.Glide64mk2_fb_read_alpha.UseVisualStyleBackColor = true; - // - // Glide64mk2_fb_hires - // - this.Glide64mk2_fb_hires.AutoSize = true; - this.Glide64mk2_fb_hires.Location = new System.Drawing.Point(6, 131); - this.Glide64mk2_fb_hires.Name = "Glide64mk2_fb_hires"; - this.Glide64mk2_fb_hires.Size = new System.Drawing.Size(109, 17); - this.Glide64mk2_fb_hires.TabIndex = 15; - this.Glide64mk2_fb_hires.Text = "Hi-res framebuffer"; - this.Glide64mk2_fb_hires.UseVisualStyleBackColor = true; - // - // Glide64mk2_detect_cpu_write - // - this.Glide64mk2_detect_cpu_write.AutoSize = true; - this.Glide64mk2_detect_cpu_write.Location = new System.Drawing.Point(6, 108); - this.Glide64mk2_detect_cpu_write.Name = "Glide64mk2_detect_cpu_write"; - this.Glide64mk2_detect_cpu_write.Size = new System.Drawing.Size(113, 17); - this.Glide64mk2_detect_cpu_write.TabIndex = 6; - this.Glide64mk2_detect_cpu_write.Text = "Detect CPU writes"; - this.Glide64mk2_detect_cpu_write.UseVisualStyleBackColor = true; - // - // Glide64mk2_decrease_fillrect_edge - // - this.Glide64mk2_decrease_fillrect_edge.AutoSize = true; - this.Glide64mk2_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); - this.Glide64mk2_decrease_fillrect_edge.Name = "Glide64mk2_decrease_fillrect_edge"; - this.Glide64mk2_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); - this.Glide64mk2_decrease_fillrect_edge.TabIndex = 5; - this.Glide64mk2_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; - this.Glide64mk2_decrease_fillrect_edge.UseVisualStyleBackColor = true; - // - // Glide64mk2_buff_clear - // - this.Glide64mk2_buff_clear.AutoSize = true; - this.Glide64mk2_buff_clear.Location = new System.Drawing.Point(6, 62); - this.Glide64mk2_buff_clear.Name = "Glide64mk2_buff_clear"; - this.Glide64mk2_buff_clear.Size = new System.Drawing.Size(72, 17); - this.Glide64mk2_buff_clear.TabIndex = 4; - this.Glide64mk2_buff_clear.Text = "Buff Clear"; - this.Glide64mk2_buff_clear.UseVisualStyleBackColor = true; - // - // Glide64mk2_alt_tex_size - // - this.Glide64mk2_alt_tex_size.AutoSize = true; - this.Glide64mk2_alt_tex_size.Location = new System.Drawing.Point(6, 39); - this.Glide64mk2_alt_tex_size.Name = "Glide64mk2_alt_tex_size"; - this.Glide64mk2_alt_tex_size.Size = new System.Drawing.Size(169, 17); - this.Glide64mk2_alt_tex_size.TabIndex = 3; - this.Glide64mk2_alt_tex_size.Text = "Alternate Texture Size Method"; - this.Glide64mk2_alt_tex_size.UseVisualStyleBackColor = true; - // - // Glide64mk2_UseDefaultHacks1 - // - this.Glide64mk2_UseDefaultHacks1.AutoSize = true; - this.Glide64mk2_UseDefaultHacks1.Location = new System.Drawing.Point(6, 6); - this.Glide64mk2_UseDefaultHacks1.Name = "Glide64mk2_UseDefaultHacks1"; - this.Glide64mk2_UseDefaultHacks1.Size = new System.Drawing.Size(165, 17); - this.Glide64mk2_UseDefaultHacks1.TabIndex = 2; - this.Glide64mk2_UseDefaultHacks1.Text = "Use defaults for current game"; - this.Glide64mk2_UseDefaultHacks1.UseVisualStyleBackColor = true; - this.Glide64mk2_UseDefaultHacks1.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks1_CheckedChanged); - // - // tabPage3 - // - this.tabPage3.Controls.Add(this.label46); - this.tabPage3.Controls.Add(this.Glide64mk2_read_back_to_screen); - this.tabPage3.Controls.Add(this.Glide64mk2_aspectmode); - this.tabPage3.Controls.Add(this.Glide64mk2_fb_crc_mode); - this.tabPage3.Controls.Add(this.label45); - this.tabPage3.Controls.Add(this.label3); - this.tabPage3.Controls.Add(this.label52); - this.tabPage3.Controls.Add(this.Glide64mk2_enable_hacks_for_game); - this.tabPage3.Controls.Add(this.label53); - this.tabPage3.Controls.Add(this.Glide64mk2_swapmode); - this.tabPage3.Controls.Add(this.label54); - this.tabPage3.Controls.Add(this.Glide64mk2_stipple_pattern); - this.tabPage3.Controls.Add(this.label55); - this.tabPage3.Controls.Add(this.Glide64mk2_stipple_mode); - this.tabPage3.Controls.Add(this.label56); - this.tabPage3.Controls.Add(this.Glide64mk2_lodmode); - this.tabPage3.Controls.Add(this.label58); - this.tabPage3.Controls.Add(this.Glide64mk2_filtering); - this.tabPage3.Controls.Add(this.Glide64mk2_UseDefaultHacks2); - this.tabPage3.Location = new System.Drawing.Point(4, 22); - this.tabPage3.Name = "tabPage3"; - this.tabPage3.Size = new System.Drawing.Size(564, 310); - this.tabPage3.TabIndex = 2; - this.tabPage3.Text = "More Per Game Settings"; - this.tabPage3.UseVisualStyleBackColor = true; - // - // label46 - // - this.label46.AutoSize = true; - this.label46.Location = new System.Drawing.Point(280, 145); - this.label46.Name = "label46"; - this.label46.Size = new System.Drawing.Size(110, 13); - this.label46.TabIndex = 60; - this.label46.Text = "Read back to screen:"; - // - // Glide64mk2_read_back_to_screen - // - this.Glide64mk2_read_back_to_screen.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_read_back_to_screen.FormattingEnabled = true; - this.Glide64mk2_read_back_to_screen.Items.AddRange(new object[] { + this.Glide64mk2_card_id.Location = new System.Drawing.Point(58, 27); + this.Glide64mk2_card_id.Name = "Glide64mk2_card_id"; + this.Glide64mk2_card_id.Size = new System.Drawing.Size(36, 21); + this.Glide64mk2_card_id.TabIndex = 4; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_always); + this.tabPage2.Controls.Add(this.Glide64mk2_useless_is_useless); + this.tabPage2.Controls.Add(this.Glide64mk2_n64_z_scale); + this.tabPage2.Controls.Add(this.Glide64mk2_old_style_adither); + this.tabPage2.Controls.Add(this.Glide64mk2_zmode_compare_less); + this.tabPage2.Controls.Add(this.Glide64mk2_adjust_aspect); + this.tabPage2.Controls.Add(this.Glide64mk2_fast_crc); + this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmax); + this.tabPage2.Controls.Add(this.Glide64mk2_clip_zmin); + this.tabPage2.Controls.Add(this.Glide64mk2_force_quad3d); + this.tabPage2.Controls.Add(this.Glide64mk2_pal230); + this.tabPage2.Controls.Add(this.Glide64mk2_texture_correction); + this.tabPage2.Controls.Add(this.Glide64mk2_correct_viewport); + this.tabPage2.Controls.Add(this.Glide64mk2_force_calc_sphere); + this.tabPage2.Controls.Add(this.Glide64mk2_use_sts1_only); + this.tabPage2.Controls.Add(this.Glide64mk2_optimize_texrect); + this.tabPage2.Controls.Add(this.Glide64mk2_increase_texrect_edge); + this.tabPage2.Controls.Add(this.Glide64mk2_ignore_aux_copy); + this.tabPage2.Controls.Add(this.Glide64mk2_hires_buf_clear); + this.tabPage2.Controls.Add(this.Glide64mk2_force_microcheck); + this.tabPage2.Controls.Add(this.Glide64mk2_fog); + this.tabPage2.Controls.Add(this.Glide64mk2_fb_smart); + this.tabPage2.Controls.Add(this.Glide64mk2_fb_read_alpha); + this.tabPage2.Controls.Add(this.Glide64mk2_fb_hires); + this.tabPage2.Controls.Add(this.Glide64mk2_detect_cpu_write); + this.tabPage2.Controls.Add(this.Glide64mk2_decrease_fillrect_edge); + this.tabPage2.Controls.Add(this.Glide64mk2_buff_clear); + this.tabPage2.Controls.Add(this.Glide64mk2_alt_tex_size); + this.tabPage2.Controls.Add(this.Glide64mk2_UseDefaultHacks1); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(564, 310); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "Per Game Settings"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_read_always + // + this.Glide64mk2_fb_read_always.AutoSize = true; + this.Glide64mk2_fb_read_always.Location = new System.Drawing.Point(362, 39); + this.Glide64mk2_fb_read_always.Name = "Glide64mk2_fb_read_always"; + this.Glide64mk2_fb_read_always.Size = new System.Drawing.Size(164, 17); + this.Glide64mk2_fb_read_always.TabIndex = 48; + this.Glide64mk2_fb_read_always.Text = "Framebuffer read every frame"; + this.Glide64mk2_fb_read_always.UseVisualStyleBackColor = true; + // + // Glide64mk2_useless_is_useless + // + this.Glide64mk2_useless_is_useless.AutoSize = true; + this.Glide64mk2_useless_is_useless.Location = new System.Drawing.Point(362, 62); + this.Glide64mk2_useless_is_useless.Name = "Glide64mk2_useless_is_useless"; + this.Glide64mk2_useless_is_useless.Size = new System.Drawing.Size(115, 17); + this.Glide64mk2_useless_is_useless.TabIndex = 46; + this.Glide64mk2_useless_is_useless.Text = "useless_is_useless"; + this.Glide64mk2_useless_is_useless.UseVisualStyleBackColor = true; + // + // Glide64mk2_n64_z_scale + // + this.Glide64mk2_n64_z_scale.AutoSize = true; + this.Glide64mk2_n64_z_scale.Location = new System.Drawing.Point(362, 154); + this.Glide64mk2_n64_z_scale.Name = "Glide64mk2_n64_z_scale"; + this.Glide64mk2_n64_z_scale.Size = new System.Drawing.Size(86, 17); + this.Glide64mk2_n64_z_scale.TabIndex = 45; + this.Glide64mk2_n64_z_scale.Text = "N64 Z Scale"; + this.Glide64mk2_n64_z_scale.UseVisualStyleBackColor = true; + // + // Glide64mk2_old_style_adither + // + this.Glide64mk2_old_style_adither.AutoSize = true; + this.Glide64mk2_old_style_adither.Location = new System.Drawing.Point(362, 131); + this.Glide64mk2_old_style_adither.Name = "Glide64mk2_old_style_adither"; + this.Glide64mk2_old_style_adither.Size = new System.Drawing.Size(104, 17); + this.Glide64mk2_old_style_adither.TabIndex = 44; + this.Glide64mk2_old_style_adither.Text = "Old Style Adither"; + this.Glide64mk2_old_style_adither.UseVisualStyleBackColor = true; + // + // Glide64mk2_zmode_compare_less + // + this.Glide64mk2_zmode_compare_less.AutoSize = true; + this.Glide64mk2_zmode_compare_less.Location = new System.Drawing.Point(362, 108); + this.Glide64mk2_zmode_compare_less.Name = "Glide64mk2_zmode_compare_less"; + this.Glide64mk2_zmode_compare_less.Size = new System.Drawing.Size(127, 17); + this.Glide64mk2_zmode_compare_less.TabIndex = 43; + this.Glide64mk2_zmode_compare_less.Text = "Z mode compare less"; + this.Glide64mk2_zmode_compare_less.UseVisualStyleBackColor = true; + // + // Glide64mk2_adjust_aspect + // + this.Glide64mk2_adjust_aspect.AutoSize = true; + this.Glide64mk2_adjust_aspect.Location = new System.Drawing.Point(362, 85); + this.Glide64mk2_adjust_aspect.Name = "Glide64mk2_adjust_aspect"; + this.Glide64mk2_adjust_aspect.Size = new System.Drawing.Size(91, 17); + this.Glide64mk2_adjust_aspect.TabIndex = 42; + this.Glide64mk2_adjust_aspect.Text = "Adjust Aspect"; + this.Glide64mk2_adjust_aspect.UseVisualStyleBackColor = true; + // + // Glide64mk2_fast_crc + // + this.Glide64mk2_fast_crc.AutoSize = true; + this.Glide64mk2_fast_crc.Location = new System.Drawing.Point(192, 269); + this.Glide64mk2_fast_crc.Name = "Glide64mk2_fast_crc"; + this.Glide64mk2_fast_crc.Size = new System.Drawing.Size(71, 17); + this.Glide64mk2_fast_crc.TabIndex = 41; + this.Glide64mk2_fast_crc.Text = "Fast CRC"; + this.Glide64mk2_fast_crc.UseVisualStyleBackColor = true; + // + // Glide64mk2_clip_zmax + // + this.Glide64mk2_clip_zmax.AutoSize = true; + this.Glide64mk2_clip_zmax.Location = new System.Drawing.Point(192, 246); + this.Glide64mk2_clip_zmax.Name = "Glide64mk2_clip_zmax"; + this.Glide64mk2_clip_zmax.Size = new System.Drawing.Size(75, 17); + this.Glide64mk2_clip_zmax.TabIndex = 40; + this.Glide64mk2_clip_zmax.Text = "Clip Z max"; + this.Glide64mk2_clip_zmax.UseVisualStyleBackColor = true; + // + // Glide64mk2_clip_zmin + // + this.Glide64mk2_clip_zmin.AutoSize = true; + this.Glide64mk2_clip_zmin.Location = new System.Drawing.Point(192, 223); + this.Glide64mk2_clip_zmin.Name = "Glide64mk2_clip_zmin"; + this.Glide64mk2_clip_zmin.Size = new System.Drawing.Size(72, 17); + this.Glide64mk2_clip_zmin.TabIndex = 39; + this.Glide64mk2_clip_zmin.Text = "Clip Z min"; + this.Glide64mk2_clip_zmin.UseVisualStyleBackColor = true; + // + // Glide64mk2_force_quad3d + // + this.Glide64mk2_force_quad3d.AutoSize = true; + this.Glide64mk2_force_quad3d.Location = new System.Drawing.Point(192, 200); + this.Glide64mk2_force_quad3d.Name = "Glide64mk2_force_quad3d"; + this.Glide64mk2_force_quad3d.Size = new System.Drawing.Size(94, 17); + this.Glide64mk2_force_quad3d.TabIndex = 38; + this.Glide64mk2_force_quad3d.Text = "Force Quad3d"; + this.Glide64mk2_force_quad3d.UseVisualStyleBackColor = true; + // + // Glide64mk2_pal230 + // + this.Glide64mk2_pal230.AutoSize = true; + this.Glide64mk2_pal230.Location = new System.Drawing.Point(192, 177); + this.Glide64mk2_pal230.Name = "Glide64mk2_pal230"; + this.Glide64mk2_pal230.Size = new System.Drawing.Size(58, 17); + this.Glide64mk2_pal230.TabIndex = 37; + this.Glide64mk2_pal230.Text = "pal230"; + this.Glide64mk2_pal230.UseVisualStyleBackColor = true; + // + // Glide64mk2_texture_correction + // + this.Glide64mk2_texture_correction.AutoSize = true; + this.Glide64mk2_texture_correction.Location = new System.Drawing.Point(192, 154); + this.Glide64mk2_texture_correction.Name = "Glide64mk2_texture_correction"; + this.Glide64mk2_texture_correction.Size = new System.Drawing.Size(113, 17); + this.Glide64mk2_texture_correction.TabIndex = 36; + this.Glide64mk2_texture_correction.Text = "Texture Correction"; + this.Glide64mk2_texture_correction.UseVisualStyleBackColor = true; + // + // Glide64mk2_correct_viewport + // + this.Glide64mk2_correct_viewport.AutoSize = true; + this.Glide64mk2_correct_viewport.Location = new System.Drawing.Point(192, 131); + this.Glide64mk2_correct_viewport.Name = "Glide64mk2_correct_viewport"; + this.Glide64mk2_correct_viewport.Size = new System.Drawing.Size(104, 17); + this.Glide64mk2_correct_viewport.TabIndex = 35; + this.Glide64mk2_correct_viewport.Text = "Correct Viewport"; + this.Glide64mk2_correct_viewport.UseVisualStyleBackColor = true; + // + // Glide64mk2_force_calc_sphere + // + this.Glide64mk2_force_calc_sphere.AutoSize = true; + this.Glide64mk2_force_calc_sphere.Location = new System.Drawing.Point(192, 108); + this.Glide64mk2_force_calc_sphere.Name = "Glide64mk2_force_calc_sphere"; + this.Glide64mk2_force_calc_sphere.Size = new System.Drawing.Size(114, 17); + this.Glide64mk2_force_calc_sphere.TabIndex = 34; + this.Glide64mk2_force_calc_sphere.Text = "Force Calc Sphere"; + this.Glide64mk2_force_calc_sphere.UseVisualStyleBackColor = true; + // + // Glide64mk2_use_sts1_only + // + this.Glide64mk2_use_sts1_only.AutoSize = true; + this.Glide64mk2_use_sts1_only.Location = new System.Drawing.Point(192, 85); + this.Glide64mk2_use_sts1_only.Name = "Glide64mk2_use_sts1_only"; + this.Glide64mk2_use_sts1_only.Size = new System.Drawing.Size(89, 17); + this.Glide64mk2_use_sts1_only.TabIndex = 32; + this.Glide64mk2_use_sts1_only.Text = "Use sts1 only"; + this.Glide64mk2_use_sts1_only.UseVisualStyleBackColor = true; + // + // Glide64mk2_optimize_texrect + // + this.Glide64mk2_optimize_texrect.AutoSize = true; + this.Glide64mk2_optimize_texrect.Location = new System.Drawing.Point(192, 62); + this.Glide64mk2_optimize_texrect.Name = "Glide64mk2_optimize_texrect"; + this.Glide64mk2_optimize_texrect.Size = new System.Drawing.Size(101, 17); + this.Glide64mk2_optimize_texrect.TabIndex = 28; + this.Glide64mk2_optimize_texrect.Text = "Optimize texrect"; + this.Glide64mk2_optimize_texrect.UseVisualStyleBackColor = true; + // + // Glide64mk2_increase_texrect_edge + // + this.Glide64mk2_increase_texrect_edge.AutoSize = true; + this.Glide64mk2_increase_texrect_edge.Location = new System.Drawing.Point(192, 39); + this.Glide64mk2_increase_texrect_edge.Name = "Glide64mk2_increase_texrect_edge"; + this.Glide64mk2_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); + this.Glide64mk2_increase_texrect_edge.TabIndex = 27; + this.Glide64mk2_increase_texrect_edge.Text = "Increase texrect edge"; + this.Glide64mk2_increase_texrect_edge.UseVisualStyleBackColor = true; + // + // Glide64mk2_ignore_aux_copy + // + this.Glide64mk2_ignore_aux_copy.AutoSize = true; + this.Glide64mk2_ignore_aux_copy.Location = new System.Drawing.Point(6, 269); + this.Glide64mk2_ignore_aux_copy.Name = "Glide64mk2_ignore_aux_copy"; + this.Glide64mk2_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); + this.Glide64mk2_ignore_aux_copy.TabIndex = 24; + this.Glide64mk2_ignore_aux_copy.Text = "Ignore aux copy"; + this.Glide64mk2_ignore_aux_copy.UseVisualStyleBackColor = true; + // + // Glide64mk2_hires_buf_clear + // + this.Glide64mk2_hires_buf_clear.AutoSize = true; + this.Glide64mk2_hires_buf_clear.Location = new System.Drawing.Point(6, 246); + this.Glide64mk2_hires_buf_clear.Name = "Glide64mk2_hires_buf_clear"; + this.Glide64mk2_hires_buf_clear.Size = new System.Drawing.Size(109, 17); + this.Glide64mk2_hires_buf_clear.TabIndex = 23; + this.Glide64mk2_hires_buf_clear.Text = "Hi-res buffer clear"; + this.Glide64mk2_hires_buf_clear.UseVisualStyleBackColor = true; + // + // Glide64mk2_force_microcheck + // + this.Glide64mk2_force_microcheck.AutoSize = true; + this.Glide64mk2_force_microcheck.Location = new System.Drawing.Point(6, 223); + this.Glide64mk2_force_microcheck.Name = "Glide64mk2_force_microcheck"; + this.Glide64mk2_force_microcheck.Size = new System.Drawing.Size(165, 17); + this.Glide64mk2_force_microcheck.TabIndex = 22; + this.Glide64mk2_force_microcheck.Text = "Check microcode each frame"; + this.Glide64mk2_force_microcheck.UseVisualStyleBackColor = true; + // + // Glide64mk2_fog + // + this.Glide64mk2_fog.AutoSize = true; + this.Glide64mk2_fog.Location = new System.Drawing.Point(6, 200); + this.Glide64mk2_fog.Name = "Glide64mk2_fog"; + this.Glide64mk2_fog.Size = new System.Drawing.Size(86, 17); + this.Glide64mk2_fog.TabIndex = 20; + this.Glide64mk2_fog.Text = "Fog Enabled"; + this.Glide64mk2_fog.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_smart + // + this.Glide64mk2_fb_smart.AutoSize = true; + this.Glide64mk2_fb_smart.Location = new System.Drawing.Point(6, 177); + this.Glide64mk2_fb_smart.Name = "Glide64mk2_fb_smart"; + this.Glide64mk2_fb_smart.Size = new System.Drawing.Size(109, 17); + this.Glide64mk2_fb_smart.TabIndex = 18; + this.Glide64mk2_fb_smart.Text = "Smart framebuffer"; + this.Glide64mk2_fb_smart.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_read_alpha + // + this.Glide64mk2_fb_read_alpha.AutoSize = true; + this.Glide64mk2_fb_read_alpha.Location = new System.Drawing.Point(6, 154); + this.Glide64mk2_fb_read_alpha.Name = "Glide64mk2_fb_read_alpha"; + this.Glide64mk2_fb_read_alpha.Size = new System.Drawing.Size(135, 17); + this.Glide64mk2_fb_read_alpha.TabIndex = 17; + this.Glide64mk2_fb_read_alpha.Text = "Framebuffer read alpha"; + this.Glide64mk2_fb_read_alpha.UseVisualStyleBackColor = true; + // + // Glide64mk2_fb_hires + // + this.Glide64mk2_fb_hires.AutoSize = true; + this.Glide64mk2_fb_hires.Location = new System.Drawing.Point(6, 131); + this.Glide64mk2_fb_hires.Name = "Glide64mk2_fb_hires"; + this.Glide64mk2_fb_hires.Size = new System.Drawing.Size(109, 17); + this.Glide64mk2_fb_hires.TabIndex = 15; + this.Glide64mk2_fb_hires.Text = "Hi-res framebuffer"; + this.Glide64mk2_fb_hires.UseVisualStyleBackColor = true; + // + // Glide64mk2_detect_cpu_write + // + this.Glide64mk2_detect_cpu_write.AutoSize = true; + this.Glide64mk2_detect_cpu_write.Location = new System.Drawing.Point(6, 108); + this.Glide64mk2_detect_cpu_write.Name = "Glide64mk2_detect_cpu_write"; + this.Glide64mk2_detect_cpu_write.Size = new System.Drawing.Size(113, 17); + this.Glide64mk2_detect_cpu_write.TabIndex = 6; + this.Glide64mk2_detect_cpu_write.Text = "Detect CPU writes"; + this.Glide64mk2_detect_cpu_write.UseVisualStyleBackColor = true; + // + // Glide64mk2_decrease_fillrect_edge + // + this.Glide64mk2_decrease_fillrect_edge.AutoSize = true; + this.Glide64mk2_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); + this.Glide64mk2_decrease_fillrect_edge.Name = "Glide64mk2_decrease_fillrect_edge"; + this.Glide64mk2_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); + this.Glide64mk2_decrease_fillrect_edge.TabIndex = 5; + this.Glide64mk2_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; + this.Glide64mk2_decrease_fillrect_edge.UseVisualStyleBackColor = true; + // + // Glide64mk2_buff_clear + // + this.Glide64mk2_buff_clear.AutoSize = true; + this.Glide64mk2_buff_clear.Location = new System.Drawing.Point(6, 62); + this.Glide64mk2_buff_clear.Name = "Glide64mk2_buff_clear"; + this.Glide64mk2_buff_clear.Size = new System.Drawing.Size(72, 17); + this.Glide64mk2_buff_clear.TabIndex = 4; + this.Glide64mk2_buff_clear.Text = "Buff Clear"; + this.Glide64mk2_buff_clear.UseVisualStyleBackColor = true; + // + // Glide64mk2_alt_tex_size + // + this.Glide64mk2_alt_tex_size.AutoSize = true; + this.Glide64mk2_alt_tex_size.Location = new System.Drawing.Point(6, 39); + this.Glide64mk2_alt_tex_size.Name = "Glide64mk2_alt_tex_size"; + this.Glide64mk2_alt_tex_size.Size = new System.Drawing.Size(169, 17); + this.Glide64mk2_alt_tex_size.TabIndex = 3; + this.Glide64mk2_alt_tex_size.Text = "Alternate Texture Size Method"; + this.Glide64mk2_alt_tex_size.UseVisualStyleBackColor = true; + // + // Glide64mk2_UseDefaultHacks1 + // + this.Glide64mk2_UseDefaultHacks1.AutoSize = true; + this.Glide64mk2_UseDefaultHacks1.Location = new System.Drawing.Point(6, 6); + this.Glide64mk2_UseDefaultHacks1.Name = "Glide64mk2_UseDefaultHacks1"; + this.Glide64mk2_UseDefaultHacks1.Size = new System.Drawing.Size(165, 17); + this.Glide64mk2_UseDefaultHacks1.TabIndex = 2; + this.Glide64mk2_UseDefaultHacks1.Text = "Use defaults for current game"; + this.Glide64mk2_UseDefaultHacks1.UseVisualStyleBackColor = true; + this.Glide64mk2_UseDefaultHacks1.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks1_CheckedChanged); + // + // tabPage3 + // + this.tabPage3.Controls.Add(this.label46); + this.tabPage3.Controls.Add(this.Glide64mk2_read_back_to_screen); + this.tabPage3.Controls.Add(this.Glide64mk2_aspectmode); + this.tabPage3.Controls.Add(this.Glide64mk2_fb_crc_mode); + this.tabPage3.Controls.Add(this.label45); + this.tabPage3.Controls.Add(this.label3); + this.tabPage3.Controls.Add(this.label52); + this.tabPage3.Controls.Add(this.Glide64mk2_enable_hacks_for_game); + this.tabPage3.Controls.Add(this.label53); + this.tabPage3.Controls.Add(this.Glide64mk2_swapmode); + this.tabPage3.Controls.Add(this.label54); + this.tabPage3.Controls.Add(this.Glide64mk2_stipple_pattern); + this.tabPage3.Controls.Add(this.label55); + this.tabPage3.Controls.Add(this.Glide64mk2_stipple_mode); + this.tabPage3.Controls.Add(this.label56); + this.tabPage3.Controls.Add(this.Glide64mk2_lodmode); + this.tabPage3.Controls.Add(this.label58); + this.tabPage3.Controls.Add(this.Glide64mk2_filtering); + this.tabPage3.Controls.Add(this.Glide64mk2_UseDefaultHacks2); + this.tabPage3.Location = new System.Drawing.Point(4, 22); + this.tabPage3.Name = "tabPage3"; + this.tabPage3.Size = new System.Drawing.Size(564, 310); + this.tabPage3.TabIndex = 2; + this.tabPage3.Text = "More Per Game Settings"; + this.tabPage3.UseVisualStyleBackColor = true; + // + // label46 + // + this.label46.AutoSize = true; + this.label46.Location = new System.Drawing.Point(280, 145); + this.label46.Name = "label46"; + this.label46.Size = new System.Drawing.Size(110, 13); + this.label46.TabIndex = 60; + this.label46.Text = "Read back to screen:"; + // + // Glide64mk2_read_back_to_screen + // + this.Glide64mk2_read_back_to_screen.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_read_back_to_screen.FormattingEnabled = true; + this.Glide64mk2_read_back_to_screen.Items.AddRange(new object[] { "0", "1", "2"}); - this.Glide64mk2_read_back_to_screen.Location = new System.Drawing.Point(396, 142); - this.Glide64mk2_read_back_to_screen.Name = "Glide64mk2_read_back_to_screen"; - this.Glide64mk2_read_back_to_screen.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_read_back_to_screen.TabIndex = 59; - // - // Glide64mk2_aspectmode - // - this.Glide64mk2_aspectmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_aspectmode.FormattingEnabled = true; - this.Glide64mk2_aspectmode.Items.AddRange(new object[] { + this.Glide64mk2_read_back_to_screen.Location = new System.Drawing.Point(396, 142); + this.Glide64mk2_read_back_to_screen.Name = "Glide64mk2_read_back_to_screen"; + this.Glide64mk2_read_back_to_screen.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_read_back_to_screen.TabIndex = 59; + // + // Glide64mk2_aspectmode + // + this.Glide64mk2_aspectmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_aspectmode.FormattingEnabled = true; + this.Glide64mk2_aspectmode.Items.AddRange(new object[] { "4:3", "16:9", "?????", "Original"}); - this.Glide64mk2_aspectmode.Location = new System.Drawing.Point(396, 87); - this.Glide64mk2_aspectmode.Name = "Glide64mk2_aspectmode"; - this.Glide64mk2_aspectmode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_aspectmode.TabIndex = 58; - // - // Glide64mk2_fb_crc_mode - // - this.Glide64mk2_fb_crc_mode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_fb_crc_mode.FormattingEnabled = true; - this.Glide64mk2_fb_crc_mode.Items.AddRange(new object[] { + this.Glide64mk2_aspectmode.Location = new System.Drawing.Point(396, 87); + this.Glide64mk2_aspectmode.Name = "Glide64mk2_aspectmode"; + this.Glide64mk2_aspectmode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_aspectmode.TabIndex = 58; + // + // Glide64mk2_fb_crc_mode + // + this.Glide64mk2_fb_crc_mode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_fb_crc_mode.FormattingEnabled = true; + this.Glide64mk2_fb_crc_mode.Items.AddRange(new object[] { "None", "Fast", "Safe"}); - this.Glide64mk2_fb_crc_mode.Location = new System.Drawing.Point(396, 115); - this.Glide64mk2_fb_crc_mode.Name = "Glide64mk2_fb_crc_mode"; - this.Glide64mk2_fb_crc_mode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_fb_crc_mode.TabIndex = 57; - // - // label45 - // - this.label45.AutoSize = true; - this.label45.Location = new System.Drawing.Point(269, 118); - this.label45.Name = "label45"; - this.label45.Size = new System.Drawing.Size(121, 13); - this.label45.TabIndex = 56; - this.label45.Text = "Framebuffer CRC Mode:"; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(317, 90); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(73, 13); - this.label3.TabIndex = 55; - this.label3.Text = "Aspect Mode:"; - // - // label52 - // - this.label52.AutoSize = true; - this.label52.Location = new System.Drawing.Point(271, 62); - this.label52.Name = "label52"; - this.label52.Size = new System.Drawing.Size(119, 13); - this.label52.TabIndex = 54; - this.label52.Text = "Enable hacks for game:"; - // - // Glide64mk2_enable_hacks_for_game - // - this.Glide64mk2_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_enable_hacks_for_game.FormattingEnabled = true; - this.Glide64mk2_enable_hacks_for_game.Items.AddRange(new object[] { + this.Glide64mk2_fb_crc_mode.Location = new System.Drawing.Point(396, 115); + this.Glide64mk2_fb_crc_mode.Name = "Glide64mk2_fb_crc_mode"; + this.Glide64mk2_fb_crc_mode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_fb_crc_mode.TabIndex = 57; + // + // label45 + // + this.label45.AutoSize = true; + this.label45.Location = new System.Drawing.Point(269, 118); + this.label45.Name = "label45"; + this.label45.Size = new System.Drawing.Size(121, 13); + this.label45.TabIndex = 56; + this.label45.Text = "Framebuffer CRC Mode:"; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(317, 90); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(73, 13); + this.label3.TabIndex = 55; + this.label3.Text = "Aspect Mode:"; + // + // label52 + // + this.label52.AutoSize = true; + this.label52.Location = new System.Drawing.Point(271, 62); + this.label52.Name = "label52"; + this.label52.Size = new System.Drawing.Size(119, 13); + this.label52.TabIndex = 54; + this.label52.Text = "Enable hacks for game:"; + // + // Glide64mk2_enable_hacks_for_game + // + this.Glide64mk2_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_enable_hacks_for_game.FormattingEnabled = true; + this.Glide64mk2_enable_hacks_for_game.Items.AddRange(new object[] { "None", "All-Star Baseball", "Banjo Tooie", @@ -2013,461 +2015,461 @@ "Tonic Trouble", "Yoshi Story", "Zelda"}); - this.Glide64mk2_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); - this.Glide64mk2_enable_hacks_for_game.Name = "Glide64mk2_enable_hacks_for_game"; - this.Glide64mk2_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); - this.Glide64mk2_enable_hacks_for_game.TabIndex = 53; - // - // label53 - // - this.label53.AutoSize = true; - this.label53.Location = new System.Drawing.Point(266, 36); - this.label53.Name = "label53"; - this.label53.Size = new System.Drawing.Size(124, 13); - this.label53.TabIndex = 40; - this.label53.Text = "Buffer swapping method:"; - // - // Glide64mk2_swapmode - // - this.Glide64mk2_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_swapmode.FormattingEnabled = true; - this.Glide64mk2_swapmode.Items.AddRange(new object[] { + this.Glide64mk2_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); + this.Glide64mk2_enable_hacks_for_game.Name = "Glide64mk2_enable_hacks_for_game"; + this.Glide64mk2_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); + this.Glide64mk2_enable_hacks_for_game.TabIndex = 53; + // + // label53 + // + this.label53.AutoSize = true; + this.label53.Location = new System.Drawing.Point(266, 36); + this.label53.Name = "label53"; + this.label53.Size = new System.Drawing.Size(124, 13); + this.label53.TabIndex = 40; + this.label53.Text = "Buffer swapping method:"; + // + // Glide64mk2_swapmode + // + this.Glide64mk2_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_swapmode.FormattingEnabled = true; + this.Glide64mk2_swapmode.Items.AddRange(new object[] { "Old", "New", "Hybrid"}); - this.Glide64mk2_swapmode.Location = new System.Drawing.Point(396, 32); - this.Glide64mk2_swapmode.Name = "Glide64mk2_swapmode"; - this.Glide64mk2_swapmode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_swapmode.TabIndex = 39; - // - // label54 - // - this.label54.AutoSize = true; - this.label54.Location = new System.Drawing.Point(3, 275); - this.label54.Name = "label54"; - this.label54.Size = new System.Drawing.Size(78, 13); - this.label54.TabIndex = 38; - this.label54.Text = "Stipple pattern:"; - // - // Glide64mk2_stipple_pattern - // - this.Glide64mk2_stipple_pattern.Location = new System.Drawing.Point(92, 272); - this.Glide64mk2_stipple_pattern.Name = "Glide64mk2_stipple_pattern"; - this.Glide64mk2_stipple_pattern.Size = new System.Drawing.Size(78, 20); - this.Glide64mk2_stipple_pattern.TabIndex = 37; - // - // label55 - // - this.label55.AutoSize = true; - this.label55.Location = new System.Drawing.Point(3, 249); - this.label55.Name = "label55"; - this.label55.Size = new System.Drawing.Size(71, 13); - this.label55.TabIndex = 36; - this.label55.Text = "Stipple mode:"; - // - // Glide64mk2_stipple_mode - // - this.Glide64mk2_stipple_mode.Location = new System.Drawing.Point(92, 246); - this.Glide64mk2_stipple_mode.Name = "Glide64mk2_stipple_mode"; - this.Glide64mk2_stipple_mode.Size = new System.Drawing.Size(36, 20); - this.Glide64mk2_stipple_mode.TabIndex = 35; - // - // label56 - // - this.label56.AutoSize = true; - this.label56.Location = new System.Drawing.Point(3, 118); - this.label56.Name = "label56"; - this.label56.Size = new System.Drawing.Size(86, 13); - this.label56.TabIndex = 26; - this.label56.Text = "LOD calculation:"; - // - // Glide64mk2_lodmode - // - this.Glide64mk2_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_lodmode.FormattingEnabled = true; - this.Glide64mk2_lodmode.Items.AddRange(new object[] { + this.Glide64mk2_swapmode.Location = new System.Drawing.Point(396, 32); + this.Glide64mk2_swapmode.Name = "Glide64mk2_swapmode"; + this.Glide64mk2_swapmode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_swapmode.TabIndex = 39; + // + // label54 + // + this.label54.AutoSize = true; + this.label54.Location = new System.Drawing.Point(3, 275); + this.label54.Name = "label54"; + this.label54.Size = new System.Drawing.Size(78, 13); + this.label54.TabIndex = 38; + this.label54.Text = "Stipple pattern:"; + // + // Glide64mk2_stipple_pattern + // + this.Glide64mk2_stipple_pattern.Location = new System.Drawing.Point(92, 272); + this.Glide64mk2_stipple_pattern.Name = "Glide64mk2_stipple_pattern"; + this.Glide64mk2_stipple_pattern.Size = new System.Drawing.Size(78, 20); + this.Glide64mk2_stipple_pattern.TabIndex = 37; + // + // label55 + // + this.label55.AutoSize = true; + this.label55.Location = new System.Drawing.Point(3, 249); + this.label55.Name = "label55"; + this.label55.Size = new System.Drawing.Size(71, 13); + this.label55.TabIndex = 36; + this.label55.Text = "Stipple mode:"; + // + // Glide64mk2_stipple_mode + // + this.Glide64mk2_stipple_mode.Location = new System.Drawing.Point(92, 246); + this.Glide64mk2_stipple_mode.Name = "Glide64mk2_stipple_mode"; + this.Glide64mk2_stipple_mode.Size = new System.Drawing.Size(36, 20); + this.Glide64mk2_stipple_mode.TabIndex = 35; + // + // label56 + // + this.label56.AutoSize = true; + this.label56.Location = new System.Drawing.Point(3, 118); + this.label56.Name = "label56"; + this.label56.Size = new System.Drawing.Size(86, 13); + this.label56.TabIndex = 26; + this.label56.Text = "LOD calculation:"; + // + // Glide64mk2_lodmode + // + this.Glide64mk2_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_lodmode.FormattingEnabled = true; + this.Glide64mk2_lodmode.Items.AddRange(new object[] { "Off", "Fast", "Precise"}); - this.Glide64mk2_lodmode.Location = new System.Drawing.Point(92, 115); - this.Glide64mk2_lodmode.Name = "Glide64mk2_lodmode"; - this.Glide64mk2_lodmode.Size = new System.Drawing.Size(78, 21); - this.Glide64mk2_lodmode.TabIndex = 25; - // - // label58 - // - this.label58.AutoSize = true; - this.label58.Location = new System.Drawing.Point(3, 65); - this.label58.Name = "label58"; - this.label58.Size = new System.Drawing.Size(75, 13); - this.label58.TabIndex = 22; - this.label58.Text = "Filtering mode:"; - // - // Glide64mk2_filtering - // - this.Glide64mk2_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide64mk2_filtering.FormattingEnabled = true; - this.Glide64mk2_filtering.Items.AddRange(new object[] { + this.Glide64mk2_lodmode.Location = new System.Drawing.Point(92, 115); + this.Glide64mk2_lodmode.Name = "Glide64mk2_lodmode"; + this.Glide64mk2_lodmode.Size = new System.Drawing.Size(78, 21); + this.Glide64mk2_lodmode.TabIndex = 25; + // + // label58 + // + this.label58.AutoSize = true; + this.label58.Location = new System.Drawing.Point(3, 65); + this.label58.Name = "label58"; + this.label58.Size = new System.Drawing.Size(75, 13); + this.label58.TabIndex = 22; + this.label58.Text = "Filtering mode:"; + // + // Glide64mk2_filtering + // + this.Glide64mk2_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide64mk2_filtering.FormattingEnabled = true; + this.Glide64mk2_filtering.Items.AddRange(new object[] { "None", "Force bilinear", "Force point-sampled"}); - this.Glide64mk2_filtering.Location = new System.Drawing.Point(92, 62); - this.Glide64mk2_filtering.Name = "Glide64mk2_filtering"; - this.Glide64mk2_filtering.Size = new System.Drawing.Size(138, 21); - this.Glide64mk2_filtering.TabIndex = 21; - // - // Glide64mk2_UseDefaultHacks2 - // - this.Glide64mk2_UseDefaultHacks2.AutoSize = true; - this.Glide64mk2_UseDefaultHacks2.Location = new System.Drawing.Point(6, 6); - this.Glide64mk2_UseDefaultHacks2.Name = "Glide64mk2_UseDefaultHacks2"; - this.Glide64mk2_UseDefaultHacks2.Size = new System.Drawing.Size(165, 17); - this.Glide64mk2_UseDefaultHacks2.TabIndex = 3; - this.Glide64mk2_UseDefaultHacks2.Text = "Use defaults for current game"; - this.Glide64mk2_UseDefaultHacks2.UseVisualStyleBackColor = true; - this.Glide64mk2_UseDefaultHacks2.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks2_CheckedChanged); - // - // Glide64TabPage - // - this.Glide64TabPage.Controls.Add(this.tabControl1); - this.Glide64TabPage.Location = new System.Drawing.Point(4, 22); - this.Glide64TabPage.Name = "Glide64TabPage"; - this.Glide64TabPage.Padding = new System.Windows.Forms.Padding(3); - this.Glide64TabPage.Size = new System.Drawing.Size(572, 343); - this.Glide64TabPage.TabIndex = 5; - this.Glide64TabPage.Text = "Glide64"; - this.Glide64TabPage.UseVisualStyleBackColor = true; - // - // tabControl1 - // - this.tabControl1.Controls.Add(this.Glide64General); - this.tabControl1.Controls.Add(this.GlidePerGameHacks1); - this.tabControl1.Controls.Add(this.GlidePerGameHacks2); - this.tabControl1.Location = new System.Drawing.Point(0, 3); - this.tabControl1.Name = "tabControl1"; - this.tabControl1.SelectedIndex = 0; - this.tabControl1.Size = new System.Drawing.Size(572, 336); - this.tabControl1.TabIndex = 0; - // - // Glide64General - // - this.Glide64General.Controls.Add(this.label39); - this.Glide64General.Controls.Add(this.Glide_scale_y); - this.Glide64General.Controls.Add(this.label40); - this.Glide64General.Controls.Add(this.Glide_scale_x); - this.Glide64General.Controls.Add(this.label38); - this.Glide64General.Controls.Add(this.Glide_offset_y); - this.Glide64General.Controls.Add(this.label37); - this.Glide64General.Controls.Add(this.Glide_offset_x); - this.Glide64General.Controls.Add(this.Glide_fb_get_info); - this.Glide64General.Controls.Add(this.Glide_disable_auxbuf); - this.Glide64General.Controls.Add(this.Glide_fbo); - this.Glide64General.Controls.Add(this.Glide_noglsl); - this.Glide64General.Controls.Add(this.Glide_noditheredalpha); - this.Glide64General.Controls.Add(this.label32); - this.Glide64General.Controls.Add(this.Glide_tex_filter); - this.Glide64General.Controls.Add(this.Glide_fb_render); - this.Glide64General.Controls.Add(this.Glide_motionblur); - this.Glide64General.Controls.Add(this.Glide_fb_read_always); - this.Glide64General.Controls.Add(this.Glide_unk_as_red); - this.Glide64General.Controls.Add(this.Glide_filter_cache); - this.Glide64General.Controls.Add(this.Glide_fast_crc); - this.Glide64General.Controls.Add(this.label31); - this.Glide64General.Controls.Add(this.Glide_wfmode); - this.Glide64General.Controls.Add(this.Glide_wireframe); - this.Glide64General.Controls.Add(this.label30); - this.Glide64General.Controls.Add(this.Glide_card_id); - this.Glide64General.Controls.Add(this.Glide_flame_corona); - this.Glide64General.Controls.Add(this.label29); - this.Glide64General.Controls.Add(this.Glide_ucode); - this.Glide64General.Controls.Add(this.Glide_autodetect_ucode); - this.Glide64General.Location = new System.Drawing.Point(4, 22); - this.Glide64General.Name = "Glide64General"; - this.Glide64General.Padding = new System.Windows.Forms.Padding(3); - this.Glide64General.Size = new System.Drawing.Size(564, 310); - this.Glide64General.TabIndex = 0; - this.Glide64General.Text = "General"; - this.Glide64General.UseVisualStyleBackColor = true; - // - // label39 - // - this.label39.AutoSize = true; - this.label39.Location = new System.Drawing.Point(386, 265); - this.label39.Name = "label39"; - this.label39.Size = new System.Drawing.Size(47, 13); - this.label39.TabIndex = 42; - this.label39.Text = "Scale Y:"; - // - // Glide_scale_y - // - this.Glide_scale_y.Location = new System.Drawing.Point(455, 262); - this.Glide_scale_y.Name = "Glide_scale_y"; - this.Glide_scale_y.Size = new System.Drawing.Size(56, 20); - this.Glide_scale_y.TabIndex = 41; - // - // label40 - // - this.label40.AutoSize = true; - this.label40.ForeColor = System.Drawing.SystemColors.ControlText; - this.label40.Location = new System.Drawing.Point(386, 239); - this.label40.Name = "label40"; - this.label40.Size = new System.Drawing.Size(47, 13); - this.label40.TabIndex = 40; - this.label40.Text = "Scale X:"; - // - // Glide_scale_x - // - this.Glide_scale_x.Location = new System.Drawing.Point(455, 236); - this.Glide_scale_x.Name = "Glide_scale_x"; - this.Glide_scale_x.Size = new System.Drawing.Size(56, 20); - this.Glide_scale_x.TabIndex = 39; - // - // label38 - // - this.label38.AutoSize = true; - this.label38.Location = new System.Drawing.Point(386, 213); - this.label38.Name = "label38"; - this.label38.Size = new System.Drawing.Size(48, 13); - this.label38.TabIndex = 38; - this.label38.Text = "Offset Y:"; - // - // Glide_offset_y - // - this.Glide_offset_y.Location = new System.Drawing.Point(455, 210); - this.Glide_offset_y.Name = "Glide_offset_y"; - this.Glide_offset_y.Size = new System.Drawing.Size(56, 20); - this.Glide_offset_y.TabIndex = 37; - // - // label37 - // - this.label37.AutoSize = true; - this.label37.Location = new System.Drawing.Point(386, 187); - this.label37.Name = "label37"; - this.label37.Size = new System.Drawing.Size(48, 13); - this.label37.TabIndex = 36; - this.label37.Text = "Offset X:"; - // - // Glide_offset_x - // - this.Glide_offset_x.Location = new System.Drawing.Point(455, 184); - this.Glide_offset_x.Name = "Glide_offset_x"; - this.Glide_offset_x.Size = new System.Drawing.Size(56, 20); - this.Glide_offset_x.TabIndex = 35; - // - // Glide_fb_get_info - // - this.Glide_fb_get_info.AutoSize = true; - this.Glide_fb_get_info.Location = new System.Drawing.Point(9, 278); - this.Glide_fb_get_info.Name = "Glide_fb_get_info"; - this.Glide_fb_get_info.Size = new System.Drawing.Size(119, 17); - this.Glide_fb_get_info.TabIndex = 23; - this.Glide_fb_get_info.Text = "Get framebuffer info"; - this.Glide_fb_get_info.UseVisualStyleBackColor = true; - // - // Glide_disable_auxbuf - // - this.Glide_disable_auxbuf.AutoSize = true; - this.Glide_disable_auxbuf.Location = new System.Drawing.Point(199, 278); - this.Glide_disable_auxbuf.Name = "Glide_disable_auxbuf"; - this.Glide_disable_auxbuf.Size = new System.Drawing.Size(111, 17); - this.Glide_disable_auxbuf.TabIndex = 22; - this.Glide_disable_auxbuf.Text = "Disable aux buffer"; - this.Glide_disable_auxbuf.UseVisualStyleBackColor = true; - // - // Glide_fbo - // - this.Glide_fbo.AutoSize = true; - this.Glide_fbo.Location = new System.Drawing.Point(199, 255); - this.Glide_fbo.Name = "Glide_fbo"; - this.Glide_fbo.Size = new System.Drawing.Size(138, 17); - this.Glide_fbo.TabIndex = 21; - this.Glide_fbo.Text = "Use framebuffer objects"; - this.Glide_fbo.UseVisualStyleBackColor = true; - // - // Glide_noglsl - // - this.Glide_noglsl.AutoSize = true; - this.Glide_noglsl.Location = new System.Drawing.Point(199, 232); - this.Glide_noglsl.Name = "Glide_noglsl"; - this.Glide_noglsl.Size = new System.Drawing.Size(142, 17); - this.Glide_noglsl.TabIndex = 20; - this.Glide_noglsl.Text = "Disable GLSL combiners"; - this.Glide_noglsl.UseVisualStyleBackColor = true; - // - // Glide_noditheredalpha - // - this.Glide_noditheredalpha.AutoSize = true; - this.Glide_noditheredalpha.Location = new System.Drawing.Point(199, 209); - this.Glide_noditheredalpha.Name = "Glide_noditheredalpha"; - this.Glide_noditheredalpha.Size = new System.Drawing.Size(131, 17); - this.Glide_noditheredalpha.TabIndex = 19; - this.Glide_noditheredalpha.Text = "Disable dithered alpha"; - this.Glide_noditheredalpha.UseVisualStyleBackColor = true; - // - // label32 - // - this.label32.AutoSize = true; - this.label32.Location = new System.Drawing.Point(206, 89); - this.label32.Name = "label32"; - this.label32.Size = new System.Drawing.Size(85, 13); - this.label32.TabIndex = 18; - this.label32.Text = "Texture Filtering:"; - // - // Glide_tex_filter - // - this.Glide_tex_filter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_tex_filter.FormattingEnabled = true; - this.Glide_tex_filter.Items.AddRange(new object[] { + this.Glide64mk2_filtering.Location = new System.Drawing.Point(92, 62); + this.Glide64mk2_filtering.Name = "Glide64mk2_filtering"; + this.Glide64mk2_filtering.Size = new System.Drawing.Size(138, 21); + this.Glide64mk2_filtering.TabIndex = 21; + // + // Glide64mk2_UseDefaultHacks2 + // + this.Glide64mk2_UseDefaultHacks2.AutoSize = true; + this.Glide64mk2_UseDefaultHacks2.Location = new System.Drawing.Point(6, 6); + this.Glide64mk2_UseDefaultHacks2.Name = "Glide64mk2_UseDefaultHacks2"; + this.Glide64mk2_UseDefaultHacks2.Size = new System.Drawing.Size(165, 17); + this.Glide64mk2_UseDefaultHacks2.TabIndex = 3; + this.Glide64mk2_UseDefaultHacks2.Text = "Use defaults for current game"; + this.Glide64mk2_UseDefaultHacks2.UseVisualStyleBackColor = true; + this.Glide64mk2_UseDefaultHacks2.CheckedChanged += new System.EventHandler(this.Glide64mk2_UseDefaultHacks2_CheckedChanged); + // + // Glide64TabPage + // + this.Glide64TabPage.Controls.Add(this.tabControl1); + this.Glide64TabPage.Location = new System.Drawing.Point(4, 22); + this.Glide64TabPage.Name = "Glide64TabPage"; + this.Glide64TabPage.Padding = new System.Windows.Forms.Padding(3); + this.Glide64TabPage.Size = new System.Drawing.Size(572, 343); + this.Glide64TabPage.TabIndex = 5; + this.Glide64TabPage.Text = "Glide64"; + this.Glide64TabPage.UseVisualStyleBackColor = true; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.Glide64General); + this.tabControl1.Controls.Add(this.GlidePerGameHacks1); + this.tabControl1.Controls.Add(this.GlidePerGameHacks2); + this.tabControl1.Location = new System.Drawing.Point(0, 3); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(572, 336); + this.tabControl1.TabIndex = 0; + // + // Glide64General + // + this.Glide64General.Controls.Add(this.label39); + this.Glide64General.Controls.Add(this.Glide_scale_y); + this.Glide64General.Controls.Add(this.label40); + this.Glide64General.Controls.Add(this.Glide_scale_x); + this.Glide64General.Controls.Add(this.label38); + this.Glide64General.Controls.Add(this.Glide_offset_y); + this.Glide64General.Controls.Add(this.label37); + this.Glide64General.Controls.Add(this.Glide_offset_x); + this.Glide64General.Controls.Add(this.Glide_fb_get_info); + this.Glide64General.Controls.Add(this.Glide_disable_auxbuf); + this.Glide64General.Controls.Add(this.Glide_fbo); + this.Glide64General.Controls.Add(this.Glide_noglsl); + this.Glide64General.Controls.Add(this.Glide_noditheredalpha); + this.Glide64General.Controls.Add(this.label32); + this.Glide64General.Controls.Add(this.Glide_tex_filter); + this.Glide64General.Controls.Add(this.Glide_fb_render); + this.Glide64General.Controls.Add(this.Glide_motionblur); + this.Glide64General.Controls.Add(this.Glide_fb_read_always); + this.Glide64General.Controls.Add(this.Glide_unk_as_red); + this.Glide64General.Controls.Add(this.Glide_filter_cache); + this.Glide64General.Controls.Add(this.Glide_fast_crc); + this.Glide64General.Controls.Add(this.label31); + this.Glide64General.Controls.Add(this.Glide_wfmode); + this.Glide64General.Controls.Add(this.Glide_wireframe); + this.Glide64General.Controls.Add(this.label30); + this.Glide64General.Controls.Add(this.Glide_card_id); + this.Glide64General.Controls.Add(this.Glide_flame_corona); + this.Glide64General.Controls.Add(this.label29); + this.Glide64General.Controls.Add(this.Glide_ucode); + this.Glide64General.Controls.Add(this.Glide_autodetect_ucode); + this.Glide64General.Location = new System.Drawing.Point(4, 22); + this.Glide64General.Name = "Glide64General"; + this.Glide64General.Padding = new System.Windows.Forms.Padding(3); + this.Glide64General.Size = new System.Drawing.Size(564, 310); + this.Glide64General.TabIndex = 0; + this.Glide64General.Text = "General"; + this.Glide64General.UseVisualStyleBackColor = true; + // + // label39 + // + this.label39.AutoSize = true; + this.label39.Location = new System.Drawing.Point(386, 265); + this.label39.Name = "label39"; + this.label39.Size = new System.Drawing.Size(47, 13); + this.label39.TabIndex = 42; + this.label39.Text = "Scale Y:"; + // + // Glide_scale_y + // + this.Glide_scale_y.Location = new System.Drawing.Point(455, 262); + this.Glide_scale_y.Name = "Glide_scale_y"; + this.Glide_scale_y.Size = new System.Drawing.Size(56, 20); + this.Glide_scale_y.TabIndex = 41; + // + // label40 + // + this.label40.AutoSize = true; + this.label40.ForeColor = System.Drawing.SystemColors.ControlText; + this.label40.Location = new System.Drawing.Point(386, 239); + this.label40.Name = "label40"; + this.label40.Size = new System.Drawing.Size(47, 13); + this.label40.TabIndex = 40; + this.label40.Text = "Scale X:"; + // + // Glide_scale_x + // + this.Glide_scale_x.Location = new System.Drawing.Point(455, 236); + this.Glide_scale_x.Name = "Glide_scale_x"; + this.Glide_scale_x.Size = new System.Drawing.Size(56, 20); + this.Glide_scale_x.TabIndex = 39; + // + // label38 + // + this.label38.AutoSize = true; + this.label38.Location = new System.Drawing.Point(386, 213); + this.label38.Name = "label38"; + this.label38.Size = new System.Drawing.Size(48, 13); + this.label38.TabIndex = 38; + this.label38.Text = "Offset Y:"; + // + // Glide_offset_y + // + this.Glide_offset_y.Location = new System.Drawing.Point(455, 210); + this.Glide_offset_y.Name = "Glide_offset_y"; + this.Glide_offset_y.Size = new System.Drawing.Size(56, 20); + this.Glide_offset_y.TabIndex = 37; + // + // label37 + // + this.label37.AutoSize = true; + this.label37.Location = new System.Drawing.Point(386, 187); + this.label37.Name = "label37"; + this.label37.Size = new System.Drawing.Size(48, 13); + this.label37.TabIndex = 36; + this.label37.Text = "Offset X:"; + // + // Glide_offset_x + // + this.Glide_offset_x.Location = new System.Drawing.Point(455, 184); + this.Glide_offset_x.Name = "Glide_offset_x"; + this.Glide_offset_x.Size = new System.Drawing.Size(56, 20); + this.Glide_offset_x.TabIndex = 35; + // + // Glide_fb_get_info + // + this.Glide_fb_get_info.AutoSize = true; + this.Glide_fb_get_info.Location = new System.Drawing.Point(9, 278); + this.Glide_fb_get_info.Name = "Glide_fb_get_info"; + this.Glide_fb_get_info.Size = new System.Drawing.Size(119, 17); + this.Glide_fb_get_info.TabIndex = 23; + this.Glide_fb_get_info.Text = "Get framebuffer info"; + this.Glide_fb_get_info.UseVisualStyleBackColor = true; + // + // Glide_disable_auxbuf + // + this.Glide_disable_auxbuf.AutoSize = true; + this.Glide_disable_auxbuf.Location = new System.Drawing.Point(199, 278); + this.Glide_disable_auxbuf.Name = "Glide_disable_auxbuf"; + this.Glide_disable_auxbuf.Size = new System.Drawing.Size(111, 17); + this.Glide_disable_auxbuf.TabIndex = 22; + this.Glide_disable_auxbuf.Text = "Disable aux buffer"; + this.Glide_disable_auxbuf.UseVisualStyleBackColor = true; + // + // Glide_fbo + // + this.Glide_fbo.AutoSize = true; + this.Glide_fbo.Location = new System.Drawing.Point(199, 255); + this.Glide_fbo.Name = "Glide_fbo"; + this.Glide_fbo.Size = new System.Drawing.Size(138, 17); + this.Glide_fbo.TabIndex = 21; + this.Glide_fbo.Text = "Use framebuffer objects"; + this.Glide_fbo.UseVisualStyleBackColor = true; + // + // Glide_noglsl + // + this.Glide_noglsl.AutoSize = true; + this.Glide_noglsl.Location = new System.Drawing.Point(199, 232); + this.Glide_noglsl.Name = "Glide_noglsl"; + this.Glide_noglsl.Size = new System.Drawing.Size(142, 17); + this.Glide_noglsl.TabIndex = 20; + this.Glide_noglsl.Text = "Disable GLSL combiners"; + this.Glide_noglsl.UseVisualStyleBackColor = true; + // + // Glide_noditheredalpha + // + this.Glide_noditheredalpha.AutoSize = true; + this.Glide_noditheredalpha.Location = new System.Drawing.Point(199, 209); + this.Glide_noditheredalpha.Name = "Glide_noditheredalpha"; + this.Glide_noditheredalpha.Size = new System.Drawing.Size(131, 17); + this.Glide_noditheredalpha.TabIndex = 19; + this.Glide_noditheredalpha.Text = "Disable dithered alpha"; + this.Glide_noditheredalpha.UseVisualStyleBackColor = true; + // + // label32 + // + this.label32.AutoSize = true; + this.label32.Location = new System.Drawing.Point(206, 89); + this.label32.Name = "label32"; + this.label32.Size = new System.Drawing.Size(85, 13); + this.label32.TabIndex = 18; + this.label32.Text = "Texture Filtering:"; + // + // Glide_tex_filter + // + this.Glide_tex_filter.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_tex_filter.FormattingEnabled = true; + this.Glide_tex_filter.Items.AddRange(new object[] { "None", "Blur edges", "Super 2xSai", "Hq2x", "Hq4x"}); - this.Glide_tex_filter.Location = new System.Drawing.Point(297, 86); - this.Glide_tex_filter.Name = "Glide_tex_filter"; - this.Glide_tex_filter.Size = new System.Drawing.Size(95, 21); - this.Glide_tex_filter.TabIndex = 17; - // - // Glide_fb_render - // - this.Glide_fb_render.AutoSize = true; - this.Glide_fb_render.Location = new System.Drawing.Point(199, 186); - this.Glide_fb_render.Name = "Glide_fb_render"; - this.Glide_fb_render.Size = new System.Drawing.Size(118, 17); - this.Glide_fb_render.TabIndex = 16; - this.Glide_fb_render.Text = "Depth buffer render"; - this.Glide_fb_render.UseVisualStyleBackColor = true; - // - // Glide_motionblur - // - this.Glide_motionblur.AutoSize = true; - this.Glide_motionblur.Location = new System.Drawing.Point(9, 255); - this.Glide_motionblur.Name = "Glide_motionblur"; - this.Glide_motionblur.Size = new System.Drawing.Size(79, 17); - this.Glide_motionblur.TabIndex = 13; - this.Glide_motionblur.Text = "Motion Blur"; - this.Glide_motionblur.UseVisualStyleBackColor = true; - // - // Glide_fb_read_always - // - this.Glide_fb_read_always.AutoSize = true; - this.Glide_fb_read_always.Location = new System.Drawing.Point(9, 232); - this.Glide_fb_read_always.Name = "Glide_fb_read_always"; - this.Glide_fb_read_always.Size = new System.Drawing.Size(164, 17); - this.Glide_fb_read_always.TabIndex = 12; - this.Glide_fb_read_always.Text = "Framebuffer read every frame"; - this.Glide_fb_read_always.UseVisualStyleBackColor = true; - // - // Glide_unk_as_red - // - this.Glide_unk_as_red.AutoSize = true; - this.Glide_unk_as_red.Location = new System.Drawing.Point(9, 209); - this.Glide_unk_as_red.Name = "Glide_unk_as_red"; - this.Glide_unk_as_red.Size = new System.Drawing.Size(187, 17); - this.Glide_unk_as_red.TabIndex = 11; - this.Glide_unk_as_red.Text = "Display unknown combines as red"; - this.Glide_unk_as_red.UseVisualStyleBackColor = true; - // - // Glide_filter_cache - // - this.Glide_filter_cache.AutoSize = true; - this.Glide_filter_cache.Location = new System.Drawing.Point(9, 186); - this.Glide_filter_cache.Name = "Glide_filter_cache"; - this.Glide_filter_cache.Size = new System.Drawing.Size(82, 17); - this.Glide_filter_cache.TabIndex = 10; - this.Glide_filter_cache.Text = "Filter Cache"; - this.Glide_filter_cache.UseVisualStyleBackColor = true; - // - // Glide_fast_crc - // - this.Glide_fast_crc.AutoSize = true; - this.Glide_fast_crc.Location = new System.Drawing.Point(9, 163); - this.Glide_fast_crc.Name = "Glide_fast_crc"; - this.Glide_fast_crc.Size = new System.Drawing.Size(71, 17); - this.Glide_fast_crc.TabIndex = 9; - this.Glide_fast_crc.Text = "Fast CRC"; - this.Glide_fast_crc.UseVisualStyleBackColor = true; - // - // label31 - // - this.label31.AutoSize = true; - this.label31.Location = new System.Drawing.Point(6, 139); - this.label31.Name = "label31"; - this.label31.Size = new System.Drawing.Size(88, 13); - this.label31.TabIndex = 8; - this.label31.Text = "Wireframe Mode:"; - // - // Glide_wfmode - // - this.Glide_wfmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_wfmode.FormattingEnabled = true; - this.Glide_wfmode.Items.AddRange(new object[] { + this.Glide_tex_filter.Location = new System.Drawing.Point(297, 86); + this.Glide_tex_filter.Name = "Glide_tex_filter"; + this.Glide_tex_filter.Size = new System.Drawing.Size(95, 21); + this.Glide_tex_filter.TabIndex = 17; + // + // Glide_fb_render + // + this.Glide_fb_render.AutoSize = true; + this.Glide_fb_render.Location = new System.Drawing.Point(199, 186); + this.Glide_fb_render.Name = "Glide_fb_render"; + this.Glide_fb_render.Size = new System.Drawing.Size(118, 17); + this.Glide_fb_render.TabIndex = 16; + this.Glide_fb_render.Text = "Depth buffer render"; + this.Glide_fb_render.UseVisualStyleBackColor = true; + // + // Glide_motionblur + // + this.Glide_motionblur.AutoSize = true; + this.Glide_motionblur.Location = new System.Drawing.Point(9, 255); + this.Glide_motionblur.Name = "Glide_motionblur"; + this.Glide_motionblur.Size = new System.Drawing.Size(79, 17); + this.Glide_motionblur.TabIndex = 13; + this.Glide_motionblur.Text = "Motion Blur"; + this.Glide_motionblur.UseVisualStyleBackColor = true; + // + // Glide_fb_read_always + // + this.Glide_fb_read_always.AutoSize = true; + this.Glide_fb_read_always.Location = new System.Drawing.Point(9, 232); + this.Glide_fb_read_always.Name = "Glide_fb_read_always"; + this.Glide_fb_read_always.Size = new System.Drawing.Size(164, 17); + this.Glide_fb_read_always.TabIndex = 12; + this.Glide_fb_read_always.Text = "Framebuffer read every frame"; + this.Glide_fb_read_always.UseVisualStyleBackColor = true; + // + // Glide_unk_as_red + // + this.Glide_unk_as_red.AutoSize = true; + this.Glide_unk_as_red.Location = new System.Drawing.Point(9, 209); + this.Glide_unk_as_red.Name = "Glide_unk_as_red"; + this.Glide_unk_as_red.Size = new System.Drawing.Size(187, 17); + this.Glide_unk_as_red.TabIndex = 11; + this.Glide_unk_as_red.Text = "Display unknown combines as red"; + this.Glide_unk_as_red.UseVisualStyleBackColor = true; + // + // Glide_filter_cache + // + this.Glide_filter_cache.AutoSize = true; + this.Glide_filter_cache.Location = new System.Drawing.Point(9, 186); + this.Glide_filter_cache.Name = "Glide_filter_cache"; + this.Glide_filter_cache.Size = new System.Drawing.Size(82, 17); + this.Glide_filter_cache.TabIndex = 10; + this.Glide_filter_cache.Text = "Filter Cache"; + this.Glide_filter_cache.UseVisualStyleBackColor = true; + // + // Glide_fast_crc + // + this.Glide_fast_crc.AutoSize = true; + this.Glide_fast_crc.Location = new System.Drawing.Point(9, 163); + this.Glide_fast_crc.Name = "Glide_fast_crc"; + this.Glide_fast_crc.Size = new System.Drawing.Size(71, 17); + this.Glide_fast_crc.TabIndex = 9; + this.Glide_fast_crc.Text = "Fast CRC"; + this.Glide_fast_crc.UseVisualStyleBackColor = true; + // + // label31 + // + this.label31.AutoSize = true; + this.label31.Location = new System.Drawing.Point(6, 139); + this.label31.Name = "label31"; + this.label31.Size = new System.Drawing.Size(88, 13); + this.label31.TabIndex = 8; + this.label31.Text = "Wireframe Mode:"; + // + // Glide_wfmode + // + this.Glide_wfmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_wfmode.FormattingEnabled = true; + this.Glide_wfmode.Items.AddRange(new object[] { "Normal colors", "Vertex colors", "Red only"}); - this.Glide_wfmode.Location = new System.Drawing.Point(100, 136); - this.Glide_wfmode.Name = "Glide_wfmode"; - this.Glide_wfmode.Size = new System.Drawing.Size(104, 21); - this.Glide_wfmode.TabIndex = 7; - // - // Glide_wireframe - // - this.Glide_wireframe.AutoSize = true; - this.Glide_wireframe.ForeColor = System.Drawing.SystemColors.ControlText; - this.Glide_wireframe.Location = new System.Drawing.Point(9, 113); - this.Glide_wireframe.Name = "Glide_wireframe"; - this.Glide_wireframe.Size = new System.Drawing.Size(110, 17); - this.Glide_wireframe.TabIndex = 6; - this.Glide_wireframe.Text = "Enable Wireframe"; - this.Glide_wireframe.UseVisualStyleBackColor = true; - // - // label30 - // - this.label30.AutoSize = true; - this.label30.Location = new System.Drawing.Point(6, 89); - this.label30.Name = "label30"; - this.label30.Size = new System.Drawing.Size(46, 13); - this.label30.TabIndex = 5; - this.label30.Text = "Card ID:"; - // - // Glide_card_id - // - this.Glide_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_card_id.FormattingEnabled = true; - this.Glide_card_id.Items.AddRange(new object[] { + this.Glide_wfmode.Location = new System.Drawing.Point(100, 136); + this.Glide_wfmode.Name = "Glide_wfmode"; + this.Glide_wfmode.Size = new System.Drawing.Size(104, 21); + this.Glide_wfmode.TabIndex = 7; + // + // Glide_wireframe + // + this.Glide_wireframe.AutoSize = true; + this.Glide_wireframe.ForeColor = System.Drawing.SystemColors.ControlText; + this.Glide_wireframe.Location = new System.Drawing.Point(9, 113); + this.Glide_wireframe.Name = "Glide_wireframe"; + this.Glide_wireframe.Size = new System.Drawing.Size(110, 17); + this.Glide_wireframe.TabIndex = 6; + this.Glide_wireframe.Text = "Enable Wireframe"; + this.Glide_wireframe.UseVisualStyleBackColor = true; + // + // label30 + // + this.label30.AutoSize = true; + this.label30.Location = new System.Drawing.Point(6, 89); + this.label30.Name = "label30"; + this.label30.Size = new System.Drawing.Size(46, 13); + this.label30.TabIndex = 5; + this.label30.Text = "Card ID:"; + // + // Glide_card_id + // + this.Glide_card_id.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_card_id.FormattingEnabled = true; + this.Glide_card_id.Items.AddRange(new object[] { "0", "1", "2", "3"}); - this.Glide_card_id.Location = new System.Drawing.Point(58, 86); - this.Glide_card_id.Name = "Glide_card_id"; - this.Glide_card_id.Size = new System.Drawing.Size(36, 21); - this.Glide_card_id.TabIndex = 4; - // - // Glide_flame_corona - // - this.Glide_flame_corona.AutoSize = true; - this.Glide_flame_corona.Location = new System.Drawing.Point(9, 63); - this.Glide_flame_corona.Name = "Glide_flame_corona"; - this.Glide_flame_corona.Size = new System.Drawing.Size(103, 17); - this.Glide_flame_corona.TabIndex = 3; - this.Glide_flame_corona.Text = "Zelda Corona fix"; - this.Glide_flame_corona.UseVisualStyleBackColor = true; - // - // label29 - // - this.label29.AutoSize = true; - this.label29.Location = new System.Drawing.Point(6, 39); - this.label29.Name = "label29"; - this.label29.Size = new System.Drawing.Size(90, 13); - this.label29.TabIndex = 2; - this.label29.Text = "Force Microcode:"; - // - // Glide_ucode - // - this.Glide_ucode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_ucode.FormattingEnabled = true; - this.Glide_ucode.Items.AddRange(new object[] { + this.Glide_card_id.Location = new System.Drawing.Point(58, 86); + this.Glide_card_id.Name = "Glide_card_id"; + this.Glide_card_id.Size = new System.Drawing.Size(36, 21); + this.Glide_card_id.TabIndex = 4; + // + // Glide_flame_corona + // + this.Glide_flame_corona.AutoSize = true; + this.Glide_flame_corona.Location = new System.Drawing.Point(9, 63); + this.Glide_flame_corona.Name = "Glide_flame_corona"; + this.Glide_flame_corona.Size = new System.Drawing.Size(103, 17); + this.Glide_flame_corona.TabIndex = 3; + this.Glide_flame_corona.Text = "Zelda Corona fix"; + this.Glide_flame_corona.UseVisualStyleBackColor = true; + // + // label29 + // + this.label29.AutoSize = true; + this.label29.Location = new System.Drawing.Point(6, 39); + this.label29.Name = "label29"; + this.label29.Size = new System.Drawing.Size(90, 13); + this.label29.TabIndex = 2; + this.label29.Text = "Force Microcode:"; + // + // Glide_ucode + // + this.Glide_ucode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_ucode.FormattingEnabled = true; + this.Glide_ucode.Items.AddRange(new object[] { "0: RSP SW 2.0X (ex. Super Mario 64)", "1: F3DEX 1.XX (ex. Star Fox 64)", "2: F3DEX 2.XX (ex. Zelda: OOT)", @@ -2477,336 +2479,336 @@ "6: S2DEX 1.XX (ex. Yoshi\'s Story)", "7: RSP SW PD (ex. Perfect Dark)", "8: F3DEXBG 2.08 (ex. Conker\'s Bad Fur Day)"}); - this.Glide_ucode.Location = new System.Drawing.Point(102, 36); - this.Glide_ucode.Name = "Glide_ucode"; - this.Glide_ucode.Size = new System.Drawing.Size(273, 21); - this.Glide_ucode.TabIndex = 1; - // - // Glide_autodetect_ucode - // - this.Glide_autodetect_ucode.AutoSize = true; - this.Glide_autodetect_ucode.Location = new System.Drawing.Point(9, 13); - this.Glide_autodetect_ucode.Name = "Glide_autodetect_ucode"; - this.Glide_autodetect_ucode.Size = new System.Drawing.Size(131, 17); - this.Glide_autodetect_ucode.TabIndex = 0; - this.Glide_autodetect_ucode.Text = "Autodetect Microcode"; - this.Glide_autodetect_ucode.UseVisualStyleBackColor = true; - // - // GlidePerGameHacks1 - // - this.GlidePerGameHacks1.Controls.Add(this.Glide_wrap_big_tex); - this.GlidePerGameHacks1.Controls.Add(this.Glide_use_sts1_only); - this.GlidePerGameHacks1.Controls.Add(this.Glide_soft_depth_compare); - this.GlidePerGameHacks1.Controls.Add(this.Glide_PPL); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_write); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_texrect); - this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_texrect_edge); - this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_primdepth); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_previous); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_aux_copy); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires_buf_clear); - this.GlidePerGameHacks1.Controls.Add(this.Glide_force_microcheck); - this.GlidePerGameHacks1.Controls.Add(this.Glide_force_depth_compare); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fog); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fillcolor_fix); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_smart); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_read_alpha); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires); - this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_clear); - this.GlidePerGameHacks1.Controls.Add(this.Glide_detect_cpu_write); - this.GlidePerGameHacks1.Controls.Add(this.Glide_decrease_fillrect_edge); - this.GlidePerGameHacks1.Controls.Add(this.Glide_buff_clear); - this.GlidePerGameHacks1.Controls.Add(this.Glide_alt_tex_size); - this.GlidePerGameHacks1.Controls.Add(this.GlideUseDefaultHacks1); - this.GlidePerGameHacks1.Location = new System.Drawing.Point(4, 22); - this.GlidePerGameHacks1.Name = "GlidePerGameHacks1"; - this.GlidePerGameHacks1.Padding = new System.Windows.Forms.Padding(3); - this.GlidePerGameHacks1.Size = new System.Drawing.Size(564, 310); - this.GlidePerGameHacks1.TabIndex = 1; - this.GlidePerGameHacks1.Text = "Per Game Settings"; - this.GlidePerGameHacks1.UseVisualStyleBackColor = true; - // - // Glide_wrap_big_tex - // - this.Glide_wrap_big_tex.AutoSize = true; - this.Glide_wrap_big_tex.Location = new System.Drawing.Point(375, 62); - this.Glide_wrap_big_tex.Name = "Glide_wrap_big_tex"; - this.Glide_wrap_big_tex.Size = new System.Drawing.Size(86, 17); - this.Glide_wrap_big_tex.TabIndex = 33; - this.Glide_wrap_big_tex.Text = "Wrap big tex"; - this.Glide_wrap_big_tex.UseVisualStyleBackColor = true; - // - // Glide_use_sts1_only - // - this.Glide_use_sts1_only.AutoSize = true; - this.Glide_use_sts1_only.Location = new System.Drawing.Point(375, 39); - this.Glide_use_sts1_only.Name = "Glide_use_sts1_only"; - this.Glide_use_sts1_only.Size = new System.Drawing.Size(89, 17); - this.Glide_use_sts1_only.TabIndex = 32; - this.Glide_use_sts1_only.Text = "Use sts1 only"; - this.Glide_use_sts1_only.UseVisualStyleBackColor = true; - // - // Glide_soft_depth_compare - // - this.Glide_soft_depth_compare.AutoSize = true; - this.Glide_soft_depth_compare.Location = new System.Drawing.Point(192, 269); - this.Glide_soft_depth_compare.Name = "Glide_soft_depth_compare"; - this.Glide_soft_depth_compare.Size = new System.Drawing.Size(119, 17); - this.Glide_soft_depth_compare.TabIndex = 31; - this.Glide_soft_depth_compare.Text = "Soft depth compare"; - this.Glide_soft_depth_compare.UseVisualStyleBackColor = true; - // - // Glide_PPL - // - this.Glide_PPL.AutoSize = true; - this.Glide_PPL.Location = new System.Drawing.Point(192, 246); - this.Glide_PPL.Name = "Glide_PPL"; - this.Glide_PPL.Size = new System.Drawing.Size(46, 17); - this.Glide_PPL.TabIndex = 30; - this.Glide_PPL.Text = "PPL"; - this.Glide_PPL.UseVisualStyleBackColor = true; - // - // Glide_fb_optimize_write - // - this.Glide_fb_optimize_write.AutoSize = true; - this.Glide_fb_optimize_write.Location = new System.Drawing.Point(192, 223); - this.Glide_fb_optimize_write.Name = "Glide_fb_optimize_write"; - this.Glide_fb_optimize_write.Size = new System.Drawing.Size(91, 17); - this.Glide_fb_optimize_write.TabIndex = 29; - this.Glide_fb_optimize_write.Text = "Optimize write"; - this.Glide_fb_optimize_write.UseVisualStyleBackColor = true; - // - // Glide_fb_optimize_texrect - // - this.Glide_fb_optimize_texrect.AutoSize = true; - this.Glide_fb_optimize_texrect.Location = new System.Drawing.Point(192, 200); - this.Glide_fb_optimize_texrect.Name = "Glide_fb_optimize_texrect"; - this.Glide_fb_optimize_texrect.Size = new System.Drawing.Size(101, 17); - this.Glide_fb_optimize_texrect.TabIndex = 28; - this.Glide_fb_optimize_texrect.Text = "Optimize texrect"; - this.Glide_fb_optimize_texrect.UseVisualStyleBackColor = true; - // - // Glide_increase_texrect_edge - // - this.Glide_increase_texrect_edge.AutoSize = true; - this.Glide_increase_texrect_edge.Location = new System.Drawing.Point(192, 177); - this.Glide_increase_texrect_edge.Name = "Glide_increase_texrect_edge"; - this.Glide_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); - this.Glide_increase_texrect_edge.TabIndex = 27; - this.Glide_increase_texrect_edge.Text = "Increase texrect edge"; - this.Glide_increase_texrect_edge.UseVisualStyleBackColor = true; - // - // Glide_increase_primdepth - // - this.Glide_increase_primdepth.AutoSize = true; - this.Glide_increase_primdepth.Location = new System.Drawing.Point(192, 154); - this.Glide_increase_primdepth.Name = "Glide_increase_primdepth"; - this.Glide_increase_primdepth.Size = new System.Drawing.Size(116, 17); - this.Glide_increase_primdepth.TabIndex = 26; - this.Glide_increase_primdepth.Text = "Increase primdepth"; - this.Glide_increase_primdepth.UseVisualStyleBackColor = true; - // - // Glide_fb_ignore_previous - // - this.Glide_fb_ignore_previous.AutoSize = true; - this.Glide_fb_ignore_previous.Location = new System.Drawing.Point(192, 131); - this.Glide_fb_ignore_previous.Name = "Glide_fb_ignore_previous"; - this.Glide_fb_ignore_previous.Size = new System.Drawing.Size(100, 17); - this.Glide_fb_ignore_previous.TabIndex = 25; - this.Glide_fb_ignore_previous.Text = "Ignore Previous"; - this.Glide_fb_ignore_previous.UseVisualStyleBackColor = true; - // - // Glide_fb_ignore_aux_copy - // - this.Glide_fb_ignore_aux_copy.AutoSize = true; - this.Glide_fb_ignore_aux_copy.Location = new System.Drawing.Point(192, 108); - this.Glide_fb_ignore_aux_copy.Name = "Glide_fb_ignore_aux_copy"; - this.Glide_fb_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); - this.Glide_fb_ignore_aux_copy.TabIndex = 24; - this.Glide_fb_ignore_aux_copy.Text = "Ignore aux copy"; - this.Glide_fb_ignore_aux_copy.UseVisualStyleBackColor = true; - // - // Glide_fb_hires_buf_clear - // - this.Glide_fb_hires_buf_clear.AutoSize = true; - this.Glide_fb_hires_buf_clear.Location = new System.Drawing.Point(192, 85); - this.Glide_fb_hires_buf_clear.Name = "Glide_fb_hires_buf_clear"; - this.Glide_fb_hires_buf_clear.Size = new System.Drawing.Size(109, 17); - this.Glide_fb_hires_buf_clear.TabIndex = 23; - this.Glide_fb_hires_buf_clear.Text = "Hi-res buffer clear"; - this.Glide_fb_hires_buf_clear.UseVisualStyleBackColor = true; - // - // Glide_force_microcheck - // - this.Glide_force_microcheck.AutoSize = true; - this.Glide_force_microcheck.Location = new System.Drawing.Point(192, 62); - this.Glide_force_microcheck.Name = "Glide_force_microcheck"; - this.Glide_force_microcheck.Size = new System.Drawing.Size(165, 17); - this.Glide_force_microcheck.TabIndex = 22; - this.Glide_force_microcheck.Text = "Check microcode each frame"; - this.Glide_force_microcheck.UseVisualStyleBackColor = true; - // - // Glide_force_depth_compare - // - this.Glide_force_depth_compare.AutoSize = true; - this.Glide_force_depth_compare.Location = new System.Drawing.Point(192, 39); - this.Glide_force_depth_compare.Name = "Glide_force_depth_compare"; - this.Glide_force_depth_compare.Size = new System.Drawing.Size(130, 17); - this.Glide_force_depth_compare.TabIndex = 21; - this.Glide_force_depth_compare.Text = "Force Depth Compare"; - this.Glide_force_depth_compare.UseVisualStyleBackColor = true; - // - // Glide_fog - // - this.Glide_fog.AutoSize = true; - this.Glide_fog.Location = new System.Drawing.Point(6, 269); - this.Glide_fog.Name = "Glide_fog"; - this.Glide_fog.Size = new System.Drawing.Size(86, 17); - this.Glide_fog.TabIndex = 20; - this.Glide_fog.Text = "Fog Enabled"; - this.Glide_fog.UseVisualStyleBackColor = true; - // - // Glide_fillcolor_fix - // - this.Glide_fillcolor_fix.AutoSize = true; - this.Glide_fillcolor_fix.Location = new System.Drawing.Point(6, 246); - this.Glide_fillcolor_fix.Name = "Glide_fillcolor_fix"; - this.Glide_fillcolor_fix.Size = new System.Drawing.Size(77, 17); - this.Glide_fillcolor_fix.TabIndex = 19; - this.Glide_fillcolor_fix.Text = "Fillcolor Fix"; - this.Glide_fillcolor_fix.UseVisualStyleBackColor = true; - // - // Glide_fb_smart - // - this.Glide_fb_smart.AutoSize = true; - this.Glide_fb_smart.Location = new System.Drawing.Point(6, 223); - this.Glide_fb_smart.Name = "Glide_fb_smart"; - this.Glide_fb_smart.Size = new System.Drawing.Size(109, 17); - this.Glide_fb_smart.TabIndex = 18; - this.Glide_fb_smart.Text = "Smart framebuffer"; - this.Glide_fb_smart.UseVisualStyleBackColor = true; - // - // Glide_fb_read_alpha - // - this.Glide_fb_read_alpha.AutoSize = true; - this.Glide_fb_read_alpha.Location = new System.Drawing.Point(6, 200); - this.Glide_fb_read_alpha.Name = "Glide_fb_read_alpha"; - this.Glide_fb_read_alpha.Size = new System.Drawing.Size(135, 17); - this.Glide_fb_read_alpha.TabIndex = 17; - this.Glide_fb_read_alpha.Text = "Framebuffer read alpha"; - this.Glide_fb_read_alpha.UseVisualStyleBackColor = true; - // - // Glide_fb_hires - // - this.Glide_fb_hires.AutoSize = true; - this.Glide_fb_hires.Location = new System.Drawing.Point(6, 154); - this.Glide_fb_hires.Name = "Glide_fb_hires"; - this.Glide_fb_hires.Size = new System.Drawing.Size(109, 17); - this.Glide_fb_hires.TabIndex = 15; - this.Glide_fb_hires.Text = "Hi-res framebuffer"; - this.Glide_fb_hires.UseVisualStyleBackColor = true; - // - // Glide_fb_clear - // - this.Glide_fb_clear.AutoSize = true; - this.Glide_fb_clear.Location = new System.Drawing.Point(6, 131); - this.Glide_fb_clear.Name = "Glide_fb_clear"; - this.Glide_fb_clear.Size = new System.Drawing.Size(106, 17); - this.Glide_fb_clear.TabIndex = 7; - this.Glide_fb_clear.Text = "Clear framebuffer"; - this.Glide_fb_clear.UseVisualStyleBackColor = true; - // - // Glide_detect_cpu_write - // - this.Glide_detect_cpu_write.AutoSize = true; - this.Glide_detect_cpu_write.Location = new System.Drawing.Point(6, 108); - this.Glide_detect_cpu_write.Name = "Glide_detect_cpu_write"; - this.Glide_detect_cpu_write.Size = new System.Drawing.Size(113, 17); - this.Glide_detect_cpu_write.TabIndex = 6; - this.Glide_detect_cpu_write.Text = "Detect CPU writes"; - this.Glide_detect_cpu_write.UseVisualStyleBackColor = true; - // - // Glide_decrease_fillrect_edge - // - this.Glide_decrease_fillrect_edge.AutoSize = true; - this.Glide_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); - this.Glide_decrease_fillrect_edge.Name = "Glide_decrease_fillrect_edge"; - this.Glide_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); - this.Glide_decrease_fillrect_edge.TabIndex = 5; - this.Glide_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; - this.Glide_decrease_fillrect_edge.UseVisualStyleBackColor = true; - // - // Glide_buff_clear - // - this.Glide_buff_clear.AutoSize = true; - this.Glide_buff_clear.Location = new System.Drawing.Point(6, 62); - this.Glide_buff_clear.Name = "Glide_buff_clear"; - this.Glide_buff_clear.Size = new System.Drawing.Size(153, 17); - this.Glide_buff_clear.TabIndex = 4; - this.Glide_buff_clear.Text = "Buffer clear on every frame"; - this.Glide_buff_clear.UseVisualStyleBackColor = true; - // - // Glide_alt_tex_size - // - this.Glide_alt_tex_size.AutoSize = true; - this.Glide_alt_tex_size.Location = new System.Drawing.Point(6, 39); - this.Glide_alt_tex_size.Name = "Glide_alt_tex_size"; - this.Glide_alt_tex_size.Size = new System.Drawing.Size(169, 17); - this.Glide_alt_tex_size.TabIndex = 3; - this.Glide_alt_tex_size.Text = "Alternate Texture Size Method"; - this.Glide_alt_tex_size.UseVisualStyleBackColor = true; - // - // GlideUseDefaultHacks1 - // - this.GlideUseDefaultHacks1.AutoSize = true; - this.GlideUseDefaultHacks1.Location = new System.Drawing.Point(6, 6); - this.GlideUseDefaultHacks1.Name = "GlideUseDefaultHacks1"; - this.GlideUseDefaultHacks1.Size = new System.Drawing.Size(165, 17); - this.GlideUseDefaultHacks1.TabIndex = 2; - this.GlideUseDefaultHacks1.Text = "Use defaults for current game"; - this.GlideUseDefaultHacks1.UseVisualStyleBackColor = true; - this.GlideUseDefaultHacks1.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks1_CheckedChanged); - // - // GlidePerGameHacks2 - // - this.GlidePerGameHacks2.Controls.Add(this.label44); - this.GlidePerGameHacks2.Controls.Add(this.Glide_enable_hacks_for_game); - this.GlidePerGameHacks2.Controls.Add(this.label43); - this.GlidePerGameHacks2.Controls.Add(this.Glide_swapmode); - this.GlidePerGameHacks2.Controls.Add(this.label42); - this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_pattern); - this.GlidePerGameHacks2.Controls.Add(this.label41); - this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_mode); - this.GlidePerGameHacks2.Controls.Add(this.label36); - this.GlidePerGameHacks2.Controls.Add(this.Glide_lodmode); - this.GlidePerGameHacks2.Controls.Add(this.label35); - this.GlidePerGameHacks2.Controls.Add(this.Glide_fix_tex_coord); - this.GlidePerGameHacks2.Controls.Add(this.label34); - this.GlidePerGameHacks2.Controls.Add(this.Glide_filtering); - this.GlidePerGameHacks2.Controls.Add(this.label33); - this.GlidePerGameHacks2.Controls.Add(this.Glide_depth_bias); - this.GlidePerGameHacks2.Controls.Add(this.GlideUseDefaultHacks2); - this.GlidePerGameHacks2.Location = new System.Drawing.Point(4, 22); - this.GlidePerGameHacks2.Name = "GlidePerGameHacks2"; - this.GlidePerGameHacks2.Size = new System.Drawing.Size(564, 310); - this.GlidePerGameHacks2.TabIndex = 2; - this.GlidePerGameHacks2.Text = "More Per Game Settings"; - this.GlidePerGameHacks2.UseVisualStyleBackColor = true; - // - // label44 - // - this.label44.AutoSize = true; - this.label44.Location = new System.Drawing.Point(271, 62); - this.label44.Name = "label44"; - this.label44.Size = new System.Drawing.Size(119, 13); - this.label44.TabIndex = 54; - this.label44.Text = "Enable hacks for game:"; - // - // Glide_enable_hacks_for_game - // - this.Glide_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_enable_hacks_for_game.FormattingEnabled = true; - this.Glide_enable_hacks_for_game.Items.AddRange(new object[] { + this.Glide_ucode.Location = new System.Drawing.Point(102, 36); + this.Glide_ucode.Name = "Glide_ucode"; + this.Glide_ucode.Size = new System.Drawing.Size(273, 21); + this.Glide_ucode.TabIndex = 1; + // + // Glide_autodetect_ucode + // + this.Glide_autodetect_ucode.AutoSize = true; + this.Glide_autodetect_ucode.Location = new System.Drawing.Point(9, 13); + this.Glide_autodetect_ucode.Name = "Glide_autodetect_ucode"; + this.Glide_autodetect_ucode.Size = new System.Drawing.Size(131, 17); + this.Glide_autodetect_ucode.TabIndex = 0; + this.Glide_autodetect_ucode.Text = "Autodetect Microcode"; + this.Glide_autodetect_ucode.UseVisualStyleBackColor = true; + // + // GlidePerGameHacks1 + // + this.GlidePerGameHacks1.Controls.Add(this.Glide_wrap_big_tex); + this.GlidePerGameHacks1.Controls.Add(this.Glide_use_sts1_only); + this.GlidePerGameHacks1.Controls.Add(this.Glide_soft_depth_compare); + this.GlidePerGameHacks1.Controls.Add(this.Glide_PPL); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_write); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_optimize_texrect); + this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_texrect_edge); + this.GlidePerGameHacks1.Controls.Add(this.Glide_increase_primdepth); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_previous); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_ignore_aux_copy); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires_buf_clear); + this.GlidePerGameHacks1.Controls.Add(this.Glide_force_microcheck); + this.GlidePerGameHacks1.Controls.Add(this.Glide_force_depth_compare); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fog); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fillcolor_fix); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_smart); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_read_alpha); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_hires); + this.GlidePerGameHacks1.Controls.Add(this.Glide_fb_clear); + this.GlidePerGameHacks1.Controls.Add(this.Glide_detect_cpu_write); + this.GlidePerGameHacks1.Controls.Add(this.Glide_decrease_fillrect_edge); + this.GlidePerGameHacks1.Controls.Add(this.Glide_buff_clear); + this.GlidePerGameHacks1.Controls.Add(this.Glide_alt_tex_size); + this.GlidePerGameHacks1.Controls.Add(this.GlideUseDefaultHacks1); + this.GlidePerGameHacks1.Location = new System.Drawing.Point(4, 22); + this.GlidePerGameHacks1.Name = "GlidePerGameHacks1"; + this.GlidePerGameHacks1.Padding = new System.Windows.Forms.Padding(3); + this.GlidePerGameHacks1.Size = new System.Drawing.Size(564, 310); + this.GlidePerGameHacks1.TabIndex = 1; + this.GlidePerGameHacks1.Text = "Per Game Settings"; + this.GlidePerGameHacks1.UseVisualStyleBackColor = true; + // + // Glide_wrap_big_tex + // + this.Glide_wrap_big_tex.AutoSize = true; + this.Glide_wrap_big_tex.Location = new System.Drawing.Point(375, 62); + this.Glide_wrap_big_tex.Name = "Glide_wrap_big_tex"; + this.Glide_wrap_big_tex.Size = new System.Drawing.Size(86, 17); + this.Glide_wrap_big_tex.TabIndex = 33; + this.Glide_wrap_big_tex.Text = "Wrap big tex"; + this.Glide_wrap_big_tex.UseVisualStyleBackColor = true; + // + // Glide_use_sts1_only + // + this.Glide_use_sts1_only.AutoSize = true; + this.Glide_use_sts1_only.Location = new System.Drawing.Point(375, 39); + this.Glide_use_sts1_only.Name = "Glide_use_sts1_only"; + this.Glide_use_sts1_only.Size = new System.Drawing.Size(89, 17); + this.Glide_use_sts1_only.TabIndex = 32; + this.Glide_use_sts1_only.Text = "Use sts1 only"; + this.Glide_use_sts1_only.UseVisualStyleBackColor = true; + // + // Glide_soft_depth_compare + // + this.Glide_soft_depth_compare.AutoSize = true; + this.Glide_soft_depth_compare.Location = new System.Drawing.Point(192, 269); + this.Glide_soft_depth_compare.Name = "Glide_soft_depth_compare"; + this.Glide_soft_depth_compare.Size = new System.Drawing.Size(119, 17); + this.Glide_soft_depth_compare.TabIndex = 31; + this.Glide_soft_depth_compare.Text = "Soft depth compare"; + this.Glide_soft_depth_compare.UseVisualStyleBackColor = true; + // + // Glide_PPL + // + this.Glide_PPL.AutoSize = true; + this.Glide_PPL.Location = new System.Drawing.Point(192, 246); + this.Glide_PPL.Name = "Glide_PPL"; + this.Glide_PPL.Size = new System.Drawing.Size(46, 17); + this.Glide_PPL.TabIndex = 30; + this.Glide_PPL.Text = "PPL"; + this.Glide_PPL.UseVisualStyleBackColor = true; + // + // Glide_fb_optimize_write + // + this.Glide_fb_optimize_write.AutoSize = true; + this.Glide_fb_optimize_write.Location = new System.Drawing.Point(192, 223); + this.Glide_fb_optimize_write.Name = "Glide_fb_optimize_write"; + this.Glide_fb_optimize_write.Size = new System.Drawing.Size(91, 17); + this.Glide_fb_optimize_write.TabIndex = 29; + this.Glide_fb_optimize_write.Text = "Optimize write"; + this.Glide_fb_optimize_write.UseVisualStyleBackColor = true; + // + // Glide_fb_optimize_texrect + // + this.Glide_fb_optimize_texrect.AutoSize = true; + this.Glide_fb_optimize_texrect.Location = new System.Drawing.Point(192, 200); + this.Glide_fb_optimize_texrect.Name = "Glide_fb_optimize_texrect"; + this.Glide_fb_optimize_texrect.Size = new System.Drawing.Size(101, 17); + this.Glide_fb_optimize_texrect.TabIndex = 28; + this.Glide_fb_optimize_texrect.Text = "Optimize texrect"; + this.Glide_fb_optimize_texrect.UseVisualStyleBackColor = true; + // + // Glide_increase_texrect_edge + // + this.Glide_increase_texrect_edge.AutoSize = true; + this.Glide_increase_texrect_edge.Location = new System.Drawing.Point(192, 177); + this.Glide_increase_texrect_edge.Name = "Glide_increase_texrect_edge"; + this.Glide_increase_texrect_edge.Size = new System.Drawing.Size(129, 17); + this.Glide_increase_texrect_edge.TabIndex = 27; + this.Glide_increase_texrect_edge.Text = "Increase texrect edge"; + this.Glide_increase_texrect_edge.UseVisualStyleBackColor = true; + // + // Glide_increase_primdepth + // + this.Glide_increase_primdepth.AutoSize = true; + this.Glide_increase_primdepth.Location = new System.Drawing.Point(192, 154); + this.Glide_increase_primdepth.Name = "Glide_increase_primdepth"; + this.Glide_increase_primdepth.Size = new System.Drawing.Size(116, 17); + this.Glide_increase_primdepth.TabIndex = 26; + this.Glide_increase_primdepth.Text = "Increase primdepth"; + this.Glide_increase_primdepth.UseVisualStyleBackColor = true; + // + // Glide_fb_ignore_previous + // + this.Glide_fb_ignore_previous.AutoSize = true; + this.Glide_fb_ignore_previous.Location = new System.Drawing.Point(192, 131); + this.Glide_fb_ignore_previous.Name = "Glide_fb_ignore_previous"; + this.Glide_fb_ignore_previous.Size = new System.Drawing.Size(100, 17); + this.Glide_fb_ignore_previous.TabIndex = 25; + this.Glide_fb_ignore_previous.Text = "Ignore Previous"; + this.Glide_fb_ignore_previous.UseVisualStyleBackColor = true; + // + // Glide_fb_ignore_aux_copy + // + this.Glide_fb_ignore_aux_copy.AutoSize = true; + this.Glide_fb_ignore_aux_copy.Location = new System.Drawing.Point(192, 108); + this.Glide_fb_ignore_aux_copy.Name = "Glide_fb_ignore_aux_copy"; + this.Glide_fb_ignore_aux_copy.Size = new System.Drawing.Size(102, 17); + this.Glide_fb_ignore_aux_copy.TabIndex = 24; + this.Glide_fb_ignore_aux_copy.Text = "Ignore aux copy"; + this.Glide_fb_ignore_aux_copy.UseVisualStyleBackColor = true; + // + // Glide_fb_hires_buf_clear + // + this.Glide_fb_hires_buf_clear.AutoSize = true; + this.Glide_fb_hires_buf_clear.Location = new System.Drawing.Point(192, 85); + this.Glide_fb_hires_buf_clear.Name = "Glide_fb_hires_buf_clear"; + this.Glide_fb_hires_buf_clear.Size = new System.Drawing.Size(109, 17); + this.Glide_fb_hires_buf_clear.TabIndex = 23; + this.Glide_fb_hires_buf_clear.Text = "Hi-res buffer clear"; + this.Glide_fb_hires_buf_clear.UseVisualStyleBackColor = true; + // + // Glide_force_microcheck + // + this.Glide_force_microcheck.AutoSize = true; + this.Glide_force_microcheck.Location = new System.Drawing.Point(192, 62); + this.Glide_force_microcheck.Name = "Glide_force_microcheck"; + this.Glide_force_microcheck.Size = new System.Drawing.Size(165, 17); + this.Glide_force_microcheck.TabIndex = 22; + this.Glide_force_microcheck.Text = "Check microcode each frame"; + this.Glide_force_microcheck.UseVisualStyleBackColor = true; + // + // Glide_force_depth_compare + // + this.Glide_force_depth_compare.AutoSize = true; + this.Glide_force_depth_compare.Location = new System.Drawing.Point(192, 39); + this.Glide_force_depth_compare.Name = "Glide_force_depth_compare"; + this.Glide_force_depth_compare.Size = new System.Drawing.Size(130, 17); + this.Glide_force_depth_compare.TabIndex = 21; + this.Glide_force_depth_compare.Text = "Force Depth Compare"; + this.Glide_force_depth_compare.UseVisualStyleBackColor = true; + // + // Glide_fog + // + this.Glide_fog.AutoSize = true; + this.Glide_fog.Location = new System.Drawing.Point(6, 269); + this.Glide_fog.Name = "Glide_fog"; + this.Glide_fog.Size = new System.Drawing.Size(86, 17); + this.Glide_fog.TabIndex = 20; + this.Glide_fog.Text = "Fog Enabled"; + this.Glide_fog.UseVisualStyleBackColor = true; + // + // Glide_fillcolor_fix + // + this.Glide_fillcolor_fix.AutoSize = true; + this.Glide_fillcolor_fix.Location = new System.Drawing.Point(6, 246); + this.Glide_fillcolor_fix.Name = "Glide_fillcolor_fix"; + this.Glide_fillcolor_fix.Size = new System.Drawing.Size(77, 17); + this.Glide_fillcolor_fix.TabIndex = 19; + this.Glide_fillcolor_fix.Text = "Fillcolor Fix"; + this.Glide_fillcolor_fix.UseVisualStyleBackColor = true; + // + // Glide_fb_smart + // + this.Glide_fb_smart.AutoSize = true; + this.Glide_fb_smart.Location = new System.Drawing.Point(6, 223); + this.Glide_fb_smart.Name = "Glide_fb_smart"; + this.Glide_fb_smart.Size = new System.Drawing.Size(109, 17); + this.Glide_fb_smart.TabIndex = 18; + this.Glide_fb_smart.Text = "Smart framebuffer"; + this.Glide_fb_smart.UseVisualStyleBackColor = true; + // + // Glide_fb_read_alpha + // + this.Glide_fb_read_alpha.AutoSize = true; + this.Glide_fb_read_alpha.Location = new System.Drawing.Point(6, 200); + this.Glide_fb_read_alpha.Name = "Glide_fb_read_alpha"; + this.Glide_fb_read_alpha.Size = new System.Drawing.Size(135, 17); + this.Glide_fb_read_alpha.TabIndex = 17; + this.Glide_fb_read_alpha.Text = "Framebuffer read alpha"; + this.Glide_fb_read_alpha.UseVisualStyleBackColor = true; + // + // Glide_fb_hires + // + this.Glide_fb_hires.AutoSize = true; + this.Glide_fb_hires.Location = new System.Drawing.Point(6, 154); + this.Glide_fb_hires.Name = "Glide_fb_hires"; + this.Glide_fb_hires.Size = new System.Drawing.Size(109, 17); + this.Glide_fb_hires.TabIndex = 15; + this.Glide_fb_hires.Text = "Hi-res framebuffer"; + this.Glide_fb_hires.UseVisualStyleBackColor = true; + // + // Glide_fb_clear + // + this.Glide_fb_clear.AutoSize = true; + this.Glide_fb_clear.Location = new System.Drawing.Point(6, 131); + this.Glide_fb_clear.Name = "Glide_fb_clear"; + this.Glide_fb_clear.Size = new System.Drawing.Size(106, 17); + this.Glide_fb_clear.TabIndex = 7; + this.Glide_fb_clear.Text = "Clear framebuffer"; + this.Glide_fb_clear.UseVisualStyleBackColor = true; + // + // Glide_detect_cpu_write + // + this.Glide_detect_cpu_write.AutoSize = true; + this.Glide_detect_cpu_write.Location = new System.Drawing.Point(6, 108); + this.Glide_detect_cpu_write.Name = "Glide_detect_cpu_write"; + this.Glide_detect_cpu_write.Size = new System.Drawing.Size(113, 17); + this.Glide_detect_cpu_write.TabIndex = 6; + this.Glide_detect_cpu_write.Text = "Detect CPU writes"; + this.Glide_detect_cpu_write.UseVisualStyleBackColor = true; + // + // Glide_decrease_fillrect_edge + // + this.Glide_decrease_fillrect_edge.AutoSize = true; + this.Glide_decrease_fillrect_edge.Location = new System.Drawing.Point(6, 85); + this.Glide_decrease_fillrect_edge.Name = "Glide_decrease_fillrect_edge"; + this.Glide_decrease_fillrect_edge.Size = new System.Drawing.Size(133, 17); + this.Glide_decrease_fillrect_edge.TabIndex = 5; + this.Glide_decrease_fillrect_edge.Text = "Decrease Fillrect Edge"; + this.Glide_decrease_fillrect_edge.UseVisualStyleBackColor = true; + // + // Glide_buff_clear + // + this.Glide_buff_clear.AutoSize = true; + this.Glide_buff_clear.Location = new System.Drawing.Point(6, 62); + this.Glide_buff_clear.Name = "Glide_buff_clear"; + this.Glide_buff_clear.Size = new System.Drawing.Size(153, 17); + this.Glide_buff_clear.TabIndex = 4; + this.Glide_buff_clear.Text = "Buffer clear on every frame"; + this.Glide_buff_clear.UseVisualStyleBackColor = true; + // + // Glide_alt_tex_size + // + this.Glide_alt_tex_size.AutoSize = true; + this.Glide_alt_tex_size.Location = new System.Drawing.Point(6, 39); + this.Glide_alt_tex_size.Name = "Glide_alt_tex_size"; + this.Glide_alt_tex_size.Size = new System.Drawing.Size(169, 17); + this.Glide_alt_tex_size.TabIndex = 3; + this.Glide_alt_tex_size.Text = "Alternate Texture Size Method"; + this.Glide_alt_tex_size.UseVisualStyleBackColor = true; + // + // GlideUseDefaultHacks1 + // + this.GlideUseDefaultHacks1.AutoSize = true; + this.GlideUseDefaultHacks1.Location = new System.Drawing.Point(6, 6); + this.GlideUseDefaultHacks1.Name = "GlideUseDefaultHacks1"; + this.GlideUseDefaultHacks1.Size = new System.Drawing.Size(165, 17); + this.GlideUseDefaultHacks1.TabIndex = 2; + this.GlideUseDefaultHacks1.Text = "Use defaults for current game"; + this.GlideUseDefaultHacks1.UseVisualStyleBackColor = true; + this.GlideUseDefaultHacks1.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks1_CheckedChanged); + // + // GlidePerGameHacks2 + // + this.GlidePerGameHacks2.Controls.Add(this.label44); + this.GlidePerGameHacks2.Controls.Add(this.Glide_enable_hacks_for_game); + this.GlidePerGameHacks2.Controls.Add(this.label43); + this.GlidePerGameHacks2.Controls.Add(this.Glide_swapmode); + this.GlidePerGameHacks2.Controls.Add(this.label42); + this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_pattern); + this.GlidePerGameHacks2.Controls.Add(this.label41); + this.GlidePerGameHacks2.Controls.Add(this.Glide_stipple_mode); + this.GlidePerGameHacks2.Controls.Add(this.label36); + this.GlidePerGameHacks2.Controls.Add(this.Glide_lodmode); + this.GlidePerGameHacks2.Controls.Add(this.label35); + this.GlidePerGameHacks2.Controls.Add(this.Glide_fix_tex_coord); + this.GlidePerGameHacks2.Controls.Add(this.label34); + this.GlidePerGameHacks2.Controls.Add(this.Glide_filtering); + this.GlidePerGameHacks2.Controls.Add(this.label33); + this.GlidePerGameHacks2.Controls.Add(this.Glide_depth_bias); + this.GlidePerGameHacks2.Controls.Add(this.GlideUseDefaultHacks2); + this.GlidePerGameHacks2.Location = new System.Drawing.Point(4, 22); + this.GlidePerGameHacks2.Name = "GlidePerGameHacks2"; + this.GlidePerGameHacks2.Size = new System.Drawing.Size(564, 310); + this.GlidePerGameHacks2.TabIndex = 2; + this.GlidePerGameHacks2.Text = "More Per Game Settings"; + this.GlidePerGameHacks2.UseVisualStyleBackColor = true; + // + // label44 + // + this.label44.AutoSize = true; + this.label44.Location = new System.Drawing.Point(271, 62); + this.label44.Name = "label44"; + this.label44.Size = new System.Drawing.Size(119, 13); + this.label44.TabIndex = 54; + this.label44.Text = "Enable hacks for game:"; + // + // Glide_enable_hacks_for_game + // + this.Glide_enable_hacks_for_game.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_enable_hacks_for_game.FormattingEnabled = true; + this.Glide_enable_hacks_for_game.Items.AddRange(new object[] { "None", "Zelda", "Bomberman64", @@ -2827,230 +2829,230 @@ "Top Gear Rally 2", "Killer Instinct", "LEGO Racers"}); - this.Glide_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); - this.Glide_enable_hacks_for_game.Name = "Glide_enable_hacks_for_game"; - this.Glide_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); - this.Glide_enable_hacks_for_game.TabIndex = 53; - // - // label43 - // - this.label43.AutoSize = true; - this.label43.Location = new System.Drawing.Point(266, 36); - this.label43.Name = "label43"; - this.label43.Size = new System.Drawing.Size(124, 13); - this.label43.TabIndex = 40; - this.label43.Text = "Buffer swapping method:"; - // - // Glide_swapmode - // - this.Glide_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_swapmode.FormattingEnabled = true; - this.Glide_swapmode.Items.AddRange(new object[] { + this.Glide_enable_hacks_for_game.Location = new System.Drawing.Point(396, 59); + this.Glide_enable_hacks_for_game.Name = "Glide_enable_hacks_for_game"; + this.Glide_enable_hacks_for_game.Size = new System.Drawing.Size(155, 21); + this.Glide_enable_hacks_for_game.TabIndex = 53; + // + // label43 + // + this.label43.AutoSize = true; + this.label43.Location = new System.Drawing.Point(266, 36); + this.label43.Name = "label43"; + this.label43.Size = new System.Drawing.Size(124, 13); + this.label43.TabIndex = 40; + this.label43.Text = "Buffer swapping method:"; + // + // Glide_swapmode + // + this.Glide_swapmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_swapmode.FormattingEnabled = true; + this.Glide_swapmode.Items.AddRange(new object[] { "Old", "New", "Hybrid"}); - this.Glide_swapmode.Location = new System.Drawing.Point(396, 32); - this.Glide_swapmode.Name = "Glide_swapmode"; - this.Glide_swapmode.Size = new System.Drawing.Size(78, 21); - this.Glide_swapmode.TabIndex = 39; - // - // label42 - // - this.label42.AutoSize = true; - this.label42.Location = new System.Drawing.Point(3, 275); - this.label42.Name = "label42"; - this.label42.Size = new System.Drawing.Size(78, 13); - this.label42.TabIndex = 38; - this.label42.Text = "Stipple pattern:"; - // - // Glide_stipple_pattern - // - this.Glide_stipple_pattern.Location = new System.Drawing.Point(92, 272); - this.Glide_stipple_pattern.Name = "Glide_stipple_pattern"; - this.Glide_stipple_pattern.Size = new System.Drawing.Size(78, 20); - this.Glide_stipple_pattern.TabIndex = 37; - // - // label41 - // - this.label41.AutoSize = true; - this.label41.Location = new System.Drawing.Point(3, 249); - this.label41.Name = "label41"; - this.label41.Size = new System.Drawing.Size(71, 13); - this.label41.TabIndex = 36; - this.label41.Text = "Stipple mode:"; - // - // Glide_stipple_mode - // - this.Glide_stipple_mode.Location = new System.Drawing.Point(92, 246); - this.Glide_stipple_mode.Name = "Glide_stipple_mode"; - this.Glide_stipple_mode.Size = new System.Drawing.Size(36, 20); - this.Glide_stipple_mode.TabIndex = 35; - // - // label36 - // - this.label36.AutoSize = true; - this.label36.Location = new System.Drawing.Point(3, 118); - this.label36.Name = "label36"; - this.label36.Size = new System.Drawing.Size(86, 13); - this.label36.TabIndex = 26; - this.label36.Text = "LOD calculation:"; - // - // Glide_lodmode - // - this.Glide_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_lodmode.FormattingEnabled = true; - this.Glide_lodmode.Items.AddRange(new object[] { + this.Glide_swapmode.Location = new System.Drawing.Point(396, 32); + this.Glide_swapmode.Name = "Glide_swapmode"; + this.Glide_swapmode.Size = new System.Drawing.Size(78, 21); + this.Glide_swapmode.TabIndex = 39; + // + // label42 + // + this.label42.AutoSize = true; + this.label42.Location = new System.Drawing.Point(3, 275); + this.label42.Name = "label42"; + this.label42.Size = new System.Drawing.Size(78, 13); + this.label42.TabIndex = 38; + this.label42.Text = "Stipple pattern:"; + // + // Glide_stipple_pattern + // + this.Glide_stipple_pattern.Location = new System.Drawing.Point(92, 272); + this.Glide_stipple_pattern.Name = "Glide_stipple_pattern"; + this.Glide_stipple_pattern.Size = new System.Drawing.Size(78, 20); + this.Glide_stipple_pattern.TabIndex = 37; + // + // label41 + // + this.label41.AutoSize = true; + this.label41.Location = new System.Drawing.Point(3, 249); + this.label41.Name = "label41"; + this.label41.Size = new System.Drawing.Size(71, 13); + this.label41.TabIndex = 36; + this.label41.Text = "Stipple mode:"; + // + // Glide_stipple_mode + // + this.Glide_stipple_mode.Location = new System.Drawing.Point(92, 246); + this.Glide_stipple_mode.Name = "Glide_stipple_mode"; + this.Glide_stipple_mode.Size = new System.Drawing.Size(36, 20); + this.Glide_stipple_mode.TabIndex = 35; + // + // label36 + // + this.label36.AutoSize = true; + this.label36.Location = new System.Drawing.Point(3, 118); + this.label36.Name = "label36"; + this.label36.Size = new System.Drawing.Size(86, 13); + this.label36.TabIndex = 26; + this.label36.Text = "LOD calculation:"; + // + // Glide_lodmode + // + this.Glide_lodmode.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_lodmode.FormattingEnabled = true; + this.Glide_lodmode.Items.AddRange(new object[] { "Off", "Fast", "Precise"}); - this.Glide_lodmode.Location = new System.Drawing.Point(92, 115); - this.Glide_lodmode.Name = "Glide_lodmode"; - this.Glide_lodmode.Size = new System.Drawing.Size(78, 21); - this.Glide_lodmode.TabIndex = 25; - // - // label35 - // - this.label35.AutoSize = true; - this.label35.Location = new System.Drawing.Point(3, 92); - this.label35.Name = "label35"; - this.label35.Size = new System.Drawing.Size(70, 13); - this.label35.TabIndex = 24; - this.label35.Text = "Fix tex coord:"; - // - // Glide_fix_tex_coord - // - this.Glide_fix_tex_coord.Location = new System.Drawing.Point(92, 89); - this.Glide_fix_tex_coord.Name = "Glide_fix_tex_coord"; - this.Glide_fix_tex_coord.Size = new System.Drawing.Size(36, 20); - this.Glide_fix_tex_coord.TabIndex = 23; - // - // label34 - // - this.label34.AutoSize = true; - this.label34.Location = new System.Drawing.Point(3, 65); - this.label34.Name = "label34"; - this.label34.Size = new System.Drawing.Size(75, 13); - this.label34.TabIndex = 22; - this.label34.Text = "Filtering mode:"; - // - // Glide_filtering - // - this.Glide_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.Glide_filtering.FormattingEnabled = true; - this.Glide_filtering.Items.AddRange(new object[] { + this.Glide_lodmode.Location = new System.Drawing.Point(92, 115); + this.Glide_lodmode.Name = "Glide_lodmode"; + this.Glide_lodmode.Size = new System.Drawing.Size(78, 21); + this.Glide_lodmode.TabIndex = 25; + // + // label35 + // + this.label35.AutoSize = true; + this.label35.Location = new System.Drawing.Point(3, 92); + this.label35.Name = "label35"; + this.label35.Size = new System.Drawing.Size(70, 13); + this.label35.TabIndex = 24; + this.label35.Text = "Fix tex coord:"; + // + // Glide_fix_tex_coord + // + this.Glide_fix_tex_coord.Location = new System.Drawing.Point(92, 89); + this.Glide_fix_tex_coord.Name = "Glide_fix_tex_coord"; + this.Glide_fix_tex_coord.Size = new System.Drawing.Size(36, 20); + this.Glide_fix_tex_coord.TabIndex = 23; + // + // label34 + // + this.label34.AutoSize = true; + this.label34.Location = new System.Drawing.Point(3, 65); + this.label34.Name = "label34"; + this.label34.Size = new System.Drawing.Size(75, 13); + this.label34.TabIndex = 22; + this.label34.Text = "Filtering mode:"; + // + // Glide_filtering + // + this.Glide_filtering.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Glide_filtering.FormattingEnabled = true; + this.Glide_filtering.Items.AddRange(new object[] { "None", "Force bilinear", "Force point-sampled"}); - this.Glide_filtering.Location = new System.Drawing.Point(92, 62); - this.Glide_filtering.Name = "Glide_filtering"; - this.Glide_filtering.Size = new System.Drawing.Size(138, 21); - this.Glide_filtering.TabIndex = 21; - // - // label33 - // - this.label33.AutoSize = true; - this.label33.Location = new System.Drawing.Point(3, 39); - this.label33.Name = "label33"; - this.label33.Size = new System.Drawing.Size(86, 13); - this.label33.TabIndex = 20; - this.label33.Text = "Depth bias level:"; - // - // Glide_depth_bias - // - this.Glide_depth_bias.Location = new System.Drawing.Point(92, 36); - this.Glide_depth_bias.Name = "Glide_depth_bias"; - this.Glide_depth_bias.Size = new System.Drawing.Size(36, 20); - this.Glide_depth_bias.TabIndex = 19; - // - // GlideUseDefaultHacks2 - // - this.GlideUseDefaultHacks2.AutoSize = true; - this.GlideUseDefaultHacks2.Location = new System.Drawing.Point(6, 6); - this.GlideUseDefaultHacks2.Name = "GlideUseDefaultHacks2"; - this.GlideUseDefaultHacks2.Size = new System.Drawing.Size(165, 17); - this.GlideUseDefaultHacks2.TabIndex = 3; - this.GlideUseDefaultHacks2.Text = "Use defaults for current game"; - this.GlideUseDefaultHacks2.UseVisualStyleBackColor = true; - this.GlideUseDefaultHacks2.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks2_CheckedChanged); - // - // RiceTabPage - // - this.RiceTabPage.Controls.Add(this.RiceTabControl); - this.RiceTabPage.Location = new System.Drawing.Point(4, 22); - this.RiceTabPage.Name = "RiceTabPage"; - this.RiceTabPage.Size = new System.Drawing.Size(572, 343); - this.RiceTabPage.TabIndex = 4; - this.RiceTabPage.Text = "Rice"; - this.RiceTabPage.UseVisualStyleBackColor = true; - // - // RiceTabControl - // - this.RiceTabControl.Controls.Add(this.RiceGeneral); - this.RiceTabControl.Controls.Add(this.RiceGameDefaultTab); - this.RiceTabControl.Controls.Add(this.RiceTextureEnhancementTab); - this.RiceTabControl.Controls.Add(this.RiceGameSpecificTab); - this.RiceTabControl.Location = new System.Drawing.Point(0, 3); - this.RiceTabControl.Name = "RiceTabControl"; - this.RiceTabControl.SelectedIndex = 0; - this.RiceTabControl.Size = new System.Drawing.Size(572, 336); - this.RiceTabControl.TabIndex = 0; - // - // RiceGeneral - // - this.RiceGeneral.Controls.Add(this.label15); - this.RiceGeneral.Controls.Add(this.RiceScreenUpdateSetting_Combo); - this.RiceGeneral.Controls.Add(this.label14); - this.RiceGeneral.Controls.Add(this.RiceMultiSampling_Combo); - this.RiceGeneral.Controls.Add(this.label13); - this.RiceGeneral.Controls.Add(this.RiceOpenGLRenderSetting_Combo); - this.RiceGeneral.Controls.Add(this.label11); - this.RiceGeneral.Controls.Add(this.RiceColorQuality_Combo); - this.RiceGeneral.Controls.Add(this.label10); - this.RiceGeneral.Controls.Add(this.RiceOpenGLDepthBufferSetting_Combo); - this.RiceGeneral.Controls.Add(this.label9); - this.RiceGeneral.Controls.Add(this.RiceTextureQuality_Combo); - this.RiceGeneral.Controls.Add(this.RiceEnableVertexShader_CB); - this.RiceGeneral.Controls.Add(this.RiceSkipFrame_CB); - this.RiceGeneral.Controls.Add(this.RiceEnableHacks_CB); - this.RiceGeneral.Controls.Add(this.RiceFullTMEMEmulation_CB); - this.RiceGeneral.Controls.Add(this.RiceOpenGLVertexClipper_CB); - this.RiceGeneral.Controls.Add(this.AnisotropicFiltering_LB); - this.RiceGeneral.Controls.Add(this.RiceAnisotropicFiltering_TB); - this.RiceGeneral.Controls.Add(this.label6); - this.RiceGeneral.Controls.Add(this.RiceFogMethod_Combo); - this.RiceGeneral.Controls.Add(this.label5); - this.RiceGeneral.Controls.Add(this.RiceMipmapping_Combo); - this.RiceGeneral.Controls.Add(this.RiceWinFrameMode_CB); - this.RiceGeneral.Controls.Add(this.RiceInN64Resolution_CB); - this.RiceGeneral.Controls.Add(this.RiceFastTextureLoading_CB); - this.RiceGeneral.Controls.Add(this.RiceAccurateTextureMapping_CB); - this.RiceGeneral.Controls.Add(this.RiceSaveVRAM_CB); - this.RiceGeneral.Controls.Add(this.RiceEnableSSE_CB); - this.RiceGeneral.Location = new System.Drawing.Point(4, 22); - this.RiceGeneral.Name = "RiceGeneral"; - this.RiceGeneral.Padding = new System.Windows.Forms.Padding(3); - this.RiceGeneral.Size = new System.Drawing.Size(564, 310); - this.RiceGeneral.TabIndex = 0; - this.RiceGeneral.Text = "General"; - this.RiceGeneral.UseVisualStyleBackColor = true; - // - // label15 - // - this.label15.AutoSize = true; - this.label15.Location = new System.Drawing.Point(287, 204); - this.label15.Name = "label15"; - this.label15.Size = new System.Drawing.Size(118, 13); - this.label15.TabIndex = 36; - this.label15.Text = "Screen Update Setting:"; - this.label15.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label15, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); - // - // RiceScreenUpdateSetting_Combo - // - this.RiceScreenUpdateSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceScreenUpdateSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceScreenUpdateSetting_Combo.FormattingEnabled = true; - this.RiceScreenUpdateSetting_Combo.Items.AddRange(new object[] { + this.Glide_filtering.Location = new System.Drawing.Point(92, 62); + this.Glide_filtering.Name = "Glide_filtering"; + this.Glide_filtering.Size = new System.Drawing.Size(138, 21); + this.Glide_filtering.TabIndex = 21; + // + // label33 + // + this.label33.AutoSize = true; + this.label33.Location = new System.Drawing.Point(3, 39); + this.label33.Name = "label33"; + this.label33.Size = new System.Drawing.Size(86, 13); + this.label33.TabIndex = 20; + this.label33.Text = "Depth bias level:"; + // + // Glide_depth_bias + // + this.Glide_depth_bias.Location = new System.Drawing.Point(92, 36); + this.Glide_depth_bias.Name = "Glide_depth_bias"; + this.Glide_depth_bias.Size = new System.Drawing.Size(36, 20); + this.Glide_depth_bias.TabIndex = 19; + // + // GlideUseDefaultHacks2 + // + this.GlideUseDefaultHacks2.AutoSize = true; + this.GlideUseDefaultHacks2.Location = new System.Drawing.Point(6, 6); + this.GlideUseDefaultHacks2.Name = "GlideUseDefaultHacks2"; + this.GlideUseDefaultHacks2.Size = new System.Drawing.Size(165, 17); + this.GlideUseDefaultHacks2.TabIndex = 3; + this.GlideUseDefaultHacks2.Text = "Use defaults for current game"; + this.GlideUseDefaultHacks2.UseVisualStyleBackColor = true; + this.GlideUseDefaultHacks2.CheckedChanged += new System.EventHandler(this.GlideUseDefaultHacks2_CheckedChanged); + // + // RiceTabPage + // + this.RiceTabPage.Controls.Add(this.RiceTabControl); + this.RiceTabPage.Location = new System.Drawing.Point(4, 22); + this.RiceTabPage.Name = "RiceTabPage"; + this.RiceTabPage.Size = new System.Drawing.Size(572, 343); + this.RiceTabPage.TabIndex = 4; + this.RiceTabPage.Text = "Rice"; + this.RiceTabPage.UseVisualStyleBackColor = true; + // + // RiceTabControl + // + this.RiceTabControl.Controls.Add(this.RiceGeneral); + this.RiceTabControl.Controls.Add(this.RiceGameDefaultTab); + this.RiceTabControl.Controls.Add(this.RiceTextureEnhancementTab); + this.RiceTabControl.Controls.Add(this.RiceGameSpecificTab); + this.RiceTabControl.Location = new System.Drawing.Point(0, 3); + this.RiceTabControl.Name = "RiceTabControl"; + this.RiceTabControl.SelectedIndex = 0; + this.RiceTabControl.Size = new System.Drawing.Size(572, 336); + this.RiceTabControl.TabIndex = 0; + // + // RiceGeneral + // + this.RiceGeneral.Controls.Add(this.label15); + this.RiceGeneral.Controls.Add(this.RiceScreenUpdateSetting_Combo); + this.RiceGeneral.Controls.Add(this.label14); + this.RiceGeneral.Controls.Add(this.RiceMultiSampling_Combo); + this.RiceGeneral.Controls.Add(this.label13); + this.RiceGeneral.Controls.Add(this.RiceOpenGLRenderSetting_Combo); + this.RiceGeneral.Controls.Add(this.label11); + this.RiceGeneral.Controls.Add(this.RiceColorQuality_Combo); + this.RiceGeneral.Controls.Add(this.label10); + this.RiceGeneral.Controls.Add(this.RiceOpenGLDepthBufferSetting_Combo); + this.RiceGeneral.Controls.Add(this.label9); + this.RiceGeneral.Controls.Add(this.RiceTextureQuality_Combo); + this.RiceGeneral.Controls.Add(this.RiceEnableVertexShader_CB); + this.RiceGeneral.Controls.Add(this.RiceSkipFrame_CB); + this.RiceGeneral.Controls.Add(this.RiceEnableHacks_CB); + this.RiceGeneral.Controls.Add(this.RiceFullTMEMEmulation_CB); + this.RiceGeneral.Controls.Add(this.RiceOpenGLVertexClipper_CB); + this.RiceGeneral.Controls.Add(this.AnisotropicFiltering_LB); + this.RiceGeneral.Controls.Add(this.RiceAnisotropicFiltering_TB); + this.RiceGeneral.Controls.Add(this.label6); + this.RiceGeneral.Controls.Add(this.RiceFogMethod_Combo); + this.RiceGeneral.Controls.Add(this.label5); + this.RiceGeneral.Controls.Add(this.RiceMipmapping_Combo); + this.RiceGeneral.Controls.Add(this.RiceWinFrameMode_CB); + this.RiceGeneral.Controls.Add(this.RiceInN64Resolution_CB); + this.RiceGeneral.Controls.Add(this.RiceFastTextureLoading_CB); + this.RiceGeneral.Controls.Add(this.RiceAccurateTextureMapping_CB); + this.RiceGeneral.Controls.Add(this.RiceSaveVRAM_CB); + this.RiceGeneral.Controls.Add(this.RiceEnableSSE_CB); + this.RiceGeneral.Location = new System.Drawing.Point(4, 22); + this.RiceGeneral.Name = "RiceGeneral"; + this.RiceGeneral.Padding = new System.Windows.Forms.Padding(3); + this.RiceGeneral.Size = new System.Drawing.Size(564, 310); + this.RiceGeneral.TabIndex = 0; + this.RiceGeneral.Text = "General"; + this.RiceGeneral.UseVisualStyleBackColor = true; + // + // label15 + // + this.label15.AutoSize = true; + this.label15.Location = new System.Drawing.Point(287, 204); + this.label15.Name = "label15"; + this.label15.Size = new System.Drawing.Size(118, 13); + this.label15.TabIndex = 36; + this.label15.Text = "Screen Update Setting:"; + this.label15.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label15, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); + // + // RiceScreenUpdateSetting_Combo + // + this.RiceScreenUpdateSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceScreenUpdateSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceScreenUpdateSetting_Combo.FormattingEnabled = true; + this.RiceScreenUpdateSetting_Combo.Items.AddRange(new object[] { "ROM default", "VI origin update", "VI origin change", @@ -3059,57 +3061,57 @@ "First primitive draw", "Before screen clear", "After screen drawn"}); - this.RiceScreenUpdateSetting_Combo.Location = new System.Drawing.Point(409, 201); - this.RiceScreenUpdateSetting_Combo.Name = "RiceScreenUpdateSetting_Combo"; - this.RiceScreenUpdateSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceScreenUpdateSetting_Combo.TabIndex = 35; - this.toolTip1.SetToolTip(this.RiceScreenUpdateSetting_Combo, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); - // - // label14 - // - this.label14.AutoSize = true; - this.label14.Location = new System.Drawing.Point(326, 177); - this.label14.Name = "label14"; - this.label14.Size = new System.Drawing.Size(75, 13); - this.label14.TabIndex = 34; - this.label14.Text = "MultiSampling:"; - this.label14.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label14, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); - // - // RiceMultiSampling_Combo - // - this.RiceMultiSampling_Combo.DisplayMember = "0,1,2,3"; - this.RiceMultiSampling_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceMultiSampling_Combo.FormattingEnabled = true; - this.RiceMultiSampling_Combo.Items.AddRange(new object[] { + this.RiceScreenUpdateSetting_Combo.Location = new System.Drawing.Point(409, 201); + this.RiceScreenUpdateSetting_Combo.Name = "RiceScreenUpdateSetting_Combo"; + this.RiceScreenUpdateSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceScreenUpdateSetting_Combo.TabIndex = 35; + this.toolTip1.SetToolTip(this.RiceScreenUpdateSetting_Combo, "Control when the screen will be updated.\r\n\r\nDefault: First CI change"); + // + // label14 + // + this.label14.AutoSize = true; + this.label14.Location = new System.Drawing.Point(326, 177); + this.label14.Name = "label14"; + this.label14.Size = new System.Drawing.Size(75, 13); + this.label14.TabIndex = 34; + this.label14.Text = "MultiSampling:"; + this.label14.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label14, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); + // + // RiceMultiSampling_Combo + // + this.RiceMultiSampling_Combo.DisplayMember = "0,1,2,3"; + this.RiceMultiSampling_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceMultiSampling_Combo.FormattingEnabled = true; + this.RiceMultiSampling_Combo.Items.AddRange(new object[] { "Off", "2x", "4x", "8x", "16x"}); - this.RiceMultiSampling_Combo.Location = new System.Drawing.Point(409, 174); - this.RiceMultiSampling_Combo.Name = "RiceMultiSampling_Combo"; - this.RiceMultiSampling_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceMultiSampling_Combo.TabIndex = 33; - this.toolTip1.SetToolTip(this.RiceMultiSampling_Combo, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); - // - // label13 - // - this.label13.AutoSize = true; - this.label13.Location = new System.Drawing.Point(287, 150); - this.label13.Name = "label13"; - this.label13.Size = new System.Drawing.Size(118, 13); - this.label13.TabIndex = 32; - this.label13.Text = "OpenGLRenderSetting:"; - this.label13.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label13, "OpenGL level to support.\r\n\r\nDefault: Auto"); - // - // RiceOpenGLRenderSetting_Combo - // - this.RiceOpenGLRenderSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceOpenGLRenderSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceOpenGLRenderSetting_Combo.FormattingEnabled = true; - this.RiceOpenGLRenderSetting_Combo.Items.AddRange(new object[] { + this.RiceMultiSampling_Combo.Location = new System.Drawing.Point(409, 174); + this.RiceMultiSampling_Combo.Name = "RiceMultiSampling_Combo"; + this.RiceMultiSampling_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceMultiSampling_Combo.TabIndex = 33; + this.toolTip1.SetToolTip(this.RiceMultiSampling_Combo, "Enable/Disable MultiSampling.\r\n\r\nDefault: Off"); + // + // label13 + // + this.label13.AutoSize = true; + this.label13.Location = new System.Drawing.Point(287, 150); + this.label13.Name = "label13"; + this.label13.Size = new System.Drawing.Size(118, 13); + this.label13.TabIndex = 32; + this.label13.Text = "OpenGLRenderSetting:"; + this.label13.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label13, "OpenGL level to support.\r\n\r\nDefault: Auto"); + // + // RiceOpenGLRenderSetting_Combo + // + this.RiceOpenGLRenderSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceOpenGLRenderSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceOpenGLRenderSetting_Combo.FormattingEnabled = true; + this.RiceOpenGLRenderSetting_Combo.Items.AddRange(new object[] { "Auto", "OGL_1.1", "OGL_1.2", @@ -3119,379 +3121,379 @@ "OGL_TNT2", "NVIDIA_OGL", "OpenGL Fragment Program Extension"}); - this.RiceOpenGLRenderSetting_Combo.Location = new System.Drawing.Point(409, 147); - this.RiceOpenGLRenderSetting_Combo.Name = "RiceOpenGLRenderSetting_Combo"; - this.RiceOpenGLRenderSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceOpenGLRenderSetting_Combo.TabIndex = 31; - this.toolTip1.SetToolTip(this.RiceOpenGLRenderSetting_Combo, "OpenGL level to support.\r\n\r\nDefault: Auto"); - // - // label11 - // - this.label11.AutoSize = true; - this.label11.Location = new System.Drawing.Point(326, 123); - this.label11.Name = "label11"; - this.label11.Size = new System.Drawing.Size(69, 13); - this.label11.TabIndex = 30; - this.label11.Text = "Color Quality:"; - this.label11.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label11, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); - // - // RiceColorQuality_Combo - // - this.RiceColorQuality_Combo.DisplayMember = "0,1,2,3"; - this.RiceColorQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceColorQuality_Combo.FormattingEnabled = true; - this.RiceColorQuality_Combo.Items.AddRange(new object[] { + this.RiceOpenGLRenderSetting_Combo.Location = new System.Drawing.Point(409, 147); + this.RiceOpenGLRenderSetting_Combo.Name = "RiceOpenGLRenderSetting_Combo"; + this.RiceOpenGLRenderSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceOpenGLRenderSetting_Combo.TabIndex = 31; + this.toolTip1.SetToolTip(this.RiceOpenGLRenderSetting_Combo, "OpenGL level to support.\r\n\r\nDefault: Auto"); + // + // label11 + // + this.label11.AutoSize = true; + this.label11.Location = new System.Drawing.Point(326, 123); + this.label11.Name = "label11"; + this.label11.Size = new System.Drawing.Size(69, 13); + this.label11.TabIndex = 30; + this.label11.Text = "Color Quality:"; + this.label11.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label11, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); + // + // RiceColorQuality_Combo + // + this.RiceColorQuality_Combo.DisplayMember = "0,1,2,3"; + this.RiceColorQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceColorQuality_Combo.FormattingEnabled = true; + this.RiceColorQuality_Combo.Items.AddRange(new object[] { "32-bits", "16-bits"}); - this.RiceColorQuality_Combo.Location = new System.Drawing.Point(409, 120); - this.RiceColorQuality_Combo.Name = "RiceColorQuality_Combo"; - this.RiceColorQuality_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceColorQuality_Combo.TabIndex = 29; - this.toolTip1.SetToolTip(this.RiceColorQuality_Combo, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); - // - // label10 - // - this.label10.AutoSize = true; - this.label10.Location = new System.Drawing.Point(326, 96); - this.label10.Name = "label10"; - this.label10.Size = new System.Drawing.Size(77, 13); - this.label10.TabIndex = 28; - this.label10.Text = "Z-buffer depth:"; - this.label10.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label10, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); - // - // RiceOpenGLDepthBufferSetting_Combo - // - this.RiceOpenGLDepthBufferSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceOpenGLDepthBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceOpenGLDepthBufferSetting_Combo.FormattingEnabled = true; - this.RiceOpenGLDepthBufferSetting_Combo.Items.AddRange(new object[] { + this.RiceColorQuality_Combo.Location = new System.Drawing.Point(409, 120); + this.RiceColorQuality_Combo.Name = "RiceColorQuality_Combo"; + this.RiceColorQuality_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceColorQuality_Combo.TabIndex = 29; + this.toolTip1.SetToolTip(this.RiceColorQuality_Combo, "Color bit depth for rendering window.\r\n\r\nDefault: 32-bits"); + // + // label10 + // + this.label10.AutoSize = true; + this.label10.Location = new System.Drawing.Point(326, 96); + this.label10.Name = "label10"; + this.label10.Size = new System.Drawing.Size(77, 13); + this.label10.TabIndex = 28; + this.label10.Text = "Z-buffer depth:"; + this.label10.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label10, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); + // + // RiceOpenGLDepthBufferSetting_Combo + // + this.RiceOpenGLDepthBufferSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceOpenGLDepthBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceOpenGLDepthBufferSetting_Combo.FormattingEnabled = true; + this.RiceOpenGLDepthBufferSetting_Combo.Items.AddRange(new object[] { "16-bits", "32-bits"}); - this.RiceOpenGLDepthBufferSetting_Combo.Location = new System.Drawing.Point(409, 93); - this.RiceOpenGLDepthBufferSetting_Combo.Name = "RiceOpenGLDepthBufferSetting_Combo"; - this.RiceOpenGLDepthBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceOpenGLDepthBufferSetting_Combo.TabIndex = 27; - this.toolTip1.SetToolTip(this.RiceOpenGLDepthBufferSetting_Combo, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); - // - // label9 - // - this.label9.AutoSize = true; - this.label9.Location = new System.Drawing.Point(298, 70); - this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(105, 13); - this.label9.TabIndex = 26; - this.label9.Text = "Texture Color Depth:"; - this.label9.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label9, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); - // - // RiceTextureQuality_Combo - // - this.RiceTextureQuality_Combo.DisplayMember = "0,1,2,3"; - this.RiceTextureQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceTextureQuality_Combo.FormattingEnabled = true; - this.RiceTextureQuality_Combo.Items.AddRange(new object[] { + this.RiceOpenGLDepthBufferSetting_Combo.Location = new System.Drawing.Point(409, 93); + this.RiceOpenGLDepthBufferSetting_Combo.Name = "RiceOpenGLDepthBufferSetting_Combo"; + this.RiceOpenGLDepthBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceOpenGLDepthBufferSetting_Combo.TabIndex = 27; + this.toolTip1.SetToolTip(this.RiceOpenGLDepthBufferSetting_Combo, "Z-buffer depth.\r\n\r\nDefault: 16-bits"); + // + // label9 + // + this.label9.AutoSize = true; + this.label9.Location = new System.Drawing.Point(298, 70); + this.label9.Name = "label9"; + this.label9.Size = new System.Drawing.Size(105, 13); + this.label9.TabIndex = 26; + this.label9.Text = "Texture Color Depth:"; + this.label9.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label9, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); + // + // RiceTextureQuality_Combo + // + this.RiceTextureQuality_Combo.DisplayMember = "0,1,2,3"; + this.RiceTextureQuality_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceTextureQuality_Combo.FormattingEnabled = true; + this.RiceTextureQuality_Combo.Items.AddRange(new object[] { "Default", "32-bits", "16-bits"}); - this.RiceTextureQuality_Combo.Location = new System.Drawing.Point(409, 66); - this.RiceTextureQuality_Combo.Name = "RiceTextureQuality_Combo"; - this.RiceTextureQuality_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceTextureQuality_Combo.TabIndex = 25; - this.toolTip1.SetToolTip(this.RiceTextureQuality_Combo, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); - // - // RiceEnableVertexShader_CB - // - this.RiceEnableVertexShader_CB.AutoSize = true; - this.RiceEnableVertexShader_CB.Location = new System.Drawing.Point(23, 223); - this.RiceEnableVertexShader_CB.Name = "RiceEnableVertexShader_CB"; - this.RiceEnableVertexShader_CB.Size = new System.Drawing.Size(93, 17); - this.RiceEnableVertexShader_CB.TabIndex = 24; - this.RiceEnableVertexShader_CB.Text = "Vertex Shader"; - this.toolTip1.SetToolTip(this.RiceEnableVertexShader_CB, "Use GPU vertex shader.\r\n\r\nDefault: Off"); - this.RiceEnableVertexShader_CB.UseVisualStyleBackColor = true; - // - // RiceSkipFrame_CB - // - this.RiceSkipFrame_CB.AutoSize = true; - this.RiceSkipFrame_CB.Location = new System.Drawing.Point(23, 246); - this.RiceSkipFrame_CB.Name = "RiceSkipFrame_CB"; - this.RiceSkipFrame_CB.Size = new System.Drawing.Size(79, 17); - this.RiceSkipFrame_CB.TabIndex = 23; - this.RiceSkipFrame_CB.Text = "Skip Frame"; - this.toolTip1.SetToolTip(this.RiceSkipFrame_CB, "If this option is enabled, the plugin will skip every other frame.\r\n\r\nDefault: Of" + + this.RiceTextureQuality_Combo.Location = new System.Drawing.Point(409, 66); + this.RiceTextureQuality_Combo.Name = "RiceTextureQuality_Combo"; + this.RiceTextureQuality_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceTextureQuality_Combo.TabIndex = 25; + this.toolTip1.SetToolTip(this.RiceTextureQuality_Combo, "Color bit depth to use for textures.\r\n\r\nDefault: Default"); + // + // RiceEnableVertexShader_CB + // + this.RiceEnableVertexShader_CB.AutoSize = true; + this.RiceEnableVertexShader_CB.Location = new System.Drawing.Point(23, 223); + this.RiceEnableVertexShader_CB.Name = "RiceEnableVertexShader_CB"; + this.RiceEnableVertexShader_CB.Size = new System.Drawing.Size(93, 17); + this.RiceEnableVertexShader_CB.TabIndex = 24; + this.RiceEnableVertexShader_CB.Text = "Vertex Shader"; + this.toolTip1.SetToolTip(this.RiceEnableVertexShader_CB, "Use GPU vertex shader.\r\n\r\nDefault: Off"); + this.RiceEnableVertexShader_CB.UseVisualStyleBackColor = true; + // + // RiceSkipFrame_CB + // + this.RiceSkipFrame_CB.AutoSize = true; + this.RiceSkipFrame_CB.Location = new System.Drawing.Point(23, 246); + this.RiceSkipFrame_CB.Name = "RiceSkipFrame_CB"; + this.RiceSkipFrame_CB.Size = new System.Drawing.Size(79, 17); + this.RiceSkipFrame_CB.TabIndex = 23; + this.RiceSkipFrame_CB.Text = "Skip Frame"; + this.toolTip1.SetToolTip(this.RiceSkipFrame_CB, "If this option is enabled, the plugin will skip every other frame.\r\n\r\nDefault: Of" + "f"); - this.RiceSkipFrame_CB.UseVisualStyleBackColor = true; - // - // RiceEnableHacks_CB - // - this.RiceEnableHacks_CB.AutoSize = true; - this.RiceEnableHacks_CB.Location = new System.Drawing.Point(23, 154); - this.RiceEnableHacks_CB.Name = "RiceEnableHacks_CB"; - this.RiceEnableHacks_CB.Size = new System.Drawing.Size(166, 17); - this.RiceEnableHacks_CB.TabIndex = 17; - this.RiceEnableHacks_CB.Text = "Enable game-specific settings"; - this.toolTip1.SetToolTip(this.RiceEnableHacks_CB, "Enable game-specific settings from INI file.\r\n\r\nDefault: On"); - this.RiceEnableHacks_CB.UseVisualStyleBackColor = true; - // - // RiceFullTMEMEmulation_CB - // - this.RiceFullTMEMEmulation_CB.AutoSize = true; - this.RiceFullTMEMEmulation_CB.Location = new System.Drawing.Point(23, 177); - this.RiceFullTMEMEmulation_CB.Name = "RiceFullTMEMEmulation_CB"; - this.RiceFullTMEMEmulation_CB.Size = new System.Drawing.Size(170, 17); - this.RiceFullTMEMEmulation_CB.TabIndex = 16; - this.RiceFullTMEMEmulation_CB.Text = "Full Texture Memory Emulation"; - this.toolTip1.SetToolTip(this.RiceFullTMEMEmulation_CB, "N64 Texture Memory Full Emulation.\r\nMay fix some games, may break others.\r\n\r\nDefa" + + this.RiceSkipFrame_CB.UseVisualStyleBackColor = true; + // + // RiceEnableHacks_CB + // + this.RiceEnableHacks_CB.AutoSize = true; + this.RiceEnableHacks_CB.Location = new System.Drawing.Point(23, 154); + this.RiceEnableHacks_CB.Name = "RiceEnableHacks_CB"; + this.RiceEnableHacks_CB.Size = new System.Drawing.Size(166, 17); + this.RiceEnableHacks_CB.TabIndex = 17; + this.RiceEnableHacks_CB.Text = "Enable game-specific settings"; + this.toolTip1.SetToolTip(this.RiceEnableHacks_CB, "Enable game-specific settings from INI file.\r\n\r\nDefault: On"); + this.RiceEnableHacks_CB.UseVisualStyleBackColor = true; + // + // RiceFullTMEMEmulation_CB + // + this.RiceFullTMEMEmulation_CB.AutoSize = true; + this.RiceFullTMEMEmulation_CB.Location = new System.Drawing.Point(23, 177); + this.RiceFullTMEMEmulation_CB.Name = "RiceFullTMEMEmulation_CB"; + this.RiceFullTMEMEmulation_CB.Size = new System.Drawing.Size(170, 17); + this.RiceFullTMEMEmulation_CB.TabIndex = 16; + this.RiceFullTMEMEmulation_CB.Text = "Full Texture Memory Emulation"; + this.toolTip1.SetToolTip(this.RiceFullTMEMEmulation_CB, "N64 Texture Memory Full Emulation.\r\nMay fix some games, may break others.\r\n\r\nDefa" + "ult: Off"); - this.RiceFullTMEMEmulation_CB.UseVisualStyleBackColor = true; - // - // RiceOpenGLVertexClipper_CB - // - this.RiceOpenGLVertexClipper_CB.AutoSize = true; - this.RiceOpenGLVertexClipper_CB.Location = new System.Drawing.Point(23, 200); - this.RiceOpenGLVertexClipper_CB.Name = "RiceOpenGLVertexClipper_CB"; - this.RiceOpenGLVertexClipper_CB.Size = new System.Drawing.Size(91, 17); - this.RiceOpenGLVertexClipper_CB.TabIndex = 15; - this.RiceOpenGLVertexClipper_CB.Text = "Vertex Clipper"; - this.toolTip1.SetToolTip(this.RiceOpenGLVertexClipper_CB, "Enable vertex clipper for fog operations.\r\n\r\nDefault: Off"); - this.RiceOpenGLVertexClipper_CB.UseVisualStyleBackColor = true; - // - // AnisotropicFiltering_LB - // - this.AnisotropicFiltering_LB.AutoSize = true; - this.AnisotropicFiltering_LB.Location = new System.Drawing.Point(287, 247); - this.AnisotropicFiltering_LB.Name = "AnisotropicFiltering_LB"; - this.AnisotropicFiltering_LB.Size = new System.Drawing.Size(116, 13); - this.AnisotropicFiltering_LB.TabIndex = 14; - this.AnisotropicFiltering_LB.Text = "Anisotropic Filtering: 00"; - this.toolTip1.SetToolTip(this.AnisotropicFiltering_LB, resources.GetString("AnisotropicFiltering_LB.ToolTip")); - // - // RiceAnisotropicFiltering_TB - // - this.RiceAnisotropicFiltering_TB.AutoSize = false; - this.RiceAnisotropicFiltering_TB.Location = new System.Drawing.Point(409, 235); - this.RiceAnisotropicFiltering_TB.Margin = new System.Windows.Forms.Padding(0); - this.RiceAnisotropicFiltering_TB.Maximum = 16; - this.RiceAnisotropicFiltering_TB.Name = "RiceAnisotropicFiltering_TB"; - this.RiceAnisotropicFiltering_TB.Size = new System.Drawing.Size(121, 25); - this.RiceAnisotropicFiltering_TB.TabIndex = 13; - this.toolTip1.SetToolTip(this.RiceAnisotropicFiltering_TB, resources.GetString("RiceAnisotropicFiltering_TB.ToolTip")); - this.RiceAnisotropicFiltering_TB.Scroll += new System.EventHandler(this.RiceAnisotropicFiltering_Tb_Scroll_1); - // - // label6 - // - this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(336, 44); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(67, 13); - this.label6.TabIndex = 12; - this.label6.Text = "Fog Method:"; - this.label6.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label6, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); - // - // RiceFogMethod_Combo - // - this.RiceFogMethod_Combo.DisplayMember = "0,1,2,3"; - this.RiceFogMethod_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFogMethod_Combo.FormattingEnabled = true; - this.RiceFogMethod_Combo.Items.AddRange(new object[] { + this.RiceFullTMEMEmulation_CB.UseVisualStyleBackColor = true; + // + // RiceOpenGLVertexClipper_CB + // + this.RiceOpenGLVertexClipper_CB.AutoSize = true; + this.RiceOpenGLVertexClipper_CB.Location = new System.Drawing.Point(23, 200); + this.RiceOpenGLVertexClipper_CB.Name = "RiceOpenGLVertexClipper_CB"; + this.RiceOpenGLVertexClipper_CB.Size = new System.Drawing.Size(91, 17); + this.RiceOpenGLVertexClipper_CB.TabIndex = 15; + this.RiceOpenGLVertexClipper_CB.Text = "Vertex Clipper"; + this.toolTip1.SetToolTip(this.RiceOpenGLVertexClipper_CB, "Enable vertex clipper for fog operations.\r\n\r\nDefault: Off"); + this.RiceOpenGLVertexClipper_CB.UseVisualStyleBackColor = true; + // + // AnisotropicFiltering_LB + // + this.AnisotropicFiltering_LB.AutoSize = true; + this.AnisotropicFiltering_LB.Location = new System.Drawing.Point(287, 247); + this.AnisotropicFiltering_LB.Name = "AnisotropicFiltering_LB"; + this.AnisotropicFiltering_LB.Size = new System.Drawing.Size(116, 13); + this.AnisotropicFiltering_LB.TabIndex = 14; + this.AnisotropicFiltering_LB.Text = "Anisotropic Filtering: 00"; + this.toolTip1.SetToolTip(this.AnisotropicFiltering_LB, resources.GetString("AnisotropicFiltering_LB.ToolTip")); + // + // RiceAnisotropicFiltering_TB + // + this.RiceAnisotropicFiltering_TB.AutoSize = false; + this.RiceAnisotropicFiltering_TB.Location = new System.Drawing.Point(409, 235); + this.RiceAnisotropicFiltering_TB.Margin = new System.Windows.Forms.Padding(0); + this.RiceAnisotropicFiltering_TB.Maximum = 16; + this.RiceAnisotropicFiltering_TB.Name = "RiceAnisotropicFiltering_TB"; + this.RiceAnisotropicFiltering_TB.Size = new System.Drawing.Size(121, 25); + this.RiceAnisotropicFiltering_TB.TabIndex = 13; + this.toolTip1.SetToolTip(this.RiceAnisotropicFiltering_TB, resources.GetString("RiceAnisotropicFiltering_TB.ToolTip")); + this.RiceAnisotropicFiltering_TB.Scroll += new System.EventHandler(this.RiceAnisotropicFiltering_Tb_Scroll_1); + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(336, 44); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(67, 13); + this.label6.TabIndex = 12; + this.label6.Text = "Fog Method:"; + this.label6.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label6, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); + // + // RiceFogMethod_Combo + // + this.RiceFogMethod_Combo.DisplayMember = "0,1,2,3"; + this.RiceFogMethod_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFogMethod_Combo.FormattingEnabled = true; + this.RiceFogMethod_Combo.Items.AddRange(new object[] { "Disabled", "Enabled (N64 choose)", "Force Fog"}); - this.RiceFogMethod_Combo.Location = new System.Drawing.Point(409, 39); - this.RiceFogMethod_Combo.Name = "RiceFogMethod_Combo"; - this.RiceFogMethod_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceFogMethod_Combo.TabIndex = 11; - this.toolTip1.SetToolTip(this.RiceFogMethod_Combo, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(314, 21); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(89, 13); - this.label5.TabIndex = 10; - this.label5.Text = "Use Mipmapping:"; - this.label5.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label5, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + + this.RiceFogMethod_Combo.Location = new System.Drawing.Point(409, 39); + this.RiceFogMethod_Combo.Name = "RiceFogMethod_Combo"; + this.RiceFogMethod_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceFogMethod_Combo.TabIndex = 11; + this.toolTip1.SetToolTip(this.RiceFogMethod_Combo, "Enable, Disable or Force fog generation.\r\n\r\nDefault: Disabled"); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(314, 21); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(89, 13); + this.label5.TabIndex = 10; + this.label5.Text = "Use Mipmapping:"; + this.label5.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label5, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + "e in some case.\r\n\r\nDefault: Bilinear\r\n"); - // - // RiceMipmapping_Combo - // - this.RiceMipmapping_Combo.DisplayMember = "0,1,2,3"; - this.RiceMipmapping_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceMipmapping_Combo.FormattingEnabled = true; - this.RiceMipmapping_Combo.Items.AddRange(new object[] { + // + // RiceMipmapping_Combo + // + this.RiceMipmapping_Combo.DisplayMember = "0,1,2,3"; + this.RiceMipmapping_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceMipmapping_Combo.FormattingEnabled = true; + this.RiceMipmapping_Combo.Items.AddRange(new object[] { "None", "Nearest", "Bilinear", "Trilinear"}); - this.RiceMipmapping_Combo.Location = new System.Drawing.Point(409, 16); - this.RiceMipmapping_Combo.Name = "RiceMipmapping_Combo"; - this.RiceMipmapping_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceMipmapping_Combo.TabIndex = 9; - this.toolTip1.SetToolTip(this.RiceMipmapping_Combo, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + + this.RiceMipmapping_Combo.Location = new System.Drawing.Point(409, 16); + this.RiceMipmapping_Combo.Name = "RiceMipmapping_Combo"; + this.RiceMipmapping_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceMipmapping_Combo.TabIndex = 9; + this.toolTip1.SetToolTip(this.RiceMipmapping_Combo, "Allows textures to look nicer viewed from far away.\r\nCan also increase performanc" + "e in some case.\r\n\r\nDefault: Bilinear"); - // - // RiceWinFrameMode_CB - // - this.RiceWinFrameMode_CB.AutoSize = true; - this.RiceWinFrameMode_CB.Location = new System.Drawing.Point(23, 33); - this.RiceWinFrameMode_CB.Name = "RiceWinFrameMode_CB"; - this.RiceWinFrameMode_CB.Size = new System.Drawing.Size(140, 17); - this.RiceWinFrameMode_CB.TabIndex = 8; - this.RiceWinFrameMode_CB.Text = "Enable WinFrame Mode"; - this.toolTip1.SetToolTip(this.RiceWinFrameMode_CB, "If enabled, graphics will be drawn in WinFrame mode instead of solid and texture " + + // + // RiceWinFrameMode_CB + // + this.RiceWinFrameMode_CB.AutoSize = true; + this.RiceWinFrameMode_CB.Location = new System.Drawing.Point(23, 33); + this.RiceWinFrameMode_CB.Name = "RiceWinFrameMode_CB"; + this.RiceWinFrameMode_CB.Size = new System.Drawing.Size(140, 17); + this.RiceWinFrameMode_CB.TabIndex = 8; + this.RiceWinFrameMode_CB.Text = "Enable WinFrame Mode"; + this.toolTip1.SetToolTip(this.RiceWinFrameMode_CB, "If enabled, graphics will be drawn in WinFrame mode instead of solid and texture " + "mode.\r\n\r\nDefault: Off"); - this.RiceWinFrameMode_CB.UseVisualStyleBackColor = true; - // - // RiceInN64Resolution_CB - // - this.RiceInN64Resolution_CB.AutoSize = true; - this.RiceInN64Resolution_CB.Location = new System.Drawing.Point(23, 56); - this.RiceInN64Resolution_CB.Name = "RiceInN64Resolution_CB"; - this.RiceInN64Resolution_CB.Size = new System.Drawing.Size(166, 17); - this.RiceInN64Resolution_CB.TabIndex = 7; - this.RiceInN64Resolution_CB.Text = "Force Native N64 Resolution"; - this.toolTip1.SetToolTip(this.RiceInN64Resolution_CB, "Force emulated frame buffers to be in N64 native resolution.\r\n\r\nDefault: Off"); - this.RiceInN64Resolution_CB.UseVisualStyleBackColor = true; - // - // RiceFastTextureLoading_CB - // - this.RiceFastTextureLoading_CB.AutoSize = true; - this.RiceFastTextureLoading_CB.Location = new System.Drawing.Point(23, 82); - this.RiceFastTextureLoading_CB.Name = "RiceFastTextureLoading_CB"; - this.RiceFastTextureLoading_CB.Size = new System.Drawing.Size(126, 17); - this.RiceFastTextureLoading_CB.TabIndex = 5; - this.RiceFastTextureLoading_CB.Text = "Fast Texture Loading"; - this.toolTip1.SetToolTip(this.RiceFastTextureLoading_CB, "Use a faster algorithm to speed up texture loading and CRC computation.\r\n\r\nDefaul" + + this.RiceWinFrameMode_CB.UseVisualStyleBackColor = true; + // + // RiceInN64Resolution_CB + // + this.RiceInN64Resolution_CB.AutoSize = true; + this.RiceInN64Resolution_CB.Location = new System.Drawing.Point(23, 56); + this.RiceInN64Resolution_CB.Name = "RiceInN64Resolution_CB"; + this.RiceInN64Resolution_CB.Size = new System.Drawing.Size(166, 17); + this.RiceInN64Resolution_CB.TabIndex = 7; + this.RiceInN64Resolution_CB.Text = "Force Native N64 Resolution"; + this.toolTip1.SetToolTip(this.RiceInN64Resolution_CB, "Force emulated frame buffers to be in N64 native resolution.\r\n\r\nDefault: Off"); + this.RiceInN64Resolution_CB.UseVisualStyleBackColor = true; + // + // RiceFastTextureLoading_CB + // + this.RiceFastTextureLoading_CB.AutoSize = true; + this.RiceFastTextureLoading_CB.Location = new System.Drawing.Point(23, 82); + this.RiceFastTextureLoading_CB.Name = "RiceFastTextureLoading_CB"; + this.RiceFastTextureLoading_CB.Size = new System.Drawing.Size(126, 17); + this.RiceFastTextureLoading_CB.TabIndex = 5; + this.RiceFastTextureLoading_CB.Text = "Fast Texture Loading"; + this.toolTip1.SetToolTip(this.RiceFastTextureLoading_CB, "Use a faster algorithm to speed up texture loading and CRC computation.\r\n\r\nDefaul" + "t: Off"); - this.RiceFastTextureLoading_CB.UseVisualStyleBackColor = true; - // - // RiceAccurateTextureMapping_CB - // - this.RiceAccurateTextureMapping_CB.AutoSize = true; - this.RiceAccurateTextureMapping_CB.Location = new System.Drawing.Point(23, 105); - this.RiceAccurateTextureMapping_CB.Name = "RiceAccurateTextureMapping_CB"; - this.RiceAccurateTextureMapping_CB.Size = new System.Drawing.Size(152, 17); - this.RiceAccurateTextureMapping_CB.TabIndex = 4; - this.RiceAccurateTextureMapping_CB.Text = "Accurate Texture Mapping"; - this.toolTip1.SetToolTip(this.RiceAccurateTextureMapping_CB, "Use different texture coordinate clamping code.\r\n\r\nDefault: On"); - this.RiceAccurateTextureMapping_CB.UseVisualStyleBackColor = true; - // - // RiceSaveVRAM_CB - // - this.RiceSaveVRAM_CB.AutoSize = true; - this.RiceSaveVRAM_CB.Location = new System.Drawing.Point(23, 128); - this.RiceSaveVRAM_CB.Name = "RiceSaveVRAM_CB"; - this.RiceSaveVRAM_CB.Size = new System.Drawing.Size(85, 17); - this.RiceSaveVRAM_CB.TabIndex = 3; - this.RiceSaveVRAM_CB.Text = "Save VRAM"; - this.toolTip1.SetToolTip(this.RiceSaveVRAM_CB, "Try to reduce Video RAM usage (should never be used).\r\n\r\nDefault: Off"); - this.RiceSaveVRAM_CB.UseVisualStyleBackColor = true; - // - // RiceEnableSSE_CB - // - this.RiceEnableSSE_CB.AutoSize = true; - this.RiceEnableSSE_CB.Location = new System.Drawing.Point(23, 10); - this.RiceEnableSSE_CB.Name = "RiceEnableSSE_CB"; - this.RiceEnableSSE_CB.Size = new System.Drawing.Size(83, 17); - this.RiceEnableSSE_CB.TabIndex = 0; - this.RiceEnableSSE_CB.Text = "Enable SSE"; - this.toolTip1.SetToolTip(this.RiceEnableSSE_CB, "Enable/Disable SSE optimizations for capable CPUs.\r\n\r\nDefault: On"); - this.RiceEnableSSE_CB.UseVisualStyleBackColor = true; - // - // RiceGameDefaultTab - // - this.RiceGameDefaultTab.Controls.Add(this.RiceNormalAlphaBlender_CB); - this.RiceGameDefaultTab.Controls.Add(this.RiceDefaultCombinerDisable_CB); - this.RiceGameDefaultTab.Controls.Add(this.RiceFrameBuffer_GroupBox); - this.RiceGameDefaultTab.Location = new System.Drawing.Point(4, 22); - this.RiceGameDefaultTab.Name = "RiceGameDefaultTab"; - this.RiceGameDefaultTab.Size = new System.Drawing.Size(564, 310); - this.RiceGameDefaultTab.TabIndex = 3; - this.RiceGameDefaultTab.Text = "Game Default Options"; - this.RiceGameDefaultTab.UseVisualStyleBackColor = true; - // - // RiceNormalAlphaBlender_CB - // - this.RiceNormalAlphaBlender_CB.AutoSize = true; - this.RiceNormalAlphaBlender_CB.Location = new System.Drawing.Point(63, 19); - this.RiceNormalAlphaBlender_CB.Name = "RiceNormalAlphaBlender_CB"; - this.RiceNormalAlphaBlender_CB.Size = new System.Drawing.Size(128, 17); - this.RiceNormalAlphaBlender_CB.TabIndex = 8; - this.RiceNormalAlphaBlender_CB.Text = "Normal Alpha Blender"; - this.toolTip1.SetToolTip(this.RiceNormalAlphaBlender_CB, "Force to use normal alpha blender.\r\n\r\nDefault: Off"); - this.RiceNormalAlphaBlender_CB.UseVisualStyleBackColor = true; - // - // RiceDefaultCombinerDisable_CB - // - this.RiceDefaultCombinerDisable_CB.AutoSize = true; - this.RiceDefaultCombinerDisable_CB.Location = new System.Drawing.Point(63, 42); - this.RiceDefaultCombinerDisable_CB.Name = "RiceDefaultCombinerDisable_CB"; - this.RiceDefaultCombinerDisable_CB.Size = new System.Drawing.Size(133, 17); - this.RiceDefaultCombinerDisable_CB.TabIndex = 7; - this.RiceDefaultCombinerDisable_CB.Text = "Normal Color Combiner"; - this.toolTip1.SetToolTip(this.RiceDefaultCombinerDisable_CB, "Force to use normal color combiner.\r\n\r\nDefault: Off"); - this.RiceDefaultCombinerDisable_CB.UseVisualStyleBackColor = true; - // - // RiceFrameBuffer_GroupBox - // - this.RiceFrameBuffer_GroupBox.Controls.Add(this.label16); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.label17); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.label18); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferWriteBackControl_Combo); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferSetting_Combo); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceDoubleSizeForSmallTxtrBuf_CB); - this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceRenderToTexture_Combo); - this.RiceFrameBuffer_GroupBox.Location = new System.Drawing.Point(87, 78); - this.RiceFrameBuffer_GroupBox.Name = "RiceFrameBuffer_GroupBox"; - this.RiceFrameBuffer_GroupBox.Size = new System.Drawing.Size(385, 191); - this.RiceFrameBuffer_GroupBox.TabIndex = 0; - this.RiceFrameBuffer_GroupBox.TabStop = false; - this.RiceFrameBuffer_GroupBox.Text = "N64 Frame Buffer Effect Emulation Options"; - // - // label16 - // - this.label16.AutoSize = true; - this.label16.Location = new System.Drawing.Point(8, 100); - this.label16.Name = "label16"; - this.label16.Size = new System.Drawing.Size(162, 13); - this.label16.TabIndex = 32; - this.label16.Text = "Frame Buffer Write Back Control:"; - this.label16.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label16, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); - // - // label17 - // - this.label17.AutoSize = true; - this.label17.Location = new System.Drawing.Point(8, 46); - this.label17.Name = "label17"; - this.label17.Size = new System.Drawing.Size(106, 13); - this.label17.TabIndex = 34; - this.label17.Text = "Frame Buffer Setting:"; - this.label17.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label17, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); - // - // label18 - // - this.label18.AutoSize = true; - this.label18.Location = new System.Drawing.Point(8, 73); - this.label18.Name = "label18"; - this.label18.Size = new System.Drawing.Size(159, 13); - this.label18.TabIndex = 36; - this.label18.Text = "Rendering to Texture Emulation:"; - this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label18, "Render-to-texture emulation.\r\n\r\nDefault: None"); - // - // RiceFrameBufferWriteBackControl_Combo - // - this.RiceFrameBufferWriteBackControl_Combo.DisplayMember = "0,1,2,3"; - this.RiceFrameBufferWriteBackControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFrameBufferWriteBackControl_Combo.FormattingEnabled = true; - this.RiceFrameBufferWriteBackControl_Combo.Items.AddRange(new object[] { + this.RiceFastTextureLoading_CB.UseVisualStyleBackColor = true; + // + // RiceAccurateTextureMapping_CB + // + this.RiceAccurateTextureMapping_CB.AutoSize = true; + this.RiceAccurateTextureMapping_CB.Location = new System.Drawing.Point(23, 105); + this.RiceAccurateTextureMapping_CB.Name = "RiceAccurateTextureMapping_CB"; + this.RiceAccurateTextureMapping_CB.Size = new System.Drawing.Size(152, 17); + this.RiceAccurateTextureMapping_CB.TabIndex = 4; + this.RiceAccurateTextureMapping_CB.Text = "Accurate Texture Mapping"; + this.toolTip1.SetToolTip(this.RiceAccurateTextureMapping_CB, "Use different texture coordinate clamping code.\r\n\r\nDefault: On"); + this.RiceAccurateTextureMapping_CB.UseVisualStyleBackColor = true; + // + // RiceSaveVRAM_CB + // + this.RiceSaveVRAM_CB.AutoSize = true; + this.RiceSaveVRAM_CB.Location = new System.Drawing.Point(23, 128); + this.RiceSaveVRAM_CB.Name = "RiceSaveVRAM_CB"; + this.RiceSaveVRAM_CB.Size = new System.Drawing.Size(85, 17); + this.RiceSaveVRAM_CB.TabIndex = 3; + this.RiceSaveVRAM_CB.Text = "Save VRAM"; + this.toolTip1.SetToolTip(this.RiceSaveVRAM_CB, "Try to reduce Video RAM usage (should never be used).\r\n\r\nDefault: Off"); + this.RiceSaveVRAM_CB.UseVisualStyleBackColor = true; + // + // RiceEnableSSE_CB + // + this.RiceEnableSSE_CB.AutoSize = true; + this.RiceEnableSSE_CB.Location = new System.Drawing.Point(23, 10); + this.RiceEnableSSE_CB.Name = "RiceEnableSSE_CB"; + this.RiceEnableSSE_CB.Size = new System.Drawing.Size(83, 17); + this.RiceEnableSSE_CB.TabIndex = 0; + this.RiceEnableSSE_CB.Text = "Enable SSE"; + this.toolTip1.SetToolTip(this.RiceEnableSSE_CB, "Enable/Disable SSE optimizations for capable CPUs.\r\n\r\nDefault: On"); + this.RiceEnableSSE_CB.UseVisualStyleBackColor = true; + // + // RiceGameDefaultTab + // + this.RiceGameDefaultTab.Controls.Add(this.RiceNormalAlphaBlender_CB); + this.RiceGameDefaultTab.Controls.Add(this.RiceDefaultCombinerDisable_CB); + this.RiceGameDefaultTab.Controls.Add(this.RiceFrameBuffer_GroupBox); + this.RiceGameDefaultTab.Location = new System.Drawing.Point(4, 22); + this.RiceGameDefaultTab.Name = "RiceGameDefaultTab"; + this.RiceGameDefaultTab.Size = new System.Drawing.Size(564, 310); + this.RiceGameDefaultTab.TabIndex = 3; + this.RiceGameDefaultTab.Text = "Game Default Options"; + this.RiceGameDefaultTab.UseVisualStyleBackColor = true; + // + // RiceNormalAlphaBlender_CB + // + this.RiceNormalAlphaBlender_CB.AutoSize = true; + this.RiceNormalAlphaBlender_CB.Location = new System.Drawing.Point(63, 19); + this.RiceNormalAlphaBlender_CB.Name = "RiceNormalAlphaBlender_CB"; + this.RiceNormalAlphaBlender_CB.Size = new System.Drawing.Size(128, 17); + this.RiceNormalAlphaBlender_CB.TabIndex = 8; + this.RiceNormalAlphaBlender_CB.Text = "Normal Alpha Blender"; + this.toolTip1.SetToolTip(this.RiceNormalAlphaBlender_CB, "Force to use normal alpha blender.\r\n\r\nDefault: Off"); + this.RiceNormalAlphaBlender_CB.UseVisualStyleBackColor = true; + // + // RiceDefaultCombinerDisable_CB + // + this.RiceDefaultCombinerDisable_CB.AutoSize = true; + this.RiceDefaultCombinerDisable_CB.Location = new System.Drawing.Point(63, 42); + this.RiceDefaultCombinerDisable_CB.Name = "RiceDefaultCombinerDisable_CB"; + this.RiceDefaultCombinerDisable_CB.Size = new System.Drawing.Size(133, 17); + this.RiceDefaultCombinerDisable_CB.TabIndex = 7; + this.RiceDefaultCombinerDisable_CB.Text = "Normal Color Combiner"; + this.toolTip1.SetToolTip(this.RiceDefaultCombinerDisable_CB, "Force to use normal color combiner.\r\n\r\nDefault: Off"); + this.RiceDefaultCombinerDisable_CB.UseVisualStyleBackColor = true; + // + // RiceFrameBuffer_GroupBox + // + this.RiceFrameBuffer_GroupBox.Controls.Add(this.label16); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.label17); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.label18); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferWriteBackControl_Combo); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceFrameBufferSetting_Combo); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceDoubleSizeForSmallTxtrBuf_CB); + this.RiceFrameBuffer_GroupBox.Controls.Add(this.RiceRenderToTexture_Combo); + this.RiceFrameBuffer_GroupBox.Location = new System.Drawing.Point(87, 78); + this.RiceFrameBuffer_GroupBox.Name = "RiceFrameBuffer_GroupBox"; + this.RiceFrameBuffer_GroupBox.Size = new System.Drawing.Size(385, 191); + this.RiceFrameBuffer_GroupBox.TabIndex = 0; + this.RiceFrameBuffer_GroupBox.TabStop = false; + this.RiceFrameBuffer_GroupBox.Text = "N64 Frame Buffer Effect Emulation Options"; + // + // label16 + // + this.label16.AutoSize = true; + this.label16.Location = new System.Drawing.Point(8, 100); + this.label16.Name = "label16"; + this.label16.Size = new System.Drawing.Size(162, 13); + this.label16.TabIndex = 32; + this.label16.Text = "Frame Buffer Write Back Control:"; + this.label16.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label16, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); + // + // label17 + // + this.label17.AutoSize = true; + this.label17.Location = new System.Drawing.Point(8, 46); + this.label17.Name = "label17"; + this.label17.Size = new System.Drawing.Size(106, 13); + this.label17.TabIndex = 34; + this.label17.Text = "Frame Buffer Setting:"; + this.label17.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label17, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); + // + // label18 + // + this.label18.AutoSize = true; + this.label18.Location = new System.Drawing.Point(8, 73); + this.label18.Name = "label18"; + this.label18.Size = new System.Drawing.Size(159, 13); + this.label18.TabIndex = 36; + this.label18.Text = "Rendering to Texture Emulation:"; + this.label18.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label18, "Render-to-texture emulation.\r\n\r\nDefault: None"); + // + // RiceFrameBufferWriteBackControl_Combo + // + this.RiceFrameBufferWriteBackControl_Combo.DisplayMember = "0,1,2,3"; + this.RiceFrameBufferWriteBackControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFrameBufferWriteBackControl_Combo.FormattingEnabled = true; + this.RiceFrameBufferWriteBackControl_Combo.Items.AddRange(new object[] { "Every Frame", "Every 2 Frames", "Every 3 Frames", @@ -3500,140 +3502,140 @@ "Every 6 Frames", "Every 7 Frames", "Every 8 Frames"}); - this.RiceFrameBufferWriteBackControl_Combo.Location = new System.Drawing.Point(226, 97); - this.RiceFrameBufferWriteBackControl_Combo.Name = "RiceFrameBufferWriteBackControl_Combo"; - this.RiceFrameBufferWriteBackControl_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceFrameBufferWriteBackControl_Combo.TabIndex = 33; - this.toolTip1.SetToolTip(this.RiceFrameBufferWriteBackControl_Combo, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); - // - // RiceFrameBufferSetting_Combo - // - this.RiceFrameBufferSetting_Combo.DisplayMember = "0,1,2,3"; - this.RiceFrameBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFrameBufferSetting_Combo.FormattingEnabled = true; - this.RiceFrameBufferSetting_Combo.Items.AddRange(new object[] { + this.RiceFrameBufferWriteBackControl_Combo.Location = new System.Drawing.Point(226, 97); + this.RiceFrameBufferWriteBackControl_Combo.Name = "RiceFrameBufferWriteBackControl_Combo"; + this.RiceFrameBufferWriteBackControl_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceFrameBufferWriteBackControl_Combo.TabIndex = 33; + this.toolTip1.SetToolTip(this.RiceFrameBufferWriteBackControl_Combo, "Frequency to write back the frame buffer.\r\n\r\nDefault: Every Frame"); + // + // RiceFrameBufferSetting_Combo + // + this.RiceFrameBufferSetting_Combo.DisplayMember = "0,1,2,3"; + this.RiceFrameBufferSetting_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFrameBufferSetting_Combo.FormattingEnabled = true; + this.RiceFrameBufferSetting_Combo.Items.AddRange(new object[] { "Rom Default", "Disable"}); - this.RiceFrameBufferSetting_Combo.Location = new System.Drawing.Point(226, 43); - this.RiceFrameBufferSetting_Combo.Name = "RiceFrameBufferSetting_Combo"; - this.RiceFrameBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceFrameBufferSetting_Combo.TabIndex = 31; - this.toolTip1.SetToolTip(this.RiceFrameBufferSetting_Combo, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); - // - // RiceDoubleSizeForSmallTxtrBuf_CB - // - this.RiceDoubleSizeForSmallTxtrBuf_CB.AutoSize = true; - this.RiceDoubleSizeForSmallTxtrBuf_CB.Location = new System.Drawing.Point(61, 159); - this.RiceDoubleSizeForSmallTxtrBuf_CB.Name = "RiceDoubleSizeForSmallTxtrBuf_CB"; - this.RiceDoubleSizeForSmallTxtrBuf_CB.Size = new System.Drawing.Size(253, 17); - this.RiceDoubleSizeForSmallTxtrBuf_CB.TabIndex = 3; - this.RiceDoubleSizeForSmallTxtrBuf_CB.Text = "Double Resolution For Small Render-to-Textures"; - this.toolTip1.SetToolTip(this.RiceDoubleSizeForSmallTxtrBuf_CB, "Enable this option to have better render-to-texture quality.\r\nThis requires more " + + this.RiceFrameBufferSetting_Combo.Location = new System.Drawing.Point(226, 43); + this.RiceFrameBufferSetting_Combo.Name = "RiceFrameBufferSetting_Combo"; + this.RiceFrameBufferSetting_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceFrameBufferSetting_Combo.TabIndex = 31; + this.toolTip1.SetToolTip(this.RiceFrameBufferSetting_Combo, "Frame Buffer Emulation.\r\n\r\nDefault: Rom Default"); + // + // RiceDoubleSizeForSmallTxtrBuf_CB + // + this.RiceDoubleSizeForSmallTxtrBuf_CB.AutoSize = true; + this.RiceDoubleSizeForSmallTxtrBuf_CB.Location = new System.Drawing.Point(61, 159); + this.RiceDoubleSizeForSmallTxtrBuf_CB.Name = "RiceDoubleSizeForSmallTxtrBuf_CB"; + this.RiceDoubleSizeForSmallTxtrBuf_CB.Size = new System.Drawing.Size(253, 17); + this.RiceDoubleSizeForSmallTxtrBuf_CB.TabIndex = 3; + this.RiceDoubleSizeForSmallTxtrBuf_CB.Text = "Double Resolution For Small Render-to-Textures"; + this.toolTip1.SetToolTip(this.RiceDoubleSizeForSmallTxtrBuf_CB, "Enable this option to have better render-to-texture quality.\r\nThis requires more " + "VRAM.\r\n\r\nDefault: Off"); - this.RiceDoubleSizeForSmallTxtrBuf_CB.UseVisualStyleBackColor = true; - // - // RiceRenderToTexture_Combo - // - this.RiceRenderToTexture_Combo.DisplayMember = "0,1,2,3"; - this.RiceRenderToTexture_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceRenderToTexture_Combo.FormattingEnabled = true; - this.RiceRenderToTexture_Combo.Items.AddRange(new object[] { + this.RiceDoubleSizeForSmallTxtrBuf_CB.UseVisualStyleBackColor = true; + // + // RiceRenderToTexture_Combo + // + this.RiceRenderToTexture_Combo.DisplayMember = "0,1,2,3"; + this.RiceRenderToTexture_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceRenderToTexture_Combo.FormattingEnabled = true; + this.RiceRenderToTexture_Combo.Items.AddRange(new object[] { "None", "Hide Render-to-texture Effects", "Basic Render-to-texture", "Basic & Write Back", "Write Back & Reload"}); - this.RiceRenderToTexture_Combo.Location = new System.Drawing.Point(226, 70); - this.RiceRenderToTexture_Combo.Name = "RiceRenderToTexture_Combo"; - this.RiceRenderToTexture_Combo.Size = new System.Drawing.Size(121, 21); - this.RiceRenderToTexture_Combo.TabIndex = 35; - this.toolTip1.SetToolTip(this.RiceRenderToTexture_Combo, "Render-to-texture emulation.\r\n\r\nDefault: None"); - // - // RiceTextureEnhancementTab - // - this.RiceTextureEnhancementTab.Controls.Add(this.label12); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceForceTextureFilter_Combo); - this.RiceTextureEnhancementTab.Controls.Add(this.label8); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancementControl_Combo); - this.RiceTextureEnhancementTab.Controls.Add(this.label7); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancement_Combo); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceTexRectOnly_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceSmallTextureOnly_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResCRCOnly_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResTextures_CB); - this.RiceTextureEnhancementTab.Controls.Add(this.RiceDumpTexturesToFiles_CB); - this.RiceTextureEnhancementTab.Location = new System.Drawing.Point(4, 22); - this.RiceTextureEnhancementTab.Name = "RiceTextureEnhancementTab"; - this.RiceTextureEnhancementTab.Size = new System.Drawing.Size(564, 310); - this.RiceTextureEnhancementTab.TabIndex = 2; - this.RiceTextureEnhancementTab.Text = "Texture Enhancement"; - this.RiceTextureEnhancementTab.UseVisualStyleBackColor = true; - // - // label12 - // - this.label12.AutoSize = true; - this.label12.Location = new System.Drawing.Point(134, 115); - this.label12.Name = "label12"; - this.label12.Size = new System.Drawing.Size(115, 13); - this.label12.TabIndex = 33; - this.label12.Text = "Force Texture Filtering:"; - this.toolTip1.SetToolTip(this.label12, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); - // - // RiceForceTextureFilter_Combo - // - this.RiceForceTextureFilter_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceForceTextureFilter_Combo.FormattingEnabled = true; - this.RiceForceTextureFilter_Combo.Items.AddRange(new object[] { + this.RiceRenderToTexture_Combo.Location = new System.Drawing.Point(226, 70); + this.RiceRenderToTexture_Combo.Name = "RiceRenderToTexture_Combo"; + this.RiceRenderToTexture_Combo.Size = new System.Drawing.Size(121, 21); + this.RiceRenderToTexture_Combo.TabIndex = 35; + this.toolTip1.SetToolTip(this.RiceRenderToTexture_Combo, "Render-to-texture emulation.\r\n\r\nDefault: None"); + // + // RiceTextureEnhancementTab + // + this.RiceTextureEnhancementTab.Controls.Add(this.label12); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceForceTextureFilter_Combo); + this.RiceTextureEnhancementTab.Controls.Add(this.label8); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancementControl_Combo); + this.RiceTextureEnhancementTab.Controls.Add(this.label7); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceTextureEnhancement_Combo); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceTexRectOnly_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceSmallTextureOnly_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResCRCOnly_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceLoadHiResTextures_CB); + this.RiceTextureEnhancementTab.Controls.Add(this.RiceDumpTexturesToFiles_CB); + this.RiceTextureEnhancementTab.Location = new System.Drawing.Point(4, 22); + this.RiceTextureEnhancementTab.Name = "RiceTextureEnhancementTab"; + this.RiceTextureEnhancementTab.Size = new System.Drawing.Size(564, 310); + this.RiceTextureEnhancementTab.TabIndex = 2; + this.RiceTextureEnhancementTab.Text = "Texture Enhancement"; + this.RiceTextureEnhancementTab.UseVisualStyleBackColor = true; + // + // label12 + // + this.label12.AutoSize = true; + this.label12.Location = new System.Drawing.Point(134, 115); + this.label12.Name = "label12"; + this.label12.Size = new System.Drawing.Size(115, 13); + this.label12.TabIndex = 33; + this.label12.Text = "Force Texture Filtering:"; + this.toolTip1.SetToolTip(this.label12, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); + // + // RiceForceTextureFilter_Combo + // + this.RiceForceTextureFilter_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceForceTextureFilter_Combo.FormattingEnabled = true; + this.RiceForceTextureFilter_Combo.Items.AddRange(new object[] { "Let N64 choose", "Force No Filtering", "Force Filtering"}); - this.RiceForceTextureFilter_Combo.Location = new System.Drawing.Point(287, 112); - this.RiceForceTextureFilter_Combo.Name = "RiceForceTextureFilter_Combo"; - this.RiceForceTextureFilter_Combo.Size = new System.Drawing.Size(120, 21); - this.RiceForceTextureFilter_Combo.TabIndex = 32; - this.toolTip1.SetToolTip(this.RiceForceTextureFilter_Combo, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); - // - // label8 - // - this.label8.AutoSize = true; - this.label8.Location = new System.Drawing.Point(134, 169); - this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(151, 13); - this.label8.TabIndex = 31; - this.label8.Text = "Texture Enhancement Control:"; - this.toolTip1.SetToolTip(this.label8, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); - // - // RiceTextureEnhancementControl_Combo - // - this.RiceTextureEnhancementControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceTextureEnhancementControl_Combo.FormattingEnabled = true; - this.RiceTextureEnhancementControl_Combo.Items.AddRange(new object[] { + this.RiceForceTextureFilter_Combo.Location = new System.Drawing.Point(287, 112); + this.RiceForceTextureFilter_Combo.Name = "RiceForceTextureFilter_Combo"; + this.RiceForceTextureFilter_Combo.Size = new System.Drawing.Size(120, 21); + this.RiceForceTextureFilter_Combo.TabIndex = 32; + this.toolTip1.SetToolTip(this.RiceForceTextureFilter_Combo, "Force to use texture filtering or not.\r\n\r\nDefault: Let N64 choose"); + // + // label8 + // + this.label8.AutoSize = true; + this.label8.Location = new System.Drawing.Point(134, 169); + this.label8.Name = "label8"; + this.label8.Size = new System.Drawing.Size(151, 13); + this.label8.TabIndex = 31; + this.label8.Text = "Texture Enhancement Control:"; + this.toolTip1.SetToolTip(this.label8, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); + // + // RiceTextureEnhancementControl_Combo + // + this.RiceTextureEnhancementControl_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceTextureEnhancementControl_Combo.FormattingEnabled = true; + this.RiceTextureEnhancementControl_Combo.Items.AddRange(new object[] { "Normal", "Smooth", "Less smooth", "2xSaI smooth", "Less 2xSaI smooth"}); - this.RiceTextureEnhancementControl_Combo.Location = new System.Drawing.Point(287, 166); - this.RiceTextureEnhancementControl_Combo.Name = "RiceTextureEnhancementControl_Combo"; - this.RiceTextureEnhancementControl_Combo.Size = new System.Drawing.Size(120, 21); - this.RiceTextureEnhancementControl_Combo.TabIndex = 30; - this.toolTip1.SetToolTip(this.RiceTextureEnhancementControl_Combo, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); - // - // label7 - // - this.label7.AutoSize = true; - this.label7.Location = new System.Drawing.Point(134, 142); - this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(140, 13); - this.label7.TabIndex = 29; - this.label7.Text = "Texture Enhancement Filter:"; - this.toolTip1.SetToolTip(this.label7, "Primary texture enhancement filter.\r\n\r\nDefault: None\r\n"); - // - // RiceTextureEnhancement_Combo - // - this.RiceTextureEnhancement_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceTextureEnhancement_Combo.FormattingEnabled = true; - this.RiceTextureEnhancement_Combo.Items.AddRange(new object[] { + this.RiceTextureEnhancementControl_Combo.Location = new System.Drawing.Point(287, 166); + this.RiceTextureEnhancementControl_Combo.Name = "RiceTextureEnhancementControl_Combo"; + this.RiceTextureEnhancementControl_Combo.Size = new System.Drawing.Size(120, 21); + this.RiceTextureEnhancementControl_Combo.TabIndex = 30; + this.toolTip1.SetToolTip(this.RiceTextureEnhancementControl_Combo, "Secondary texture enhancement filter.\r\n\r\nDefault: Normal"); + // + // label7 + // + this.label7.AutoSize = true; + this.label7.Location = new System.Drawing.Point(134, 142); + this.label7.Name = "label7"; + this.label7.Size = new System.Drawing.Size(140, 13); + this.label7.TabIndex = 29; + this.label7.Text = "Texture Enhancement Filter:"; + this.toolTip1.SetToolTip(this.label7, "Primary texture enhancement filter.\r\n\r\nDefault: None\r\n"); + // + // RiceTextureEnhancement_Combo + // + this.RiceTextureEnhancement_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceTextureEnhancement_Combo.FormattingEnabled = true; + this.RiceTextureEnhancement_Combo.Items.AddRange(new object[] { "None", "2X", "2XSAI", @@ -3644,133 +3646,133 @@ "Sharpen More", "External", "Mirrored"}); - this.RiceTextureEnhancement_Combo.Location = new System.Drawing.Point(287, 139); - this.RiceTextureEnhancement_Combo.Name = "RiceTextureEnhancement_Combo"; - this.RiceTextureEnhancement_Combo.Size = new System.Drawing.Size(120, 21); - this.RiceTextureEnhancement_Combo.TabIndex = 28; - this.toolTip1.SetToolTip(this.RiceTextureEnhancement_Combo, "Primary texture enhancement filter.\r\n\r\nDefault: None"); - // - // RiceTexRectOnly_CB - // - this.RiceTexRectOnly_CB.AutoSize = true; - this.RiceTexRectOnly_CB.Location = new System.Drawing.Point(316, 66); - this.RiceTexRectOnly_CB.Name = "RiceTexRectOnly_CB"; - this.RiceTexRectOnly_CB.Size = new System.Drawing.Size(91, 17); - this.RiceTexRectOnly_CB.TabIndex = 27; - this.RiceTexRectOnly_CB.Text = "TexRect Only"; - this.toolTip1.SetToolTip(this.RiceTexRectOnly_CB, "If enabled, texture enhancement will be done only for TxtRect ucode.\r\n\r\nDefault: " + + this.RiceTextureEnhancement_Combo.Location = new System.Drawing.Point(287, 139); + this.RiceTextureEnhancement_Combo.Name = "RiceTextureEnhancement_Combo"; + this.RiceTextureEnhancement_Combo.Size = new System.Drawing.Size(120, 21); + this.RiceTextureEnhancement_Combo.TabIndex = 28; + this.toolTip1.SetToolTip(this.RiceTextureEnhancement_Combo, "Primary texture enhancement filter.\r\n\r\nDefault: None"); + // + // RiceTexRectOnly_CB + // + this.RiceTexRectOnly_CB.AutoSize = true; + this.RiceTexRectOnly_CB.Location = new System.Drawing.Point(316, 66); + this.RiceTexRectOnly_CB.Name = "RiceTexRectOnly_CB"; + this.RiceTexRectOnly_CB.Size = new System.Drawing.Size(91, 17); + this.RiceTexRectOnly_CB.TabIndex = 27; + this.RiceTexRectOnly_CB.Text = "TexRect Only"; + this.toolTip1.SetToolTip(this.RiceTexRectOnly_CB, "If enabled, texture enhancement will be done only for TxtRect ucode.\r\n\r\nDefault: " + "Off"); - this.RiceTexRectOnly_CB.UseVisualStyleBackColor = true; - // - // RiceSmallTextureOnly_CB - // - this.RiceSmallTextureOnly_CB.AutoSize = true; - this.RiceSmallTextureOnly_CB.Location = new System.Drawing.Point(36, 66); - this.RiceSmallTextureOnly_CB.Name = "RiceSmallTextureOnly_CB"; - this.RiceSmallTextureOnly_CB.Size = new System.Drawing.Size(160, 17); - this.RiceSmallTextureOnly_CB.TabIndex = 26; - this.RiceSmallTextureOnly_CB.Text = "Enhance Small Texture Only"; - this.toolTip1.SetToolTip(this.RiceSmallTextureOnly_CB, "If enabled, texture enhancement will be done \r\nonly for textures width+height<=12" + + this.RiceTexRectOnly_CB.UseVisualStyleBackColor = true; + // + // RiceSmallTextureOnly_CB + // + this.RiceSmallTextureOnly_CB.AutoSize = true; + this.RiceSmallTextureOnly_CB.Location = new System.Drawing.Point(36, 66); + this.RiceSmallTextureOnly_CB.Name = "RiceSmallTextureOnly_CB"; + this.RiceSmallTextureOnly_CB.Size = new System.Drawing.Size(160, 17); + this.RiceSmallTextureOnly_CB.TabIndex = 26; + this.RiceSmallTextureOnly_CB.Text = "Enhance Small Texture Only"; + this.toolTip1.SetToolTip(this.RiceSmallTextureOnly_CB, "If enabled, texture enhancement will be done \r\nonly for textures width+height<=12" + "8.\r\n\r\nDefault: Off"); - this.RiceSmallTextureOnly_CB.UseVisualStyleBackColor = true; - // - // RiceLoadHiResCRCOnly_CB - // - this.RiceLoadHiResCRCOnly_CB.AutoSize = true; - this.RiceLoadHiResCRCOnly_CB.Location = new System.Drawing.Point(316, 43); - this.RiceLoadHiResCRCOnly_CB.Name = "RiceLoadHiResCRCOnly_CB"; - this.RiceLoadHiResCRCOnly_CB.Size = new System.Drawing.Size(201, 17); - this.RiceLoadHiResCRCOnly_CB.TabIndex = 25; - this.RiceLoadHiResCRCOnly_CB.Text = "Use CRC only to load Hi-Res Texture"; - this.toolTip1.SetToolTip(this.RiceLoadHiResCRCOnly_CB, "Select hi-resolution textures based only on the CRC and \r\nignore format+size info" + + this.RiceSmallTextureOnly_CB.UseVisualStyleBackColor = true; + // + // RiceLoadHiResCRCOnly_CB + // + this.RiceLoadHiResCRCOnly_CB.AutoSize = true; + this.RiceLoadHiResCRCOnly_CB.Location = new System.Drawing.Point(316, 43); + this.RiceLoadHiResCRCOnly_CB.Name = "RiceLoadHiResCRCOnly_CB"; + this.RiceLoadHiResCRCOnly_CB.Size = new System.Drawing.Size(201, 17); + this.RiceLoadHiResCRCOnly_CB.TabIndex = 25; + this.RiceLoadHiResCRCOnly_CB.Text = "Use CRC only to load Hi-Res Texture"; + this.toolTip1.SetToolTip(this.RiceLoadHiResCRCOnly_CB, "Select hi-resolution textures based only on the CRC and \r\nignore format+size info" + "rmation (Glide64 compatibility).\r\n\r\nDefault: On"); - this.RiceLoadHiResCRCOnly_CB.UseVisualStyleBackColor = true; - // - // RiceLoadHiResTextures_CB - // - this.RiceLoadHiResTextures_CB.AutoSize = true; - this.RiceLoadHiResTextures_CB.Location = new System.Drawing.Point(36, 43); - this.RiceLoadHiResTextures_CB.Name = "RiceLoadHiResTextures_CB"; - this.RiceLoadHiResTextures_CB.Size = new System.Drawing.Size(178, 17); - this.RiceLoadHiResTextures_CB.TabIndex = 24; - this.RiceLoadHiResTextures_CB.Text = "Load Hi-Res textures if available"; - this.toolTip1.SetToolTip(this.RiceLoadHiResTextures_CB, "Enable hi-resolution texture file loading.\r\n\r\nDefault: Off"); - this.RiceLoadHiResTextures_CB.UseVisualStyleBackColor = true; - // - // RiceDumpTexturesToFiles_CB - // - this.RiceDumpTexturesToFiles_CB.AutoSize = true; - this.RiceDumpTexturesToFiles_CB.Location = new System.Drawing.Point(273, 248); - this.RiceDumpTexturesToFiles_CB.Name = "RiceDumpTexturesToFiles_CB"; - this.RiceDumpTexturesToFiles_CB.Size = new System.Drawing.Size(134, 17); - this.RiceDumpTexturesToFiles_CB.TabIndex = 23; - this.RiceDumpTexturesToFiles_CB.Text = "Dump Textures to Files"; - this.toolTip1.SetToolTip(this.RiceDumpTexturesToFiles_CB, "Enable texture dumping.\r\n\r\nDefault: Off"); - this.RiceDumpTexturesToFiles_CB.UseVisualStyleBackColor = true; - // - // RiceGameSpecificTab - // - this.RiceGameSpecificTab.Controls.Add(this.label27); - this.RiceGameSpecificTab.Controls.Add(this.RiceEnableHacksForGame_Combo); - this.RiceGameSpecificTab.Controls.Add(this.RiceForceDepthBuffer_CB); - this.RiceGameSpecificTab.Controls.Add(this.label28); - this.RiceGameSpecificTab.Controls.Add(this.RiceUseCIWidthAndRatio_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label26); - this.RiceGameSpecificTab.Controls.Add(this.RiceRenderToTextureOption_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label25); - this.RiceGameSpecificTab.Controls.Add(this.RiceFrameBufferOption_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label24); - this.RiceGameSpecificTab.Controls.Add(this.RiceNormalBlender_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label23); - this.RiceGameSpecificTab.Controls.Add(this.RiceAccurateTextureMappingHack_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label22); - this.RiceGameSpecificTab.Controls.Add(this.RiceFastTextureCRC_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label21); - this.RiceGameSpecificTab.Controls.Add(this.RiceFullTMEM_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label20); - this.RiceGameSpecificTab.Controls.Add(this.RiceScreenUpdateSettingHack_Combo); - this.RiceGameSpecificTab.Controls.Add(this.label19); - this.RiceGameSpecificTab.Controls.Add(this.RiceVIHeight_Text); - this.RiceGameSpecificTab.Controls.Add(this.label4); - this.RiceGameSpecificTab.Controls.Add(this.RiceVIWidth_Text); - this.RiceGameSpecificTab.Controls.Add(this.RiceTextureScaleHack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceFastLoadTile_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceUseSmallerTexture_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceEnableTxtLOD_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceZHack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RicePrimaryDepthHack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableObjBG_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableBlender_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceForceScreenClear_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceEmulateClear_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceTxtSizeMethod2_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceIncTexRectEdge_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableCulling_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceDisableTextureCRC_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceTexture1Hack_CB); - this.RiceGameSpecificTab.Controls.Add(this.RiceUseDefaultHacks_CB); - this.RiceGameSpecificTab.Location = new System.Drawing.Point(4, 22); - this.RiceGameSpecificTab.Name = "RiceGameSpecificTab"; - this.RiceGameSpecificTab.Padding = new System.Windows.Forms.Padding(3); - this.RiceGameSpecificTab.Size = new System.Drawing.Size(564, 310); - this.RiceGameSpecificTab.TabIndex = 1; - this.RiceGameSpecificTab.Text = "Per-Game Hacks"; - this.RiceGameSpecificTab.UseVisualStyleBackColor = true; - // - // label27 - // - this.label27.AutoSize = true; - this.label27.Location = new System.Drawing.Point(310, 178); - this.label27.Name = "label27"; - this.label27.Size = new System.Drawing.Size(119, 13); - this.label27.TabIndex = 52; - this.label27.Text = "Enable hacks for game:"; - // - // RiceEnableHacksForGame_Combo - // - this.RiceEnableHacksForGame_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceEnableHacksForGame_Combo.FormattingEnabled = true; - this.RiceEnableHacksForGame_Combo.Items.AddRange(new object[] { + this.RiceLoadHiResCRCOnly_CB.UseVisualStyleBackColor = true; + // + // RiceLoadHiResTextures_CB + // + this.RiceLoadHiResTextures_CB.AutoSize = true; + this.RiceLoadHiResTextures_CB.Location = new System.Drawing.Point(36, 43); + this.RiceLoadHiResTextures_CB.Name = "RiceLoadHiResTextures_CB"; + this.RiceLoadHiResTextures_CB.Size = new System.Drawing.Size(178, 17); + this.RiceLoadHiResTextures_CB.TabIndex = 24; + this.RiceLoadHiResTextures_CB.Text = "Load Hi-Res textures if available"; + this.toolTip1.SetToolTip(this.RiceLoadHiResTextures_CB, "Enable hi-resolution texture file loading.\r\n\r\nDefault: Off"); + this.RiceLoadHiResTextures_CB.UseVisualStyleBackColor = true; + // + // RiceDumpTexturesToFiles_CB + // + this.RiceDumpTexturesToFiles_CB.AutoSize = true; + this.RiceDumpTexturesToFiles_CB.Location = new System.Drawing.Point(273, 248); + this.RiceDumpTexturesToFiles_CB.Name = "RiceDumpTexturesToFiles_CB"; + this.RiceDumpTexturesToFiles_CB.Size = new System.Drawing.Size(134, 17); + this.RiceDumpTexturesToFiles_CB.TabIndex = 23; + this.RiceDumpTexturesToFiles_CB.Text = "Dump Textures to Files"; + this.toolTip1.SetToolTip(this.RiceDumpTexturesToFiles_CB, "Enable texture dumping.\r\n\r\nDefault: Off"); + this.RiceDumpTexturesToFiles_CB.UseVisualStyleBackColor = true; + // + // RiceGameSpecificTab + // + this.RiceGameSpecificTab.Controls.Add(this.label27); + this.RiceGameSpecificTab.Controls.Add(this.RiceEnableHacksForGame_Combo); + this.RiceGameSpecificTab.Controls.Add(this.RiceForceDepthBuffer_CB); + this.RiceGameSpecificTab.Controls.Add(this.label28); + this.RiceGameSpecificTab.Controls.Add(this.RiceUseCIWidthAndRatio_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label26); + this.RiceGameSpecificTab.Controls.Add(this.RiceRenderToTextureOption_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label25); + this.RiceGameSpecificTab.Controls.Add(this.RiceFrameBufferOption_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label24); + this.RiceGameSpecificTab.Controls.Add(this.RiceNormalBlender_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label23); + this.RiceGameSpecificTab.Controls.Add(this.RiceAccurateTextureMappingHack_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label22); + this.RiceGameSpecificTab.Controls.Add(this.RiceFastTextureCRC_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label21); + this.RiceGameSpecificTab.Controls.Add(this.RiceFullTMEM_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label20); + this.RiceGameSpecificTab.Controls.Add(this.RiceScreenUpdateSettingHack_Combo); + this.RiceGameSpecificTab.Controls.Add(this.label19); + this.RiceGameSpecificTab.Controls.Add(this.RiceVIHeight_Text); + this.RiceGameSpecificTab.Controls.Add(this.label4); + this.RiceGameSpecificTab.Controls.Add(this.RiceVIWidth_Text); + this.RiceGameSpecificTab.Controls.Add(this.RiceTextureScaleHack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceFastLoadTile_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceUseSmallerTexture_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceEnableTxtLOD_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceZHack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RicePrimaryDepthHack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableObjBG_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableBlender_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceForceScreenClear_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceEmulateClear_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceTxtSizeMethod2_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceIncTexRectEdge_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableCulling_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceDisableTextureCRC_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceTexture1Hack_CB); + this.RiceGameSpecificTab.Controls.Add(this.RiceUseDefaultHacks_CB); + this.RiceGameSpecificTab.Location = new System.Drawing.Point(4, 22); + this.RiceGameSpecificTab.Name = "RiceGameSpecificTab"; + this.RiceGameSpecificTab.Padding = new System.Windows.Forms.Padding(3); + this.RiceGameSpecificTab.Size = new System.Drawing.Size(564, 310); + this.RiceGameSpecificTab.TabIndex = 1; + this.RiceGameSpecificTab.Text = "Per-Game Hacks"; + this.RiceGameSpecificTab.UseVisualStyleBackColor = true; + // + // label27 + // + this.label27.AutoSize = true; + this.label27.Location = new System.Drawing.Point(310, 178); + this.label27.Name = "label27"; + this.label27.Size = new System.Drawing.Size(119, 13); + this.label27.TabIndex = 52; + this.label27.Text = "Enable hacks for game:"; + // + // RiceEnableHacksForGame_Combo + // + this.RiceEnableHacksForGame_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceEnableHacksForGame_Combo.FormattingEnabled = true; + this.RiceEnableHacksForGame_Combo.Items.AddRange(new object[] { "NO_HACK_FOR_GAME", "HACK_FOR_BANJO_TOOIE", "HACK_FOR_DR_MARIO", @@ -3807,81 +3809,81 @@ "HACK_FOR_DUKE_NUKEM", "HACK_FOR_ZELDA_MM", "HACK_FOR_MARIO_KART"}); - this.RiceEnableHacksForGame_Combo.Location = new System.Drawing.Point(313, 194); - this.RiceEnableHacksForGame_Combo.Name = "RiceEnableHacksForGame_Combo"; - this.RiceEnableHacksForGame_Combo.Size = new System.Drawing.Size(240, 21); - this.RiceEnableHacksForGame_Combo.TabIndex = 51; - // - // RiceForceDepthBuffer_CB - // - this.RiceForceDepthBuffer_CB.AutoSize = true; - this.RiceForceDepthBuffer_CB.Location = new System.Drawing.Point(173, 186); - this.RiceForceDepthBuffer_CB.Name = "RiceForceDepthBuffer_CB"; - this.RiceForceDepthBuffer_CB.Size = new System.Drawing.Size(116, 17); - this.RiceForceDepthBuffer_CB.TabIndex = 50; - this.RiceForceDepthBuffer_CB.Text = "Force Depth Buffer"; - this.RiceForceDepthBuffer_CB.UseVisualStyleBackColor = true; - // - // label28 - // - this.label28.AutoSize = true; - this.label28.Location = new System.Drawing.Point(12, 275); - this.label28.Name = "label28"; - this.label28.Size = new System.Drawing.Size(115, 13); - this.label28.TabIndex = 49; - this.label28.Text = "Screen Update Setting"; - // - // RiceUseCIWidthAndRatio_Combo - // - this.RiceUseCIWidthAndRatio_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceUseCIWidthAndRatio_Combo.FormattingEnabled = true; - this.RiceUseCIWidthAndRatio_Combo.Items.AddRange(new object[] { + this.RiceEnableHacksForGame_Combo.Location = new System.Drawing.Point(313, 194); + this.RiceEnableHacksForGame_Combo.Name = "RiceEnableHacksForGame_Combo"; + this.RiceEnableHacksForGame_Combo.Size = new System.Drawing.Size(240, 21); + this.RiceEnableHacksForGame_Combo.TabIndex = 51; + // + // RiceForceDepthBuffer_CB + // + this.RiceForceDepthBuffer_CB.AutoSize = true; + this.RiceForceDepthBuffer_CB.Location = new System.Drawing.Point(173, 186); + this.RiceForceDepthBuffer_CB.Name = "RiceForceDepthBuffer_CB"; + this.RiceForceDepthBuffer_CB.Size = new System.Drawing.Size(116, 17); + this.RiceForceDepthBuffer_CB.TabIndex = 50; + this.RiceForceDepthBuffer_CB.Text = "Force Depth Buffer"; + this.RiceForceDepthBuffer_CB.UseVisualStyleBackColor = true; + // + // label28 + // + this.label28.AutoSize = true; + this.label28.Location = new System.Drawing.Point(12, 275); + this.label28.Name = "label28"; + this.label28.Size = new System.Drawing.Size(115, 13); + this.label28.TabIndex = 49; + this.label28.Text = "Screen Update Setting"; + // + // RiceUseCIWidthAndRatio_Combo + // + this.RiceUseCIWidthAndRatio_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceUseCIWidthAndRatio_Combo.FormattingEnabled = true; + this.RiceUseCIWidthAndRatio_Combo.Items.AddRange(new object[] { "NOT_USE_CI_WIDTH_AND_RATIO", "USE_CI_WIDTH_AND_RATIO_FOR_NTSC", "USE_CI_WIDTH_AND_RATIO_FOR_PAL"}); - this.RiceUseCIWidthAndRatio_Combo.Location = new System.Drawing.Point(133, 241); - this.RiceUseCIWidthAndRatio_Combo.Name = "RiceUseCIWidthAndRatio_Combo"; - this.RiceUseCIWidthAndRatio_Combo.Size = new System.Drawing.Size(274, 21); - this.RiceUseCIWidthAndRatio_Combo.TabIndex = 48; - // - // label26 - // - this.label26.AutoSize = true; - this.label26.Location = new System.Drawing.Point(310, 138); - this.label26.Name = "label26"; - this.label26.Size = new System.Drawing.Size(121, 13); - this.label26.TabIndex = 47; - this.label26.Text = "Render to texture option"; - // - // RiceRenderToTextureOption_Combo - // - this.RiceRenderToTextureOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceRenderToTextureOption_Combo.FormattingEnabled = true; - this.RiceRenderToTextureOption_Combo.Items.AddRange(new object[] { + this.RiceUseCIWidthAndRatio_Combo.Location = new System.Drawing.Point(133, 241); + this.RiceUseCIWidthAndRatio_Combo.Name = "RiceUseCIWidthAndRatio_Combo"; + this.RiceUseCIWidthAndRatio_Combo.Size = new System.Drawing.Size(274, 21); + this.RiceUseCIWidthAndRatio_Combo.TabIndex = 48; + // + // label26 + // + this.label26.AutoSize = true; + this.label26.Location = new System.Drawing.Point(310, 138); + this.label26.Name = "label26"; + this.label26.Size = new System.Drawing.Size(121, 13); + this.label26.TabIndex = 47; + this.label26.Text = "Render to texture option"; + // + // RiceRenderToTextureOption_Combo + // + this.RiceRenderToTextureOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceRenderToTextureOption_Combo.FormattingEnabled = true; + this.RiceRenderToTextureOption_Combo.Items.AddRange(new object[] { "TXT_BUF_NONE", "TXT_BUF_IGNORE", "TXT_BUF_NORMAL", "TXT_BUF_WRITE_BACK", "TXT_BUF_WRITE_BACK_AND_RELOAD"}); - this.RiceRenderToTextureOption_Combo.Location = new System.Drawing.Point(313, 154); - this.RiceRenderToTextureOption_Combo.Name = "RiceRenderToTextureOption_Combo"; - this.RiceRenderToTextureOption_Combo.Size = new System.Drawing.Size(240, 21); - this.RiceRenderToTextureOption_Combo.TabIndex = 46; - // - // label25 - // - this.label25.AutoSize = true; - this.label25.Location = new System.Drawing.Point(310, 95); - this.label25.Name = "label25"; - this.label25.Size = new System.Drawing.Size(97, 13); - this.label25.TabIndex = 45; - this.label25.Text = "Framebuffer Option"; - // - // RiceFrameBufferOption_Combo - // - this.RiceFrameBufferOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFrameBufferOption_Combo.FormattingEnabled = true; - this.RiceFrameBufferOption_Combo.Items.AddRange(new object[] { + this.RiceRenderToTextureOption_Combo.Location = new System.Drawing.Point(313, 154); + this.RiceRenderToTextureOption_Combo.Name = "RiceRenderToTextureOption_Combo"; + this.RiceRenderToTextureOption_Combo.Size = new System.Drawing.Size(240, 21); + this.RiceRenderToTextureOption_Combo.TabIndex = 46; + // + // label25 + // + this.label25.AutoSize = true; + this.label25.Location = new System.Drawing.Point(310, 95); + this.label25.Name = "label25"; + this.label25.Size = new System.Drawing.Size(97, 13); + this.label25.TabIndex = 45; + this.label25.Text = "Framebuffer Option"; + // + // RiceFrameBufferOption_Combo + // + this.RiceFrameBufferOption_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFrameBufferOption_Combo.FormattingEnabled = true; + this.RiceFrameBufferOption_Combo.Items.AddRange(new object[] { "FRM_BUF_NONE", "FRM_BUF_IGNORE", "FRM_BUF_BASIC", @@ -3892,121 +3894,121 @@ "FRM_BUF_BASIC_AND_WITH_EMULATOR", "FRM_BUF_WITH_EMULATOR_READ_ONLY", "FRM_BUF_WITH_EMULATOR_WRITE_ONLY"}); - this.RiceFrameBufferOption_Combo.Location = new System.Drawing.Point(313, 114); - this.RiceFrameBufferOption_Combo.Name = "RiceFrameBufferOption_Combo"; - this.RiceFrameBufferOption_Combo.Size = new System.Drawing.Size(239, 21); - this.RiceFrameBufferOption_Combo.TabIndex = 44; - // - // label24 - // - this.label24.AutoSize = true; - this.label24.Location = new System.Drawing.Point(430, 34); - this.label24.Name = "label24"; - this.label24.Size = new System.Drawing.Size(79, 13); - this.label24.TabIndex = 43; - this.label24.Text = "Normal Blender"; - this.label24.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // RiceNormalBlender_Combo - // - this.RiceNormalBlender_Combo.DisplayMember = "0,1,2,3"; - this.RiceNormalBlender_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceNormalBlender_Combo.FormattingEnabled = true; - this.RiceNormalBlender_Combo.Items.AddRange(new object[] { + this.RiceFrameBufferOption_Combo.Location = new System.Drawing.Point(313, 114); + this.RiceFrameBufferOption_Combo.Name = "RiceFrameBufferOption_Combo"; + this.RiceFrameBufferOption_Combo.Size = new System.Drawing.Size(239, 21); + this.RiceFrameBufferOption_Combo.TabIndex = 44; + // + // label24 + // + this.label24.AutoSize = true; + this.label24.Location = new System.Drawing.Point(430, 34); + this.label24.Name = "label24"; + this.label24.Size = new System.Drawing.Size(79, 13); + this.label24.TabIndex = 43; + this.label24.Text = "Normal Blender"; + this.label24.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // RiceNormalBlender_Combo + // + this.RiceNormalBlender_Combo.DisplayMember = "0,1,2,3"; + this.RiceNormalBlender_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceNormalBlender_Combo.FormattingEnabled = true; + this.RiceNormalBlender_Combo.Items.AddRange(new object[] { "0", "1", "2"}); - this.RiceNormalBlender_Combo.Location = new System.Drawing.Point(515, 31); - this.RiceNormalBlender_Combo.Name = "RiceNormalBlender_Combo"; - this.RiceNormalBlender_Combo.Size = new System.Drawing.Size(37, 21); - this.RiceNormalBlender_Combo.TabIndex = 42; - // - // label23 - // - this.label23.AutoSize = true; - this.label23.Location = new System.Drawing.Point(376, 7); - this.label23.Name = "label23"; - this.label23.Size = new System.Drawing.Size(133, 13); - this.label23.TabIndex = 41; - this.label23.Text = "Accurate Texture Mapping"; - this.label23.TextAlign = System.Drawing.ContentAlignment.TopRight; - this.toolTip1.SetToolTip(this.label23, "\\"); - // - // RiceAccurateTextureMappingHack_Combo - // - this.RiceAccurateTextureMappingHack_Combo.DisplayMember = "0,1,2,3"; - this.RiceAccurateTextureMappingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceAccurateTextureMappingHack_Combo.FormattingEnabled = true; - this.RiceAccurateTextureMappingHack_Combo.Items.AddRange(new object[] { + this.RiceNormalBlender_Combo.Location = new System.Drawing.Point(515, 31); + this.RiceNormalBlender_Combo.Name = "RiceNormalBlender_Combo"; + this.RiceNormalBlender_Combo.Size = new System.Drawing.Size(37, 21); + this.RiceNormalBlender_Combo.TabIndex = 42; + // + // label23 + // + this.label23.AutoSize = true; + this.label23.Location = new System.Drawing.Point(376, 7); + this.label23.Name = "label23"; + this.label23.Size = new System.Drawing.Size(133, 13); + this.label23.TabIndex = 41; + this.label23.Text = "Accurate Texture Mapping"; + this.label23.TextAlign = System.Drawing.ContentAlignment.TopRight; + this.toolTip1.SetToolTip(this.label23, "\\"); + // + // RiceAccurateTextureMappingHack_Combo + // + this.RiceAccurateTextureMappingHack_Combo.DisplayMember = "0,1,2,3"; + this.RiceAccurateTextureMappingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceAccurateTextureMappingHack_Combo.FormattingEnabled = true; + this.RiceAccurateTextureMappingHack_Combo.Items.AddRange(new object[] { "0", "1"}); - this.RiceAccurateTextureMappingHack_Combo.Location = new System.Drawing.Point(515, 4); - this.RiceAccurateTextureMappingHack_Combo.Name = "RiceAccurateTextureMappingHack_Combo"; - this.RiceAccurateTextureMappingHack_Combo.Size = new System.Drawing.Size(37, 21); - this.RiceAccurateTextureMappingHack_Combo.TabIndex = 40; - // - // label22 - // - this.label22.AutoSize = true; - this.label22.Location = new System.Drawing.Point(418, 61); - this.label22.Name = "label22"; - this.label22.Size = new System.Drawing.Size(91, 13); - this.label22.TabIndex = 39; - this.label22.Text = "Fast Texture CRC"; - this.label22.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // RiceFastTextureCRC_Combo - // - this.RiceFastTextureCRC_Combo.DisplayMember = "0,1,2,3"; - this.RiceFastTextureCRC_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFastTextureCRC_Combo.FormattingEnabled = true; - this.RiceFastTextureCRC_Combo.Items.AddRange(new object[] { + this.RiceAccurateTextureMappingHack_Combo.Location = new System.Drawing.Point(515, 4); + this.RiceAccurateTextureMappingHack_Combo.Name = "RiceAccurateTextureMappingHack_Combo"; + this.RiceAccurateTextureMappingHack_Combo.Size = new System.Drawing.Size(37, 21); + this.RiceAccurateTextureMappingHack_Combo.TabIndex = 40; + // + // label22 + // + this.label22.AutoSize = true; + this.label22.Location = new System.Drawing.Point(418, 61); + this.label22.Name = "label22"; + this.label22.Size = new System.Drawing.Size(91, 13); + this.label22.TabIndex = 39; + this.label22.Text = "Fast Texture CRC"; + this.label22.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // RiceFastTextureCRC_Combo + // + this.RiceFastTextureCRC_Combo.DisplayMember = "0,1,2,3"; + this.RiceFastTextureCRC_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFastTextureCRC_Combo.FormattingEnabled = true; + this.RiceFastTextureCRC_Combo.Items.AddRange(new object[] { "0", "1", "2"}); - this.RiceFastTextureCRC_Combo.Location = new System.Drawing.Point(515, 58); - this.RiceFastTextureCRC_Combo.Name = "RiceFastTextureCRC_Combo"; - this.RiceFastTextureCRC_Combo.Size = new System.Drawing.Size(37, 21); - this.RiceFastTextureCRC_Combo.TabIndex = 38; - // - // label21 - // - this.label21.AutoSize = true; - this.label21.Location = new System.Drawing.Point(454, 88); - this.label21.Name = "label21"; - this.label21.Size = new System.Drawing.Size(55, 13); - this.label21.TabIndex = 37; - this.label21.Text = "FullTMEM"; - this.label21.TextAlign = System.Drawing.ContentAlignment.TopRight; - // - // RiceFullTMEM_Combo - // - this.RiceFullTMEM_Combo.DisplayMember = "0,1,2,3"; - this.RiceFullTMEM_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceFullTMEM_Combo.FormattingEnabled = true; - this.RiceFullTMEM_Combo.Items.AddRange(new object[] { + this.RiceFastTextureCRC_Combo.Location = new System.Drawing.Point(515, 58); + this.RiceFastTextureCRC_Combo.Name = "RiceFastTextureCRC_Combo"; + this.RiceFastTextureCRC_Combo.Size = new System.Drawing.Size(37, 21); + this.RiceFastTextureCRC_Combo.TabIndex = 38; + // + // label21 + // + this.label21.AutoSize = true; + this.label21.Location = new System.Drawing.Point(454, 88); + this.label21.Name = "label21"; + this.label21.Size = new System.Drawing.Size(55, 13); + this.label21.TabIndex = 37; + this.label21.Text = "FullTMEM"; + this.label21.TextAlign = System.Drawing.ContentAlignment.TopRight; + // + // RiceFullTMEM_Combo + // + this.RiceFullTMEM_Combo.DisplayMember = "0,1,2,3"; + this.RiceFullTMEM_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceFullTMEM_Combo.FormattingEnabled = true; + this.RiceFullTMEM_Combo.Items.AddRange(new object[] { "0", "1", "2"}); - this.RiceFullTMEM_Combo.Location = new System.Drawing.Point(515, 85); - this.RiceFullTMEM_Combo.Name = "RiceFullTMEM_Combo"; - this.RiceFullTMEM_Combo.Size = new System.Drawing.Size(36, 21); - this.RiceFullTMEM_Combo.TabIndex = 36; - // - // label20 - // - this.label20.AutoSize = true; - this.label20.Location = new System.Drawing.Point(8, 244); - this.label20.Name = "label20"; - this.label20.Size = new System.Drawing.Size(119, 13); - this.label20.TabIndex = 35; - this.label20.Text = "Use CI Width and Ratio"; - // - // RiceScreenUpdateSettingHack_Combo - // - this.RiceScreenUpdateSettingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.RiceScreenUpdateSettingHack_Combo.FormattingEnabled = true; - this.RiceScreenUpdateSettingHack_Combo.Items.AddRange(new object[] { + this.RiceFullTMEM_Combo.Location = new System.Drawing.Point(515, 85); + this.RiceFullTMEM_Combo.Name = "RiceFullTMEM_Combo"; + this.RiceFullTMEM_Combo.Size = new System.Drawing.Size(36, 21); + this.RiceFullTMEM_Combo.TabIndex = 36; + // + // label20 + // + this.label20.AutoSize = true; + this.label20.Location = new System.Drawing.Point(8, 244); + this.label20.Name = "label20"; + this.label20.Size = new System.Drawing.Size(119, 13); + this.label20.TabIndex = 35; + this.label20.Text = "Use CI Width and Ratio"; + // + // RiceScreenUpdateSettingHack_Combo + // + this.RiceScreenUpdateSettingHack_Combo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.RiceScreenUpdateSettingHack_Combo.FormattingEnabled = true; + this.RiceScreenUpdateSettingHack_Combo.Items.AddRange(new object[] { "SCREEN_UPDATE_DEFAULT", "SCREEN_UPDATE_AT_VI_UPDATE", "SCREEN_UPDATE_AT_VI_CHANGE", @@ -4015,428 +4017,428 @@ "SCREEN_UPDATE_AT_1ST_PRIMITIVE", "SCREEN_UPDATE_BEFORE_SCREEN_CLEAR", "SCREEN_UPDATE_AT_VI_UPDATE_AND_DRAWN"}); - this.RiceScreenUpdateSettingHack_Combo.Location = new System.Drawing.Point(133, 272); - this.RiceScreenUpdateSettingHack_Combo.Name = "RiceScreenUpdateSettingHack_Combo"; - this.RiceScreenUpdateSettingHack_Combo.Size = new System.Drawing.Size(274, 21); - this.RiceScreenUpdateSettingHack_Combo.TabIndex = 34; - // - // label19 - // - this.label19.AutoSize = true; - this.label19.Location = new System.Drawing.Point(306, 62); - this.label19.Name = "label19"; - this.label19.Size = new System.Drawing.Size(51, 13); - this.label19.TabIndex = 20; - this.label19.Text = "VI Height"; - // - // RiceVIHeight_Text - // - this.RiceVIHeight_Text.Location = new System.Drawing.Point(363, 59); - this.RiceVIHeight_Text.Name = "RiceVIHeight_Text"; - this.RiceVIHeight_Text.Size = new System.Drawing.Size(36, 20); - this.RiceVIHeight_Text.TabIndex = 19; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(309, 34); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(48, 13); - this.label4.TabIndex = 18; - this.label4.Text = "VI Width"; - // - // RiceVIWidth_Text - // - this.RiceVIWidth_Text.Location = new System.Drawing.Point(363, 31); - this.RiceVIWidth_Text.Name = "RiceVIWidth_Text"; - this.RiceVIWidth_Text.Size = new System.Drawing.Size(36, 20); - this.RiceVIWidth_Text.TabIndex = 17; - // - // RiceTextureScaleHack_CB - // - this.RiceTextureScaleHack_CB.AutoSize = true; - this.RiceTextureScaleHack_CB.Location = new System.Drawing.Point(6, 140); - this.RiceTextureScaleHack_CB.Name = "RiceTextureScaleHack_CB"; - this.RiceTextureScaleHack_CB.Size = new System.Drawing.Size(121, 17); - this.RiceTextureScaleHack_CB.TabIndex = 16; - this.RiceTextureScaleHack_CB.Text = "Texture Scale Hack"; - this.RiceTextureScaleHack_CB.UseVisualStyleBackColor = true; - // - // RiceFastLoadTile_CB - // - this.RiceFastLoadTile_CB.AutoSize = true; - this.RiceFastLoadTile_CB.Location = new System.Drawing.Point(6, 209); - this.RiceFastLoadTile_CB.Name = "RiceFastLoadTile_CB"; - this.RiceFastLoadTile_CB.Size = new System.Drawing.Size(93, 17); - this.RiceFastLoadTile_CB.TabIndex = 15; - this.RiceFastLoadTile_CB.Text = "Fast Load Tile"; - this.RiceFastLoadTile_CB.UseVisualStyleBackColor = true; - // - // RiceUseSmallerTexture_CB - // - this.RiceUseSmallerTexture_CB.AutoSize = true; - this.RiceUseSmallerTexture_CB.ForeColor = System.Drawing.SystemColors.ControlText; - this.RiceUseSmallerTexture_CB.Location = new System.Drawing.Point(173, 209); - this.RiceUseSmallerTexture_CB.Name = "RiceUseSmallerTexture_CB"; - this.RiceUseSmallerTexture_CB.Size = new System.Drawing.Size(121, 17); - this.RiceUseSmallerTexture_CB.TabIndex = 14; - this.RiceUseSmallerTexture_CB.Text = "Use Smaller Texture"; - this.RiceUseSmallerTexture_CB.UseVisualStyleBackColor = true; - // - // RiceEnableTxtLOD_CB - // - this.RiceEnableTxtLOD_CB.AutoSize = true; - this.RiceEnableTxtLOD_CB.Location = new System.Drawing.Point(173, 71); - this.RiceEnableTxtLOD_CB.Name = "RiceEnableTxtLOD_CB"; - this.RiceEnableTxtLOD_CB.Size = new System.Drawing.Size(102, 17); - this.RiceEnableTxtLOD_CB.TabIndex = 13; - this.RiceEnableTxtLOD_CB.Text = "Enable Txt LOD"; - this.RiceEnableTxtLOD_CB.UseVisualStyleBackColor = true; - // - // RiceZHack_CB - // - this.RiceZHack_CB.AutoSize = true; - this.RiceZHack_CB.Location = new System.Drawing.Point(6, 117); - this.RiceZHack_CB.Name = "RiceZHack_CB"; - this.RiceZHack_CB.Size = new System.Drawing.Size(62, 17); - this.RiceZHack_CB.TabIndex = 12; - this.RiceZHack_CB.Text = "Z Hack"; - this.RiceZHack_CB.UseVisualStyleBackColor = true; - // - // RicePrimaryDepthHack_CB - // - this.RicePrimaryDepthHack_CB.AutoSize = true; - this.RicePrimaryDepthHack_CB.Location = new System.Drawing.Point(6, 163); - this.RicePrimaryDepthHack_CB.Name = "RicePrimaryDepthHack_CB"; - this.RicePrimaryDepthHack_CB.Size = new System.Drawing.Size(121, 17); - this.RicePrimaryDepthHack_CB.TabIndex = 11; - this.RicePrimaryDepthHack_CB.Text = "Primary Depth Hack"; - this.RicePrimaryDepthHack_CB.UseVisualStyleBackColor = true; - // - // RiceDisableObjBG_CB - // - this.RiceDisableObjBG_CB.AutoSize = true; - this.RiceDisableObjBG_CB.Location = new System.Drawing.Point(173, 163); - this.RiceDisableObjBG_CB.Name = "RiceDisableObjBG_CB"; - this.RiceDisableObjBG_CB.Size = new System.Drawing.Size(98, 17); - this.RiceDisableObjBG_CB.TabIndex = 10; - this.RiceDisableObjBG_CB.Text = "Disable Obj BG"; - this.RiceDisableObjBG_CB.UseVisualStyleBackColor = true; - // - // RiceDisableBlender_CB - // - this.RiceDisableBlender_CB.AutoSize = true; - this.RiceDisableBlender_CB.Location = new System.Drawing.Point(173, 140); - this.RiceDisableBlender_CB.Name = "RiceDisableBlender_CB"; - this.RiceDisableBlender_CB.Size = new System.Drawing.Size(100, 17); - this.RiceDisableBlender_CB.TabIndex = 9; - this.RiceDisableBlender_CB.Text = "Disable Blender"; - this.RiceDisableBlender_CB.UseVisualStyleBackColor = true; - // - // RiceForceScreenClear_CB - // - this.RiceForceScreenClear_CB.AutoSize = true; - this.RiceForceScreenClear_CB.Location = new System.Drawing.Point(173, 117); - this.RiceForceScreenClear_CB.Name = "RiceForceScreenClear_CB"; - this.RiceForceScreenClear_CB.Size = new System.Drawing.Size(117, 17); - this.RiceForceScreenClear_CB.TabIndex = 8; - this.RiceForceScreenClear_CB.Text = "Force Screen Clear"; - this.RiceForceScreenClear_CB.UseVisualStyleBackColor = true; - // - // RiceEmulateClear_CB - // - this.RiceEmulateClear_CB.AutoSize = true; - this.RiceEmulateClear_CB.Location = new System.Drawing.Point(173, 94); - this.RiceEmulateClear_CB.Name = "RiceEmulateClear_CB"; - this.RiceEmulateClear_CB.Size = new System.Drawing.Size(91, 17); - this.RiceEmulateClear_CB.TabIndex = 7; - this.RiceEmulateClear_CB.Text = "Emulate Clear"; - this.RiceEmulateClear_CB.UseVisualStyleBackColor = true; - // - // RiceTxtSizeMethod2_CB - // - this.RiceTxtSizeMethod2_CB.AutoSize = true; - this.RiceTxtSizeMethod2_CB.Location = new System.Drawing.Point(173, 48); - this.RiceTxtSizeMethod2_CB.Name = "RiceTxtSizeMethod2_CB"; - this.RiceTxtSizeMethod2_CB.Size = new System.Drawing.Size(112, 17); - this.RiceTxtSizeMethod2_CB.TabIndex = 6; - this.RiceTxtSizeMethod2_CB.Text = "Txt Size Method 2"; - this.RiceTxtSizeMethod2_CB.UseVisualStyleBackColor = true; - // - // RiceIncTexRectEdge_CB - // - this.RiceIncTexRectEdge_CB.AutoSize = true; - this.RiceIncTexRectEdge_CB.Location = new System.Drawing.Point(6, 94); - this.RiceIncTexRectEdge_CB.Name = "RiceIncTexRectEdge_CB"; - this.RiceIncTexRectEdge_CB.Size = new System.Drawing.Size(116, 17); - this.RiceIncTexRectEdge_CB.TabIndex = 5; - this.RiceIncTexRectEdge_CB.Text = "Inc Tex Rect Edge"; - this.RiceIncTexRectEdge_CB.UseVisualStyleBackColor = true; - // - // RiceDisableCulling_CB - // - this.RiceDisableCulling_CB.AutoSize = true; - this.RiceDisableCulling_CB.Location = new System.Drawing.Point(6, 71); - this.RiceDisableCulling_CB.Name = "RiceDisableCulling_CB"; - this.RiceDisableCulling_CB.Size = new System.Drawing.Size(95, 17); - this.RiceDisableCulling_CB.TabIndex = 4; - this.RiceDisableCulling_CB.Text = "Disable Culling"; - this.RiceDisableCulling_CB.UseVisualStyleBackColor = true; - // - // RiceDisableTextureCRC_CB - // - this.RiceDisableTextureCRC_CB.AutoSize = true; - this.RiceDisableTextureCRC_CB.Location = new System.Drawing.Point(6, 48); - this.RiceDisableTextureCRC_CB.Name = "RiceDisableTextureCRC_CB"; - this.RiceDisableTextureCRC_CB.Size = new System.Drawing.Size(125, 17); - this.RiceDisableTextureCRC_CB.TabIndex = 3; - this.RiceDisableTextureCRC_CB.Text = "Disable Texture CRC"; - this.RiceDisableTextureCRC_CB.UseVisualStyleBackColor = true; - // - // RiceTexture1Hack_CB - // - this.RiceTexture1Hack_CB.AutoSize = true; - this.RiceTexture1Hack_CB.Location = new System.Drawing.Point(6, 186); - this.RiceTexture1Hack_CB.Name = "RiceTexture1Hack_CB"; - this.RiceTexture1Hack_CB.Size = new System.Drawing.Size(97, 17); - this.RiceTexture1Hack_CB.TabIndex = 2; - this.RiceTexture1Hack_CB.Text = "Texture1 Hack"; - this.RiceTexture1Hack_CB.UseVisualStyleBackColor = true; - // - // RiceUseDefaultHacks_CB - // - this.RiceUseDefaultHacks_CB.AutoSize = true; - this.RiceUseDefaultHacks_CB.Location = new System.Drawing.Point(6, 6); - this.RiceUseDefaultHacks_CB.Name = "RiceUseDefaultHacks_CB"; - this.RiceUseDefaultHacks_CB.Size = new System.Drawing.Size(165, 17); - this.RiceUseDefaultHacks_CB.TabIndex = 1; - this.RiceUseDefaultHacks_CB.Text = "Use defaults for current game"; - this.RiceUseDefaultHacks_CB.UseVisualStyleBackColor = true; - this.RiceUseDefaultHacks_CB.CheckedChanged += new System.EventHandler(this.RiceUseDefaultHacks_Cb_CheckedChanged); - // - // label71 - // - this.label71.AutoSize = true; - this.label71.Location = new System.Drawing.Point(15, 94); - this.label71.Name = "label71"; - this.label71.Size = new System.Drawing.Size(91, 13); - this.label71.TabIndex = 32; - this.label71.Text = "Resolution Height"; - // - // label70 - // - this.label70.AutoSize = true; - this.label70.Location = new System.Drawing.Point(15, 64); - this.label70.Name = "label70"; - this.label70.Size = new System.Drawing.Size(88, 13); - this.label70.TabIndex = 31; - this.label70.Text = "Resolution Width"; - // - // label69 - // - this.label69.AutoSize = true; - this.label69.Location = new System.Drawing.Point(15, 34); - this.label69.Name = "label69"; - this.label69.Size = new System.Drawing.Size(106, 13); - this.label69.TabIndex = 0; - this.label69.Text = "Direct3D Clear Mode"; - // - // label68 - // - this.label68.AutoSize = true; - this.label68.Location = new System.Drawing.Point(51, 316); - this.label68.Name = "label68"; - this.label68.Size = new System.Drawing.Size(87, 13); - this.label68.TabIndex = 27; - this.label68.Text = "Copy framebuffer"; - // - // label67 - // - this.label67.AutoSize = true; - this.label67.Location = new System.Drawing.Point(49, 286); - this.label67.Name = "label67"; - this.label67.Size = new System.Drawing.Size(89, 13); - this.label67.TabIndex = 25; - this.label67.Text = "Force Z Compare"; - // - // label66 - // - this.label66.AutoSize = true; - this.label66.Location = new System.Drawing.Point(24, 256); - this.label66.Name = "label66"; - this.label66.Size = new System.Drawing.Size(114, 13); - this.label66.TabIndex = 23; - this.label66.Text = "Direct3D trans pipeline"; - // - // label65 - // - this.label65.AutoSize = true; - this.label65.Location = new System.Drawing.Point(32, 226); - this.label65.Name = "label65"; - this.label65.Size = new System.Drawing.Size(106, 13); - this.label65.TabIndex = 21; - this.label65.Text = "Force alpha blending"; - // - // label64 - // - this.label64.AutoSize = true; - this.label64.Location = new System.Drawing.Point(15, 195); - this.label64.Name = "label64"; - this.label64.Size = new System.Drawing.Size(123, 13); - this.label64.TabIndex = 19; - this.label64.Text = "Use legacy pixel pipeline"; - // - // label63 - // - this.label63.AutoSize = true; - this.label63.Location = new System.Drawing.Point(15, 177); - this.label63.Name = "label63"; - this.label63.Size = new System.Drawing.Size(76, 13); - this.label63.TabIndex = 17; - this.label63.Text = "to match yours"; - // - // label62 - // - this.label62.AutoSize = true; - this.label62.Location = new System.Drawing.Point(15, 164); - this.label62.Name = "label62"; - this.label62.Size = new System.Drawing.Size(123, 13); - this.label62.TabIndex = 16; - this.label62.Text = "Adjust game aspect ratio"; - // - // label61 - // - this.label61.AutoSize = true; - this.label61.Location = new System.Drawing.Point(49, 49); - this.label61.Name = "label61"; - this.label61.Size = new System.Drawing.Size(89, 13); - this.label61.TabIndex = 14; - this.label61.Text = "Antialiasing Level"; - // - // label60 - // - this.label60.AutoSize = true; - this.label60.Location = new System.Drawing.Point(21, 136); - this.label60.Name = "label60"; - this.label60.Size = new System.Drawing.Size(117, 13); - this.label60.TabIndex = 12; - this.label60.Text = "Always use texture filter"; - // - // label59 - // - this.label59.AutoSize = true; - this.label59.Location = new System.Drawing.Point(76, 107); - this.label59.Name = "label59"; - this.label59.Size = new System.Drawing.Size(62, 13); - this.label59.TabIndex = 10; - this.label59.Text = "Super 2xsal"; - // - // label57 - // - this.label57.AutoSize = true; - this.label57.Location = new System.Drawing.Point(82, 79); - this.label57.Name = "label57"; - this.label57.Size = new System.Drawing.Size(56, 13); - this.label57.TabIndex = 7; - this.label57.Text = "Brightness"; - // - // label51 - // - this.label51.AutoSize = true; - this.label51.Location = new System.Drawing.Point(18, 15); - this.label51.Name = "label51"; - this.label51.Size = new System.Drawing.Size(120, 13); - this.label51.TabIndex = 5; - this.label51.Text = "Anisotropic filtering level"; - // - // SaveButton - // - this.SaveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.SaveButton.Location = new System.Drawing.Point(409, 373); - this.SaveButton.Name = "SaveButton"; - this.SaveButton.Size = new System.Drawing.Size(75, 23); - this.SaveButton.TabIndex = 100; - this.SaveButton.Text = "Save"; - this.SaveButton.UseVisualStyleBackColor = true; - this.SaveButton.Click += new System.EventHandler(this.Button1_Click); - // - // CancelBT - // - this.CancelBT.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CancelBT.DialogResult = System.Windows.Forms.DialogResult.Cancel; - this.CancelBT.Location = new System.Drawing.Point(490, 373); - this.CancelBT.Name = "CancelBT"; - this.CancelBT.Size = new System.Drawing.Size(75, 23); - this.CancelBT.TabIndex = 105; - this.CancelBT.Text = "Cancel"; - this.CancelBT.UseVisualStyleBackColor = true; - this.CancelBT.Click += new System.EventHandler(this.CancelBtn_Click); - // - // toolTip1 - // - this.toolTip1.AutoPopDelay = 10000; - this.toolTip1.InitialDelay = 500; - this.toolTip1.IsBalloon = true; - this.toolTip1.ReshowDelay = 100; - this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; - // - // N64VideoPluginconfig - // - this.AcceptButton = this.SaveButton; - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.CancelButton = this.CancelBT; - this.ClientSize = new System.Drawing.Size(577, 401); - this.Controls.Add(this.CancelBT); - this.Controls.Add(this.SaveButton); - this.Controls.Add(this.N64plugintabcontrol); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Name = "N64VideoPluginconfig"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - this.Text = "Plugin Settings"; - this.Load += new System.EventHandler(this.N64VideoPluginconfig_Load); - this.N64plugintabcontrol.ResumeLayout(false); - this.N64vpluginglobaltab.ResumeLayout(false); - this.N64vpluginglobaltab.PerformLayout(); - this.GLideN64Tab.ResumeLayout(false); - this.tabControl3.ResumeLayout(false); - this.tabPage5.ResumeLayout(false); - this.tabPage5.PerformLayout(); - this.tabPage6.ResumeLayout(false); - this.tabPage6.PerformLayout(); - this.tabPage7.ResumeLayout(false); - this.tabPage7.PerformLayout(); - this.Glide64mk2TabPage.ResumeLayout(false); - this.tabControl2.ResumeLayout(false); - this.tabPage1.ResumeLayout(false); - this.tabPage1.PerformLayout(); - this.tabPage2.ResumeLayout(false); - this.tabPage2.PerformLayout(); - this.tabPage3.ResumeLayout(false); - this.tabPage3.PerformLayout(); - this.Glide64TabPage.ResumeLayout(false); - this.tabControl1.ResumeLayout(false); - this.Glide64General.ResumeLayout(false); - this.Glide64General.PerformLayout(); - this.GlidePerGameHacks1.ResumeLayout(false); - this.GlidePerGameHacks1.PerformLayout(); - this.GlidePerGameHacks2.ResumeLayout(false); - this.GlidePerGameHacks2.PerformLayout(); - this.RiceTabPage.ResumeLayout(false); - this.RiceTabControl.ResumeLayout(false); - this.RiceGeneral.ResumeLayout(false); - this.RiceGeneral.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).EndInit(); - this.RiceGameDefaultTab.ResumeLayout(false); - this.RiceGameDefaultTab.PerformLayout(); - this.RiceFrameBuffer_GroupBox.ResumeLayout(false); - this.RiceFrameBuffer_GroupBox.PerformLayout(); - this.RiceTextureEnhancementTab.ResumeLayout(false); - this.RiceTextureEnhancementTab.PerformLayout(); - this.RiceGameSpecificTab.ResumeLayout(false); - this.RiceGameSpecificTab.PerformLayout(); - this.ResumeLayout(false); + this.RiceScreenUpdateSettingHack_Combo.Location = new System.Drawing.Point(133, 272); + this.RiceScreenUpdateSettingHack_Combo.Name = "RiceScreenUpdateSettingHack_Combo"; + this.RiceScreenUpdateSettingHack_Combo.Size = new System.Drawing.Size(274, 21); + this.RiceScreenUpdateSettingHack_Combo.TabIndex = 34; + // + // label19 + // + this.label19.AutoSize = true; + this.label19.Location = new System.Drawing.Point(306, 62); + this.label19.Name = "label19"; + this.label19.Size = new System.Drawing.Size(51, 13); + this.label19.TabIndex = 20; + this.label19.Text = "VI Height"; + // + // RiceVIHeight_Text + // + this.RiceVIHeight_Text.Location = new System.Drawing.Point(363, 59); + this.RiceVIHeight_Text.Name = "RiceVIHeight_Text"; + this.RiceVIHeight_Text.Size = new System.Drawing.Size(36, 20); + this.RiceVIHeight_Text.TabIndex = 19; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(309, 34); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(48, 13); + this.label4.TabIndex = 18; + this.label4.Text = "VI Width"; + // + // RiceVIWidth_Text + // + this.RiceVIWidth_Text.Location = new System.Drawing.Point(363, 31); + this.RiceVIWidth_Text.Name = "RiceVIWidth_Text"; + this.RiceVIWidth_Text.Size = new System.Drawing.Size(36, 20); + this.RiceVIWidth_Text.TabIndex = 17; + // + // RiceTextureScaleHack_CB + // + this.RiceTextureScaleHack_CB.AutoSize = true; + this.RiceTextureScaleHack_CB.Location = new System.Drawing.Point(6, 140); + this.RiceTextureScaleHack_CB.Name = "RiceTextureScaleHack_CB"; + this.RiceTextureScaleHack_CB.Size = new System.Drawing.Size(121, 17); + this.RiceTextureScaleHack_CB.TabIndex = 16; + this.RiceTextureScaleHack_CB.Text = "Texture Scale Hack"; + this.RiceTextureScaleHack_CB.UseVisualStyleBackColor = true; + // + // RiceFastLoadTile_CB + // + this.RiceFastLoadTile_CB.AutoSize = true; + this.RiceFastLoadTile_CB.Location = new System.Drawing.Point(6, 209); + this.RiceFastLoadTile_CB.Name = "RiceFastLoadTile_CB"; + this.RiceFastLoadTile_CB.Size = new System.Drawing.Size(93, 17); + this.RiceFastLoadTile_CB.TabIndex = 15; + this.RiceFastLoadTile_CB.Text = "Fast Load Tile"; + this.RiceFastLoadTile_CB.UseVisualStyleBackColor = true; + // + // RiceUseSmallerTexture_CB + // + this.RiceUseSmallerTexture_CB.AutoSize = true; + this.RiceUseSmallerTexture_CB.ForeColor = System.Drawing.SystemColors.ControlText; + this.RiceUseSmallerTexture_CB.Location = new System.Drawing.Point(173, 209); + this.RiceUseSmallerTexture_CB.Name = "RiceUseSmallerTexture_CB"; + this.RiceUseSmallerTexture_CB.Size = new System.Drawing.Size(121, 17); + this.RiceUseSmallerTexture_CB.TabIndex = 14; + this.RiceUseSmallerTexture_CB.Text = "Use Smaller Texture"; + this.RiceUseSmallerTexture_CB.UseVisualStyleBackColor = true; + // + // RiceEnableTxtLOD_CB + // + this.RiceEnableTxtLOD_CB.AutoSize = true; + this.RiceEnableTxtLOD_CB.Location = new System.Drawing.Point(173, 71); + this.RiceEnableTxtLOD_CB.Name = "RiceEnableTxtLOD_CB"; + this.RiceEnableTxtLOD_CB.Size = new System.Drawing.Size(102, 17); + this.RiceEnableTxtLOD_CB.TabIndex = 13; + this.RiceEnableTxtLOD_CB.Text = "Enable Txt LOD"; + this.RiceEnableTxtLOD_CB.UseVisualStyleBackColor = true; + // + // RiceZHack_CB + // + this.RiceZHack_CB.AutoSize = true; + this.RiceZHack_CB.Location = new System.Drawing.Point(6, 117); + this.RiceZHack_CB.Name = "RiceZHack_CB"; + this.RiceZHack_CB.Size = new System.Drawing.Size(62, 17); + this.RiceZHack_CB.TabIndex = 12; + this.RiceZHack_CB.Text = "Z Hack"; + this.RiceZHack_CB.UseVisualStyleBackColor = true; + // + // RicePrimaryDepthHack_CB + // + this.RicePrimaryDepthHack_CB.AutoSize = true; + this.RicePrimaryDepthHack_CB.Location = new System.Drawing.Point(6, 163); + this.RicePrimaryDepthHack_CB.Name = "RicePrimaryDepthHack_CB"; + this.RicePrimaryDepthHack_CB.Size = new System.Drawing.Size(121, 17); + this.RicePrimaryDepthHack_CB.TabIndex = 11; + this.RicePrimaryDepthHack_CB.Text = "Primary Depth Hack"; + this.RicePrimaryDepthHack_CB.UseVisualStyleBackColor = true; + // + // RiceDisableObjBG_CB + // + this.RiceDisableObjBG_CB.AutoSize = true; + this.RiceDisableObjBG_CB.Location = new System.Drawing.Point(173, 163); + this.RiceDisableObjBG_CB.Name = "RiceDisableObjBG_CB"; + this.RiceDisableObjBG_CB.Size = new System.Drawing.Size(98, 17); + this.RiceDisableObjBG_CB.TabIndex = 10; + this.RiceDisableObjBG_CB.Text = "Disable Obj BG"; + this.RiceDisableObjBG_CB.UseVisualStyleBackColor = true; + // + // RiceDisableBlender_CB + // + this.RiceDisableBlender_CB.AutoSize = true; + this.RiceDisableBlender_CB.Location = new System.Drawing.Point(173, 140); + this.RiceDisableBlender_CB.Name = "RiceDisableBlender_CB"; + this.RiceDisableBlender_CB.Size = new System.Drawing.Size(100, 17); + this.RiceDisableBlender_CB.TabIndex = 9; + this.RiceDisableBlender_CB.Text = "Disable Blender"; + this.RiceDisableBlender_CB.UseVisualStyleBackColor = true; + // + // RiceForceScreenClear_CB + // + this.RiceForceScreenClear_CB.AutoSize = true; + this.RiceForceScreenClear_CB.Location = new System.Drawing.Point(173, 117); + this.RiceForceScreenClear_CB.Name = "RiceForceScreenClear_CB"; + this.RiceForceScreenClear_CB.Size = new System.Drawing.Size(117, 17); + this.RiceForceScreenClear_CB.TabIndex = 8; + this.RiceForceScreenClear_CB.Text = "Force Screen Clear"; + this.RiceForceScreenClear_CB.UseVisualStyleBackColor = true; + // + // RiceEmulateClear_CB + // + this.RiceEmulateClear_CB.AutoSize = true; + this.RiceEmulateClear_CB.Location = new System.Drawing.Point(173, 94); + this.RiceEmulateClear_CB.Name = "RiceEmulateClear_CB"; + this.RiceEmulateClear_CB.Size = new System.Drawing.Size(91, 17); + this.RiceEmulateClear_CB.TabIndex = 7; + this.RiceEmulateClear_CB.Text = "Emulate Clear"; + this.RiceEmulateClear_CB.UseVisualStyleBackColor = true; + // + // RiceTxtSizeMethod2_CB + // + this.RiceTxtSizeMethod2_CB.AutoSize = true; + this.RiceTxtSizeMethod2_CB.Location = new System.Drawing.Point(173, 48); + this.RiceTxtSizeMethod2_CB.Name = "RiceTxtSizeMethod2_CB"; + this.RiceTxtSizeMethod2_CB.Size = new System.Drawing.Size(112, 17); + this.RiceTxtSizeMethod2_CB.TabIndex = 6; + this.RiceTxtSizeMethod2_CB.Text = "Txt Size Method 2"; + this.RiceTxtSizeMethod2_CB.UseVisualStyleBackColor = true; + // + // RiceIncTexRectEdge_CB + // + this.RiceIncTexRectEdge_CB.AutoSize = true; + this.RiceIncTexRectEdge_CB.Location = new System.Drawing.Point(6, 94); + this.RiceIncTexRectEdge_CB.Name = "RiceIncTexRectEdge_CB"; + this.RiceIncTexRectEdge_CB.Size = new System.Drawing.Size(116, 17); + this.RiceIncTexRectEdge_CB.TabIndex = 5; + this.RiceIncTexRectEdge_CB.Text = "Inc Tex Rect Edge"; + this.RiceIncTexRectEdge_CB.UseVisualStyleBackColor = true; + // + // RiceDisableCulling_CB + // + this.RiceDisableCulling_CB.AutoSize = true; + this.RiceDisableCulling_CB.Location = new System.Drawing.Point(6, 71); + this.RiceDisableCulling_CB.Name = "RiceDisableCulling_CB"; + this.RiceDisableCulling_CB.Size = new System.Drawing.Size(95, 17); + this.RiceDisableCulling_CB.TabIndex = 4; + this.RiceDisableCulling_CB.Text = "Disable Culling"; + this.RiceDisableCulling_CB.UseVisualStyleBackColor = true; + // + // RiceDisableTextureCRC_CB + // + this.RiceDisableTextureCRC_CB.AutoSize = true; + this.RiceDisableTextureCRC_CB.Location = new System.Drawing.Point(6, 48); + this.RiceDisableTextureCRC_CB.Name = "RiceDisableTextureCRC_CB"; + this.RiceDisableTextureCRC_CB.Size = new System.Drawing.Size(125, 17); + this.RiceDisableTextureCRC_CB.TabIndex = 3; + this.RiceDisableTextureCRC_CB.Text = "Disable Texture CRC"; + this.RiceDisableTextureCRC_CB.UseVisualStyleBackColor = true; + // + // RiceTexture1Hack_CB + // + this.RiceTexture1Hack_CB.AutoSize = true; + this.RiceTexture1Hack_CB.Location = new System.Drawing.Point(6, 186); + this.RiceTexture1Hack_CB.Name = "RiceTexture1Hack_CB"; + this.RiceTexture1Hack_CB.Size = new System.Drawing.Size(97, 17); + this.RiceTexture1Hack_CB.TabIndex = 2; + this.RiceTexture1Hack_CB.Text = "Texture1 Hack"; + this.RiceTexture1Hack_CB.UseVisualStyleBackColor = true; + // + // RiceUseDefaultHacks_CB + // + this.RiceUseDefaultHacks_CB.AutoSize = true; + this.RiceUseDefaultHacks_CB.Location = new System.Drawing.Point(6, 6); + this.RiceUseDefaultHacks_CB.Name = "RiceUseDefaultHacks_CB"; + this.RiceUseDefaultHacks_CB.Size = new System.Drawing.Size(165, 17); + this.RiceUseDefaultHacks_CB.TabIndex = 1; + this.RiceUseDefaultHacks_CB.Text = "Use defaults for current game"; + this.RiceUseDefaultHacks_CB.UseVisualStyleBackColor = true; + this.RiceUseDefaultHacks_CB.CheckedChanged += new System.EventHandler(this.RiceUseDefaultHacks_Cb_CheckedChanged); + // + // label71 + // + this.label71.AutoSize = true; + this.label71.Location = new System.Drawing.Point(15, 94); + this.label71.Name = "label71"; + this.label71.Size = new System.Drawing.Size(91, 13); + this.label71.TabIndex = 32; + this.label71.Text = "Resolution Height"; + // + // label70 + // + this.label70.AutoSize = true; + this.label70.Location = new System.Drawing.Point(15, 64); + this.label70.Name = "label70"; + this.label70.Size = new System.Drawing.Size(88, 13); + this.label70.TabIndex = 31; + this.label70.Text = "Resolution Width"; + // + // label69 + // + this.label69.AutoSize = true; + this.label69.Location = new System.Drawing.Point(15, 34); + this.label69.Name = "label69"; + this.label69.Size = new System.Drawing.Size(106, 13); + this.label69.TabIndex = 0; + this.label69.Text = "Direct3D Clear Mode"; + // + // label68 + // + this.label68.AutoSize = true; + this.label68.Location = new System.Drawing.Point(51, 316); + this.label68.Name = "label68"; + this.label68.Size = new System.Drawing.Size(87, 13); + this.label68.TabIndex = 27; + this.label68.Text = "Copy framebuffer"; + // + // label67 + // + this.label67.AutoSize = true; + this.label67.Location = new System.Drawing.Point(49, 286); + this.label67.Name = "label67"; + this.label67.Size = new System.Drawing.Size(89, 13); + this.label67.TabIndex = 25; + this.label67.Text = "Force Z Compare"; + // + // label66 + // + this.label66.AutoSize = true; + this.label66.Location = new System.Drawing.Point(24, 256); + this.label66.Name = "label66"; + this.label66.Size = new System.Drawing.Size(114, 13); + this.label66.TabIndex = 23; + this.label66.Text = "Direct3D trans pipeline"; + // + // label65 + // + this.label65.AutoSize = true; + this.label65.Location = new System.Drawing.Point(32, 226); + this.label65.Name = "label65"; + this.label65.Size = new System.Drawing.Size(106, 13); + this.label65.TabIndex = 21; + this.label65.Text = "Force alpha blending"; + // + // label64 + // + this.label64.AutoSize = true; + this.label64.Location = new System.Drawing.Point(15, 195); + this.label64.Name = "label64"; + this.label64.Size = new System.Drawing.Size(123, 13); + this.label64.TabIndex = 19; + this.label64.Text = "Use legacy pixel pipeline"; + // + // label63 + // + this.label63.AutoSize = true; + this.label63.Location = new System.Drawing.Point(15, 177); + this.label63.Name = "label63"; + this.label63.Size = new System.Drawing.Size(76, 13); + this.label63.TabIndex = 17; + this.label63.Text = "to match yours"; + // + // label62 + // + this.label62.AutoSize = true; + this.label62.Location = new System.Drawing.Point(15, 164); + this.label62.Name = "label62"; + this.label62.Size = new System.Drawing.Size(123, 13); + this.label62.TabIndex = 16; + this.label62.Text = "Adjust game aspect ratio"; + // + // label61 + // + this.label61.AutoSize = true; + this.label61.Location = new System.Drawing.Point(49, 49); + this.label61.Name = "label61"; + this.label61.Size = new System.Drawing.Size(89, 13); + this.label61.TabIndex = 14; + this.label61.Text = "Antialiasing Level"; + // + // label60 + // + this.label60.AutoSize = true; + this.label60.Location = new System.Drawing.Point(21, 136); + this.label60.Name = "label60"; + this.label60.Size = new System.Drawing.Size(117, 13); + this.label60.TabIndex = 12; + this.label60.Text = "Always use texture filter"; + // + // label59 + // + this.label59.AutoSize = true; + this.label59.Location = new System.Drawing.Point(76, 107); + this.label59.Name = "label59"; + this.label59.Size = new System.Drawing.Size(62, 13); + this.label59.TabIndex = 10; + this.label59.Text = "Super 2xsal"; + // + // label57 + // + this.label57.AutoSize = true; + this.label57.Location = new System.Drawing.Point(82, 79); + this.label57.Name = "label57"; + this.label57.Size = new System.Drawing.Size(56, 13); + this.label57.TabIndex = 7; + this.label57.Text = "Brightness"; + // + // label51 + // + this.label51.AutoSize = true; + this.label51.Location = new System.Drawing.Point(18, 15); + this.label51.Name = "label51"; + this.label51.Size = new System.Drawing.Size(120, 13); + this.label51.TabIndex = 5; + this.label51.Text = "Anisotropic filtering level"; + // + // SaveButton + // + this.SaveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.SaveButton.Location = new System.Drawing.Point(409, 373); + this.SaveButton.Name = "SaveButton"; + this.SaveButton.Size = new System.Drawing.Size(75, 23); + this.SaveButton.TabIndex = 100; + this.SaveButton.Text = "Save"; + this.SaveButton.UseVisualStyleBackColor = true; + this.SaveButton.Click += new System.EventHandler(this.Button1_Click); + // + // CancelBT + // + this.CancelBT.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBT.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBT.Location = new System.Drawing.Point(490, 373); + this.CancelBT.Name = "CancelBT"; + this.CancelBT.Size = new System.Drawing.Size(75, 23); + this.CancelBT.TabIndex = 105; + this.CancelBT.Text = "Cancel"; + this.CancelBT.UseVisualStyleBackColor = true; + this.CancelBT.Click += new System.EventHandler(this.CancelBtn_Click); + // + // toolTip1 + // + this.toolTip1.AutoPopDelay = 10000; + this.toolTip1.InitialDelay = 500; + this.toolTip1.IsBalloon = true; + this.toolTip1.ReshowDelay = 100; + this.toolTip1.ToolTipIcon = System.Windows.Forms.ToolTipIcon.Info; + // + // N64VideoPluginconfig + // + this.AcceptButton = this.SaveButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBT; + this.ClientSize = new System.Drawing.Size(577, 401); + this.Controls.Add(this.CancelBT); + this.Controls.Add(this.SaveButton); + this.Controls.Add(this.N64plugintabcontrol); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "N64VideoPluginconfig"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Plugin Settings"; + this.Load += new System.EventHandler(this.N64VideoPluginconfig_Load); + this.N64plugintabcontrol.ResumeLayout(false); + this.N64vpluginglobaltab.ResumeLayout(false); + this.N64vpluginglobaltab.PerformLayout(); + this.GLideN64Tab.ResumeLayout(false); + this.tabControl3.ResumeLayout(false); + this.tabPage5.ResumeLayout(false); + this.tabPage5.PerformLayout(); + this.tabPage6.ResumeLayout(false); + this.tabPage6.PerformLayout(); + this.tabPage7.ResumeLayout(false); + this.tabPage7.PerformLayout(); + this.Glide64mk2TabPage.ResumeLayout(false); + this.tabControl2.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage1.PerformLayout(); + this.tabPage2.ResumeLayout(false); + this.tabPage2.PerformLayout(); + this.tabPage3.ResumeLayout(false); + this.tabPage3.PerformLayout(); + this.Glide64TabPage.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.Glide64General.ResumeLayout(false); + this.Glide64General.PerformLayout(); + this.GlidePerGameHacks1.ResumeLayout(false); + this.GlidePerGameHacks1.PerformLayout(); + this.GlidePerGameHacks2.ResumeLayout(false); + this.GlidePerGameHacks2.PerformLayout(); + this.RiceTabPage.ResumeLayout(false); + this.RiceTabControl.ResumeLayout(false); + this.RiceGeneral.ResumeLayout(false); + this.RiceGeneral.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.RiceAnisotropicFiltering_TB)).EndInit(); + this.RiceGameDefaultTab.ResumeLayout(false); + this.RiceGameDefaultTab.PerformLayout(); + this.RiceFrameBuffer_GroupBox.ResumeLayout(false); + this.RiceFrameBuffer_GroupBox.PerformLayout(); + this.RiceTextureEnhancementTab.ResumeLayout(false); + this.RiceTextureEnhancementTab.PerformLayout(); + this.RiceGameSpecificTab.ResumeLayout(false); + this.RiceGameSpecificTab.PerformLayout(); + this.ResumeLayout(false); } diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.Designer.cs new file mode 100644 index 0000000000..21b2122306 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.Designer.cs @@ -0,0 +1,210 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumAudioSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumAudioSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.panTypecomboBox1 = new System.Windows.Forms.ComboBox(); + this.lblBorderInfo = new System.Windows.Forms.Label(); + this.tapeVolumetrackBar = new System.Windows.Forms.TrackBar(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.earVolumetrackBar = new System.Windows.Forms.TrackBar(); + this.label5 = new System.Windows.Forms.Label(); + this.ayVolumetrackBar = new System.Windows.Forms.TrackBar(); + ((System.ComponentModel.ISupportInitialize)(this.tapeVolumetrackBar)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.earVolumetrackBar)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.ayVolumetrackBar)).BeginInit(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(247, 298); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(313, 298); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(140, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Audio Settings"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 236); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(135, 13); + this.label2.TabIndex = 23; + this.label2.Text = "AY-3-8912 Panning Config:"; + // + // panTypecomboBox1 + // + this.panTypecomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.panTypecomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.panTypecomboBox1.FormattingEnabled = true; + this.panTypecomboBox1.Location = new System.Drawing.Point(12, 252); + this.panTypecomboBox1.Name = "panTypecomboBox1"; + this.panTypecomboBox1.Size = new System.Drawing.Size(157, 21); + this.panTypecomboBox1.TabIndex = 22; + // + // lblBorderInfo + // + this.lblBorderInfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblBorderInfo.Location = new System.Drawing.Point(175, 245); + this.lblBorderInfo.Name = "lblBorderInfo"; + this.lblBorderInfo.Size = new System.Drawing.Size(196, 37); + this.lblBorderInfo.TabIndex = 24; + this.lblBorderInfo.Text = "Selects a particular panning configuration for the 3ch AY-3-8912 Programmable Sou" + + "nd Generator (128K models only)"; + this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // tapeVolumetrackBar + // + this.tapeVolumetrackBar.Location = new System.Drawing.Point(12, 60); + this.tapeVolumetrackBar.Maximum = 100; + this.tapeVolumetrackBar.Name = "tapeVolumetrackBar"; + this.tapeVolumetrackBar.Size = new System.Drawing.Size(359, 45); + this.tapeVolumetrackBar.TabIndex = 25; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(12, 44); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(73, 13); + this.label3.TabIndex = 26; + this.label3.Text = "Tape Volume:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 108); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(80, 13); + this.label4.TabIndex = 28; + this.label4.Text = "Buzzer Volume:"; + // + // earVolumetrackBar + // + this.earVolumetrackBar.Location = new System.Drawing.Point(12, 124); + this.earVolumetrackBar.Maximum = 100; + this.earVolumetrackBar.Name = "earVolumetrackBar"; + this.earVolumetrackBar.Size = new System.Drawing.Size(359, 45); + this.earVolumetrackBar.TabIndex = 27; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(12, 172); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(98, 13); + this.label5.TabIndex = 30; + this.label5.Text = "AY-3-8912 Volume:"; + // + // ayVolumetrackBar + // + this.ayVolumetrackBar.Location = new System.Drawing.Point(12, 188); + this.ayVolumetrackBar.Maximum = 100; + this.ayVolumetrackBar.Name = "ayVolumetrackBar"; + this.ayVolumetrackBar.Size = new System.Drawing.Size(359, 45); + this.ayVolumetrackBar.TabIndex = 29; + // + // ZXSpectrumAudioSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(385, 333); + this.Controls.Add(this.label5); + this.Controls.Add(this.ayVolumetrackBar); + this.Controls.Add(this.label4); + this.Controls.Add(this.earVolumetrackBar); + this.Controls.Add(this.label3); + this.Controls.Add(this.tapeVolumetrackBar); + this.Controls.Add(this.lblBorderInfo); + this.Controls.Add(this.label2); + this.Controls.Add(this.panTypecomboBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumAudioSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Audio Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + ((System.ComponentModel.ISupportInitialize)(this.tapeVolumetrackBar)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.earVolumetrackBar)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.ayVolumetrackBar)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox panTypecomboBox1; + private System.Windows.Forms.Label lblBorderInfo; + private System.Windows.Forms.TrackBar tapeVolumetrackBar; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.TrackBar earVolumetrackBar; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.TrackBar ayVolumetrackBar; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs new file mode 100644 index 0000000000..0eb6e977cc --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.cs @@ -0,0 +1,79 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumAudioSettings : Form + { + private ZXSpectrum.ZXSpectrumSettings _settings; + + public ZXSpectrumAudioSettings() + { + InitializeComponent(); + } + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _settings = ((ZXSpectrum)Global.Emulator).GetSettings().Clone(); + + // AY panning config + var panTypes = Enum.GetNames(typeof(AY38912.AYPanConfig)); + foreach (var val in panTypes) + { + panTypecomboBox1.Items.Add(val); + } + panTypecomboBox1.SelectedItem = _settings.AYPanConfig.ToString(); + + // tape volume + tapeVolumetrackBar.Value = _settings.TapeVolume; + + // ear volume + earVolumetrackBar.Value = _settings.EarVolume; + + // ay volume + ayVolumetrackBar.Value = _settings.AYVolume; + + + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _settings.AYPanConfig.ToString() != panTypecomboBox1.SelectedItem.ToString() + || _settings.TapeVolume != tapeVolumetrackBar.Value + || _settings.EarVolume != earVolumetrackBar.Value + || _settings.AYVolume != ayVolumetrackBar.Value; + + if (changed) + { + _settings.AYPanConfig = (AY38912.AYPanConfig)Enum.Parse(typeof(AY38912.AYPanConfig), panTypecomboBox1.SelectedItem.ToString()); + + _settings.TapeVolume = tapeVolumetrackBar.Value; + _settings.EarVolume = earVolumetrackBar.Value; + _settings.AYVolume = ayVolumetrackBar.Value; + + GlobalWin.MainForm.PutCoreSettings(_settings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Misc settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumAudioSettings.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAoIJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACYXODs4BCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + KTNDQ0M7OAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALllbYmJZQBcAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYYWNwcHBwWy8mAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFLanBwcHBwYz0eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABpqcHBwcHBwZVkUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl11w + cHBwcHBwcGcSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIXdwcHBwcHBwcGkSAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXBwcHBwcHBwd2wYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAACXbnBwdXB5dXl0eW4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAid3R5eXl5eXl5q6wzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9eXV5 + i7CxsbGxsblLKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABndYuwsbm8uby5vMFnHgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJt3q7G3vMHB1cLBwdWuEgAAAAAAAAAAAAAAAAAA + AAAAAAAeEhMSCiUAAAAAAAAAAEexsbm/1dXZ2dnZ1da5ZgwAAAAAAAAAAAAAAAAAAAAjEjNZaW5qXRMl + AAAAAAAAADW5s7/V2N7i4uLi3dzZrQQPAAAAAAAAAAAAAAAAHxhZbm5uaWltd6ASAAAAAAAAAEmzvMLZ + 3uP29/fw4uTkuUAWCy0AAAAAAAAAAB4YYXd3gG13vbm5vb8zAAAAAAAAAE6xwdXd4/b6+/r38OTl1Vlc + OAMIFAweFBQSM2mtrYB3vdXT0NXExNU1AAAAAAAAAE65wtXe8Pr7/Pz79+fn1WphZ25pXV1mbHetrXd3 + tdXT4vXw49nZ3NYgAAAAAAAAAEu3wdje9vv7/Pz79+fn34B3d2xtoHeud66uudXT4vD39/Dj49zk5G0A + AAAAAAAAAD2xwcwoH0/L/Pukyenp5K27u7m5uczM0Nve4vb3+vr56OPl5eXl1igAAAAAAAAAADWxwQgB + BQYNmveZK/Dp6cG/wcTV2eP3+vr6+/r6+ejm5ufn5+nkIgAAAAAAAAAAAJmruR4sjC2WLFCdDd3p6dXW + 1tXI3vn67pCO9Ojp6efo5+fm59wiAAAAAAAAAAAAAABLsZ0FmC0qKgHMRcjp6dzc1Y2KiO3RlfKTj+np + 5ubm5eXk1SIAAAAAAAAAAAAAAACdab/Lp5aWnEfV1cHm6ebk6pGSiabZ8fOU0uXl5eTk3NyuRQAAAAAA + AAAAAAAAAAAAn0ux0KFTaMHBv7nC6efp3Ovv7OTm3OPl3Nzc3NfW1U6fAAAAAAAAAAAAAAAAAAAAAABF + Wa25t7yxs7Gw5+fn5Obk18XG3NyBfHvD1cSgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAFUzarGwsHl5sefn + 39zEgoZ/hL19fnqirj2jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj09ZXV0cLzn3NXChYeDub+1pbQ9 + VQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0rXj+rpInTBDcHCz5NW/ucG5u7GAM1QAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXjU1h6NnZv/Fc3T/x3Bx/8dw + cf/HcHH/x3Bx/8dwcf/HcHH/w3Jz/6ZoZ/9ZMzPTAQAAPQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyFxccektK0b12 + dv/HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xXR0/69wb/9jOjneBwMDSQAAAAUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABNKSlNlmBf9sh3d//HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xnd3/7Z4d/9sQUDnDgcHVQAA + AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABkOjqKsXFw/8lyc//HcXL/yHJz/8l0df/JdXb/yXV2/8l1dv/JdHX/ynt7/7+B + f/94SknvFgsLZQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACILCxB7TUzDwXd3/8lyc//KdXb/y3h5/8x7fP/NfX7/zX5+/819 + fv/NfH3/zoOC/8iJiP+GVVX3Hg8QegAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMiIi+SXl3oynp7/8t4ef/NfX7/z4GC/9GE + hf/Sh4j/04iJ/9KIiP/Rhof/04uK/8+RkP+XY2L9KxcXlwAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABwAA + AA0AAAAPAAAACwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUvL1enbW37zn5+/85/ + gP/Rhob/1IuM/9aPkP/XkpP/2JOU/9iTlP/XkZH/15OT/9eZl/+rdHP/QSUlvAAAADwAAAAFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAACQAA + ABgAAAAvAgEBSwcDA2EFAgJoAAAAWAAAADYAAAARAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU8 + O4W5eXn/0IKD/9KIif/Wj5D/2ZWW/9ubm//dnp//3qCg/92foP/cnZ3/3Jyc/9+in//CiYf/Zj8/4wYC + AnAAAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + AA4AAAAnCQQEUCISEoQ+IiKzVzEx1mU6OuZiOTnmRigo0hgNDZsAAABMAAAAEAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABnVJSK/HhIP/04eI/9aQkf/amJn/3qCh/+Gmp//jq6v/5Kyt/+OsrP/iqan/4aal/+ap + p//Umpj/nmxr/C8ZGboAAABXAAAAGAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAIAAAAOAQAALRkNDWY+IiKpZDo63YZRUfigZGP/sHBv/7V0c/+xcnH/oWZm/2k+PvEfEBCcAAAAMQAA + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + cP/Gb3D/oF9e/hMKCmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4QECynZmX7xnBx/sdwcf/HcHH/tG1t/h8REYMAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAx + MIzFc3T+xm9w/sdwcf7HcHH+vHR0/jAcHJkAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ4OAYVSUtfIcnP/yXZ3/st5ef/LeHn/xoB//kQq + KrEAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJxYWGrNvb/7Nfn//0oeI/tSNjf/UjI3/1ZOS/mE+PtQAAAAXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAIAAAARAAAALQAAADUAAAARAAAAAAAAAAAAAAAAQyYmUM6Ghv/Wj5D/3J2e/uCl + pf/fpKT/4KOi/qRycPkHBARlAAAABQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAJh8REYBYNTXMhVJR8XxM + TO8gEhKeAAAAEAAAAAAAAAAAbUVEe9aPkP7doKD+5rKz/uu9vv7rvLz+6rKx/tqfnf5iNzfnCAQEcwAA + ACoAAAAbAAAAIQIBATorGBiQhFNT67Z3dv68fn3+wYSD/siKiP6aZmX2AQAAKQAAAAAAAAAAd05Ni9eT + lP/jq6z/7cLC/vXS0v/zz9D/8b69/uyxrv+samr/l15d+2tDQ+NkPz7bdkxL451nZve+gYD/yY2M/tWg + n//jtrT/46+t/uOmpP+mdHPwBQMDFAAAAAAAAAAAdkpJh9iUlf7Hl5f+tJeX/uzOzv7lyMj+57y6/vS6 + t/7HhoX+xYaE/saJh/7MkpD+0ZmY/tejov7mt7X+7cXD/vDFxP7vvLr+8Le0/u2zsf5PMzOMDQcHAQAA + AAAAAAAAYTg4X9OOj/9aUlL/YGJi/nh2dv+skJD/qo2M/vnAvf/dn53/4KKg/+Cnp/7vxsT/u8PM/sHI + 0P/1xsT/9sG+/ve+u//3vrv/87q3/ntVVLkkFhYIAAAAAAAAAAAAAAAAVC8wD6BkZOWjhIT/jo6O/n1+ + fv+eenv/xpGR/vi/vP/wtbL/mZPP/0Z2+v69nrr/gd/x/nfD2v/2vLr/9Lq3/vG2tP/lq6j/elJRrjQg + IAoAAAAAAAAAAAAAAAAAAAAAAAAAAGc7OyeOWVnGv4eH/r2Fhf7YlZb+1Y6P/uinpv74v7z+3ay3/seo + w/7srZ/+7LGv/qmyjv63qI7+5Kel/r2GhPZ1S0p1QCcmAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAd0pKOpReXtKxb3D/yXl6/sx5ev/ws7D/6q6s/+Ked/7npFb/2ZiP/ny7gP+OjW/9h1dWr2I7 + OiMAAAAAAAAAAAAAAAAAAAAAAAAAALSCggSqcXIbo2dnN61xcVS/h4eIzp2c2cKWle2OY2OGbz4+Y4xN + Tr6zaWn84Jyb/9aXlv7Ji4r/p25t9INTUqZlPDw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJg + YASjcnMorH9/a6h7e4yabm6Df1NTU3VKSgwAAAAAAAAAAAAAAABgNDQgcj8/bntHR4ZnPDxTVTExDQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////APx//wD4P/8A8D//AOA//wDgH/8A4B//AMAf + /wDAH8EAwA8AAMAAAADAAAAAwAAAAMAAAQDAAAMA4AAHAPgAHwAAAH8AAcH/AP///wD///8A////ACgA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQc + HA5LKSlUNBwcSAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsO + DgV/SkqHm1hY+X5HR90tGRkuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB4SEhCr2Zm7sZwcf+oYWL5UC8vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAACnl9fnMRwcf/IcXL/tmxs/mI8PGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa0NCGbRsbdbMenv/zn5//8R9ff9ySkmCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA + AAkAAAAAAAAAAItYWDvFfn/y2ZWW/92fn//anJv/jWFgvwAAAB0AAAAAAAAAAAAAAAIzHBwiYjs7a3pM + S6pqQkKjLBoaMwAAAACeZ2dZ05KS/em0tP/vxMT/77u6/8CHhfpmPDyvRysqYlExMV1ySEiGnWdn07qB + gPzLkI//w4iG/HJLS3YAAAAAomloXsyRkf/DoKD/48bG/+jAv//hpKL/vX17/7h/fPu/iYj7z5qZ/+Gw + rv/rvLr/77q3/9ScmuR9U1I+AAAAAJZbWz2ndnbxdG9v/4yCgv+4lJP/77Wy/86erP+6nsH/tsXR/8PH + 0P/4wsD/9b26/+Cppu2peXdiAAAAAQAAAABYKCgHn2lqe6eCguSsgoL90pKS//Cxrv/TrcP/s5y+/8i3 + s/+quab/26mh/82UktSgbm1TBAAAAwAAAACud3cEvYGBC7N6ehyyfHtyt39+3bNub9vLgYH05qak/+Kg + g//OlH39jZR04Zd0aYmDT1EiAAAAAAAAAAAAAAAAr3t7D7aCgki5h4Z8uImJgah+fUltPz8ajU1ORq1s + bI6vdHOgm2RkaYxJUiZgCygCAAAAAAAAAAAAAAAAAAAAAGo9PQF9UVEHcEdHCTodHQIAAAAAAAAAAAAA + AAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AADh/wAAwf8AAMH/ + AACB/wAAgfkAAIDAAACAAAAAgAAAAIAAAACAAQAAAAcAAAAPAAAOfwAA//8AAA== + + + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs new file mode 100644 index 0000000000..b7f58ec911 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.Designer.cs @@ -0,0 +1,214 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumCoreEmulationSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumCoreEmulationSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.MachineSelectionComboBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.lblMachineNotes = new System.Windows.Forms.Label(); + this.determEmucheckBox1 = new System.Windows.Forms.CheckBox(); + this.label2 = new System.Windows.Forms.Label(); + this.borderTypecomboBox1 = new System.Windows.Forms.ComboBox(); + this.lblBorderInfo = new System.Windows.Forms.Label(); + this.lblAutoLoadText = new System.Windows.Forms.Label(); + this.autoLoadcheckBox1 = new System.Windows.Forms.CheckBox(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(247, 434); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(313, 434); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 46); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(98, 13); + this.label4.TabIndex = 15; + this.label4.Text = "Emulated Machine:"; + // + // MachineSelectionComboBox + // + this.MachineSelectionComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.MachineSelectionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.MachineSelectionComboBox.FormattingEnabled = true; + this.MachineSelectionComboBox.Location = new System.Drawing.Point(12, 62); + this.MachineSelectionComboBox.Name = "MachineSelectionComboBox"; + this.MachineSelectionComboBox.Size = new System.Drawing.Size(361, 21); + this.MachineSelectionComboBox.TabIndex = 13; + this.MachineSelectionComboBox.SelectionChangeCommitted += new System.EventHandler(this.MachineSelectionComboBox_SelectionChangeCommitted); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(159, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Emulation Settings"; + // + // lblMachineNotes + // + this.lblMachineNotes.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblMachineNotes.Location = new System.Drawing.Point(15, 95); + this.lblMachineNotes.Name = "lblMachineNotes"; + this.lblMachineNotes.Size = new System.Drawing.Size(358, 204); + this.lblMachineNotes.TabIndex = 20; + this.lblMachineNotes.Text = "null\r\n"; + // + // determEmucheckBox1 + // + this.determEmucheckBox1.AutoSize = true; + this.determEmucheckBox1.Location = new System.Drawing.Point(15, 302); + this.determEmucheckBox1.Name = "determEmucheckBox1"; + this.determEmucheckBox1.Size = new System.Drawing.Size(135, 17); + this.determEmucheckBox1.TabIndex = 21; + this.determEmucheckBox1.Text = "Deterministic Emulation"; + this.determEmucheckBox1.UseVisualStyleBackColor = true; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 335); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(118, 13); + this.label2.TabIndex = 23; + this.label2.Text = "Rendered Border Type:"; + // + // borderTypecomboBox1 + // + this.borderTypecomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.borderTypecomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.borderTypecomboBox1.FormattingEnabled = true; + this.borderTypecomboBox1.Location = new System.Drawing.Point(12, 351); + this.borderTypecomboBox1.Name = "borderTypecomboBox1"; + this.borderTypecomboBox1.Size = new System.Drawing.Size(157, 21); + this.borderTypecomboBox1.TabIndex = 22; + this.borderTypecomboBox1.SelectedIndexChanged += new System.EventHandler(this.borderTypecomboBox1_SelectedIndexChanged); + // + // lblBorderInfo + // + this.lblBorderInfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblBorderInfo.Location = new System.Drawing.Point(175, 351); + this.lblBorderInfo.Name = "lblBorderInfo"; + this.lblBorderInfo.Size = new System.Drawing.Size(196, 21); + this.lblBorderInfo.TabIndex = 24; + this.lblBorderInfo.Text = "null"; + this.lblBorderInfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // lblAutoLoadText + // + this.lblAutoLoadText.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblAutoLoadText.Location = new System.Drawing.Point(175, 388); + this.lblAutoLoadText.Name = "lblAutoLoadText"; + this.lblAutoLoadText.Size = new System.Drawing.Size(196, 30); + this.lblAutoLoadText.TabIndex = 27; + this.lblAutoLoadText.Text = "When enabled ZXHawk will attempt to control the tape device automatically when th" + + "e correct traps are detected"; + this.lblAutoLoadText.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // autoLoadcheckBox1 + // + this.autoLoadcheckBox1.AutoSize = true; + this.autoLoadcheckBox1.Location = new System.Drawing.Point(15, 394); + this.autoLoadcheckBox1.Name = "autoLoadcheckBox1"; + this.autoLoadcheckBox1.Size = new System.Drawing.Size(103, 17); + this.autoLoadcheckBox1.TabIndex = 26; + this.autoLoadcheckBox1.Text = "Auto-Load Tape"; + this.autoLoadcheckBox1.UseVisualStyleBackColor = true; + // + // ZXSpectrumCoreEmulationSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(385, 469); + this.Controls.Add(this.lblAutoLoadText); + this.Controls.Add(this.autoLoadcheckBox1); + this.Controls.Add(this.lblBorderInfo); + this.Controls.Add(this.label2); + this.Controls.Add(this.borderTypecomboBox1); + this.Controls.Add(this.determEmucheckBox1); + this.Controls.Add(this.lblMachineNotes); + this.Controls.Add(this.label1); + this.Controls.Add(this.label4); + this.Controls.Add(this.MachineSelectionComboBox); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumCoreEmulationSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Core Emulation Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox MachineSelectionComboBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label lblMachineNotes; + private System.Windows.Forms.CheckBox determEmucheckBox1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ComboBox borderTypecomboBox1; + private System.Windows.Forms.Label lblBorderInfo; + private System.Windows.Forms.Label lblAutoLoadText; + private System.Windows.Forms.CheckBox autoLoadcheckBox1; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs new file mode 100644 index 0000000000..1f6929cac3 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.cs @@ -0,0 +1,122 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumCoreEmulationSettings : Form + { + private ZXSpectrum.ZXSpectrumSyncSettings _syncSettings; + + public ZXSpectrumCoreEmulationSettings() + { + InitializeComponent(); + } + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _syncSettings = ((ZXSpectrum)Global.Emulator).GetSyncSettings().Clone(); + + // machine selection + var machineTypes = Enum.GetNames(typeof(MachineType)); + foreach (var val in machineTypes) + { + MachineSelectionComboBox.Items.Add(val); + } + MachineSelectionComboBox.SelectedItem = _syncSettings.MachineType.ToString(); + UpdateMachineNotes((MachineType)Enum.Parse(typeof(MachineType), MachineSelectionComboBox.SelectedItem.ToString())); + + // border selecton + var borderTypes = Enum.GetNames(typeof(ZXSpectrum.BorderType)); + foreach (var val in borderTypes) + { + borderTypecomboBox1.Items.Add(val); + } + borderTypecomboBox1.SelectedItem = _syncSettings.BorderType.ToString(); + UpdateBorderNotes((ZXSpectrum.BorderType)Enum.Parse(typeof(ZXSpectrum.BorderType), borderTypecomboBox1.SelectedItem.ToString())); + + // deterministic emulation + determEmucheckBox1.Checked = _syncSettings.DeterministicEmulation; + + // autoload tape + autoLoadcheckBox1.Checked = _syncSettings.AutoLoadTape; + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _syncSettings.MachineType.ToString() != MachineSelectionComboBox.SelectedItem.ToString() + || _syncSettings.BorderType.ToString() != borderTypecomboBox1.SelectedItem.ToString() + || _syncSettings.DeterministicEmulation != determEmucheckBox1.Checked + || _syncSettings.AutoLoadTape != autoLoadcheckBox1.Checked; + + if (changed) + { + _syncSettings.MachineType = (MachineType)Enum.Parse(typeof(MachineType), MachineSelectionComboBox.SelectedItem.ToString()); + _syncSettings.BorderType = (ZXSpectrum.BorderType)Enum.Parse(typeof(ZXSpectrum.BorderType), borderTypecomboBox1.SelectedItem.ToString()); + _syncSettings.DeterministicEmulation = determEmucheckBox1.Checked; + _syncSettings.AutoLoadTape = autoLoadcheckBox1.Checked; + + GlobalWin.MainForm.PutCoreSyncSettings(_syncSettings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Core emulator settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + + private void MachineSelectionComboBox_SelectionChangeCommitted(object sender, EventArgs e) + { + ComboBox cb = sender as ComboBox; + UpdateMachineNotes((MachineType)Enum.Parse(typeof(MachineType), cb.SelectedItem.ToString())); + } + + private void UpdateMachineNotes(MachineType type) + { + lblMachineNotes.Text = ZXMachineMetaData.GetMetaString(type); + } + + private void borderTypecomboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + ComboBox cb = sender as ComboBox; + UpdateBorderNotes((ZXSpectrum.BorderType)Enum.Parse(typeof(ZXSpectrum.BorderType), cb.SelectedItem.ToString())); + } + + private void UpdateBorderNotes(ZXSpectrum.BorderType type) + { + switch (type) + { + case ZXSpectrum.BorderType.Full: + lblBorderInfo.Text = "Original border sizes"; + break; + case ZXSpectrum.BorderType.Medium: + lblBorderInfo.Text = "All borders 24px"; + break; + case ZXSpectrum.BorderType.None: + lblBorderInfo.Text = "No border at all"; + break; + case ZXSpectrum.BorderType.Small: + lblBorderInfo.Text = "All borders 10px"; + break; + case ZXSpectrum.BorderType.Widescreen: + lblBorderInfo.Text = "No top and bottom border (almost 16:9)"; + break; + } + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumCoreEmulationSettings.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAoIJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACYXODs4BCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + KTNDQ0M7OAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALllbYmJZQBcAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYYWNwcHBwWy8mAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFLanBwcHBwYz0eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABpqcHBwcHBwZVkUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl11w + cHBwcHBwcGcSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIXdwcHBwcHBwcGkSAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXBwcHBwcHBwd2wYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAACXbnBwdXB5dXl0eW4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAid3R5eXl5eXl5q6wzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9eXV5 + i7CxsbGxsblLKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABndYuwsbm8uby5vMFnHgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJt3q7G3vMHB1cLBwdWuEgAAAAAAAAAAAAAAAAAA + AAAAAAAeEhMSCiUAAAAAAAAAAEexsbm/1dXZ2dnZ1da5ZgwAAAAAAAAAAAAAAAAAAAAjEjNZaW5qXRMl + AAAAAAAAADW5s7/V2N7i4uLi3dzZrQQPAAAAAAAAAAAAAAAAHxhZbm5uaWltd6ASAAAAAAAAAEmzvMLZ + 3uP29/fw4uTkuUAWCy0AAAAAAAAAAB4YYXd3gG13vbm5vb8zAAAAAAAAAE6xwdXd4/b6+/r38OTl1Vlc + OAMIFAweFBQSM2mtrYB3vdXT0NXExNU1AAAAAAAAAE65wtXe8Pr7/Pz79+fn1WphZ25pXV1mbHetrXd3 + tdXT4vXw49nZ3NYgAAAAAAAAAEu3wdje9vv7/Pz79+fn34B3d2xtoHeud66uudXT4vD39/Dj49zk5G0A + AAAAAAAAAD2xwcwoH0/L/Pukyenp5K27u7m5uczM0Nve4vb3+vr56OPl5eXl1igAAAAAAAAAADWxwQgB + BQYNmveZK/Dp6cG/wcTV2eP3+vr6+/r6+ejm5ufn5+nkIgAAAAAAAAAAAJmruR4sjC2WLFCdDd3p6dXW + 1tXI3vn67pCO9Ojp6efo5+fm59wiAAAAAAAAAAAAAABLsZ0FmC0qKgHMRcjp6dzc1Y2KiO3RlfKTj+np + 5ubm5eXk1SIAAAAAAAAAAAAAAACdab/Lp5aWnEfV1cHm6ebk6pGSiabZ8fOU0uXl5eTk3NyuRQAAAAAA + AAAAAAAAAAAAn0ux0KFTaMHBv7nC6efp3Ovv7OTm3OPl3Nzc3NfW1U6fAAAAAAAAAAAAAAAAAAAAAABF + Wa25t7yxs7Gw5+fn5Obk18XG3NyBfHvD1cSgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAFUzarGwsHl5sefn + 39zEgoZ/hL19fnqirj2jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj09ZXV0cLzn3NXChYeDub+1pbQ9 + VQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0rXj+rpInTBDcHCz5NW/ucG5u7GAM1QAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXjU1h6NnZv/Fc3T/x3Bx/8dw + cf/HcHH/x3Bx/8dwcf/HcHH/w3Jz/6ZoZ/9ZMzPTAQAAPQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyFxccektK0b12 + dv/HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xXR0/69wb/9jOjneBwMDSQAAAAUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABNKSlNlmBf9sh3d//HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xnd3/7Z4d/9sQUDnDgcHVQAA + AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABkOjqKsXFw/8lyc//HcXL/yHJz/8l0df/JdXb/yXV2/8l1dv/JdHX/ynt7/7+B + f/94SknvFgsLZQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACILCxB7TUzDwXd3/8lyc//KdXb/y3h5/8x7fP/NfX7/zX5+/819 + fv/NfH3/zoOC/8iJiP+GVVX3Hg8QegAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMiIi+SXl3oynp7/8t4ef/NfX7/z4GC/9GE + hf/Sh4j/04iJ/9KIiP/Rhof/04uK/8+RkP+XY2L9KxcXlwAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABwAA + AA0AAAAPAAAACwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUvL1enbW37zn5+/85/ + gP/Rhob/1IuM/9aPkP/XkpP/2JOU/9iTlP/XkZH/15OT/9eZl/+rdHP/QSUlvAAAADwAAAAFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAACQAA + ABgAAAAvAgEBSwcDA2EFAgJoAAAAWAAAADYAAAARAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU8 + O4W5eXn/0IKD/9KIif/Wj5D/2ZWW/9ubm//dnp//3qCg/92foP/cnZ3/3Jyc/9+in//CiYf/Zj8/4wYC + AnAAAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + AA4AAAAnCQQEUCISEoQ+IiKzVzEx1mU6OuZiOTnmRigo0hgNDZsAAABMAAAAEAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABnVJSK/HhIP/04eI/9aQkf/amJn/3qCh/+Gmp//jq6v/5Kyt/+OsrP/iqan/4aal/+ap + p//Umpj/nmxr/C8ZGboAAABXAAAAGAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAIAAAAOAQAALRkNDWY+IiKpZDo63YZRUfigZGP/sHBv/7V0c/+xcnH/oWZm/2k+PvEfEBCcAAAAMQAA + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + cP/Gb3D/oF9e/hMKCmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4QECynZmX7xnBx/sdwcf/HcHH/tG1t/h8REYMAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAx + MIzFc3T+xm9w/sdwcf7HcHH+vHR0/jAcHJkAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ4OAYVSUtfIcnP/yXZ3/st5ef/LeHn/xoB//kQq + KrEAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJxYWGrNvb/7Nfn//0oeI/tSNjf/UjI3/1ZOS/mE+PtQAAAAXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAIAAAARAAAALQAAADUAAAARAAAAAAAAAAAAAAAAQyYmUM6Ghv/Wj5D/3J2e/uCl + pf/fpKT/4KOi/qRycPkHBARlAAAABQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAJh8REYBYNTXMhVJR8XxM + TO8gEhKeAAAAEAAAAAAAAAAAbUVEe9aPkP7doKD+5rKz/uu9vv7rvLz+6rKx/tqfnf5iNzfnCAQEcwAA + ACoAAAAbAAAAIQIBATorGBiQhFNT67Z3dv68fn3+wYSD/siKiP6aZmX2AQAAKQAAAAAAAAAAd05Ni9eT + lP/jq6z/7cLC/vXS0v/zz9D/8b69/uyxrv+samr/l15d+2tDQ+NkPz7bdkxL451nZve+gYD/yY2M/tWg + n//jtrT/46+t/uOmpP+mdHPwBQMDFAAAAAAAAAAAdkpJh9iUlf7Hl5f+tJeX/uzOzv7lyMj+57y6/vS6 + t/7HhoX+xYaE/saJh/7MkpD+0ZmY/tejov7mt7X+7cXD/vDFxP7vvLr+8Le0/u2zsf5PMzOMDQcHAQAA + AAAAAAAAYTg4X9OOj/9aUlL/YGJi/nh2dv+skJD/qo2M/vnAvf/dn53/4KKg/+Cnp/7vxsT/u8PM/sHI + 0P/1xsT/9sG+/ve+u//3vrv/87q3/ntVVLkkFhYIAAAAAAAAAAAAAAAAVC8wD6BkZOWjhIT/jo6O/n1+ + fv+eenv/xpGR/vi/vP/wtbL/mZPP/0Z2+v69nrr/gd/x/nfD2v/2vLr/9Lq3/vG2tP/lq6j/elJRrjQg + IAoAAAAAAAAAAAAAAAAAAAAAAAAAAGc7OyeOWVnGv4eH/r2Fhf7YlZb+1Y6P/uinpv74v7z+3ay3/seo + w/7srZ/+7LGv/qmyjv63qI7+5Kel/r2GhPZ1S0p1QCcmAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAd0pKOpReXtKxb3D/yXl6/sx5ev/ws7D/6q6s/+Ked/7npFb/2ZiP/ny7gP+OjW/9h1dWr2I7 + OiMAAAAAAAAAAAAAAAAAAAAAAAAAALSCggSqcXIbo2dnN61xcVS/h4eIzp2c2cKWle2OY2OGbz4+Y4xN + Tr6zaWn84Jyb/9aXlv7Ji4r/p25t9INTUqZlPDw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJg + YASjcnMorH9/a6h7e4yabm6Df1NTU3VKSgwAAAAAAAAAAAAAAABgNDQgcj8/bntHR4ZnPDxTVTExDQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////APx//wD4P/8A8D//AOA//wDgH/8A4B//AMAf + /wDAH8EAwA8AAMAAAADAAAAAwAAAAMAAAQDAAAMA4AAHAPgAHwAAAH8AAcH/AP///wD///8A////ACgA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQc + HA5LKSlUNBwcSAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsO + DgV/SkqHm1hY+X5HR90tGRkuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB4SEhCr2Zm7sZwcf+oYWL5UC8vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAACnl9fnMRwcf/IcXL/tmxs/mI8PGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa0NCGbRsbdbMenv/zn5//8R9ff9ySkmCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA + AAkAAAAAAAAAAItYWDvFfn/y2ZWW/92fn//anJv/jWFgvwAAAB0AAAAAAAAAAAAAAAIzHBwiYjs7a3pM + S6pqQkKjLBoaMwAAAACeZ2dZ05KS/em0tP/vxMT/77u6/8CHhfpmPDyvRysqYlExMV1ySEiGnWdn07qB + gPzLkI//w4iG/HJLS3YAAAAAomloXsyRkf/DoKD/48bG/+jAv//hpKL/vX17/7h/fPu/iYj7z5qZ/+Gw + rv/rvLr/77q3/9ScmuR9U1I+AAAAAJZbWz2ndnbxdG9v/4yCgv+4lJP/77Wy/86erP+6nsH/tsXR/8PH + 0P/4wsD/9b26/+Cppu2peXdiAAAAAQAAAABYKCgHn2lqe6eCguSsgoL90pKS//Cxrv/TrcP/s5y+/8i3 + s/+quab/26mh/82UktSgbm1TBAAAAwAAAACud3cEvYGBC7N6ehyyfHtyt39+3bNub9vLgYH05qak/+Kg + g//OlH39jZR04Zd0aYmDT1EiAAAAAAAAAAAAAAAAr3t7D7aCgki5h4Z8uImJgah+fUltPz8ajU1ORq1s + bI6vdHOgm2RkaYxJUiZgCygCAAAAAAAAAAAAAAAAAAAAAGo9PQF9UVEHcEdHCTodHQIAAAAAAAAAAAAA + AAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AADh/wAAwf8AAMH/ + AACB/wAAgfkAAIDAAACAAAAAgAAAAIAAAACAAQAAAAcAAAAPAAAOfwAA//8AAA== + + + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.Designer.cs new file mode 100644 index 0000000000..342e833dc1 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.Designer.cs @@ -0,0 +1,184 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumJoystickSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumJoystickSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label5 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.Port2ComboBox = new System.Windows.Forms.ComboBox(); + this.Port1ComboBox = new System.Windows.Forms.ComboBox(); + this.label1 = new System.Windows.Forms.Label(); + this.Port3ComboBox = new System.Windows.Forms.ComboBox(); + this.label2 = new System.Windows.Forms.Label(); + this.lblDoubleSize = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(170, 312); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(236, 312); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(9, 207); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(57, 13); + this.label5.TabIndex = 16; + this.label5.Text = "Joystick 2:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 157); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(57, 13); + this.label4.TabIndex = 15; + this.label4.Text = "Joystick 1:"; + // + // Port2ComboBox + // + this.Port2ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.Port2ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Port2ComboBox.FormattingEnabled = true; + this.Port2ComboBox.Location = new System.Drawing.Point(12, 223); + this.Port2ComboBox.Name = "Port2ComboBox"; + this.Port2ComboBox.Size = new System.Drawing.Size(284, 21); + this.Port2ComboBox.TabIndex = 14; + // + // Port1ComboBox + // + this.Port1ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.Port1ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Port1ComboBox.FormattingEnabled = true; + this.Port1ComboBox.Location = new System.Drawing.Point(12, 173); + this.Port1ComboBox.Name = "Port1ComboBox"; + this.Port1ComboBox.Size = new System.Drawing.Size(284, 21); + this.Port1ComboBox.TabIndex = 13; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(151, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Joystick Settings"; + // + // Port3ComboBox + // + this.Port3ComboBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.Port3ComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.Port3ComboBox.FormattingEnabled = true; + this.Port3ComboBox.Location = new System.Drawing.Point(12, 275); + this.Port3ComboBox.Name = "Port3ComboBox"; + this.Port3ComboBox.Size = new System.Drawing.Size(284, 21); + this.Port3ComboBox.TabIndex = 18; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 259); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(57, 13); + this.label2.TabIndex = 19; + this.label2.Text = "Joystick 3:"; + // + // lblDoubleSize + // + this.lblDoubleSize.Location = new System.Drawing.Point(26, 40); + this.lblDoubleSize.Name = "lblDoubleSize"; + this.lblDoubleSize.Size = new System.Drawing.Size(254, 117); + this.lblDoubleSize.TabIndex = 20; + this.lblDoubleSize.Text = resources.GetString("lblDoubleSize.Text"); + // + // ZXSpectrumJoystickSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(308, 347); + this.Controls.Add(this.lblDoubleSize); + this.Controls.Add(this.label2); + this.Controls.Add(this.Port3ComboBox); + this.Controls.Add(this.label1); + this.Controls.Add(this.label5); + this.Controls.Add(this.label4); + this.Controls.Add(this.Port2ComboBox); + this.Controls.Add(this.Port1ComboBox); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumJoystickSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Joystick Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox Port2ComboBox; + private System.Windows.Forms.ComboBox Port1ComboBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.ComboBox Port3ComboBox; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label lblDoubleSize; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.cs new file mode 100644 index 0000000000..fc151c843c --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.cs @@ -0,0 +1,127 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumJoystickSettings : Form + { + private ZXSpectrum.ZXSpectrumSyncSettings _syncSettings; + + public ZXSpectrumJoystickSettings() + { + InitializeComponent(); + } + + private string[] possibleControllers; + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _syncSettings = ((ZXSpectrum)Global.Emulator).GetSyncSettings().Clone(); + + possibleControllers = Enum.GetNames(typeof(JoystickType)); + + foreach (var val in possibleControllers) + { + Port1ComboBox.Items.Add(val); + Port2ComboBox.Items.Add(val); + Port3ComboBox.Items.Add(val); + } + + Port1ComboBox.SelectedItem = _syncSettings.JoystickType1.ToString(); + Port2ComboBox.SelectedItem = _syncSettings.JoystickType2.ToString(); + Port3ComboBox.SelectedItem = _syncSettings.JoystickType3.ToString(); + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _syncSettings.JoystickType1.ToString() != Port1ComboBox.SelectedItem.ToString() + || _syncSettings.JoystickType2.ToString() != Port2ComboBox.SelectedItem.ToString() + || _syncSettings.JoystickType3.ToString() != Port3ComboBox.SelectedItem.ToString(); + + if (changed) + { + // enforce unique joystick selection + + bool selectionValid = true; + + var j1 = Port1ComboBox.SelectedItem.ToString(); + if (j1 != possibleControllers.First()) + { + if (j1 == Port2ComboBox.SelectedItem.ToString()) + { + Port2ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + if (j1 == Port3ComboBox.SelectedItem.ToString()) + { + Port3ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + } + + var j2 = Port2ComboBox.SelectedItem.ToString(); + if (j2 != possibleControllers.First()) + { + if (j2 == Port1ComboBox.SelectedItem.ToString()) + { + Port1ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + if (j2 == Port3ComboBox.SelectedItem.ToString()) + { + Port3ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + } + + var j3 = Port3ComboBox.SelectedItem.ToString(); + if (j3 != possibleControllers.First()) + { + if (j3 == Port1ComboBox.SelectedItem.ToString()) + { + Port1ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + if (j3 == Port2ComboBox.SelectedItem.ToString()) + { + Port2ComboBox.SelectedItem = possibleControllers.First(); + selectionValid = false; + } + } + + if (selectionValid) + { + _syncSettings.JoystickType1 = (JoystickType)Enum.Parse(typeof(JoystickType), Port1ComboBox.SelectedItem.ToString()); + _syncSettings.JoystickType2 = (JoystickType)Enum.Parse(typeof(JoystickType), Port2ComboBox.SelectedItem.ToString()); + _syncSettings.JoystickType3 = (JoystickType)Enum.Parse(typeof(JoystickType), Port3ComboBox.SelectedItem.ToString()); + + GlobalWin.MainForm.PutCoreSyncSettings(_syncSettings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + MessageBox.Show("Invalid joystick configuration. \nDuplicates have automatically been changed to NULL.\n\nPlease review the configuration"); + } + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Joystick settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.resx new file mode 100644 index 0000000000..c45473925d --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumJoystickSettings.resx @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ZXHawk is set up to allow 3 different unique joysticks to be attached at one time. + +This is because the Kempston joystick had to be attached via a Kempton interface plugged into the single expansion port. The Sinclair and Cursor joysticks effectively mapped to different key presses on the keyboard. + + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAoIJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACYXODs4BCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + KTNDQ0M7OAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALllbYmJZQBcAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYYWNwcHBwWy8mAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFLanBwcHBwYz0eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABpqcHBwcHBwZVkUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl11w + cHBwcHBwcGcSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIXdwcHBwcHBwcGkSAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXBwcHBwcHBwd2wYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAACXbnBwdXB5dXl0eW4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAid3R5eXl5eXl5q6wzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9eXV5 + i7CxsbGxsblLKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABndYuwsbm8uby5vMFnHgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJt3q7G3vMHB1cLBwdWuEgAAAAAAAAAAAAAAAAAA + AAAAAAAeEhMSCiUAAAAAAAAAAEexsbm/1dXZ2dnZ1da5ZgwAAAAAAAAAAAAAAAAAAAAjEjNZaW5qXRMl + AAAAAAAAADW5s7/V2N7i4uLi3dzZrQQPAAAAAAAAAAAAAAAAHxhZbm5uaWltd6ASAAAAAAAAAEmzvMLZ + 3uP29/fw4uTkuUAWCy0AAAAAAAAAAB4YYXd3gG13vbm5vb8zAAAAAAAAAE6xwdXd4/b6+/r38OTl1Vlc + OAMIFAweFBQSM2mtrYB3vdXT0NXExNU1AAAAAAAAAE65wtXe8Pr7/Pz79+fn1WphZ25pXV1mbHetrXd3 + tdXT4vXw49nZ3NYgAAAAAAAAAEu3wdje9vv7/Pz79+fn34B3d2xtoHeud66uudXT4vD39/Dj49zk5G0A + AAAAAAAAAD2xwcwoH0/L/Pukyenp5K27u7m5uczM0Nve4vb3+vr56OPl5eXl1igAAAAAAAAAADWxwQgB + BQYNmveZK/Dp6cG/wcTV2eP3+vr6+/r6+ejm5ufn5+nkIgAAAAAAAAAAAJmruR4sjC2WLFCdDd3p6dXW + 1tXI3vn67pCO9Ojp6efo5+fm59wiAAAAAAAAAAAAAABLsZ0FmC0qKgHMRcjp6dzc1Y2KiO3RlfKTj+np + 5ubm5eXk1SIAAAAAAAAAAAAAAACdab/Lp5aWnEfV1cHm6ebk6pGSiabZ8fOU0uXl5eTk3NyuRQAAAAAA + AAAAAAAAAAAAn0ux0KFTaMHBv7nC6efp3Ovv7OTm3OPl3Nzc3NfW1U6fAAAAAAAAAAAAAAAAAAAAAABF + Wa25t7yxs7Gw5+fn5Obk18XG3NyBfHvD1cSgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAFUzarGwsHl5sefn + 39zEgoZ/hL19fnqirj2jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj09ZXV0cLzn3NXChYeDub+1pbQ9 + VQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0rXj+rpInTBDcHCz5NW/ucG5u7GAM1QAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXjU1h6NnZv/Fc3T/x3Bx/8dw + cf/HcHH/x3Bx/8dwcf/HcHH/w3Jz/6ZoZ/9ZMzPTAQAAPQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyFxccektK0b12 + dv/HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xXR0/69wb/9jOjneBwMDSQAAAAUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABNKSlNlmBf9sh3d//HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xnd3/7Z4d/9sQUDnDgcHVQAA + AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABkOjqKsXFw/8lyc//HcXL/yHJz/8l0df/JdXb/yXV2/8l1dv/JdHX/ynt7/7+B + f/94SknvFgsLZQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACILCxB7TUzDwXd3/8lyc//KdXb/y3h5/8x7fP/NfX7/zX5+/819 + fv/NfH3/zoOC/8iJiP+GVVX3Hg8QegAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMiIi+SXl3oynp7/8t4ef/NfX7/z4GC/9GE + hf/Sh4j/04iJ/9KIiP/Rhof/04uK/8+RkP+XY2L9KxcXlwAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABwAA + AA0AAAAPAAAACwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUvL1enbW37zn5+/85/ + gP/Rhob/1IuM/9aPkP/XkpP/2JOU/9iTlP/XkZH/15OT/9eZl/+rdHP/QSUlvAAAADwAAAAFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAACQAA + ABgAAAAvAgEBSwcDA2EFAgJoAAAAWAAAADYAAAARAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU8 + O4W5eXn/0IKD/9KIif/Wj5D/2ZWW/9ubm//dnp//3qCg/92foP/cnZ3/3Jyc/9+in//CiYf/Zj8/4wYC + AnAAAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + AA4AAAAnCQQEUCISEoQ+IiKzVzEx1mU6OuZiOTnmRigo0hgNDZsAAABMAAAAEAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABnVJSK/HhIP/04eI/9aQkf/amJn/3qCh/+Gmp//jq6v/5Kyt/+OsrP/iqan/4aal/+ap + p//Umpj/nmxr/C8ZGboAAABXAAAAGAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAIAAAAOAQAALRkNDWY+IiKpZDo63YZRUfigZGP/sHBv/7V0c/+xcnH/oWZm/2k+PvEfEBCcAAAAMQAA + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + cP/Gb3D/oF9e/hMKCmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4QECynZmX7xnBx/sdwcf/HcHH/tG1t/h8REYMAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAx + MIzFc3T+xm9w/sdwcf7HcHH+vHR0/jAcHJkAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ4OAYVSUtfIcnP/yXZ3/st5ef/LeHn/xoB//kQq + KrEAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJxYWGrNvb/7Nfn//0oeI/tSNjf/UjI3/1ZOS/mE+PtQAAAAXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAIAAAARAAAALQAAADUAAAARAAAAAAAAAAAAAAAAQyYmUM6Ghv/Wj5D/3J2e/uCl + pf/fpKT/4KOi/qRycPkHBARlAAAABQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAJh8REYBYNTXMhVJR8XxM + TO8gEhKeAAAAEAAAAAAAAAAAbUVEe9aPkP7doKD+5rKz/uu9vv7rvLz+6rKx/tqfnf5iNzfnCAQEcwAA + ACoAAAAbAAAAIQIBATorGBiQhFNT67Z3dv68fn3+wYSD/siKiP6aZmX2AQAAKQAAAAAAAAAAd05Ni9eT + lP/jq6z/7cLC/vXS0v/zz9D/8b69/uyxrv+samr/l15d+2tDQ+NkPz7bdkxL451nZve+gYD/yY2M/tWg + n//jtrT/46+t/uOmpP+mdHPwBQMDFAAAAAAAAAAAdkpJh9iUlf7Hl5f+tJeX/uzOzv7lyMj+57y6/vS6 + t/7HhoX+xYaE/saJh/7MkpD+0ZmY/tejov7mt7X+7cXD/vDFxP7vvLr+8Le0/u2zsf5PMzOMDQcHAQAA + AAAAAAAAYTg4X9OOj/9aUlL/YGJi/nh2dv+skJD/qo2M/vnAvf/dn53/4KKg/+Cnp/7vxsT/u8PM/sHI + 0P/1xsT/9sG+/ve+u//3vrv/87q3/ntVVLkkFhYIAAAAAAAAAAAAAAAAVC8wD6BkZOWjhIT/jo6O/n1+ + fv+eenv/xpGR/vi/vP/wtbL/mZPP/0Z2+v69nrr/gd/x/nfD2v/2vLr/9Lq3/vG2tP/lq6j/elJRrjQg + IAoAAAAAAAAAAAAAAAAAAAAAAAAAAGc7OyeOWVnGv4eH/r2Fhf7YlZb+1Y6P/uinpv74v7z+3ay3/seo + w/7srZ/+7LGv/qmyjv63qI7+5Kel/r2GhPZ1S0p1QCcmAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAd0pKOpReXtKxb3D/yXl6/sx5ev/ws7D/6q6s/+Ked/7npFb/2ZiP/ny7gP+OjW/9h1dWr2I7 + OiMAAAAAAAAAAAAAAAAAAAAAAAAAALSCggSqcXIbo2dnN61xcVS/h4eIzp2c2cKWle2OY2OGbz4+Y4xN + Tr6zaWn84Jyb/9aXlv7Ji4r/p25t9INTUqZlPDw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJg + YASjcnMorH9/a6h7e4yabm6Df1NTU3VKSgwAAAAAAAAAAAAAAABgNDQgcj8/bntHR4ZnPDxTVTExDQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////APx//wD4P/8A8D//AOA//wDgH/8A4B//AMAf + /wDAH8EAwA8AAMAAAADAAAAAwAAAAMAAAQDAAAMA4AAHAPgAHwAAAH8AAcH/AP///wD///8A////ACgA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQc + HA5LKSlUNBwcSAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsO + DgV/SkqHm1hY+X5HR90tGRkuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB4SEhCr2Zm7sZwcf+oYWL5UC8vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAACnl9fnMRwcf/IcXL/tmxs/mI8PGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa0NCGbRsbdbMenv/zn5//8R9ff9ySkmCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA + AAkAAAAAAAAAAItYWDvFfn/y2ZWW/92fn//anJv/jWFgvwAAAB0AAAAAAAAAAAAAAAIzHBwiYjs7a3pM + S6pqQkKjLBoaMwAAAACeZ2dZ05KS/em0tP/vxMT/77u6/8CHhfpmPDyvRysqYlExMV1ySEiGnWdn07qB + gPzLkI//w4iG/HJLS3YAAAAAomloXsyRkf/DoKD/48bG/+jAv//hpKL/vX17/7h/fPu/iYj7z5qZ/+Gw + rv/rvLr/77q3/9ScmuR9U1I+AAAAAJZbWz2ndnbxdG9v/4yCgv+4lJP/77Wy/86erP+6nsH/tsXR/8PH + 0P/4wsD/9b26/+Cppu2peXdiAAAAAQAAAABYKCgHn2lqe6eCguSsgoL90pKS//Cxrv/TrcP/s5y+/8i3 + s/+quab/26mh/82UktSgbm1TBAAAAwAAAACud3cEvYGBC7N6ehyyfHtyt39+3bNub9vLgYH05qak/+Kg + g//OlH39jZR04Zd0aYmDT1EiAAAAAAAAAAAAAAAAr3t7D7aCgki5h4Z8uImJgah+fUltPz8ajU1ORq1s + bI6vdHOgm2RkaYxJUiZgCygCAAAAAAAAAAAAAAAAAAAAAGo9PQF9UVEHcEdHCTodHQIAAAAAAAAAAAAA + AAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AADh/wAAwf8AAMH/ + AACB/wAAgfkAAIDAAACAAAAAgAAAAIAAAACAAQAAAAcAAAAPAAAOfwAA//8AAA== + + + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs new file mode 100644 index 0000000000..6e2c603520 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.Designer.cs @@ -0,0 +1,135 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumNonSyncSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumNonSyncSettings)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.lblOSDVerbinfo = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.osdMessageVerbositycomboBox1 = new System.Windows.Forms.ComboBox(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(247, 142); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(313, 142); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(185, 13); + this.label1.TabIndex = 17; + this.label1.Text = "ZX Spectrum Misc Non-Sync Settings"; + // + // lblOSDVerbinfo + // + this.lblOSDVerbinfo.Font = new System.Drawing.Font("Lucida Console", 6.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblOSDVerbinfo.Location = new System.Drawing.Point(175, 107); + this.lblOSDVerbinfo.Name = "lblOSDVerbinfo"; + this.lblOSDVerbinfo.Size = new System.Drawing.Size(196, 21); + this.lblOSDVerbinfo.TabIndex = 28; + this.lblOSDVerbinfo.Text = "null"; + this.lblOSDVerbinfo.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 91); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(125, 13); + this.label4.TabIndex = 27; + this.label4.Text = "OSD Message Verbosity:"; + // + // osdMessageVerbositycomboBox1 + // + this.osdMessageVerbositycomboBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.osdMessageVerbositycomboBox1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.osdMessageVerbositycomboBox1.FormattingEnabled = true; + this.osdMessageVerbositycomboBox1.Location = new System.Drawing.Point(12, 107); + this.osdMessageVerbositycomboBox1.Name = "osdMessageVerbositycomboBox1"; + this.osdMessageVerbositycomboBox1.Size = new System.Drawing.Size(157, 21); + this.osdMessageVerbositycomboBox1.TabIndex = 26; + this.osdMessageVerbositycomboBox1.SelectionChangeCommitted += new System.EventHandler(this.OSDComboBox_SelectionChangeCommitted); + // + // ZXSpectrumNonSyncSettings + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(385, 177); + this.Controls.Add(this.lblOSDVerbinfo); + this.Controls.Add(this.label4); + this.Controls.Add(this.osdMessageVerbositycomboBox1); + this.Controls.Add(this.label1); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumNonSyncSettings"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Other Non-Sync Settings"; + this.Load += new System.EventHandler(this.IntvControllerSettings_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label lblOSDVerbinfo; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.ComboBox osdMessageVerbositycomboBox1; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs new file mode 100644 index 0000000000..983e896359 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.cs @@ -0,0 +1,86 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumNonSyncSettings : Form + { + private ZXSpectrum.ZXSpectrumSettings _settings; + + public ZXSpectrumNonSyncSettings() + { + InitializeComponent(); + } + + private void IntvControllerSettings_Load(object sender, EventArgs e) + { + _settings = ((ZXSpectrum)Global.Emulator).GetSettings().Clone(); + + + + // OSD Message Verbosity + var osdTypes = Enum.GetNames(typeof(ZXSpectrum.OSDVerbosity)); + foreach (var val in osdTypes) + { + osdMessageVerbositycomboBox1.Items.Add(val); + } + osdMessageVerbositycomboBox1.SelectedItem = _settings.OSDMessageVerbosity.ToString(); + UpdateOSDNotes((ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), osdMessageVerbositycomboBox1.SelectedItem.ToString())); + } + + private void OkBtn_Click(object sender, EventArgs e) + { + bool changed = + _settings.OSDMessageVerbosity.ToString() != osdMessageVerbositycomboBox1.SelectedItem.ToString(); + + if (changed) + { + _settings.OSDMessageVerbosity = (ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), osdMessageVerbositycomboBox1.SelectedItem.ToString()); + + GlobalWin.MainForm.PutCoreSettings(_settings); + + DialogResult = DialogResult.OK; + Close(); + } + else + { + DialogResult = DialogResult.OK; + Close(); + } + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("Misc settings aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + + private void UpdateOSDNotes(ZXSpectrum.OSDVerbosity type) + { + switch (type) + { + case ZXSpectrum.OSDVerbosity.Full: + lblOSDVerbinfo.Text = "Show all OSD messages"; + break; + case ZXSpectrum.OSDVerbosity.Medium: + lblOSDVerbinfo.Text = "Only show machine/device generated messages"; + break; + case ZXSpectrum.OSDVerbosity.None: + lblOSDVerbinfo.Text = "No core-driven OSD messages"; + break; + } + } + + private void OSDComboBox_SelectionChangeCommitted(object sender, EventArgs e) + { + ComboBox cb = sender as ComboBox; + UpdateOSDNotes((ZXSpectrum.OSDVerbosity)Enum.Parse(typeof(ZXSpectrum.OSDVerbosity), cb.SelectedItem.ToString())); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumNonSyncSettings.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAoIJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACYXODs4BCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + KTNDQ0M7OAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALllbYmJZQBcAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYYWNwcHBwWy8mAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFLanBwcHBwYz0eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABpqcHBwcHBwZVkUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl11w + cHBwcHBwcGcSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIXdwcHBwcHBwcGkSAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXBwcHBwcHBwd2wYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAACXbnBwdXB5dXl0eW4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAid3R5eXl5eXl5q6wzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9eXV5 + i7CxsbGxsblLKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABndYuwsbm8uby5vMFnHgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJt3q7G3vMHB1cLBwdWuEgAAAAAAAAAAAAAAAAAA + AAAAAAAeEhMSCiUAAAAAAAAAAEexsbm/1dXZ2dnZ1da5ZgwAAAAAAAAAAAAAAAAAAAAjEjNZaW5qXRMl + AAAAAAAAADW5s7/V2N7i4uLi3dzZrQQPAAAAAAAAAAAAAAAAHxhZbm5uaWltd6ASAAAAAAAAAEmzvMLZ + 3uP29/fw4uTkuUAWCy0AAAAAAAAAAB4YYXd3gG13vbm5vb8zAAAAAAAAAE6xwdXd4/b6+/r38OTl1Vlc + OAMIFAweFBQSM2mtrYB3vdXT0NXExNU1AAAAAAAAAE65wtXe8Pr7/Pz79+fn1WphZ25pXV1mbHetrXd3 + tdXT4vXw49nZ3NYgAAAAAAAAAEu3wdje9vv7/Pz79+fn34B3d2xtoHeud66uudXT4vD39/Dj49zk5G0A + AAAAAAAAAD2xwcwoH0/L/Pukyenp5K27u7m5uczM0Nve4vb3+vr56OPl5eXl1igAAAAAAAAAADWxwQgB + BQYNmveZK/Dp6cG/wcTV2eP3+vr6+/r6+ejm5ufn5+nkIgAAAAAAAAAAAJmruR4sjC2WLFCdDd3p6dXW + 1tXI3vn67pCO9Ojp6efo5+fm59wiAAAAAAAAAAAAAABLsZ0FmC0qKgHMRcjp6dzc1Y2KiO3RlfKTj+np + 5ubm5eXk1SIAAAAAAAAAAAAAAACdab/Lp5aWnEfV1cHm6ebk6pGSiabZ8fOU0uXl5eTk3NyuRQAAAAAA + AAAAAAAAAAAAn0ux0KFTaMHBv7nC6efp3Ovv7OTm3OPl3Nzc3NfW1U6fAAAAAAAAAAAAAAAAAAAAAABF + Wa25t7yxs7Gw5+fn5Obk18XG3NyBfHvD1cSgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAFUzarGwsHl5sefn + 39zEgoZ/hL19fnqirj2jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj09ZXV0cLzn3NXChYeDub+1pbQ9 + VQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0rXj+rpInTBDcHCz5NW/ucG5u7GAM1QAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXjU1h6NnZv/Fc3T/x3Bx/8dw + cf/HcHH/x3Bx/8dwcf/HcHH/w3Jz/6ZoZ/9ZMzPTAQAAPQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyFxccektK0b12 + dv/HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xXR0/69wb/9jOjneBwMDSQAAAAUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABNKSlNlmBf9sh3d//HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xnd3/7Z4d/9sQUDnDgcHVQAA + AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABkOjqKsXFw/8lyc//HcXL/yHJz/8l0df/JdXb/yXV2/8l1dv/JdHX/ynt7/7+B + f/94SknvFgsLZQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACILCxB7TUzDwXd3/8lyc//KdXb/y3h5/8x7fP/NfX7/zX5+/819 + fv/NfH3/zoOC/8iJiP+GVVX3Hg8QegAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMiIi+SXl3oynp7/8t4ef/NfX7/z4GC/9GE + hf/Sh4j/04iJ/9KIiP/Rhof/04uK/8+RkP+XY2L9KxcXlwAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABwAA + AA0AAAAPAAAACwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUvL1enbW37zn5+/85/ + gP/Rhob/1IuM/9aPkP/XkpP/2JOU/9iTlP/XkZH/15OT/9eZl/+rdHP/QSUlvAAAADwAAAAFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAACQAA + ABgAAAAvAgEBSwcDA2EFAgJoAAAAWAAAADYAAAARAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU8 + O4W5eXn/0IKD/9KIif/Wj5D/2ZWW/9ubm//dnp//3qCg/92foP/cnZ3/3Jyc/9+in//CiYf/Zj8/4wYC + AnAAAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + AA4AAAAnCQQEUCISEoQ+IiKzVzEx1mU6OuZiOTnmRigo0hgNDZsAAABMAAAAEAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABnVJSK/HhIP/04eI/9aQkf/amJn/3qCh/+Gmp//jq6v/5Kyt/+OsrP/iqan/4aal/+ap + p//Umpj/nmxr/C8ZGboAAABXAAAAGAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAIAAAAOAQAALRkNDWY+IiKpZDo63YZRUfigZGP/sHBv/7V0c/+xcnH/oWZm/2k+PvEfEBCcAAAAMQAA + AAMAAAAAAAAAAAAAAAAAAAAALhAQFIZXVs/RjIz/1Y2O/9qYmP/eoaL/46qr/+aysv/ot7f/6rm5/+m4 + uf/otbX/5q+v/+uvrf/jqab/wYeF/28/P/QhEhKvAAAAXwAAACgAAAANAAAABQAAAAMAAAACAAAAAwAA + AAUAAAAKAAAAFQAAADAdDg9oSSkptHZHRu2dYmL+t3Z1/758e/+6enn/tnh3/7d5eP+8fn3/w4SD/7Z6 + ef9eODfbBgICTgAAAAgAAAAAAAAAAAAAAAAAAAAAPhwcJJVjYuPXkZH/2JOU/92fn//iqqr/57O0/+u8 + vP/uwsL/78XG/+/Exf/twMD/67i4/+60sv/wtrP/zZKQ/5taWv9xQED2MRsaxAgEBIcAAABaAAAAQQAA + ADcAAAA2AAAAOwAAAEUEAgJZHA4OfUcnJ7l5SkntqGxr/8CAfv/DgoH/vH59/7p+ff/DiIb/zZGP/9GT + kf/UlJP/1peV/9eZl/+GVlbuGQsLVwAAAAcAAAAAAAAAAAAAAAAAAAAARiIiLZ9rauvZk5P/2peY/+Ck + pP/lsLD/6ru7/+/Fxf/yzMz/9NDQ//PPz//xycr/7sDA//K5tv/1u7j/36Kg/6dmZf+mZWX/j1ZW/WM6 + OutDJSXQNBwcvDAaGrQ0HBy1PiIivUwsLMtkPDzfh1VU9a1xcP/EhIP/xIWE/7+Cgf/Ch4b/zZST/9mk + ov/grq3/4a6t/96lo//eoJ7/36Kg/+Cjof+IWVjnGwwMQwAAAAIAAAAAAAAAAAAAAAAAAAAARyQkL6Br + auzZk5P/25qb/+GnqP/ntLT/7cDA//LLy//209T/+NjY//fX1//00ND/8cbG//W9u//4vrz/46ak/7d0 + c/+vb27/s3Jy/7d2df+ucXD/pWpp/6Npaf+nbWz/sHVz/7p9fP/EhYT/yImI/8WIhv/DiIb/ypGP/9eg + n//hr63/57q5/+rCwP/rwsD/6bq4/+evrf/nq6n/6q6r/9qgnv9wRkbDBwAAHgAAAAAAAAAAAAAAAAAA + AAAAAAAASCQkLZ1nZuvYkpP/25uc/+Opqv/qtrf/7cHB//TOzv/52Nj/+tzc//na2v/xz9D/8MfH//fA + vv/6wb7/6a6r/8OBgP/DgoD/vX58/7h7ev+8fn3/woOC/8aHhv/HiYj/xoqJ/8aLif/Ijoz/zZST/9eg + nv/hrav/6Lm3/+zCwf/uyMf/78nH/+/Dwf/uvLr/7ba0/+60sf/vtLL/8ri1/7J+fflMKSltAAAABAAA + AAAAAAAAAAAAAAAAAAAAAAAAQyEhI5JcXOPWj5D/3Juc/8qVlf+BZmb/bl5e/4l4eP/AqKj/8tPT//LO + zv+5p6b/w6qq//fBv//7wr//8LWy/86Ojf/Ojoz/0ZGP/9GSkP/OkY//zpOR/9GamP/VoJ//2qel/+Gv + rf/nt7X/6727/+3Dwf/wycf/8czL//LLyf/yxsT/8cC+//G7uf/yubf/87m3//S7uP/4vrv/1J6c/3JH + RrAdCgsWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANRcXEYJNTcvPiIn/15aW/2VNTf85Ojr/Q0VF/0JF + RP9dXFz/n5GR/+S/v/+bh4f/hXp6/+25uP/7wr//9bu4/9qcmv/Zmpj/252b/96gnf/ipKH/5q+s/+u+ + vP/vycf/8srI/+3Hxv/wysj/9c7M//TNy//0ysj/9MbE//TBv//1vrz/9r26//e9u//4vrv/+L+8//vB + vv/hqqf/g1ZVzDwcHC4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAW4+Ppq/env/05OT/2ZX + V/9rbm7/fX9//3l6ev99f3//cHJy/5F9ff+ff3//XFhY/9eop//8wr//+L+8/+Wppv/ipaP/5qil/96i + pP/Kmaz/1qi1//LGxP/tyMf/qb3J/23E3P9kw9//vMTN//jDwP/3wb//+MC9//i/vf/5v73/+b+8//i/ + vP/3vrv/+L68/92mo/+IWlnRRSMjOAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFcv + L0mbX1/y15GS/6GAgP9XV1b/iYuL/4CBgf98fX3/cnR0/1dPT/++j4//km9w/9Sfnv/6wL3/+cC9/+6z + sP/ssK3/0Z+u/4OH1P9YffD/QGPs/7KYyv/Ct7z/Ytrz/3Ts//8s2f//cbvU//m+u//4v7z/+L67//e9 + uv/1vLn/9Lq3//O5tv/zuLX/0puZ/4RVVctGIyM4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAADIXFwdrPDySq2ts/diZmf/ApKT/sKur/4CBgP95enr/iYiI/49zdP/do6P/36Ch/96e + nv/zuLX/+sK///W7uP/1ubT/qZC//2qY+/9tnf//MGT6/56FxP/esK//nMbS/57n8/9+z+T/ybG3//a6 + t//zubb/8re0//C1s//utLH/7rKw/+qvrP++iIb9dklJtkMgISoAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABHIyMSazw8kZ5hYvXNjI3/2aSk/7OMjP+bd3f/sIKC/9KV + lv/cnJz/2peY/9aRkf/koqL/+sG+//nAvf/5v7z/4amw/6qZx/+aouP/qpvP/+mxtv/2urj/6rGv/+S6 + u//ptrX/466n/+Ovqf/ssK7/6q6s/+isqv/oq6n/2J2b/6JubfFoPT2NOxoaFwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOBoaCFowMFd7SEjAomZm9sWC + gv/XkZL/25SV/9iSk//Wj5D/1IyN/9KHiP/UiIj/8bOx//rCv//3vbv/9ru4//O3s//xuLX/7q6e/+ej + hf/npIn/7bCp/+Otp/+KsX3/ULdm/1WjWv+7oYz/5KWk/9uenP+4gH79glJRzVYuLlQgCAkGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAA8HBwQVy4uS3FBQaCPV1fjsG5v/cmAgf/ShYb/0YKD/85+f//LeXr/2I2M//e8uf/1vLn/7rOx/+2y + sP/lpJX/5qFY/+6xXP/djS3/35h9/86gl/9SwW7/Nd90/0WxXP+vlH//wYSE/49cW+VlOTmBQR4eHAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAGk7OhqIWFd8oG5u8J5qav+eX2D/tmts/8Z0df/KdHX/yXJz/92T + k//3vLn/7LGu/+Snpf/dm5L/4Z1q/+61dP/fmmX/15WM/9eYlv/Bm43/r6uR/6uNgP+WYWDtbkBAnUwn + JzQVAQECAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiFJSBnhC + QgpqNDQJWSUlB08dHQdfKisKfENDFJJWViinbGtRvYOCjtOcm8/pt7X157y6/7eOjfhxRUW7aTk5m4RK + StehWlr6uGdo/8Zwcf/dkpH/8bSx/+OnpP/YmZj/1ZWT/9ealP/Vl5X/0JCP/8eIhv+zdnb/lFtc6nA/ + QKRSKio/JQwNBgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AADTn6AB2qioDMuUlCHBhYU8voCAWcCBgXTEhoaLzZGQqdeensngrKvn47Sz/NOop/+yiIfyi2Bgs2k+ + PlZXKysPAAAAAUYlJRxcMTFYcj4+pYpMTeWmXF3+xnl5/9+Zl//dnJr/z46M/8KCgf+vc3L/ll9e831L + S8hlOTl/TigoMy0REQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABzQUIDnmprDriGhifHlpZMzp6eeNCgoZ7On5+2yJqaybuPj9WnfHzVj2RkunVJ + SYNbLy8/PRQUCgAAAAAAAAAAAAAAAAAAAAAAAAAAKRUVBU0pKSphNDRtd0BAsotNTd2ZW1vrkVlY4HtJ + Sb5lOTmCUysrQTsbGxEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWCwsA2Y4OA5xQkImdkhIRHhKSll0R0dibUBAWWI2 + NkNUKCgoOhISDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMhkZB0km + Jh5LJiYsRSEhITATFAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAD/+H////8AAP/gH////wAA/8Af//// + AAD/gA////8AAP+AD////wAA/wAP////AAD/AA////8AAP4AB////wAA/gAH////AAD8AAf///8AAPwA + B////wAA/AAH////AAD8AAf///8AAPgAB////wAA+AAH//4HAAD4AAP/8AEAAPgAAf/AAQAA8AAA/wAA + AADwAAAAAAAAAPAAAAAAAAAA8AAAAAAAAADwAAAAAAEAAPAAAAAAAQAA8AAAAAADAADwAAAAAAcAAPAA + AAAADwAA+AAAAAAfAAD4AAAAAD8AAPwAAAAAfwAA/gAAAAD/AAD/gAAAA/8AAP/gAAAH/wAAgAAAAB// + AAAAAAAAf/8AAAAD4AP//wAAgB/8H///AAD///////8AAP///////wAA////////AAD///////8AAP// + /////wAA////////AAAoAAAAIAAAAEAAAAABACAAAAAAAIAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAYAAAAZAAAAGQAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAARCQkYOh8fb0ooKK80HByiCQUFTAAAAAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAIhERFmA2Np2ITUz3lVNT/4dLS/5IKCi9AAAALwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAANjODiBllhY+61kZP+vY2P/pV5e/3xHRvEhEhJfAAAAAgAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAASSgoN41VVeS6bW3/xW9w/8dwcf+9bG3/klZW/jogIIEAAAAGAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZ1RkWcs2xs/8dxcv/HcHH/x3Bx/8Zwcf+iYWH/SSkpmAAA + AAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUC0tMZtgX+fGcnP/x3Bx/8dwcf/HcHH/x3Fy/61q + av9UMTGqAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABxRER1tm9v/8hxcv/HcHH/x3Bx/8dw + cf/HcnP/tnRz/185OboAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwAACIxXV7TEdHT/yHJz/8l1 + dv/Kd3j/ynd4/8p4eP/Bf37/bURDywAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABNKysjo2Zm4Mt4 + ef/NfH3/z4GC/9GFhf/RhYb/0YWF/82Mi/9+UVHeCAICOwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAJAAAACwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAGc+ + Pkm1c3P30IGC/9OJiv/XkZL/2ZaW/9mWl//YlJX/2JmY/5hnZfMeEBBrAAAABwAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAA0FAgItHhAQWzAbG4IqFxeHDQcHWwAAABkAAAAAAAAAAAAA + AAAAAAAAek1MdMN/f//VjI3/2piZ/9+io//hqKn/4qmp/+Clpf/jpqT/wImH/04xMLwAAAA6AAAABQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAABEbDg5GRygokW5CQs+MVlbxnGJh/JdfXvxnPz7hHA8PbgAA + AAwAAAAAAAAAAAAAAACMW1qbz4qK/9qXl//gpqb/5rKz/+q6u//rvLz/6La2/+qxr//epKL/j1lZ+DUc + HLACAQFPAAAAHQAAAA8AAAAPAAAAEwAAACIbDg5MVDExnYZUU+SpbWz+uXl4/7x+fP/AgoD/xoeF/72A + f/9fOzu1AAAAHAAAAAAAAAAAAAAABJhkZK/VkZH/3Z+g/+axsf/twMD/8svL//LNzf/vxcX/8Lq4/+6z + sf+1dHP/j1VU+144N9g7IiKqMhwclDcfH5RGKSmiYTw7v4tZWOiydXT+woOC/8aKiP/Ol5X/2aWj/9ui + of/cnpz/2pyb/35TUrgAAAAVAAAAAAAAAAAAAAAFmmVkstaTk//hpaX/7Lm6//TLy//419f/+NnZ//TP + z//1wb//9Lq3/8aGhP+1dHP/s3Rz/6xwb/+pb27+rnNy/7Z7ev/BhIL/yY2L/8+WlP/apqT/5be2/+vB + v//rvrz/6bKw/+uvrf/Um5n/bUVEgAAAAAMAAAAAAAAAAAAAAAOTXV2q1ZGR/9CYmP+dfX7/o4yM/9e8 + vP/z0tL/zLOz/+u8u//5v7z/1peV/8uLif/Ki4r/yoyL/86Ukv/TnJv/2qSi/+Gtq//nuLb/7cPB//DJ + x//xxsT/8b+9//G6t//zubf/77az/6d1dM89Hx8lAAAAAAAAAAAAAAAAAAAAAIJOTojNiIn/jGlp/01O + Tv9UVlb/dnNz/7uhof+Pfn7/xJ+e//zCv//lqKb/3J2b/+Chnv/hpaT/7Ly5/+vHxv/MxMn/0MjN//LK + yf/1x8X/9sLA//a/vP/3vrv/+L+8//S7uP+5hoXhYTo5RwAAAAAAAAAAAAAAAAAAAAAAAAAAaTs7RrVz + dPKmfn7/cXJx/4SGhv97fX3/b2Zm/516ev+7kJD/+sG+//C2s//lqqr/rpbA/3aB2/+ql83/tMHK/2jc + 9P9OzOz/2r3B//q/vP/3vrv/9ry6//a8uf/ss7D/tYGA32c+Pk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAvEhIHg01Njbp9fvrCn5//nI+P/4R7ev+fgID/2Jyd/9ybnP/ytrT/+b+8/+ewtf+Mld3/ZI36/5eI + zv/Ttrn/sNLc/6/Czv/stLT/8re0/++0sf/tsq//2qCe/6Rxb8phODg+AAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABCIB8MeUZGbqRpata8gYH8x4mJ/9eTk//YkpP/04qL/+Cbmv/5wL3/9726/+Sw + t//Zrrn/56qY/+2smf/lr6n/nLWJ/4Gtdf/Pppn/3qGf/7yEg/KJWViYTyoqIAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQh0dGXJAQGOXXl7NtnR1/8V7fP/MfH3/znt8/+il + o//0urj/7LCu/+Whg//rq13/35VX/9Kek/9yvXz/ZbNv/6iCdfqYY2O/aj4+TCUJCgcAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAACcamsBjFRVB4FERAh9PT0JjU1ND6VnZx+/hINF0JqZiNOjoty0iIf2hFBQw5lX + V8+wY2P4xXR0/+aioP/oq6j/2pqT/92fif/Vlor/yYqJ/7N8efiVZmPGdERFYkEfHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAALiFhgXFkJEdx5CQSMqSknbNlZWbz5uaws2cnOXBlJPnqH18r4dc + XFFULy8OSCUlFm07O0+FSUmeoV1d3sF9fPrGhoX/snZ295xkZNiFUlKbbD4+T0UdHxIAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc0JDA5FgYRKdbm46onR0Zp9ycnuWampzhFlZVmY6 + OikvDAwHAAAAAAAAAAAAAAAAAAAAAB0ODgRULCwhbjo7UXhERGVrPDxHTCYmGxAAAQMAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAP//////////////////////D////gf///wH///4A///+AP///AD///wA///8AP//+AD + ///gA//D4AH+AeAA+ADgAAAAwAAAAMAAAADAAAAB4AAAA+AAAAfgAAAP8AAAH/wAAD8AAAD/AAAD/wB4 + D//H////////////////////KAAAABgAAAAwAAAAAQAgAAAAAABgCQAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAABMAAAAtAAAAEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAgIO1cwMM1qOjrsHhAQmwAA + ABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAATCgogfUhI6ahgYP6lXV3+f0hI9wIBAT0AAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsGBgFPLy6kuW1t/sZv + cP/Gb3D/oF9e/hMKCmgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4QECynZmX7xnBx/sdwcf/HcHH/tG1t/h8REYMAAAABAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAx + MIzFc3T+xm9w/sdwcf7HcHH+vHR0/jAcHJkAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ4OAYVSUtfIcnP/yXZ3/st5ef/LeHn/xoB//kQq + KrEAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAJxYWGrNvb/7Nfn//0oeI/tSNjf/UjI3/1ZOS/mE+PtQAAAAXAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAIAAAARAAAALQAAADUAAAARAAAAAAAAAAAAAAAAQyYmUM6Ghv/Wj5D/3J2e/uCl + pf/fpKT/4KOi/qRycPkHBARlAAAABQAAAAAAAAAAAAAAAAAAAAAAAAADAQAAJh8REYBYNTXMhVJR8XxM + TO8gEhKeAAAAEAAAAAAAAAAAbUVEe9aPkP7doKD+5rKz/uu9vv7rvLz+6rKx/tqfnf5iNzfnCAQEcwAA + ACoAAAAbAAAAIQIBATorGBiQhFNT67Z3dv68fn3+wYSD/siKiP6aZmX2AQAAKQAAAAAAAAAAd05Ni9eT + lP/jq6z/7cLC/vXS0v/zz9D/8b69/uyxrv+samr/l15d+2tDQ+NkPz7bdkxL451nZve+gYD/yY2M/tWg + n//jtrT/46+t/uOmpP+mdHPwBQMDFAAAAAAAAAAAdkpJh9iUlf7Hl5f+tJeX/uzOzv7lyMj+57y6/vS6 + t/7HhoX+xYaE/saJh/7MkpD+0ZmY/tejov7mt7X+7cXD/vDFxP7vvLr+8Le0/u2zsf5PMzOMDQcHAQAA + AAAAAAAAYTg4X9OOj/9aUlL/YGJi/nh2dv+skJD/qo2M/vnAvf/dn53/4KKg/+Cnp/7vxsT/u8PM/sHI + 0P/1xsT/9sG+/ve+u//3vrv/87q3/ntVVLkkFhYIAAAAAAAAAAAAAAAAVC8wD6BkZOWjhIT/jo6O/n1+ + fv+eenv/xpGR/vi/vP/wtbL/mZPP/0Z2+v69nrr/gd/x/nfD2v/2vLr/9Lq3/vG2tP/lq6j/elJRrjQg + IAoAAAAAAAAAAAAAAAAAAAAAAAAAAGc7OyeOWVnGv4eH/r2Fhf7YlZb+1Y6P/uinpv74v7z+3ay3/seo + w/7srZ/+7LGv/qmyjv63qI7+5Kel/r2GhPZ1S0p1QCcmAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAd0pKOpReXtKxb3D/yXl6/sx5ev/ws7D/6q6s/+Ked/7npFb/2ZiP/ny7gP+OjW/9h1dWr2I7 + OiMAAAAAAAAAAAAAAAAAAAAAAAAAALSCggSqcXIbo2dnN61xcVS/h4eIzp2c2cKWle2OY2OGbz4+Y4xN + Tr6zaWn84Jyb/9aXlv7Ji4r/p25t9INTUqZlPDw3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJJg + YASjcnMorH9/a6h7e4yabm6Df1NTU3VKSgwAAAAAAAAAAAAAAABgNDQgcj8/bntHR4ZnPDxTVTExDQAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////APx//wD4P/8A8D//AOA//wDgH/8A4B//AMAf + /wDAH8EAwA8AAMAAAADAAAAAwAAAAMAAAQDAAAMA4AAHAPgAHwAAAH8AAcH/AP///wD///8A////ACgA + AAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQc + HA5LKSlUNBwcSAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsO + DgV/SkqHm1hY+X5HR90tGRkuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAB4SEhCr2Zm7sZwcf+oYWL5UC8vUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAACnl9fnMRwcf/IcXL/tmxs/mI8PGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAa0NCGbRsbdbMenv/zn5//8R9ff9ySkmCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAA + AAkAAAAAAAAAAItYWDvFfn/y2ZWW/92fn//anJv/jWFgvwAAAB0AAAAAAAAAAAAAAAIzHBwiYjs7a3pM + S6pqQkKjLBoaMwAAAACeZ2dZ05KS/em0tP/vxMT/77u6/8CHhfpmPDyvRysqYlExMV1ySEiGnWdn07qB + gPzLkI//w4iG/HJLS3YAAAAAomloXsyRkf/DoKD/48bG/+jAv//hpKL/vX17/7h/fPu/iYj7z5qZ/+Gw + rv/rvLr/77q3/9ScmuR9U1I+AAAAAJZbWz2ndnbxdG9v/4yCgv+4lJP/77Wy/86erP+6nsH/tsXR/8PH + 0P/4wsD/9b26/+Cppu2peXdiAAAAAQAAAABYKCgHn2lqe6eCguSsgoL90pKS//Cxrv/TrcP/s5y+/8i3 + s/+quab/26mh/82UktSgbm1TBAAAAwAAAACud3cEvYGBC7N6ehyyfHtyt39+3bNub9vLgYH05qak/+Kg + g//OlH39jZR04Zd0aYmDT1EiAAAAAAAAAAAAAAAAr3t7D7aCgki5h4Z8uImJgah+fUltPz8ajU1ORq1s + bI6vdHOgm2RkaYxJUiZgCygCAAAAAAAAAAAAAAAAAAAAAGo9PQF9UVEHcEdHCTodHQIAAAAAAAAAAAAA + AAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP//AADh/wAAwf8AAMH/ + AACB/wAAgfkAAIDAAACAAAAAgAAAAIAAAACAAQAAAAcAAAAPAAAOfwAA//8AAA== + + + \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.Designer.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.Designer.cs new file mode 100644 index 0000000000..c2f10b196f --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.Designer.cs @@ -0,0 +1,165 @@ +namespace BizHawk.Client.EmuHawk +{ + partial class ZXSpectrumPokeMemory + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ZXSpectrumPokeMemory)); + this.OkBtn = new System.Windows.Forms.Button(); + this.CancelBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.numericUpDownAddress = new System.Windows.Forms.NumericUpDown(); + this.label3 = new System.Windows.Forms.Label(); + this.numericUpDownByte = new System.Windows.Forms.NumericUpDown(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownAddress)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownByte)).BeginInit(); + this.SuspendLayout(); + // + // OkBtn + // + this.OkBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkBtn.Location = new System.Drawing.Point(150, 109); + this.OkBtn.Name = "OkBtn"; + this.OkBtn.Size = new System.Drawing.Size(60, 23); + this.OkBtn.TabIndex = 3; + this.OkBtn.Text = "&OK"; + this.OkBtn.UseVisualStyleBackColor = true; + this.OkBtn.Click += new System.EventHandler(this.OkBtn_Click); + // + // CancelBtn + // + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.CancelBtn.Location = new System.Drawing.Point(216, 109); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(60, 23); + this.CancelBtn.TabIndex = 4; + this.CancelBtn.Text = "&Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 14); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(273, 13); + this.label1.TabIndex = 17; + this.label1.Text = "Enter an address to POKE along with a single byte value"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(12, 52); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(93, 13); + this.label4.TabIndex = 27; + this.label4.Text = "Address (0-65535)"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 27); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(266, 13); + this.label2.TabIndex = 29; + this.label2.Text = "(Address values that target ROM space will be ignored)"; + // + // numericUpDownAddress + // + this.numericUpDownAddress.Location = new System.Drawing.Point(15, 69); + this.numericUpDownAddress.Maximum = new decimal(new int[] { + 65535, + 0, + 0, + 0}); + this.numericUpDownAddress.Name = "numericUpDownAddress"; + this.numericUpDownAddress.Size = new System.Drawing.Size(90, 20); + this.numericUpDownAddress.TabIndex = 30; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(123, 52); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(70, 13); + this.label3.TabIndex = 31; + this.label3.Text = "Value (0-255)"; + // + // numericUpDownByte + // + this.numericUpDownByte.Location = new System.Drawing.Point(126, 68); + this.numericUpDownByte.Maximum = new decimal(new int[] { + 255, + 0, + 0, + 0}); + this.numericUpDownByte.Name = "numericUpDownByte"; + this.numericUpDownByte.Size = new System.Drawing.Size(67, 20); + this.numericUpDownByte.TabIndex = 32; + // + // ZXSpectrumPokeMemory + // + this.AcceptButton = this.OkBtn; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.CancelBtn; + this.ClientSize = new System.Drawing.Size(288, 144); + this.Controls.Add(this.numericUpDownByte); + this.Controls.Add(this.label3); + this.Controls.Add(this.numericUpDownAddress); + this.Controls.Add(this.label2); + this.Controls.Add(this.label4); + this.Controls.Add(this.label1); + this.Controls.Add(this.CancelBtn); + this.Controls.Add(this.OkBtn); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.Name = "ZXSpectrumPokeMemory"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Poke Memory"; + + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownAddress)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.numericUpDownByte)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button OkBtn; + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.NumericUpDown numericUpDownAddress; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.NumericUpDown numericUpDownByte; + } +} \ No newline at end of file diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.cs b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.cs new file mode 100644 index 0000000000..de0c799f24 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.cs @@ -0,0 +1,39 @@ +using System; +using System.Linq; +using System.Windows.Forms; + +using BizHawk.Client.Common; +using BizHawk.Emulation.Cores.Computers.SinclairSpectrum; +using System.Text; + +namespace BizHawk.Client.EmuHawk +{ + public partial class ZXSpectrumPokeMemory : Form + { + private ZXSpectrum.ZXSpectrumSettings _settings; + + public ZXSpectrumPokeMemory() + { + InitializeComponent(); + } + + private void OkBtn_Click(object sender, EventArgs e) + { + var speccy = (ZXSpectrum)Global.Emulator; + var addr = (ushort)numericUpDownAddress.Value; + var val = (byte)numericUpDownByte.Value; + + speccy.PokeMemory(addr, val); + + DialogResult = DialogResult.OK; + Close(); + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + GlobalWin.OSD.AddMessage("POKE memory aborted"); + DialogResult = DialogResult.Cancel; + Close(); + } + } +} diff --git a/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.resx b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.resx new file mode 100644 index 0000000000..ca821b54f8 --- /dev/null +++ b/BizHawk.Client.EmuHawk/config/ZXSpectrum/ZXSpectrumPokeMemory.resx @@ -0,0 +1,624 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + AAABAAwAMDAQAAAABABoBgAAxgAAACAgEAAAAAQA6AIAAC4HAAAYGBAAAAAEAOgBAAAWCgAAEBAQAAAA + BAAoAQAA/gsAADAwAAAAAAgAqA4AACYNAAAgIAAAAAAIAKgIAADOGwAAGBgAAAAACADIBgAAdiQAABAQ + AAAAAAgAaAUAAD4rAAAwMAAAAAAgAKglAACmMAAAICAAAAAAIACoEAAATlYAABgYAAAAACAAiAkAAPZm + AAAQEAAAAAAgAGgEAAB+cAAAKAAAADAAAABgAAAAAQAEAAAAAACABAAAAAAAAAAAAAAQAAAAEAAAAAAA + AAAAAIAAAIAAAACAgACAAAAAgACAAICAAACAgIAAwMDAAAAA/wAA/wAAAP//AP8AAAD/AP8A//8AAP// + /wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAHR3AAAAAAAAAAAAAAAAAAAAAAAAAAAAdHdEcAAAAAAAAAAAAAAAAA + AAAAAAAAAHd0d3QAAAAAAAAAAAAAAAAAAAAAAAAAAEd8d3UAAAAAAAAAAAAAAAAAAAAAAAAAB3yHfHZw + AAAAAAAAAAAAAAAAAAAAAAAAd3fIyHVwAAAAAAAAAAAAAAAAAAAAAAAAfHh3jIxwAAAAAAAAAAAAAAAA + AAAAAAAHd8jIyHdgAAAAAAAAAAAAAAAAAAAAAAAHd4yHfIdAAAAAAAAAAAAAAAAAAAAAAAAHyMjIyMhQ + AAAAAAAAAAAAAAAAAAAAAAB3d3eMh4dgAAAAAAAAAAAAAAAAAAAAAAB8jIyIfIdQAAAAAAAAAAAAAAAA + AAAAAAB3h4jIiMh3AAAAAAAAAAAAAAAAAAAAAAB8jIeHeIjHAAAAAAAAAAAAAAAAAAAAAAeIiHh4eMiE + AAAAAAAAAAAAB0dHcAAAAAd8h4eIiIiHcAAAAAAAAAB0d3d3RwAAAAeIeIiIiIh3RwAAAAAAAHR3d8h3 + dAAAAAfIh4iIiHiIx0cAAAAAdHh3eIeHhwAAAAeHiIiIiIiId3R3dHR0eHd4h4eHhAAAAAd4eIiIiIiH + x3d2d3eId4iIiIiIhwAAAAd4eIiI+IiIh3d3eHh3iIiIiIeHwAAAAAfIjHeIiIiIyIeHh4iIiIiIiIiI + cAAAAAeIQ0R3h3iIiMiIiIiIiIiIiIiEAAAAAAfIR3d3d0iIiIh4iIeIiIiIiHhAAAAAAAB4d3d3SHiI + h4fTiIi3iIiIeIwAAAAAAAB3h4d3eIeIiHiJiIuIiIh4jHAAAAAAAAAHyId3h3h4iIh4iIiIiIiHeAAA + AAAAAAAAB8iMiMjIiIiIh4h3aMjHAAAAAAAAAAAAAAdYyIeIiIiMjId6d4eAAAAAAAAAAAAAAAAHdsjH + eIeH6MiId3AAAAAAAAAAAAAAAIiIh4V8jIh4eIfHcAAAAAAAAAAAAACIiIh3AAAHd3h3fHcAAAAAAAAA + AAAAAAiIjHgAAAAAAHx8eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAD///////8AAP///////wAA////////AAD///////8AAP///////wAA//////// + AAD///////8AAP///////wAA//h/////AAD/4D////8AAP/AP////wAA/8A/////AAD/gB////8AAP8A + H////wAA/wAf////AAD+AB////8AAP4AH////wAA/gAf////AAD8AB////8AAPwAH////wAA/AAP//// + AAD8AA////8AAPgAD//+BwAA+AAH//ADAAD4AAP/wAMAAPgAAP8AAwAA+AAAAAADAAD4AAAAAAMAAPgA + AAAABwAA+AAAAAAHAAD4AAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAH8AAP4AAAAA/wAA/4AAAAP/ + AAD/4AAAB/8AAP/4AAAf/wAA/8AAAH//AAD8A+AD//8AAPgP/A///wAA////////AAD///////8AAP// + /////wAA////////AAD///////8AAP///////wAA////////AAAoAAAAIAAAAEAAAAABAAQAAAAAAAAC + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdwAAAAAAAAAAAAAAAA + AAd0dAAAAAAAAAAAAAAAAAB3x3cAAAAAAAAAAAAAAAAAd3fHcAAAAAAAAAAAAAAAB3yMh3AAAAAAAAAA + AAAAAAfIeMdwAAAAAAAAAAAAAAAHjIyHQAAAAAAAAAAAAAAAfId4yHAAAAAAAAAAAAAAAHjIyIdQAAAA + AAAAAAAAAAB3iId4YAAAAAAAAAdwAAAAjIiIiIUAAAAAAHd3dAAAB4iIiHh8cAAAAHd3x4dwAAd4iIiI + h3Z3d3R3yIh4cAAHh4iIiIfHd3d4iIiIh3AAB3jHiIiIiHeHiIiIiIwAAAh3dXh4iMiIiIiIiIhwAAAA + yGd0d4iIeIi4iIiMAAAAAIeHd4iIh32IiIiIcAAAAAAAd4jIyIiIiHeHyAAAAAAAAAB3h4iIh8h3dwAA + AAAAAAAIh8fIh4eIaAAAAAAAAACIiHAAB8jIyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//////// + ////////////////////n////g////wP///8B///+Af///gH///4B///8Af///AH///wB//n8AP/A+AB + /AHgAAAB4AAAAeAAAAPgAAAH8AAAD/AAAB/8AAA//wAA//4AA//weA////////////////////////// + //8oAAAAGAAAADAAAAABAAQAAAAAACABAAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAA + AACAAIAAgIAAAICAgADAwMAAAAD/AAD/AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHRwAAAAAAAAAAAAB3dAAAAAAAAAAAAA + d8dwAAAAAAAAAAAAfId3AAAAAAAAAAAHeMjHAAAAAAAAAAAHyHh3AAAAAAAAAAAHh3eEAAAAAAAAAAAI + yIiHAAAAAHd2cAAIiIiIQAAAd3d4UACHiIiId3d3eHiIcACHh4iIyHeHiIiIcAAIR3d4iIiIiIiMAAAH + d3eIh3iIiIhwAAAAeMh4iIiHiMAAAAAAAHfIiMh4aAAAAAAAiIgHyIfIAAAAAAAIgAAAAIAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wD///8A////AP///wD8f/8A+H//APB/ + /wDwP/8A4D//AOA//wDgP/8A4D/BAOAfAQDAAAEAwAABAOAAAwDgAAcA8AAfAPwAPwDwgP8A5/f/AP// + /wD///8A////ACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACA + AAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD//wD/AAAA/wD/AP//AAD///8AAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAd1AAAAAAAAB8cAAAAAAAB4eAAAAAAAAHyMgAAAAAAAiIhwAAAHcACI + iHcAd3hwAIz4jIeIiIAAd3eIiIiIAACHeIiIiHAAAACMeMh4AAAAiAAIgAAAAAAAAAAAAAAAAAAAAAAA + AAD//wAA//8AAP//AADj/wAA4/8AAMP/AADB/wAAwfkAAMDBAADAAQAAwAMAAMAHAADwDwAAzn8AAP// + AAD//wAAKAAAADAAAABgAAAAAQAIAAAAAAAACQAAAAAAAAAAAAAAAQAAAAEAAAAAAAA9OzsAZD8/AGg8 + PABtPj4AQkNDAEZIRwBWQkIAV0REAF5AQABbRkYAVklJAFxPTwBTU1MAXFJSAF5ZWQBkQEAAYUREAGZF + RQBqQkEAYEtLAGNPTwBwQUEAfUZGAHJKSgB2SUkAfU9PAGBRUQBgVFQAZlZWAGZYWABqWVkAclZWAHpU + VAB9W1oAbmJiAGtoaABtaWkAcWdnAHdnZwB8Y2MAe2pqAHJxcQB+dHQAd3l5AHl6egCGT08AiU9PAIFP + UACGU1MAjVFRAIlWVgCMV1cAg1xbAIxaWQCQUlIAlVJSAJFXVgCXVVUAmVVVAJZaWQCSXV0AlV9eAJpZ + WgCeW1sAml5eAKBZWgCgXFwAql9fAIRmZQCIZWQAhWtrAI5ragCTYmEAnGBhAJ9kYwCaZmYAk25uAJ1s + awCFdHQAiXd3AIt+fgCWd3cAmHR0AJV5eQCbfHwAo2JhAKZhYQChZWUApGVkAKplZACsZGQAqmhnAKZr + agCnbGsAqmloAKlubQCsbW0AtGZnALhsbACxb3AAv29wAKVxcACrc3IAr35+ALN0cwC5c3MAvXBxALR4 + dgC1fHsAunt6AMNtbgDGb3AAw3FyAMZwcQDGdXUAyHR1AMp3eADBeXkAxnt7AMB/fgDLensANLBSAEWf + TgBBtFwAPMdnADHkdgDciiIAvoF/AISrdwDln0sA35lhAN2XfADgmmEA8LdlAO61cAArWPIALWT+AEh5 + +gDOf4AAfoCAAHiA1ABZv9wAZrnUAGK+2ABxnv4Ad6P/ADPX/QBw0OcAW+D7AIKEgwCPgoIAjI2NAJuC + ggCUiIgAmYqKAJGSkgCjhIQAqoKCAKKLiwC+hIMAsoqKALaSgQCum5sAsZubALqqlQCdgr4Ar6ytALGh + oAC6pKQAwoSDAMyBggDGiIYAyYiHAMWMigDMjIoA0ISFANKHiADUjIwA2Y6NAMCUjQDIk44A0JCPANaP + kADHlZQAzpSSAMScmwDUkpIA2ZSVANWYlgDampcA2ZeYANWcnADam5sA4p2cAMChjwDeoJ4A5aCFAOaj + jQDlpJoA2p6hAMOkowDOoaEAy62tANegoADdoqEA2aGpANGsrwDdq6kAwbG4ANGysQDdtLQA2ri3AOGk + owDjqKYA66ylAOGnqADjq6oA6a2rAOOwrwDssK4A5K+wAOaztADttLIA57i2AO24tgDmurgA6rq6APC1 + swDyuLYA9Ly5APi+uwD1wL0A+cC9AKKMwACkk8QAqprMALSayACptsEAlaDkAOy/wACRxtQAgOv9AJnr + 9wDEwsoA5sbGAOzCwgDuyMcA7MzMAPPEwgDxy8oA9dPTAPja2gAAAAAAAAAAAP///wAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAoIJQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAACYXODs4BCUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + KTNDQ0M7OAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALllbYmJZQBcAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYYWNwcHBwWy8mAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFLanBwcHBwYz0eAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAABpqcHBwcHBwZVkUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl11w + cHBwcHBwcGcSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIXdwcHBwcHBwcGkSAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPXBwcHBwcHBwd2wYAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAACXbnBwdXB5dXl0eW4hAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAid3R5eXl5eXl5q6wzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9eXV5 + i7CxsbGxsblLKgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABndYuwsbm8uby5vMFnHgAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJt3q7G3vMHB1cLBwdWuEgAAAAAAAAAAAAAAAAAA + AAAAAAAeEhMSCiUAAAAAAAAAAEexsbm/1dXZ2dnZ1da5ZgwAAAAAAAAAAAAAAAAAAAAjEjNZaW5qXRMl + AAAAAAAAADW5s7/V2N7i4uLi3dzZrQQPAAAAAAAAAAAAAAAAHxhZbm5uaWltd6ASAAAAAAAAAEmzvMLZ + 3uP29/fw4uTkuUAWCy0AAAAAAAAAAB4YYXd3gG13vbm5vb8zAAAAAAAAAE6xwdXd4/b6+/r38OTl1Vlc + OAMIFAweFBQSM2mtrYB3vdXT0NXExNU1AAAAAAAAAE65wtXe8Pr7/Pz79+fn1WphZ25pXV1mbHetrXd3 + tdXT4vXw49nZ3NYgAAAAAAAAAEu3wdje9vv7/Pz79+fn34B3d2xtoHeud66uudXT4vD39/Dj49zk5G0A + AAAAAAAAAD2xwcwoH0/L/Pukyenp5K27u7m5uczM0Nve4vb3+vr56OPl5eXl1igAAAAAAAAAADWxwQgB + BQYNmveZK/Dp6cG/wcTV2eP3+vr6+/r6+ejm5ufn5+nkIgAAAAAAAAAAAJmruR4sjC2WLFCdDd3p6dXW + 1tXI3vn67pCO9Ojp6efo5+fm59wiAAAAAAAAAAAAAABLsZ0FmC0qKgHMRcjp6dzc1Y2KiO3RlfKTj+np + 5ubm5eXk1SIAAAAAAAAAAAAAAACdab/Lp5aWnEfV1cHm6ebk6pGSiabZ8fOU0uXl5eTk3NyuRQAAAAAA + AAAAAAAAAAAAn0ux0KFTaMHBv7nC6efp3Ovv7OTm3OPl3Nzc3NfW1U6fAAAAAAAAAAAAAAAAAAAAAABF + Wa25t7yxs7Gw5+fn5Obk18XG3NyBfHvD1cSgNQAAAAAAAAAAAAAAAAAAAAAAAAAAAFUzarGwsHl5sefn + 39zEgoZ/hL19fnqirj2jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj09ZXV0cLzn3NXChYeDub+1pbQ9 + VQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0rXj+rpInTBDcHCz5NW/ucG5u7GAM1QAAAAAAAAAAAAAAAAA + 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 + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACXjU1h6NnZv/Fc3T/x3Bx/8dw + cf/HcHH/x3Bx/8dwcf/HcHH/w3Jz/6ZoZ/9ZMzPTAQAAPQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyFxccektK0b12 + dv/HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xXR0/69wb/9jOjneBwMDSQAAAAUAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AABNKSlNlmBf9sh3d//HcHH/x3Bx/8dwcf/HcHH/x3Bx/8dwcf/HcHH/xnd3/7Z4d/9sQUDnDgcHVQAA + AAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAABkOjqKsXFw/8lyc//HcXL/yHJz/8l0df/JdXb/yXV2/8l1dv/JdHX/ynt7/7+B + f/94SknvFgsLZQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAACILCxB7TUzDwXd3/8lyc//KdXb/y3h5/8x7fP/NfX7/zX5+/819 + fv/NfH3/zoOC/8iJiP+GVVX3Hg8QegAAABIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMiIi+SXl3oynp7/8t4ef/NfX7/z4GC/9GE + hf/Sh4j/04iJ/9KIiP/Rhof/04uK/8+RkP+XY2L9KxcXlwAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAABwAA + AA0AAAAPAAAACwAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFUvL1enbW37zn5+/85/ + gP/Rhob/1IuM/9aPkP/XkpP/2JOU/9iTlP/XkZH/15OT/9eZl/+rdHP/QSUlvAAAADwAAAAFAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAACQAA + ABgAAAAvAgEBSwcDA2EFAgJoAAAAWAAAADYAAAARAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGU8 + O4W5eXn/0IKD/9KIif/Wj5D/2ZWW/9ubm//dnp//3qCg/92foP/cnZ3/3Jyc/9+in//CiYf/Zj8/4wYC + AnAAAAAbAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAA + AA4AAAAnCQQEUCISEoQ+IiKzVzEx1mU6OuZiOTnmRigo0hgNDZsAAABMAAAAEAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAABnVJSK/HhIP/04eI/9aQkf/amJn/3qCh/+Gmp//jq6v/5Kyt/+OsrP/iqan/4aal/+ap + p//Umpj/nmxr/C8ZGboAAABXAAAAGAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAIAAAAOAQAALRkNDWY+IiKpZDo63YZRUfigZGP/sHBv/7V0c/+xcnH/oWZm/2k+PvEfEBCcAAAAMQAA + 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/movie/SubtitleMaker.Designer.cs b/BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs index df23542d0d..f9640b8f53 100644 --- a/BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs +++ b/BizHawk.Client.EmuHawk/movie/SubtitleMaker.Designer.cs @@ -179,7 +179,7 @@ // this.FrameNumeric.Location = new System.Drawing.Point(78, 19); this.FrameNumeric.Maximum = new decimal(new int[] { - 999999, + 2147483647, 0, 0, 0}); diff --git a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs index a509b57639..1b6c98b9e4 100644 --- a/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs +++ b/BizHawk.Client.EmuHawk/tools/HexEditor/HexEditor.cs @@ -235,6 +235,28 @@ namespace BizHawk.Client.EmuHawk return str.Select(Convert.ToByte).ToArray(); } + public byte[] ConvertHexStringToByteArray(string str) + { + if (string.IsNullOrWhiteSpace(str)) { + return new byte[0]; + } + + // TODO: Better method of handling this? + if (str.Length % 2 == 1) + { + str += "0"; + } + + byte[] bytes = new byte[str.Length / 2]; + + for (int i = 0; i < str.Length; i += 2) + { + bytes[i / 2] = Convert.ToByte(str.Substring(i, 2), 16); + } + + return bytes; + } + public void FindNext(string value, bool wrap) { long found = -1; @@ -261,16 +283,20 @@ namespace BizHawk.Client.EmuHawk startByte = _addressHighlighted + DataSize; } + byte[] searchBytes = ConvertHexStringToByteArray(search); for (var i = startByte; i < (_domain.Size - numByte); i++) { - var ramblock = new StringBuilder(); + bool differenceFound = false; for (var j = 0; j < numByte; j++) { - ramblock.Append(string.Format("{0:X2}", (int)_domain.PeekByte(i + j))); + if (_domain.PeekByte(i + j) != searchBytes[j]) + { + differenceFound = true; + break; + } } - var block = ramblock.ToString().ToUpper(); - if (search == block) + if (!differenceFound) { found = i; break; @@ -313,16 +339,19 @@ namespace BizHawk.Client.EmuHawk startByte = _addressHighlighted - 1; } + byte[] searchBytes = ConvertHexStringToByteArray(search); for (var i = startByte; i >= 0; i--) { - var ramblock = new StringBuilder(); + bool differenceFound = false; for (var j = 0; j < numByte; j++) { - ramblock.Append(string.Format("{0:X2}", (int)_domain.PeekByte(i + j))); + if (_domain.PeekByte(i + j) != searchBytes[j]) { + differenceFound = true; + break; + } } - var block = ramblock.ToString().ToUpper(); - if (search == block) + if (!differenceFound) { found = i; break; @@ -1242,6 +1271,11 @@ namespace BizHawk.Client.EmuHawk private bool LoadTable(string path) { + if (string.IsNullOrWhiteSpace(path)) + { + return false; + } + var file = new FileInfo(path); if (!file.Exists) { diff --git a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs index e73c85da03..2fe833b845 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/Libraries/EmuLuaLibrary.Forms.cs @@ -183,7 +183,7 @@ namespace BizHawk.Client.EmuHawk { for (var i = _luaForms.Count - 1; i >= 0; i--) { - _luaForms.ElementAt(i).Close(); + _luaForms[i].Close(); } } diff --git a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs index 1b0994ffd4..5c03a82218 100644 --- a/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs +++ b/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs @@ -260,22 +260,8 @@ namespace BizHawk.Client.EmuHawk if (!Global.Config.DisableLuaScriptsOnLoad) { - try - { - LuaSandbox.Sandbox(null, () => - { - luaFile.Thread = LuaImp.SpawnCoroutine(pathToLoad); - LuaSandbox.CreateSandbox(luaFile.Thread, Path.GetDirectoryName(pathToLoad)); - luaFile.State = LuaFile.RunState.Running; - }, () => - { - luaFile.State = LuaFile.RunState.Disabled; - }); - } - catch (Exception e) - { - MessageBox.Show(e.ToString()); - } + luaFile.State = LuaFile.RunState.Running; + EnableLuaFile(luaFile); } else { @@ -815,46 +801,18 @@ namespace BizHawk.Client.EmuHawk private void ToggleScriptMenuItem_Click(object sender, EventArgs e) { var files = !SelectedFiles.Any() && Global.Config.ToggleAllIfNoneSelected ? LuaImp.ScriptList : SelectedFiles; - foreach (var item in files) + foreach (var file in files) { - item.Toggle(); + file.Toggle(); - if (item.Enabled && item.Thread == null) + if (file.Enabled && file.Thread == null) { - try - { - LuaSandbox.Sandbox(null, () => - { - string pathToLoad = Path.IsPathRooted(item.Path) - ? item.Path - : PathManager.MakeProgramRelativePath(item.Path); - - item.Thread = LuaImp.SpawnCoroutine(pathToLoad); - LuaSandbox.CreateSandbox(item.Thread, Path.GetDirectoryName(pathToLoad)); - }, () => - { - item.State = LuaFile.RunState.Disabled; - }); - - // Shenanigans - // We want any gui.text messages from a script to immediately update even when paused - GlobalWin.OSD.ClearGUIText(); - GlobalWin.Tools.UpdateToolsAfter(); - LuaImp.EndLuaDrawing(); - LuaImp.StartLuaDrawing(); - } - catch (IOException) - { - ConsoleLog("Unable to access file " + item.Path); - } - catch (Exception ex) - { - MessageBox.Show(ex.ToString()); - } + EnableLuaFile(file); } - else if (!item.Enabled && item.Thread != null) + + else if (!file.Enabled && file.Thread != null) { - LuaImp.CallExitEvent(item.Thread); + LuaImp.CallExitEvent(file.Thread); var items = SelectedItems.ToList(); foreach (var sitem in items) @@ -869,8 +827,8 @@ namespace BizHawk.Client.EmuHawk UpdateRegisteredFunctionsDialog(); } - LuaImp.CallExitEvent(item.Thread); - item.Stop(); + LuaImp.CallExitEvent(file.Thread); + file.Stop(); if (Global.Config.RemoveRegisteredFunctionsOnToggle) { LuaImp.RegisteredFunctions.ClearAll(); @@ -883,6 +841,40 @@ namespace BizHawk.Client.EmuHawk LuaListView.Refresh(); } + private void EnableLuaFile(LuaFile item) + { + try + { + LuaSandbox.Sandbox(null, () => + { + string pathToLoad = Path.IsPathRooted(item.Path) + ? item.Path + : PathManager.MakeProgramRelativePath(item.Path); + + item.Thread = LuaImp.SpawnCoroutine(pathToLoad); + LuaSandbox.CreateSandbox(item.Thread, Path.GetDirectoryName(pathToLoad)); + }, () => + { + item.State = LuaFile.RunState.Disabled; + }); + + // Shenanigans + // We want any gui.text messages from a script to immediately update even when paused + GlobalWin.OSD.ClearGUIText(); + GlobalWin.Tools.UpdateToolsAfter(); + LuaImp.EndLuaDrawing(); + LuaImp.StartLuaDrawing(); + } + catch (IOException) + { + ConsoleLog("Unable to access file " + item.Path); + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString()); + } + } + private void PauseScriptMenuItem_Click(object sender, EventArgs e) { SelectedFiles.ToList().ForEach(x => x.TogglePause()); diff --git a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs index 3c1e2f2e9e..5616d71e02 100644 --- a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs +++ b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs @@ -143,7 +143,8 @@ "GB", "PCFX", "PSX", - "SAT"}); + "SAT", + "ZXSpectrum"}); this.SystemDropDown.Location = new System.Drawing.Point(425, 75); this.SystemDropDown.Name = "SystemDropDown"; this.SystemDropDown.Size = new System.Drawing.Size(69, 21); diff --git a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs index aed7b087ef..3f5e344729 100644 --- a/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs +++ b/BizHawk.Client.EmuHawk/tools/TAStudio/BookmarksBranchesBox.cs @@ -157,6 +157,7 @@ namespace BizHawk.Client.EmuHawk BranchView.RowCount = Movie.BranchCount; Movie.CurrentBranch = Movie.BranchCount - 1; BranchView.ScrollToIndex(Movie.CurrentBranch); + BranchView.SelectRow(Movie.CurrentBranch, true); BranchView.Refresh(); Tastudio.RefreshDialog(); } @@ -626,7 +627,11 @@ namespace BizHawk.Client.EmuHawk { if (e.NewCell != null && e.NewCell.IsDataCell && e.OldCell.RowIndex.Value < Movie.BranchCount) { + int currenthash = Movie.BranchHashByIndex(Movie.CurrentBranch); Movie.SwapBranches(e.OldCell.RowIndex.Value, e.NewCell.RowIndex.Value); + int newindex = Movie.BranchIndexByHash(currenthash); + Movie.CurrentBranch = newindex; + BranchView.SelectRow(newindex, true); } } diff --git a/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs new file mode 100644 index 0000000000..cdbf9228c0 --- /dev/null +++ b/BizHawk.Client.EmuHawk/tools/VirtualPads/schema/ZXSpectrumSchema.cs @@ -0,0 +1,260 @@ +using System.Collections.Generic; +using System.Drawing; + +using BizHawk.Emulation.Common; +using System.Linq; + +namespace BizHawk.Client.EmuHawk +{ + [Schema("ZXSpectrum")] + class ZXSpectrumSchema : IVirtualPadSchema + { + public IEnumerable GetPadSchemas(IEmulator core) + { + yield return Joystick(1); + yield return Joystick(2); + yield return Joystick(3); + yield return Keyboard(); + //yield return TapeDevice(); + } + + private static PadSchema Joystick(int controller) + { + return new PadSchema + { + DisplayName = "Joystick " + controller, + IsConsole = false, + DefaultSize = new Size(174, 74), + MaxSize = new Size(174, 74), + Buttons = new[] + { + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Up", + DisplayName = "", + Icon = Properties.Resources.BlueUp, + Location = new Point(23, 15), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Down", + DisplayName = "", + Icon = Properties.Resources.BlueDown, + Location = new Point(23, 36), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Left", + DisplayName = "", + Icon = Properties.Resources.Back, + Location = new Point(2, 24), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Right", + DisplayName = "", + Icon = Properties.Resources.Forward, + Location = new Point(44, 24), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "P" + controller + " Button", + DisplayName = "B", + Location = new Point(124, 24), + Type = PadSchema.PadInputType.Boolean + } + } + }; + } + + private class ButtonLayout + { + public string Name { get; set; } + public string DisName { get; set; } + public double WidthFactor { get; set; } + public int Row { get; set; } + public bool IsActive = true; + } + + private static PadSchema Keyboard() + { + List bls = new List + { + new ButtonLayout { Name = "Key True Video", DisName = "TV", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Inv Video", DisName = "IV", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 1", DisName = "1", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 2", DisName = "2", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 3", DisName = "3", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 4", DisName = "4", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 5", DisName = "5", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 6", DisName = "6", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 7", DisName = "7", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 8", DisName = "8", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 9", DisName = "9", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key 0", DisName = "0", Row = 0, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Break", DisName = "BREAK", Row = 0, WidthFactor = 1.5 }, + + new ButtonLayout { Name = "Key Delete", DisName = "DEL", Row = 1, WidthFactor = 1.5 }, + new ButtonLayout { Name = "Key Graph", DisName = "GR", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Q", DisName = "Q", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key W", DisName = "W", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key E", DisName = "E", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key R", DisName = "R", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key T", DisName = "T", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Y", DisName = "Y", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key U", DisName = "U", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key I", DisName = "I", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key O", DisName = "O", Row = 1, WidthFactor = 1 }, + new ButtonLayout { Name = "Key P", DisName = "P", Row = 1, WidthFactor = 1 }, + + new ButtonLayout { Name = "Key Extend Mode", DisName = "EM", Row = 2, WidthFactor = 1.5 }, + new ButtonLayout { Name = "Key Edit", DisName = "ED", Row = 2, WidthFactor = 1.25}, + new ButtonLayout { Name = "Key A", DisName = "A", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key S", DisName = "S", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key D", DisName = "D", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key F", DisName = "F", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key G", DisName = "G", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key H", DisName = "H", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key J", DisName = "J", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key K", DisName = "K", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key L", DisName = "L", Row = 2, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Return", DisName = "ENTER", Row = 2, WidthFactor = 1.75 }, + + new ButtonLayout { Name = "Key Caps Shift", DisName = "CAPS-S", Row = 3, WidthFactor = 2.25 }, + new ButtonLayout { Name = "Key Caps Lock", DisName = "CL", Row = 3, WidthFactor = 1}, + new ButtonLayout { Name = "Key Z", DisName = "Z", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key X", DisName = "X", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key C", DisName = "C", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key V", DisName = "V", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key B", DisName = "B", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key N", DisName = "N", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key M", DisName = "M", Row = 3, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Period", DisName = ".", Row = 3, WidthFactor = 1}, + new ButtonLayout { Name = "Key Caps Shift", DisName = "CAPS-S", Row = 3, WidthFactor = 2.25 }, + + new ButtonLayout { Name = "Key Symbol Shift", DisName = "SS", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Semi-Colon", DisName = ";", Row = 4, WidthFactor = 1}, + new ButtonLayout { Name = "Key Quote", DisName = "\"", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Left Cursor", DisName = "←", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Right Cursor", DisName = "→", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Space", DisName = "SPACE", Row = 4, WidthFactor = 4.5 }, + new ButtonLayout { Name = "Key Up Cursor", DisName = "↑", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Down Cursor", DisName = "↓", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Comma", DisName = ",", Row = 4, WidthFactor = 1 }, + new ButtonLayout { Name = "Key Symbol Shift", DisName = "SS", Row = 4, WidthFactor = 1 }, + }; + + PadSchema ps = new PadSchema + { + DisplayName = "Keyboard", + IsConsole = false, + DefaultSize = new Size(500, 170) + }; + + List btns = new List(); + + int rowHeight = 29; //24 + int stdButtonWidth = 29; //24 + int yPos = 18; + int xPos = 22; + int currRow = 0; + + foreach (var b in bls) + { + if (b.Row > currRow) + { + currRow++; + yPos += rowHeight; + xPos = 22; + } + + int txtLength = b.DisName.Length; + int btnSize = System.Convert.ToInt32((double)stdButtonWidth * b.WidthFactor); + + + string disp = b.DisName; + if (txtLength == 1) + disp = " " + disp; + + switch(b.DisName) + { + case "SPACE": disp = " " + disp + " "; break; + case "I": disp = " " + disp + " "; break; + case "W": disp = b.DisName; break; + } + + + if (b.IsActive) + { + PadSchema.ButtonSchema btn = new PadSchema.ButtonSchema(); + btn.Name = b.Name; + btn.DisplayName = disp; + btn.Location = new Point(xPos, yPos); + btn.Type = PadSchema.PadInputType.Boolean; + btns.Add(btn); + } + + xPos += btnSize; + } + + ps.Buttons = btns.ToArray(); + return ps; + } + + private static PadSchema TapeDevice() + { + return new PadSchema + { + DisplayName = "DATACORDER", + IsConsole = false, + DefaultSize = new Size(174, 74), + MaxSize = new Size(174, 74), + Buttons = new[] + { + new PadSchema.ButtonSchema + { + Name = "Play Tape", + Icon = Properties.Resources.Play, + Location = new Point(23, 22), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "Stop Tape", + Icon = Properties.Resources.Stop, + Location = new Point(53, 22), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "RTZ Tape", + Icon = Properties.Resources.BackMore, + Location = new Point(83, 22), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "Insert Next Tape", + DisplayName = "NEXT TAPE", + //Icon = Properties.Resources.MoveRight, + Location = new Point(23, 52), + Type = PadSchema.PadInputType.Boolean + }, + new PadSchema.ButtonSchema + { + Name = "Insert Previous Tape", + DisplayName = "PREV TAPE", + //Icon = Properties.Resources.MoveLeft, + Location = new Point(100, 52), + Type = PadSchema.PadInputType.Boolean + }, + + } + }; + } + } +} diff --git a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs index a208731d81..f480085983 100644 --- a/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs +++ b/BizHawk.Client.EmuHawk/tools/Watch/RamWatch.cs @@ -855,6 +855,7 @@ namespace BizHawk.Client.EmuHawk private void ClearChangeCountsMenuItem_Click(object sender, EventArgs e) { _watches.ClearChangeCounts(); + UpdateValues(); } private void MoveUpMenuItem_Click(object sender, EventArgs e) diff --git a/BizHawk.Client.MultiHawk/BizHawk.Client.MultiHawk.csproj.DotSettings b/BizHawk.Client.MultiHawk/BizHawk.Client.MultiHawk.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Client.MultiHawk/BizHawk.Client.MultiHawk.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Common/BizHawk.Common.csproj.DotSettings b/BizHawk.Common/BizHawk.Common.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Common/BizHawk.Common.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs b/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs index 084a110174..c28c1f9c8d 100644 --- a/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs +++ b/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs @@ -33,8 +33,6 @@ namespace BizHawk.Emulation.Common private readonly ObservableCollection _writes = new ObservableCollection(); private readonly ObservableCollection _execs = new ObservableCollection(); - private bool _empty = true; - private bool _hasReads; private bool _hasWrites; private bool _hasExecutes; @@ -54,24 +52,19 @@ namespace BizHawk.Emulation.Common { case MemoryCallbackType.Execute: _execs.Add(callback); - _hasExecutes = true; break; case MemoryCallbackType.Read: _reads.Add(callback); - _hasReads = true; break; case MemoryCallbackType.Write: _writes.Add(callback); - _hasWrites = true; break; } - if (_empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = false; } private static void Call(ObservableCollection cbs, uint addr, string scope) @@ -130,11 +123,17 @@ namespace BizHawk.Emulation.Common return _execs.Where(e => e.Scope == scope).Any(); } - private void UpdateHasVariables() + private bool UpdateHasVariables() { + bool hadReads = _hasReads; + bool hadWrites = _hasWrites; + bool hadExecutes = _hasExecutes; + _hasReads = _reads.Count > 0; _hasWrites = _writes.Count > 0; _hasExecutes = _execs.Count > 0; + + return (_hasReads != hadReads || _hasWrites != hadWrites || _hasExecutes != hadExecutes); } private int RemoveInternal(Action action) @@ -158,8 +157,6 @@ namespace BizHawk.Emulation.Common _execs.Remove(exec); } - UpdateHasVariables(); - return readsToRemove.Count + writesToRemove.Count + execsToRemove.Count; } @@ -167,13 +164,10 @@ namespace BizHawk.Emulation.Common { if (RemoveInternal(action) > 0) { - bool newEmpty = !HasReads && !HasWrites && !HasExecutes; - if (newEmpty != _empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = newEmpty; } } @@ -187,16 +181,11 @@ namespace BizHawk.Emulation.Common if (changed) { - bool newEmpty = !HasReads && !HasWrites && !HasExecutes; - if (newEmpty != _empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = newEmpty; } - - UpdateHasVariables(); } public void Clear() @@ -217,14 +206,10 @@ namespace BizHawk.Emulation.Common _execs.RemoveAt(i); } - if (!_empty) + if (UpdateHasVariables()) { Changes(); } - - _empty = true; - - UpdateHasVariables(); } public delegate void ActiveChangedEventHandler(); diff --git a/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj.DotSettings b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.Emulation.Common/Database/Database.cs b/BizHawk.Emulation.Common/Database/Database.cs index df1a2562c7..5fcf3ce89f 100644 --- a/BizHawk.Emulation.Common/Database/Database.cs +++ b/BizHawk.Emulation.Common/Database/Database.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; using BizHawk.Common.BufferExtensions; +using System.Linq; namespace BizHawk.Emulation.Common { @@ -298,12 +299,26 @@ namespace BizHawk.Emulation.Common case ".D64": case ".T64": case ".G64": - case ".CRT": - case ".TAP": + case ".CRT": game.System = "C64"; break; - case ".Z64": + case ".TZX": + case ".PZX": + case ".CSW": + case ".WAV": + game.System = "ZXSpectrum"; + break; + + case ".TAP": + byte[] head = romData.Take(8).ToArray(); + if (System.Text.Encoding.Default.GetString(head).Contains("C64-TAPE")) + game.System = "C64"; + else + game.System = "ZXSpectrum"; + break; + + case ".Z64": case ".V64": case ".N64": game.System = "N64"; @@ -327,6 +342,15 @@ namespace BizHawk.Emulation.Common break; case ".DSK": + byte[] head2 = romData.Take(20).ToArray(); + string ident = System.Text.Encoding.Default.GetString(head2); + if (ident.ToUpper().Contains("EXTENDED CPC DSK") || + ident.ToUpper().Contains("MV - CPC")) + game.System = "ZXSpectrum"; + else + game.System = "AppleII"; + break; + case ".PO": case ".DO": game.System = "AppleII"; diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 45e30fc9cb..e279f00c5d 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -50,9 +50,17 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("AB16F56989B27D89BABE5F89C5A8CB3DA71A82F0", 16384, "C64", "Drive1541", "drive-1541.bin", "1541 Disk Drive Rom"); FirmwareAndOption("D3B78C3DBAC55F5199F33F3FE0036439811F7FB3", 16384, "C64", "Drive1541II", "drive-1541ii.bin", "1541-II Disk Drive Rom"); - // for saturn, we think any bios region can pretty much run any iso - // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region - var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); + // ZX Spectrum + /* These are now shipped with bizhawk + FirmwareAndOption("5EA7C2B824672E914525D1D5C419D71B84A426A2", 16384, "ZXSpectrum", "48ROM", "48.ROM", "Spectrum 48K ROM"); + FirmwareAndOption("16375D42EA109B47EDDED7A16028DE7FDB3013A1", 32768, "ZXSpectrum", "128ROM", "128.ROM", "Spectrum 128K ROM"); + FirmwareAndOption("8CAFB292AF58617907B9E6B9093D3588A75849B8", 32768, "ZXSpectrum", "PLUS2ROM", "PLUS2.ROM", "Spectrum 128K +2 ROM"); + FirmwareAndOption("929BF1A5E5687EBD8D7245F9B513A596C0EC21A4", 65536, "ZXSpectrum", "PLUS3ROM", "PLUS3.ROM", "Spectrum 128K +3 ROM"); + */ + + // for saturn, we think any bios region can pretty much run any iso + // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region + var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); var ss_100_ue = File("FAA8EA183A6D7BBE5D4E03BB1332519800D3FBC3", 524288, "saturn-1.00-(U+E).bin", "Bios v1.00 (U+E)"); var ss_100a_ue = File("3BB41FEB82838AB9A35601AC666DE5AACFD17A58", 524288, "saturn-1.00a-(U+E).bin", "Bios v1.00a (U+E)"); // ?? is this size correct? var ss_101_j = File("DF94C5B4D47EB3CC404D88B33A8FDA237EAF4720", 524288, "saturn-1.01-(J).bin", "Bios v1.01 (J)"); // ?? is this size correct? diff --git a/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs b/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs index 69d140890a..e1655b888a 100644 --- a/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs +++ b/BizHawk.Emulation.Common/Interfaces/Services/IDebuggable.cs @@ -41,7 +41,7 @@ namespace BizHawk.Emulation.Common /// Gets the total number of CPU cycles since the beginning of the core's lifecycle /// Note that the CPU in this case is the "main" CPU, for some cores that may be somewhat subjective /// - int TotalExecutedCycles { get; } // TODO: this should probably be a long, but most cores were using int, oh well + long TotalExecutedCycles { get; } // TODO: this should probably be a long, but most cores were using int, oh well } public class RegisterValue diff --git a/BizHawk.Emulation.Common/SystemLookup.cs b/BizHawk.Emulation.Common/SystemLookup.cs index f8e1a0de3c..4c6c692e46 100644 --- a/BizHawk.Emulation.Common/SystemLookup.cs +++ b/BizHawk.Emulation.Common/SystemLookup.cs @@ -32,7 +32,8 @@ namespace BizHawk.Emulation.Common new SystemInfo { SystemId = "C64", FullName = "Commodore 64" }, new SystemInfo { SystemId = "AppleII", FullName = "Apple II" }, - new SystemInfo { SystemId = "INTV", FullName = "Intellivision" } + new SystemInfo { SystemId = "INTV", FullName = "Intellivision" }, + new SystemInfo { SystemId = "ZXSpectrum", FullName = "Sinclair ZX Spectrum" } }; public SystemInfo this[string systemId] diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 85124ebf54..faf0089eb2 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -258,6 +258,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Atari2600.cs @@ -586,17 +657,32 @@ + - - - - - - - + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + + + GBHawk.cs + @@ -616,6 +702,12 @@ + + PPU.cs + + + PPU.cs + @@ -1208,6 +1300,7 @@ + @@ -1338,16 +1431,32 @@ + + + + + + + + + + + + + + + + "$(SolutionDir)subwcrev.bat" "$(ProjectDir)" diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings index 235b742b90..6e05d923f6 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj.DotSettings @@ -1,4 +1,5 @@  + CSharp60 DO_NOT_SHOW \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs index f68b2689c8..c30a5675f8 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Execute.cs @@ -4,8 +4,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { public partial class LR35902 { - private int totalExecutedCycles; - public int TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } + private ulong totalExecutedCycles; + public ulong TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } private int EI_pending; private bool interrupts_enabled; diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs index 0dba320f46..12979e363d 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Interrupts.cs @@ -4,37 +4,35 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { public partial class LR35902 { - private bool iff1; - public bool IFF1 { get { return iff1; } set { iff1 = value; } } - - private bool iff2; - public bool IFF2 { get { return iff2; } set { iff2 = value; } } - - private bool nonMaskableInterrupt; - public bool NonMaskableInterrupt - { - get { return nonMaskableInterrupt; } - set { if (value && !nonMaskableInterrupt) NonMaskableInterruptPending = true; nonMaskableInterrupt = value; } - } - - private bool nonMaskableInterruptPending; - public bool NonMaskableInterruptPending { get { return nonMaskableInterruptPending; } set { nonMaskableInterruptPending = value; } } - - private int interruptMode; - public int InterruptMode - { - get { return interruptMode; } - set { if (value < 0 || value > 2) throw new ArgumentOutOfRangeException(); interruptMode = value; } - } - private void INTERRUPT_() { cur_instr = new ushort[] {IDLE, + DEC16, SPl, SPh, + IDLE, + WR, SPl, SPh, PCh, + IDLE, + DEC16, SPl, SPh, + INT_GET, W,// NOTE: here is where we check for a cancelled IRQ + WR, SPl, SPh, PCl, IDLE, IDLE, IDLE, IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + TR, PCl, W, + ASGN, PCh, 0, + IDLE, + OP }; + } + + private void INTERRUPT_GBC_NOP() + { + cur_instr = new ushort[] + {IDLE, DEC16, SPl, SPh, IDLE, WR, SPl, SPh, PCh, @@ -49,20 +47,34 @@ namespace BizHawk.Emulation.Common.Components.LR35902 TR, PCl, W, ASGN, PCh, 0, IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, OP }; } private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60, 0x00}; public ushort int_src; + public int stop_time; + public bool stop_check; + public bool is_GBC; // GBC automatically adds a NOP to avoid the HALT bug (according to Sinimas) + public bool I_use; // in halt mode, the I flag is checked earlier then when deicision to IRQ is taken + public bool skip_once; + public bool Halt_bug_2; + public bool Halt_bug_3; private void ResetInterrupts() { - IFF1 = false; - IFF2 = false; - NonMaskableInterrupt = false; - NonMaskableInterruptPending = false; - InterruptMode = 1; + I_use = false; + skip_once = false; + Halt_bug_2 = false; + Halt_bug_3 = false; } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs index 1b475ef337..2df6418354 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/LR35902.cs @@ -57,6 +57,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public const ushort RD_F = 42; // special read case to pop value into F public const ushort EI_RETI = 43; // reti has no delay in interrupt enable public const ushort INT_GET = 44; + public const ushort HALT_CHK = 45; // when in halt mode, actually check I Flag here public LR35902() { @@ -67,8 +68,9 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { ResetRegisters(); ResetInterrupts(); - TotalExecutedCycles = 0; - cur_instr = new ushort[] { OP }; + TotalExecutedCycles = 8; + stop_check = false; + cur_instr = new ushort[] { IDLE, IDLE, HALT_CHK, OP }; } // Memory Access @@ -78,6 +80,9 @@ namespace BizHawk.Emulation.Common.Components.LR35902 public Func PeekMemory; public Func DummyReadMemory; + // Special Function for Speed switching executed on a STOP + public Func SpeedFunc; + //this only calls when the first byte of an instruction is fetched. public Action OnExecFetch; @@ -122,7 +127,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } } - if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + if (I_use && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; @@ -146,6 +151,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 FetchInstruction(ReadMemory(RegPC++)); } instr_pntr = 0; + I_use = false; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -250,6 +256,17 @@ namespace BizHawk.Emulation.Common.Components.LR35902 case HALT: halted = true; + bool temp = false; + + if (cur_instr[instr_pntr++] == 1) + { + temp = FlagI; + } + else + { + temp = I_use; + } + if (EI_pending > 0 && !CB_prefix) { EI_pending--; @@ -260,8 +277,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 } // if the I flag is asserted at the time of halt, don't halt - - if (FlagI && interrupts_enabled && !CB_prefix && !jammed) + if (temp && interrupts_enabled && !CB_prefix && !jammed) { interrupts_enabled = false; @@ -274,10 +290,30 @@ namespace BizHawk.Emulation.Common.Components.LR35902 }); } halted = false; - // call interrupt processor - INTERRUPT_(); + + if (is_GBC) + { + // call the interrupt processor after 4 extra cycles + if (!Halt_bug_3) + { + INTERRUPT_GBC_NOP(); + //INTERRUPT_(); + } + else + { + INTERRUPT_(); + Halt_bug_3 = false; + Console.WriteLine("Hit INT"); + } + } + else + { + // call interrupt processor + INTERRUPT_(); + Halt_bug_3 = false; + } } - else if (FlagI) + else if (temp) { // even if interrupt servicing is disabled, any interrupt flag raised still resumes execution if (TraceCallback != null) @@ -289,24 +325,113 @@ namespace BizHawk.Emulation.Common.Components.LR35902 }); } halted = false; - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null && !CB_prefix) TraceCallback(State()); - FetchInstruction(ReadMemory(RegPC++)); + + if (is_GBC) + { + // extra 4 cycles for GBC + if (Halt_bug_3) + { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + + RegPC++; + FetchInstruction(ReadMemory(RegPC)); + Halt_bug_3 = false; + Console.WriteLine("Hit un"); + } + else + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP }; + } + } + else + { + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + + if (Halt_bug_3) + { + //special variant of halt bug where RegPC also isn't incremented post fetch + RegPC++; + FetchInstruction(ReadMemory(RegPC)); + Halt_bug_3 = false; + } + else + { + FetchInstruction(ReadMemory(RegPC++)); + } + } } else { - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - HALT }; + if (skip_once) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + HALT, 0 }; + + skip_once = false; + } + else + { + cur_instr = new ushort[] + {IDLE, + HALT_CHK, + IDLE, + HALT, 0 }; + } + } + I_use = false; instr_pntr = 0; break; case STOP: stopped = true; + if (!stop_check) + { + stop_time = SpeedFunc(0); + stop_check = true; + } + + if (stop_time > 0) + { + stop_time--; + if (stop_time == 0) + { + if (TraceCallback != null) + { + TraceCallback(new TraceInfo + { + Disassembly = "====un-stop====", + RegisterInfo = "" + }); + } - if (interrupt_src.Bit(4)) // button pressed, not actually an interrupt though + stopped = false; + if (OnExecFetch != null) OnExecFetch(RegPC); + if (TraceCallback != null && !CB_prefix) TraceCallback(State()); + FetchInstruction(ReadMemory(RegPC++)); + instr_pntr = 0; + + stop_check = false; + } + else + { + instr_pntr = 0; + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + STOP }; + } + } + else if (interrupt_src.Bit(4)) // button pressed, not actually an interrupt though { if (TraceCallback != null) { @@ -322,6 +447,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 if (TraceCallback != null && !CB_prefix) TraceCallback(State()); FetchInstruction(ReadMemory(RegPC++)); instr_pntr = 0; + + stop_check = false; } else { @@ -375,6 +502,18 @@ namespace BizHawk.Emulation.Common.Components.LR35902 Regs[cur_instr[instr_pntr++]] = INT_vectors[int_src]; break; + case HALT_CHK: + I_use = FlagI; + if (Halt_bug_2 && I_use) + { + RegPC--; + Halt_bug_3 = true; + Console.WriteLine("Halt_bug_3"); + Console.WriteLine(totalExecutedCycles); + } + + Halt_bug_2 = false; + break; } totalExecutedCycles++; } @@ -425,18 +564,20 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ser.BeginSection("LR35902"); ser.Sync("Regs", ref Regs, false); ser.Sync("IRQ", ref interrupts_enabled); - ser.Sync("NMI", ref nonMaskableInterrupt); - ser.Sync("NMIPending", ref nonMaskableInterruptPending); - ser.Sync("IM", ref interruptMode); - ser.Sync("IFF1", ref iff1); - ser.Sync("IFF2", ref iff2); + ser.Sync("I_use", ref I_use); + ser.Sync("skip_once", ref skip_once); + ser.Sync("Halt_bug_2", ref Halt_bug_2); + ser.Sync("Halt_bug_3", ref Halt_bug_3); ser.Sync("Halted", ref halted); ser.Sync("ExecutedCycles", ref totalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); ser.Sync("int_src", ref int_src); + ser.Sync("stop_time", ref stop_time); + ser.Sync("stop_check", ref stop_check); + ser.Sync("is_GBC", ref is_GBC); - ser.Sync("instruction_pointer", ref instr_pntr); - ser.Sync("current instruction", ref cur_instr, false); + ser.Sync("instr_pntr", ref instr_pntr); + ser.Sync("cur_instr", ref cur_instr, false); ser.Sync("CB Preifx", ref CB_prefix); ser.Sync("Stopped", ref stopped); ser.Sync("opcode", ref opcode); diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs index a611b780b9..b5e005948f 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Direct.cs @@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -25,7 +25,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 INC16, src_l, src_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -39,7 +39,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 DEC16, src_l, src_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -52,7 +52,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ADD16, dest_l, dest_h, src_l, src_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -61,7 +61,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {operation, dest, src, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -76,25 +76,47 @@ namespace BizHawk.Emulation.Common.Components.LR35902 private void HALT_() { - if (FlagI && (EI_pending == 0)) + if (FlagI && (EI_pending == 0) && !interrupts_enabled) { - // if interrupts are disabled, - // a glitchy decrement to the program counter happens - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - OP_G}; + if (is_GBC) + { + // in GBC mode, the HALT bug is worked around by simply adding a NOP + // so it just takes 4 cycles longer to reach the next instruction + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP_G}; + } + else + { // if interrupts are disabled, + // a glitchy decrement to the program counter happens + { + cur_instr = new ushort[] + {IDLE, + IDLE, + IDLE, + OP_G}; + } + } } else { cur_instr = new ushort[] - {IDLE, + { + IDLE, + HALT_CHK, IDLE, - IDLE, - HALT }; + HALT, 0 }; + + if (!is_GBC) { skip_once = true; } + // If the interrupt flag is not currently set, but it does get set in the first check + // then a bug is triggered + // With interrupts enabled, this runs the halt command twice + // when they are disabled, it reads the next byte twice + if (!FlagI ||(FlagI && !interrupts_enabled)) { Halt_bug_2 = true; } + } - } private void JR_COND(bool cond) @@ -112,7 +134,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ASGN, Z, 0, IDLE, ADDS, PCl, PCh, W, Z, - IDLE, + HALT_CHK, OP }; } else @@ -124,7 +146,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, + HALT_CHK, OP }; } } @@ -148,7 +170,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 TR, PCl, W, IDLE, TR, PCh, Z, - IDLE, + HALT_CHK, OP }; } else @@ -164,7 +186,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, + HALT_CHK, OP }; } } @@ -186,7 +208,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -207,7 +229,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 EI_RETI, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -235,7 +257,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } else @@ -247,7 +269,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } } @@ -276,10 +298,10 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, DEC16, SPl, SPh, - WR, SPl, SPh, PCl, - IDLE, + WR, SPl, SPh, PCl, TR, PCl, W, TR, PCh, Z, + HALT_CHK, OP }; } else @@ -295,7 +317,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, Z, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, + HALT_CHK, OP }; } } @@ -305,7 +327,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {operation, src, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -314,7 +336,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {operation, bit, src, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -335,7 +357,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 WR, SPl, SPh, src_l, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -357,7 +379,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, src_h, SPl, SPh, IDLE, INC16, SPl, SPh, - IDLE, + HALT_CHK, OP }; } else @@ -373,7 +395,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, src_h, SPl, SPh, IDLE, INC16, SPl, SPh, - IDLE, + HALT_CHK, OP }; } } @@ -393,9 +415,9 @@ namespace BizHawk.Emulation.Common.Components.LR35902 IDLE, IDLE, WR, SPl, SPh, PCl, - IDLE, ASGN, PCh, 0, ASGN, PCl, n, + HALT_CHK, OP }; } @@ -413,7 +435,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {DI, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -422,7 +444,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 cur_instr = new ushort[] {EI, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -430,8 +452,8 @@ namespace BizHawk.Emulation.Common.Components.LR35902 { cur_instr = new ushort[] {TR, PCl, L, - IDLE, TR, PCh, H, + HALT_CHK, OP }; } @@ -452,7 +474,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 ADDS, SPl, SPh, W, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -465,7 +487,7 @@ namespace BizHawk.Emulation.Common.Components.LR35902 TR, SPl, L, IDLE, TR, SPh, H, - IDLE, + HALT_CHK, OP }; } @@ -478,11 +500,11 @@ namespace BizHawk.Emulation.Common.Components.LR35902 RD, W, PCl, PCh, IDLE, INC16, PCl, PCh, - IDLE, TR, H, SPh, TR, L, SPl, ASGN, Z, 0, ADDS, L, H, W, Z, + HALT_CHK, OP }; } diff --git a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs index 79d2e67341..c1f68bc4f1 100644 --- a/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/LR35902/Tables_Indirect.cs @@ -14,8 +14,8 @@ IDLE, WR, src_l, src_h, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -32,7 +32,7 @@ WR, src_l, src_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -45,7 +45,7 @@ RD, Z, src_l, src_h, IDLE, operation, bit, Z, - IDLE, + HALT_CHK, OP }; } @@ -56,9 +56,9 @@ IDLE, IDLE, RD, Z, src_l, src_h, - IDLE, operation, dest, Z, INC16, src_l, src_h, + HALT_CHK, OP }; } @@ -71,7 +71,7 @@ RD, Z, src_l, src_h, IDLE, operation, dest, Z, - IDLE, + HALT_CHK, OP }; } @@ -96,7 +96,7 @@ WR, W, Z, dest_h, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -109,7 +109,7 @@ WR, dest_l, dest_h, src, IDLE, INC16, dest_l, dest_h, - IDLE, + HALT_CHK, OP }; } @@ -122,7 +122,7 @@ WR, dest_l, dest_h, src, IDLE, DEC16, dest_l, dest_h, - IDLE, + HALT_CHK, OP }; } @@ -134,8 +134,8 @@ IDLE, WR, dest_l, dest_h, src, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -152,7 +152,7 @@ WR, dest_l, dest_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -165,7 +165,7 @@ RD, dest, src_l, src_h, IDLE, INC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -178,7 +178,7 @@ RD, dest, src_l, src_h, IDLE, INC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -191,7 +191,7 @@ RD, dest, src_l, src_h, IDLE, DEC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -208,7 +208,7 @@ RD, dest_h, src_l, src_h, IDLE, INC16, src_l, src_h, - IDLE, + HALT_CHK, OP }; } @@ -225,7 +225,7 @@ WR, src_l, src_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -242,7 +242,7 @@ WR, src_l, src_h, Z, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -259,8 +259,8 @@ ASGN, Z , 0xFF, RD, dest, W, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -277,7 +277,7 @@ WR, W, Z, src, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } @@ -289,8 +289,8 @@ ASGN, Z , 0xFF, RD, dest, C, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -302,8 +302,8 @@ ASGN, Z , 0xFF, WR, C, Z, src, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -323,8 +323,8 @@ IDLE, RD, dest, W, Z, IDLE, - IDLE, IDLE, + HALT_CHK, OP }; } @@ -345,7 +345,7 @@ WR, W, Z, src, IDLE, IDLE, - IDLE, + HALT_CHK, OP }; } } diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs index 23075ee8f0..25d28f85c3 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Disassembler.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; namespace BizHawk.Emulation.Cores.Components.M6502 { - public partial class MOS6502X : IDisassemblable + public partial class MOS6502X : IDisassemblable { private static ushort peeker_word(ushort address, Func peeker) { @@ -15,7 +15,43 @@ namespace BizHawk.Emulation.Cores.Components.M6502 public string Disassemble(ushort pc, out int bytesToAdvance) { - return Disassemble(pc, out bytesToAdvance, PeekMemory); + return MOS6502X.Disassemble(pc, out bytesToAdvance, _link.PeekMemory); + } + + public string Cpu + { + get + { + return "6502"; + } + set + { + } + } + + public string PCRegisterName + { + get { return "PC"; } + } + + public IEnumerable AvailableCpus + { + get { yield return "6502"; } + } + + public string Disassemble(MemoryDomain m, uint addr, out int length) + { + return MOS6502X.Disassemble((ushort)addr, out length, a => m.PeekByte((int)a)); + } + } + + public static class MOS6502X + { + private static ushort peeker_word(ushort address, Func peeker) + { + byte l = peeker(address); + byte h = peeker(++address); + return (ushort)((h << 8) | l); } /// @@ -208,31 +244,5 @@ namespace BizHawk.Emulation.Cores.Components.M6502 bytesToAdvance = 1; return "???"; } - - public string Cpu - { - get - { - return "6502"; - } - set - { - } - } - - public string PCRegisterName - { - get { return "PC"; } - } - - public IEnumerable AvailableCpus - { - get { yield return "6502"; } - } - - public string Disassemble(MemoryDomain m, uint addr, out int length) - { - return Disassemble((ushort)addr, out length, a => m.PeekByte((int)a)); - } } } diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs index 7e4ad61e6d..9724899735 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/Execute.cs @@ -7,7 +7,7 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Components.M6502 { - public partial class MOS6502X + public partial class MOS6502X { //dont know whether this system is any faster. hard to get benchmarks someone else try it? //static ShortBuffer CompiledMicrocode; @@ -537,7 +537,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - DummyReadMemory(PC); + _link.DummyReadMemory(PC); } } @@ -610,10 +610,10 @@ namespace BizHawk.Emulation.Cores.Components.M6502 { if (debug) Console.WriteLine(State()); branch_irq_hack = false; - if (OnExecFetch != null) OnExecFetch(PC); + _link.OnExecFetch(PC); if (TraceCallback != null) TraceCallback(State()); - opcode = ReadMemory(PC++); + opcode = _link.ReadMemory(PC++); mi = -1; } } @@ -622,7 +622,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode2 = ReadMemory(PC++); + opcode2 = _link.ReadMemory(PC++); } } void Fetch3() @@ -630,21 +630,21 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode3 = ReadMemory(PC++); + opcode3 = _link.ReadMemory(PC++); } } void PushPCH() { - WriteMemory((ushort)(S-- + 0x100), (byte)(PC >> 8)); + _link.WriteMemory((ushort)(S-- + 0x100), (byte)(PC >> 8)); } void PushPCL() { - WriteMemory((ushort)(S-- + 0x100), (byte)PC); + _link.WriteMemory((ushort)(S-- + 0x100), (byte)PC); } void PushP_BRK() { FlagB = true; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); FlagI = true; ea = BRKVector; @@ -652,7 +652,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 void PushP_IRQ() { FlagB = false; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); FlagI = true; ea = IRQVector; @@ -660,7 +660,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 void PushP_NMI() { FlagB = false; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); FlagI = true; //is this right? ea = NMIVector; @@ -692,7 +692,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 NMI = false; ea = NMIVector; } - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } @@ -701,7 +701,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp += ReadMemory((ushort)(ea + 1)) << 8; + alu_temp += _link.ReadMemory((ushort)(ea + 1)) << 8; PC = (ushort)alu_temp; } @@ -859,36 +859,36 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_WRITE_STA() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), A); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), A); } void Abs_WRITE_STX() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), X); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), X); } void Abs_WRITE_STY() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), Y); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), Y); } void Abs_WRITE_SAX() { - WriteMemory((ushort)((opcode3 << 8) + opcode2), (byte)(X & A)); + _link.WriteMemory((ushort)((opcode3 << 8) + opcode2), (byte)(X & A)); } void ZP_WRITE_STA() { - WriteMemory(opcode2, A); + _link.WriteMemory(opcode2, A); } void ZP_WRITE_STY() { - WriteMemory(opcode2, Y); + _link.WriteMemory(opcode2, Y); } void ZP_WRITE_STX() { - WriteMemory(opcode2, X); + _link.WriteMemory(opcode2, X); } void ZP_WRITE_SAX() { - WriteMemory(opcode2, (byte)(X & A)); + _link.WriteMemory(opcode2, (byte)(X & A)); } void IndIdx_Stage3() @@ -896,7 +896,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ea = ReadMemory(opcode2); + ea = _link.ReadMemory(opcode2); } } @@ -906,7 +906,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { alu_temp = ea + Y; - ea = (ReadMemory((byte)(opcode2 + 1)) << 8) + ea = (_link.ReadMemory((byte)(opcode2 + 1)) << 8) | ((alu_temp & 0xFF)); } @@ -916,7 +916,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory((ushort)ea); + _link.ReadMemory((ushort)ea); ea += (alu_temp >> 8) << 8; } @@ -934,7 +934,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } else { - ReadMemory((ushort)ea); + _link.ReadMemory((ushort)ea); ea = (ushort)(ea + 0x100); } } @@ -944,7 +944,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory((ushort)ea); + _link.ReadMemory((ushort)ea); if (alu_temp.Bit(8)) ea = (ushort)(ea + 0x100); @@ -953,12 +953,12 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_WRITE_Stage6_STA() { - WriteMemory((ushort)ea, A); + _link.WriteMemory((ushort)ea, A); } void IndIdx_WRITE_Stage6_SHA() { - WriteMemory((ushort)ea, (byte)(A & X & 7)); + _link.WriteMemory((ushort)ea, (byte)(A & X & 7)); } void IndIdx_READ_Stage6_LDA() @@ -966,7 +966,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -975,7 +975,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Cmp(); } } @@ -984,7 +984,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _And(); } } @@ -993,7 +993,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Eor(); } } @@ -1002,7 +1002,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = X = ReadMemory((ushort)ea); + A = X = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -1011,7 +1011,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Adc(); } } @@ -1020,7 +1020,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Sbc(); } } @@ -1029,7 +1029,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Ora(); } } @@ -1038,13 +1038,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void IndIdx_RMW_Stage7_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)((value8 << 1)); @@ -1053,7 +1053,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -1062,7 +1062,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -1070,14 +1070,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 + 1); _Sbc(); } void IndIdx_RMW_Stage7_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 - 1); FlagC = (temp8 & 1) != 0; @@ -1085,7 +1085,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage7_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -1094,7 +1094,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IndIdx_RMW_Stage8() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void RelBranch_Stage2_BVS() { @@ -1142,7 +1142,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode2 = ReadMemory(PC++); + opcode2 = _link.ReadMemory(PC++); if (branch_taken) { branch_taken = false; @@ -1213,7 +1213,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - PC = (ushort)((ReadMemory((ushort)(PC)) << 8) + opcode2); + PC = (ushort)((_link.ReadMemory((ushort)(PC)) << 8) + opcode2); } } void PullP() @@ -1221,7 +1221,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - P = ReadMemory((ushort)(S++ + 0x100)); + P = _link.ReadMemory((ushort)(S++ + 0x100)); FlagT = true; //force T always to remain true } @@ -1232,7 +1232,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { PC &= 0xFF00; - PC |= ReadMemory((ushort)(S++ + 0x100)); + PC |= _link.ReadMemory((ushort)(S++ + 0x100)); } } @@ -1242,7 +1242,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { PC &= 0xFF; - PC |= (ushort)(ReadMemory((ushort)(S + 0x100)) << 8); + PC |= (ushort)(_link.ReadMemory((ushort)(S + 0x100)) << 8); } } @@ -1251,7 +1251,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + A = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); NZ_A(); } } @@ -1260,7 +1260,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + Y = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); NZ_Y(); } } @@ -1269,7 +1269,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + X = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); NZ_X(); } } @@ -1278,7 +1278,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Bit(); } } @@ -1287,8 +1287,8 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); - A = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); + A = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); X = A; NZ_A(); } @@ -1298,7 +1298,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _And(); } } @@ -1307,7 +1307,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Eor(); } } @@ -1316,7 +1316,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Ora(); } } @@ -1325,7 +1325,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Adc(); } } @@ -1334,7 +1334,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Cmp(); } } @@ -1343,7 +1343,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Cpy(); } } @@ -1352,7 +1352,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); } } @@ -1361,7 +1361,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Cpx(); } } @@ -1370,7 +1370,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)((opcode3 << 8) + opcode2)); + alu_temp = _link.ReadMemory((ushort)((opcode3 << 8) + opcode2)); _Sbc(); } @@ -1380,7 +1380,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); + _link.ReadMemory(opcode2); opcode2 = (byte)(opcode2 + X); //a bit sneaky to shove this into opcode2... but we can reuse all the zero page uops if we do that } @@ -1390,7 +1390,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); + _link.ReadMemory(opcode2); opcode2 = (byte)(opcode2 + Y); //a bit sneaky to shove this into opcode2... but we can reuse all the zero page uops if we do that } @@ -1400,13 +1400,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); } } void ZpIdx_RMW_Stage6() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); } @@ -1415,7 +1415,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Eor(); } } @@ -1424,7 +1424,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Bit(); } } @@ -1433,7 +1433,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory(opcode2); + A = _link.ReadMemory(opcode2); NZ_A(); } } @@ -1442,7 +1442,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory(opcode2); + Y = _link.ReadMemory(opcode2); NZ_Y(); } } @@ -1451,7 +1451,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory(opcode2); + X = _link.ReadMemory(opcode2); NZ_X(); } } @@ -1461,7 +1461,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { //?? is this right?? - X = ReadMemory(opcode2); + X = _link.ReadMemory(opcode2); A = X; NZ_A(); } @@ -1471,7 +1471,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Cpy(); } } @@ -1480,7 +1480,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Cmp(); } } @@ -1489,7 +1489,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Cpx(); } } @@ -1498,7 +1498,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Ora(); } } @@ -1507,7 +1507,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); //just a dummy + _link.ReadMemory(opcode2); //just a dummy } } @@ -1516,7 +1516,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Sbc(); } } @@ -1525,7 +1525,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _Adc(); } } @@ -1534,7 +1534,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); _And(); } @@ -1696,7 +1696,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Eor(); } } @@ -1705,7 +1705,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Anc(); } } @@ -1714,7 +1714,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Asr(); } } @@ -1723,7 +1723,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Axs(); } } @@ -1732,7 +1732,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Arr(); } } @@ -1741,7 +1741,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Lxa(); } } @@ -1750,7 +1750,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Ora(); } } @@ -1759,7 +1759,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Cpy(); } } @@ -1768,7 +1768,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Cpx(); } } @@ -1777,7 +1777,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Cmp(); } } @@ -1786,7 +1786,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Sbc(); } } @@ -1795,7 +1795,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _And(); } } @@ -1804,7 +1804,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(PC++); + alu_temp = _link.ReadMemory(PC++); _Adc(); } } @@ -1813,7 +1813,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory(PC++); + A = _link.ReadMemory(PC++); NZ_A(); } } @@ -1822,7 +1822,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory(PC++); + X = _link.ReadMemory(PC++); NZ_X(); } } @@ -1831,7 +1831,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory(PC++); + Y = _link.ReadMemory(PC++); NZ_Y(); } } @@ -1840,7 +1840,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(PC++); + _link.ReadMemory(PC++); } } @@ -1849,7 +1849,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(opcode2); //dummy? + _link.ReadMemory(opcode2); //dummy? alu_temp = (opcode2 + X) & 0xFF; } @@ -1859,7 +1859,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ea = ReadMemory((ushort)alu_temp); + ea = _link.ReadMemory((ushort)alu_temp); } } @@ -1868,7 +1868,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ea += (ReadMemory((byte)(alu_temp + 1)) << 8); + ea += (_link.ReadMemory((byte)(alu_temp + 1)) << 8); } } @@ -1878,7 +1878,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { //TODO make uniform with others - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -1887,7 +1887,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Ora(); } } @@ -1896,7 +1896,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = X = ReadMemory((ushort)ea); + A = X = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -1905,7 +1905,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Cmp(); } } @@ -1914,7 +1914,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Adc(); } } @@ -1923,7 +1923,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _And(); } } @@ -1932,7 +1932,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Eor(); } } @@ -1941,19 +1941,19 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Sbc(); } } void IdxInd_Stage6_WRITE_STA() { - WriteMemory((ushort)ea, A); + _link.WriteMemory((ushort)ea, A); } void IdxInd_Stage6_WRITE_SAX() { alu_temp = A & X; - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); //flag writing skipped on purpose } @@ -1962,13 +1962,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void IdxInd_Stage7_RMW_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)((value8 << 1)); @@ -1977,14 +1977,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 + 1); _Sbc(); } void IdxInd_Stage7_RMW_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 - 1); FlagC = (temp8 & 1) != 0; @@ -1992,7 +1992,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2001,7 +2001,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); @@ -2010,7 +2010,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage7_RMW_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2019,26 +2019,26 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void IdxInd_Stage8_RMW() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void PushP() { FlagB = true; - WriteMemory((ushort)(S-- + 0x100), P); + _link.WriteMemory((ushort)(S-- + 0x100), P); } void PushA() { - WriteMemory((ushort)(S-- + 0x100), A); + _link.WriteMemory((ushort)(S-- + 0x100), A); } void PullA_NoInc() { rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)(S + 0x100)); + A = _link.ReadMemory((ushort)(S + 0x100)); NZ_A(); } } @@ -2048,7 +2048,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { my_iflag = FlagI; - P = ReadMemory((ushort)(S + 0x100)); + P = _link.ReadMemory((ushort)(S + 0x100)); iflag_pending = FlagI; FlagI = my_iflag; FlagT = true; //force T always to remain true @@ -2107,7 +2107,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - PC = (ushort)((ReadMemory(PC) << 8) + opcode2); + PC = (ushort)((_link.ReadMemory(PC) << 8) + opcode2); } } @@ -2116,7 +2116,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - ReadMemory(PC); + _link.ReadMemory(PC); PC++; } } @@ -2125,29 +2125,29 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory(opcode2); + alu_temp = _link.ReadMemory(opcode2); } } void ZP_RMW_Stage5() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); } void ZP_RMW_INC() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); alu_temp = (byte)((alu_temp + 1) & 0xFF); P = (byte)((P & 0x7D) | TableNZ[alu_temp]); } void ZP_RMW_DEC() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); alu_temp = (byte)((alu_temp - 1) & 0xFF); P = (byte)((P & 0x7D) | TableNZ[alu_temp]); } void ZP_RMW_ASL() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2155,7 +2155,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_SRE() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2164,7 +2164,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_RRA() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2172,7 +2172,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_DCP() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 - 1); FlagC = (temp8 & 1) != 0; @@ -2180,7 +2180,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_LSR() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2189,7 +2189,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_ROR() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2197,7 +2197,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_ROL() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2205,7 +2205,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_SLO() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)((value8 << 1)); @@ -2214,14 +2214,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void ZP_RMW_ISC() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = (byte)alu_temp; alu_temp = value8 = (byte)(value8 + 1); _Sbc(); } void ZP_RMW_RLA() { - WriteMemory(opcode2, (byte)alu_temp); + _link.WriteMemory(opcode2, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2233,7 +2233,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode3 = ReadMemory(PC++); + opcode3 = _link.ReadMemory(PC++); alu_temp = opcode2 + Y; ea = (opcode3 << 8) + (alu_temp & 0xFF); @@ -2245,7 +2245,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - opcode3 = ReadMemory(PC++); + opcode3 = _link.ReadMemory(PC++); alu_temp = opcode2 + X; ea = (opcode3 << 8) + (alu_temp & 0xFF); } @@ -2264,7 +2264,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } else { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); ea = (ushort)(ea + 0x100); } } @@ -2279,36 +2279,36 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (alu_temp.Bit(8)) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); ea = (ushort)(ea + 0x100); } - else alu_temp = ReadMemory((ushort)ea); + else alu_temp = _link.ReadMemory((ushort)ea); } } void AbsIdx_WRITE_Stage5_STA() { - WriteMemory((ushort)ea, A); + _link.WriteMemory((ushort)ea, A); } void AbsIdx_WRITE_Stage5_SHY() { alu_temp = Y & (ea >> 8); ea = (ea & 0xFF) | (alu_temp << 8); //"(the bank where the value is stored may be equal to the value stored)" -- more like IS. - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void AbsIdx_WRITE_Stage5_SHX() { alu_temp = X & (ea >> 8); ea = (ea & 0xFF) | (alu_temp << 8); //"(the bank where the value is stored may be equal to the value stored)" -- more like IS. - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void AbsIdx_WRITE_Stage5_ERROR() { S = (byte)(X & A); - WriteMemory((ushort)ea, (byte)(S & (opcode3+1))); + _link.WriteMemory((ushort)ea, (byte)(S & (opcode3+1))); } void AbsIdx_RMW_Stage5() @@ -2316,44 +2316,44 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void AbsIdx_RMW_Stage7() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } void AbsIdx_RMW_Stage6_DEC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp - 1); P = (byte)((P & 0x7D) | TableNZ[value8]); } void AbsIdx_RMW_Stage6_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp - 1); _Cmp(); } void AbsIdx_RMW_Stage6_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp + 1); _Sbc(); } void AbsIdx_RMW_Stage6_INC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); alu_temp = value8 = (byte)(alu_temp + 1); P = (byte)((P & 0x7D) | TableNZ[value8]); } void AbsIdx_RMW_Stage6_ROL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2362,7 +2362,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_LSR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2371,7 +2371,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2380,7 +2380,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2389,7 +2389,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2397,7 +2397,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2406,7 +2406,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_ASL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2415,7 +2415,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void AbsIdx_RMW_Stage6_ROR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2428,7 +2428,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); NZ_A(); } } @@ -2437,7 +2437,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - X = ReadMemory((ushort)ea); + X = _link.ReadMemory((ushort)ea); NZ_X(); } } @@ -2446,7 +2446,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - A = ReadMemory((ushort)ea); + A = _link.ReadMemory((ushort)ea); X = A; NZ_A(); } @@ -2456,7 +2456,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - Y = ReadMemory((ushort)ea); + Y = _link.ReadMemory((ushort)ea); NZ_Y(); } } @@ -2465,7 +2465,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Ora(); } } @@ -2474,7 +2474,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } @@ -2483,7 +2483,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Cmp(); } } @@ -2492,7 +2492,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Sbc(); } } @@ -2501,7 +2501,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Adc(); } } @@ -2510,7 +2510,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _Eor(); } } @@ -2519,7 +2519,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); _And(); } } @@ -2528,7 +2528,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 rdy_freeze = !RDY; if (RDY) { - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); S &= (byte)alu_temp; X = S; A = S; @@ -2542,7 +2542,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { ea = (opcode3 << 8) + opcode2; - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void AbsInd_JMP_Stage5() @@ -2551,7 +2551,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { ea = (opcode3 << 8) + (byte)(opcode2 + 1); - alu_temp += ReadMemory((ushort)ea) << 8; + alu_temp += _link.ReadMemory((ushort)ea) << 8; PC = (ushort)alu_temp; } @@ -2562,13 +2562,13 @@ namespace BizHawk.Emulation.Cores.Components.M6502 if (RDY) { ea = (opcode3 << 8) + opcode2; - alu_temp = ReadMemory((ushort)ea); + alu_temp = _link.ReadMemory((ushort)ea); } } void Abs_RMW_Stage5_INC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp + 1); alu_temp = value8; P = (byte)((P & 0x7D) | TableNZ[value8]); @@ -2576,7 +2576,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_DEC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp - 1); alu_temp = value8; P = (byte)((P & 0x7D) | TableNZ[value8]); @@ -2584,21 +2584,21 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_DCP() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp - 1); alu_temp = value8; _Cmp(); } void Abs_RMW_Stage5_ISC() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)(alu_temp + 1); alu_temp = value8; _Sbc(); } void Abs_RMW_Stage5_ASL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2607,7 +2607,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_ROR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2616,7 +2616,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_SLO() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 0x80) != 0; alu_temp = value8 = (byte)(value8 << 1); @@ -2625,7 +2625,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_RLA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2634,7 +2634,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_SRE() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2643,7 +2643,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_RRA() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 >> 1) | ((P & 1) << 7)); FlagC = (temp8 & 1) != 0; @@ -2651,7 +2651,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_ROL() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = temp8 = (byte)alu_temp; alu_temp = value8 = (byte)((value8 << 1) | (P & 1)); FlagC = (temp8 & 0x80) != 0; @@ -2660,7 +2660,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage5_LSR() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); value8 = (byte)alu_temp; FlagC = (value8 & 1) != 0; alu_temp = value8 = (byte)(value8 >> 1); @@ -2670,7 +2670,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 } void Abs_RMW_Stage6() { - WriteMemory((ushort)ea, (byte)alu_temp); + _link.WriteMemory((ushort)ea, (byte)alu_temp); } diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/IMOS6502XLink.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/IMOS6502XLink.cs new file mode 100644 index 0000000000..06a990c2a0 --- /dev/null +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/IMOS6502XLink.cs @@ -0,0 +1,18 @@ +namespace BizHawk.Emulation.Cores.Components.M6502 +{ + // Interface that has all the methods required by the MOS 6502X to talk to + // the emulator core. + // Should only be used as a generic type argument for the MOS 6502X, and + // implementations should be structs where possible. This combination allows + // the JITer to generate much faster code than calling a Func<> or Action<>. + public interface IMOS6502XLink + { + byte ReadMemory(ushort address); + byte DummyReadMemory(ushort address); + byte PeekMemory(ushort address); + void WriteMemory(ushort address, byte value); + + // This only calls when the first byte of an instruction is fetched. + void OnExecFetch(ushort address); + } +} diff --git a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs index bcfa5a80a3..83d94aad78 100644 --- a/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs +++ b/BizHawk.Emulation.Cores/CPUs/MOS 6502X/MOS6502X.cs @@ -6,10 +6,13 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Components.M6502 { - public sealed partial class MOS6502X + public sealed partial class MOS6502X where TLink : IMOS6502XLink { - public MOS6502X() + private readonly TLink _link; + + public MOS6502X(TLink link) { + _link = link; InitOpcodeHandlers(); Reset(); } @@ -62,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Components.M6502 for (int i = 0; i < length; i++) { - rawbytes += string.Format(" {0:X2}", PeekMemory((ushort)(PC + i))); + rawbytes += string.Format(" {0:X2}", _link.PeekMemory((ushort)(PC + i))); } return new TraceInfo @@ -204,41 +207,19 @@ namespace BizHawk.Emulation.Cores.Components.M6502 private set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); } } - public int TotalExecutedCycles; - - public Func ReadMemory; - public Func DummyReadMemory; - public Func PeekMemory; - public Action WriteMemory; - - //this only calls when the first byte of an instruction is fetched. - public Action OnExecFetch; - - public void SetCallbacks - ( - Func ReadMemory, - Func DummyReadMemory, - Func PeekMemory, - Action WriteMemory - ) - { - this.ReadMemory = ReadMemory; - this.DummyReadMemory = DummyReadMemory; - this.PeekMemory = PeekMemory; - this.WriteMemory = WriteMemory; - } + public long TotalExecutedCycles; public ushort ReadWord(ushort address) { - byte l = ReadMemory(address); - byte h = ReadMemory(++address); + byte l = _link.ReadMemory(address); + byte h = _link.ReadMemory(++address); return (ushort)((h << 8) | l); } public ushort PeekWord(ushort address) { - byte l = PeekMemory(address); - byte h = PeekMemory(++address); + byte l = _link.PeekMemory(address); + byte h = _link.PeekMemory(++address); return (ushort)((h << 8) | l); } @@ -246,14 +227,14 @@ namespace BizHawk.Emulation.Cores.Components.M6502 { byte l = (byte)(value & 0xFF); byte h = (byte)(value >> 8); - WriteMemory(address, l); - WriteMemory(++address, h); + _link.WriteMemory(address, l); + _link.WriteMemory(++address, h); } private ushort ReadWordPageWrap(ushort address) { ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF)); - return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8)); + return (ushort)(_link.ReadMemory(address) | (_link.ReadMemory(highAddress) << 8)); } // SO pin diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs index c70e4aa503..318ef27300 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Execute.cs @@ -4,8 +4,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { public partial class Z80A { - private int totalExecutedCycles; - public int TotalExecutedCycles { get { return totalExecutedCycles; } set { totalExecutedCycles = value; } } + public long TotalExecutedCycles; private int EI_pending; @@ -19,14 +18,16 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // variables for executing instructions public int instr_pntr = 0; + public int bus_pntr = 0; + public int mem_pntr = 0; public ushort[] cur_instr; - public int opcode; + public ushort[] BUSRQ; + public ushort[] MEMRQ; + public byte opcode; public bool NO_prefix, CB_prefix, IX_prefix, EXTD_prefix, IY_prefix, IXCB_prefix, IYCB_prefix; - public bool IXCB_prefetch, IYCB_prefetch; // value is fetched before opcode public bool halted; - public ushort PF; - public void FetchInstruction(byte opcode) + public void FetchInstruction() { if (NO_prefix) { @@ -102,7 +103,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x43: REG_OP(TR, B, E); break; // LD B, E case 0x44: REG_OP(TR, B, H); break; // LD B, H case 0x45: REG_OP(TR, B, L); break; // LD B, L - case 0x46: REG_OP_IND(TR, B, L, H); break; // LD B, (HL) + case 0x46: REG_OP_IND_HL(TR, B); break; // LD B, (HL) case 0x47: REG_OP(TR, B, A); break; // LD B, A case 0x48: REG_OP(TR, C, B); break; // LD C, B case 0x49: REG_OP(TR, C, C); break; // LD C, C @@ -110,7 +111,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x4B: REG_OP(TR, C, E); break; // LD C, E case 0x4C: REG_OP(TR, C, H); break; // LD C, H case 0x4D: REG_OP(TR, C, L); break; // LD C, L - case 0x4E: REG_OP_IND(TR, C, L, H); break; // LD C, (HL) + case 0x4E: REG_OP_IND_HL(TR, C); break; // LD C, (HL) case 0x4F: REG_OP(TR, C, A); break; // LD C, A case 0x50: REG_OP(TR, D, B); break; // LD D, B case 0x51: REG_OP(TR, D, C); break; // LD D, C @@ -118,7 +119,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x53: REG_OP(TR, D, E); break; // LD D, E case 0x54: REG_OP(TR, D, H); break; // LD D, H case 0x55: REG_OP(TR, D, L); break; // LD D, L - case 0x56: REG_OP_IND(TR, D, L, H); break; // LD D, (HL) + case 0x56: REG_OP_IND_HL(TR, D); break; // LD D, (HL) case 0x57: REG_OP(TR, D, A); break; // LD D, A case 0x58: REG_OP(TR, E, B); break; // LD E, B case 0x59: REG_OP(TR, E, C); break; // LD E, C @@ -126,7 +127,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x5B: REG_OP(TR, E, E); break; // LD E, E case 0x5C: REG_OP(TR, E, H); break; // LD E, H case 0x5D: REG_OP(TR, E, L); break; // LD E, L - case 0x5E: REG_OP_IND(TR, E, L, H); break; // LD E, (HL) + case 0x5E: REG_OP_IND_HL(TR, E); break; // LD E, (HL) case 0x5F: REG_OP(TR, E, A); break; // LD E, A case 0x60: REG_OP(TR, H, B); break; // LD H, B case 0x61: REG_OP(TR, H, C); break; // LD H, C @@ -134,7 +135,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x63: REG_OP(TR, H, E); break; // LD H, E case 0x64: REG_OP(TR, H, H); break; // LD H, H case 0x65: REG_OP(TR, H, L); break; // LD H, L - case 0x66: REG_OP_IND(TR, H, L, H); break; // LD H, (HL) + case 0x66: REG_OP_IND_HL(TR, H); break; // LD H, (HL) case 0x67: REG_OP(TR, H, A); break; // LD H, A case 0x68: REG_OP(TR, L, B); break; // LD L, B case 0x69: REG_OP(TR, L, C); break; // LD L, C @@ -142,23 +143,23 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0x6B: REG_OP(TR, L, E); break; // LD L, E case 0x6C: REG_OP(TR, L, H); break; // LD L, H case 0x6D: REG_OP(TR, L, L); break; // LD L, L - case 0x6E: REG_OP_IND(TR, L, L, H); break; // LD L, (HL) + case 0x6E: REG_OP_IND_HL(TR, L); break; // LD L, (HL) case 0x6F: REG_OP(TR, L, A); break; // LD L, A - case 0x70: LD_8_IND(L, H, B); break; // LD (HL), B - case 0x71: LD_8_IND(L, H, C); break; // LD (HL), C - case 0x72: LD_8_IND(L, H, D); break; // LD (HL), D - case 0x73: LD_8_IND(L, H, E); break; // LD (HL), E - case 0x74: LD_8_IND(L, H, H); break; // LD (HL), H - case 0x75: LD_8_IND(L, H, L); break; // LD (HL), L + case 0x70: LD_8_IND_HL(B); break; // LD (HL), B + case 0x71: LD_8_IND_HL(C); break; // LD (HL), C + case 0x72: LD_8_IND_HL(D); break; // LD (HL), D + case 0x73: LD_8_IND_HL(E); break; // LD (HL), E + case 0x74: LD_8_IND_HL(H); break; // LD (HL), H + case 0x75: LD_8_IND_HL(L); break; // LD (HL), L case 0x76: HALT_(); break; // HALT - case 0x77: LD_8_IND(L, H, A); break; // LD (HL), A + case 0x77: LD_8_IND_HL( A); break; // LD (HL), A case 0x78: REG_OP(TR, A, B); break; // LD A, B case 0x79: REG_OP(TR, A, C); break; // LD A, C case 0x7A: REG_OP(TR, A, D); break; // LD A, D case 0x7B: REG_OP(TR, A, E); break; // LD A, E case 0x7C: REG_OP(TR, A, H); break; // LD A, H case 0x7D: REG_OP(TR, A, L); break; // LD A, L - case 0x7E: REG_OP_IND(TR, A, L, H); break; // LD A, (HL) + case 0x7E: REG_OP_IND_HL(TR, A); break; // LD A, (HL) case 0x7F: REG_OP(TR, A, A); break; // LD A, A case 0x80: REG_OP(ADD8, A, B); break; // ADD A, B case 0x81: REG_OP(ADD8, A, C); break; // ADD A, C @@ -856,7 +857,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0xC8: RET_COND(FlagZ); break; // RET Z case 0xC9: RET_(); break; // RET case 0xCA: JP_COND(FlagZ); break; // JP Z - case 0xCB: PREFIX_(IXCBpre); break; // PREFIX IXCB + case 0xCB: PREFETCH_(IXCBpre); break; // PREFIX IXCB case 0xCC: CALL_COND(FlagZ); break; // CALL Z case 0xCD: CALL_COND(true); break; // CALL case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n @@ -1121,7 +1122,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case 0xC8: RET_COND(FlagZ); break; // RET Z case 0xC9: RET_(); break; // RET case 0xCA: JP_COND(FlagZ); break; // JP Z - case 0xCB: PREFIX_(IYCBpre); break; // PREFIy IyCB + case 0xCB: PREFETCH_(IYCBpre); break; // PREFIX IyCB case 0xCC: CALL_COND(FlagZ); break; // CALL Z case 0xCD: CALL_COND(true); break; // CALL case 0xCE: REG_OP_IND_INC(ADC8, A, PCl, PCh); break; // ADC A, n @@ -1181,24 +1182,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // the first byte fetched is the prefetch value to use with the instruction // we pick Ix or Iy here, the indexed value is stored in WZ // In this way, we don't need to pass them as an argument to the I_Funcs. - if (IXCB_prefetch) - { - IXCB_prefetch = false; - PF = opcode; - Regs[ALU] = PF; - PREFETCH_(Ixl, Ixh); - return; - } - - if (IYCB_prefetch) - { - IYCB_prefetch = false; - PF = opcode; - Regs[ALU] = PF; - PREFETCH_(Iyl, Iyh); - return; - } - IXCB_prefix = false; IYCB_prefix = false; NO_prefix = true; diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs index 8dc1cd24ce..6bc915c296 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Interrupts.cs @@ -33,17 +33,20 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void NMI_() { cur_instr = new ushort[] - {IDLE, - DEC16, SPl, SPh, - WR, SPl, SPh, PCh, - IDLE, - DEC16, SPl, SPh, - WR, SPl, SPh, PCl, - IDLE, - ASGN, PCl, 0x66, - ASGN, PCh, 0, - IDLE, + {DEC16, SPl, SPh, + TR, ALU, PCl, + WAIT, + WR_DEC, SPl, SPh, PCh, + TR16, PCl, PCh, NMI_V, ZERO, + WAIT, + WR, SPl, SPh, ALU, + IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } // Mode 0 interrupts only take effect if a CALL or RST is on the data bus @@ -55,60 +58,66 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - RD, ALU, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; } // Just jump to $0038 private void INTERRUPT_1() { cur_instr = new ushort[] - {DEC16, SPl, SPh, - IDLE, - WR, SPl, SPh, PCh, - IDLE, + {IDLE, + TR, ALU, PCl, DEC16, SPl, SPh, IDLE, - WR, SPl, SPh, PCl, - IDLE, - ASGN, PCl, 0x38, - IDLE, - ASGN, PCh, 0, + WAIT, + WR_DEC, SPl, SPh, PCh, + TR16, PCl, PCh, IRQ_V, ZERO, + WAIT, + WR, SPl, SPh, ALU, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } // Interrupt mode 2 uses the I vector combined with a byte on the data bus - // Again for now we assume only a 0 on the data bus and jump to (0xI00) - private void INTERRUPT_2(ushort src) + private void INTERRUPT_2() { cur_instr = new ushort[] - {IDLE, + {FTCH_DB, IDLE, DEC16, SPl, SPh, - WR, SPl, SPh, PCh, + TR16, Z, W, DB, I, + WAIT, + WR_DEC, SPl, SPh, PCh, IDLE, - DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCl, - IDLE, - ASGN, PCl, 0, - TR, PCh, I, - IDLE, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, + WAIT, + RD_INC, PCl, Z, W, IDLE, - RD, W, PCl, PCh, + WAIT, + RD, PCh, Z, W, IDLE, - TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; - } - private static ushort[] INT_vectors = new ushort[] {0x40, 0x48, 0x50, 0x58, 0x60}; + BUSRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0 ,0 ,PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { I, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + } private void ResetInterrupts() { diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs index a312975d78..8bc3938c7d 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/NewDisassembler.cs @@ -435,6 +435,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A addr += extra_inc; size = addr - start_addr; + // handle case of addr wrapping around at 16 bit boundary + if (addr < start_addr) + { + size = (0x10000 + addr) - start_addr; + } + return temp; } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs index cfaf517ec7..69c6b78a7e 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Operations.cs @@ -8,31 +8,54 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public void Read_Func(ushort dest, ushort src_l, ushort src_h) { Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); + Regs[DB] = Regs[dest]; } - public void I_Read_Func(ushort dest, ushort src_l, ushort src_h, ushort inc) + public void Read_INC_Func(ushort dest, ushort src_l, ushort src_h) { - Regs[dest] = ReadMemory((ushort)((Regs[src_l] | (Regs[src_h] << 8)) + inc)); + Regs[dest] = ReadMemory((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); + Regs[DB] = Regs[dest]; + INC16_Func(src_l, src_h); } public void Write_Func(ushort dest_l, ushort dest_h, ushort src) { + Regs[DB] = Regs[src]; WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]); } - public void I_Write_Func(ushort dest_l, ushort dest_h, ushort inc, ushort src) + public void Write_INC_Func(ushort dest_l, ushort dest_h, ushort src) { - WriteMemory((ushort)((Regs[dest_l] | (Regs[dest_h] << 8)) + inc), (byte)Regs[src]); + Regs[DB] = Regs[src]; + WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]); + INC16_Func(dest_l, dest_h); } - public void OUT_Func(ushort dest, ushort src) + public void Write_DEC_Func(ushort dest_l, ushort dest_h, ushort src) { - WriteHardware(Regs[dest], (byte)(Regs[src])); + Regs[DB] = Regs[src]; + WriteMemory((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)Regs[src]); + DEC16_Func(dest_l, dest_h); } - public void IN_Func(ushort dest, ushort src) + public void OUT_Func(ushort dest_l, ushort dest_h, ushort src) { - Regs[dest] = ReadHardware(Regs[src]); + Regs[DB] = Regs[src]; + WriteHardware((ushort)(Regs[dest_l] | (Regs[dest_h] << 8)), (byte)(Regs[src])); + } + + public void IN_Func(ushort dest, ushort src_l, ushort src_h) + { + Regs[dest] = ReadHardware((ushort)(Regs[src_l] | (Regs[src_h]) << 8)); + Regs[DB] = Regs[dest]; + + FlagZ = Regs[dest] == 0; + FlagP = TableParity[Regs[dest]]; + FlagH = false; + FlagN = false; + FlagS = Regs[dest].Bit(7); + Flag5 = Regs[dest].Bit(5); + Flag3 = Regs[dest].Bit(3); } public void TR_Func(ushort dest, ushort src) @@ -734,9 +757,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagZ = Regs[A] == 0; FlagS = Regs[A] > 127; FlagP = iff2; - Flag5 = (Regs[A] & 0x02) != 0; + Flag5 = (Regs[A] & 0x20) != 0; Flag3 = (Regs[A] & 0x08) != 0; } } + + public void FTCH_DB_Func() + { + Regs[DB] = FetchDB(); + } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt b/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt index d11f79b637..db37bed6fa 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/ReadMe.txt @@ -1,8 +1,5 @@ TODO: -Mode 0 and 2 interrupts -Check T-cycle level memory access timing -Check R register -new tests for WZ Registers -Memory refresh - IR is pushed onto the address bus at instruction start, does anything need this? -Data Bus - For mode zero and 2 interrupts, need a system that uses it to test +Mode 0 (nothing to test with) +IRQ BUS timings (nothing to test with) +BusAQ (nothing to test with) diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs index 628243c19e..339fb6864e 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Registers.cs @@ -6,7 +6,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public partial class Z80A { // registers - // note these are not constants. When shadows are used, they will be changed accordingly public ushort PCl = 0; public ushort PCh = 1; public ushort SPl = 2; @@ -40,10 +39,28 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public ushort E_s = 29; public ushort H_s = 30; public ushort L_s = 31; + public ushort DB = 32; + public ushort scratch = 33; + public ushort IRQ_V = 34; // IRQ mode 1 vector + public ushort NMI_V = 35; // NMI vector public ushort[] Regs = new ushort[36]; - public bool FlagI; + // IO Contention Constants. Need to distinguish port access and normal memory accesses for zx spectrum + public const ushort BIO1 = 100; + public const ushort BIO2 = 101; + public const ushort BIO3 = 102; + public const ushort BIO4 = 103; + + public const ushort WIO1 = 105; + public const ushort WIO2 = 106; + public const ushort WIO3 = 107; + public const ushort WIO4 = 108; + + + public bool FlagI, FlagI1, FlagI2, FlagI3, FlagI4, FlagI5; + + public bool FlagW; // wait flag, when set to true reads / writes will be delayed public bool FlagC { @@ -109,6 +126,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { Regs[i] = 0; } + + // the IRQ1 vector is 0x38 + Regs[IRQ_V] = 0x38; + // The NMI vector is constant 0x66 + Regs[NMI_V] = 0x66; + + FlagI = false; + FlagW = false; } private bool[] TableParity; @@ -125,8 +150,5 @@ namespace BizHawk.Emulation.Cores.Components.Z80A TableParity[i] = (Bits & 1) == 0; } } - - - } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs index 71b39d6018..d5a2e8c3a2 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Direct.cs @@ -10,10 +10,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void NOP_() { cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - OP }; + { IDLE, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } // NOTE: In a real Z80, this operation just flips a switch to choose between 2 registers @@ -22,18 +25,24 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {EXCH, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void EXX_() { cur_instr = new ushort[] {EXX, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } // this exchanges 2 16 bit registers @@ -41,32 +50,41 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {EXCH_16, dest_l, dest_h, src_l, src_h, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void INC_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, + {INC16, src_l, src_h, IDLE, IDLE, - INC16, src_l, src_h, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0}; + MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 }; } private void DEC_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, - IDLE, - DEC16, src_l, src_h, + {DEC16, src_l, src_h, IDLE, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0}; + MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 }; } // this is done in two steps technically, but the flags don't work out using existing funcitons @@ -74,26 +92,32 @@ namespace BizHawk.Emulation.Cores.Components.Z80A private void ADD_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, + {TR16, Z, W, dest_l, dest_h, IDLE, - IDLE, - TR16, Z, W, dest_l, dest_h, INC16, Z, W, IDLE, - IDLE, ADD16, dest_l, dest_h, src_l, src_h, IDLE, IDLE, + IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, I, I, I, I, I, I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP(ushort operation, ushort dest, ushort src) { cur_instr = new ushort[] {operation, dest, src, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } // Operations using the I and R registers take one T-cycle longer @@ -101,10 +125,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {operation, dest, src, - IDLE, - IDLE, SET_FL_IR, dest, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, 0 }; } // note: do not use DEC here since no flags are affected by this operation @@ -114,30 +141,36 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + ASGN, B, (ushort)((Regs[B] - 1) & 0xFF), + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - ASGN, B, (ushort)((Regs[B] - 1) & 0xFF), - IDLE, - RD, Z, PCl, PCh, - IDLE, - INC16, PCl, PCh, IDLE, ASGN, W, 0, - IDLE, ADDS, PCl, PCh, Z, W, TR16, Z, W, PCl, PCh, + IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { cur_instr = new ushort[] {IDLE, ASGN, B, (ushort)((Regs[B] - 1) & 0xFF), + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - RD, ALU, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -148,6 +181,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A IDLE, IDLE, HALT }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void JR_COND(bool cond) @@ -156,28 +192,34 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, ASGN, W, 0, IDLE, - ADDS, PCl, PCh, Z, W, - TR16, Z, W, PCl, PCh, IDLE, + ADDS, PCl, PCh, Z, W, + TR16, Z, W, PCl, PCh, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } else { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, ALU, PCl, PCh, IDLE, - IDLE, - RD, ALU, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -187,29 +229,35 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, TR16, PCl, PCh, Z, W, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, 0 }; } else { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - IDLE, - RD, W, PCl, PCh, - INC16, PCl, PCh, - IDLE, + WAIT, + RD_INC, W, PCl, PCh, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -217,45 +265,54 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, SPl, SPh, IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, - IDLE, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, - TR16, PCl, PCh, Z, W, + WAIT, + RD_INC, W, SPl, SPh, + TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void RETI_() { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, SPl, SPh, IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, - IDLE, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, + WAIT, + RD_INC, W, SPl, SPh, TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void RETN_() { cur_instr = new ushort[] {IDLE, - IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, + WAIT, + RD_INC, Z, SPl, SPh, EI_RETN, + WAIT, + RD_INC, W, SPl, SPh, TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } @@ -266,24 +323,30 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - RD, Z, SPl, SPh, - INC16, SPl, SPh, + WAIT, + RD_INC, Z, SPl, SPh, IDLE, - IDLE, - RD, W, SPl, SPh, - INC16, SPl, SPh, - IDLE, + WAIT, + RD_INC, W, SPl, SPh, TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } else { cur_instr = new ushort[] {IDLE, IDLE, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, PCh, 0, 0, 0 }; } } @@ -293,36 +356,42 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, - INC16, PCl, PCh, - IDLE, + WAIT, RD, W, PCl, PCh, INC16, PCl, PCh, - IDLE, DEC16, SPl, SPh, + WAIT, + WR_DEC, SPl, SPh, PCh, IDLE, - WR, SPl, SPh, PCh, - DEC16, SPl, SPh, + WAIT, WR, SPl, SPh, PCl, - IDLE, - TR, PCl, Z, - TR, PCh, W, + TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } else { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - INC16, PCl, PCh, - IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, 0, 0, 0 }; } } @@ -330,34 +399,43 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {operation, src, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void BIT_OP(ushort operation, ushort bit, ushort src) { cur_instr = new ushort[] {operation, bit, src, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void PUSH_(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, + {DEC16, SPl, SPh, IDLE, - DEC16, SPl, SPh, - IDLE, - WR, SPl, SPh, src_h, - IDLE, - DEC16, SPl, SPh, + WAIT, + WR_DEC, SPl, SPh, src_h, IDLE, + WAIT, WR, SPl, SPh, src_l, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } @@ -365,76 +443,112 @@ namespace BizHawk.Emulation.Cores.Components.Z80A { cur_instr = new ushort[] {IDLE, - RD, src_l, SPl, SPh, + WAIT, + RD_INC, src_l, SPl, SPh, IDLE, - INC16, SPl, SPh, - IDLE, - RD, src_h, SPl, SPh, - IDLE, - INC16, SPl, SPh, + WAIT, + RD_INC, src_h, SPl, SPh, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { SPh, 0, 0, SPh, 0, 0, PCh, 0, 0, 0 }; } private void RST_(ushort n) { cur_instr = new ushort[] - {IDLE, + {DEC16, SPl, SPh, IDLE, - DEC16, SPl, SPh, - WR, SPl, SPh, PCh, - DEC16, SPl, SPh, - WR, SPl, SPh, PCl, - IDLE, - ASGN, Z, n, - ASGN, W, 0, + WAIT, + WR_DEC, SPl, SPh, PCh, + RST, n, + WAIT, + WR, SPl, SPh, PCl, TR16, PCl, PCh, Z, W, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, SPh, 0, 0, SPh, 0, 0, W, 0, 0, 0 }; } private void PREFIX_(ushort src) { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, + OP_F, PREFIX, src}; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } - private void PREFETCH_(ushort src_l, ushort src_h) + private void PREFETCH_(ushort src) { + if (src == IXCBpre) + { + Regs[W] = Regs[Ixh]; + Regs[Z] = Regs[Ixl]; + } + else + { + Regs[W] = Regs[Iyh]; + Regs[Z] = Regs[Iyl]; + } + cur_instr = new ushort[] - {TR16, Z, W, src_l, src_h, + {IDLE, + WAIT, + RD_INC, ALU, PCl, PCh, ADDS, Z, W, ALU, ZERO, + WAIT, + OP_F, IDLE, - PREFIX, IXYprefetch }; + PREFIX, src,}; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0 }; } private void DI_() { cur_instr = new ushort[] {DI, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void EI_() { cur_instr = new ushort[] {EI, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void JP_16(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {TR, PCl, src_l, - IDLE, - TR, PCh, src_h, + {TR16, PCl, PCh, src_l, src_h, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0 }; } private void LD_SP_16(ushort src_l, ushort src_h) @@ -442,68 +556,83 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - TR, SPl, src_l, - TR, SPh, src_h, - IDLE, + TR16, SPl, SPh, src_l, src_h, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { I, I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, PCh, 0, 0, 0 }; } private void OUT_() { cur_instr = new ushort[] - {IDLE, - RD, ALU, PCl, PCh, - IDLE, - INC16, PCl, PCh, - TR, W, A, - OUT, ALU, A, - TR, Z, ALU, + {TR, W, A, + WAIT, + RD_INC, Z, PCl, PCh, + IDLE, + WAIT, + WAIT, + OUT, Z, W, A, INC16, Z, ALU, - IDLE, - IDLE, + WAIT, + OP_F, OP}; + + BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; } private void OUT_REG_(ushort dest, ushort src) { cur_instr = new ushort[] - {IDLE, - IDLE, - OUT, dest, src, - IDLE, - TR16, Z, W, C, B, - INC16, Z, W, + {TR16, Z, W, C, B, + IDLE, IDLE, + OUT, Z, W, src, + INC16, Z, W, + WAIT, + OP_F, OP}; + + BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; } private void IN_() { cur_instr = new ushort[] - {IDLE, - RD, ALU, PCl, PCh, + {TR, W, A, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, - TR, W, A, - IN, A, ALU, - TR, Z, ALU, + WAIT, + WAIT, + IN, A, Z, W, INC16, Z, W, - IDLE, - IDLE, + WAIT, + OP_F, OP}; + + BUSRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, WIO1, WIO2, WIO3, WIO4, PCh, 0, 0, 0 }; } private void IN_REG_(ushort dest, ushort src) { cur_instr = new ushort[] - {IDLE, - IDLE, - IN, dest, src, - IDLE, - TR16, Z, W, C, B, + {TR16, Z, W, C, B, + WAIT, + WAIT, + IN, dest, Z, W, INC16, Z, W, - IDLE, + WAIT, + OP_F, OP}; + + BUSRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { BIO1, BIO2, BIO3, BIO4, PCh, 0, 0, 0 }; } private void REG_OP_16_(ushort op, ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) @@ -511,62 +640,74 @@ namespace BizHawk.Emulation.Cores.Components.Z80A cur_instr = new ushort[] {IDLE, IDLE, - IDLE, TR16, Z, W, dest_l, dest_h, INC16, Z, W, IDLE, IDLE, op, dest_l, dest_h, src_l, src_h, IDLE, - IDLE, + WAIT, + OP_F, OP}; + + BUSRQ = new ushort[] { I, I, I, I, I, I, I, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; } private void INT_MODE_(ushort src) { cur_instr = new ushort[] - {IDLE, - IDLE, - INT_MODE, src, + {INT_MODE, src, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; } private void RRD_() { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, L, H, - IDLE, + {TR16, Z, W, L, H, + WAIT, RD, ALU, Z, W, IDLE, RRD, ALU, A, IDLE, - WR, Z, W, ALU, - IDLE, - INC16, Z, W, IDLE, IDLE, + WAIT, + WR_INC, Z, W, ALU, + IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void RLD_() { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, L, H, - IDLE, + {TR16, Z, W, L, H, + WAIT, RD, ALU, Z, W, IDLE, RLD, ALU, A, IDLE, - WR, Z, W, ALU, - IDLE, - INC16, Z, W, IDLE, IDLE, + WAIT, + WR_INC, Z, W, ALU, + IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs index 4a1c46cc03..9c13e3d062 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Tables_Indirect.cs @@ -6,32 +6,38 @@ { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, IDLE, operation, ALU, - IDLE, + WAIT, WR, src_l, src_h, ALU, - IDLE, - IDLE, + IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void BIT_OP_IND(ushort operation, ushort bit, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, - IDLE, operation, bit, ALU, IDLE, + WAIT, WR, src_l, src_h, ALU, IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } // Note that this operation uses I_BIT, same as indexed BIT. @@ -42,214 +48,272 @@ { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, IDLE, I_BIT, bit, ALU, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, src_h, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP_IND_INC(ushort operation, ushort dest, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, - RD, ALU, src_l, src_h, - IDLE, + WAIT, + RD_INC, ALU, src_l, src_h, operation, dest, ALU, - INC16, src_l, src_h, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } private void REG_OP_IND(ushort operation, ushort dest, ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, src_l, src_h, - RD, ALU, Z, W, - INC16, Z, W, - operation, dest, ALU, + {TR16, Z, W, src_l, src_h, + WAIT, + RD_INC, ALU, Z, W, + operation, dest, ALU, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; + } + + // different because HL doesn't effect WZ + private void REG_OP_IND_HL(ushort operation, ushort dest) + { + cur_instr = new ushort[] + {IDLE, + WAIT, + RD, ALU, L, H, + operation, dest, ALU, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; } private void LD_16_IND_nn(ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, - WR, Z, W, src_l, - IDLE, - INC16, Z, W, + WAIT, + WR_INC, Z, W, src_l, IDLE, + WAIT, WR, Z, W, src_h, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_16_nn(ushort dest_l, ushort dest_h) { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, - RD, dest_l, Z, W, - IDLE, - INC16, Z, W, + WAIT, + RD_INC, dest_l, Z, W, IDLE, + WAIT, RD, dest_h, Z, W, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND_nn(ushort src) { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - RD, W, PCl, PCh, - IDLE, - INC16, PCl, PCh, - IDLE, - WR, Z, W, src, - INC16, Z, W, + WAIT, + WR_INC, Z, W, src, TR, W, A, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_8_nn(ushort dest) { cur_instr = new ushort[] {IDLE, - RD, Z, PCl, PCh, + WAIT, + RD_INC, Z, PCl, PCh, IDLE, - INC16, PCl, PCh, + WAIT, + RD_INC, W, PCl, PCh, IDLE, - RD, W, PCl, PCh, + WAIT, + RD_INC, dest, Z, W, IDLE, - INC16, PCl, PCh, - IDLE, - RD, dest, Z, W, - IDLE, - INC16, Z, W, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND(ushort dest_l, ushort dest_h, ushort src) { cur_instr = new ushort[] - {IDLE, - IDLE, - TR16, Z, W, dest_l, dest_h, - WR, Z, W, src, - INC16, Z, W, - TR, W, A, + {TR16, Z, W, dest_l, dest_h, + WAIT, + WR_INC, Z, W, src, + TR, W, A, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { dest_h, 0, 0, PCh, 0, 0, 0 }; + } + + // seperate HL needed since it doesn't effect the WZ pair + private void LD_8_IND_HL(ushort src) + { + cur_instr = new ushort[] + {IDLE, + WAIT, + WR, L, H, src, + IDLE, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { H, 0, 0, PCh, 0, 0, 0 }; } private void LD_8_IND_IND(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, ALU, src_l, src_h, IDLE, - IDLE, - RD, ALU, src_l, src_h, - IDLE, - INC16, src_l, src_h, - IDLE, + WAIT, WR, dest_l, dest_h, ALU, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, dest_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_8_INC(ushort dest, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, dest, src_l, src_h, IDLE, - IDLE, - RD, dest, src_l, src_h, - IDLE, - INC16, src_l, src_h, + WAIT, + OP_F, OP }; - } - private void LD_IND_8_DEC(ushort dest, ushort src_l, ushort src_h) - { - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - RD, dest, src_l, src_h, - IDLE, - DEC16, src_l, src_h, - IDLE, - OP }; + BUSRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, PCh, 0, 0, 0 }; } private void LD_IND_16(ushort dest_l, ushort dest_h, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, + WAIT, + RD_INC, dest_l, src_l, src_h, IDLE, + WAIT, + RD_INC, dest_h, src_l, src_h, IDLE, - RD, dest_l, src_l, src_h, - IDLE, - INC16, src_l, src_h, - RD, dest_h, src_l, src_h, - IDLE, - INC16, src_l, src_h, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void INC_8_IND(ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, - IDLE, INC8, ALU, IDLE, + WAIT, WR, src_l, src_h, ALU, IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } private void DEC_8_IND(ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, RD, ALU, src_l, src_h, - IDLE, DEC8, ALU, IDLE, + WAIT, WR, src_l, src_h, ALU, IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { src_h, 0, 0, src_h, src_h, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { src_h, 0, 0, 0, src_h, 0, 0, PCh, 0, 0, 0 }; } // NOTE: WZ implied for the wollowing 3 functions @@ -257,197 +321,214 @@ { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, RD, ALU, Z, W, - IDLE, operation, ALU, IDLE, + WAIT, WR, Z, W, ALU, - IDLE, TR, dest, ALU, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_OP(ushort operation, ushort bit, ushort dest) { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, RD, ALU, Z, W, IDLE, operation, bit, ALU, - IDLE, + WAIT, WR, Z, W, ALU, - IDLE, TR, dest, ALU, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_BIT_TE(ushort bit) { cur_instr = new ushort[] {IDLE, - IDLE, - IDLE, + WAIT, RD, ALU, Z, W, IDLE, I_BIT, bit, ALU, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { W, 0, 0, W, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { W, 0, 0, 0, PCh, 0, 0, 0 }; } private void I_OP_n(ushort operation, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, RD, ALU, PCl, PCh, - INC16, PCl, PCh, + IDLE, IDLE, TR16, Z, W, src_l, src_h, + ADDS, Z, W, ALU, ZERO, IDLE, - ADDS, Z, W, ALU, ZERO, - IDLE, + INC16, PCl, PCh, + WAIT, RD, ALU, Z, W, - IDLE, - IDLE, operation, ALU, IDLE, - IDLE, - IDLE, + WAIT, WR, Z, W, ALU, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, W, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_OP_n_n(ushort src_l, ushort src_h) { cur_instr = new ushort[] - {IDLE, - IDLE, - RD, ALU, PCl, PCh, - INC16, PCl, PCh, - IDLE, - TR16, Z, W, src_l, src_h, - IDLE, + {TR16, Z, W, src_l, src_h, + WAIT, + RD_INC, ALU, PCl, PCh, ADDS, Z, W, ALU, ZERO, - IDLE, + WAIT, RD, ALU, PCl, PCh, - INC16, PCl, PCh, IDLE, + IDLE, + INC16, PCl, PCh, + WAIT, WR, Z, W, ALU, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, PCh, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_REG_OP_IND_n(ushort operation, ushort dest, ushort src_l, ushort src_h) { cur_instr = new ushort[] {IDLE, - IDLE, + WAIT, RD, ALU, PCl, PCh, IDLE, - INC16, PCl, PCh, - IDLE, TR16, Z, W, src_l, src_h, IDLE, ADDS, Z, W, ALU, ZERO, IDLE, + INC16, PCl, PCh, + WAIT, RD, ALU, Z, W, - IDLE, - operation, dest, ALU, - IDLE, + operation, dest, ALU, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, W, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, W, 0, 0, PCh, 0, 0, 0 }; } private void I_LD_8_IND_n(ushort dest_l, ushort dest_h, ushort src) { cur_instr = new ushort[] {IDLE, + WAIT, RD, ALU, PCl, PCh, IDLE, - INC16, PCl, PCh, IDLE, TR16, Z, W, dest_l, dest_h, - IDLE, ADDS, Z, W, ALU, ZERO, IDLE, - WR, Z, W, src, - IDLE, - IDLE, - IDLE, + INC16, PCl, PCh, + WAIT, + WR, Z, W, src, IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, PCh, PCh, PCh, PCh, PCh, Z, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0, 0, 0, 0, 0, Z, 0, 0, PCh, 0, 0, 0 }; } private void LD_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {RD, ALU, L, H, - IDLE, - WR, E, D, ALU, - IDLE, - operation, L, H, - IDLE, - operation, E, D, - IDLE, - DEC16, C, B, - SET_FL_LD, - IDLE, - OP_R, 0, operation, repeat_instr }; + {IDLE, + WAIT, + RD, ALU, L, H, + operation, L, H, + WAIT, + WR, E, D, ALU, + IDLE, + SET_FL_LD_R, 0, operation, repeat_instr}; + + BUSRQ = new ushort[] { H, 0, 0, D, 0, 0, D, D }; + MEMRQ = new ushort[] { H, 0, 0, D, 0, 0, 0, 0 }; } private void CP_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {IDLE, - IDLE, + {IDLE, + WAIT, RD, ALU, L, H, - operation, L, H, - IDLE, IDLE, DEC16, C, B, - SET_FL_CP, - IDLE, operation, Z, W, IDLE, - OP_R, 1, operation, repeat_instr }; + SET_FL_CP_R, 1, operation, repeat_instr}; + + BUSRQ = new ushort[] { H, 0, 0, H, H, H, H, H }; + MEMRQ = new ushort[] { H, 0, 0, 0, 0, 0, 0, 0 }; } private void IN_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {IN, ALU, C, + {IDLE, IDLE, - WR, L, H, ALU, + WAIT, + WAIT, + IN, ALU, C, B, IDLE, - operation, L, H, - IDLE, - TR16, Z, W, C, B, - operation, Z, W, - IDLE, - DEC8, B, - IDLE, - OP_R, 2, operation, repeat_instr }; + WAIT, + REP_OP_I, L, H, ALU, operation, 2, operation, repeat_instr }; + + BUSRQ = new ushort[] { I, BIO1, BIO2, BIO3, BIO4, H, 0, 0}; + MEMRQ = new ushort[] { 0, BIO1, BIO2, BIO3, BIO4, H, 0, 0 }; } private void OUT_OP_R(ushort operation, ushort repeat_instr) { cur_instr = new ushort[] - {RD, ALU, L, H, + {IDLE, IDLE, - OUT, C, ALU, + WAIT, + RD, ALU, L, H, IDLE, - IDLE, - operation, L, H, - DEC8, B, - IDLE, - TR16, Z, W, C, B, - operation, Z, W, - IDLE, - OP_R, 3, operation, repeat_instr }; + WAIT, + WAIT, + REP_OP_O, C, B, ALU, operation, 3, operation, repeat_instr }; + + BUSRQ = new ushort[] { I, H, 0, 0, BIO1, BIO2, BIO3, BIO4 }; + MEMRQ = new ushort[] { 0, H, 0, 0, BIO1, BIO2, BIO3, BIO4 }; } // this is an indirect change of a a 16 bit register with memory @@ -455,24 +536,27 @@ { cur_instr = new ushort[] {IDLE, - IDLE, - RD, Z, dest_l, dest_h, + WAIT, + RD_INC, Z, dest_l, dest_h, IDLE, - IDLE, - I_RD, W, dest_l, dest_h, 1, + WAIT, + RD, W, dest_l, dest_h, IDLE, IDLE, - WR, dest_l, dest_h, src_l, - IDLE, - IDLE, - I_WR, dest_l, dest_h, 1, src_h, + WAIT, + WR_DEC, dest_l, dest_h, src_h, IDLE, + WAIT, + WR, dest_l, dest_h, src_l, + IDLE, IDLE, TR16, src_l, src_h, Z, W, - IDLE, - IDLE, - IDLE, + WAIT, + OP_F, OP }; + + BUSRQ = new ushort[] { dest_h, 0, 0, dest_h, 0, 0, dest_h, dest_h, 0, 0, dest_h, 0, 0, dest_h, dest_h, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { dest_h, 0, 0, dest_h, 0, 0, 0, dest_h, 0, 0, dest_h, 0, 0, 0, 0, PCh, 0, 0, 0 }; } } } diff --git a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs index 46abee9809..242b21dbac 100644 --- a/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs +++ b/BizHawk.Emulation.Cores/CPUs/Z80A/Z80A.cs @@ -14,67 +14,75 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // operations that can take place in an instruction public const ushort IDLE = 0; public const ushort OP = 1; - public const ushort OP_R = 2; // used for repeating operations + public const ushort OP_F = 2; // used for repeating operations public const ushort HALT = 3; public const ushort RD = 4; public const ushort WR = 5; - public const ushort I_RD = 6; - public const ushort I_WR = 7; - public const ushort TR = 8; - public const ushort TR16 = 9; - public const ushort ADD16 = 10; - public const ushort ADD8 = 11; - public const ushort SUB8 = 12; - public const ushort ADC8 = 13; - public const ushort SBC8 = 14; - public const ushort SBC16 = 15; - public const ushort ADC16 = 16; - public const ushort INC16 = 17; - public const ushort INC8 = 18; - public const ushort DEC16 = 19; - public const ushort DEC8 = 20; - public const ushort RLC = 21; - public const ushort RL = 22; - public const ushort RRC = 23; - public const ushort RR = 24; - public const ushort CPL = 25; - public const ushort DA = 26; - public const ushort SCF = 27; - public const ushort CCF = 28; - public const ushort AND8 = 29; - public const ushort XOR8 = 30; - public const ushort OR8 = 31; - public const ushort CP8 = 32; - public const ushort SLA = 33; - public const ushort SRA = 34; - public const ushort SRL = 35; - public const ushort SLL = 36; - public const ushort BIT = 37; - public const ushort RES = 38; - public const ushort SET = 39; - public const ushort EI = 40; - public const ushort DI = 41; - public const ushort EXCH = 42; - public const ushort EXX = 43; - public const ushort EXCH_16 = 44; - public const ushort PREFIX = 45; - public const ushort PREFETCH = 46; - public const ushort ASGN = 47; - public const ushort ADDS = 48; // signed 16 bit operation used in 2 instructions - public const ushort INT_MODE = 49; - public const ushort EI_RETN = 50; - public const ushort EI_RETI = 51; // reti has no delay in interrupt enable - public const ushort OUT = 52; - public const ushort IN = 53; - public const ushort NEG = 54; - public const ushort RRD = 55; - public const ushort RLD = 56; - public const ushort SET_FL_LD = 57; - public const ushort SET_FL_CP = 58; - public const ushort SET_FL_IR = 59; - public const ushort I_BIT = 60; - public const ushort HL_BIT = 61; + public const ushort RD_INC = 6; // read and increment + public const ushort WR_INC = 7; // write and increment + public const ushort WR_DEC = 8; // write and increment (for stack pointer) + public const ushort TR = 9; + public const ushort TR16 = 10; + public const ushort ADD16 = 11; + public const ushort ADD8 = 12; + public const ushort SUB8 = 13; + public const ushort ADC8 = 14; + public const ushort SBC8 = 15; + public const ushort SBC16 = 16; + public const ushort ADC16 = 17; + public const ushort INC16 = 18; + public const ushort INC8 = 19; + public const ushort DEC16 = 20; + public const ushort DEC8 = 21; + public const ushort RLC = 22; + public const ushort RL = 23; + public const ushort RRC = 24; + public const ushort RR = 25; + public const ushort CPL = 26; + public const ushort DA = 27; + public const ushort SCF = 28; + public const ushort CCF = 29; + public const ushort AND8 = 30; + public const ushort XOR8 = 31; + public const ushort OR8 = 32; + public const ushort CP8 = 33; + public const ushort SLA = 34; + public const ushort SRA = 35; + public const ushort SRL = 36; + public const ushort SLL = 37; + public const ushort BIT = 38; + public const ushort RES = 39; + public const ushort SET = 40; + public const ushort EI = 41; + public const ushort DI = 42; + public const ushort EXCH = 43; + public const ushort EXX = 44; + public const ushort EXCH_16 = 45; + public const ushort PREFIX = 46; + public const ushort PREFETCH = 47; + public const ushort ASGN = 48; + public const ushort ADDS = 49; // signed 16 bit operation used in 2 instructions + public const ushort INT_MODE = 50; + public const ushort EI_RETN = 51; + public const ushort EI_RETI = 52; // reti has no delay in interrupt enable + public const ushort OUT = 53; + public const ushort IN = 54; + public const ushort NEG = 55; + public const ushort RRD = 56; + public const ushort RLD = 57; + public const ushort SET_FL_LD_R = 58; + public const ushort SET_FL_CP_R = 59; + public const ushort SET_FL_IR = 60; + public const ushort I_BIT = 61; + public const ushort HL_BIT = 62; + public const ushort FTCH_DB = 63; + public const ushort WAIT = 64; // enterred when readin or writing and FlagW is true + public const ushort RST = 65; + public const ushort REP_OP_I = 66; + public const ushort REP_OP_O = 67; + // non-state variables + public ushort Ztemp1, Ztemp2, Ztemp3, Ztemp4; public byte temp_R; public Z80A() @@ -88,8 +96,18 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ResetRegisters(); ResetInterrupts(); TotalExecutedCycles = 0; - cur_instr = new ushort[] { OP }; - instr_pntr = 0; + cur_instr = new ushort[] + {IDLE, + DEC16, F, A, + DEC16, SPl, SPh, + IDLE, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { 0, 0, 0, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, PCh, 0, 0, 0 }; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; NO_prefix = true; } @@ -106,6 +124,11 @@ namespace BizHawk.Emulation.Cores.Components.Z80A public Func ReadHardware; public Action WriteHardware; + // Data Bus + // Interrupting Devices are responsible for putting a value onto the data bus + // for as long as the interrupt is valid + public Func FetchDB; + //this only calls when the first byte of an instruction is fetched. public Action OnExecFetch; @@ -140,7 +163,13 @@ namespace BizHawk.Emulation.Cores.Components.Z80A // Execute instructions public void ExecuteOne() { - if (Regs[A] > 255) { Console.WriteLine(RegPC); } + FlagI5 = FlagI4; + FlagI4 = FlagI3; + FlagI3 = FlagI2; + FlagI2 = FlagI1; + FlagI1 = FlagI; + + bus_pntr++; mem_pntr++; switch (cur_instr[instr_pntr++]) { case IDLE: @@ -168,8 +197,9 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = false; NMI_(); NMICallback(); + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; } - else if (iff1 && FlagI) + else if (iff1 && FlagI5) { iff1 = iff2 = false; EI_pending = 0; @@ -190,155 +220,34 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INTERRUPT_1(); break; case 2: - // Low byte of interrupt vector comes from data bus - // We'll assume it's zero for now - INTERRUPT_2(0); + INTERRUPT_2(); break; } IRQCallback(); + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; } else { if (OnExecFetch != null) OnExecFetch(RegPC); if (TraceCallback != null) TraceCallback(State()); - FetchInstruction(FetchMemory(RegPC++)); + RegPC++; + FetchInstruction(); + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; } - instr_pntr = 0; temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); break; - case OP_R: - // determine if we repeat based on what operation we are doing - // single execution versions also come here, but never repeat - ushort temp1 = cur_instr[instr_pntr++]; - ushort temp2 = cur_instr[instr_pntr++]; - ushort temp3 = cur_instr[instr_pntr++]; - - bool repeat = false; - int Reg16_d = Regs[C] | (Regs[B] << 8); - switch (temp1) - { - case 0: - repeat = Reg16_d != 0; - break; - case 1: - repeat = (Reg16_d != 0) && !FlagZ; - break; - case 2: - repeat = Regs[B] != 0; - break; - case 3: - repeat = Regs[B] != 0; - break; - } - - // if we repeat, we do a 5 cycle refresh which decrements PC by 2 - // if we don't repeat, continue on as a normal opcode fetch - if (repeat && temp3 > 0) - { - cur_instr = new ushort[] - {IDLE, - DEC16, PCl, PCh, - IDLE, - DEC16, PCl, PCh, - OP }; - - // adjust WZ register accordingly - switch (temp1) - { - case 0: - // TEST: PC before or after the instruction? - Regs[Z] = Regs[PCl]; - Regs[W] = Regs[PCh]; - INC16_Func(Z, W); - break; - case 1: - // TEST: PC before or after the instruction? - Regs[Z] = Regs[PCl]; - Regs[W] = Regs[PCh]; - INC16_Func(Z, W); - break; - case 2: - // Nothing - break; - case 3: - // Nothing - break; - } - } - else - { - // Interrupts can occur at this point, so process them accordingly - // Read the opcode of the next instruction - if (EI_pending > 0) - { - EI_pending--; - if (EI_pending == 0) { IFF1 = IFF2 = true; } - } - - // Process interrupt requests. - if (nonMaskableInterruptPending) - { - nonMaskableInterruptPending = false; - - if (TraceCallback != null) - { - TraceCallback(new TraceInfo{Disassembly = "====NMI====", RegisterInfo = ""}); - } - - iff2 = iff1; - iff1 = false; - NMI_(); - NMICallback(); - } - else if (iff1 && FlagI) - { - iff1 = iff2 = false; - EI_pending = 0; - - if (TraceCallback != null) - { - TraceCallback(new TraceInfo{Disassembly = "====IRQ====", RegisterInfo = ""}); - } - - switch (interruptMode) - { - case 0: - // Requires something to be pushed onto the data bus - // we'll assume it's a zero for now - INTERRUPT_0(0); - break; - case 1: - INTERRUPT_1(); - break; - case 2: - // Low byte of interrupt vector comes from data bus - // We'll assume it's zero for now - INTERRUPT_2(0); - break; - } - IRQCallback(); - } - else - { - if (OnExecFetch != null) OnExecFetch(RegPC); - if (TraceCallback != null) TraceCallback(State()); - FetchInstruction(FetchMemory(RegPC++)); - } - - temp_R = (byte)(Regs[R] & 0x7F); - temp_R++; - temp_R &= 0x7F; - Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); - } - instr_pntr = 0; + case OP_F: + opcode = FetchMemory(RegPC); break; - case HALT: halted = true; + // NOTE: Check how halt state effects the DB + Regs[DB] = 0xFF; + if (EI_pending > 0) { EI_pending--; @@ -361,7 +270,7 @@ namespace BizHawk.Emulation.Cores.Components.Z80A NMICallback(); halted = false; } - else if (iff1 && FlagI) + else if (iff1 && FlagI5) { iff1 = iff2 = false; EI_pending = 0; @@ -382,28 +291,19 @@ namespace BizHawk.Emulation.Cores.Components.Z80A INTERRUPT_1(); break; case 2: - // Low byte of interrupt vector comes from data bus - // We'll assume it's zero for now - INTERRUPT_2(0); + INTERRUPT_2(); break; } IRQCallback(); halted = false; } - else - { - cur_instr = new ushort[] - {IDLE, - IDLE, - IDLE, - HALT }; - } + temp_R = (byte)(Regs[R] & 0x7F); temp_R++; temp_R &= 0x7F; Regs[R] = (byte)((Regs[R] & 0x80) | temp_R); - instr_pntr = 0; + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; case RD: Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -411,11 +311,14 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case WR: Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; - case I_RD: - I_Read_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + case RD_INC: + Read_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; - case I_WR: - I_Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + case WR_INC: + Write_INC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + case WR_DEC: + Write_DEC_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case TR: TR_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); @@ -540,11 +443,12 @@ namespace BizHawk.Emulation.Cores.Components.Z80A if (prefix_src == EXTDpre) { EXTD_prefix = true; } if (prefix_src == IXpre) { IX_prefix = true; } if (prefix_src == IYpre) { IY_prefix = true; } - if (prefix_src == IXCBpre) { IXCB_prefix = true; IXCB_prefetch = true; } - if (prefix_src == IYCBpre) { IYCB_prefix = true; IYCB_prefetch = true; } + if (prefix_src == IXCBpre) { IXCB_prefix = true; } + if (prefix_src == IYCBpre) { IYCB_prefix = true; } - FetchInstruction(FetchMemory(RegPC++)); - instr_pntr = 0; + RegPC++; + FetchInstruction(); + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; // only the first prefix in a double prefix increases R, although I don't know how / why if (prefix_src < 4) { @@ -569,10 +473,10 @@ namespace BizHawk.Emulation.Cores.Components.Z80A iff1 = iff2; break; case OUT: - OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case IN: - IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + IN_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; case NEG: NEG_8_Func(cur_instr[instr_pntr++]); @@ -586,17 +490,217 @@ namespace BizHawk.Emulation.Cores.Components.Z80A case RLD: RLD_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); break; - case SET_FL_LD: + case SET_FL_LD_R: + DEC16_Func(C, B); SET_FL_LD_Func(); + + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0)) + { + cur_instr = new ushort[] + {DEC16, PCl, PCh, + DEC16, PCl, PCh, + TR16, Z, W, PCl, PCh, + INC16, Z, W, + IDLE, + Ztemp2, E, D, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { D, D, D, D, D, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { Ztemp2, E, D, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; - case SET_FL_CP: + case SET_FL_CP_R: SET_FL_CP_Func(); + + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if (((Regs[C] | (Regs[B] << 8)) != 0) && (Ztemp3 > 0) && !FlagZ) + { + cur_instr = new ushort[] + {DEC16, PCl, PCh, + DEC16, PCl, PCh, + TR16, Z, W, PCl, PCh, + INC16, Z, W, + IDLE, + Ztemp2, L, H, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { Ztemp2, L, H, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; break; case SET_FL_IR: SET_FL_IR_Func(cur_instr[instr_pntr++]); break; + case FTCH_DB: + FTCH_DB_Func(); + break; + case WAIT: + if (FlagW) + { + instr_pntr--; bus_pntr--; mem_pntr--; + } + break; + case RST: + Regs[Z] = cur_instr[instr_pntr++]; + Regs[W] = 0; + break; + case REP_OP_I: + Write_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + + Ztemp4 = cur_instr[instr_pntr++]; + if (Ztemp4 == DEC16) + { + TR16_Func(Z, W, C, B); + DEC16_Func(Z, W); + DEC8_Func(B); + + // take care of other flags + // taken from 'undocumented z80 documented' and Fuse + FlagN = Regs[ALU].Bit(7); + FlagH = FlagC = ((Regs[ALU] + Regs[C] - 1) & 0xFF) < Regs[ALU]; + FlagP = TableParity[((Regs[ALU] + Regs[C] - 1) & 7) ^ Regs[B]]; + } + else + { + TR16_Func(Z, W, C, B); + INC16_Func(Z, W); + DEC8_Func(B); + + // take care of other flags + // taken from 'undocumented z80 documented' and Fuse + FlagN = Regs[ALU].Bit(7); + FlagH = FlagC = ((Regs[ALU] + Regs[C] + 1) & 0xFF) < Regs[ALU]; + FlagP = TableParity[((Regs[ALU] + Regs[C] + 1) & 7) ^ Regs[B]]; + } + + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if ((Regs[B] != 0) && (Ztemp3 > 0)) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + DEC16, PCl, PCh, + DEC16, PCl, PCh, + IDLE, + Ztemp2, L, H, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { H, H, H, H, H, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { Ztemp2, L, H, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; + break; + case REP_OP_O: + OUT_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + + Ztemp4 = cur_instr[instr_pntr++]; + if (Ztemp4 == DEC16) + { + DEC16_Func(L, H); + DEC8_Func(B); + TR16_Func(Z, W, C, B); + DEC16_Func(Z, W); + } + else + { + INC16_Func(L, H); + DEC8_Func(B); + TR16_Func(Z, W, C, B); + INC16_Func(Z, W); + } + + // take care of other flags + // taken from 'undocumented z80 documented' + FlagN = Regs[ALU].Bit(7); + FlagH = FlagC = (Regs[ALU] + Regs[L]) > 0xFF; + FlagP = TableParity[((Regs[ALU] + Regs[L]) & 7) ^ (Regs[B])]; + + Ztemp1 = cur_instr[instr_pntr++]; + Ztemp2 = cur_instr[instr_pntr++]; + Ztemp3 = cur_instr[instr_pntr++]; + + if ((Regs[B] != 0) && (Ztemp3 > 0)) + { + cur_instr = new ushort[] + {IDLE, + IDLE, + DEC16, PCl, PCh, + DEC16, PCl, PCh, + IDLE, + IDLE, + WAIT, + OP_F, + OP}; + + BUSRQ = new ushort[] { B, B, B, B, B, PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { 0, 0, 0, 0, 0, PCh, 0, 0, 0 }; + } + else + { + cur_instr = new ushort[] + { IDLE, + WAIT, + OP_F, + OP }; + + BUSRQ = new ushort[] { PCh, 0, 0, 0 }; + MEMRQ = new ushort[] { PCh, 0, 0, 0 }; + } + instr_pntr = 0; bus_pntr = 0; mem_pntr = 0; + break; } - totalExecutedCycles++; + TotalExecutedCycles++; } // tracer stuff @@ -651,8 +755,8 @@ namespace BizHawk.Emulation.Cores.Components.Z80A FlagI ? "E" : "e") }; } - // State Save/Load + // State Save/Load public void SyncState(Serializer ser) { ser.BeginSection("Z80A"); @@ -663,13 +767,23 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("IFF1", ref iff1); ser.Sync("IFF2", ref iff2); ser.Sync("Halted", ref halted); - ser.Sync("ExecutedCycles", ref totalExecutedCycles); + ser.Sync("ExecutedCycles", ref TotalExecutedCycles); ser.Sync("EI_pending", ref EI_pending); - ser.Sync("instruction_pointer", ref instr_pntr); - ser.Sync("current instruction", ref cur_instr, false); + ser.Sync("instr_pntr", ref instr_pntr); + ser.Sync("bus_pntr", ref bus_pntr); + ser.Sync("mem_pntr", ref mem_pntr); + ser.Sync("cur_instr", ref cur_instr, false); + ser.Sync("BUSRQ", ref BUSRQ, false); + ser.Sync("MEMRQ", ref MEMRQ, false); ser.Sync("opcode", ref opcode); ser.Sync("FlagI", ref FlagI); + ser.Sync("FlagI1", ref FlagI1); + ser.Sync("FlagI2", ref FlagI2); + ser.Sync("FlagI3", ref FlagI3); + ser.Sync("FlagI4", ref FlagI4); + ser.Sync("FlagI5", ref FlagI5); + ser.Sync("FlagW", ref FlagW); ser.Sync("NO Preifx", ref NO_prefix); ser.Sync("CB Preifx", ref CB_prefix); @@ -678,9 +792,6 @@ namespace BizHawk.Emulation.Cores.Components.Z80A ser.Sync("IXCB_prefix", ref IXCB_prefix); ser.Sync("IYCB_prefix", ref IYCB_prefix); ser.Sync("EXTD_prefix", ref EXTD_prefix); - ser.Sync("IXCB_prefetch", ref IXCB_prefetch); - ser.Sync("IYCB_prefetch", ref IYCB_prefetch); - ser.Sync("PF", ref PF); ser.EndSection(); } diff --git a/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs b/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs index c5d795fcd5..e6477b56fd 100644 --- a/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Calculator/TI83.IDebuggable.cs @@ -12,35 +12,35 @@ namespace BizHawk.Emulation.Cores.Calculators { return new Dictionary { - ["A"] = _cpu.Regs[_cpu.A], - ["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8), - ["B"] = _cpu.Regs[_cpu.B], - ["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8), - ["C"] = _cpu.Regs[_cpu.C], - ["D"] = _cpu.Regs[_cpu.D], - ["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8), - ["E"] = _cpu.Regs[_cpu.E], - ["F"] = _cpu.Regs[_cpu.F], - ["H"] = _cpu.Regs[_cpu.H], - ["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8), - ["I"] = _cpu.Regs[_cpu.I], - ["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8), - ["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), - ["L"] = _cpu.Regs[_cpu.L], - ["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8), - ["R"] = _cpu.Regs[_cpu.R], - ["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8), - ["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8), - ["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8), - ["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8), - ["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), - ["Flag C"] = _cpu.FlagC, - ["Flag N"] = _cpu.FlagN, - ["Flag P/V"] = _cpu.FlagP, - ["Flag 3rd"] = _cpu.Flag3, - ["Flag H"] = _cpu.FlagH, - ["Flag 5th"] = _cpu.Flag5, - ["Flag Z"] = _cpu.FlagZ, + ["A"] = _cpu.Regs[_cpu.A], + ["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8), + ["B"] = _cpu.Regs[_cpu.B], + ["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8), + ["C"] = _cpu.Regs[_cpu.C], + ["D"] = _cpu.Regs[_cpu.D], + ["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8), + ["E"] = _cpu.Regs[_cpu.E], + ["F"] = _cpu.Regs[_cpu.F], + ["H"] = _cpu.Regs[_cpu.H], + ["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8), + ["I"] = _cpu.Regs[_cpu.I], + ["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8), + ["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["L"] = _cpu.Regs[_cpu.L], + ["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8), + ["R"] = _cpu.Regs[_cpu.R], + ["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8), + ["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8), + ["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8), + ["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8), + ["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["Flag C"] = _cpu.FlagC, + ["Flag N"] = _cpu.FlagN, + ["Flag P/V"] = _cpu.FlagP, + ["Flag 3rd"] = _cpu.Flag3, + ["Flag H"] = _cpu.FlagH, + ["Flag 5th"] = _cpu.Flag5, + ["Flag Z"] = _cpu.FlagZ, ["Flag S"] = _cpu.FlagS }; } @@ -49,85 +49,85 @@ namespace BizHawk.Emulation.Cores.Calculators { switch (register) { - default: - throw new InvalidOperationException(); - case "A": - _cpu.Regs[_cpu.A] = (ushort)value; - break; - case "AF": - _cpu.Regs[_cpu.F] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00); - break; - case "B": - _cpu.Regs[_cpu.B] = (ushort)value; - break; - case "BC": - _cpu.Regs[_cpu.C] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00); - break; - case "C": - _cpu.Regs[_cpu.C] = (ushort)value; - break; - case "D": - _cpu.Regs[_cpu.D] = (ushort)value; - break; - case "DE": - _cpu.Regs[_cpu.E] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00); - break; - case "E": - _cpu.Regs[_cpu.E] = (ushort)value; - break; - case "F": - _cpu.Regs[_cpu.F] = (ushort)value; - break; - case "H": - _cpu.Regs[_cpu.H] = (ushort)value; - break; - case "HL": - _cpu.Regs[_cpu.L] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00); - break; - case "I": - _cpu.Regs[_cpu.I] = (ushort)value; - break; - case "IX": - _cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00); - break; - case "IY": - _cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00); - break; - case "L": - _cpu.Regs[_cpu.L] = (ushort)value; - break; - case "PC": - _cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00); - break; - case "R": - _cpu.Regs[_cpu.R] = (ushort)value; - break; - case "Shadow AF": - _cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00); - break; - case "Shadow BC": - _cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00); - break; - case "Shadow DE": - _cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00); - break; - case "Shadow HL": - _cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00); - break; - case "SP": - _cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF); - _cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00); + default: + throw new InvalidOperationException(); + case "A": + _cpu.Regs[_cpu.A] = (ushort)value; + break; + case "AF": + _cpu.Regs[_cpu.F] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00); + break; + case "B": + _cpu.Regs[_cpu.B] = (ushort)value; + break; + case "BC": + _cpu.Regs[_cpu.C] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00); + break; + case "C": + _cpu.Regs[_cpu.C] = (ushort)value; + break; + case "D": + _cpu.Regs[_cpu.D] = (ushort)value; + break; + case "DE": + _cpu.Regs[_cpu.E] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00); + break; + case "E": + _cpu.Regs[_cpu.E] = (ushort)value; + break; + case "F": + _cpu.Regs[_cpu.F] = (ushort)value; + break; + case "H": + _cpu.Regs[_cpu.H] = (ushort)value; + break; + case "HL": + _cpu.Regs[_cpu.L] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00); + break; + case "I": + _cpu.Regs[_cpu.I] = (ushort)value; + break; + case "IX": + _cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00); + break; + case "IY": + _cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00); + break; + case "L": + _cpu.Regs[_cpu.L] = (ushort)value; + break; + case "PC": + _cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00); + break; + case "R": + _cpu.Regs[_cpu.R] = (ushort)value; + break; + case "Shadow AF": + _cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00); + break; + case "Shadow BC": + _cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00); + break; + case "Shadow DE": + _cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00); + break; + case "Shadow HL": + _cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00); + break; + case "SP": + _cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00); break; } } @@ -145,6 +145,6 @@ namespace BizHawk.Emulation.Cores.Calculators return false; } - public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Calculator/TI83.cs b/BizHawk.Emulation.Cores/Calculator/TI83.cs index cfe82f74eb..2cc63e9b7c 100644 --- a/BizHawk.Emulation.Cores/Calculator/TI83.cs +++ b/BizHawk.Emulation.Cores/Calculator/TI83.cs @@ -50,7 +50,7 @@ namespace BizHawk.Emulation.Cores.Calculators private readonly byte[] _rom; // configuration - private IController _controller; + private IController _controller = NullController.Instance; private byte[] _ram; private byte[] _vram = new byte[0x300]; @@ -137,6 +137,8 @@ namespace BizHawk.Emulation.Cores.Calculators private void WriteHardware(ushort addr, byte value) { + addr &= 0xFF; + switch (addr) { case 0: // PORT_LINK @@ -232,6 +234,8 @@ namespace BizHawk.Emulation.Cores.Calculators private byte ReadHardware(ushort addr) { + addr &= 0xFF; + switch (addr) { case 0: // PORT_LINK diff --git a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs index f36cef6f8a..1284ab2dd0 100644 --- a/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/AppleII/AppleII.IDebuggable.cs @@ -104,7 +104,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII } } - public int TotalExecutedCycles => (int)_machine.Cpu.Cycles; + public long TotalExecutedCycles => _machine.Cpu.Cycles; private RegisterValue GetRegisterValue(KeyValuePair reg) { diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs index 29ae7368c5..29d110c705 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.IDebuggable.cs @@ -63,7 +63,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 _selectedDebuggable.Step(type); } - public int TotalExecutedCycles => _selectedDebuggable.TotalExecutedCycles; + public long TotalExecutedCycles => _selectedDebuggable.TotalExecutedCycles; private readonly IMemoryCallbackSystem _memoryCallbacks; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs index e0f51a0b5d..b4b5a7b9b1 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/C64.Motherboard.cs @@ -29,7 +29,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64 // ports public readonly CartridgePort CartPort; public readonly CassettePort Cassette; - public IController Controller; + public IController Controller = NullController.Instance; public readonly SerialPort Serial; public readonly TapeDrive TapeDrive; public readonly UserPort User; diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs index b64b70cb3e..67ba9a45c1 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.IDebuggable.cs @@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS } } - int IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; + long IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; private void StepInto() { diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs index 1c2f2e73c8..8d3e5125b0 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/MOS/Chip6510.cs @@ -10,11 +10,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS public sealed partial class Chip6510 { // ------------------------------------ - private readonly MOS6502X _cpu; + private readonly MOS6502X _cpu; private bool _pinNmiLast; private LatchedPort _port; private bool _thisNmi; + private struct CpuLink : IMOS6502XLink + { + private readonly Chip6510 _chip; + + public CpuLink(Chip6510 chip) + { + _chip = chip; + } + + public byte DummyReadMemory(ushort address) => unchecked((byte)_chip.Read(address)); + + public void OnExecFetch(ushort address) { } + + public byte PeekMemory(ushort address) => unchecked((byte)_chip.Peek(address)); + + public byte ReadMemory(ushort address) => unchecked((byte)_chip.Read(address)); + + public void WriteMemory(ushort address, byte value) => _chip.Write(address, value); + } + public Func PeekMemory; public Action PokeMemory; public Func ReadAec; @@ -33,13 +53,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS public Chip6510() { // configure cpu r/w - _cpu = new MOS6502X - { - DummyReadMemory = CpuRead, - ReadMemory = CpuRead, - WriteMemory = CpuWrite, - PeekMemory = CpuPeek - }; + _cpu = new MOS6502X(new CpuLink(this)); // perform hard reset HardReset(); diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs index 8f51fa55c0..e869a3ecfb 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.IDebuggable.cs @@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial } } - int IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; + long IDebuggable.TotalExecutedCycles => _cpu.TotalExecutedCycles; private void StepInto() { diff --git a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs index ddd47146a7..11846447a6 100644 --- a/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs +++ b/BizHawk.Emulation.Cores/Computers/Commodore64/Serial/Drive1541.cs @@ -19,7 +19,7 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial private bool _motorEnabled; private bool _ledEnabled; private int _motorStep; - private readonly MOS6502X _cpu; + private readonly MOS6502X _cpu; private int[] _ram; public readonly Via Via0; public readonly Via Via1; @@ -31,15 +31,31 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64.Serial public Action DebuggerStep; public readonly Chip23128 DriveRom; + private struct CpuLink : IMOS6502XLink + { + private readonly Drive1541 _drive; + + public CpuLink(Drive1541 drive) + { + _drive = drive; + } + + public byte DummyReadMemory(ushort address) => unchecked((byte)_drive.Read(address)); + + public void OnExecFetch(ushort address) { } + + public byte PeekMemory(ushort address) => unchecked((byte)_drive.Peek(address)); + + public byte ReadMemory(ushort address) => unchecked((byte)_drive.Read(address)); + + public void WriteMemory(ushort address, byte value) => _drive.Write(address, value); + } + public Drive1541(int clockNum, int clockDen) { DriveRom = new Chip23128(); - _cpu = new MOS6502X + _cpu = new MOS6502X(new CpuLink(this)) { - ReadMemory = CpuRead, - WriteMemory = CpuWrite, - DummyReadMemory = CpuRead, - PeekMemory = CpuPeek, NMI = false }; diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs new file mode 100644 index 0000000000..3988706911 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IBeeperDevice.cs @@ -0,0 +1,29 @@ +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a beeper/buzzer device + /// + public interface IBeeperDevice + { + /// + /// Initialisation + /// + /// + /// + void Init(int sampleRate, int tStatesPerFrame); + + /// + /// Processes an incoming pulse value and adds it to the blipbuffer + /// + /// + void ProcessPulseValue(bool pulse); + + /// + /// State serialization + /// + /// + void SyncState(Serializer ser); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs new file mode 100644 index 0000000000..a62c6fe567 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IFDDHost.cs @@ -0,0 +1,30 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Defines an object that can load a floppy disk image + /// + public interface IFDDHost + { + /// + /// The currently inserted diskimage + /// + FloppyDisk Disk { get; set; } + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + /// + void FDD_LoadDisk(byte[] diskData); + + /// + /// Ejects the current disk + /// + void FDD_EjectDisk(); + + /// + /// Signs whether the current active drive has a disk inserted + /// + bool FDD_IsDiskLoaded { get; } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs new file mode 100644 index 0000000000..b971e3c412 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IJoystick.cs @@ -0,0 +1,38 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a spectrum joystick + /// + public interface IJoystick + { + /// + /// The type of joystick + /// + JoystickType JoyType { get; } + + /// + /// Array of all the possibly button press names + /// + string[] ButtonCollection { get; set; } + + /// + /// The player number that this controller is currently assigned to + /// + int PlayerNumber { get; set; } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + void SetJoyInput(string key, bool isPressed); + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + bool GetJoyInput(string key); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs new file mode 100644 index 0000000000..278d39b6fd --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IKeyboard.cs @@ -0,0 +1,80 @@ +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a spectrum keyboard + /// + public interface IKeyboard : IPortIODevice + { + /// + /// The calling spectrumbase class + /// + SpectrumBase _machine { get; } + + /// + /// The keyboard matrix for a particular spectrum model + /// + string[] KeyboardMatrix { get; set; } + + /// + /// Other keyboard keys that are not in the matrix + /// (usually keys derived from key combos) + /// + string[] NonMatrixKeys { get; set; } + + /// + /// Represents the spectrum key state + /// + int[] KeyLine { get; set; } + + /// + /// Resets the line status + /// + void ResetLineStatus(); + + /// + /// There are some slight differences in how PortIN and PortOUT functions + /// between Issue2 and Issue3 keyboards (16k/48k spectrum only) + /// It is possible that some very old games require Issue2 emulation + /// + bool IsIssue2Keyboard { get; set; } + + /// + /// Sets the spectrum key status + /// + /// + /// + void SetKeyStatus(string key, bool isPressed); + + /// + /// Gets the status of a spectrum key + /// + /// + /// + bool GetKeyStatus(string key); + + /// + /// Returns the query byte + /// + /// + /// + byte GetLineStatus(byte lines); + + /// + /// Reads a keyboard byte + /// + /// + /// + byte ReadKeyboardByte(ushort addr); + + /// + /// Looks up a key in the keyboard matrix and returns the relevent byte value + /// + /// + /// + byte GetByteFromKeyMatrix(string key); + + void SyncState(Serializer ser); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs new file mode 100644 index 0000000000..f3cde8c900 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPSG.cs @@ -0,0 +1,67 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a PSG device (in this case an AY-3-891x) + /// + public interface IPSG : ISoundProvider, IPortIODevice + { + /// + /// Initlization routine + /// + /// + /// + void Init(int sampleRate, int tStatesPerFrame); + + /// + /// Activates a register + /// + int SelectedRegister { get; set; } + + /// + /// Writes to the PSG + /// + /// + void PortWrite(int value); + + /// + /// Reads from the PSG + /// + int PortRead(); + + + /// + /// Resets the PSG + /// + void Reset(); + + /// + /// The volume of the AY chip + /// + int Volume { get; set; } + + /// + /// Called at the start of a frame + /// + void StartFrame(); + + /// + /// called at the end of a frame + /// + void EndFrame(); + + /// + /// Updates the sound based on number of frame cycles + /// + /// + void UpdateSound(int frameCycle); + + /// + /// IStatable serialization + /// + /// + void SyncState(Serializer ser); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs new file mode 100644 index 0000000000..4a30e5eaa6 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Abstraction/IPortIODevice.cs @@ -0,0 +1,25 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a device that utilizes port IN & OUT + /// + public interface IPortIODevice + { + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + bool ReadPort(ushort port, ref int result); + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + bool WritePort(ushort port, int result); + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs new file mode 100644 index 0000000000..55e4a1d14c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Datacorder/DatacorderDevice.cs @@ -0,0 +1,1053 @@ +using BizHawk.Common; +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the tape device (or build-in datacorder as it was called +2 and above) + /// + public class DatacorderDevice : IPortIODevice + { + #region Construction + + private SpectrumBase _machine { get; set; } + private Z80A _cpu { get; set; } + private IBeeperDevice _buzzer { get; set; } + + /// + /// Default constructor + /// + public DatacorderDevice(bool autoplay) + { + _autoPlay = autoplay; + } + + /// + /// Initializes the datacorder device + /// + /// + public void Init(SpectrumBase machine) + { + _machine = machine; + _cpu = _machine.CPU; + _buzzer = machine.TapeBuzzer; + } + + #endregion + + #region State Information + + /// + /// Internal counter used to trigger tape buzzer output + /// + private int counter = 0; + + /// + /// The index of the current tape data block that is loaded + /// + private int _currentDataBlockIndex = 0; + public int CurrentDataBlockIndex + { + get + { + if (_dataBlocks.Count() > 0) { return _currentDataBlockIndex; } + else { return -1; } + } + set + { + if (value == _currentDataBlockIndex) { return; } + if (value < _dataBlocks.Count() && value >= 0) + { + _currentDataBlockIndex = value; + _position = 0; + } + } + } + + /// + /// The current position within the current data block + /// + private int _position = 0; + public int Position + { + get + { + if (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count) { return 0; } + else { return _position; } + } + } + + /// + /// Signs whether the tape is currently playing or not + /// + private bool _tapeIsPlaying = false; + public bool TapeIsPlaying + { + get { return _tapeIsPlaying; } + } + + /// + /// A list of the currently loaded data blocks + /// + private List _dataBlocks = new List(); + public List DataBlocks + { + get { return _dataBlocks; } + set { _dataBlocks = value; } + } + + /// + /// Stores the last CPU t-state value + /// + private long _lastCycle = 0; + + /// + /// Edge + /// + private int _waitEdge = 0; + + /// + /// Current tapebit state + /// + private bool currentState = false; + + #endregion + + #region Datacorder Device Settings + + /// + /// Signs whether the device should autodetect when the Z80 has entered into + /// 'load' mode and auto-play the tape if neccesary + /// + private bool _autoPlay; + + #endregion + + #region Emulator + + /// + /// Should be fired at the end of every frame + /// Primary purpose is to detect tape traps and manage auto play (if/when this is ever implemented) + /// + public void EndFrame() + { + MonitorFrame(); + } + + public void StartFrame() + { + //_buzzer.ProcessPulseValue(currentState); + } + + #endregion + + #region Tape Controls + + /// + /// Starts the tape playing from the beginning of the current block + /// + public void Play() + { + if (_tapeIsPlaying) + return; + + _machine.Spectrum.OSD_TapePlaying(); + + // update the lastCycle + _lastCycle = _cpu.TotalExecutedCycles; + + // reset waitEdge and position + _waitEdge = 0; + _position = 0; + + if ( + _dataBlocks.Count > 0 && // data blocks are present && + _currentDataBlockIndex >= 0 // the current data block index is 1 or greater + ) + { + while (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count) + { + // we are at the end of a data block - move to the next + _position = 0; + _currentDataBlockIndex++; + + // are we at the end of the tape? + if (_currentDataBlockIndex >= _dataBlocks.Count) + { + break; + } + } + + // check for end of tape + if (_currentDataBlockIndex >= _dataBlocks.Count) + { + // end of tape reached. Rewind to beginning + AutoStopTape(); + RTZ(); + return; + } + + // update waitEdge with the current position in the current block + _waitEdge = _dataBlocks[_currentDataBlockIndex].DataPeriods[_position]; + + // sign that the tape is now playing + _tapeIsPlaying = true; + } + } + + /// + /// Stops the tape + /// (should move to the beginning of the next block) + /// + public void Stop() + { + if (!_tapeIsPlaying) + return; + + _machine.Spectrum.OSD_TapeStopped(); + + // sign that the tape is no longer playing + _tapeIsPlaying = false; + + if ( + _currentDataBlockIndex >= 0 && // we are at datablock 1 or above + _position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count - 1 // the block is still playing back + ) + { + // move to the next block + _currentDataBlockIndex++; + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + { + _currentDataBlockIndex = -1; + } + + // reset waitEdge and position + _waitEdge = 0; + _position = 0; + + if ( + _currentDataBlockIndex < 0 && // block index is -1 + _dataBlocks.Count() > 0 // number of blocks is greater than 0 + ) + { + // move the index on to 0 + _currentDataBlockIndex = 0; + } + } + + // update the lastCycle + _lastCycle = _cpu.TotalExecutedCycles; + } + + /// + /// Rewinds the tape to it's beginning (return to zero) + /// + public void RTZ() + { + Stop(); + _machine.Spectrum.OSD_TapeRTZ(); + _currentDataBlockIndex = 0; + } + + /// + /// Performs a block skip operation on the current tape + /// TRUE: skip forward + /// FALSE: skip backward + /// + /// + public void SkipBlock(bool skipForward) + { + int blockCount = _dataBlocks.Count; + int targetBlockId = _currentDataBlockIndex; + + if (skipForward) + { + if (_currentDataBlockIndex == blockCount - 1) + { + // last block, go back to beginning + targetBlockId = 0; + } + else + { + targetBlockId++; + } + } + else + { + if (_currentDataBlockIndex == 0) + { + // already first block, goto last block + targetBlockId = blockCount - 1; + } + else + { + targetBlockId--; + } + } + + var bl = _dataBlocks[targetBlockId]; + + StringBuilder sbd = new StringBuilder(); + sbd.Append("("); + sbd.Append((targetBlockId + 1) + " of " + _dataBlocks.Count()); + sbd.Append(") : "); + //sbd.Append("ID" + bl.BlockID.ToString("X2") + " - "); + sbd.Append(bl.BlockDescription); + if (bl.MetaData.Count > 0) + { + sbd.Append(" - "); + sbd.Append(bl.MetaData.First().Key + ": " + bl.MetaData.First().Value); + //sbd.Append("\n"); + //sbd.Append(bl.MetaData.Skip(1).First().Key + ": " + bl.MetaData.Skip(1).First().Value); + } + + if (skipForward) + _machine.Spectrum.OSD_TapeNextBlock(sbd.ToString()); + else + _machine.Spectrum.OSD_TapePrevBlock(sbd.ToString()); + + CurrentDataBlockIndex = targetBlockId; + } + + /// + /// Inserts a new tape and sets up the tape device accordingly + /// + /// + public void LoadTape(byte[] tapeData) + { + // instantiate converters + TzxConverter tzxSer = new TzxConverter(this); + TapConverter tapSer = new TapConverter(this); + PzxConverter pzxSer = new PzxConverter(this); + CswConverter cswSer = new CswConverter(this); + WavConverter wavSer = new WavConverter(this); + + // TZX + if (tzxSer.CheckType(tapeData)) + { + // this file has a tzx header - attempt serialization + try + { + tzxSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid TZX header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } + } + + // PZX + else if (pzxSer.CheckType(tapeData)) + { + // this file has a pzx header - attempt serialization + try + { + pzxSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid PZX header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } + } + + // CSW + else if (cswSer.CheckType(tapeData)) + { + // this file has a csw header - attempt serialization + try + { + cswSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid CSW header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } + } + + // WAV + else if (wavSer.CheckType(tapeData)) + { + // this file has a csw header - attempt serialization + try + { + wavSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nTape image file has a valid WAV header, but threw an exception whilst data was being parsed.\n\n" + e.ToString()); + } + } + + // Assume TAP + else + { + try + { + tapSer.Read(tapeData); + // reset block index + CurrentDataBlockIndex = 0; + return; + } + catch (Exception ex) + { + // exception during operation + var e = ex; + throw new Exception(this.GetType().ToString() + + "\n\nAn exception was thrown whilst data from this tape image was being parsed as TAP.\n\n" + e.ToString()); + } + } + } + + /// + /// Resets the tape + /// + public void Reset() + { + RTZ(); + } + + #endregion + + #region Tape Device Methods + + /// + /// Is called every cpu cycle but runs every 50 t-states + /// This enables the tape devices to play out even if the spectrum itself is not + /// requesting tape data + /// + public void TapeCycle() + { + if (TapeIsPlaying) + { + counter++; + + if (counter > 20) + { + counter = 0; + bool state = GetEarBit(_machine.CPU.TotalExecutedCycles); + _buzzer.ProcessPulseValue(state); + } + } + } + + /// + /// Simulates the spectrum 'EAR' input reading data from the tape + /// + /// + /// + public bool GetEarBit(long cpuCycle) + { + // decide how many cycles worth of data we are capturing + long cycles = cpuCycle - _lastCycle; + + bool is48k = _machine.IsIn48kMode(); + + // check whether tape is actually playing + if (_tapeIsPlaying == false) + { + // it's not playing. Update lastCycle and return + _lastCycle = cpuCycle; + return false; + } + + // check for end of tape + if (_currentDataBlockIndex < 0) + { + // end of tape reached - RTZ (and stop) + RTZ(); + return currentState; + } + + // process the cycles based on the waitEdge + while (cycles >= _waitEdge) + { + // decrement cycles + cycles -= _waitEdge; + + if (_position == 0 && _tapeIsPlaying) + { + // start of block - take care of initial pulse level for PZX + switch (_dataBlocks[_currentDataBlockIndex].BlockDescription) + { + case BlockType.PULS: + // initial pulse level is always low + if (currentState) + FlipTapeState(); + break; + case BlockType.DATA: + // initial pulse level is stored in block + if (currentState != _dataBlocks[_currentDataBlockIndex].InitialPulseLevel) + FlipTapeState(); + break; + case BlockType.PAUS: + // initial pulse level is stored in block + if (currentState != _dataBlocks[_currentDataBlockIndex].InitialPulseLevel) + FlipTapeState(); + break; + } + + // notify about the current block + var bl = _dataBlocks[_currentDataBlockIndex]; + + StringBuilder sbd = new StringBuilder(); + sbd.Append("("); + sbd.Append((_currentDataBlockIndex + 1) + " of " + _dataBlocks.Count()); + sbd.Append(") : "); + //sbd.Append("ID" + bl.BlockID.ToString("X2") + " - "); + sbd.Append(bl.BlockDescription); + if (bl.MetaData.Count > 0) + { + sbd.Append(" - "); + sbd.Append(bl.MetaData.First().Key + ": " + bl.MetaData.First().Value); + } + _machine.Spectrum.OSD_TapePlayingBlockInfo(sbd.ToString()); + } + + + // increment the current period position + _position++; + + if (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count()) + { + // we have reached the end of the current block + + if (_dataBlocks[_currentDataBlockIndex].DataPeriods.Count() == 0) + { + // notify about the current block (we are skipping it because its empty) + var bl = _dataBlocks[_currentDataBlockIndex]; + StringBuilder sbd = new StringBuilder(); + sbd.Append("("); + sbd.Append((_currentDataBlockIndex + 1) + " of " + _dataBlocks.Count()); + sbd.Append(") : "); + //sbd.Append("ID" + bl.BlockID.ToString("X2") + " - "); + sbd.Append(bl.BlockDescription); + if (bl.MetaData.Count > 0) + { + sbd.Append(" - "); + sbd.Append(bl.MetaData.First().Key + ": " + bl.MetaData.First().Value); + } + _machine.Spectrum.OSD_TapePlayingSkipBlockInfo(sbd.ToString()); + + } + + // skip any empty blocks (and process any command blocks) + while (_position >= _dataBlocks[_currentDataBlockIndex].DataPeriods.Count()) + { + // check for any commands + var command = _dataBlocks[_currentDataBlockIndex].Command; + var block = _dataBlocks[_currentDataBlockIndex]; + bool shouldStop = false; + switch (command) + { + // Stop the tape command found - if this is the end of the tape RTZ + // otherwise just STOP and move to the next block + case TapeCommand.STOP_THE_TAPE: + + _machine.Spectrum.OSD_TapeStoppedAuto(); + shouldStop = true; + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + + _monitorTimeOut = 2000; + + break; + case TapeCommand.STOP_THE_TAPE_48K: + if (is48k) + { + _machine.Spectrum.OSD_TapeStoppedAuto(); + shouldStop = true; + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + RTZ(); + else + { + Stop(); + } + + _monitorTimeOut = 2000; + } + break; + } + + if (shouldStop) + break; + + _position = 0; + _currentDataBlockIndex++; + + if (_currentDataBlockIndex >= _dataBlocks.Count()) + { + break; + } + } + + // check for end of tape + if (_currentDataBlockIndex >= _dataBlocks.Count()) + { + _currentDataBlockIndex = -1; + RTZ(); + return currentState; + } + } + + // update waitEdge with current position within the current block + _waitEdge = _dataBlocks[_currentDataBlockIndex].DataPeriods[_position]; + + // flip the current state + FlipTapeState(); + + } + + // update lastCycle and return currentstate + _lastCycle = cpuCycle - (long)cycles; + + // play the buzzer + //_buzzer.ProcessPulseValue(false, currentState); + + return currentState; + } + + private void FlipTapeState() + { + currentState = !currentState; + } + + /// + /// Flash loading implementation + /// (Deterministic Emulation must be FALSE) + /// + private bool FlashLoad() + { + // deterministic emulation must = false + //if (_machine.Spectrum.SyncSettings.DeterministicEmulation) + //return; + + var util = _machine.Spectrum; + + if (_currentDataBlockIndex < 0) + _currentDataBlockIndex = 0; + + if (_currentDataBlockIndex >= DataBlocks.Count) + return false; + + //var val = GetEarBit(_cpu.TotalExecutedCycles); + //_buzzer.ProcessPulseValue(true, val); + + ushort addr = _cpu.RegPC; + + if (_machine.Spectrum.SyncSettings.DeterministicEmulation) + { + + } + + var tb = DataBlocks[_currentDataBlockIndex]; + var tData = tb.BlockData; + + if (tData == null || tData.Length < 2) + { + // skip this + return false; + } + + var toRead = tData.Length - 1; + + if (toRead < _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8)) + { + + } + else + { + toRead = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8); + } + + if (toRead <= 0) + return false; + + var parity = tData[0]; + + if (parity != _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8) >> 8) + return false; + + util.SetCpuRegister("Shadow AF", 0x0145); + + for (var i = 0; i < toRead; i++) + { + var v = tData[i + 1]; + _cpu.Regs[_cpu.L] = v; + parity ^= v; + var d = (ushort)(_cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8) + 1); + _machine.WriteBus(d, v); + } + var pc = (ushort)0x05DF; + + if (_cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8) == toRead && + toRead + 1 < tData.Length) + { + var v = tData[toRead + 1]; + _cpu.Regs[_cpu.L] = v; + parity ^= v; + _cpu.Regs[_cpu.B] = 0xB0; + } + else + { + _cpu.Regs[_cpu.L] = 1; + _cpu.Regs[_cpu.B] = 0; + _cpu.Regs[_cpu.F] = 0x50; + _cpu.Regs[_cpu.A] = parity; + pc = 0x05EE; + } + + _cpu.Regs[_cpu.H] = parity; + var de = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8); + util.SetCpuRegister("DE", de - toRead); + var ix = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8); + util.SetCpuRegister("IX", ix + toRead); + + util.SetCpuRegister("PC", pc); + + _currentDataBlockIndex++; + + return true; + + } + + #endregion + + #region TapeMonitor + + private long _lastINCycle = 0; + private int _monitorCount; + private int _monitorTimeOut; + private ushort _monitorLastPC; + private ushort[] _monitorLastRegs = new ushort[7]; + + /// + /// Resets the TapeMonitor + /// + private void MonitorReset() + { + _lastINCycle = 0; + _monitorCount = 0; + _monitorLastPC = 0; + _monitorLastRegs = null; + } + + /// + /// An iteration of the monitor process + /// + public void MonitorRead() + { + long cpuCycle = _cpu.TotalExecutedCycles; + int delta = (int)(cpuCycle - _lastINCycle); + _lastINCycle = cpuCycle; + + var nRegs = new ushort[] + { + _cpu.Regs[_cpu.A], + _cpu.Regs[_cpu.B], + _cpu.Regs[_cpu.C], + _cpu.Regs[_cpu.D], + _cpu.Regs[_cpu.E], + _cpu.Regs[_cpu.H], + _cpu.Regs[_cpu.L] + }; + + if (delta > 0 && + delta < 96 && + _cpu.RegPC == _monitorLastPC && + _monitorLastRegs != null) + { + int dCnt = 0; + int dVal = 0; + + for (int i = 0; i < nRegs.Length; i++) + { + if (_monitorLastRegs[i] != nRegs[i]) + { + dVal = _monitorLastRegs[i] - nRegs[i]; + dCnt++; + } + } + + if (dCnt == 1 && + (dVal == 1 || dVal == -1)) + { + _monitorCount++; + + if (_monitorCount >= 16 && _autoPlay) + { + if (!_tapeIsPlaying) + { + Play(); + _machine.Spectrum.OSD_TapePlayingAuto(); + } + + _monitorTimeOut = 50; + } + } + else + { + _monitorCount = 0; + } + } + + _monitorLastRegs = nRegs; + _monitorLastPC = _cpu.RegPC; + } + + public void AutoStopTape() + { + if (!_tapeIsPlaying) + return; + + if (!_autoPlay) + return; + + Stop(); + _machine.Spectrum.OSD_TapeStoppedAuto(); + } + + public void AutoStartTape() + { + if (_tapeIsPlaying) + return; + + if (!_autoPlay) + return; + + Play(); + _machine.Spectrum.OSD_TapePlayingAuto(); + } + + public int MaskableInterruptCount = 0; + + private void MonitorFrame() + { + if (_tapeIsPlaying && _autoPlay) + { + if (DataBlocks.Count > 1 || + (_dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.CSW_Recording && + _dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.WAV_Recording)) + { + // we should only stop the tape when there are multiple blocks + // if we just have one big block (maybe a CSW or WAV) then auto stopping will cock things up + _monitorTimeOut--; + } + + if (_monitorTimeOut < 0) + { + if (_dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.PAUSE_BLOCK && + _dataBlocks[_currentDataBlockIndex].BlockDescription != BlockType.PAUS) + { + AutoStopTape(); + } + + return; + } + + // fallback in case usual monitor detection methods do not work + + // number of t-states since last IN operation + long diff = _machine.CPU.TotalExecutedCycles - _lastINCycle; + + // get current datablock + var block = DataBlocks[_currentDataBlockIndex]; + + // is this a pause block? + if (block.BlockDescription == BlockType.PAUS || block.BlockDescription == BlockType.PAUSE_BLOCK) + { + // dont autostop the tape here + return; + } + + // pause in ms at the end of the current block + int blockPause = block.PauseInMS; + + // timeout in t-states (equiv. to blockpause) + int timeout = ((_machine.ULADevice.FrameLength * 50) / 1000) * blockPause; + + // dont use autostop detection if block has no pause at the end + if (timeout == 0) + return; + + // dont autostop if there is only 1 block + if (DataBlocks.Count == 1 || _dataBlocks[_currentDataBlockIndex].BlockDescription == BlockType.CSW_Recording || + _dataBlocks[_currentDataBlockIndex].BlockDescription == BlockType.WAV_Recording + ) + { + return; + } + + if (diff >= timeout * 2) + { + // There have been no attempted tape reads by the CPU within the double timeout period + // Autostop the tape + AutoStopTape(); + _lastCycle = _cpu.TotalExecutedCycles; + } + } + } + + #endregion + + #region IPortIODevice + + /// + /// Mask constants + /// + private const int TAPE_BIT = 0x40; + private const int EAR_BIT = 0x10; + private const int MIC_BIT = 0x08; + + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + public bool ReadPort(ushort port, ref int result) + { + if (TapeIsPlaying) + { + GetEarBit(_cpu.TotalExecutedCycles); + } + if (currentState) + { + result |= TAPE_BIT; + } + else + { + result &= ~TAPE_BIT; + } + + if (!TapeIsPlaying) + { + if (_machine.UPDDiskDevice == null || !_machine.UPDDiskDevice.FDD_IsDiskLoaded) + MonitorRead(); + } + if (_machine.UPDDiskDevice == null || !_machine.UPDDiskDevice.FDD_IsDiskLoaded) + MonitorRead(); + + /* + + if (TapeIsPlaying) + { + if (GetEarBit(_cpu.TotalExecutedCycles)) + { + result &= ~(TAPE_BIT); // reset is EAR ON + } + else + { + result |= (TAPE_BIT); // set is EAR Off + } + } + else + { + if (_machine.KeyboardDevice.IsIssue2Keyboard) + { + if ((_machine.LASTULAOutByte & (EAR_BIT + MIC_BIT)) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= (TAPE_BIT); + } + } + else + { + if ((_machine.LASTULAOutByte & EAR_BIT) == 0) + { + result &= ~(TAPE_BIT); + } + else + { + result |= TAPE_BIT; + } + } + } + + */ + + return true; + } + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + public bool WritePort(ushort port, int result) + { + if (!TapeIsPlaying) + { + currentState = ((byte)result & 0x10) != 0; + } + + return true; + } + + #endregion + + #region State Serialization + + /// + /// Bizhawk state serialization + /// + /// + public void SyncState(Serializer ser) + { + ser.BeginSection("DatacorderDevice"); + ser.Sync("counter", ref counter); + ser.Sync("_currentDataBlockIndex", ref _currentDataBlockIndex); + ser.Sync("_position", ref _position); + ser.Sync("_tapeIsPlaying", ref _tapeIsPlaying); + ser.Sync("_lastCycle", ref _lastCycle); + ser.Sync("_waitEdge", ref _waitEdge); + ser.Sync("currentState", ref currentState); + ser.Sync("_lastINCycle", ref _lastINCycle); + ser.Sync("_monitorCount", ref _monitorCount); + ser.Sync("_monitorTimeOut", ref _monitorTimeOut); + ser.Sync("_monitorLastPC", ref _monitorLastPC); + ser.Sync("_monitorLastRegs", ref _monitorLastRegs, false); + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs new file mode 100644 index 0000000000..d9f5c9822e --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/CHRN.cs @@ -0,0 +1,180 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Used for the sector CHRN structure + /// + public class CHRN + { + /// + /// Track + /// + public byte C { get; set; } + + /// + /// Side + /// + public byte H { get; set; } + + /// + /// Sector ID + /// + public byte R { get; set; } + + /// + /// Sector Size + /// + public byte N { get; set; } + + /// + /// Status register 1 + /// + private byte _flag1; + public byte Flag1 + { + get { return _flag1; } + set { _flag1 = value; } + } + + /// + /// Status register 2 + /// + private byte _flag2; + public byte Flag2 + { + get { return _flag2; } + set { _flag2 = value; } + } + + /// + /// Used to store the last transmitted/received data bytes + /// + public byte[] DataBytes { get; set; } + + /// + /// ID for the read/write data command + /// + public int DataID { get; set; } + + #region Helper Methods + + /// + /// Missing Address Mark (Sector_ID or DAM not found) + /// + public bool ST1MA + { + get { return NECUPD765.GetBit(0, _flag1); } + set + { + if (value) { NECUPD765.SetBit(0, ref _flag1); } + else { NECUPD765.UnSetBit(0, ref _flag1); } + } + } + + /// + /// No Data (Sector_ID not found, CRC fail in ID_field) + /// + public bool ST1ND + { + get { return NECUPD765.GetBit(2, _flag1); } + set + { + if (value) { NECUPD765.SetBit(2, ref _flag1); } + else { NECUPD765.UnSetBit(2, ref _flag1); } + } + } + + /// + /// Data Error (CRC-fail in ID- or Data-Field) + /// + public bool ST1DE + { + get { return NECUPD765.GetBit(5, _flag1); } + set + { + if (value) { NECUPD765.SetBit(5, ref _flag1); } + else { NECUPD765.UnSetBit(5, ref _flag1); } + } + } + + /// + /// End of Track (set past most read/write commands) (see IC) + /// + public bool ST1EN + { + get { return NECUPD765.GetBit(7, _flag1); } + set + { + if (value) { NECUPD765.SetBit(7, ref _flag1); } + else { NECUPD765.UnSetBit(7, ref _flag1); } + } + } + + /// + /// Missing Address Mark in Data Field (DAM not found) + /// + public bool ST2MD + { + get { return NECUPD765.GetBit(0, _flag2); } + set + { + if (value) { NECUPD765.SetBit(0, ref _flag2); } + else { NECUPD765.UnSetBit(0, ref _flag2); } + } + } + + /// + /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) + /// + public bool ST2BC + { + get { return NECUPD765.GetBit(1, _flag2); } + set + { + if (value) { NECUPD765.SetBit(1, ref _flag2); } + else { NECUPD765.UnSetBit(1, ref _flag2); } + } + } + + /// + /// Wrong Cylinder (read/programmed track-ID different) (see b1) + /// + public bool ST2WC + { + get { return NECUPD765.GetBit(4, _flag2); } + set + { + if (value) { NECUPD765.SetBit(4, ref _flag2); } + else { NECUPD765.UnSetBit(4, ref _flag2); } + } + } + + /// + /// Data Error in Data Field (CRC-fail in data-field) + /// + public bool ST2DD + { + get { return NECUPD765.GetBit(5, _flag2); } + set + { + if (value) { NECUPD765.SetBit(5, ref _flag2); } + else { NECUPD765.UnSetBit(5, ref _flag2); } + } + } + + /// + /// Control Mark (read/scan command found sector with deleted DAM) + /// + public bool ST2CM + { + get { return NECUPD765.GetBit(6, _flag2); } + set + { + if (value) { NECUPD765.SetBit(6, ref _flag2); } + else { NECUPD765.UnSetBit(6, ref _flag2); } + } + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs new file mode 100644 index 0000000000..0d025e4e24 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Definitions.cs @@ -0,0 +1,826 @@ +using BizHawk.Common; +using System; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Definitions + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + #region Enums + + /// + /// Defines the current phase of the controller + /// + private enum Phase + { + /// + /// FDC is in an idle state, awaiting the next initial command byte + /// + Idle, + + /// + /// FDC is in a state waiting for the next command instruction + /// A command consists of a command byte (eventually including the MF, MK, SK bits), and up to eight parameter bytes + /// + Command, + + /// + /// During this phase, the actual data is transferred (if any). Usually that are the data bytes for the read/written sector(s), except for the Format Track Command, + /// in that case four bytes for each sector are transferred + /// + Execution, + + /// + /// Returns up to seven result bytes (depending on the command) that are containing status information. The Recalibrate and Seek Track commands do not return result bytes directly, + /// instead the program must wait until the Main Status Register signalizes that the command has been completed, and then it must (!) send a + /// Sense Interrupt State command to 'terminate' the Seek/Recalibrate command. + /// + Result + } + + /// + /// The lifecycle of an instruction + /// Similar to phase, this describes the current 'sub-phase' we are in when dealing with an instruction + /// + private enum InstructionState + { + /// + /// FDC has received a command byte and is currently reading parameter bytes from the data bus + /// + ReceivingParameters, + + /// + /// All parameter bytes have been received. This phase allows any neccessary setup before instruction execution starts + /// + PreExecution, + + /// + /// The start of instruction execution. This may end up with the FDC moving into result phase, + /// but also may also prepare the way for further processing to occur later in execution phase + /// + StartExecute, + + /// + /// Data is read or written in execution phase + /// + ExecutionReadWrite, + + /// + /// Execution phase is well under way. This state primarily deals with data transfer between CPU and FDC + /// + ExecutionWrite, + + /// + /// Execution phase is well under way. This state primarily deals with data transfer between FDC and CPU + /// + ExecutionRead, + + /// + /// Execution has finished and results bytes are ready to be read by the CPU + /// Initial result setup + /// + StartResult, + + /// + /// Result processing + /// + ProcessResult, + + /// + /// Results are being sent + /// + SendingResults, + + /// + /// Final cleanup tasks when the instruction has fully completed + /// + Completed + + } + + /// + /// Represents internal interrupt state of the FDC + /// + public enum InterruptState + { + /// + /// There is no interrupt + /// + None, + /// + /// Execution interrupt + /// + Execution, + /// + /// Result interrupt + /// + Result, + /// + /// Ready interrupt + /// + Ready, + /// + /// Seek interrupt + /// + Seek + } + + /// + /// Possible main states that each drive can be in + /// + public enum DriveMainState + { + /// + /// Drive is not doing anything + /// + None, + /// + /// Seek operation is in progress + /// + Seek, + /// + /// Recalibrate operation is in progress + /// + Recalibrate, + /// + /// A scan data operation is in progress + /// + Scan, + /// + /// A read ID operation is in progress + /// + ReadID, + /// + /// A read data operation is in progress + /// + ReadData, + /// + /// A read diagnostic (read track) operation is in progress + /// + ReadDiagnostic, + /// + /// A write id (format track) operation is in progress + /// + WriteID, + /// + /// A write data operation is in progress + /// + WriteData, + } + + /// + /// State information during a seek/recalibration operation + /// + public enum SeekSubState + { + /// + /// Seek hasnt started yet + /// + Idle, + /// + /// Delayed + /// + Wait, + /// + /// Setup for head move + /// + MoveInit, + /// + /// Seek is currently happening + /// + HeadMove, + /// + /// Head move with no delay + /// + MoveImmediate, + /// + /// Ready to complete + /// + PerformCompletion, + /// + /// Seek operation has completed + /// + SeekCompleted + } + + /// + /// Seek int code + /// + public enum SeekIntStatus + { + Normal, + Abnormal, + DriveNotReady, + } + + /// + /// The direction of a specific command + /// + private enum CommandDirection + { + /// + /// Data flows from UPD765A to Z80 + /// + OUT, + /// + /// Data flows from Z80 to UPD765A + /// + IN + } + + /// + /// Enum defining the different types of result that can be returned + /// + private enum ResultType + { + /// + /// Standard 7 result bytes are returned + /// + Standard, + /// + /// 1 byte returned - ST3 + /// (used for SenseDriveStatus) + /// + ST3, + /// + /// 1 byte returned - ST0 + /// (used for version & invalid) + /// + ST0, + /// + /// 2 bytes returned for sense interrupt status command + /// ST0 + /// CurrentCylinder + /// + Interrupt + } + + /// + /// Possible list of encountered drive status errors + /// + public enum Status + { + /// + /// No error detected + /// + None, + /// + /// An undefined error has been detected + /// + Undefined, + /// + /// Drive is not ready + /// + DriveNotReady, + /// + /// Invalid command received + /// + Invalid, + /// + /// The disk has its write protection tab enabled + /// + WriteProtected, + /// + /// The requested sector has not been found + /// + SectorNotFound + } + + /// + /// Represents the direction that the head is moving over the cylinders + /// Increment: Track number increasing (head moving from outside of disk inwards) + /// Decrement: Track number decreasing (head moving from inside of disk outwards) + /// + public enum SkipDirection + { + Increment, + Decrement + } + + #endregion + + #region Constants + + // Command Instruction Constants + // Designates the default postitions within the cmdbuffer array + + public const int CM_HEAD = 0; + /// + /// C - Track + /// + public const int CM_C = 1; + /// + /// H - Side + /// + public const int CM_H = 2; + /// + /// R - Sector ID + /// + public const int CM_R = 3; + /// + /// N - Sector size + /// + public const int CM_N = 4; + /// + /// EOT - End of track + /// + public const int CM_EOT = 5; + /// + /// GPL - Gap length + /// + public const int CM_GPL = 6; + /// + /// DTL - Data length + /// + public const int CM_DTL = 7; + /// + /// STP - Step + /// + public const int CM_STP = 7; + + // Result Instruction Constants + // Designates the default postitions within the cmdbuffer array + + /// + /// Status register 0 + /// + public const int RS_ST0 = 0; + /// + /// Status register 1 + /// + public const int RS_ST1 = 1; + /// + /// Status register 2 + /// + public const int RS_ST2 = 2; + /// + /// C - Track + /// + public const int RS_C = 3; + /// + /// H - Side + /// + public const int RS_H = 4; + /// + /// R - Sector ID + /// + public const int RS_R = 5; + /// + /// N - Sector size + /// + public const int RS_N = 6; + + // Main Status Register Constants + // Designates the bit positions within the Main status register + + /// + /// FDD0 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 0 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D0B = 0; + /// + /// FDD1 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 1 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D1B = 1; + /// + /// FDD2 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 2 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D2B = 2; + /// + /// FDD3 Busy (seek/recalib active, until succesful sense intstat) + /// FDD number 3 is in the seek mode. If any of the DnB bits IS set FDC will not accept read or write command. + /// + public const int MSR_D3B = 3; + /// + /// FDC Busy (still in command-, execution- or result-phase) + /// A Read or Write command is in orocess. (FDC Busy) FDC will not accept any other command + /// + public const int MSR_CB = 4; + /// + /// Execution Mode (still in execution-phase, non_DMA_only) + /// This bit is set only during execution ohase (Execution Mode) in non-DMA mode When DB5 goes low, execution phase has ended and result phase has started.It operates only during + /// non-DMA mode of operation + /// + public const int MSR_EXM = 5; + /// + /// Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) + /// Indicates direction of data transfer between FDC and data regrster If DIO = 1, then transfer is from data register to the + /// processor.If DIO = 0, then transfer is from the processor to data register + /// + public const int MSR_DIO = 6; + /// + /// Request For Master (1=ready for next byte) (see b6 for direction) + /// ndicates data register IS ready to send or receive data to or from the processor Both bits DIO and RQM should be + /// used to perform the hand-shaking functions of “ready” and “directron” to the processor + /// + public const int MSR_RQM = 7; + + // Status Register 0 Constants + // Designates the bit positions within the status register 0 + + /// + /// Unit Select (driveno during interrupt) + /// This flag IS used to indicate a drive unit number at interrupt + /// + public const int SR0_US0 = 0; + + /// + /// Unit Select (driveno during interrupt) + /// This flag IS used to indicate a drive unit number at interrupt + /// + public const int SR0_US1 = 1; + + /// + /// Head Address (head during interrupt) + /// State of the head at interrupt + /// + public const int SR0_HD = 2; + + /// + /// Not Ready (drive not ready or non-existing 2nd head selected) + /// Not Ready - When the FDD IS in the not-ready state and a Read or Write command IS Issued, this + /// flag IS set If a Read or Write command is issued to side 1 of a single-sided drive, then this flag IS set + /// + public const int SR0_NR = 3; + + /// + /// Equipment Check (drive failure or recalibrate failed (retry)) + /// Equipment check - If a fault srgnal IS received from the FDD, or if the track 0 srgnal fails to occur after 77 + /// step pulses(Recalibrate Command) then this flag is set + /// + public const int SR0_EC = 4; + + /// + /// Seek End (Set if seek-command completed) + /// Seek end - When the FDC completes the Seek command, this flag IS set lo 1 (high) + /// + public const int SR0_SE = 5; + + /// + /// Interrupt Code (low byte) + /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + /// or senseint with no int occured, 3=aborted:disc removed etc.) + /// + public const int SR0_IC0 = 6; + + /// + /// Interrupt Code (high byte) + /// Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + /// or senseint with no int occured, 3=aborted:disc removed etc.) + /// + public const int SR0_IC1 = 7; + + // Status Register 1 Constants + // Designates the bit positions within the status register 1 + + /// + /// Missing Address Mark (Sector_ID or DAM not found) + /// Missing address mark - This bit is set i f the FDC does not detect the IDAM before 2 index pulses It is also set if + /// the FDC cannot find the DAM or DDAM after the IDAM i s found.MD bit of ST2 is also set at this time + /// + public const int SR1_MA = 0; + + /// + /// Not Writeable (tried to write/format disc with wprot_tab=on) + /// Not writable (write protect) - During execution of Write Data, Write Deleted Data or Write ID command. if the FDC + /// detect: a write protect srgnal from the FDD.then this flag is Set + /// + public const int SR1_NW = 1; + + /// + /// No Data + /// No Data (Sector_ID not found, CRC fail in ID_field) + /// + /// During execution of Read Data. Read Deleted Data Write Data.Write Deleted Data or Scan command, if the FDC cannot find + /// the sector specified in the IDR(2)Register, this flag i s set. + /// + /// During execution of the Read ID command. if the FDC cannot read the ID field without an error, then this flag IS set + /// + /// During execution of the Read Diagnostic command. if the starting sector cannot be found, then this flag is set + /// + public const int SR1_ND = 2; + + /// + /// Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) + /// Overrun - If the FDC i s not serviced by the host system during data transfers within a certain time interval.this flaa i s set + /// + public const int SR1_OR = 4; + + /// + /// Data Error (CRC-fail in ID- or Data-Field) + /// Data error - When the FDC detects a CRC(1) error in either the ID field or the data field, this flag is set + /// + public const int SR1_DE = 5; + + /// + /// End of Track (set past most read/write commands) (see IC) + /// End of cylinder - When the FDC tries to access a sector beyond the final sector of a cylinder, this flag I S set + /// + public const int SR1_EN = 7; + + // Status Register 2 Constants + // Designates the bit positions within the status register 2 + + /// + /// Missing Address Mark in Data Field (DAM not found) + /// Missing address mark - When data IS read from the medium, i f the FDC cannot find a data address mark or deleted + /// data address mark, then this flag is set + /// + public const int SR2_MD = 0; + + /// + /// Bad Cylinder (read/programmed track-ID different and read-ID = FF) + /// Bad cylinder - This bit i s related to the ND bit. and when the contents of C on the medium is different + /// from that stored i n the IDR and the contents of C IS FFH.then this flag IS set + /// + public const int SR2_BC = 1; + + /// + /// Scan Not Satisfied (no fitting sector found) + /// Scan not satisfied - During execution of the Scan command, i f the F D cannot find a sector on the cylinder + /// which meets the condition.then this flag i s set + /// + public const int SR2_SN = 2; + + /// + /// Scan Equal Hit (equal) + /// Scan equal hit - During execution of the Scan command. i f the condition of “equal” is satisfied, this flag i s set + /// + public const int SR2_SH = 3; + + /// + /// Wrong Cylinder (read/programmed track-ID different) (see b1) + /// Wrong cylinder - This bit IS related to the ND bit, and when the contents of C(3) on the medium is different + /// from that stored i n the IDR.this flag is set + /// + public const int SR2_WC = 4; + + /// + /// Data Error in Data Field (CRC-fail in data-field) + /// Data error in data field - If the FDC detects a CRC error i n the data field then this flag is set + /// + public const int SR2_DD = 5; + + /// + /// Control Mark (read/scan command found sector with deleted DAM) + /// Control mark - During execution of the Read Data or Scan command, if the FDC encounters a sector + /// which contains a deleted data address mark, this flag is set Also set if DAM is + /// found during Read Deleted Data + /// + public const int SR2_CM = 6; + + // Status Register 3 Constants + // Designates the bit positions within the status register 3 + + /// + /// Unit select 0 + /// Unit Select (pin 28,29 of FDC) + /// + public const int SR3_US0 = 0; + + /// + /// Unit select 1 + /// Unit Select (pin 28,29 of FDC) + /// + public const int SR3_US1 = 1; + + /// + /// Head address (side select) + /// Head Address (pin 27 of FDC) + /// + public const int SR3_HD = 2; + + /// + /// Two Side (0=yes, 1=no (!)) + /// Two-side - This bit IS used to indicate the status of the two-side signal from the FDD + /// + public const int SR3_TS = 3; + + /// + /// Track 0 (on track 0 we are) + /// Track 0 - This bit IS used to indicate the status of the track 0 signal from the FDD + /// + public const int SR3_T0 = 4; + + /// + /// Ready - status of the ready signal from the fdd + /// Ready (drive ready signal) + /// + public const int SR3_RY = 5; + + /// + /// Write Protected (write protected) + /// Write protect - status of the wp signal from the fdd + /// + public const int SR3_WP = 6; + + /// + /// Fault - This bit is used to indicate the status of the fault signal from the FDD + /// Fault (if supported: 1=Drive failure) + /// + public const int SR3_FT = 7; + + // Interrupt Code Masks + + /// + /// 1 = aborted:readfail / OK if EN (end of track) + /// + public const byte IC_OK = 0x00; + + /// + /// 1 = aborted:readfail / OK if EN (end of track) + /// + public const byte IC_ABORTED_RF_OKEN = 0x40; + + /// + /// 2 = unknown cmd or senseint with no int occured + /// + public const byte IC_NO_INT_OCCURED = 0x80; + + /// + /// 3 = aborted:disc removed etc + /// + public const byte IC_ABORTED_DISCREMOVED = 0xC0; + + // command code constants + public const int CC_READ_DATA = 0x06; + public const int CC_READ_ID = 0x0a; + public const int CC_SPECIFY = 0x03; + public const int CC_READ_DIAGNOSTIC = 0x02; + public const int CC_SCAN_EQUAL = 0x11; + public const int CC_SCAN_HIGHOREQUAL = 0x1d; + public const int CC_SCAN_LOWOREQUAL = 0x19; + public const int CC_READ_DELETEDDATA = 0x0c; + public const int CC_WRITE_DATA = 0x05; + public const int CC_WRITE_ID = 0x0d; + public const int CC_WRITE_DELETEDDATA = 0x09; + public const int CC_SEEK = 0x0f; + public const int CC_RECALIBRATE = 0x07; + public const int CC_SENSE_INTSTATUS = 0x08; + public const int CC_SENSE_DRIVESTATUS = 0x04; + public const int CC_VERSION = 0x10; + public const int CC_INVALID = 0x00; + + // drive seek state constants + public const int SEEK_IDLE = 0; + public const int SEEK_SEEK = 1; + public const int SEEK_RECALIBRATE = 2; + // seek interrupt + public const int SEEK_INTACKNOWLEDGED = 3; + public const int SEEK_NORMALTERM = 4; + public const int SEEK_ABNORMALTERM = 5; + public const int SEEK_DRIVENOTREADY = 6; + + #endregion + + #region Classes & Structs + + /// + /// Class that holds information about a specific command + /// + private class Command + { + /// + /// Mask to remove potential parameter bits (5,6, and or 7) in order to identify the command + /// + //public int BitMask { get; set; } + /// + /// The command code after bitmask has been applied + /// + public int CommandCode { get; set; } + /// + /// The number of bytes that make up the full command + /// + public int ParameterByteCount { get; set; } + /// + /// The number of result bytes that will be generated from the command + /// + public int ResultByteCount { get; set; } + /// + /// The command direction + /// IN - Z80 to UPD765A + /// OUT - UPD765A to Z80 + /// + public CommandDirection Direction { get; set; } + /// + /// Command makes use of the MT bit + /// + public bool MT; + /// + /// Command makes use of the MF bit + /// + public bool MF; + /// + /// Command makes use of the SK bit + /// + public bool SK; + /// + /// Read/Write command that is READ + /// + public bool IsRead; + /// + /// Read/Write command that is WRITE + /// + public bool IsWrite; + + /// + /// Delegate function that is called by this command + /// bool 1: EXECUTE - if TRUE the command will be executed. if FALSE the method will instead parse commmand parameter bytes + /// bool 2: RESULT - if TRUE + /// + public Action CommandDelegate { get; set; } + } + + /// + /// Storage for command parameters + /// + public class CommandParameters + { + /// + /// The requested drive + /// + public byte UnitSelect; + /// + /// The requested physical side + /// + public byte Side; + /// + /// The requested track (C) + /// + public byte Cylinder; + /// + /// The requested head (H) + /// + public byte Head; + /// + /// The requested sector (R) + /// + public byte Sector; + /// + /// The specified sector size (N) + /// + public byte SectorSize; + /// + /// The end of track or last sector value (EOT) + /// + public byte EOT; + /// + /// Gap3 length (GPL) + /// + public byte Gap3Length; + /// + /// Data length (DTL) - When N is defined as 00, DTL stands for the data length + /// which users are going to read out or write into the sector + /// + public byte DTL; + + /// + /// Clear down + /// + public void Reset() + { + UnitSelect = 0; + Side = 0; + Cylinder = 0; + Head = 0; + Sector = 0; + SectorSize = 0; + EOT = 0; + Gap3Length = 0; + DTL = 0; + } + + public void SyncState(Serializer ser) + { + ser.BeginSection("ActiveCmdParams"); + + ser.Sync("UnitSelect", ref UnitSelect); + ser.Sync("Side", ref Side); + ser.Sync("Cylinder", ref Cylinder); + ser.Sync("Head", ref Head); + ser.Sync("Sector", ref Sector); + ser.Sync("SectorSize", ref SectorSize); + ser.Sync("EOT", ref EOT); + ser.Sync("Gap3Length", ref Gap3Length); + ser.Sync("DTL", ref DTL); + + ser.EndSection(); + } + } + + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs new file mode 100644 index 0000000000..62b42c7c09 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDC.cs @@ -0,0 +1,2841 @@ +using BizHawk.Common.NumberExtensions; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// FDC State and Methods + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + #region Controller State + + /// + /// Signs whether the drive is active + /// + public bool DriveLight; + + /// + /// Collection of possible commands + /// + private List CommandList; + + /// + /// State parameters relating to the Active command + /// + public CommandParameters ActiveCommandParams = new CommandParameters(); + + /// + /// The current active phase of the controller + /// + private Phase ActivePhase = Phase.Command; + + /// + /// The currently active interrupt + /// + private InterruptState ActiveInterrupt = InterruptState.None; + /// + /// Command buffer + /// This does not contain the initial command byte (only parameter bytes) + /// + private byte[] CommBuffer = new byte[9]; + + /// + /// Current index within the command buffer + /// + private int CommCounter = 0; + + /// + /// Initial command byte flag + /// Bit7 Multi Track (continue multi-sector-function on other head) + /// + private bool CMD_FLAG_MT; + + /// + /// Initial command byte flag + /// Bit6 MFM-Mode-Bit (Default 1=Double Density) + /// + private bool CMD_FLAG_MF; + + /// + /// Initial command byte flag + /// Bit5 Skip-Bit (set if secs with deleted DAM shall be skipped) + /// + private bool CMD_FLAG_SK; + + /// + /// Step Rate Time (supplied via the specify command) + /// SRT stands for the steooino rate for the FDD ( 1 to 16 ms in 1 ms increments). + /// Stepping rate applies to all drives(FH= 1ms, EH= 2ms, etc.). + /// + private int SRT; + + /// + /// Keeps track of the current SRT state + /// + private int SRT_Counter; + + /// + /// Head Unload Time (supplied via the specify command) + /// HUT stands for the head unload time after a Read or Write operation has occurred + /// (16 to 240 ms in 16 ms Increments) + /// + private int HUT; + + /// + /// Keeps track of the current HUT state + /// + private int HUT_Counter; + + /// + /// Head load Time (supplied via the specify command) + /// HLT stands for the head load time in the FDD (2 to 254 ms in 2 ms Increments) + /// + private int HLT; + + /// + /// Keeps track of the current HLT state + /// + private int HLT_Counter; + + /// + /// Non-DMA Mode (supplied via the specify command) + /// ND stands for operation in the non-DMA mode + /// + private bool ND; + + /// + /// In lieu of actual timing, this will count status reads in execution phase + /// where the CPU hasnt actually read any bytes + /// + private int OverrunCounter; + + /// + /// Contains result bytes in result phase + /// + private byte[] ResBuffer = new byte[7]; + + /// + /// Contains sector data to be written/read in execution phase + /// + private byte[] ExecBuffer = new byte[0x8000]; + + /// + /// Interrupt result buffer + /// Persists (and returns when needed) the last result data when a sense interrupt status command happens + /// + private byte[] InterruptResultBuffer = new byte[2]; + + /// + /// Current index within the result buffer + /// + private int ResCounter = 0; + + /// + /// The byte length of the currently active command + /// This may or may not be the same as the actual command resultbytes value + /// + private int ResLength = 0; + + /// + /// Index for sector data within the result buffer + /// + private int ExecCounter = 0; + + /// + /// The length of the current exec command + /// + private int ExecLength = 0; + + /// + /// The last write byte that was received during execution phase + /// + private byte LastSectorDataWriteByte = 0; + + /// + /// The last read byte to be sent during execution phase + /// + private byte LastSectorDataReadByte = 0; + + /// + /// The last parameter byte that was written to the FDC + /// + private byte LastByteReceived = 0; + + /// + /// Delay for reading sector + /// + private int SectorDelayCounter = 0; + + /// + /// The phyical sector ID + /// + private int SectorID = 0; + + /// + /// Counter for index pulses + /// + private int IndexPulseCounter; + + /// + /// Specifies the index of the currently selected command (in the CommandList) + /// + public int CMDIndex + { + get { return _cmdIndex; } + set + { + _cmdIndex = value; + ActiveCommand = CommandList[_cmdIndex]; + } + } + private int _cmdIndex; + + /// + /// The currently active command + /// + private Command ActiveCommand; + + /// + /// Main status register (accessed via reads to port 0x2ffd) + /// + /* + b0..3 DB FDD0..3 Busy (seek/recalib active, until succesful sense intstat) + b4 CB FDC Busy (still in command-, execution- or result-phase) + b5 EXM Execution Mode (still in execution-phase, non_DMA_only) + b6 DIO Data Input/Output (0=CPU->FDC, 1=FDC->CPU) (see b7) + b7 RQM Request For Master (1=ready for next byte) (see b6 for direction) + */ + private byte StatusMain; + + /// + /// Status Register 0 + /// + /* + b0,1 US Unit Select (driveno during interrupt) + b2 HD Head Address (head during interrupt) + b3 NR Not Ready (drive not ready or non-existing 2nd head selected) + b4 EC Equipment Check (drive failure or recalibrate failed (retry)) + b5 SE Seek End (Set if seek-command completed) + b6,7 IC Interrupt Code (0=OK, 1=aborted:readfail/OK if EN, 2=unknown cmd + or senseint with no int occured, 3=aborted:disc removed etc.) + */ + private byte Status0; + + /// + /// Status Register 1 + /// + /* + b0 MA Missing Address Mark (Sector_ID or DAM not found) + b1 NW Not Writeable (tried to write/format disc with wprot_tab=on) + b2 ND No Data (Sector_ID not found, CRC fail in ID_field) + b3,6 0 Not used + b4 OR Over Run (CPU too slow in execution-phase (ca. 26us/Byte)) + b5 DE Data Error (CRC-fail in ID- or Data-Field) + b7 EN End of Track (set past most read/write commands) (see IC) + */ + private byte Status1; + + /// + /// Status Register 2 + /// + /* + b0 MD Missing Address Mark in Data Field (DAM not found) + b1 BC Bad Cylinder (read/programmed track-ID different and read-ID = FF) + b2 SN Scan Not Satisfied (no fitting sector found) + b3 SH Scan Equal Hit (equal) + b4 WC Wrong Cylinder (read/programmed track-ID different) (see b1) + b5 DD Data Error in Data Field (CRC-fail in data-field) + b6 CM Control Mark (read/scan command found sector with deleted DAM) + b7 0 Not Used + */ + private byte Status2; + + /// + /// Status Register 3 + /// + /* + b0,1 US Unit Select (pin 28,29 of FDC) + b2 HD Head Address (pin 27 of FDC) + b3 TS Two Side (0=yes, 1=no (!)) + b4 T0 Track 0 (on track 0 we are) + b5 RY Ready (drive ready signal) + b6 WP Write Protected (write protected) + b7 FT Fault (if supported: 1=Drive failure) + */ + private byte Status3; + + #endregion + + #region UPD Internal Functions + + #region READ Commands + + /// + /// Read Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ReadData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + int buffPos = 0; + int sectorSize = 0; + int maxTransferCap = 0; + + // calculate requested size of data required + if (ActiveCommandParams.SectorSize == 0) + { + // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual + // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) + // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform + // a Multi-Sector Read Operation. + sectorSize = ActiveCommandParams.DTL; + + // calculate maximum transfer capacity + if (!CMD_FLAG_MF) + maxTransferCap = 3328; + } + else + { + // When N is non - zero, then DTL has no meaning and should be set to ffh + ActiveCommandParams.DTL = 0xFF; + + // calculate maximum transfer capacity + switch (ActiveCommandParams.SectorSize) + { + case 1: + if (CMD_FLAG_MF) + maxTransferCap = 6656; + else + maxTransferCap = 3840; + break; + case 2: + if (CMD_FLAG_MF) + maxTransferCap = 7680; + else + maxTransferCap = 4096; + break; + case 3: + if (CMD_FLAG_MF) + maxTransferCap = 8192; + else + maxTransferCap = 4096; + break; + } + + sectorSize = 0x80 << ActiveCommandParams.SectorSize; + } + + // get the current track + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track == null || track.NumberOfSectors <= 0) + { + // track could not be found + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + FloppyDisk.Sector sector = null; + + // sector read loop + for (;;) + { + bool terminate = false; + + // lookup the sector + sector = GetSector(); + + if (sector == null) + { + // sector was not found after two passes of the disk index hole + SetBit(SR1_ND, ref Status1); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + + // sector ID was found on this track + + // get status regs from sector + Status1 = sector.Status1; + Status2 = sector.Status2; + + // we dont need EN + UnSetBit(SR1_EN, ref Status1); + + // If SK=1, the FDC skips the sector with the Deleted Data Address Mark and reads the next sector. + // The CRC bits in the deleted data field are not checked when SK=1 + if (CMD_FLAG_SK && Status2.Bit(SR2_CM)) + { + if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) + { + // increment the sector ID and search again + ActiveCommandParams.Sector++; + continue; + } + else + { + // no execution phase + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + } + + // read the sector + for (int i = 0; i < sector.DataLen; i++) + { + ExecBuffer[buffPos++] = sector.ActualData[i]; + } + + // mark the sector read + sector.SectorReadCompleted(); + + // any CRC errors? + if (Status1.Bit(SR1_DE) || Status2.Bit(SR2_DD)) + { + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + if (!CMD_FLAG_SK && Status2.Bit(SR2_CM)) + { + // deleted address mark was detected with NO skip flag set + ActiveCommandParams.EOT = ActiveCommandParams.Sector; + SetBit(SR2_CM, ref Status2); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + if (sector.SectorID == ActiveCommandParams.EOT || terminate) + { + // this was the last sector to read + // or termination requested + + SetBit(SR1_EN, ref Status1); + + int keyIndex = 0; + for (int i = 0; i < track.Sectors.Length; i++) + { + if (track.Sectors[i].SectorID == sector.SectorID) + { + keyIndex = i; + break; + } + } + + if (keyIndex == track.Sectors.Length - 1) + { + // last sector on the cylinder, set EN + SetBit(SR1_EN, ref Status1); + + // increment cylinder + ActiveCommandParams.Cylinder++; + + // reset sector + ActiveCommandParams.Sector = sector.SectorID; // 1; + ActiveDrive.SectorIndex = 0; + } + else + { + ActiveDrive.SectorIndex++; + } + + UnSetBit(SR0_IC1, ref Status0); + if (terminate) + SetBit(SR0_IC0, ref Status0); + else + UnSetBit(SR0_IC0, ref Status0); + + SetBit(SR0_IC0, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Execution; + break; + } + else + { + // continue with multi-sector read operation + ActiveCommandParams.Sector++; + //ActiveDrive.SectorIndex++; + } + } + + if (ActivePhase == Phase.Execution) + { + ExecLength = buffPos; + ExecCounter = buffPos; + + DriveLight = true; + } + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + + var index = ExecLength - ExecCounter; + + LastSectorDataReadByte = ExecBuffer[index]; + + OverrunCounter--; + ExecCounter--; + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Read Deleted Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ReadDeletedData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + int buffPos = 0; + int sectorSize = 0; + int maxTransferCap = 0; + + // calculate requested size of data required + if (ActiveCommandParams.SectorSize == 0) + { + // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual + // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) + // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform + // a Multi-Sector Read Operation. + sectorSize = ActiveCommandParams.DTL; + + // calculate maximum transfer capacity + if (!CMD_FLAG_MF) + maxTransferCap = 3328; + } + else + { + // When N is non - zero, then DTL has no meaning and should be set to ffh + ActiveCommandParams.DTL = 0xFF; + + // calculate maximum transfer capacity + switch (ActiveCommandParams.SectorSize) + { + case 1: + if (CMD_FLAG_MF) + maxTransferCap = 6656; + else + maxTransferCap = 3840; + break; + case 2: + if (CMD_FLAG_MF) + maxTransferCap = 7680; + else + maxTransferCap = 4096; + break; + case 3: + if (CMD_FLAG_MF) + maxTransferCap = 8192; + else + maxTransferCap = 4096; + break; + } + + sectorSize = 0x80 << ActiveCommandParams.SectorSize; + } + + // get the current track + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track == null || track.NumberOfSectors <= 0) + { + // track could not be found + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + FloppyDisk.Sector sector = null; + + // sector read loop + for (;;) + { + bool terminate = false; + + // lookup the sector + sector = GetSector(); + + if (sector == null) + { + // sector was not found after two passes of the disk index hole + SetBit(SR1_ND, ref Status1); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + + // sector ID was found on this track + + // get status regs from sector + Status1 = sector.Status1; + Status2 = sector.Status2; + + // we dont need EN + UnSetBit(SR1_EN, ref Status1); + + // invert CM for read deleted data command + if (Status2.Bit(SR2_CM)) + UnSetBit(SR2_CM, ref Status2); + else + SetBit(SR2_CM, ref Status2); + + // skip flag is set and no DAM found + if (CMD_FLAG_SK && Status2.Bit(SR2_CM)) + { + if (ActiveCommandParams.Sector != ActiveCommandParams.EOT) + { + // increment the sector ID and search again + ActiveCommandParams.Sector++; + continue; + } + else + { + // no execution phase + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Result; + break; + } + } + // we can read this sector + else + { + // if DAM is not set this will be the last sector to read + if (Status2.Bit(SR2_CM)) + { + ActiveCommandParams.EOT = ActiveCommandParams.Sector; + } + + if (!CMD_FLAG_SK && !Status2.Bit(SR2_CM) && + ActiveDrive.Disk.Protection == ProtectionType.PaulOwens) + { + ActiveCommandParams.EOT = ActiveCommandParams.Sector; + SetBit(SR2_CM, ref Status2); + SetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_IC1, ref Status0); + terminate = true; + } + + // read the sector + for (int i = 0; i < sectorSize; i++) + { + ExecBuffer[buffPos++] = sector.ActualData[i]; + } + + // mark the sector read + sector.SectorReadCompleted(); + + if (sector.SectorID == ActiveCommandParams.EOT) + { + // this was the last sector to read + + SetBit(SR1_EN, ref Status1); + + int keyIndex = 0; + for (int i = 0; i < track.Sectors.Length; i++) + { + if (track.Sectors[i].SectorID == sector.SectorID) + { + keyIndex = i; + break; + } + } + + if (keyIndex == track.Sectors.Length - 1) + { + // last sector on the cylinder, set EN + SetBit(SR1_EN, ref Status1); + + // increment cylinder + ActiveCommandParams.Cylinder++; + + // reset sector + ActiveCommandParams.Sector = 1; + ActiveDrive.SectorIndex = 0; + } + else + { + ActiveDrive.SectorIndex++; + } + + UnSetBit(SR0_IC1, ref Status0); + if (terminate) + SetBit(SR0_IC0, ref Status0); + else + UnSetBit(SR0_IC0, ref Status0); + + SetBit(SR0_IC0, ref Status0); + + // result requires the actual track id, rather than the sector track id + ActiveCommandParams.Cylinder = track.TrackNumber; + + // remove CM (appears to be required to defeat Alkatraz copy protection) + UnSetBit(SR2_CM, ref Status2); + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Execution; + break; + } + else + { + // continue with multi-sector read operation + ActiveCommandParams.Sector++; + //ActiveDrive.SectorIndex++; + } + } + } + + if (ActivePhase == Phase.Execution) + { + ExecLength = buffPos; + ExecCounter = buffPos; + DriveLight = true; + } + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + var index = ExecLength - ExecCounter; + + LastSectorDataReadByte = ExecBuffer[index]; + + OverrunCounter--; + ExecCounter--; + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Read Diagnostic (read track) + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDD and FDC. FDC reads all data fields from index hole to EDT + /// RESULT: 7 result bytes + /// + private void UPD_ReadDiagnostic() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + int buffPos = 0; + int sectorSize = 0; + int maxTransferCap = 0; + + // calculate requested size of data required + if (ActiveCommandParams.SectorSize == 0) + { + // When N=0, then DTL defines the data length which the FDC must treat as a sector. If DTL is smaller than the actual + // data length in a sector, the data beyond DTL in the sector is not sent to the Data Bus. The FDC reads (internally) + // the complete sector performing the CRC check and, depending upon the manner of command termination, may perform + // a Multi-Sector Read Operation. + sectorSize = ActiveCommandParams.DTL; + + // calculate maximum transfer capacity + if (!CMD_FLAG_MF) + maxTransferCap = 3328; + } + else + { + // When N is non - zero, then DTL has no meaning and should be set to ffh + ActiveCommandParams.DTL = 0xFF; + + // calculate maximum transfer capacity + switch (ActiveCommandParams.SectorSize) + { + case 1: + if (CMD_FLAG_MF) + maxTransferCap = 6656; + else + maxTransferCap = 3840; + break; + case 2: + if (CMD_FLAG_MF) + maxTransferCap = 7680; + else + maxTransferCap = 4096; + break; + case 3: + if (CMD_FLAG_MF) + maxTransferCap = 8192; + else + maxTransferCap = 4096; + break; + } + + sectorSize = 0x80 << ActiveCommandParams.SectorSize; + } + + // get the current track + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track == null || track.NumberOfSectors <= 0) + { + // track could not be found + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + FloppyDisk.Sector sector = null; + ActiveDrive.SectorIndex = 0; + + int secCount = 0; + + // read the whole track + for (int i = 0; i < track.Sectors.Length; i++) + { + if (secCount >= ActiveCommandParams.EOT) + { + break; + } + + var sec = track.Sectors[i]; + for (int b = 0; b < sec.ActualData.Length; b++) + { + ExecBuffer[buffPos++] = sec.ActualData[b]; + } + + // mark the sector read + sec.SectorReadCompleted(); + + // end of sector - compare IDs + if (sec.TrackNumber != ActiveCommandParams.Cylinder || + sec.SideNumber != ActiveCommandParams.Head || + sec.SectorID != ActiveCommandParams.Sector || + sec.SectorSize != ActiveCommandParams.SectorSize) + { + SetBit(SR1_ND, ref Status1); + } + + secCount++; + ActiveDrive.SectorIndex = i; + } + + if (secCount == ActiveCommandParams.EOT) + { + // this was the last sector to read + // or termination requested + + int keyIndex = 0; + for (int i = 0; i < track.Sectors.Length; i++) + { + if (track.Sectors[i].SectorID == track.Sectors[ActiveDrive.SectorIndex].SectorID) + { + keyIndex = i; + break; + } + } + + if (keyIndex == track.Sectors.Length - 1) + { + // last sector on the cylinder, set EN + SetBit(SR1_EN, ref Status1); + + // increment cylinder + ActiveCommandParams.Cylinder++; + + // reset sector + ActiveCommandParams.Sector = 1; + ActiveDrive.SectorIndex = 0; + } + else + { + ActiveDrive.SectorIndex++; + } + + UnSetBit(SR0_IC1, ref Status0); + UnSetBit(SR0_IC0, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + ActivePhase = Phase.Execution; + } + + if (ActivePhase == Phase.Execution) + { + ExecLength = buffPos; + ExecCounter = buffPos; + + DriveLight = true; + } + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + + var index = ExecLength - ExecCounter; + + LastSectorDataReadByte = ExecBuffer[index]; + + OverrunCounter--; + ExecCounter--; + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Read ID + /// COMMAND: 1 parameter byte + /// EXECUTION: The first correct ID information on the cylinder is stored in the data register + /// RESULT: 7 result bytes + /// + private void UPD_ReadID() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + DriveLight = true; + + // all parameter bytes received + ClearResultBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // set unit select + //SetUnitSelect(ActiveDrive.ID, ref Status0); + + // HD should always be 0 + UnSetBit(SR0_HD, ref Status0); + + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + // it is at this point the +3 detects whether a disk is present + // if not (and after another readid and SIS) it will eventually proceed to loading from tape + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + // setup the result buffer + ResBuffer[RS_ST0] = Status0; + for (int i = 1; i < 7; i++) + ResBuffer[i] = 0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + var track = ActiveDrive.Disk.DiskTracks.Where(a => a.TrackNumber == ActiveDrive.CurrentTrackID).FirstOrDefault(); + + if (track != null && track.NumberOfSectors > 0 && track.TrackNumber != 0xff) + { + // formatted track + + // is the index out of bounds? + if (ActiveDrive.SectorIndex >= track.NumberOfSectors) + { + // reset the index + ActiveDrive.SectorIndex = 0; + } + + if (ActiveDrive.SectorIndex == 0 && ActiveDrive.Disk.DiskTracks[ActiveDrive.CurrentTrackID].Sectors.Length > 1) + { + // looks like readid always skips the first sector on a track + ActiveDrive.SectorIndex++; + } + + // read the sector data + var data = track.Sectors[ActiveDrive.SectorIndex]; //.GetCHRN(); + ResBuffer[RS_C] = data.TrackNumber; + ResBuffer[RS_H] = data.SideNumber; + ResBuffer[RS_R] = data.SectorID; + ResBuffer[RS_N] = data.SectorSize; + + ResBuffer[RS_ST0] = Status0; + + // check for DAM & CRC + //if (data.Status2.Bit(SR2_CM)) + //SetBit(SR2_CM, ref ResBuffer[RS_ST2]); + + + // increment the current sector + ActiveDrive.SectorIndex++; + + // is the index out of bounds? + if (ActiveDrive.SectorIndex >= track.NumberOfSectors) + { + // reset the index + ActiveDrive.SectorIndex = 0; + } + } + else + { + // unformatted track? + CommitResultCHRN(); + + SetBit(SR0_IC0, ref Status0); + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = 0x01; + } + + ActivePhase = Phase.Result; + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + #endregion + + #region WRITE Commands + + /// + /// Write Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDC and FDD + /// RESULT: 7 result bytes + /// + private void UPD_WriteData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + // check write protect tab + if (ActiveDrive.FLAG_WRITEPROTECT) + { + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + else + { + + // calculate the number of bytes to write + int byteCounter = 0; + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; + + // get the first sector + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + for (int s = 0; s < track.Sectors.Length; s++) + { + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + for (int i = 0; i < 0x80 << ActiveCommandParams.SectorSize; i++) + { + byteCounter++; + + if (i == (0x80 << ActiveCommandParams.SectorSize) - 1 && lastSec) + { + break; + } + } + + if (lastSec) + break; + } + + ExecCounter = byteCounter; + ExecLength = byteCounter; + ActivePhase = Phase.Execution; + DriveLight = true; + break; + } + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + + var index = ExecLength - ExecCounter; + + ExecBuffer[index] = LastSectorDataWriteByte; + + OverrunCounter--; + ExecCounter--; + + if (ExecCounter <= 0) + { + int cnt = 0; + + // all data received + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + + for (int s = 0; s < track.Sectors.Length; s++) + { + if (cnt == ExecLength) + break; + + ActiveCommandParams.Sector = track.Sectors[s].SectorID; + + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + int size = 0x80 << track.Sectors[s].SectorSize; + + for (int d = 0; d < size; d++) + { + track.Sectors[s].SectorData[d] = ExecBuffer[cnt++]; + } + + if (lastSec) + break; + } + + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_EN, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + } + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Write ID (format write) + /// COMMAND: 5 parameter bytes + /// EXECUTION: Entire track is formatted + /// RESULT: 7 result bytes + /// + private void UPD_WriteID() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + DriveLight = true; + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + // check write protect tab + if (ActiveDrive.FLAG_WRITEPROTECT) + { + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + else + { + // not implemented yet + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Write Deleted Data + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data transfer between FDC and FDD + /// RESULT: 7 result bytes + /// + private void UPD_WriteDeletedData() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received - setup for execution phase + + // clear exec buffer and status registers + ClearExecBuffer(); + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + // temp sector index + byte secIdx = ActiveCommandParams.Sector; + + // hack for when another drive (non-existent) is being called + if (ActiveDrive.ID != 0) + DiskDriveIndex = 0; + + // do we have a valid disk inserted? + if (!ActiveDrive.FLAG_READY) + { + // no disk, no tracks or motor is not on + SetBit(SR0_IC0, ref Status0); + SetBit(SR0_NR, ref Status0); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + + // check write protect tab + if (ActiveDrive.FLAG_WRITEPROTECT) + { + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_NW, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + //ResBuffer[RS_ST0] = Status0; + + // move to result phase + ActivePhase = Phase.Result; + break; + } + else + { + + // calculate the number of bytes to write + int byteCounter = 0; + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; + + // get the first sector + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + for (int s = 0; s < track.Sectors.Length; s++) + { + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + for (int i = 0; i < 0x80 << ActiveCommandParams.SectorSize; i++) + { + byteCounter++; + + if (i == (0x80 << ActiveCommandParams.SectorSize) - 1 && lastSec) + { + break; + } + } + + if (lastSec) + break; + } + + ExecCounter = byteCounter; + ExecLength = byteCounter; + ActivePhase = Phase.Execution; + DriveLight = true; + break; + } + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + + var index = ExecLength - ExecCounter; + + ExecBuffer[index] = LastSectorDataWriteByte; + + OverrunCounter--; + ExecCounter--; + + if (ExecCounter <= 0) + { + int cnt = 0; + + // all data received + byte startSecID = ActiveCommandParams.Sector; + byte endSecID = ActiveCommandParams.EOT; + bool lastSec = false; + var track = ActiveDrive.Disk.DiskTracks[ActiveCommandParams.Cylinder]; + int secIndex = 0; + + for (int s = 0; s < track.Sectors.Length; s++) + { + if (cnt == ExecLength) + break; + + ActiveCommandParams.Sector = track.Sectors[s].SectorID; + + if (track.Sectors[s].SectorID == endSecID) + lastSec = true; + + int size = 0x80 << track.Sectors[s].SectorSize; + + for (int d = 0; d < size; d++) + { + track.Sectors[s].SectorData[d] = ExecBuffer[cnt++]; + } + + if (lastSec) + break; + } + + SetBit(SR0_IC0, ref Status0); + SetBit(SR1_EN, ref Status1); + + CommitResultCHRN(); + CommitResultStatus(); + } + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + #endregion + + #region SCAN Commands + + /// + /// Scan Equal + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data compared between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ScanEqual() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Scan Low or Equal + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data compared between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ScanLowOrEqual() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Scan High or Equal + /// COMMAND: 8 parameter bytes + /// EXECUTION: Data compared between the FDD and FDC + /// RESULT: 7 result bytes + /// + private void UPD_ScanHighOrEqual() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + #endregion + + #region OTHER Commands + + /// + /// Specify + /// COMMAND: 2 parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: NO result phase + /// + /// Looks like specify command returns status 0x80 throughout its lifecycle + /// so CB is NOT set + /// + private void UPD_Specify() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + byte currByte = CommBuffer[CommCounter]; + BitArray bi = new BitArray(new byte[] { currByte }); + + switch (CommCounter) + { + // SRT & HUT + case 0: + SRT = 16 - (currByte >> 4) & 0x0f; + HUT = (currByte & 0x0f) << 4; + if (HUT == 0) + { + HUT = 255; + } + break; + // HLT & ND + case 1: + if (bi[0]) + ND = true; + else + ND = false; + + HLT = currByte & 0xfe; + if (HLT == 0) + { + HLT = 255; + } + break; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + ActivePhase = Phase.Idle; + } + + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Seek + /// COMMAND: 2 parameter bytes + /// EXECUTION: Head is positioned over proper cylinder on disk + /// RESULT: NO result phase + /// + private void UPD_Seek() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + byte currByte = CommBuffer[CommCounter]; + switch (CommCounter) + { + case 0: + ParseParamByteStandard(CommCounter); + break; + case 1: + ActiveDrive.SeekingTrack = currByte; + break; + } + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + DriveLight = true; + ActivePhase = Phase.Execution; + ActiveCommand.CommandDelegate(); + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // set seek flag + ActiveDrive.SeekStatus = SEEK_SEEK; + + if (ActiveDrive.CurrentTrackID == CommBuffer[CM_C]) + { + // we are already on the correct track + ActiveDrive.SectorIndex = 0; + } + else + { + // immediate seek + ActiveDrive.CurrentTrackID = CommBuffer[CM_C]; + + ActiveDrive.SectorIndex = 0; + + if (ActiveDrive.Disk.DiskTracks[ActiveDrive.CurrentTrackID].Sectors.Length > 1) + { + // always read the first sector + //ActiveDrive.SectorIndex++; + } + } + + // skip execution mode and go directly to idle + // result is determined by SIS command + ActivePhase = Phase.Idle; + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Recalibrate (seek track 0) + /// COMMAND: 1 parameter byte + /// EXECUTION: Head retracted to track 0 + /// RESULT: NO result phase + /// + private void UPD_Recalibrate() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + DriveLight = true; + ActivePhase = Phase.Execution; + ActiveCommand.CommandDelegate(); + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + + // immediate recalibration + ActiveDrive.TrackIndex = 0; + ActiveDrive.SectorIndex = 0; + + // recalibrate appears to always skip the first sector + //if (ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex].Sectors.Length > 1) + //ActiveDrive.SectorIndex++; + + // set seek flag + ActiveDrive.SeekStatus = SEEK_RECALIBRATE; + + // skip execution mode and go directly to idle + // result is determined by SIS command + ActivePhase = Phase.Idle; + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Sense Interrupt Status + /// COMMAND: NO parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: 2 result bytes + /// + private void UPD_SenseInterruptStatus() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // SIS should return 2 bytes if sucessfully sensed an interrupt + // 1 byte otherwise + + // it seems like the +3 ROM makes 3 SIS calls for each seek/recalibrate call for some reason + // possibly one for each drive??? + // 1 - the interrupt is acknowleged with ST0 = 32 and track number + // 2 - second sis returns 1 ST0 byte with 192 + // 3 - third SIS call returns standard 1 byte 0x80 (unknown cmd or SIS with no interrupt occured) + // for now I will assume that the first call is aimed at DriveA, the second at DriveB (which we are NOT implementing) + + // check active drive first + if (ActiveDrive.SeekStatus == SEEK_RECALIBRATE || + ActiveDrive.SeekStatus == SEEK_SEEK) + { + // interrupt has been raised for this drive + // acknowledge + ActiveDrive.SeekStatus = SEEK_IDLE;// SEEK_INTACKNOWLEDGED; + + // result length 2 + ResLength = 2; + + // first byte ST0 0x20 + Status0 = 0x20; + ResBuffer[0] = Status0; + // second byte is the current track id + ResBuffer[1] = ActiveDrive.CurrentTrackID; + } + /* + else if (ActiveDrive.SeekStatus == SEEK_INTACKNOWLEDGED) + { + // DriveA interrupt has already been acknowledged + ActiveDrive.SeekStatus = SEEK_IDLE; + + ResLength = 1; + Status0 = 192; + ResBuffer[0] = Status0; + } + */ + else if (ActiveDrive.SeekStatus == SEEK_IDLE) + { + // SIS with no interrupt + ResLength = 1; + Status0 = 0x80; + ResBuffer[0] = Status0; + } + + ActivePhase = Phase.Result; + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Sense Drive Status + /// COMMAND: 1 parameter byte + /// EXECUTION: NO execution phase + /// RESULT: 1 result byte + /// + /// The ZX spectrum appears to only specify drive 1 as the parameter byte, NOT drive 0 + /// After the final param byte is received main status changes to 0xd0 + /// Data register (ST3) result is 0x51 if drive/disk not available + /// 0x71 if disk is present in 2nd drive + /// + private void UPD_SenseDriveStatus() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + // store the parameter in the command buffer + CommBuffer[CommCounter] = LastByteReceived; + + // process parameter byte + ParseParamByteStandard(CommCounter); + + // increment command parameter counter + CommCounter++; + + // was that the last parameter byte? + if (CommCounter == ActiveCommand.ParameterByteCount) + { + // all parameter bytes received + ActivePhase = Phase.Execution; + UPD_SenseDriveStatus(); + } + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // one ST3 byte required + + // set US + Status3 = (byte)ActiveDrive.ID; + + if (Status3 != 0) + { + // we only support 1 drive + SetBit(SR3_FT, ref Status3); + } + else + { + // HD - only one side + UnSetBit(SR3_HD, ref Status3); + + // write protect + if (ActiveDrive.FLAG_WRITEPROTECT) + SetBit(SR3_WP, ref Status3); + + // track 0 + if (ActiveDrive.FLAG_TRACK0) + SetBit(SR3_T0, ref Status3); + + // rdy + if (ActiveDrive.Disk != null) + SetBit(SR3_RY, ref Status3); + } + + ResBuffer[0] = Status3; + ActivePhase = Phase.Result; + + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + break; + } + } + + /// + /// Version + /// COMMAND: NO parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: 1 result byte + /// + private void UPD_Version() + { + switch (ActivePhase) + { + case Phase.Idle: + case Phase.Command: + case Phase.Execution: + case Phase.Result: + UPD_Invalid(); + break; + } + } + + /// + /// Invalid + /// COMMAND: NO parameter bytes + /// EXECUTION: NO execution phase + /// RESULT: 1 result byte + /// + private void UPD_Invalid() + { + switch (ActivePhase) + { + //---------------------------------------- + // FDC is waiting for a command byte + //---------------------------------------- + case Phase.Idle: + break; + + //---------------------------------------- + // Receiving command parameter bytes + //---------------------------------------- + case Phase.Command: + break; + + //---------------------------------------- + // FDC in execution phase reading/writing bytes + //---------------------------------------- + case Phase.Execution: + // no execution phase + ActivePhase = Phase.Result; + UPD_Invalid(); + break; + + //---------------------------------------- + // Result bytes being sent to CPU + //---------------------------------------- + case Phase.Result: + ResBuffer[0] = 0x80; + break; + } + } + + #endregion + + #endregion + + #region Controller Methods + + /// + /// Called when a status register read is required + /// This can be called at any time + /// The main status register appears to be queried nearly all the time + /// so needs to be kept updated. It keeps the CPU informed of the current state + /// + private byte ReadMainStatus() + { + SetBit(MSR_RQM, ref StatusMain); + + switch (ActivePhase) + { + case Phase.Idle: + UnSetBit(MSR_DIO, ref StatusMain); + UnSetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + break; + case Phase.Command: + UnSetBit(MSR_DIO, ref StatusMain); + SetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + break; + case Phase.Execution: + if (ActiveCommand.Direction == CommandDirection.OUT) + SetBit(MSR_DIO, ref StatusMain); + else + UnSetBit(MSR_DIO, ref StatusMain); + + SetBit(MSR_EXM, ref StatusMain); + SetBit(MSR_CB, ref StatusMain); + + // overrun detection + OverrunCounter++; + if (OverrunCounter >= 64) + { + // CPU has read the status register 64 times without reading the data register + // switch the current command into result phase + ActivePhase = Phase.Result; + + // reset the overun counter + OverrunCounter = 0; + } + + break; + case Phase.Result: + SetBit(MSR_DIO, ref StatusMain); + SetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + break; + } + + //if (!CheckTiming()) + //{ + // UnSetBit(MSR_EXM, ref StatusMain); + //} + + return StatusMain; + } + private int testCount = 0; + /// + /// Handles CPU reading from the data register + /// + /// + private byte ReadDataRegister() + { + // default return value + byte res = 0xff; + + // check RQM flag status + if (!GetBit(MSR_RQM, StatusMain)) + { + // FDC is not ready to return data + return res; + } + + // check active direction + if (!GetBit(MSR_DIO, StatusMain)) + { + // FDC is expecting to receive, not send data + return res; + } + + switch (ActivePhase) + { + case Phase.Execution: + // reset overrun counter + OverrunCounter = 0; + + // execute read + ActiveCommand.CommandDelegate(); + + res = LastSectorDataReadByte; + + if (ExecCounter <= 0) + { + // end of execution phase + ActivePhase = Phase.Result; + } + + return res; + + case Phase.Result: + + DriveLight = false; + + ActiveCommand.CommandDelegate(); + + // result byte reading + res = ResBuffer[ResCounter]; + + // increment result counter + ResCounter++; + + if (ResCounter >= ResLength) + { + ActivePhase = Phase.Idle; + } + + break; + } + + return res; + } + + /// + /// Handles CPU writing to the data register + /// + /// + private void WriteDataRegister(byte data) + { + if (!GetBit(MSR_RQM, StatusMain) || GetBit(MSR_DIO, StatusMain)) + { + // FDC will not receive and process any bytes + return; + } + + // store the incoming byte + LastByteReceived = data; + + // process incoming bytes + switch (ActivePhase) + { + //// controller is idle awaiting the first command byte of a new instruction + case Phase.Idle: + ParseCommandByte(data); + break; + //// we are in command phase + case Phase.Command: + // attempt to process this parameter byte + //ProcessCommand(data); + ActiveCommand.CommandDelegate(); + break; + //// we are in execution phase + case Phase.Execution: + // CPU is going to be sending data bytes to the FDC to be written to disk + + // store the byte + LastSectorDataWriteByte = data; + ActiveCommand.CommandDelegate(); + + if (ExecCounter <= 0) + { + // end of execution phase + ActivePhase = Phase.Result; + } + + break; + //// result phase + case Phase.Result: + // data register will not receive bytes during result phase + break; + } + } + + /// + /// Processes the first command byte (within a command instruction) + /// Returns TRUE if successful. FALSE if otherwise + /// Called only in idle phase + /// + /// + /// + /// + private bool ParseCommandByte(byte cmdByte) + { + // clear counters + CommCounter = 0; + ResCounter = 0; + + // get the first 4 bytes + byte cByte = (byte)(cmdByte & 0x0f); + + // get MT, MD and SK states + CMD_FLAG_MT = cmdByte.Bit(7); + CMD_FLAG_MF = cmdByte.Bit(6); + CMD_FLAG_SK = cmdByte.Bit(5); + + cmdByte = cByte; + + // lookup the command + var cmd = CommandList.Where(a => a.CommandCode == cmdByte).FirstOrDefault(); + + if (cmd == null) + { + // no command found - use invalid + CMDIndex = CommandList.Count() - 1; + } + else + { + // valid command found + CMDIndex = CommandList.FindIndex(a => a.CommandCode == cmdByte); + + // check validity of command byte flags + // if a flag is set but not valid for this command then it is invalid + bool invalid = false; + + if (!ActiveCommand.MT) + if (CMD_FLAG_MT) + invalid = true; + if (!ActiveCommand.MF) + if (CMD_FLAG_MF) + invalid = true; + if (!ActiveCommand.SK) + if (CMD_FLAG_SK) + invalid = true; + + if (invalid) + { + // command byte included spurious bit 5,6 or 7 flags + CMDIndex = CommandList.Count() - 1; + } + + /* + if ((CMD_FLAG_MF && !ActiveCommand.MF) || + (CMD_FLAG_MT && !ActiveCommand.MT) || + (CMD_FLAG_SK && !ActiveCommand.SK)) + { + // command byte included spurious bit 5,6 or 7 flags + CMDIndex = CommandList.Count() - 1; + } + */ + } + + CommCounter = 0; + ResCounter = 0; + + // there will now be an active command set + // move to command phase + ActivePhase = Phase.Command; + + /* + // check for invalid SIS + if (ActiveInterrupt == InterruptState.None && CMDIndex == CC_SENSE_INTSTATUS) + { + CMDIndex = CC_INVALID; + //ActiveCommand.CommandDelegate(InstructionState.StartResult); + } + */ + + // set reslength + ResLength = ActiveCommand.ResultByteCount; + + // if there are no expected param bytes to receive - go ahead and run the command + if (ActiveCommand.ParameterByteCount == 0) + { + ActivePhase = Phase.Execution; + ActiveCommand.CommandDelegate(); + } + + return true; + } + + /// + /// Parses the first 5 command argument bytes that are of the standard format + /// + /// + private void ParseParamByteStandard(int index) + { + byte currByte = CommBuffer[index]; + BitArray bi = new BitArray(new byte[] { currByte }); + + switch (index) + { + // HD & US + case CM_HEAD: + if (bi[2]) + ActiveCommandParams.Side = 1; + else + ActiveCommandParams.Side = 0; + + ActiveCommandParams.UnitSelect = (byte)(GetUnitSelect(currByte)); + DiskDriveIndex = ActiveCommandParams.UnitSelect; + break; + + // C + case CM_C: + ActiveCommandParams.Cylinder = currByte; + break; + + // H + case CM_H: + ActiveCommandParams.Head = currByte; + break; + + // R + case CM_R: + ActiveCommandParams.Sector = currByte; + break; + + // N + case CM_N: + ActiveCommandParams.SectorSize = currByte; + break; + + // EOT + case CM_EOT: + ActiveCommandParams.EOT = currByte; + break; + + // GPL + case CM_GPL: + ActiveCommandParams.Gap3Length = currByte; + break; + + // DTL + case CM_DTL: + ActiveCommandParams.DTL = currByte; + break; + + default: + break; + } + } + + /// + /// Clears the result buffer + /// + public void ClearResultBuffer() + { + for (int i = 0; i < ResBuffer.Length; i++) + { + ResBuffer[i] = 0; + } + } + + /// + /// Clears the result buffer + /// + public void ClearExecBuffer() + { + for (int i = 0; i < ExecBuffer.Length; i++) + { + ExecBuffer[i] = 0; + } + } + + /// + /// Populates the result status registers + /// + private void CommitResultStatus() + { + // check for read diag + if (ActiveCommand.CommandCode == 0x02) + { + // commit to result buffer + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = Status1; + return; + } + + // check for error bits + if (GetBit(SR1_DE, Status1) || + GetBit(SR1_MA, Status1) || + GetBit(SR1_ND, Status1) || + GetBit(SR1_NW, Status1) || + GetBit(SR1_OR, Status1) || + GetBit(SR2_BC, Status2) || + GetBit(SR2_CM, Status2) || + GetBit(SR2_DD, Status2) || + GetBit(SR2_MD, Status2) || + GetBit(SR2_SN, Status2) || + GetBit(SR2_WC, Status2)) + { + // error bits set - unset end of track + UnSetBit(SR1_EN, ref Status1); + } + + // check for data errors + if (GetBit(SR1_DE, Status1) || + GetBit(SR2_DD, Status2)) + { + // unset control mark + UnSetBit(SR2_CM, ref Status2); + } + else if (GetBit(SR2_CM, Status2)) + { + // DAM found - unset IC and US0 + UnSetBit(SR0_IC0, ref Status0); + UnSetBit(SR0_US0, ref Status0); + } + + // commit to result buffer + ResBuffer[RS_ST0] = Status0; + ResBuffer[RS_ST1] = Status1; + ResBuffer[RS_ST2] = Status2; + + } + + /// + /// Populates the result CHRN values + /// + private void CommitResultCHRN() + { + ResBuffer[RS_C] = ActiveCommandParams.Cylinder; + ResBuffer[RS_H] = ActiveCommandParams.Head; + ResBuffer[RS_R] = ActiveCommandParams.Sector; + ResBuffer[RS_N] = ActiveCommandParams.SectorSize; + } + + /// + /// Moves active phase into idle + /// + public void SetPhase_Idle() + { + ActivePhase = Phase.Idle; + + // active direction + UnSetBit(MSR_DIO, ref StatusMain); + // CB + UnSetBit(MSR_CB, ref StatusMain); + // RQM + SetBit(MSR_RQM, ref StatusMain); + + CommCounter = 0; + ResCounter = 0; + } + + /// + /// Moves to result phase + /// + public void SetPhase_Result() + { + ActivePhase = Phase.Result; + + // active direction + SetBit(MSR_DIO, ref StatusMain); + // CB + SetBit(MSR_CB, ref StatusMain); + // RQM + SetBit(MSR_RQM, ref StatusMain); + // EXM + UnSetBit(MSR_EXM, ref StatusMain); + + CommCounter = 0; + ResCounter = 0; + } + + /// + /// Moves to command phase + /// + public void SetPhase_Command() + { + ActivePhase = Phase.Command; + + // default 0x80 - just RQM + SetBit(MSR_RQM, ref StatusMain); + UnSetBit(MSR_DIO, ref StatusMain); + UnSetBit(MSR_CB, ref StatusMain); + UnSetBit(MSR_EXM, ref StatusMain); + CommCounter = 0; + ResCounter = 0; + } + + /// + /// Moves to execution phase + /// + public void SetPhase_Execution() + { + ActivePhase = Phase.Execution; + + // EXM + SetBit(MSR_EXM, ref StatusMain); + // CB + SetBit(MSR_CB, ref StatusMain); + // RQM + UnSetBit(MSR_RQM, ref StatusMain); + + CommCounter = 0; + ResCounter = 0; + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs new file mode 100644 index 0000000000..0d79ee1bed --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.FDD.cs @@ -0,0 +1,893 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Floppy drive related stuff + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 : IFDDHost + { + #region Drive State + + /// + /// FDD Flag - motor on/off + /// + public bool FDD_FLAG_MOTOR; + + /// + /// The index of the currently active disk drive + /// + public int DiskDriveIndex + { + get { return _diskDriveIndex; } + set + { + // when index is changed update the ActiveDrive + _diskDriveIndex = value; + ActiveDrive = DriveStates[_diskDriveIndex]; + } + } + private int _diskDriveIndex = 0; + + /// + /// The currently active drive + /// + private DriveState ActiveDrive; + + /// + /// Array that holds state information for each possible drive + /// + private DriveState[] DriveStates = new DriveState[4]; + + #endregion + + #region FDD Methods + + /// + /// Initialization / reset of the floppy drive subsystem + /// + private void FDD_Init() + { + for (int i = 0; i < 4; i++) + { + DriveState ds = new DriveState(i, this); + DriveStates[i] = ds; + } + } + + /// + /// Searches for the requested sector + /// + /// + private FloppyDisk.Sector GetSector() + { + FloppyDisk.Sector sector = null; + + // get the current track + var trk = ActiveDrive.Disk.DiskTracks[ActiveDrive.TrackIndex]; + + // get the current sector index + int index = ActiveDrive.SectorIndex; + + // make sure this index exists + if (index > trk.Sectors.Length) + { + index = 0; + } + + // index hole count + int iHole = 0; + + // loop through the sectors in a track + // the loop ends with either the sector being found + // or the index hole being passed twice + while (iHole <= 2) + { + // does the requested sector match the current sector + if (trk.Sectors[index].SectorIDInfo.C == ActiveCommandParams.Cylinder && + trk.Sectors[index].SectorIDInfo.H == ActiveCommandParams.Head && + trk.Sectors[index].SectorIDInfo.R == ActiveCommandParams.Sector && + trk.Sectors[index].SectorIDInfo.N == ActiveCommandParams.SectorSize) + { + // sector has been found + sector = trk.Sectors[index]; + + UnSetBit(SR2_BC, ref Status2); + UnSetBit(SR2_WC, ref Status2); + break; + } + + // check for bad cylinder + if (trk.Sectors[index].SectorIDInfo.C == 255) + { + SetBit(SR2_BC, ref Status2); + } + // check for no cylinder + else if (trk.Sectors[index].SectorIDInfo.C != ActiveCommandParams.Cylinder) + { + SetBit(SR2_WC, ref Status2); + } + + // incrememnt sector index + index++; + + // have we reached the index hole? + if (trk.Sectors.Length <= index) + { + // wrap around + index = 0; + iHole++; + } + } + + // search loop has completed and the sector may or may not have been found + + // bad cylinder detected? + if (Status2.Bit(SR2_BC)) + { + // remove WC + UnSetBit(SR2_WC, ref Status2); + } + + // update sectorindex on drive + ActiveDrive.SectorIndex = index; + + return sector; + } + + #endregion + + #region IFDDHost + + // IFDDHost methods that fall through to the currently active drive + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + /// + public void FDD_LoadDisk(byte[] diskData) + { + // we are only going to load into the first drive + DriveStates[0].FDD_LoadDisk(diskData); + } + + /// + /// Ejects the current disk + /// + public void FDD_EjectDisk() + { + DriveStates[0].FDD_EjectDisk(); + } + + /// + /// Signs whether the current active drive has a disk inserted + /// + public bool FDD_IsDiskLoaded + { + get { return DriveStates[DiskDriveIndex].FDD_IsDiskLoaded; } + } + + /// + /// Returns the disk object from drive 0 + /// + public FloppyDisk DiskPointer + { + get { return DriveStates[0].Disk; } + } + + public FloppyDisk Disk { get; set; } + + #endregion + + #region Drive Status Class + + /// + /// Holds specfic state information about a drive + /// + private class DriveState : IFDDHost + { + #region State + + /// + /// The drive ID from an FDC perspective + /// + public int ID; + + /// + /// Signs whether this drive ready + /// TRUE if both drive exists and has a disk inserted + /// + public bool FLAG_READY + { + get + { + if (!FDD_IsDiskLoaded || Disk.GetTrackCount() == 0 || !FDC.FDD_FLAG_MOTOR) + return false; + else + return true; + } + } + + /// + /// Disk is write protected (TRUE BY DEFAULT) + /// + public bool FLAG_WRITEPROTECT = false; + + /// + /// Storage for seek steps + /// One step for each indexpulse (track index) until seeked track + /// + public int SeekCounter; + + /// + /// Seek status + /// + public int SeekStatus; + + /// + /// Age counter + /// + public int SeekAge; + + /// + /// The current side + /// + public byte CurrentSide; + + /// + /// The current track index in the DiskTracks array + /// + public byte TrackIndex; + + /// + /// The track ID of the current cylinder + /// + public byte CurrentTrackID + { + get + { + // default invalid track + int id = 0xff; + + if (Disk == null) + return (byte)id; + + if (Disk.DiskTracks.Count() == 0) + return (byte)id; + + if (TrackIndex >= Disk.GetTrackCount()) + TrackIndex = 0; + else if (TrackIndex < 0) + TrackIndex = 0; + + var track = Disk.DiskTracks[TrackIndex]; + + id = track.TrackNumber; + + return (byte)id; + } + set + { + for (int i = 0; i < Disk.GetTrackCount(); i++) + { + if (Disk.DiskTracks[i].TrackNumber == value) + { + TrackIndex = (byte)i; + break; + } + } + } + } + + + /// + /// The new track that the drive is seeking to + /// (used in seek operations) + /// + public int SeekingTrack; + + /// + /// The current sector index in the Sectors array + /// + public int SectorIndex; + + /// + /// The currently loaded floppy disk + /// + public FloppyDisk Disk { get; set; } + + /// + /// The parent controller + /// + private NECUPD765 FDC; + + #endregion + + #region Lookups + + /// + /// TRUE if we are on track 0 + /// + public bool FLAG_TRACK0 + { + get + { + if (TrackIndex == 0) { return true; } + else { return false; } + } + } + + #endregion + + #region Public Methods + /* + /// + /// Moves the head across the disk cylinders + /// + /// + /// + public void MoveHead(SkipDirection direction, int cylinderCount) + { + // get total tracks + int trackCount = Disk.DiskTracks.Count(); + + int trk = 0; + + switch (direction) + { + case SkipDirection.Increment: + trk = (int)CurrentTrack + cylinderCount; + if (trk >= trackCount) + { + // past the last track + trk = trackCount - 1; + } + else if (trk < 0) + trk = 0; + break; + case SkipDirection.Decrement: + trk = (int)CurrentTrack - cylinderCount; + if (trk < 0) + { + // before the first track + trk = 0; + } + else if (trk >= trackCount) + trk = trackCount - 1; + break; + } + + // move the head + CurrentTrack = (byte)trk; + } + */ + + /* + + /// + /// Finds a supplied sector + /// + /// + /// + /// + /// + public FloppyDisk.Sector FindSector(ref byte[] resBuffer, CommandParameters prms) + { + int index =CurrentSector; + int lc = 0; + FloppyDisk.Sector sector = null; + + bool found = false; + + do + { + sector = Disk.DiskTracks[CurrentTrack].Sectors[index]; + if (sector != null && sector.SectorID == prms.Sector) + { + // sector found + // check for data errors + if ((sector.Status1 & 0x20) != 0 || (sector.Status2 & 0x20) != 0) + { + // data errors found + } + found = true; + break; + } + + // sector doesnt match + var c = Disk.DiskTracks[CurrentTrack].Sectors[index].TrackNumber; + if (c == 255) + { + // bad cylinder + resBuffer[RS_ST2] |= 0x02; + } + else if (prms.Cylinder != c) + { + // cylinder mismatch + resBuffer[RS_ST2] |= 0x10; + } + + // increment index + index++; + + if (index >= Disk.DiskTracks[CurrentTrack].NumberOfSectors) + { + // out of bounds + index = 0; + lc++; + } + + } while (lc < 2); + + if ((resBuffer[RS_ST2] & 0x02) != 0) + { + // bad cylinder set - remove no cylinder + UnSetBit(SR2_WC, ref resBuffer[RS_ST2]); + } + + // update current sector + CurrentSector = index; + + if (found) + return sector; + else + return null; + } + + + /// + /// Populates a result buffer + /// + /// + /// + public void FillResult(ref byte[] resBuffer, CHRN chrn) + { + // clear results + resBuffer[RS_ST0] = 0; + resBuffer[RS_ST1] = 0; + resBuffer[RS_ST2] = 0; + resBuffer[RS_C] = 0; + resBuffer[RS_H] = 0; + resBuffer[RS_R] = 0; + resBuffer[RS_N] = 0; + + if (chrn == null) + { + // no chrn supplied + resBuffer[RS_ST0] = ST0; + resBuffer[RS_ST1] = 0; + resBuffer[RS_ST2] = 0; + resBuffer[RS_C] = 0; + resBuffer[RS_H] = 0; + resBuffer[RS_R] = 0; + resBuffer[RS_N] = 0; + } + } + + + + /// + /// Populates the result buffer with ReadID data + /// + /// + public void ReadID(ref byte[] resBuffer) + { + if (CheckDriveStatus() == false) + { + // drive not ready + resBuffer[RS_ST0] = ST0; + return; + } + + var track = Disk.DiskTracks.Where(a => a.TrackNumber == CurrentTrack).FirstOrDefault(); + + if (track != null && track.NumberOfSectors > 0) + { + // formatted track + + // get the current sector + int index = CurrentSector; + + // is the index out of bounds? + if (index >= track.NumberOfSectors) + { + // reset the index + index = 0; + } + + // read the sector data + var data = track.Sectors[index]; + resBuffer[RS_C] = data.TrackNumber; + resBuffer[RS_H] = data.SideNumber; + resBuffer[RS_R] = data.SectorID; + resBuffer[RS_N] = data.SectorSize; + + resBuffer[RS_ST0] = ST0; + + // increment the current sector + CurrentSector = index + 1; + return; + } + else + { + // unformatted track? + resBuffer[RS_C] = FDC.CommBuffer[CM_C]; + resBuffer[RS_H] = FDC.CommBuffer[CM_H]; + resBuffer[RS_R] = FDC.CommBuffer[CM_R]; + resBuffer[RS_N] = FDC.CommBuffer[CM_N]; + + SetBit(SR0_IC0, ref ST0); + resBuffer[RS_ST0] = ST0; + resBuffer[RS_ST1] = 0x01; + return; + } + } + */ + + /* + + /// + /// The drive performs a seek operation if necessary + /// Return value TRUE indicates seek complete + /// + public void DoSeek() + { + if (CurrentState != DriveMainState.Recalibrate && + CurrentState != DriveMainState.Seek) + { + // no seek/recalibrate has been asked for + return; + } + + if (GetBit(ID, FDC.StatusMain)) + { + // drive is already seeking + return; + } + + RunSeekCycle(); + } + + /// + /// Runs a seek cycle + /// + public void RunSeekCycle() + { + for (;;) + { + switch (SeekState) + { + // seek or recalibrate has been requested + case SeekSubState.Idle: + + if (CurrentState == DriveMainState.Recalibrate) + { + // recalibrate always seeks to track 0 + SeekingTrack = 0; + } + SeekState = SeekSubState.MoveInit; + + // mark drive as busy + // this should be cleared by SIS command + SetBit(ID, ref FDC.StatusMain); + + break; + + // setup for the head move + case SeekSubState.MoveInit: + + if (CurrentTrack == SeekingTrack) + { + // we are already at the required track + if (CurrentState == DriveMainState.Recalibrate && + !FLAG_TRACK0) + { + // recalibration fail + SeekIntState = SeekIntStatus.Abnormal; + + // raise seek interrupt + FDC.ActiveInterrupt = InterruptState.Seek; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + // equipment check + SetBit(SR0_EC, ref FDC.Status0); + + SeekState = SeekSubState.PerformCompletion; + break; + } + + if (CurrentState == DriveMainState.Recalibrate && + FLAG_TRACK0) + { + // recalibration success + SeekIntState = SeekIntStatus.Normal; + + // raise seek interrupt + FDC.ActiveInterrupt = InterruptState.Seek; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + SeekState = SeekSubState.PerformCompletion; + break; + } + } + + // check for error + if (IntStatus >= IC_ABORTED_DISCREMOVED || Disk == null) + { + // drive not ready + FLAG_READY = false; + + // drive not ready + SeekIntState = SeekIntStatus.DriveNotReady; + + // cancel any interrupt + FDC.ActiveInterrupt = InterruptState.None; + + // unset DB bit + UnSetBit(ID, ref FDC.StatusMain); + + SeekState = SeekSubState.PerformCompletion; + break; + } + + if (SeekCounter > 1) + { + // not ready to seek yet + SeekCounter--; + return; + } + + if (FDC.SRT < 1 && CurrentTrack != SeekingTrack) + { + SeekState = SeekSubState.MoveImmediate; + break; + } + + // head move + SeekState = SeekSubState.HeadMove; + + break; + + case SeekSubState.HeadMove: + + // do the seek + SeekCounter = FDC.SRT; + + if (CurrentTrack < SeekingTrack) + { + // we are seeking forward + var delta = SeekingTrack - CurrentTrack; + MoveHead(SkipDirection.Increment, 1); + } + else if (CurrentTrack > SeekingTrack) + { + // we are seeking backward + var delta = CurrentTrack - SeekingTrack; + MoveHead(SkipDirection.Decrement, 1); + } + + // should the seek be completed now? + if (CurrentTrack == SeekingTrack) + { + SeekState = SeekSubState.PerformCompletion; + break; + } + + // seek not finished yet + return; + + // seek emulation processed immediately + case SeekSubState.MoveImmediate: + + if (CurrentTrack < SeekingTrack) + { + // we are seeking forward + var delta = SeekingTrack - CurrentTrack; + MoveHead(SkipDirection.Increment, delta); + + } + else if (CurrentTrack > SeekingTrack) + { + // we are seeking backward + var delta = CurrentTrack - SeekingTrack; + MoveHead(SkipDirection.Decrement, delta); + } + + SeekState = SeekSubState.PerformCompletion; + break; + + case SeekSubState.PerformCompletion: + SeekDone(); + SeekState = SeekSubState.SeekCompleted; + break; + + case SeekSubState.SeekCompleted: + // seek has already completed + return; + } + } + } + + /// + /// Called when a seek operation has completed + /// + public void SeekDone() + { + SeekCounter = 0; + SeekingTrack = CurrentTrack; + + // generate ST0 register data + + // get only the IC bits + IntStatus &= IC_ABORTED_DISCREMOVED; + + // drive ready? + if (!FLAG_READY) + { + SetBit(SR0_NR, ref IntStatus); + SetBit(SR0_EC, ref IntStatus); + + // are we recalibrating? + if (CurrentState == DriveMainState.Recalibrate) + { + SetBit(SR0_EC, ref IntStatus); + } + } + + // set seek end + SetBit(SR0_SE, ref IntStatus); + /* + // head address + if (CurrentSide > 0) + { + SetBit(SR0_HD, ref IntStatus); + + // drive only supports 1 head + // set the EC bit + SetBit(SR0_EC, ref IntStatus); + } + */ + /* + // UnitSelect + SetUnitSelect(ID, ref IntStatus); + + // move to none state + //CurrentState = DriveMainState.None; + + //SeekState = SeekSubState.SeekCompleted; + + // set the seek interrupt flag for this drive + // this will be cleared at the next successful senseint + FLAG_SEEK_INTERRUPT = true; + + //CurrentState = DriveMainState.None; + + } + */ + + #endregion + + #region Construction + + public DriveState(int driveID, NECUPD765 fdc) + { + ID = driveID; + FDC = fdc; + } + + #endregion + + #region IFDDHost + + /// + /// Parses a new disk image and loads it into this floppy drive + /// + /// + public void FDD_LoadDisk(byte[] diskData) + { + // try dsk first + FloppyDisk fdd = null; + bool found = false; + + foreach (DiskType type in Enum.GetValues(typeof(DiskType))) + { + switch (type) + { + case DiskType.CPCExtended: + fdd = new CPCExtendedFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + case DiskType.CPC: + fdd = new CPCFloppyDisk(); + found = fdd.ParseDisk(diskData); + break; + } + + if (found) + { + Disk = fdd; + break; + } + } + + if (!found) + { + throw new Exception(this.GetType().ToString() + + "\n\nDisk image file could not be parsed. Potentially an unknown format."); + } + } + + /// + /// Ejects the current disk + /// + public void FDD_EjectDisk() + { + Disk = null; + //FLAG_READY = false; + } + + /// + /// Signs whether the current active drive has a disk inserted + /// + public bool FDD_IsDiskLoaded + { + get + { + if (Disk != null) + return true; + else + return false; + } + } + + #endregion + + #region StateSerialization + + public void SyncState(Serializer ser) + { + ser.Sync("ID", ref ID); + ser.Sync("FLAG_WRITEPROTECT", ref FLAG_WRITEPROTECT); + //ser.Sync("FLAG_DISKCHANGED", ref FLAG_DISKCHANGED); + //ser.Sync("FLAG_RECALIBRATING", ref FLAG_RECALIBRATING); + //ser.Sync("FLAG_SEEK_INTERRUPT", ref FLAG_SEEK_INTERRUPT); + //ser.Sync("IntStatus", ref IntStatus); + //ser.Sync("ST0", ref ST0); + //ser.Sync("RecalibrationCounter", ref RecalibrationCounter); + ser.Sync("SeekCounter", ref SeekCounter); + ser.Sync("SeekStatus", ref SeekStatus); + ser.Sync("SeekAge", ref SeekAge); + ser.Sync("CurrentSide", ref CurrentSide); + //ser.Sync("CurrentTrack", ref CurrentTrack); + ser.Sync("TrackIndex", ref TrackIndex); + ser.Sync("SeekingTrack", ref SeekingTrack); + //ser.Sync("CurrentSector", ref CurrentSector); + ser.Sync("SectorIndex", ref SectorIndex); + //ser.Sync("RAngles", ref RAngles); + //ser.Sync("DataPointer", ref DataPointer); + //ser.SyncEnum("CurrentState", ref CurrentState); + //ser.SyncEnum("SeekState", ref SeekState); + //ser.SyncEnum("SeekIntState", ref SeekIntState); + } + + #endregion + } + +#endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs new file mode 100644 index 0000000000..3c53007c0b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.IPortIODevice.cs @@ -0,0 +1,151 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// IPortIODevice + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 : IPortIODevice + { + #region Dev Logging + + public string outputfile = @"D:\Dropbox\Dropbox\_Programming\TASVideos\BizHawk\output\zxhawkio-" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".csv"; + public string outputString = "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN\r\n"; + public bool writeDebug = false; + + public List dLog = new List + { + "STATUS,WRITE,READ,CODE,MT,MF,SK,CMDCNT,RESCNT,EXECCNT,EXECLEN" + }; + + + /* + * Status read + * Data write + * Data read + * CMD code + * CMD string + * MT flag + * MK flag + * SK flag + * */ + private string[] workingArr = new string[3]; + + private void BuildCSVLine() + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 3; i++) + { + sb.Append(workingArr[i]); + sb.Append(","); + workingArr[i] = ""; + } + + sb.Append(ActiveCommand.CommandCode).Append(","); + + sb.Append(CMD_FLAG_MT).Append(","); + sb.Append(CMD_FLAG_MF).Append(","); + sb.Append(CMD_FLAG_SK).Append(","); + + sb.Append(CommCounter).Append(","); + sb.Append(ResCounter).Append(","); + sb.Append(ExecCounter).Append(","); + sb.Append(ExecLength); + + //sb.Append("\r\n"); + + //outputString += sb.ToString(); + dLog.Add(sb.ToString()); + } + + #endregion + + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + public bool ReadPort(ushort port, ref int data) + { + BitArray bits = new BitArray(new byte[] { (byte)data }); + + if (port == 0x3ffd) + { + // Z80 is trying to read from the data register + data = ReadDataRegister(); + if (writeDebug) + { + workingArr[2] = data.ToString(); + //outputString += ",," + data + "," + ActiveCommand.CommandCode + "\r\n"; + BuildCSVLine(); + } + + return true; + } + + if (port == 0x2ffd) + { + // read main status register + // this can happen at any time + data = ReadMainStatus(); + if (writeDebug) + { + //outputString += data + ",,," + ActiveCommand.CommandCode + "\r\n"; + workingArr[0] = data.ToString(); + BuildCSVLine(); + //System.IO.File.WriteAllText(outputfile, outputString); + } + + return true; + } + + return false; + } + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + public bool WritePort(ushort port, int data) + { + BitArray bits = new BitArray(new byte[] { (byte)data }); + + if (port == 0x3ffd) + { + // Z80 is attempting to write to the data register + WriteDataRegister((byte)data); + if (writeDebug) + { + //outputString += "," + data + ",," + ActiveCommand.CommandCode + "\r\n"; + workingArr[1] = data.ToString(); + BuildCSVLine(); + //System.IO.File.WriteAllText(outputfile, outputString); + } + + return true; + } + + if (port == 0x1ffd) + { + // set disk motor on/off + FDD_FLAG_MOTOR = bits[3]; + return true; + } + return false; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs new file mode 100644 index 0000000000..ee4224e8a9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.Timing.cs @@ -0,0 +1,121 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Timimng + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + /// + /// The current Z80 cycle + /// + private long CurrentCPUCycle + { + get + { + if (_machine == null) + return 0; + else + return _machine.CPU.TotalExecutedCycles; + } + } + + /// + /// The last CPU cycle when the FDC accepted an IO read/write + /// + private long LastCPUCycle; + + /// + /// The current delay figure (in Z80 t-states) + /// This implementation only introduces delay upon main status register reads + /// All timing calculations should be done during the other read/write operations + /// + private long StatusDelay; + + /// + /// Defines the numbers of Z80 cycles per MS + /// + private long CPUCyclesPerMs; + + /// + /// The floppy drive emulated clock speed + /// + public const double DriveClock = 31250; + + /// + /// The number of floppy drive cycles per MS + /// + public long DriveCyclesPerMs; + + /// + /// The number of T-States in one floppy drive clock tick + /// + public long StatesPerDriveTick; + + /// + /// Responsible for measuring when the floppy drive is ready to run a cycle + /// + private long TickCounter; + + /// + /// Internal drive cycle counter + /// + private int DriveCycleCounter = 1; + + /// + /// Initializes the timing routines + /// + private void TimingInit() + { + // z80 timing + double frameSize = _machine.ULADevice.FrameLength; + double rRate = _machine.ULADevice.ClockSpeed / frameSize; + long tPerSecond = (long)(frameSize * rRate); + CPUCyclesPerMs = tPerSecond / 1000; + + // drive timing + double dRate = DriveClock / frameSize; + long dPerSecond = (long)(frameSize * dRate); + DriveCyclesPerMs = dPerSecond / 1000; + + long TStatesPerDriveCycle = (long)((double)_machine.ULADevice.ClockSpeed / DriveClock); + StatesPerDriveTick = TStatesPerDriveCycle; + + } + + /// + /// Called by reads to the main status register + /// Returns true if there is no delay + /// Returns false if read is to be deferred + /// + /// + private bool CheckTiming() + { + // get delta + long delta = CurrentCPUCycle - LastCPUCycle; + + if (StatusDelay >= delta) + { + // there is still delay remaining + StatusDelay -= delta; + LastCPUCycle = CurrentCPUCycle; + return false; + } + else + { + // no delay remaining + StatusDelay = 0; + LastCPUCycle = CurrentCPUCycle; + return true; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs new file mode 100644 index 0000000000..a78c2f77df --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPD765.cs @@ -0,0 +1,246 @@ +using BizHawk.Common; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The NEC floppy disk controller (and floppy drive) found in the +3 + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + #region Devices + + /// + /// The emulated spectrum machine + /// + private SpectrumBase _machine; + + #endregion + + #region Construction & Initialization + + /// + /// Main constructor + /// + /// + public NECUPD765() + { + InitCommandList(); + } + + /// + /// Initialization routine + /// + public void Init(SpectrumBase machine) + { + _machine = machine; + FDD_Init(); + TimingInit(); + Reset(); + } + + /// + /// Resets the FDC + /// + public void Reset() + { + // setup main status + StatusMain = 0; + + Status0 = 0; + Status1 = 0; + Status2 = 0; + Status3 = 0; + + SetBit(MSR_RQM, ref StatusMain); + + SetPhase_Idle(); + + //FDC_FLAG_RQM = true; + //ActiveDirection = CommandDirection.IN; + SRT = 6; + HUT = 16; + HLT = 2; + HLT_Counter = 0; + HUT_Counter = 0; + IndexPulseCounter = 0; + CMD_FLAG_MF = false; + + foreach (var d in DriveStates) + { + //d.SeekingTrack = d.CurrentTrack; + ////d.SeekCounter = 0; + //d.FLAG_SEEK_INTERRUPT = false; + //d.IntStatus = 0; + //d.SeekState = SeekSubState.Idle; + //d.SeekIntState = SeekIntStatus.Normal; + + } + + } + + /// + /// Setup the command structure + /// Each command represents one of the internal UPD765 commands + /// + private void InitCommandList() + { + CommandList = new List + { + // read data + new Command { CommandDelegate = UPD_ReadData, CommandCode = 0x06, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, + // read id + new Command { CommandDelegate = UPD_ReadID, CommandCode = 0x0a, MF = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 7 }, + // specify + new Command { CommandDelegate = UPD_Specify, CommandCode = 0x03, + Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, + // read diagnostic + new Command { CommandDelegate = UPD_ReadDiagnostic, CommandCode = 0x02, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, + // scan equal + new Command { CommandDelegate = UPD_ScanEqual, CommandCode = 0x11, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // scan high or equal + new Command { CommandDelegate = UPD_ScanHighOrEqual, CommandCode = 0x1d, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // scan low or equal + new Command { CommandDelegate = UPD_ScanLowOrEqual, CommandCode = 0x19, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // read deleted data + new Command { CommandDelegate = UPD_ReadDeletedData, CommandCode = 0x0c, MT = true, MF = true, SK = true, IsRead = true, + Direction = CommandDirection.OUT, ParameterByteCount = 8, ResultByteCount = 7 }, + // write data + new Command { CommandDelegate = UPD_WriteData, CommandCode = 0x05, MT = true, MF = true, IsWrite = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // write id + new Command { CommandDelegate = UPD_WriteID, CommandCode = 0x0d, MF = true, IsWrite = true, + Direction = CommandDirection.IN, ParameterByteCount = 5, ResultByteCount = 7 }, + // write deleted data + new Command { CommandDelegate = UPD_WriteDeletedData, CommandCode = 0x09, MT = true, MF = true, IsWrite = true, + Direction = CommandDirection.IN, ParameterByteCount = 8, ResultByteCount = 7 }, + // seek + new Command { CommandDelegate = UPD_Seek, CommandCode = 0x0f, + Direction = CommandDirection.OUT, ParameterByteCount = 2, ResultByteCount = 0 }, + // recalibrate (seek track00) + new Command { CommandDelegate = UPD_Recalibrate, CommandCode = 0x07, + Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 0 }, + // sense interrupt status + new Command { CommandDelegate = UPD_SenseInterruptStatus, CommandCode = 0x08, + Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 2 }, + // sense drive status + new Command { CommandDelegate = UPD_SenseDriveStatus, CommandCode = 0x04, + Direction = CommandDirection.OUT, ParameterByteCount = 1, ResultByteCount = 1 }, + // version + new Command { CommandDelegate = UPD_Version, CommandCode = 0x10, + Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, + // invalid + new Command { CommandDelegate = UPD_Invalid, CommandCode = 0x00, + Direction = CommandDirection.OUT, ParameterByteCount = 0, ResultByteCount = 1 }, + }; + } + + #endregion + + #region State Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("NEC-UPD765"); + + #region FDD + + ser.Sync("FDD_FLAG_MOTOR", ref FDD_FLAG_MOTOR); + + for (int i = 0; i < 4; i++) + { + ser.BeginSection("HITDrive_" + i); + DriveStates[i].SyncState(ser); + ser.EndSection(); + } + + ser.Sync("DiskDriveIndex", ref _diskDriveIndex); + // set active drive + DiskDriveIndex = _diskDriveIndex; + + #endregion + + #region Registers + + ser.Sync("_RegMain", ref StatusMain); + ser.Sync("_Reg0", ref Status0); + ser.Sync("_Reg1", ref Status1); + ser.Sync("_Reg2", ref Status2); + ser.Sync("_Reg3", ref Status3); + + #endregion + + #region Controller state + + ser.Sync("DriveLight", ref DriveLight); + ser.SyncEnum("ActivePhase", ref ActivePhase); + //ser.SyncEnum("ActiveDirection", ref ActiveDirection); + ser.SyncEnum("ActiveInterrupt", ref ActiveInterrupt); + ser.Sync("CommBuffer", ref CommBuffer, false); + ser.Sync("CommCounter", ref CommCounter); + ser.Sync("ResBuffer", ref ResBuffer, false); + ser.Sync("ExecBuffer", ref ExecBuffer, false); + ser.Sync("ExecCounter", ref ExecCounter); + ser.Sync("ExecLength", ref ExecLength); + ser.Sync("InterruptResultBuffer", ref InterruptResultBuffer, false); + ser.Sync("ResCounter", ref ResCounter); + ser.Sync("ResLength", ref ResLength); + ser.Sync("LastSectorDataWriteByte", ref LastSectorDataWriteByte); + ser.Sync("LastSectorDataReadByte", ref LastSectorDataReadByte); + ser.Sync("LastByteReceived", ref LastByteReceived); + + ser.Sync("_cmdIndex", ref _cmdIndex); + // resync the ActiveCommand + CMDIndex = _cmdIndex; + + ActiveCommandParams.SyncState(ser); + + ser.Sync("IndexPulseCounter", ref IndexPulseCounter); + //ser.SyncEnum("_activeStatus", ref _activeStatus); + //ser.SyncEnum("_statusRaised", ref _statusRaised); + + ser.Sync("CMD_FLAG_MT", ref CMD_FLAG_MT); + ser.Sync("CMD_FLAG_MF", ref CMD_FLAG_MF); + ser.Sync("CMD_FLAG_SK", ref CMD_FLAG_SK); + ser.Sync("SRT", ref SRT); + ser.Sync("HUT", ref HUT); + ser.Sync("HLT", ref HLT); + ser.Sync("ND", ref ND); + ser.Sync("SRT_Counter", ref SRT_Counter); + ser.Sync("HUT_Counter", ref HUT_Counter); + ser.Sync("HLT_Counter", ref HLT_Counter); + + ser.Sync("SectorDelayCounter", ref SectorDelayCounter); + ser.Sync("SectorID", ref SectorID); + + #endregion + + #region Timing + + ser.Sync("LastCPUCycle", ref LastCPUCycle); + ser.Sync("StatusDelay", ref StatusDelay); + ser.Sync("TickCounter", ref TickCounter); + ser.Sync("DriveCycleCounter", ref DriveCycleCounter); + + #endregion + + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs new file mode 100644 index 0000000000..7788bca84c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Disk/NECUPS765.Static.cs @@ -0,0 +1,107 @@ +using System.Collections; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Static helper methods + /// + #region Attribution + /* + Implementation based on the information contained here: + http://www.cpcwiki.eu/index.php/765_FDC + and here: + http://www.cpcwiki.eu/imgs/f/f3/UPD765_Datasheet_OCRed.pdf + */ + #endregion + public partial class NECUPD765 + { + /// + /// Returns the specified bit value from supplied byte + /// + /// + /// + /// + public static bool GetBit(int bitNumber, byte dataByte) + { + if (bitNumber < 0 || bitNumber > 7) + return false; + + BitArray bi = new BitArray(new byte[] { dataByte }); + + return bi[bitNumber]; + } + + /// + /// Sets the specified bit of the supplied byte to 1 + /// + /// + /// + public static void SetBit(int bitNumber, ref byte dataByte) + { + if (bitNumber < 0 || bitNumber > 7) + return; + + int db = (int)dataByte; + + db |= 1 << bitNumber; + + dataByte = (byte)db; + } + + /// + /// Sets the specified bit of the supplied byte to 0 + /// + /// + /// + public static void UnSetBit(int bitNumber, ref byte dataByte) + { + if (bitNumber < 0 || bitNumber > 7) + return; + + int db = (int)dataByte; + + db &= ~(1 << bitNumber); + + dataByte = (byte)db; + } + + /// + /// Returns a drive number (0-3) based on the first two bits of the supplied byte + /// + /// + /// + public static int GetUnitSelect(byte dataByte) + { + int driveNumber = dataByte & 0x03; + return driveNumber; + } + + /// + /// Sets the first two bits of a byte based on the supplied drive number (0-3) + /// + /// + /// + public static void SetUnitSelect(int driveNumber, ref byte dataByte) + { + switch (driveNumber) + { + case 0: + UnSetBit(SR0_US0, ref dataByte); + UnSetBit(SR0_US1, ref dataByte); + break; + case 1: + SetBit(SR0_US0, ref dataByte); + UnSetBit(SR0_US1, ref dataByte); + break; + case 2: + SetBit(SR0_US1, ref dataByte); + UnSetBit(SR0_US0, ref dataByte); + break; + case 3: + SetBit(SR0_US0, ref dataByte); + SetBit(SR0_US1, ref dataByte); + break; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs new file mode 100644 index 0000000000..e0828551f2 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/CursorJoystick.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Cursor joystick + /// Maps to a combination of 0xf7fe and 0xeffe + /// + public class CursorJoystick : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public CursorJoystick(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + private List btnLookups = new List + { + "Key 5", // left + "Key 8", // right + "Key 6", // down + "Key 7", // up + "Key 0", // fire + }; + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.Cursor; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + + if (isPressed) + { + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true); + } + else + { + if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos])) + { + // key is already pressed elswhere - leave it as is + } + else + { + // key is safe to unpress + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false); + } + } + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + if (_machine == null) + return false; + + var l = _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]); + return l; + } + + #endregion + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs new file mode 100644 index 0000000000..87cf5f491d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/KempstonJoystick.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + public class KempstonJoystick : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public KempstonJoystick(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.Kempston; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + if (isPressed) + _joyLine |= (1 << pos); + else + _joyLine &= ~(1 << pos); + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + return (_joyLine & (1 << pos)) != 0; + } + + #endregion + + /// + /// Active bits high + /// 0 0 0 F U D L R + /// + public int JoyLine + { + get { return _joyLine; } + set { _joyLine = value; } + } + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + + + /* + public readonly string[] _bitPos = new string[] + { + "P1 Right", + "P1 Left", + "P1 Down", + "P1 Up", + "P1 Button" + }; + */ + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs new file mode 100644 index 0000000000..850b371054 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/NullJoystick.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// A null joystick object + /// + public class NullJoystick : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public NullJoystick(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + + }.ToArray(); + } + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.NULL; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + if (isPressed) + _joyLine |= (1 << pos); + else + _joyLine &= ~(1 << pos); + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + return (_joyLine & (1 << pos)) != 0; + } + + #endregion + + /// + /// Active bits high + /// 0 0 0 F U D L R + /// + public int JoyLine + { + get { return _joyLine; } + set { _joyLine = value; } + } + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + + + /* + public readonly string[] _bitPos = new string[] + { + "P1 Right", + "P1 Left", + "P1 Down", + "P1 Up", + "P1 Button" + }; + */ + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs new file mode 100644 index 0000000000..e224646913 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick1.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Sinclair Joystick LEFT + /// Just maps to the standard keyboard and is read the same (from port 0xf7fe) + /// + public class SinclairJoystick1 : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public SinclairJoystick1(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + private List btnLookups = new List + { + "Key 1", // left + "Key 2", // right + "Key 3", // down + "Key 4", // up + "Key 5", // fire + }; + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.SinclairLEFT; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + + if (isPressed) + { + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true); + } + else + { + if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos])) + { + // key is already pressed elswhere - leave it as is + } + else + { + // key is safe to unpress + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false); + } + } + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + if (_machine == null) + return false; + + return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]); + } + + #endregion + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs new file mode 100644 index 0000000000..6533e76f23 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/SinclairJoystick2.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Sinclair Joystick RIGHT + /// Just maps to the standard keyboard and is read the same (from port 0xeffe) + /// + public class SinclairJoystick2 : IJoystick + { + private int _joyLine; + private SpectrumBase _machine; + + #region Construction + + public SinclairJoystick2(SpectrumBase machine, int playerNumber) + { + _machine = machine; + _joyLine = 0; + _playerNumber = playerNumber; + + ButtonCollection = new List + { + "P" + _playerNumber + " Left", + "P" + _playerNumber + " Right", + "P" + _playerNumber + " Down", + "P" + _playerNumber + " Up", + "P" + _playerNumber + " Button", + }.ToArray(); + } + + private List btnLookups = new List + { + "Key 6", // left + "Key 7", // right + "Key 8", // down + "Key 9", // up + "Key 0", // fire + }; + + #endregion + + #region IJoystick + + public JoystickType JoyType => JoystickType.SinclairRIGHT; + + public string[] ButtonCollection { get; set; } + + private int _playerNumber; + public int PlayerNumber + { + get { return _playerNumber; } + set { _playerNumber = value; } + } + + /// + /// Sets the joystick line based on key pressed + /// + /// + /// + public void SetJoyInput(string key, bool isPressed) + { + var pos = GetBitPos(key); + + if (isPressed) + { + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], true); + } + else + { + if (_machine.KeyboardDevice.GetKeyStatus(btnLookups[pos])) + { + // key is already pressed elswhere - leave it as is + } + else + { + // key is safe to unpress + _machine.KeyboardDevice.SetKeyStatus(btnLookups[pos], false); + } + } + } + + /// + /// Gets the state of a particular joystick binding + /// + /// + /// + public bool GetJoyInput(string key) + { + var pos = GetBitPos(key); + if (_machine == null) + return false; + + return _machine.KeyboardDevice.GetKeyStatus(btnLookups[pos]); + } + + #endregion + + /// + /// Gets the bit position of a particular joystick binding from the matrix + /// + /// + /// + public int GetBitPos(string key) + { + int index = Array.IndexOf(ButtonCollection, key); + return index; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs new file mode 100644 index 0000000000..7e23f843a7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Input/StandardKeyboard.cs @@ -0,0 +1,287 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The 48k keyboard device + /// + public class StandardKeyboard : IKeyboard + { + public SpectrumBase _machine { get; set; } + private byte[] LineStatus; + private string[] _keyboardMatrix; + private int[] _keyLine; + private bool _isIssue2Keyboard; + private string[] _nonMatrixKeys; + + public bool IsIssue2Keyboard + { + get { return _isIssue2Keyboard; } + set { _isIssue2Keyboard = value; } + } + + public int[] KeyLine + { + get { return _keyLine; } + set { _keyLine = value; } + } + + public string[] KeyboardMatrix + { + get { return _keyboardMatrix; } + set { _keyboardMatrix = value; } + } + + public string[] NonMatrixKeys + { + get { return _nonMatrixKeys; } + set { _nonMatrixKeys = value; } + } + + public StandardKeyboard(SpectrumBase machine) + { + _machine = machine; + + KeyboardMatrix = new string[] + { + // 0xfefe - 0 - 4 + "Key Caps Shift", "Key Z", "Key X", "Key C", "Key V", + // 0xfdfe - 5 - 9 + "Key A", "Key S", "Key D", "Key F", "Key G", + // 0xfbfe - 10 - 14 + "Key Q", "Key W", "Key E", "Key R", "Key T", + // 0xf7fe - 15 - 19 + "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", + // 0xeffe - 20 - 24 + "Key 0", "Key 9", "Key 8", "Key 7", "Key 6", + // 0xdffe - 25 - 29 + "Key P", "Key O", "Key I", "Key U", "Key Y", + // 0xbffe - 30 - 34 + "Key Return", "Key L", "Key K", "Key J", "Key H", + // 0x7ffe - 35 - 39 + "Key Space", "Key Symbol Shift", "Key M", "Key N", "Key B" + }; + + var nonMatrix = new List(); + + foreach (var key in _machine.Spectrum.ZXSpectrumControllerDefinition.BoolButtons) + { + if (!KeyboardMatrix.Any(s => s == key)) + nonMatrix.Add(key); + } + + NonMatrixKeys = nonMatrix.ToArray(); + + LineStatus = new byte[8]; + _keyLine = new int[] { 255, 255, 255, 255, 255, 255, 255, 255 }; + IsIssue2Keyboard = true; + } + + public void SetKeyStatus(string key, bool isPressed) + { + int k = GetByteFromKeyMatrix(key); + + if (k != 255) + { + var lineIndex = k / 5; + var lineMask = 1 << k % 5; + + _keyLine[lineIndex] = isPressed + ? (byte)(_keyLine[lineIndex] & ~lineMask) + : (byte)(_keyLine[lineIndex] | lineMask); + } + + // Combination keys that are not in the keyboard matrix + // but are available on the Spectrum+, 128k +2 & +3 + // (GetByteFromKeyMatrix() should return 255) + // Processed after the matrix keys - only presses handled (unpressed get done above) + if (k == 255) + { + if (isPressed) + { + switch (key) + { + // Delete key (simulates Caps Shift + 0) + case "Key Delete": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x1); + break; + // Cursor left (simulates Caps Shift + 5) + case "Key Left Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[3] = _keyLine[3] & ~(0x10); + break; + // Cursor right (simulates Caps Shift + 8) + case "Key Right Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x04); + break; + // Cursor up (simulates Caps Shift + 7) + case "Key Up Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x08); + break; + // Cursor down (simulates Caps Shift + 6) + case "Key Down Cursor": + _keyLine[0] = _keyLine[0] & ~(0x1); + _keyLine[4] = _keyLine[4] & ~(0x10); + break; + } + } + } + } + + public bool GetKeyStatus(string key) + { + byte keyByte = GetByteFromKeyMatrix(key); + var lineIndex = keyByte / 5; + var lineMask = 1 << keyByte % 5; + + return (_keyLine[lineIndex] & lineMask) == 0; + } + + public void ResetLineStatus() + { + lock (this) + { + for (int i = 0; i < KeyLine.Length; i++) + KeyLine[i] = 255; + } + } + + public byte GetLineStatus(byte lines) + { + lock(this) + { + byte status = 0; + lines = (byte)~lines; + var lineIndex = 0; + while (lines > 0) + { + if ((lines & 0x01) != 0) + status |= (byte)_keyLine[lineIndex]; + lineIndex++; + lines >>= 1; + } + var result = (byte)status; + + return result; + } + } + + public byte ReadKeyboardByte(ushort addr) + { + return GetLineStatus((byte)(addr >> 8)); + } + + public byte GetByteFromKeyMatrix(string key) + { + int index = Array.IndexOf(KeyboardMatrix, key); + return (byte)index; + } + + + #region IPortIODevice + + /// + /// Device responds to an IN instruction + /// + /// + /// + /// + public bool ReadPort(ushort port, ref int result) + { + /* + The high byte indicates which half-row of keys is being polled + A zero on one of these lines selects a particular half-row of five keys: + + IN: Reads keys (bit 0 to bit 4 inclusive) + 0xfefe SHIFT, Z, X, C, V 0xeffe 0, 9, 8, 7, 6 + 0xfdfe A, S, D, F, G 0xdffe P, O, I, U, Y + 0xfbfe Q, W, E, R, T 0xbffe ENTER, L, K, J, H + 0xf7fe 1, 2, 3, 4, 5 0x7ffe SPACE, SYM SHFT, M, N, B + + A zero in one of the five lowest bits means that the corresponding key is pressed. If more than one address line + is made low, the result is the logical AND of all single inputs, so a zero in a bit means that at least one of the + appropriate keys is pressed. For example, only if each of the five lowest bits of the result from reading from Port 00FE + (for instance by XOR A/IN A,(FE)) is one, no key is pressed + */ + + if ((port & 0x0001) != 0) + return false; + + if ((port & 0x8000) == 0) + { + result &= KeyLine[7]; + } + + if ((port & 0x4000) == 0) + { + result &= KeyLine[6]; + } + + if ((port & 0x2000) == 0) + { + result &= KeyLine[5]; + } + + if ((port & 0x1000) == 0) + { + result &= KeyLine[4]; + } + + if ((port & 0x800) == 0) + { + result &= KeyLine[3]; + } + + if ((port & 0x400) == 0) + { + result &= KeyLine[2]; + } + + if ((port & 0x200) == 0) + { + result &= KeyLine[1]; + } + + if ((port & 0x100) == 0) + { + result &= KeyLine[0]; + } + + // mask out lower 4 bits + result = result & 0x1f; + + // set bit 5 & 7 to 1 + result = result | 0xa0; + + return true; + } + + /// + /// Device responds to an OUT instruction + /// + /// + /// + /// + public bool WritePort(ushort port, int result) + { + // not implemented + return false; + } + + #endregion + + public void SyncState(Serializer ser) + { + ser.BeginSection("Keyboard"); + ser.Sync("LineStatus", ref LineStatus, false); + ser.Sync("_keyLine", ref _keyLine, false); + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs new file mode 100644 index 0000000000..972f7030aa --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/Rom/RomData.cs @@ -0,0 +1,83 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Information about spectrum ROM + /// + public class RomData + { + /// + /// ROM Contents + /// + public byte[] RomBytes + { + get { return _romBytes; } + set { _romBytes = value; } + } + + /// + /// Useful ROM addresses that are needed during tape operations + /// + public ushort SaveBytesRoutineAddress + { + get { return _saveBytesRoutineAddress; } + set { _saveBytesRoutineAddress = value; } + } + public ushort LoadBytesRoutineAddress + { + get { return _loadBytesRoutineAddress; } + set { _loadBytesRoutineAddress = value; } + } + public ushort SaveBytesResumeAddress + { + get { return _saveBytesResumeAddress; } + set { _saveBytesResumeAddress = value; } + } + public ushort LoadBytesResumeAddress + { + get { return _loadBytesResumeAddress; } + set { _loadBytesResumeAddress = value; } + } + public ushort LoadBytesInvalidHeaderAddress + { + get { return _loadBytesInvalidHeaderAddress; } + set { _loadBytesInvalidHeaderAddress = value; } + } + + private byte[] _romBytes; + private ushort _saveBytesRoutineAddress; + private ushort _loadBytesRoutineAddress; + private ushort _saveBytesResumeAddress; + private ushort _loadBytesResumeAddress; + private ushort _loadBytesInvalidHeaderAddress; + + + public static RomData InitROM(MachineType machineType, byte[] rom) + { + RomData RD = new RomData(); + RD.RomBytes = new byte[rom.Length]; + RD.RomBytes = rom; + + switch (machineType) + { + case MachineType.ZXSpectrum48: + RD.SaveBytesRoutineAddress = 0x04C2; + RD.SaveBytesResumeAddress = 0x0000; + RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C; + RD.LoadBytesResumeAddress = 0x05E2; + RD.LoadBytesInvalidHeaderAddress = 0x05B6; + break; + + case MachineType.ZXSpectrum128: + RD.SaveBytesRoutineAddress = 0x04C2; + RD.SaveBytesResumeAddress = 0x0000; + RD.LoadBytesRoutineAddress = 0x0808; //0x0556; //0x056C; + RD.LoadBytesResumeAddress = 0x05E2; + RD.LoadBytesInvalidHeaderAddress = 0x05B6; + break; + } + + return RD; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs new file mode 100644 index 0000000000..394c18875b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/AY38912.cs @@ -0,0 +1,808 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// AY-3-8912 Emulated Device + /// + /// Based heavily on the YM-2149F / AY-3-8910 emulator used in Unreal Speccy + /// (Originally created under Public Domain license by SMT jan.2006) + /// + /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.cpp + /// https://github.com/mkoloberdin/unrealspeccy/blob/master/sndrender/sndchip.h + /// + public class AY38912 : IPSG + { + #region Device Fields + + /// + /// The emulated machine (passed in via constructor) + /// + private SpectrumBase _machine; + + private int _tStatesPerFrame; + private int _sampleRate; + private int _samplesPerFrame; + private int _tStatesPerSample; + private short[] _audioBuffer; + private int _audioBufferIndex; + private int _lastStateRendered; + + #endregion + + #region Construction & Initialization + + /// + /// Main constructor + /// + public AY38912(SpectrumBase machine) + { + _machine = machine; + } + + /// + /// Initialises the AY chip + /// + public void Init(int sampleRate, int tStatesPerFrame) + { + InitTiming(sampleRate, tStatesPerFrame); + UpdateVolume(); + Reset(); + } + + #endregion + + #region IPortIODevice + + public bool ReadPort(ushort port, ref int value) + { + if (port != 0xfffd) + { + // port read is not addressing this device + return false; + } + + value = PortRead(); + + return true; + } + + public bool WritePort(ushort port, int value) + { + if (port == 0xfffd) + { + // register select + SelectedRegister = value & 0x0f; + return true; + } + else if (port == 0xbffd) + { + // Update the audiobuffer based on the current CPU cycle + // (this process the previous data BEFORE writing to the currently selected register) + int d = (int)(_machine.CurrentFrameCycle); + BufferUpdate(d); + + // write to register + PortWrite(value); + return true; + } + return false; + } + + #endregion + + #region AY Implementation + + #region Public Properties + + /// + /// AY mixer panning configuration + /// + [Flags] + public enum AYPanConfig + { + MONO = 0, + ABC = 1, + ACB = 2, + BAC = 3, + BCA = 4, + CAB = 5, + CBA = 6, + } + + /// + /// The AY panning configuration + /// + public AYPanConfig PanningConfiguration + { + get + { + return _currentPanTab; + } + set + { + if (value != _currentPanTab) + { + _currentPanTab = value; + UpdateVolume(); + } + } + } + + /// + /// The AY chip output volume + /// (0 - 100) + /// + public int Volume + { + get + { + return _volume; + } + set + { + //value = Math.Max(0, value); + //value = Math.Max(100, value); + if (_volume == value) + { + return; + } + _volume = value; + UpdateVolume(); + } + } + + /// + /// The currently selected register + /// + public int SelectedRegister + { + get { return _activeRegister; } + set + { + _activeRegister = (byte)value; + } + } + + #endregion + + #region Public Methods + + /// + /// Resets the PSG + /// + public void Reset() + { + /* + _noiseVal = 0x0FFFF; + _outABC = 0; + _outNoiseABC = 0; + _counterNoise = 0; + _counterA = 0; + _counterB = 0; + _counterC = 0; + _EnvelopeCounterBend = 0; + + // clear all the registers + for (int i = 0; i < 14; i++) + { + SelectedRegister = i; + PortWrite(0); + } + + randomSeed = 1; + + // number of frames to update + var fr = (_audioBufferIndex * _tStatesPerFrame) / _audioBuffer.Length; + + // update the audio buffer + BufferUpdate(fr); + */ + } + + /// + /// Reads the value from the currently selected register + /// + /// + public int PortRead() + { + return _registers[_activeRegister]; + } + + /// + /// Writes to the currently selected register + /// + /// + public void PortWrite(int value) + { + if (_activeRegister >= 0x10) + return; + + byte val = (byte)value; + + if (((1 << _activeRegister) & ((1 << 1) | (1 << 3) | (1 << 5) | (1 << 13))) != 0) + val &= 0x0F; + + if (((1 << _activeRegister) & ((1 << 6) | (1 << 8) | (1 << 9) | (1 << 10))) != 0) + val &= 0x1F; + + if (_activeRegister != 13 && _registers[_activeRegister] == val) + return; + + _registers[_activeRegister] = val; + + switch (_activeRegister) + { + // Channel A (Combined Pitch) + // (not written to directly) + case 0: + case 1: + _dividerA = _registers[AY_A_FINE] | (_registers[AY_A_COARSE] << 8); + break; + // Channel B (Combined Pitch) + // (not written to directly) + case 2: + case 3: + _dividerB = _registers[AY_B_FINE] | (_registers[AY_B_COARSE] << 8); + break; + // Channel C (Combined Pitch) + // (not written to directly) + case 4: + case 5: + _dividerC = _registers[AY_C_FINE] | (_registers[AY_C_COARSE] << 8); + break; + // Noise Pitch + case 6: + _dividerN = val * 2; + break; + // Mixer + case 7: + _bit0 = 0 - ((val >> 0) & 1); + _bit1 = 0 - ((val >> 1) & 1); + _bit2 = 0 - ((val >> 2) & 1); + _bit3 = 0 - ((val >> 3) & 1); + _bit4 = 0 - ((val >> 4) & 1); + _bit5 = 0 - ((val >> 5) & 1); + break; + // Channel Volumes + case 8: + _eMaskA = (val & 0x10) != 0 ? -1 : 0; + _vA = ((val & 0x0F) * 2 + 1) & ~_eMaskA; + break; + case 9: + _eMaskB = (val & 0x10) != 0 ? -1 : 0; + _vB = ((val & 0x0F) * 2 + 1) & ~_eMaskB; + break; + case 10: + _eMaskC = (val & 0x10) != 0 ? -1 : 0; + _vC = ((val & 0x0F) * 2 + 1) & ~_eMaskC; + break; + // Envelope (Combined Duration) + // (not written to directly) + case 11: + case 12: + _dividerE = _registers[AY_E_FINE] | (_registers[AY_E_COARSE] << 8); + break; + // Envelope Shape + case 13: + // reset the envelope counter + _countE = 0; + + if ((_registers[AY_E_SHAPE] & 4) != 0) + { + // attack + _eState = 0; + _eDirection = 1; + } + else + { + // decay + _eState = 31; + _eDirection = -1; + } + break; + case 14: + // IO Port - not implemented + break; + } + } + + /// + /// Start of frame + /// + public void StartFrame() + { + _audioBufferIndex = 0; + BufferUpdate(0); + } + + /// + /// End of frame + /// + public void EndFrame() + { + BufferUpdate(_tStatesPerFrame); + } + + /// + /// Updates the audiobuffer based on the current frame t-state + /// + /// + public void UpdateSound(int frameCycle) + { + BufferUpdate(frameCycle); + } + + #endregion + + #region Private Fields + + /// + /// Register indicies + /// + private const int AY_A_FINE = 0; + private const int AY_A_COARSE = 1; + private const int AY_B_FINE = 2; + private const int AY_B_COARSE = 3; + private const int AY_C_FINE = 4; + private const int AY_C_COARSE = 5; + private const int AY_NOISEPITCH = 6; + private const int AY_MIXER = 7; + private const int AY_A_VOL = 8; + private const int AY_B_VOL = 9; + private const int AY_C_VOL = 10; + private const int AY_E_FINE = 11; + private const int AY_E_COARSE = 12; + private const int AY_E_SHAPE = 13; + private const int AY_PORT_A = 14; + private const int AY_PORT_B = 15; + + /// + /// The register array + /* + The AY-3-8910/8912 contains 16 internal registers as follows: + + Register Function Range + 0 Channel A fine pitch 8-bit (0-255) + 1 Channel A course pitch 4-bit (0-15) + 2 Channel B fine pitch 8-bit (0-255) + 3 Channel B course pitch 4-bit (0-15) + 4 Channel C fine pitch 8-bit (0-255) + 5 Channel C course pitch 4-bit (0-15) + 6 Noise pitch 5-bit (0-31) + 7 Mixer 8-bit (see below) + 8 Channel A volume 4-bit (0-15, see below) + 9 Channel B volume 4-bit (0-15, see below) + 10 Channel C volume 4-bit (0-15, see below) + 11 Envelope fine duration 8-bit (0-255) + 12 Envelope course duration 8-bit (0-255) + 13 Envelope shape 4-bit (0-15) + 14 I/O port A 8-bit (0-255) + 15 I/O port B 8-bit (0-255) (Not present on the AY-3-8912) + + * The volume registers (8, 9 and 10) contain a 4-bit setting but if bit 5 is set then that channel uses the + envelope defined by register 13 and ignores its volume setting. + * The mixer (register 7) is made up of the following bits (low=enabled): + + Bit: 7 6 5 4 3 2 1 0 + Register: I/O I/O Noise Noise Noise Tone Tone Tone + Channel: B A C B A C B A + + The AY-3-8912 ignores bit 7 of this register. + */ + /// + private int[] _registers = new int[16]; + + /// + /// The currently selected register + /// + private byte _activeRegister; + + /// + /// The frequency of the AY chip + /// + private static int _chipFrequency = 1773400; + + /// + /// The rendering resolution of the chip + /// + private double _resolution = 50D * 8D / _chipFrequency; + + /// + /// Channel generator state + /// + private int _bitA; + private int _bitB; + private int _bitC; + + /// + /// Envelope state + /// + private int _eState; + + /// + /// Envelope direction + /// + private int _eDirection; + + /// + /// Noise seed + /// + private int _noiseSeed; + + /// + /// Mixer state + /// + private int _bit0; + private int _bit1; + private int _bit2; + private int _bit3; + private int _bit4; + private int _bit5; + + /// + /// Noise generator state + /// + private int _bitN; + + /// + /// Envelope masks + /// + private int _eMaskA; + private int _eMaskB; + private int _eMaskC; + + /// + /// Amplitudes + /// + private int _vA; + private int _vB; + private int _vC; + + /// + /// Channel gen counters + /// + private int _countA; + private int _countB; + private int _countC; + + /// + /// Envelope gen counter + /// + private int _countE; + + /// + /// Noise gen counter + /// + private int _countN; + + /// + /// Channel gen dividers + /// + private int _dividerA; + private int _dividerB; + private int _dividerC; + + /// + /// Envelope gen divider + /// + private int _dividerE; + + /// + /// Noise gen divider + /// + private int _dividerN; + + /// + /// Panning table list + /// + private static List PanTabs = new List + { + // MONO + new uint[] { 50,50, 50,50, 50,50 }, + // ABC + new uint[] { 100,10, 66,66, 10,100 }, + // ACB + new uint[] { 100,10, 10,100, 66,66 }, + // BAC + new uint[] { 66,66, 100,10, 10,100 }, + // BCA + new uint[] { 10,100, 100,10, 66,66 }, + // CAB + new uint[] { 66,66, 10,100, 100,10 }, + // CBA + new uint[] { 10,100, 66,66, 100,10 } + }; + + /// + /// The currently selected panning configuration + /// + private AYPanConfig _currentPanTab = AYPanConfig.ABC; + + /// + /// The current volume + /// + private int _volume = 75; + + /// + /// Volume tables state + /// + private uint[][] _volumeTables; + + /// + /// Volume table to be used + /// + private static uint[] AYVolumes = new uint[] + { + 0x0000,0x0000,0x0340,0x0340,0x04C0,0x04C0,0x06F2,0x06F2, + 0x0A44,0x0A44,0x0F13,0x0F13,0x1510,0x1510,0x227E,0x227E, + 0x289F,0x289F,0x414E,0x414E,0x5B21,0x5B21,0x7258,0x7258, + 0x905E,0x905E,0xB550,0xB550,0xD7A0,0xD7A0,0xFFFF,0xFFFF, + }; + + #endregion + + #region Private Methods + + /// + /// Forces an update of the volume tables + /// + private void UpdateVolume() + { + int upperFloor = 40000; + var inc = (0xFFFF - upperFloor) / 100; + + var vol = inc * _volume; // ((ulong)0xFFFF * (ulong)_volume / 100UL) - 20000 ; + _volumeTables = new uint[6][]; + + // parent array + for (int j = 0; j < _volumeTables.Length; j++) + { + _volumeTables[j] = new uint[32]; + + // child array + for (int i = 0; i < _volumeTables[j].Length; i++) + { + _volumeTables[j][i] = (uint)( + (PanTabs[(int)_currentPanTab][j] * AYVolumes[i] * vol) / + (3 * 65535 * 100)); + } + } + } + + private int mult_const; + + /// + /// Initializes timing information for the frame + /// + /// + /// + private void InitTiming(int sampleRate, int frameTactCount) + { + _sampleRate = sampleRate; + _tStatesPerFrame = frameTactCount; + _samplesPerFrame = 882; + + _tStatesPerSample = 79; //(int)Math.Round(((double)_tStatesPerFrame * 50D) / + //(16D * (double)_sampleRate), + //MidpointRounding.AwayFromZero); + + //_samplesPerFrame = _tStatesPerFrame / _tStatesPerSample; + _audioBuffer = new short[_samplesPerFrame * 2]; //[_sampleRate / 50]; + _audioBufferIndex = 0; + + mult_const = ((_chipFrequency / 8) << 14) / _machine.ULADevice.ClockSpeed; + + var aytickspercputick = (double)_machine.ULADevice.ClockSpeed / (double)_chipFrequency; + int ayCyclesPerSample = (int)((double)_tStatesPerSample * (double)aytickspercputick); + } + + /// + /// Updates the audiobuffer based on the current frame t-state + /// + /// + private void BufferUpdate(int cycle) + { + if (cycle > _tStatesPerFrame) + { + // we are outside of the frame - just process the last value + cycle = _tStatesPerFrame; + } + + // get the current length of the audiobuffer + int bufferLength = _samplesPerFrame; // _audioBuffer.Length; + + int toEnd = ((bufferLength * cycle) / _tStatesPerFrame); + + // loop through the number of samples we need to render + while (_audioBufferIndex < toEnd) + { + // run the AY chip processing at the correct resolution + for (int i = 0; i < _tStatesPerSample / 14; i++) + { + if (++_countA >= _dividerA) + { + _countA = 0; + _bitA ^= -1; + } + + if (++_countB >= _dividerB) + { + _countB = 0; + _bitB ^= -1; + } + + if (++_countC >= _dividerC) + { + _countC = 0; + _bitC ^= -1; + } + + if (++_countN >= _dividerN) + { + _countN = 0; + _noiseSeed = (_noiseSeed * 2 + 1) ^ (((_noiseSeed >> 16) ^ (_noiseSeed >> 13)) & 1); + _bitN = 0 - ((_noiseSeed >> 16) & 1); + } + + if (++_countE >= _dividerE) + { + _countE = 0; + _eState += +_eDirection; + + if ((_eState & ~31) != 0) + { + var mask = (1 << _registers[AY_E_SHAPE]); + + if ((mask & ((1 << 0) | (1 << 1) | (1 << 2) | + (1 << 3) | (1 << 4) | (1 << 5) | (1 << 6) | + (1 << 7) | (1 << 9) | (1 << 15))) != 0) + { + _eState = _eDirection = 0; + } + else if ((mask & ((1 << 8) | (1 << 12))) != 0) + { + _eState &= 31; + } + else if ((mask & ((1 << 10) | (1 << 14))) != 0) + { + _eDirection = -_eDirection; + _eState += _eDirection; + } + else + { + // 11,13 + _eState = 31; + _eDirection = 0; + } + } + } + } + + // mix the sample + var mixA = ((_eMaskA & _eState) | _vA) & ((_bitA | _bit0) & (_bitN | _bit3)); + var mixB = ((_eMaskB & _eState) | _vB) & ((_bitB | _bit1) & (_bitN | _bit4)); + var mixC = ((_eMaskC & _eState) | _vC) & ((_bitC | _bit2) & (_bitN | _bit5)); + + var l = _volumeTables[0][mixA]; + var r = _volumeTables[1][mixA]; + + l += _volumeTables[2][mixB]; + r += _volumeTables[3][mixB]; + l += _volumeTables[4][mixC]; + r += _volumeTables[5][mixC]; + + _audioBuffer[_audioBufferIndex * 2] = (short)l; + _audioBuffer[(_audioBufferIndex * 2) + 1] = (short)r; + + _audioBufferIndex++; + } + + _lastStateRendered = cycle; + } + + #endregion + + #endregion + + #region ISoundProvider + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + _audioBuffer = new short[_samplesPerFrame * 2]; + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + nsamp = _samplesPerFrame; + samples = _audioBuffer; + DiscardSamples(); + } + + #endregion + + #region State Serialization + + public int nullDump = 0; + + /// + /// State serialization + /// + /// + public void SyncState(Serializer ser) + { + ser.BeginSection("PSG-AY"); + + ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); + ser.Sync("_sampleRate", ref _sampleRate); + ser.Sync("_samplesPerFrame", ref _samplesPerFrame); + ser.Sync("_tStatesPerSample", ref _tStatesPerSample); + ser.Sync("_audioBufferIndex", ref _audioBufferIndex); + ser.Sync("_audioBuffer", ref _audioBuffer, false); + + ser.Sync("_registers", ref _registers, false); + ser.Sync("_activeRegister", ref _activeRegister); + ser.Sync("_bitA", ref _bitA); + ser.Sync("_bitB", ref _bitB); + ser.Sync("_bitC", ref _bitC); + ser.Sync("_eState", ref _eState); + ser.Sync("_eDirection", ref _eDirection); + ser.Sync("_noiseSeed", ref _noiseSeed); + ser.Sync("_bit0", ref _bit0); + ser.Sync("_bit1", ref _bit1); + ser.Sync("_bit2", ref _bit2); + ser.Sync("_bit3", ref _bit3); + ser.Sync("_bit4", ref _bit4); + ser.Sync("_bit5", ref _bit5); + ser.Sync("_bitN", ref _bitN); + ser.Sync("_eMaskA", ref _eMaskA); + ser.Sync("_eMaskB", ref _eMaskB); + ser.Sync("_eMaskC", ref _eMaskC); + ser.Sync("_vA", ref _vA); + ser.Sync("_vB", ref _vB); + ser.Sync("_vC", ref _vC); + ser.Sync("_countA", ref _countA); + ser.Sync("_countB", ref _countB); + ser.Sync("_countC", ref _countC); + ser.Sync("_countE", ref _countE); + ser.Sync("_countN", ref _countN); + ser.Sync("_dividerA", ref _dividerA); + ser.Sync("_dividerB", ref _dividerB); + ser.Sync("_dividerC", ref _dividerC); + ser.Sync("_dividerE", ref _dividerE); + ser.Sync("_dividerN", ref _dividerN); + ser.SyncEnum("_currentPanTab", ref _currentPanTab); + ser.Sync("_volume", ref nullDump); + + for (int i = 0; i < 6; i++) + { + ser.Sync("volTable" + i, ref _volumeTables[i], false); + } + + if (ser.IsReader) + _volume = _machine.Spectrum.Settings.AYVolume; + + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs new file mode 100644 index 0000000000..5b518b031c --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Hardware/SoundOuput/Beeper.cs @@ -0,0 +1,218 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Logical Beeper class + /// Represents the piezoelectric buzzer used in the Spectrum to produce sound + /// The beeper is controlled by rapidly toggling bit 4 of port &FE + /// + /// It is instantiated twice, once for speccy beeper output, and once tape buzzer emulation + /// + /// This implementation uses BlipBuffer and should *always* output at 44100 with 882 samples per frame + /// (so that it can be mixed easily further down the line) + /// + public class Beeper : ISoundProvider, IBeeperDevice + { + #region Fields and Properties + + /// + /// Sample Rate + /// This usually has to be 44100 for ISoundProvider + /// + private int _sampleRate; + public int SampleRate + { + get { return _sampleRate; } + set { _sampleRate = value; } + } + + /// + /// Buzzer volume + /// Accepts an int 0-100 value + /// + private int _volume; + public int Volume + { + get + { + return VolumeConverterOut(_volume); + } + set + { + var newVol = VolumeConverterIn(value); + if (newVol != _volume) + blip.Clear(); + _volume = VolumeConverterIn(value); + } + } + + /// + /// The last used volume (used to modify blipbuffer delta values) + /// + private int lastVolume; + + /// + /// The number of cpu cycles per frame + /// + private long _tStatesPerFrame; + + /// + /// The parent emulated machine + /// + private SpectrumBase _machine; + + /// + /// The last pulse + /// + private bool LastPulse; + + /// + /// The last T-State (cpu cycle) that the last pulse was received + /// + private long LastPulseTState; + + /// + /// Device blipbuffer + /// + private readonly BlipBuffer blip = new BlipBuffer(882); + + #endregion + + #region Private Methods + + /// + /// Takes an int 0-100 and returns the relevant short volume to output + /// + /// + /// + private int VolumeConverterIn(int vol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + return vol * increment; + } + + /// + /// Takes an short volume and returns the relevant int value 0-100 + /// + /// + /// + private int VolumeConverterOut(int shortvol) + { + int maxLimit = short.MaxValue / 3; + int increment = maxLimit / 100; + + if (shortvol > maxLimit) + shortvol = maxLimit; + + return shortvol / increment; + } + + #endregion + + #region Construction & Initialisation + + public Beeper(SpectrumBase machine) + { + _machine = machine; + } + + /// + /// Initialises the beeper + /// + public void Init(int sampleRate, int tStatesPerFrame) + { + blip.SetRates((tStatesPerFrame * 50), sampleRate); + _sampleRate = sampleRate; + _tStatesPerFrame = tStatesPerFrame; + } + + #endregion + + #region IBeeperDevice + + /// + /// Processes an incoming pulse value and adds it to the blipbuffer + /// + /// + public void ProcessPulseValue(bool pulse) + { + if (!_machine._renderSound) + return; + + if (LastPulse == pulse) + { + // no change + blip.AddDelta((uint)_machine.CurrentFrameCycle, 0); + } + + else + { + if (pulse) + blip.AddDelta((uint)_machine.CurrentFrameCycle, (short)(_volume)); + else + blip.AddDelta((uint)_machine.CurrentFrameCycle, -(short)(_volume)); + + lastVolume = _volume; + } + + LastPulse = pulse; + } + + #endregion + + #region ISoundProvider + + public bool CanProvideAsync => false; + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + blip.Clear(); + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + blip.EndFrame((uint)_tStatesPerFrame); + nsamp = blip.SamplesAvailable(); + samples = new short[nsamp * 2]; + blip.ReadSamples(samples, nsamp, true); + for (int i = 0; i < nsamp * 2; i += 2) + { + samples[i + 1] = samples[i]; + } + } + + #endregion + + #region State Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("Buzzer"); + ser.Sync("_tStatesPerFrame", ref _tStatesPerFrame); + ser.Sync("_sampleRate", ref _sampleRate); + ser.Sync("LastPulse", ref LastPulse); + ser.Sync("LastPulseTState", ref LastPulseTState); + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs new file mode 100644 index 0000000000..10a1728cb9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/CPUMonitor.cs @@ -0,0 +1,408 @@ +using BizHawk.Common; +using BizHawk.Emulation.Cores.Components.Z80A; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// An intermediary class that manages cycling the ULA and CPU + /// along with inherent Port and Memory contention + /// + public class CPUMonitor + { + #region Devices + + private SpectrumBase _machine; + private Z80A _cpu; + public MachineType machineType = MachineType.ZXSpectrum48; + + #endregion + + #region Lookups + + /// + /// CPU total executes t-states + /// + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; + + /// + /// Current BUSRQ line array + /// + public ushort BUSRQ + { + get + { + switch (machineType) + { + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + return _cpu.MEMRQ[_cpu.bus_pntr]; + default: + return _cpu.BUSRQ[_cpu.mem_pntr]; + } + } + } + + #endregion + + #region Construction + + public CPUMonitor(SpectrumBase machine) + { + _machine = machine; + _cpu = _machine.CPU; + } + + #endregion + + #region State + + /// + /// The last 16-bit port address that was detected + /// + public ushort lastPortAddr; + + /// + /// If true, the next read memory operation has been contended + /// + public bool NextMemReadContended; + + #endregion + + #region Methods + + /// + /// Handles the ULA and CPU cycle clocks, along with any memory and port contention + /// + public void ExecuteCycle() + { + // simulate the ULA clock cycle before the CPU cycle + _machine.ULADevice.CycleClock(TotalExecutedCycles); + + // is the next CPU cycle causing a BUSRQ or IORQ? + if (BUSRQ > 0) + { + // check for IORQ + if (!CheckIO()) + { + // is the memory address of the BUSRQ potentially contended? + if (_machine.IsContended(AscertainBUSRQAddress())) + { + var cont = _machine.ULADevice.GetContentionValue((int)_machine.CurrentFrameCycle); + if (cont > 0) + { + _cpu.TotalExecutedCycles += cont; + NextMemReadContended = true; + } + } + } + } + + _cpu.ExecuteOne(); + } + + /// + /// Looks up the current BUSRQ address that is about to be signalled on the upcoming cycle + /// + /// + private ushort AscertainBUSRQAddress() + { + ushort addr = 0; + switch (BUSRQ) + { + // PCh + case 1: + addr = (ushort)(_cpu.Regs[_cpu.PCl] | _cpu.Regs[_cpu.PCh] << 8); + break; + // SPh + case 3: + addr = (ushort)(_cpu.Regs[_cpu.SPl] | _cpu.Regs[_cpu.SPh] << 8); + break; + // A + case 4: + addr = (ushort)(_cpu.Regs[_cpu.F] | _cpu.Regs[_cpu.A] << 8); + break; + // B + case 6: + addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8); + break; + // D + case 8: + addr = (ushort)(_cpu.Regs[_cpu.E] | _cpu.Regs[_cpu.D] << 8); + break; + // H + case 10: + addr = (ushort)(_cpu.Regs[_cpu.L] | _cpu.Regs[_cpu.H] << 8); + break; + // W + case 12: + addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8); + break; + // Ixh + case 16: + addr = (ushort)(_cpu.Regs[_cpu.Ixl] | _cpu.Regs[_cpu.Ixh] << 8); + break; + // Iyh + case 18: + addr = (ushort)(_cpu.Regs[_cpu.Iyl] | _cpu.Regs[_cpu.Iyh] << 8); + break; + // I + case 21: + addr = (ushort)(_cpu.Regs[_cpu.R] | _cpu.Regs[_cpu.I] << 8); + break; + // BC + case Z80A.BIO1: + case Z80A.BIO2: + case Z80A.BIO3: + case Z80A.BIO4: + addr = (ushort)(_cpu.Regs[_cpu.C] | _cpu.Regs[_cpu.B] << 8); + break; + // WZ + case Z80A.WIO1: + case Z80A.WIO2: + case Z80A.WIO3: + case Z80A.WIO4: + addr = (ushort)(_cpu.Regs[_cpu.Z] | _cpu.Regs[_cpu.W] << 8); + break; + } + + return addr; + } + + /// + /// Running every cycle, this determines whether the upcoming BUSRQ is for an IO operation + /// Also processes any contention + /// + /// + private bool CheckIO() + { + bool isIO = false; + + switch (BUSRQ) + { + // BC: T1 + case Z80A.BIO1: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(1)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T2 + case Z80A.BIO2: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(2)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T3 + case Z80A.BIO3: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(3)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // BC: T4 + case Z80A.BIO4: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(4)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + + // WZ: T1 + case Z80A.WIO1: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(1)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T2 + case Z80A.WIO2: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(2)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T3 + case Z80A.WIO3: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(3)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + // WZ: T4 + case Z80A.WIO4: + lastPortAddr = AscertainBUSRQAddress(); + isIO = true; + if (IsIOCycleContended(4)) + _cpu.TotalExecutedCycles += _machine.ULADevice.GetPortContentionValue((int)_machine.CurrentFrameCycle); + break; + } + + return isIO; + } + + /// + /// Returns TRUE if the supplied T-cycle within an IO operation has the possibility of being contended + /// This can be different based on the emulated ZX Spectrum model + /// + /// + /// + private bool IsIOCycleContended(int T) + { + bool lowBitSet = (lastPortAddr & 0x0001) != 0; + bool highByte407f = false; + + switch (machineType) + { + case MachineType.ZXSpectrum16: + case MachineType.ZXSpectrum48: + + if ((lastPortAddr & 0xc000) == 0x4000) + highByte407f = true; + + if (highByte407f) + { + // high byte 40-7f + if (lowBitSet) + { + // high byte 40-7f + // low bit set + // C:1, C:1, C:1, C:1 + switch (T) + { + case 1: + case 2: + case 3: + case 4: + return true; + } + } + else + { + // high byte 40-7f + // low bit reset + // C:1, C:3 + switch (T) + { + case 1: + case 2: + return true; + } + } + } + else + { + // high byte not 40-7f + if (lowBitSet) + { + // high byte not 40-7f + // low bit set + // N:4 + } + else + { + // high byte not 40-7f + // low bit reset + // N:1, C:3 + switch (T) + { + case 2: + return true; + } + } + } + break; + + case MachineType.ZXSpectrum128: + case MachineType.ZXSpectrum128Plus2: + if ((lastPortAddr & 0xc000) == 0x4000 || (lastPortAddr & 0xc000) == 0xc000 && _machine.ContendedBankPaged()) + highByte407f = true; + + if (highByte407f) + { + // high byte 40-7f + if (lowBitSet) + { + // high byte 40-7f + // low bit set + // C:1, C:1, C:1, C:1 + switch (T) + { + case 1: + case 2: + case 3: + case 4: + return true; + } + } + else + { + // high byte 40-7f + // low bit reset + // C:1, C:3 + switch (T) + { + case 1: + case 2: + return true; + } + } + } + else + { + // high byte not 40-7f + if (lowBitSet) + { + // high byte not 40-7f + // low bit set + // N:4 + } + else + { + // high byte not 40-7f + // low bit reset + // N:1, C:3 + switch (T) + { + case 2: + return true; + } + } + } + break; + + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + // No contention occurs as the ULA only applies contention when the Z80 MREQ line is active + // (which is not during an IO operation) + break; + } + + return false; + } + + /// + /// Called when the first byte of an instruction is fetched + /// + /// + public void OnExecFetch(ushort firstByte) + { + // fetch instruction without incrementing pc + //_cpu.FetchInstruction(_cpu.FetchMemory(firstByte)); + } + + #endregion + + #region Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("CPUMonitor"); + ser.Sync("lastPortAddr", ref lastPortAddr); + ser.Sync("NextMemReadContended", ref NextMemReadContended); + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs new file mode 100644 index 0000000000..1e5901a6ad --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/MachineType.cs @@ -0,0 +1,39 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The various spectrum models ZXHawk emulates + /// + public enum MachineType + { + /// + /// Original Sinclair Spectrum 16K model + /// + ZXSpectrum16, + + /// + /// Sinclair Spectrum 48K model + /// + ZXSpectrum48, + + /// + /// Sinclair Spectrum 128K model + /// + ZXSpectrum128, + + /// + /// Sinclair Spectrum 128 +2 model + /// + ZXSpectrum128Plus2, + + /// + /// Sinclair Spectrum 128 +2a model (same as the +3 just without disk drive) + /// + ZXSpectrum128Plus2a, + + /// + /// Sinclair Spectrum 128 +3 model + /// + ZXSpectrum128Plus3 + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs new file mode 100644 index 0000000000..b64fa125eb --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Input.cs @@ -0,0 +1,350 @@ +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Input * + /// + public abstract partial class SpectrumBase + { + string Play = "Play Tape"; + string Stop = "Stop Tape"; + string RTZ = "RTZ Tape"; + string Record = "Record Tape"; + string NextTape = "Insert Next Tape"; + string PrevTape = "Insert Previous Tape"; + string NextBlock = "Next Tape Block"; + string PrevBlock = "Prev Tape Block"; + string TapeStatus = "Get Tape Status"; + + string NextDisk = "Insert Next Disk"; + string PrevDisk = "Insert Previous Disk"; + string EjectDisk = "Eject Current Disk"; + string DiskStatus = "Get Disk Status"; + + string HardResetStr = "Power"; + string SoftResetStr = "Reset"; + + bool pressed_Play = false; + bool pressed_Stop = false; + bool pressed_RTZ = false; + bool pressed_NextTape = false; + bool pressed_PrevTape = false; + bool pressed_NextBlock = false; + bool pressed_PrevBlock = false; + bool pressed_TapeStatus = false; + bool pressed_NextDisk = false; + bool pressed_PrevDisk = false; + bool pressed_EjectDisk = false; + bool pressed_DiskStatus = false; + bool pressed_HardReset = false; + bool pressed_SoftReset = false; + + /// + /// Cycles through all the input callbacks + /// This should be done once per frame + /// + public void PollInput() + { + Spectrum.InputCallbacks.Call(); + + lock (this) + { + // parse single keyboard matrix keys + for (var i = 0; i < KeyboardDevice.KeyboardMatrix.Length; i++) + { + string key = KeyboardDevice.KeyboardMatrix[i]; + bool prevState = KeyboardDevice.GetKeyStatus(key); + bool currState = Spectrum._controller.IsPressed(key); + + if (currState != prevState) + KeyboardDevice.SetKeyStatus(key, currState); + } + + // non matrix keys + foreach (string k in KeyboardDevice.NonMatrixKeys) + { + if (!k.StartsWith("Key")) + continue; + + bool currState = Spectrum._controller.IsPressed(k); + + KeyboardDevice.SetKeyStatus(k, currState); + } + + // J1 + foreach (string j in JoystickCollection[0].ButtonCollection) + { + bool prevState = JoystickCollection[0].GetJoyInput(j); + bool currState = Spectrum._controller.IsPressed(j); + + if (currState != prevState) + JoystickCollection[0].SetJoyInput(j, currState); + } + + // J2 + foreach (string j in JoystickCollection[1].ButtonCollection) + { + bool prevState = JoystickCollection[1].GetJoyInput(j); + bool currState = Spectrum._controller.IsPressed(j); + + if (currState != prevState) + JoystickCollection[1].SetJoyInput(j, currState); + } + + // J3 + foreach (string j in JoystickCollection[2].ButtonCollection) + { + bool prevState = JoystickCollection[2].GetJoyInput(j); + bool currState = Spectrum._controller.IsPressed(j); + + if (currState != prevState) + JoystickCollection[2].SetJoyInput(j, currState); + } + } + + // Tape control + if (Spectrum._controller.IsPressed(Play)) + { + if (!pressed_Play) + { + Spectrum.OSD_FireInputMessage(Play); + TapeDevice.Play(); + pressed_Play = true; + } + } + else + pressed_Play = false; + + if (Spectrum._controller.IsPressed(Stop)) + { + if (!pressed_Stop) + { + Spectrum.OSD_FireInputMessage(Stop); + TapeDevice.Stop(); + pressed_Stop = true; + } + } + else + pressed_Stop = false; + + if (Spectrum._controller.IsPressed(RTZ)) + { + if (!pressed_RTZ) + { + Spectrum.OSD_FireInputMessage(RTZ); + TapeDevice.RTZ(); + pressed_RTZ = true; + } + } + else + pressed_RTZ = false; + + if (Spectrum._controller.IsPressed(Record)) + { + + } + if (Spectrum._controller.IsPressed(NextTape)) + { + if (!pressed_NextTape) + { + Spectrum.OSD_FireInputMessage(NextTape); + TapeMediaIndex++; + pressed_NextTape = true; + } + } + else + pressed_NextTape = false; + + if (Spectrum._controller.IsPressed(PrevTape)) + { + if (!pressed_PrevTape) + { + Spectrum.OSD_FireInputMessage(PrevTape); + TapeMediaIndex--; + pressed_PrevTape = true; + } + } + else + pressed_PrevTape = false; + + if (Spectrum._controller.IsPressed(NextBlock)) + { + if (!pressed_NextBlock) + { + Spectrum.OSD_FireInputMessage(NextBlock); + TapeDevice.SkipBlock(true); + pressed_NextBlock = true; + } + } + else + pressed_NextBlock = false; + + if (Spectrum._controller.IsPressed(PrevBlock)) + { + if (!pressed_PrevBlock) + { + Spectrum.OSD_FireInputMessage(PrevBlock); + TapeDevice.SkipBlock(false); + pressed_PrevBlock = true; + } + } + else + pressed_PrevBlock = false; + + if (Spectrum._controller.IsPressed(TapeStatus)) + { + if (!pressed_TapeStatus) + { + //Spectrum.OSD_FireInputMessage(TapeStatus); + Spectrum.OSD_ShowTapeStatus(); + pressed_TapeStatus = true; + } + } + else + pressed_TapeStatus = false; + + if (Spectrum._controller.IsPressed(HardResetStr)) + { + if (!pressed_HardReset) + { + HardReset(); + pressed_HardReset = true; + } + } + else + pressed_HardReset = false; + + if (Spectrum._controller.IsPressed(SoftResetStr)) + { + if (!pressed_SoftReset) + { + SoftReset(); + pressed_SoftReset = true; + } + } + else + pressed_SoftReset = false; + + // disk control + if (Spectrum._controller.IsPressed(NextDisk)) + { + if (!pressed_NextDisk) + { + Spectrum.OSD_FireInputMessage(NextDisk); + DiskMediaIndex++; + pressed_NextDisk = true; + } + } + else + pressed_NextDisk = false; + + if (Spectrum._controller.IsPressed(PrevDisk)) + { + if (!pressed_PrevDisk) + { + Spectrum.OSD_FireInputMessage(PrevDisk); + DiskMediaIndex--; + pressed_PrevDisk = true; + } + } + else + pressed_PrevDisk = false; + + if (Spectrum._controller.IsPressed(EjectDisk)) + { + if (!pressed_EjectDisk) + { + Spectrum.OSD_FireInputMessage(EjectDisk); + if (UPDDiskDevice != null) + UPDDiskDevice.FDD_EjectDisk(); + } + } + else + pressed_EjectDisk = false; + + if (Spectrum._controller.IsPressed(DiskStatus)) + { + if (!pressed_DiskStatus) + { + //Spectrum.OSD_FireInputMessage(TapeStatus); + Spectrum.OSD_ShowDiskStatus(); + pressed_DiskStatus = true; + } + } + else + pressed_DiskStatus = false; + } + + /// + /// Instantiates the joysticks array + /// + /// + protected void InitJoysticks(List joys) + { + List jCollection = new List(); + + for (int i = 0; i < joys.Count(); i++) + { + jCollection.Add(InstantiateJoystick(joys[i], i + 1)); + } + + JoystickCollection = jCollection.ToArray(); + + for (int i = 0; i < JoystickCollection.Length; i++) + { + Spectrum.OSD_FireInputMessage("Joystick " + (i + 1) + ": " + JoystickCollection[i].JoyType.ToString()); + } + } + + /// + /// Instantiates a new IJoystick object + /// + /// + /// + /// + public IJoystick InstantiateJoystick(JoystickType type, int playerNumber) + { + switch (type) + { + case JoystickType.Kempston: + return new KempstonJoystick(this, playerNumber); + case JoystickType.Cursor: + return new CursorJoystick(this, playerNumber); + case JoystickType.SinclairLEFT: + return new SinclairJoystick1(this, playerNumber); + case JoystickType.SinclairRIGHT: + return new SinclairJoystick2(this, playerNumber); + case JoystickType.NULL: + return new NullJoystick(this, playerNumber); + } + + return null; + } + + /// + /// Returns a IJoystick object depending on the type (or null if not found) + /// + /// + /// + protected IJoystick LocateUniqueJoystick(JoystickType type) + { + return JoystickCollection.Where(a => a.JoyType == type).FirstOrDefault(); + } + + /// + /// Signs whether input read has been requested + /// This forms part of the IEmulator LagFrame implementation + /// + private bool inputRead; + public bool InputRead + { + get { return inputRead; } + set { inputRead = value; } + } + + } +} + diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs new file mode 100644 index 0000000000..50af7674b0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Media.cs @@ -0,0 +1,227 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Imported media * + /// + public abstract partial class SpectrumBase + { + /// + /// The tape or disk image(s) that are passed in from the main ZXSpectrum class + /// + protected List mediaImages { get; set; } + + /// + /// Tape images + /// + public List tapeImages { get; set; } + + /// + /// Disk images + /// + public List diskImages { get; set; } + + /// + /// The index of the currently 'loaded' tape image + /// + protected int tapeMediaIndex; + public int TapeMediaIndex + { + get { return tapeMediaIndex; } + set + { + int tmp = value; + int result = value; + + if (tapeImages == null || tapeImages.Count() == 0) + { + // no tape images found + return; + } + + if (value >= tapeImages.Count()) + { + // media at this index does not exist - loop back to 0 + result = 0; + } + else if (value < 0) + { + // negative index not allowed - move to last item in the collection + result = tapeImages.Count() - 1; + } + + // load the media into the tape device + tapeMediaIndex = result; + // fire osd message + Spectrum.OSD_TapeInserted(); + LoadTapeMedia(); + } + } + + /// + /// The index of the currently 'loaded' disk image + /// + protected int diskMediaIndex; + public int DiskMediaIndex + { + get { return diskMediaIndex; } + set + { + int tmp = value; + int result = value; + + if (diskImages == null || diskImages.Count() == 0) + { + // no tape images found + return; + } + + if (value >= diskImages.Count()) + { + // media at this index does not exist - loop back to 0 + result = 0; + } + else if (value < 0) + { + // negative index not allowed - move to last item in the collection + result = diskImages.Count() - 1; + } + + // load the media into the disk device + diskMediaIndex = result; + + // fire osd message + Spectrum.OSD_DiskInserted(); + + LoadDiskMedia(); + } + } + + /// + /// Called on first instantiation (and subsequent core reboots) + /// + /// + protected void InitializeMedia(List files) + { + mediaImages = files; + LoadAllMedia(); + } + + /// + /// Attempts to load all media into the relevant structures + /// + protected void LoadAllMedia() + { + tapeImages = new List(); + diskImages = new List(); + + int cnt = 0; + foreach (var m in mediaImages) + { + switch (IdentifyMedia(m)) + { + case SpectrumMediaType.Tape: + tapeImages.Add(m); + Spectrum._tapeInfo.Add(Spectrum._gameInfo[cnt]); + break; + case SpectrumMediaType.Disk: + diskImages.Add(m); + Spectrum._diskInfo.Add(Spectrum._gameInfo[cnt]); + break; + } + + cnt++; + } + + if (tapeImages.Count > 0) + LoadTapeMedia(); + + if (diskImages.Count > 0) + LoadDiskMedia(); + } + + /// + /// Attempts to load a tape into the tape device based on tapeMediaIndex + /// + protected void LoadTapeMedia() + { + TapeDevice.LoadTape(tapeImages[tapeMediaIndex]); + } + + /// + /// Attempts to load a disk into the disk device based on diskMediaIndex + /// + protected void LoadDiskMedia() + { + if (this.GetType() != typeof(ZX128Plus3)) + { + Spectrum.CoreComm.ShowMessage("You are trying to load one of more disk images.\n\n Please select ZX Spectrum +3 emulation immediately and reboot the core"); + return; + } + else + { + //Spectrum.CoreComm.ShowMessage("You are attempting to load a disk into the +3 disk drive.\n\nThis DOES NOT currently work properly but IS under active development."); + } + + UPDDiskDevice.FDD_LoadDisk(diskImages[diskMediaIndex]); + } + + /// + /// Identifies and sorts the various media types + /// + /// + private SpectrumMediaType IdentifyMedia(byte[] data) + { + // get first 16 bytes as a string + string hdr = Encoding.ASCII.GetString(data.Take(16).ToArray()); + + // disk checking first + if (hdr.ToUpper().Contains("EXTENDED CPC DSK") || hdr.ToUpper().Contains("MV - CPC")) + { + // spectrum .dsk disk file + return SpectrumMediaType.Disk; + } + if (hdr.ToUpper().StartsWith("FDI")) + { + // spectrum .fdi disk file + return SpectrumMediaType.Disk; + } + + // tape checking + if (hdr.ToUpper().StartsWith("ZXTAPE!")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } + if (hdr.ToUpper().StartsWith("PZXT")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } + if (hdr.ToUpper().StartsWith("COMPRESSED SQ")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } + if (hdr.ToUpper().Contains("WAVE")) + { + // spectrum .tzx tape file + return SpectrumMediaType.Tape; + } + + // if we get this far, assume a .tap file + return SpectrumMediaType.Tape; + } + } + + public enum SpectrumMediaType + { + None, + Tape, + Disk + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs new file mode 100644 index 0000000000..bdb8416c3a --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Memory.cs @@ -0,0 +1,259 @@ +using System; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Memory * + /// + public abstract partial class SpectrumBase + { + #region Memory Fields & Properties + + /// + /// ROM Banks + /// + public byte[] ROM0 = new byte[0x4000]; + public byte[] ROM1 = new byte[0x4000]; + public byte[] ROM2 = new byte[0x4000]; + public byte[] ROM3 = new byte[0x4000]; + + /// + /// RAM Banks + /// + public byte[] RAM0 = new byte[0x4000]; // Bank 0 + public byte[] RAM1 = new byte[0x4000]; // Bank 1 + public byte[] RAM2 = new byte[0x4000]; // Bank 2 + public byte[] RAM3 = new byte[0x4000]; // Bank 3 + public byte[] RAM4 = new byte[0x4000]; // Bank 4 + public byte[] RAM5 = new byte[0x4000]; // Bank 5 + public byte[] RAM6 = new byte[0x4000]; // Bank 6 + public byte[] RAM7 = new byte[0x4000]; // Bank 7 + + /// + /// Signs that the shadow screen is now displaying + /// Note: normal screen memory in RAM5 is not altered, the ULA just outputs Screen1 instead (RAM7) + /// + protected bool SHADOWPaged; + + /// + /// Index of the current RAM page + /// /// 128k, +2/2a and +3 only + /// + public int RAMPaged; + + /// + /// Signs that all paging is disabled + /// If this is TRUE, then 128k and above machines need a hard reset before paging is allowed again + /// + public bool PagingDisabled; + + /// + /// Index of the currently paged ROM + /// 128k, +2/2a and +3 only + /// + protected int ROMPaged; + public virtual int _ROMpaged + { + get { return ROMPaged; } + set { ROMPaged = value; } + } + + /* + * +3/+2A only + */ + + /// + /// High bit of the ROM selection (in normal paging mode) + /// + protected bool ROMhigh = false; + + /// + /// Low bit of the ROM selection (in normal paging mode) + /// + protected bool ROMlow = false; + + /// + /// Signs that the +2a/+3 special paging mode is activated + /// + protected bool SpecialPagingMode; + + /// + /// Index of the current special paging mode (0-3) + /// + protected int PagingConfiguration; + + /// + /// The last byte that was read after contended cycles + /// + public byte LastContendedReadByte; + + #endregion + + #region Memory Related Methods + + /// + /// Simulates reading from the bus + /// Paging should be handled here + /// + /// + /// + public abstract byte ReadBus(ushort addr); + + /// + /// Pushes a value onto the data bus that should be valid as long as the interrupt is true + /// + /// + /// + public virtual byte PushBus() + { + return 0xFF; + } + + /// + /// Simulates writing to the bus + /// Paging should be handled here + /// + /// + /// + public virtual void WriteBus(ushort addr, byte value) + { + throw new NotImplementedException("Must be overriden"); + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public abstract byte ReadMemory(ushort addr); + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public abstract void WriteMemory(ushort addr, byte value); + + /// + /// Sets up the ROM + /// + /// + public abstract void InitROM(RomData romData); + + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// + /// + /// + public virtual byte FetchScreenMemory(ushort addr) + { + var value = ReadBus((ushort)((addr & 0x3FFF) + 0x4000)); + //var value = ReadBus(addr); + return value; + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public abstract bool IsContended(ushort addr); + + + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public abstract bool ContendedBankPaged(); + + #endregion + + #region Helper Methods + + /// + /// Detects whether this is a 48k machine (or a 128k in 48k mode) + /// + /// + public virtual bool IsIn48kMode() + { + if (this.GetType() == typeof(ZX48) || + this.GetType() == typeof(ZX16) || + PagingDisabled) + { + return true; + } + else + return false; + } + + /// + /// Monitors ROM access + /// Used to auto start/stop the tape device when appropriate + /// + /// + public virtual void TestForTapeTraps(int addr) + { + if (TapeDevice.TapeIsPlaying) + { + // THE 'ERROR' RESTART + if (addr == 8) + { + //TapeDevice?.AutoStopTape(); + return; + } + + // THE 'ED-ERROR' SUBROUTINE + if (addr == 4223) + { + //TapeDevice?.AutoStopTape(); + return; + } + + // THE 'ERROR-2' ROUTINE + if (addr == 83) + { + //TapeDevice?.AutoStopTape(); + return; + } + + // THE 'MASKABLE INTERRUPT' ROUTINE + // This is sometimes used when the tape is to be stopped + // + if (addr == 56) + { + //TapeDevice.MaskableInterruptCount++; + + //if (TapeDevice.MaskableInterruptCount > 50) + //{ + //TapeDevice.MaskableInterruptCount = 0; + //TapeDevice.AutoStopTape(); + //} + + //TapeDevice?.AutoStopTape(); + return; + } + } + else + { + // THE 'LD-BYTES' SUBROUTINE + if (addr == 1366) + { + //TapeDevice?.AutoStartTape(); + return; + } + + // THE 'LD-EDGE-2' AND 'LD-EDGE-1' SUBROUTINES + if (addr == 1507) + { + //TapeDevice?.AutoStartTape(); + return; + } + } + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs new file mode 100644 index 0000000000..2d15f3f2fa --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.Port.cs @@ -0,0 +1,51 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Port Access * + /// + public abstract partial class SpectrumBase + { + /// + /// The last OUT data that was sent to the ULA + /// + protected byte LastULAOutByte; + public byte LASTULAOutByte + { + get { return LastULAOutByte; } + set { LastULAOutByte = value; } + } + + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public abstract byte ReadPort(ushort port); + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public abstract void WritePort(ushort port, byte value); + + /// + /// Increments the CPU totalCycles counter by the tStates value specified + /// + /// + //public virtual void PortContention(int tStates) + //{ + // CPU.TotalExecutedCycles += tStates; + //} + + /// + /// Simulates IO port contention based on the supplied address + /// This method is for 48k and 128k/+2 machines only and should be overridden for other models + /// + /// + //public abstract void ContendPort(ushort addr); + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs new file mode 100644 index 0000000000..75f146f2a5 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/SpectrumBase.cs @@ -0,0 +1,390 @@ +using BizHawk.Common; +using BizHawk.Emulation.Cores.Components.Z80A; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The abstract class that all emulated models will inherit from + /// * Main properties / fields / contruction* + /// + public abstract partial class SpectrumBase + { + #region Devices + + /// + /// The calling ZXSpectrum class (piped in via constructor) + /// + public ZXSpectrum Spectrum { get; set; } + + /// + /// Reference to the instantiated Z80 cpu (piped in via constructor) + /// + public Z80A CPU { get; set; } + + /// + /// ROM and extended info + /// + public RomData RomData { get; set; } + + /// + /// The emulated ULA device + /// + //public ULABase ULADevice { get; set; } + public ULA ULADevice { get; set; } + + /// + /// Monitors CPU cycles + /// + public CPUMonitor CPUMon { get; set; } + + /// + /// The spectrum buzzer/beeper + /// + public IBeeperDevice BuzzerDevice { get; set; } + + /// + /// A second beeper for the tape + /// + public IBeeperDevice TapeBuzzer { get; set; } + + /// + /// Device representing the AY-3-8912 chip found in the 128k and up spectrums + /// + public IPSG AYDevice { get; set; } + + /// + /// The spectrum keyboard + /// + public virtual IKeyboard KeyboardDevice { get; set; } + + /// + /// The spectrum datacorder device + /// + public virtual DatacorderDevice TapeDevice { get; set; } + + /// + /// The +3 built-in disk drive + /// + public virtual NECUPD765 UPDDiskDevice { get; set; } + + /// + /// Holds the currently selected joysticks + /// + public virtual IJoystick[] JoystickCollection { get; set; } + + /// + /// Signs whether the disk motor is on or off + /// + //protected bool DiskMotorState; + + /// + /// +3/2a printer port strobe + /// + protected bool PrinterPortStrobe; + + #endregion + + #region Emulator State + + /// + /// Signs whether the frame has ended + /// + public bool FrameCompleted; + + /// + /// Overflow from the previous frame (in Z80 cycles) + /// + public int OverFlow; + + /// + /// The total number of frames rendered + /// + public int FrameCount; + + /// + /// The current cycle (T-State) that we are at in the frame + /// + public long _frameCycles; + + /// + /// Stores where we are in the frame after each CPU cycle + /// + public long LastFrameStartCPUTick; + + /// + /// Gets the current frame cycle according to the CPU tick count + /// + public virtual long CurrentFrameCycle => CPU.TotalExecutedCycles - LastFrameStartCPUTick; + + /// + /// Non-Deterministic bools + /// + public bool _render; + public bool _renderSound; + + #endregion + + #region Constants + + /// + /// Mask constants & misc + /// + protected const int BORDER_BIT = 0x07; + protected const int EAR_BIT = 0x10; + protected const int MIC_BIT = 0x08; + protected const int TAPE_BIT = 0x40; + protected const int AY_SAMPLE_RATE = 16; + + #endregion + + #region Emulation Loop + + /// + /// Executes a single frame + /// + public virtual void ExecuteFrame(bool render, bool renderSound) + { + ULADevice.FrameEnd = false; + ULADevice.ULACycleCounter = CurrentFrameCycle; + + InputRead = false; + _render = render; + _renderSound = renderSound; + + FrameCompleted = false; + + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) + TapeDevice.StartFrame(); + + if (_renderSound) + { + if (AYDevice != null) + AYDevice.StartFrame(); + } + + PollInput(); + + for (;;) + { + // run the CPU Monitor cycle + CPUMon.ExecuteCycle(); + + // cycle the tape device + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) + TapeDevice.TapeCycle(); + + // has frame end been reached? + if (ULADevice.FrameEnd) + break; + } + + OverFlow = (int)CurrentFrameCycle - ULADevice.FrameLength; + + // we have reached the end of a frame + LastFrameStartCPUTick = CPU.TotalExecutedCycles - OverFlow; + + ULADevice.LastTState = 0; + + if (AYDevice != null) + AYDevice.EndFrame(); + + FrameCount++; + + if (UPDDiskDevice == null || !UPDDiskDevice.FDD_IsDiskLoaded) + TapeDevice.EndFrame(); + + FrameCompleted = true; + + // is this a lag frame? + Spectrum.IsLagFrame = !InputRead; + + // FDC debug + if (UPDDiskDevice != null && UPDDiskDevice.writeDebug) + { + // only write UPD log every second + if (FrameCount % 10 == 0) + { + System.IO.File.AppendAllLines(UPDDiskDevice.outputfile, UPDDiskDevice.dLog); + UPDDiskDevice.dLog = new System.Collections.Generic.List(); + //System.IO.File.WriteAllText(UPDDiskDevice.outputfile, UPDDiskDevice.outputString); + } + } + } + + #endregion + + #region Reset Functions + + /// + /// Hard reset of the emulated machine + /// + public virtual void HardReset() + { + //ULADevice.ResetInterrupt(); + ROMPaged = 0; + SpecialPagingMode = false; + RAMPaged = 0; + CPU.RegPC = 0; + + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("IY", 0xFFFF); + Spectrum.SetCpuRegister("IX", 0xFFFF); + Spectrum.SetCpuRegister("AF", 0xFFFF); + Spectrum.SetCpuRegister("BC", 0xFFFF); + Spectrum.SetCpuRegister("DE", 0xFFFF); + Spectrum.SetCpuRegister("HL", 0xFFFF); + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("Shadow AF", 0xFFFF); + Spectrum.SetCpuRegister("Shadow BC", 0xFFFF); + Spectrum.SetCpuRegister("Shadow DE", 0xFFFF); + Spectrum.SetCpuRegister("Shadow HL", 0xFFFF); + + CPU.Regs[CPU.I] = 0; + CPU.Regs[CPU.R] = 0; + + TapeDevice.Reset(); + if (AYDevice != null) + AYDevice.Reset(); + + byte[][] rams = new byte[][] + { + RAM0, + RAM1, + RAM2, + RAM3, + RAM4, + RAM5, + RAM6, + RAM7 + }; + + foreach (var r in rams) + { + for (int i = 0; i < r.Length; i++) + { + r[i] = 0x00; + } + } + } + + /// + /// Soft reset of the emulated machine + /// + public virtual void SoftReset() + { + //ULADevice.ResetInterrupt(); + ROMPaged = 0; + SpecialPagingMode = false; + RAMPaged = 0; + CPU.RegPC = 0; + + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("IY", 0xFFFF); + Spectrum.SetCpuRegister("IX", 0xFFFF); + Spectrum.SetCpuRegister("AF", 0xFFFF); + Spectrum.SetCpuRegister("BC", 0xFFFF); + Spectrum.SetCpuRegister("DE", 0xFFFF); + Spectrum.SetCpuRegister("HL", 0xFFFF); + Spectrum.SetCpuRegister("SP", 0xFFFF); + Spectrum.SetCpuRegister("Shadow AF", 0xFFFF); + Spectrum.SetCpuRegister("Shadow BC", 0xFFFF); + Spectrum.SetCpuRegister("Shadow DE", 0xFFFF); + Spectrum.SetCpuRegister("Shadow HL", 0xFFFF); + + CPU.Regs[CPU.I] = 0; + CPU.Regs[CPU.R] = 0; + + TapeDevice.Reset(); + if (AYDevice != null) + AYDevice.Reset(); + + byte[][] rams = new byte[][] + { + RAM0, + RAM1, + RAM2, + RAM3, + RAM4, + RAM5, + RAM6, + RAM7 + }; + + foreach (var r in rams) + { + for (int i = 0; i < r.Length; i++) + { + r[i] = 0x00; + } + } + } + + #endregion + + #region IStatable + + public void SyncState(Serializer ser) + { + ser.BeginSection("ZXMachine"); + ser.Sync("FrameCompleted", ref FrameCompleted); + ser.Sync("OverFlow", ref OverFlow); + ser.Sync("FrameCount", ref FrameCount); + ser.Sync("_frameCycles", ref _frameCycles); + ser.Sync("inputRead", ref inputRead); + ser.Sync("LastFrameStartCPUTick", ref LastFrameStartCPUTick); + ser.Sync("LastULAOutByte", ref LastULAOutByte); + ser.Sync("ROM0", ref ROM0, false); + ser.Sync("ROM1", ref ROM1, false); + ser.Sync("ROM2", ref ROM2, false); + ser.Sync("ROM3", ref ROM3, false); + ser.Sync("RAM0", ref RAM0, false); + ser.Sync("RAM1", ref RAM1, false); + ser.Sync("RAM2", ref RAM2, false); + ser.Sync("RAM3", ref RAM3, false); + ser.Sync("RAM4", ref RAM4, false); + ser.Sync("RAM5", ref RAM5, false); + ser.Sync("RAM6", ref RAM6, false); + ser.Sync("RAM7", ref RAM7, false); + ser.Sync("ROMPaged", ref ROMPaged); + ser.Sync("SHADOWPaged", ref SHADOWPaged); + ser.Sync("RAMPaged", ref RAMPaged); + ser.Sync("PagingDisabled", ref PagingDisabled); + ser.Sync("SpecialPagingMode", ref SpecialPagingMode); + ser.Sync("PagingConfiguration", ref PagingConfiguration); + ser.Sync("ROMhigh", ref ROMhigh); + ser.Sync("ROMlow", ref ROMlow); + ser.Sync("LastContendedReadByte", ref LastContendedReadByte); + + KeyboardDevice.SyncState(ser); + BuzzerDevice.SyncState(ser); + TapeBuzzer.SyncState(ser); + ULADevice.SyncState(ser); + CPUMon.SyncState(ser); + + if (AYDevice != null) + { + AYDevice.SyncState(ser); + ((AY38912)AYDevice as AY38912).PanningConfiguration = Spectrum.Settings.AYPanConfig; + } + + ser.Sync("tapeMediaIndex", ref tapeMediaIndex); + if (ser.IsReader) + TapeMediaIndex = tapeMediaIndex; + + TapeDevice.SyncState(ser); + + ser.Sync("diskMediaIndex", ref diskMediaIndex); + if (ser.IsReader) + DiskMediaIndex = diskMediaIndex; + + if (UPDDiskDevice != null) + { + UPDDiskDevice.SyncState(ser); + } + + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs new file mode 100644 index 0000000000..0f46892833 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ULA.cs @@ -0,0 +1,1060 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Uncommitted logic array implementation (ULA) + /// + public abstract class ULA : IVideoProvider + { + #region Other Devices + + /// + /// The emulated spectrum + /// + protected SpectrumBase _machine; + + /// + /// The CPU monitor class + /// + protected CPUMonitor CPUMon; + + #endregion + + #region Construction & Initialisation + + public ULA (SpectrumBase machine) + { + _machine = machine; + CPUMon = _machine.CPUMon; + borderType = _machine.Spectrum.SyncSettings.BorderType; + } + + #endregion + + #region Palettes + + /// + /// The standard ULA palette + /// + private static readonly int[] ULAPalette = + { + Colors.ARGB(0x00, 0x00, 0x00), // Black + Colors.ARGB(0x00, 0x00, 0xD7), // Blue + Colors.ARGB(0xD7, 0x00, 0x00), // Red + Colors.ARGB(0xD7, 0x00, 0xD7), // Magenta + Colors.ARGB(0x00, 0xD7, 0x00), // Green + Colors.ARGB(0x00, 0xD7, 0xD7), // Cyan + Colors.ARGB(0xD7, 0xD7, 0x00), // Yellow + Colors.ARGB(0xD7, 0xD7, 0xD7), // White + Colors.ARGB(0x00, 0x00, 0x00), // Bright Black + Colors.ARGB(0x00, 0x00, 0xFF), // Bright Blue + Colors.ARGB(0xFF, 0x00, 0x00), // Bright Red + Colors.ARGB(0xFF, 0x00, 0xFF), // Bright Magenta + Colors.ARGB(0x00, 0xFF, 0x00), // Bright Green + Colors.ARGB(0x00, 0xFF, 0xFF), // Bright Cyan + Colors.ARGB(0xFF, 0xFF, 0x00), // Bright Yellow + Colors.ARGB(0xFF, 0xFF, 0xFF), // Bright White + }; + + #endregion + + #region Timing + + /// + /// The CPU speed + /// + public int ClockSpeed; + + /// + /// Length of frame in T-State cycles + /// + public int FrameCycleLength; + + /// + /// The T-State at which the interrupt should be raised within the frame + /// + public int InterruptStartTime; + + /// + /// The period for which the interrupt should he held + /// (simulated /INT pin held low) + /// + public int InterruptLength; + + /// + /// Contention offset + /// + public int ContentionOffset; + + /// + /// Arbitrary offset for render table generation + /// + public int RenderTableOffset; + + /// + /// The offset when return floating bus bytes + /// + public int FloatingBusOffset; + + /// + /// The time in T-States for one scanline to complete + /// + public int ScanlineTime; + + /// + /// T-States at the left border + /// + public int BorderLeftTime; + + /// + /// T-States at the right border + /// + public int BorderRightTime; + + public int FirstPaperLine; + public int FirstPaperTState; + public bool Border4T; + public int Border4TStage; + + #endregion + + #region Interrupt Generation + + /// + /// Signs that an interrupt has been raised in this frame. + /// + protected bool InterruptRaised; + + public long ULACycleCounter; + public long LastULATick; + public bool FrameEnd; + + /// + /// Cycles the ULA clock + /// Handles interrupt generation + /// + /// + public virtual void CycleClock(long totalCycles) + { + // render the screen + if (_machine._render) + RenderScreen((int)_machine.CurrentFrameCycle); + + // has more than one cycle past since this last ran + // (this can be true if contention has taken place) + var ticksToProcess = totalCycles - LastULATick; + + // store the current cycle + LastULATick = totalCycles; + + // process the cycles past as well as the upcoming one + for (int i = 0; i < ticksToProcess; i++) + { + ULACycleCounter++; + + if (InterruptRaised) + { + // /INT pin is currently being held low + if (ULACycleCounter < InterruptLength + InterruptStartTime) + { + // ULA should still hold the /INT pin low + _machine.CPU.FlagI = true; + } + else + { + // its time (or past time) to stop holding the /INT pin low + _machine.CPU.FlagI = false; + InterruptRaised = false; + } + } + else + { + // interrupt is currently not raised + if (ULACycleCounter == FrameLength + InterruptStartTime) + { + // time to raise the interrupt + InterruptRaised = true; + _machine.CPU.FlagI = true; + FrameEnd = true; + ULACycleCounter = InterruptStartTime; + CalcFlashCounter(); + } + } + } + } + + /// + /// Flash processing + /// + public void CalcFlashCounter() + { + flashCounter++; + + if (flashCounter > 15) + { + flashOn = !flashOn; + flashCounter = 0; + } + } + + #endregion + + #region Screen Layout + + /// + /// Total pixels in one display row + /// + protected int ScreenWidth; + /// + /// Total pixels in one display column + /// + protected int ScreenHeight; + /// + /// Total pixels in top border + /// + protected int BorderTopHeight; + /// + /// Total pixels in bottom border + /// + protected int BorderBottomHeight; + /// + /// Total pixels in left border width + /// + protected int BorderLeftWidth; + /// + /// Total pixels in right border width + /// + protected int BorderRightWidth; + /// + /// Total pixels in one scanline + /// + protected int ScanLineWidth; + + #endregion + + #region State + + /// + /// The last T-State cycle at which the screen was rendered + /// + public int LastTState; + + /// + /// Flash state + /// + public bool flashOn; + + private int flashCounter; + + protected byte fetchB1; + protected byte fetchA1; + protected byte fetchB2; + protected byte fetchA2; + protected int ink; + protected int paper; + protected int fetchBorder; + protected int bright; + protected int flash; + + public int palPaper; + public int palInk; + + public int BorderColor = 7; + + #endregion + + #region Conversions + + public int FrameLength => FrameCycleLength; + + #endregion + + #region Rendering Configuration + + /// + /// Holds all information regarding rendering the screen based on the current T-State + /// + public RenderTable RenderingTable; + + /// + /// Holds all information regarding rendering the screen based on the current T-State + /// + public class RenderTable + { + /// + /// The ULA device + /// + private ULA _ula; + + /// + /// Array of rendercycle entries + /// Starting from the interrupt + /// + public RenderCycle[] Renderer; + + /// + /// The emulated machine + /// + public MachineType _machineType; + + public int Offset; + + /// + /// Constructor + /// + /// + public RenderTable(ULA ula, MachineType machineType) + { + _ula = ula; + _machineType = machineType; + Renderer = new RenderCycle[_ula.FrameCycleLength]; + InitRenderer(machineType); + } + + /// + /// Initializes the renderer + /// + /// + private void InitRenderer(MachineType machineType) + { + for (var t = 0; t < _ula.FrameCycleLength; t++) + { + var tStateScreen = t + _ula.RenderTableOffset;// + _ula.InterruptStartTime; + + if (tStateScreen < 0) + tStateScreen += _ula.FrameCycleLength; + else if (tStateScreen >= _ula.FrameCycleLength) + tStateScreen -= _ula.FrameCycleLength; + + CalculateRenderItem(t, tStateScreen / _ula.ScanlineTime, tStateScreen % _ula.ScanlineTime); + } + + CreateContention(machineType); + } + + private void CalculateRenderItem(int item, int line, int pix) + { + Renderer[item] = new RenderCycle(); + + Renderer[item].RAction = RenderAction.None; + int pitchWidth = _ula.ScreenWidth + _ula.BorderRightWidth + _ula.BorderLeftWidth; + + int scrPix = pix - _ula.FirstPaperTState; + int scrLin = line - _ula.FirstPaperLine; + + if ((line >= (_ula.FirstPaperLine - _ula.BorderTopHeight)) && (line < (_ula.FirstPaperLine + 192 + _ula.BorderBottomHeight)) && + (pix >= (_ula.FirstPaperTState - _ula.BorderLeftTime)) && (pix < (_ula.FirstPaperTState + 128 + _ula.BorderRightTime))) + { + // visibleArea (vertical) + if ((line >= _ula.FirstPaperLine) && (line < (_ula.FirstPaperLine + 192)) && + (pix >= _ula.FirstPaperTState) && (pix < (_ula.FirstPaperTState + 128))) + { + // pixel area + switch (scrPix & 7) + { + case 0: + Renderer[item].RAction = RenderAction.Shift1AndFetchByte2; // shift 1 + fetch B2 + // +4 = prefetch! + Renderer[item].ByteAddress = CalculateByteAddress(scrPix + 4, scrLin); + break; + case 1: + Renderer[item].RAction = RenderAction.Shift1AndFetchAttribute2; // shift 1 + fetch A2 + // +3 = prefetch! + Renderer[item].AttributeAddress = CalculateAttributeAddress(scrPix + 3, scrLin); + break; + case 2: + Renderer[item].RAction = RenderAction.Shift1; // shift 1 + break; + case 3: + Renderer[item].RAction = RenderAction.Shift1Last; // shift 1 (last) + break; + case 4: + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + break; + case 5: + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + break; + case 6: + if (pix < (_ula.FirstPaperTState + 128 - 2)) + { + Renderer[item].RAction = RenderAction.Shift2AndFetchByte1; // shift 2 + fetch B2 + } + else + { + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + } + + // +2 = prefetch! + Renderer[item].ByteAddress = CalculateByteAddress(scrPix + 2, scrLin); + break; + case 7: + if (pix < (_ula.FirstPaperTState + 128 - 2)) + { + //??? + Renderer[item].RAction = RenderAction.Shift2AndFetchAttribute1; // shift 2 + fetch A2 + } + else + { + Renderer[item].RAction = RenderAction.Shift2; // shift 2 + } + + // +1 = prefetch! + Renderer[item].AttributeAddress = CalculateAttributeAddress(scrPix + 1, scrLin); + break; + } + } + else if ((line >= _ula.FirstPaperLine) && (line < (_ula.FirstPaperLine + 192)) && + (pix == (_ula.FirstPaperTState - 2))) // border & fetch B1 + { + Renderer[item].RAction = RenderAction.BorderAndFetchByte1; // border & fetch B1 + // +2 = prefetch! + Renderer[item].ByteAddress = CalculateByteAddress(scrPix + 2, scrLin); + } + else if ((line >= _ula.FirstPaperLine) && (line < (_ula.FirstPaperLine + 192)) && + (pix == (_ula.FirstPaperTState - 1))) // border & fetch A1 + { + Renderer[item].RAction = RenderAction.BorderAndFetchAttribute1; // border & fetch A1 + // +1 = prefetch! + Renderer[item].AttributeAddress = CalculateAttributeAddress(scrPix + 1, scrLin); + } + else + { + Renderer[item].RAction = RenderAction.Border; // border + } + + int wy = line - (_ula.FirstPaperLine - _ula.BorderTopHeight); + int wx = (pix - (_ula.FirstPaperTState - _ula.BorderLeftTime)) * 2; + Renderer[item].LineOffset = wy * pitchWidth + wx; + } + } + + private void CreateContention(MachineType machineType) + { + int[] conPattern = new int[8]; + + switch (machineType) + { + case MachineType.ZXSpectrum16: + case MachineType.ZXSpectrum48: + case MachineType.ZXSpectrum128: + case MachineType.ZXSpectrum128Plus2: + conPattern = new int[] { 6, 5, 4, 3, 2, 1, 0, 0 }; + break; + + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + conPattern = new int[] { 1, 0, 7, 6, 5, 4, 3, 2 }; + break; + } + + // calculate contention values + for (int t = 0; t < _ula.FrameCycleLength; t++) + { + int shifted = t + _ula.RenderTableOffset + _ula.ContentionOffset; // _ula.InterruptStartTime; + if (shifted < 0) + shifted += _ula.FrameCycleLength; + shifted %= _ula.FrameCycleLength; + + Renderer[t].ContentionValue = 0; + + int line = shifted / _ula.ScanlineTime; + int pix = shifted % _ula.ScanlineTime; + if (line < _ula.FirstPaperLine || line >= (_ula.FirstPaperLine + 192)) + { + Renderer[t].ContentionValue = 0; + continue; + } + int scrPix = pix - _ula.FirstPaperTState; + if (scrPix < 0 || scrPix >= 128) + { + Renderer[t].ContentionValue = 0; + continue; + } + int pixByte = scrPix % 8; + + Renderer[t].ContentionValue = conPattern[pixByte]; + } + } + + private ushort CalculateByteAddress(int x, int y) + { + x >>= 2; + var vp = x | (y << 5); + return (ushort)((vp & 0x181F) | ((vp & 0x0700) >> 3) | ((vp & 0x00E0) << 3)); + } + + private ushort CalculateAttributeAddress(int x, int y) + { + x >>= 2; + var ap = x | ((y >> 3) << 5); + return (ushort)(6144 + ap); + } + + /// + /// Render/contention information for a single T-State + /// + public class RenderCycle + { + /// + /// The ULA render action at this T-State + /// + public RenderAction RAction; + /// + /// The contention value at this T-State + /// + public int ContentionValue; + /// + /// The screen byte address at this T-State + /// + public ushort ByteAddress; + /// + /// The screen attribute address at this T-State + /// + public ushort AttributeAddress; + /// + /// The byte address returned by the floating bus at this T-State + /// + public ushort FloatingBusAddress; + /// + /// The offset + /// + public int LineOffset; + } + + public enum RenderAction + { + None, + Border, + BorderAndFetchByte1, + BorderAndFetchAttribute1, + Shift1AndFetchByte2, + Shift1AndFetchAttribute2, + Shift1, + Shift1Last, + Shift2, + Shift2Last, + Shift2AndFetchByte1, + Shift2AndFetchAttribute1 + } + } + + #endregion + + #region Render Methods + + /// + /// Renders to the screen buffer based on the current cycle + /// + /// + public void RenderScreen(int toCycle) + { + // check boundaries + if (toCycle > FrameCycleLength) + toCycle = FrameCycleLength; + + // render the required number of cycles + for (int t = LastTState; t < toCycle; t++) + { + if (!Border4T || (t & 3) == Border4TStage) + { + fetchBorder = BorderColor; + } + else + { + + } + + //fetchBorder = BorderColor; + + // get the table entry + var item = RenderingTable.Renderer[t]; + + switch (item.RAction) + { + case RenderTable.RenderAction.None: + break; + + case RenderTable.RenderAction.Border: + ScreenBuffer[item.LineOffset] = ULAPalette[fetchBorder]; + ScreenBuffer[item.LineOffset + 1] = ULAPalette[fetchBorder]; + break; + + case RenderTable.RenderAction.BorderAndFetchByte1: + ScreenBuffer[item.LineOffset] = ULAPalette[fetchBorder]; + ScreenBuffer[item.LineOffset + 1] = ULAPalette[fetchBorder]; + fetchB1 = _machine.FetchScreenMemory(item.ByteAddress); + break; + + case RenderTable.RenderAction.BorderAndFetchAttribute1: + ScreenBuffer[item.LineOffset] = ULAPalette[fetchBorder]; + ScreenBuffer[item.LineOffset + 1] = ULAPalette[fetchBorder]; + fetchA1 = _machine.FetchScreenMemory(item.AttributeAddress); + ProcessInkPaper(fetchA1); + break; + + case RenderTable.RenderAction.Shift1AndFetchByte2: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + fetchB2 = _machine.FetchScreenMemory(item.ByteAddress); + break; + + case RenderTable.RenderAction.Shift1AndFetchAttribute2: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + fetchA2 = _machine.FetchScreenMemory(item.AttributeAddress); + break; + + case RenderTable.RenderAction.Shift1: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + break; + + case RenderTable.RenderAction.Shift1Last: + ScreenBuffer[item.LineOffset] = ((fetchB1 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB1 & 0x40) != 0) ? palInk : palPaper; + fetchB1 <<= 2; + ProcessInkPaper(fetchA2); + break; + + case RenderTable.RenderAction.Shift2: + ScreenBuffer[item.LineOffset] = ((fetchB2 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB2 & 0x40) != 0) ? palInk : palPaper; + fetchB2 <<= 2; + break; + + case RenderTable.RenderAction.Shift2AndFetchByte1: + ScreenBuffer[item.LineOffset] = ((fetchB2 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB2 & 0x40) != 0) ? palInk : palPaper; + fetchB2 <<= 2; + fetchB1 = _machine.FetchScreenMemory(item.ByteAddress); + break; + + case RenderTable.RenderAction.Shift2AndFetchAttribute1: + ScreenBuffer[item.LineOffset] = ((fetchB2 & 0x80) != 0) ? palInk : palPaper; + ScreenBuffer[item.LineOffset + 1] = ((fetchB2 & 0x40) != 0) ? palInk : palPaper; + fetchB2 <<= 2; + fetchA1 = _machine.FetchScreenMemory(item.AttributeAddress); + ProcessInkPaper(fetchA1); + break; + } + } + + LastTState = toCycle; + } + + private void ProcessInkPaper(byte attrData) + { + bright = (attrData & 0x40) >> 3; + flash = (attrData & 0x80) >> 7; + ink = (attrData & 0x07); + paper = ((attrData >> 3) & 0x7); + + palInk = ULAPalette[ink + bright]; + palPaper = ULAPalette[paper + bright]; + + // swap paper and ink when flash is on + if (flashOn && (flash != 0)) + { + int temp = palInk; + palInk = palPaper; + palPaper = temp; + } + } + + /// + /// Generates the port lookup table for +2a/+3 allowed floating bus ports + /// + public void GenerateP3PortTable() + { + List table = new List(); + for (int i = 0; i < 0x1000; i++) + { + ushort r = (ushort)(1 + (4 * i)); + if (r > 4093) + break; + table.Add(r); + } + + Plus3FBPortTable = table.ToArray(); + } + + private ushort[] Plus3FBPortTable = new ushort[1]; + + /// + /// Returns floating bus value (if available) + /// + /// + /// + public void ReadFloatingBus(int tstate, ref int result, ushort port) + { + tstate += FloatingBusOffset; + if (tstate >= RenderingTable.Renderer.Length) + tstate -= RenderingTable.Renderer.Length; + if (tstate < 0) + tstate += RenderingTable.Renderer.Length; + + var item = RenderingTable.Renderer[tstate]; + + switch (RenderingTable._machineType) + { + case MachineType.ZXSpectrum16: + case MachineType.ZXSpectrum48: + case MachineType.ZXSpectrum128: + case MachineType.ZXSpectrum128Plus2: + + switch (item.RAction) + { + case RenderTable.RenderAction.BorderAndFetchByte1: + case RenderTable.RenderAction.Shift1AndFetchByte2: + case RenderTable.RenderAction.Shift2AndFetchByte1: + result = _machine.FetchScreenMemory(item.ByteAddress); + break; + case RenderTable.RenderAction.BorderAndFetchAttribute1: + case RenderTable.RenderAction.Shift1AndFetchAttribute2: + case RenderTable.RenderAction.Shift2AndFetchAttribute1: + result = _machine.FetchScreenMemory(item.AttributeAddress); + break; + default: + break; + } + break; + + case MachineType.ZXSpectrum128Plus2a: + case MachineType.ZXSpectrum128Plus3: + + // http://sky.relative-path.com/zx/floating_bus.html + if (_machine.PagingDisabled) + { + result = 0xff; + break; + } + + // check whether fb is found on this port + ushort pLook = Array.Find(Plus3FBPortTable, s => s == port); + if (pLook == 0) + { + result = 0xff; + break; + } + + // floating bus on +2a/+3 always returns a byte with Bit0 set + switch (item.RAction) + { + case RenderTable.RenderAction.BorderAndFetchByte1: + case RenderTable.RenderAction.Shift1AndFetchByte2: + case RenderTable.RenderAction.Shift2AndFetchByte1: + result = (byte)(_machine.FetchScreenMemory(item.ByteAddress) | 0x01); + break; + case RenderTable.RenderAction.BorderAndFetchAttribute1: + case RenderTable.RenderAction.Shift1AndFetchAttribute2: + case RenderTable.RenderAction.Shift2AndFetchAttribute1: + result = (byte)(_machine.FetchScreenMemory(item.AttributeAddress) | 0x01); + break; + default: + result = (byte)(_machine.LastContendedReadByte | 0x01); + break; + } + + break; + } + } + + #endregion + + #region Contention + + /// + /// Returns the contention value for the current t-state + /// + /// + public int GetContentionValue() + { + return GetContentionValue((int)_machine.CurrentFrameCycle); + } + + /// + /// Returns the contention value for the supplied t-state + /// + /// + public int GetContentionValue(int tstate) + { + //tstate += MemoryContentionOffset; + if (tstate >= FrameCycleLength) + tstate -= FrameCycleLength; + + if (tstate < 0) + tstate += FrameCycleLength; + + return RenderingTable.Renderer[tstate].ContentionValue; + } + + /// + /// Returns the contention value for the supplied t-state + /// + /// + public int GetPortContentionValue(int tstate) + { + //tstate += PortContentionOffset; + if (tstate >= FrameCycleLength) + tstate -= FrameCycleLength; + + if (tstate < 0) + tstate += FrameCycleLength; + + return RenderingTable.Renderer[tstate].ContentionValue; + } + + #endregion + + #region IVideoProvider + + /// + /// Video output buffer + /// + public int[] ScreenBuffer; + + private int _virtualWidth; + private int _virtualHeight; + private int _bufferWidth; + private int _bufferHeight; + + public int BackgroundColor + { + get { return ULAPalette[7]; } //ULAPalette[borderColour]; } + } + + public int VirtualWidth + { + get { return _virtualWidth; } + set { _virtualWidth = value; } + } + + public int VirtualHeight + { + get { return _virtualHeight; } + set { _virtualHeight = value; } + } + + public int BufferWidth + { + get { return _bufferWidth; } + set { _bufferWidth = value; } + } + + public int BufferHeight + { + get { return _bufferHeight; } + set { _bufferHeight = value; } + } + + public int VsyncNumerator + { + get { return ClockSpeed * 50; }// ClockSpeed; } + set { } + } + + public int VsyncDenominator + { + get { return ClockSpeed; }//FrameLength; } + } + + public int[] GetVideoBuffer() + { + switch (borderType) + { + // Full side borders, no top or bottom border (giving *almost* 16:9 output) + case ZXSpectrum.BorderType.Widescreen: + // we are cropping out the top and bottom borders + var startPixelsToCrop = ScanLineWidth * BorderTopHeight; + var endPixelsToCrop = ScanLineWidth * BorderBottomHeight; + int index = 0; + for (int i = startPixelsToCrop; i < ScreenBuffer.Length - endPixelsToCrop; i++) + { + croppedBuffer[index++] = ScreenBuffer[i]; + } + return croppedBuffer; + + // The full spectrum border + case ZXSpectrum.BorderType.Full: + return ScreenBuffer; + + case ZXSpectrum.BorderType.Medium: + // all border sizes now 24 + var lR = BorderLeftWidth - 24; + var rR = BorderRightWidth - 24; + var tR = BorderTopHeight - 24; + var bR = BorderBottomHeight - 24; + var startP = ScanLineWidth * tR; + var endP = ScanLineWidth * bR; + + int index2 = 0; + // line by line + for (int i = startP; i < ScreenBuffer.Length - endP; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR; p++) + { + if (index2 == croppedBuffer.Length) + break; + croppedBuffer[index2++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + + case ZXSpectrum.BorderType.Small: + // all border sizes now 24 + var lR_ = BorderLeftWidth - 10; + var rR_ = BorderRightWidth - 10; + var tR_ = BorderTopHeight - 10; + var bR_ = BorderBottomHeight - 10; + var startP_ = ScanLineWidth * tR_; + var endP_ = ScanLineWidth * bR_; + + int index2_ = 0; + // line by line + for (int i = startP_; i < ScreenBuffer.Length - endP_; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR_; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR_; p++) + { + if (index2_ == croppedBuffer.Length) + break; + croppedBuffer[index2_++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + + case ZXSpectrum.BorderType.None: + // all border sizes now 24 + var lR__ = BorderLeftWidth; + var rR__ = BorderRightWidth; + var tR__ = BorderTopHeight; + var bR__ = BorderBottomHeight; + var startP__ = ScanLineWidth * tR__; + var endP__ = ScanLineWidth * bR__; + + int index2__ = 0; + // line by line + for (int i = startP__; i < ScreenBuffer.Length - endP__; i += ScreenWidth + BorderLeftWidth + BorderRightWidth) + { + // each pixel in each line + for (int p = lR__; p < ScreenWidth + BorderLeftWidth + BorderRightWidth - rR__; p++) + { + if (index2__ == croppedBuffer.Length) + break; + croppedBuffer[index2__++] = ScreenBuffer[i + p]; + } + } + + return croppedBuffer; + } + + return ScreenBuffer; + } + + protected void SetupScreenSize() + { + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + ScreenBuffer = new int[BufferWidth * BufferHeight]; + + switch (borderType) + { + case ZXSpectrum.BorderType.Full: + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight + BorderTopHeight + BorderBottomHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + ScreenBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Widescreen: + BufferWidth = ScreenWidth + BorderLeftWidth + BorderRightWidth; + BufferHeight = ScreenHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Medium: + BufferWidth = ScreenWidth + (24) + (24); + BufferHeight = ScreenHeight + (24) + (24); + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.Small: + BufferWidth = ScreenWidth + (10) + (10); + BufferHeight = ScreenHeight + (10) + (10); + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + + case ZXSpectrum.BorderType.None: + BufferWidth = ScreenWidth; + BufferHeight = ScreenHeight; + VirtualHeight = BufferHeight; + VirtualWidth = BufferWidth; + croppedBuffer = new int[BufferWidth * BufferHeight]; + break; + } + } + + protected int[] croppedBuffer; + + private ZXSpectrum.BorderType _borderType; + + public ZXSpectrum.BorderType borderType + { + get { return _borderType; } + set { _borderType = value; } + } + + #endregion + + #region Serialization + + public void SyncState(Serializer ser) + { + ser.BeginSection("ULA"); + if (ScreenBuffer != null) + ser.Sync("ScreenBuffer", ref ScreenBuffer, false); + ser.Sync("BorderColor", ref BorderColor); + ser.Sync("LastTState", ref LastTState); + ser.Sync("flashOn", ref flashOn); + ser.Sync("fetchB1", ref fetchB1); + ser.Sync("fetchA1", ref fetchA1); + ser.Sync("fetchB2", ref fetchB2); + ser.Sync("fetchA2", ref fetchA2); + ser.Sync("ink", ref ink); + ser.Sync("paper", ref paper); + ser.Sync("fetchBorder", ref fetchBorder); + ser.Sync("bright", ref bright); + ser.Sync("flash", ref flash); + ser.Sync("palPaper", ref palPaper); + ser.Sync("palInk", ref palInk); + + ser.Sync("LastULATick", ref LastULATick); + ser.Sync("ULACycleCounter", ref ULACycleCounter); + ser.Sync("FrameEnd", ref FrameEnd); + + ser.Sync("InterruptRaised", ref InterruptRaised); + ser.EndSection(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs new file mode 100644 index 0000000000..374ccb13eb --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Memory.cs @@ -0,0 +1,296 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 128K and +2 Memory + /// + public partial class ZX128 : SpectrumBase + { + /* 128k paging controlled by writes to port 0x7ffd + * + * + + #7FFD (32765) - decoded as A15=0, A1=0 and /IORQ=0. Bits 0..5 are latched. Bits 0..2 select RAM bank in secton D. Bit 3 selects RAM bank to dispay screen (0 - RAM5, 1 - RAM7). Bit 4 selects ROM bank (0 - ROM0, 1 - ROM1). Bit 5, when set locks future writing to #7FFD port until reset. Reading #7FFD port is the same as writing #FF into it. + #BFFD (49149) - write data byte into AY-3-8912 chip. + #FFFD (65533) - select AY-3-8912 addres (D4..D7 ignored) and reading data byte. + + * 0xffff +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 0 | Bank 1 | Bank 2 | Bank 3 | Bank 4 | Bank 5 | Bank 6 | Bank 7 | + | | |(also at| | |(also at| | | + | | | 0x8000)| | | 0x4000)| | | + | | | | | | screen | | screen | + 0xc000 +--------+--------+--------+--------+--------+--------+--------+--------+ + | Bank 2 | Any one of these pages may be switched in. + | | + | | + | | + 0x8000 +--------+ + | Bank 5 | + | | + | | + | screen | + 0x4000 +--------+--------+ + | ROM 0 | ROM 1 | Either ROM may be switched in. + | | | + | | | + | | | + 0x0000 +--------+--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + byte result = 0xff; + + switch (divisor) + { + // ROM 0x000 + case 0: + TestForTapeTraps(addr % 0x4000); + + if (ROMPaged == 0) + result = ROM0[addr % 0x4000]; + else + result = ROM1[addr % 0x4000]; + break; + + // RAM 0x4000 (RAM5 - Bank5) + case 1: + result = RAM5[addr % 0x4000]; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = RAM2[addr % 0x4000]; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = RAM0[addr % 0x4000]; + break; + case 1: + result = RAM1[addr % 0x4000]; + break; + case 2: + result = RAM2[addr % 0x4000]; + break; + case 3: + result = RAM3[addr % 0x4000]; + break; + case 4: + result = RAM4[addr % 0x4000]; + break; + case 5: + result = RAM5[addr % 0x4000]; + break; + case 6: + result = RAM6[addr % 0x4000]; + break; + case 7: + result = RAM7[addr % 0x4000]; + break; + } + break; + default: + break; + } + + return result; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + + switch (divisor) + { + // ROM 0x000 + case 0: + // cannot write to ROMs + /* + if (ROMPaged == 0) + ROM0[addr % 0x4000] = value; + else + ROM1[addr % 0x4000] = value; + */ + break; + + // RAM 0x4000 (RAM5 - Bank5 or shadow bank RAM7) + case 1: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + RAM2[addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + RAM0[addr % 0x4000] = value; + break; + case 1: + RAM1[addr % 0x4000] = value; + break; + case 2: + RAM2[addr % 0x4000] = value; + break; + case 3: + RAM3[addr % 0x4000] = value; + break; + case 4: + RAM4[addr % 0x4000] = value; + break; + case 5: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + case 6: + RAM6[addr % 0x4000] = value; + break; + case 7: + RAM7[addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + WriteBus(addr, value); + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + var a = addr & 0xc000; + + if (a == 0x4000) + { + // low port contention + return true; + } + + if (a == 0xc000) + { + // high port contention - check for contended bank paged in + switch (RAMPaged) + { + case 1: + case 3: + case 5: + case 7: + return true; + } + } + + return false; + } + + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + switch (RAMPaged) + { + case 1: + case 3: + case 5: + case 7: + return true; + } + + return false; + } + + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // 128k uses ROM0 and ROM1 + // 128k loader is in ROM0, and fallback 48k rom is in ROM1 + for (int i = 0; i < 0x4000; i++) + { + ROM0[i] = RomData.RomBytes[i]; + if (RomData.RomBytes.Length > 0x4000) + ROM1[i] = RomData.RomBytes[i + 0x4000]; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs new file mode 100644 index 0000000000..6b33600bcc --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Port.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 128K/+2 Port + /// + public partial class ZX128 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + bool deviceAddressed = true; + + int result = 0xFF; + + // ports 0x3ffd & 0x7ffd + // traditionally thought to be write-only + if (port == 0x3ffd || port == 0x7ffd) + { + // https://faqwiki.zxnet.co.uk/wiki/ZX_Spectrum_128 + // HAL bugs + // Reads from port 0x7ffd cause a crash, as the 128's HAL10H8 chip does not distinguish between reads and writes to this port, + // resulting in a floating data bus being used to set the paging registers. + + // -asni (2018-06-08) - need this to pass the final portread tests from fusetest.tap + + // get the floating bus value + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); + // use this to set the paging registers + WritePort(port, (byte)result); + // return the floating bus value + return (byte)result; + } + + // check AY + if (AYDevice.ReadPort(port, ref result)) + return (byte)result; + + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over keyboard input + // if this is detected just return the kempston byte + if (lowByte == 0x1f) + { + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; + } + else + { + if (KeyboardDevice.ReadPort(port, ref result)) + { + // not a lagframe + InputRead = true; + + // process tape INs + TapeDevice.ReadPort(port, ref result); + } + else + deviceAddressed = false; + } + + if (!deviceAddressed) + { + // If this is an unused port the floating memory bus should be returned + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + + // handle AY port writes + AYDevice.WritePort(port, value); + + // memory paging + // this is controlled by writes to port 0x7ffd + // but it is only partially decoded so it actually responds to any port with bits 1 and 15 reset + if (portBits[1] == false && portBits[15] == false) + { + // if paging is disabled then all writes to this port are ignored until the next reboot + if (!PagingDisabled) + { + // Bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (RAMPaged != rp && rp < 8) + RAMPaged = rp; + + // bit 3 controls shadow screen + if (SHADOWPaged != bits[3]) + SHADOWPaged = bits[3]; + + // ROM page + if (bits[4]) + { + // 48k basic rom + ROMPaged = 1; + } + else + { + // 128k editor and menu system + ROMPaged = 0; + } + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + } + else + { + // no changes to paging + } + } + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + //ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } + + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); + TapeDevice.WritePort(port, value); + + // Tape + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs new file mode 100644 index 0000000000..2c7d1a722b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.Screen.cs @@ -0,0 +1,48 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 128K/+2 ULA + /// + class Screen128 : ULA + { + #region Construction + + public Screen128(SpectrumBase machine) + : base(machine) + { + // interrupt + InterruptStartTime = 3; + InterruptLength = 36; + // offsets + RenderTableOffset = 58; + ContentionOffset = 6; + FloatingBusOffset = 1; + // timing + ClockSpeed = 3546900; + FrameCycleLength = 70908; + ScanlineTime = 228; + BorderLeftTime = 24; + BorderRightTime = 24; + FirstPaperLine = 63; + FirstPaperTState = 64; + // screen layout + Border4T = true; + Border4TStage = 2; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48; // 55; // 48; + BorderBottomHeight = 48; // 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + RenderingTable = new RenderTable(this, + MachineType.ZXSpectrum128); + + SetupScreenSize(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs new file mode 100644 index 0000000000..009c882d38 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128K/ZX128.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using BizHawk.Emulation.Cores.Components.Z80A; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 128K Constructor + /// + public partial class ZX128 : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + { + Spectrum = spectrum; + CPU = cpu; + + CPUMon = new CPUMonitor(this); + CPUMon.machineType = MachineType.ZXSpectrum128; + + ROMPaged = 0; + SHADOWPaged = false; + RAMPaged = 0; + PagingDisabled = false; + + ULADevice = new Screen128(this); + + BuzzerDevice = new Beeper(this); + BuzzerDevice.Init(44100, ULADevice.FrameLength); + + TapeBuzzer = new Beeper(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + + AYDevice = new AY38912(this); + AYDevice.Init(44100, ULADevice.FrameLength); + + KeyboardDevice = new StandardKeyboard(this); + + InitJoysticks(joysticks); + + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); + TapeDevice.Init(this); + + InitializeMedia(files); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs new file mode 100644 index 0000000000..c5f833dfe4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2/ZX128Plus2.cs @@ -0,0 +1,27 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The +2 is almost identical to the 128k from an emulation point of view + /// There are just a few small changes in the ROMs + /// + public partial class ZX128Plus2 : ZX128 + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128Plus2(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + : base(spectrum, cpu, borderType, files, joysticks) + { + + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs new file mode 100644 index 0000000000..e8c384efd9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Memory.cs @@ -0,0 +1,504 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// +2A Memory + /// + public partial class ZX128Plus2a : SpectrumBase + { + /* http://www.worldofspectrum.org/faq/reference/128kreference.htm + * + * Port 0x7ffd behaves in the almost exactly the same way as on the 128K/+2, with two exceptions: + + Bit 4 is now the low bit of the ROM selection. + The partial decoding used is now slightly different: the hardware will respond only to those port addresses with bit 1 reset, bit 14 set and bit 15 reset (as opposed to just bits 1 and 15 reset on the 128K/+2). + The extra paging features of the +2A/+3 are controlled by port 0x1ffd (again, partial decoding applies here: the hardware will respond to all port addresses with bit 1 reset, bit 12 set and bits 13, 14 and 15 reset). This port is also write-only, and its last value should be saved at 0x5b67 (23399). + + Port 0x1ffd responds as follows: + + Bit 0: Paging mode. 0=normal, 1=special + Bit 1: In normal mode, ignored. + Bit 2: In normal mode, high bit of ROM selection. The four ROMs are: + ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + Bit 3: Disk motor; 1=on, 0=off + Bit 4: Printer port strobe. + When special mode is selected, the memory map changes to one of four configurations specified in bits 1 and 2 of port 0x1ffd: + Bit 2 =0 Bit 2 =0 Bit 2 =1 Bit 2 =1 + Bit 1 =0 Bit 1 =1 Bit 1 =0 Bit 1 =1 + 0xffff +--------+ +--------+ +--------+ +--------+ + | Bank 3 | | Bank 7 | | Bank 3 | | Bank 3 | + | | | | | | | | + | | | | | | | | + | | | screen | | | | | + 0xc000 +--------+ +--------+ +--------+ +--------+ + | Bank 2 | | Bank 6 | | Bank 6 | | Bank 6 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x8000 +--------+ +--------+ +--------+ +--------+ + | Bank 1 | | Bank 5 | | Bank 5 | | Bank 7 | + | | | | | | | | + | | | | | | | | + | | | screen | | screen | | screen | + 0x4000 +--------+ +--------+ +--------+ +--------+ + | Bank 0 | | Bank 4 | | Bank 4 | | Bank 4 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x0000 +--------+ +--------+ +--------+ +--------+ + RAM banks 1,3,4 and 6 are used for the disc cache and RAMdisc, while Bank 7 contains editor scratchpads and +3DOS workspace. + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + byte result = 0xff; + + // special paging + if (SpecialPagingMode) + { + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + result = RAM0[addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = RAM4[addr % 0x4000]; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + result = RAM1[addr % 0x4000]; + break; + case 1: + case 2: + result = RAM5[addr % 0x4000]; + break; + case 3: + result = RAM7[addr % 0x4000]; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + result = RAM0[addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = RAM6[addr % 0x4000]; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + result = RAM3[addr % 0x4000]; + break; + case 1: + result = RAM7[addr % 0x4000]; + break; + } + break; + } + } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + switch (_ROMpaged) + { + case 0: + result = ROM0[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); + break; + case 1: + result = ROM1[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); + break; + case 2: + result = ROM2[addr % 0x4000]; + break; + case 3: + result = ROM3[addr % 0x4000]; + break; + } + break; + + // RAM 0x4000 (RAM5 - Bank5 always) + case 1: + result = RAM5[addr % 0x4000]; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = RAM2[addr % 0x4000]; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = RAM0[addr % 0x4000]; + break; + case 1: + result = RAM1[addr % 0x4000]; + break; + case 2: + result = RAM2[addr % 0x4000]; + break; + case 3: + result = RAM3[addr % 0x4000]; + break; + case 4: + result = RAM4[addr % 0x4000]; + break; + case 5: + result = RAM5[addr % 0x4000]; + break; + case 6: + result = RAM6[addr % 0x4000]; + break; + case 7: + result = RAM7[addr % 0x4000]; + break; + } + break; + default: + break; + } + } + + return result; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + + // special paging + if (SpecialPagingMode) + { + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + RAM0[addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + RAM4[addr % 0x4000] = value; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + RAM1[addr % 0x4000] = value; + break; + case 1: + case 2: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + case 3: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM7[addr % 0x4000] = value; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + RAM2[addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + RAM6[addr % 0x4000] = value; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + RAM3[addr % 0x4000] = value; + break; + case 1: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM7[addr % 0x4000] = value; + break; + } + break; + } + } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + /* + switch (_ROMpaged) + { + // cannot write to ROMs + case 0: + ROM0[addr % 0x4000] = value; + break; + case 1: + ROM1[addr % 0x4000] = value; + break; + case 2: + ROM2[addr % 0x4000] = value; + break; + case 3: + ROM3[addr % 0x4000] = value; + break; + } + */ + break; + + // RAM 0x4000 (RAM5 - Bank5 only) + case 1: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + RAM2[addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + RAM0[addr % 0x4000] = value; + break; + case 1: + RAM1[addr % 0x4000] = value; + break; + case 2: + RAM2[addr % 0x4000] = value; + break; + case 3: + RAM3[addr % 0x4000] = value; + break; + case 4: + RAM4[addr % 0x4000] = value; + break; + case 5: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + case 6: + RAM6[addr % 0x4000] = value; + break; + case 7: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM7[addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if (CPUMon.NextMemReadContended) + { + LastContendedReadByte = data; + CPUMon.NextMemReadContended = false; + } + + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + // update ULA screen buffer if necessary BEFORE T1 write + /* + if (!SpecialPagingMode) + { + if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render) + ULADevice.RenderScreen((int)CurrentFrameCycle); + } + else + { + switch (PagingConfiguration) + { + case 2: + case 3: + if ((addr & 49152) == 16384) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + case 1: + if ((addr & 49152) == 16384 || addr >= 0xc000) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + } + } + */ + WriteBus(addr, value); + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + var a = addr & 0xc000; + + if (a == 0x4000) + { + // low port contention + return true; + } + + if (a == 0xc000) + { + // high port contention - check for contended bank paged in + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + } + + return false; + } + + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + + return false; + } + + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // +3 uses ROM0, ROM1, ROM2 & ROM3 + /* ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + */ + Stream stream = new MemoryStream(RomData.RomBytes); + stream.Read(ROM0, 0, 16384); + stream.Read(ROM1, 0, 16384); + stream.Read(ROM2, 0, 16384); + stream.Read(ROM3, 0, 16384); + stream.Dispose(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs new file mode 100644 index 0000000000..6809731766 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Port.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// +2a Port + /// + public partial class ZX128Plus2a : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + bool deviceAddressed = true; + + int result = 0xFF; + + // check AY + if (AYDevice.ReadPort(port, ref result)) + return (byte)result; + + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over all keyboard input + // if this is detected just return the kempston byte + if (lowByte == 0x1f) + { + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; + } + else + { + if (KeyboardDevice.ReadPort(port, ref result)) + { + // not a lagframe + InputRead = true; + + // process tape INs + TapeDevice.ReadPort(port, ref result); + } + else + deviceAddressed = false; + } + + if (!deviceAddressed) + { + // If this is an unused port the floating memory bus should be returned + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + + // Check whether the low bit is reset + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; + + AYDevice.WritePort(port, value); + + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (port == 0x7ffd) + { + if (!PagingDisabled) + { + // bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + + // portbit 4 is the LOW BIT of the ROM selection + ROMlow = bits[4]; + } + } + // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + if (port == 0x1ffd) + { + if (!PagingDisabled) + { + if (!bits[0]) + { + // special paging is not enabled - get the ROMpage high byte + ROMhigh = bits[2]; + + // set the special paging mode flag + SpecialPagingMode = false; + } + else + { + // special paging is enabled + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // set the special paging mode flag + SpecialPagingMode = true; + } + } + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + //ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } + + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); + + // Tape + TapeDevice.WritePort(port, value); + + // Tape + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + + + LastULAOutByte = value; + } + + /// + /// +3 and 2a overidden method + /// + public override int _ROMpaged + { + get + { + // calculate the ROMpage from the high and low bits + var rp = ZXSpectrum.GetIntFromBitArray(new BitArray(new bool[] { ROMlow, ROMhigh })); + + if (rp != 0) + { + + } + + return rp; + } + set { ROMPaged = value; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs new file mode 100644 index 0000000000..7d9deeed41 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.Screen.cs @@ -0,0 +1,50 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// +2A/+3 ULA + /// + class Screen128Plus2a : ULA + { + #region Construction + + public Screen128Plus2a(SpectrumBase machine) + : base(machine) + { + // interrupt + InterruptStartTime = 0; + InterruptLength = 32; + // offsets + RenderTableOffset = 58; + ContentionOffset = 9; + FloatingBusOffset = 0; + // timing + ClockSpeed = 3546900; + FrameCycleLength = 70908; + ScanlineTime = 228; + BorderLeftTime = 24; + BorderRightTime = 24; + FirstPaperLine = 63; + FirstPaperTState = 64; + // screen layout + Border4T = true; + Border4TStage = 2; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48;// 55; + BorderBottomHeight = 48; // 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + RenderingTable = new RenderTable(this, + MachineType.ZXSpectrum128Plus2a); + + SetupScreenSize(); + + GenerateP3PortTable(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs new file mode 100644 index 0000000000..b38ba2ef0a --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus2a/ZX128Plus2a.cs @@ -0,0 +1,54 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// +2A Constructor + /// + public partial class ZX128Plus2a : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128Plus2a(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + { + Spectrum = spectrum; + CPU = cpu; + + CPUMon = new CPUMonitor(this); + CPUMon.machineType = MachineType.ZXSpectrum128Plus2a; + + ROMPaged = 0; + SHADOWPaged = false; + RAMPaged = 0; + PagingDisabled = false; + + ULADevice = new Screen128Plus2a(this); + + BuzzerDevice = new Beeper(this); + BuzzerDevice.Init(44100, ULADevice.FrameLength); + + TapeBuzzer = new Beeper(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + + AYDevice = new AY38912(this); + AYDevice.Init(44100, ULADevice.FrameLength); + + KeyboardDevice = new StandardKeyboard(this); + + InitJoysticks(joysticks); + + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); + TapeDevice.Init(this); + + InitializeMedia(files); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs new file mode 100644 index 0000000000..54295e06d8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Memory.cs @@ -0,0 +1,505 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// +3 Memory + /// + public partial class ZX128Plus3 : SpectrumBase + { + /* http://www.worldofspectrum.org/faq/reference/128kreference.htm + * + * Port 0x7ffd behaves in the almost exactly the same way as on the 128K/+2, with two exceptions: + + Bit 4 is now the low bit of the ROM selection. + The partial decoding used is now slightly different: the hardware will respond only to those port addresses with bit 1 reset, bit 14 set and bit 15 reset (as opposed to just bits 1 and 15 reset on the 128K/+2). + The extra paging features of the +2A/+3 are controlled by port 0x1ffd (again, partial decoding applies here: the hardware will respond to all port addresses with bit 1 reset, bit 12 set and bits 13, 14 and 15 reset). This port is also write-only, and its last value should be saved at 0x5b67 (23399). + + Port 0x1ffd responds as follows: + + Bit 0: Paging mode. 0=normal, 1=special + Bit 1: In normal mode, ignored. + Bit 2: In normal mode, high bit of ROM selection. The four ROMs are: + ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + Bit 3: Disk motor; 1=on, 0=off + Bit 4: Printer port strobe. + When special mode is selected, the memory map changes to one of four configurations specified in bits 1 and 2 of port 0x1ffd: + Bit 2 =0 Bit 2 =0 Bit 2 =1 Bit 2 =1 + Bit 1 =0 Bit 1 =1 Bit 1 =0 Bit 1 =1 + 0xffff +--------+ +--------+ +--------+ +--------+ + | Bank 3 | | Bank 7 | | Bank 3 | | Bank 3 | + | | | | | | | | + | | | | | | | | + | | | screen | | | | | + 0xc000 +--------+ +--------+ +--------+ +--------+ + | Bank 2 | | Bank 6 | | Bank 6 | | Bank 6 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x8000 +--------+ +--------+ +--------+ +--------+ + | Bank 1 | | Bank 5 | | Bank 5 | | Bank 7 | + | | | | | | | | + | | | | | | | | + | | | screen | | screen | | screen | + 0x4000 +--------+ +--------+ +--------+ +--------+ + | Bank 0 | | Bank 4 | | Bank 4 | | Bank 4 | + | | | | | | | | + | | | | | | | | + | | | | | | | | + 0x0000 +--------+ +--------+ +--------+ +--------+ + RAM banks 1,3,4 and 6 are used for the disc cache and RAMdisc, while Bank 7 contains editor scratchpads and +3DOS workspace. + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + byte result = 0xff; + + // special paging + if (SpecialPagingMode) + { + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + result = RAM0[addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = RAM4[addr % 0x4000]; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + result = RAM1[addr % 0x4000]; + break; + case 1: + case 2: + result = RAM5[addr % 0x4000]; + break; + case 3: + result = RAM7[addr % 0x4000]; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + result = RAM0[addr % 0x4000]; + break; + case 1: + case 2: + case 3: + result = RAM6[addr % 0x4000]; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + result = RAM3[addr % 0x4000]; + break; + case 1: + result = RAM7[addr % 0x4000]; + break; + } + break; + } + } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + switch (_ROMpaged) + { + case 0: + result = ROM0[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); + break; + case 1: + result = ROM1[addr % 0x4000]; + TestForTapeTraps(addr % 0x4000); + break; + case 2: + result = ROM2[addr % 0x4000]; + break; + case 3: + result = ROM3[addr % 0x4000]; + break; + } + break; + + // RAM 0x4000 (RAM5 - Bank5 always) + case 1: + result = RAM5[addr % 0x4000]; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + result = RAM2[addr % 0x4000]; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + result = RAM0[addr % 0x4000]; + break; + case 1: + result = RAM1[addr % 0x4000]; + break; + case 2: + result = RAM2[addr % 0x4000]; + break; + case 3: + result = RAM3[addr % 0x4000]; + break; + case 4: + result = RAM4[addr % 0x4000]; + break; + case 5: + result = RAM5[addr % 0x4000]; + break; + case 6: + result = RAM6[addr % 0x4000]; + break; + case 7: + result = RAM7[addr % 0x4000]; + break; + } + break; + default: + break; + } + } + + return result; + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + + // special paging + if (SpecialPagingMode) + { + switch (divisor) + { + case 0: + switch (PagingConfiguration) + { + case 0: + RAM0[addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + RAM4[addr % 0x4000] = value; + break; + } + break; + case 1: + switch (PagingConfiguration) + { + case 0: + RAM1[addr % 0x4000] = value; + break; + case 1: + case 2: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + case 3: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM7[addr % 0x4000] = value; + break; + } + break; + case 2: + switch (PagingConfiguration) + { + case 0: + RAM2[addr % 0x4000] = value; + break; + case 1: + case 2: + case 3: + RAM6[addr % 0x4000] = value; + break; + } + break; + case 3: + switch (PagingConfiguration) + { + case 0: + case 2: + case 3: + RAM3[addr % 0x4000] = value; + break; + case 1: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM7[addr % 0x4000] = value; + break; + } + break; + } + } + else + { + switch (divisor) + { + // ROM 0x000 + case 0: + /* + switch (_ROMpaged) + { + // cannot write to ROMs + case 0: + ROM0[addr % 0x4000] = value; + break; + case 1: + ROM1[addr % 0x4000] = value; + break; + case 2: + ROM2[addr % 0x4000] = value; + break; + case 3: + ROM3[addr % 0x4000] = value; + break; + } + */ + break; + + // RAM 0x4000 (RAM5 - Bank5 only) + case 1: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + + // RAM 0x8000 (RAM2 - Bank2) + case 2: + RAM2[addr % 0x4000] = value; + break; + + // RAM 0xc000 (any ram bank 0 - 7 may be paged in - default bank0) + case 3: + switch (RAMPaged) + { + case 0: + RAM0[addr % 0x4000] = value; + break; + case 1: + RAM1[addr % 0x4000] = value; + break; + case 2: + RAM2[addr % 0x4000] = value; + break; + case 3: + RAM3[addr % 0x4000] = value; + break; + case 4: + RAM4[addr % 0x4000] = value; + break; + case 5: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM5[addr % 0x4000] = value; + break; + case 6: + RAM6[addr % 0x4000] = value; + break; + case 7: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM7[addr % 0x4000] = value; + break; + } + break; + default: + break; + } + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + if (CPUMon.NextMemReadContended) + { + LastContendedReadByte = data; + CPUMon.NextMemReadContended = false; + } + + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + /* + // update ULA screen buffer if necessary BEFORE T1 write + if (!SpecialPagingMode) + { + if (((addr & 49152) == 16384 || ((addr & 0xc000) == 0xc000) && (RAMPaged == 5 || RAMPaged == 7)) && _render) + ULADevice.RenderScreen((int)CurrentFrameCycle); + } + else + { + switch (PagingConfiguration) + { + case 2: + case 3: + if ((addr & 49152) == 16384) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + case 1: + if ((addr & 49152) == 16384 || addr >= 0xc000) + ULADevice.RenderScreen((int)CurrentFrameCycle); + break; + } + } + */ + + WriteBus(addr, value); + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + var a = addr & 0xc000; + + if (a == 0x4000) + { + // low port contention + return true; + } + + if (a == 0xc000) + { + // high port contention - check for contended bank paged in + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + } + + return false; + } + + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + switch (RAMPaged) + { + case 4: + case 5: + case 6: + case 7: + return true; + } + + return false; + } + + /// + /// ULA reads the memory at the specified address + /// (No memory contention) + /// Will read RAM5 (screen0) by default, unless RAM7 (screen1) is selected as output + /// + /// + /// + public override byte FetchScreenMemory(ushort addr) + { + byte value = new byte(); + + if (SHADOWPaged && !PagingDisabled) + { + // shadow screen should be outputted + // this lives in RAM7 + value = RAM7[addr & 0x3FFF]; + } + else + { + // shadow screen is not set to display or paging is disabled (probably in 48k mode) + // (use screen0 at RAM5) + value = RAM5[addr & 0x3FFF]; + } + + return value; + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // +3 uses ROM0, ROM1, ROM2 & ROM3 + /* ROM 0: 128k editor, menu system and self-test program + ROM 1: 128k syntax checker + ROM 2: +3DOS + ROM 3: 48 BASIC + */ + Stream stream = new MemoryStream(RomData.RomBytes); + stream.Read(ROM0, 0, 16384); + stream.Read(ROM1, 0, 16384); + stream.Read(ROM2, 0, 16384); + stream.Read(ROM3, 0, 16384); + stream.Dispose(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs new file mode 100644 index 0000000000..5e13f828ca --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.Port.cs @@ -0,0 +1,194 @@ +using System; +using System.Collections; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// +3 Port + /// + public partial class ZX128Plus3 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + bool deviceAddressed = true; + + int result = 0xFF; + + // check AY + if (AYDevice.ReadPort(port, ref result)) + return (byte)result; + + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over all keyboard input + // if this is detected just return the kempston byte + if (lowByte == 0x1f) + { + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + InputRead = true; + } + else if (UPDDiskDevice.ReadPort(port, ref result)) + { + return (byte)result; + } + else + { + if (KeyboardDevice.ReadPort(port, ref result)) + { + // not a lagframe + InputRead = true; + + // process tape INs + TapeDevice.ReadPort(port, ref result); + } + else + deviceAddressed = false; + } + + if (!deviceAddressed) + { + // If this is an unused port the floating memory bus should be returned + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // get a BitArray of the port + BitArray portBits = new BitArray(BitConverter.GetBytes(port)); + // get a BitArray of the value byte + BitArray bits = new BitArray(new byte[] { value }); + + // Check whether the low bit is reset + bool lowBitReset = !portBits[0]; // (port & 0x01) == 0; + + AYDevice.WritePort(port, value); + + UPDDiskDevice.WritePort(port, value); + + // port 0x7ffd - hardware should only respond when bits 1 & 15 are reset and bit 14 is set + if (port == 0x7ffd) + { + if (!PagingDisabled) + { + // bits 0, 1, 2 select the RAM page + var rp = value & 0x07; + if (rp < 8) + RAMPaged = rp; + + // bit 3 controls shadow screen + SHADOWPaged = bits[3]; + + // Bit 5 set signifies that paging is disabled until next reboot + PagingDisabled = bits[5]; + + // portbit 4 is the LOW BIT of the ROM selection + ROMlow = bits[4]; + } + } + // port 0x1ffd - hardware should only respond when bits 1, 13, 14 & 15 are reset and bit 12 is set + if (port == 0x1ffd) + { + if (!PagingDisabled) + { + if (!bits[0]) + { + // special paging is not enabled - get the ROMpage high byte + ROMhigh = bits[2]; + + // set the special paging mode flag + SpecialPagingMode = false; + } + else + { + // special paging is enabled + // this is decided based on combinations of bits 1 & 2 + // Config 0 = Bit1-0 Bit2-0 + // Config 1 = Bit1-1 Bit2-0 + // Config 2 = Bit1-0 Bit2-1 + // Config 3 = Bit1-1 Bit2-1 + BitArray confHalfNibble = new BitArray(2); + confHalfNibble[0] = bits[1]; + confHalfNibble[1] = bits[2]; + + // set special paging configuration + PagingConfiguration = ZXSpectrum.GetIntFromBitArray(confHalfNibble); + + // set the special paging mode flag + SpecialPagingMode = true; + } + } + + // bit 4 is the printer port strobe + PrinterPortStrobe = bits[4]; + } + + // Only even addresses address the ULA + if (lowBitReset) + { + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + //ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } + + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); + + // Tape + TapeDevice.WritePort(port, value); + + // Tape + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + } + + + LastULAOutByte = value; + } + + /// + /// +3 and 2a overidden method + /// + public override int _ROMpaged + { + get + { + // calculate the ROMpage from the high and low bits + var rp = ZXSpectrum.GetIntFromBitArray(new BitArray(new bool[] { ROMlow, ROMhigh })); + + if (rp != 0) + { + + } + + return rp; + } + set { ROMPaged = value; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs new file mode 100644 index 0000000000..c27f74a265 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum128KPlus3/ZX128Plus3.cs @@ -0,0 +1,58 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// +3 Constructor + /// + public partial class ZX128Plus3 : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX128Plus3(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + { + Spectrum = spectrum; + CPU = cpu; + + + CPUMon = new CPUMonitor(this); + CPUMon.machineType = MachineType.ZXSpectrum128Plus3; + + ROMPaged = 0; + SHADOWPaged = false; + RAMPaged = 0; + PagingDisabled = false; + + ULADevice = new Screen128Plus2a(this); + + BuzzerDevice = new Beeper(this); + BuzzerDevice.Init(44100, ULADevice.FrameLength); + + TapeBuzzer = new Beeper(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + + AYDevice = new AY38912(this); + AYDevice.Init(44100, ULADevice.FrameLength); + + KeyboardDevice = new StandardKeyboard(this); + + InitJoysticks(joysticks); + + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); + TapeDevice.Init(this); + + UPDDiskDevice = new NECUPD765(); + UPDDiskDevice.Init(this); + + InitializeMedia(files); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs new file mode 100644 index 0000000000..f59a7652a1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum16K/ZX16.cs @@ -0,0 +1,130 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 16K is idential to 48K, just without the top 32KB of RAM + /// + public class ZX16 : ZX48 + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX16(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + : base(spectrum, cpu, borderType, files, joysticks) + { + + } + + #endregion + + #region Memory + + /* 48K Spectrum has NO memory paging + * + * + | Bank 0 | + | | + | | + | screen | + 0x4000 +--------+ + | ROM 0 | + | | + | | + | | + 0x0000 +--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + var index = addr % 0x4000; + + // paging logic goes here + + switch (divisor) + { + case 0: + TestForTapeTraps(addr % 0x4000); + return ROM0[index]; + case 1: return RAM0[index]; + default: + // memory does not exist + return 0xff; + } + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + var index = addr % 0x4000; + + // paging logic goes here + + switch (divisor) + { + case 0: + // cannot write to ROM + break; + case 1: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM0[index] = value; + break; + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + WriteBus(addr, value); + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // for 16/48k machines only ROM0 is used (no paging) + RomData.RomBytes?.CopyTo(ROM0, 0); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs new file mode 100644 index 0000000000..e222e7114a --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Memory.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 48K Memory + /// + public partial class ZX48 : SpectrumBase + { + /* 48K Spectrum has NO memory paging + * + * 0xffff +--------+ + | Bank 2 | + | | + | | + | | + 0xc000 +--------+ + | Bank 1 | + | | + | | + | | + 0x8000 +--------+ + | Bank 0 | + | | + | | + | screen | + 0x4000 +--------+ + | ROM 0 | + | | + | | + | | + 0x0000 +--------+ + */ + + /// + /// Simulates reading from the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override byte ReadBus(ushort addr) + { + int divisor = addr / 0x4000; + var index = addr % 0x4000; + + // paging logic goes here + + switch (divisor) + { + case 0: + TestForTapeTraps(addr % 0x4000); + return ROM0[index]; + case 1: return RAM0[index]; + case 2: return RAM1[index]; + case 3: return RAM2[index]; + default: return 0; + } + } + + /// + /// Simulates writing to the bus (no contention) + /// Paging should be handled here + /// + /// + /// + public override void WriteBus(ushort addr, byte value) + { + int divisor = addr / 0x4000; + var index = addr % 0x4000; + + // paging logic goes here + + switch (divisor) + { + case 0: + // cannot write to ROM + break; + case 1: + //ULADevice.RenderScreen((int)CurrentFrameCycle); + RAM0[index] = value; + break; + case 2: + RAM1[index] = value; + break; + case 3: + RAM2[index] = value; + break; + } + } + + /// + /// Reads a byte of data from a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override byte ReadMemory(ushort addr) + { + var data = ReadBus(addr); + return data; + } + + /// + /// Writes a byte of data to a specified memory address + /// (with memory contention if appropriate) + /// + /// + /// + public override void WriteMemory(ushort addr, byte value) + { + WriteBus(addr, value); + } + + /// + /// Checks whether supplied address is in a potentially contended bank + /// + /// + public override bool IsContended(ushort addr) + { + if ((addr & 49152) == 16384) + return true; + return false; + } + + /// + /// Returns TRUE if there is a contended bank paged in + /// + /// + public override bool ContendedBankPaged() + { + return false; + } + + /// + /// Sets up the ROM + /// + /// + /// + public override void InitROM(RomData romData) + { + RomData = romData; + // for 16/48k machines only ROM0 is used (no paging) + RomData.RomBytes?.CopyTo(ROM0, 0); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs new file mode 100644 index 0000000000..019b82e666 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Port.cs @@ -0,0 +1,103 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 48K Port + /// + public partial class ZX48 : SpectrumBase + { + /// + /// Reads a byte of data from a specified port address + /// + /// + /// + public override byte ReadPort(ushort port) + { + int result = 0xFF; + + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + bool lowBitReset = (port & 0x0001) == 0; + byte lowByte = (byte)(port & 0xff); + + // Kempston joystick input takes priority over keyboard input + // if this is detected just return the kempston byte + if (lowByte == 0x1f) + { + if (LocateUniqueJoystick(JoystickType.Kempston) != null) + return (byte)((KempstonJoystick)LocateUniqueJoystick(JoystickType.Kempston) as KempstonJoystick).JoyLine; + + // not a lag frame + InputRead = true; + } + // Even ports always address the ULA + else if (lowBitReset) + { + // Even I/O address so get input from keyboard + KeyboardDevice.ReadPort(port, ref result); + + // not a lagframe + InputRead = true; + + // process tape INs + TapeDevice.ReadPort(port, ref result); + } + else + { + // devices other than the ULA will respond here + // (e.g. the AY sound chip in a 128k spectrum + + // AY register activate - no AY chip in a 48k spectrum + + // Kemptson Mouse (not implemented yet) + + + // If this is an unused port the floating memory bus should be returned + ULADevice.ReadFloatingBus((int)CurrentFrameCycle, ref result, port); + } + + return (byte)result; + } + + /// + /// Writes a byte of data to a specified port address + /// + /// + /// + public override void WritePort(ushort port, byte value) + { + // Check whether the low bit is reset + // Technically the ULA should respond to every even I/O address + if ((port & 0x0001) != 0) + return; + + // store the last OUT byte + LastULAOutByte = value; + + /* + Bit 7 6 5 4 3 2 1 0 + +-------------------------------+ + | | | | E | M | Border | + +-------------------------------+ + */ + + // Border - LSB 3 bits hold the border colour + if (ULADevice.BorderColor != (value & BORDER_BIT)) + { + //ULADevice.RenderScreen((int)CurrentFrameCycle); + ULADevice.BorderColor = value & BORDER_BIT; + } + + // Buzzer + BuzzerDevice.ProcessPulseValue((value & EAR_BIT) != 0); + + // Tape + TapeDevice.WritePort(port, value); + + // Tape mic processing (not implemented yet) + //TapeDevice.ProcessMicBit((value & MIC_BIT) != 0); + + } + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs new file mode 100644 index 0000000000..9f01e83c04 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.Screen.cs @@ -0,0 +1,48 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 48K ULA + /// + class Screen48 : ULA + { + #region Construction + + public Screen48(SpectrumBase machine) + : base(machine) + { + // interrupt + InterruptStartTime = 3; + InterruptLength = 32; + // offsets + RenderTableOffset = 56; + ContentionOffset = 6; + FloatingBusOffset = 1; + // timing + ClockSpeed = 3500000; + FrameCycleLength = 69888; + ScanlineTime = 224; + BorderLeftTime = 24; + BorderRightTime = 24; + FirstPaperLine = 64; + FirstPaperTState = 64; + // screen layout + Border4T = true; + Border4TStage = 0; + ScreenWidth = 256; + ScreenHeight = 192; + BorderTopHeight = 48;// 55;// 48; + BorderBottomHeight = 48;// 56; + BorderLeftWidth = 48; + BorderRightWidth = 48; + ScanLineWidth = BorderLeftWidth + ScreenWidth + BorderRightWidth; + + RenderingTable = new RenderTable(this, + MachineType.ZXSpectrum48); + + SetupScreenSize(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs new file mode 100644 index 0000000000..1f2508dd63 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Machine/ZXSpectrum48K/ZX48.cs @@ -0,0 +1,62 @@ +using BizHawk.Emulation.Cores.Components.Z80A; +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// 48K construction + /// + public partial class ZX48 : SpectrumBase + { + #region Construction + + /// + /// Main constructor + /// + /// + /// + public ZX48(ZXSpectrum spectrum, Z80A cpu, ZXSpectrum.BorderType borderType, List files, List joysticks) + { + Spectrum = spectrum; + CPU = cpu; + + CPUMon = new CPUMonitor(this); + + //ULADevice = new ULA48(this); + ULADevice = new Screen48(this); + + BuzzerDevice = new Beeper(this); + BuzzerDevice.Init(44100, ULADevice.FrameLength); + + TapeBuzzer = new Beeper(this); + TapeBuzzer.Init(44100, ULADevice.FrameLength); + + KeyboardDevice = new StandardKeyboard(this); + + InitJoysticks(joysticks); + + TapeDevice = new DatacorderDevice(spectrum.SyncSettings.AutoLoadTape); + TapeDevice.Init(this); + + InitializeMedia(files); + } + + #endregion + + #region Reset + + public override void HardReset() + { + base.HardReset(); + + Random rn = new Random(); + for (int d = 0; d < 6912; d++) + { + RAM0[d] = (byte)rn.Next(255); + } + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs new file mode 100644 index 0000000000..6217f2a282 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCExtendedFloppyDisk.cs @@ -0,0 +1,174 @@ +using System.Text; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Logical object representing a standard +3 disk image + /// + public class CPCExtendedFloppyDisk : FloppyDisk + { + /// + /// The format type + /// + public override DiskType DiskFormatType => DiskType.CPCExtended; + + /// + /// Attempts to parse incoming disk data + /// + /// + /// + /// TRUE: disk parsed + /// FALSE: unable to parse disk + /// + public override bool ParseDisk(byte[] data) + { + // look for standard magic string + string ident = Encoding.ASCII.GetString(data, 0, 16); + + if (!ident.ToUpper().Contains("EXTENDED CPC DSK")) + { + // incorrect format + return false; + } + + // read the disk information block + DiskHeader.DiskIdent = ident; + DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14); + DiskHeader.NumberOfTracks = data[0x30]; + DiskHeader.NumberOfSides = data[0x31]; + DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskData = data; + int pos = 0x34; + + if (DiskHeader.NumberOfSides > 1) + { + StringBuilder sbm = new StringBuilder(); + sbm.AppendLine(); + sbm.AppendLine(); + sbm.AppendLine("The detected disk image contains multiple sides."); + sbm.AppendLine("This is NOT currently supported in ZXHawk."); + sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk)."); + throw new System.NotImplementedException(sbm.ToString()); + } + + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + DiskHeader.TrackSizes[i] = data[pos++] * 256; + } + + // move to first track information block + pos = 0x100; + + // parse each track + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + // check for unformatted track + if (DiskHeader.TrackSizes[i] == 0) + { + DiskTracks[i] = new Track(); + DiskTracks[i].Sectors = new Sector[0]; + continue; + } + + int p = pos; + DiskTracks[i] = new Track(); + + // track info block + DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12); + p += 16; + DiskTracks[i].TrackNumber = data[p++]; + DiskTracks[i].SideNumber = data[p++]; + DiskTracks[i].DataRate = data[p++]; + DiskTracks[i].RecordingMode = data[p++]; + DiskTracks[i].SectorSize = data[p++]; + DiskTracks[i].NumberOfSectors = data[p++]; + DiskTracks[i].GAP3Length = data[p++]; + DiskTracks[i].FillerByte = data[p++]; + + int dpos = pos + 0x100; + + // sector info list + DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors]; + for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++) + { + DiskTracks[i].Sectors[s] = new Sector(); + + DiskTracks[i].Sectors[s].TrackNumber = data[p++]; + DiskTracks[i].Sectors[s].SideNumber = data[p++]; + DiskTracks[i].Sectors[s].SectorID = data[p++]; + DiskTracks[i].Sectors[s].SectorSize = data[p++]; + DiskTracks[i].Sectors[s].Status1 = data[p++]; + DiskTracks[i].Sectors[s].Status2 = data[p++]; + DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p); + p += 2; + + // sector data - begins at 0x100 offset from the start of the track info block (in this case dpos) + DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength]; + + // copy the data + for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++) + { + DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b]; + } + + // check for multiple weak/random sectors stored + if (DiskTracks[i].Sectors[s].SectorSize <= 7) + { + // sectorsize n=8 is equivilent to n=0 - FDC will use DTL for length + int specifiedSize = 0x80 << DiskTracks[i].Sectors[s].SectorSize; + + if (specifiedSize < DiskTracks[i].Sectors[s].ActualDataByteLength) + { + // more data stored than sectorsize defines + // check for multiple weak/random copies + if (DiskTracks[i].Sectors[s].ActualDataByteLength % specifiedSize != 0) + { + DiskTracks[i].Sectors[s].ContainsMultipleWeakSectors = true; + } + } + } + + // move dpos to the next sector data postion + dpos += DiskTracks[i].Sectors[s].ActualDataByteLength; + } + + // move to the next track info block + pos += DiskHeader.TrackSizes[i]; + } + + // run protection scheme detector + ParseProtection(); + + return true; + } + + /// + /// State serlialization + /// + /// + public override void SyncState(Serializer ser) + { + ser.BeginSection("Plus3FloppyDisk"); + + ser.Sync("CylinderCount", ref CylinderCount); + ser.Sync("SideCount", ref SideCount); + ser.Sync("BytesPerTrack", ref BytesPerTrack); + ser.Sync("WriteProtected", ref WriteProtected); + ser.SyncEnum("Protection", ref Protection); + + ser.Sync("DirtyData", ref DirtyData); + if (DirtyData) + { + + } + + // sync deterministic track and sector counters + ser.Sync(" _randomCounter", ref _randomCounter); + RandomCounter = _randomCounter; + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs new file mode 100644 index 0000000000..00dfe4f61d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/CPCFloppyDisk.cs @@ -0,0 +1,175 @@ +using System.Text; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Logical object representing a standard +3 disk image + /// + public class CPCFloppyDisk : FloppyDisk + { + /// + /// The format type + /// + public override DiskType DiskFormatType => DiskType.CPC; + + /// + /// Attempts to parse incoming disk data + /// + /// + /// + /// TRUE: disk parsed + /// FALSE: unable to parse disk + /// + public override bool ParseDisk(byte[] data) + { + // look for standard magic string + string ident = Encoding.ASCII.GetString(data, 0, 16); + + if (!ident.ToUpper().Contains("MV - CPC")) + { + // incorrect format + return false; + } + + // read the disk information block + DiskHeader.DiskIdent = ident; + DiskHeader.DiskCreatorString = Encoding.ASCII.GetString(data, 0x22, 14); + DiskHeader.NumberOfTracks = data[0x30]; + DiskHeader.NumberOfSides = data[0x31]; + DiskHeader.TrackSizes = new int[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskTracks = new Track[DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides]; + DiskData = data; + int pos = 0x32; + + if (DiskHeader.NumberOfSides > 1) + { + StringBuilder sbm = new StringBuilder(); + sbm.AppendLine(); + sbm.AppendLine(); + sbm.AppendLine("The detected disk image contains multiple sides."); + sbm.AppendLine("This is NOT currently supported in ZXHawk."); + sbm.AppendLine("Please find an alternate image/dump where each side has been saved as a separate *.dsk image (and use the mutli-disk bundler tool to load into Bizhawk)."); + throw new System.NotImplementedException(sbm.ToString()); + } + + // standard CPC format all track sizes are the same in the image + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + DiskHeader.TrackSizes[i] = MediaConverter.GetWordValue(data, pos); + } + + // move to first track information block + pos = 0x100; + + // parse each track + for (int i = 0; i < DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; i++) + { + // check for unformatted track + if (DiskHeader.TrackSizes[i] == 0) + { + DiskTracks[i] = new Track(); + DiskTracks[i].Sectors = new Sector[0]; + continue; + } + + int p = pos; + DiskTracks[i] = new Track(); + + // track info block + DiskTracks[i].TrackIdent = Encoding.ASCII.GetString(data, p, 12); + p += 16; + DiskTracks[i].TrackNumber = data[p++]; + DiskTracks[i].SideNumber = data[p++]; + p += 2; + DiskTracks[i].SectorSize = data[p++]; + DiskTracks[i].NumberOfSectors = data[p++]; + DiskTracks[i].GAP3Length = data[p++]; + DiskTracks[i].FillerByte = data[p++]; + + int dpos = pos + 0x100; + + // sector info list + DiskTracks[i].Sectors = new Sector[DiskTracks[i].NumberOfSectors]; + for (int s = 0; s < DiskTracks[i].NumberOfSectors; s++) + { + DiskTracks[i].Sectors[s] = new Sector(); + + DiskTracks[i].Sectors[s].TrackNumber = data[p++]; + DiskTracks[i].Sectors[s].SideNumber = data[p++]; + DiskTracks[i].Sectors[s].SectorID = data[p++]; + DiskTracks[i].Sectors[s].SectorSize = data[p++]; + DiskTracks[i].Sectors[s].Status1 = data[p++]; + DiskTracks[i].Sectors[s].Status2 = data[p++]; + DiskTracks[i].Sectors[s].ActualDataByteLength = MediaConverter.GetWordValue(data, p); + p += 2; + + // actualdatabytelength value is calculated now + if (DiskTracks[i].Sectors[s].SectorSize == 0) + { + // no sectorsize specified - DTL will be used at runtime + DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i]; + } + else if (DiskTracks[i].Sectors[s].SectorSize > 6) + { + // invalid - wrap around to 0 + DiskTracks[i].Sectors[s].ActualDataByteLength = DiskHeader.TrackSizes[i]; + } + else if (DiskTracks[i].Sectors[s].SectorSize == 6) + { + // only 0x1800 bytes are stored + DiskTracks[i].Sectors[s].ActualDataByteLength = 0x1800; + } + else + { + // valid sector size for this format + DiskTracks[i].Sectors[s].ActualDataByteLength = 0x80 << DiskTracks[i].Sectors[s].SectorSize; + } + + // sector data - begins at 0x100 offset from the start of the track info block (in this case dpos) + DiskTracks[i].Sectors[s].SectorData = new byte[DiskTracks[i].Sectors[s].ActualDataByteLength]; + + // copy the data + for (int b = 0; b < DiskTracks[i].Sectors[s].ActualDataByteLength; b++) + { + DiskTracks[i].Sectors[s].SectorData[b] = data[dpos + b]; + } + + // move dpos to the next sector data postion + dpos += DiskTracks[i].Sectors[s].ActualDataByteLength; + } + + // move to the next track info block + pos += DiskHeader.TrackSizes[i]; + } + + // run protection scheme detector + ParseProtection(); + + return true; + } + + /// + /// State serlialization + /// + /// + public override void SyncState(Serializer ser) + { + ser.BeginSection("Plus3FloppyDisk"); + + ser.Sync("CylinderCount", ref CylinderCount); + ser.Sync("SideCount", ref SideCount); + ser.Sync("BytesPerTrack", ref BytesPerTrack); + ser.Sync("WriteProtected", ref WriteProtected); + ser.SyncEnum("Protection", ref Protection); + + ser.Sync("DirtyData", ref DirtyData); + if (DirtyData) + { + + } + + ser.EndSection(); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs new file mode 100644 index 0000000000..51ee9f75f4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/DiskType.cs @@ -0,0 +1,19 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// The different disk formats ZXHawk currently supports + /// + public enum DiskType + { + /// + /// Standard CPCEMU disk format (used in the built-in +3 disk drive) + /// + CPC, + + /// + /// Extended CPCEMU disk format (used in the built-in +3 disk drive) + /// + CPCExtended + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs new file mode 100644 index 0000000000..6aa6dc8d06 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Disk/FloppyDisk.cs @@ -0,0 +1,750 @@ +using BizHawk.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// This abstract class defines a logical floppy disk + /// + public abstract class FloppyDisk + { + /// + /// The disk format type + /// + public abstract DiskType DiskFormatType { get; } + + /// + /// Disk information header + /// + public Header DiskHeader = new Header(); + + /// + /// Track array + /// + public Track[] DiskTracks = null; + + /// + /// No. of tracks per side + /// + public int CylinderCount; + + /// + /// The number of physical sides + /// + public int SideCount; + + /// + /// The number of bytes per track + /// + public int BytesPerTrack; + + /// + /// The write-protect tab on the disk + /// + public bool WriteProtected; + + /// + /// The detected protection scheme (if any) + /// + public ProtectionType Protection; + + /// + /// The actual disk image data + /// + public byte[] DiskData; + + /// + /// If TRUE then data on the disk has changed (been written to) + /// This will be used to determine whether the disk data needs to be included + /// in any SyncState operations + /// + protected bool DirtyData = false; + + /// + /// Used to deterministically choose a 'random' sector when dealing with weak reads + /// + public int RandomCounter + { + get { return _randomCounter; } + set + { + _randomCounter = value; + + foreach (var trk in DiskTracks) + { + foreach (var sec in trk.Sectors) + { + sec.RandSecCounter = _randomCounter; + } + } + } + } + protected int _randomCounter; + + + /// + /// Attempts to parse incoming disk data + /// + /// + /// + /// TRUE: disk parsed + /// FALSE: unable to parse disk + /// + public virtual bool ParseDisk(byte[] diskData) + { + // default result + // override in inheriting class + return false; + } + + /// + /// Examines the floppydisk data to work out what protection (if any) is present + /// If possible it will also fix the disk data for this protection + /// This should be run at the end of the ParseDisk() method + /// + public virtual void ParseProtection() + { + int[] weakArr = new int[2]; + + // speedlock + if (DetectSpeedlock(ref weakArr)) + { + Protection = ProtectionType.Speedlock; + + Sector sec = DiskTracks[0].Sectors[1]; + if (!sec.ContainsMultipleWeakSectors) + { + byte[] origData = sec.SectorData.ToArray(); + List data = new List(); + for (int m = 0; m < 3; m++) + { + for (int i = 0; i < 512; i++) + { + // deterministic 'random' implementation + int n = origData[i] + m + 1; + if (n > 0xff) + n = n - 0xff; + else if (n < 0) + n = 0xff + n; + + byte nByte = (byte)n; + + if (m == 0) + { + data.Add(origData[i]); + continue; + } + + if (i < weakArr[0]) + { + data.Add(origData[i]); + } + + else if (weakArr[1] > 0) + { + data.Add(nByte); + weakArr[1]--; + } + + else + { + data.Add(origData[i]); + } + } + } + + sec.SectorData = data.ToArray(); + sec.ActualDataByteLength = data.Count(); + sec.ContainsMultipleWeakSectors = true; + } + } + else if (DetectAlkatraz(ref weakArr)) + { + Protection = ProtectionType.Alkatraz; + } + else if (DetectPaulOwens(ref weakArr)) + { + Protection = ProtectionType.PaulOwens; + } + else if (DetectHexagon(ref weakArr)) + { + Protection = ProtectionType.Hexagon; + } + else if (DetectShadowOfTheBeast()) + { + Protection = ProtectionType.ShadowOfTheBeast; + } + } + + /// + /// Detection routine for shadow of the beast game + /// Still cannot get this to work, but at least the game is detected + /// + /// + public bool DetectShadowOfTheBeast() + { + if (DiskTracks[0].Sectors.Length != 9) + return false; + + var zeroSecs = DiskTracks[0].Sectors; + if (zeroSecs[0].SectorID != 65 || + zeroSecs[1].SectorID != 66 || + zeroSecs[2].SectorID != 67 || + zeroSecs[3].SectorID != 68 || + zeroSecs[4].SectorID != 69 || + zeroSecs[5].SectorID != 70 || + zeroSecs[6].SectorID != 71 || + zeroSecs[7].SectorID != 72 || + zeroSecs[8].SectorID != 73) + return false; + + var oneSecs = DiskTracks[1].Sectors; + + if (oneSecs.Length != 8) + return false; + + if (oneSecs[0].SectorID != 17 || + oneSecs[1].SectorID != 18 || + oneSecs[2].SectorID != 19 || + oneSecs[3].SectorID != 20 || + oneSecs[4].SectorID != 21 || + oneSecs[5].SectorID != 22 || + oneSecs[6].SectorID != 23 || + oneSecs[7].SectorID != 24) + return false; + + return true; + } + + /// + /// Detect speedlock weak sector + /// + /// + /// + public bool DetectSpeedlock(ref int[] weak) + { + // SPEEDLOCK NOTES (-asni 2018-05-01) + // --------------------------------- + // Speedlock is one of the more common +3 disk protections and there are a few different versions + // Usually, track 0 sector 1 (ID 2) has data CRC errors that result in certain bytes returning a different value every time they are read + // Speedlock will generally read this track a number of times during the load process + // and if the correct bytes are not different between reads, the load fails + + // always must have track 0 containing 9 sectors + if (DiskTracks[0].Sectors.Length != 9) + return false; + + // check for SPEEDLOCK ident in sector 0 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[0].SectorData, 0, DiskTracks[0].Sectors[0].SectorData.Length); + if (!ident.ToUpper().Contains("SPEEDLOCK")) + return false; + + // check for correct sector 0 lengths + if (DiskTracks[0].Sectors[0].SectorSize != 2 || + DiskTracks[0].Sectors[0].SectorData.Length < 0x200) + return false; + + // sector[1] (SectorID 2) contains the weak sectors + Sector sec = DiskTracks[0].Sectors[1]; + + // check for correct sector 1 lengths + if (sec.SectorSize != 2 || + sec.SectorData.Length < 0x200) + return false; + + // secID 2 needs a CRC error + //if (!(sec.Status1.Bit(5) || sec.Status2.Bit(5))) + //return false; + + // check for filler + bool startFillerFound = true; + for (int i = 0; i < 250; i++) + { + if (sec.SectorData[i] != sec.SectorData[i + 1]) + { + startFillerFound = false; + break; + } + } + + if (!startFillerFound) + { + weak[0] = 0; + weak[1] = 0x200; + } + else + { + weak[0] = 0x150; + weak[1] = 0x20; + } + + return true; + } + + /// + /// Detect Alkatraz + /// + /// + /// + public bool DetectAlkatraz(ref int[] weak) + { + try + { + var data1 = DiskTracks[0].Sectors[0].SectorData; + var data2 = DiskTracks[0].Sectors[0].SectorData.Length; + } + catch (Exception) + { + return false; + } + + // check for ALKATRAZ ident in sector 0 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[0].SectorData, 0, DiskTracks[0].Sectors[0].SectorData.Length); + if (!ident.ToUpper().Contains("ALKATRAZ PROTECTION SYSTEM")) + return false; + + // ALKATRAZ NOTES (-asni 2018-05-01) + // --------------------------------- + // Alkatraz protection appears to revolve around a track on the disk with 18 sectors, + // (track position is not consistent) with the sector ID info being incorrect: + // TrackID is consistent between the sectors although is usually high (233, 237 etc) + // SideID is fairly random looking but with all IDs being even + // SectorID is also fairly random looking but contains both odd and even numbers + // + // There doesnt appear to be any CRC errors in this track, but the sector size is always 1 (256 bytes) + // Each sector contains different filler byte + // Once track 0 is loaded the CPU completely reads all the sectors in this track one-by-one. + // Data transferred during execution must be correct, also result ST0, ST1 and ST2 must be 64, 128 and 0 respectively + + // Immediately following this track are a number of tracks and sectors with a DAM set. + // These are all read in sector by sector + // Again, Alkatraz appears to require that ST0, ST1, and ST2 result bytes are set to 64, 128 and 0 respectively + // (so the CM in ST2 needs to be reset) + + return true; + } + + /// + /// Detect Paul Owens + /// + /// + /// + public bool DetectPaulOwens(ref int[] weak) + { + try + { + var data1 = DiskTracks[0].Sectors[2].SectorData; + var data2 = DiskTracks[0].Sectors[2].SectorData.Length; + } + catch (Exception) + { + return false; + } + + // check for PAUL OWENS ident in sector 2 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[2].SectorData, 0, DiskTracks[0].Sectors[2].SectorData.Length); + if (!ident.ToUpper().Contains("PAUL OWENS")) + return false; + + // Paul Owens Disk Protection Notes (-asni 2018-05-01) + // --------------------------------------------------- + // + // This scheme looks a little similar to Alkatraz with incorrect sector ID info in many places + // and deleted address marks (although these do not seem to show the strict relience on removing the CM mark from ST2 result that Alkatraz does) + // There are also data CRC errors but these dont look to be read more than once/checked for changes during load + // Main identifiers: + // + // * There are more than 10 cylinders + // * Cylinder 1 has no sector data + // * The sector ID infomation in most cases contains incorrect track IDs + // * Tracks 0 (boot) and 5 appear to be pretty much the only tracks that do not have incorrect sector ID marks + + return true; + } + + /// + /// Detect Hexagon copy protection + /// + /// + /// + public bool DetectHexagon(ref int[] weak) + { + try + { + var data1 = DiskTracks[0].Sectors.Length; + var data2 = DiskTracks[0].Sectors[8].ActualDataByteLength; + var data3 = DiskTracks[0].Sectors[8].SectorData; + var data4 = DiskTracks[0].Sectors[8].SectorData.Length; + var data5 = DiskTracks[1].Sectors[0]; + } + catch (Exception) + { + return false; + } + + if (DiskTracks[0].Sectors.Length != 10 || DiskTracks[0].Sectors[8].ActualDataByteLength != 512) + return false; + + // check for Hexagon ident in sector 8 + string ident = Encoding.ASCII.GetString(DiskTracks[0].Sectors[8].SectorData, 0, DiskTracks[0].Sectors[8].SectorData.Length); + if (ident.ToUpper().Contains("GON DISK PROT")) + return true; + + // hexagon protection may not be labelled as such + var track = DiskTracks[1]; + var sector = track.Sectors[0]; + + if (sector.SectorSize == 6 && sector.Status1 == 0x20 && sector.Status2 == 0x60) + { + if (track.Sectors.Length == 1) + return true; + } + + + // Hexagon Copy Protection Notes (-asni 2018-05-01) + // --------------------------------------------------- + // + // + + return false; + } + + /* + /// + /// Should be run at the end of the ParseDisk process + /// If speedlock is detected the flag is set in the disk image + /// + /// + protected virtual void SpeedlockDetection() + { + + if (DiskTracks.Length == 0) + return; + + // check for speedlock copyright notice + string ident = Encoding.ASCII.GetString(DiskData, 0x100, 0x1400); + if (!ident.ToUpper().Contains("SPEEDLOCK")) + { + // speedlock not found + return; + } + + // get cylinder 0 + var cyl = DiskTracks[0]; + + // get sector with ID=2 + var sec = cyl.Sectors.Where(a => a.SectorID == 2).FirstOrDefault(); + + if (sec == null) + return; + + // check for already multiple weak copies + if (sec.ContainsMultipleWeakSectors || sec.SectorData.Length != 0x80 << sec.SectorSize) + return; + + // check for invalid crcs in sector 2 + if (sec.Status1.Bit(5) || sec.Status2.Bit(5)) + { + Protection = ProtectionType.Speedlock; + } + else + { + return; + } + + // we are going to create a total of 5 weak sector copies + // keeping the original copy + byte[] origData = sec.SectorData.ToArray(); + List data = new List(); + //Random rnd = new Random(); + + for (int i = 0; i < 6; i++) + { + for (int s = 0; s < origData.Length; s++) + { + if (i == 0) + { + data.Add(origData[s]); + continue; + } + + // deterministic 'random' implementation + int n = origData[s] + i + 1; + if (n > 0xff) + n = n - 0xff; + else if (n < 0) + n = 0xff + n; + + byte nByte = (byte)n; + + if (s < 336) + { + // non weak data + data.Add(origData[s]); + } + else if (s < 511) + { + // weak data + data.Add(nByte); + } + else if (s == 511) + { + // final sector byte + data.Add(nByte); + } + else + { + // speedlock sector should not be more than 512 bytes + // but in case it is just do non weak + data.Add(origData[i]); + } + } + } + + // commit the sector data + sec.SectorData = data.ToArray(); + sec.ContainsMultipleWeakSectors = true; + sec.ActualDataByteLength = data.Count(); + + } + */ + + /// + /// Returns the track count for the disk + /// + /// + public virtual int GetTrackCount() + { + return DiskHeader.NumberOfTracks * DiskHeader.NumberOfSides; + } + + /// + /// Reads the current sector ID info + /// + /// + /// + public virtual CHRN ReadID(byte trackIndex, byte side, int sectorIndex) + { + if (side != 0) + return null; + + if (DiskTracks.Length <= trackIndex || trackIndex < 0) + { + // invalid track - wrap around + trackIndex = 0; + } + + var track = DiskTracks[trackIndex]; + + if (track.NumberOfSectors <= sectorIndex) + { + // invalid sector - wrap around + sectorIndex = 0; + } + + var sector = track.Sectors[sectorIndex]; + + CHRN chrn = new CHRN(); + + chrn.C = sector.TrackNumber; + chrn.H = sector.SideNumber; + chrn.R = sector.SectorID; + + // wrap around for N > 7 + if (sector.SectorSize > 7) + { + chrn.N = (byte)(sector.SectorSize - 7); + } + else if (sector.SectorSize < 0) + { + chrn.N = 0; + } + else + { + chrn.N = sector.SectorSize; + } + + chrn.Flag1 = (byte)(sector.Status1 & 0x25); + chrn.Flag2 = (byte)(sector.Status2 & 0x61); + + chrn.DataBytes = sector.ActualData; + + return chrn; + } + + /// + /// State serialization routines + /// + /// + public abstract void SyncState(Serializer ser); + + + public class Header + { + public string DiskIdent { get; set; } + public string DiskCreatorString { get; set; } + public byte NumberOfTracks { get; set; } + public byte NumberOfSides { get; set; } + public int[] TrackSizes { get; set; } + } + + public class Track + { + public string TrackIdent { get; set; } + public byte TrackNumber { get; set; } + public byte SideNumber { get; set; } + public byte DataRate { get; set; } + public byte RecordingMode { get; set; } + public byte SectorSize { get; set; } + public byte NumberOfSectors { get; set; } + public byte GAP3Length { get; set; } + public byte FillerByte { get; set; } + public Sector[] Sectors { get; set; } + + /// + /// Presents a contiguous byte array of all sector data for this track + /// (including any multiple weak/random data) + /// + public byte[] TrackSectorData + { + get + { + List list = new List(); + + foreach (var sec in Sectors) + { + list.AddRange(sec.ActualData); + } + + return list.ToArray(); + } + } + } + + public class Sector + { + public byte TrackNumber { get; set; } + public byte SideNumber { get; set; } + public byte SectorID { get; set; } + public byte SectorSize { get; set; } + public byte Status1 { get; set; } + public byte Status2 { get; set; } + public int ActualDataByteLength { get; set; } + public byte[] SectorData { get; set; } + public bool ContainsMultipleWeakSectors { get; set; } + + public int WeakReadIndex = 0; + + public void SectorReadCompleted() + { + if (ContainsMultipleWeakSectors) + WeakReadIndex++; + } + + public int DataLen + { + get + { + if (!ContainsMultipleWeakSectors) + { + return ActualDataByteLength; + } + else + { + return ActualDataByteLength / (ActualDataByteLength / (0x80 << SectorSize)); + } + } + } + + + public int RandSecCounter = 0; + + public byte[] ActualData + { + get + { + if (!ContainsMultipleWeakSectors) + { + // check whether filler bytes are needed + int size = 0x80 << SectorSize; + if (size > ActualDataByteLength) + { + List l = new List(); + l.AddRange(SectorData); + for (int i = 0; i < size - ActualDataByteLength; i++) + { + //l.Add(SectorData[i]); + l.Add(SectorData.Last()); + } + + return l.ToArray(); + } + else + { + return SectorData; + } + } + else + { + // weak read neccessary + int copies = ActualDataByteLength / (0x80 << SectorSize); + + // handle index wrap-around + if (WeakReadIndex > copies - 1) + WeakReadIndex = copies - 1; + + // get the sector data based on the current weakreadindex + int step = WeakReadIndex * (0x80 << SectorSize); + byte[] res = new byte[(0x80 << SectorSize)]; + Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); + return res; + + /* + int copies = ActualDataByteLength / (0x80 << SectorSize); + Random rnd = new Random(); + int r = rnd.Next(0, copies - 1); + int step = r * (0x80 << SectorSize); + byte[] res = new byte[(0x80 << SectorSize)]; + Array.Copy(SectorData, step, res, 0, 0x80 << SectorSize); + return res; + */ + } + } + } + + public CHRN SectorIDInfo + { + get + { + return new CHRN + { + C = TrackNumber, + H = SideNumber, + R = SectorID, + N = SectorSize, + Flag1 = Status1, + Flag2 = Status2, + }; + } + } + } + } + + /// + /// Defines the type of speedlock detection found + /// + public enum ProtectionType + { + None, + Speedlock, + Alkatraz, + Hexagon, + Frontier, + PaulOwens, + ShadowOfTheBeast + } + +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs new file mode 100644 index 0000000000..f9059c7e10 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverter.cs @@ -0,0 +1,153 @@ +using System; +using System.IO; +using System.IO.Compression; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Abtract class that represents all Media Converters + /// + public abstract class MediaConverter + { + /// + /// The type of serializer + /// + public abstract MediaConverterType FormatType { get; } + + /// + /// Signs whether this class can be used to read the data format + /// + public virtual bool IsReader + { + get + { + return false; + } + } + + /// + /// Signs whether this class can be used to write the data format + /// + public virtual bool IsWriter + { + get + { + return false; + } + } + + /// + /// Serialization method + /// + /// + public virtual void Read(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Read operation is not implemented for this converter"); + } + + /// + /// DeSerialization method + /// + /// + public virtual void Write(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Write operation is not implemented for this converter"); + } + + /// + /// Serializer does a quick check, returns TRUE if file is detected as this type + /// + /// + public virtual bool CheckType(byte[] data) + { + throw new NotImplementedException(this.GetType().ToString() + + "Check type operation is not implemented for this converter"); + } + + #region Static Tools + + /// + /// Converts an int32 value into a byte array + /// + /// + /// + public static byte[] GetBytes(int value) + { + byte[] buf = new byte[4]; + buf[0] = (byte)value; + buf[1] = (byte)(value >> 8); + buf[2] = (byte)(value >> 16); + buf[3] = (byte)(value >> 24); + return buf; + } + + /// + /// Returns an int32 from a byte array based on offset + /// + /// + /// + /// + public static int GetInt32(byte[] buf, int offsetIndex) + { + return buf[offsetIndex] | buf[offsetIndex + 1] << 8 | buf[offsetIndex + 2] << 16 | buf[offsetIndex + 3] << 24; + } + + /// + /// Returns an uint16 from a byte array based on offset + /// + /// + /// + /// + public static ushort GetWordValue(byte[] buf, int offsetIndex) + { + return (ushort)(buf[offsetIndex] | buf[offsetIndex + 1] << 8); + } + + /// + /// Updates a byte array with a uint16 value based on offset + /// + /// + /// + /// + public static void SetWordValue(byte[] buf, int offsetIndex, ushort value) + { + buf[offsetIndex] = (byte)value; + buf[offsetIndex + 1] = (byte)(value >> 8); + } + + /// + /// Takes a PauseInMilliseconds value and returns the value in T-States + /// + /// + /// + public static int TranslatePause(int pauseInMS) + { + // t-states per millisecond + var tspms = (69888 * 50) / 1000; + // get value + int res = pauseInMS * tspms; + + return res; + } + + /// + /// Decompresses a byte array that is Z-RLE compressed + /// + /// + /// + public static void DecompressZRLE(byte[] sourceBuffer, ref byte[] destBuffer) + { + MemoryStream stream = new MemoryStream(); + stream.Write(sourceBuffer, 0, sourceBuffer.Length); + stream.Position = 0; + stream.ReadByte(); + stream.ReadByte(); + DeflateStream ds = new DeflateStream(stream, CompressionMode.Decompress, false); + ds.Read(destBuffer, 0, destBuffer.Length); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs new file mode 100644 index 0000000000..c82133037b --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/MediaConverterType.cs @@ -0,0 +1,17 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the different types of media serializer avaiable + /// + public enum MediaConverterType + { + NONE, + TZX, + TAP, + PZX, + CSW, + WAV, + DSK + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs new file mode 100644 index 0000000000..8f0ae34787 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/CSW/CswConverter.cs @@ -0,0 +1,268 @@ +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Responsible for Compressed Square Wave conversion + /// https://web.archive.org/web/20171024182530/http://ramsoft.bbk.org.omegahg.com/csw.html + /// + public class CswConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.CSW; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Position counter + /// + private int _position = 0; + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + #region Construction + + private DatacorderDevice _datacorder; + + public CswConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// Returns TRUE if pzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + // CSW Header + + // check whether this is a valid csw format file by looking at the identifier in the header + // (first 22 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 22); + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident.ToUpper() != "COMPRESSED SQUARE WAVE") + { + // this is not a valid CSW format file + return false; + } + else + { + return true; + } + } + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + + // CSW Header + + // check whether this is a valid csw format file by looking at the identifier in the header + // (first 22 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 22); + + if (ident.ToUpper() != "COMPRESSED SQUARE WAVE") + { + // this is not a valid CSW format file + throw new Exception(this.GetType().ToString() + + "This is not a valid CSW format file"); + } + + if (data[0x16] != 0x1a) + { + // invalid terminator code + throw new Exception(this.GetType().ToString() + + "This image reports as a CSW but has an invalid terminator code"); + } + + _position = 0; + + // version info + int majorVer = data[0x17]; + int minorVer = data[0x18]; + + int sampleRate; + int totalPulses; + byte compressionType; + byte flags; + byte headerExtensionLen; + byte[] cswData; + byte[] cswDataUncompressed; + + if (majorVer == 2) + { + /* + CSW-2 Header + CSW global file header - status: required + Offset Value Type Description + 0x00 (note) ASCII[22] "Compressed Square Wave" signature + 0x16 0x1A BYTE Terminator code + 0x17 0x02 BYTE CSW major revision number + 0x18 0x00 BYTE CSW minor revision number + 0x19 - DWORD Sample rate + 0x1D - DWORD Total number of pulses (after decompression) + 0x21 - BYTE Compression type (see notes below) + 0x01: RLE + 0x02: Z-RLE + 0x22 - BYTE Flags + b0: initial polarity; if set, the signal starts at logical high + 0x23 HDR BYTE Header extension length in bytes (0x00) + For future expansions only, see note below. + 0x24 - ASCIIZ[16] Encoding application description + Information about the tool which created the file (e.g. name and version) + 0x34 - BYTE[HDR] Header extension data (if present) + 0x34+HDR - - CSW data. + */ + + _position = 0x19; + sampleRate = GetInt32(data, _position); + _position += 4; + + totalPulses = GetInt32(data, _position); + cswDataUncompressed = new byte[totalPulses + 1]; + _position += 4; + + compressionType = data[_position++]; + flags = data[_position++]; + headerExtensionLen = data[_position++]; + + _position = 0x34 + headerExtensionLen; + + cswData = new byte[data.Length - _position]; + Array.Copy(data, _position, cswData, 0, cswData.Length); + + ProcessCSWV2(cswData, ref cswDataUncompressed, compressionType, totalPulses); + } + else if (majorVer == 1) + { + /* + CSW-1 Header + CSW global file header - status: required + Offset Value Type Description + 0x00 (note) ASCII[22] "Compressed Square Wave" signature + 0x16 0x1A BYTE Terminator code + 0x17 0x01 BYTE CSW major revision number + 0x18 0x01 BYTE CSW minor revision number + 0x19 - WORD Sample rate + 0x1B 0x01 BYTE Compression type + 0x01: RLE + 0x1C - BYTE Flags + b0: initial polarity; if set, the signal starts at logical high + 0x1D 0x00 BYTE[3] Reserved. + 0x20 - - CSW data. + */ + + _position = 0x19; + sampleRate = GetWordValue(data, _position); + _position += 2; + + compressionType = data[_position++]; + flags = data[_position++]; + + _position += 3; + + cswDataUncompressed = new byte[data.Length - _position]; + + if (compressionType == 1) + Array.Copy(data, _position, cswDataUncompressed, 0, cswDataUncompressed.Length); + else + throw new Exception(this.GetType().ToString() + + "CSW Format unknown compression type"); + } + else + { + throw new Exception(this.GetType().ToString() + + "CSW Format Version " + majorVer + "." + minorVer + " is not currently supported"); + } + + // create the single tape block + // (use DATA block for now so initial signal level is handled correctly by the datacorder device) + TapeDataBlock t = new TapeDataBlock(); + t.BlockDescription = BlockType.CSW_Recording; + t.BlockID = 0x18; + t.DataPeriods = new List(); + + if (flags.Bit(0)) + t.InitialPulseLevel = true; + else + t.InitialPulseLevel = false; + + var rate = (69888 * 50) / sampleRate; + + for (int i = 0; i < cswDataUncompressed.Length;) + { + int length = cswDataUncompressed[i++] * rate; + if (length == 0) + { + length = GetInt32(cswDataUncompressed, i) / rate; + i += 4; + } + + t.DataPeriods.Add(length); + } + + // add closing period + t.DataPeriods.Add((69888 * 50) / 10); + + // add to datacorder + _datacorder.DataBlocks.Add(t); + } + + /// + /// Processes a CSW v2 data block + /// + /// + /// + /// + /// + /// + public static void ProcessCSWV2( + byte[] srcBuff, + ref byte[] destBuff, + byte compType, + int pulseCount) + { + if (compType == 1) + { + Array.Copy(srcBuff, 0, destBuff, 0, pulseCount); + } + else if (compType == 2) + { + DecompressZRLE(srcBuff, ref destBuff); + } + else + throw new Exception("CSW Format unknown compression type"); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs new file mode 100644 index 0000000000..6ada823be8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/PZX/PzxConverter.cs @@ -0,0 +1,401 @@ +using BizHawk.Common.NumberExtensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for PZX format serializaton + /// Based on the information here: http://zxds.raxoft.cz/docs/pzx.txt + /// + public class PzxConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.PZX; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + /// + /// Working list of generated tape data blocks + /// + private List _blocks = new List(); + + /// + /// Position counter + /// + private int _position = 0; + + /// + /// Object to keep track of loops - this assumes there is only one loop at a time + /// + private List> _loopCounter = new List>(); + + #region Construction + + private DatacorderDevice _datacorder; + + public PzxConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// Returns TRUE if pzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + // PZX Header + + // check whether this is a valid pzx format file by looking at the identifier in the header + // (first 4 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 4); + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident.ToUpper() != "PZXT") + { + // this is not a valid PZX format file + return false; + } + else + { + return true; + } + } + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + + /* + // PZX uniform block layout + offset type name meaning + ------ ---- ---- ------- + 0 u32 tag unique identifier for the block type. + 4 u32 size size of the block in bytes, excluding the tag and size fields themselves. + 8 u8[size] data arbitrary amount of block data. + */ + + // check whether this is a valid pzx format file by looking at the identifier in the header block + string ident = Encoding.ASCII.GetString(data, 0, 4); + + if (ident.ToUpper() != "PZXT") + { + // this is not a valid TZX format file + throw new Exception(this.GetType().ToString() + + "This is not a valid PZX format file"); + } + + _position = 0; + + // parse all blocks out into seperate byte arrays first + List bDatas = new List(); + + while (_position < data.Length) + { + int startPos = _position; + + // data size + _position += 4; + int blockSize = GetInt32(data, _position); + _position += 4; + + // block data + byte[] bd = new byte[8 + blockSize]; + Array.Copy(data, startPos, bd, 0, bd.Length); + bDatas.Add(bd); + + _position += blockSize; + } + + // process the blocks + foreach (var b in bDatas) + { + int pos = 8; + string blockId = Encoding.ASCII.GetString(b, 0, 4); + int blockSize = GetInt32(b, 4); + + TapeDataBlock t = new TapeDataBlock(); + + switch (blockId) + { + // PZXT - PZX header block + /* + offset type name meaning + 0 u8 major major version number (currently 1). + 1 u8 minor minor version number (currently 0). + 2 u8[?] info tape info, see below. + */ + case "PZXT": + + break; + + // PULS - Pulse sequence + /* + offset type name meaning + 0 u16 count bits 0-14 optional repeat count (see bit 15), always greater than zero + bit 15 repeat count present: 0 not present 1 present + 2 u16 duration1 bits 0-14 low/high (see bit 15) pulse duration bits + bit 15 duration encoding: 0 duration1 1 ((duration1<<16)+duration2) + 4 u16 duration2 optional low bits of pulse duration (see bit 15 of duration1) + 6 ... ... ditto repeated until the end of the block + */ + case "PULS": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + t.InitialPulseLevel = false; + + List pulses = new List(); + + while (pos < blockSize + 8) + { + ushort[] p = new ushort[2]; + p[0] = 1; + p[1] = GetWordValue(b, pos); + pos += 2; + + if (p[1] > 0x8000) + { + p[0] = (ushort)(p[1] & 0x7fff); + p[1] = GetWordValue(b, pos); + pos += 2; + } + + if (p[1] >= 0x8000) + { + p[1] &= 0x7fff; + p[1] <<= 16; + p[1] |= GetWordValue(b, pos); + pos += 2; + } + + pulses.Add(p); + } + + // convert to tape block + t.BlockDescription = BlockType.PULS; + t.PauseInMS = 0; + + foreach (var x in pulses) + { + for (int i = 0; i < x[0]; i++) + { + t.DataPeriods.Add(x[1]); + } + } + + _datacorder.DataBlocks.Add(t); + + break; + + // DATA - Data block + /* + offset type name meaning + 0 u32 count bits 0-30 number of bits in the data stream + bit 31 initial pulse level: 0 low 1 high + 4 u16 tail duration of extra pulse after last bit of the block + 6 u8 p0 number of pulses encoding bit equal to 0. + 7 u8 p1 number of pulses encoding bit equal to 1. + 8 u16[p0] s0 sequence of pulse durations encoding bit equal to 0. + 8+2*p0 u16[p1] s1 sequence of pulse durations encoding bit equal to 1. + 8+2*(p0+p1) u8[ceil(bits/8)] data data stream, see below. + */ + case "DATA": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + List s0 = new List(); + List s1 = new List(); + List dData = new List(); + + uint initPulseLevel = 1; + int dCount = 1; + ushort tail = 0; + + while (pos < blockSize + 8) + { + dCount = GetInt32(b, pos); + initPulseLevel = (uint)((dCount & 0x80000000) == 0 ? 0 : 1); + + t.InitialPulseLevel = initPulseLevel == 1 ? true : false; + + dCount = (int)(dCount & 0x7FFFFFFF); + pos += 4; + + tail = GetWordValue(b, pos); + pos += 2; + + var p0 = b[pos++]; + var p1 = b[pos++]; + + for (int i = 0; i < p1; i++) + { + var s = GetWordValue(b, pos); + pos += 2; + s0.Add(s); + } + + for (int i = 0; i < p1; i++) + { + var s = GetWordValue(b, pos); + pos += 2; + s1.Add(s); + } + + for (int i = 0; i < Math.Ceiling((decimal)dCount / 8); i++) + { + var buff = b[pos++]; + dData.Add(buff); + } + + foreach (var by in dData) + { + for (int i = 7; i >= 0; i--) + { + if (by.Bit(i) == true) + { + foreach (var pu in s1) + { + t.DataPeriods.Add((int)pu); + } + + } + else + { + foreach (var pu in s0) + { + t.DataPeriods.Add((int)pu); + } + + } + } + } + if (tail > 0) + t.DataPeriods.Add(tail); + dData.Clear(); + } + + // convert to tape block + t.BlockDescription = BlockType.DATA; + t.PauseInMS = 0; + + // tail + //t.DataPeriods.Add(tail); + + _datacorder.DataBlocks.Add(t); + + break; + + // PAUS - Pause + /* + offset type name meaning + 0 u32 duration bits 0-30 duration of the pause + bit 31 initial pulse level: 0 low 1 high + */ + case "PAUS": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + int iniPulseLevel = 1; + int pCount = 0; + + var d = GetInt32(b, pos); + iniPulseLevel = ((d & 0x80000000) == 0 ? 0 : 1); + t.InitialPulseLevel = iniPulseLevel == 1 ? true : false; + pCount = (d & 0x7FFFFFFF); + + // convert to tape block + t.BlockDescription = BlockType.PAUS; + t.DataPeriods.Add(0); + t.DataPeriods.Add(pCount); + t.DataPeriods.Add(0); + + _datacorder.DataBlocks.Add(t); + + break; + + // BRWS - Browse point + /* + offset type name meaning + 0 u8[?] text text describing this browse point + */ + case "BRWS": + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + string info = Encoding.ASCII.GetString(b, 8, blockSize); + + // convert to tape block + t.BlockDescription = BlockType.BRWS; + t.MetaData.Add(BlockDescriptorTitle.Comments, info); + t.PauseInMS = 0; + + _datacorder.DataBlocks.Add(t); + + break; + + // STOP - Stop tape command + /* + offset type name meaning + 0 u16 flags when exactly to stop the tape (1 48k only, other always). + */ + case "STOP": + + + t.BlockID = GetInt32(b, 0); + t.DataPeriods = new List(); + + var flags = GetWordValue(b, pos); + if (flags == 1) + { + t.BlockDescription = BlockType.Stop_the_Tape_48K; + t.Command = TapeCommand.STOP_THE_TAPE_48K; + } + else + { + t.BlockDescription = BlockType.Pause_or_Stop_the_Tape; + t.Command = TapeCommand.STOP_THE_TAPE; + } + + _datacorder.DataBlocks.Add(t); + + break; + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs new file mode 100644 index 0000000000..97e1b448cf --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TAP/TapConverter.cs @@ -0,0 +1,387 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for TAP format serializaton + /// + public class TapConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.TAP; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + #region Construction + + private DatacorderDevice _datacorder; + + public TapConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + #region TAP Format Constants + + /// + /// Pilot pulse length + /// + public const int PILOT_PL = 2168; + + /// + /// Pilot pulses in the ROM header block + /// + public const int HEADER_PILOT_COUNT = 8063; + + /// + /// Pilot pulses in the ROM data block + /// + public const int DATA_PILOT_COUNT = 3223; + + /// + /// Sync 1 pulse length + /// + public const int SYNC_1_PL = 667; + + /// + /// Sync 2 pulse lenth + /// + public const int SYNC_2_PL = 735; + + /// + /// Bit 0 pulse length + /// + public const int BIT_0_PL = 855; + + /// + /// Bit 1 pulse length + /// + public const int BIT_1_PL = 1710; + + /// + /// End sync pulse length + /// + public const int TERM_SYNC = 947; + + /// + /// 1 millisecond pause + /// + public const int PAUSE_MS = 3500; + + /// + /// Used bit count in last byte + /// + public const int BIT_COUNT_IN_LAST = 8; + + #endregion + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + /* + The .TAP files contain blocks of tape-saved data. All blocks start with two bytes specifying how many bytes will follow (not counting the two length bytes). Then raw tape data follows, including the flag and checksum bytes. The checksum is the bitwise XOR of all bytes including the flag byte. For example, when you execute the line SAVE "ROM" CODE 0,2 this will result: + + |------ Spectrum-generated data -------| |---------| + + 13 00 00 03 52 4f 4d 7x20 02 00 00 00 00 80 f1 04 00 ff f3 af a3 + + ^^^^^...... first block is 19 bytes (17 bytes+flag+checksum) + ^^... flag byte (A reg, 00 for headers, ff for data blocks) + ^^ first byte of header, indicating a code block + + file name ..^^^^^^^^^^^^^ + header info ..............^^^^^^^^^^^^^^^^^ + checksum of header .........................^^ + length of second block ........................^^^^^ + flag byte ............................................^^ + first two bytes of rom .................................^^^^^ + checksum (checkbittoggle would be a better name!).............^^ + */ + + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + + // convert bytearray to memory stream + MemoryStream stream = new MemoryStream(data); + + // the first 2 bytes of the TAP file designate the length of the first data block + // this (I think) should always be 17 bytes (as this is the tape header) + byte[] blockLengthData = new byte[2]; + + // we are now going to stream through the entire file processing a block at a time + while (stream.Position < stream.Length) + { + // read and calculate the length of the block + stream.Read(blockLengthData, 0, 2); + int blockSize = BitConverter.ToUInt16(blockLengthData, 0); + if (blockSize == 0) + { + // block size is 0 - this is probably invalid (but I guess could be EoF in some situations) + break; + } + + // copy the entire block into a new bytearray + byte[] blockdata = new byte[blockSize]; + stream.Read(blockdata, 0, blockSize); + + // create and populate a new tapedatablock object + TapeDataBlock tdb = new TapeDataBlock(); + + // ascertain the block description + string description = string.Empty; + byte crc = 0; + byte crcValue = 0; + byte crcFile = 0; + byte[] programData = new byte[10]; + + // calculate block checksum value + for (int i = 0; i < blockSize; i++) + { + crc ^= blockdata[i]; + if (i < blockSize - 1) + { + crcValue = crc; + } + else + { + crcFile = blockdata[i]; + } + } + + // process the type byte + /* (The type is 0,1,2 or 3 for a Program, Number array, Character array or Code file. + A SCREEN$ file is regarded as a Code file with start address 16384 and length 6912 decimal. + If the file is a Program file, parameter 1 holds the autostart line number (or a number >=32768 if no LINE parameter was given) + and parameter 2 holds the start of the variable area relative to the start of the program. If it's a Code file, parameter 1 holds + the start of the code block when saved, and parameter 2 holds 32768. For data files finally, the byte at position 14 decimal holds the variable name.) + */ + + tdb.MetaData = new Dictionary(); + + if (blockdata[0] == 0x00 && blockSize == 19) + { + string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); + string type = "Unknown Type"; + StringBuilder sb = new StringBuilder(); + + var param1 = GetWordValue(blockdata, 12); + var param2 = GetWordValue(blockdata, 14); + + // header block - examine first byte of header + if (blockdata[1] == 0) + { + type = "Program"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 1) + { + type = "NumArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 2) + { + type = "CharArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 3) + { + type = "Code"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + } + else if (blockdata[0] == 0xff) + { + // data block + description = "Data Block " + (blockSize - 2) + "bytes"; + tdb.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); + } + else + { + // some other type (turbo data etc..) + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize); + //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + tdb.AddMetaData(BlockDescriptorTitle.Undefined, description); + } + /* + if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || blockdata[1] == 3) + { + // This is the PROGRAM header + // take the 10 filename bytes (that start at offset 2) + programData = blockdata.Skip(2).Take(10).ToArray(); + + // get the filename as a string (with padding removed) + string fileName = Encoding.ASCII.GetString(programData).Trim(); + + // get the type + string type = ""; + if (blockdata[0] == 0x00) + { + type = "Program"; + } + else + { + type = "Bytes"; + } + + // now build the description string + StringBuilder sb = new StringBuilder(); + sb.Append(type + ": "); + sb.Append(fileName + " "); + sb.Append(GetWordValue(blockdata, 14)); + sb.Append(":"); + sb.Append(GetWordValue(blockdata, 12)); + description = sb.ToString(); + } + else if (blockdata[0] == 0xFF) + { + // this is a data block + description = "Data Block " + (blockSize - 2) + "bytes"; + } + else + { + // other type + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize - 2); + description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + } + */ + + tdb.BlockDescription = BlockType.Standard_Speed_Data_Block; + + // calculate the data periods for this block + int pilotLength = 0; + + // work out pilot length + if (blockdata[0] < 4) + { + pilotLength = 8064; + } + else + { + pilotLength = 3220; + } + + // create a list to hold the data periods + List dataPeriods = new List(); + + // generate pilot pulses + for (int i = 0; i < pilotLength; i++) + { + dataPeriods.Add(PILOT_PL); + } + + // add syncro pulses + dataPeriods.Add(SYNC_1_PL); + dataPeriods.Add(SYNC_2_PL); + + int pos = 0; + + // add bit0 and bit1 periods + for (int i = 0; i < blockSize - 1; i++, pos++) + { + for (byte b = 0x80; b != 0; b >>= 1) + { + if ((blockdata[i] & b) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + if ((blockdata[i] & b) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + } + } + + // add the last byte + for (byte c = 0x80; c != (byte)(0x80 >> BIT_COUNT_IN_LAST); c >>= 1) + { + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(BIT_1_PL); + else + dataPeriods.Add(BIT_0_PL); + } + + // add block pause + int actualPause = PAUSE_MS * 1000; + //dataPeriods.Add(actualPause); + + // default pause for tap files + tdb.PauseInMS = 1000; + + // add to the tapedatablock object + tdb.DataPeriods = dataPeriods; + + // add the raw data + tdb.BlockData = blockdata; + + // generate separate PAUS block + TapeDataBlock tdbPause = new TapeDataBlock(); + tdbPause.DataPeriods = new List(); + tdbPause.BlockDescription = BlockType.PAUSE_BLOCK; + tdbPause.PauseInMS = 0; + var pauseInTStates = TranslatePause(tdb.PauseInMS); + //if (pauseInTStates > 0) + //tdbPause.DataPeriods.Add(pauseInTStates); + tdb.PauseInMS = 0; + + // add block to the tape + _datacorder.DataBlocks.Add(tdb); + + // PAUS block if neccessary + if (pauseInTStates > 0) + { + tdbPause.AddMetaData(BlockDescriptorTitle.Block_ID, pauseInTStates.ToString() + " cycles"); + + int by1000 = pauseInTStates / 70000; + int rem1000 = pauseInTStates % 70000; + + if (by1000 > 1) + { + tdbPause.DataPeriods.Add(35000); + tdbPause.DataPeriods.Add(pauseInTStates - 35000); + } + else + { + tdbPause.DataPeriods.Add(pauseInTStates); + tdbPause.DataPeriods.Add(0); + } + + _datacorder.DataBlocks.Add(tdbPause); + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs new file mode 100644 index 0000000000..052be1824f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TZX/TzxConverter.cs @@ -0,0 +1,1934 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for TZX format serializaton + /// + public class TzxConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.TZX; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + /// + /// Working list of generated tape data blocks + /// + private List _blocks = new List(); + + /// + /// Position counter + /// + private int _position = 0; + + /// + /// Object to keep track of loops - this assumes there is only one loop at a time + /// + private List> _loopCounter = new List>(); + + #region Construction + + private DatacorderDevice _datacorder; + + public TzxConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// Returns TRUE if tzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + /* + // TZX Header + length: 10 bytes + Offset Value Type Description + 0x00 "ZXTape!" ASCII[7] TZX signature + 0x07 0x1A BYTE End of text file marker + 0x08 1 BYTE TZX major revision number + 0x09 20 BYTE TZX minor revision number + */ + + // check whether this is a valid tzx format file by looking at the identifier in the header + // (first 7 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 7); + // and 'end of text' marker + byte eotm = data[7]; + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident != "ZXTape!" || eotm != 0x1A) + { + // this is not a valid TZX format file + return false; + } + else + { + return true; + } + } + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + +/* + // TZX Header + length: 10 bytes + Offset Value Type Description + 0x00 "ZXTape!" ASCII[7] TZX signature + 0x07 0x1A BYTE End of text file marker + 0x08 1 BYTE TZX major revision number + 0x09 20 BYTE TZX minor revision number +*/ + + // check whether this is a valid tzx format file by looking at the identifier in the header + // (first 7 bytes of the file) + string ident = Encoding.ASCII.GetString(data, 0, 7); + // and 'end of text' marker + byte eotm = data[7]; + + // version info + int majorVer = data[8]; + int minorVer = data[9]; + + if (ident != "ZXTape!" || eotm != 0x1A) + { + // this is not a valid TZX format file + throw new Exception(this.GetType().ToString() + + "This is not a valid TZX format file"); + } + + // iterate through each block + _position = 10; + while (_position < data.Length) + { + // block ID is the first byte in a new block + int ID = data[_position++]; + + // process the data + ProcessBlock(data, ID); + } + + } + + /// + /// Processes a TZX block + /// + /// + /// + private void ProcessBlock(byte[] data, int id) + { + // process based on detected block ID + switch (id) + { + // ID 10 - Standard Speed Data Block + case 0x10: + ProcessBlockID10(data); + break; + // ID 11 - Turbo Speed Data Block + case 0x11: + ProcessBlockID11(data); + break; + // ID 12 - Pure Tone + case 0x12: + ProcessBlockID12(data); + break; + // ID 13 - Pulse sequence + case 0x13: + ProcessBlockID13(data); + break; + // ID 14 - Pure Data Block + case 0x14: + ProcessBlockID14(data); + break; + // ID 15 - Direct Recording + case 0x15: + ProcessBlockID15(data); + break; + // ID 18 - CSW Recording + case 0x18: + ProcessBlockID18(data); + break; + // ID 19 - Generalized Data Block + case 0x19: + ProcessBlockID19(data); + break; + // ID 20 - Pause (silence) or 'Stop the Tape' command + case 0x20: + ProcessBlockID20(data); + break; + // ID 21 - Group start + case 0x21: + ProcessBlockID21(data); + break; + // ID 22 - Group end + case 0x22: + ProcessBlockID22(data); + break; + // ID 23 - Jump to block + case 0x23: + ProcessBlockID23(data); + break; + // ID 24 - Loop start + case 0x24: + ProcessBlockID24(data); + break; + // ID 25 - Loop end + case 0x25: + ProcessBlockID25(data); + break; + // ID 26 - Call sequence + case 0x26: + ProcessBlockID26(data); + break; + // ID 27 - Return from sequence + case 0x27: + ProcessBlockID27(data); + break; + // ID 28 - Select block + case 0x28: + ProcessBlockID28(data); + break; + // ID 2A - Stop the tape if in 48K mode + case 0x2A: + ProcessBlockID2A(data); + break; + // ID 2B - Set signal level + case 0x2B: + ProcessBlockID2B(data); + break; + // ID 30 - Text description + case 0x30: + ProcessBlockID30(data); + break; + // ID 31 - Message block + case 0x31: + ProcessBlockID31(data); + break; + // ID 32 - Archive info + case 0x32: + ProcessBlockID32(data); + break; + // ID 33 - Hardware type + case 0x33: + ProcessBlockID33(data); + break; + // ID 35 - Custom info block + case 0x35: + ProcessBlockID35(data); + break; + // ID 5A - "Glue" block + case 0x5A: + ProcessBlockID5A(data); + break; + + #region Depreciated Blocks + + // ID 16 - C64 ROM Type Data Block + case 0x16: + ProcessBlockID16(data); + break; + // ID 17 - C64 Turbo Tape Data Block + case 0x17: + ProcessBlockID17(data); + break; + // ID 34 - Emulation info + case 0x34: + ProcessBlockID34(data); + break; + // ID 40 - Snapshot block + case 0x40: + ProcessBlockID40(data); + break; + + #endregion + + default: + ProcessUnidentifiedBlock(data); + break; + } + } + + #region TZX Block Processors + + #region ID 10 - Standard Speed Data Block +/* length: [02,03]+04 + Offset Value Type Description + 0x00 - WORD Pause after this block (ms.) {1000} + 0x02 N WORD Length of data that follow + 0x04 - BYTE[N] Data as in .TAP files + + This block must be replayed with the standard Spectrum ROM timing values - see the values in + curly brackets in block ID 11. The pilot tone consists in 8063 pulses if the first data byte + (flag byte) is < 128, 3223 otherwise. This block can be used for the ROM loading routines AND + for custom loading routines that use the same timings as ROM ones do. */ + private void ProcessBlockID10(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x10; + t.BlockDescription = BlockType.Standard_Speed_Data_Block; + t.DataPeriods = new List(); + + int pauseLen = GetWordValue(data, _position); + if (pauseLen == 0) + pauseLen = 1000; + + t.PauseInMS = pauseLen; + + int blockLen = GetWordValue(data, _position + 2); + + _position += 4; + + byte[] tmp = new byte[blockLen]; + tmp = data.Skip(_position).Take(blockLen).ToArray(); + + var t2 = DecodeDataBlock(t, tmp, DataBlockType.Standard, pauseLen); + + // add the block + _datacorder.DataBlocks.Add(t2); + + // advance the position to the next block + _position += blockLen; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); + } + #endregion + + #region ID 11 - Turbo Speed Data Block +/* length: [0F,10,11]+12 + Offset Value Type Description + 0x00 - WORD Length of PILOT pulse {2168} + 0x02 - WORD Length of SYNC first pulse {667} + 0x04 - WORD Length of SYNC second pulse {735} + 0x06 - WORD Length of ZERO bit pulse {855} + 0x08 - WORD Length of ONE bit pulse {1710} + 0x0A - WORD Length of PILOT tone (number of pulses) {8063 header (flag<128), 3223 data (flag>=128)} + 0x0C - BYTE Used bits in the last byte (other bits should be 0) {8} + (e.g. if this is 6, then the bits used (x) in the last byte are: xxxxxx00, + where MSb is the leftmost bit, LSb is the rightmost bit) + 0x0D - WORD Pause after this block (ms.) {1000} + 0x0F N BYTE[3] Length of data that follow + 0x12 - BYTE[N] Data as in .TAP files + + This block is very similar to the normal TAP block but with some additional info on the timings and other important + differences. The same tape encoding is used as for the standard speed data block. If a block should use some non-standard + sync or pilot tones (i.e. all sorts of protection schemes) then use the next three blocks to describe it.*/ + private void ProcessBlockID11(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x11; + t.BlockDescription = BlockType.Turbo_Speed_Data_Block; + t.DataPeriods = new List(); + + int pilotPL = GetWordValue(data, _position); + int sync1P = GetWordValue(data, _position + 2); + int sync2P = GetWordValue(data, _position + 4); + int bit0P = GetWordValue(data, _position + 6); + int bit1P = GetWordValue(data, _position + 8); + int pilotTL = GetWordValue(data, _position + 10); + int bitinbyte = data[_position + 12]; + int pause = GetWordValue(data, _position + 13); + + + int blockLen = 0xFFFFFF & GetInt32(data, _position + 0x0F); + + byte[] bLenArr = data.Skip(_position + 0x0F).Take(3).ToArray(); + + _position += 0x12; + + byte[] tmp = new byte[blockLen]; + tmp = data.Skip(_position).Take(blockLen).ToArray(); + + var t2 = DecodeDataBlock(t, tmp, DataBlockType.Turbo, pause, pilotTL, pilotPL, sync1P, sync2P, bit0P, bit1P, bitinbyte); + + t.PauseInMS = pause; + + // add the block + _datacorder.DataBlocks.Add(t2); + + // advance the position to the next block + _position += blockLen; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); + } + #endregion + + #region ID 12 - Pure Tone +/* length: 04 + Offset Value Type Description + 0x00 - WORD Length of one pulse in T-states + 0x02 - WORD Number of pulses + + This will produce a tone which is basically the same as the pilot tone in the ID 10, ID 11 blocks. You can define how + long the pulse is and how many pulses are in the tone. */ + private void ProcessBlockID12(byte[] data) + { + int blockLen = 4; + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x12; + t.BlockDescription = BlockType.Pure_Tone; + t.DataPeriods = new List(); + t.PauseInMS = 0; + + // get values + int pulseLength = GetWordValue(data, _position); + int pulseCount = GetWordValue(data, _position + 2); + + t.AddMetaData(BlockDescriptorTitle.Pulse_Length, pulseLength.ToString() + " T-States"); + t.AddMetaData(BlockDescriptorTitle.Pulse_Count, pulseCount.ToString()); + + // build period information + for (int p = 0; p < pulseCount; p++) + { + t.DataPeriods.Add(pulseLength); + } + + // add the block + _datacorder.DataBlocks.Add(t); + + // advance the position to the next block + _position += blockLen; + } + #endregion + + #region ID 13 - Pulse sequence +/* length: [00]*02+01 + Offset Value Type Description + 0x00 N BYTE Number of pulses + 0x01 - WORD[N] Pulses' lengths + + This will produce N pulses, each having its own timing. Up to 255 pulses can be stored in this block; this is useful for non-standard + sync tones used by some protection schemes. */ + private void ProcessBlockID13(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x13; + t.BlockDescription = BlockType.Pulse_Sequence; + t.DataPeriods = new List(); + + t.PauseInMS = 0; + + // get pulse count + int pulseCount = data[_position]; + t.AddMetaData(BlockDescriptorTitle.Pulse_Count, pulseCount.ToString()); + _position++; + + // build period information + for (int p = 0; p < pulseCount; p++, _position += 2) + { + // get pulse length + int pulseLength = GetWordValue(data, _position); + t.AddMetaData(BlockDescriptorTitle.Needs_Parsing, "Pulse " + p + " Length\t" + pulseLength.ToString() + " T-States"); + t.DataPeriods.Add(pulseLength); + } + + // add the block + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 14 - Pure Data Block +/* length: [07,08,09]+0A + Offset Value Type Description + 0x00 - WORD Length of ZERO bit pulse + 0x02 - WORD Length of ONE bit pulse + 0x04 - BYTE Used bits in last byte (other bits should be 0) + (e.g. if this is 6, then the bits used (x) in the last byte are: xxxxxx00, + where MSb is the leftmost bit, LSb is the rightmost bit) + 0x05 - WORD Pause after this block (ms.) + 0x07 N BYTE[3] Length of data that follow + 0x0A - BYTE[N] Data as in .TAP files + + This is the same as in the turbo loading data block, except that it has no pilot or sync pulses. */ + private void ProcessBlockID14(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x14; + t.BlockDescription = BlockType.Pure_Data_Block; + t.DataPeriods = new List(); + + int pilotPL = 0; + int sync1P = 0; + int sync2P = 0; + int bit0P = GetWordValue(data, _position + 0); + int bit1P = GetWordValue(data, _position + 2); + int pilotTL = 0; + int bitinbyte = data[_position + 4]; + int pause = GetWordValue(data, _position + 5); + + int blockLen = 0xFFFFFF & GetInt32(data, _position + 0x07); + + _position += 0x0A; + + byte[] tmp = new byte[blockLen]; + tmp = data.Skip(_position).Take(blockLen).ToArray(); + + var t2 = DecodeDataBlock(t, tmp, DataBlockType.Pure, pause, pilotTL, pilotPL, sync1P, sync2P, bit0P, bit1P, bitinbyte); + + t.PauseInMS = pause; + + // add the block + _datacorder.DataBlocks.Add(t2); + + // advance the position to the next block + _position += blockLen; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); + } + #endregion + + #region ID 15 - Direct Recording +/* length: [05,06,07]+08 + Offset Value Type Description + 0x00 - WORD Number of T-states per sample (bit of data) + 0x02 - WORD Pause after this block in milliseconds (ms.) + 0x04 - BYTE Used bits (samples) in last byte of data (1-8) + (e.g. if this is 2, only first two samples of the last byte will be played) + 0x05 N BYTE[3] Length of samples' data + 0x08 - BYTE[N] Samples data. Each bit represents a state on the EAR port (i.e. one sample). + MSb is played first. + + This block is used for tapes which have some parts in a format such that the turbo loader block cannot be used. + This is not like a VOC file, since the information is much more compact. Each sample value is represented by one bit only + (0 for low, 1 for high) which means that the block will be at most 1/8 the size of the equivalent VOC. + The preferred sampling frequencies are 22050 or 44100 Hz (158 or 79 T-states/sample). + Please, if you can, don't use other sampling frequencies. + Please use this block only if you cannot use any other block. */ + private void ProcessBlockID15(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x15; + t.BlockDescription = BlockType.Direct_Recording; + t.DataPeriods = new List(); + + // get values + int samLen = GetInt32(data, _position + 5); + int samSize = 0xFFFFFF & samLen; + + int tStatesPerSample = GetWordValue(data, _position); + int pauseAfterBlock = GetWordValue(data, _position + 2); + int usedBitsInLastByte = data[_position + 4]; + + // skip to samples data + _position += 8; + + int pulseLength = 0; + int pulseCount = 0; + + // ascertain the pulse count + for (int i = 0; i < samSize; i++) + { + for (int p = 0x80; p != 0; p >>= 1) + { + if (((data[_position + i] ^ pulseLength) & p) != 0) + { + pulseCount++; + pulseLength ^= -1; + } + } + } + + // get the pulses + t.DataPeriods = new List(pulseCount + 2); + int tStateCount = 0; + pulseLength = 0; + for (int i = 1; i < samSize; i++) + { + for (int p = 0x80; p != 0; p >>= 1) + { + tStateCount += tStatesPerSample; + if (((data[_position] ^ pulseLength) & p) != 0) + { + t.DataPeriods.Add(tStateCount); + pulseLength ^= -1; + tStateCount = 0; + } + } + + // incrememt position + _position++; + } + + // get the pulses in the last byte of data + for (int p = 0x80; p != (byte)(0x80 >> usedBitsInLastByte); p >>= 1) + { + tStateCount += tStatesPerSample; + if (((data[_position] ^ pulseLength) & p) != 0) + { + t.DataPeriods.Add(tStateCount); + pulseLength ^= -1; + tStateCount = 0; + } + } + + // add final pulse + t.DataPeriods.Add(tStateCount); + + // add end of block pause + if (pauseAfterBlock > 0) + { + //t.DataPeriods.Add(3500 * pauseAfterBlock); + } + + t.PauseInMS = pauseAfterBlock; + + // increment position + _position++; + + // add the block + _datacorder.DataBlocks.Add(t); + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); + } + #endregion + + #region ID 18 - CSW Recording +/* length: [00,01,02,03]+04 + Offset Value Type Description + 0x00 10+N DWORD Block length (without these four bytes) + 0x04 - WORD Pause after this block (in ms). + 0x06 - BYTE[3] Sampling rate + 0x09 - BYTE Compression type + 0x01: RLE + 0x02: Z-RLE + 0x0A - DWORD Number of stored pulses (after decompression, for validation purposes) + 0x0E - BYTE[N] CSW data, encoded according to the CSW file format specification. + + This block contains a sequence of raw pulses encoded in CSW format v2 (Compressed Square Wave). */ + private void ProcessBlockID18(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x18; + t.BlockDescription = BlockType.CSW_Recording; + t.DataPeriods = new List(); + + int blockLen = GetInt32(data, _position); + _position += 4; + + t.PauseInMS = GetWordValue(data, _position); + + _position += 2; + + int sampleRate = data[_position++] << 16 | data[_position++] << 8 | data[_position++]; + byte compType = data[_position++]; + int pulses = GetInt32(data, _position); + _position += 4; + + int dataLen = blockLen - 10; + + // build source array + byte[] src = new byte[dataLen]; + // build destination array + byte[] dest = new byte[pulses + 1]; + + // process the CSW data + CswConverter.ProcessCSWV2(src, ref dest, compType, pulses); + + // create the periods + var rate = (69888 * 50) / sampleRate; + + for (int i = 0; i < dest.Length;) + { + int length = dest[i++] * rate; + if (length == 0) + { + length = GetInt32(dest, i) / rate; + i += 4; + } + + t.DataPeriods.Add(length); + } + + // add closing period + t.DataPeriods.Add((69888 * 50) / 10); + + _position += dataLen; + //_position += blockLen; + + // add the block + _datacorder.DataBlocks.Add(t); + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); + } + #endregion + + #region ID 19 - Generalized Data Block +/* length: [00,01,02,03]+04 + Offset Value Type Description + 0x00 - DWORD Block length (without these four bytes) + 0x04 - WORD Pause after this block (ms) + 0x06 TOTP DWORD Total number of symbols in pilot/sync block (can be 0) + 0x0A NPP BYTE Maximum number of pulses per pilot/sync symbol + 0x0B ASP BYTE Number of pilot/sync symbols in the alphabet table (0=256) + 0x0C TOTD DWORD Total number of symbols in data stream (can be 0) + 0x10 NPD BYTE Maximum number of pulses per data symbol + 0x11 ASD BYTE Number of data symbols in the alphabet table (0=256) + 0x12 - SYMDEF[ASP] Pilot and sync symbols definition table + This field is present only if TOTP>0 + 0x12+ + (2*NPP+1)*ASP - PRLE[TOTP] Pilot and sync data stream + This field is present only if TOTP>0 + 0x12+ + (TOTP>0)*((2*NPP+1)*ASP)+ + TOTP*3 - SYMDEF[ASD] Data symbols definition table + This field is present only if TOTD>0 + 0x12+ + (TOTP>0)*((2*NPP+1)*ASP)+ + TOTP*3+ + (2*NPD+1)*ASD - BYTE[DS] Data stream + This field is present only if TOTD>0 + + This block has been specifically developed to represent an extremely wide range of data encoding techniques. + The basic idea is that each loading component (pilot tone, sync pulses, data) is associated to a specific sequence + of pulses, where each sequence (wave) can contain a different number of pulses from the others. + In this way we can have a situation where bit 0 is represented with 4 pulses and bit 1 with 8 pulses. + + ---- + SYMDEF structure format + Offset Value Type Description + 0x00 - BYTE Symbol flags + b0-b1: starting symbol polarity + 00: opposite to the current level (make an edge, as usual) - default + 01: same as the current level (no edge - prolongs the previous pulse) + 10: force low level + 11: force high level + 0x01 - WORD[MAXP] Array of pulse lengths. + + The alphabet is stored using a table where each symbol is a row of pulses. The number of columns (i.e. pulses) of the table is the + length of the longest sequence amongst all (MAXP=NPP or NPD, for pilot/sync or data blocks respectively); shorter waves are terminated by a + zero-length pulse in the sequence. + Any number of data symbols is allowed, so we can have more than two distinct waves; for example, imagine a loader which writes two bits at a + time by encoding them with four distinct pulse lengths: this loader would have an alphabet of four symbols, each associated to a specific + sequence of pulses (wave). + ---- + ---- + PRLE structure format + Offset Value Type Description + 0x00 - BYTE Symbol to be represented + 0x01 - WORD Number of repetitions + + Most commonly, pilot and sync are repetitions of the same pulse, thus they are represented using a very simple RLE encoding structure which stores + the symbol and the number of times it must be repeated. + Each symbol in the data stream is represented by a string of NB bits of the block data, where NB = ceiling(Log2(ASD)). + Thus the length of the whole data stream in bits is NB*TOTD, or in bytes DS=ceil(NB*TOTD/8). + ---- */ + private void ProcessBlockID19(byte[] data) + { + // not currently implemented properly + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x19; + t.BlockDescription = BlockType.Generalized_Data_Block; + t.DataPeriods = new List(); + + int blockLen = GetInt32(data, _position); + _position += 4; + + int pause = GetWordValue(data, _position); + _position += 2; + + int totp = GetInt32(data, _position); + _position += 4; + + int npp = data[_position++]; + + int asp = data[_position++]; + + int totd = GetInt32(data, _position); + _position += 4; + + int npd = data[_position++]; + + int asd = data[_position++]; + + // add the block + _datacorder.DataBlocks.Add(t); + + // advance the position to the next block + _position += blockLen; + } + #endregion + + #region ID 20 - Pause (silence) or 'Stop the Tape' command +/* length: 02 + Offset Value Type Description + 0x00 - WORD Pause duration (ms.) + + This will make a silence (low amplitude level (0)) for a given time in milliseconds. If the value is 0 then the + emulator or utility should (in effect) STOP THE TAPE, i.e. should not continue loading until the user or emulator requests it. */ + private void ProcessBlockID20(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x20; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Pause_or_Stop_the_Tape; + + int pauseDuration = GetWordValue(data, _position); + if (pauseDuration != 0) + { + //t.BlockDescription = "Pause: " + pauseDuration + " ms"; + } + else + { + //t.BlockDescription = "[STOP THE TAPE]"; + } + + t.PauseInMS = pauseDuration; + + if (pauseDuration == 0) + { + // issue stop the tape command + t.Command = TapeCommand.STOP_THE_TAPE; + // add 1ms period + //t.DataPeriods.Add(3500); + //pauseDuration = -1; + + } + else + { + // this is actually just a pause + //pauseDuration = 3500 * pauseDuration; + //t.DataPeriods.Add(pauseDuration); + } + + // add end of block pause + //t.DataPeriods.Add(pauseDuration); + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advanced position to next block + _position += 2; + + // generate PAUSE block + CreatePauseBlock(_datacorder.DataBlocks.Last()); + + } + #endregion + + #region ID 21 - Group start +/* length: [00]+01 + Offset Value Type Description + 0x00 L BYTE Length of the group name string + 0x01 - CHAR[L] Group name in ASCII format (please keep it under 30 characters long) + + This block marks the start of a group of blocks which are to be treated as one single (composite) block. + This is very handy for tapes that use lots of subblocks like Bleepload (which may well have over 160 custom loading blocks). + You can also give the group a name (example 'Bleepload Block 1'). + For each group start block, there must be a group end block. Nesting of groups is not allowed. */ + private void ProcessBlockID21(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x21; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Group_Start; + + int nameLength = data[_position]; + _position++; + + string name = Encoding.ASCII.GetString(data, _position, nameLength); + //t.BlockDescription = "[GROUP: " + name + "]"; + t.Command = TapeCommand.BEGIN_GROUP; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += nameLength; + } + #endregion + + #region ID 22 - Group end +/* length: 00 + + This indicates the end of a group. This block has no body. */ + private void ProcessBlockID22(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x22; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Group_End; + t.Command = TapeCommand.END_GROUP; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 23 - Jump to block +/* length: 02 + Offset Value Type Description + 0x00 - WORD Relative jump value + + This block will enable you to jump from one block to another within the file. The value is a signed short word + (usually 'signed short' in C); Some examples: + Jump 0 = 'Loop Forever' - this should never happen + Jump 1 = 'Go to the next block' - it is like NOP in assembler ;) + Jump 2 = 'Skip one block' + Jump -1 = 'Go to the previous block' + All blocks are included in the block count!. */ + private void ProcessBlockID23(byte[] data) + { + // not implemented properly + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x23; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Jump_to_Block; + + int relativeJumpValue = GetWordValue(data, _position); + string result = string.Empty; + + switch(relativeJumpValue) + { + case 0: + result = "Loop Forever"; + break; + case 1: + result = "To Next Block"; + break; + case 2: + result = "Skip One Block"; + break; + case -1: + result = "Go to Previous Block"; + break; + } + + //t.BlockDescription = "[JUMP BLOCK - " + result +"]"; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 2; + } + #endregion + + #region ID 24 - Loop start +/* length: 02 + Offset Value Type Description + 0x00 - WORD Number of repetitions (greater than 1) + + If you have a sequence of identical blocks, or of identical groups of blocks, you can use this block to tell how many times they should + be repeated. This block is the same as the FOR statement in BASIC. + For simplicity reasons don't nest loop blocks! */ + private void ProcessBlockID24(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x24; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Loop_Start; + + // loop should start from the next block + int loopStart = _datacorder.DataBlocks.Count() + 1; + + int numberOfRepetitions = GetWordValue(data, _position); + + // update loop counter + _loopCounter.Add( + new KeyValuePair( + loopStart, + numberOfRepetitions)); + + // update description + //t.BlockDescription = "[LOOP START - " + numberOfRepetitions + " times]"; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 2; + } + #endregion + + #region ID 25 - Loop end +/* length: 00 + + This is the same as BASIC's NEXT statement. It means that the utility should jump back to the start of the loop if it hasn't + been run for the specified number of times. + This block has no body. */ + private void ProcessBlockID25(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x25; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Loop_End; + + // get the most recent loop info + var loop = _loopCounter.LastOrDefault(); + + int loopStart = loop.Key; + int numberOfRepetitions = loop.Value; + + if (numberOfRepetitions == 0) + { + return; + } + + // get the number of blocks to loop + int blockCnt = _datacorder.DataBlocks.Count() - loopStart; + + // loop through each group to repeat + for (int b = 0; b < numberOfRepetitions; b++) + { + TapeDataBlock repeater = new TapeDataBlock(); + //repeater.BlockDescription = "[LOOP REPEAT - " + (b + 1) + "]"; + repeater.DataPeriods = new List(); + + // add the repeat block + _datacorder.DataBlocks.Add(repeater); + + // now iterate through and add the blocks to be repeated + for (int i = 0; i < blockCnt; i++) + { + var block = _datacorder.DataBlocks[loopStart + i]; + _datacorder.DataBlocks.Add(block); + } + } + } + #endregion + + #region ID 26 - Call sequence +/* length: [00,01]*02+02 + Offset Value Type Description + 0x00 N WORD Number of calls to be made + 0x02 - WORD[N] Array of call block numbers (relative-signed offsets) + + This block is an analogue of the CALL Subroutine statement. It basically executes a sequence of blocks that are somewhere else and + then goes back to the next block. Because more than one call can be normally used you can include a list of sequences to be called. + The 'nesting' of call blocks is also not allowed for the simplicity reasons. You can, of course, use the CALL blocks in the LOOP + sequences and vice versa. The value is relative for the obvious reasons - so that you can add some blocks in the beginning of the + file without disturbing the call values. Please take a look at 'Jump To Block' for reference on the values. */ + private void ProcessBlockID26(byte[] data) + { + // block processing not implemented for this - just gets added for informational purposes only + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x26; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Call_Sequence; + + int blockSize = 2 + 2 * GetWordValue(data, _position); + t.PauseInMS = 0; + + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockSize; + } + #endregion + + #region ID 27 - Return from sequence +/* length: 00 + + This block indicates the end of the Called Sequence. The next block played will be the block after the last CALL block (or the next Call, + if the Call block had multiple calls). + Again, this block has no body. */ + private void ProcessBlockID27(byte[] data) + { + // block processing not implemented for this - just gets added for informational purposes only + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x27; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Return_From_Sequence; + t.PauseInMS = 0; + + + // add to tape + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 28 - Select block +/* length: [00,01]+02 + Offset Value Type Description + 0x00 - WORD Length of the whole block (without these two bytes) + 0x02 N BYTE Number of selections + 0x03 - SELECT[N] List of selections + + ---- + SELECT structure format + Offset Value Type Description + 0x00 - WORD Relative Offset + 0x02 L BYTE Length of description text + 0x03 - CHAR[L] Description text (please use single line and max. 30 chars) + ---- + + This block is useful when the tape consists of two or more separately-loadable parts. With this block, you are able to select + one of the parts and the utility/emulator will start loading from that block. For example you can use it when the game has a + separate Trainer or when it is a multiload. Of course, to make some use of it the emulator/utility has to show a menu with the + selections when it encounters such a block. All offsets are relative signed words. */ + private void ProcessBlockID28(byte[] data) + { + // block processing not implemented for this - just gets added for informational purposes only + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x28; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Select_Block; + + int blockSize = 2 + GetWordValue(data, _position); + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockSize; + } + #endregion + + #region ID 2A - Stop the tape if in 48K mode +/* length: 04 + Offset Value Type Description + 0x00 0 DWORD Length of the block without these four bytes (0) + + When this block is encountered, the tape will stop ONLY if the machine is an 48K Spectrum. This block is to be used for + multiloading games that load one level at a time in 48K mode, but load the entire tape at once if in 128K mode. + This block has no body of its own, but follows the extension rule. */ + private void ProcessBlockID2A(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x2A; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Stop_the_Tape_48K; + t.Command = TapeCommand.STOP_THE_TAPE_48K; + + int blockSize = 4 + GetWordValue(data, _position); + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockSize; + } + #endregion + + #region ID 2B - Set signal level +/* length: 05 + Offset Value Type Description + 0x00 1 DWORD Block length (without these four bytes) + 0x04 - BYTE Signal level (0=low, 1=high) + + This block sets the current signal level to the specified value (high or low). It should be used whenever it is necessary to avoid any + ambiguities, e.g. with custom loaders which are level-sensitive. */ + private void ProcessBlockID2B(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x2B; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Set_Signal_Level; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 5; + } + #endregion + + #region ID 30 - Text description +/* length: [00]+01 + Offset Value Type Description + 0x00 N BYTE Length of the text description + 0x01 - CHAR[N] Text description in ASCII format + + This is meant to identify parts of the tape, so you know where level 1 starts, where to rewind to when the game ends, etc. + This description is not guaranteed to be shown while the tape is playing, but can be read while browsing the tape or changing + the tape pointer. + The description can be up to 255 characters long but please keep it down to about 30 so the programs can show it in one line + (where this is appropriate). + Please use 'Archive Info' block for title, authors, publisher, etc. */ + private void ProcessBlockID30(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x30; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Text_Description; + + int textLen = data[_position]; + _position++; + + string desc = Encoding.ASCII.GetString(data, _position, textLen); + + t.PauseInMS = 0; + + //t.BlockDescription = "[" + desc + "]"; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += textLen; + } + #endregion + + #region ID 31 - Message block +/* length: [01]+02 + Offset Value Type Description + 0x00 - BYTE Time (in seconds) for which the message should be displayed + 0x01 N BYTE Length of the text message + 0x02 - CHAR[N] Message that should be displayed in ASCII format + + This will enable the emulators to display a message for a given time. This should not stop the tape and it should not make silence. + If the time is 0 then the emulator should wait for the user to press a key. + The text message should: + stick to a maximum of 30 chars per line; + use single 0x0D (13 decimal) to separate lines; + stick to a maximum of 8 lines. + If you do not obey these rules, emulators may display your message in any way they like. */ + private void ProcessBlockID31(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x31; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Message_Block; + + _position++; + + int msgLen = data[_position]; + _position++; + + string desc = Encoding.ASCII.GetString(data, _position, msgLen); + + t.Command = TapeCommand.SHOW_MESSAGE; + + //t.BlockDescription = "[MESSAGE: " + desc + "]"; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += msgLen; + } + #endregion + + #region ID 32 - Archive info +/* length: [00,01]+02 + Offset Value Type Description + 0x00 - WORD Length of the whole block (without these two bytes) + 0x02 N BYTE Number of text strings + 0x03 - TEXT[N] List of text strings + + ---- + TEXT structure format + Offset Value Type Description + 0x00 - BYTE Text identification byte: + 00 - Full title + 01 - Software house/publisher + 02 - Author(s) + 03 - Year of publication + 04 - Language + 05 - Game/utility type + 06 - Price + 07 - Protection scheme/loader + 08 - Origin + FF - Comment(s) + 0x01 L BYTE Length of text string + 0x02 - CHAR[L] Text string in ASCII format + ---- + + Use this block at the beginning of the tape to identify the title of the game, author, publisher, year of publication, price (including + the currency), type of software (arcade adventure, puzzle, word processor, ...), protection scheme it uses (Speedlock 1, Alkatraz, ...) + and its origin (Original, Budget re-release, ...), etc. This block is built in a way that allows easy future expansion. + The block consists of a series of text strings. Each text has its identification number (which tells us what the text means) and then + the ASCII text. To make it possible to skip this block, if needed, the length of the whole block is at the beginning of it. + If all texts on the tape are in English language then you don't have to supply the 'Language' field + The information about what hardware the tape uses is in the 'Hardware Type' block, so no need for it here. */ + private void ProcessBlockID32(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x32; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Archive_Info; + + int blockLen = GetWordValue(data, _position); + _position += 2; + int stringCount = data[_position++]; + + // iterate through each string + for (int s = 0; s < stringCount; s++) + { + // identify the type of text + int type = data[_position++]; + + // get text length + int strLen = data[_position++]; + + string title = "Info: "; + + switch (type) + { + case 0x00: + title = "Full Title: "; + break; + case 0x01: + title = "Software House/Publisher: "; + break; + case 0x02: + title = "Author(s): "; + break; + case 0x03: + title = "Year of Publication: "; + break; + case 0x04: + title = "Language: "; + break; + case 0x05: + title = "Game/Utility Type: "; + break; + case 0x06: + title = "Price: "; + break; + case 0x07: + title = "Protection Scheme/Loader: "; + break; + case 0x08: + title = "Origin: "; + break; + case 0xFF: + title = "Comment(s): "; + break; + default: + break; + } + + // add title to description + //t.BlockDescription += title; + + // get string data + string val = Encoding.ASCII.GetString(data, _position, strLen); + //t.BlockDescription += val + " \n"; + + t.PauseInMS = 0; + + // advance to next string block + _position += strLen; + } + + // add to tape + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 33 - Hardware type +/* length: [00]*03+01 + Offset Value Type Description + 0x00 N BYTE Number of machines and hardware types for which info is supplied + 0x01 - HWINFO[N] List of machines and hardware + + ---- + HWINFO structure format + Offset Value Type Description + 0x00 - BYTE Hardware type + 0x01 - BYTE Hardware ID + 0x02 - BYTE Hardware information: + 00 - The tape RUNS on this machine or with this hardware, + but may or may not use the hardware or special features of the machine. + 01 - The tape USES the hardware or special features of the machine, + such as extra memory or a sound chip. + 02 - The tape RUNS but it DOESN'T use the hardware + or special features of the machine. + 03 - The tape DOESN'T RUN on this machine or with this hardware. + ---- + + This blocks contains information about the hardware that the programs on this tape use. Please include only machines and hardware for + which you are 100% sure that it either runs (or doesn't run) on or with, or you know it uses (or doesn't use) the hardware or special + features of that machine. + If the tape runs only on the ZX81 (and TS1000, etc.) then it clearly won't work on any Spectrum or Spectrum variant, so there's no + need to list this information. + If you are not sure or you haven't tested a tape on some particular machine/hardware combination then do not include it in the list. + The list of hardware types and IDs is somewhat large, and may be found at the end of the format description. */ + private void ProcessBlockID33(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x33; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Hardware_Type; + + t.PauseInMS = 0; + + // first byte contains number of HWINFOs + int infos = data[_position]; + + _position += 1; + + // now starts the HW infos (each block 3 bytes) + for (int i = 0; i < infos; i++) + { + _position += 3; + } + + // add to tape + _datacorder.DataBlocks.Add(t); + } + #endregion + + #region ID 35 - Custom info block +/* length: [10,11,12,13]+14 + Offset Value Type Description + 0x00 - CHAR[10] Identification string (in ASCII) + 0x10 L DWORD Length of the custom info + 0x14 - BYTE[L] Custom info + + This block can be used to save any information you want. For example, it might contain some information written by a utility, + extra settings required by a particular emulator, or even poke data. */ + private void ProcessBlockID35(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x35; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Custom_Info_Block; + + t.PauseInMS = 0; + + string info = Encoding.ASCII.GetString(data, _position, 0x10); + //t.BlockDescription = "[CUSTOM INFO: " + info + "]"; + _position += 0x10; + + int blockLen = BitConverter.ToInt32(data, _position); + _position += 4; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockLen; + } + #endregion + + #region ID 5A - "Glue" block +/* length: 09 + Offset Value Type Description + 0x00 - BYTE[9] Value: { "XTape!",0x1A,MajR,MinR } + Just skip these 9 bytes and you will end up on the next ID. + + This block is generated when you merge two ZX Tape files together. It is here so that you can easily copy the files together and use + them. Of course, this means that resulting file would be 10 bytes longer than if this block was not used. All you have to do + if you encounter this block ID is to skip next 9 bytes. + If you can avoid using this block for this purpose, then do so; it is preferable to use a utility to join the two files and + ensure that they are both of the higher version number. */ + private void ProcessBlockID5A(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x5A; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Glue_Block; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 9; + } + #endregion + + #region UnDetected Blocks + + private void ProcessUnidentifiedBlock(byte[] data) + { + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = -2; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Unsupported; + //t.BlockDescription = "[UNSUPPORTED - 0x" + data[_position - 1] + "]"; + + _position += GetInt32(data, _position) & 0xFFFFFF; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 4; + } + + #endregion + + #region Depreciated Blocks + + // These mostly should be ignored by ZXHawk - here for completeness + + #region ID 16 - C64 ROM Type Data Block + private void ProcessBlockID16(byte[] data) + { + // zxhawk will not implement this block. it will however handle it so subsequent blocks can be parsed + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x16; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.C64_ROM_Type_Data_Block; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + int blockLen = GetInt32(data, _position); + _position += blockLen; + } + #endregion + + #region ID 17 - C64 Turbo Tape Data Block + private void ProcessBlockID17(byte[] data) + { + // zxhawk will not implement this block. it will however handle it so subsequent blocks can be parsed + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x17; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.C64_Turbo_Tape_Data_Block; + + t.PauseInMS = 0; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + int blockLen = GetInt32(data, _position); + _position += blockLen; + } + #endregion + + #region ID 34 - Emulation info + private void ProcessBlockID34(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x34; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Emulation_Info; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += 8; + } + #endregion + + #region ID 40 - Snapshot block + /* length: [01,02,03]+04 + Offset Value Type Description + 0x00 - BYTE Snapshot type: + 00: .Z80 format + 01: .SNA format + 0x01 L BYTE[3] Snapshot length + 0x04 - BYTE[L] Snapshot itself + + This would enable one to snapshot the game at the start and still have all the tape blocks (level data, etc.) in the same file. + Only .Z80 and .SNA snapshots are supported for compatibility reasons! + The emulator should take care of that the snapshot is not taken while the actual Tape loading is taking place (which doesn't do much sense). + And when an emulator encounters the snapshot block it should load it and then continue with the next block. */ + private void ProcessBlockID40(byte[] data) + { + // currently not implemented properly in ZXHawk + + TapeDataBlock t = new TapeDataBlock(); + t.BlockID = 0x40; + t.DataPeriods = new List(); + t.BlockDescription = BlockType.Snapshot_Block; + + _position++; + + int blockLen = data[_position] | + data[_position + 1] << 8 | + data[_position + 2] << 16; + _position += 3; + + // add to tape + _datacorder.DataBlocks.Add(t); + + // advance to next block + _position += blockLen; + } + #endregion + + #endregion + + #endregion + + #region DataBlockDecoder + + /// + /// Used to process either a standard or turbo data block + /// + /// + /// + /// + private TapeDataBlock DecodeDataBlock + ( + TapeDataBlock block, + byte[] blockdata, + DataBlockType dataBlockType, + int pauseAfterBlock, + int pilotCount, + + int pilotToneLength = 2168, + int sync1PulseLength = 667, + int sync2PulseLength = 735, + int bit0PulseLength = 855, + int bit1PulseLength = 1710, + int bitsInLastByte = 8 + ) + { + // first get the block description + string description = string.Empty; + + // process the type byte + /* (The type is 0,1,2 or 3 for a Program, Number array, Character array or Code file. + A SCREEN$ file is regarded as a Code file with start address 16384 and length 6912 decimal. + If the file is a Program file, parameter 1 holds the autostart line number (or a number >=32768 if no LINE parameter was given) + and parameter 2 holds the start of the variable area relative to the start of the program. If it's a Code file, parameter 1 holds + the start of the code block when saved, and parameter 2 holds 32768. For data files finally, the byte at position 14 decimal holds the variable name.) + */ + + int blockSize = blockdata.Length; + + // dont get description info for Pure Data Blocks + if (dataBlockType != DataBlockType.Pure) + { + if (blockdata[0] == 0x00 && blockSize == 19) + { + string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); + string type = "Unknown Type"; + StringBuilder sb = new StringBuilder(); + + var param1 = GetWordValue(blockdata, 12); + var param2 = GetWordValue(blockdata, 14); + + // header block - examine first byte of header + if (blockdata[1] == 0) + { + type = "Program"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 1) + { + type = "NumArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 2) + { + type = "CharArray"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + else if (blockdata[1] == 3) + { + type = "Code"; + sb.Append(type + ": "); + sb.Append(fileName + " "); + } + } + else if (blockdata[0] == 0xff) + { + // data block + description = "Data Block " + (blockSize - 2) + "bytes"; + block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); + } + else + { + // some other type (turbo data etc..) + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize); + //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + block.AddMetaData(BlockDescriptorTitle.Undefined, description); + } + /* + if (blockdata[0] == 0x00 && blockSize == 19 && (blockdata[1] == 0x00) || (blockdata[1] == 3 && blockdata.Length > 3)) + { + if (dataBlockType != DataBlockType.Turbo) + { + // This is the program header + string fileName = Encoding.ASCII.GetString(blockdata.Skip(2).Take(10).ToArray()).Trim(); + + string type = ""; + if (blockdata[0] == 0x00) + { + type = "Program"; + block.AddMetaData(BlockDescriptorTitle.Program, fileName); + } + else + { + type = "Bytes"; + block.AddMetaData(BlockDescriptorTitle.Bytes, fileName); + } + + // now build the description string + StringBuilder sb = new StringBuilder(); + sb.Append(type + ": "); + sb.Append(fileName + " "); + sb.Append(GetWordValue(blockdata, 14)); + sb.Append(":"); + sb.Append(GetWordValue(blockdata, 12)); + description = sb.ToString(); + } + } + else if (blockdata[0] == 0xFF) + { + // this is a data block + description = "Data Block " + (blockSize - 2) + "bytes"; + block.AddMetaData(BlockDescriptorTitle.Data_Bytes, (blockSize - 2).ToString() + " Bytes"); + } + else + { + // other type + description = string.Format("#{0} block, {1} bytes", blockdata[0].ToString("X2"), blockSize); + //description += string.Format(", crc {0}", ((crc != 0) ? string.Format("bad (#{0:X2}!=#{1:X2})", crcFile, crcValue) : "ok")); + block.AddMetaData(BlockDescriptorTitle.Undefined, description); + } + */ + } + + // update metadata + switch (dataBlockType) + { + case DataBlockType.Standard: + case DataBlockType.Turbo: + + if (dataBlockType == DataBlockType.Standard) + block.BlockDescription = BlockType.Standard_Speed_Data_Block; + if (dataBlockType == DataBlockType.Turbo) + block.BlockDescription = BlockType.Turbo_Speed_Data_Block; + + block.AddMetaData(BlockDescriptorTitle.Pilot_Pulse_Length, pilotToneLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.Pilot_Pulse_Count, pilotCount.ToString() + " Pulses"); + block.AddMetaData(BlockDescriptorTitle.First_Sync_Length, sync1PulseLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.Second_Sync_Length, sync2PulseLength.ToString() + " T-States"); + break; + + case DataBlockType.Pure: + block.BlockDescription = BlockType.Pure_Data_Block; + break; + } + + block.AddMetaData(BlockDescriptorTitle.Zero_Bit_Length, bit0PulseLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.One_Bit_Length, bit1PulseLength.ToString() + " T-States"); + block.AddMetaData(BlockDescriptorTitle.Data_Length, blockSize.ToString() + " Bytes"); + block.AddMetaData(BlockDescriptorTitle.Bits_In_Last_Byte, bitsInLastByte.ToString() + " Bits"); + block.AddMetaData(BlockDescriptorTitle.Pause_After_Data, pauseAfterBlock.ToString() + " ms"); + + // calculate period information + List dataPeriods = new List(); + + // generate pilot pulses + + if (pilotCount > 0) + { + for (int i = 0; i < pilotCount; i++) + { + dataPeriods.Add(pilotToneLength); + } + + // add syncro pulses + dataPeriods.Add(sync1PulseLength); + dataPeriods.Add(sync2PulseLength); + } + + int pos = 0; + + // add bit0 and bit1 periods + for (int i = 0; i < blockSize - 1; i++, pos++) + { + for (byte b = 0x80; b != 0; b >>= 1) + { + if ((blockdata[i] & b) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + if ((blockdata[i] & b) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + } + } + + // add the last byte + for (byte c = 0x80; c != (byte)(0x80 >> bitsInLastByte); c >>= 1) + { + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + if ((blockdata[pos] & c) != 0) + dataPeriods.Add(bit1PulseLength); + else + dataPeriods.Add(bit0PulseLength); + } + + // add block pause if pause is not 0 + if (pauseAfterBlock != 0) + { + block.PauseInMS = pauseAfterBlock; + //int actualPause = pauseAfterBlock * 3500; + //dataPeriods.Add(actualPause); + } + + // add to the tapedatablock object + block.DataPeriods = dataPeriods; + + // add the raw data + block.BlockData = blockdata; + + return block; + } + + /// + /// Used to process either a standard or turbo data block + /// + /// + /// + /// + private TapeDataBlock DecodeDataBlock + ( + TapeDataBlock block, + byte[] blockData, + DataBlockType dataBlockType, + int pauseAfterBlock, + + int pilotToneLength = 2168, + int sync1PulseLength = 667, + int sync2PulseLength = 735, + int bit0PulseLength = 855, + int bit1PulseLength = 1710, + int bitsInLastByte = 8 + ) + { + + // pilot count needs to be ascertained from flag byte + int pilotCount; + if (blockData[0] < 128) + pilotCount = 8063; + else + pilotCount = 3223; + + // now we can decode + var nBlock = DecodeDataBlock + ( + block, + blockData, + dataBlockType, + pauseAfterBlock, + pilotCount, + pilotToneLength, + sync1PulseLength, + sync2PulseLength, + bit0PulseLength, + bit1PulseLength, + bitsInLastByte + ); + + + return nBlock; + } + + #endregion + + #region Pause Block Creator + + /// + /// If neccessary a seperate PAUSE block will be created + /// + /// + private void CreatePauseBlock(TapeDataBlock original) + { + if (original.PauseInMS > 0) + { + TapeDataBlock pBlock = new TapeDataBlock(); + pBlock.DataPeriods = new List(); + pBlock.BlockDescription = BlockType.PAUSE_BLOCK; + pBlock.PauseInMS = 0; + var pauseInTStates = TranslatePause(original.PauseInMS); + + pBlock.AddMetaData(BlockDescriptorTitle.Block_ID, pauseInTStates.ToString() + " cycles"); + + int by1000 = pauseInTStates / 70000; + int rem1000 = pauseInTStates % 70000; + + if (by1000 > 1) + { + pBlock.DataPeriods.Add(35000); + pBlock.DataPeriods.Add(pauseInTStates - 35000); + } + else + { + pBlock.DataPeriods.Add(pauseInTStates); + pBlock.DataPeriods.Add(0); + } + + _datacorder.DataBlocks.Add(pBlock); + } + } + + #endregion + } + + public enum DataBlockType + { + Standard, + Turbo, + Pure + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs new file mode 100644 index 0000000000..f61fc49e0f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeCommand.cs @@ -0,0 +1,16 @@ + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents the possible commands that can be raised from each tape block + /// + public enum TapeCommand + { + NONE, + STOP_THE_TAPE, + STOP_THE_TAPE_48K, + BEGIN_GROUP, + END_GROUP, + SHOW_MESSAGE, + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs new file mode 100644 index 0000000000..adcc0eee97 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/TapeDataBlock.cs @@ -0,0 +1,288 @@ +using BizHawk.Common; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Represents a tape block + /// + public class TapeDataBlock + { + /// + /// Either the TZX block ID, or -1 in the case of non-tzx blocks + /// + private int _blockID = -1; + public int BlockID + { + get { return _blockID; } + set { + _blockID = value; + + if (MetaData == null) + MetaData = new Dictionary(); + + AddMetaData(BlockDescriptorTitle.Block_ID, value.ToString()); + } + } + + /// + /// The block type + /// + private BlockType _blockType; + public BlockType BlockDescription + { + get { return _blockType; } + set { + _blockType = value; + if (MetaData == null) + MetaData = new Dictionary(); + } + } + + /// + /// Byte array containing the raw block data + /// + private byte[] _blockData; + public byte[] BlockData + { + get { return _blockData; } + set { _blockData = value; } + } + + /// + /// An array of bytearray encoded strings (stored in this format for easy Bizhawk serialization) + /// Its basically tape information + /// + private byte[][] _tapeDescriptionData; + + /// + /// Returns the Tape Description Data in a human readable format + /// + public List TapeDescriptionData + { + get + { + List data = new List(); + + foreach (byte[] b in _tapeDescriptionData) + { + data.Add(Encoding.ASCII.GetString(b)); + } + + return data; + } + } + + + #region Block Meta Data + + /// + /// Dictionary of block related data + /// + public Dictionary MetaData { get; set; } + + /// + /// Adds a single metadata item to the Dictionary + /// + /// + /// + public void AddMetaData(BlockDescriptorTitle descriptor, string data) + { + // check whether entry already exists + bool check = MetaData.ContainsKey(descriptor); + if (check) + { + // already exists - update + MetaData[descriptor] = data; + } + else + { + // create new + MetaData.Add(descriptor, data); + } + } + + #endregion + + + + /// + /// List containing the pulse timing values + /// + public List DataPeriods = new List(); + + public bool InitialPulseLevel; + + /// + /// Command that is raised by this data block + /// (that may or may not need to be acted on) + /// + private TapeCommand _command = TapeCommand.NONE; + public TapeCommand Command + { + get { return _command; } + set { _command = value; } + } + + /// + /// The defined post-block pause + /// + private int _pauseInMS; + public int PauseInMS + { + get { return _pauseInMS; } + set { _pauseInMS = value; } + } + + + /// + /// Returns the data periods as an array + /// (primarily to aid in bizhawk state serialization) + /// + /// + public int[] GetDataPeriodsArray() + { + return DataPeriods.ToArray(); + } + + /// + /// Accepts an array of data periods and updates the DataPeriods list accordingly + /// (primarily to aid in bizhawk state serialization) + /// + /// + public void SetDataPeriodsArray(int[] periodArray) + { + DataPeriods = new List(); + + if (periodArray == null) + return; + + DataPeriods = periodArray.ToList(); + } + + /// + /// Bizhawk state serialization + /// + /// + public void SyncState(Serializer ser, int blockPosition) + { + ser.BeginSection("DataBlock" + blockPosition); + + ser.Sync("_blockID", ref _blockID); + //ser.SyncFixedString("_blockDescription", ref _blockDescription, 200); + ser.SyncEnum("_blockType", ref _blockType); + ser.Sync("_blockData", ref _blockData, true); + ser.SyncEnum("_command", ref _command); + + int[] tempArray = null; + + if (ser.IsWriter) + { + tempArray = GetDataPeriodsArray(); + ser.Sync("_periods", ref tempArray, true); + } + else + { + ser.Sync("_periods", ref tempArray, true); + SetDataPeriodsArray(tempArray); + } + + ser.EndSection(); + } + } + + /// + /// The types of TZX blocks + /// + public enum BlockType + { + Standard_Speed_Data_Block = 0x10, + Turbo_Speed_Data_Block = 0x11, + Pure_Tone = 0x12, + Pulse_Sequence = 0x13, + Pure_Data_Block = 0x14, + Direct_Recording = 0x15, + CSW_Recording = 0x18, + Generalized_Data_Block = 0x19, + Pause_or_Stop_the_Tape = 0x20, + Group_Start = 0x21, + Group_End = 0x22, + Jump_to_Block = 0x23, + Loop_Start = 0x24, + Loop_End = 0x25, + Call_Sequence = 0x26, + Return_From_Sequence = 0x27, + Select_Block = 0x28, + Stop_the_Tape_48K = 0x2A, + Set_Signal_Level = 0x2B, + Text_Description = 0x30, + Message_Block = 0x31, + Archive_Info = 0x32, + Hardware_Type = 0x33, + Custom_Info_Block = 0x35, + Glue_Block = 0x5A, + + // depreciated blocks + C64_ROM_Type_Data_Block = 0x16, + C64_Turbo_Tape_Data_Block = 0x17, + Emulation_Info = 0x34, + Snapshot_Block = 0x40, + + // unsupported / undetected + Unsupported, + + // PZX blocks + PZXT, + PULS, + DATA, + BRWS, + PAUS, + + // zxhawk proprietry + PAUSE_BLOCK, + + WAV_Recording + } + + + /// + /// Different title possibilities + /// + public enum BlockDescriptorTitle + { + Undefined, + Block_ID, + Program, + Data_Bytes, + Bytes, + + Pilot_Pulse_Length, + Pilot_Pulse_Count, + First_Sync_Length, + Second_Sync_Length, + Zero_Bit_Length, + One_Bit_Length, + Data_Length, + Bits_In_Last_Byte, + Pause_After_Data, + + Pulse_Length, + Pulse_Count, + + Text_Description, + Title, + Publisher, + Author, + Year, + Language, + Type, + Price, + Protection, + Origin, + Comments, + + Needs_Parsing + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/StreamHelper.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/StreamHelper.cs new file mode 100644 index 0000000000..5c24a66b61 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/StreamHelper.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// From https://archive.codeplex.com/?p=zxmak2 + /// + public static class StreamHelper + { + public static void Write(Stream stream, Int32 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, UInt32 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, Int16 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, UInt16 value) + { + byte[] data = BitConverter.GetBytes(value); + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, Byte value) + { + byte[] data = new byte[1]; + data[0] = value; + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, SByte value) + { + byte[] data = new byte[1]; + data[0] = (byte)value; + stream.Write(data, 0, data.Length); + } + + public static void Write(Stream stream, byte[] value) + { + stream.Write(value, 0, value.Length); + } + + + public static void Read(Stream stream, out Int32 value) + { + byte[] data = new byte[4]; + stream.Read(data, 0, data.Length); + //if (!BitConverter.IsLittleEndian) + // Array.Reverse(data); + value = BitConverter.ToInt32(data, 0); + } + + public static void Read(Stream stream, out UInt32 value) + { + byte[] data = new byte[4]; + stream.Read(data, 0, data.Length); + value = BitConverter.ToUInt32(data, 0); + } + + public static void Read(Stream stream, out Int16 value) + { + byte[] data = new byte[2]; + stream.Read(data, 0, data.Length); + value = BitConverter.ToInt16(data, 0); + } + + public static void Read(Stream stream, out UInt16 value) + { + byte[] data = new byte[2]; + stream.Read(data, 0, data.Length); + value = BitConverter.ToUInt16(data, 0); + } + + public static void Read(Stream stream, out Byte value) + { + byte[] data = new byte[1]; + stream.Read(data, 0, data.Length); + value = data[0]; + } + + public static void Read(Stream stream, out SByte value) + { + byte[] data = new byte[1]; + stream.Read(data, 0, data.Length); + value = (sbyte)data[0]; + } + + public static void Read(Stream stream, byte[] value) + { + stream.Read(value, 0, value.Length); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs new file mode 100644 index 0000000000..07c8d3e300 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavConverter.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// Reponsible for WAV format conversion + /// Based heavily on code from zxmak2: https://archive.codeplex.com/?p=zxmak2 + /// + public class WavConverter : MediaConverter + { + /// + /// The type of serializer + /// + private MediaConverterType _formatType = MediaConverterType.WAV; + public override MediaConverterType FormatType + { + get + { + return _formatType; + } + } + + /// + /// Signs whether this class can be used to read the data format + /// + public override bool IsReader { get { return true; } } + + /// + /// Signs whether this class can be used to write the data format + /// + public override bool IsWriter { get { return false; } } + + /// + /// Position counter + /// + private int _position = 0; + + #region Construction + + private DatacorderDevice _datacorder; + + public WavConverter(DatacorderDevice _tapeDevice) + { + _datacorder = _tapeDevice; + } + + #endregion + + /// + /// Returns TRUE if pzx header is detected + /// + /// + public override bool CheckType(byte[] data) + { + // WAV Header + + // check whether this is a valid wav format file by looking at the identifier in the header + string ident = Encoding.ASCII.GetString(data, 8, 4); + + if (ident.ToUpper() != "WAVE") + { + // this is not a valid WAV format file + return false; + } + else + { + return true; + } + } + + /// + /// DeSerialization method + /// + /// + public override void Read(byte[] data) + { + // clear existing tape blocks + _datacorder.DataBlocks.Clear(); + + // check whether this is a valid pzx format file by looking at the identifier in the header block + string ident = Encoding.ASCII.GetString(data, 8, 4); + + if (ident.ToUpper() != "WAVE") + { + // this is not a valid TZX format file + throw new Exception(this.GetType().ToString() + + "This is not a valid WAV format file"); + } + + _position = 0; + + MemoryStream stream = new MemoryStream(); + stream.Write(data, 0, data.Length); + stream.Position = 0; + + WavStreamReader reader = new WavStreamReader(stream); + + int rate = (69888 * 50) / reader.Header.sampleRate; + int smpCounter = 0; + int state = reader.ReadNext(); + + // create the single tape block + TapeDataBlock t = new TapeDataBlock(); + t.BlockDescription = BlockType.WAV_Recording; + t.BlockID = 0; + t.DataPeriods = new List(); + + for (int i = 0; i < reader.Count; i++) + { + int sample = reader.ReadNext(); + smpCounter++; + if ((state < 0 && sample < 0) || (state >= 0 && sample >= 0)) + continue; + t.DataPeriods.Add(smpCounter * rate); + smpCounter = 0; + state = sample; + } + + // add closing period + t.DataPeriods.Add((69888 * 50) / 10); + + // add to datacorder + _datacorder.DataBlocks.Add(t); + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavHeader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavHeader.cs new file mode 100644 index 0000000000..8a19b53ed9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavHeader.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// From https://archive.codeplex.com/?p=zxmak2 + /// + public class WavHeader + { + // RIFF chunk (12 bytes) + public Int32 chunkID; // "RIFF" + public Int32 fileSize; + public Int32 riffType; // "WAVE" + + // Format chunk (24 bytes) + public Int32 fmtID; // "fmt " + public Int32 fmtSize; + public Int16 fmtCode; + public Int16 channels; + public Int32 sampleRate; + public Int32 fmtAvgBPS; + public Int16 fmtBlockAlign; + public Int16 bitDepth; + public Int16 fmtExtraSize; + + // Data chunk + public Int32 dataID; // "data" + public Int32 dataSize; // The data size should be file size - 36 bytes. + + + public void Deserialize(Stream stream) + { + StreamHelper.Read(stream, out chunkID); + StreamHelper.Read(stream, out fileSize); + StreamHelper.Read(stream, out riffType); + if (chunkID != BitConverter.ToInt32(Encoding.ASCII.GetBytes("RIFF"), 0)) + { + throw new FormatException("Invalid WAV file header"); + } + if (riffType != BitConverter.ToInt32(Encoding.ASCII.GetBytes("WAVE"), 0)) + { + throw new FormatException(string.Format( + "Not supported RIFF type: '{0}'", + Encoding.ASCII.GetString(BitConverter.GetBytes(riffType)))); + } + Int32 chunkId; + Int32 chunkSize; + while (stream.Position < stream.Length) + { + StreamHelper.Read(stream, out chunkId); + StreamHelper.Read(stream, out chunkSize); + string strChunkId = Encoding.ASCII.GetString( + BitConverter.GetBytes(chunkId)); + if (strChunkId == "fmt ") + { + read_fmt(stream, chunkId, chunkSize); + } + else if (strChunkId == "data") + { + read_data(stream, chunkId, chunkSize); + break; + } + else + { + stream.Seek(chunkSize, SeekOrigin.Current); + } + } + if (fmtID != BitConverter.ToInt32(Encoding.ASCII.GetBytes("fmt "), 0)) + { + throw new FormatException("WAV format chunk not found"); + } + if (dataID != BitConverter.ToInt32(Encoding.ASCII.GetBytes("data"), 0)) + { + throw new FormatException("WAV data chunk not found"); + } + } + + private void read_data(Stream stream, int chunkId, int chunkSize) + { + dataID = chunkId; + dataSize = chunkSize; + } + + private void read_fmt(Stream stream, int chunkId, int chunkSize) + { + fmtID = chunkId; + fmtSize = chunkSize; + StreamHelper.Read(stream, out fmtCode); + StreamHelper.Read(stream, out channels); + StreamHelper.Read(stream, out sampleRate); + StreamHelper.Read(stream, out fmtAvgBPS); + StreamHelper.Read(stream, out fmtBlockAlign); + StreamHelper.Read(stream, out bitDepth); + if (fmtSize == 18) + { + // Read any extra values + StreamHelper.Read(stream, out fmtExtraSize); + stream.Seek(fmtExtraSize, SeekOrigin.Current); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavStreamReader.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavStreamReader.cs new file mode 100644 index 0000000000..042b276c7e --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/Media/Tape/WAV/WavStreamReader.cs @@ -0,0 +1,117 @@ +using System; +using System.IO; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// From https://archive.codeplex.com/?p=zxmak2 + /// + public class WavStreamReader + { + private Stream m_stream; + private WavHeader m_header = new WavHeader(); + + public WavStreamReader(Stream stream) + { + m_stream = stream; + m_header.Deserialize(stream); + } + + public WavHeader Header { get { return m_header; } } + + public int Count { get { return m_header.dataSize / m_header.fmtBlockAlign; } } + + public Int32 ReadNext() + { + // check - sample should be in PCM format + if (m_header.fmtCode != WAVE_FORMAT_PCM && + m_header.fmtCode != WAVE_FORMAT_IEEE_FLOAT) + { + throw new FormatException(string.Format( + "Not supported audio format: fmtCode={0}, bitDepth={1}", + m_header.fmtCode, + m_header.bitDepth)); + } + byte[] data = new byte[m_header.fmtBlockAlign]; + m_stream.Read(data, 0, data.Length); + if (m_header.fmtCode == WAVE_FORMAT_PCM) + { + // use first channel only + if (m_header.bitDepth == 8) + return getSamplePcm8(data, 0, 0); + if (m_header.bitDepth == 16) + return getSamplePcm16(data, 0, 0); + if (m_header.bitDepth == 24) + return getSamplePcm24(data, 0, 0); + if (m_header.bitDepth == 32) + return getSamplePcm32(data, 0, 0); + } + else if (m_header.fmtCode == WAVE_FORMAT_IEEE_FLOAT) + { + // use first channel only + if (m_header.bitDepth == 32) + return getSampleFloat32(data, 0, 0); + if (m_header.bitDepth == 64) + return getSampleFloat64(data, 0, 0); + } + throw new NotSupportedException(string.Format( + "Not supported audio format ({0}/{1} bit)", + m_header.fmtCode == WAVE_FORMAT_PCM ? "PCM" : "FLOAT", + m_header.bitDepth)); + } + + private Int32 getSamplePcm8(byte[] bufferRaw, int offset, int channel) + { + return bufferRaw[offset + channel] - 128; + } + + private Int32 getSamplePcm16(byte[] bufferRaw, int offset, int channel) + { + return BitConverter.ToInt16(bufferRaw, offset + 2 * channel); + } + + private Int32 getSamplePcm24(byte[] bufferRaw, int offset, int channel) + { + Int32 result; + int subOffset = offset + channel * 3; + if (BitConverter.IsLittleEndian) + { + result = ((sbyte)bufferRaw[2 + subOffset]) * 0x10000; + result |= bufferRaw[1 + subOffset] * 0x100; + result |= bufferRaw[0 + subOffset]; + } + else + { + result = ((sbyte)bufferRaw[0 + subOffset]) * 0x10000; + result |= bufferRaw[1 + subOffset] * 0x100; + result |= bufferRaw[2 + subOffset]; + } + return result; + } + + private Int32 getSamplePcm32(byte[] bufferRaw, int offset, int channel) + { + return BitConverter.ToInt32(bufferRaw, offset + 4 * channel); + } + + private Int32 getSampleFloat32(byte[] data, int offset, int channel) + { + float fSample = BitConverter.ToSingle(data, offset + 4 * channel); + // convert to 32 bit integer + return (Int32)(fSample * Int32.MaxValue); + } + + private Int32 getSampleFloat64(byte[] data, int offset, int channel) + { + double fSample = BitConverter.ToDouble(data, offset + 8 * channel); + // convert to 32 bit integer + return (Int32)(fSample * Int32.MaxValue); + } + + private const int WAVE_FORMAT_PCM = 1; /* PCM */ + private const int WAVE_FORMAT_IEEE_FLOAT = 3; /* IEEE float */ + private const int WAVE_FORMAT_ALAW = 6; /* 8-bit ITU-T G.711 A-law */ + private const int WAVE_FORMAT_MULAW = 7; /* 8-bit ITU-T G.711 µ-law */ + private const int WAVE_FORMAT_EXTENSIBLE = 0xFFFE; /* Determined by SubFormat */ + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs new file mode 100644 index 0000000000..55df2265f9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/SoundProviderMixer.cs @@ -0,0 +1,213 @@ +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// My attempt at mixing multiple ISoundProvider sources together and outputting another ISoundProvider + /// Currently only supports SyncSoundMode.Sync + /// Attached ISoundProvider sources must already be stereo 44.1khz and ideally sound buffers should be the same length (882) + /// (if not, only 882 samples of their buffer will be used) + /// + internal sealed class SoundProviderMixer : ISoundProvider + { + private class Provider + { + public ISoundProvider SoundProvider { get; set; } + public string ProviderDescription { get; set; } + public int MaxVolume { get; set; } + public short[] Buffer { get; set; } + public int NSamp { get; set; } + } + + private bool _stereo = true; + public bool Stereo + { + get { return _stereo; } + set { _stereo = value; } + } + + private readonly List SoundProviders; + + public SoundProviderMixer(params ISoundProvider[] soundProviders) + { + SoundProviders = new List(); + + foreach (var s in soundProviders) + { + SoundProviders.Add(new Provider + { + SoundProvider = s, + MaxVolume = short.MaxValue, + }); + } + + EqualizeVolumes(); + } + + public SoundProviderMixer(short maxVolume, string description, params ISoundProvider[] soundProviders) + { + SoundProviders = new List(); + + foreach (var s in soundProviders) + { + SoundProviders.Add(new Provider + { + SoundProvider = s, + MaxVolume = maxVolume, + ProviderDescription = description + }); + } + + EqualizeVolumes(); + } + + public void AddSource(ISoundProvider source, string description) + { + SoundProviders.Add(new Provider + { + SoundProvider = source, + MaxVolume = short.MaxValue, + ProviderDescription = description + }); + + EqualizeVolumes(); + } + + public void AddSource(ISoundProvider source, short maxVolume, string description) + { + SoundProviders.Add(new Provider + { + SoundProvider = source, + MaxVolume = maxVolume, + ProviderDescription = description + }); + + EqualizeVolumes(); + } + + public void DisableSource(ISoundProvider source) + { + var sp = SoundProviders.Where(a => a.SoundProvider == source); + if (sp.Count() == 1) + SoundProviders.Remove(sp.First()); + else if (sp.Count() > 1) + foreach (var s in sp) + SoundProviders.Remove(s); + + EqualizeVolumes(); + } + + public void EqualizeVolumes() + { + if (SoundProviders.Count < 1) + return; + + int eachVolume = short.MaxValue / SoundProviders.Count; + foreach (var source in SoundProviders) + { + source.MaxVolume = eachVolume; + } + } + + #region ISoundProvider + + public bool CanProvideAsync => false; + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + throw new InvalidOperationException("Only Sync mode is supported."); + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + foreach (var soundSource in SoundProviders) + { + soundSource.SoundProvider.DiscardSamples(); + } + } + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + samples = null; + nsamp = 0; + + // get samples from all the providers + foreach (var sp in SoundProviders) + { + int sampCount; + short[] samp; + sp.SoundProvider.GetSamplesSync(out samp, out sampCount); + sp.NSamp = sampCount; + sp.Buffer = samp; + } + + // are all the sample lengths the same? + var firstEntry = SoundProviders.First(); + bool sameCount = SoundProviders.All(s => s.NSamp == firstEntry.NSamp); + + if (!sameCount) + { + // this is a bit hacky, really all ISoundProviders should be supplying 44100 with 882 samples per frame. + // we will make sure this happens (no matter how it sounds) + if (SoundProviders.Count > 1) + { + for (int i = 0; i < SoundProviders.Count; i++) + { + int ns = SoundProviders[i].NSamp; + short[] buff = new short[882 * 2]; + + for (int b = 0; b < 882 * 2; b++) + { + if (b == SoundProviders[i].Buffer.Length - 1) + { + // end of source buffer + break; + } + + buff[b] = SoundProviders[i].Buffer[b]; + } + + // save back to the soundprovider + SoundProviders[i].NSamp = 882; + SoundProviders[i].Buffer = buff; + } + } + else + { + // just process what we have as-is + } + } + + // mix the soundproviders together + nsamp = 882; + samples = new short[nsamp * 2]; + + for (int i = 0; i < samples.Length; i++) + { + short sectorVal = 0; + foreach (var sp in SoundProviders) + { + if (sp.Buffer[i] > sp.MaxVolume) + sectorVal += (short)sp.MaxVolume; + else + sectorVal += sp.Buffer[i]; + } + + samples[i] = sectorVal; + } + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs new file mode 100644 index 0000000000..749fc03830 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Controllers.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * Controllers * + /// + public partial class ZXSpectrum + { + /// + /// The one ZX Hawk ControllerDefinition + /// + public ControllerDefinition ZXSpectrumControllerDefinition + { + get + { + ControllerDefinition definition = new ControllerDefinition(); + definition.Name = "ZXSpectrum Controller"; + + // joysticks + List joys1 = new List + { + // P1 Joystick + "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button", + }; + + foreach (var s in joys1) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "J1 (" + ((ZXSpectrumSyncSettings)SyncSettings as ZXSpectrumSyncSettings).JoystickType1.ToString() + ")"; + } + + List joys2 = new List + { + // P2 Joystick + "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button", + }; + + foreach (var s in joys2) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "J2 (" + ((ZXSpectrumSyncSettings)SyncSettings as ZXSpectrumSyncSettings).JoystickType2.ToString() + ")"; + } + + List joys3 = new List + { + // P3 Joystick + "P3 Up", "P3 Down", "P3 Left", "P3 Right", "P3 Button", + }; + + foreach (var s in joys3) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "J3 (" + ((ZXSpectrumSyncSettings)SyncSettings as ZXSpectrumSyncSettings).JoystickType3.ToString() + ")"; + } + + // keyboard + List keys = new List + { + /// Controller mapping includes all keyboard keys from the following models: + /// https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/ZXSpectrum48k.jpg/1200px-ZXSpectrum48k.jpg + /// https://upload.wikimedia.org/wikipedia/commons/c/ca/ZX_Spectrum%2B.jpg + + // Keyboard - row 1 + "Key True Video", "Key Inv Video", "Key 1", "Key 2", "Key 3", "Key 4", "Key 5", "Key 6", "Key 7", "Key 8", "Key 9", "Key 0", "Key Break", + // Keyboard - row 2 + "Key Delete", "Key Graph", "Key Q", "Key W", "Key E", "Key R", "Key T", "Key Y", "Key U", "Key I", "Key O", "Key P", + // Keyboard - row 3 + "Key Extend Mode", "Key Edit", "Key A", "Key S", "Key D", "Key F", "Key G", "Key H", "Key J", "Key K", "Key L", "Key Return", + // Keyboard - row 4 + "Key Caps Shift", "Key Caps Lock", "Key Z", "Key X", "Key C", "Key V", "Key B", "Key N", "Key M", "Key Period", + // Keyboard - row 5 + "Key Symbol Shift", "Key Semi-Colon", "Key Quote", "Key Left Cursor", "Key Right Cursor", "Key Space", "Key Up Cursor", "Key Down Cursor", "Key Comma", + }; + + foreach (var s in keys) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Keyboard"; + } + + // Power functions + List power = new List + { + // Power functions + "Reset", "Power" + }; + + foreach (var s in power) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Power"; + } + + // Datacorder (tape device) + List tape = new List + { + // Tape functions + "Play Tape", "Stop Tape", "RTZ Tape", "Record Tape", "Insert Next Tape", + "Insert Previous Tape", "Next Tape Block", "Prev Tape Block", "Get Tape Status" + }; + + foreach (var s in tape) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Datacorder"; + } + + // Datacorder (tape device) + List disk = new List + { + // Tape functions + "Insert Next Disk", "Insert Previous Disk", /*"Eject Current Disk",*/ "Get Disk Status" + }; + + foreach (var s in disk) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "+3 Disk Drive"; + } + + return definition; + } + } + } + + /// + /// The possible joystick types + /// + public enum JoystickType + { + NULL, + Kempston, + SinclairLEFT, + SinclairRIGHT, + Cursor + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs new file mode 100644 index 0000000000..ccb68cf475 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IDebuggable.cs @@ -0,0 +1,149 @@ +using System; +using System.Collections.Generic; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * IDebugggable * + /// + public partial class ZXSpectrum : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + return new Dictionary + { + ["A"] = _cpu.Regs[_cpu.A], + ["AF"] = _cpu.Regs[_cpu.F] + (_cpu.Regs[_cpu.A] << 8), + ["B"] = _cpu.Regs[_cpu.B], + ["BC"] = _cpu.Regs[_cpu.C] + (_cpu.Regs[_cpu.B] << 8), + ["C"] = _cpu.Regs[_cpu.C], + ["D"] = _cpu.Regs[_cpu.D], + ["DE"] = _cpu.Regs[_cpu.E] + (_cpu.Regs[_cpu.D] << 8), + ["E"] = _cpu.Regs[_cpu.E], + ["F"] = _cpu.Regs[_cpu.F], + ["H"] = _cpu.Regs[_cpu.H], + ["HL"] = _cpu.Regs[_cpu.L] + (_cpu.Regs[_cpu.H] << 8), + ["I"] = _cpu.Regs[_cpu.I], + ["IX"] = _cpu.Regs[_cpu.Ixl] + (_cpu.Regs[_cpu.Ixh] << 8), + ["IY"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["L"] = _cpu.Regs[_cpu.L], + ["PC"] = _cpu.Regs[_cpu.PCl] + (_cpu.Regs[_cpu.PCh] << 8), + ["R"] = _cpu.Regs[_cpu.R], + ["Shadow AF"] = _cpu.Regs[_cpu.F_s] + (_cpu.Regs[_cpu.A_s] << 8), + ["Shadow BC"] = _cpu.Regs[_cpu.C_s] + (_cpu.Regs[_cpu.B_s] << 8), + ["Shadow DE"] = _cpu.Regs[_cpu.E_s] + (_cpu.Regs[_cpu.D_s] << 8), + ["Shadow HL"] = _cpu.Regs[_cpu.L_s] + (_cpu.Regs[_cpu.H_s] << 8), + ["SP"] = _cpu.Regs[_cpu.Iyl] + (_cpu.Regs[_cpu.Iyh] << 8), + ["Flag C"] = _cpu.FlagC, + ["Flag N"] = _cpu.FlagN, + ["Flag P/V"] = _cpu.FlagP, + ["Flag 3rd"] = _cpu.Flag3, + ["Flag H"] = _cpu.FlagH, + ["Flag 5th"] = _cpu.Flag5, + ["Flag Z"] = _cpu.FlagZ, + ["Flag S"] = _cpu.FlagS + }; + } + + public void SetCpuRegister(string register, int value) + { + switch (register) + { + default: + throw new InvalidOperationException(); + case "A": + _cpu.Regs[_cpu.A] = (ushort)value; + break; + case "AF": + _cpu.Regs[_cpu.F] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A] = (ushort)(value & 0xFF00); + break; + case "B": + _cpu.Regs[_cpu.B] = (ushort)value; + break; + case "BC": + _cpu.Regs[_cpu.C] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B] = (ushort)(value & 0xFF00); + break; + case "C": + _cpu.Regs[_cpu.C] = (ushort)value; + break; + case "D": + _cpu.Regs[_cpu.D] = (ushort)value; + break; + case "DE": + _cpu.Regs[_cpu.E] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D] = (ushort)(value & 0xFF00); + break; + case "E": + _cpu.Regs[_cpu.E] = (ushort)value; + break; + case "F": + _cpu.Regs[_cpu.F] = (ushort)value; + break; + case "H": + _cpu.Regs[_cpu.H] = (ushort)value; + break; + case "HL": + _cpu.Regs[_cpu.L] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H] = (ushort)(value & 0xFF00); + break; + case "I": + _cpu.Regs[_cpu.I] = (ushort)value; + break; + case "IX": + _cpu.Regs[_cpu.Ixl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Ixh] = (ushort)(value & 0xFF00); + break; + case "IY": + _cpu.Regs[_cpu.Iyl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.Iyh] = (ushort)(value & 0xFF00); + break; + case "L": + _cpu.Regs[_cpu.L] = (ushort)value; + break; + case "PC": + _cpu.Regs[_cpu.PCl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.PCh] = (ushort)(value & 0xFF00); + break; + case "R": + _cpu.Regs[_cpu.R] = (ushort)value; + break; + case "Shadow AF": + _cpu.Regs[_cpu.F_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.A_s] = (ushort)(value & 0xFF00); + break; + case "Shadow BC": + _cpu.Regs[_cpu.C_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.B_s] = (ushort)(value & 0xFF00); + break; + case "Shadow DE": + _cpu.Regs[_cpu.E_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.D_s] = (ushort)(value & 0xFF00); + break; + case "Shadow HL": + _cpu.Regs[_cpu.L_s] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.H_s] = (ushort)(value & 0xFF00); + break; + case "SP": + _cpu.Regs[_cpu.SPl] = (ushort)(value & 0xFF); + _cpu.Regs[_cpu.SPh] = (ushort)(value & 0xFF00); + break; + } + } + + public IMemoryCallbackSystem MemoryCallbacks { get; } + + public bool CanStep(StepType type) => false; + + [FeatureNotImplemented] + public void Step(StepType type) + { + throw new NotImplementedException(); + } + + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs new file mode 100644 index 0000000000..1276521373 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IEmulator.cs @@ -0,0 +1,83 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * IEmulator * + /// + public partial class ZXSpectrum : IEmulator + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition { get; set; } + + public void FrameAdvance(IController controller, bool render, bool renderSound) + { + _controller = controller; + + bool ren = render; + bool renSound = renderSound; + + if (DeterministicEmulation) + { + ren = true; + renSound = true; + } + + _isLag = true; + + if (_tracer.Enabled) + { + _cpu.TraceCallback = s => _tracer.Put(s); + } + else + { + _cpu.TraceCallback = null; + } + + _machine.ExecuteFrame(ren, renSound); + + if (_isLag) + { + _lagCount++; + } + } + + public int Frame + { + get + { + if (_machine == null) + return 0; + else + return _machine.FrameCount; + } + } + + public string SystemId => "ZXSpectrum"; + + private bool deterministicEmulation; + public bool DeterministicEmulation + { + get { return deterministicEmulation; } + } + + public void ResetCounters() + { + _machine.FrameCount = 0; + _lagCount = 0; + _isLag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + if (_machine != null) + { + _machine = null; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs new file mode 100644 index 0000000000..6861bb50ff --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IInputPollable.cs @@ -0,0 +1,29 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * IInputPollable * + /// + public partial class ZXSpectrum : IInputPollable + { + public int LagCount + { + get { return _lagCount; } + set { _lagCount = value; } + } + + public bool IsLagFrame + { + get { return _isLag; } + set { _isLag = value; } + } + + public IInputCallbackSystem InputCallbacks { get; } + + private int _lagCount = 0; + private bool _isLag = false; + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs new file mode 100644 index 0000000000..47e9bd83be --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IMemoryDomains.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * Memory Domains * + /// + public partial class ZXSpectrum + { + internal IMemoryDomains memoryDomains; + private readonly Dictionary _byteArrayDomains = new Dictionary(); + private bool _memoryDomainsInit = false; + + private void SetupMemoryDomains() + { + var domains = new List + { + new MemoryDomainDelegate("System Bus", 0x10000, MemoryDomain.Endian.Little, + (addr) => + { + if (addr < 0 || addr >= 65536) + { + throw new ArgumentOutOfRangeException(); + } + return _machine.ReadBus((ushort)addr); + }, + (addr, value) => + { + if (addr < 0 || addr >= 65536) + { + throw new ArgumentOutOfRangeException(); + } + + _machine.WriteBus((ushort)addr, value); + }, 1) + }; + + SyncAllByteArrayDomains(); + + memoryDomains = new MemoryDomainList(_byteArrayDomains.Values.Concat(domains).ToList()); + (ServiceProvider as BasicServiceProvider).Register(memoryDomains); + + _memoryDomainsInit = true; + } + + private void SyncAllByteArrayDomains() + { + SyncByteArrayDomain("ROM0", _machine.ROM0); + SyncByteArrayDomain("ROM1", _machine.ROM1); + SyncByteArrayDomain("ROM2", _machine.ROM2); + SyncByteArrayDomain("ROM3", _machine.ROM3); + SyncByteArrayDomain("RAM0", _machine.RAM0); + SyncByteArrayDomain("RAM1", _machine.RAM1); + SyncByteArrayDomain("RAM2", _machine.RAM2); + SyncByteArrayDomain("RAM3", _machine.RAM3); + SyncByteArrayDomain("RAM4", _machine.RAM4); + SyncByteArrayDomain("RAM5", _machine.RAM5); + SyncByteArrayDomain("RAM6", _machine.RAM6); + SyncByteArrayDomain("RAM7", _machine.RAM7); + } + + private void SyncByteArrayDomain(string name, byte[] data) + { + + if (_memoryDomainsInit || _byteArrayDomains.ContainsKey(name)) + { + var m = _byteArrayDomains[name]; + m.Data = data; + } + else + { + var m = new MemoryDomainByteArray(name, MemoryDomain.Endian.Little, data, true, 1); + _byteArrayDomains.Add(name, m); + } + + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs new file mode 100644 index 0000000000..59bbe6e9c4 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.ISettable.cs @@ -0,0 +1,366 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using System.ComponentModel; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * ISettable * + /// + public partial class ZXSpectrum : ISettable + { + internal ZXSpectrumSettings Settings = new ZXSpectrumSettings(); + internal ZXSpectrumSyncSettings SyncSettings = new ZXSpectrumSyncSettings(); + + public ZXSpectrumSettings GetSettings() + { + return Settings.Clone(); + } + + public ZXSpectrumSyncSettings GetSyncSettings() + { + return SyncSettings.Clone(); + } + + public bool PutSettings(ZXSpectrumSettings o) + { + // restore user settings to devices + if (_machine != null && _machine.AYDevice != null) + { + ((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = o.AYPanConfig; + _machine.AYDevice.Volume = o.AYVolume; + } + if (_machine != null && _machine.BuzzerDevice != null) + { + ((Beeper)_machine.BuzzerDevice as Beeper).Volume = o.EarVolume; + } + if (_machine != null && _machine.TapeBuzzer != null) + { + ((Beeper)_machine.TapeBuzzer as Beeper).Volume = o.TapeVolume; + } + + Settings = o; + + return false; + } + + public bool PutSyncSettings(ZXSpectrumSyncSettings o) + { + bool ret = ZXSpectrumSyncSettings.NeedsReboot(SyncSettings, o); + SyncSettings = o; + return ret; + } + + public class ZXSpectrumSettings + { + [DisplayName("AY-3-8912 Panning Config")] + [Description("Set the PSG panning configuration.\nThe chip has 3 audio channels that can be outputed in different configurations")] + [DefaultValue(AY38912.AYPanConfig.ABC)] + public AY38912.AYPanConfig AYPanConfig { get; set; } + + [DisplayName("Core OSD Message Verbosity")] + [Description("Full: Display all GUI messages\nMedium: Display only emulator/device generated messages\nNone: Show no messages")] + [DefaultValue(OSDVerbosity.Medium)] + public OSDVerbosity OSDMessageVerbosity { get; set; } + + [DisplayName("Tape Loading Volume")] + [Description("The buzzer volume when the tape is playing")] + [DefaultValue(50)] + public int TapeVolume { get; set; } + + [DisplayName("Ear (buzzer output) Volume")] + [Description("The buzzer volume when sound is being generated by the spectrum")] + [DefaultValue(90)] + public int EarVolume { get; set; } + + [DisplayName("AY-3-8912 Volume")] + [Description("The AY chip volume")] + [DefaultValue(75)] + public int AYVolume { get; set; } + + + public ZXSpectrumSettings Clone() + { + return (ZXSpectrumSettings)MemberwiseClone(); + } + + public ZXSpectrumSettings() + { + BizHawk.Common.SettingsUtil.SetDefaultValues(this); + } + } + + public class ZXSpectrumSyncSettings + { + [DisplayName("Deterministic Emulation")] + [Description("If true, the core agrees to behave in a completely deterministic manner")] + [DefaultValue(true)] + public bool DeterministicEmulation { get; set; } + + [DisplayName("Spectrum model")] + [Description("The model of spectrum to be emulated")] + [DefaultValue(MachineType.ZXSpectrum48)] + public MachineType MachineType { get; set; } + + [DisplayName("Border type")] + [Description("Select how to show the border area")] + [DefaultValue(BorderType.Full)] + public BorderType BorderType { get; set; } + + [DisplayName("Tape Load Speed")] + [Description("Select how fast the spectrum loads the game from tape")] + [DefaultValue(TapeLoadSpeed.Accurate)] + public TapeLoadSpeed TapeLoadSpeed { get; set; } + + [DisplayName("Joystick 1")] + [Description("The emulated joystick assigned to P1 (SHOULD BE UNIQUE TYPE!)")] + [DefaultValue(JoystickType.Kempston)] + public JoystickType JoystickType1 { get; set; } + + [DisplayName("Joystick 2")] + [Description("The emulated joystick assigned to P2 (SHOULD BE UNIQUE TYPE!)")] + [DefaultValue(JoystickType.SinclairLEFT)] + public JoystickType JoystickType2 { get; set; } + + [DisplayName("Joystick 3")] + [Description("The emulated joystick assigned to P3 (SHOULD BE UNIQUE TYPE!)")] + [DefaultValue(JoystickType.SinclairRIGHT)] + public JoystickType JoystickType3 { get; set; } + + [DisplayName("Auto-load/stop tape")] + [Description("Auto or manual tape operation. Auto will attempt to detect CPU tape traps and automatically Stop/Start the tape")] + [DefaultValue(true)] + public bool AutoLoadTape { get; set; } + + + public ZXSpectrumSyncSettings Clone() + { + return (ZXSpectrumSyncSettings)MemberwiseClone(); + } + + public ZXSpectrumSyncSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public static bool NeedsReboot(ZXSpectrumSyncSettings x, ZXSpectrumSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + + /// + /// Verbosity of the ZXHawk generated OSD messages + /// + public enum OSDVerbosity + { + /// + /// Show all OSD messages + /// + Full, + /// + /// Only show machine/device generated messages + /// + Medium, + /// + /// No core-driven OSD messages + /// + None + } + + /// + /// The size of the Spectrum border + /// + public enum BorderType + { + /// + /// How it was originally back in the day + /// + Full, + + /// + /// All borders 24px + /// + Medium, + + /// + /// All borders 10px + /// + Small, + + /// + /// No border at all + /// + None, + + /// + /// Top and bottom border removed so that the result is *almost* 16:9 + /// + Widescreen, + } + + /// + /// The speed at which the tape is loaded + /// NOT IN USE YET + /// + public enum TapeLoadSpeed + { + Accurate, + //Fast, + //Fastest + } + } + + /// + /// Provides information on each emulated machine + /// + public class ZXMachineMetaData + { + public MachineType MachineType { get; set; } + public string Name { get; set; } + public string Description { get; set; } + public string Released { get; set; } + public string CPU { get; set; } + public string Memory { get; set; } + public string Video { get; set; } + public string Audio { get; set; } + public string Media { get; set; } + public string OtherMisc { get; set; } + + + public static ZXMachineMetaData GetMetaObject(MachineType type) + { + ZXMachineMetaData m = new ZXMachineMetaData(); + m.MachineType = type; + + switch (type) + { + case MachineType.ZXSpectrum16: + m.Name = "Sinclair ZX Spectrum 16K"; + m.Description = "The original ZX Spectrum 16K RAM version. Aside from available RAM this machine is technically identical to the 48K machine that was released at the same time. "; + m.Description += "Due to the small amount of RAM, very few games were actually made to run on this model."; + m.Released = "1982"; + m.CPU = "Zilog Z80A @ 3.5MHz"; + m.Memory = "16KB ROM / 16KB RAM"; + m.Video = "ULA @ 7MHz - PAL (50.08Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) - Internal Speaker"; + m.Media = "Cassette Tape (via 3rd party external tape player)"; + break; + case MachineType.ZXSpectrum48: + m.Name = "Sinclair ZX Spectrum 48K / 48K+"; + m.Description = "The original ZX Spectrum 48K RAM version. 2 years later a 'plus' version was released that had a better keyboard. "; + m.Description += "Electronically both the 48K and + are identical, so ZXHawk treats them as the same emulated machine. "; + m.Description += "These machines dominated the UK 8-bit home computer market throughout the 1980's so most non-128k only games are compatible."; + m.Released = "1982 (48K) / 1984 (48K+)"; + m.CPU = "Zilog Z80A @ 3.5MHz"; + m.Memory = "16KB ROM / 48KB RAM"; + m.Video = "ULA @ 7MHz - PAL (50.08Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) - Internal Speaker"; + m.Media = "Cassette Tape (via 3rd party external tape player)"; + break; + case MachineType.ZXSpectrum128: + m.Name = "Sinclair ZX Spectrum 128"; + m.Description = "The first Spectrum 128K machine released in Spain in 1985 and later UK in 1986. "; + m.Description += "With an updated ROM and new memory paging system to work around the Z80's 16-bit address bus. "; + m.Description += "The 128 shipped with a copy of the 48k ROM (that is paged in when required) and a new startup menu with the option of dropping into a '48k mode'. "; + m.Description += "Even so, there were some compatibility issues with older Spectrum games that were written to utilise some of the previous model's intricacies. "; + m.Description += "Many games released after 1985 supported the new AY-3-8912 PSG chip making for far superior audio. The extra memory also enabled many games to be loaded in all at once (rather than loading each level from tape when needed)."; + m.Released = "1985 / 1986"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "32KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "Cassette Tape (via 3rd party external tape player)"; + break; + case MachineType.ZXSpectrum128Plus2: + m.Name = "Sinclair ZX Spectrum +2"; + m.Description = "The first Sinclair Spectrum 128K machine that was released after Amstrad purchased Sinclair in 1986. "; + m.Description += "Electronically it was almost identical to the 128, but with the addition of a built-in tape deck and 2 Sinclair Joystick ports."; + m.Released = "1986"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "32KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "Cassette Tape (via built-in Datacorder)"; + break; + case MachineType.ZXSpectrum128Plus2a: + m.Name = "Sinclair ZX Spectrum +2a"; + m.Description = "The +2a looks almost identical to the +2 but is a variant of the +3 machine that was released the same year (except with the same built-in datacorder that the +2 had rather than a floppy drive). "; + m.Description += "Memory paging again changed significantly and this (along with memory contention timing changes) caused more compatibility issues with some older games. "; + m.Description += "Although functionally identical to the +3, it does not contain floppy disk controller."; + m.Released = "1987"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "64KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "Cassette Tape (via built-in Datacorder)"; + break; + case MachineType.ZXSpectrum128Plus3: + m.Name = "Sinclair ZX Spectrum +3"; + m.Description = "Amstrad released the +3 the same year as the +2a, but it featured a built-in floppy drive rather than a datacorder. An external cassette player could still be connected though as in the older 48k models. "; + m.Description += "Memory paging again changed significantly and this (along with memory contention timing changes) caused more compatibility issues with some older games. "; + m.Description += "Currently ZXHawk does not emulate the floppy drive or floppy controller so the machine reports as a +2a on boot."; + m.Released = "1987"; + m.CPU = "Zilog Z80A @ 3.5469 MHz"; + m.Memory = "64KB ROM / 128KB RAM"; + m.Video = "ULA @ 7.0938MHz - PAL (50.01Hz Interrupt)"; + m.Audio = "Beeper (HW 1ch. / 10oct.) & General Instruments AY-3-8912 PSG (3ch) - RF Output"; + m.Media = "3\" Floppy Disk (via built-in Floppy Drive)"; + break; + } + return m; + } + + public static string GetMetaString(MachineType type) + { + var m = GetMetaObject(type); + + StringBuilder sb = new StringBuilder(); + + sb.Append(m.Name); + sb.Append("\n"); + sb.Append("-----------------------------------------------------------------\n"); + // Release + sb.Append("Released:"); + sb.Append(" "); + sb.Append(m.Released); + sb.Append("\n"); + // CPU + sb.Append("CPU:"); + sb.Append(" "); + sb.Append(m.CPU); + sb.Append("\n"); + // Memory + sb.Append("Memory:"); + sb.Append(" "); + sb.Append(m.Memory); + sb.Append("\n"); + // Video + sb.Append("Video:"); + sb.Append(" "); + sb.Append(m.Video); + sb.Append("\n"); + // Audio + sb.Append("Audio:"); + sb.Append(" "); + sb.Append(m.Audio); + sb.Append("\n"); + // Audio + sb.Append("Media:"); + sb.Append(" "); + sb.Append(m.Media); + sb.Append("\n"); + + sb.Append("-----------------------------------------------------------------\n"); + // description + sb.Append(m.Description); + if (m.OtherMisc != null) + sb.Append("\n" + m.OtherMisc); + + return sb.ToString(); + + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs new file mode 100644 index 0000000000..74ab460903 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.IStatable.cs @@ -0,0 +1,100 @@ +using System.IO; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * IStatable * + /// + public partial class ZXSpectrum : IStatable + { + public bool BinarySaveStatesPreferred + { + get { return true; } + } + + public void SaveStateText(TextWriter writer) + { + SyncState(new Serializer(writer)); + } + + public void LoadStateText(TextReader reader) + { + SyncState(new Serializer(reader)); + } + + public void SaveStateBinary(BinaryWriter bw) + { + SyncState(new Serializer(bw)); + } + + public void LoadStateBinary(BinaryReader br) + { + SyncState(new Serializer(br)); + } + + public byte[] SaveStateBinary() + { + MemoryStream ms = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + return ms.ToArray(); + } + + private void SyncState(Serializer ser) + { + byte[] core = null; + if (ser.IsWriter) + { + var ms = new MemoryStream(); + ms.Close(); + core = ms.ToArray(); + } + + if (ser.IsWriter) + { + ser.SyncEnum("_machineType", ref _machineType); + + _cpu.SyncState(ser); + ser.BeginSection("ZXSpectrum"); + _machine.SyncState(ser); + ser.Sync("Frame", ref _machine.FrameCount); + ser.Sync("LagCount", ref _lagCount); + ser.Sync("IsLag", ref _isLag); + ser.EndSection(); + } + + if (ser.IsReader) + { + var tmpM = _machineType; + ser.SyncEnum("_machineType", ref _machineType); + if (tmpM != _machineType && _machineType.ToString() != "72") + { + string msg = "SAVESTATE FAILED TO LOAD!!\n\n"; + msg += "Current Configuration: " + tmpM.ToString(); + msg += "\n"; + msg += "Saved Configuration: " + _machineType.ToString(); + msg += "\n\n"; + msg += "If you wish to load this SaveState ensure that you have the correct machine configuration selected, reboot the core, then try again."; + CoreComm.ShowMessage(msg); + _machineType = tmpM; + } + else + { + _cpu.SyncState(ser); + ser.BeginSection("ZXSpectrum"); + _machine.SyncState(ser); + ser.Sync("Frame", ref _machine.FrameCount); + ser.Sync("LagCount", ref _lagCount); + ser.Sync("IsLag", ref _isLag); + ser.EndSection(); + + SyncAllByteArrayDomains(); + } + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs new file mode 100644 index 0000000000..4a3a20d6ca --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Messaging.cs @@ -0,0 +1,494 @@ +using System; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * Handles all messaging (OSD) operations * + /// + public partial class ZXSpectrum + { + /// + /// Writes a message to the OSD + /// + /// + /// + public void SendMessage(string message, MessageCategory category) + { + if (!CheckMessageSettings(category)) + return; + + StringBuilder sb = new StringBuilder(); + + switch (category) + { + case MessageCategory.Tape: + sb.Append("DATACORDER: "); + sb.Append(message); + break; + case MessageCategory.Input: + sb.Append("INPUT DETECTED: "); + sb.Append(message); + break; + case MessageCategory.Disk: + sb.Append("DISK DRIVE: "); + sb.Append(message); + break; + case MessageCategory.Emulator: + case MessageCategory.Misc: + sb.Append("ZXHAWK: "); + sb.Append(message); + break; + } + + CoreComm.Notify(sb.ToString()); + } + + #region Input Message Methods + + /// + /// Called when certain input presses are detected + /// + /// + public void OSD_FireInputMessage(string input) + { + StringBuilder sb = new StringBuilder(); + sb.Append(input); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Input); + } + + #endregion + + #region DiskDevice Message Methods + + /// + /// Disk message that is fired on core init + /// + public void OSD_DiskInit() + { + StringBuilder sb = new StringBuilder(); + if (_machine.diskImages != null && _machine.UPDDiskDevice != null) + { + sb.Append("Disk Media Imported (count: " + _machine.diskImages.Count() + ")"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator); + } + } + + /// + /// Disk message that is fired when a new disk is inserted into the drive + /// + public void OSD_DiskInserted() + { + StringBuilder sb = new StringBuilder(); + + if (_machine.UPDDiskDevice == null) + { + sb.Append("No Drive Present"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + + sb.Append("DISK INSERTED (" + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name + ")"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + } + + /// + /// Tape message that prints the current status of the tape device + /// + public void OSD_ShowDiskStatus() + { + StringBuilder sb = new StringBuilder(); + + if (_machine.UPDDiskDevice == null) + { + sb.Append("No Drive Present"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + + if (_diskInfo.Count == 0) + { + sb.Append("No Disk Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + + if (_machine.UPDDiskDevice != null) + { + if (_machine.UPDDiskDevice.DiskPointer == null) + { + sb.Append("No Disk Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + return; + } + + sb.Append("Disk: " + _machine.DiskMediaIndex + ": " + _diskInfo[_machine.DiskMediaIndex].Name); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + sb.Clear(); + + string protection = "None"; + protection = Enum.GetName(typeof(ProtectionType), _machine.UPDDiskDevice.DiskPointer.Protection); + if (protection == "None") + protection += " (OR UNKNOWN)"; + + sb.Append("Detected Protection: " + protection); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + sb.Clear(); + + sb.Append("Status: "); + + if (_machine.UPDDiskDevice.DriveLight) + sb.Append("READING/WRITING DATA"); + else + sb.Append("UNKNOWN"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Disk); + sb.Clear(); + } + } + + #endregion + + #region TapeDevice Message Methods + + /// + /// Tape message that is fired on core init + /// + public void OSD_TapeInit() + { + if (_tapeInfo.Count == 0) + return; + + StringBuilder sb = new StringBuilder(); + sb.Append("Tape Media Imported (count: " + _tapeInfo.Count() + ")"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Emulator); + } + + /// + /// Tape message that is fired when tape is playing + /// + public void OSD_TapePlaying() + { + if (_tapeInfo.Count == 0) + return; + + StringBuilder sb = new StringBuilder(); + sb.Append("PLAYING (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when tape is stopped + /// + public void OSD_TapeStopped() + { + if (_tapeInfo.Count == 0) + return; + + StringBuilder sb = new StringBuilder(); + sb.Append("STOPPED (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when tape is rewound + /// + public void OSD_TapeRTZ() + { + if (_tapeInfo.Count == 0) + return; + + StringBuilder sb = new StringBuilder(); + sb.Append("REWOUND (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a new tape is inserted into the datacorder + /// + public void OSD_TapeInserted() + { + if (_tapeInfo.Count == 0) + return; + + StringBuilder sb = new StringBuilder(); + sb.Append("TAPE INSERTED (" + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name + ")"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + + /// + /// Tape message that is fired when a tape is stopped automatically + /// + public void OSD_TapeStoppedAuto() + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("STOPPED (Auto Tape Trap Detected)"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a tape is started automatically + /// + public void OSD_TapePlayingAuto() + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("PLAYING (Auto Tape Trap Detected)"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a new block starts playing + /// + public void OSD_TapePlayingBlockInfo(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("...Starting Block " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a tape block is skipped (because it is empty) + /// + public void OSD_TapePlayingSkipBlockInfo(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("...Skipping Empty Block " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when a tape is started automatically + /// + public void OSD_TapeEndDetected(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("...Skipping Empty Block " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when user has manually skipped to the next block + /// + public void OSD_TapeNextBlock(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("Manual Skip Next " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that is fired when user has manually skipped to the next block + /// + public void OSD_TapePrevBlock(string blockinfo) + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + + sb.Append("Manual Skip Prev " + blockinfo); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + /// + /// Tape message that prints the current status of the tape device + /// + public void OSD_ShowTapeStatus() + { + StringBuilder sb = new StringBuilder(); + + if (_tapeInfo.Count == 0) + { + sb.Append("No Tape Loaded"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + return; + } + + sb.Append("Status: "); + + if (_machine.TapeDevice.TapeIsPlaying) + sb.Append("PLAYING"); + else + sb.Append("STOPPED"); + + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + + sb.Append("Tape: " + _machine.TapeMediaIndex + ": " + _tapeInfo[_machine.TapeMediaIndex].Name); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + + sb.Append("Block: "); + sb.Append("(" + (_machine.TapeDevice.CurrentDataBlockIndex + 1) + + " of " + _machine.TapeDevice.DataBlocks.Count() + ") " + + _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].BlockDescription); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + + sb.Append("Block Pos: "); + + int pos = _machine.TapeDevice.Position; + int end = _machine.TapeDevice.DataBlocks[_machine.TapeDevice.CurrentDataBlockIndex].DataPeriods.Count; + double p = 0; + if (end != 0) + p = ((double)pos / (double)end) * (double)100; + + sb.Append(p.ToString("N0") + "%"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + sb.Clear(); + + // get position within the tape itself + sb.Append("Tape Pos: "); + var ind = _machine.TapeDevice.CurrentDataBlockIndex; + int cnt = 0; + for (int i = 0; i < ind; i++) + { + cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count; + } + // now we are at our current block + int ourPos = cnt + pos; + cnt += end; + // count periods in the remaining blocks + for (int i = ind + 1; i < _machine.TapeDevice.DataBlocks.Count; i++) + { + cnt += _machine.TapeDevice.DataBlocks[i].DataPeriods.Count; + } + // work out overall position within the tape + p = 0; + p = ((double)ourPos / (double)cnt) * (double)100; + sb.Append(p.ToString("N0") + "%"); + SendMessage(sb.ToString().TrimEnd('\n'), MessageCategory.Tape); + } + + #endregion + + /// + /// Checks whether message category is allowed to be sent + /// + /// + /// + public bool CheckMessageSettings(MessageCategory category) + { + switch (Settings.OSDMessageVerbosity) + { + case OSDVerbosity.Full: + return true; + case OSDVerbosity.None: + return false; + case OSDVerbosity.Medium: + switch (category) + { + case MessageCategory.Disk: + case MessageCategory.Emulator: + case MessageCategory.Tape: + case MessageCategory.Misc: + return true; + default: + return false; + } + default: + return true; + } + } + + /// + /// Defines the different message categories + /// + public enum MessageCategory + { + /// + /// No defined category as such + /// + Misc, + /// + /// User generated input messages (at the moment only tape/disk controls) + /// + Input, + /// + /// Tape device generated messages + /// + Tape, + /// + /// Disk device generated messages + /// + Disk, + /// + /// Emulator generated messages + /// + Emulator + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs new file mode 100644 index 0000000000..8423f50e1f --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.Util.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * Misc Utilities * + /// + public partial class ZXSpectrum + { + /// + /// Helper method that returns a single INT32 from a BitArray + /// + /// + /// + public static int GetIntFromBitArray(BitArray bitArray) + { + if (bitArray.Length > 32) + throw new ArgumentException("Argument length shall be at most 32 bits."); + + int[] array = new int[1]; + bitArray.CopyTo(array, 0); + return array[0]; + } + + /// + /// POKEs a memory bus address + /// + /// + /// + public void PokeMemory(ushort addr, byte value) + { + _machine.WriteBus(addr, value); + } + + + public string GetMachineType() + { + string m = ""; + switch (SyncSettings.MachineType) + { + case MachineType.ZXSpectrum16: + m = "(Sinclair) ZX Spectrum 16K"; + break; + case MachineType.ZXSpectrum48: + m = "(Sinclair) ZX Spectrum 48K"; + break; + case MachineType.ZXSpectrum128: + m = "(Sinclair) ZX Spectrum 128K"; + break; + case MachineType.ZXSpectrum128Plus2: + m = "(Amstrad) ZX Spectrum 128K +2"; + break; + case MachineType.ZXSpectrum128Plus2a: + m = "(Amstrad) ZX Spectrum 128K +2a"; + break; + case MachineType.ZXSpectrum128Plus3: + m = "(Amstrad) ZX Spectrum 128K +3"; + break; + } + + return m; + } + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs new file mode 100644 index 0000000000..7a50f1e7d3 --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/ZXSpectrum.cs @@ -0,0 +1,291 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Emulation.Cores.Properties; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace BizHawk.Emulation.Cores.Computers.SinclairSpectrum +{ + /// + /// ZXHawk: Core Class + /// * Main Initialization * + /// + [Core( + "ZXHawk", + "Asnivor, Alyosha", + isPorted: false, + isReleased: true)] + public partial class ZXSpectrum : IRegionable, IDriveLight + { + public ZXSpectrum(CoreComm comm, IEnumerable files, List game, object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + ServiceProvider = ser; + InputCallbacks = new InputCallbackSystem(); + MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); + + CoreComm = comm; + + _gameInfo = game; + + _cpu = new Z80A(); + + _tracer = new TraceBuffer { Header = _cpu.TraceHeader }; + + //_file = file; + _files = files?.ToList() ?? new List(); + + if (settings == null) + settings = new ZXSpectrumSettings(); + if (syncSettings == null) + syncSettings = new ZXSpectrumSyncSettings(); + + PutSyncSettings((ZXSpectrumSyncSettings)syncSettings ?? new ZXSpectrumSyncSettings()); + PutSettings((ZXSpectrumSettings)settings ?? new ZXSpectrumSettings()); + + List joysticks = new List(); + joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType1); + joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType2); + joysticks.Add(((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).JoystickType3); + + deterministicEmulation = ((ZXSpectrumSyncSettings)syncSettings as ZXSpectrumSyncSettings).DeterministicEmulation; + + switch (SyncSettings.MachineType) + { + case MachineType.ZXSpectrum16: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum16, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); + break; + case MachineType.ZXSpectrum48: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum48, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); + break; + case MachineType.ZXSpectrum128: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); + break; + case MachineType.ZXSpectrum128Plus2: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128Plus2, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); + break; + case MachineType.ZXSpectrum128Plus2a: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128Plus2a, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); + break; + case MachineType.ZXSpectrum128Plus3: + ControllerDefinition = ZXSpectrumControllerDefinition; + Init(MachineType.ZXSpectrum128Plus3, SyncSettings.BorderType, SyncSettings.TapeLoadSpeed, _files, joysticks); + break; + default: + throw new InvalidOperationException("Machine not yet emulated"); + } + + _cpu.MemoryCallbacks = MemoryCallbacks; + + HardReset = _machine.HardReset; + SoftReset = _machine.SoftReset; + + _cpu.FetchMemory = _machine.ReadMemory; + _cpu.ReadMemory = _machine.ReadMemory; + _cpu.WriteMemory = _machine.WriteMemory; + _cpu.ReadHardware = _machine.ReadPort; + _cpu.WriteHardware = _machine.WritePort; + _cpu.FetchDB = _machine.PushBus; + _cpu.OnExecFetch = _machine.CPUMon.OnExecFetch; + + ser.Register(_tracer); + ser.Register(_cpu); + ser.Register(_machine.ULADevice); + + // initialize sound mixer and attach the various ISoundProvider devices + SoundMixer = new SoundProviderMixer((int)(32767 / 10), "System Beeper", (ISoundProvider)_machine.BuzzerDevice); + SoundMixer.AddSource((ISoundProvider)_machine.TapeBuzzer, "Tape Audio"); + if (_machine.AYDevice != null) + SoundMixer.AddSource(_machine.AYDevice, "AY-3-3912"); + + // set audio device settings + if (_machine.AYDevice != null && _machine.AYDevice.GetType() == typeof(AY38912)) + { + ((AY38912)_machine.AYDevice as AY38912).PanningConfiguration = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYPanConfig; + _machine.AYDevice.Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).AYVolume; + } + + if (_machine.BuzzerDevice != null) + { + ((Beeper)_machine.BuzzerDevice as Beeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).EarVolume; + } + + if (_machine.TapeBuzzer != null) + { + ((Beeper)_machine.TapeBuzzer as Beeper).Volume = ((ZXSpectrumSettings)settings as ZXSpectrumSettings).TapeVolume; + } + + ser.Register(SoundMixer); + //ser.Register < ISoundProvider>(((ISoundProvider)_machine.BuzzerDevice)); + + + HardReset(); + SetupMemoryDomains(); + } + + public Action HardReset; + public Action SoftReset; + + private readonly Z80A _cpu; + private readonly TraceBuffer _tracer; + public IController _controller; + public SpectrumBase _machine; + + public List _gameInfo; + + public List _tapeInfo = new List(); + public List _diskInfo = new List(); + + private SoundProviderMixer SoundMixer; + + private readonly List _files; + + public bool DiagRom = false; + + private List diagRoms = new List + { + @"\DiagROM.v28", + @"\zx-diagnostics\testrom.bin" + }; + private int diagIndex = 1; + + private byte[] GetFirmware(int length, params string[] names) + { + if (DiagRom & File.Exists(Directory.GetCurrentDirectory() + diagRoms[diagIndex])) + { + var rom = File.ReadAllBytes(Directory.GetCurrentDirectory() + diagRoms[diagIndex]); + return rom; + } + + // Amstrad licensed ROMs are free to distribute and shipped with BizHawk + byte[] embeddedRom = new byte[length]; + bool embeddedFound = true; + switch (names.FirstOrDefault()) + { + case "48ROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_48_ROM)); + break; + case "128ROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_128_ROM)); + break; + case "PLUS2ROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus2_rom)); + break; + case "PLUS2AROM": + embeddedRom = Util.DecompressGzipFile(new MemoryStream(Resources.ZX_plus2a_rom)); + break; + case "PLUS3ROM": + byte[] r0 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM0_bin)); + byte[] r1 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM1_bin)); + byte[] r2 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM2_bin)); + byte[] r3 = Util.DecompressGzipFile(new MemoryStream(Resources.Spectrum3_V4_0_ROM3_bin)); + embeddedRom = r0.Concat(r1).Concat(r2).Concat(r3).ToArray(); + break; + default: + embeddedFound = false; + break; + } + + if (embeddedFound) + return embeddedRom; + + // Embedded ROM not found, maybe this is a peripheral ROM? + var result = names.Select(n => CoreComm.CoreFileProvider.GetFirmware("ZXSpectrum", n, false)).FirstOrDefault(b => b != null && b.Length == length); + if (result == null) + { + throw new MissingFirmwareException($"At least one of these firmwares is required: {string.Join(", ", names)}"); + } + + return result; + } + + private MachineType _machineType; + + private void Init(MachineType machineType, BorderType borderType, TapeLoadSpeed tapeLoadSpeed, List files, List joys) + { + _machineType = machineType; + + // setup the emulated model based on the MachineType + switch (machineType) + { + case MachineType.ZXSpectrum16: + _machine = new ZX16(this, _cpu, borderType, files, joys); + var _systemRom16 = GetFirmware(0x4000, "48ROM"); + var romData16 = RomData.InitROM(machineType, _systemRom16); + _machine.InitROM(romData16); + break; + case MachineType.ZXSpectrum48: + _machine = new ZX48(this, _cpu, borderType, files, joys); + var _systemRom = GetFirmware(0x4000, "48ROM"); + var romData = RomData.InitROM(machineType, _systemRom); + _machine.InitROM(romData); + break; + case MachineType.ZXSpectrum128: + _machine = new ZX128(this, _cpu, borderType, files, joys); + var _systemRom128 = GetFirmware(0x8000, "128ROM"); + var romData128 = RomData.InitROM(machineType, _systemRom128); + _machine.InitROM(romData128); + break; + case MachineType.ZXSpectrum128Plus2: + _machine = new ZX128Plus2(this, _cpu, borderType, files, joys); + var _systemRomP2 = GetFirmware(0x8000, "PLUS2ROM"); + var romDataP2 = RomData.InitROM(machineType, _systemRomP2); + _machine.InitROM(romDataP2); + break; + case MachineType.ZXSpectrum128Plus2a: + _machine = new ZX128Plus2a(this, _cpu, borderType, files, joys); + var _systemRomP4 = GetFirmware(0x10000, "PLUS2AROM"); + var romDataP4 = RomData.InitROM(machineType, _systemRomP4); + _machine.InitROM(romDataP4); + break; + case MachineType.ZXSpectrum128Plus3: + _machine = new ZX128Plus3(this, _cpu, borderType, files, joys); + var _systemRomP3 = GetFirmware(0x10000, "PLUS3ROM"); + var romDataP3 = RomData.InitROM(machineType, _systemRomP3); + _machine.InitROM(romDataP3); + //System.Windows.Forms.MessageBox.Show("+3 is not working at all yet :/"); + break; + } + } + + #region IRegionable + + public DisplayType Region => DisplayType.PAL; + + #endregion + + #region IDriveLight + + public bool DriveLightEnabled + { + get + { + return true; + } + } + + public bool DriveLightOn + { + get + { + if (_machine != null && + (_machine.TapeDevice != null && _machine.TapeDevice.TapeIsPlaying) || + (_machine.UPDDiskDevice != null && _machine.UPDDiskDevice.DriveLight)) + return true; + + return false; + } + } + + #endregion + + } +} diff --git a/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md new file mode 100644 index 0000000000..58280ac42d --- /dev/null +++ b/BizHawk.Emulation.Cores/Computers/SinclairSpectrum/readme.md @@ -0,0 +1,32 @@ +## ZXHawk + +### Whats in the box? +* Emulates the Sinclair ZX Spectrum 16k, 48k, 128k, +2, +2A & +3 +* Accurate Z80A implementation +* Precise screen timing, floating bus, memory contention and port contention for all models +* Full keyboard emulation +* Kempston, Cursor and Sinclair joysticks emulated +* Full beeper and AY-3-3912 sound emulation +* Tape device (datacorder) emulation +* Internal 3" disk drive emulation (found in the +3 model) +* Currently supports the following tape image formats: *.tzx, *.tap, *.pzx, *.csw, *.wav +* Currently supports the following disk image formats (+3 only): *.dsk +* Fully integrated into the Bizhawk ecosystem +* See the ZXSpectrum menu for all available configuration options + +### Firmware +ZXHawk ships with the official ZX Spectrum ROMs embedded (licensed by Amstrad). + +"Amstrad have kindly given their permission for the redistribution of their copyrighted material but retain that copyright" +http://www.worldofspectrum.org/permits/amstrad-roms.txt + +### Issues +* Tape images are read-only. This may change in the future, but with bizhawk's savestate system this is not strictly a necessity +* Disk images are currently read-only as well. There is certain write functionality implemented within the emulated UPD756A disk controller (in order to make games work that require this), but this is not persistent +* Disk drive emulation timing is currently not accurate, meaning that disk games will load faster than they would on a real +3. Due to how the Spectrum interfaces with the disk controller though, this should not cause any compatibility issues + +Any questions, issues or bug reports, either use the GitHub issue tracker, or post in the forum thread: + +http://tasvideos.org/forum/viewtopic.php?t=20004 + +-Asnivor diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs index 6d5e8b0f27..4d9bb84286 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.Core.cs @@ -18,7 +18,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private MapperBase _mapper; private byte[] _ram; - private IController _controller; + private IController _controller = NullController.Instance; private int _frame; private int _lastAddress; @@ -28,11 +28,35 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private bool _leftDifficultySwitchHeld; private bool _rightDifficultySwitchHeld; - internal MOS6502X Cpu { get; private set; } + internal MOS6502X Cpu { get; private set; } internal byte[] Ram => _ram; internal byte[] Rom { get; } internal int DistinctAccessCount { get; private set; } + public bool SP_FRAME = false; + public bool SP_RESET = false; + public bool unselect_reset; + + internal struct CpuLink : IMOS6502XLink + { + private readonly Atari2600 _atari2600; + + public CpuLink(Atari2600 atari2600) + { + _atari2600 = atari2600; + } + + public byte DummyReadMemory(ushort address) => _atari2600.ReadMemory(address); + + public void OnExecFetch(ushort address) => _atari2600.ExecFetch(address); + + public byte PeekMemory(ushort address) => _atari2600.ReadMemory(address); + + public byte ReadMemory(ushort address) => _atari2600.ReadMemory(address); + + public void WriteMemory(ushort address, byte value) => _atari2600.WriteMemory(address, value); + } + // keeps track of tia cycles, 3 cycles per CPU cycle private int cyc_counter; @@ -292,14 +316,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _mapper.Core = this; _lagcount = 0; - Cpu = new MOS6502X - { - ReadMemory = ReadMemory, - WriteMemory = WriteMemory, - PeekMemory = PeekMemory, - DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch - }; + Cpu = new MOS6502X(new CpuLink(this)); if (_game["PAL"]) { @@ -325,6 +342,22 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 // Show mapper class on romstatusdetails CoreComm.RomStatusDetails = $"{this._game.Name}\r\nSHA1:{Rom.HashSHA1()}\r\nMD5:{Rom.HashMD5()}\r\nMapper Impl \"{_mapper.GetType()}\""; + + // Some games (ex. 3D tic tac toe), turn off the screen for extended periods, so we need to allow for this here. + if (_game.GetOptionsDict().ContainsKey("SP_FRAME")) + { + if (_game.GetOptionsDict()["SP_FRAME"] == "true") + { + SP_FRAME = true; + } + } + if (_game.GetOptionsDict().ContainsKey("SP_RESET")) + { + if (_game.GetOptionsDict()["SP_RESET"] == "true") + { + SP_RESET = true; + } + } } private bool _pal; @@ -334,14 +367,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _ram = new byte[128]; _mapper.HardReset(); - Cpu = new MOS6502X - { - ReadMemory = ReadMemory, - WriteMemory = WriteMemory, - PeekMemory = PeekMemory, - DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch - }; + Cpu = new MOS6502X(new CpuLink(this)); _tia.Reset(); _m6532 = new M6532(this); @@ -415,6 +441,11 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 bool select = _controller.IsPressed("Select"); bool reset = _controller.IsPressed("Reset"); + if (unselect_reset) + { + reset = false; + } + if (reset) { value &= 0xFE; } if (select) { value &= 0xFD; } if (SyncSettings.BW) { value &= 0xF7; } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs index 75c16f62ff..1a5068cb10 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IDebuggable.cs @@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 } - public int TotalExecutedCycles => Cpu.TotalExecutedCycles; + public long TotalExecutedCycles => Cpu.TotalExecutedCycles; private int JSRCount = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs index 13164fdd0f..1dad16b1d3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IEmulator.cs @@ -42,12 +42,24 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 _rightDifficultySwitchHeld = false; } + unselect_reset = false; + int count = 0; while (!_tia.New_Frame) { Cycle(); count++; - if (count > 1000000) { throw new Exception("ERROR: Unable to resolve Frame. Please Report."); } + if (count > 1000000 && !SP_FRAME) + { + if (SP_RESET) + { + unselect_reset = true; + } + else + { + throw new Exception("ERROR: Unable to resolve Frame. Please Report."); + } + } } _tia.New_Frame = false; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs index dcff99f432..69a1191c46 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Atari2600.IStatable.cs @@ -51,6 +51,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 ser.Sync("rightDifficultySwitchPressed", ref _rightDifficultySwitchPressed); ser.Sync("leftDifficultySwitchHeld", ref _leftDifficultySwitchHeld); ser.Sync("rightDifficultySwitchHeld", ref _rightDifficultySwitchHeld); + ser.Sync("unselect_reset", ref unselect_reset); _tia.SyncState(ser); _m6532.SyncState(ser); diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs index af5ea5f124..bb6aa0783d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/2600/Tia/TIA.cs @@ -106,7 +106,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600 private bool _doTicks; private byte _hsyncCnt; - private int _capChargeStart; + private long _capChargeStart; private bool _capCharging; private bool _vblankEnabled; private bool _vsyncEnabled; diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs index afdc4168bd..343f831e5e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IDebuggable.cs @@ -67,7 +67,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { get { return cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs index 27f51380c7..a4aa5a5b3d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.IEmulator.cs @@ -76,7 +76,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk GetControllerState(controller); GetConsoleState(controller); - + maria.RunFrame(); if (_islag) diff --git a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs index 32ac8c29f3..41a953d2a8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Atari/A7800Hawk/A7800Hawk.cs @@ -44,13 +44,33 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk private readonly ITraceable _tracer; - public MOS6502X cpu; + public MOS6502X cpu; public Maria maria; public bool _isPAL; public M6532 m6532; public TIA tia; public Pokey pokey; + public struct CpuLink : IMOS6502XLink + { + private readonly A7800Hawk _a7800; + + public CpuLink(A7800Hawk a7800) + { + _a7800 = a7800; + } + + public byte DummyReadMemory(ushort address) => _a7800.ReadMemory(address); + + public void OnExecFetch(ushort address) => _a7800.ExecFetch(address); + + public byte PeekMemory(ushort address) => _a7800.ReadMemory(address); + + public byte ReadMemory(ushort address) => _a7800.ReadMemory(address); + + public void WriteMemory(ushort address, byte value) => _a7800.WriteMemory(address, value); + } + public A7800Hawk(CoreComm comm, GameInfo game, byte[] rom, string gameDbFn, object settings, object syncSettings) { var ser = new BasicServiceProvider(this); @@ -60,14 +80,7 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk m6532 = new M6532(); pokey = new Pokey(); - cpu = new MOS6502X - { - ReadMemory = ReadMemory, - WriteMemory = WriteMemory, - PeekMemory = ReadMemory, - DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch - }; + cpu = new MOS6502X(new CpuLink(this)); maria = new Maria { @@ -255,7 +268,6 @@ namespace BizHawk.Emulation.Cores.Atari.A7800Hawk tia.Reset(); cpu.Reset(); - cpu.SetCallbacks(ReadMemory, ReadMemory, ReadMemory, WriteMemory); maria.Reset(); m6532.Reset(); diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs index c48bbabf8f..73ca2c072e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.IDebuggable.cs @@ -142,6 +142,6 @@ namespace BizHawk.Emulation.Cores.ColecoVision throw new NotImplementedException(); } - public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs index e9bcba4f8f..1a5b88475c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs +++ b/BizHawk.Emulation.Cores/Consoles/Coleco/ColecoVision.cs @@ -75,7 +75,7 @@ namespace BizHawk.Emulation.Cores.ColecoVision public bool temp_1_prev, temp_2_prev; private int _frame; - private IController _controller; + private IController _controller = NullController.Instance; private enum InputPortMode { diff --git a/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs index c80c5832a2..78d5f9a4e7 100644 --- a/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Intellivision/Intellivision.IDebuggable.cs @@ -95,6 +95,6 @@ namespace BizHawk.Emulation.Cores.Intellivision throw new NotImplementedException(); } - public int TotalExecutedCycles => _cpu.TotalExecutedCycles; + public long TotalExecutedCycles => _cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs index 57809282b0..eb97242ded 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.IDebuggable.cs @@ -44,7 +44,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs index 7720dc4595..0028c7b4e9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs @@ -3,7 +3,7 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Nintendo.GBA { - [Core("mGBA", "endrift", true, true, "0.6.1 (c3e9258bc3a1e8a0e68ff73d5ff9674b56257eb7)", "https://mgba.io/", false)] + [Core("mGBA", "endrift", true, true, "0.6.3 (c4dfb265c35ecba4050d646bd65a02d8b3704948)", "https://mgba.io/", false)] [ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))] public partial class MGBAHawk : IEmulator, IVideoProvider, ISoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable, ISettable, diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs index 2b9424e729..71592fbc3d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.IDebuggable.cs @@ -33,7 +33,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs index 2574978258..8ebbe4d9a2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Audio.cs @@ -12,6 +12,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public GBHawk Core { get; set; } + private readonly BlipBuffer _blip_L = new BlipBuffer(15000); + private readonly BlipBuffer _blip_R = new BlipBuffer(15000); + public static int[] DUTY_CYCLES = new int[] {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, @@ -104,9 +107,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte AUD_CTRL_vol_L; public byte AUD_CTRL_vol_R; - public int sequencer_len, sequencer_vol, sequencer_swp, sequencer_tick; + public int sequencer_len, sequencer_vol, sequencer_swp; + public bool timer_bit_old; - public int master_audio_clock; + public byte sample; + + public uint master_audio_clock; + + public int latched_sample_L, latched_sample_R; public byte ReadReg(int addr) { @@ -155,7 +163,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF3F: if (WAVE_enable) { - if (WAVE_can_get) { ret = Wave_RAM[WAVE_wave_cntr >> 1]; } + if (WAVE_can_get || Core.is_GBC) { ret = Wave_RAM[WAVE_wave_cntr >> 1]; } else { ret = 0xFF; } } else { ret = Wave_RAM[addr & 0x0F]; } @@ -367,7 +375,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (WAVE_trigger) { // some corruption occurs if triggering while reading - if (WAVE_enable && WAVE_intl_cntr == 2) + if (WAVE_enable && (WAVE_intl_cntr == 2) && !Core.is_GBC) { // we want to use the previous wave cntr value since it was just incremented int t_wave_cntr = (WAVE_wave_cntr + 1) & 31; @@ -500,33 +508,49 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 0xFF3F: if (WAVE_enable) { - if (WAVE_can_get) { Wave_RAM[WAVE_wave_cntr >> 1] = value; } + if (WAVE_can_get || Core.is_GBC) { Wave_RAM[WAVE_wave_cntr >> 1] = value; } + } + else + { + Wave_RAM[addr & 0xF] = value; } - else { Wave_RAM[addr & 0xF] = value; } break; } } // when power is off, only length counters and waveRAM are effected by writes + // ON GBC, length counters cannot be written to either else { switch (addr) { case 0xFF11: // NR11 (sound length / wave pattern duty %) - SQ1_length = (ushort)(64 - (value & 0x3F)); - SQ1_len_cntr = SQ1_length; + if (!Core.is_GBC) + { + SQ1_length = (ushort)(64 - (value & 0x3F)); + SQ1_len_cntr = SQ1_length; + } break; case 0xFF16: // NR21 (sound length / wave pattern duty %) - SQ2_length = (ushort)(64 - (value & 0x3F)); - SQ2_len_cntr = SQ2_length; + if (!Core.is_GBC) + { + SQ2_length = (ushort)(64 - (value & 0x3F)); + SQ2_len_cntr = SQ2_length; + } break; case 0xFF1B: // NR31 (length) - WAVE_length = (ushort)(256 - value); - WAVE_len_cntr = WAVE_length; + if (!Core.is_GBC) + { + WAVE_length = (ushort)(256 - value); + WAVE_len_cntr = WAVE_length; + } break; case 0xFF20: // NR41 (length) - NOISE_length = (ushort)(64 - (value & 0x3F)); - NOISE_len_cntr = NOISE_length; + if (!Core.is_GBC) + { + NOISE_length = (ushort)(64 - (value & 0x3F)); + NOISE_len_cntr = NOISE_length; + } break; case 0xFF26: // NR52 (ctrl) AUD_CTRL_power = (value & 0x80) > 0; @@ -607,13 +631,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (WAVE_intl_cntr == 0) { - WAVE_intl_cntr = (2048 - WAVE_frq) * 2; - WAVE_wave_cntr++; - WAVE_wave_cntr &= 0x1F; - WAVE_can_get = true; - byte sample = Wave_RAM[WAVE_wave_cntr >> 1]; + WAVE_intl_cntr = (2048 - WAVE_frq) * 2; if ((WAVE_wave_cntr & 1) == 0) { @@ -639,10 +659,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WAVE_output = sample; + // NOTE: The sample buffer is only reloaded after the current sample is played, even if just triggered + WAVE_wave_cntr++; + WAVE_wave_cntr &= 0x1F; + sample = Wave_RAM[WAVE_wave_cntr >> 1]; + if (!WAVE_DAC_pow) { WAVE_output = 0; } // avoid aliasing at high frequenices - if (WAVE_frq > 0x7F0) { WAVE_output = 0; } + //if (WAVE_frq > 0x7F0) { WAVE_output = 0; } } } @@ -665,7 +690,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk NOISE_LFSR |= (bit_lfsr << 6); } - NOISE_output = NOISE_LFSR & 1; + NOISE_output = (NOISE_LFSR & 1) > 0 ? 0 : 1; NOISE_output *= NOISE_vol_state; } } @@ -684,30 +709,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; } if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; } - L_final *= (AUD_CTRL_vol_L + 1); - R_final *= (AUD_CTRL_vol_R + 1); + L_final *= (AUD_CTRL_vol_L + 1) * 40; + R_final *= (AUD_CTRL_vol_R + 1) * 40; - // send out an actual sample every 94 cycles - master_audio_clock++; - if (master_audio_clock == 94) + if (L_final != latched_sample_L) { - master_audio_clock = 0; - if (AudioClocks < 1500) - { - AudioSamples[AudioClocks] = (short)(L_final * 20); - AudioClocks++; - AudioSamples[AudioClocks] = (short)(R_final * 20); - AudioClocks++; - } + _blip_L.AddDelta(master_audio_clock, L_final - latched_sample_L); + latched_sample_L = L_final; } - // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) - sequencer_tick++; - - if (sequencer_tick == 8192) + if (R_final != latched_sample_R) { - sequencer_tick = 0; + _blip_R.AddDelta(master_audio_clock, R_final - latched_sample_R); + latched_sample_R = R_final; + } + master_audio_clock++; + + // frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over) + // the sequencer is actually the timer DIV register + // so if it's constantly written to, these values won't update + bool check = Core.double_speed ? Core.timer.divider_reg.Bit(13) : Core.timer.divider_reg.Bit(12); + + if (check && !timer_bit_old) + { sequencer_vol++; sequencer_vol &= 0x7; sequencer_len++; sequencer_len &= 0x7; sequencer_swp++; sequencer_swp &= 0x7; @@ -859,13 +884,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } } } + timer_bit_old = Core.double_speed ? Core.timer.divider_reg.Bit(13) : Core.timer.divider_reg.Bit(12); } public void power_off() { for (int i = 0; i < 0x16; i++) { - WriteReg(0xFF10 + i, 0); + WriteReg(0xFF10 + i, 0); } // duty and length are reset @@ -878,6 +904,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SQ1_output = SQ2_output = WAVE_output = NOISE_output = 0; + // on GBC, lengths are also reset + if (Core.is_GBC) + { + SQ1_length = SQ2_length = WAVE_length = NOISE_length = 0; + SQ1_len_cntr = SQ2_len_cntr = WAVE_len_cntr = NOISE_len_cntr = 0; + } + sequencer_len = 0; sequencer_vol = 0; sequencer_swp = 0; @@ -885,18 +918,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void Reset() { - Wave_RAM = new byte[] { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, + if (Core.is_GBC) + { + Wave_RAM = new byte[] { 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF }; + } + else + { + Wave_RAM = new byte[] { 0x84, 0x40, 0x43, 0xAA, 0x2D, 0x78, 0x92, 0x3C, 0x60, 0x59, 0x59, 0xB0, 0x34, 0xB8, 0x2E, 0xDA }; - + } + Audio_Regs = new byte[21]; - AudioClocks = 0; master_audio_clock = 0; sequencer_len = 0; sequencer_swp = 0; sequencer_vol = 0; - sequencer_tick = 0; + + sample = 0; + + _blip_L.SetRates(4194304, 44100); + _blip_R.SetRates(4194304, 44100); } public void SyncState(Serializer ser) @@ -983,10 +1027,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("sequencer_len", ref sequencer_len); ser.Sync("sequencer_vol", ref sequencer_vol); ser.Sync("sequencer_swp", ref sequencer_swp); - ser.Sync("sequencer_tick", ref sequencer_tick); + ser.Sync("timer_bit_old", ref timer_bit_old); ser.Sync("master_audio_clock", ref master_audio_clock); + ser.Sync("sample", ref sample); + ser.Sync("latched_sample_L", ref latched_sample_L); + ser.Sync("latched_sample_R", ref latched_sample_R); + ser.Sync("AUD_CTRL_vin_L_en", ref AUD_CTRL_vin_L_en); ser.Sync("AUD_CTRL_vin_R_en", ref AUD_CTRL_vin_R_en); ser.Sync("AUD_CTRL_sq1_L_en", ref AUD_CTRL_sq1_L_en); @@ -1016,8 +1064,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool CanProvideAsync => false; - public int AudioClocks; - public short[] AudioSamples = new short[1500]; + public short[] AudioSamples = new short[150000]; public void SetSyncMode(SyncSoundMode mode) { @@ -1031,17 +1078,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void GetSamplesSync(out short[] samples, out int nsamp) { - nsamp = AudioClocks / 2; - short[] temp_samp = new short[AudioClocks]; + _blip_L.EndFrame(master_audio_clock); + _blip_R.EndFrame(master_audio_clock); + + nsamp = _blip_R.SamplesAvailable(); - for (int i = 0; i < AudioClocks; i++) - { - temp_samp[i] = AudioSamples[i]; - } + samples = new short[nsamp * 2]; - samples = temp_samp; + _blip_L.ReadSamplesLeft(samples, nsamp); + _blip_R.ReadSamplesRight(samples, nsamp); - AudioClocks = 0; + master_audio_clock = 0; } public void GetSamplesAsync(short[] samples) @@ -1051,7 +1098,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void DiscardSamples() { - AudioClocks = 0; + _blip_L.Clear(); + _blip_R.Clear(); + master_audio_clock = 0; } private void GetSamples(short[] samples) @@ -1060,5 +1109,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } #endregion + + public void DisposeSound() + { + _blip_L.Dispose(); + _blip_R.Dispose(); + } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs new file mode 100644 index 0000000000..1988ff98fa --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBC_PPU.cs @@ -0,0 +1,1612 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class GBC_PPU : PPU + { + // individual byte used in palette colors + public byte[] BG_bytes = new byte[64]; + public byte[] OBJ_bytes = new byte[64]; + public bool BG_bytes_inc; + public bool OBJ_bytes_inc; + public byte BG_bytes_index; + public byte OBJ_bytes_index; + public byte BG_transfer_byte; + public byte OBJ_transfer_byte; + + // HDMA is unique to GBC, do it as part of the PPU tick + public byte HDMA_src_hi; + public byte HDMA_src_lo; + public byte HDMA_dest_hi; + public byte HDMA_dest_lo; + public int HDMA_tick; + public byte HDMA_byte; + + // accessors for derived values + public byte BG_pal_ret + { + get { return (byte)(((BG_bytes_inc ? 1 : 0) << 7) | (BG_bytes_index & 0x3F)); } + } + + public byte OBJ_pal_ret + { + get { return (byte)(((OBJ_bytes_inc ? 1 : 0) << 7) | (OBJ_bytes_index & 0x3F)); } + } + + public byte HDMA_ctrl + { + get { return (byte)(((HDMA_active ? 0 : 1) << 7) | ((HDMA_length >> 16) - 1)); } + } + + + // controls for tile attributes + public int VRAM_sel; + public bool BG_V_flip; + public bool HDMA_mode; + public ushort cur_DMA_src; + public ushort cur_DMA_dest; + public int HDMA_length; + public int HDMA_countdown; + public int HBL_HDMA_count; + public int last_HBL; + public bool HBL_HDMA_go; + public bool HBL_test; + + public override byte ReadReg(int addr) + { + byte ret = 0; + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + switch (addr) + { + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: ret = DMA_addr; break; // DMA + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX + + // These are GBC specific Regs + case 0xFF51: ret = HDMA_src_hi; break; // HDMA1 + case 0xFF52: ret = HDMA_src_lo; break; // HDMA2 + case 0xFF53: ret = HDMA_dest_hi; break; // HDMA3 + case 0xFF54: ret = HDMA_dest_lo; break; // HDMA4 + case 0xFF55: ret = HDMA_ctrl; break; // HDMA5 + case 0xFF68: ret = BG_pal_ret; break; // BGPI + case 0xFF69: ret = BG_bytes[BG_bytes_index]; break; // BGPD + case 0xFF6A: ret = OBJ_pal_ret; break; // OBPI + case 0xFF6B: ret = OBJ_bytes[OBJ_bytes_index]; break; // OBPD + } + + return ret; + } + + public override void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF40: // LCDC + if (LCDC.Bit(7) && !value.Bit(7)) + { + VRAM_access_read = true; + VRAM_access_write = true; + OAM_access_read = true; + OAM_access_write = true; + } + + if (!LCDC.Bit(7) && value.Bit(7)) + { + // don't draw for one frame after turning on + blank_frame = true; + } + + LCDC = value; + break; + case 0xFF41: // STAT + // writing to STAT during mode 0 or 2 causes a STAT IRQ + if (LCDC.Bit(7)) + { + if (((STAT & 3) == 0) || ((STAT & 3) == 1)) + { + LYC_INT = true; + } + } + STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); + break; + case 0xFF42: // SCY + scroll_y = value; + break; + case 0xFF43: // SCX + scroll_x = value; + // calculate the column number of the tile to start with + x_tile = (int)Math.Floor((float)(scroll_x) / 8); + break; + case 0xFF44: // LY + LY = 0; /*reset*/ + break; + case 0xFF45: // LYC + LYC = value; + if (LCDC.Bit(7)) + { + if (LY != LYC) { STAT &= 0xFB; } + else { STAT |= 0x4; } + } + break; + case 0xFF46: // DMA + DMA_addr = value; + DMA_start = true; + DMA_OAM_access = true; + DMA_clock = 0; + DMA_inc = 0; + break; + case 0xFF47: // BGP + BGP = value; + break; + case 0xFF48: // OBP0 + obj_pal_0 = value; + break; + case 0xFF49: // OBP1 + obj_pal_1 = value; + break; + case 0xFF4A: // WY + window_y = value; + break; + case 0xFF4B: // WX + window_x = value; + break; + + // These are GBC specific Regs + case 0xFF51: // HDMA1 + HDMA_src_hi = value; + cur_DMA_src = (ushort)(((HDMA_src_hi & 0xFF) << 8) | (cur_DMA_src & 0xF0)); + break; + case 0xFF52: // HDMA2 + HDMA_src_lo = value; + cur_DMA_src = (ushort)((cur_DMA_src & 0xFF00) | (HDMA_src_lo & 0xF0)); + break; + case 0xFF53: // HDMA3 + HDMA_dest_hi = value; + cur_DMA_dest = (ushort)(((HDMA_dest_hi & 0x1F) << 8) | (cur_DMA_dest & 0xF0)); + break; + case 0xFF54: // HDMA4 + HDMA_dest_lo = value; + cur_DMA_dest = (ushort)((cur_DMA_dest & 0xFF00) | (HDMA_dest_lo & 0xF0)); + break; + case 0xFF55: // HDMA5 + if (!HDMA_active) + { + HDMA_mode = value.Bit(7); + HDMA_countdown = 4; + HDMA_tick = 0; + if (value.Bit(7)) + { + // HDMA during HBlank only + HDMA_active = true; + HBL_HDMA_count = 0x10; + + // TODO: DOES HDMA start if triggered in mode 0 immediately? (for now assume no) + if ((STAT & 3) == 0) + { + last_HBL = LY; + } + else + { + last_HBL = LY - 1; + } + + HBL_test = true; + HBL_HDMA_go = false; + } + else + { + // HDMA immediately + HDMA_active = true; + Core.HDMA_transfer = true; + } + + HDMA_length = ((value & 0x7F) + 1) * 16; + } + else + { + //terminate the transfer + if (!value.Bit(7)) + { + HDMA_active = false; + } + } + + break; + case 0xFF68: // BGPI + BG_bytes_index = (byte)(value & 0x3F); + BG_bytes_inc = ((value & 0x80) == 0x80); + break; + case 0xFF69: // BGPD + BG_transfer_byte = value; + BG_bytes[BG_bytes_index] = value; + + // change the appropriate palette color + color_compute_BG(); + if (BG_bytes_inc) { BG_bytes_index++; BG_bytes_index &= 0x3F; } + break; + case 0xFF6A: // OBPI + OBJ_bytes_index = (byte)(value & 0x3F); + OBJ_bytes_inc = ((value & 0x80) == 0x80); + break; + case 0xFF6B: // OBPD + OBJ_transfer_byte = value; + OBJ_bytes[OBJ_bytes_index] = value; + + // change the appropriate palette color + color_compute_OBJ(); + + if (OBJ_bytes_inc) { OBJ_bytes_index++; OBJ_bytes_index &= 0x3F; } + break; + } + } + + public override void tick() + { + // Do HDMA ticks + if (HDMA_active) + { + if (HDMA_countdown == 0) + { + if (HDMA_length > 0) + { + if (!HDMA_mode) + { + // immediately transfer bytes, 2 bytes per cycles + if ((HDMA_tick % 2) == 0) + { + HDMA_byte = Core.ReadMemory(cur_DMA_src); + } + else + { + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = HDMA_byte; + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; + } + + HDMA_tick++; + } + else + { + // only transfer during mode 0, and only 16 bytes at a time + if (((STAT & 3) == 0) && (LY != last_HBL) && HBL_test && (LY_inc == 1)) + { + HBL_HDMA_go = true; + HBL_test = false; + } + + if (HBL_HDMA_go && (HBL_HDMA_count > 0)) + { + Core.HDMA_transfer = true; + + if ((HDMA_tick % 2) == 0) + { + HDMA_byte = Core.ReadMemory(cur_DMA_src); + } + else + { + Core.VRAM[(Core.VRAM_Bank * 0x2000) + cur_DMA_dest] = HDMA_byte; + cur_DMA_dest = (ushort)((cur_DMA_dest + 1) & 0x1FFF); + cur_DMA_src = (ushort)((cur_DMA_src + 1) & 0xFFFF); + HDMA_length--; + HBL_HDMA_count--; + } + + if ((HBL_HDMA_count == 0) && (HDMA_length != 0)) + { + HBL_test = true; + last_HBL = LY; + HBL_HDMA_count = 0x10; + HBL_HDMA_go = false; + } + + HDMA_tick++; + } + else + { + Core.HDMA_transfer = false; + } + } + } + else + { + HDMA_active = false; + Core.HDMA_transfer = false; + } + } + else + { + HDMA_countdown--; + } + } + + + // the ppu only does anything if it is turned on via bit 7 of LCDC + if (LCDC.Bit(7)) + { + // start the next scanline + if (cycle == 456) + { + // scanline callback + if ((LY + LY_inc) == Core._scanlineCallbackLine) + { + if (Core._scanlineCallback != null) + { + Core.GetGPU(); + Core._scanlineCallback(LCDC); + } + } + + cycle = 0; + LY += LY_inc; + Core.cpu.LY = LY; + + no_scan = false; + + if (LY == 0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + + /* + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + */ + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; + if (!LCDC.Bit(5)) { window_is_reset = true; } + + } + + // Automatically restore access to VRAM at this time (force end drawing) + // Who Framed Roger Rabbit seems to run into this. + VRAM_access_write = true; + VRAM_access_read = true; + + if (LY == 144) + { + Core.in_vblank = true; + } + } + + // exit vblank if LCD went from off to on + if (LCD_was_off) + { + //VBL_INT = false; + Core.in_vblank = false; + LCD_was_off = false; + + // we exit vblank into mode 0 for 4 cycles + // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 + STAT &= 0xFC; + + // also the LCD doesn't turn on right away + + // also, the LCD does not enter mode 2 on scanline 0 when first turned on + no_scan = true; + cycle = 8; + } + + // the VBL stat is continuously asserted + if ((LY >= 144)) + { + if (STAT.Bit(4)) + { + if ((cycle >= 4) && (LY == 144)) + { + VBL_INT = true; + } + else if (LY > 144) + { + VBL_INT = true; + } + } + + if ((cycle == 4) && (LY == 144)) { + + HBL_INT = false; + + // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled + STAT &= 0xFC; + STAT |= 0x01; + + if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x01; + } + + if ((LY >= 144) && (cycle == 4)) + { + // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + //if (STAT.Bit(5)) { OAM_INT = true; } + } + + if ((LY == 153) && (cycle == 6)) + { + LY = 0; + LY_inc = 0; + Core.cpu.LY = LY; + } + } + + if (!Core.in_vblank) + { + if (no_scan) + { + // timings are slightly different if we just turned on the LCD + // there is no mode 2 (presumably it missed the trigger) + // mode 3 is very short, probably in some self test mode before turning on? + + if (cycle == 8) + { + if (LY != LYC) + { + LYC_INT = false; + STAT &= 0xFB; + } + + if ((LY == LYC) && !STAT.Bit(2)) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access_read = false; + OAM_access_write = false; + VRAM_access_read = false; + VRAM_access_write = false; + } + + if (cycle == 256) + { + STAT &= 0xFC; + OAM_INT = false; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + else + { + if (cycle < 80) + { + if (cycle == 2) + { + if (LY != 0) { if (STAT.Bit(5)) { OAM_INT = true; } } + } + else if (cycle == 4) + { + // apparently, writes can make it to OAM one cycle longer then reads + OAM_access_write = false; + + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + + if (LY == 0) { if (STAT.Bit(5)) { OAM_INT = true; } } + + HBL_INT = false; + // DMG exits VBlank into mode 0, but not GBC, so this line is needed + // (This is important for Wacky Racers and Altered Space) + VBL_INT = false; + } + + // here OAM scanning is performed + OAM_scan(cycle); + } + else if ((cycle >= 80) && (LY < 144)) + { + + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + OAM_access_write = false; + VRAM_access_write = false; + } + + // render the screen and handle hblank + render(cycle - 80); + } + } + } + + if ((LY_inc == 0)) + { + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + // Special case of LY = LYC + if ((LY == LYC) && !STAT.Bit(2)) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + + // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + //if (STAT.Bit(5)) { OAM_INT = true; } + } + + //if (cycle == 92) { OAM_INT = false; } + } + + // here LY=LYC will be asserted or cleared (but only if LY isnt 0 as that's a special case) + if ((cycle == 2) && (LY != 0)) + { + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + } + else if ((cycle == 4) && (LY != 0)) + { + if ((LY == LYC) && !STAT.Bit(2)) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + cycle++; + } + else + { + STAT &= 0xFC; + + VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; + + Core.in_vblank = true; + + LCD_was_off = true; + + LY = 0; + Core.cpu.LY = LY; + + cycle = 0; + } + + // assert the STAT IRQ line if the line went from zero to 1 + stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; + + if (stat_line && !stat_line_old) + { + if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x02; + } + + stat_line_old = stat_line; + + // process latch delays + //latch_delay(); + } + + // might be needed, not sure yet + public override void latch_delay() + { + //BGP_l = BGP; + } + + public override void render(int render_cycle) + { + + // we are now in STAT mode 3 + // NOTE: presumably the first necessary sprite is fetched at sprite evaulation + // i.e. just keeping track of the lowest x-value sprite + if (render_cycle == 0) + { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + + // window X is latched for the scanline, mid-line changes have no effect + window_x_latch = window_x; + + OAM_scan_index = 0; + read_case = 0; + internal_cycle = 0; + pre_render = true; + tile_inc = 0; + pixel_counter = -8; + sl_use_index = 0; + fetch_sprite = false; + fetch_sprite_01 = false; + fetch_sprite_4 = false; + going_to_fetch = false; + first_fetch = true; + no_sprites = false; + evaled_sprites = 0; + window_pre_render = false; + window_latch = LCDC.Bit(5); + + // TODO: If Window is turned on midscanline what happens? When is this check done exactly? + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY >= window_y))) + { + window_y_tile_inc++; + if (window_y_tile_inc==8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + window_started = false; + + if (SL_sprites_index == 0) { no_sprites = true; } + // it is much easier to process sprites if we order them according to the rules of sprite priority first + if (!no_sprites) { reorder_and_assemble_sprites(); } + + } + + // before anything else, we have to check if windowing is in effect + if (window_latch && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) + { + /* + Console.Write(LY); + Console.Write(" "); + Console.Write(cycle); + Console.Write(" "); + Console.Write(window_y_tile_inc); + Console.Write(" "); + Console.Write(window_x_latch); + Console.Write(" "); + Console.WriteLine(pixel_counter); + */ + if (window_x_latch <= 7) + { + // if the window starts at zero, we still do the first access to the BG + // but then restart all over again at the window + read_case = 9; + } + else + { + // otherwise, just restart the whole process as if starting BG again + read_case = 4; + } + window_pre_render = true; + + window_counter = 0; + render_counter = 0; + + window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); + + window_tile_inc = 0; + window_started = true; + window_is_reset = false; + } + + if (!pre_render && !fetch_sprite) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + if (tile_data_latch[2].Bit(5) && Core.GBC_compat) + { + pixel = tile_data_latch[0].Bit(render_counter % 8) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(render_counter % 8) ? 2 : 0; + } + else + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + } + + int ref_pixel = pixel; + + if (!Core.GBC_compat) + { + if (LCDC.Bit(0)) + { + pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + pixel = 0; + } + } + + int pal_num = tile_data_latch[2] & 0x7; + + bool use_sprite = false; + + int s_pixel = 0; + + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int sprite_attr = 0; + + if (sprite_present_list[pixel_counter] == 1) + { + have_sprite = true; + s_pixel = sprite_pixel_list[pixel_counter]; + sprite_attr = sprite_attr_list[pixel_counter]; + } + + if (have_sprite) + { + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + use_sprite = true; + } + else if (ref_pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + + // There is another priority bit in GBC, that can still override sprite priority + if (LCDC.Bit(0) && tile_data_latch[2].Bit(7) && Core.GBC_compat) + { + use_sprite = false; + } + } + + if (use_sprite) + { + pal_num = sprite_attr & 7; + + if (!Core.GBC_compat) + { + pal_num = sprite_attr.Bit(4) ? 1 : 0; + + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + if (Core.GBC_compat) + { + if (use_sprite) + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)OBJ_palette[pal_num * 4 + s_pixel]; + } + else + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)BG_palette[pal_num * 4 + pixel]; + } + } + else + { + if (use_sprite) + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)OBJ_palette[pal_num * 4 + pixel]; + } + else + { + Core._vidbuffer[LY * 160 + pixel_counter] = (int)BG_palette[pixel]; + } + } + + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + hbl_countdown = 5; + } + } + else if (pixel_counter < 0) + { + pixel_counter++; + } + render_counter++; + } + + if (!fetch_sprite) + { + if (!pre_render) + { + // before we go on to read case 3, we need to know if we stall there or not + // Gekkio's tests show that if sprites are at position 0 or 1 (mod 8) + // then it takes an extra cycle (1 or 2 more t-states) to process them + + if (!no_sprites && (pixel_counter < 160)) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + going_to_fetch = true; + fetch_sprite = true; + + if ((SL_sprites[i * 4 + 1] % 8) < 2) + { + fetch_sprite_01 = true; + } + if ((SL_sprites[i * 4 + 1] % 8) > 3) + { + fetch_sprite_4 = true; + } + } + } + } + } + + switch (read_case) + { + case 0: // read a background tile + if ((internal_cycle % 2) == 0) + { + // calculate the row number of the tiles to be fetched + y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; + + temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; + tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; + VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; + + BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; + } + else + { + read_case = 1; + if (!pre_render) + { + tile_inc++; + } + } + break; + + case 1: // read from tile graphics (0) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + } + else + { + read_case = 2; + } + break; + + case 2: // read from tile graphics (1) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + } + else + { + if (pre_render) + { + // here we set up rendering + pre_render = false; + render_offset = scroll_x % 8; + render_counter = 0; + latch_counter = 0; + read_case = 0; + } + else + { + read_case = 3; + } + } + break; + + case 3: // read from sprite data + if ((internal_cycle % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 0; + latch_new_data = true; + } + break; + + case 4: // read from window data + if ((window_counter % 2) == 0) + { + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; + tile_data[2] = Core.VRAM[0x3800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; + VRAM_sel = tile_data[2].Bit(3) ? 1 : 0; + BG_V_flip = tile_data[2].Bit(6) & Core.GBC_compat; + } + else + { + window_tile_inc++; + read_case = 5; + } + window_counter++; + break; + + case 5: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + } + else + { + read_case = 6; + } + window_counter++; + break; + + case 6: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + + if (BG_V_flip) + { + y_scroll_offset = 7 - y_scroll_offset; + } + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + tile_data[1] = Core.VRAM[(VRAM_sel * 0x2000) + 0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (window_pre_render) + { + // here we set up rendering + // unlike for the normal background case, there is no pre-render period for the window + // so start shifting in data to the screen right away + render_offset = 0; + render_counter = 8; + latch_counter = 0; + latch_new_data = true; + + window_pre_render = false; + read_case = 4; + } + else + { + read_case = 7; + } + } + window_counter++; + break; + + case 7: // read from sprite data + if ((window_counter % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 4; + latch_new_data = true; + } + window_counter++; + break; + + case 8: // done reading, we are now in phase 0 + + pre_render = true; + + // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here + if (hbl_countdown > 0) + { + hbl_countdown--; + if (hbl_countdown == 0) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + break; + + case 9: + // this is a degenerate case for starting the window at 0 + // kevtris' timing doc indicates an additional normal BG access + // but this information is thrown away, so it's faster to do this then constantly check + // for it in read case 0 + read_case = 4; + break; + } + internal_cycle++; + + if (latch_new_data) + { + latch_new_data = false; + tile_data_latch[0] = tile_data[0]; + tile_data_latch[1] = tile_data[1]; + tile_data_latch[2] = tile_data[2]; + } + } + + // every in range sprite takes 6 cycles to process + // sprites located at x=0 still take 6 cycles to process even though they don't appear on screen + // sprites above x=168 do not take any cycles to process however + if (fetch_sprite) + { + if (going_to_fetch) + { + going_to_fetch = false; + sprite_fetch_counter = first_fetch ? 2 : 0; + first_fetch = false; + + if (fetch_sprite_01) + { + sprite_fetch_counter += 2; + fetch_sprite_01 = false; + } + + if (fetch_sprite_4) + { + sprite_fetch_counter -= 2; + fetch_sprite_4 = false; + } + + int last_eval = 0; + + // at this time it is unknown what each cycle does, but we only need to accurately keep track of cycles + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + sprite_fetch_counter += 6; + evaled_sprites |= (1 << i); + last_eval = SL_sprites[i * 4 + 1]; + } + } + + // if we didn't evaluate all the sprites immediately, 2 more cycles are added to restart it + if (evaled_sprites != (Math.Pow(2,SL_sprites_index) - 1)) + { + if ((last_eval % 8) == 0) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 1) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 2) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 3) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 4) { sprite_fetch_counter += 3; } + else { sprite_fetch_counter += 2; } + } + } + else + { + sprite_fetch_counter--; + if (sprite_fetch_counter == 0) + { + fetch_sprite = false; + } + } + } + + } + + public override void process_sprite() + { + int y; + int VRAM_temp = (SL_sprites[sl_use_index * 4 + 3].Bit(3) && Core.GBC_compat) ? 1 : 0; + + if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 15 - y; + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 7 - y; + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + else + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + (SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(VRAM_temp * 0x2000) + SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + + if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) + { + int b0, b1, b2, b3, b4, b5, b6, b7 = 0; + for (int i = 0; i < 2; i++) + { + b0 = (sprite_sel[i] & 0x01) << 7; + b1 = (sprite_sel[i] & 0x02) << 5; + b2 = (sprite_sel[i] & 0x04) << 3; + b3 = (sprite_sel[i] & 0x08) << 1; + b4 = (sprite_sel[i] & 0x10) >> 1; + b5 = (sprite_sel[i] & 0x20) >> 3; + b6 = (sprite_sel[i] & 0x40) >> 5; + b7 = (sprite_sel[i] & 0x80) >> 7; + + sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); + } + } + } + + // normal DMA moves twice as fast in double speed mode on GBC + // So give it it's own function so we can seperate it from PPU tick + public override void DMA_tick() + { + // Note that DMA is halted when the CPU is halted + if (DMA_start && !Core.cpu.halted) + { + if (DMA_clock >= 4) + { + DMA_OAM_access = false; + if ((DMA_clock % 4) == 1) + { + // the cpu can't access memory during this time, but we still need the ppu to be able to. + DMA_start = false; + // Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses + // So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF) + byte DMA_actual = DMA_addr; + if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; } + DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc)); + DMA_start = true; + } + else if ((DMA_clock % 4) == 3) + { + Core.OAM[DMA_inc] = DMA_byte; + + if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } + } + } + + DMA_clock++; + + if (DMA_clock == 648) + { + DMA_start = false; + DMA_OAM_access = true; + } + } + } + + // order sprites according to x coordinate + // note that for sprites of equal x coordinate, priority goes to first on the list + public override void reorder_and_assemble_sprites() + { + sprite_ordered_index = 0; + + // In CGB mode, sprites are ordered solely based on their position in OAM, so they are already ordered + + if (Core.GBC_compat) + { + for (int j = 0; j < SL_sprites_index; j++) + { + sl_use_index = j; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; + sprite_ordered_index++; + } + } + else + { + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < SL_sprites_index; j++) + { + if (SL_sprites[j * 4 + 1] == i) + { + sl_use_index = j; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; + sprite_ordered_index++; + } + } + } + } + + bool have_pixel = false; + byte s_pixel = 0; + byte sprite_attr = 0; + + for (int i = 0; i < 160; i++) + { + have_pixel = false; + for (int j = 0; j < SL_sprites_index; j++) + { + if ((i >= (SL_sprites_ordered[j * 4] - 8)) && + (i < SL_sprites_ordered[j * 4]) && + !have_pixel) + { + // we can use the current sprite, so pick out a pixel for it + int t_index = i - (SL_sprites_ordered[j * 4] - 8); + + t_index = 7 - t_index; + + sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1); + sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1); + + s_pixel = (byte)(sprite_data[0] + sprite_data[1]); + sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3]; + + // pixel color of 0 is transparent, so if this is the case we dont have a pixel + if (s_pixel != 0) + { + have_pixel = true; + } + } + } + + if (have_pixel) + { + sprite_present_list[i] = 1; + sprite_pixel_list[i] = s_pixel; + sprite_attr_list[i] = sprite_attr; + } + else + { + sprite_present_list[i] = 0; + } + } + } + + public override void OAM_scan(int OAM_cycle) + { + // we are now in STAT mode 2 + // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? + if (OAM_cycle == 0) + { + OAM_access_read = false; + + OAM_scan_index = 0; + SL_sprites_index = 0; + write_sprite = 0; + } + + // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw + // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close + if (OAM_cycle < 10) + { + // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) + SL_sprites[OAM_cycle * 4] = 0; + SL_sprites[OAM_cycle * 4 + 1] = 0; + SL_sprites[OAM_cycle * 4 + 2] = 0; + SL_sprites[OAM_cycle * 4 + 3] = 0; + } + else + { + if (write_sprite == 0) + { + if (OAM_scan_index < 40) + { + ushort temp = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4] : (ushort)0xFF; + // (sprite Y - 16) equals LY, we have a sprite + if ((temp - 16) <= LY && + ((temp - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) + { + // always pick the first 10 in range sprites + if (SL_sprites_index < 10) + { + SL_sprites[SL_sprites_index * 4] = temp; + + write_sprite = 1; + } + else + { + // if we already have 10 sprites, there's nothing to do, increment the index + OAM_scan_index++; + } + } + else + { + OAM_scan_index++; + } + } + } + else + { + ushort temp2 = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4 + write_sprite] : (ushort)0xFF; + SL_sprites[SL_sprites_index * 4 + write_sprite] = temp2; + write_sprite++; + + if (write_sprite == 4) + { + write_sprite = 0; + SL_sprites_index++; + OAM_scan_index++; + } + } + } + } + + public void color_compute_BG() + { + uint R; + uint G; + uint B; + + if ((BG_bytes_index % 2) == 0) + { + R = (uint)(BG_bytes[BG_bytes_index] & 0x1F); + G = (uint)(((BG_bytes[BG_bytes_index] & 0xE0) | ((BG_bytes[BG_bytes_index + 1] & 0x03) << 8)) >> 5); + B = (uint)((BG_bytes[BG_bytes_index + 1] & 0x7C) >> 2); + } + else + { + R = (uint)(BG_bytes[BG_bytes_index - 1] & 0x1F); + G = (uint)(((BG_bytes[BG_bytes_index - 1] & 0xE0) | ((BG_bytes[BG_bytes_index] & 0x03) << 8)) >> 5); + B = (uint)((BG_bytes[BG_bytes_index] & 0x7C) >> 2); + } + + uint retR = ((R * 13 + G * 2 + B) >> 1) & 0xFF; + uint retG = ((G * 3 + B) << 1) & 0xFF; + uint retB = ((R * 3 + G * 2 + B * 11) >> 1) & 0xFF; + + BG_palette[BG_bytes_index >> 1] = (uint)(0xFF000000 | (retR << 16) | (retG << 8) | retB); + } + + public void color_compute_OBJ() + { + uint R; + uint G; + uint B; + + if ((OBJ_bytes_index % 2) == 0) + { + R = (uint)(OBJ_bytes[OBJ_bytes_index] & 0x1F); + G = (uint)(((OBJ_bytes[OBJ_bytes_index] & 0xE0) | ((OBJ_bytes[OBJ_bytes_index + 1] & 0x03) << 8)) >> 5); + B = (uint)((OBJ_bytes[OBJ_bytes_index + 1] & 0x7C) >> 2); + } + else + { + R = (uint)(OBJ_bytes[OBJ_bytes_index - 1] & 0x1F); + G = (uint)(((OBJ_bytes[OBJ_bytes_index - 1] & 0xE0) | ((OBJ_bytes[OBJ_bytes_index] & 0x03) << 8)) >> 5); + B = (uint)((OBJ_bytes[OBJ_bytes_index] & 0x7C) >> 2); + } + + uint retR = ((R * 13 + G * 2 + B) >> 1) & 0xFF; + uint retG = ((G * 3 + B) << 1) & 0xFF; + uint retB = ((R * 3 + G * 2 + B * 11) >> 1) & 0xFF; + + OBJ_palette[OBJ_bytes_index >> 1] = (uint)(0xFF000000 | (retR << 16) | (retG << 8) | retB); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("pal_transfer_byte", ref BG_transfer_byte); + ser.Sync("spr_transfer_byte", ref OBJ_transfer_byte); + ser.Sync("HDMA_src_hi", ref HDMA_src_hi); + ser.Sync("HDMA_src_lo", ref HDMA_src_lo); + ser.Sync("HDMA_dest_hi", ref HDMA_dest_hi); + ser.Sync("HDMA_dest_lo", ref HDMA_dest_lo); + ser.Sync("HDMA_tick", ref HDMA_tick); + ser.Sync("HDMA_byte", ref HDMA_byte); + + ser.Sync("VRAM_sel", ref VRAM_sel); + ser.Sync("BG_V_flip", ref BG_V_flip); + ser.Sync("HDMA_mode", ref HDMA_mode); + ser.Sync("cur_DMA_src", ref cur_DMA_src); + ser.Sync("cur_DMA_dest", ref cur_DMA_dest); + ser.Sync("HDMA_length", ref HDMA_length); + ser.Sync("HDMA_countdown", ref HDMA_countdown); + ser.Sync("HBL_HDMA_count", ref HBL_HDMA_count); + ser.Sync("last_HBL", ref last_HBL); + ser.Sync("HBL_HDMA_go", ref HBL_HDMA_go); + ser.Sync("HBL_test", ref HBL_test); + + ser.Sync("BG_bytes", ref BG_bytes, false); + ser.Sync("OBJ_bytes", ref OBJ_bytes, false); + ser.Sync("BG_bytes_inc", ref BG_bytes_inc); + ser.Sync("OBJ_bytes_inc", ref OBJ_bytes_inc); + ser.Sync("BG_bytes_index", ref BG_bytes_index); + ser.Sync("OBJ_bytes_index", ref OBJ_bytes_index); + + base.SyncState(ser); + } + + public override void Reset() + { + LCDC = 0; + STAT = 0x80; + scroll_y = 0; + scroll_x = 0; + LY = 0; + LYC = 0; + DMA_addr = 0xFF; + BGP = 0xFF; + obj_pal_0 = 0xFF; + obj_pal_1 = 0xFF; + window_y = 0x0; + window_x = 0x0; + window_x_latch = 0xFF; + LY_inc = 1; + no_scan = false; + OAM_access_read = true; + VRAM_access_read = true; + OAM_access_write = true; + VRAM_access_write = true; + DMA_OAM_access = true; + + cycle = 0; + LYC_INT = false; + HBL_INT = false; + VBL_INT = false; + OAM_INT = false; + + stat_line = false; + stat_line_old = false; + + window_counter = 0; + window_pre_render = false; + window_started = false; + window_tile_inc = 0; + window_y_tile = 0; + window_x_tile = 0; + window_y_tile_inc = 0; + + BG_bytes_inc = false; + OBJ_bytes_inc = false; + BG_bytes_index = 0; + OBJ_bytes_index = 0; + BG_transfer_byte = 0; + OBJ_transfer_byte = 0; + + HDMA_src_hi = 0; + HDMA_src_lo = 0; + HDMA_dest_hi = 0; + HDMA_dest_lo = 0; + + VRAM_sel = 0; + BG_V_flip = false; + HDMA_active = false; + HDMA_mode = false; + cur_DMA_src = 0; + cur_DMA_dest = 0; + HDMA_length = 0; + HDMA_countdown = 0; + HBL_HDMA_count = 0; + last_HBL = 0; + HBL_HDMA_go = false; + HBL_test = false; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs index 778b17acfc..a1323f8564 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IDebuggable.cs @@ -69,9 +69,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { - get { return cpu.TotalExecutedCycles; } + get { return (long)cpu.TotalExecutedCycles; } } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index bc094d10b3..14570a59f2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -13,6 +13,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; public byte controller_state; + public ushort Acc_X_state; + public ushort Acc_Y_state; public bool in_vblank_old; public bool in_vblank; public bool vblank_rise; @@ -50,8 +52,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (controller.IsPressed("Power")) { - // it seems that theMachine.Reset() doesn't clear ram, etc - // this should leave hsram intact but clear most other things HardReset(); } @@ -63,8 +63,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (_scanlineCallback != null) { - GetGPU(); - _scanlineCallback(ppu.LCDC); + if (_scanlineCallbackLine == -1) + { + GetGPU(); + _scanlineCallback(ppu.LCDC); + } } if (_islag) @@ -114,16 +117,41 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk while (!vblank_rise) { + // These things do not change speed in GBC double spped mode audio.tick(); - timer.tick_1(); ppu.tick(); - serialport.serial_transfer_tick(); + if (Use_MT) { mapper.Mapper_Tick(); } - if (Use_RTC) { mapper.RTC_Tick(); } + if (!HDMA_transfer) + { + // These things all tick twice as fast in GBC double speed mode + ppu.DMA_tick(); + timer.tick_1(); + serialport.serial_transfer_tick(); + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); + timer.tick_2(); - cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); - - timer.tick_2(); + if (double_speed) + { + ppu.DMA_tick(); + timer.tick_1(); + serialport.serial_transfer_tick(); + cpu.ExecuteOne(ref REG_FF0F, REG_FFFF); + timer.tick_2(); + } + } + else + { + timer.tick_1(); + timer.tick_2(); + cpu.TotalExecutedCycles++; + if (double_speed) + { + timer.tick_1(); + timer.tick_2(); + cpu.TotalExecutedCycles++; + } + } if (in_vblank && !in_vblank_old) { @@ -131,7 +159,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } ticker++; - if (ticker > 10000000) { throw new Exception("ERROR: Unable to Resolve Frame"); } + //if (ticker > 10000000) { vblank_rise = true; }//throw new Exception("ERROR: Unable to Resolve Frame"); } in_vblank_old = in_vblank; } @@ -139,15 +167,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk vblank_rise = false; } - public void RunCPUCycle() + // Switch Speed (GBC only) + public int SpeedFunc(int temp) { - + if (is_GBC) + { + if (speed_switch) + { + speed_switch = false; + + int ret = double_speed ? 50000 : 25000; // actual time needs checking + double_speed = !double_speed; + return ret; + } + + // if we are not switching speed, return 0 + return 0; + } + + // if we are in GB mode, return 0 indicating not switching speed + return 0; } public void GetControllerState(IController controller) { InputCallbacks.Call(); controller_state = _controllerDeck.ReadPort1(controller); + + Acc_X_state = _controllerDeck.ReadAccX1(controller); + Acc_Y_state = _controllerDeck.ReadAccY1(controller); } public int Frame => _frame; @@ -171,6 +219,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk Marshal.FreeHGlobal(iptr1); Marshal.FreeHGlobal(iptr2); Marshal.FreeHGlobal(iptr3); + + audio.DisposeSound(); } #region Video provider diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs index 375abbd592..ecb0940aa5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IMemoryDomains.cs @@ -40,10 +40,23 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _rom.Length, MemoryDomain.Endian.Little, addr => _rom[addr], - (addr, value) => ZP_RAM[addr] = value, + (addr, value) => _rom[addr] = value, + 1), + new MemoryDomainDelegate( + "VRAM", + VRAM.Length, + MemoryDomain.Endian.Little, + addr => VRAM[addr], + (addr, value) => VRAM[addr] = value, 1) }; + if (cart_RAM != null) + { + var CartRam = new MemoryDomainByteArray("Cart RAM", MemoryDomain.Endian.Little, cart_RAM, true, 1); + domains.Add(CartRam); + } + MemoryDomains = new MemoryDomainList(domains); (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs index d1fa270c30..8e9c0ddbe8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISaveRam.cs @@ -7,19 +7,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public byte[] CloneSaveRam() { - return (byte[])_sram.Clone(); + return (byte[])cart_RAM.Clone(); } public void StoreSaveRam(byte[] data) { - Buffer.BlockCopy(data, 0, _sram, 0, data.Length); + Buffer.BlockCopy(data, 0, cart_RAM, 0, data.Length); + Console.WriteLine("loading SRAM here"); } public bool SaveRamModified { get { - return false; + return has_bat & _syncSettings.Use_SRAM; } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs index 56cea73ae2..b6acb3d8d0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.ISettable.cs @@ -58,23 +58,50 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public class GBSyncSettings { - private string _port1 = GBHawkControllerDeck.DefaultControllerName; + [JsonIgnore] + public string Port1 = GBHawkControllerDeck.DefaultControllerName; + + public enum ControllerType + { + Default, + Tilt + } [JsonIgnore] - public string Port1 + private ControllerType _GBController; + + [DisplayName("Controller")] + [Description("Select Controller Type")] + [DefaultValue(ControllerType.Default)] + public ControllerType GBController { - get { return _port1; } + get { return _GBController; } set { - if (!GBHawkControllerDeck.ValidControllerTypes.ContainsKey(value)) - { - throw new InvalidOperationException("Invalid controller type: " + value); - } + if (value == ControllerType.Default) { Port1 = GBHawkControllerDeck.DefaultControllerName; } + else { Port1 = "Gameboy Controller + Tilt"; } - _port1 = value; + _GBController = value; } } + public enum ConsoleModeType + { + Auto, + GB, + GBC + } + + [DisplayName("Console Mode")] + [Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")] + [DefaultValue(ConsoleModeType.Auto)] + public ConsoleModeType ConsoleMode { get; set; } + + [DisplayName("CGB in GBA")] + [Description("Emulate GBA hardware running a CGB game, instead of CGB hardware. Relevant only for titles that detect the presense of a GBA, such as Shantae.")] + [DefaultValue(false)] + public bool GBACGB { get; set; } + [DisplayName("RTC Initial Time")] [Description("Set the initial RTC time in terms of elapsed seconds.")] [DefaultValue(0)] @@ -84,6 +111,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk set { _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } } + [DisplayName("Timer Div Initial Time")] + [Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] + [DefaultValue(8)] + public int DivInitialTime + { + get { return _DivInitialTime; } + set { _DivInitialTime = Math.Min((ushort)65535, (ushort)value); } + } + [DisplayName("Use Existing SaveRAM")] [Description("When true, existing SaveRAM will be loaded at boot up")] [DefaultValue(false)] @@ -92,6 +128,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk [JsonIgnore] private int _RTCInitialTime; + [JsonIgnore] + public ushort _DivInitialTime; public GBSyncSettings Clone() diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index dbccea7f07..c7f2f425f3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -62,6 +62,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _controllerDeck.SyncState(ser); ser.Sync("controller_state", ref controller_state); + ser.Sync("Acc_X_state", ref Acc_X_state); + ser.Sync("Acc_Y_state", ref Acc_Y_state); ser.Sync("in_vblank", ref in_vblank); ser.Sync("in_vblank_old", ref in_vblank_old); ser.Sync("vblank_rise", ref vblank_rise); @@ -70,21 +72,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("REG_FFFF", ref REG_FFFF); ser.Sync("REG_FF0F", ref REG_FF0F); - ser.Sync("enable_VBL", ref enable_VBL); - ser.Sync("enable_LCDC", ref enable_PRS); - ser.Sync("enable_TIMO", ref enable_TIMO); - ser.Sync("enable_SER", ref enable_SER); - ser.Sync("enable_STAT", ref enable_STAT); // memory domains ser.Sync("RAM", ref RAM, false); ser.Sync("ZP_RAM", ref ZP_RAM, false); - ser.Sync("CHR_RAM", ref CHR_RAM, false); - ser.Sync("BG_map_1", ref BG_map_1, false); - ser.Sync("BG_map_2", ref BG_map_2, false); + ser.Sync("VRAM", ref VRAM, false); ser.Sync("OAM", ref OAM, false); - ser.Sync("Use_RTC", ref Use_RTC); + ser.Sync("_bios", ref _bios, false); + + ser.Sync("RAM_Bank", ref RAM_Bank); + ser.Sync("VRAM_Bank", ref VRAM_Bank); + ser.Sync("is_GBC", ref is_GBC); + ser.Sync("GBC_compat", ref GBC_compat); + ser.Sync("double_speed", ref double_speed); + ser.Sync("speed_switch", ref speed_switch); + ser.Sync("HDMA_transfer", ref HDMA_transfer); + + ser.Sync("Use_MT", ref Use_MT); + ser.Sync("addr_access", ref addr_access); // probably a better way to do this if (cart_RAM != null) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index 02660d1f7f..1a65681be0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk "GBHawk", "", isPorted: false, - isReleased: false)] + isReleased: true)] [ServiceNotApplicable(typeof(IDriveLight))] public partial class GBHawk : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, IGameboyCommon, ISettable @@ -28,31 +28,42 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte REG_FFFF; // The unused bits in this register (interrupt flags) are always set public byte REG_FF0F = 0xE0; - public bool enable_VBL; - public bool enable_STAT; - public bool enable_TIMO; - public bool enable_SER; - public bool enable_PRS; - // memory domains - public byte[] RAM = new byte[0x2000]; + public byte[] RAM = new byte[0x8000]; // only 0x2000 available to GB public byte[] ZP_RAM = new byte[0x80]; - public byte[] CHR_RAM = new byte[0x1800]; - public byte[] BG_map_1 = new byte[0x400]; - public byte[] BG_map_2 = new byte[0x400]; + /* + * VRAM is arranged as: + * 0x1800 Tiles + * 0x400 BG Map 1 + * 0x400 BG Map 2 + * 0x1800 Tiles + * 0x400 CA Map 1 + * 0x400 CA Map 2 + * Only the top set is available in GB (i.e. VRAM_Bank = 0) + */ + public byte[] VRAM = new byte[0x4000]; public byte[] OAM = new byte[0xA0]; - public readonly byte[] _rom; - public readonly byte[] _bios; - public readonly byte[] _sram = new byte[2048]; + public int RAM_Bank; + public byte VRAM_Bank; + public bool is_GBC; + public bool GBC_compat; // compatibility mode for GB games played on GBC + public bool double_speed; + public bool speed_switch; + public bool HDMA_transfer; // stalls CPU when in progress + + public byte[] _bios; + public readonly byte[] _rom; public readonly byte[] header = new byte[0x50]; public byte[] cart_RAM; + public bool has_bat; private int _frame = 0; - public bool Use_RTC; + public bool Use_MT; + public ushort addr_access; public MapperBase mapper; @@ -64,7 +75,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public Audio audio; public SerialPort serialport; - [CoreConstructor("GB")] + private static byte[] GBA_override = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 }; + + [CoreConstructor("GB", "GBC")] public GBHawk(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings) { var ser = new BasicServiceProvider(this); @@ -75,9 +88,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WriteMemory = WriteMemory, PeekMemory = PeekMemory, DummyReadMemory = ReadMemory, - OnExecFetch = ExecFetch + OnExecFetch = ExecFetch, + SpeedFunc = SpeedFunc, }; - ppu = new PPU(); + timer = new Timer(); audio = new Audio(); serialport = new SerialPort(); @@ -88,15 +102,55 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk _syncSettings = (GBSyncSettings)syncSettings ?? new GBSyncSettings(); _controllerDeck = new GBHawkControllerDeck(_syncSettings.Port1); - byte[] Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); + byte[] Bios = null; + + // Load up a BIOS and initialize the correct PPU + if (_syncSettings.ConsoleMode == GBSyncSettings.ConsoleModeType.Auto) + { + if (game.System == "GB") + { + Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GB_PPU(); + } + else + { + Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GBC_PPU(); + is_GBC = true; + } + + } + else if (_syncSettings.ConsoleMode == GBSyncSettings.ConsoleModeType.GB) + { + Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GB_PPU(); + } + else + { + Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load"); + ppu = new GBC_PPU(); + is_GBC = true; + } if (Bios == null) { throw new MissingFirmwareException("Missing Gamboy Bios"); } - + _bios = Bios; + // Here we modify the BIOS if GBA mode is set (credit to ExtraTricky) + if (is_GBC && _syncSettings.GBACGB) + { + for (int i = 0; i < 13; i++) + { + _bios[i + 0xF3] = (byte)((GBA_override[i] + _bios[i + 0xF3]) & 0xFF); + } + } + + // CPU needs to know about GBC status too + cpu.is_GBC = is_GBC; + Buffer.BlockCopy(rom, 0x100, header, 0, 0x50); string hash_md5 = null; @@ -126,17 +180,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk SetupMemoryDomains(); HardReset(); - iptr0 = Marshal.AllocHGlobal(CHR_RAM.Length + BG_map_1.Length + BG_map_2.Length + 1); + iptr0 = Marshal.AllocHGlobal(VRAM.Length + 1); iptr1 = Marshal.AllocHGlobal(OAM.Length + 1); - iptr2 = Marshal.AllocHGlobal(color_palette.Length * 2 * 8 + 1); - iptr3 = Marshal.AllocHGlobal(color_palette.Length * 8 + 1); + iptr2 = Marshal.AllocHGlobal(color_palette.Length * 8 * 8 + 1); + iptr3 = Marshal.AllocHGlobal(color_palette.Length * 8 * 8 + 1); _scanlineCallback = null; } #region GPUViewer - public bool IsCGBMode() => false; + public bool IsCGBMode() => is_GBC; public IntPtr iptr0 = IntPtr.Zero; public IntPtr iptr1 = IntPtr.Zero; @@ -147,39 +201,39 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { get { - byte[] temp = new byte[CHR_RAM.Length + BG_map_1.Length + BG_map_2.Length]; - - for (int i = 0; i < CHR_RAM.Length; i++) - { - temp[i] = CHR_RAM[i]; - } - for (int i = 0; i < BG_map_1.Length; i++) - { - temp[CHR_RAM.Length + i] = BG_map_1[i]; - } - for (int i = 0; i < BG_map_2.Length; i++) - { - temp[CHR_RAM.Length + BG_map_1.Length + i] = BG_map_2[i]; - } - - Marshal.Copy(temp, 0, iptr0, temp.Length); + Marshal.Copy(VRAM, 0, iptr0, VRAM.Length); Marshal.Copy(OAM, 0, iptr1, OAM.Length); - int[] cp2 = new int[8]; - for (int i = 0; i < 4; i++) + if (is_GBC) { - cp2[i] = (int)color_palette[(ppu.obj_pal_0 >> (i * 2)) & 3]; - cp2[i + 4] = (int)color_palette[(ppu.obj_pal_1 >> (i * 2)) & 3]; - } - Marshal.Copy(cp2, 0, iptr2, cp2.Length); + int[] cp2 = new int[32]; + int[] cp = new int[32]; + for (int i = 0; i < 32; i++) + { + cp2[i] = (int)ppu.OBJ_palette[i]; + cp[i] = (int)ppu.BG_palette[i]; + } - int[] cp = new int[4]; - for (int i = 0; i < 4; i++) + Marshal.Copy(cp2, 0, iptr2, ppu.OBJ_palette.Length); + Marshal.Copy(cp, 0, iptr3, ppu.BG_palette.Length); + } + else { - cp[i] = (int)color_palette[(ppu.BGP >> (i * 2)) & 3]; - } - Marshal.Copy(cp, 0, iptr3, cp.Length); + int[] cp2 = new int[8]; + for (int i = 0; i < 4; i++) + { + cp2[i] = (int)color_palette[(ppu.obj_pal_0 >> (i * 2)) & 3]; + cp2[i + 4] = (int)color_palette[(ppu.obj_pal_1 >> (i * 2)) & 3]; + } + Marshal.Copy(cp2, 0, iptr2, cp2.Length); + int[] cp = new int[4]; + for (int i = 0; i < 4; i++) + { + cp[i] = (int)color_palette[(ppu.BGP >> (i * 2)) & 3]; + } + Marshal.Copy(cp, 0, iptr3, cp.Length); + } return new GPUMemoryAreas(iptr0, iptr1, iptr2, iptr3); } @@ -218,14 +272,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk private void HardReset() { GB_bios_register = 0; // bios enable + GBC_compat = true; in_vblank = true; // we start off in vblank since the LCD is off in_vblank_old = true; - // Start off with RAM all 0xFF (the game 'X' (proto) expects this) - for (int i = 0; i < RAM.Length; i++) - { - RAM[i] = 0xFF; - } + RAM_Bank = 1; // RAM bank always starts as 1 (even writing zero still sets 1) Register_Reset(); timer.Reset(); @@ -250,39 +301,39 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk switch (header[0x47]) { - case 0x0: mapper = new MapperDefault(); mppr = "NROM"; break; - case 0x1: mapper = new MapperMBC1(); mppr = "MBC1"; break; - case 0x2: mapper = new MapperMBC1(); mppr = "MBC1"; break; - case 0x3: mapper = new MapperMBC1(); mppr = "MBC1"; break; - case 0x5: mapper = new MapperMBC2(); mppr = "MBC2"; break; - case 0x6: mapper = new MapperMBC2(); mppr = "MBC2"; break; - case 0x8: mapper = new MapperDefault(); mppr = "NROM"; break; - case 0x9: mapper = new MapperDefault(); mppr = "NROM"; break; - case 0xB: mapper = new MapperMMM01(); mppr = "MMM01"; break; - case 0xC: mapper = new MapperMMM01(); mppr = "MMM01"; break; - case 0xD: mapper = new MapperMMM01(); mppr = "MMM01"; break; - case 0xF: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x10: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x11: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x12: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x13: mapper = new MapperMBC3(); mppr = "MBC3"; break; - case 0x19: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1A: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1B: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1C: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1D: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x1E: mapper = new MapperMBC5(); mppr = "MBC5"; break; - case 0x20: mapper = new MapperMBC6(); mppr = "MBC6"; break; - case 0x22: mapper = new MapperMBC7(); mppr = "MBC7"; break; - case 0xFC: mapper = new MapperCamera(); mppr = "CAM"; break; - case 0xFD: mapper = new MapperTAMA5(); mppr = "TAMA5"; break; - case 0xFE: mapper = new MapperHuC3(); mppr = "HuC3"; break; - case 0xFF: mapper = new MapperHuC1(); mppr = "HuC1"; break; + case 0x0: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x1: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x2: mapper = new MapperMBC1(); mppr = "MBC1"; break; + case 0x3: mapper = new MapperMBC1(); mppr = "MBC1"; has_bat = true; break; + case 0x5: mapper = new MapperMBC2(); mppr = "MBC2"; break; + case 0x6: mapper = new MapperMBC2(); mppr = "MBC2"; has_bat = true; break; + case 0x8: mapper = new MapperDefault(); mppr = "NROM"; break; + case 0x9: mapper = new MapperDefault(); mppr = "NROM"; has_bat = true; break; + case 0xB: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xC: mapper = new MapperMMM01(); mppr = "MMM01"; break; + case 0xD: mapper = new MapperMMM01(); mppr = "MMM01"; has_bat = true; break; + case 0xF: mapper = new MapperMBC3(); mppr = "MBC3"; has_bat = true; break; + case 0x10: mapper = new MapperMBC3(); mppr = "MBC3"; has_bat = true; break; + case 0x11: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x12: mapper = new MapperMBC3(); mppr = "MBC3"; break; + case 0x13: mapper = new MapperMBC3(); mppr = "MBC3"; has_bat = true; break; + case 0x19: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1A: mapper = new MapperMBC5(); mppr = "MBC5"; has_bat = true; break; + case 0x1B: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1C: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1D: mapper = new MapperMBC5(); mppr = "MBC5"; break; + case 0x1E: mapper = new MapperMBC5(); mppr = "MBC5"; has_bat = true; break; + case 0x20: mapper = new MapperMBC6(); mppr = "MBC6"; break; + case 0x22: mapper = new MapperMBC7(); mppr = "MBC7"; has_bat = true; break; + case 0xFC: mapper = new MapperCamera(); mppr = "CAM"; break; + case 0xFD: mapper = new MapperTAMA5(); mppr = "TAMA5"; break; + case 0xFE: mapper = new MapperHuC3(); mppr = "HuC3"; break; + case 0xFF: mapper = new MapperHuC1(); mppr = "HuC1"; break; // Bootleg mappers // NOTE: Sachen mapper selection does not account for scrambling, so if another bootleg mapper // identifies itself as 0x31, this will need to be modified - case 0x31: mapper = new MapperSachen2(); mppr = "Schn2"; break; + case 0x31: mapper = new MapperSachen2(); mppr = "Schn2"; break; case 0x4: case 0x7: @@ -299,8 +350,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk // mapper not implemented Console.WriteLine(header[0x47]); throw new Exception("Mapper not implemented"); - break; - } // special case for multi cart mappers @@ -313,6 +362,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk mapper = new MapperMBC1Multi(); } + // special case for bootlegs + if ((_rom.HashMD5(0, _rom.Length) == "CAE0998A899DF2EE6ABA8E7695C2A096")) + { + Console.WriteLine("Using RockMan 8 (Unlicensed) Mapper"); + mapper = new MapperRM8(); + } + if ((_rom.HashMD5(0, _rom.Length) == "D3C1924D847BC5D125BF54C2076BE27A")) + { + Console.WriteLine("Using Sachen 1 (Unlicensed) Mapper"); + mapper = new MapperSachen1(); + mppr = "Schn1"; + } + Console.Write("Mapper: "); Console.WriteLine(mppr); @@ -335,12 +397,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk case 5: cart_RAM = new byte[0x10000]; break; + case 0: + Console.WriteLine("Mapper Number indicates Battery Backed RAM but none present."); + Console.WriteLine("Disabling Battery Setting."); + has_bat = false; + break; } // Sachen maper not known to have RAM if ((mppr == "Schn1") || (mppr == "Schn2")) { cart_RAM = null; + Use_MT = true; } // mbc2 carts have built in RAM @@ -349,32 +417,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk cart_RAM = new byte[0x200]; } + // mbc7 has 256 bytes of RAM, regardless of any header info + if (mppr == "MBC7") + { + cart_RAM = new byte[0x100]; + has_bat = true; + } + mapper.Core = this; mapper.Initialize(); - if (cart_RAM != null) + if (cart_RAM != null && (mppr != "MBC7")) { - Console.Write("RAM: "); Console.WriteLine(cart_RAM.Length); - if (_syncSettings.Use_SRAM) + for (int i = 0; i < cart_RAM.Length; i++) { - // load cartRAM here - } - else - { - for (int i = 0; i < cart_RAM.Length; i++) - { - cart_RAM[i] = 0xFF; - } + cart_RAM[i] = 0xFF; } } - // Extra RTC initialization for mbc3 if (mppr == "MBC3") { - Use_RTC = true; + Use_MT = true; int days = (int)Math.Floor(_syncSettings.RTCInitialTime / 86400.0); int days_upper = ((days & 0x100) >> 8) | ((days & 0x200) >> 2); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs index 684b022ab8..5f21be47e0 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllerDeck.cs @@ -36,6 +36,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return Port1.Read(c); } + public ushort ReadAccX1(IController c) + { + return Port1.ReadAccX(c); + } + + public ushort ReadAccY1(IController c) + { + return Port1.ReadAccY(c); + } + public ControllerDefinition Definition { get; } public void SyncState(Serializer ser) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs index 87006a5539..3ef5ffaf4d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawkControllers.cs @@ -15,6 +15,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { byte Read(IController c); + ushort ReadAccX(IController c); + + ushort ReadAccY(IController c); + ControllerDefinition Definition { get; } void SyncState(Serializer ser); @@ -81,6 +85,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return result; } + public ushort ReadAccX(IController c) + { + return 0; + } + + public ushort ReadAccY(IController c) + { + return 0; + } + private static readonly string[] BaseDefinition = { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" @@ -91,4 +105,113 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk //nothing } } + + [DisplayName("Gameboy Controller + Tilt")] + public class StandardTilt : IPort + { + public StandardTilt(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Gameboy Controller + Tilt", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList(), + FloatControls = { "P" + PortNum + " Tilt X", "P" + PortNum + " Tilt Y" }, + FloatRanges = { new[] { -45.0f, 0, 45.0f }, new[] { -45.0f, 0, 45.0f } } + }; + } + + public int PortNum { get; } + + public float theta, phi, theta_prev, phi_prev; + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + + if (c.IsPressed(Definition.BoolButtons[0])) + { + result -= 4; + } + if (c.IsPressed(Definition.BoolButtons[1])) + { + result -= 8; + } + if (c.IsPressed(Definition.BoolButtons[2])) + { + result -= 2; + } + if (c.IsPressed(Definition.BoolButtons[3])) + { + result -= 1; + } + if (c.IsPressed(Definition.BoolButtons[4])) + { + result -= 128; + } + if (c.IsPressed(Definition.BoolButtons[5])) + { + result -= 64; + } + if (c.IsPressed(Definition.BoolButtons[6])) + { + result -= 32; + } + if (c.IsPressed(Definition.BoolButtons[7])) + { + result -= 16; + } + + return result; + } + + // acc x is the result of rotating around body y AFTER rotating around body x + // therefore this control scheme gives decreasing sensitivity in X as Y rotation inscreases + public ushort ReadAccX(IController c) + { + theta_prev = theta; + phi_prev = phi; + + theta = (float)(c.GetFloat(Definition.FloatControls[1]) * Math.PI / 180.0); + phi = (float)(c.GetFloat(Definition.FloatControls[0]) * Math.PI / 180.0); + + float temp = (float)(Math.Cos(theta) * Math.Sin(phi)); + + // here we add in rates of change parameters. + // a typical rate of change for a fast rotation is guessed at 0.5 rad / frame + // since rotations about X have less of a moment arm compared to by, we take 1/5 of the effect as a baseline + float temp2 = (float)((phi - phi_prev) / 0.5 * 25); + + return (ushort)(0x81D0 - Math.Floor(temp * 125) - temp2); + } + + // acc y is just the sine of the angle + // we assume that ReadAccX is called first, which updates the the states + public ushort ReadAccY(IController c) + { + float temp = (float)Math.Sin(theta); + + // here we add in rates of change parameters. + // a typical rate of change for a fast rotation is guessed at 0.5 rad / frame + // further it will be assumed that the resulting acceleration is roughly eqvuivalent to gravity + float temp2 = (float)((theta - theta_prev)/0.5 * 125); + + return (ushort)(0x81D0 - Math.Floor(temp * 125) + temp2); + } + + private static readonly string[] BaseDefinition = + { + "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" + }; + + public void SyncState(Serializer ser) + { + // since we need rate of change of angle, need to savestate them + ser.Sync("theta", ref theta); + } + } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs new file mode 100644 index 0000000000..255cbf9d1b --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GB_PPU.cs @@ -0,0 +1,1182 @@ +using System; +using BizHawk.Emulation.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + public class GB_PPU : PPU + { + public override byte ReadReg(int addr) + { + byte ret = 0; + + switch (addr) + { + case 0xFF40: ret = LCDC; break; // LCDC + case 0xFF41: ret = STAT; break; // STAT + case 0xFF42: ret = scroll_y; break; // SCY + case 0xFF43: ret = scroll_x; break; // SCX + case 0xFF44: ret = LY; break; // LY + case 0xFF45: ret = LYC; break; // LYC + case 0xFF46: ret = DMA_addr; break; // DMA + case 0xFF47: ret = BGP; break; // BGP + case 0xFF48: ret = obj_pal_0; break; // OBP0 + case 0xFF49: ret = obj_pal_1; break; // OBP1 + case 0xFF4A: ret = window_y; break; // WY + case 0xFF4B: ret = window_x; break; // WX + } + + return ret; + } + + public override void WriteReg(int addr, byte value) + { + switch (addr) + { + case 0xFF40: // LCDC + if (LCDC.Bit(7) && !value.Bit(7)) + { + VRAM_access_read = true; + VRAM_access_write = true; + OAM_access_read = true; + OAM_access_write = true; + } + + if (!LCDC.Bit(7) && value.Bit(7)) + { + // don't draw for one frame after turning on + blank_frame = true; + } + + LCDC = value; + break; + case 0xFF41: // STAT + // writing to STAT during mode 0 or 2 causes a STAT IRQ + if (LCDC.Bit(7)) + { + if (((STAT & 3) == 0) || ((STAT & 3) == 1)) + { + LYC_INT = true; + } + } + STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); + break; + case 0xFF42: // SCY + scroll_y = value; + break; + case 0xFF43: // SCX + scroll_x = value; + // calculate the column number of the tile to start with + x_tile = (int)Math.Floor((float)(scroll_x) / 8); + break; + case 0xFF44: // LY + LY = 0; /*reset*/ + break; + case 0xFF45: // LYC + LYC = value; + if (LCDC.Bit(7)) + { + if (LY != LYC) { STAT &= 0xFB; } + else { STAT |= 0x4; } + } + break; + case 0xFF46: // DMA + DMA_addr = value; + DMA_start = true; + DMA_OAM_access = true; + DMA_clock = 0; + DMA_inc = 0; + break; + case 0xFF47: // BGP + BGP = value; + break; + case 0xFF48: // OBP0 + obj_pal_0 = value; + break; + case 0xFF49: // OBP1 + obj_pal_1 = value; + break; + case 0xFF4A: // WY + window_y = value; + break; + case 0xFF4B: // WX + window_x = value; + break; + } + } + + public override void tick() + { + // the ppu only does anything if it is turned on via bit 7 of LCDC + if (LCDC.Bit(7)) + { + // start the next scanline + if (cycle == 456) + { + // scanline callback + if ((LY + LY_inc) == Core._scanlineCallbackLine) + { + if (Core._scanlineCallback != null) + { + Core.GetGPU(); + Core._scanlineCallback(LCDC); + } + } + + cycle = 0; + + LY += LY_inc; + Core.cpu.LY = LY; + + no_scan = false; + + if (LY == 0 && LY_inc == 0) + { + LY_inc = 1; + Core.in_vblank = false; + + VBL_INT = false; + if (STAT.Bit(3)) { HBL_INT = true; } + + STAT &= 0xFC; + + // special note here, the y coordiate of the window is kept if the window is deactivated + // meaning it will pick up where it left off if re-enabled later + // so we don't reset it in the scanline loop + window_y_tile = 0; + window_y_tile_inc = 0; + window_started = false; + if (!LCDC.Bit(5)) { window_is_reset = true; } + } + + // Automatically restore access to VRAM at this time (force end drawing) + // Who Framed Roger Rabbit seems to run into this. + VRAM_access_write = true; + VRAM_access_read = true; + + if (LY == 144) + { + Core.in_vblank = true; + } + } + + // exit vblank if LCD went from off to on + if (LCD_was_off) + { + //VBL_INT = false; + Core.in_vblank = false; + LCD_was_off = false; + + // we exit vblank into mode 0 for 4 cycles + // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 + STAT &= 0xFC; + + // also the LCD doesn't turn on right away + + // also, the LCD does not enter mode 2 on scanline 0 when first turned on + no_scan = true; + cycle = 8; + } + + // the VBL stat is continuously asserted + if ((LY >= 144)) + { + if (STAT.Bit(4)) + { + if ((cycle >= 4) && (LY == 144)) + { + VBL_INT = true; + } + else if (LY > 144) + { + VBL_INT = true; + } + } + + if ((cycle == 4) && (LY == 144)) { + + HBL_INT = false; + + // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled + STAT &= 0xFC; + STAT |= 0x01; + + if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x01; + } + + if ((LY >= 144) && (cycle == 4)) + { + // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if ((LY == 153) && (cycle == 6)) + { + LY = 0; + LY_inc = 0; + Core.cpu.LY = LY; + } + } + + if (!Core.in_vblank) + { + if (no_scan) + { + // timings are slightly different if we just turned on the LCD + // there is no mode 2 (presumably it missed the trigger) + // mode 3 is very short, probably in some self test mode before turning on? + + if (cycle == 8) + { + if (LY != LYC) + { + LYC_INT = false; + STAT &= 0xFB; + } + + if ((LY == LYC) && !STAT.Bit(2)) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + if (cycle == 84) + { + + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + + OAM_access_read = false; + OAM_access_write = false; + VRAM_access_read = false; + VRAM_access_write = false; + } + + if (cycle == 256) + { + STAT &= 0xFC; + OAM_INT = false; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + else + { + if (cycle < 80) + { + if (cycle == 2) + { + if (LY != 0) { if (STAT.Bit(5)) { OAM_INT = true; } } + } + else if (cycle == 4) + { + // apparently, writes can make it to OAM one cycle longer then reads + OAM_access_write = false; + + // here mode 2 will be set to true and interrupts fired if enabled + STAT &= 0xFC; + STAT |= 0x2; + + if (LY == 0) { if (STAT.Bit(5)) { OAM_INT = true; } } + + HBL_INT = false; + } + + // here OAM scanning is performed + OAM_scan(cycle); + } + else if ((cycle >= 80) && (LY < 144)) + { + if (cycle == 84) + { + STAT &= 0xFC; + STAT |= 0x03; + OAM_INT = false; + OAM_access_write = false; + VRAM_access_write = false; + } + + // render the screen and handle hblank + render(cycle - 80); + } + } + } + + if ((LY_inc == 0)) + { + if (cycle == 12) + { + LYC_INT = false; + STAT &= 0xFB; + + // Special case of LY = LYC + if ((LY == LYC) && !STAT.Bit(2)) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + + // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 + if (STAT.Bit(5)) { OAM_INT = true; } + } + + if (cycle == 92) { OAM_INT = false; } + } + + // here LY=LYC will be asserted or cleared (but only if LY isnt 0 as that's a special case) + if ((cycle == 2) && (LY != 0)) + { + if (LY_inc == 1) + { + LYC_INT = false; + STAT &= 0xFB; + } + + } + else if ((cycle == 4) && (LY != 0)) + { + if ((LY == LYC) && !STAT.Bit(2)) + { + // set STAT coincidence FLAG and interrupt flag if it is enabled + STAT |= 0x04; + if (STAT.Bit(6)) { LYC_INT = true; } + } + } + + cycle++; + } + else + { + STAT &= 0xFC; + + VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; + + Core.in_vblank = true; + + LCD_was_off = true; + + LY = 0; + Core.cpu.LY = LY; + + cycle = 0; + } + + // assert the STAT IRQ line if the line went from zero to 1 + stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; + + if (stat_line && !stat_line_old) + { + if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x02; + } + + stat_line_old = stat_line; + + // process latch delays + //latch_delay(); + } + + // might be needed, not sure yet + public override void latch_delay() + { + //BGP_l = BGP; + } + + public override void render(int render_cycle) + { + // we are now in STAT mode 3 + // NOTE: presumably the first necessary sprite is fetched at sprite evaulation + // i.e. just keeping track of the lowest x-value sprite + if (render_cycle == 0) + { + OAM_access_read = false; + OAM_access_write = true; + VRAM_access_read = false; + + // window X is latched for the scanline, mid-line changes have no effect + window_x_latch = window_x; + + OAM_scan_index = 0; + read_case = 0; + internal_cycle = 0; + pre_render = true; + tile_inc = 0; + pixel_counter = -8; + sl_use_index = 0; + fetch_sprite = false; + fetch_sprite_01 = false; + fetch_sprite_4 = false; + going_to_fetch = false; + first_fetch = true; + no_sprites = false; + evaled_sprites = 0; + + window_pre_render = false; + window_latch = LCDC.Bit(5); + + // TODO: If Window is turned on midscanline what happens? When is this check done exactly? + if ((window_started && window_latch) || (window_is_reset && !window_latch && (LY >= window_y))) + { + window_y_tile_inc++; + if (window_y_tile_inc==8) + { + window_y_tile_inc = 0; + window_y_tile++; + window_y_tile %= 32; + } + } + window_started = false; + + if (SL_sprites_index == 0) { no_sprites = true; } + // it is much easier to process sprites if we order them according to the rules of sprite priority first + if (!no_sprites) { reorder_and_assemble_sprites(); } + + } + + // before anything else, we have to check if windowing is in effect + if (window_latch && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) + { + /* + Console.Write(LY); + Console.Write(" "); + Console.Write(cycle); + Console.Write(" "); + Console.Write(window_y_tile_inc); + Console.Write(" "); + Console.Write(window_x_latch); + Console.Write(" "); + Console.WriteLine(pixel_counter); + */ + if (window_x_latch <= 7) + { + // if the window starts at zero, we still do the first access to the BG + // but then restart all over again at the window + read_case = 9; + } + else + { + // otherwise, just restart the whole process as if starting BG again + read_case = 4; + } + window_pre_render = true; + + window_counter = 0; + render_counter = 0; + + window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); + + window_tile_inc = 0; + window_started = true; + window_is_reset = false; + } + + if (!pre_render && !fetch_sprite) + { + // start shifting data into the LCD + if (render_counter >= (render_offset + 8)) + { + pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; + pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; + + int ref_pixel = pixel; + if (LCDC.Bit(0)) + { + pixel = (BGP >> (pixel * 2)) & 3; + } + else + { + pixel = 0; + } + + // now we have the BG pixel, we next need the sprite pixel + if (!no_sprites) + { + bool have_sprite = false; + int s_pixel = 0; + int sprite_attr = 0; + + if (sprite_present_list[pixel_counter] == 1) + { + have_sprite = true; + s_pixel = sprite_pixel_list[pixel_counter]; + sprite_attr = sprite_attr_list[pixel_counter]; + } + + if (have_sprite) + { + bool use_sprite = false; + if (LCDC.Bit(1)) + { + if (!sprite_attr.Bit(7)) + { + use_sprite = true; + } + else if (ref_pixel == 0) + { + use_sprite = true; + } + + if (!LCDC.Bit(0)) + { + use_sprite = true; + } + } + + if (use_sprite) + { + if (sprite_attr.Bit(4)) + { + pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; + } + else + { + pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; + } + } + } + } + + // based on sprite priority and pixel values, pick a final pixel color + Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel]; + pixel_counter++; + + if (pixel_counter == 160) + { + read_case = 8; + hbl_countdown = 5; + } + } + else if (pixel_counter < 0) + { + pixel_counter++; + } + render_counter++; + } + + if (!fetch_sprite) + { + if (!pre_render) + { + // before we go on to read case 3, we need to know if we stall there or not + // Gekkio's tests show that if sprites are at position 0 or 1 (mod 8) + // then it takes an extra cycle (1 or 2 more t-states) to process them + + if (!no_sprites && (pixel_counter < 160)) + { + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + going_to_fetch = true; + fetch_sprite = true; + + if ((SL_sprites[i * 4 + 1] % 8) < 2) + { + fetch_sprite_01 = true; + } + if ((SL_sprites[i * 4 + 1] % 8) > 3) + { + fetch_sprite_4 = true; + } + } + } + } + } + + switch (read_case) + { + case 0: // read a background tile + if ((internal_cycle % 2) == 0) + { + // calculate the row number of the tiles to be fetched + y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; + + temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(3) ? 1 : 0) * 0x400 + temp_fetch]; + } + else + { + read_case = 1; + if (!pre_render) + { + tile_inc++; + } + } + break; + + case 1: // read from tile graphics (0) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + tile_data[0] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + tile_data[0] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + + } + else + { + read_case = 2; + } + break; + + case 2: // read from tile graphics (1) + if ((internal_cycle % 2) == 0) + { + y_scroll_offset = (scroll_y + LY) % 8; + + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (pre_render) + { + // here we set up rendering + pre_render = false; + render_offset = scroll_x % 8; + render_counter = 0; + latch_counter = 0; + read_case = 0; + } + else + { + read_case = 3; + } + } + break; + + case 3: // read from sprite data + if ((internal_cycle % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 0; + latch_new_data = true; + } + break; + + case 4: // read from window data + if ((window_counter % 2) == 0) + { + temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; + tile_byte = Core.VRAM[0x1800 + (LCDC.Bit(6) ? 1 : 0) * 0x400 + temp_fetch]; ; + } + else + { + window_tile_inc++; + read_case = 5; + } + window_counter++; + break; + + case 5: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + + if (LCDC.Bit(4)) + { + + tile_data[0] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2]; + + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7)) + { + tile_byte -= 256; + } + + tile_data[0] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; + } + } + else + { + read_case = 6; + } + window_counter++; + break; + + case 6: // read from tile graphics (for the window) + if ((window_counter % 2) == 0) + { + y_scroll_offset = (window_y_tile_inc) % 8; + if (LCDC.Bit(4)) + { + // if LCDC somehow changed between the two reads, make sure we have a positive number + if (tile_byte < 0) + { + tile_byte += 256; + } + + tile_data[1] = Core.VRAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + else + { + // same as before except now tile byte represents a signed byte + if (tile_byte.Bit(7) && tile_byte > 0) + { + tile_byte -= 256; + } + + tile_data[1] = Core.VRAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; + } + + } + else + { + if (window_pre_render) + { + // here we set up rendering + // unlike for the normal background case, there is no pre-render period for the window + // so start shifting in data to the screen right away + render_offset = 0; + render_counter = 8; + latch_counter = 0; + latch_new_data = true; + + window_pre_render = false; + read_case = 4; + } + else + { + read_case = 7; + } + } + window_counter++; + break; + + case 7: // read from sprite data + if ((window_counter % 2) == 0) + { + // nothing to do if not fetching + } + else + { + read_case = 4; + latch_new_data = true; + } + window_counter++; + break; + + case 8: // done reading, we are now in phase 0 + pre_render = true; + + // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here + if (hbl_countdown > 0) + { + hbl_countdown--; + if (hbl_countdown == 0) + { + STAT &= 0xFC; + STAT |= 0x00; + + if (STAT.Bit(3)) { HBL_INT = true; } + + OAM_access_read = true; + OAM_access_write = true; + VRAM_access_read = true; + VRAM_access_write = true; + } + } + break; + + case 9: + // this is a degenerate case for starting the window at 0 + // kevtris' timing doc indicates an additional normal BG access + // but this information is thrown away, so it's faster to do this then constantly check + // for it in read case 0 + read_case = 4; + break; + } + internal_cycle++; + + if (latch_new_data) + { + latch_new_data = false; + tile_data_latch[0] = tile_data[0]; + tile_data_latch[1] = tile_data[1]; + } + } + + // every in range sprite takes 6 cycles to process + // sprites located at x=0 still take 6 cycles to process even though they don't appear on screen + // sprites above x=168 do not take any cycles to process however + if (fetch_sprite) + { + if (going_to_fetch) + { + going_to_fetch = false; + sprite_fetch_counter = first_fetch ? 2 : 0; + first_fetch = false; + + if (fetch_sprite_01) + { + sprite_fetch_counter += 2; + fetch_sprite_01 = false; + } + + if (fetch_sprite_4) + { + sprite_fetch_counter -= 2; + fetch_sprite_4 = false; + } + + int last_eval = 0; + + // at this time it is unknown what each cycle does, but we only need to accurately keep track of cycles + for (int i = 0; i < SL_sprites_index; i++) + { + if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && + (pixel_counter < (SL_sprites[i * 4 + 1])) && + !evaled_sprites.Bit(i)) + { + sprite_fetch_counter += 6; + evaled_sprites |= (1 << i); + last_eval = SL_sprites[i * 4 + 1]; + } + } + + // if we didn't evaluate all the sprites immediately, 2 more cycles are added to restart it + if (evaled_sprites != (Math.Pow(2,SL_sprites_index) - 1)) + { + if ((last_eval % 8) == 0) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 1) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 2) { sprite_fetch_counter += 3; } + else if ((last_eval % 8) == 3) { sprite_fetch_counter += 2; } + else if ((last_eval % 8) == 4) { sprite_fetch_counter += 3; } + else { sprite_fetch_counter += 2; } + } + } + else + { + sprite_fetch_counter--; + if (sprite_fetch_counter == 0) + { + fetch_sprite = false; + } + } + } + } + + public override void process_sprite() + { + int y; + + if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 15 - y; + sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + y = 7 - y; + sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + else + { + if (LCDC.Bit(2)) + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; + } + else + { + y = LY - (SL_sprites[sl_use_index * 4] - 16); + sprite_sel[0] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; + sprite_sel[1] = Core.VRAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; + } + } + + if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) + { + int b0, b1, b2, b3, b4, b5, b6, b7 = 0; + for (int i = 0; i < 2; i++) + { + b0 = (sprite_sel[i] & 0x01) << 7; + b1 = (sprite_sel[i] & 0x02) << 5; + b2 = (sprite_sel[i] & 0x04) << 3; + b3 = (sprite_sel[i] & 0x08) << 1; + b4 = (sprite_sel[i] & 0x10) >> 1; + b5 = (sprite_sel[i] & 0x20) >> 3; + b6 = (sprite_sel[i] & 0x40) >> 5; + b7 = (sprite_sel[i] & 0x80) >> 7; + + sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); + } + } + } + + // normal DMA moves twice as fast in double speed mode on GBC + // So give it it's own function so we can seperate it from PPU tick + public override void DMA_tick() + { + // Note that DMA is halted when the CPU is halted + if (DMA_start && !Core.cpu.halted) + { + if (DMA_clock >= 4) + { + DMA_OAM_access = false; + if ((DMA_clock % 4) == 1) + { + // the cpu can't access memory during this time, but we still need the ppu to be able to. + DMA_start = false; + // Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses + // So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF) + byte DMA_actual = DMA_addr; + if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; } + DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc)); + DMA_start = true; + } + else if ((DMA_clock % 4) == 3) + { + Core.OAM[DMA_inc] = DMA_byte; + + if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } + } + } + + DMA_clock++; + + if (DMA_clock == 648) + { + DMA_start = false; + DMA_OAM_access = true; + } + } + } + + // order sprites according to x coordinate + // note that for sprites of equal x coordinate, priority goes to first on the list + public override void reorder_and_assemble_sprites() + { + sprite_ordered_index = 0; + + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < SL_sprites_index; j++) + { + if (SL_sprites[j * 4 + 1] == i) + { + sl_use_index = j; + process_sprite(); + SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; + SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; + SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; + sprite_ordered_index++; + } + } + } + + bool have_pixel = false; + byte s_pixel = 0; + byte sprite_attr = 0; + + for (int i = 0; i < 160; i++) + { + have_pixel = false; + for (int j = 0; j < SL_sprites_index; j++) + { + if ((i >= (SL_sprites_ordered[j * 4] - 8)) && + (i < SL_sprites_ordered[j * 4]) && + !have_pixel) + { + // we can use the current sprite, so pick out a pixel for it + int t_index = i - (SL_sprites_ordered[j * 4] - 8); + + t_index = 7 - t_index; + + sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1); + sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1); + + s_pixel = (byte)(sprite_data[0] + sprite_data[1]); + sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3]; + + // pixel color of 0 is transparent, so if this is the case we dont have a pixel + if (s_pixel != 0) + { + have_pixel = true; + } + } + } + + if (have_pixel) + { + sprite_present_list[i] = 1; + sprite_pixel_list[i] = s_pixel; + sprite_attr_list[i] = sprite_attr; + } + else + { + sprite_present_list[i] = 0; + } + } + } + + public override void OAM_scan(int OAM_cycle) + { + // we are now in STAT mode 2 + // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? + if (OAM_cycle == 0) + { + OAM_access_read = false; + + OAM_scan_index = 0; + SL_sprites_index = 0; + write_sprite = 0; + } + + // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw + // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close + if (OAM_cycle < 10) + { + // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) + SL_sprites[OAM_cycle * 4] = 0; + SL_sprites[OAM_cycle * 4 + 1] = 0; + SL_sprites[OAM_cycle * 4 + 2] = 0; + SL_sprites[OAM_cycle * 4 + 3] = 0; + } + else + { + if (write_sprite == 0) + { + if (OAM_scan_index < 40) + { + ushort temp = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4] : (ushort)0xFF; + // (sprite Y - 16) equals LY, we have a sprite + if ((temp - 16) <= LY && + ((temp - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) + { + // always pick the first 10 in range sprites + if (SL_sprites_index < 10) + { + SL_sprites[SL_sprites_index * 4] = temp; + + write_sprite = 1; + } + else + { + // if we already have 10 sprites, there's nothing to do, increment the index + OAM_scan_index++; + } + } + else + { + OAM_scan_index++; + } + } + } + else + { + ushort temp2 = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4 + write_sprite] : (ushort)0xFF; + SL_sprites[SL_sprites_index * 4 + write_sprite] = temp2; + write_sprite++; + + if (write_sprite == 4) + { + write_sprite = 0; + SL_sprites_index++; + OAM_scan_index++; + } + } + } + } + + public override void Reset() + { + LCDC = 0; + STAT = 0x80; + scroll_y = 0; + scroll_x = 0; + LY = 0; + LYC = 0; + DMA_addr = 0xFF; + BGP = 0xFF; + obj_pal_0 = 0xFF; + obj_pal_1 = 0xFF; + window_y = 0x0; + window_x = 0x0; + window_x_latch = 0xFF; + LY_inc = 1; + no_scan = false; + OAM_access_read = true; + VRAM_access_read = true; + OAM_access_write = true; + VRAM_access_write = true; + DMA_OAM_access = true; + + cycle = 0; + LYC_INT = false; + HBL_INT = false; + VBL_INT = false; + OAM_INT = false; + + stat_line = false; + stat_line_old = false; + + window_counter = 0; + window_pre_render = false; + window_started = false; + window_is_reset = true; + window_tile_inc = 0; + window_y_tile = 0; + window_x_tile = 0; + window_y_tile_inc = 0; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs index daf9161210..a3ef71cad3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/HW_Registers.cs @@ -99,11 +99,66 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ret = ppu.ReadReg(addr); break; + // Speed Control for GBC + case 0xFF4D: + if (is_GBC) + { + ret = (byte)(((double_speed ? 1 : 0) << 7) + ((speed_switch ? 1 : 0))); + } + else + { + ret = 0xFF; + } + break; + + case 0xFF4F: // VBK + if (is_GBC) + { + ret = VRAM_Bank; + } + else + { + ret = 0xFF; + } + break; + // Bios control register. Not sure if it is readable case 0xFF50: ret = 0xFF; break; + // PPU Regs for GBC + case 0xFF51: + case 0xFF52: + case 0xFF53: + case 0xFF54: + case 0xFF55: + case 0xFF68: + case 0xFF69: + case 0xFF6A: + case 0xFF6B: + if (is_GBC) + { + ret = ppu.ReadReg(addr); + } + else + { + ret = 0xFF; + } + break; + + // Speed Control for GBC + case 0xFF70: + if (is_GBC) + { + ret = (byte)RAM_Bank; + } + else + { + ret = 0xFF; + } + break; + // interrupt control register case 0xFFFF: ret = REG_FFFF; @@ -254,6 +309,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ppu.WriteReg(addr, value); break; + // GBC compatibility register (I think) + case 0xFF4C: + if ((value != 0xC0) && (value != 0x80)) + { + Console.Write("GBC Compatibility? "); + Console.WriteLine(value); + GBC_compat = false; + } + break; + + // Speed Control for GBC + case 0xFF4D: + if (is_GBC) + { + speed_switch = (value & 1) > 0; + } + break; + + // VBK + case 0xFF4F: + if (is_GBC && !ppu.HDMA_active) + { + VRAM_Bank = (byte)(value & 1); + } + break; + // Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs case 0xFF50: //Console.WriteLine(value); @@ -263,14 +344,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } break; + // PPU Regs for GBC + case 0xFF51: + case 0xFF52: + case 0xFF53: + case 0xFF54: + case 0xFF55: + case 0xFF68: + case 0xFF69: + case 0xFF6A: + case 0xFF6B: + if (is_GBC) + { + ppu.WriteReg(addr, value); + } + break; + + // RAM Bank in GBC mode + case 0xFF70: + //Console.WriteLine(value); + if (is_GBC) + { + RAM_Bank = value & 7; + if (RAM_Bank == 0) { RAM_Bank = 1; } + } + break; + // interrupt control register case 0xFFFF: REG_FFFF = value; - enable_VBL = REG_FFFF.Bit(0); - enable_STAT = REG_FFFF.Bit(1); - enable_TIMO = REG_FFFF.Bit(2); - enable_SER = REG_FFFF.Bit(3); - enable_PRS = REG_FFFF.Bit(4); // check if enabling any of the bits triggered an IRQ for (int i = 0; i < 5; i++) @@ -285,6 +387,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu.FlagI = false; } break; + + default: + Console.Write(addr); + Console.Write(" "); + Console.WriteLine(value); + break; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs index ce4ed125bb..89b8ce1034 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/MapperBase.cs @@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { } - public virtual void RTC_Tick() + public virtual void Mapper_Tick() { } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs index f1ed2a807d..e0649675ab 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC3.cs @@ -158,7 +158,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk RTC_regs[index] = value; } - public override void RTC_Tick() + public override void Mapper_Tick() { if (!halt) { diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs index 529f48ca4a..3c1ea0c60f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC5.cs @@ -4,29 +4,60 @@ using System; namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { - // Default mapper with no bank switching + // MBC5, common mapper for GBC games public class MapperMBC5 : MapperBase { + public int ROM_bank; + public int RAM_bank; + public bool RAM_enable; + public int ROM_mask; + public int RAM_mask; + public override void Initialize() { - // nothing to initialize + ROM_bank = 1; + RAM_bank = 0; + RAM_enable = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + + // some games have sizes that result in a degenerate ROM, account for it here + if (ROM_mask > 4) { ROM_mask |= 3; } + + RAM_mask = 0; + if (Core.cart_RAM != null) + { + RAM_mask = Core.cart_RAM.Length / 0x2000 - 1; + if (Core.cart_RAM.Length == 0x800) { RAM_mask = 0; } + } } public override byte ReadMemory(ushort addr) { - if (addr < 0x8000) + if (addr < 0x4000) { return Core._rom[addr]; } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } else { if (Core.cart_RAM != null) { - return Core.cart_RAM[addr - 0xA000]; + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + return Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000]; + } + else + { + return 0xFF; + } + } else { - return 0; + return 0xFF; } } } @@ -40,13 +71,40 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (addr < 0x8000) { - // no mapping hardware available + if (addr < 0x2000) + { + RAM_enable = (value & 0xF) == 0xA; + } + else if (addr < 0x3000) + { + value &= 0xFF; + + ROM_bank &= 0x100; + ROM_bank |= value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x4000) + { + value &= 1; + + ROM_bank &= 0xFF; + ROM_bank |= (value << 8); + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + RAM_bank = value & 0xF; + RAM_bank &= RAM_mask; + } } else { if (Core.cart_RAM != null) { - Core.cart_RAM[addr - 0xA000] = value; + if (RAM_enable && (((addr - 0xA000) + RAM_bank * 0x2000) < Core.cart_RAM.Length)) + { + Core.cart_RAM[(addr - 0xA000) + RAM_bank * 0x2000] = value; + } } } } @@ -55,5 +113,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { WriteMemory(addr, value); } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_Bank", ref RAM_bank); + ser.Sync("RAM_Mask", ref RAM_mask); + ser.Sync("RAM_enable", ref RAM_enable); + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs index 24d957c653..41b9dd1506 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_MBC7.cs @@ -4,31 +4,81 @@ using System; namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { - // Default mapper with no bank switching + // Mapper with built in EEPROM, also used with Kirby's tilt 'n tumble + // The EEPROM contains 256 bytes of read/write memory public class MapperMBC7 : MapperBase { + public int ROM_bank; + public bool RAM_enable_1, RAM_enable_2; + public int ROM_mask; + public byte acc_x_low; + public byte acc_x_high; + public byte acc_y_low; + public byte acc_y_high; + public bool is_erased; + + // EEPROM related + public bool CS_prev; + public bool CLK_prev; + public bool DI_prev; + public bool DO; + public bool instr_read; + public bool perf_instr; + public int instr_bit_counter; + public int instr; + public bool WR_EN; + public int EE_addr; + public int instr_case; + public int instr_clocks; + public int EE_value; + public int countdown; + public bool countdown_start; + + public override void Initialize() { - // nothing to initialize + ROM_bank = 1; + RAM_enable_1 = RAM_enable_2 = false; + ROM_mask = Core._rom.Length / 0x4000 - 1; + + // some games have sizes that result in a degenerate ROM, account for it here + if (ROM_mask > 4) { ROM_mask |= 3; } + + acc_x_low = 0; + acc_x_high = 0x80; + acc_y_low = 0; + acc_y_high = 0x80; } public override byte ReadMemory(ushort addr) { - if (addr < 0x8000) + if (addr < 0x4000) { return Core._rom[addr]; } - else + else if (addr < 0x8000) { - if (Core.cart_RAM != null) + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else if (addr < 0xA000) + { + return 0xFF; + } + else if (addr < 0xB000) + { + if (RAM_enable_1 && RAM_enable_2) { - return Core.cart_RAM[addr - 0xA000]; + return Register_Access_Read(addr); } else { - return 0; + return 0xFF; } } + else + { + return 0xFF; + } } public override byte PeekMemory(ushort addr) @@ -38,15 +88,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override void WriteMemory(ushort addr, byte value) { - if (addr < 0x8000) + if (addr < 0xA000) { - // no mapping hardware available + if (addr < 0x2000) + { + RAM_enable_1 = (value & 0xF) == 0xA; + } + else if (addr < 0x4000) + { + value &= 0xFF; + + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + //Console.WriteLine(value); + + ROM_bank &= 0x100; + ROM_bank |= value; + ROM_bank &= ROM_mask; + } + else if (addr < 0x6000) + { + RAM_enable_2 = (value & 0xF0) == 0x40; + } } else { - if (Core.cart_RAM != null) + if (RAM_enable_1 && RAM_enable_2) { - Core.cart_RAM[addr - 0xA000] = value; + Register_Access_Write(addr, value); } } } @@ -55,5 +123,320 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { WriteMemory(addr, value); } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + ser.Sync("RAM_enable_1", ref RAM_enable_1); + ser.Sync("RAM_enable_2", ref RAM_enable_2); + ser.Sync("acc_x_low", ref acc_x_low); + ser.Sync("acc_x_high", ref acc_x_high); + ser.Sync("acc_y_low", ref acc_y_low); + ser.Sync("acc_y_high", ref acc_y_high); + ser.Sync("is_erased", ref is_erased); + + ser.Sync("CS_prev", ref CS_prev); + ser.Sync("CLK_prev", ref CLK_prev); + ser.Sync("DI_prev", ref DI_prev); + ser.Sync("DO", ref DO); + ser.Sync("instr_read", ref instr_read); + ser.Sync("perf_instr", ref perf_instr); + ser.Sync("instr_bit_counter", ref instr_bit_counter); + ser.Sync("instr", ref instr); + ser.Sync("WR_EN", ref WR_EN); + ser.Sync("EE_addr", ref EE_addr); + ser.Sync("instr_case", ref instr_case); + ser.Sync("instr_clocks", ref instr_clocks); + ser.Sync("EE_value", ref EE_value); + ser.Sync("countdown", ref countdown); + ser.Sync("countdown_start", ref countdown_start); + } + + public byte Register_Access_Read(ushort addr) + { + if ((addr & 0xA0F0) == 0xA000) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA010) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA020) + { + return acc_x_low; + } + else if ((addr & 0xA0F0) == 0xA030) + { + return acc_x_high; + } + else if ((addr & 0xA0F0) == 0xA040) + { + return acc_y_low; + } + else if ((addr & 0xA0F0) == 0xA050) + { + return acc_y_high; + } + else if ((addr & 0xA0F0) == 0xA060) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA070) + { + return 0xFF; + } + else if ((addr & 0xA0F0) == 0xA080) + { + return (byte)((CS_prev ? 0x80 : 0) | + (CLK_prev ? 0x40 : 0) | + (DI_prev ? 2 : 0) | + (DO ? 1 : 0)); + } + else + { + return 0xFF; + } + } + + public void Register_Access_Write(ushort addr, byte value) + { + if ((addr & 0xA0F0) == 0xA000) + { + if (value == 0x55) + { + //Console.WriteLine("Erasing ACC"); + + is_erased = true; + acc_x_low = 0x00; + acc_x_high = 0x80; + acc_y_low = 0x00; + acc_y_high = 0x80; + } + } + else if ((addr & 0xA0F0) == 0xA010) + { + if ((value == 0xAA) && is_erased) + { + // latch new accelerometer values + //Console.WriteLine("Latching ACC"); + acc_x_low = (byte)(Core.Acc_X_state & 0xFF); + acc_x_high = (byte)((Core.Acc_X_state & 0xFF00) >> 8); + acc_y_low = (byte)(Core.Acc_Y_state & 0xFF); + acc_y_high = (byte)((Core.Acc_Y_state & 0xFF00) >> 8); + } + } + else if ((addr & 0xA0F0) == 0xA080) + { + // EEPROM writes + EEPROM_write(value); + } + } + + private void EEPROM_write(byte value) + { + bool CS = value.Bit(7); + bool CLK = value.Bit(6); + bool DI = value.Bit(1); + + // if we deselect the chip, complete instructions or countdown and stop + if (!CS) + { + CS_prev = CS; + CLK_prev = CLK; + DI_prev = DI; + + DO = true; + countdown_start = false; + perf_instr = false; + instr_read = false; + + //Console.Write("Chip De-selected: "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + + if (!instr_read && !perf_instr) + { + // if we aren't performing an operation or reading an incoming instruction, we are waiting for one + // this is signalled by CS and DI both being 1 while CLK goes from 0 to 1 + if (CLK && !CLK_prev && DI && CS) + { + instr_read = true; + instr_bit_counter = 0; + instr = 0; + DO = false; + //Console.Write("Initiating command: "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + else if (instr_read && CLK && !CLK_prev) + { + // all instructions are 10 bits long + instr = (instr << 1) | ((value & 2) >> 1); + + instr_bit_counter++; + if (instr_bit_counter == 10) + { + instr_read = false; + instr_clocks = 0; + EE_addr = instr & 0x7F; + EE_value = 0; + + switch (instr & 0x300) + { + case 0x0: + switch (instr & 0xC0) + { + case 0x0: // disable writes + instr_case = 0; + WR_EN = false; + DO = true; + break; + case 0x40: // fill mem with value + instr_case = 1; + perf_instr = true; + break; + case 0x80: // fill mem with FF + instr_case = 2; + if (WR_EN) + { + for (int i = 0; i < 256; i++) + { + Core.cart_RAM[i] = 0xFF; + } + } + DO = true; + break; + case 0xC0: // enable writes + instr_case = 3; + WR_EN = true; + DO = true; + break; + } + break; + case 0x100: // write to address + instr_case = 4; + perf_instr = true; + break; + case 0x200: // read from address + instr_case = 5; + perf_instr = true; + break; + case 0x300: // set address to FF + instr_case = 6; + if (WR_EN) + { + Core.cart_RAM[EE_addr * 2] = 0xFF; + Core.cart_RAM[EE_addr * 2 + 1] = 0xFF; + } + DO = true; + break; + } + + //Console.Write("Selected Command: "); + //Console.Write(instr_case); + //Console.Write(" "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + else if (perf_instr && CLK && !CLK_prev) + { + //Console.Write("Command In progress, Cycle: "); + //Console.Write(instr_clocks); + //Console.Write(" "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + + // for commands that require additional clocking + switch (instr_case) + { + case 1: + EE_value = (EE_value << 1) | ((value & 2) >> 1); + + if (instr_clocks == 15) + { + if (WR_EN) + { + for (int i = 0; i < 128; i++) + { + Core.cart_RAM[i * 2] = (byte)(EE_value & 0xFF); + Core.cart_RAM[i * 2 + 1] = (byte)((EE_value & 0xFF00) >> 8); + } + } + instr_case = 7; + countdown = 8; + } + break; + + case 4: + EE_value = (EE_value << 1) | ((value & 2) >> 1); + + if (instr_clocks == 15) + { + if (WR_EN) + { + Core.cart_RAM[EE_addr * 2] = (byte)(EE_value & 0xFF); + Core.cart_RAM[EE_addr * 2 + 1] = (byte)((EE_value & 0xFF00) >> 8); + } + instr_case = 7; + countdown = 8; + } + break; + + case 5: + if ((instr_clocks >= 0) && (instr_clocks <= 7)) + { + DO = ((Core.cart_RAM[EE_addr * 2 + 1] >> (7 - instr_clocks)) & 1) == 1 ? true : false; + } + else if ((instr_clocks >= 8) && (instr_clocks <= 15)) + { + DO = ((Core.cart_RAM[EE_addr * 2] >> (15 - instr_clocks)) & 1) == 1 ? true : false; + } + + if (instr_clocks == 15) + { + instr_case = 7; + countdown = 8; + } + break; + + case 6: + + instr_case = 7; + countdown = 8; + break; + + case 7: + // completed operations take time, so countdown a bit here. + // not cycle accurate for operations like writing to all of the EEPROM, but good enough + + break; + } + + if (instr_case == 7) + { + perf_instr = false; + countdown_start = true; + } + + instr_clocks++; + } + else if (countdown_start) + { + countdown--; + if (countdown == 0) + { + countdown_start = false; + DO = true; + + //Console.Write("Command Complete: "); + //Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + } + + CS_prev = CS; + CLK_prev = CLK; + DI_prev = DI; + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_RockMan8.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_RockMan8.cs new file mode 100644 index 0000000000..a45175f84e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_RockMan8.cs @@ -0,0 +1,69 @@ +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using System; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawk +{ + // RockMan 8, just some simple bankswitching + public class MapperRM8 : MapperBase + { + public int ROM_bank; + public int ROM_mask; + + public override void Initialize() + { + ROM_bank = 1; + ROM_mask = Core._rom.Length / 0x4000 - 1; + + // some games have sizes that result in a degenerate ROM, account for it here + if (ROM_mask > 4) { ROM_mask |= 3; } + } + + public override byte ReadMemory(ushort addr) + { + if (addr < 0x4000) + { + // lowest bank is fixed + return Core._rom[addr]; + + } + else if (addr < 0x8000) + { + return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; + } + else + { + return 0xFF; + } + } + + public override byte PeekMemory(ushort addr) + { + return ReadMemory(addr); + } + + public override void WriteMemory(ushort addr, byte value) + { + if ((addr >= 0x2000) && (addr < 0x4000)) + { + value &= 0x1F; + + if (value == 0) { value = 1; } + + // in hhugboy they just subtract 8, but to me looks like bits 4 and 5 are just swapped (and bit 4 is unused?) + ROM_bank = ((value & 0xF) | ((value & 0x10) >> 1)) & ROM_mask; + } + } + + public override void PokeMemory(ushort addr, byte value) + { + WriteMemory(addr, value); + } + + public override void SyncState(Serializer ser) + { + ser.Sync("ROM_Bank", ref ROM_bank); + ser.Sync("ROM_Mask", ref ROM_mask); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs index b178fe1037..c96600a6fd 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC1.cs @@ -17,6 +17,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int ROM_bank_mask; public int BASE_ROM_Bank; public bool reg_access; + public ushort addr_last; + public int counter; public override void Initialize() { @@ -26,37 +28,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ROM_bank_mask = 0xFF; locked = true; reg_access = false; + addr_last = 0; + counter = 0; } public override byte ReadMemory(ushort addr) { if (addr < 0x4000) { - ushort t_addr = addr; - - // header is scrambled - if ((addr >= 0x100) && (addr < 0x200)) + if (locked) { - int temp0 = (addr & 1); - int temp1 = (addr & 2); - int temp4 = (addr & 0x10); - int temp6 = (addr & 0x40); + // header is scrambled + if ((addr >= 0x100) && (addr < 0x200)) + { + int temp0 = (addr & 1); + int temp1 = (addr & 2); + int temp4 = (addr & 0x10); + int temp6 = (addr & 0x40); - temp0 = temp0 << 6; - temp1 = temp1 << 3; - temp4 = temp4 >> 3; - temp6 = temp6 >> 6; + temp0 = temp0 << 6; + temp1 = temp1 << 3; + temp4 = temp4 >> 3; + temp6 = temp6 >> 6; - addr &= 0x1AC; - addr |= (ushort)(temp0 | temp1 | temp4 | temp6); - } - - if (locked) { addr |= 0x80; } - - if (t_addr == 0x133) - { - locked = false; - Console.WriteLine("cleared"); + addr &= 0x1AC; + addr |= (ushort)(temp0 | temp1 | temp4 | temp6); + } + addr |= 0x80; } return Core._rom[addr + BASE_ROM_Bank * 0x4000]; @@ -73,39 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override byte PeekMemory(ushort addr) { - if (addr < 0x4000) - { - ushort t_addr = addr; - - // header is scrambled - if ((addr >= 0x100) && (addr < 0x200)) - { - int temp0 = (addr & 1); - int temp1 = (addr & 2); - int temp4 = (addr & 0x10); - int temp6 = (addr & 0x40); - - temp0 = temp0 << 6; - temp1 = temp1 << 3; - temp4 = temp4 >> 3; - temp6 = temp6 >> 6; - - addr &= 0x1AC; - addr |= (ushort)(temp0 | temp1 | temp4 | temp6); - } - - if (locked) { addr |= 0x80; } - - return Core._rom[addr + BASE_ROM_Bank * 0x4000]; - } - else if (addr < 0x8000) - { - return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; - } - else - { - return 0xFF; - } + return ReadMemory(addr); } public override void WriteMemory(ushort addr, byte value) @@ -144,6 +110,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WriteMemory(addr, value); } + public override void Mapper_Tick() + { + if (locked) + { + if (((Core.addr_access & 0x8000) == 0) && ((addr_last & 0x8000) > 0) && (Core.addr_access >= 0x100)) + { + counter++; + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + + if (Core.addr_access >= 0x100) + { + addr_last = Core.addr_access; + } + + if (counter == 0x30) + { + locked = false; + Console.WriteLine("Unlocked"); + } + } + } + public override void SyncState(Serializer ser) { ser.Sync("ROM_Bank", ref ROM_bank); @@ -152,6 +141,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("ROM_bank_mask", ref ROM_bank_mask); ser.Sync("BASE_ROM_Bank", ref BASE_ROM_Bank); ser.Sync("reg_access", ref reg_access); + ser.Sync("addr_last", ref addr_last); + ser.Sync("counter", ref counter); } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs index d43e912cf8..29f16a8212 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Mappers/Mapper_Sachen_MMC2.cs @@ -12,11 +12,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk class MapperSachen2 : MapperBase { public int ROM_bank; - public bool locked; + public bool locked, locked_GBC, finished; public int ROM_mask; public int ROM_bank_mask; public int BASE_ROM_Bank; public bool reg_access; + public ushort addr_last; + public int counter; public override void Initialize() { @@ -24,16 +26,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ROM_mask = Core._rom.Length / 0x4000 - 1; BASE_ROM_Bank = 0; ROM_bank_mask = 0; - locked = false; + locked = true; + locked_GBC = false; + finished = false; reg_access = false; + addr_last = 0; + counter = 0; } public override byte ReadMemory(ushort addr) { if (addr < 0x4000) { - ushort t_addr = addr; - // header is scrambled if ((addr >= 0x100) && (addr < 0x200)) { @@ -51,12 +55,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk addr |= (ushort)(temp0 | temp1 | temp4 | temp6); } - if (locked) { addr |= 0x80; } - - if (t_addr == 0x133) - { - if ((Core.GB_bios_register & 0x1) == 0) { locked ^= true; } - } + if (locked_GBC) { addr |= 0x80; } return Core._rom[addr + BASE_ROM_Bank * 0x4000]; } @@ -75,39 +74,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public override byte PeekMemory(ushort addr) { - if (addr < 0x4000) - { - ushort t_addr = addr; - - // header is scrambled - if ((addr >= 0x100) && (addr < 0x200)) - { - int temp0 = (addr & 1); - int temp1 = (addr & 2); - int temp4 = (addr & 0x10); - int temp6 = (addr & 0x40); - - temp0 = temp0 << 6; - temp1 = temp1 << 3; - temp4 = temp4 >> 3; - temp6 = temp6 >> 6; - - addr &= 0x1AC; - addr |= (ushort)(temp0 | temp1 | temp4 | temp6); - } - - if (locked) { addr |= 0x80; } - - return Core._rom[addr + BASE_ROM_Bank * 0x4000]; - } - else if (addr < 0x8000) - { - return Core._rom[(addr - 0x4000) + ROM_bank * 0x4000]; - } - else - { - return 0xFF; - } + return ReadMemory(addr); } public override void WriteMemory(ushort addr, byte value) @@ -146,14 +113,71 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk WriteMemory(addr, value); } + public override void Mapper_Tick() + { + if (locked) + { + if (((Core.addr_access & 0x8000) == 0) && ((addr_last & 0x8000) > 0) && (Core.addr_access >= 0x100)) + { + counter++; + } + + if (Core.addr_access >= 0x100) + { + addr_last = Core.addr_access; + } + + if (counter == 0x30) + { + locked = false; + locked_GBC = true; + counter = 0; + } + } + else if (locked_GBC) + { + if (((Core.addr_access & 0x8000) == 0) && ((addr_last & 0x8000) > 0) && (Core.addr_access >= 0x100)) + { + counter++; + } + + if (Core.addr_access >= 0x100) + { + addr_last = Core.addr_access; + } + + if (counter == 0x30) + { + locked_GBC = false; + finished = true; + Console.WriteLine("Finished"); + Console.WriteLine(Core.cpu.TotalExecutedCycles); + } + + // The above condition seems to never be reached as described in the mapper notes + // so for now add this one + + if ((Core.addr_access == 0x133) && (counter == 1)) + { + locked_GBC = false; + finished = true; + Console.WriteLine("Unlocked"); + } + } + } + public override void SyncState(Serializer ser) { ser.Sync("ROM_Bank", ref ROM_bank); ser.Sync("ROM_Mask", ref ROM_mask); ser.Sync("locked", ref locked); + ser.Sync("locked_GBC", ref locked_GBC); + ser.Sync("finished", ref finished); ser.Sync("ROM_bank_mask", ref ROM_bank_mask); ser.Sync("BASE_ROM_Bank", ref BASE_ROM_Bank); ser.Sync("reg_access", ref reg_access); + ser.Sync("addr_last", ref addr_last); + ser.Sync("counter", ref counter); } } } \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs index b1b241d39b..12037bdc4a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/MemoryMap.cs @@ -30,16 +30,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public byte ReadMemory(ushort addr) { MemoryCallbacks.CallReads(addr, "System Bus"); + addr_access = addr; if (ppu.DMA_start) { - if (addr < 0x4000) + // some of gekkio's tests require these to be accessible during DMA + if (addr < 0x8000) { - return mapper.ReadMemory(addr); // some of gekkio's tests require this to be accessible during DMA + if (ppu.DMA_addr < 0x80) + { + return 0xFF; + } + else + { + return mapper.ReadMemory(addr); + } } - else if ((addr >= 0xE000) && (addr < 0xFE00)) + else if ((addr >= 0xE000) && (addr < 0xF000)) { - return RAM[addr - 0xE000]; // some of gekkio's tests require this to be accessible during DMA + return RAM[addr - 0xE000]; + } + else if ((addr >= 0xF000) && (addr < 0xFE00)) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; } else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access) { @@ -57,12 +70,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return 0xFF; } - if (addr < 0x100) + if (addr < 0x900) { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) + if (addr < 0x100) { - return _bios[addr]; // Return BIOS + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } + } + else if (addr >= 0x200) + { + // return Either BIOS ROM or Game ROM + if (((GB_bios_register & 0x1) == 0) && is_GBC) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } } else { @@ -73,33 +105,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { return mapper.ReadMemory(addr); } - else if (addr < 0x9800) - { - if (ppu.VRAM_access_read) { return CHR_RAM[addr - 0x8000]; } - else { return 0xFF; } - } - else if (addr < 0x9C00) - { - if (ppu.VRAM_access_read) { return BG_map_1[addr - 0x9800]; } - else { return 0xFF; } - } else if (addr < 0xA000) { - if (ppu.VRAM_access_read) { return BG_map_2[addr - 0x9C00]; } + if (ppu.VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; } else { return 0xFF; } } else if (addr < 0xC000) { return mapper.ReadMemory(addr); } - else if (addr < 0xE000) + else if (addr < 0xD000) { return RAM[addr - 0xC000]; } - else if (addr < 0xFE00) + else if (addr < 0xE000) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)]; + } + else if (addr < 0xF000) { return RAM[addr - 0xE000]; } + else if (addr < 0xFE00) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; + } else if (addr < 0xFEA0) { if (ppu.OAM_access_read) { return OAM[addr - 0xFE00]; } @@ -128,12 +158,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void WriteMemory(ushort addr, byte value) { MemoryCallbacks.CallWrites(addr, "System Bus"); - + addr_access = addr; + if (ppu.DMA_start) { - if ((addr >= 0xE000) && (addr < 0xFE00)) + // some of gekkio's tests require this to be accessible during DMA + if ((addr >= 0xE000) && (addr < 0xF000)) { - RAM[addr - 0xE000] = value; // some of gekkio's tests require this to be accessible during DMA + RAM[addr - 0xE000] = value; + } + else if ((addr >= 0xF000) && (addr < 0xFE00)) + { + RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value; } else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access) { @@ -150,12 +186,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return; } - if (addr < 0x100) + if (addr < 0x900) { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) + if (addr < 0x100) { - // Can't write to BIOS region + if ((GB_bios_register & 0x1) == 0) + { + // No Writing to BIOS + } + else + { + mapper.WriteMemory(addr, value); + } + } + else if (addr >= 0x200) + { + if (((GB_bios_register & 0x1) == 0) && is_GBC) + { + // No Writing to BIOS + } + else + { + mapper.WriteMemory(addr, value); + } } else { @@ -166,30 +219,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { mapper.WriteMemory(addr, value); } - else if (addr < 0x9800) - { - if (ppu.VRAM_access_write) { CHR_RAM[addr - 0x8000] = value; } - } - else if (addr < 0x9C00) - { - if (ppu.VRAM_access_write) { BG_map_1[addr - 0x9800] = value; } - } else if (addr < 0xA000) { - if (ppu.VRAM_access_write) { BG_map_2[addr - 0x9C00] = value; } + if (ppu.VRAM_access_write) { VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)] = value; } } else if (addr < 0xC000) { mapper.WriteMemory(addr, value); } - else if (addr < 0xE000) + else if (addr < 0xD000) { RAM[addr - 0xC000] = value; } - else if (addr < 0xFE00) + else if (addr < 0xE000) + { + RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)] = value; + } + else if (addr < 0xF000) { RAM[addr - 0xE000] = value; } + else if (addr < 0xFE00) + { + RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value; + } else if (addr < 0xFEA0) { if (ppu.OAM_access_write) { OAM[addr - 0xFE00] = value; } @@ -216,13 +269,25 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { if (ppu.DMA_start) { - if (addr < 0x4000) + // some of gekkio's tests require these to be accessible during DMA + if (addr < 0x8000) { - return mapper.ReadMemory(addr); // some of gekkio's tests require this to be accessible during DMA + if (ppu.DMA_addr < 0x80) + { + return 0xFF; + } + else + { + return mapper.ReadMemory(addr); + } } - else if ((addr >= 0xE000) && (addr < 0xFE00)) + else if ((addr >= 0xE000) && (addr < 0xF000)) { - return RAM[addr - 0xE000]; // some of gekkio's tests require this to be accessible during DMA + return RAM[addr - 0xE000]; + } + else if ((addr >= 0xF000) && (addr < 0xFE00)) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; } else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access) { @@ -240,49 +305,66 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk return 0xFF; } - if (addr < 0x100) + if (addr < 0x900) { - // return Either BIOS ROM or Game ROM - if ((GB_bios_register & 0x1) == 0) + if (addr < 0x100) { - return _bios[addr]; // Return BIOS + // return Either BIOS ROM or Game ROM + if ((GB_bios_register & 0x1) == 0) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } + } + else if (addr >= 0x200) + { + // return Either BIOS ROM or Game ROM + if (((GB_bios_register & 0x1) == 0) && is_GBC) + { + return _bios[addr]; // Return BIOS + } + else + { + return mapper.ReadMemory(addr); + } } else { - return mapper.PeekMemory(addr); + return mapper.ReadMemory(addr); } } else if (addr < 0x8000) { return mapper.PeekMemory(addr); } - else if (addr < 0x9800) - { - if (ppu.VRAM_access_read) { return CHR_RAM[addr - 0x8000]; } - else { return 0xFF; } - } - else if (addr < 0x9C00) - { - if (ppu.VRAM_access_read) { return BG_map_1[addr - 0x9800]; } - else { return 0xFF; } - } else if (addr < 0xA000) { - if (ppu.VRAM_access_read) { return BG_map_2[addr - 0x9C00]; } + if (ppu.VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; } else { return 0xFF; } } else if (addr < 0xC000) { return mapper.PeekMemory(addr); } - else if (addr < 0xE000) + else if (addr < 0xD000) { return RAM[addr - 0xC000]; } - else if (addr < 0xFE00) + else if (addr < 0xE000) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)]; + } + else if (addr < 0xF000) { return RAM[addr - 0xE000]; } + else if (addr < 0xFE00) + { + return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)]; + } else if (addr < 0xFEA0) { if (ppu.OAM_access_read) { return OAM[addr - 0xFE00]; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs index 6dc0c08613..6c86840b18 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/PPU.cs @@ -9,12 +9,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk { public GBHawk Core { get; set; } + public uint[] BG_palette = new uint[32]; + public uint[] OBJ_palette = new uint[32]; + + + public bool HDMA_active; + // register variables public byte LCDC; public byte STAT; public byte scroll_y; public byte scroll_x; public byte LY; + public byte LY_actual; public byte LY_inc; public byte LYC; public byte DMA_addr; @@ -62,6 +69,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public bool fetch_sprite_01; public bool fetch_sprite_4; public bool going_to_fetch; + public bool first_fetch; public int sprite_fetch_counter; public byte[] sprite_attr_list = new byte[160]; public byte[] sprite_pixel_list = new byte[160]; @@ -69,8 +77,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int temp_fetch; public int tile_inc; public bool pre_render; - public byte[] tile_data = new byte[2]; - public byte[] tile_data_latch = new byte[2]; + public byte[] tile_data = new byte[3]; + public byte[] tile_data_latch = new byte[3]; public int latch_counter; public bool latch_new_data; public int render_counter; @@ -85,1160 +93,88 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public int evaled_sprites; public int sprite_ordered_index; public bool blank_frame; + public bool window_latch; // windowing state public int window_counter; public bool window_pre_render; public bool window_started; + public bool window_is_reset; public int window_tile_inc; public int window_y_tile; public int window_x_tile; public int window_y_tile_inc; public int window_x_latch; - public byte ReadReg(int addr) + public virtual byte ReadReg(int addr) { - byte ret = 0; - - switch (addr) - { - case 0xFF40: ret = LCDC; break; // LCDC - case 0xFF41: ret = STAT; break; // STAT - case 0xFF42: ret = scroll_y; break; // SCY - case 0xFF43: ret = scroll_x; break; // SCX - case 0xFF44: ret = LY; break; // LY - case 0xFF45: ret = LYC; break; // LYC - case 0xFF46: ret = 0xFF; break; // DMA (not readable?) /*ret = DMA_addr; */ - case 0xFF47: ret = BGP; break; // BGP - case 0xFF48: ret = obj_pal_0; break; // OBP0 - case 0xFF49: ret = obj_pal_1; break; // OBP1 - case 0xFF4A: ret = window_y; break; // WY - case 0xFF4B: ret = window_x; break; // WX - } - - return ret; + return 0; } - public void WriteReg(int addr, byte value) + public virtual void WriteReg(int addr, byte value) { - switch (addr) - { - case 0xFF40: // LCDC - if (LCDC.Bit(7) && !value.Bit(7)) - { - VRAM_access_read = true; - VRAM_access_write = true; - OAM_access_read = true; - OAM_access_write = true; - } - if (!LCDC.Bit(7) && value.Bit(7)) - { - // don't draw for one frame after turning on - blank_frame = true; - } - - LCDC = value; - break; - case 0xFF41: // STAT - // writing to STAT during mode 0 or 2 causes a STAT IRQ - if (LCDC.Bit(7)) - { - if (((STAT & 3) == 0) || ((STAT & 3) == 1)) - { - LYC_INT = true; - } - } - STAT = (byte)((value & 0xF8) | (STAT & 7) | 0x80); - break; - case 0xFF42: // SCY - scroll_y = value; - break; - case 0xFF43: // SCX - scroll_x = value; - // calculate the column number of the tile to start with - x_tile = (int)Math.Floor((float)(scroll_x) / 8); - break; - case 0xFF44: // LY - LY = 0; /*reset*/ - break; - case 0xFF45: // LYC - LYC = value; - if (LY != LYC) { STAT &= 0xFB; } - break; - case 0xFF46: // DMA - DMA_addr = value; - DMA_start = true; - DMA_OAM_access = true; - DMA_clock = 0; - DMA_inc = 0; - break; - case 0xFF47: // BGP - BGP = value; - break; - case 0xFF48: // OBP0 - obj_pal_0 = value; - break; - case 0xFF49: // OBP1 - obj_pal_1 = value; - break; - case 0xFF4A: // WY - window_y = value; - break; - case 0xFF4B: // WX - window_x = value; - break; - } } - public void tick() + public virtual void tick() { - // tick DMA, note that DMA is halted when the CPU is halted - if (DMA_start && !Core.cpu.halted) - { - if (DMA_clock >= 4) - { - DMA_OAM_access = false; - if ((DMA_clock % 4) == 1) - { - // the cpu can't access memory during this time, but we still need the ppu to be able to. - DMA_start = false; - DMA_byte = Core.ReadMemory((ushort)((DMA_addr << 8) + DMA_inc)); - DMA_start = true; - } - else if ((DMA_clock % 4) == 3) - { - Core.OAM[DMA_inc] = DMA_byte; - if (DMA_inc < (0xA0 - 1)) { DMA_inc++; } - } - } - - DMA_clock++; - - if (DMA_clock == 648) - { - DMA_start = false; - DMA_OAM_access = true; - } - } - - // the ppu only does anything if it is turned on via bit 7 of LCDC - if (LCDC.Bit(7)) - { - // start the next scanline - if (cycle == 456) - { - // scanline callback - if ((LY + LY_inc) == Core._scanlineCallbackLine) - { - if (Core._scanlineCallback != null) - { - Core.GetGPU(); - Core._scanlineCallback(LCDC); - } - } - - cycle = 0; - LY += LY_inc; - no_scan = false; - - // here is where LY = LYC gets cleared (but only if LY isnt 0 as that's a special case - if (LY_inc == 1) - { - LYC_INT = false; - STAT &= 0xFB; - } - - if (LY == 0 && LY_inc == 0) - { - LY_inc = 1; - Core.in_vblank = false; - VBL_INT = false; - if (STAT.Bit(3)) { HBL_INT = true; } - - STAT &= 0xFC; - - // special note here, the y coordiate of the window is kept if the window is deactivated - // meaning it will pick up where it left off if re-enabled later - // so we don't reset it in the scanline loop - window_y_tile = 0; - window_y_tile_inc = 0; - window_started = false; - } - - Core.cpu.LY = LY; - - // Automatically restore access to VRAM at this time (force end drawing) - // Who Framed Roger Rabbit seems to run into this. - VRAM_access_write = true; - VRAM_access_read = true; - - if (LY == 144) - { - Core.in_vblank = true; - } - } - - // exit vblank if LCD went from off to on - if (LCD_was_off) - { - //VBL_INT = false; - Core.in_vblank = false; - LCD_was_off = false; - - // we exit vblank into mode 0 for 4 cycles - // but no hblank interrupt, presumably this only happens transitioning from mode 3 to 0 - STAT &= 0xFC; - - // also the LCD doesn't turn on right away - - // also, the LCD does not enter mode 2 on scanline 0 when first turned on - no_scan = true; - cycle = 8; - } - - // the VBL stat is continuously asserted - if ((LY >= 144)) - { - if (STAT.Bit(4)) - { - if ((cycle >= 4) && (LY == 144)) - { - VBL_INT = true; - } - else if (LY > 144) - { - VBL_INT = true; - } - } - - if ((cycle == 4) && (LY == 144)) { - - HBL_INT = false; - - // set STAT mode to 1 (VBlank) and interrupt flag if it is enabled - STAT &= 0xFC; - STAT |= 0x01; - - if (Core.REG_FFFF.Bit(0)) { Core.cpu.FlagI = true; } - Core.REG_FF0F |= 0x01; - } - - if ((LY >= 144) && (cycle == 4)) - { - // a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } - } - - if ((LY == 153) && (cycle == 8)) - { - LY = 0; - LY_inc = 0; - Core.cpu.LY = LY; - } - } - - if (!Core.in_vblank) - { - if (no_scan) - { - // timings are slightly different if we just turned on the LCD - // there is no mode 2 (presumably it missed the trigger) - // mode 3 is very short, probably in some self test mode before turning on? - - if (cycle == 12) - { - LYC_INT = false; - STAT &= 0xFB; - - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - } - - if (cycle == 84) - { - - STAT &= 0xFC; - STAT |= 0x03; - OAM_INT = false; - - OAM_access_read = false; - OAM_access_write = false; - VRAM_access_read = false; - VRAM_access_write = false; - } - - if (cycle == 256) - { - STAT &= 0xFC; - OAM_INT = false; - - if (STAT.Bit(3)) { HBL_INT = true; } - - OAM_access_read = true; - OAM_access_write = true; - VRAM_access_read = true; - VRAM_access_write = true; - } - } - else - { - if (cycle < 80) - { - if (cycle == 4) - { - // apparently, writes can make it to OAM one cycle longer then reads - OAM_access_write = false; - - // here mode 2 will be set to true and interrupts fired if enabled - STAT &= 0xFC; - STAT |= 0x2; - if (STAT.Bit(5)) { OAM_INT = true; } - - HBL_INT = false; - } - - // here OAM scanning is performed - OAM_scan(cycle); - } - else if (cycle >= 80 && LY < 144) - { - - if (cycle == 84) - { - STAT &= 0xFC; - STAT |= 0x03; - OAM_INT = false; - OAM_access_write = false; - VRAM_access_write = false; - } - - // render the screen and handle hblank - render(cycle - 80); - } - } - } - - if ((LY_inc == 0)) - { - if (cycle == 12) - { - LYC_INT = false; - STAT &= 0xFB; - - // Special case of LY = LYC - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - - // also a special case of OAM mode 2 IRQ assertion, even though PPU Mode still is 1 - if (STAT.Bit(5)) { OAM_INT = true; } - } - - if (cycle == 92) { OAM_INT = false; } - } - - // here LY=LYC will be asserted - if ((cycle == 4) && (LY != 0)) - { - if (LY == LYC) - { - // set STAT coincidence FLAG and interrupt flag if it is enabled - STAT |= 0x04; - if (STAT.Bit(6)) { LYC_INT = true; } - } - } - - cycle++; - } - else - { - STAT &= 0xF8; - - VBL_INT = LYC_INT = HBL_INT = OAM_INT = false; - - Core.in_vblank = true; - - LCD_was_off = true; - - LY = 0; - Core.cpu.LY = LY; - - cycle = 0; - } - - // assert the STAT IRQ line if the line went from zero to 1 - stat_line = VBL_INT | LYC_INT | HBL_INT | OAM_INT; - - if (stat_line && !stat_line_old) - { - if (Core.REG_FFFF.Bit(1)) { Core.cpu.FlagI = true; } - Core.REG_FF0F |= 0x02; - } - - stat_line_old = stat_line; - - // process latch delays - //latch_delay(); } // might be needed, not sure yet - public void latch_delay() + public virtual void latch_delay() { - //BGP_l = BGP; + } - public void OAM_scan(int OAM_cycle) + public virtual void render(int render_cycle) { - // we are now in STAT mode 2 - // TODO: maybe stat mode 2 flags are set at cycle 0 on visible scanlines? - if (OAM_cycle == 0) - { - OAM_access_read = false; - OAM_scan_index = 0; - SL_sprites_index = 0; - write_sprite = 0; - } - - // the gameboy has 80 cycles to scan through 40 sprites, picking out the first 10 it finds to draw - // the following is a guessed at implmenentation based on how NES does it, it's probably pretty close - if (OAM_cycle < 10) - { - // start by clearing the sprite table (probably just clears X on hardware, but let's be safe here.) - SL_sprites[OAM_cycle * 4] = 0; - SL_sprites[OAM_cycle * 4 + 1] = 0; - SL_sprites[OAM_cycle * 4 + 2] = 0; - SL_sprites[OAM_cycle * 4 + 3] = 0; - } - else - { - if (write_sprite == 0) - { - if (OAM_scan_index < 40) - { - ushort temp = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4] : (ushort)0xFF; - // (sprite Y - 16) equals LY, we have a sprite - if ((temp - 16) <= LY && - ((temp - 16) + 8 + (LCDC.Bit(2) ? 8 : 0)) > LY) - { - // always pick the first 10 in range sprites - if (SL_sprites_index < 10) - { - SL_sprites[SL_sprites_index * 4] = temp; - - write_sprite = 1; - } - else - { - // if we already have 10 sprites, there's nothing to do, increment the index - OAM_scan_index++; - } - } - else - { - OAM_scan_index++; - } - } - } - else - { - ushort temp2 = DMA_OAM_access ? Core.OAM[OAM_scan_index * 4 + write_sprite] : (ushort)0xFF; - SL_sprites[SL_sprites_index * 4 + write_sprite] = temp2; - write_sprite++; - - if (write_sprite == 4) - { - write_sprite = 0; - SL_sprites_index++; - OAM_scan_index++; - } - } - } } - public void render(int render_cycle) + public virtual void process_sprite() { - // we are now in STAT mode 3 - // NOTE: presumably the first necessary sprite is fetched at sprite evaulation - // i.e. just keeping track of the lowest x-value sprite - if (render_cycle == 0) - { - OAM_access_read = false; - OAM_access_write = true; - VRAM_access_read = false; - // window X is latched for the scanline, mid-line changes have no effect - window_x_latch = window_x; - - OAM_scan_index = 0; - read_case = 0; - internal_cycle = 0; - pre_render = true; - tile_inc = 0; - pixel_counter = -8; - sl_use_index = 0; - fetch_sprite = false; - fetch_sprite_01 = false; - fetch_sprite_4 = false; - going_to_fetch = false; - no_sprites = false; - evaled_sprites = 0; - - window_pre_render = false; - if (window_started && LCDC.Bit(5)) - { - window_y_tile_inc++; - if (window_y_tile_inc==8) - { - window_y_tile_inc = 0; - window_y_tile++; - window_y_tile %= 32; - } - } - window_started = false; - - if (SL_sprites_index == 0) { no_sprites = true; } - // it is much easier to process sprites if we order them according to the rules of sprite priority first - if (!no_sprites) { reorder_and_assemble_sprites(); } - - } - - // before anything else, we have to check if windowing is in effect - if (LCDC.Bit(5) && !window_started && (LY >= window_y) && (pixel_counter >= (window_x_latch - 7)) && (window_x_latch < 167)) - { - /* - Console.Write(LY); - Console.Write(" "); - Console.Write(cycle); - Console.Write(" "); - Console.Write(window_y_tile_inc); - Console.Write(" "); - Console.Write(window_x_latch); - Console.Write(" "); - Console.WriteLine(pixel_counter); - */ - if (pixel_counter == 0 && window_x_latch <= 7) - { - // if the window starts at zero, we still do the first access to the BG - // but then restart all over again at the window - window_pre_render = true; - } - else - { - // otherwise, just restart the whole process as if starting BG again - window_pre_render = true; - read_case = 4; - } - window_counter = 0; - - window_x_tile = (int)Math.Floor((float)(pixel_counter - (window_x_latch - 7)) / 8); - - window_tile_inc = 0; - window_started = true; - } - - if (!pre_render && !fetch_sprite && !window_pre_render) - { - // start shifting data into the LCD - if (render_counter >= (render_offset + 8)) - { - pixel = tile_data_latch[0].Bit(7 - (render_counter % 8)) ? 1 : 0; - pixel |= tile_data_latch[1].Bit(7 - (render_counter % 8)) ? 2 : 0; - - int ref_pixel = pixel; - if (LCDC.Bit(0)) - { - pixel = (BGP >> (pixel * 2)) & 3; - } - else - { - pixel = 0; - } - - // now we have the BG pixel, we next need the sprite pixel - if (!no_sprites) - { - bool have_sprite = false; - int s_pixel = 0; - int sprite_attr = 0; - - if (sprite_present_list[pixel_counter] == 1) - { - have_sprite = true; - s_pixel = sprite_pixel_list[pixel_counter]; - sprite_attr = sprite_attr_list[pixel_counter]; - } - - if (have_sprite) - { - bool use_sprite = false; - if (LCDC.Bit(1)) - { - if (!sprite_attr.Bit(7)) - { - use_sprite = true; - } - else if (ref_pixel == 0) - { - use_sprite = true; - } - - if (!LCDC.Bit(0)) - { - use_sprite = true; - } - } - - if (use_sprite) - { - if (sprite_attr.Bit(4)) - { - pixel = (obj_pal_1 >> (s_pixel * 2)) & 3; - } - else - { - pixel = (obj_pal_0 >> (s_pixel * 2)) & 3; - } - } - } - } - - // based on sprite priority and pixel values, pick a final pixel color - Core._vidbuffer[LY * 160 + pixel_counter] = (int)Core.color_palette[pixel]; - pixel_counter++; - - if (pixel_counter == 160) - { - read_case = 8; - hbl_countdown = 7; - } - } - else if ((render_counter >= render_offset) && (pixel_counter < 0)) - { - pixel_counter++; - } - render_counter++; - } - - if (!fetch_sprite) - { - if (!pre_render) - { - // before we go on to read case 3, we need to know if we stall there or not - // Gekkio's tests show that if sprites are at position 0 or 1 (mod 8) - // then it takes an extra cycle (1 or 2 more t-states) to process them - - if (!no_sprites && (pixel_counter < 160)) - { - for (int i = 0; i < SL_sprites_index; i++) - { - if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && - (pixel_counter < (SL_sprites[i * 4 + 1])) && - !evaled_sprites.Bit(i)) - { - going_to_fetch = true; - fetch_sprite = true; - - if ((SL_sprites[i * 4 + 1] % 8) < 2) - { - fetch_sprite_01 = true; - } - if ((SL_sprites[i * 4 + 1] % 8) > 3) - { - fetch_sprite_4 = true; - } - } - } - } - } - - switch (read_case) - { - case 0: // read a background tile - if ((internal_cycle % 2) == 0) - { - // calculate the row number of the tiles to be fetched - y_tile = ((int)Math.Floor((float)(scroll_y + LY) / 8)) % 32; - - temp_fetch = y_tile * 32 + (x_tile + tile_inc) % 32; - tile_byte = LCDC.Bit(3) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; - } - else - { - read_case = 1; - if (!pre_render) - { - tile_inc++; - if (window_pre_render) - { - read_case = 4; - } - } - } - break; - - case 1: // read from tile graphics (0) - if ((internal_cycle % 2) == 0) - { - y_scroll_offset = (scroll_y + LY) % 8; - - if (LCDC.Bit(4)) - { - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7)) - { - tile_byte -= 256; - } - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; - } - - } - else - { - read_case = 2; - } - break; - - case 2: // read from tile graphics (1) - if ((internal_cycle % 2) == 0) - { - y_scroll_offset = (scroll_y + LY) % 8; - - if (LCDC.Bit(4)) - { - // if LCDC somehow changed between the two reads, make sure we have a positive number - if (tile_byte < 0) - { - tile_byte += 256; - } - - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7) && tile_byte > 0) - { - tile_byte -= 256; - } - - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - - } - else - { - if (pre_render) - { - // here we set up rendering - pre_render = false; - render_offset = scroll_x % 8; - render_counter = 0; - latch_counter = 0; - read_case = 0; - } - else - { - read_case = 3; - } - } - break; - - case 3: // read from sprite data - if ((internal_cycle % 2) == 0) - { - // nothing to do if not fetching - } - else - { - read_case = 0; - latch_new_data = true; - } - break; - - case 4: // read from window data - if ((window_counter % 2) == 0) - { - temp_fetch = window_y_tile * 32 + (window_x_tile + window_tile_inc) % 32; - tile_byte = LCDC.Bit(6) ? Core.BG_map_2[temp_fetch] : Core.BG_map_1[temp_fetch]; - } - else - { - if (!window_pre_render) - { - window_tile_inc++; - } - read_case = 5; - } - window_counter++; - break; - - case 5: // read from tile graphics (for the window) - if ((window_counter % 2) == 0) - { - y_scroll_offset = (window_y_tile_inc) % 8; - - if (LCDC.Bit(4)) - { - - tile_data[0] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2]; - - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7)) - { - tile_byte -= 256; - } - - tile_data[0] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2]; - } - } - else - { - read_case = 6; - } - window_counter++; - break; - - case 6: // read from tile graphics (for the window) - if ((window_counter % 2) == 0) - { - y_scroll_offset = (window_y_tile_inc) % 8; - if (LCDC.Bit(4)) - { - // if LCDC somehow changed between the two reads, make sure we have a positive number - if (tile_byte < 0) - { - tile_byte += 256; - } - - tile_data[1] = Core.CHR_RAM[tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - else - { - // same as before except now tile byte represents a signed byte - if (tile_byte.Bit(7) && tile_byte > 0) - { - tile_byte -= 256; - } - - tile_data[1] = Core.CHR_RAM[0x1000 + tile_byte * 16 + y_scroll_offset * 2 + 1]; - } - - } - else - { - if (window_pre_render) - { - // here we set up rendering - window_pre_render = false; - render_offset = 0; - render_counter = 0; - latch_counter = 0; - read_case = 4; - } - else - { - read_case = 7; - - } - } - window_counter++; - break; - - case 7: // read from sprite data - if ((window_counter % 2) == 0) - { - // nothing to do if not fetching - } - else - { - read_case = 4; - latch_new_data = true; - } - window_counter++; - break; - - case 8: // done reading, we are now in phase 0 - - pre_render = true; - - // the other interrupts appear to be delayed by 1 CPU cycle, so do the same here - if (hbl_countdown > 0) - { - hbl_countdown--; - if (hbl_countdown == 0) - { - STAT &= 0xFC; - STAT |= 0x00; - - if (STAT.Bit(3)) { HBL_INT = true; } - - OAM_access_read = true; - OAM_access_write = true; - VRAM_access_read = true; - VRAM_access_write = true; - } - } - - break; - } - internal_cycle++; - - if (latch_new_data) - { - latch_new_data = false; - tile_data_latch[0] = tile_data[0]; - tile_data_latch[1] = tile_data[1]; - } - } - - // every in range sprite takes 6 cycles to process - // sprites located at x=0 still take 6 cycles to process even though they don't appear on screen - // sprites above x=168 do not take any cycles to process however - if (fetch_sprite) - { - if (going_to_fetch) - { - going_to_fetch = false; - sprite_fetch_counter = 0; - - if (fetch_sprite_01) - { - sprite_fetch_counter += 2; - fetch_sprite_01 = false; - } - - if (fetch_sprite_4) - { - sprite_fetch_counter -= 2; - fetch_sprite_4 = false; - } - - int last_eval = 0; - - // at this time it is unknown what each cycle does, but we only need to accurately keep track of cycles - for (int i = 0; i < SL_sprites_index; i++) - { - if ((pixel_counter >= (SL_sprites[i * 4 + 1] - 8)) && - (pixel_counter < (SL_sprites[i * 4 + 1])) && - !evaled_sprites.Bit(i)) - { - sprite_fetch_counter += 6; - evaled_sprites |= (1 << i); - last_eval = SL_sprites[i * 4 + 1]; - } - } - - // if we didn't evaluate all the sprites immediately, 2 more cycles are added to restart it - if (evaled_sprites != (Math.Pow(2,SL_sprites_index) - 1)) - { - if ((last_eval % 8) == 0) { sprite_fetch_counter += 3; } - else if ((last_eval % 8) == 1) { sprite_fetch_counter += 2; } - else if ((last_eval % 8) == 2) { sprite_fetch_counter += 3; } - else if ((last_eval % 8) == 3) { sprite_fetch_counter += 2; } - else if ((last_eval % 8) == 4) { sprite_fetch_counter += 3; } - else { sprite_fetch_counter += 2; } - } - } - else - { - sprite_fetch_counter--; - if (sprite_fetch_counter == 0) - { - fetch_sprite = false; - } - } - } } - public void Reset() + // normal DMA moves twice as fast in double speed mode on GBC + // So give it it's own function so we can seperate it from PPU tick + public virtual void DMA_tick() { - LCDC = 0; - STAT = 0x80; - scroll_y = 0; - scroll_x = 0; - LY = 0; - LYC = 0; - DMA_addr = 0; - BGP = 0xFF; - obj_pal_0 = 0xFF; - obj_pal_1 = 0xFF; - window_y = 0x0; - window_x = 0x0; - window_x_latch = 0xFF; - LY_inc = 1; - no_scan = false; - OAM_access_read = true; - VRAM_access_read = true; - OAM_access_write = true; - VRAM_access_write = true; - DMA_OAM_access = true; - cycle = 0; - LYC_INT = false; - HBL_INT = false; - VBL_INT = false; - OAM_INT = false; + } - stat_line = false; - stat_line_old = false; - - window_counter = 0; - window_pre_render = false; - window_started = false; - window_tile_inc = 0; - window_y_tile = 0; - window_x_tile = 0; - window_y_tile_inc = 0; - } - - public void process_sprite() + public virtual void OAM_scan(int OAM_cycle) { - int y; - if (SL_sprites[sl_use_index * 4 + 3].Bit(6)) - { - if (LCDC.Bit(2)) - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - y = 15 - y; - sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; - } - else - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - y = 7 - y; - sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; - } - } - else - { - if (LCDC.Bit(2)) - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[(SL_sprites[sl_use_index * 4 + 2] & 0xFE) * 16 + y * 2 + 1]; - } - else - { - y = LY - (SL_sprites[sl_use_index * 4] - 16); - sprite_sel[0] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2]; - sprite_sel[1] = Core.CHR_RAM[SL_sprites[sl_use_index * 4 + 2] * 16 + y * 2 + 1]; - } - } + } - if (SL_sprites[sl_use_index * 4 + 3].Bit(5)) - { - int b0, b1, b2, b3, b4, b5, b6, b7 = 0; - for (int i = 0; i < 2; i++) - { - b0 = (sprite_sel[i] & 0x01) << 7; - b1 = (sprite_sel[i] & 0x02) << 5; - b2 = (sprite_sel[i] & 0x04) << 3; - b3 = (sprite_sel[i] & 0x08) << 1; - b4 = (sprite_sel[i] & 0x10) >> 1; - b5 = (sprite_sel[i] & 0x20) >> 3; - b6 = (sprite_sel[i] & 0x40) >> 5; - b7 = (sprite_sel[i] & 0x80) >> 7; - sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7); - } - } + public virtual void Reset() + { + } // order sprites according to x coordinate // note that for sprites of equal x coordinate, priority goes to first on the list - public void reorder_and_assemble_sprites() + public virtual void reorder_and_assemble_sprites() { - sprite_ordered_index = 0; - - for (int i = 0; i < 256; i++) - { - for (int j = 0; j < SL_sprites_index; j++) - { - if (SL_sprites[j * 4 + 1] == i) - { - sl_use_index = j; - process_sprite(); - SL_sprites_ordered[sprite_ordered_index * 4] = SL_sprites[j * 4 + 1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 1] = sprite_sel[0]; - SL_sprites_ordered[sprite_ordered_index * 4 + 2] = sprite_sel[1]; - SL_sprites_ordered[sprite_ordered_index * 4 + 3] = SL_sprites[j * 4 + 3]; - sprite_ordered_index++; - } - } - } - bool have_pixel = false; - byte s_pixel = 0; - byte sprite_attr = 0; - - for (int i = 0; i < 160; i++) - { - have_pixel = false; - for (int j = 0; j < SL_sprites_index; j++) - { - if ((i >= (SL_sprites_ordered[j * 4] - 8)) && - (i < SL_sprites_ordered[j * 4]) && - !have_pixel) - { - // we can use the current sprite, so pick out a pixel for it - int t_index = i - (SL_sprites_ordered[j * 4] - 8); - - t_index = 7 - t_index; - - sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1); - sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1); - - s_pixel = (byte)(sprite_data[0] + sprite_data[1]); - sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3]; - - // pixel color of 0 is transparent, so if this is the case we dont have a pixel - if (s_pixel != 0) - { - have_pixel = true; - } - } - } - - if (have_pixel) - { - sprite_present_list[i] = 1; - sprite_pixel_list[i] = s_pixel; - sprite_attr_list[i] = sprite_attr; - } - else - { - sprite_present_list[i] = 0; - } - } } - public void SyncState(Serializer ser) + public virtual void SyncState(Serializer ser) { + + ser.Sync("BG_palette", ref BG_palette, false); + ser.Sync("OBJ_palette", ref OBJ_palette, false); + ser.Sync("HDMA_active", ref HDMA_active); + ser.Sync("LCDC", ref LCDC); ser.Sync("STAT", ref STAT); ser.Sync("scroll_y", ref scroll_y); ser.Sync("scroll_x", ref scroll_x); ser.Sync("LY", ref LY); + ser.Sync("LY_actual", ref LY_actual); ser.Sync("LYinc", ref LY_inc); ser.Sync("LYC", ref LYC); ser.Sync("DMA_addr", ref DMA_addr); @@ -1285,6 +221,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("fetch_sprite_01", ref fetch_sprite_01); ser.Sync("fetch_sprite_4", ref fetch_sprite_4); ser.Sync("going_to_fetch", ref going_to_fetch); + ser.Sync("first_fetch", ref first_fetch); ser.Sync("sprite_fetch_counter", ref sprite_fetch_counter); ser.Sync("sprite_attr_list", ref sprite_attr_list, false); ser.Sync("sprite_pixel_list", ref sprite_pixel_list, false); @@ -1308,10 +245,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("SL_sprites_ordered", ref SL_sprites_ordered, false); ser.Sync("sprite_ordered_index", ref sprite_ordered_index); ser.Sync("blank_frame", ref blank_frame); + ser.Sync("window_latch", ref window_latch); ser.Sync("window_counter", ref window_counter); ser.Sync("window_pre_render", ref window_pre_render); ser.Sync("window_started", ref window_started); + ser.Sync("window_is_reset", ref window_is_reset); ser.Sync("window_tile_inc", ref window_tile_inc); ser.Sync("window_y_tile", ref window_y_tile); ser.Sync("window_x_tile", ref window_x_tile); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs index 817c28201e..5f3fb8e9ce 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/Timer.cs @@ -131,11 +131,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk timer_old = timer; timer++; - // if overflow, set the interrupt flag and reload the timer (4 clocks later) + // if overflow happens, set the interrupt flag and reload the timer (if applicable) if (timer < timer_old) { - pending_reload = 4; - reload_block = false; + if (timer_control.Bit(2)) + { + pending_reload = 4; + reload_block = false; + } + else + { + //TODO: Check if timer still gets reloaded if TAC diabled causes overflow + if (Core.REG_FFFF.Bit(2)) { Core.cpu.FlagI = true; } + Core.REG_FF0F |= 0x04; + } } } @@ -144,7 +153,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk public void Reset() { - divider_reg = 0; + divider_reg = Core._syncSettings._DivInitialTime; timer_reload = 0; timer = 0; timer_old = 0; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs index 8423fd437b..075ee71d27 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs @@ -44,19 +44,35 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy throw new NotImplementedException(); } - [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { - get { throw new NotImplementedException(); } + get { return Math.Max((long)_cycleCount, (long)callbackCycleCount); } } + private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks; private LibGambatte.MemoryCallback _readcb; private LibGambatte.MemoryCallback _writecb; private LibGambatte.MemoryCallback _execcb; - private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); + private void ReadCallback(uint address, ulong cycleOffset) + { + callbackCycleCount = _cycleCount + cycleOffset; + MemoryCallbacks.CallReads(address, "System Bus"); + } + + private void WriteCallback(uint address, ulong cycleOffset) + { + callbackCycleCount = _cycleCount + cycleOffset; + MemoryCallbacks.CallWrites(address, "System Bus"); + } + + private void ExecCallback(uint address, ulong cycleOffset) + { + callbackCycleCount = _cycleCount + cycleOffset; + MemoryCallbacks.CallExecutes(address, "System Bus"); + } /// /// for use in dual core @@ -68,9 +84,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy private void InitMemoryCallbacks() { - _readcb = addr => MemoryCallbacks.CallReads(addr, "System Bus"); - _writecb = addr => MemoryCallbacks.CallWrites(addr, "System Bus"); - _execcb = addr => MemoryCallbacks.CallExecutes(addr, "System Bus"); + _readcb = new LibGambatte.MemoryCallback(ReadCallback); + _writecb = new LibGambatte.MemoryCallback(WriteCallback); + _execcb = new LibGambatte.MemoryCallback(ExecCallback); _memorycallbacks.ActiveChanged += RefreshMemoryCallbacks; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs index 2fd947256c..297b029f11 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -47,12 +47,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public class GambatteSettings { + /* Green Palette private static readonly int[] DefaultPalette = { 10798341, 8956165, 1922333, 337157, 10798341, 8956165, 1922333, 337157, 10798341, 8956165, 1922333, 337157 }; + */ + // Grey Scale Palette + private static readonly int[] DefaultPalette = + { + 0xFFFFFF, 0xAAAAAA, 0x555555, 0, + 0xFFFFFF, 0xAAAAAA, 0x555555, 0, + 0xFFFFFF, 0xAAAAAA, 0x555555, 0 + }; public int[] GBPalette; public GBColors.ColorType CGBColors; @@ -121,7 +130,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy [DisplayName("Equal Length Frames")] [Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")] - [DefaultValue(true)] + [DefaultValue(false)] public bool EqualLengthFrames { get { return _equalLengthFrames; } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 4fa2a67ec4..1f1891e1a5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -244,6 +244,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// total cycles actually executed /// private ulong _cycleCount = 0; + private ulong callbackCycleCount = 0; /// /// number of extra cycles we overran in the last frame diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs index d6b4d8d276..363895ef05 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/GambatteLink.IDebuggable.cs @@ -45,7 +45,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs index ecb9b432ca..1a05daea20 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibGambatte.cs @@ -190,7 +190,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy /// /// the address which the cpu is read\writing [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void MemoryCallback(uint address); + public delegate void MemoryCallback(uint address, ulong cycleOffset); /// /// type of the CDLogger callback diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs index 5aadf2b2d5..6a7b66d383 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.IDebuggable.cs @@ -100,7 +100,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs index 92abebab5d..9a0cd9c61e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64SyncSettings.GLideN64.cs @@ -20,7 +20,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64 UseNativeResolutionFactor = 0; bilinearMode = bilinearFilteringMode.Standard; MaxAnisotropy = false; - CacheSize = 500; + CacheSize = 8000; EnableNoise = true; EnableLOD = true; EnableHWLighting = false; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs index 477ffd3ef9..13d30b938d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/Boards/MMC3_family/Mapper219.cs @@ -1,4 +1,7 @@ -namespace BizHawk.Emulation.Cores.Nintendo.NES +using System; +using BizHawk.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.NES { public sealed class Mapper219 : MMC3Board_Base { @@ -15,14 +18,15 @@ case "MAPPER219": break; default: - return false; + return false; } BaseSetup(); - prgregs[1] = 1; - prgregs[2] = 2; - prgregs[3] = 3; + prgregs[0] = 0xFC; + prgregs[1] = 0xFD; + prgregs[2] = 0xFE; + prgregs[3] = 0xFF; byte r0_0 = (byte)(0 & ~1); byte r0_1 = (byte)(0 | 1); @@ -108,16 +112,21 @@ public override byte ReadPRG(int addr) { int bank_prg = addr >> 13; - bank_prg = prgregs[bank_prg]; + bank_prg = prgregs[bank_prg] & prg_mask; return ROM[((bank_prg << 13) + (addr & 0x1FFF))]; } public override byte ReadPPU(int addr) { + if (addr<0x2000) { int bank_chr = addr >> 10; - bank_chr = chrregs[bank_chr]; + bank_chr = chrregs[bank_chr] & chr_mask; + if (VROM != null) + { + return VROM[((bank_chr << 10) + (addr & 0x3FF))]; + } return VRAM[((bank_chr << 10) + (addr & 0x3FF))]; } else @@ -135,5 +144,16 @@ else base.WritePPU(addr, value); } + + public override void SyncState(Serializer ser) + { + ser.Sync("exregs", ref exregs, false); + ser.Sync("prgregs", ref prgregs, false); + ser.Sync("chrregs", ref chrregs, false); + ser.Sync("bits_rev", ref bits_rev); + ser.Sync("reg_value", ref reg_value); + + base.SyncState(ser); + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs index 7828f82e1d..af0b2fc36c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.Core.cs @@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public partial class NES : IEmulator, ICycleTiming { //hardware/state - public MOS6502X cpu; + public MOS6502X cpu; public PPU ppu; public APU apu; public byte[] ram; @@ -159,11 +159,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void HardReset() { - cpu = new MOS6502X(); - cpu.SetCallbacks(ReadMemory, ReadMemory, PeekMemory, WriteMemory); + cpu = new MOS6502X(new CpuLink(this)) + { + BCD_Enabled = false + }; - cpu.BCD_Enabled = false; - cpu.OnExecFetch = ExecFetch; ppu = new PPU(this); ram = new byte[0x800]; CIRAM = new byte[0x800]; @@ -309,7 +309,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES private int VsyncNum { get; set; } private int VsyncDen { get; set; } - private IController _controller; + private IController _controller = NullController.Instance; bool resetSignal; bool hardResetSignal; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.CpuLink.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.CpuLink.cs new file mode 100644 index 0000000000..13ae69905f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.CpuLink.cs @@ -0,0 +1,27 @@ +using BizHawk.Emulation.Cores.Components.M6502; + +namespace BizHawk.Emulation.Cores.Nintendo.NES +{ + public partial class NES + { + public struct CpuLink : IMOS6502XLink + { + private readonly NES _nes; + + public CpuLink(NES nes) + { + _nes = nes; + } + + public byte DummyReadMemory(ushort address) => _nes.ReadMemory(address); + + public void OnExecFetch(ushort address) => _nes.ExecFetch(address); + + public byte PeekMemory(ushort address) => _nes.CDL == null ? _nes.PeekMemory(address) : _nes.FetchMemory_CDL(address); + + public byte ReadMemory(ushort address) => _nes.CDL == null ? _nes.ReadMemory(address) : _nes.ReadMemory_CDL(address); + + public void WriteMemory(ushort address, byte value) => _nes.WriteMemory(address, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs index c1f2a5b4fa..07ab8aa44b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ICodeDataLogger.cs @@ -9,18 +9,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES public void SetCDL(ICodeDataLog cdl) { CDL = cdl; - if (cdl == null) - { - cpu.ReadMemory = ReadMemory; - cpu.WriteMemory = WriteMemory; - cpu.PeekMemory = PeekMemory; - } - else - { - cpu.ReadMemory = ReadMemory_CDL; - cpu.WriteMemory = WriteMemory; - cpu.PeekMemory = FetchMemory_CDL; - } } public void NewCDL(ICodeDataLog cdl) diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs index d1fe787ab8..58b920f85e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.IDebuggable.cs @@ -64,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES [FeatureNotImplemented] public void Step(StepType type) { throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { get { return cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs index e78ef46cb3..d2bb1f5212 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/QuickNES/QuickNES.IDebuggable.cs @@ -39,7 +39,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs index 4f61f1c23d..0af74a5e41 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesApi.cs @@ -215,6 +215,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES public uint aa, rd; [FieldOffset(28)] public byte sp, dp, db, mdr; + [FieldOffset(32)] + public ushort v, h; } [StructLayout(LayoutKind.Sequential)] @@ -300,17 +302,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES [FieldOffset(176)] public CPURegs cpuregs; - [FieldOffset(208)] + [FieldOffset(212)] public LayerEnables layerEnables; - [FieldOffset(220)] + [FieldOffset(224)] //static configuration-type information which can be grabbed off the core at any time without even needing a QUERY command public SNES_REGION region; - [FieldOffset(224)] - public SNES_MAPPER mapper; - [FieldOffset(228)] - public int unused; + public SNES_MAPPER mapper; //utilities //TODO: make internal, wrap on the API instead of the comm diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs index 92fa298885..7736c32b70 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/SNES/LibsnesCore.IDebuggable.cs @@ -46,6 +46,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES ["Flag I"] = fi, ["Flag Z"] = fz, ["Flag C"] = fc, + ["V"] = regs.v, + ["H"] = regs.h }; } @@ -69,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs index 5fa09dfe3b..5b74440545 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.IDebuggable.cs @@ -46,6 +46,6 @@ namespace BizHawk.Emulation.Cores.PCEngine throw new NotImplementedException(); } - public int TotalExecutedCycles => (int)Cpu.TotalExecutedCycles; + public long TotalExecutedCycles => Cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs index 808a751862..918bb5699b 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/PCEngine.cs @@ -124,7 +124,7 @@ namespace BizHawk.Emulation.Cores.PCEngine private VPC VPC; private ScsiCDBus SCSI; private ADPCM ADPCM; - private IController _controller; + private IController _controller = NullController.Instance; public HuC6280PSG PSG; internal CDAudio CDAudio; diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs index afa8a8a790..1a8472594f 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/ScsiCDBus.cs @@ -409,7 +409,12 @@ namespace BizHawk.Emulation.Cores.PCEngine if (CommandBuffer[4] == 0) SectorsLeftToRead = 256; - DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 5000; // figure out proper read delay later + // figure out proper read delay later + // 10000 fixes Mugen Senshi Valis, which runs code in a timed loop, expecting a certain number of VBlanks + // to happen before reading is complete + // 175000 fixes 4 in 1 CD, loading Gate of Thunder + // which expects a certain number of timer interrupts to happen before loading is complete + DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 175000; pce.CDAudio.Stop(); } diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs index 0ddbd0030a..4122f23bc8 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.Render.cs @@ -22,6 +22,8 @@ namespace BizHawk.Emulation.Cores.PCEngine public int BackgroundY; public int RCRCounter; public int ActiveLine; + public bool latch_bgy; + public int ActiveDisplayStartLine; public int HBlankCycles = 79; public bool PerformSpriteLimit; @@ -31,10 +33,28 @@ namespace BizHawk.Emulation.Cores.PCEngine public void ExecFrame(bool render) { - if (MultiResHack > 0 && render) - Array.Clear(FrameBuffer, 0, FrameBuffer.Length); + Array.Clear(FrameBuffer, 0, FrameBuffer.Length); - int ActiveDisplayStartLine = DisplayStartLine; + ActiveDisplayStartLine = DisplayStartLine; + + /* + Console.Write("VDS: "); + Console.Write((Registers[VPR] >> 8)); + Console.Write(" VSW: "); + Console.Write((Registers[VPR] & 0xFF)); + Console.Write(" VDR: "); + Console.Write((Registers[VDW] & 0xFF)); + Console.Write(" VCR: "); + Console.Write((Registers[VCR] & 0xFF)); + Console.Write(" HDS: "); + Console.Write((Registers[HSR] >> 8)); + Console.Write(" HSW: "); + Console.Write((Registers[HSR] & 0xFF)); + Console.Write(" HDE: "); + Console.Write((Registers[HDR] >> 8)); + Console.Write(" HDW: "); + Console.WriteLine((Registers[HDR] & 0xFF)); + */ while (true) { @@ -59,7 +79,7 @@ namespace BizHawk.Emulation.Cores.PCEngine } } - cpu.Execute(HBlankCycles); + cpu.Execute(24); if (InActiveDisplay) { @@ -67,10 +87,21 @@ namespace BizHawk.Emulation.Cores.PCEngine BackgroundY = Registers[BYR]; else { + if (latch_bgy) + { + BackgroundY = Registers[BYR]; + latch_bgy = false; + } BackgroundY++; BackgroundY &= 0x01FF; + } + } + cpu.Execute(HBlankCycles - 24); + + if (InActiveDisplay) + { if (render) RenderScanLine(); } @@ -107,8 +138,8 @@ namespace BizHawk.Emulation.Cores.PCEngine public void RenderScanLine() { - if (((ActiveLine + ViewStartLine) >= pce.Settings.Bottom_Line) || - ((ActiveLine + ViewStartLine) < pce.Settings.Top_Line)) + if ((ScanLine >= pce.Settings.Bottom_Line) || + (ScanLine < pce.Settings.Top_Line)) return; RenderBackgroundScanline(pce.Settings.ShowBG1); @@ -126,7 +157,7 @@ namespace BizHawk.Emulation.Cores.PCEngine int p = vce.Palette[256]; fixed (int* FBptr = FrameBuffer) { - int* dst = FBptr + (ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch; + int* dst = FBptr + (ScanLine - pce.Settings.Top_Line) * FramePitch; for (int i = 0; i < FrameWidth; i++) *dst++ = p; } @@ -150,7 +181,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { // pointer to the BAT and the framebuffer for this line ushort* BatRow = VRAMptr + yTile * BatWidth; - int* dst = FBptr + (ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch; + int* dst = FBptr + (ScanLine - pce.Settings.Top_Line) * FramePitch; // parameters that change per tile ushort BatEnt; @@ -204,7 +235,7 @@ namespace BizHawk.Emulation.Cores.PCEngine if (BackgroundEnabled == false) { for (int i = 0; i < FrameWidth; i++) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + i] = vce.Palette[256]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + i] = vce.Palette[256]; return; } @@ -228,10 +259,10 @@ namespace BizHawk.Emulation.Cores.PCEngine byte c = PatternBuffer[(tileNo * 64) + (yOfs * 8) + xOfs]; if (c == 0) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + x] = vce.Palette[0]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + x] = vce.Palette[0]; else { - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + x] = show ? vce.Palette[paletteBase + c] : vce.Palette[0]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + x] = show ? vce.Palette[paletteBase + c] : vce.Palette[0]; PriorityBuffer[x] = 1; } } @@ -363,7 +394,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } } @@ -380,7 +411,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } @@ -401,7 +432,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } if (width == 32) @@ -417,7 +448,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { InterSpritePriorityBuffer[xs] = 1; if ((priority || PriorityBuffer[xs] == 0) && show) - FrameBuffer[((ActiveLine + ViewStartLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; + FrameBuffer[((ScanLine - pce.Settings.Top_Line) * FramePitch) + xs] = vce.Palette[paletteBase + pixel]; } } } diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs index 824f4c6a7d..7574ce7f93 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VDC.cs @@ -1,6 +1,7 @@ using BizHawk.Common; using BizHawk.Emulation.Common; using BizHawk.Emulation.Cores.Components.H6280; +using System; namespace BizHawk.Emulation.Cores.PCEngine { @@ -48,8 +49,7 @@ namespace BizHawk.Emulation.Cores.PCEngine public int RequestedFrameWidth => ((Registers[HDR] & 0x3F) + 1) * 8; public int RequestedFrameHeight => (Registers[VDW] & 0x1FF) + 1; - public int DisplayStartLine => (Registers[VPR] >> 8) + (Registers[VPR] & 0x1F); - public int ViewStartLine => (Registers[VPR] >> 8) + 2; + public int DisplayStartLine => (Registers[VPR] >> 8) + 3 + (Registers[VPR] & 0x1F); private const int MAWR = 0; // Memory Address Write Register private const int MARR = 1; // Memory Address Read Register @@ -118,8 +118,8 @@ namespace BizHawk.Emulation.Cores.PCEngine Registers[RegisterLatch] &= 0xFF00; Registers[RegisterLatch] |= value; - if (RegisterLatch == BYR) - BackgroundY = Registers[BYR] & 0x1FF; + if (RegisterLatch == BYR) { latch_bgy = true; } + //BackgroundY = Registers[BYR] & 0x1FF; RegisterCommit(RegisterLatch, msbComplete: false); } @@ -154,14 +154,14 @@ namespace BizHawk.Emulation.Cores.PCEngine break; case BYR: Registers[BYR] &= 0x1FF; - BackgroundY = Registers[BYR]; + latch_bgy = true; + //BackgroundY = Registers[BYR]; break; case HDR: // Horizontal Display Register - update framebuffer size FrameWidth = RequestedFrameWidth; FramePitch = MultiResHack == 0 ? FrameWidth : MultiResHack; //if (FrameBuffer.Length != FramePitch * FrameHeight) //FrameBuffer = new int[FramePitch * FrameHeight]; - FrameBuffer = new int[320 * 262]; break; case VDW: // Vertical Display Word? - update framebuffer size //FrameHeight = RequestedFrameHeight; @@ -172,7 +172,6 @@ namespace BizHawk.Emulation.Cores.PCEngine FramePitch = MultiResHack; //if (FrameBuffer.Length != FramePitch * FrameHeight) //FrameBuffer = new int[FramePitch * FrameHeight]; - FrameBuffer = new int[320 * 262]; break; case LENR: // Initiate DMA transfer if (!msbComplete) break; @@ -323,6 +322,7 @@ namespace BizHawk.Emulation.Cores.PCEngine ser.Sync("ScanLine", ref ScanLine); ser.Sync("BackgroundY", ref BackgroundY); + ser.Sync("latch_bgy", ref latch_bgy); ser.Sync("RCRCounter", ref RCRCounter); ser.Sync("ActiveLine", ref ActiveLine); ser.EndSection(); diff --git a/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs b/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs index 9d9e27b460..8c68463029 100644 --- a/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs +++ b/BizHawk.Emulation.Cores/Consoles/PC Engine/VPC.cs @@ -101,6 +101,7 @@ namespace BizHawk.Emulation.Cores.PCEngine const int DCR = 15; int EffectivePriorityMode = 0; + int ScanLine; int FrameHeight; int FrameWidth; @@ -122,12 +123,14 @@ namespace BizHawk.Emulation.Cores.PCEngine EffectivePriorityMode = 0; } + Array.Clear(FrameBuffer, 0, FrameBuffer.Length); + // Latch frame dimensions and framebuffer, for purely dumb reasons FrameWidth = VDC1.BufferWidth; FrameHeight = VDC1.BufferHeight; FrameBuffer = VDC1.GetVideoBuffer(); - int ScanLine = 0; + ScanLine = 0; int ActiveDisplayStartLine = VDC1.DisplayStartLine; while (true) @@ -172,7 +175,7 @@ namespace BizHawk.Emulation.Cores.PCEngine } } - CPU.Execute(VDC1.HBlankCycles); + CPU.Execute(24); if (InActiveDisplay) { @@ -183,15 +186,33 @@ namespace BizHawk.Emulation.Cores.PCEngine } else { + if (VDC1.latch_bgy) + { + VDC1.BackgroundY = VDC2.Registers[BYR]; + VDC1.latch_bgy = false; + } + + if (VDC2.latch_bgy) + { + VDC2.BackgroundY = VDC2.Registers[BYR]; + VDC2.latch_bgy = false; + } + VDC1.BackgroundY++; VDC1.BackgroundY &= 0x01FF; VDC2.BackgroundY++; VDC2.BackgroundY &= 0x01FF; } + } + CPU.Execute(VDC1.HBlankCycles - 24); + + if (InActiveDisplay) + { if (render) RenderScanLine(); } + if (ScanLine == VBlankLine && VDC1.VBlankInterruptEnabled) VDC1.StatusByte |= VDC.StatusVerticalBlanking; @@ -239,12 +260,12 @@ namespace BizHawk.Emulation.Cores.PCEngine private void RenderScanLine() { - if (((VDC1.ActiveLine + VDC1.ViewStartLine) >= PCE.Settings.Bottom_Line) || - ((VDC1.ActiveLine + VDC1.ViewStartLine) < PCE.Settings.Top_Line)) + if ((ScanLine >= PCE.Settings.Bottom_Line) || + (ScanLine < PCE.Settings.Top_Line)) { return; } - InitializeScanLine(VDC1.ActiveLine); + InitializeScanLine(ScanLine); switch (EffectivePriorityMode) { @@ -269,7 +290,7 @@ namespace BizHawk.Emulation.Cores.PCEngine Array.Clear(PriorityBuffer, 0, FrameWidth); // Initialize scanline to background color for (int i = 0; i < FrameWidth; i++) - FrameBuffer[((scanline + VDC1.ViewStartLine) * FrameWidth) + i] = VCE.Palette[256]; + FrameBuffer[((scanline) * FrameWidth) + i] = VCE.Palette[256]; } private unsafe void RenderBackgroundScanline(VDC vdc, byte priority, bool show) @@ -293,7 +314,7 @@ namespace BizHawk.Emulation.Cores.PCEngine { // pointer to the BAT and the framebuffer for this line ushort* BatRow = VRAMptr + yTile * vdc.BatWidth; - int* dst = FBptr + (vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth; + int* dst = FBptr + (ScanLine - PCE.Settings.Top_Line) * FrameWidth; // parameters that change per tile ushort BatEnt; @@ -449,7 +470,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } @@ -468,7 +489,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } @@ -490,7 +511,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } @@ -508,7 +529,7 @@ namespace BizHawk.Emulation.Cores.PCEngine byte myPriority = priority ? highPriority : lowPriority; if (PriorityBuffer[xs] < myPriority) { - if (show) FrameBuffer[((vdc.ActiveLine + vdc.ViewStartLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; + if (show) FrameBuffer[((ScanLine - PCE.Settings.Top_Line) * FrameWidth) + xs] = VCE.Palette[paletteBase + pixel]; PriorityBuffer[xs] = myPriority; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs index d8b0d15402..2f142e92da 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IDebuggable.cs @@ -142,7 +142,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem throw new NotImplementedException(); } - public int TotalExecutedCycles + public long TotalExecutedCycles { get { return Cpu.TotalExecutedCycles; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs index 59ab099595..e848c03d2d 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IEmulator.cs @@ -10,7 +10,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem { get { - if (IsGameGear) + if (IsGameGear_C) { return GGController; } @@ -58,10 +58,14 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem Cpu.TraceCallback = null; } - if (IsGameGear == false) + if (IsGameGear_C == false) { Cpu.NonMaskableInterrupt = controller.IsPressed("Pause"); } + else + { + Cpu.NonMaskableInterrupt = controller.IsPressed("P1 Start"); + } if (IsGame3D && Settings.Fix3D) { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs index c01b5f50a3..f2b241bd3a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.IStatable.cs @@ -64,6 +64,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem ser.Sync("Bios_bank", ref Bios_bank); ser.Sync("Port01", ref Port01); ser.Sync("Port02", ref Port02); + ser.Sync("Port05", ref Port05); ser.Sync("Port3E", ref Port3E); ser.Sync("Port3F", ref Port3F); ser.Sync("Controller1SelectHigh", ref Controller1SelectHigh); diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs index ffbe65cd1b..58f1a4683c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.Input.cs @@ -587,14 +587,14 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem byte ReadPort0() { - if (IsGameGear == false) + if (IsGameGear_C == false) { return 0xFF; } byte value = 0xFF; if ((_controller.IsPressed("Pause") && !IsGameGear) || - (_controller.IsPressed("P1 Start") && IsGameGear)) + (_controller.IsPressed("P1 Start") && IsGameGear_C)) { value ^= 0x80; } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs index 0f393dff0c..be58fdfc49 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/SMS/SMS.cs @@ -5,6 +5,7 @@ using BizHawk.Emulation.Common; using BizHawk.Emulation.Common.Components; using BizHawk.Emulation.Cores.Components; using BizHawk.Emulation.Cores.Components.Z80A; +using BizHawk.Common.BufferExtensions; /***************************************************** TODO: @@ -35,6 +36,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem MemoryCallbacks = new MemoryCallbackSystem(new[] { "System Bus" }); IsGameGear = game.System == "GG"; + IsGameGear_C = game.System == "GG"; IsSG1000 = game.System == "SG"; RomData = rom; @@ -91,6 +93,17 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem OnExecFetch = OnExecMemory }; + + if (game["GG_in_SMS"]) + { + // skip setting the BIOS because this is a game gear game that puts the system + // in SMS compatibility mode (it will fail the check sum if played on an actual SMS though.) + IsGameGear = false; + IsGameGear_C = true; + game.System = "GG"; + Console.WriteLine("Using SMS Compatibility mode for Game Gear System"); + } + Vdp = new VDP(this, Cpu, IsGameGear ? VdpMode.GameGear : VdpMode.SMS, Region); (ServiceProvider as BasicServiceProvider).Register(Vdp); PSG = new SN76489(); @@ -150,7 +163,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem Port3E = 0xF7; // Disable cartridge, enable BIOS rom InitBiosMapper(); } - else if (game.System == "SMS") + else if ((game.System == "SMS") && !game["GG_in_SMS"]) { BiosRom = comm.CoreFileProvider.GetFirmware("SMS", RegionStr, false); @@ -212,16 +225,18 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem private SN76489 PSG; private YM2413 YM2413; public bool IsGameGear { get; set; } + public bool IsGameGear_C { get; set; } public bool IsSG1000 { get; set; } private bool HasYM2413 = false; private bool PortDEEnabled = false; - private IController _controller; + private IController _controller = NullController.Instance; private int _frame = 0; private byte Port01 = 0xFF; private byte Port02 = 0xFF; + private byte Port05 = 0x00; private byte Port3E = 0xAF; private byte Port3F = 0xFF; private byte PortDE = 0x00; @@ -308,6 +323,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem port &= 0xFF; if (port < 0x40) // General IO ports { + switch (port) { case 0x00: return ReadPort0(); @@ -315,7 +331,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem case 0x02: return Port02; case 0x03: return 0x00; case 0x04: return 0xFF; - case 0x05: return 0x00; + case 0x05: return Port05; case 0x06: return 0xFF; case 0x3E: return Port3E; default: return 0xFF; @@ -356,6 +372,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem { case 0x01: Port01 = value; break; case 0x02: Port02 = value; break; + case 0x05: Port05 = value; break; case 0x06: PSG.StereoPanning = value; break; case 0x3E: Port3E = value; break; case 0x3F: Port3F = value; break; diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs index ba82d0ff1a..9b5e7363d9 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs @@ -47,7 +47,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs index 261a4418a3..e5ef0463c2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs @@ -198,6 +198,63 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx [DefaultValue(false)] public bool PadScreen320 { get { return _PadScreen320; } set { _PadScreen320 = value; } } + + [DeepEqualsIgnore] + [JsonIgnore] + private bool _Backdrop; + + [DisplayName("Use custom backdrop color")] + [Description("Filler when layers are off")] + [DefaultValue((bool)false)] + public bool Backdrop { get { return _Backdrop; } set { _Backdrop = value; } } + + + public GPGXSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public GPGXSettings Clone() + { + return (GPGXSettings)MemberwiseClone(); + } + + public LibGPGX.DrawMask GetDrawMask() + { + LibGPGX.DrawMask ret = 0; + if (DrawBGA) ret |= LibGPGX.DrawMask.BGA; + if (DrawBGB) ret |= LibGPGX.DrawMask.BGB; + if (DrawBGW) ret |= LibGPGX.DrawMask.BGW; + if (DrawObj) ret |= LibGPGX.DrawMask.Obj; + if (Backdrop) ret |= LibGPGX.DrawMask.Backdrop; + return ret; + } + + public static bool NeedsReboot(GPGXSettings x, GPGXSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + + + } + + public class GPGXSyncSettings + { + [DisplayName("Use Six Button Controllers")] + [Description("Controls the type of any attached normal controllers; six button controllers are used if true, otherwise three button controllers. Some games don't work correctly with six button controllers. Not relevant if other controller types are connected.")] + [DefaultValue(true)] + public bool UseSixButton { get; set; } + + [DisplayName("Control Type")] + [Description("Sets the type of controls that are plugged into the console. Some games will automatically load with a different control type.")] + [DefaultValue(ControlType.Normal)] + public ControlType ControlType { get; set; } + + [DisplayName("Autodetect Region")] + [Description("Sets the region of the emulated console. Many games can run on multiple regions and will behave differently on different ones. Some games may require a particular region.")] + [DefaultValue(LibGPGX.Region.Autodetect)] + public LibGPGX.Region Region { get; set; } + [DisplayName("Audio Filter")] [DefaultValue(LibGPGX.InitSettings.FilterType.LowPass)] public LibGPGX.InitSettings.FilterType Filter { get; set; } @@ -233,46 +290,11 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx [DefaultValue((short)1)] public short HighGain { get; set; } - [DeepEqualsIgnore] - [JsonIgnore] - private bool _Backdrop; - - [DisplayName("Use custom backdrop color")] - [Description("Filler when layers are off")] - [DefaultValue((bool)false)] - public bool Backdrop { get { return _Backdrop; } set { _Backdrop = value; } } - [Description("Magic pink by default. Requires core reboot")] [TypeConverter(typeof(UintToHexConverter))] [DefaultValue((uint)0xffff00ff)] public uint BackdropColor { get; set; } - public GPGXSettings() - { - SettingsUtil.SetDefaultValues(this); - } - - public GPGXSettings Clone() - { - return (GPGXSettings)MemberwiseClone(); - } - - public LibGPGX.DrawMask GetDrawMask() - { - LibGPGX.DrawMask ret = 0; - if (DrawBGA) ret |= LibGPGX.DrawMask.BGA; - if (DrawBGB) ret |= LibGPGX.DrawMask.BGB; - if (DrawBGW) ret |= LibGPGX.DrawMask.BGW; - if (DrawObj) ret |= LibGPGX.DrawMask.Obj; - if (Backdrop) ret |= LibGPGX.DrawMask.Backdrop; - return ret; - } - - public static bool NeedsReboot(GPGXSettings x, GPGXSettings y) - { - return !DeepEquality.DeepEquals(x, y); - } - public LibGPGX.InitSettings GetNativeSettings() { return new LibGPGX.InitSettings @@ -287,24 +309,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx BackdropColor = BackdropColor }; } - } - - public class GPGXSyncSettings - { - [DisplayName("Use Six Button Controllers")] - [Description("Controls the type of any attached normal controllers; six button controllers are used if true, otherwise three button controllers. Some games don't work correctly with six button controllers. Not relevant if other controller types are connected.")] - [DefaultValue(true)] - public bool UseSixButton { get; set; } - - [DisplayName("Control Type")] - [Description("Sets the type of controls that are plugged into the console. Some games will automatically load with a different control type.")] - [DefaultValue(ControlType.Normal)] - public ControlType ControlType { get; set; } - - [DisplayName("Autodetect Region")] - [Description("Sets the region of the emulated console. Many games can run on multiple regions and will behave differently on different ones. Some games may require a particular region.")] - [DefaultValue(LibGPGX.Region.Autodetect)] - public LibGPGX.Region Region { get; set; } public GPGXSyncSettings() { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs index fafb34dde3..c28bfe7067 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs @@ -113,7 +113,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx break; } - if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings())) + if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _syncSettings.GetNativeSettings())) throw new Exception("gpgx_init() failed"); { diff --git a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs index 47cc4221e9..b71f3223f5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.IDebuggable.cs @@ -89,7 +89,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSX public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } diff --git a/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs b/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs index e1defde253..49f91a1824 100644 --- a/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs +++ b/BizHawk.Emulation.Cores/Consoles/WonderSwan/WonderSwan.cs @@ -131,7 +131,7 @@ namespace BizHawk.Emulation.Cores.WonderSwan public void Step(StepType type) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles { get { throw new NotImplementedException(); } } + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } BizSwan.MemoryCallback ReadCallbackD; BizSwan.MemoryCallback WriteCallbackD; diff --git a/BizHawk.Emulation.Cores/FileID.cs b/BizHawk.Emulation.Cores/FileID.cs index edc5252484..1314be8b6f 100644 --- a/BizHawk.Emulation.Cores/FileID.cs +++ b/BizHawk.Emulation.Cores/FileID.cs @@ -42,6 +42,7 @@ namespace BizHawk.Emulation.Cores WS, WSC, NGC, C64, + ZXSpectrum, INT, A26, A52, A78, LNX, diff --git a/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs b/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs index 243688c6f0..f52709963b 100644 --- a/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs +++ b/BizHawk.Emulation.Cores/Libretro/LibretroCore.cs @@ -17,9 +17,9 @@ using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Libretro { - [Core("Libretro", "zeromus")] - [ServiceNotApplicable(typeof(IDriveLight))] - public unsafe partial class LibretroCore : IEmulator, ISettable, + [Core("Libretro", "zeromus")] + [ServiceNotApplicable(typeof(IDriveLight))] + public unsafe partial class LibretroCore : IEmulator, ISettable, ISaveRam, IStatable, IVideoProvider, IInputPollable { private LibretroApi api; @@ -92,23 +92,23 @@ namespace BizHawk.Emulation.Cores.Libretro public bool LoadData(byte[] data, string id) { - bool ret = api.CMD_LoadData(data, id); - LoadHandler(); + bool ret = api.CMD_LoadData(data, id); + LoadHandler(); return ret; } - public bool LoadPath(string path) - { - bool ret = api.CMD_LoadPath(path); - LoadHandler(); - return ret; - } - - public bool LoadNoGame() - { - bool ret = api.CMD_LoadNoGame(); - LoadHandler(); - return ret; + public bool LoadPath(string path) + { + bool ret = api.CMD_LoadPath(path); + LoadHandler(); + return ret; + } + + public bool LoadNoGame() + { + bool ret = api.CMD_LoadNoGame(); + LoadHandler(); + return ret; } void LoadHandler() @@ -150,7 +150,7 @@ namespace BizHawk.Emulation.Cores.Libretro [FeatureNotImplemented] public void SetCpuRegister(string register, int value) { throw new NotImplementedException(); } [FeatureNotImplemented] - public int TotalExecutedCycles { get { throw new NotImplementedException(); } } + public long TotalExecutedCycles { get { throw new NotImplementedException(); } } private IController _controller; diff --git a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs index 5a3bbcce9a..73259a9985 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs +++ b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs @@ -89,5 +89,85 @@ namespace BizHawk.Emulation.Cores.Properties { return ((byte[])(obj)); } } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM0_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM0_bin", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM1_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM1_bin", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM2_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM2_bin", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Spectrum3_V4_0_ROM3_bin { + get { + object obj = ResourceManager.GetObject("Spectrum3_V4_0_ROM3_bin", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_128_ROM { + get { + object obj = ResourceManager.GetObject("ZX_128_ROM", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_48_ROM { + get { + object obj = ResourceManager.GetObject("ZX_48_ROM", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_plus2_rom { + get { + object obj = ResourceManager.GetObject("ZX_plus2_rom", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ZX_plus2a_rom { + get { + object obj = ResourceManager.GetObject("ZX_plus2a_rom", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/BizHawk.Emulation.Cores/Properties/Resources.resx b/BizHawk.Emulation.Cores/Properties/Resources.resx index bdb07b9a9f..783b8f5ede 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.resx +++ b/BizHawk.Emulation.Cores/Properties/Resources.resx @@ -127,4 +127,28 @@ ..\Resources\sgb-cart-present.spc.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file + + ..\Resources\Spectrum3_V4-0_ROM0.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\Spectrum3_V4-0_ROM1.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\Spectrum3_V4-0_ROM2.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\Spectrum3_V4-0_ROM3.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\128.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\48.ROM.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\plus2a.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\plus2.rom.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/BizHawk.Emulation.Cores/Resources/128.ROM.gz b/BizHawk.Emulation.Cores/Resources/128.ROM.gz new file mode 100644 index 0000000000..697dc6e872 Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/128.ROM.gz differ diff --git a/BizHawk.Emulation.Cores/Resources/48.ROM.gz b/BizHawk.Emulation.Cores/Resources/48.ROM.gz new file mode 100644 index 0000000000..76b8c9d536 Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/48.ROM.gz differ diff --git a/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM0.bin.gz b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM0.bin.gz new file mode 100644 index 0000000000..d70d805d62 Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM0.bin.gz differ diff --git a/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM1.bin.gz b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM1.bin.gz new file mode 100644 index 0000000000..f813586462 Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM1.bin.gz differ diff --git a/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM2.bin.gz b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM2.bin.gz new file mode 100644 index 0000000000..df586562a5 Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM2.bin.gz differ diff --git a/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM3.bin.gz b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM3.bin.gz new file mode 100644 index 0000000000..3062bc7fb1 Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/Spectrum3_V4-0_ROM3.bin.gz differ diff --git a/BizHawk.Emulation.Cores/Resources/plus2.rom.gz b/BizHawk.Emulation.Cores/Resources/plus2.rom.gz new file mode 100644 index 0000000000..274507f352 Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/plus2.rom.gz differ diff --git a/BizHawk.Emulation.Cores/Resources/plus2a.rom.gz b/BizHawk.Emulation.Cores/Resources/plus2a.rom.gz new file mode 100644 index 0000000000..418ea1f1cf Binary files /dev/null and b/BizHawk.Emulation.Cores/Resources/plus2a.rom.gz differ diff --git a/BizHawk.Emulation.Cores/Sound/SN76489.cs b/BizHawk.Emulation.Cores/Sound/SN76489.cs index 1944edf267..c5e7e113c5 100644 --- a/BizHawk.Emulation.Cores/Sound/SN76489.cs +++ b/BizHawk.Emulation.Cores/Sound/SN76489.cs @@ -21,15 +21,15 @@ namespace BizHawk.Emulation.Cores.Components const int SampleRate = 44100; private static readonly byte[] LogScale = { 0, 10, 13, 16, 20, 26, 32, 40, 51, 64, 81, 102, 128, 161, 203, 255 }; - public void Mix(short[] samples, int start, int len, int maxVolume) + public void Mix(short[] samples, long start, long len, int maxVolume) { if (Volume == 0) return; float adjustedWaveLengthInSamples = SampleRate / (Noise ? (Frequency / (float)Wave.Length) : Frequency); float moveThroughWaveRate = Wave.Length / adjustedWaveLengthInSamples; - int end = start + len; - for (int i = start; i < end; ) + long end = start + len; + for (long i = start; i < end; ) { short value = Wave[(int)WaveOffset]; @@ -46,7 +46,7 @@ namespace BizHawk.Emulation.Cores.Components public byte PsgLatch; private readonly Queue commands = new Queue(256); - int frameStartTime, frameStopTime; + long frameStartTime, frameStopTime; const int PsgBase = 111861; @@ -84,7 +84,7 @@ namespace BizHawk.Emulation.Cores.Components } } - public void BeginFrame(int cycles) + public void BeginFrame(long cycles) { while (commands.Count > 0) { @@ -94,12 +94,12 @@ namespace BizHawk.Emulation.Cores.Components frameStartTime = cycles; } - public void EndFrame(int cycles) + public void EndFrame(long cycles) { frameStopTime = cycles; } - public void WritePsgData(byte value, int cycles) + public void WritePsgData(byte value, long cycles) { commands.Enqueue(new QueuedCommand { Value = value, Time = cycles - frameStartTime }); } @@ -227,15 +227,15 @@ namespace BizHawk.Emulation.Cores.Components public void DiscardSamples() { commands.Clear(); } public void GetSamples(short[] samples) { - int elapsedCycles = frameStopTime - frameStartTime; + long elapsedCycles = frameStopTime - frameStartTime; if (elapsedCycles == 0) elapsedCycles = 1; // hey it's better than diving by zero - int start = 0; + long start = 0; while (commands.Count > 0) { var cmd = commands.Dequeue(); - int pos = ((cmd.Time * samples.Length) / elapsedCycles) & ~1; + long pos = ((cmd.Time * samples.Length) / elapsedCycles) & ~1; GetSamplesImmediate(samples, start, pos - start); start = pos; WritePsgDataImmediate(cmd.Value); @@ -243,16 +243,16 @@ namespace BizHawk.Emulation.Cores.Components GetSamplesImmediate(samples, start, samples.Length - start); } - public void GetSamplesImmediate(short[] samples, int start, int len) + public void GetSamplesImmediate(short[] samples, long start, long len) { - for (int i = 0; i < 4; i++) + for (long i = 0; i < 4; i++) Channels[i].Mix(samples, start, len, MaxVolume); } class QueuedCommand { public byte Value; - public int Time; + public long Time; } } } \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj.DotSettings b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/BizHawk.sln.DotSettings b/BizHawk.sln.DotSettings index e447905302..33207db7ed 100644 --- a/BizHawk.sln.DotSettings +++ b/BizHawk.sln.DotSettings @@ -2,6 +2,7 @@ False True ExplicitlyExcluded + DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW DO_NOT_SHOW @@ -47,6 +48,21 @@ DO_NOT_SHOW HINT DO_NOT_SHOW + 0 + None + False + False + False + USE_TABS_ONLY + Tab + NEXT_LINE + True + False + True + True + CHOP_ALWAYS + CHOP_ALWAYS + CHOP_ALWAYS AF ARGB AV @@ -96,5 +112,61 @@ UI VBA ROM - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + C:\Users\Andy Delikat\AppData\Local\JetBrains\Transient\ReSharperPlatformVs15\v11_5c6a09eb\SolutionCaches + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + LIVE_MONITOR + DO_NOTHING + LIVE_MONITOR \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL.GdiPlus/BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL.GdiPlus/BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL.GdiPlus/BizHawk.Bizware.BizwareGL.GdiPlus.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL.OpenTK/BizHawk.Bizware.BizwareGL.OpenTK.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL.SlimDX/BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL.SlimDX/BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL.SlimDX/BizHawk.Bizware.BizwareGL.SlimDX.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj.DotSettings b/Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Bizware/BizHawk.Bizware.BizwareGL/BizHawk.Bizware.BizwareGL.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/ReadMe.txt b/ReadMe.txt deleted file mode 100644 index bc60bf4b01..0000000000 --- a/ReadMe.txt +++ /dev/null @@ -1 +0,0 @@ -TODO: diff --git a/Version/Version.csproj.DotSettings b/Version/Version.csproj.DotSettings new file mode 100644 index 0000000000..73e96563f9 --- /dev/null +++ b/Version/Version.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp60 \ No newline at end of file diff --git a/libgambatte/include/gambatte.h b/libgambatte/include/gambatte.h index 2b953984a9..94bab7befb 100644 --- a/libgambatte/include/gambatte.h +++ b/libgambatte/include/gambatte.h @@ -28,6 +28,7 @@ namespace gambatte { enum { BG_PALETTE = 0, SP1_PALETTE = 1, SP2_PALETTE = 2 }; +typedef void (*MemoryCallback)(int32_t address, int64_t cycleOffset); typedef void (*CDCallback)(int32_t addr, int32_t addrtype, int32_t flags); enum eCDLog_AddrType @@ -105,9 +106,9 @@ public: /** Sets the callback used for getting input state. */ void setInputGetter(unsigned (*getInput)()); - void setReadCallback(void (*callback)(unsigned)); - void setWriteCallback(void (*callback)(unsigned)); - void setExecCallback(void (*callback)(unsigned)); + void setReadCallback(MemoryCallback); + void setWriteCallback(MemoryCallback); + void setExecCallback(MemoryCallback); void setCDCallback(CDCallback); void setTraceCallback(void (*callback)(void *)); void setScanlineCallback(void (*callback)(), int sl); diff --git a/libgambatte/src/cinterface.cpp b/libgambatte/src/cinterface.cpp index fc4d793a75..ad8247f17c 100644 --- a/libgambatte/src/cinterface.cpp +++ b/libgambatte/src/cinterface.cpp @@ -85,17 +85,17 @@ GBEXPORT void gambatte_setinputgetter(GB *g, unsigned (*getinput)(void)) g->setInputGetter(getinput); } -GBEXPORT void gambatte_setreadcallback(GB *g, void (*callback)(unsigned)) +GBEXPORT void gambatte_setreadcallback(GB *g, MemoryCallback callback) { g->setReadCallback(callback); } -GBEXPORT void gambatte_setwritecallback(GB *g, void (*callback)(unsigned)) +GBEXPORT void gambatte_setwritecallback(GB *g, MemoryCallback callback) { g->setWriteCallback(callback); } -GBEXPORT void gambatte_setexeccallback(GB *g, void (*callback)(unsigned)) +GBEXPORT void gambatte_setexeccallback(GB *g, MemoryCallback callback) { g->setExecCallback(callback); } diff --git a/libgambatte/src/cpu.cpp b/libgambatte/src/cpu.cpp index 0fcebe0ca6..8ea6714cb3 100644 --- a/libgambatte/src/cpu.cpp +++ b/libgambatte/src/cpu.cpp @@ -45,6 +45,7 @@ CPU::CPU() } long CPU::runFor(const unsigned long cycles) { + memory.setBasetime(cycleCounter_); process(cycles/* << memory.isDoubleSpeed()*/); const long csb = memory.cyclesSinceBlit(cycleCounter_); @@ -633,15 +634,6 @@ void CPU::process(const unsigned long cycles) { //Halt CPU and LCD display until button pressed: case 0x10: { - unsigned char followingByte; - PEEK(followingByte, PC); - PC = (PC + 1) & 0xFFFF; - - //if (followingByte != 0x00) { - //memory.di(); - //memory.blackScreen(); - //} - cycleCounter = memory.stop(cycleCounter); if (cycleCounter < memory.nextEventTime()) { @@ -1164,13 +1156,13 @@ void CPU::process(const unsigned long cycles) { //halt (4 cycles): case 0x76: - if (!memory.ime() && (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F)) { - if (memory.isCgb()) - cycleCounter += 4; + if (memory.ff_read(0xFF0F, cycleCounter) & memory.ff_read(0xFFFF, cycleCounter) & 0x1F) { + if (memory.ime()) + PC = (PC - 1) & 0xFFFF; else skip = true; } else { - memory.halt(); + memory.halt(cycleCounter); if (cycleCounter < memory.nextEventTime()) { const unsigned long cycles = memory.nextEventTime() - cycleCounter; diff --git a/libgambatte/src/cpu.h b/libgambatte/src/cpu.h index 46dc592015..427847e95d 100644 --- a/libgambatte/src/cpu.h +++ b/libgambatte/src/cpu.h @@ -72,15 +72,15 @@ public: memory.setInputGetter(getInput); } - void setReadCallback(void (*callback)(unsigned)) { + void setReadCallback(MemoryCallback callback) { memory.setReadCallback(callback); } - void setWriteCallback(void (*callback)(unsigned)) { + void setWriteCallback(MemoryCallback callback) { memory.setWriteCallback(callback); } - void setExecCallback(void (*callback)(unsigned)) { + void setExecCallback(MemoryCallback callback) { memory.setExecCallback(callback); } diff --git a/libgambatte/src/gambatte.cpp b/libgambatte/src/gambatte.cpp index 167e1093e1..a1e3dfa65c 100644 --- a/libgambatte/src/gambatte.cpp +++ b/libgambatte/src/gambatte.cpp @@ -108,15 +108,15 @@ void GB::setInputGetter(unsigned (*getInput)()) { p_->cpu.setInputGetter(getInput); } -void GB::setReadCallback(void (*callback)(unsigned)) { +void GB::setReadCallback(MemoryCallback callback) { p_->cpu.setReadCallback(callback); } -void GB::setWriteCallback(void (*callback)(unsigned)) { +void GB::setWriteCallback(MemoryCallback callback) { p_->cpu.setWriteCallback(callback); } -void GB::setExecCallback(void (*callback)(unsigned)) { +void GB::setExecCallback(MemoryCallback callback) { p_->cpu.setExecCallback(callback); } diff --git a/libgambatte/src/memory.cpp b/libgambatte/src/memory.cpp index a5fcc148ef..0b92460ca5 100644 --- a/libgambatte/src/memory.cpp +++ b/libgambatte/src/memory.cpp @@ -154,6 +154,9 @@ unsigned long Memory::event(unsigned long cycleCounter) { switch (intreq.minEventId()) { case UNHALT: + nontrivial_ff_write(0xFF04, 0, cycleCounter); + PC = (PC + 1) & 0xFFFF; + cycleCounter += 4; intreq.unhalt(); intreq.setEventTime(DISABLED_TIME); break; @@ -265,8 +268,12 @@ unsigned long Memory::event(unsigned long cycleCounter) { display.update(cycleCounter); break; case INTERRUPTS: + if (stopped) { + intreq.setEventTime(DISABLED_TIME); + break; + } if (halted()) { - if (gbIsCgb_) + if (gbIsCgb_ || (!gbIsCgb_ && cycleCounter <= halttime + 4)) cycleCounter += 4; intreq.unhalt(); @@ -311,7 +318,7 @@ unsigned long Memory::event(unsigned long cycleCounter) { } unsigned long Memory::stop(unsigned long cycleCounter) { - cycleCounter += 4 << isDoubleSpeed(); + cycleCounter += 4; if (ioamhram[0x14D] & isCgb()) { sound.generate_samples(cycleCounter, isDoubleSpeed()); @@ -329,17 +336,11 @@ unsigned long Memory::stop(unsigned long cycleCounter) { // otherwise, the cpu should be allowed to stay halted as long as needed // so only execute this line when switching speed intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); + intreq.setEventTime(cycleCounter + 0x20000); } else { - if ((ioamhram[0x100] & 0x30) == 0x30) { - di(); - intreq.halt(); - } - else { - intreq.halt(); - intreq.setEventTime(cycleCounter + 0x20000 + isDoubleSpeed() * 8); - } + stopped = true; + intreq.halt(); } return cycleCounter; @@ -651,6 +652,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned case 0x04: ioamhram[0x104] = 0; divLastUpdate = cycleCounter; + tima.resTac(cycleCounter, TimaInterruptRequester(intreq)); return; case 0x05: tima.setTima(data, cycleCounter, TimaInterruptRequester(intreq)); @@ -660,7 +662,7 @@ void Memory::nontrivial_ff_write(const unsigned P, unsigned data, const unsigned break; case 0x07: data |= 0xF8; - tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq)); + tima.setTac(data, cycleCounter, TimaInterruptRequester(intreq), gbIsCgb_); break; case 0x0F: updateIrqs(cycleCounter); @@ -1141,6 +1143,8 @@ SYNCFUNC(Memory) NSS(cgbSwitching); NSS(agbMode); NSS(gbIsCgb_); + NSS(stopped); + NSS(halttime); SSS(intreq); SSS(tima); diff --git a/libgambatte/src/memory.h b/libgambatte/src/memory.h index 95a2e18f16..af9758eab5 100644 --- a/libgambatte/src/memory.h +++ b/libgambatte/src/memory.h @@ -42,12 +42,15 @@ class Memory { bool cgbSwitching; bool agbMode; bool gbIsCgb_; + bool stopped; unsigned short &SP; unsigned short &PC; + unsigned long basetime; + unsigned long halttime; - void (*readCallback)(unsigned); - void (*writeCallback)(unsigned); - void (*execCallback)(unsigned); + MemoryCallback readCallback; + MemoryCallback writeCallback; + MemoryCallback execCallback; CDCallback cdCallback; void(*linkCallback)(); @@ -128,12 +131,14 @@ public: return cc < intreq.eventTime(BLIT) ? -1 : static_cast((cc - intreq.eventTime(BLIT)) >> isDoubleSpeed()); } - void halt() { intreq.halt(); } + void halt(unsigned long cycleCounter) { halttime = cycleCounter; intreq.halt(); } void ei(unsigned long cycleCounter) { if (!ime()) { intreq.ei(cycleCounter); } } void di() { intreq.di(); } unsigned ff_read(const unsigned P, const unsigned long cycleCounter) { + if (readCallback) + readCallback(P, (cycleCounter - basetime) >> 1); return P < 0xFF80 ? nontrivial_ff_read(P, cycleCounter) : ioamhram[P - 0xFE00]; } @@ -206,7 +211,7 @@ public: unsigned read(const unsigned P, const unsigned long cycleCounter) { if (readCallback) - readCallback(P); + readCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -221,7 +226,7 @@ public: unsigned read_excb(const unsigned P, const unsigned long cycleCounter, bool first) { if (execCallback) - execCallback(P); + execCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -254,7 +259,7 @@ public: } else nontrivial_write(P, data, cycleCounter); if (writeCallback) - writeCallback(P); + writeCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -268,6 +273,8 @@ public: ioamhram[P - 0xFE00] = data; } else nontrivial_ff_write(P, data, cycleCounter); + if (writeCallback) + writeCallback(P, (cycleCounter - basetime) >> 1); if(cdCallback) { CDMapResult map = CDMap(P); @@ -285,13 +292,13 @@ public: this->getInput = getInput; } - void setReadCallback(void (*callback)(unsigned)) { + void setReadCallback(MemoryCallback callback) { this->readCallback = callback; } - void setWriteCallback(void (*callback)(unsigned)) { + void setWriteCallback(MemoryCallback callback) { this->writeCallback = callback; } - void setExecCallback(void (*callback)(unsigned)) { + void setExecCallback(MemoryCallback callback) { this->execCallback = callback; } void setCDCallback(CDCallback cdc) { @@ -310,6 +317,7 @@ public: this->linkCallback = callback; } + void setBasetime(unsigned long cc) { basetime = cc; } void setEndtime(unsigned long cc, unsigned long inc); void setSoundBuffer(uint_least32_t *const buf) { sound.setBuffer(buf); } diff --git a/libgambatte/src/tima.cpp b/libgambatte/src/tima.cpp index 5272443ba2..979ffded65 100644 --- a/libgambatte/src/tima.cpp +++ b/libgambatte/src/tima.cpp @@ -119,7 +119,7 @@ void Tima::setTma(const unsigned data, const unsigned long cycleCounter, const T tma_ = data; } -void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq) { +void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const TimaInterruptRequester timaIrq, bool gbIsCgb) { if (tac_ ^ data) { unsigned long nextIrqEventTime = timaIrq.nextIrqEventTime(); @@ -142,6 +142,13 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T if (data & 4) { lastUpdate_ = (cycleCounter >> timaClock[data & 3]) << timaClock[data & 3]; + unsigned long diff = cycleCounter - basetime_; + + if (gbIsCgb) { + if (((diff >> (timaClock[tac_ & 3] - 1)) & 1) == 1 && ((diff >> (timaClock[data & 3] - 1)) & 1) == 0) + tima_++; + } + lastUpdate_ = basetime_ + ((diff >> timaClock[data & 3]) << timaClock[data & 3]); nextIrqEventTime = lastUpdate_ + ((256u - tima_) << timaClock[data & 3]) + 3; } @@ -151,6 +158,14 @@ void Tima::setTac(const unsigned data, const unsigned long cycleCounter, const T tac_ = data; } +void Tima::resTac(unsigned long const cycleCounter, TimaInterruptRequester timaIrq) { + basetime_ = cycleCounter; + if (tac_ & 0x04) { + setTac(tac_ & ~0x04, cycleCounter, timaIrq, false); + setTac(tac_ | 0x04, cycleCounter, timaIrq, false); + } +} + unsigned Tima::tima(unsigned long cycleCounter) { if (tac_ & 0x04) updateTima(cycleCounter); @@ -166,6 +181,7 @@ void Tima::doIrqEvent(const TimaInterruptRequester timaIrq) { SYNCFUNC(Tima) { NSS(lastUpdate_); + NSS(basetime_); NSS(tmatime_); NSS(tima_); NSS(tma_); diff --git a/libgambatte/src/tima.h b/libgambatte/src/tima.h index a3c0c995bb..a66f987837 100644 --- a/libgambatte/src/tima.h +++ b/libgambatte/src/tima.h @@ -34,6 +34,7 @@ public: }; class Tima { + unsigned long basetime_; unsigned long lastUpdate_; unsigned long tmatime_; @@ -55,7 +56,8 @@ public: void setTima(unsigned tima, unsigned long cc, TimaInterruptRequester timaIrq); void setTma(unsigned tma, unsigned long cc, TimaInterruptRequester timaIrq); - void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq); + void setTac(unsigned tac, unsigned long cc, TimaInterruptRequester timaIrq, bool gbIsCgb); + void resTac(unsigned long cc, TimaInterruptRequester timaIrq); unsigned tima(unsigned long cc); void doIrqEvent(TimaInterruptRequester timaIrq); diff --git a/libmupen64plus/GLideN64 b/libmupen64plus/GLideN64 index 3d09b7719e..fec7309dc9 160000 --- a/libmupen64plus/GLideN64 +++ b/libmupen64plus/GLideN64 @@ -1 +1 @@ -Subproject commit 3d09b7719e0181ace91c7f2c7c966cfae410561c +Subproject commit fec7309dc945e7c37f707a1f8e96d6e4bff9c63d diff --git a/libmupen64plus/mupen64plus-rsp-hle/LICENSES b/libmupen64plus/mupen64plus-rsp-hle/LICENSES index e1403bd131..4319820ae1 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/LICENSES +++ b/libmupen64plus/mupen64plus-rsp-hle/LICENSES @@ -5,7 +5,8 @@ Mupen64Plus-rsp-hle is licensed under the GNU General Public License version 2. The authors of Mupen64Plus-rsp-hle are: * Richard Goedeken (Richard42) - * Bobby Smiles + * Bobby Smiles (bsmiles32) + * Sven Eckelmann (ecsv) * John Chadwick (NMN) * James Hood (Ebenblues) * Scott Gorman (okaygo) @@ -29,7 +30,7 @@ Mupen64Plus is based on GPL-licensed source code from Mupen64 v0.5, originally w Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 675 Mass Ave, Cambridge, MA 02139, USA + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. diff --git a/libmupen64plus/mupen64plus-rsp-hle/RELEASE b/libmupen64plus/mupen64plus-rsp-hle/RELEASE index 7a661112fe..0b19ad2357 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/RELEASE +++ b/libmupen64plus/mupen64plus-rsp-hle/RELEASE @@ -1,6 +1,17 @@ RSP High-Level Emulation plugin for Mupen64Plus ----------------------------------------------- +Mupen64Plus-rsp-hle v2.5 - April 26, 2015 +------------------------------------------------- + - Game-specific fixes: Bottom of the 9th, IndianaJones, BattleForNaboo, Conkers Bad Fur Day + - Support for MusyX microcodes + - Improve audio microcode identification + - Huge quantity of code cleanups and refactoring to improve organization + - Add support for additional audio commands: #16, POLEF, RESAMPLE_ZOH + - Multiple bugfixes in audio processing code + - Move global variables into a struct so code is re-entrant + - bugfix: microcode detection could sometimes fail after reset + Mupen64Plus-rsp-hle v2.0 - July 4, 2013 ------------------------------------------------- - Add support for MusyX ucode detection diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/VisualStudio2013/mupen64plus-rsp-hle.vcxproj b/libmupen64plus/mupen64plus-rsp-hle/projects/VisualStudio2013/mupen64plus-rsp-hle.vcxproj new file mode 100644 index 0000000000..3b6349f016 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/VisualStudio2013/mupen64plus-rsp-hle.vcxproj @@ -0,0 +1,194 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {2EC7CEE3-C7A7-4F2E-B2C8-4DF6AFEC3E9A} + mupen64plusrsphle + Win32Proj + + + + DynamicLibrary + MultiByte + true + v120_xp + + + DynamicLibrary + MultiByte + true + v120 + + + DynamicLibrary + MultiByte + v120 + + + DynamicLibrary + MultiByte + v120 + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + true + true + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + false + false + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + Disabled + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + Default + + + true + Windows + MachineX86 + + + + + Disabled + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + Default + + + true + Windows + + + + + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + Default + + + true + Windows + true + true + MachineX86 + + + + + ..\..\..\mupen64plus-core\src\api;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_DEPRECATE;inline=__inline;%(PreprocessorDefinitions) + MultiThreadedDLL + + + Level3 + ProgramDatabase + Default + + + true + Windows + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj index f71023ea49..c73605209c 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc11/mupen64plus-rsp-hle.vcxproj @@ -187,22 +187,33 @@ + + + + + - - - - - + + + + + + - - + + + - + + + + + - \ No newline at end of file + diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj index 1b9522852c..6ee59896c7 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/msvc8/mupen64plus-rsp-hle.vcproj @@ -182,32 +182,56 @@ RelativePath="..\..\src\alist.c" > + + + + + + + + + + + + @@ -221,11 +245,15 @@ > + + + + + + + + + + diff --git a/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile b/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile index ca5e657d3c..f9162a2bde 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile +++ b/libmupen64plus/mupen64plus-rsp-hle/projects/unix/Makefile @@ -1,6 +1,6 @@ #/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * # * mupen64plus-rsp-hle - Makefile * -# * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * +# * Mupen64Plus homepage: https://mupen64plus.org/ * # * Copyright (C) 2008-2009 Richard Goedeken * # * Copyright (C) 2007-2008 DarkJeztr Tillin9 * # * * @@ -53,7 +53,6 @@ ifeq ("$(UNAME)","OpenBSD") OS = FREEBSD SO_EXTENSION = so SHARED = -shared - $(warning OS type "$(UNAME)" not officially supported.') endif ifneq ("$(filter GNU/kFreeBSD kfreebsd,$(UNAME))","") OS = LINUX @@ -67,7 +66,7 @@ ifeq ("$(patsubst MINGW%,MINGW,$(UNAME))","MINGW") PIC = 0 endif ifeq ("$(OS)","NONE") - $(error OS type "$(UNAME)" not supported. Please file bug report at 'http://code.google.com/p/mupen64plus/issues') + $(error OS type "$(UNAME)" not supported. Please file bug report at 'https://github.com/mupen64plus/mupen64plus-core/issues') endif # detect system architecture @@ -111,15 +110,20 @@ ifneq ("$(filter arm%,$(HOST_CPU))","") $(warning Architecture "$(HOST_CPU)" not officially supported.') endif endif +ifneq ("$(filter mips,$(HOST_CPU))","") + CPU := MIPS + ARCH_DETECTED := 32BITS + PIC ?= 1 + $(warning Architecture "$(HOST_CPU)" not officially supported.') +endif ifeq ("$(CPU)","NONE") - $(error CPU type "$(HOST_CPU)" not supported. Please file bug report at 'http://code.google.com/p/mupen64plus/issues') + $(error CPU type "$(HOST_CPU)" not supported. Please file bug report at 'https://github.com/mupen64plus/mupen64plus-core/issues') endif # base CFLAGS, LDLIBS, and LDFLAGS OPTFLAGS ?= -O3 -flto WARNFLAGS ?= -Wall CFLAGS += $(OPTFLAGS) $(WARNFLAGS) -ffast-math -fno-strict-aliasing -fvisibility=hidden -I../../src -CXXFLAGS += -fvisibility-inlines-hidden LDFLAGS += $(SHARED) # Since we are building a shared library, we must compile with -fPIC on some architectures @@ -147,11 +151,10 @@ endif ifeq ($(OS), LINUX) # only export api symbols LDFLAGS += -Wl,-version-script,$(SRCDIR)/rsp_api_export.ver + LDLIBS += -ldl endif ifeq ($(OS), OSX) - #xcode-select has been around since XCode 3.0, i.e. OS X 10.5 - OSX_SDK_ROOT = $(shell xcode-select -print-path)/Platforms/MacOSX.platform/Developer/SDKs - OSX_SDK_PATH = $(OSX_SDK_ROOT)/$(shell ls $(OSX_SDK_ROOT) | tail -1) + OSX_SDK_PATH = $(shell xcrun --sdk macosx --show-sdk-path) ifeq ($(CPU), X86) ifeq ($(ARCH_DETECTED), 64BITS) @@ -189,26 +192,24 @@ endif ifneq ($(findstring $(MAKEFLAGS),s),s) ifndef V Q_CC = @echo ' CC '$@; - Q_CXX = @echo ' CXX '$@; Q_LD = @echo ' LD '$@; endif endif # set base program pointers and flags CC = $(CROSS_COMPILE)gcc -CXX = $(CROSS_COMPILE)g++ RM ?= rm -f INSTALL ?= install MKDIR ?= mkdir -p COMPILE.c = $(Q_CC)$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -COMPILE.cc = $(Q_CXX)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -LINK.o = $(Q_LD)$(CXX) $(CXXFLAGS) $(LDFLAGS) $(TARGET_ARCH) +LINK.o = $(Q_LD)$(CC) $(CFLAGS) $(LDFLAGS) $(TARGET_ARCH) # set special flags for given Makefile parameters ifeq ($(DEBUG),1) CFLAGS += -g INSTALL_STRIP_FLAG ?= else + CFLAGS += -DNDEBUG ifneq ($(OS),OSX) INSTALL_STRIP_FLAG ?= -s endif @@ -225,23 +226,41 @@ ifeq ($(PLUGINDIR),) PLUGINDIR := $(LIBDIR)/mupen64plus endif +# enable/disable task dumping support +ifeq ($(DUMP), 1) + CFLAGS += -DENABLE_TASK_DUMP +endif + + SRCDIR = ../../src OBJDIR = _obj$(POSTFIX) # list of source files to compile SOURCE = \ - $(SRCDIR)/main.c \ $(SRCDIR)/alist.c \ + $(SRCDIR)/alist_audio.c \ + $(SRCDIR)/alist_naudio.c \ + $(SRCDIR)/alist_nead.c \ + $(SRCDIR)/audio.c \ $(SRCDIR)/cicx105.c \ + $(SRCDIR)/hle.c \ $(SRCDIR)/jpeg.c \ - $(SRCDIR)/ucode3.cpp \ - $(SRCDIR)/ucode2.cpp \ - $(SRCDIR)/ucode1.cpp \ - $(SRCDIR)/ucode3mp3.cpp + $(SRCDIR)/memory.c \ + $(SRCDIR)/mp3.c \ + $(SRCDIR)/musyx.c \ + $(SRCDIR)/re2.c \ + $(SRCDIR)/plugin.c + +ifeq ($(OS), MINGW) +SOURCE += \ + $(SRCDIR)/osal_dynamiclib_win32.c +else +SOURCE += \ + $(SRCDIR)/osal_dynamiclib_unix.c +endif # generate a list of object files build, make a temporary directory for them OBJECTS := $(patsubst $(SRCDIR)/%.c, $(OBJDIR)/%.o, $(filter %.c, $(SOURCE))) -OBJECTS += $(patsubst $(SRCDIR)/%.cpp, $(OBJDIR)/%.o, $(filter %.cpp, $(SOURCE))) OBJDIRS = $(dir $(OBJECTS)) $(shell $(MKDIR) $(OBJDIRS)) @@ -263,6 +282,7 @@ targets: @echo " WARNFLAGS=flag == compiler warning levels (default: -Wall)" @echo " PIC=(1|0) == Force enable/disable of position independent code" @echo " POSTFIX=name == String added to the name of the the build (default: '')" + @echo " DUMP=(1|0) == Enable/Disable unknown task dumping (default: 0)" @echo " Install Options:" @echo " PREFIX=path == install/uninstall prefix (default: /usr/local)" @echo " LIBDIR=path == library prefix (default: PREFIX/lib)" @@ -287,18 +307,13 @@ clean: rebuild: clean all # build dependency files -CFLAGS += -MD +CFLAGS += -MD -MP -include $(OBJECTS:.o=.d) -CXXFLAGS += $(CFLAGS) - # standard build rules $(OBJDIR)/%.o: $(SRCDIR)/%.c $(COMPILE.c) -o $@ $< -$(OBJDIR)/%.o: $(SRCDIR)/%.cpp - $(COMPILE.cc) -o $@ $< - $(TARGET): $(OBJECTS) $(LINK.o) $^ $(LOADLIBES) $(LDLIBS) -o $@ diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist.c index c1418939e2..ef3adc2ec4 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/alist.c +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist.c @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - alist.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2012 Bobby Smiles * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * Copyright (C) 2009 Richard Goedeken * * Copyright (C) 2002 Hacktarux * * * @@ -21,62 +21,1004 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#include "hle.h" -#include "alist_internal.h" +#include +#include +#include +#include -// FIXME: this decomposition into 3 ABI is not accurate, -// there are a least 9 or 10 different ABI, each with one or a few revisions -// for a total of almost 16 differents audio ucode. -// -// ABI2 in fact is a mix of at least 7 differents ABI which are mostly compatible -// but not totally, that's why there is a isZeldaABI/isMKABI workaround. -// -extern const acmd_callback_t ABI1[0x10]; -extern const acmd_callback_t ABI2[0x20]; -extern const acmd_callback_t ABI3[0x10]; +#include "alist.h" +#include "arithmetics.h" +#include "audio.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" + +struct ramp_t +{ + int64_t value; + int64_t step; + int64_t target; +}; /* local functions */ -static void alist_process(const acmd_callback_t abi[], unsigned int abi_size) +static void swap(int16_t **a, int16_t **b) { - u32 inst1, inst2; - unsigned int acmd; - const OSTask_t * const task = get_task(); + int16_t* tmp = *b; + *b = *a; + *a = tmp; +} - const unsigned int *alist = (unsigned int*)(rsp.RDRAM + task->data_ptr); - const unsigned int * const alist_end = alist + (task->data_size >> 2); +static int16_t* sample(struct hle_t* hle, unsigned pos) +{ + return (int16_t*)hle->alist_buffer + ((pos ^ S) & 0xfff); +} - while (alist != alist_end) +static uint8_t* alist_u8(struct hle_t* hle, uint16_t dmem) +{ + return (uint8_t*)(hle->alist_buffer + ((dmem ^ S8) & 0xfff)); +} + +static int16_t* alist_s16(struct hle_t* hle, uint16_t dmem) +{ + return (int16_t*)(hle->alist_buffer + ((dmem ^ S16) & 0xfff)); +} + + +static void sample_mix(int16_t* dst, int16_t src, int16_t gain) +{ + *dst = clamp_s16(*dst + ((src * gain) >> 15)); +} + +static void alist_envmix_mix(size_t n, int16_t** dst, const int16_t* gains, int16_t src) +{ + size_t i; + + for(i = 0; i < n; ++i) + sample_mix(dst[i], src, gains[i]); +} + +static int16_t ramp_step(struct ramp_t* ramp) +{ + bool target_reached; + + ramp->value += ramp->step; + + target_reached = (ramp->step <= 0) + ? (ramp->value <= ramp->target) + : (ramp->value >= ramp->target); + + if (target_reached) { - inst1 = *(alist++); - inst2 = *(alist++); - - acmd = inst1 >> 24; - - if (acmd < abi_size) - { - (*abi[acmd])(inst1, inst2); - } - else - { - DebugMessage(M64MSG_WARNING, "Invalid ABI command %u", acmd); - } + ramp->value = ramp->target; + ramp->step = 0; } + + return (int16_t)(ramp->value >> 16); } /* global functions */ -void alist_process_ABI1() +void alist_process(struct hle_t* hle, const acmd_callback_t abi[], unsigned int abi_size) { - alist_process(ABI1, 0x10); + uint32_t w1, w2; + unsigned int acmd; + + const uint32_t *alist = dram_u32(hle, *dmem_u32(hle, TASK_DATA_PTR)); + const uint32_t *const alist_end = alist + (*dmem_u32(hle, TASK_DATA_SIZE) >> 2); + + while (alist != alist_end) { + w1 = *(alist++); + w2 = *(alist++); + + acmd = (w1 >> 24) & 0x7f; + + if (acmd < abi_size) + (*abi[acmd])(hle, w1, w2); + else + HleWarnMessage(hle->user_defined, "Invalid ABI command %u", acmd); + } } -void alist_process_ABI2() +uint32_t alist_get_address(struct hle_t* hle, uint32_t so, const uint32_t *segments, size_t n) { - alist_process(ABI2, 0x20); + uint8_t segment = (so >> 24) & 0x3f; + uint32_t offset = (so & 0xffffff); + + if (segment >= n) { + HleWarnMessage(hle->user_defined, "Invalid segment %u", segment); + return offset; + } + + return segments[segment] + offset; } -void alist_process_ABI3() +void alist_set_address(struct hle_t* hle, uint32_t so, uint32_t *segments, size_t n) { - alist_process(ABI3, 0x10); + uint8_t segment = (so >> 24) & 0x3f; + uint32_t offset = (so & 0xffffff); + + if (segment >= n) { + HleWarnMessage(hle->user_defined, "Invalid segment %u", segment); + return; + } + + segments[segment] = offset; +} + +void alist_clear(struct hle_t* hle, uint16_t dmem, uint16_t count) +{ + while(count != 0) { + *alist_u8(hle, dmem++) = 0; + --count; + } +} + +void alist_load(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count) +{ + /* enforce DMA alignment constraints */ + dmem &= ~3; + address &= ~7; + count = align(count, 8); + memcpy(hle->alist_buffer + dmem, hle->dram + address, count); +} + +void alist_save(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count) +{ + /* enforce DMA alignment constraints */ + dmem &= ~3; + address &= ~7; + count = align(count, 8); + memcpy(hle->dram + address, hle->alist_buffer + dmem, count); +} + +void alist_move(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count) +{ + while (count != 0) { + *alist_u8(hle, dmemo++) = *alist_u8(hle, dmemi++); + --count; + } +} + +void alist_copy_every_other_sample(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count) +{ + while (count != 0) { + *alist_s16(hle, dmemo) = *alist_s16(hle, dmemi); + dmemo += 2; + dmemi += 4; + --count; + } +} + +void alist_repeat64(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint8_t count) +{ + uint16_t buffer[64]; + + memcpy(buffer, hle->alist_buffer + dmemi, 128); + + while(count != 0) { + memcpy(hle->alist_buffer + dmemo, buffer, 128); + dmemo += 128; + --count; + } +} + +void alist_copy_blocks(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t block_size, uint8_t count) +{ + int block_left = count; + + do + { + int bytes_left = block_size; + + do + { + memcpy(hle->alist_buffer + dmemo, hle->alist_buffer + dmemi, 0x20); + bytes_left -= 0x20; + + dmemi += 0x20; + dmemo += 0x20; + + } while(bytes_left > 0); + + --block_left; + } while(block_left > 0); +} + +void alist_interleave(struct hle_t* hle, uint16_t dmemo, uint16_t left, uint16_t right, uint16_t count) +{ + uint16_t *dst = (uint16_t*)(hle->alist_buffer + dmemo); + const uint16_t *srcL = (uint16_t*)(hle->alist_buffer + left); + const uint16_t *srcR = (uint16_t*)(hle->alist_buffer + right); + + count >>= 2; + + while(count != 0) { + uint16_t l1 = *(srcL++); + uint16_t l2 = *(srcL++); + uint16_t r1 = *(srcR++); + uint16_t r2 = *(srcR++); + +#if M64P_BIG_ENDIAN + *(dst++) = l1; + *(dst++) = r1; + *(dst++) = l2; + *(dst++) = r2; +#else + *(dst++) = r2; + *(dst++) = l2; + *(dst++) = r1; + *(dst++) = l1; +#endif + --count; + } } +void alist_envmix_exp( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address) +{ + size_t n = (aux) ? 4 : 2; + + const int16_t* const in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t* const dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t* const dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t* const wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t* const wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + struct ramp_t ramps[2]; + int32_t exp_seq[2]; + int32_t exp_rates[2]; + + uint32_t ptr = 0; + int x, y; + short save_buffer[40]; + + memcpy((uint8_t *)save_buffer, (hle->dram + address), sizeof(save_buffer)); + if (init) { + ramps[0].value = (vol[0] << 16); + ramps[1].value = (vol[1] << 16); + ramps[0].target = (target[0] << 16); + ramps[1].target = (target[1] << 16); + exp_rates[0] = rate[0]; + exp_rates[1] = rate[1]; + exp_seq[0] = (vol[0] * rate[0]); + exp_seq[1] = (vol[1] * rate[1]); + } else { + wet = *(int16_t *)(save_buffer + 0); /* 0-1 */ + dry = *(int16_t *)(save_buffer + 2); /* 2-3 */ + ramps[0].target = *(int32_t *)(save_buffer + 4); /* 4-5 */ + ramps[1].target = *(int32_t *)(save_buffer + 6); /* 6-7 */ + exp_rates[0] = *(int32_t *)(save_buffer + 8); /* 8-9 (save_buffer is a 16bit pointer) */ + exp_rates[1] = *(int32_t *)(save_buffer + 10); /* 10-11 */ + exp_seq[0] = *(int32_t *)(save_buffer + 12); /* 12-13 */ + exp_seq[1] = *(int32_t *)(save_buffer + 14); /* 14-15 */ + ramps[0].value = *(int32_t *)(save_buffer + 16); /* 12-13 */ + ramps[1].value = *(int32_t *)(save_buffer + 18); /* 14-15 */ + } + + /* init which ensure ramp.step != 0 iff ramp.value == ramp.target */ + ramps[0].step = ramps[0].target - ramps[0].value; + ramps[1].step = ramps[1].target - ramps[1].value; + + for (y = 0; y < count; y += 16) { + + if (ramps[0].step != 0) + { + exp_seq[0] = ((int64_t)exp_seq[0]*(int64_t)exp_rates[0]) >> 16; + ramps[0].step = (exp_seq[0] - ramps[0].value) >> 3; + } + + if (ramps[1].step != 0) + { + exp_seq[1] = ((int64_t)exp_seq[1]*(int64_t)exp_rates[1]) >> 16; + ramps[1].step = (exp_seq[1] - ramps[1].value) >> 3; + } + + for (x = 0; x < 8; ++x) { + int16_t gains[4]; + int16_t* buffers[4]; + int16_t l_vol = ramp_step(&ramps[0]); + int16_t r_vol = ramp_step(&ramps[1]); + + buffers[0] = dl + (ptr^S); + buffers[1] = dr + (ptr^S); + buffers[2] = wl + (ptr^S); + buffers[3] = wr + (ptr^S); + + gains[0] = clamp_s16((l_vol * dry + 0x4000) >> 15); + gains[1] = clamp_s16((r_vol * dry + 0x4000) >> 15); + gains[2] = clamp_s16((l_vol * wet + 0x4000) >> 15); + gains[3] = clamp_s16((r_vol * wet + 0x4000) >> 15); + + alist_envmix_mix(n, buffers, gains, in[ptr^S]); + ++ptr; + } + } + + *(int16_t *)(save_buffer + 0) = wet; /* 0-1 */ + *(int16_t *)(save_buffer + 2) = dry; /* 2-3 */ + *(int32_t *)(save_buffer + 4) = (int32_t)ramps[0].target; /* 4-5 */ + *(int32_t *)(save_buffer + 6) = (int32_t)ramps[1].target; /* 6-7 */ + *(int32_t *)(save_buffer + 8) = exp_rates[0]; /* 8-9 (save_buffer is a 16bit pointer) */ + *(int32_t *)(save_buffer + 10) = exp_rates[1]; /* 10-11 */ + *(int32_t *)(save_buffer + 12) = exp_seq[0]; /* 12-13 */ + *(int32_t *)(save_buffer + 14) = exp_seq[1]; /* 14-15 */ + *(int32_t *)(save_buffer + 16) = (int32_t)ramps[0].value; /* 12-13 */ + *(int32_t *)(save_buffer + 18) = (int32_t)ramps[1].value; /* 14-15 */ + memcpy(hle->dram + address, (uint8_t *)save_buffer, sizeof(save_buffer)); +} + +void alist_envmix_ge( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address) +{ + unsigned k; + size_t n = (aux) ? 4 : 2; + + const int16_t* const in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t* const dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t* const dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t* const wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t* const wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + struct ramp_t ramps[2]; + short save_buffer[40]; + + memcpy((uint8_t *)save_buffer, (hle->dram + address), 80); + if (init) { + ramps[0].value = (vol[0] << 16); + ramps[1].value = (vol[1] << 16); + ramps[0].target = (target[0] << 16); + ramps[1].target = (target[1] << 16); + ramps[0].step = rate[0] / 8; + ramps[1].step = rate[1] / 8; + } else { + wet = *(int16_t *)(save_buffer + 0); /* 0-1 */ + dry = *(int16_t *)(save_buffer + 2); /* 2-3 */ + ramps[0].target = *(int32_t *)(save_buffer + 4); /* 4-5 */ + ramps[1].target = *(int32_t *)(save_buffer + 6); /* 6-7 */ + ramps[0].step = *(int32_t *)(save_buffer + 8); /* 8-9 (save_buffer is a 16bit pointer) */ + ramps[1].step = *(int32_t *)(save_buffer + 10); /* 10-11 */ + /* *(int32_t *)(save_buffer + 12);*/ /* 12-13 */ + /* *(int32_t *)(save_buffer + 14);*/ /* 14-15 */ + ramps[0].value = *(int32_t *)(save_buffer + 16); /* 12-13 */ + ramps[1].value = *(int32_t *)(save_buffer + 18); /* 14-15 */ + } + + count >>= 1; + for (k = 0; k < count; ++k) { + int16_t gains[4]; + int16_t* buffers[4]; + int16_t l_vol = ramp_step(&ramps[0]); + int16_t r_vol = ramp_step(&ramps[1]); + + buffers[0] = dl + (k^S); + buffers[1] = dr + (k^S); + buffers[2] = wl + (k^S); + buffers[3] = wr + (k^S); + + gains[0] = clamp_s16((l_vol * dry + 0x4000) >> 15); + gains[1] = clamp_s16((r_vol * dry + 0x4000) >> 15); + gains[2] = clamp_s16((l_vol * wet + 0x4000) >> 15); + gains[3] = clamp_s16((r_vol * wet + 0x4000) >> 15); + + alist_envmix_mix(n, buffers, gains, in[k^S]); + } + + *(int16_t *)(save_buffer + 0) = wet; /* 0-1 */ + *(int16_t *)(save_buffer + 2) = dry; /* 2-3 */ + *(int32_t *)(save_buffer + 4) = (int32_t)ramps[0].target; /* 4-5 */ + *(int32_t *)(save_buffer + 6) = (int32_t)ramps[1].target; /* 6-7 */ + *(int32_t *)(save_buffer + 8) = (int32_t)ramps[0].step; /* 8-9 (save_buffer is a 16bit pointer) */ + *(int32_t *)(save_buffer + 10) = (int32_t)ramps[1].step; /* 10-11 */ + /**(int32_t *)(save_buffer + 12);*/ /* 12-13 */ + /**(int32_t *)(save_buffer + 14);*/ /* 14-15 */ + *(int32_t *)(save_buffer + 16) = (int32_t)ramps[0].value; /* 12-13 */ + *(int32_t *)(save_buffer + 18) = (int32_t)ramps[1].value; /* 14-15 */ + memcpy(hle->dram + address, (uint8_t *)save_buffer, 80); +} + +void alist_envmix_lin( + struct hle_t* hle, + bool init, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address) +{ + size_t k; + struct ramp_t ramps[2]; + int16_t save_buffer[40]; + + const int16_t * const in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t* const dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t* const dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t* const wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t* const wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + memcpy((uint8_t *)save_buffer, hle->dram + address, 80); + if (init) { + ramps[0].step = rate[0] / 8; + ramps[0].value = (vol[0] << 16); + ramps[0].target = (target[0] << 16); + ramps[1].step = rate[1] / 8; + ramps[1].value = (vol[1] << 16); + ramps[1].target = (target[1] << 16); + } + else { + wet = *(int16_t *)(save_buffer + 0); /* 0-1 */ + dry = *(int16_t *)(save_buffer + 2); /* 2-3 */ + ramps[0].target = *(int16_t *)(save_buffer + 4) << 16; /* 4-5 */ + ramps[1].target = *(int16_t *)(save_buffer + 6) << 16; /* 6-7 */ + ramps[0].step = *(int32_t *)(save_buffer + 8); /* 8-9 (save_buffer is a 16bit pointer) */ + ramps[1].step = *(int32_t *)(save_buffer + 10); /* 10-11 */ + ramps[0].value = *(int32_t *)(save_buffer + 16); /* 16-17 */ + ramps[1].value = *(int32_t *)(save_buffer + 18); /* 16-17 */ + } + + count >>= 1; + for(k = 0; k < count; ++k) { + int16_t gains[4]; + int16_t* buffers[4]; + int16_t l_vol = ramp_step(&ramps[0]); + int16_t r_vol = ramp_step(&ramps[1]); + + buffers[0] = dl + (k^S); + buffers[1] = dr + (k^S); + buffers[2] = wl + (k^S); + buffers[3] = wr + (k^S); + + gains[0] = clamp_s16((l_vol * dry + 0x4000) >> 15); + gains[1] = clamp_s16((r_vol * dry + 0x4000) >> 15); + gains[2] = clamp_s16((l_vol * wet + 0x4000) >> 15); + gains[3] = clamp_s16((r_vol * wet + 0x4000) >> 15); + + alist_envmix_mix(4, buffers, gains, in[k^S]); + } + + *(int16_t *)(save_buffer + 0) = wet; /* 0-1 */ + *(int16_t *)(save_buffer + 2) = dry; /* 2-3 */ + *(int16_t *)(save_buffer + 4) = (int16_t)(ramps[0].target >> 16); /* 4-5 */ + *(int16_t *)(save_buffer + 6) = (int16_t)(ramps[1].target >> 16); /* 6-7 */ + *(int32_t *)(save_buffer + 8) = (int32_t)ramps[0].step; /* 8-9 (save_buffer is a 16bit pointer) */ + *(int32_t *)(save_buffer + 10) = (int32_t)ramps[1].step; /* 10-11 */ + *(int32_t *)(save_buffer + 16) = (int32_t)ramps[0].value; /* 16-17 */ + *(int32_t *)(save_buffer + 18) = (int32_t)ramps[1].value; /* 18-19 */ + memcpy(hle->dram + address, (uint8_t *)save_buffer, 80); +} + +void alist_envmix_nead( + struct hle_t* hle, + bool swap_wet_LR, + uint16_t dmem_dl, + uint16_t dmem_dr, + uint16_t dmem_wl, + uint16_t dmem_wr, + uint16_t dmemi, + unsigned count, + uint16_t *env_values, + uint16_t *env_steps, + const int16_t *xors) +{ + int16_t *in = (int16_t*)(hle->alist_buffer + dmemi); + int16_t *dl = (int16_t*)(hle->alist_buffer + dmem_dl); + int16_t *dr = (int16_t*)(hle->alist_buffer + dmem_dr); + int16_t *wl = (int16_t*)(hle->alist_buffer + dmem_wl); + int16_t *wr = (int16_t*)(hle->alist_buffer + dmem_wr); + + /* make sure count is a multiple of 8 */ + count = align(count, 8); + + if (swap_wet_LR) + swap(&wl, &wr); + + while (count != 0) { + size_t i; + for(i = 0; i < 8; ++i) { + int16_t l = (((int32_t)in[i^S] * (uint32_t)env_values[0]) >> 16) ^ xors[0]; + int16_t r = (((int32_t)in[i^S] * (uint32_t)env_values[1]) >> 16) ^ xors[1]; + int16_t l2 = (((int32_t)l * (uint32_t)env_values[2]) >> 16) ^ xors[2]; + int16_t r2 = (((int32_t)r * (uint32_t)env_values[2]) >> 16) ^ xors[3]; + + dl[i^S] = clamp_s16(dl[i^S] + l); + dr[i^S] = clamp_s16(dr[i^S] + r); + wl[i^S] = clamp_s16(wl[i^S] + l2); + wr[i^S] = clamp_s16(wr[i^S] + r2); + } + + env_values[0] += env_steps[0]; + env_values[1] += env_steps[1]; + env_values[2] += env_steps[2]; + + dl += 8; + dr += 8; + wl += 8; + wr += 8; + in += 8; + count -= 8; + } +} + + +void alist_mix(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count, int16_t gain) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + const int16_t *src = (int16_t*)(hle->alist_buffer + dmemi); + + count >>= 1; + + while(count != 0) { + sample_mix(dst, *src, gain); + + ++dst; + ++src; + --count; + } +} + +void alist_multQ44(struct hle_t* hle, uint16_t dmem, uint16_t count, int8_t gain) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmem); + + count >>= 1; + + while(count != 0) { + *dst = clamp_s16(*dst * gain >> 4); + + ++dst; + --count; + } +} + +void alist_add(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + const int16_t *src = (int16_t*)(hle->alist_buffer + dmemi); + + count >>= 1; + + while(count != 0) { + *dst = clamp_s16(*dst + *src); + + ++dst; + ++src; + --count; + } +} + +static void alist_resample_reset(struct hle_t* hle, uint16_t pos, uint32_t* pitch_accu) +{ + unsigned k; + + for(k = 0; k < 4; ++k) + *sample(hle, pos + k) = 0; + + *pitch_accu = 0; +} + +static void alist_resample_load(struct hle_t* hle, uint32_t address, uint16_t pos, uint32_t* pitch_accu) +{ + *sample(hle, pos + 0) = *dram_u16(hle, address + 0); + *sample(hle, pos + 1) = *dram_u16(hle, address + 2); + *sample(hle, pos + 2) = *dram_u16(hle, address + 4); + *sample(hle, pos + 3) = *dram_u16(hle, address + 6); + + *pitch_accu = *dram_u16(hle, address + 8); +} + +static void alist_resample_save(struct hle_t* hle, uint32_t address, uint16_t pos, uint32_t pitch_accu) +{ + *dram_u16(hle, address + 0) = *sample(hle, pos + 0); + *dram_u16(hle, address + 2) = *sample(hle, pos + 1); + *dram_u16(hle, address + 4) = *sample(hle, pos + 2); + *dram_u16(hle, address + 6) = *sample(hle, pos + 3); + + *dram_u16(hle, address + 8) = pitch_accu; +} + +void alist_resample( + struct hle_t* hle, + bool init, + bool flag2, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint32_t pitch, /* Q16.16 */ + uint32_t address) +{ + uint32_t pitch_accu; + + uint16_t ipos = dmemi >> 1; + uint16_t opos = dmemo >> 1; + count >>= 1; + ipos -= 4; + + if (flag2) + HleWarnMessage(hle->user_defined, "alist_resample: flag2 is not implemented"); + + if (init) + alist_resample_reset(hle, ipos, &pitch_accu); + else + alist_resample_load(hle, address, ipos, &pitch_accu); + + while (count != 0) { + const int16_t* lut = RESAMPLE_LUT + ((pitch_accu & 0xfc00) >> 8); + + *sample(hle, opos++) = clamp_s16( ( + (*sample(hle, ipos ) * lut[0]) + + (*sample(hle, ipos + 1) * lut[1]) + + (*sample(hle, ipos + 2) * lut[2]) + + (*sample(hle, ipos + 3) * lut[3]) ) >> 15); + + pitch_accu += pitch; + ipos += (pitch_accu >> 16); + pitch_accu &= 0xffff; + --count; + } + + alist_resample_save(hle, address, ipos, pitch_accu); +} + +void alist_resample_zoh( + struct hle_t* hle, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint32_t pitch, + uint32_t pitch_accu) +{ + uint16_t ipos = dmemi >> 1; + uint16_t opos = dmemo >> 1; + count >>= 1; + + while(count != 0) { + + *sample(hle, opos++) = *sample(hle, ipos); + + pitch_accu += pitch; + ipos += (pitch_accu >> 16); + pitch_accu &= 0xffff; + --count; + } +} + +typedef unsigned int (*adpcm_predict_frame_t)(struct hle_t* hle, + int16_t* dst, uint16_t dmemi, unsigned char scale); + +static unsigned int adpcm_predict_frame_4bits(struct hle_t* hle, + int16_t* dst, uint16_t dmemi, unsigned char scale) +{ + unsigned int i; + unsigned int rshift = (scale < 12) ? 12 - scale : 0; + + for(i = 0; i < 8; ++i) { + uint8_t byte = *alist_u8(hle, dmemi++); + + *(dst++) = adpcm_predict_sample(byte, 0xf0, 8, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x0f, 12, rshift); + } + + return 8; +} + +static unsigned int adpcm_predict_frame_2bits(struct hle_t* hle, + int16_t* dst, uint16_t dmemi, unsigned char scale) +{ + unsigned int i; + unsigned int rshift = (scale < 14) ? 14 - scale : 0; + + for(i = 0; i < 4; ++i) { + uint8_t byte = *alist_u8(hle, dmemi++); + + *(dst++) = adpcm_predict_sample(byte, 0xc0, 8, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x30, 10, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x0c, 12, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x03, 14, rshift); + } + + return 4; +} + +void alist_adpcm( + struct hle_t* hle, + bool init, + bool loop, + bool two_bit_per_sample, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + const int16_t* codebook, + uint32_t loop_address, + uint32_t last_frame_address) +{ + int16_t last_frame[16]; + size_t i; + + adpcm_predict_frame_t predict_frame = (two_bit_per_sample) + ? adpcm_predict_frame_2bits + : adpcm_predict_frame_4bits; + + assert((count & 0x1f) == 0); + + if (init) + memset(last_frame, 0, 16*sizeof(last_frame[0])); + else + dram_load_u16(hle, (uint16_t*)last_frame, (loop) ? loop_address : last_frame_address, 16); + + for(i = 0; i < 16; ++i, dmemo += 2) + *alist_s16(hle, dmemo) = last_frame[i]; + + while (count != 0) { + int16_t frame[16]; + uint8_t code = *alist_u8(hle, dmemi++); + unsigned char scale = (code & 0xf0) >> 4; + const int16_t* const cb_entry = codebook + ((code & 0xf) << 4); + + dmemi += predict_frame(hle, frame, dmemi, scale); + + adpcm_compute_residuals(last_frame , frame , cb_entry, last_frame + 14, 8); + adpcm_compute_residuals(last_frame + 8, frame + 8, cb_entry, last_frame + 6 , 8); + + for(i = 0; i < 16; ++i, dmemo += 2) + *alist_s16(hle, dmemo) = last_frame[i]; + + count -= 32; + } + + dram_store_u16(hle, (uint16_t*)last_frame, last_frame_address, 16); +} + + +void alist_filter( + struct hle_t* hle, + uint16_t dmem, + uint16_t count, + uint32_t address, + const uint32_t* lut_address) +{ + int x; + int16_t outbuff[0x3c0]; + int16_t *outp = outbuff; + + int16_t* const lutt6 = (int16_t*)(hle->dram + lut_address[0]); + int16_t* const lutt5 = (int16_t*)(hle->dram + lut_address[1]); + + int16_t* in1 = (int16_t*)(hle->dram + address); + int16_t* in2 = (int16_t*)(hle->alist_buffer + dmem); + + + for (x = 0; x < 8; ++x) { + int32_t v = (lutt5[x] + lutt6[x]) >> 1; + lutt5[x] = lutt6[x] = v; + } + + for (x = 0; x < count; x += 16) { + int32_t v[8]; + + v[1] = in1[0] * lutt6[6]; + v[1] += in1[3] * lutt6[7]; + v[1] += in1[2] * lutt6[4]; + v[1] += in1[5] * lutt6[5]; + v[1] += in1[4] * lutt6[2]; + v[1] += in1[7] * lutt6[3]; + v[1] += in1[6] * lutt6[0]; + v[1] += in2[1] * lutt6[1]; /* 1 */ + + v[0] = in1[3] * lutt6[6]; + v[0] += in1[2] * lutt6[7]; + v[0] += in1[5] * lutt6[4]; + v[0] += in1[4] * lutt6[5]; + v[0] += in1[7] * lutt6[2]; + v[0] += in1[6] * lutt6[3]; + v[0] += in2[1] * lutt6[0]; + v[0] += in2[0] * lutt6[1]; + + v[3] = in1[2] * lutt6[6]; + v[3] += in1[5] * lutt6[7]; + v[3] += in1[4] * lutt6[4]; + v[3] += in1[7] * lutt6[5]; + v[3] += in1[6] * lutt6[2]; + v[3] += in2[1] * lutt6[3]; + v[3] += in2[0] * lutt6[0]; + v[3] += in2[3] * lutt6[1]; + + v[2] = in1[5] * lutt6[6]; + v[2] += in1[4] * lutt6[7]; + v[2] += in1[7] * lutt6[4]; + v[2] += in1[6] * lutt6[5]; + v[2] += in2[1] * lutt6[2]; + v[2] += in2[0] * lutt6[3]; + v[2] += in2[3] * lutt6[0]; + v[2] += in2[2] * lutt6[1]; + + v[5] = in1[4] * lutt6[6]; + v[5] += in1[7] * lutt6[7]; + v[5] += in1[6] * lutt6[4]; + v[5] += in2[1] * lutt6[5]; + v[5] += in2[0] * lutt6[2]; + v[5] += in2[3] * lutt6[3]; + v[5] += in2[2] * lutt6[0]; + v[5] += in2[5] * lutt6[1]; + + v[4] = in1[7] * lutt6[6]; + v[4] += in1[6] * lutt6[7]; + v[4] += in2[1] * lutt6[4]; + v[4] += in2[0] * lutt6[5]; + v[4] += in2[3] * lutt6[2]; + v[4] += in2[2] * lutt6[3]; + v[4] += in2[5] * lutt6[0]; + v[4] += in2[4] * lutt6[1]; + + v[7] = in1[6] * lutt6[6]; + v[7] += in2[1] * lutt6[7]; + v[7] += in2[0] * lutt6[4]; + v[7] += in2[3] * lutt6[5]; + v[7] += in2[2] * lutt6[2]; + v[7] += in2[5] * lutt6[3]; + v[7] += in2[4] * lutt6[0]; + v[7] += in2[7] * lutt6[1]; + + v[6] = in2[1] * lutt6[6]; + v[6] += in2[0] * lutt6[7]; + v[6] += in2[3] * lutt6[4]; + v[6] += in2[2] * lutt6[5]; + v[6] += in2[5] * lutt6[2]; + v[6] += in2[4] * lutt6[3]; + v[6] += in2[7] * lutt6[0]; + v[6] += in2[6] * lutt6[1]; + + outp[1] = ((v[1] + 0x4000) >> 15); + outp[0] = ((v[0] + 0x4000) >> 15); + outp[3] = ((v[3] + 0x4000) >> 15); + outp[2] = ((v[2] + 0x4000) >> 15); + outp[5] = ((v[5] + 0x4000) >> 15); + outp[4] = ((v[4] + 0x4000) >> 15); + outp[7] = ((v[7] + 0x4000) >> 15); + outp[6] = ((v[6] + 0x4000) >> 15); + in1 = in2; + in2 += 8; + outp += 8; + } + + memcpy(hle->dram + address, in2 - 8, 16); + memcpy(hle->alist_buffer + dmem, outbuff, count); +} + +void alist_polef( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint16_t gain, + int16_t* table, + uint32_t address) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + + const int16_t* const h1 = table; + int16_t* const h2 = table + 8; + + unsigned i; + int16_t l1, l2; + int16_t h2_before[8]; + + count = align(count, 16); + + if (init) { + l1 = 0; + l2 = 0; + } + else { + l1 = *dram_u16(hle, address + 4); + l2 = *dram_u16(hle, address + 6); + } + + for(i = 0; i < 8; ++i) { + h2_before[i] = h2[i]; + h2[i] = (((int32_t)h2[i] * gain) >> 14); + } + + do + { + int16_t frame[8]; + + for(i = 0; i < 8; ++i, dmemi += 2) + frame[i] = *alist_s16(hle, dmemi); + + for(i = 0; i < 8; ++i) { + int32_t accu = frame[i] * gain; + accu += h1[i]*l1 + h2_before[i]*l2 + rdot(i, h2, frame); + dst[i^S] = clamp_s16(accu >> 14); + } + + l1 = dst[6^S]; + l2 = dst[7^S]; + + dst += 8; + count -= 16; + } while (count != 0); + + dram_store_u32(hle, (uint32_t*)(dst - 4), address, 2); +} + +void alist_iirf( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + int16_t* table, + uint32_t address) +{ + int16_t *dst = (int16_t*)(hle->alist_buffer + dmemo); + int32_t i, prev; + int16_t frame[8]; + int16_t ibuf[4]; + uint16_t index = 7; + + + count = align(count, 16); + + if(init) + { + for(i = 0; i < 8; ++i) + frame[i] = 0; + ibuf[1] = 0; + ibuf[2] = 0; + } + else + { + frame[6] = *dram_u16(hle, address + 4); + frame[7] = *dram_u16(hle, address + 6); + ibuf[1] = (int16_t)*dram_u16(hle, address + 8); + ibuf[2] = (int16_t)*dram_u16(hle, address + 10); + } + + prev = vmulf(table[9], frame[6]) * 2; + do + { + for(i = 0; i < 8; ++i) + { + int32_t accu; + ibuf[index&3] = *alist_s16(hle, dmemi); + + accu = prev + vmulf(table[0], ibuf[index&3]) + vmulf(table[1], ibuf[(index-1)&3]) + vmulf(table[0], ibuf[(index-2)&3]); + accu += vmulf(table[8], frame[index]) * 2; + prev = vmulf(table[9], frame[index]) * 2; + dst[i^S] = frame[i] = accu; + + index=(index+1)&7; + dmemi += 2; + } + dst += 8; + count -= 0x10; + } while (count > 0); + + dram_store_u16(hle, (uint16_t*)&frame[6], address + 4, 4); + dram_store_u16(hle, (uint16_t*)&ibuf[(index-2)&3], address+8, 2); + dram_store_u16(hle, (uint16_t*)&ibuf[(index-1)&3], address+10, 2); +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist.h b/libmupen64plus/mupen64plus-rsp-hle/src/alist.h index 43f9f04bcb..398cdb8179 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/alist.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - alist.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,15 +19,152 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef ALIST_H -#define ALIST_H +#ifndef ALIST_INTERNAL_H +#define ALIST_INTERNAL_H -void alist_process_ABI1(); -void alist_process_ABI2(); -void alist_process_ABI3(); +#include +#include +#include -// FIXME: to remove when isZeldaABI/isMKABI workaround is gone -void init_ucode2(); +struct hle_t; + +typedef void (*acmd_callback_t)(struct hle_t* hle, uint32_t w1, uint32_t w2); + +void alist_process(struct hle_t* hle, const acmd_callback_t abi[], unsigned int abi_size); +uint32_t alist_get_address(struct hle_t* hle, uint32_t so, const uint32_t *segments, size_t n); +void alist_set_address(struct hle_t* hle, uint32_t so, uint32_t *segments, size_t n); +void alist_clear(struct hle_t* hle, uint16_t dmem, uint16_t count); +void alist_load(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count); +void alist_save(struct hle_t* hle, uint16_t dmem, uint32_t address, uint16_t count); +void alist_move(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count); +void alist_copy_every_other_sample(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count); +void alist_repeat64(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint8_t count); +void alist_copy_blocks(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t block_size, uint8_t count); +void alist_interleave(struct hle_t* hle, uint16_t dmemo, uint16_t left, uint16_t right, uint16_t count); + +void alist_envmix_exp( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address); + +void alist_envmix_ge( + struct hle_t* hle, + bool init, + bool aux, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address); + +void alist_envmix_lin( + struct hle_t* hle, + bool init, + uint16_t dmem_dl, uint16_t dmem_dr, + uint16_t dmem_wl, uint16_t dmem_wr, + uint16_t dmemi, uint16_t count, + int16_t dry, int16_t wet, + const int16_t *vol, + const int16_t *target, + const int32_t *rate, + uint32_t address); + +void alist_envmix_nead( + struct hle_t* hle, + bool swap_wet_LR, + uint16_t dmem_dl, + uint16_t dmem_dr, + uint16_t dmem_wl, + uint16_t dmem_wr, + uint16_t dmemi, + unsigned count, + uint16_t *env_values, + uint16_t *env_steps, + const int16_t *xors); + +void alist_mix(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count, int16_t gain); +void alist_multQ44(struct hle_t* hle, uint16_t dmem, uint16_t count, int8_t gain); +void alist_add(struct hle_t* hle, uint16_t dmemo, uint16_t dmemi, uint16_t count); + +void alist_adpcm( + struct hle_t* hle, + bool init, + bool loop, + bool two_bit_per_sample, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + const int16_t* codebook, + uint32_t loop_address, + uint32_t last_frame_address); + +void alist_resample( + struct hle_t* hle, + bool init, + bool flag2, + uint16_t dmemo, uint16_t dmemi, uint16_t count, + uint32_t pitch, uint32_t address); + +void alist_resample_zoh( + struct hle_t* hle, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint32_t pitch, + uint32_t pitch_accu); + +void alist_filter( + struct hle_t* hle, + uint16_t dmem, + uint16_t count, + uint32_t address, + const uint32_t* lut_address); + +void alist_polef( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + uint16_t gain, + int16_t* table, + uint32_t address); + +void alist_iirf( + struct hle_t* hle, + bool init, + uint16_t dmemo, + uint16_t dmemi, + uint16_t count, + int16_t* table, + uint32_t address); + +/* + * Audio flags + */ + +#define A_INIT 0x01 +#define A_CONTINUE 0x00 +#define A_LOOP 0x02 +#define A_OUT 0x02 +#define A_LEFT 0x02 +#define A_RIGHT 0x00 +#define A_VOL 0x04 +#define A_RATE 0x00 +#define A_AUX 0x08 +#define A_NOAUX 0x00 +#define A_MAIN 0x00 +#define A_MIX 0x10 #endif - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_audio.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist_audio.c new file mode 100644 index 0000000000..0d2dceacfb --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist_audio.c @@ -0,0 +1,310 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - alist_audio.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "alist.h" +#include "common.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +enum { DMEM_BASE = 0x5c0 }; + +/* helper functions */ +static uint32_t get_address(struct hle_t* hle, uint32_t so) +{ + return alist_get_address(hle, so, hle->alist_audio.segments, N_SEGMENTS); +} + +static void set_address(struct hle_t* hle, uint32_t so) +{ + alist_set_address(hle, so, hle->alist_audio.segments, N_SEGMENTS); +} + +static void clear_segments(struct hle_t* hle) +{ + memset(hle->alist_audio.segments, 0, N_SEGMENTS*sizeof(hle->alist_audio.segments[0])); +} + +/* audio commands definition */ +static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmem = w1 + DMEM_BASE; + uint16_t count = w2 & 0xfff; + + if (count == 0) + return; + + alist_clear(hle, dmem, align(count, 16)); +} + +static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = get_address(hle, w2); + + alist_envmix_exp( + hle, + flags & A_INIT, + flags & A_AUX, + hle->alist_audio.out, hle->alist_audio.dry_right, + hle->alist_audio.wet_left, hle->alist_audio.wet_right, + hle->alist_audio.in, hle->alist_audio.count, + hle->alist_audio.dry, hle->alist_audio.wet, + hle->alist_audio.vol, + hle->alist_audio.target, + hle->alist_audio.rate, + address); +} + +static void ENVMIXER_GE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = get_address(hle, w2); + + alist_envmix_ge( + hle, + flags & A_INIT, + flags & A_AUX, + hle->alist_audio.out, hle->alist_audio.dry_right, + hle->alist_audio.wet_left, hle->alist_audio.wet_right, + hle->alist_audio.in, hle->alist_audio.count, + hle->alist_audio.dry, hle->alist_audio.wet, + hle->alist_audio.vol, + hle->alist_audio.target, + hle->alist_audio.rate, + address); +} + +static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t pitch = w1; + uint32_t address = get_address(hle, w2); + + alist_resample( + hle, + flags & A_INIT, + flags & 0x2, + hle->alist_audio.out, + hle->alist_audio.in, + align(hle->alist_audio.count, 16), + pitch << 1, + address); +} + +static void SETVOL(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + + if (flags & A_AUX) { + hle->alist_audio.dry = w1; + hle->alist_audio.wet = w2; + } + else { + unsigned lr = (flags & A_LEFT) ? 0 : 1; + + if (flags & A_VOL) + hle->alist_audio.vol[lr] = w1; + else { + hle->alist_audio.target[lr] = w1; + hle->alist_audio.rate[lr] = w2; + } + } +} + +static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_audio.loop = get_address(hle, w2); +} + +static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = get_address(hle, w2); + + alist_adpcm( + hle, + flags & A_INIT, + flags & A_LOOP, + false, /* unsupported in this ucode */ + hle->alist_audio.out, + hle->alist_audio.in, + align(hle->alist_audio.count, 32), + hle->alist_audio.table, + hle->alist_audio.loop, + address); +} + +static void LOADBUFF(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint32_t address = get_address(hle, w2); + + if (hle->alist_audio.count == 0) + return; + + alist_load(hle, hle->alist_audio.in, address, hle->alist_audio.count); +} + +static void SAVEBUFF(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint32_t address = get_address(hle, w2); + + if (hle->alist_audio.count == 0) + return; + + alist_save(hle, hle->alist_audio.out, address, hle->alist_audio.count); +} + +static void SETBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + + if (flags & A_AUX) { + hle->alist_audio.dry_right = w1 + DMEM_BASE; + hle->alist_audio.wet_left = (w2 >> 16) + DMEM_BASE; + hle->alist_audio.wet_right = w2 + DMEM_BASE; + } else { + hle->alist_audio.in = w1 + DMEM_BASE; + hle->alist_audio.out = (w2 >> 16) + DMEM_BASE; + hle->alist_audio.count = w2; + } +} + +static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmemi = w1 + DMEM_BASE; + uint16_t dmemo = (w2 >> 16) + DMEM_BASE; + uint16_t count = w2; + + if (count == 0) + return; + + alist_move(hle, dmemo, dmemi, align(count, 16)); +} + +static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint32_t address = get_address(hle, w2); + + dram_load_u16(hle, (uint16_t*)hle->alist_audio.table, address, align(count, 8) >> 1); +} + +static void INTERLEAVE(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint16_t left = (w2 >> 16) + DMEM_BASE; + uint16_t right = w2 + DMEM_BASE; + + if (hle->alist_audio.count == 0) + return; + + alist_interleave(hle, hle->alist_audio.out, left, right, align(hle->alist_audio.count, 16)); +} + +static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t gain = w1; + uint16_t dmemi = (w2 >> 16) + DMEM_BASE; + uint16_t dmemo = w2 + DMEM_BASE; + + if (hle->alist_audio.count == 0) + return; + + alist_mix(hle, dmemo, dmemi, align(hle->alist_audio.count, 32), gain); +} + +static void SEGMENT(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + set_address(hle, w2); +} + +static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t gain = w1; + uint32_t address = get_address(hle, w2); + + if (hle->alist_audio.count == 0) + return; + + alist_polef( + hle, + flags & A_INIT, + hle->alist_audio.out, + hle->alist_audio.in, + align(hle->alist_audio.count, 16), + gain, + hle->alist_audio.table, + address); +} + +/* global functions */ +void alist_process_audio(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM , CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT, + SETBUFF, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, POLEF, SETLOOP + }; + + clear_segments(hle); + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_audio_ge(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM , CLEARBUFF, ENVMIXER_GE, + LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT, + SETBUFF, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, POLEF, SETLOOP + }; + + clear_segments(hle); + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_audio_bc(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM , CLEARBUFF, ENVMIXER_GE, + LOADBUFF, RESAMPLE, SAVEBUFF, SEGMENT, + SETBUFF, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, POLEF, SETLOOP + }; + + clear_segments(hle); + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_naudio.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist_naudio.c new file mode 100644 index 0000000000..bb53dd77b8 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist_naudio.c @@ -0,0 +1,333 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - alist_naudio.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "alist.h" +#include "common.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +enum { NAUDIO_COUNT = 0x170 }; /* ie 184 samples */ +enum { + NAUDIO_MAIN = 0x4f0, + NAUDIO_MAIN2 = 0x660, + NAUDIO_DRY_LEFT = 0x9d0, + NAUDIO_DRY_RIGHT = 0xb40, + NAUDIO_WET_LEFT = 0xcb0, + NAUDIO_WET_RIGHT = 0xe20 +}; + + +/* audio commands definition */ +static void UNKNOWN(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t acmd = (w1 >> 24); + + HleWarnMessage(hle->user_defined, + "Unknown audio command %d: %08x %08x", + acmd, w1, w2); +} + + +static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void NAUDIO_0000(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + /* ??? */ + UNKNOWN(hle, w1, w2); +} + +static void NAUDIO_02B0(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + /* emulate code at 0x12b0 (inside SETVOL), because PC always execute in IMEM */ + hle->alist_naudio.rate[1] &= ~0xffff; + hle->alist_naudio.rate[1] |= (w2 & 0xffff); +} + +static void NAUDIO_14(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t gain = w1; + uint8_t select_main = (w2 >> 24); + uint32_t address = (w2 & 0xffffff); + + uint16_t dmem = (select_main == 0) ? NAUDIO_MAIN : NAUDIO_MAIN2; + + if (hle->alist_naudio.table[0] == 0 && hle->alist_naudio.table[1] == 0) { + alist_polef( + hle, + flags & A_INIT, + dmem, + dmem, + NAUDIO_COUNT, + gain, + hle->alist_naudio.table, + address); + } + else + { + alist_iirf( + hle, + flags & A_INIT, + dmem, + dmem, + NAUDIO_COUNT, + hle->alist_naudio.table, + address); + } + +} + +static void SETVOL(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + + if (flags & A_VOL) { + if (flags & A_LEFT) { + hle->alist_naudio.vol[0] = w1; + hle->alist_naudio.dry = (w2 >> 16); + hle->alist_naudio.wet = w2; + } + else { /* A_RIGHT */ + hle->alist_naudio.target[1] = w1; + hle->alist_naudio.rate[1] = w2; + } + } + else { /* A_RATE */ + hle->alist_naudio.target[0] = w1; + hle->alist_naudio.rate[0] = w2; + } +} + +static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = (w2 & 0xffffff); + + hle->alist_naudio.vol[1] = w1; + + alist_envmix_lin( + hle, + flags & A_INIT, + NAUDIO_DRY_LEFT, + NAUDIO_DRY_RIGHT, + NAUDIO_WET_LEFT, + NAUDIO_WET_RIGHT, + NAUDIO_MAIN, + NAUDIO_COUNT, + hle->alist_naudio.dry, + hle->alist_naudio.wet, + hle->alist_naudio.vol, + hle->alist_naudio.target, + hle->alist_naudio.rate, + address); +} + +static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmem = w1 + NAUDIO_MAIN; + uint16_t count = w2 & 0xfff; + + alist_clear(hle, dmem, count); +} + +static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t gain = w1; + uint16_t dmemi = (w2 >> 16) + NAUDIO_MAIN; + uint16_t dmemo = w2 + NAUDIO_MAIN; + + alist_mix(hle, dmemo, dmemi, NAUDIO_COUNT, gain); +} + +static void LOADBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN; + uint32_t address = (w2 & 0xffffff); + + alist_load(hle, dmem, address, count); +} + +static void SAVEBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff) + NAUDIO_MAIN; + uint32_t address = (w2 & 0xffffff); + + alist_save(hle, dmem, address, count); +} + +static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint32_t address = (w2 & 0xffffff); + + dram_load_u16(hle, (uint16_t*)hle->alist_naudio.table, address, count >> 1); +} + +static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmemi = w1 + NAUDIO_MAIN; + uint16_t dmemo = (w2 >> 16) + NAUDIO_MAIN; + uint16_t count = w2; + + alist_move(hle, dmemo, dmemi, (count + 3) & ~3); +} + +static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_naudio.loop = (w2 & 0xffffff); +} + +static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint32_t address = (w1 & 0xffffff); + uint8_t flags = (w2 >> 28); + uint16_t count = (w2 >> 16) & 0xfff; + uint16_t dmemi = ((w2 >> 12) & 0xf) + NAUDIO_MAIN; + uint16_t dmemo = (w2 & 0xfff) + NAUDIO_MAIN; + + alist_adpcm( + hle, + flags & A_INIT, + flags & A_LOOP, + false, /* unsuported by this ucode */ + dmemo, + dmemi, + (count + 0x1f) & ~0x1f, + hle->alist_naudio.table, + hle->alist_naudio.loop, + address); +} + +static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint32_t address = (w1 & 0xffffff); + uint8_t flags = (w2 >> 30); + uint16_t pitch = (w2 >> 14); + uint16_t dmemi = ((w2 >> 2) & 0xfff) + NAUDIO_MAIN; + uint16_t dmemo = (w2 & 0x3) ? NAUDIO_MAIN2 : NAUDIO_MAIN; + + alist_resample( + hle, + flags & A_INIT, + false, /* TODO: check which ABI supports it */ + dmemo, + dmemi, + NAUDIO_COUNT, + pitch << 1, + address); +} + +static void INTERLEAVE(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ + alist_interleave(hle, NAUDIO_MAIN, NAUDIO_DRY_LEFT, NAUDIO_DRY_RIGHT, NAUDIO_COUNT); +} + +static void MP3ADDY(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void MP3(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + unsigned index = (w1 & 0x1e); + uint32_t address = (w2 & 0xffffff); + + mp3_task(hle, index, address); +} + +/* global functions */ +void alist_process_naudio(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, NAUDIO_0000, + NAUDIO_0000, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_bk(struct hle_t* hle) +{ + /* TODO: see what differs from alist_process_naudio */ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, NAUDIO_0000, + NAUDIO_0000, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_dk(struct hle_t* hle) +{ + /* TODO: see what differs from alist_process_naudio */ + static const acmd_callback_t ABI[0x10] = { + SPNOOP, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, MIXER, + MIXER, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_02B0, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_mp3(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x10] = { + UNKNOWN, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, MP3, + MP3ADDY, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_14, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_naudio_cbfd(struct hle_t* hle) +{ + /* TODO: see what differs from alist_process_naudio_mp3 */ + static const acmd_callback_t ABI[0x10] = { + UNKNOWN, ADPCM, CLEARBUFF, ENVMIXER, + LOADBUFF, RESAMPLE, SAVEBUFF, MP3, + MP3ADDY, SETVOL, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, NAUDIO_14, SETLOOP + }; + + alist_process(hle, ABI, 0x10); + rsp_break(hle, SP_STATUS_TASKDONE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_nead.c b/libmupen64plus/mupen64plus-rsp-hle/src/alist_nead.c new file mode 100644 index 0000000000..11dcb80595 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/alist_nead.c @@ -0,0 +1,556 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - alist_nead.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "alist.h" +#include "common.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +/* remove windows define to 0x06 */ +#ifdef DUPLICATE +#undef DUPLICATE +#endif + +/* audio commands definition */ +static void UNKNOWN(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t acmd = (w1 >> 24); + + HleWarnMessage(hle->user_defined, + "Unknown audio command %d: %08x %08x", + acmd, w1, w2); +} + + +static void SPNOOP(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void LOADADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint32_t address = (w2 & 0xffffff); + + dram_load_u16(hle, (uint16_t*)hle->alist_nead.table, address, count >> 1); +} + +static void SETLOOP(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_nead.loop = w2 & 0xffffff; +} + +static void SETBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + hle->alist_nead.in = w1; + hle->alist_nead.out = (w2 >> 16); + hle->alist_nead.count = w2; +} + +static void ADPCM(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = (w2 & 0xffffff); + + alist_adpcm( + hle, + flags & 0x1, + flags & 0x2, + flags & 0x4, + hle->alist_nead.out, + hle->alist_nead.in, + (hle->alist_nead.count + 0x1f) & ~0x1f, + hle->alist_nead.table, + hle->alist_nead.loop, + address); +} + +static void CLEARBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmem = w1; + uint16_t count = w2 & 0xfff; + + if (count == 0) + return; + + alist_clear(hle, dmem, count); +} + +static void LOADBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff); + uint32_t address = (w2 & 0xffffff); + + alist_load(hle, dmem, address, count); +} + +static void SAVEBUFF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xfff; + uint16_t dmem = (w1 & 0xfff); + uint32_t address = (w2 & 0xffffff); + + alist_save(hle, dmem, address, count); +} + +static void MIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xff0; + int16_t gain = w1; + uint16_t dmemi = (w2 >> 16); + uint16_t dmemo = w2; + + alist_mix(hle, dmemo, dmemi, count, gain); +} + + +static void RESAMPLE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t pitch = w1; + uint32_t address = (w2 & 0xffffff); + + alist_resample( + hle, + flags & 0x1, + false, /* TODO: check which ABI supports it */ + hle->alist_nead.out, + hle->alist_nead.in, + (hle->alist_nead.count + 0xf) & ~0xf, + pitch << 1, + address); +} + +static void RESAMPLE_ZOH(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t pitch = w1; + uint16_t pitch_accu = w2; + + alist_resample_zoh( + hle, + hle->alist_nead.out, + hle->alist_nead.in, + hle->alist_nead.count, + pitch << 1, + pitch_accu); +} + +static void DMEMMOVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t dmemi = w1; + uint16_t dmemo = (w2 >> 16); + uint16_t count = w2; + + if (count == 0) + return; + + alist_move(hle, dmemo, dmemi, (count + 3) & ~3); +} + +static void ENVSETUP1_MK(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00; + hle->alist_nead.env_steps[2] = 0; + hle->alist_nead.env_steps[0] = (w2 >> 16); + hle->alist_nead.env_steps[1] = w2; +} + +static void ENVSETUP1(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + hle->alist_nead.env_values[2] = (w1 >> 8) & 0xff00; + hle->alist_nead.env_steps[2] = w1; + hle->alist_nead.env_steps[0] = (w2 >> 16); + hle->alist_nead.env_steps[1] = w2; +} + +static void ENVSETUP2(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + hle->alist_nead.env_values[0] = (w2 >> 16); + hle->alist_nead.env_values[1] = w2; +} + +static void ENVMIXER_MK(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t xors[4]; + + uint16_t dmemi = (w1 >> 12) & 0xff0; + uint8_t count = (w1 >> 8) & 0xff; + uint16_t dmem_dl = (w2 >> 20) & 0xff0; + uint16_t dmem_dr = (w2 >> 12) & 0xff0; + uint16_t dmem_wl = (w2 >> 4) & 0xff0; + uint16_t dmem_wr = (w2 << 4) & 0xff0; + + xors[2] = 0; /* unsupported by this ucode */ + xors[3] = 0; /* unsupported by this ucode */ + xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1); + xors[1] = 0 - (int16_t)((w1 & 0x1) ); + + alist_envmix_nead( + hle, + false, /* unsupported by this ucode */ + dmem_dl, dmem_dr, + dmem_wl, dmem_wr, + dmemi, count, + hle->alist_nead.env_values, + hle->alist_nead.env_steps, + xors); +} + +static void ENVMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int16_t xors[4]; + + uint16_t dmemi = (w1 >> 12) & 0xff0; + uint8_t count = (w1 >> 8) & 0xff; + bool swap_wet_LR = (w1 >> 4) & 0x1; + uint16_t dmem_dl = (w2 >> 20) & 0xff0; + uint16_t dmem_dr = (w2 >> 12) & 0xff0; + uint16_t dmem_wl = (w2 >> 4) & 0xff0; + uint16_t dmem_wr = (w2 << 4) & 0xff0; + + xors[2] = 0 - (int16_t)((w1 & 0x8) >> 1); + xors[3] = 0 - (int16_t)((w1 & 0x4) >> 1); + xors[0] = 0 - (int16_t)((w1 & 0x2) >> 1); + xors[1] = 0 - (int16_t)((w1 & 0x1) ); + + alist_envmix_nead( + hle, + swap_wet_LR, + dmem_dl, dmem_dr, + dmem_wl, dmem_wr, + dmemi, count, + hle->alist_nead.env_values, + hle->alist_nead.env_steps, + xors); +} + +static void DUPLICATE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t count = (w1 >> 16); + uint16_t dmemi = w1; + uint16_t dmemo = (w2 >> 16); + + alist_repeat64(hle, dmemo, dmemi, count); +} + +static void INTERL(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = w1; + uint16_t dmemi = (w2 >> 16); + uint16_t dmemo = w2; + + alist_copy_every_other_sample(hle, dmemo, dmemi, count); +} + +static void INTERLEAVE_MK(struct hle_t* hle, uint32_t UNUSED(w1), uint32_t w2) +{ + uint16_t left = (w2 >> 16); + uint16_t right = w2; + + if (hle->alist_nead.count == 0) + return; + + alist_interleave(hle, hle->alist_nead.out, left, right, hle->alist_nead.count); +} + +static void INTERLEAVE(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = ((w1 >> 12) & 0xff0); + uint16_t dmemo = w1; + uint16_t left = (w2 >> 16); + uint16_t right = w2; + + alist_interleave(hle, dmemo, left, right, count); +} + +static void ADDMIXER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint16_t count = (w1 >> 12) & 0xff0; + uint16_t dmemi = (w2 >> 16); + uint16_t dmemo = w2; + + alist_add(hle, dmemo, dmemi, count); +} + +static void HILOGAIN(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + int8_t gain = (w1 >> 16); /* Q4.4 signed */ + uint16_t count = w1 & 0xfff; + uint16_t dmem = (w2 >> 16); + + alist_multQ44(hle, dmem, count, gain); +} + +static void FILTER(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint32_t address = (w2 & 0xffffff); + + if (flags > 1) { + hle->alist_nead.filter_count = w1; + hle->alist_nead.filter_lut_address[0] = address; /* t6 */ + } + else { + uint16_t dmem = w1; + + hle->alist_nead.filter_lut_address[1] = address + 0x10; /* t5 */ + alist_filter(hle, dmem, hle->alist_nead.filter_count, address, hle->alist_nead.filter_lut_address); + } +} + +static void SEGMENT(struct hle_t* UNUSED(hle), uint32_t UNUSED(w1), uint32_t UNUSED(w2)) +{ +} + +static void NEAD_16(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t count = (w1 >> 16); + uint16_t dmemi = w1; + uint16_t dmemo = (w2 >> 16); + uint16_t block_size = w2; + + alist_copy_blocks(hle, dmemo, dmemi, block_size, count); +} + +static void POLEF(struct hle_t* hle, uint32_t w1, uint32_t w2) +{ + uint8_t flags = (w1 >> 16); + uint16_t gain = w1; + uint32_t address = (w2 & 0xffffff); + + if (hle->alist_nead.count == 0) + return; + + alist_polef( + hle, + flags & A_INIT, + hle->alist_nead.out, + hle->alist_nead.in, + hle->alist_nead.count, + gain, + hle->alist_nead.table, + address); +} + + +void alist_process_nead_mk(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + SPNOOP, RESAMPLE, SPNOOP, SEGMENT, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE_MK, POLEF, SETLOOP, + NEAD_16, INTERL, ENVSETUP1_MK, ENVMIXER_MK, + LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_sf(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE_MK, POLEF, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, SPNOOP, + HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_sfj(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE_MK, POLEF, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN, + HILOGAIN, UNKNOWN, DUPLICATE, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_fz(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + UNKNOWN, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, SPNOOP, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, SPNOOP, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN, + SPNOOP, UNKNOWN, DUPLICATE, SPNOOP, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_wrjb(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x20] = { + SPNOOP, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, SPNOOP, + SETBUFF, SPNOOP, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, SPNOOP, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN, + HILOGAIN, UNKNOWN, DUPLICATE, FILTER, + SPNOOP, SPNOOP, SPNOOP, SPNOOP + }; + + alist_process(hle, ABI, 0x20); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_ys(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_1080(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_oot(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, UNKNOWN, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_mm(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_mmb(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + SPNOOP, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_ac(struct hle_t* hle) +{ + static const acmd_callback_t ABI[0x18] = { + UNKNOWN, ADPCM, CLEARBUFF, SPNOOP, + ADDMIXER, RESAMPLE, RESAMPLE_ZOH, FILTER, + SETBUFF, DUPLICATE, DMEMMOVE, LOADADPCM, + MIXER, INTERLEAVE, HILOGAIN, SETLOOP, + NEAD_16, INTERL, ENVSETUP1, ENVMIXER, + LOADBUFF, SAVEBUFF, ENVSETUP2, UNKNOWN + }; + + alist_process(hle, ABI, 0x18); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void alist_process_nead_mats(struct hle_t* hle) +{ + /* FIXME: implement proper ucode + * Forward the task if possible, + * otherwise better to have no sound than garbage sound + */ + if (HleForwardTask(hle->user_defined) != 0) { + rsp_break(hle, SP_STATUS_TASKDONE); + } +} + +void alist_process_nead_efz(struct hle_t* hle) +{ + /* FIXME: implement proper ucode + * Forward the task if possible, + * otherwise use FZero ucode which should be very similar + */ + if (HleForwardTask(hle->user_defined) != 0) { + alist_process_nead_fz(hle); + } +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.h b/libmupen64plus/mupen64plus-rsp-hle/src/arithmetics.h similarity index 71% rename from libmupen64plus/mupen64plus-rsp-hle/src/jpeg.h rename to libmupen64plus/mupen64plus-rsp-hle/src/arithmetics.h index b7deaf6cc4..2250b248fa 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/arithmetics.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - jpeg.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64plus-rsp-hle - arithmetics.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,12 +19,25 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef JPEG_H -#define JPEG_H +#ifndef ARITHMETICS_H +#define ARITHMETICS_H -void jpeg_decode_PS0(); -void jpeg_decode_PS(); -void jpeg_decode_OB(); +#include + +#include "common.h" + +static inline int16_t clamp_s16(int_fast32_t x) +{ + x = (x < INT16_MIN) ? INT16_MIN: x; + x = (x > INT16_MAX) ? INT16_MAX: x; + + return x; +} + +static inline int32_t vmulf(int16_t x, int16_t y) +{ + return (((int32_t)(x))*((int32_t)(y))+0x4000)>>15; +} #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/audio.c b/libmupen64plus/mupen64plus-rsp-hle/src/audio.c new file mode 100644 index 0000000000..fac2a101aa --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/audio.c @@ -0,0 +1,128 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - audio.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "arithmetics.h" + +const int16_t RESAMPLE_LUT[64 * 4] = { + (int16_t)0x0c39, (int16_t)0x66ad, (int16_t)0x0d46, (int16_t)0xffdf, + (int16_t)0x0b39, (int16_t)0x6696, (int16_t)0x0e5f, (int16_t)0xffd8, + (int16_t)0x0a44, (int16_t)0x6669, (int16_t)0x0f83, (int16_t)0xffd0, + (int16_t)0x095a, (int16_t)0x6626, (int16_t)0x10b4, (int16_t)0xffc8, + (int16_t)0x087d, (int16_t)0x65cd, (int16_t)0x11f0, (int16_t)0xffbf, + (int16_t)0x07ab, (int16_t)0x655e, (int16_t)0x1338, (int16_t)0xffb6, + (int16_t)0x06e4, (int16_t)0x64d9, (int16_t)0x148c, (int16_t)0xffac, + (int16_t)0x0628, (int16_t)0x643f, (int16_t)0x15eb, (int16_t)0xffa1, + (int16_t)0x0577, (int16_t)0x638f, (int16_t)0x1756, (int16_t)0xff96, + (int16_t)0x04d1, (int16_t)0x62cb, (int16_t)0x18cb, (int16_t)0xff8a, + (int16_t)0x0435, (int16_t)0x61f3, (int16_t)0x1a4c, (int16_t)0xff7e, + (int16_t)0x03a4, (int16_t)0x6106, (int16_t)0x1bd7, (int16_t)0xff71, + (int16_t)0x031c, (int16_t)0x6007, (int16_t)0x1d6c, (int16_t)0xff64, + (int16_t)0x029f, (int16_t)0x5ef5, (int16_t)0x1f0b, (int16_t)0xff56, + (int16_t)0x022a, (int16_t)0x5dd0, (int16_t)0x20b3, (int16_t)0xff48, + (int16_t)0x01be, (int16_t)0x5c9a, (int16_t)0x2264, (int16_t)0xff3a, + (int16_t)0x015b, (int16_t)0x5b53, (int16_t)0x241e, (int16_t)0xff2c, + (int16_t)0x0101, (int16_t)0x59fc, (int16_t)0x25e0, (int16_t)0xff1e, + (int16_t)0x00ae, (int16_t)0x5896, (int16_t)0x27a9, (int16_t)0xff10, + (int16_t)0x0063, (int16_t)0x5720, (int16_t)0x297a, (int16_t)0xff02, + (int16_t)0x001f, (int16_t)0x559d, (int16_t)0x2b50, (int16_t)0xfef4, + (int16_t)0xffe2, (int16_t)0x540d, (int16_t)0x2d2c, (int16_t)0xfee8, + (int16_t)0xffac, (int16_t)0x5270, (int16_t)0x2f0d, (int16_t)0xfedb, + (int16_t)0xff7c, (int16_t)0x50c7, (int16_t)0x30f3, (int16_t)0xfed0, + (int16_t)0xff53, (int16_t)0x4f14, (int16_t)0x32dc, (int16_t)0xfec6, + (int16_t)0xff2e, (int16_t)0x4d57, (int16_t)0x34c8, (int16_t)0xfebd, + (int16_t)0xff0f, (int16_t)0x4b91, (int16_t)0x36b6, (int16_t)0xfeb6, + (int16_t)0xfef5, (int16_t)0x49c2, (int16_t)0x38a5, (int16_t)0xfeb0, + (int16_t)0xfedf, (int16_t)0x47ed, (int16_t)0x3a95, (int16_t)0xfeac, + (int16_t)0xfece, (int16_t)0x4611, (int16_t)0x3c85, (int16_t)0xfeab, + (int16_t)0xfec0, (int16_t)0x4430, (int16_t)0x3e74, (int16_t)0xfeac, + (int16_t)0xfeb6, (int16_t)0x424a, (int16_t)0x4060, (int16_t)0xfeaf, + (int16_t)0xfeaf, (int16_t)0x4060, (int16_t)0x424a, (int16_t)0xfeb6, + (int16_t)0xfeac, (int16_t)0x3e74, (int16_t)0x4430, (int16_t)0xfec0, + (int16_t)0xfeab, (int16_t)0x3c85, (int16_t)0x4611, (int16_t)0xfece, + (int16_t)0xfeac, (int16_t)0x3a95, (int16_t)0x47ed, (int16_t)0xfedf, + (int16_t)0xfeb0, (int16_t)0x38a5, (int16_t)0x49c2, (int16_t)0xfef5, + (int16_t)0xfeb6, (int16_t)0x36b6, (int16_t)0x4b91, (int16_t)0xff0f, + (int16_t)0xfebd, (int16_t)0x34c8, (int16_t)0x4d57, (int16_t)0xff2e, + (int16_t)0xfec6, (int16_t)0x32dc, (int16_t)0x4f14, (int16_t)0xff53, + (int16_t)0xfed0, (int16_t)0x30f3, (int16_t)0x50c7, (int16_t)0xff7c, + (int16_t)0xfedb, (int16_t)0x2f0d, (int16_t)0x5270, (int16_t)0xffac, + (int16_t)0xfee8, (int16_t)0x2d2c, (int16_t)0x540d, (int16_t)0xffe2, + (int16_t)0xfef4, (int16_t)0x2b50, (int16_t)0x559d, (int16_t)0x001f, + (int16_t)0xff02, (int16_t)0x297a, (int16_t)0x5720, (int16_t)0x0063, + (int16_t)0xff10, (int16_t)0x27a9, (int16_t)0x5896, (int16_t)0x00ae, + (int16_t)0xff1e, (int16_t)0x25e0, (int16_t)0x59fc, (int16_t)0x0101, + (int16_t)0xff2c, (int16_t)0x241e, (int16_t)0x5b53, (int16_t)0x015b, + (int16_t)0xff3a, (int16_t)0x2264, (int16_t)0x5c9a, (int16_t)0x01be, + (int16_t)0xff48, (int16_t)0x20b3, (int16_t)0x5dd0, (int16_t)0x022a, + (int16_t)0xff56, (int16_t)0x1f0b, (int16_t)0x5ef5, (int16_t)0x029f, + (int16_t)0xff64, (int16_t)0x1d6c, (int16_t)0x6007, (int16_t)0x031c, + (int16_t)0xff71, (int16_t)0x1bd7, (int16_t)0x6106, (int16_t)0x03a4, + (int16_t)0xff7e, (int16_t)0x1a4c, (int16_t)0x61f3, (int16_t)0x0435, + (int16_t)0xff8a, (int16_t)0x18cb, (int16_t)0x62cb, (int16_t)0x04d1, + (int16_t)0xff96, (int16_t)0x1756, (int16_t)0x638f, (int16_t)0x0577, + (int16_t)0xffa1, (int16_t)0x15eb, (int16_t)0x643f, (int16_t)0x0628, + (int16_t)0xffac, (int16_t)0x148c, (int16_t)0x64d9, (int16_t)0x06e4, + (int16_t)0xffb6, (int16_t)0x1338, (int16_t)0x655e, (int16_t)0x07ab, + (int16_t)0xffbf, (int16_t)0x11f0, (int16_t)0x65cd, (int16_t)0x087d, + (int16_t)0xffc8, (int16_t)0x10b4, (int16_t)0x6626, (int16_t)0x095a, + (int16_t)0xffd0, (int16_t)0x0f83, (int16_t)0x6669, (int16_t)0x0a44, + (int16_t)0xffd8, (int16_t)0x0e5f, (int16_t)0x6696, (int16_t)0x0b39, + (int16_t)0xffdf, (int16_t)0x0d46, (int16_t)0x66ad, (int16_t)0x0c39 +}; + +int32_t rdot(size_t n, const int16_t *x, const int16_t *y) +{ + int32_t accu = 0; + + y += n; + + while (n != 0) { + accu += *(x++) * *(--y); + --n; + } + + return accu; +} + +void adpcm_compute_residuals(int16_t* dst, const int16_t* src, + const int16_t* cb_entry, const int16_t* last_samples, size_t count) +{ + const int16_t* const book1 = cb_entry; + const int16_t* const book2 = cb_entry + 8; + + const int16_t l1 = last_samples[0]; + const int16_t l2 = last_samples[1]; + + size_t i; + + assert(count <= 8); + + for(i = 0; i < count; ++i) { + int32_t accu = (int32_t)src[i] << 11; + accu += book1[i]*l1 + book2[i]*l2 + rdot(i, book2, src); + dst[i] = clamp_s16(accu >> 11); + } +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/alist_internal.h b/libmupen64plus/mupen64plus-rsp-hle/src/audio.h similarity index 61% rename from libmupen64plus/mupen64plus-rsp-hle/src/alist_internal.h rename to libmupen64plus/mupen64plus-rsp-hle/src/audio.h index ae39883b45..21c7a09167 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/alist_internal.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/audio.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - alist_internal.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64plus-rsp-hle - audio.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,32 +19,27 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef ALIST_INTERNAL_H -#define ALIST_INTERNAL_H +#ifndef AUDIO_H +#define AUDIO_H -#include "hle.h" +#include +#include -typedef void (*acmd_callback_t)(u32 inst1, u32 inst2); +#include "common.h" -/* - * Audio flags - */ +extern const int16_t RESAMPLE_LUT[64 * 4]; -#define A_INIT 0x01 -#define A_CONTINUE 0x00 -#define A_LOOP 0x02 -#define A_OUT 0x02 -#define A_LEFT 0x02 -#define A_RIGHT 0x00 -#define A_VOL 0x04 -#define A_RATE 0x00 -#define A_AUX 0x08 -#define A_NOAUX 0x00 -#define A_MAIN 0x00 -#define A_MIX 0x10 +int32_t rdot(size_t n, const int16_t *x, const int16_t *y); -extern u16 AudioInBuffer, AudioOutBuffer, AudioCount; -extern u16 AudioAuxA, AudioAuxC, AudioAuxE; -extern u32 loopval; // Value set by A_SETLOOP : Possible conflict with SETVOLUME??? +static inline int16_t adpcm_predict_sample(uint8_t byte, uint8_t mask, + unsigned lshift, unsigned rshift) +{ + int16_t sample = (uint16_t)(byte & mask) << lshift; + sample >>= rshift; /* signed */ + return sample; +} + +void adpcm_compute_residuals(int16_t* dst, const int16_t* src, + const int16_t* cb_entry, const int16_t* last_samples, size_t count); #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c b/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c index 1e89056915..bda5ecab18 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c +++ b/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.c @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - cicx105.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Mupen64Plus homepage: https://mupen64plus.org/ * * Copyright (C) 2012 Bobby Smiles * * Copyright (C) 2009 Richard Goedeken * * Copyright (C) 2002 Hacktarux * @@ -23,33 +23,34 @@ #include -#include "hle.h" +#include "hle_internal.h" /** * During IPL3 stage of CIC x105 games, the RSP performs some checks and transactions * necessary for booting the game. - * + * * We only implement the needed DMA transactions for booting. * * Found in Banjo-Tooie, Zelda, Perfect Dark, ...) **/ -void cicx105_ucode() +void cicx105_ucode(struct hle_t* hle) { - // memcpy is okay to use because access constrains are met (alignment, size) + /* memcpy is okay to use because access constrains are met (alignment, size) */ unsigned int i; - unsigned char * dst = rsp.RDRAM + 0x2fb1f0; - unsigned char * src = rsp.IMEM + 0x120; + unsigned char *dst = hle->dram + 0x2fb1f0; + unsigned char *src = hle->imem + 0x120; /* dma_read(0x1120, 0x1e8, 0x1e8) */ - memcpy(rsp.IMEM + 0x120, rsp.RDRAM + 0x1e8, 0x1f0); + memcpy(hle->imem + 0x120, hle->dram + 0x1e8, 0x1f0); /* dma_write(0x1120, 0x2fb1f0, 0xfe817000) */ - for (i = 0; i < 24; ++i) - { + for (i = 0; i < 24; ++i) { memcpy(dst, src, 8); dst += 0xff0; src += 0x8; } + + rsp_break(hle, 0); } diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.h b/libmupen64plus/mupen64plus-rsp-hle/src/common.h similarity index 73% rename from libmupen64plus/mupen64plus-rsp-hle/src/cicx105.h rename to libmupen64plus/mupen64plus-rsp-hle/src/common.h index fbfcfad0a7..cd5dfa6948 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/cicx105.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/common.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - cicx105.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64plus-rsp-hle - common.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -19,10 +19,20 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ -#ifndef CICX105_H -#define CICX105_H +#ifndef COMMON_H +#define COMMON_H -void cicx105_ucode(); +/* macro for unused variable warning suppression */ +#ifdef __GNUC__ +# define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) +#else +# define UNUSED(x) UNUSED_ ## x +#endif + +/* macro for inline keyword */ +#ifdef _MSC_VER +#define inline __inline +#endif #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle.c b/libmupen64plus/mupen64plus-rsp-hle/src/hle.c new file mode 100644 index 0000000000..ddc0404404 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle.c @@ -0,0 +1,489 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - hle.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2012 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#ifdef ENABLE_TASK_DUMP +#include +#endif + +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" +#include "ucodes.h" + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* some rdp status flags */ +#define DP_STATUS_FREEZE 0x2 + + + +/* helper functions prototypes */ +static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size); +static bool is_task(struct hle_t* hle); +static void send_dlist_to_gfx_plugin(struct hle_t* hle); +static bool try_fast_audio_dispatching(struct hle_t* hle); +static bool try_fast_task_dispatching(struct hle_t* hle); +static void normal_task_dispatching(struct hle_t* hle); +static void non_task_dispatching(struct hle_t* hle); +static bool try_re2_task_dispatching(struct hle_t* hle); + +#ifdef ENABLE_TASK_DUMP +static void dump_binary(struct hle_t* hle, const char *const filename, + const unsigned char *const bytes, unsigned int size); +static void dump_task(struct hle_t* hle, const char *const filename); +static void dump_unknown_task(struct hle_t* hle, unsigned int sum); +static void dump_unknown_non_task(struct hle_t* hle, unsigned int sum); +#endif + +/* Global functions */ +void hle_init(struct hle_t* hle, + unsigned char* dram, + unsigned char* dmem, + unsigned char* imem, + unsigned int* mi_intr, + unsigned int* sp_mem_addr, + unsigned int* sp_dram_addr, + unsigned int* sp_rd_length, + unsigned int* sp_wr_length, + unsigned int* sp_status, + unsigned int* sp_dma_full, + unsigned int* sp_dma_busy, + unsigned int* sp_pc, + unsigned int* sp_semaphore, + unsigned int* dpc_start, + unsigned int* dpc_end, + unsigned int* dpc_current, + unsigned int* dpc_status, + unsigned int* dpc_clock, + unsigned int* dpc_bufbusy, + unsigned int* dpc_pipebusy, + unsigned int* dpc_tmem, + void* user_defined) +{ + hle->dram = dram; + hle->dmem = dmem; + hle->imem = imem; + hle->mi_intr = mi_intr; + hle->sp_mem_addr = sp_mem_addr; + hle->sp_dram_addr = sp_dram_addr; + hle->sp_rd_length = sp_rd_length; + hle->sp_wr_length = sp_wr_length; + hle->sp_status = sp_status; + hle->sp_dma_full = sp_dma_full; + hle->sp_dma_busy = sp_dma_busy; + hle->sp_pc = sp_pc; + hle->sp_semaphore = sp_semaphore; + hle->dpc_start = dpc_start; + hle->dpc_end = dpc_end; + hle->dpc_current = dpc_current; + hle->dpc_status = dpc_status; + hle->dpc_clock = dpc_clock; + hle->dpc_bufbusy = dpc_bufbusy; + hle->dpc_pipebusy = dpc_pipebusy; + hle->dpc_tmem = dpc_tmem; + hle->user_defined = user_defined; +} + +void hle_execute(struct hle_t* hle) +{ + if (is_task(hle)) { + if (!try_fast_task_dispatching(hle)) + normal_task_dispatching(hle); + } else { + non_task_dispatching(hle); + } +} + +/* local functions */ +static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size) +{ + unsigned int sum = 0; + const unsigned char *const bytes_end = bytes + size; + + while (bytes != bytes_end) + sum += *bytes++; + + return sum; +} + +/** + * Try to figure if the RSP was launched using osSpTask* functions + * and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless). + * + * Previously, the ucode_size field was used to determine this, + * but it is not robust enough (hi Pokemon Stadium !) because games could write anything + * in this field : most ucode_boot discard the value and just use 0xf7f anyway. + * + * Using ucode_boot_size should be more robust in this regard. + **/ +static bool is_task(struct hle_t* hle) +{ + return (*dmem_u32(hle, TASK_UCODE_BOOT_SIZE) <= 0x1000); +} + +void rsp_break(struct hle_t* hle, unsigned int setbits) +{ + *hle->sp_status |= setbits | SP_STATUS_BROKE | SP_STATUS_HALT; + + if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK)) { + *hle->mi_intr |= MI_INTR_SP; + HleCheckInterrupts(hle->user_defined); + } +} + +static void send_alist_to_audio_plugin(struct hle_t* hle) +{ + HleProcessAlistList(hle->user_defined); + rsp_break(hle, SP_STATUS_TASKDONE); +} + +static void send_dlist_to_gfx_plugin(struct hle_t* hle) +{ + /* Since GFX_INFO version 2, these bits are set before calling the ProcessDlistList function. + * And the GFX plugin is responsible to unset them if needed. + * For GFX_INFO version < 2, the GFX plugin didn't have access to sp_status so + * it doesn't matter if we set these bits before calling ProcessDlistList function. + */ + *hle->sp_status |= SP_STATUS_TASKDONE | SP_STATUS_BROKE | SP_STATUS_HALT; + + HleProcessDlistList(hle->user_defined); + + if ((*hle->sp_status & SP_STATUS_INTR_ON_BREAK) && (*hle->sp_status & (SP_STATUS_TASKDONE | SP_STATUS_BROKE | SP_STATUS_HALT))) { + *hle->mi_intr |= MI_INTR_SP; + HleCheckInterrupts(hle->user_defined); + } +} + +static bool try_fast_audio_dispatching(struct hle_t* hle) +{ + /* identify audio ucode by using the content of ucode_data */ + uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA); + uint32_t v; + + if (*dram_u32(hle, ucode_data) == 0x00000001) { + if (*dram_u32(hle, ucode_data + 0x30) == 0xf0000f00) { + v = *dram_u32(hle, ucode_data + 0x28); + switch(v) + { + case 0x1e24138c: /* audio ABI (most common) */ + alist_process_audio(hle); return true; + case 0x1dc8138c: /* GoldenEye */ + alist_process_audio_ge(hle); return true; + case 0x1e3c1390: /* BlastCorp, DiddyKongRacing */ + alist_process_audio_bc(hle); return true; + default: + HleWarnMessage(hle->user_defined, "ABI1 identification regression: v=%08x", v); + } + } else { + v = *dram_u32(hle, ucode_data + 0x10); + switch(v) + { + case 0x11181350: /* MarioKart, WaveRace (E) */ + alist_process_nead_mk(hle); return true; + case 0x111812e0: /* StarFox (J) */ + alist_process_nead_sfj(hle); return true; + case 0x110412ac: /* WaveRace (J RevB) */ + alist_process_nead_wrjb(hle); return true; + case 0x110412cc: /* StarFox/LylatWars (except J) */ + alist_process_nead_sf(hle); return true; + case 0x1cd01250: /* FZeroX */ + alist_process_nead_fz(hle); return true; + case 0x1f08122c: /* YoshisStory */ + alist_process_nead_ys(hle); return true; + case 0x1f38122c: /* 1080° Snowboarding */ + alist_process_nead_1080(hle); return true; + case 0x1f681230: /* Zelda OoT / Zelda MM (J, J RevA) */ + alist_process_nead_oot(hle); return true; + case 0x1f801250: /* Zelda MM (except J, J RevA, E Beta), PokemonStadium 2 */ + alist_process_nead_mm(hle); return true; + case 0x109411f8: /* Zelda MM (E Beta) */ + alist_process_nead_mmb(hle); return true; + case 0x1eac11b8: /* AnimalCrossing */ + alist_process_nead_ac(hle); return true; + case 0x00010010: /* MusyX v2 (IndianaJones, BattleForNaboo) */ + musyx_v2_task(hle); return true; + case 0x1f701238: /* Mario Artist Talent Studio */ + alist_process_nead_mats(hle); return true; + case 0x1f4c1230: /* FZeroX Expansion */ + alist_process_nead_efz(hle); return true; + default: + HleWarnMessage(hle->user_defined, "ABI2 identification regression: v=%08x", v); + } + } + } else { + v = *dram_u32(hle, ucode_data + 0x10); + switch(v) + { + case 0x00000001: /* MusyX v1 + RogueSquadron, ResidentEvil2, PolarisSnoCross, + TheWorldIsNotEnough, RugratsInParis, NBAShowTime, + HydroThunder, Tarzan, GauntletLegend, Rush2049 */ + musyx_v1_task(hle); return true; + case 0x0000127c: /* naudio (many games) */ + alist_process_naudio(hle); return true; + case 0x00001280: /* BanjoKazooie */ + alist_process_naudio_bk(hle); return true; + case 0x1c58126c: /* DonkeyKong */ + alist_process_naudio_dk(hle); return true; + case 0x1ae8143c: /* BanjoTooie, JetForceGemini, MickeySpeedWayUSA, PerfectDark */ + alist_process_naudio_mp3(hle); return true; + case 0x1ab0140c: /* ConkerBadFurDay */ + alist_process_naudio_cbfd(hle); return true; + + default: + HleWarnMessage(hle->user_defined, "ABI3 identification regression: v=%08x", v); + } + } + + return false; +} + +static bool try_fast_task_dispatching(struct hle_t* hle) +{ + /* identify task ucode by its type */ + switch (*dmem_u32(hle, TASK_TYPE)) { + case 1: + /* Resident evil 2 */ + if (*dmem_u32(hle, TASK_DATA_PTR) == 0) { + return try_re2_task_dispatching(hle); + } + + if (hle->hle_gfx) { + send_dlist_to_gfx_plugin(hle); + return true; + } + break; + + case 2: + if (hle->hle_aud) { + send_alist_to_audio_plugin(hle); + return true; + } else if (try_fast_audio_dispatching(hle)) + return true; + break; + + case 7: + HleShowCFB(hle->user_defined); + break; + } + + return false; +} + +static void normal_task_dispatching(struct hle_t* hle) +{ + const unsigned int sum = + sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), min(*dmem_u32(hle, TASK_UCODE_SIZE), 0xf80) >> 1); + + switch (sum) { + /* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */ + case 0x278: + /* Nothing to emulate */ + rsp_break(hle, SP_STATUS_TASKDONE); + return; + + /* GFX: Twintris [misleading task->type == 0] */ + case 0x212ee: + if (hle->hle_gfx) { + send_dlist_to_gfx_plugin(hle); + return; + } + break; + + /* JPEG: found in Pokemon Stadium J */ + case 0x2c85a: + jpeg_decode_PS0(hle); + return; + + /* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */ + case 0x2caa6: + jpeg_decode_PS(hle); + return; + + /* JPEG: found in Ogre Battle, Bottom of the 9th */ + case 0x130de: + case 0x278b0: + jpeg_decode_OB(hle); + return; + } + + /* Forward task to RSP Fallback. + * If task is not forwarded, use the regular "unknown task" path */ + if (HleForwardTask(hle->user_defined) != 0) { + + /* Send task_done signal for unknown ucodes to allow further processings */ + rsp_break(hle, SP_STATUS_TASKDONE); + + HleWarnMessage(hle->user_defined, "unknown OSTask: sum: %x PC:%x", sum, *hle->sp_pc); +#ifdef ENABLE_TASK_DUMP + dump_unknown_task(hle, sum); +#endif + } +} + +static void non_task_dispatching(struct hle_t* hle) +{ + const unsigned int sum = sum_bytes(hle->imem, 44); + + if (sum == 0x9e2) + { + /* CIC x105 ucode (used during boot of CIC x105 games) */ + cicx105_ucode(hle); + return; + } + + /* Forward task to RSP Fallback. + * If task is not forwarded, use the regular "unknown ucode" path */ + if (HleForwardTask(hle->user_defined) != 0) { + + HleWarnMessage(hle->user_defined, "unknown RSP code: sum: %x PC:%x", sum, *hle->sp_pc); +#ifdef ENABLE_TASK_DUMP + dump_unknown_non_task(hle, sum); +#endif + } +} + +/* Resident evil 2 */ +static bool try_re2_task_dispatching(struct hle_t* hle) +{ + const unsigned int sum = + sum_bytes((void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE)), 256); + + switch (sum) { + + case 0x450f: + resize_bilinear_task(hle); + return true; + + case 0x3b44: + decode_video_frame_task(hle); + return true; + + case 0x3d84: + fill_video_double_buffer_task(hle); + return true; + } + + return false; +} + +#ifdef ENABLE_TASK_DUMP +static void dump_unknown_task(struct hle_t* hle, unsigned int sum) +{ + char filename[256]; + uint32_t ucode = *dmem_u32(hle, TASK_UCODE); + uint32_t ucode_data = *dmem_u32(hle, TASK_UCODE_DATA); + uint32_t data_ptr = *dmem_u32(hle, TASK_DATA_PTR); + + sprintf(&filename[0], "task_%x.log", sum); + dump_task(hle, filename); + + /* dump ucode_boot */ + sprintf(&filename[0], "ucode_boot_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, *dmem_u32(hle, TASK_UCODE_BOOT)), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE)); + + /* dump ucode */ + if (ucode != 0) { + sprintf(&filename[0], "ucode_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, ucode), 0xf80); + } + + /* dump ucode_data */ + if (ucode_data != 0) { + sprintf(&filename[0], "ucode_data_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, ucode_data), *dmem_u32(hle, TASK_UCODE_DATA_SIZE)); + } + + /* dump data */ + if (data_ptr != 0) { + sprintf(&filename[0], "data_%x.bin", sum); + dump_binary(hle, filename, (void*)dram_u32(hle, data_ptr), *dmem_u32(hle, TASK_DATA_SIZE)); + } +} + +static void dump_unknown_non_task(struct hle_t* hle, unsigned int sum) +{ + char filename[256]; + + /* dump IMEM & DMEM for further analysis */ + sprintf(&filename[0], "imem_%x.bin", sum); + dump_binary(hle, filename, hle->imem, 0x1000); + + sprintf(&filename[0], "dmem_%x.bin", sum); + dump_binary(hle, filename, hle->dmem, 0x1000); +} + +static void dump_binary(struct hle_t* hle, const char *const filename, + const unsigned char *const bytes, unsigned int size) +{ + FILE *f; + + /* if file already exists, do nothing */ + f = fopen(filename, "r"); + if (f == NULL) { + /* else we write bytes to the file */ + f = fopen(filename, "wb"); + if (f != NULL) { + if (fwrite(bytes, 1, size, f) != size) + HleErrorMessage(hle->user_defined, "Writing error on %s", filename); + fclose(f); + } else + HleErrorMessage(hle->user_defined, "Couldn't open %s for writing !", filename); + } else + fclose(f); +} + +static void dump_task(struct hle_t* hle, const char *const filename) +{ + FILE *f; + + f = fopen(filename, "r"); + if (f == NULL) { + f = fopen(filename, "w"); + fprintf(f, + "type = %d\n" + "flags = %d\n" + "ucode_boot = %#08x size = %#x\n" + "ucode = %#08x size = %#x\n" + "ucode_data = %#08x size = %#x\n" + "dram_stack = %#08x size = %#x\n" + "output_buff = %#08x *size = %#x\n" + "data = %#08x size = %#x\n" + "yield_data = %#08x size = %#x\n", + *dmem_u32(hle, TASK_TYPE), + *dmem_u32(hle, TASK_FLAGS), + *dmem_u32(hle, TASK_UCODE_BOOT), *dmem_u32(hle, TASK_UCODE_BOOT_SIZE), + *dmem_u32(hle, TASK_UCODE), *dmem_u32(hle, TASK_UCODE_SIZE), + *dmem_u32(hle, TASK_UCODE_DATA), *dmem_u32(hle, TASK_UCODE_DATA_SIZE), + *dmem_u32(hle, TASK_DRAM_STACK), *dmem_u32(hle, TASK_DRAM_STACK_SIZE), + *dmem_u32(hle, TASK_OUTPUT_BUFF), *dmem_u32(hle, TASK_OUTPUT_BUFF_SIZE), + *dmem_u32(hle, TASK_DATA_PTR), *dmem_u32(hle, TASK_DATA_SIZE), + *dmem_u32(hle, TASK_YIELD_DATA_PTR), *dmem_u32(hle, TASK_YIELD_DATA_SIZE)); + fclose(f); + } else + fclose(f); +} +#endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle.h b/libmupen64plus/mupen64plus-rsp-hle/src/hle.h index ea517e735e..a420b752d3 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/hle.h +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle.h @@ -1,7 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - hle.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2002 Hacktarux * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,68 +22,33 @@ #ifndef HLE_H #define HLE_H -#define M64P_PLUGIN_PROTOTYPES 1 -#include "m64p_plugin.h" +#include "hle_internal.h" -#define RSP_HLE_VERSION 0x020000 -#define RSP_PLUGIN_API_VERSION 0x020000 +void hle_init(struct hle_t* hle, + unsigned char* dram, + unsigned char* dmem, + unsigned char* imem, + unsigned int* mi_intr, + unsigned int* sp_mem_addr, + unsigned int* sp_dram_addr, + unsigned int* sp_rd_length, + unsigned int* sp_wr_length, + unsigned int* sp_status, + unsigned int* sp_dma_full, + unsigned int* sp_dma_busy, + unsigned int* sp_pc, + unsigned int* sp_semaphore, + unsigned int* dpc_start, + unsigned int* dpc_end, + unsigned int* dpc_current, + unsigned int* dpc_status, + unsigned int* dpc_clock, + unsigned int* dpc_bufbusy, + unsigned int* dpc_pipebusy, + unsigned int* dpc_tmem, + void* user_defined); -#ifdef M64P_BIG_ENDIAN -#define S 0 -#define S16 0 -#define S8 0 -#else -#define S 1 -#define S16 2 -#define S8 3 -#endif - -// types -typedef unsigned char u8; -typedef unsigned short u16; -typedef unsigned int u32; -typedef unsigned long long u64; - -typedef signed char s8; -typedef signed short s16; -typedef signed int s32; -typedef signed long long s64; - -extern RSP_INFO rsp; - -typedef struct -{ - unsigned int type; - unsigned int flags; - - unsigned int ucode_boot; - unsigned int ucode_boot_size; - - unsigned int ucode; - unsigned int ucode_size; - - unsigned int ucode_data; - unsigned int ucode_data_size; - - unsigned int dram_stack; - unsigned int dram_stack_size; - - unsigned int output_buff; - unsigned int output_buff_size; - - unsigned int data_ptr; - unsigned int data_size; - - unsigned int yield_data_ptr; - unsigned int yield_data_size; -} OSTask_t; - -static const OSTask_t * const get_task() -{ - return (OSTask_t*)(rsp.DMEM + 0xfc0); -} - -void DebugMessage(int level, const char *message, ...); +void hle_execute(struct hle_t* hle); #endif diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle_external.h b/libmupen64plus/mupen64plus-rsp-hle/src/hle_external.h new file mode 100644 index 0000000000..5dcbf2ea47 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle_external.h @@ -0,0 +1,40 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - hle_external.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef HLE_EXTERNAL_H +#define HLE_EXTERNAL_H + +/* users of the hle core are expected to define these functions */ + +void HleVerboseMessage(void* user_defined, const char *message, ...); +void HleInfoMessage(void* user_defined, const char *message, ...); +void HleErrorMessage(void* user_defined, const char *message, ...); +void HleWarnMessage(void* user_defined, const char *message, ...); + +void HleCheckInterrupts(void* user_defined); +void HleProcessDlistList(void* user_defined); +void HleProcessAlistList(void* user_defined); +void HleProcessRdpList(void* user_defined); +void HleShowCFB(void* user_defined); +int HleForwardTask(void* user_defined); + +#endif + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/hle_internal.h b/libmupen64plus/mupen64plus-rsp-hle/src/hle_internal.h new file mode 100644 index 0000000000..ad1708bb1b --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/hle_internal.h @@ -0,0 +1,91 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - hle_internal.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef HLE_INTERNAL_H +#define HLE_INTERNAL_H + +#include + +#include "ucodes.h" + +/* rsp hle internal state - internal usage only */ +struct hle_t +{ + unsigned char* dram; + unsigned char* dmem; + unsigned char* imem; + + unsigned int* mi_intr; + + unsigned int* sp_mem_addr; + unsigned int* sp_dram_addr; + unsigned int* sp_rd_length; + unsigned int* sp_wr_length; + unsigned int* sp_status; + unsigned int* sp_dma_full; + unsigned int* sp_dma_busy; + unsigned int* sp_pc; + unsigned int* sp_semaphore; + + unsigned int* dpc_start; + unsigned int* dpc_end; + unsigned int* dpc_current; + unsigned int* dpc_status; + unsigned int* dpc_clock; + unsigned int* dpc_bufbusy; + unsigned int* dpc_pipebusy; + unsigned int* dpc_tmem; + + /* for user convenience, this will be passed to "external" functions */ + void* user_defined; + + int hle_gfx; + int hle_aud; + + /* alist.c */ + uint8_t alist_buffer[0x1000]; + + /* alist_audio.c */ + struct alist_audio_t alist_audio; + + /* alist_naudio.c */ + struct alist_naudio_t alist_naudio; + + /* alist_nead.c */ + struct alist_nead_t alist_nead; + + /* mp3.c */ + uint8_t mp3_buffer[0x1000]; +}; + +/* some mips interface interrupt flags */ +#define MI_INTR_SP 0x1 + +/* some rsp status flags */ +#define SP_STATUS_HALT 0x1 +#define SP_STATUS_BROKE 0x2 +#define SP_STATUS_INTR_ON_BREAK 0x40 +#define SP_STATUS_TASKDONE 0x200 + +void rsp_break(struct hle_t* hle, unsigned int setbits); + +#endif + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c b/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c index 28fcc8bdd9..ff424c4033 100644 --- a/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c +++ b/libmupen64plus/mupen64plus-rsp-hle/src/jpeg.c @@ -1,6 +1,6 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Mupen64plus-rsp-hle - jpeg.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Mupen64Plus homepage: https://mupen64plus.org/ * * Copyright (C) 2012 Bobby Smiles * * Copyright (C) 2009 Richard Goedeken * * Copyright (C) 2002 Hacktarux * @@ -22,49 +22,48 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include -#include #include +#include -#define M64P_PLUGIN_PROTOTYPES 1 -#include "m64p_types.h" -#include "m64p_plugin.h" -#include "hle.h" +#include "arithmetics.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" #define SUBBLOCK_SIZE 64 -typedef void (*tile_line_emitter_t)(const int16_t *y, const int16_t *u, uint32_t address); -typedef void (*std_macroblock_decoder_t)(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]); - -/* rdram operations */ -// FIXME: these functions deserve their own module -static void rdram_read_many_u16(uint16_t *dst, uint32_t address, unsigned int count); -static void rdram_write_many_u16(const uint16_t *src, uint32_t address, unsigned int count); -static uint32_t rdram_read_u32(uint32_t address); -static void rdram_write_many_u32(const uint32_t *src, uint32_t address, unsigned int count); +typedef void (*tile_line_emitter_t)(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address); +typedef void (*subblock_transform_t)(int16_t *dst, const int16_t *src); /* standard jpeg ucode decoder */ -static void jpeg_decode_std(const char * const version, const std_macroblock_decoder_t decode_mb, const tile_line_emitter_t emit_line); +static void jpeg_decode_std(struct hle_t* hle, + const char *const version, + const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + const tile_line_emitter_t emit_line); /* helper functions */ static uint8_t clamp_u8(int16_t x); static int16_t clamp_s12(int16_t x); -static int16_t clamp_s16(int32_t x); static uint16_t clamp_RGBA_component(int16_t x); -/* pixel conversion & foratting */ +/* pixel conversion & formatting */ static uint32_t GetUYVY(int16_t y1, int16_t y2, int16_t u, int16_t v); static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v); /* tile line emitters */ -static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address); -static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t address); +static void EmitYUVTileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address); +static void EmitRGBATileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address); /* macroblocks operations */ -static void DecodeMacroblock1(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable); -static void DecodeMacroblock2(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]); -static void DecodeMacroblock3(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]); -static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); -static void EmitTilesMode2(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); +static void decode_macroblock_ob(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable); +static void decode_macroblock_std(const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + int16_t *macroblock, + unsigned int subblock_count, + const int16_t qtables[3][SUBBLOCK_SIZE]); +static void EmitTilesMode0(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); +static void EmitTilesMode2(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address); /* subblocks operations */ static void TransposeSubBlock(int16_t *dst, const int16_t *src); @@ -73,14 +72,13 @@ static void ReorderSubBlock(int16_t *dst, const int16_t *src, const unsigned int static void MultSubBlocks(int16_t *dst, const int16_t *src1, const int16_t *src2, unsigned int shift); static void ScaleSubBlock(int16_t *dst, const int16_t *src, int16_t scale); static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift); -static void InverseDCT1D(const float * const x, float *dst, unsigned int stride); +static void InverseDCT1D(const float *const x, float *dst, unsigned int stride); static void InverseDCTSubBlock(int16_t *dst, const int16_t *src); static void RescaleYSubBlock(int16_t *dst, const int16_t *src); static void RescaleUVSubBlock(int16_t *dst, const int16_t *src); /* transposed dequantization table */ -static const int16_t DEFAULT_QTABLE[SUBBLOCK_SIZE] = -{ +static const int16_t DEFAULT_QTABLE[SUBBLOCK_SIZE] = { 16, 12, 14, 14, 18, 24, 49, 72, 11, 12, 13, 17, 22, 35, 64, 92, 10, 14, 16, 22, 37, 55, 78, 95, @@ -92,8 +90,7 @@ static const int16_t DEFAULT_QTABLE[SUBBLOCK_SIZE] = }; /* zig-zag indices */ -static const unsigned int ZIGZAG_TABLE[SUBBLOCK_SIZE] = -{ +static const unsigned int ZIGZAG_TABLE[SUBBLOCK_SIZE] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, @@ -105,8 +102,7 @@ static const unsigned int ZIGZAG_TABLE[SUBBLOCK_SIZE] = }; /* transposition indices */ -static const unsigned int TRANSPOSE_TABLE[SUBBLOCK_SIZE] = -{ +static const unsigned int TRANSPOSE_TABLE[SUBBLOCK_SIZE] = { 0, 8, 16, 24, 32, 40, 48, 56, 1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, 42, 50, 58, @@ -123,18 +119,17 @@ static const unsigned int TRANSPOSE_TABLE[SUBBLOCK_SIZE] = * Cn = alpha * cos(n * PI / 16) (alpha is chosen such as C4 = 1) */ static const float IDCT_C3 = 1.175875602f; static const float IDCT_C6 = 0.541196100f; -static const float IDCT_K[10] = -{ - 0.765366865f, /* C2-C6 */ - -1.847759065f, /* -C2-C6 */ - -0.390180644f, /* C5-C3 */ - -1.961570561f, /* -C5-C3 */ - 1.501321110f, /* C1+C3-C5-C7 */ - 2.053119869f, /* C1+C3-C5+C7 */ - 3.072711027f, /* C1+C3+C5-C7 */ - 0.298631336f, /* -C1+C3+C5-C7 */ - -0.899976223f, /* C7-C3 */ - -2.562915448f /* -C1-C3 */ +static const float IDCT_K[10] = { + 0.765366865f, /* C2-C6 */ + -1.847759065f, /* -C2-C6 */ + -0.390180644f, /* C5-C3 */ + -1.961570561f, /* -C5-C3 */ + 1.501321110f, /* C1+C3-C5-C7 */ + 2.053119869f, /* C1+C3-C5+C7 */ + 3.072711027f, /* C1+C3+C5-C7 */ + 0.298631336f, /* -C1+C3+C5-C7 */ + -0.899976223f, /* C7-C3 */ + -2.562915448f /* -C1-C3 */ }; @@ -143,24 +138,26 @@ static const float IDCT_K[10] = /*************************************************************************** * JPEG decoding ucode found in Japanese exclusive version of Pokemon Stadium. **************************************************************************/ -void jpeg_decode_PS0() +void jpeg_decode_PS0(struct hle_t* hle) { - jpeg_decode_std("PS0", DecodeMacroblock3, EmitYUVTileLine); + jpeg_decode_std(hle, "PS0", RescaleYSubBlock, RescaleUVSubBlock, EmitYUVTileLine); + rsp_break(hle, SP_STATUS_TASKDONE); } /*************************************************************************** * JPEG decoding ucode found in Ocarina of Time, Pokemon Stadium 1 and * Pokemon Stadium 2. **************************************************************************/ -void jpeg_decode_PS() +void jpeg_decode_PS(struct hle_t* hle) { - jpeg_decode_std("PS", DecodeMacroblock2, EmitRGBATileLine); + jpeg_decode_std(hle, "PS", NULL, NULL, EmitRGBATileLine); + rsp_break(hle, SP_STATUS_TASKDONE); } /*************************************************************************** * JPEG decoding ucode found in Ogre Battle and Bottom of the 9th. **************************************************************************/ -void jpeg_decode_OB() +void jpeg_decode_OB(struct hle_t* hle) { int16_t qtable[SUBBLOCK_SIZE]; unsigned int mb; @@ -168,45 +165,43 @@ void jpeg_decode_OB() int32_t y_dc = 0; int32_t u_dc = 0; int32_t v_dc = 0; - - const OSTask_t * const task = get_task(); - uint32_t address = task->data_ptr; - const unsigned int macroblock_count = task->data_size; - const int qscale = task->yield_data_size; + uint32_t address = *dmem_u32(hle, TASK_DATA_PTR); + const unsigned int macroblock_count = *dmem_u32(hle, TASK_DATA_SIZE); + const int qscale = *dmem_u32(hle, TASK_YIELD_DATA_SIZE); - DebugMessage(M64MSG_VERBOSE, "jpeg_decode_OB: *buffer=%x, #MB=%d, qscale=%d", - address, - macroblock_count, - qscale); + HleVerboseMessage(hle->user_defined, + "jpeg_decode_OB: *buffer=%x, #MB=%d, qscale=%d", + address, + macroblock_count, + qscale); - if (qscale != 0) - { + if (qscale != 0) { if (qscale > 0) - { ScaleSubBlock(qtable, DEFAULT_QTABLE, qscale); - } else - { RShiftSubBlock(qtable, DEFAULT_QTABLE, -qscale); - } } - for (mb = 0; mb < macroblock_count; ++mb) - { - int16_t macroblock[6*SUBBLOCK_SIZE]; + for (mb = 0; mb < macroblock_count; ++mb) { + int16_t macroblock[6 * SUBBLOCK_SIZE]; - rdram_read_many_u16((uint16_t*)macroblock, address, 6*SUBBLOCK_SIZE); - DecodeMacroblock1(macroblock, &y_dc, &u_dc, &v_dc, (qscale != 0) ? qtable : NULL); - EmitTilesMode2(EmitYUVTileLine, macroblock, address); + dram_load_u16(hle, (uint16_t *)macroblock, address, 6 * SUBBLOCK_SIZE); + decode_macroblock_ob(macroblock, &y_dc, &u_dc, &v_dc, (qscale != 0) ? qtable : NULL); + EmitTilesMode2(hle, EmitYUVTileLine, macroblock, address); - address += (2*6*SUBBLOCK_SIZE); + address += (2 * 6 * SUBBLOCK_SIZE); } + rsp_break(hle, SP_STATUS_TASKDONE); } /* local functions */ -static void jpeg_decode_std(const char * const version, const std_macroblock_decoder_t decode_mb, const tile_line_emitter_t emit_line) +static void jpeg_decode_std(struct hle_t* hle, + const char *const version, + const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + const tile_line_emitter_t emit_line) { int16_t qtables[3][SUBBLOCK_SIZE]; unsigned int mb; @@ -218,68 +213,59 @@ static void jpeg_decode_std(const char * const version, const std_macroblock_dec uint32_t qtableV_ptr; unsigned int subblock_count; unsigned int macroblock_size; - int16_t *macroblock; - const OSTask_t * const task = get_task(); + /* macroblock contains at most 6 subblocks */ + int16_t macroblock[6 * SUBBLOCK_SIZE]; + uint32_t data_ptr; - if (task->flags & 0x1) - { - DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: task yielding not implemented", version); + if (*dmem_u32(hle, TASK_FLAGS) & 0x1) { + HleWarnMessage(hle->user_defined, + "jpeg_decode_%s: task yielding not implemented", version); return; } - address = rdram_read_u32(task->data_ptr); - macroblock_count = rdram_read_u32(task->data_ptr + 4); - mode = rdram_read_u32(task->data_ptr + 8); - qtableY_ptr = rdram_read_u32(task->data_ptr + 12); - qtableU_ptr = rdram_read_u32(task->data_ptr + 16); - qtableV_ptr = rdram_read_u32(task->data_ptr + 20); + data_ptr = *dmem_u32(hle, TASK_DATA_PTR); + address = *dram_u32(hle, data_ptr); + macroblock_count = *dram_u32(hle, data_ptr + 4); + mode = *dram_u32(hle, data_ptr + 8); + qtableY_ptr = *dram_u32(hle, data_ptr + 12); + qtableU_ptr = *dram_u32(hle, data_ptr + 16); + qtableV_ptr = *dram_u32(hle, data_ptr + 20); - DebugMessage(M64MSG_VERBOSE, "jpeg_decode_%s: *buffer=%x, #MB=%d, mode=%d, *Qy=%x, *Qu=%x, *Qv=%x", - version, - address, - macroblock_count, - mode, - qtableY_ptr, - qtableU_ptr, - qtableV_ptr); + HleVerboseMessage(hle->user_defined, + "jpeg_decode_%s: *buffer=%x, #MB=%d, mode=%d, *Qy=%x, *Qu=%x, *Qv=%x", + version, + address, + macroblock_count, + mode, + qtableY_ptr, + qtableU_ptr, + qtableV_ptr); - if (mode != 0 && mode != 2) - { - DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: invalid mode %d", version, mode); + if (mode != 0 && mode != 2) { + HleWarnMessage(hle->user_defined, + "jpeg_decode_%s: invalid mode %d", version, mode); return; } - + subblock_count = mode + 4; - macroblock_size = 2*subblock_count*SUBBLOCK_SIZE; + macroblock_size = subblock_count * SUBBLOCK_SIZE; - rdram_read_many_u16((uint16_t*)qtables[0], qtableY_ptr, SUBBLOCK_SIZE); - rdram_read_many_u16((uint16_t*)qtables[1], qtableU_ptr, SUBBLOCK_SIZE); - rdram_read_many_u16((uint16_t*)qtables[2], qtableV_ptr, SUBBLOCK_SIZE); + dram_load_u16(hle, (uint16_t *)qtables[0], qtableY_ptr, SUBBLOCK_SIZE); + dram_load_u16(hle, (uint16_t *)qtables[1], qtableU_ptr, SUBBLOCK_SIZE); + dram_load_u16(hle, (uint16_t *)qtables[2], qtableV_ptr, SUBBLOCK_SIZE); - macroblock = malloc(sizeof(*macroblock) * macroblock_size); - if (!macroblock) - { - DebugMessage(M64MSG_WARNING, "jpeg_decode_%s: could not allocate macroblock", version); - return; - } - - for (mb = 0; mb < macroblock_count; ++mb) - { - rdram_read_many_u16((uint16_t*)macroblock, address, macroblock_size >> 1); - decode_mb(macroblock, subblock_count, (const int16_t (*)[SUBBLOCK_SIZE])qtables); + for (mb = 0; mb < macroblock_count; ++mb) { + dram_load_u16(hle, (uint16_t *)macroblock, address, macroblock_size); + decode_macroblock_std(transform_luma, transform_chroma, + macroblock, subblock_count, (const int16_t (*)[SUBBLOCK_SIZE])qtables); if (mode == 0) - { - EmitTilesMode0(emit_line, macroblock, address); - } + EmitTilesMode0(hle, emit_line, macroblock, address); else - { - EmitTilesMode2(emit_line, macroblock, address); - } + EmitTilesMode2(hle, emit_line, macroblock, address); - address += macroblock_size; + address += 2 * macroblock_size; } - free(macroblock); } static uint8_t clamp_u8(int16_t x) @@ -289,28 +275,28 @@ static uint8_t clamp_u8(int16_t x) static int16_t clamp_s12(int16_t x) { - if (x < -0x800) { x = -0x800; } else if (x > 0x7f0) { x = 0x7f0; } - return x; -} - -static int16_t clamp_s16(int32_t x) -{ - if (x > 32767) { x = 32767; } else if (x < -32768) { x = -32768; } + if (x < -0x800) + x = -0x800; + else if (x > 0x7f0) + x = 0x7f0; return x; } static uint16_t clamp_RGBA_component(int16_t x) { - if (x > 0xff0) { x = 0xff0; } else if (x < 0) { x = 0; } + if (x > 0xff0) + x = 0xff0; + else if (x < 0) + x = 0; return (x & 0xf80); } static uint32_t GetUYVY(int16_t y1, int16_t y2, int16_t u, int16_t v) { - return (uint32_t)clamp_u8(u) << 24 - | (uint32_t)clamp_u8(y1) << 16 - | (uint32_t)clamp_u8(v) << 8 - | (uint32_t)clamp_u8(y2); + return (uint32_t)clamp_u8(u) << 24 | + (uint32_t)clamp_u8(y1) << 16 | + (uint32_t)clamp_u8(v) << 8 | + (uint32_t)clamp_u8(y2); } static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v) @@ -319,19 +305,19 @@ static uint16_t GetRGBA(int16_t y, int16_t u, int16_t v) const float fU = (float)u; const float fV = (float)v; - const uint16_t r = clamp_RGBA_component((int16_t)(fY + 1.4025*fV)); - const uint16_t g = clamp_RGBA_component((int16_t)(fY - 0.3443*fU - 0.7144*fV)); - const uint16_t b = clamp_RGBA_component((int16_t)(fY + 1.7729*fU )); + const uint16_t r = clamp_RGBA_component((int16_t)(fY + 1.4025 * fV)); + const uint16_t g = clamp_RGBA_component((int16_t)(fY - 0.3443 * fU - 0.7144 * fV)); + const uint16_t b = clamp_RGBA_component((int16_t)(fY + 1.7729 * fU)); return (r << 4) | (g >> 1) | (b >> 6) | 1; } -static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address) +static void EmitYUVTileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address) { uint32_t uyvy[8]; - const int16_t * const v = u + SUBBLOCK_SIZE; - const int16_t * const y2 = y + SUBBLOCK_SIZE; + const int16_t *const v = u + SUBBLOCK_SIZE; + const int16_t *const y2 = y + SUBBLOCK_SIZE; uyvy[0] = GetUYVY(y[0], y[1], u[0], v[0]); uyvy[1] = GetUYVY(y[2], y[3], u[1], v[1]); @@ -342,15 +328,15 @@ static void EmitYUVTileLine(const int16_t *y, const int16_t *u, uint32_t address uyvy[6] = GetUYVY(y2[4], y2[5], u[6], v[6]); uyvy[7] = GetUYVY(y2[6], y2[7], u[7], v[7]); - rdram_write_many_u32(uyvy, address, 8); + dram_store_u32(hle, uyvy, address, 8); } -static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t address) +static void EmitRGBATileLine(struct hle_t* hle, const int16_t *y, const int16_t *u, uint32_t address) { uint16_t rgba[16]; - const int16_t * const v = u + SUBBLOCK_SIZE; - const int16_t * const y2 = y + SUBBLOCK_SIZE; + const int16_t *const v = u + SUBBLOCK_SIZE; + const int16_t *const y2 = y + SUBBLOCK_SIZE; rgba[0] = GetRGBA(y[0], u[0], v[0]); rgba[1] = GetRGBA(y[1], u[0], v[0]); @@ -369,19 +355,18 @@ static void EmitRGBATileLine(const int16_t *y, const int16_t *u, uint32_t addres rgba[14] = GetRGBA(y2[6], u[7], v[7]); rgba[15] = GetRGBA(y2[7], u[7], v[7]); - rdram_write_many_u16(rgba, address, 16); + dram_store_u16(hle, rgba, address, 16); } -static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) +static void EmitTilesMode0(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) { unsigned int i; unsigned int y_offset = 0; - unsigned int u_offset = 2*SUBBLOCK_SIZE; + unsigned int u_offset = 2 * SUBBLOCK_SIZE; - for (i = 0; i < 8; ++i) - { - emit_line(¯oblock[y_offset], ¯oblock[u_offset], address); + for (i = 0; i < 8; ++i) { + emit_line(hle, ¯oblock[y_offset], ¯oblock[u_offset], address); y_offset += 8; u_offset += 8; @@ -389,95 +374,86 @@ static void EmitTilesMode0(const tile_line_emitter_t emit_line, const int16_t *m } } -static void EmitTilesMode2(const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) +static void EmitTilesMode2(struct hle_t* hle, const tile_line_emitter_t emit_line, const int16_t *macroblock, uint32_t address) { unsigned int i; unsigned int y_offset = 0; - unsigned int u_offset = 4*SUBBLOCK_SIZE; + unsigned int u_offset = 4 * SUBBLOCK_SIZE; - for (i = 0; i < 8; ++i) - { - emit_line(¯oblock[y_offset], ¯oblock[u_offset], address); - emit_line(¯oblock[y_offset + 8], ¯oblock[u_offset], address + 32); + for (i = 0; i < 8; ++i) { + emit_line(hle, ¯oblock[y_offset], ¯oblock[u_offset], address); + emit_line(hle, ¯oblock[y_offset + 8], ¯oblock[u_offset], address + 32); - y_offset += (i == 3) ? SUBBLOCK_SIZE+16 : 16; + y_offset += (i == 3) ? SUBBLOCK_SIZE + 16 : 16; u_offset += 8; address += 64; } } -static void DecodeMacroblock1(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable) +static void decode_macroblock_ob(int16_t *macroblock, int32_t *y_dc, int32_t *u_dc, int32_t *v_dc, const int16_t *qtable) { int sb; - for (sb = 0; sb < 6; ++sb) - { + for (sb = 0; sb < 6; ++sb) { int16_t tmp_sb[SUBBLOCK_SIZE]; /* update DC */ int32_t dc = (int32_t)macroblock[0]; - switch(sb) - { - case 0: case 1: case 2: case 3: - *y_dc += dc; macroblock[0] = *y_dc & 0xffff; break; - case 4: *u_dc += dc; macroblock[0] = *u_dc & 0xffff; break; - case 5: *v_dc += dc; macroblock[0] = *v_dc & 0xffff; break; + switch (sb) { + case 0: + case 1: + case 2: + case 3: + *y_dc += dc; + macroblock[0] = *y_dc & 0xffff; + break; + case 4: + *u_dc += dc; + macroblock[0] = *u_dc & 0xffff; + break; + case 5: + *v_dc += dc; + macroblock[0] = *v_dc & 0xffff; + break; } ZigZagSubBlock(tmp_sb, macroblock); - if (qtable != NULL) { MultSubBlocks(tmp_sb, tmp_sb, qtable, 0); } + if (qtable != NULL) + MultSubBlocks(tmp_sb, tmp_sb, qtable, 0); TransposeSubBlock(macroblock, tmp_sb); InverseDCTSubBlock(macroblock, macroblock); - + macroblock += SUBBLOCK_SIZE; } } -static void DecodeMacroblock2(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]) +static void decode_macroblock_std(const subblock_transform_t transform_luma, + const subblock_transform_t transform_chroma, + int16_t *macroblock, + unsigned int subblock_count, + const int16_t qtables[3][SUBBLOCK_SIZE]) { unsigned int sb; unsigned int q = 0; - for (sb = 0; sb < subblock_count; ++sb) - { + for (sb = 0; sb < subblock_count; ++sb) { int16_t tmp_sb[SUBBLOCK_SIZE]; const int isChromaSubBlock = (subblock_count - sb <= 2); - if (isChromaSubBlock) { ++q; } - - MultSubBlocks(macroblock, macroblock, qtables[q], 4); - ZigZagSubBlock(tmp_sb, macroblock); - InverseDCTSubBlock(macroblock, tmp_sb); - - macroblock += SUBBLOCK_SIZE; - } - -} - -static void DecodeMacroblock3(int16_t *macroblock, unsigned int subblock_count, const int16_t qtables[3][SUBBLOCK_SIZE]) -{ - unsigned int sb; - unsigned int q = 0; - - for (sb = 0; sb < subblock_count; ++sb) - { - int16_t tmp_sb[SUBBLOCK_SIZE]; - const int isChromaSubBlock = (subblock_count - sb <= 2); - - if (isChromaSubBlock) { ++q; } - - MultSubBlocks(macroblock, macroblock, qtables[q], 4); - ZigZagSubBlock(tmp_sb, macroblock); - InverseDCTSubBlock(macroblock, tmp_sb); - if (isChromaSubBlock) - { - RescaleUVSubBlock(macroblock, macroblock); - } - else - { - RescaleYSubBlock(macroblock, macroblock); + ++q; + + MultSubBlocks(macroblock, macroblock, qtables[q], 4); + ZigZagSubBlock(tmp_sb, macroblock); + InverseDCTSubBlock(macroblock, tmp_sb); + + if (isChromaSubBlock) { + if (transform_chroma != NULL) + transform_chroma(macroblock, macroblock); + } else { + if (transform_luma != NULL) + transform_luma(macroblock, macroblock); } macroblock += SUBBLOCK_SIZE; @@ -499,20 +475,17 @@ static void ReorderSubBlock(int16_t *dst, const int16_t *src, const unsigned int unsigned int i; /* source and destination sublocks cannot overlap */ - assert(abs(dst - src) > SUBBLOCK_SIZE); + assert(labs(dst - src) > SUBBLOCK_SIZE); for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = src[table[i]]; - } } static void MultSubBlocks(int16_t *dst, const int16_t *src1, const int16_t *src2, unsigned int shift) { unsigned int i; - for (i = 0; i < SUBBLOCK_SIZE; ++i) - { + for (i = 0; i < SUBBLOCK_SIZE; ++i) { int32_t v = src1[i] * src2[i]; dst[i] = clamp_s16(v) << shift; } @@ -522,8 +495,7 @@ static void ScaleSubBlock(int16_t *dst, const int16_t *src, int16_t scale) { unsigned int i; - for (i = 0; i < SUBBLOCK_SIZE; ++i) - { + for (i = 0; i < SUBBLOCK_SIZE; ++i) { int32_t v = src[i] * scale; dst[i] = clamp_s16(v); } @@ -534,9 +506,7 @@ static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift) unsigned int i; for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = src[i] >> shift; - } } /*************************************************************************** @@ -545,7 +515,7 @@ static void RShiftSubBlock(int16_t *dst, const int16_t *src, unsigned int shift) * Implementation based on Wikipedia : * http://fr.wikipedia.org/wiki/Transform%C3%A9e_en_cosinus_discr%C3%A8te **************************************************************************/ -static void InverseDCT1D(const float * const x, float *dst, unsigned int stride) +static void InverseDCT1D(const float *const x, float *dst, unsigned int stride) { float e[4]; float f[4]; @@ -560,22 +530,29 @@ static void InverseDCT1D(const float * const x, float *dst, unsigned int stride) f[0] = x[0] + x[4]; f[1] = x[0] - x[4]; - f[2] = x26 + IDCT_K[0]*x[2]; - f[3] = x26 + IDCT_K[1]*x[6]; + f[2] = x26 + IDCT_K[0] * x[2]; + f[3] = x26 + IDCT_K[1] * x[6]; - e[0] = x1357 + x15 + IDCT_K[4]*x[1] + x17; - e[1] = x1357 + x37 + IDCT_K[6]*x[3] + x35; - e[2] = x1357 + x15 + IDCT_K[5]*x[5] + x35; - e[3] = x1357 + x37 + IDCT_K[7]*x[7] + x17; + e[0] = x1357 + x15 + IDCT_K[4] * x[1] + x17; + e[1] = x1357 + x37 + IDCT_K[6] * x[3] + x35; + e[2] = x1357 + x15 + IDCT_K[5] * x[5] + x35; + e[3] = x1357 + x37 + IDCT_K[7] * x[7] + x17; - *dst = f[0] + f[2] + e[0]; dst += stride; - *dst = f[1] + f[3] + e[1]; dst += stride; - *dst = f[1] - f[3] + e[2]; dst += stride; - *dst = f[0] - f[2] + e[3]; dst += stride; - *dst = f[0] - f[2] - e[3]; dst += stride; - *dst = f[1] - f[3] - e[2]; dst += stride; - *dst = f[1] + f[3] - e[1]; dst += stride; - *dst = f[0] + f[2] - e[0]; dst += stride; + *dst = f[0] + f[2] + e[0]; + dst += stride; + *dst = f[1] + f[3] + e[1]; + dst += stride; + *dst = f[1] - f[3] + e[2]; + dst += stride; + *dst = f[0] - f[2] + e[3]; + dst += stride; + *dst = f[0] - f[2] - e[3]; + dst += stride; + *dst = f[1] - f[3] - e[2]; + dst += stride; + *dst = f[1] + f[3] - e[1]; + dst += stride; + *dst = f[0] + f[2] - e[0]; } static void InverseDCTSubBlock(int16_t *dst, const int16_t *src) @@ -585,26 +562,20 @@ static void InverseDCTSubBlock(int16_t *dst, const int16_t *src) unsigned int i, j; /* idct 1d on rows (+transposition) */ - for (i = 0; i < 8; ++i) - { + for (i = 0; i < 8; ++i) { for (j = 0; j < 8; ++j) - { - x[j] = (float)src[i*8+j]; - } + x[j] = (float)src[i * 8 + j]; InverseDCT1D(x, &block[i], 8); } /* idct 1d on columns (thanks to previous transposition) */ - for (i = 0; i < 8; ++i) - { - InverseDCT1D(&block[i*8], x, 1); + for (i = 0; i < 8; ++i) { + InverseDCT1D(&block[i * 8], x, 1); /* C4 = 1 normalization implies a division by 8 */ for (j = 0; j < 8; ++j) - { - dst[i+j*8] = (int16_t)x[j] >> 3; - } + dst[i + j * 8] = (int16_t)x[j] >> 3; } } @@ -613,9 +584,7 @@ static void RescaleYSubBlock(int16_t *dst, const int16_t *src) unsigned int i; for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = (((uint32_t)(clamp_s12(src[i]) + 0x800) * 0xdb0) >> 16) + 0x10; - } } static void RescaleUVSubBlock(int16_t *dst, const int16_t *src) @@ -623,61 +592,6 @@ static void RescaleUVSubBlock(int16_t *dst, const int16_t *src) unsigned int i; for (i = 0; i < SUBBLOCK_SIZE; ++i) - { dst[i] = (((int)clamp_s12(src[i]) * 0xe00) >> 16) + 0x80; - } -} - - - -/* FIXME: assume presence of expansion pack */ -#define MEMMASK 0x7fffff - -static void rdram_read_many_u16(uint16_t *dst, uint32_t address, unsigned int count) -{ - while (count != 0) - { - uint16_t s = rsp.RDRAM[((address++)^S8) & MEMMASK]; - s <<= 8; - s |= rsp.RDRAM[((address++)^S8) & MEMMASK]; - - *(dst++) = s; - - --count; - } -} - -static void rdram_write_many_u16(const uint16_t *src, uint32_t address, unsigned int count) -{ - while (count != 0) - { - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 8); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*(src++) & 0xff); - - --count; - } -} - -static uint32_t rdram_read_u32(uint32_t address) -{ - uint32_t r = rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8; - r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8; - r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; r <<= 8; - r |= rsp.RDRAM[((address++) ^ S8) & MEMMASK]; - - return r; -} - -static void rdram_write_many_u32(const uint32_t *src, uint32_t address, unsigned int count) -{ - while (count != 0) - { - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 24); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 16); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*src >> 8); - rsp.RDRAM[((address++)^S8) & MEMMASK] = (uint8_t)(*(src++) & 0xff); - - --count; - } } diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/main.c b/libmupen64plus/mupen64plus-rsp-hle/src/main.c deleted file mode 100644 index ff6525aefa..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/main.c +++ /dev/null @@ -1,476 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - main.c * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2012 Bobby Smiles * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include -#include -#include - -#define M64P_PLUGIN_PROTOTYPES 1 -#include "m64p_types.h" -#include "m64p_common.h" -#include "m64p_plugin.h" -#include "hle.h" -#include "alist.h" -#include "cicx105.h" -#include "jpeg.h" - -#define min(a,b) (((a) < (b)) ? (a) : (b)) - -/* some rsp status flags */ -#define RSP_STATUS_HALT 0x1 -#define RSP_STATUS_BROKE 0x2 -#define RSP_STATUS_INTR_ON_BREAK 0x40 -#define RSP_STATUS_TASKDONE 0x200 - -/* some rdp status flags */ -#define DP_STATUS_FREEZE 0x2 - -/* some mips interface interrupt flags */ -#define MI_INTR_SP 0x1 - - -/* helper functions prototypes */ -static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size); -static void dump_binary(const char * const filename, const unsigned char * const bytes, - unsigned int size); -static void dump_task(const char * const filename, const OSTask_t * const task); - -static void handle_unknown_task(unsigned int sum); -static void handle_unknown_non_task(unsigned int sum); - -/* global variables */ -RSP_INFO rsp; - -/* local variables */ -static const int FORWARD_AUDIO = 0, FORWARD_GFX = 1; -static void (*l_DebugCallback)(void *, int, const char *) = NULL; -static void *l_DebugCallContext = NULL; -static int l_PluginInit = 0; - -/* local functions */ - - -/** - * Try to figure if the RSP was launched using osSpTask* functions - * and not run directly (in which case DMEM[0xfc0-0xfff] is meaningless). - * - * Previously, the ucode_size field was used to determine this, - * but it is not robust enough (hi Pokemon Stadium !) because games could write anything - * in this field : most ucode_boot discard the value and just use 0xf7f anyway. - * - * Using ucode_boot_size should be more robust in this regard. - **/ -static int is_task() -{ - return (get_task()->ucode_boot_size <= 0x1000); -} - -static void rsp_break(unsigned int setbits) -{ - *rsp.SP_STATUS_REG |= setbits | RSP_STATUS_BROKE | RSP_STATUS_HALT; - - if ((*rsp.SP_STATUS_REG & RSP_STATUS_INTR_ON_BREAK)) - { - *rsp.MI_INTR_REG |= MI_INTR_SP; - rsp.CheckInterrupts(); - } -} - -static void forward_gfx_task() -{ - if (rsp.ProcessDlistList != NULL) - { - rsp.ProcessDlistList(); - *rsp.DPC_STATUS_REG &= ~DP_STATUS_FREEZE; - } -} - -static void forward_audio_task() -{ - if (rsp.ProcessAlistList != NULL) - { - rsp.ProcessAlistList(); - } -} - -static void show_cfb() -{ - if (rsp.ShowCFB != NULL) - { - rsp.ShowCFB(); - } -} - -static int try_fast_audio_dispatching() -{ - /* identify audio ucode by using the content of ucode_data */ - const OSTask_t * const task = get_task(); - const unsigned char * const udata_ptr = rsp.RDRAM + task->ucode_data; - - if (*(unsigned int*)(udata_ptr + 0) == 0x00000001) - { - if (*(unsigned int*)(udata_ptr + 0x30) == 0xf0000f00) - { - /** - * Many games including: - * Super Mario 64, Diddy Kong Racing, BlastCorp, GoldenEye, ... (most common) - **/ - alist_process_ABI1(); return 1; - } - else - { - /** - * Mario Kart / Wave Race, - * LylatWars, - * FZeroX, - * Yoshi Story, - * 1080 Snowboarding, - * Zelda Ocarina of Time, - * Zelda Majoras Mask / Pokemon Stadium 2, - * Animal Crossing - * - * FIXME: in fact, all these games do not share the same ABI. - * That's the reason of the workaround in ucode2.cpp with isZeldaABI and isMKABI - **/ - alist_process_ABI2(); return 1; - } - } - else - { - if (*(unsigned int*)(udata_ptr + 0x10) == 0x00000001) - { - /** - * Musyx ucode found in following games: - * RogueSquadron, ResidentEvil2, SnowCrossPolaris, TheWorldIsNotEnough, - * RugratsInParis, NBAShowTime, HydroThunder, Tarzan, - * GauntletLegend, Rush2049, IndianaJones, BattleForNaboo - * TODO: implement ucode - **/ - DebugMessage(M64MSG_WARNING, "MusyX ucode not implemented."); - /* return 1; */ - } - else - { - /** - * Many games including: - * Pokemon Stadium, Banjo Kazooie, Donkey Kong, Banjo Tooie, Jet Force Gemini, - * Mickey SpeedWay USA, Perfect Dark, Conker Bad Fur Day ... - **/ - alist_process_ABI3(); return 1; - } - } - - return 0; -} - -static int try_fast_task_dispatching() -{ - /* identify task ucode by its type */ - const OSTask_t * const task = get_task(); - - switch (task->type) - { - case 1: if (FORWARD_GFX) { forward_gfx_task(); return 1; } break; - - case 2: - if (FORWARD_AUDIO) { forward_audio_task(); return 1; } - else if (try_fast_audio_dispatching()) { return 1; } - break; - - case 7: show_cfb(); return 1; - } - - return 0; -} - -static void normal_task_dispatching() -{ - const OSTask_t * const task = get_task(); - const unsigned int sum = - sum_bytes(rsp.RDRAM + task->ucode, min(task->ucode_size, 0xf80) >> 1); - - switch (sum) - { - /* StoreVe12: found in Zelda Ocarina of Time [misleading task->type == 4] */ - case 0x278: /* Nothing to emulate */ return; - - /* GFX: Twintris [misleading task->type == 0] */ - case 0x212ee: - if (FORWARD_GFX) { forward_gfx_task(); return; } - break; - - /* JPEG: found in Pokemon Stadium J */ - case 0x2c85a: jpeg_decode_PS0(); return; - - /* JPEG: found in Zelda Ocarina of Time, Pokemon Stadium 1, Pokemon Stadium 2 */ - case 0x2caa6: jpeg_decode_PS(); return; - - /* JPEG: found in Ogre Battle, Bottom of the 9th */ - case 0x130de: jpeg_decode_OB(); return; - } - - handle_unknown_task(sum); -} - -static void non_task_dispatching() -{ - const unsigned int sum = sum_bytes(rsp.IMEM, 0x1000 >> 1); - - switch(sum) - { - /* CIC x105 ucode (used during boot of CIC x105 games) */ - case 0x9e2: /* CIC 6105 */ - case 0x9f2: /* CIC 7105 */ - cicx105_ucode(); return; - } - - handle_unknown_non_task(sum); -} - -static void handle_unknown_task(unsigned int sum) -{ - char filename[256]; - const OSTask_t * const task = get_task(); - - DebugMessage(M64MSG_WARNING, "unknown OSTask: sum %x PC:%x", sum, *rsp.SP_PC_REG); - - sprintf(&filename[0], "task_%x.log", sum); - dump_task(filename, task); - - // dump ucode_boot - sprintf(&filename[0], "ucode_boot_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->ucode_boot & 0x7fffff), task->ucode_boot_size); - - // dump ucode - if (task->ucode != 0) - { - sprintf(&filename[0], "ucode_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->ucode & 0x7fffff), 0xf80); - } - - // dump ucode_data - if (task->ucode_data != 0) - { - sprintf(&filename[0], "ucode_data_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->ucode_data & 0x7fffff), task->ucode_data_size); - } - - // dump data - if (task->data_ptr != 0) - { - sprintf(&filename[0], "data_%x.bin", sum); - dump_binary(filename, rsp.RDRAM + (task->data_ptr & 0x7fffff), task->data_size); - } -} - -static void handle_unknown_non_task(unsigned int sum) -{ - char filename[256]; - - DebugMessage(M64MSG_WARNING, "unknown RSP code: sum: %x PC:%x", sum, *rsp.SP_PC_REG); - - // dump IMEM & DMEM for further analysis - sprintf(&filename[0], "imem_%x.bin", sum); - dump_binary(filename, rsp.IMEM, 0x1000); - - sprintf(&filename[0], "dmem_%x.bin", sum); - dump_binary(filename, rsp.DMEM, 0x1000); -} - - -/* Global functions */ -void DebugMessage(int level, const char *message, ...) -{ - char msgbuf[1024]; - va_list args; - - if (l_DebugCallback == NULL) - return; - - va_start(args, message); - vsprintf(msgbuf, message, args); - - (*l_DebugCallback)(l_DebugCallContext, level, msgbuf); - - va_end(args); -} - -/* DLL-exported functions */ -EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context, - void (*DebugCallback)(void *, int, const char *)) -{ - if (l_PluginInit) - return M64ERR_ALREADY_INIT; - - /* first thing is to set the callback function for debug info */ - l_DebugCallback = DebugCallback; - l_DebugCallContext = Context; - - /* this plugin doesn't use any Core library functions (ex for Configuration), so no need to keep the CoreLibHandle */ - - l_PluginInit = 1; - return M64ERR_SUCCESS; -} - -EXPORT m64p_error CALL PluginShutdown(void) -{ - if (!l_PluginInit) - return M64ERR_NOT_INIT; - - /* reset some local variable */ - l_DebugCallback = NULL; - l_DebugCallContext = NULL; - - l_PluginInit = 0; - return M64ERR_SUCCESS; -} - -EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities) -{ - /* set version info */ - if (PluginType != NULL) - *PluginType = M64PLUGIN_RSP; - - if (PluginVersion != NULL) - *PluginVersion = RSP_HLE_VERSION; - - if (APIVersion != NULL) - *APIVersion = RSP_PLUGIN_API_VERSION; - - if (PluginNamePtr != NULL) - *PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin"; - - if (Capabilities != NULL) - { - *Capabilities = 0; - } - - return M64ERR_SUCCESS; -} - -EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles) -{ - if (is_task()) - { - if (!try_fast_task_dispatching()) { normal_task_dispatching(); } - rsp_break(RSP_STATUS_TASKDONE); - } - else - { - non_task_dispatching(); - rsp_break(0); - } - - return Cycles; -} - -EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, unsigned int *CycleCount) -{ - rsp = Rsp_Info; -} - -EXPORT void CALL RomClosed(void) -{ - memset(rsp.DMEM, 0, 0x1000); - memset(rsp.IMEM, 0, 0x1000); - - init_ucode2(); -} - - -/* local helper functions */ -static unsigned int sum_bytes(const unsigned char *bytes, unsigned int size) -{ - unsigned int sum = 0; - const unsigned char * const bytes_end = bytes + size; - - while (bytes != bytes_end) - sum += *bytes++; - - return sum; -} - - -static void dump_binary(const char * const filename, const unsigned char * const bytes, - unsigned int size) -{ - FILE *f; - - // if file already exists, do nothing - f = fopen(filename, "r"); - if (f == NULL) - { - // else we write bytes to the file - f= fopen(filename, "wb"); - if (f != NULL) { - if (fwrite(bytes, 1, size, f) != size) - { - DebugMessage(M64MSG_ERROR, "Writing error on %s", filename); - } - fclose(f); - } - else - { - DebugMessage(M64MSG_ERROR, "Couldn't open %s for writing !", filename); - } - } - else - { - fclose(f); - } -} - -static void dump_task(const char * const filename, const OSTask_t * const task) -{ - FILE *f; - - f = fopen(filename, "r"); - if (f == NULL) - { - f = fopen(filename, "w"); - fprintf(f, - "type = %d\n" - "flags = %d\n" - "ucode_boot = %#08x size = %#x\n" - "ucode = %#08x size = %#x\n" - "ucode_data = %#08x size = %#x\n" - "dram_stack = %#08x size = %#x\n" - "output_buff = %#08x *size = %#x\n" - "data = %#08x size = %#x\n" - "yield_data = %#08x size = %#x\n", - task->type, task->flags, - task->ucode_boot, task->ucode_boot_size, - task->ucode, task->ucode_size, - task->ucode_data, task->ucode_data_size, - task->dram_stack, task->dram_stack_size, - task->output_buff, task->output_buff_size, - task->data_ptr, task->data_size, - task->yield_data_ptr, task->yield_data_size); - fclose(f); - } - else - { - fclose(f); - } -} - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/memory.c b/libmupen64plus/mupen64plus-rsp-hle/src/memory.c new file mode 100644 index 0000000000..7d5066bfa1 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/memory.c @@ -0,0 +1,74 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - memory.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include + +#include "memory.h" + +/* Global functions */ +void load_u8(uint8_t* dst, const unsigned char* buffer, unsigned address, size_t count) +{ + while (count != 0) { + *(dst++) = *u8(buffer, address); + address += 1; + --count; + } +} + +void load_u16(uint16_t* dst, const unsigned char* buffer, unsigned address, size_t count) +{ + while (count != 0) { + *(dst++) = *u16(buffer, address); + address += 2; + --count; + } +} + +void load_u32(uint32_t* dst, const unsigned char* buffer, unsigned address, size_t count) +{ + /* Optimization for uint32_t */ + memcpy(dst, u32(buffer, address), count * sizeof(uint32_t)); +} + +void store_u8(unsigned char* buffer, unsigned address, const uint8_t* src, size_t count) +{ + while (count != 0) { + *u8(buffer, address) = *(src++); + address += 1; + --count; + } +} + +void store_u16(unsigned char* buffer, unsigned address, const uint16_t* src, size_t count) +{ + while (count != 0) { + *u16(buffer, address) = *(src++); + address += 2; + --count; + } +} + +void store_u32(unsigned char* buffer, unsigned address, const uint32_t* src, size_t count) +{ + /* Optimization for uint32_t */ + memcpy(u32(buffer, address), src, count * sizeof(uint32_t)); +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/memory.h b/libmupen64plus/mupen64plus-rsp-hle/src/memory.h new file mode 100644 index 0000000000..9c85104838 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/memory.h @@ -0,0 +1,185 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - memory.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef MEMORY_H +#define MEMORY_H + +#include +#include +#include + +#include "common.h" +#include "hle_internal.h" + +#ifdef M64P_BIG_ENDIAN +#define S 0 +#define S16 0 +#define S8 0 +#else +#define S 1 +#define S16 2 +#define S8 3 +#endif + +enum { + TASK_TYPE = 0xfc0, + TASK_FLAGS = 0xfc4, + TASK_UCODE_BOOT = 0xfc8, + TASK_UCODE_BOOT_SIZE = 0xfcc, + TASK_UCODE = 0xfd0, + TASK_UCODE_SIZE = 0xfd4, + TASK_UCODE_DATA = 0xfd8, + TASK_UCODE_DATA_SIZE = 0xfdc, + TASK_DRAM_STACK = 0xfe0, + TASK_DRAM_STACK_SIZE = 0xfe4, + TASK_OUTPUT_BUFF = 0xfe8, + TASK_OUTPUT_BUFF_SIZE = 0xfec, + TASK_DATA_PTR = 0xff0, + TASK_DATA_SIZE = 0xff4, + TASK_YIELD_DATA_PTR = 0xff8, + TASK_YIELD_DATA_SIZE = 0xffc +}; + +static inline unsigned int align(unsigned int x, unsigned amount) +{ + --amount; + return (x + amount) & ~amount; +} + +static inline uint8_t* u8(const unsigned char* buffer, unsigned address) +{ + return (uint8_t*)(buffer + (address ^ S8)); +} + +static inline uint16_t* u16(const unsigned char* buffer, unsigned address) +{ + assert((address & 1) == 0); + return (uint16_t*)(buffer + (address ^ S16)); +} + +static inline uint32_t* u32(const unsigned char* buffer, unsigned address) +{ + assert((address & 3) == 0); + return (uint32_t*)(buffer + address); +} + +void load_u8 (uint8_t* dst, const unsigned char* buffer, unsigned address, size_t count); +void load_u16(uint16_t* dst, const unsigned char* buffer, unsigned address, size_t count); +void load_u32(uint32_t* dst, const unsigned char* buffer, unsigned address, size_t count); +void store_u8 (unsigned char* buffer, unsigned address, const uint8_t* src, size_t count); +void store_u16(unsigned char* buffer, unsigned address, const uint16_t* src, size_t count); +void store_u32(unsigned char* buffer, unsigned address, const uint32_t* src, size_t count); + + +/* convenient functions for DMEM access */ +static inline uint8_t* dmem_u8(struct hle_t* hle, uint16_t address) +{ + return u8(hle->dmem, address & 0xfff); +} + +static inline uint16_t* dmem_u16(struct hle_t* hle, uint16_t address) +{ + return u16(hle->dmem, address & 0xfff); +} + +static inline uint32_t* dmem_u32(struct hle_t* hle, uint16_t address) +{ + return u32(hle->dmem, address & 0xfff); +} + +static inline void dmem_load_u8(struct hle_t* hle, uint8_t* dst, uint16_t address, size_t count) +{ + load_u8(dst, hle->dmem, address & 0xfff, count); +} + +static inline void dmem_load_u16(struct hle_t* hle, uint16_t* dst, uint16_t address, size_t count) +{ + load_u16(dst, hle->dmem, address & 0xfff, count); +} + +static inline void dmem_load_u32(struct hle_t* hle, uint32_t* dst, uint16_t address, size_t count) +{ + load_u32(dst, hle->dmem, address & 0xfff, count); +} + +static inline void dmem_store_u8(struct hle_t* hle, const uint8_t* src, uint16_t address, size_t count) +{ + store_u8(hle->dmem, address & 0xfff, src, count); +} + +static inline void dmem_store_u16(struct hle_t* hle, const uint16_t* src, uint16_t address, size_t count) +{ + store_u16(hle->dmem, address & 0xfff, src, count); +} + +static inline void dmem_store_u32(struct hle_t* hle, const uint32_t* src, uint16_t address, size_t count) +{ + store_u32(hle->dmem, address & 0xfff, src, count); +} + +/* convenient functions DRAM access */ +static inline uint8_t* dram_u8(struct hle_t* hle, uint32_t address) +{ + return u8(hle->dram, address & 0xffffff); +} + +static inline uint16_t* dram_u16(struct hle_t* hle, uint32_t address) +{ + return u16(hle->dram, address & 0xffffff); +} + +static inline uint32_t* dram_u32(struct hle_t* hle, uint32_t address) +{ + return u32(hle->dram, address & 0xffffff); +} + +static inline void dram_load_u8(struct hle_t* hle, uint8_t* dst, uint32_t address, size_t count) +{ + load_u8(dst, hle->dram, address & 0xffffff, count); +} + +static inline void dram_load_u16(struct hle_t* hle, uint16_t* dst, uint32_t address, size_t count) +{ + load_u16(dst, hle->dram, address & 0xffffff, count); +} + +static inline void dram_load_u32(struct hle_t* hle, uint32_t* dst, uint32_t address, size_t count) +{ + load_u32(dst, hle->dram, address & 0xffffff, count); +} + +static inline void dram_store_u8(struct hle_t* hle, const uint8_t* src, uint32_t address, size_t count) +{ + store_u8(hle->dram, address & 0xffffff, src, count); +} + +static inline void dram_store_u16(struct hle_t* hle, const uint16_t* src, uint32_t address, size_t count) +{ + store_u16(hle->dram, address & 0xffffff, src, count); +} + +static inline void dram_store_u32(struct hle_t* hle, const uint32_t* src, uint32_t address, size_t count) +{ + store_u32(hle->dram, address & 0xffffff, src, count); +} + +#endif + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/mp3.c b/libmupen64plus/mupen64plus-rsp-hle/src/mp3.c new file mode 100644 index 0000000000..4e8ea14afd --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/mp3.c @@ -0,0 +1,684 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - mp3.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include + +#include "arithmetics.h" +#include "hle_internal.h" +#include "memory.h" + +static void InnerLoop(struct hle_t* hle, + uint32_t outPtr, uint32_t inPtr, + uint32_t t6, uint32_t t5, uint32_t t4); + +static const uint16_t DeWindowLUT [0x420] = { + 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, + 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, + 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, + 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, + 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, + 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, + 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, + 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, + 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, + 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, + 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, + 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, + 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, + 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, + 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, + 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, + 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, + 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, + 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, + 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, + 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, + 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, + 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, + 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, + 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, + 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, + 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, + 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, + 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, + 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, + 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, + 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, + 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, + 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, + 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, + 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, + 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, + 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, + 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, + 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, + 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, + 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, + 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, + 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, + 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, + 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, + 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, + 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, + 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, + 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, + 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, + 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, + 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, + 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, + 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, + 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, + 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, + 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, + 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, + 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, + 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, + 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, + 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, + 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, + 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, + 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, + 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, + 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, + 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, + 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, + 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, + 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, + 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, + 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, + 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, + 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, + 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, + 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, + 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, + 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, + 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, + 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0003, + 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, + 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0004, + 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, + 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, + 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, + 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, + 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, + 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0004, + 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, + 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0005, + 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, + 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0005, + 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, + 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0006, + 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, + 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, + 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, + 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, + 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, + 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0006, + 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, + 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0007, + 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, + 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, + 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, + 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, + 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, + 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0007, + 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, + 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0008, + 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, + 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0008, + 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, + 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0009, + 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, + 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x0009, + 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, + 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x000A, + 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, + 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000A, + 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, + 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000B, + 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, + 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000B, + 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, + 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000D, + 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, + 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x000D, + 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, + 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x0000 +}; + +static void MP3AB0(int32_t* v) +{ + /* Part 2 - 100% Accurate */ + static const uint16_t LUT2[8] = { + 0xFEC4, 0xF4FA, 0xC5E4, 0xE1C4, + 0x1916, 0x4A50, 0xA268, 0x78AE + }; + static const uint16_t LUT3[4] = { 0xFB14, 0xD4DC, 0x31F2, 0x8E3A }; + int i; + + for (i = 0; i < 8; i++) { + v[16 + i] = v[0 + i] + v[8 + i]; + v[24 + i] = ((v[0 + i] - v[8 + i]) * LUT2[i]) >> 0x10; + } + + /* Part 3: 4-wide butterflies */ + + for (i = 0; i < 4; i++) { + v[0 + i] = v[16 + i] + v[20 + i]; + v[4 + i] = ((v[16 + i] - v[20 + i]) * LUT3[i]) >> 0x10; + + v[8 + i] = v[24 + i] + v[28 + i]; + v[12 + i] = ((v[24 + i] - v[28 + i]) * LUT3[i]) >> 0x10; + } + + /* Part 4: 2-wide butterflies - 100% Accurate */ + + for (i = 0; i < 16; i += 4) { + v[16 + i] = v[0 + i] + v[2 + i]; + v[18 + i] = ((v[0 + i] - v[2 + i]) * 0xEC84) >> 0x10; + + v[17 + i] = v[1 + i] + v[3 + i]; + v[19 + i] = ((v[1 + i] - v[3 + i]) * 0x61F8) >> 0x10; + } +} + +void mp3_task(struct hle_t* hle, unsigned int index, uint32_t address) +{ + uint32_t inPtr, outPtr; + uint32_t t6;/* = 0x08A0; - I think these are temporary storage buffers */ + uint32_t t5;/* = 0x0AC0; */ + uint32_t t4;/* = (w1 & 0x1E); */ + + /* Initialization Code */ + uint32_t readPtr; /* s5 */ + uint32_t writePtr; /* s6 */ + uint32_t tmp; + int cnt, cnt2; + + /* I think these are temporary storage buffers */ + t6 = 0x08A0; + t5 = 0x0AC0; + t4 = index; + + writePtr = readPtr = address; + /* Just do that for efficiency... may remove and use directly later anyway */ + memcpy(hle->mp3_buffer + 0xCE8, hle->dram + readPtr, 8); + /* This must be a header byte or whatnot */ + readPtr += 8; + + for (cnt = 0; cnt < 0x480; cnt += 0x180) { + /* DMA: 0xCF0 <- RDRAM[s5] : 0x180 */ + memcpy(hle->mp3_buffer + 0xCF0, hle->dram + readPtr, 0x180); + inPtr = 0xCF0; /* s7 */ + outPtr = 0xE70; /* s3 */ +/* --------------- Inner Loop Start -------------------- */ + for (cnt2 = 0; cnt2 < 0x180; cnt2 += 0x40) { + t6 &= 0xFFE0; + t5 &= 0xFFE0; + t6 |= t4; + t5 |= t4; + InnerLoop(hle, outPtr, inPtr, t6, t5, t4); + t4 = (t4 - 2) & 0x1E; + tmp = t6; + t6 = t5; + t5 = tmp; + inPtr += 0x40; + outPtr += 0x40; + } +/* --------------- Inner Loop End -------------------- */ + memcpy(hle->dram + writePtr, hle->mp3_buffer + 0xe70, 0x180); + writePtr += 0x180; + readPtr += 0x180; + } +} + +static void InnerLoop(struct hle_t* hle, + uint32_t outPtr, uint32_t inPtr, + uint32_t t6, uint32_t t5, uint32_t t4) +{ + /* Part 1: 100% Accurate */ + + /* 0, 1, 3, 2, 7, 6, 4, 5, 7, 6, 4, 5, 0, 1, 3, 2 */ + static const uint16_t LUT6[16] = { + 0xFFB2, 0xFD3A, 0xF10A, 0xF854, + 0xBDAE, 0xCDA0, 0xE76C, 0xDB94, + 0x1920, 0x4B20, 0xAC7C, 0x7C68, + 0xABEC, 0x9880, 0xDAE8, 0x839C + }; + int i; + uint32_t t0; + uint32_t t1; + uint32_t t2; + uint32_t t3; + int32_t v2 = 0, v4 = 0, v6 = 0, v8 = 0; + uint32_t offset; + uint32_t addptr; + int x; + int32_t mult6; + int32_t mult4; + int tmp; + int32_t hi0; + int32_t hi1; + int32_t vt; + int32_t v[32]; + + v[0] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x00 ^ S16)); + v[31] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3E ^ S16)); + v[0] += v[31]; + v[1] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x02 ^ S16)); + v[30] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3C ^ S16)); + v[1] += v[30]; + v[2] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x06 ^ S16)); + v[28] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x38 ^ S16)); + v[2] += v[28]; + v[3] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x04 ^ S16)); + v[29] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3A ^ S16)); + v[3] += v[29]; + + v[4] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0E ^ S16)); + v[24] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x30 ^ S16)); + v[4] += v[24]; + v[5] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0C ^ S16)); + v[25] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x32 ^ S16)); + v[5] += v[25]; + v[6] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x08 ^ S16)); + v[27] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x36 ^ S16)); + v[6] += v[27]; + v[7] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0A ^ S16)); + v[26] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x34 ^ S16)); + v[7] += v[26]; + + v[8] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1E ^ S16)); + v[16] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x20 ^ S16)); + v[8] += v[16]; + v[9] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1C ^ S16)); + v[17] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x22 ^ S16)); + v[9] += v[17]; + v[10] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x18 ^ S16)); + v[19] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x26 ^ S16)); + v[10] += v[19]; + v[11] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1A ^ S16)); + v[18] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x24 ^ S16)); + v[11] += v[18]; + + v[12] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x10 ^ S16)); + v[23] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2E ^ S16)); + v[12] += v[23]; + v[13] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x12 ^ S16)); + v[22] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2C ^ S16)); + v[13] += v[22]; + v[14] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x16 ^ S16)); + v[20] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x28 ^ S16)); + v[14] += v[20]; + v[15] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x14 ^ S16)); + v[21] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2A ^ S16)); + v[15] += v[21]; + + /* Part 2-4 */ + + MP3AB0(v); + + /* Part 5 - 1-Wide Butterflies - 100% Accurate but need SSVs!!! */ + + t0 = t6 + 0x100; + t1 = t6 + 0x200; + t2 = t5 + 0x100; + t3 = t5 + 0x200; + + /* 0x13A8 */ + v[1] = 0; + v[11] = ((v[16] - v[17]) * 0xB504) >> 0x10; + + v[16] = -v[16] - v[17]; + v[2] = v[18] + v[19]; + /* ** Store v[11] -> (T6 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x0))) = (short)v[11]; + + + v[11] = -v[11]; + /* ** Store v[16] -> (T3 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0x0))) = (short)v[16]; + /* ** Store v[11] -> (T5 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x0))) = (short)v[11]; + /* 0x13E8 - Verified.... */ + v[2] = -v[2]; + /* ** Store v[2] -> (T2 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x0))) = (short)v[2]; + v[3] = (((v[18] - v[19]) * 0x16A09) >> 0x10) + v[2]; + /* ** Store v[3] -> (T0 + 0)** */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x0))) = (short)v[3]; + /* 0x1400 - Verified */ + v[4] = -v[20] - v[21]; + v[6] = v[22] + v[23]; + v[5] = ((v[20] - v[21]) * 0x16A09) >> 0x10; + /* ** Store v[4] -> (T3 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFF80))) = (short)v[4]; + v[7] = ((v[22] - v[23]) * 0x2D413) >> 0x10; + v[5] = v[5] - v[4]; + v[7] = v[7] - v[5]; + v[6] = v[6] + v[6]; + v[5] = v[5] - v[6]; + v[4] = -v[4] - v[6]; + /* *** Store v[7] -> (T1 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFF80))) = (short)v[7]; + /* *** Store v[4] -> (T2 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFF80))) = (short)v[4]; + /* *** Store v[5] -> (T0 + 0xFF80) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFF80))) = (short)v[5]; + v[8] = v[24] + v[25]; + + + v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; + v[2] = v[8] + v[9]; + v[11] = ((v[26] - v[27]) * 0x2D413) >> 0x10; + v[13] = ((v[28] - v[29]) * 0x2D413) >> 0x10; + + v[10] = v[26] + v[27]; + v[10] = v[10] + v[10]; + v[12] = v[28] + v[29]; + v[12] = v[12] + v[12]; + v[14] = v[30] + v[31]; + v[3] = v[8] + v[10]; + v[14] = v[14] + v[14]; + v[13] = (v[13] - v[2]) + v[12]; + v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - (v[11] + v[2]); + v[14] = -(v[14] + v[14]) + v[3]; + v[17] = v[13] - v[10]; + v[9] = v[9] + v[14]; + /* ** Store v[9] -> (T6 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x40))) = (short)v[9]; + v[11] = v[11] - v[13]; + /* ** Store v[17] -> (T0 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFFC0))) = (short)v[17]; + v[12] = v[8] - v[12]; + /* ** Store v[11] -> (T0 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x40))) = (short)v[11]; + v[8] = -v[8]; + /* ** Store v[15] -> (T1 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFFC0))) = (short)v[15]; + v[10] = -v[10] - v[12]; + /* ** Store v[12] -> (T2 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x40))) = (short)v[12]; + /* ** Store v[8] -> (T3 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFFC0))) = (short)v[8]; + /* ** Store v[14] -> (T5 + 0x40) */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x40))) = (short)v[14]; + /* ** Store v[10] -> (T2 + 0xFFC0) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFFC0))) = (short)v[10]; + /* 0x14FC - Verified... */ + + /* Part 6 - 100% Accurate */ + + v[0] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x00 ^ S16)); + v[31] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3E ^ S16)); + v[0] -= v[31]; + v[1] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x02 ^ S16)); + v[30] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3C ^ S16)); + v[1] -= v[30]; + v[2] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x06 ^ S16)); + v[28] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x38 ^ S16)); + v[2] -= v[28]; + v[3] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x04 ^ S16)); + v[29] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x3A ^ S16)); + v[3] -= v[29]; + + v[4] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0E ^ S16)); + v[24] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x30 ^ S16)); + v[4] -= v[24]; + v[5] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0C ^ S16)); + v[25] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x32 ^ S16)); + v[5] -= v[25]; + v[6] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x08 ^ S16)); + v[27] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x36 ^ S16)); + v[6] -= v[27]; + v[7] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x0A ^ S16)); + v[26] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x34 ^ S16)); + v[7] -= v[26]; + + v[8] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1E ^ S16)); + v[16] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x20 ^ S16)); + v[8] -= v[16]; + v[9] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1C ^ S16)); + v[17] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x22 ^ S16)); + v[9] -= v[17]; + v[10] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x18 ^ S16)); + v[19] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x26 ^ S16)); + v[10] -= v[19]; + v[11] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x1A ^ S16)); + v[18] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x24 ^ S16)); + v[11] -= v[18]; + + v[12] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x10 ^ S16)); + v[23] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2E ^ S16)); + v[12] -= v[23]; + v[13] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x12 ^ S16)); + v[22] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2C ^ S16)); + v[13] -= v[22]; + v[14] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x16 ^ S16)); + v[20] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x28 ^ S16)); + v[14] -= v[20]; + v[15] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x14 ^ S16)); + v[21] = *(int16_t *)(hle->mp3_buffer + inPtr + (0x2A ^ S16)); + v[15] -= v[21]; + + for (i = 0; i < 16; i++) + v[0 + i] = (v[0 + i] * LUT6[i]) >> 0x10; + v[0] = v[0] + v[0]; + v[1] = v[1] + v[1]; + v[2] = v[2] + v[2]; + v[3] = v[3] + v[3]; + v[4] = v[4] + v[4]; + v[5] = v[5] + v[5]; + v[6] = v[6] + v[6]; + v[7] = v[7] + v[7]; + v[12] = v[12] + v[12]; + v[13] = v[13] + v[13]; + v[15] = v[15] + v[15]; + + MP3AB0(v); + + /* Part 7: - 100% Accurate + SSV - Unoptimized */ + + v[0] = (v[17] + v[16]) >> 1; + v[1] = ((v[17] * (int)((short)0xA57E * 2)) + (v[16] * 0xB504)) >> 0x10; + v[2] = -v[18] - v[19]; + v[3] = ((v[18] - v[19]) * 0x16A09) >> 0x10; + v[4] = v[20] + v[21] + v[0]; + v[5] = (((v[20] - v[21]) * 0x16A09) >> 0x10) + v[1]; + v[6] = (((v[22] + v[23]) << 1) + v[0]) - v[2]; + v[7] = (((v[22] - v[23]) * 0x2D413) >> 0x10) + v[0] + v[1] + v[3]; + /* 0x16A8 */ + /* Save v[0] -> (T3 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFFE0))) = (short) - v[0]; + v[8] = v[24] + v[25]; + v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; + v[10] = ((v[26] + v[27]) << 1) + v[8]; + v[11] = (((v[26] - v[27]) * 0x2D413) >> 0x10) + v[8] + v[9]; + v[12] = v[4] - ((v[28] + v[29]) << 1); + /* ** Store v12 -> (T2 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x20))) = (short)v[12]; + v[13] = (((v[28] - v[29]) * 0x2D413) >> 0x10) - v[12] - v[5]; + v[14] = v[30] + v[31]; + v[14] = v[14] + v[14]; + v[14] = v[14] + v[14]; + v[14] = v[6] - v[14]; + v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - v[7]; + /* Store v14 -> (T5 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x20))) = (short)v[14]; + v[14] = v[14] + v[1]; + /* Store v[14] -> (T6 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x20))) = (short)v[14]; + /* Store v[15] -> (T1 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFFE0))) = (short)v[15]; + v[9] = v[9] + v[10]; + v[1] = v[1] + v[6]; + v[6] = v[10] - v[6]; + v[1] = v[9] - v[1]; + /* Store v[6] -> (T5 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t5 + (short)0x60))) = (short)v[6]; + v[10] = v[10] + v[2]; + v[10] = v[4] - v[10]; + /* Store v[10] -> (T2 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFFA0))) = (short)v[10]; + v[12] = v[2] - v[12]; + /* Store v[12] -> (T2 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0xFFE0))) = (short)v[12]; + v[5] = v[4] + v[5]; + v[4] = v[8] - v[4]; + /* Store v[4] -> (T2 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t2 + (short)0x60))) = (short)v[4]; + v[0] = v[0] - v[8]; + /* Store v[0] -> (T3 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t3 + (short)0xFFA0))) = (short)v[0]; + v[7] = v[7] - v[11]; + /* Store v[7] -> (T1 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t1 + (short)0xFFA0))) = (short)v[7]; + v[11] = v[11] - v[3]; + /* Store v[1] -> (T6 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t6 + (short)0x60))) = (short)v[1]; + v[11] = v[11] - v[5]; + /* Store v[11] -> (T0 + 0x60) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x60))) = (short)v[11]; + v[3] = v[3] - v[13]; + /* Store v[3] -> (T0 + 0x20) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0x20))) = (short)v[3]; + v[13] = v[13] + v[2]; + /* Store v[13] -> (T0 + 0xFFE0) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFFE0))) = (short)v[13]; + v[2] = (v[5] - v[2]) - v[9]; + /* Store v[2] -> (T0 + 0xFFA0) */ + *(int16_t *)(hle->mp3_buffer + ((t0 + (short)0xFFA0))) = (short)v[2]; + /* 0x7A8 - Verified... */ + + /* Step 8 - Dewindowing */ + + addptr = t6 & 0xFFE0; + + offset = 0x10 - (t4 >> 1); + for (x = 0; x < 8; x++) { + int32_t v0; + int32_t v18; + v2 = v4 = v6 = v8 = 0; + + for (i = 7; i >= 0; i--) { + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + v6 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x20) * (short)DeWindowLUT[offset + 0x20] + 0x4000) >> 0xF; + v8 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x30) * (short)DeWindowLUT[offset + 0x28] + 0x4000) >> 0xF; + addptr += 2; + offset++; + } + v0 = v2 + v4; + v18 = v6 + v8; + /* Clamp(v0); */ + /* Clamp(v18); */ + /* clamp??? */ + *(int16_t *)(hle->mp3_buffer + (outPtr ^ S16)) = v0; + *(int16_t *)(hle->mp3_buffer + ((outPtr + 2)^S16)) = v18; + outPtr += 4; + addptr += 0x30; + offset += 0x38; + } + + offset = 0x10 - (t4 >> 1) + 8 * 0x40; + v2 = v4 = 0; + for (i = 0; i < 4; i++) { + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + addptr += 2; + offset++; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + addptr += 2; + offset++; + } + mult6 = *(int32_t *)(hle->mp3_buffer + 0xCE8); + mult4 = *(int32_t *)(hle->mp3_buffer + 0xCEC); + if (t4 & 0x2) { + v2 = (v2 **(uint32_t *)(hle->mp3_buffer + 0xCE8)) >> 0x10; + *(int16_t *)(hle->mp3_buffer + (outPtr ^ S16)) = v2; + } else { + v4 = (v4 **(uint32_t *)(hle->mp3_buffer + 0xCE8)) >> 0x10; + *(int16_t *)(hle->mp3_buffer + (outPtr ^ S16)) = v4; + mult4 = *(uint32_t *)(hle->mp3_buffer + 0xCE8); + } + addptr -= 0x50; + + for (x = 0; x < 8; x++) { + int32_t v0; + int32_t v18; + v2 = v4 = v6 = v8 = 0; + + offset = (0x22F - (t4 >> 1) + x * 0x40); + + for (i = 0; i < 4; i++) { + v2 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x20) * (short)DeWindowLUT[offset + 0x00] + 0x4000) >> 0xF; + v2 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x20) * (short)DeWindowLUT[offset + 0x01] + 0x4000) >> 0xF; + v4 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x30) * (short)DeWindowLUT[offset + 0x08] + 0x4000) >> 0xF; + v4 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x30) * (short)DeWindowLUT[offset + 0x09] + 0x4000) >> 0xF; + v6 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x00) * (short)DeWindowLUT[offset + 0x20] + 0x4000) >> 0xF; + v6 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x00) * (short)DeWindowLUT[offset + 0x21] + 0x4000) >> 0xF; + v8 += ((int) * (int16_t *)(hle->mp3_buffer + (addptr) + 0x10) * (short)DeWindowLUT[offset + 0x28] + 0x4000) >> 0xF; + v8 -= ((int) * (int16_t *)(hle->mp3_buffer + ((addptr + 2)) + 0x10) * (short)DeWindowLUT[offset + 0x29] + 0x4000) >> 0xF; + addptr += 4; + offset += 2; + } + v0 = v2 + v4; + v18 = v6 + v8; + /* Clamp(v0); */ + /* Clamp(v18); */ + /* clamp??? */ + *(int16_t *)(hle->mp3_buffer + ((outPtr + 2)^S16)) = v0; + *(int16_t *)(hle->mp3_buffer + ((outPtr + 4)^S16)) = v18; + outPtr += 4; + addptr -= 0x50; + } + + tmp = outPtr; + hi0 = mult6; + hi1 = mult4; + + hi0 = (int)hi0 >> 0x10; + hi1 = (int)hi1 >> 0x10; + for (i = 0; i < 8; i++) { + /* v0 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x40)^S16)) * hi0); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x40)^S16)) = clamp_s16(vt); + + /* v17 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x30)^S16)) * hi0); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x30)^S16)) = clamp_s16(vt); + + /* v2 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0x1E)^S16)) * hi1); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0x1E)^S16)) = clamp_s16(vt); + + /* v4 */ + vt = (*(int16_t *)(hle->mp3_buffer + ((tmp - 0xE)^S16)) * hi1); + *(int16_t *)((uint8_t *)hle->mp3_buffer + ((tmp - 0xE)^S16)) = clamp_s16(vt); + + tmp += 2; + } +} + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/musyx.c b/libmupen64plus/mupen64plus-rsp-hle/src/musyx.c new file mode 100644 index 0000000000..d5a630a033 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/musyx.c @@ -0,0 +1,988 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - musyx.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2013 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include +#include + +#include "arithmetics.h" +#include "audio.h" +#include "common.h" +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" + +/* various constants */ +enum { SUBFRAME_SIZE = 192 }; +enum { MAX_VOICES = 32 }; + +enum { SAMPLE_BUFFER_SIZE = 0x200 }; + + +enum { + SFD_VOICE_COUNT = 0x0, + SFD_SFX_INDEX = 0x2, + SFD_VOICE_BITMASK = 0x4, + SFD_STATE_PTR = 0x8, + SFD_SFX_PTR = 0xc, + SFD_VOICES = 0x10, + + /* v2 only */ + SFD2_10_PTR = 0x10, + SFD2_14_BITMASK = 0x14, + SFD2_15_BITMASK = 0x15, + SFD2_16_BITMASK = 0x16, + SFD2_18_PTR = 0x18, + SFD2_1C_PTR = 0x1c, + SFD2_20_PTR = 0x20, + SFD2_24_PTR = 0x24, + SFD2_VOICES = 0x28 +}; + +enum { + VOICE_ENV_BEGIN = 0x00, + VOICE_ENV_STEP = 0x10, + VOICE_PITCH_Q16 = 0x20, + VOICE_PITCH_SHIFT = 0x22, + VOICE_CATSRC_0 = 0x24, + VOICE_CATSRC_1 = 0x30, + VOICE_ADPCM_FRAMES = 0x3c, + VOICE_SKIP_SAMPLES = 0x3e, + + /* for PCM16 */ + VOICE_U16_40 = 0x40, + VOICE_U16_42 = 0x42, + + /* for ADPCM */ + VOICE_ADPCM_TABLE_PTR = 0x40, + + VOICE_INTERLEAVED_PTR = 0x44, + VOICE_END_POINT = 0x48, + VOICE_RESTART_POINT = 0x4a, + VOICE_U16_4C = 0x4c, + VOICE_U16_4E = 0x4e, + + VOICE_SIZE = 0x50 +}; + +enum { + CATSRC_PTR1 = 0x00, + CATSRC_PTR2 = 0x04, + CATSRC_SIZE1 = 0x08, + CATSRC_SIZE2 = 0x0a +}; + +enum { + STATE_LAST_SAMPLE = 0x0, + STATE_BASE_VOL = 0x100, + STATE_CC0 = 0x110, + STATE_740_LAST4_V1 = 0x290, + + STATE_740_LAST4_V2 = 0x110 +}; + +enum { + SFX_CBUFFER_PTR = 0x00, + SFX_CBUFFER_LENGTH = 0x04, + SFX_TAP_COUNT = 0x08, + SFX_FIR4_HGAIN = 0x0a, + SFX_TAP_DELAYS = 0x0c, + SFX_TAP_GAINS = 0x2c, + SFX_U16_3C = 0x3c, + SFX_U16_3E = 0x3e, + SFX_FIR4_HCOEFFS = 0x40 +}; + + +/* struct definition */ +typedef struct { + /* internal subframes */ + int16_t left[SUBFRAME_SIZE]; + int16_t right[SUBFRAME_SIZE]; + int16_t cc0[SUBFRAME_SIZE]; + int16_t e50[SUBFRAME_SIZE]; + + /* internal subframes base volumes */ + int32_t base_vol[4]; + + /* */ + int16_t subframe_740_last4[4]; +} musyx_t; + +typedef void (*mix_sfx_with_main_subframes_t)(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains); + +/* helper functions prototypes */ +static void load_base_vol(struct hle_t* hle, int32_t *base_vol, uint32_t address); +static void save_base_vol(struct hle_t* hle, const int32_t *base_vol, uint32_t address); +static void update_base_vol(struct hle_t* hle, int32_t *base_vol, + uint32_t voice_mask, uint32_t last_sample_ptr, + uint8_t mask_15, uint32_t ptr_24); + +static void init_subframes_v1(musyx_t *musyx); +static void init_subframes_v2(musyx_t *musyx); + +static uint32_t voice_stage(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, uint32_t last_sample_ptr); + +static void dma_cat8(struct hle_t* hle, uint8_t *dst, uint32_t catsrc_ptr); +static void dma_cat16(struct hle_t* hle, uint16_t *dst, uint32_t catsrc_ptr); + +static void load_samples_PCM16(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset); +static void load_samples_ADPCM(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset); + +static void adpcm_decode_frames(struct hle_t* hle, + int16_t *dst, const uint8_t *src, + const int16_t *table, uint8_t count, + uint8_t skip_samples); + +static void adpcm_predict_frame(int16_t *dst, const uint8_t *src, + const uint8_t *nibbles, + unsigned int rshift); + +static void mix_voice_samples(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, const int16_t *samples, + unsigned segbase, unsigned offset, uint32_t last_sample_ptr); + +static void sfx_stage(struct hle_t* hle, + mix_sfx_with_main_subframes_t mix_sfx_with_main_subframes, + musyx_t *musyx, uint32_t sfx_ptr, uint16_t idx); + +static void mix_sfx_with_main_subframes_v1(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains); +static void mix_sfx_with_main_subframes_v2(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains); + +static void mix_samples(int16_t *y, int16_t x, int16_t hgain); +static void mix_subframes(int16_t *y, const int16_t *x, int16_t hgain); +static void mix_fir4(int16_t *y, const int16_t *x, int16_t hgain, const int16_t *hcoeffs); + + +static void interleave_stage_v1(struct hle_t* hle, musyx_t *musyx, + uint32_t output_ptr); + +static void interleave_stage_v2(struct hle_t* hle, musyx_t *musyx, + uint16_t mask_16, uint32_t ptr_18, + uint32_t ptr_1c, uint32_t output_ptr); + +static int32_t dot4(const int16_t *x, const int16_t *y) +{ + size_t i; + int32_t accu = 0; + + for (i = 0; i < 4; ++i) + accu = clamp_s16(accu + (((int32_t)x[i] * (int32_t)y[i]) >> 15)); + + return accu; +} + +/************************************************************************** + * MusyX v1 audio ucode + **************************************************************************/ +void musyx_v1_task(struct hle_t* hle) +{ + uint32_t sfd_ptr = *dmem_u32(hle, TASK_DATA_PTR); + uint32_t sfd_count = *dmem_u32(hle, TASK_DATA_SIZE); + uint32_t state_ptr; + musyx_t musyx; + + HleVerboseMessage(hle->user_defined, + "musyx_v1_task: *data=%x, #SF=%d", + sfd_ptr, + sfd_count); + + state_ptr = *dram_u32(hle, sfd_ptr + SFD_STATE_PTR); + + /* load initial state */ + load_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_load_u16(hle, (uint16_t *)musyx.cc0, state_ptr + STATE_CC0, SUBFRAME_SIZE); + dram_load_u16(hle, (uint16_t *)musyx.subframe_740_last4, state_ptr + STATE_740_LAST4_V1, + 4); + + for (;;) { + /* parse SFD structure */ + uint16_t sfx_index = *dram_u16(hle, sfd_ptr + SFD_SFX_INDEX); + uint32_t voice_mask = *dram_u32(hle, sfd_ptr + SFD_VOICE_BITMASK); + uint32_t sfx_ptr = *dram_u32(hle, sfd_ptr + SFD_SFX_PTR); + uint32_t voice_ptr = sfd_ptr + SFD_VOICES; + uint32_t last_sample_ptr = state_ptr + STATE_LAST_SAMPLE; + uint32_t output_ptr; + + /* initialize internal subframes using updated base volumes */ + update_base_vol(hle, musyx.base_vol, voice_mask, last_sample_ptr, 0, 0); + init_subframes_v1(&musyx); + + /* active voices get mixed into L,R,cc0,e50 subframes (optional) */ + output_ptr = voice_stage(hle, &musyx, voice_ptr, last_sample_ptr); + + /* apply delay-based effects (optional) */ + sfx_stage(hle, mix_sfx_with_main_subframes_v1, + &musyx, sfx_ptr, sfx_index); + + /* emit interleaved L,R subframes */ + interleave_stage_v1(hle, &musyx, output_ptr); + + --sfd_count; + if (sfd_count == 0) + break; + + sfd_ptr += SFD_VOICES + MAX_VOICES * VOICE_SIZE; + state_ptr = *dram_u32(hle, sfd_ptr + SFD_STATE_PTR); + } + + /* writeback updated state */ + save_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_store_u16(hle, (uint16_t *)musyx.cc0, state_ptr + STATE_CC0, SUBFRAME_SIZE); + dram_store_u16(hle, (uint16_t *)musyx.subframe_740_last4, state_ptr + STATE_740_LAST4_V1, + 4); + + rsp_break(hle, SP_STATUS_TASKDONE); +} + +/************************************************************************** + * MusyX v2 audio ucode + **************************************************************************/ +void musyx_v2_task(struct hle_t* hle) +{ + uint32_t sfd_ptr = *dmem_u32(hle, TASK_DATA_PTR); + uint32_t sfd_count = *dmem_u32(hle, TASK_DATA_SIZE); + musyx_t musyx; + + HleVerboseMessage(hle->user_defined, + "musyx_v2_task: *data=%x, #SF=%d", + sfd_ptr, + sfd_count); + + for (;;) { + /* parse SFD structure */ + uint16_t sfx_index = *dram_u16(hle, sfd_ptr + SFD_SFX_INDEX); + uint32_t voice_mask = *dram_u32(hle, sfd_ptr + SFD_VOICE_BITMASK); + uint32_t state_ptr = *dram_u32(hle, sfd_ptr + SFD_STATE_PTR); + uint32_t sfx_ptr = *dram_u32(hle, sfd_ptr + SFD_SFX_PTR); + uint32_t voice_ptr = sfd_ptr + SFD2_VOICES; + + uint32_t ptr_10 = *dram_u32(hle, sfd_ptr + SFD2_10_PTR); + uint8_t mask_14 = *dram_u8 (hle, sfd_ptr + SFD2_14_BITMASK); + uint8_t mask_15 = *dram_u8 (hle, sfd_ptr + SFD2_15_BITMASK); + uint16_t mask_16 = *dram_u16(hle, sfd_ptr + SFD2_16_BITMASK); + uint32_t ptr_18 = *dram_u32(hle, sfd_ptr + SFD2_18_PTR); + uint32_t ptr_1c = *dram_u32(hle, sfd_ptr + SFD2_1C_PTR); + uint32_t ptr_20 = *dram_u32(hle, sfd_ptr + SFD2_20_PTR); + uint32_t ptr_24 = *dram_u32(hle, sfd_ptr + SFD2_24_PTR); + + uint32_t last_sample_ptr = state_ptr + STATE_LAST_SAMPLE; + uint32_t output_ptr; + + /* load state */ + load_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_load_u16(hle, (uint16_t *)musyx.subframe_740_last4, + state_ptr + STATE_740_LAST4_V2, 4); + + /* initialize internal subframes using updated base volumes */ + update_base_vol(hle, musyx.base_vol, voice_mask, last_sample_ptr, mask_15, ptr_24); + init_subframes_v2(&musyx); + + if (ptr_10) { + /* TODO */ + HleWarnMessage(hle->user_defined, + "ptr_10=%08x mask_14=%02x ptr_24=%08x", + ptr_10, mask_14, ptr_24); + } + + /* active voices get mixed into L,R,cc0,e50 subframes (optional) */ + output_ptr = voice_stage(hle, &musyx, voice_ptr, last_sample_ptr); + + /* apply delay-based effects (optional) */ + sfx_stage(hle, mix_sfx_with_main_subframes_v2, + &musyx, sfx_ptr, sfx_index); + + dram_store_u16(hle, (uint16_t*)musyx.left, output_ptr , SUBFRAME_SIZE); + dram_store_u16(hle, (uint16_t*)musyx.right, output_ptr + 2*SUBFRAME_SIZE, SUBFRAME_SIZE); + dram_store_u16(hle, (uint16_t*)musyx.cc0, output_ptr + 4*SUBFRAME_SIZE, SUBFRAME_SIZE); + + /* store state */ + save_base_vol(hle, musyx.base_vol, state_ptr + STATE_BASE_VOL); + dram_store_u16(hle, (uint16_t*)musyx.subframe_740_last4, + state_ptr + STATE_740_LAST4_V2, 4); + + if (mask_16) + interleave_stage_v2(hle, &musyx, mask_16, ptr_18, ptr_1c, ptr_20); + + --sfd_count; + if (sfd_count == 0) + break; + + sfd_ptr += SFD2_VOICES + MAX_VOICES * VOICE_SIZE; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} + + + + + +static void load_base_vol(struct hle_t* hle, int32_t *base_vol, uint32_t address) +{ + base_vol[0] = ((uint32_t)(*dram_u16(hle, address)) << 16) | (*dram_u16(hle, address + 8)); + base_vol[1] = ((uint32_t)(*dram_u16(hle, address + 2)) << 16) | (*dram_u16(hle, address + 10)); + base_vol[2] = ((uint32_t)(*dram_u16(hle, address + 4)) << 16) | (*dram_u16(hle, address + 12)); + base_vol[3] = ((uint32_t)(*dram_u16(hle, address + 6)) << 16) | (*dram_u16(hle, address + 14)); +} + +static void save_base_vol(struct hle_t* hle, const int32_t *base_vol, uint32_t address) +{ + unsigned k; + + for (k = 0; k < 4; ++k) { + *dram_u16(hle, address) = (uint16_t)(base_vol[k] >> 16); + address += 2; + } + + for (k = 0; k < 4; ++k) { + *dram_u16(hle, address) = (uint16_t)(base_vol[k]); + address += 2; + } +} + +static void update_base_vol(struct hle_t* hle, int32_t *base_vol, + uint32_t voice_mask, uint32_t last_sample_ptr, + uint8_t mask_15, uint32_t ptr_24) +{ + unsigned i, k; + uint32_t mask; + + HleVerboseMessage(hle->user_defined, "base_vol voice_mask = %08x", voice_mask); + HleVerboseMessage(hle->user_defined, + "BEFORE: base_vol = %08x %08x %08x %08x", + base_vol[0], base_vol[1], base_vol[2], base_vol[3]); + + /* optim: skip voices contributions entirely if voice_mask is empty */ + if (voice_mask != 0) { + for (i = 0, mask = 1; i < MAX_VOICES; + ++i, mask <<= 1, last_sample_ptr += 8) { + if ((voice_mask & mask) == 0) + continue; + + for (k = 0; k < 4; ++k) + base_vol[k] += (int16_t)*dram_u16(hle, last_sample_ptr + k * 2); + } + } + + /* optim: skip contributions entirely if mask_15 is empty */ + if (mask_15 != 0) { + for(i = 0, mask = 1; i < 4; + ++i, mask <<= 1, ptr_24 += 8) { + if ((mask_15 & mask) == 0) + continue; + + for(k = 0; k < 4; ++k) + base_vol[k] += (int16_t)*dram_u16(hle, ptr_24 + k * 2); + } + } + + /* apply 3% decay */ + for (k = 0; k < 4; ++k) + base_vol[k] = (base_vol[k] * 0x0000f850) >> 16; + + HleVerboseMessage(hle->user_defined, + "AFTER: base_vol = %08x %08x %08x %08x", + base_vol[0], base_vol[1], base_vol[2], base_vol[3]); +} + + + + +static void init_subframes_v1(musyx_t *musyx) +{ + unsigned i; + + int16_t base_cc0 = clamp_s16(musyx->base_vol[2]); + int16_t base_e50 = clamp_s16(musyx->base_vol[3]); + + int16_t *left = musyx->left; + int16_t *right = musyx->right; + int16_t *cc0 = musyx->cc0; + int16_t *e50 = musyx->e50; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + *(e50++) = base_e50; + *(left++) = clamp_s16(*cc0 + base_cc0); + *(right++) = clamp_s16(-*cc0 - base_cc0); + *(cc0++) = 0; + } +} + +static void init_subframes_v2(musyx_t *musyx) +{ + unsigned i,k; + int16_t values[4]; + int16_t* subframes[4]; + + for(k = 0; k < 4; ++k) + values[k] = clamp_s16(musyx->base_vol[k]); + + subframes[0] = musyx->left; + subframes[1] = musyx->right; + subframes[2] = musyx->cc0; + subframes[3] = musyx->e50; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + + for(k = 0; k < 4; ++k) + *(subframes[k]++) = values[k]; + } +} + +/* Process voices, and returns interleaved subframe destination address */ +static uint32_t voice_stage(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, uint32_t last_sample_ptr) +{ + uint32_t output_ptr; + int i = 0; + + /* voice stage can be skipped if first voice has no samples */ + if (*dram_u16(hle, voice_ptr + VOICE_CATSRC_0 + CATSRC_SIZE1) == 0) { + HleVerboseMessage(hle->user_defined, "Skipping Voice stage"); + output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR); + } else { + /* otherwise process voices until a non null output_ptr is encountered */ + for (;;) { + /* load voice samples (PCM16 or APDCM) */ + int16_t samples[SAMPLE_BUFFER_SIZE]; + unsigned segbase; + unsigned offset; + + HleVerboseMessage(hle->user_defined, "Processing Voice #%d", i); + + if (*dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES) == 0) + load_samples_PCM16(hle, voice_ptr, samples, &segbase, &offset); + else + load_samples_ADPCM(hle, voice_ptr, samples, &segbase, &offset); + + /* mix them with each internal subframes */ + mix_voice_samples(hle, musyx, voice_ptr, samples, segbase, offset, + last_sample_ptr + i * 8); + + /* check break condition */ + output_ptr = *dram_u32(hle, voice_ptr + VOICE_INTERLEAVED_PTR); + if (output_ptr != 0) + break; + + /* next voice */ + ++i; + voice_ptr += VOICE_SIZE; + } + } + + return output_ptr; +} + +static void dma_cat8(struct hle_t* hle, uint8_t *dst, uint32_t catsrc_ptr) +{ + uint32_t ptr1 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR1); + uint32_t ptr2 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR2); + uint16_t size1 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE1); + uint16_t size2 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE2); + + size_t count1 = size1; + size_t count2 = size2; + + HleVerboseMessage(hle->user_defined, + "dma_cat: %08x %08x %04x %04x", + ptr1, + ptr2, + size1, + size2); + + dram_load_u8(hle, dst, ptr1, count1); + + if (size2 == 0) + return; + + dram_load_u8(hle, dst + count1, ptr2, count2); +} + +static void dma_cat16(struct hle_t* hle, uint16_t *dst, uint32_t catsrc_ptr) +{ + uint32_t ptr1 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR1); + uint32_t ptr2 = *dram_u32(hle, catsrc_ptr + CATSRC_PTR2); + uint16_t size1 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE1); + uint16_t size2 = *dram_u16(hle, catsrc_ptr + CATSRC_SIZE2); + + size_t count1 = size1 >> 1; + size_t count2 = size2 >> 1; + + HleVerboseMessage(hle->user_defined, + "dma_cat: %08x %08x %04x %04x", + ptr1, + ptr2, + size1, + size2); + + dram_load_u16(hle, dst, ptr1, count1); + + if (size2 == 0) + return; + + dram_load_u16(hle, dst + count1, ptr2, count2); +} + +static void load_samples_PCM16(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset) +{ + + uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES); + uint16_t u16_40 = *dram_u16(hle, voice_ptr + VOICE_U16_40); + uint16_t u16_42 = *dram_u16(hle, voice_ptr + VOICE_U16_42); + + unsigned count = align(u16_40 + u8_3e, 4); + + HleVerboseMessage(hle->user_defined, "Format: PCM16"); + + *segbase = SAMPLE_BUFFER_SIZE - count; + *offset = u8_3e; + + dma_cat16(hle, (uint16_t *)samples + *segbase, voice_ptr + VOICE_CATSRC_0); + + if (u16_42 != 0) + dma_cat16(hle, (uint16_t *)samples, voice_ptr + VOICE_CATSRC_1); +} + +static void load_samples_ADPCM(struct hle_t* hle, uint32_t voice_ptr, int16_t *samples, + unsigned *segbase, unsigned *offset) +{ + /* decompressed samples cannot exceed 0x400 bytes; + * ADPCM has a compression ratio of 5/16 */ + uint8_t buffer[SAMPLE_BUFFER_SIZE * 2 * 5 / 16]; + int16_t adpcm_table[128]; + + uint8_t u8_3c = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES ); + uint8_t u8_3d = *dram_u8(hle, voice_ptr + VOICE_ADPCM_FRAMES + 1); + uint8_t u8_3e = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES ); + uint8_t u8_3f = *dram_u8(hle, voice_ptr + VOICE_SKIP_SAMPLES + 1); + uint32_t adpcm_table_ptr = *dram_u32(hle, voice_ptr + VOICE_ADPCM_TABLE_PTR); + unsigned count; + + HleVerboseMessage(hle->user_defined, "Format: ADPCM"); + + HleVerboseMessage(hle->user_defined, "Loading ADPCM table: %08x", adpcm_table_ptr); + dram_load_u16(hle, (uint16_t *)adpcm_table, adpcm_table_ptr, 128); + + count = u8_3c << 5; + + *segbase = SAMPLE_BUFFER_SIZE - count; + *offset = u8_3e & 0x1f; + + dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_0); + adpcm_decode_frames(hle, samples + *segbase, buffer, adpcm_table, u8_3c, u8_3e); + + if (u8_3d != 0) { + dma_cat8(hle, buffer, voice_ptr + VOICE_CATSRC_1); + adpcm_decode_frames(hle, samples, buffer, adpcm_table, u8_3d, u8_3f); + } +} + +static void adpcm_decode_frames(struct hle_t* hle, + int16_t *dst, const uint8_t *src, + const int16_t *table, uint8_t count, + uint8_t skip_samples) +{ + int16_t frame[32]; + const uint8_t *nibbles = src + 8; + unsigned i; + bool jump_gap = false; + + HleVerboseMessage(hle->user_defined, + "ADPCM decode: count=%d, skip=%d", + count, skip_samples); + + if (skip_samples >= 32) { + jump_gap = true; + nibbles += 16; + src += 4; + } + + for (i = 0; i < count; ++i) { + uint8_t c2 = nibbles[0]; + + const int16_t *book = (c2 & 0xf0) + table; + unsigned int rshift = (c2 & 0x0f); + + adpcm_predict_frame(frame, src, nibbles, rshift); + + memcpy(dst, frame, 2 * sizeof(frame[0])); + adpcm_compute_residuals(dst + 2, frame + 2, book, dst , 6); + adpcm_compute_residuals(dst + 8, frame + 8, book, dst + 6, 8); + adpcm_compute_residuals(dst + 16, frame + 16, book, dst + 14, 8); + adpcm_compute_residuals(dst + 24, frame + 24, book, dst + 22, 8); + + if (jump_gap) { + nibbles += 8; + src += 32; + } + + jump_gap = !jump_gap; + nibbles += 16; + src += 4; + dst += 32; + } +} + +static void adpcm_predict_frame(int16_t *dst, const uint8_t *src, + const uint8_t *nibbles, + unsigned int rshift) +{ + unsigned int i; + + *(dst++) = (src[0] << 8) | src[1]; + *(dst++) = (src[2] << 8) | src[3]; + + for (i = 1; i < 16; ++i) { + uint8_t byte = nibbles[i]; + + *(dst++) = adpcm_predict_sample(byte, 0xf0, 8, rshift); + *(dst++) = adpcm_predict_sample(byte, 0x0f, 12, rshift); + } +} + +static void mix_voice_samples(struct hle_t* hle, musyx_t *musyx, + uint32_t voice_ptr, const int16_t *samples, + unsigned segbase, unsigned offset, uint32_t last_sample_ptr) +{ + int i, k; + + /* parse VOICE structure */ + const uint16_t pitch_q16 = *dram_u16(hle, voice_ptr + VOICE_PITCH_Q16); + const uint16_t pitch_shift = *dram_u16(hle, voice_ptr + VOICE_PITCH_SHIFT); /* Q4.12 */ + + const uint16_t end_point = *dram_u16(hle, voice_ptr + VOICE_END_POINT); + const uint16_t restart_point = *dram_u16(hle, voice_ptr + VOICE_RESTART_POINT); + + const uint16_t u16_4e = *dram_u16(hle, voice_ptr + VOICE_U16_4E); + + /* init values and pointers */ + const int16_t *sample = samples + segbase + offset + u16_4e; + const int16_t *const sample_end = samples + segbase + end_point; + const int16_t *const sample_restart = samples + (restart_point & 0x7fff) + + (((restart_point & 0x8000) != 0) ? 0x000 : segbase); + + + uint32_t pitch_accu = pitch_q16; + uint32_t pitch_step = pitch_shift << 4; + + int32_t v4_env[4]; + int32_t v4_env_step[4]; + int16_t *v4_dst[4]; + int16_t v4[4]; + + dram_load_u32(hle, (uint32_t *)v4_env, voice_ptr + VOICE_ENV_BEGIN, 4); + dram_load_u32(hle, (uint32_t *)v4_env_step, voice_ptr + VOICE_ENV_STEP, 4); + + v4_dst[0] = musyx->left; + v4_dst[1] = musyx->right; + v4_dst[2] = musyx->cc0; + v4_dst[3] = musyx->e50; + + HleVerboseMessage(hle->user_defined, + "Voice debug: segbase=%d" + "\tu16_4e=%04x\n" + "\tpitch: frac0=%04x shift=%04x\n" + "\tend_point=%04x restart_point=%04x\n" + "\tenv = %08x %08x %08x %08x\n" + "\tenv_step = %08x %08x %08x %08x\n", + segbase, + u16_4e, + pitch_q16, pitch_shift, + end_point, restart_point, + v4_env[0], v4_env[1], v4_env[2], v4_env[3], + v4_env_step[0], v4_env_step[1], v4_env_step[2], v4_env_step[3]); + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + /* update sample and lut pointers and then pitch_accu */ + const int16_t *lut = (RESAMPLE_LUT + ((pitch_accu & 0xfc00) >> 8)); + int dist; + int16_t v; + + sample += (pitch_accu >> 16); + pitch_accu &= 0xffff; + pitch_accu += pitch_step; + + /* handle end/restart points */ + dist = sample - sample_end; + if (dist >= 0) + sample = sample_restart + dist; + + /* apply resample filter */ + v = clamp_s16(dot4(sample, lut)); + + for (k = 0; k < 4; ++k) { + /* envmix */ + int32_t accu = (v * (v4_env[k] >> 16)) >> 15; + v4[k] = clamp_s16(accu); + *(v4_dst[k]) = clamp_s16(accu + *(v4_dst[k])); + + /* update envelopes and dst pointers */ + ++(v4_dst[k]); + v4_env[k] += v4_env_step[k]; + } + } + + /* save last resampled sample */ + dram_store_u16(hle, (uint16_t *)v4, last_sample_ptr, 4); + + HleVerboseMessage(hle->user_defined, + "last_sample = %04x %04x %04x %04x", + v4[0], v4[1], v4[2], v4[3]); +} + + +static void sfx_stage(struct hle_t* hle, mix_sfx_with_main_subframes_t mix_sfx_with_main_subframes, + musyx_t *musyx, uint32_t sfx_ptr, uint16_t idx) +{ + unsigned int i; + + int16_t buffer[SUBFRAME_SIZE + 4]; + int16_t *subframe = buffer + 4; + + uint32_t tap_delays[8]; + int16_t tap_gains[8]; + int16_t fir4_hcoeffs[4]; + + int16_t delayed[SUBFRAME_SIZE]; + int dpos, dlength; + + const uint32_t pos = idx * SUBFRAME_SIZE; + + uint32_t cbuffer_ptr; + uint32_t cbuffer_length; + uint16_t tap_count; + int16_t fir4_hgain; + uint16_t sfx_gains[2]; + + HleVerboseMessage(hle->user_defined, "SFX: %08x, idx=%d", sfx_ptr, idx); + + if (sfx_ptr == 0) + return; + + /* load sfx parameters */ + cbuffer_ptr = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_PTR); + cbuffer_length = *dram_u32(hle, sfx_ptr + SFX_CBUFFER_LENGTH); + + tap_count = *dram_u16(hle, sfx_ptr + SFX_TAP_COUNT); + + dram_load_u32(hle, tap_delays, sfx_ptr + SFX_TAP_DELAYS, 8); + dram_load_u16(hle, (uint16_t *)tap_gains, sfx_ptr + SFX_TAP_GAINS, 8); + + fir4_hgain = *dram_u16(hle, sfx_ptr + SFX_FIR4_HGAIN); + dram_load_u16(hle, (uint16_t *)fir4_hcoeffs, sfx_ptr + SFX_FIR4_HCOEFFS, 4); + + sfx_gains[0] = *dram_u16(hle, sfx_ptr + SFX_U16_3C); + sfx_gains[1] = *dram_u16(hle, sfx_ptr + SFX_U16_3E); + + HleVerboseMessage(hle->user_defined, + "cbuffer: ptr=%08x length=%x", cbuffer_ptr, + cbuffer_length); + + HleVerboseMessage(hle->user_defined, + "fir4: hgain=%04x hcoeff=%04x %04x %04x %04x", + fir4_hgain, + fir4_hcoeffs[0], fir4_hcoeffs[1], fir4_hcoeffs[2], fir4_hcoeffs[3]); + + HleVerboseMessage(hle->user_defined, + "tap count=%d\n" + "delays: %08x %08x %08x %08x %08x %08x %08x %08x\n" + "gains: %04x %04x %04x %04x %04x %04x %04x %04x", + tap_count, + tap_delays[0], tap_delays[1], tap_delays[2], tap_delays[3], + tap_delays[4], tap_delays[5], tap_delays[6], tap_delays[7], + tap_gains[0], tap_gains[1], tap_gains[2], tap_gains[3], + tap_gains[4], tap_gains[5], tap_gains[6], tap_gains[7]); + + HleVerboseMessage(hle->user_defined, "sfx_gains=%04x %04x", sfx_gains[0], sfx_gains[1]); + + /* mix up to 8 delayed subframes */ + memset(subframe, 0, SUBFRAME_SIZE * sizeof(subframe[0])); + for (i = 0; i < tap_count; ++i) { + + dpos = pos - tap_delays[i]; + if (dpos <= 0) + dpos += cbuffer_length; + dlength = SUBFRAME_SIZE; + + if ((uint32_t)(dpos + SUBFRAME_SIZE) > cbuffer_length) { + dlength = cbuffer_length - dpos; + dram_load_u16(hle, (uint16_t *)delayed + dlength, cbuffer_ptr, SUBFRAME_SIZE - dlength); + } + + dram_load_u16(hle, (uint16_t *)delayed, cbuffer_ptr + dpos * 2, dlength); + + mix_subframes(subframe, delayed, tap_gains[i]); + } + + /* add resulting subframe to main subframes */ + mix_sfx_with_main_subframes(musyx, subframe, sfx_gains); + + /* apply FIR4 filter and writeback filtered result */ + memcpy(buffer, musyx->subframe_740_last4, 4 * sizeof(int16_t)); + memcpy(musyx->subframe_740_last4, subframe + SUBFRAME_SIZE - 4, 4 * sizeof(int16_t)); + mix_fir4(musyx->e50, buffer + 1, fir4_hgain, fir4_hcoeffs); + dram_store_u16(hle, (uint16_t *)musyx->e50, cbuffer_ptr + pos * 2, SUBFRAME_SIZE); +} + +static void mix_sfx_with_main_subframes_v1(musyx_t *musyx, const int16_t *subframe, + const uint16_t* UNUSED(gains)) +{ + unsigned i; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + int16_t v = subframe[i]; + musyx->left[i] = clamp_s16(musyx->left[i] + v); + musyx->right[i] = clamp_s16(musyx->right[i] + v); + } +} + +static void mix_sfx_with_main_subframes_v2(musyx_t *musyx, const int16_t *subframe, + const uint16_t* gains) +{ + unsigned i; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + int16_t v = subframe[i]; + int16_t v1 = (int32_t)(v * gains[0]) >> 16; + int16_t v2 = (int32_t)(v * gains[1]) >> 16; + + musyx->left[i] = clamp_s16(musyx->left[i] + v1); + musyx->right[i] = clamp_s16(musyx->right[i] + v1); + musyx->cc0[i] = clamp_s16(musyx->cc0[i] + v2); + } +} + +static void mix_samples(int16_t *y, int16_t x, int16_t hgain) +{ + *y = clamp_s16(*y + ((x * hgain + 0x4000) >> 15)); +} + +static void mix_subframes(int16_t *y, const int16_t *x, int16_t hgain) +{ + unsigned int i; + + for (i = 0; i < SUBFRAME_SIZE; ++i) + mix_samples(&y[i], x[i], hgain); +} + +static void mix_fir4(int16_t *y, const int16_t *x, int16_t hgain, const int16_t *hcoeffs) +{ + unsigned int i; + int32_t h[4]; + + h[0] = (hgain * hcoeffs[0]) >> 15; + h[1] = (hgain * hcoeffs[1]) >> 15; + h[2] = (hgain * hcoeffs[2]) >> 15; + h[3] = (hgain * hcoeffs[3]) >> 15; + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + int32_t v = (h[0] * x[i] + h[1] * x[i + 1] + h[2] * x[i + 2] + h[3] * x[i + 3]) >> 15; + y[i] = clamp_s16(y[i] + v); + } +} + +static void interleave_stage_v1(struct hle_t* hle, musyx_t *musyx, uint32_t output_ptr) +{ + size_t i; + + int16_t base_left; + int16_t base_right; + + int16_t *left; + int16_t *right; + uint32_t *dst; + + HleVerboseMessage(hle->user_defined, "interleave: %08x", output_ptr); + + base_left = clamp_s16(musyx->base_vol[0]); + base_right = clamp_s16(musyx->base_vol[1]); + + left = musyx->left; + right = musyx->right; + dst = dram_u32(hle, output_ptr); + + for (i = 0; i < SUBFRAME_SIZE; ++i) { + uint16_t l = clamp_s16(*(left++) + base_left); + uint16_t r = clamp_s16(*(right++) + base_right); + + *(dst++) = (l << 16) | r; + } +} + +static void interleave_stage_v2(struct hle_t* hle, musyx_t *musyx, + uint16_t mask_16, uint32_t ptr_18, + uint32_t ptr_1c, uint32_t output_ptr) +{ + unsigned i, k; + int16_t subframe[SUBFRAME_SIZE]; + uint32_t *dst; + uint16_t mask; + + HleVerboseMessage(hle->user_defined, + "mask_16=%04x ptr_18=%08x ptr_1c=%08x output_ptr=%08x", + mask_16, ptr_18, ptr_1c, output_ptr); + + /* compute L_total, R_total and update subframe @ptr_1c */ + memset(subframe, 0, SUBFRAME_SIZE*sizeof(subframe[0])); + + for(i = 0; i < SUBFRAME_SIZE; ++i) { + int16_t v = *dram_u16(hle, ptr_1c + i*2); + musyx->left[i] = v; + musyx->right[i] = clamp_s16(-v); + } + + for (k = 0, mask = 1; k < 8; ++k, mask <<= 1, ptr_18 += 8) { + int16_t hgain; + uint32_t address; + + if ((mask_16 & mask) == 0) + continue; + + address = *dram_u32(hle, ptr_18); + hgain = *dram_u16(hle, ptr_18 + 4); + + for(i = 0; i < SUBFRAME_SIZE; ++i, address += 2) { + mix_samples(&musyx->left[i], *dram_u16(hle, address), hgain); + mix_samples(&musyx->right[i], *dram_u16(hle, address + 2*SUBFRAME_SIZE), hgain); + mix_samples(&subframe[i], *dram_u16(hle, address + 4*SUBFRAME_SIZE), hgain); + } + } + + /* interleave L_total and R_total */ + dst = dram_u32(hle, output_ptr); + for(i = 0; i < SUBFRAME_SIZE; ++i) { + uint16_t l = musyx->left[i]; + uint16_t r = musyx->right[i]; + *(dst++) = (l << 16) | r; + } + + /* writeback subframe @ptr_1c */ + dram_store_u16(hle, (uint16_t*)subframe, ptr_1c, SUBFRAME_SIZE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib.h b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib.h new file mode 100644 index 0000000000..dc5e5bab53 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib.h @@ -0,0 +1,34 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if !defined(OSAL_DYNAMICLIB_H) +#define OSAL_DYNAMICLIB_H + +#include "m64p_types.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath); + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName); + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle); + +#endif /* #define OSAL_DYNAMICLIB_H */ + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_unix.c b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_unix.c new file mode 100644 index 0000000000..9d937ed370 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_unix.c @@ -0,0 +1,71 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib_unix.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include +#include + +#include "m64p_types.h" +#include "hle_external.h" +#include "osal_dynamiclib.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath) +{ + if (pLibHandle == NULL || pccLibraryPath == NULL) + return M64ERR_INPUT_ASSERT; + + *pLibHandle = dlopen(pccLibraryPath, RTLD_NOW); + + if (*pLibHandle == NULL) + { + /* only print an error message if there is a directory separator (/) in the pathname */ + /* this prevents us from throwing an error for the use case where Mupen64Plus is not installed */ + if (strchr(pccLibraryPath, '/') != NULL) + HleErrorMessage(NULL, "dlopen('%s') failed: %s", pccLibraryPath, dlerror()); + return M64ERR_INPUT_NOT_FOUND; + } + + return M64ERR_SUCCESS; +} + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return dlsym(LibHandle, pccProcedureName); +} + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle) +{ + int rval = dlclose(LibHandle); + + if (rval != 0) + { + HleErrorMessage(NULL, "dlclose() failed: %s", dlerror()); + return M64ERR_INTERNAL; + } + + return M64ERR_SUCCESS; +} + + diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_win32.c b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_win32.c new file mode 100644 index 0000000000..fe77cf3aad --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/osal_dynamiclib_win32.c @@ -0,0 +1,75 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib_win32.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "m64p_types.h" +#include "hle_external.h" +#include "osal_dynamiclib.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath) +{ + if (pLibHandle == NULL || pccLibraryPath == NULL) + return M64ERR_INPUT_ASSERT; + + *pLibHandle = LoadLibrary(pccLibraryPath); + + if (*pLibHandle == NULL) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + HleErrorMessage(NULL, "LoadLibrary('%s') error: %s", pccLibraryPath, pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INPUT_NOT_FOUND; + } + + return M64ERR_SUCCESS; +} + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return GetProcAddress(LibHandle, pccProcedureName); +} + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle) +{ + int rval = FreeLibrary(LibHandle); + + if (rval == 0) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + HleErrorMessage(NULL, "FreeLibrary() error: %s", pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INTERNAL; + } + + return M64ERR_SUCCESS; +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/plugin.c b/libmupen64plus/mupen64plus-rsp-hle/src/plugin.c new file mode 100644 index 0000000000..e0818bdb63 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/plugin.c @@ -0,0 +1,495 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - plugin.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * Copyright (C) 2009 Richard Goedeken * + * Copyright (C) 2002 Hacktarux * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "common.h" +#include "hle.h" +#include "hle_internal.h" +#include "hle_external.h" + +#define M64P_PLUGIN_PROTOTYPES 1 +#include "m64p_common.h" +#include "m64p_config.h" +#include "m64p_frontend.h" +#include "m64p_plugin.h" +#include "m64p_types.h" + +#include "osal_dynamiclib.h" + +#define CONFIG_API_VERSION 0x020100 +#define CONFIG_PARAM_VERSION 1.00 + +#define RSP_API_VERSION 0x20000 +#define RSP_HLE_VERSION 0x020500 +#define RSP_PLUGIN_API_VERSION 0x020000 + +#define RSP_HLE_CONFIG_SECTION "Rsp-HLE" +#define RSP_HLE_CONFIG_VERSION "Version" +#define RSP_HLE_CONFIG_FALLBACK "RspFallback" +#define RSP_HLE_CONFIG_HLE_GFX "DisplayListToGraphicsPlugin" +#define RSP_HLE_CONFIG_HLE_AUD "AudioListToAudioPlugin" + + +#define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff) + +/* Handy macro to avoid code bloat when loading symbols */ +#define GET_FUNC(type, field, name) \ + ((field = (type)osal_dynlib_getproc(handle, name)) != NULL) + +/* local variables */ +static struct hle_t g_hle; +static void (*l_CheckInterrupts)(void) = NULL; +static void (*l_ProcessDlistList)(void) = NULL; +static void (*l_ProcessAlistList)(void) = NULL; +static void (*l_ProcessRdpList)(void) = NULL; +static void (*l_ShowCFB)(void) = NULL; +static void (*l_DebugCallback)(void *, int, const char *) = NULL; +static void *l_DebugCallContext = NULL; +static m64p_dynlib_handle l_CoreHandle = NULL; +static int l_PluginInit = 0; + +static m64p_handle l_ConfigRspHle; +static m64p_dynlib_handle l_RspFallback; +static ptr_InitiateRSP l_InitiateRSP = NULL; +static ptr_DoRspCycles l_DoRspCycles = NULL; +static ptr_RomClosed l_RomClosed = NULL; +static ptr_PluginShutdown l_PluginShutdown = NULL; + +/* definitions of pointers to Core functions */ +static ptr_ConfigOpenSection ConfigOpenSection = NULL; +static ptr_ConfigDeleteSection ConfigDeleteSection = NULL; +static ptr_ConfigSaveSection ConfigSaveSection = NULL; +static ptr_ConfigSetParameter ConfigSetParameter = NULL; +static ptr_ConfigGetParameter ConfigGetParameter = NULL; +static ptr_ConfigSetDefaultInt ConfigSetDefaultInt = NULL; +static ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat = NULL; +static ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL; +static ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL; +static ptr_ConfigGetParamInt ConfigGetParamInt = NULL; +static ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL; +static ptr_ConfigGetParamBool ConfigGetParamBool = NULL; +static ptr_ConfigGetParamString ConfigGetParamString = NULL; +static ptr_CoreDoCommand CoreDoCommand = NULL; + +/* local function */ +static void teardown_rsp_fallback() +{ + if (l_RspFallback != NULL) { + (*l_PluginShutdown)(); + osal_dynlib_close(l_RspFallback); + } + + l_RspFallback = NULL; + l_DoRspCycles = NULL; + l_InitiateRSP = NULL; + l_RomClosed = NULL; + l_PluginShutdown = NULL; +} + +static void setup_rsp_fallback(const char* rsp_fallback_path) +{ + m64p_dynlib_handle handle = NULL; + + /* reset rsp fallback */ + teardown_rsp_fallback(); + + if (rsp_fallback_path == NULL || strlen(rsp_fallback_path) == 0) { + HleInfoMessage(NULL, "RSP Fallback disabled !"); + return; + } + + /* load plugin */ + if (osal_dynlib_open(&handle, rsp_fallback_path) != M64ERR_SUCCESS) { + HleErrorMessage(NULL, "Can't load library: %s", rsp_fallback_path); + return; + } + + /* call the GetVersion function for the plugin and check compatibility */ + ptr_PluginGetVersion PluginGetVersion = (ptr_PluginGetVersion) osal_dynlib_getproc(handle, "PluginGetVersion"); + if (PluginGetVersion == NULL) + { + HleErrorMessage(NULL, "library '%s' is not a Mupen64Plus library.", rsp_fallback_path); + goto close_handle; + } + + m64p_plugin_type plugin_type = (m64p_plugin_type)0; + int plugin_version = 0; + const char *plugin_name = NULL; + int api_version = 0; + + (*PluginGetVersion)(&plugin_type, &plugin_version, &api_version, &plugin_name, NULL); + + if (plugin_type != M64PLUGIN_RSP) { + HleErrorMessage(NULL, "plugin %s is not an RSP plugin (%u)", plugin_name, plugin_type); + goto close_handle; + } + + if ((api_version & 0xffff0000) != (RSP_API_VERSION & 0xffff0000)) { + HleErrorMessage(NULL, "plugin %s. Version mismatch: %u.%u. Expected >= %u.0", + plugin_name, + (uint16_t)(api_version >> 16), + (uint16_t)(api_version), + (uint16_t)(RSP_API_VERSION >> 16)); + goto close_handle; + } + + /* load functions */ + ptr_PluginStartup PluginStartup; + + if (!GET_FUNC(ptr_PluginStartup, PluginStartup, "PluginStartup") || + !GET_FUNC(ptr_PluginShutdown, l_PluginShutdown, "PluginShutdown") || + !GET_FUNC(ptr_DoRspCycles, l_DoRspCycles, "DoRspCycles") || + !GET_FUNC(ptr_InitiateRSP, l_InitiateRSP, "InitiateRSP") || + !GET_FUNC(ptr_RomClosed, l_RomClosed, "RomClosed")) + { + HleErrorMessage(NULL, "broken RSP plugin; function(s) not found."); + l_PluginShutdown = NULL; + l_DoRspCycles = NULL; + l_InitiateRSP = NULL; + l_RomClosed = NULL; + goto close_handle; + } + + /* call the plugin's initialization function and make sure it starts okay */ + if ((*PluginStartup)(l_CoreHandle, l_DebugCallContext, l_DebugCallback) != M64ERR_SUCCESS) { + HleErrorMessage(NULL, "Error: %s plugin library '%s' failed to start.", plugin_name, rsp_fallback_path); + goto close_handle; + } + + /* OK we're done ! */ + l_RspFallback = handle; + HleInfoMessage(NULL, "RSP Fallback '%s' loaded successfully !", rsp_fallback_path); + return; + +close_handle: + osal_dynlib_close(handle); +} + +static void DebugMessage(int level, const char *message, va_list args) +{ + char msgbuf[1024]; + + if (l_DebugCallback == NULL) + return; + + vsprintf(msgbuf, message, args); + + (*l_DebugCallback)(l_DebugCallContext, level, msgbuf); +} + +/* Global functions needed by HLE core */ +void HleVerboseMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_VERBOSE, message, args); + va_end(args); +} + +void HleInfoMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_INFO, message, args); + va_end(args); +} + +void HleErrorMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_ERROR, message, args); + va_end(args); +} + +void HleWarnMessage(void* UNUSED(user_defined), const char *message, ...) +{ + va_list args; + va_start(args, message); + DebugMessage(M64MSG_WARNING, message, args); + va_end(args); +} + +void HleCheckInterrupts(void* UNUSED(user_defined)) +{ + if (l_CheckInterrupts == NULL) + return; + + (*l_CheckInterrupts)(); +} + +void HleProcessDlistList(void* UNUSED(user_defined)) +{ + if (l_ProcessDlistList == NULL) + return; + + (*l_ProcessDlistList)(); +} + +void HleProcessAlistList(void* UNUSED(user_defined)) +{ + if (l_ProcessAlistList == NULL) + return; + + (*l_ProcessAlistList)(); +} + +void HleProcessRdpList(void* UNUSED(user_defined)) +{ + if (l_ProcessRdpList == NULL) + return; + + (*l_ProcessRdpList)(); +} + +void HleShowCFB(void* UNUSED(user_defined)) +{ + if (l_ShowCFB == NULL) + return; + + (*l_ShowCFB)(); +} + + +int HleForwardTask(void* user_defined) +{ + if (l_DoRspCycles == NULL) + return -1; + + (*l_DoRspCycles)(-1); + return 0; +} + + +/* DLL-exported functions */ +EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context, + void (*DebugCallback)(void *, int, const char *)) +{ + ptr_CoreGetAPIVersions CoreAPIVersionFunc; + int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion, bSaveConfig; + float fConfigParamsVersion = 0.0f; + + if (l_PluginInit) + return M64ERR_ALREADY_INIT; + + /* first thing is to set the callback function for debug info */ + l_DebugCallback = DebugCallback; + l_DebugCallContext = Context; + + /* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */ + CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions"); + if (CoreAPIVersionFunc == NULL) + { + HleErrorMessage(NULL, "Core emulator broken; no CoreAPIVersionFunc() function found."); + return M64ERR_INCOMPATIBLE; + } + + (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL); + if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000)) + { + HleErrorMessage(NULL, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)", + VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION)); + return M64ERR_INCOMPATIBLE; + } + + /* Get the core config function pointers from the library handle */ + ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection"); + ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection"); + ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection"); + ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter"); + ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter"); + ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt"); + ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat"); + ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool"); + ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString"); + ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt"); + ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat"); + ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool"); + ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString"); + + if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSetParameter || !ConfigGetParameter || + !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString || + !ConfigGetParamInt || !ConfigGetParamFloat || !ConfigGetParamBool || !ConfigGetParamString) + return M64ERR_INCOMPATIBLE; + + /* ConfigSaveSection was added in Config API v2.1.0 */ + if (ConfigAPIVersion >= 0x020100 && !ConfigSaveSection) + return M64ERR_INCOMPATIBLE; + + /* Get core DoCommand function */ + CoreDoCommand = (ptr_CoreDoCommand) osal_dynlib_getproc(CoreLibHandle, "CoreDoCommand"); + if (!CoreDoCommand) { + return M64ERR_INCOMPATIBLE; + } + + /* get a configuration section handle */ + if (ConfigOpenSection(RSP_HLE_CONFIG_SECTION, &l_ConfigRspHle) != M64ERR_SUCCESS) + { + HleErrorMessage(NULL, "Couldn't open config section '" RSP_HLE_CONFIG_SECTION "'"); + return M64ERR_INPUT_NOT_FOUND; + } + + /* check the section version number */ + bSaveConfig = 0; + if (ConfigGetParameter(l_ConfigRspHle, RSP_HLE_CONFIG_VERSION, M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS) + { + HleWarnMessage(NULL, "No version number in '" RSP_HLE_CONFIG_SECTION "' config section. Setting defaults."); + ConfigDeleteSection(RSP_HLE_CONFIG_SECTION); + ConfigOpenSection(RSP_HLE_CONFIG_SECTION, &l_ConfigRspHle); + bSaveConfig = 1; + } + else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION)) + { + HleWarnMessage(NULL, "Incompatible version %.2f in '" RSP_HLE_CONFIG_SECTION "' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION); + ConfigDeleteSection(RSP_HLE_CONFIG_SECTION); + ConfigOpenSection(RSP_HLE_CONFIG_SECTION, &l_ConfigRspHle); + bSaveConfig = 1; + } + else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f) + { + /* handle upgrades */ + float fVersion = CONFIG_PARAM_VERSION; + ConfigSetParameter(l_ConfigRspHle, "Version", M64TYPE_FLOAT, &fVersion); + HleInfoMessage(NULL, "Updating parameter set version in '" RSP_HLE_CONFIG_SECTION "' config section to %.2f", fVersion); + bSaveConfig = 1; + } + + /* set the default values for this plugin */ + ConfigSetDefaultFloat(l_ConfigRspHle, RSP_HLE_CONFIG_VERSION, CONFIG_PARAM_VERSION, + "Mupen64Plus RSP HLE Plugin config parameter version number"); + ConfigSetDefaultString(l_ConfigRspHle, RSP_HLE_CONFIG_FALLBACK, "", + "Path to a RSP plugin which will be used when encountering an unknown ucode." + "You can disable this by letting an empty string."); + ConfigSetDefaultBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_GFX, 1, + "Send display lists to the graphics plugin"); + ConfigSetDefaultBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_AUD, 0, + "Send audio lists to the audio plugin"); + + if (bSaveConfig && ConfigAPIVersion >= 0x020100) + ConfigSaveSection(RSP_HLE_CONFIG_SECTION); + + l_CoreHandle = CoreLibHandle; + + l_PluginInit = 1; + return M64ERR_SUCCESS; +} + +EXPORT m64p_error CALL PluginShutdown(void) +{ + if (!l_PluginInit) + return M64ERR_NOT_INIT; + + /* reset some local variable */ + l_DebugCallback = NULL; + l_DebugCallContext = NULL; + l_CoreHandle = NULL; + + teardown_rsp_fallback(); + + l_PluginInit = 0; + return M64ERR_SUCCESS; +} + +EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities) +{ + /* set version info */ + if (PluginType != NULL) + *PluginType = M64PLUGIN_RSP; + + if (PluginVersion != NULL) + *PluginVersion = RSP_HLE_VERSION; + + if (APIVersion != NULL) + *APIVersion = RSP_PLUGIN_API_VERSION; + + if (PluginNamePtr != NULL) + *PluginNamePtr = "Hacktarux/Azimer High-Level Emulation RSP Plugin"; + + if (Capabilities != NULL) + *Capabilities = 0; + + return M64ERR_SUCCESS; +} + +EXPORT unsigned int CALL DoRspCycles(unsigned int Cycles) +{ + hle_execute(&g_hle); + return Cycles; +} + +EXPORT void CALL InitiateRSP(RSP_INFO Rsp_Info, unsigned int* CycleCount) +{ + hle_init(&g_hle, + Rsp_Info.RDRAM, + Rsp_Info.DMEM, + Rsp_Info.IMEM, + Rsp_Info.MI_INTR_REG, + Rsp_Info.SP_MEM_ADDR_REG, + Rsp_Info.SP_DRAM_ADDR_REG, + Rsp_Info.SP_RD_LEN_REG, + Rsp_Info.SP_WR_LEN_REG, + Rsp_Info.SP_STATUS_REG, + Rsp_Info.SP_DMA_FULL_REG, + Rsp_Info.SP_DMA_BUSY_REG, + Rsp_Info.SP_PC_REG, + Rsp_Info.SP_SEMAPHORE_REG, + Rsp_Info.DPC_START_REG, + Rsp_Info.DPC_END_REG, + Rsp_Info.DPC_CURRENT_REG, + Rsp_Info.DPC_STATUS_REG, + Rsp_Info.DPC_CLOCK_REG, + Rsp_Info.DPC_BUFBUSY_REG, + Rsp_Info.DPC_PIPEBUSY_REG, + Rsp_Info.DPC_TMEM_REG, + NULL); + + l_CheckInterrupts = Rsp_Info.CheckInterrupts; + l_ProcessDlistList = Rsp_Info.ProcessDlistList; + l_ProcessAlistList = Rsp_Info.ProcessAlistList; + l_ProcessRdpList = Rsp_Info.ProcessRdpList; + l_ShowCFB = Rsp_Info.ShowCFB; + + setup_rsp_fallback(ConfigGetParamString(l_ConfigRspHle, RSP_HLE_CONFIG_FALLBACK)); + + m64p_rom_header rom_header; + CoreDoCommand(M64CMD_ROM_GET_HEADER, sizeof(rom_header), &rom_header); + + g_hle.hle_gfx = ConfigGetParamBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_GFX); + g_hle.hle_aud = ConfigGetParamBool(l_ConfigRspHle, RSP_HLE_CONFIG_HLE_AUD); + + /* notify fallback plugin */ + if (l_InitiateRSP) { + l_InitiateRSP(Rsp_Info, CycleCount); + } +} + +EXPORT void CALL RomClosed(void) +{ + /* notify fallback plugin */ + if (l_RomClosed) { + l_RomClosed(); + } +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/re2.c b/libmupen64plus/mupen64plus-rsp-hle/src/re2.c new file mode 100644 index 0000000000..8ae178804d --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/re2.c @@ -0,0 +1,224 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - re2.c * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2016 Gilles Siberlin * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include +#include + +#include "hle_external.h" +#include "hle_internal.h" +#include "memory.h" + +#define SATURATE8(x) ((unsigned int) x <= 255 ? x : (x < 0 ? 0: 255)) + +/************************************************************************** + * Resident evil 2 ucodes + **************************************************************************/ +void resize_bilinear_task(struct hle_t* hle) +{ + int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA); + + int src_addr = *dram_u32(hle, data_ptr); + int dst_addr = *dram_u32(hle, data_ptr + 4); + int dst_width = *dram_u32(hle, data_ptr + 8); + int dst_height = *dram_u32(hle, data_ptr + 12); + int x_ratio = *dram_u32(hle, data_ptr + 16); + int y_ratio = *dram_u32(hle, data_ptr + 20); +#if 0 /* unused, but keep it for documentation purpose */ + int dst_stride = *dram_u32(hle, data_ptr + 24); +#endif + int src_offset = *dram_u32(hle, data_ptr + 36); + + int a, b, c ,d, index, y_index, xr, yr, blue, green, red, addr, i, j; + long long x, y, x_diff, y_diff, one_min_x_diff, one_min_y_diff; + unsigned short pixel; + + src_addr += (src_offset >> 16) * (320 * 3); + x = y = 0; + + for(i = 0; i < dst_height; i++) + { + yr = (int)(y >> 16); + y_diff = y - (yr << 16); + one_min_y_diff = 65536 - y_diff; + y_index = yr * 320; + x = 0; + + for(j = 0; j < dst_width; j++) + { + xr = (int)(x >> 16); + x_diff = x - (xr << 16); + one_min_x_diff = 65536 - x_diff; + index = y_index + xr; + addr = src_addr + (index * 3); + + dram_load_u8(hle, (uint8_t*)&a, addr, 3); + dram_load_u8(hle, (uint8_t*)&b, (addr + 3), 3); + dram_load_u8(hle, (uint8_t*)&c, (addr + (320 * 3)), 3); + dram_load_u8(hle, (uint8_t*)&d, (addr + (320 * 3) + 3), 3); + + blue = (int)(((a&0xff)*one_min_x_diff*one_min_y_diff + (b&0xff)*x_diff*one_min_y_diff + + (c&0xff)*y_diff*one_min_x_diff + (d&0xff)*x_diff*y_diff) >> 32); + + green = (int)((((a>>8)&0xff)*one_min_x_diff*one_min_y_diff + ((b>>8)&0xff)*x_diff*one_min_y_diff + + ((c>>8)&0xff)*y_diff*one_min_x_diff + ((d>>8)&0xff)*x_diff*y_diff) >> 32); + + red = (int)((((a>>16)&0xff)*one_min_x_diff*one_min_y_diff + ((b>>16)&0xff)*x_diff*one_min_y_diff + + ((c>>16)&0xff)*y_diff*one_min_x_diff + ((d>>16)&0xff)*x_diff*y_diff) >> 32); + + blue = (blue >> 3) & 0x001f; + green = (green >> 3) & 0x001f; + red = (red >> 3) & 0x001f; + pixel = (red << 11) | (green << 6) | (blue << 1) | 1; + + dram_store_u16(hle, &pixel, dst_addr, 1); + dst_addr += 2; + + x += x_ratio; + } + y += y_ratio; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} + +static uint32_t YCbCr_to_RGBA(uint8_t Y, uint8_t Cb, uint8_t Cr) +{ + int r, g, b; + + r = (int)(((double)Y * 0.582199097) + (0.701004028 * (double)(Cr - 128))); + g = (int)(((double)Y * 0.582199097) - (0.357070923 * (double)(Cr - 128)) - (0.172073364 * (double)(Cb - 128))); + b = (int)(((double)Y * 0.582199097) + (0.886001587 * (double)(Cb - 128))); + + r = SATURATE8(r); + g = SATURATE8(g); + b = SATURATE8(b); + + return (r << 24) | (g << 16) | (b << 8) | 0; +} + +void decode_video_frame_task(struct hle_t* hle) +{ + int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA); + + int pLuminance = *dram_u32(hle, data_ptr); + int pCb = *dram_u32(hle, data_ptr + 4); + int pCr = *dram_u32(hle, data_ptr + 8); + int pDestination = *dram_u32(hle, data_ptr + 12); + int nMovieWidth = *dram_u32(hle, data_ptr + 16); + int nMovieHeight = *dram_u32(hle, data_ptr + 20); +#if 0 /* unused, but keep it for documentation purpose */ + int nRowsPerDMEM = *dram_u32(hle, data_ptr + 24); + int nDMEMPerFrame = *dram_u32(hle, data_ptr + 28); + int nLengthSkipCount = *dram_u32(hle, data_ptr + 32); +#endif + int nScreenDMAIncrement = *dram_u32(hle, data_ptr + 36); + + int i, j; + uint8_t Y, Cb, Cr; + uint32_t pixel; + int pY_1st_row, pY_2nd_row, pDest_1st_row, pDest_2nd_row; + + for (i = 0; i < nMovieHeight; i += 2) + { + pY_1st_row = pLuminance; + pY_2nd_row = pLuminance + nMovieWidth; + pDest_1st_row = pDestination; + pDest_2nd_row = pDestination + (nScreenDMAIncrement >> 1); + + for (j = 0; j < nMovieWidth; j += 2) + { + dram_load_u8(hle, (uint8_t*)&Cb, pCb++, 1); + dram_load_u8(hle, (uint8_t*)&Cr, pCr++, 1); + + /*1st row*/ + dram_load_u8(hle, (uint8_t*)&Y, pY_1st_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_1st_row, 1); + pDest_1st_row += 4; + + dram_load_u8(hle, (uint8_t*)&Y, pY_1st_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_1st_row, 1); + pDest_1st_row += 4; + + /*2nd row*/ + dram_load_u8(hle, (uint8_t*)&Y, pY_2nd_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_2nd_row, 1); + pDest_2nd_row += 4; + + dram_load_u8(hle, (uint8_t*)&Y, pY_2nd_row++, 1); + pixel = YCbCr_to_RGBA(Y, Cb, Cr); + dram_store_u32(hle, &pixel, pDest_2nd_row, 1); + pDest_2nd_row += 4; + } + + pLuminance += (nMovieWidth << 1); + pDestination += nScreenDMAIncrement; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} + +void fill_video_double_buffer_task(struct hle_t* hle) +{ + int data_ptr = *dmem_u32(hle, TASK_UCODE_DATA); + + int pSrc = *dram_u32(hle, data_ptr); + int pDest = *dram_u32(hle, data_ptr + 0x4); + int width = *dram_u32(hle, data_ptr + 0x8) >> 1; + int height = *dram_u32(hle, data_ptr + 0x10) << 1; + int stride = *dram_u32(hle, data_ptr + 0x1c) >> 1; + + assert((*dram_u32(hle, data_ptr + 0x28) >> 16) == 0x8000); + +#if 0 /* unused, but keep it for documentation purpose */ + int arg3 = *dram_u32(hle, data_ptr + 0xc); + int arg5 = *dram_u32(hle, data_ptr + 0x14); + int arg6 = *dram_u32(hle, data_ptr + 0x18); +#endif + + int i, j; + int r, g, b; + uint32_t pixel, pixel1, pixel2; + + for(i = 0; i < height; i++) + { + for(j = 0; j < width; j=j+4) + { + pixel1 = *dram_u32(hle, pSrc+j); + pixel2 = *dram_u32(hle, pDest+j); + + r = (((pixel1 >> 24) & 0xff) + ((pixel2 >> 24) & 0xff)) >> 1; + g = (((pixel1 >> 16) & 0xff) + ((pixel2 >> 16) & 0xff)) >> 1; + b = (((pixel1 >> 8) & 0xff) + ((pixel2 >> 8) & 0xff)) >> 1; + + pixel = (r << 24) | (g << 16) | (b << 8) | 0; + + dram_store_u32(hle, &pixel, pDest+j, 1); + } + pSrc += stride; + pDest += stride; + } + + rsp_break(hle, SP_STATUS_TASKDONE); +} diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode1.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode1.cpp deleted file mode 100644 index eb869cbb7b..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode1.cpp +++ /dev/null @@ -1,951 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode1.cpp * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include - -extern "C" { - #include "hle.h" - #include "alist_internal.h" -} - -//#include "rsp.h" -//#define SAFE_MEMORY -/* -#ifndef SAFE_MEMORY -# define wr8 (src , address); -# define rd8 (dest, address); -# define wr16 (src, address); -# define rd16 (dest, address); -# define wr32 (src, address); -# define rd32 (dest, address); -# define wr64 (src, address); -# define rd64 (dest, address); -# define dmamem (dest, src, size) memcpy (dest, src, size); -# define clrmem (dest, size) memset (dest, 0, size); -#else - void wr8 (u8 src, void *address); - void rd8 (u8 dest, void *address); - void wr16 (u16 src, void *address); - void rd16 (u16 dest, void *address); - void wr32 (u16 src, void *address); - void rd32 (u16 dest, void *address); - void wr64 (u16 src, void *address); - void rd64 (u16 dest, void *address); - void dmamem (void *dest, void *src, int size); - void clrmem (void *dest, int size); -#endif -*/ -/******** DMEM Memory Map for ABI 1 *************** -Address/Range Description -------------- ------------------------------- -0x000..0x2BF UCodeData - 0x000-0x00F Constants - 0000 0001 0002 FFFF 0020 0800 7FFF 4000 - 0x010-0x02F Function Jump Table (16 Functions * 2 bytes each = 32) 0x20 - 0x030-0x03F Constants - F000 0F00 00F0 000F 0001 0010 0100 1000 - 0x040-0x03F Used by the Envelope Mixer (But what for?) - 0x070-0x07F Used by the Envelope Mixer (But what for?) -0x2C0..0x31F -0x320..0x35F Segments -0x360 Audio In Buffer (Location) -0x362 Audio Out Buffer (Location) -0x364 Audio Buffer Size (Location) -0x366 Initial Volume for Left Channel -0x368 Initial Volume for Right Channel -0x36A Auxillary Buffer #1 (Location) -0x36C Auxillary Buffer #2 (Location) -0x36E Auxillary Buffer #3 (Location) -0x370 Loop Value (shared location) -0x370 Target Volume (Left) -0x372 Ramp?? (Left) -0x374 Rate?? (Left) -0x376 Target Volume (Right) -0x378 Ramp?? (Right) -0x37A Rate?? (Right) -0x37C Dry?? -0x37E Wet?? -0x380..0x4BF Alist data -0x4C0..0x4FF ADPCM CodeBook -0x500..0x5BF -0x5C0..0xF7F Buffers... -0xF80..0xFFF -***************************************************/ -#ifdef USE_EXPANSION - #define MEMMASK 0x7FFFFF -#else - #define MEMMASK 0x3FFFFF -#endif - -static void SPNOOP (u32 inst1, u32 inst2) { - //MessageBox (NULL, "Unknown Audio Command in ABI 1", "Audio HLE Error", MB_OK); -} - -u32 SEGMENTS[0x10]; // 0x0320 -// T8 = 0x360 -u16 AudioInBuffer; // 0x0000(T8) -u16 AudioOutBuffer; // 0x0002(T8) -u16 AudioCount; // 0x0004(T8) -s16 Vol_Left; // 0x0006(T8) -s16 Vol_Right; // 0x0008(T8) -u16 AudioAuxA; // 0x000A(T8) -u16 AudioAuxC; // 0x000C(T8) -u16 AudioAuxE; // 0x000E(T8) -u32 loopval; // 0x0010(T8) // Value set by A_SETLOOP : Possible conflict with SETVOLUME??? -s16 VolTrg_Left; // 0x0010(T8) -s32 VolRamp_Left; // m_LeftVolTarget -//u16 VolRate_Left; // m_LeftVolRate -s16 VolTrg_Right; // m_RightVol -s32 VolRamp_Right; // m_RightVolTarget -//u16 VolRate_Right; // m_RightVolRate -s16 Env_Dry; // 0x001C(T8) -s16 Env_Wet; // 0x001E(T8) - -u8 BufferSpace[0x10000]; - -short hleMixerWorkArea[256]; -u16 adpcmtable[0x88]; - -extern const u16 ResampleLUT [0x200] = { - 0x0C39, 0x66AD, 0x0D46, 0xFFDF, 0x0B39, 0x6696, 0x0E5F, 0xFFD8, - 0x0A44, 0x6669, 0x0F83, 0xFFD0, 0x095A, 0x6626, 0x10B4, 0xFFC8, - 0x087D, 0x65CD, 0x11F0, 0xFFBF, 0x07AB, 0x655E, 0x1338, 0xFFB6, - 0x06E4, 0x64D9, 0x148C, 0xFFAC, 0x0628, 0x643F, 0x15EB, 0xFFA1, - 0x0577, 0x638F, 0x1756, 0xFF96, 0x04D1, 0x62CB, 0x18CB, 0xFF8A, - 0x0435, 0x61F3, 0x1A4C, 0xFF7E, 0x03A4, 0x6106, 0x1BD7, 0xFF71, - 0x031C, 0x6007, 0x1D6C, 0xFF64, 0x029F, 0x5EF5, 0x1F0B, 0xFF56, - 0x022A, 0x5DD0, 0x20B3, 0xFF48, 0x01BE, 0x5C9A, 0x2264, 0xFF3A, - 0x015B, 0x5B53, 0x241E, 0xFF2C, 0x0101, 0x59FC, 0x25E0, 0xFF1E, - 0x00AE, 0x5896, 0x27A9, 0xFF10, 0x0063, 0x5720, 0x297A, 0xFF02, - 0x001F, 0x559D, 0x2B50, 0xFEF4, 0xFFE2, 0x540D, 0x2D2C, 0xFEE8, - 0xFFAC, 0x5270, 0x2F0D, 0xFEDB, 0xFF7C, 0x50C7, 0x30F3, 0xFED0, - 0xFF53, 0x4F14, 0x32DC, 0xFEC6, 0xFF2E, 0x4D57, 0x34C8, 0xFEBD, - 0xFF0F, 0x4B91, 0x36B6, 0xFEB6, 0xFEF5, 0x49C2, 0x38A5, 0xFEB0, - 0xFEDF, 0x47ED, 0x3A95, 0xFEAC, 0xFECE, 0x4611, 0x3C85, 0xFEAB, - 0xFEC0, 0x4430, 0x3E74, 0xFEAC, 0xFEB6, 0x424A, 0x4060, 0xFEAF, - 0xFEAF, 0x4060, 0x424A, 0xFEB6, 0xFEAC, 0x3E74, 0x4430, 0xFEC0, - 0xFEAB, 0x3C85, 0x4611, 0xFECE, 0xFEAC, 0x3A95, 0x47ED, 0xFEDF, - 0xFEB0, 0x38A5, 0x49C2, 0xFEF5, 0xFEB6, 0x36B6, 0x4B91, 0xFF0F, - 0xFEBD, 0x34C8, 0x4D57, 0xFF2E, 0xFEC6, 0x32DC, 0x4F14, 0xFF53, - 0xFED0, 0x30F3, 0x50C7, 0xFF7C, 0xFEDB, 0x2F0D, 0x5270, 0xFFAC, - 0xFEE8, 0x2D2C, 0x540D, 0xFFE2, 0xFEF4, 0x2B50, 0x559D, 0x001F, - 0xFF02, 0x297A, 0x5720, 0x0063, 0xFF10, 0x27A9, 0x5896, 0x00AE, - 0xFF1E, 0x25E0, 0x59FC, 0x0101, 0xFF2C, 0x241E, 0x5B53, 0x015B, - 0xFF3A, 0x2264, 0x5C9A, 0x01BE, 0xFF48, 0x20B3, 0x5DD0, 0x022A, - 0xFF56, 0x1F0B, 0x5EF5, 0x029F, 0xFF64, 0x1D6C, 0x6007, 0x031C, - 0xFF71, 0x1BD7, 0x6106, 0x03A4, 0xFF7E, 0x1A4C, 0x61F3, 0x0435, - 0xFF8A, 0x18CB, 0x62CB, 0x04D1, 0xFF96, 0x1756, 0x638F, 0x0577, - 0xFFA1, 0x15EB, 0x643F, 0x0628, 0xFFAC, 0x148C, 0x64D9, 0x06E4, - 0xFFB6, 0x1338, 0x655E, 0x07AB, 0xFFBF, 0x11F0, 0x65CD, 0x087D, - 0xFFC8, 0x10B4, 0x6626, 0x095A, 0xFFD0, 0x0F83, 0x6669, 0x0A44, - 0xFFD8, 0x0E5F, 0x6696, 0x0B39, 0xFFDF, 0x0D46, 0x66AD, 0x0C39 -}; - -static void CLEARBUFF (u32 inst1, u32 inst2) { - u32 addr = (u32)(inst1 & 0xffff); - u32 count = (u32)(inst2 & 0xffff); - addr &= 0xFFFC; - memset(BufferSpace+addr, 0, (count+3)&0xFFFC); -} - -//FILE *dfile = fopen ("d:\\envmix.txt", "wt"); - -static void ENVMIXER (u32 inst1, u32 inst2) { - //static int envmixcnt = 0; - u8 flags = (u8)((inst1 >> 16) & 0xff); - u32 addy = (inst2 & 0xFFFFFF);// + SEGMENTS[(inst2>>24)&0xf]; - //static -// ********* Make sure these conditions are met... *********** - /*if ((AudioInBuffer | AudioOutBuffer | AudioAuxA | AudioAuxC | AudioAuxE | AudioCount) & 0x3) { - MessageBox (NULL, "Unaligned EnvMixer... please report this to Azimer with the following information: RomTitle, Place in the rom it occurred, and any save state just before the error", "AudioHLE Error", MB_OK); - }*/ -// ------------------------------------------------------------ - short *inp=(short *)(BufferSpace+AudioInBuffer); - short *out=(short *)(BufferSpace+AudioOutBuffer); - short *aux1=(short *)(BufferSpace+AudioAuxA); - short *aux2=(short *)(BufferSpace+AudioAuxC); - short *aux3=(short *)(BufferSpace+AudioAuxE); - s32 MainR; - s32 MainL; - s32 AuxR; - s32 AuxL; - int i1,o1,a1,a2=0,a3=0; - unsigned short AuxIncRate=1; - short zero[8]; - memset(zero,0,16); - s32 LVol, RVol; - s32 LAcc, RAcc; - s32 LTrg, RTrg; - s16 Wet, Dry; - u32 ptr = 0; - s32 RRamp, LRamp; - s32 LAdderStart, RAdderStart, LAdderEnd, RAdderEnd; - s32 oMainR, oMainL, oAuxR, oAuxL; - - //envmixcnt++; - - //fprintf (dfile, "\n----------------------------------------------------\n"); - if (flags & A_INIT) { - LVol = ((Vol_Left * (s32)VolRamp_Left)); - RVol = ((Vol_Right * (s32)VolRamp_Right)); - Wet = (s16)Env_Wet; Dry = (s16)Env_Dry; // Save Wet/Dry values - LTrg = (VolTrg_Left << 16); RTrg = (VolTrg_Right << 16); // Save Current Left/Right Targets - LAdderStart = Vol_Left << 16; - RAdderStart = Vol_Right << 16; - LAdderEnd = LVol; - RAdderEnd = RVol; - RRamp = VolRamp_Right; - LRamp = VolRamp_Left; - } else { - // Load LVol, RVol, LAcc, and RAcc (all 32bit) - // Load Wet, Dry, LTrg, RTrg - memcpy((u8 *)hleMixerWorkArea, (rsp.RDRAM+addy), 80); - Wet = *(s16 *)(hleMixerWorkArea + 0); // 0-1 - Dry = *(s16 *)(hleMixerWorkArea + 2); // 2-3 - LTrg = *(s32 *)(hleMixerWorkArea + 4); // 4-5 - RTrg = *(s32 *)(hleMixerWorkArea + 6); // 6-7 - LRamp= *(s32 *)(hleMixerWorkArea + 8); // 8-9 (hleMixerWorkArea is a 16bit pointer) - RRamp= *(s32 *)(hleMixerWorkArea + 10); // 10-11 - LAdderEnd = *(s32 *)(hleMixerWorkArea + 12); // 12-13 - RAdderEnd = *(s32 *)(hleMixerWorkArea + 14); // 14-15 - LAdderStart = *(s32 *)(hleMixerWorkArea + 16); // 12-13 - RAdderStart = *(s32 *)(hleMixerWorkArea + 18); // 14-15 - } - - if(!(flags&A_AUX)) { - AuxIncRate=0; - aux2=aux3=zero; - } - - oMainL = (Dry * (LTrg>>16) + 0x4000) >> 15; - oAuxL = (Wet * (LTrg>>16) + 0x4000) >> 15; - oMainR = (Dry * (RTrg>>16) + 0x4000) >> 15; - oAuxR = (Wet * (RTrg>>16) + 0x4000) >> 15; - - for (int y = 0; y < AudioCount; y += 0x10) { - - if (LAdderStart != LTrg) { - LAcc = LAdderStart; - LVol = (LAdderEnd - LAdderStart) >> 3; - LAdderEnd = (s32) (((s64)LAdderEnd * (s64)LRamp) >> 16); - LAdderStart = (s32) (((s64)LAcc * (s64)LRamp) >> 16); - } else { - LAcc = LTrg; - LVol = 0; - } - - if (RAdderStart != RTrg) { - RAcc = RAdderStart; - RVol = (RAdderEnd - RAdderStart) >> 3; - RAdderEnd = (s32) (((s64)RAdderEnd * (s64)RRamp) >> 16); - RAdderStart = (s32) (((s64)RAcc * (s64)RRamp) >> 16); - } else { - RAcc = RTrg; - RVol = 0; - } - - for (int x = 0; x < 8; x++) { - i1=(int)inp[ptr^S]; - o1=(int)out[ptr^S]; - a1=(int)aux1[ptr^S]; - if (AuxIncRate) { - a2=(int)aux2[ptr^S]; - a3=(int)aux3[ptr^S]; - } - // TODO: here... - //LAcc = LTrg; - //RAcc = RTrg; - - LAcc += LVol; - RAcc += RVol; - - if (LVol <= 0) { // Decrementing - if (LAcc < LTrg) { - LAcc = LTrg; - LAdderStart = LTrg; - MainL = oMainL; - AuxL = oAuxL; - } else { - MainL = (Dry * ((s32)LAcc>>16) + 0x4000) >> 15; - AuxL = (Wet * ((s32)LAcc>>16) + 0x4000) >> 15; - } - } else { - if (LAcc > LTrg) { - LAcc = LTrg; - LAdderStart = LTrg; - MainL = oMainL; - AuxL = oAuxL; - } else { - MainL = (Dry * ((s32)LAcc>>16) + 0x4000) >> 15; - AuxL = (Wet * ((s32)LAcc>>16) + 0x4000) >> 15; - } - } - - if (RVol <= 0) { // Decrementing - if (RAcc < RTrg) { - RAcc = RTrg; - RAdderStart = RTrg; - MainR = oMainR; - AuxR = oAuxR; - } else { - MainR = (Dry * ((s32)RAcc>>16) + 0x4000) >> 15; - AuxR = (Wet * ((s32)RAcc>>16) + 0x4000) >> 15; - } - } else { - if (RAcc > RTrg) { - RAcc = RTrg; - RAdderStart = RTrg; - MainR = oMainR; - AuxR = oAuxR; - } else { - MainR = (Dry * ((s32)RAcc>>16) + 0x4000) >> 15; - AuxR = (Wet * ((s32)RAcc>>16) + 0x4000) >> 15; - } - } - - //fprintf (dfile, "%04X ", (LAcc>>16)); - - /*MainL = (((s64)Dry*2 * (s64)(LAcc>>16)) + 0x8000) >> 16; - MainR = (((s64)Dry*2 * (s64)(RAcc>>16)) + 0x8000) >> 16; - AuxL = (((s64)Wet*2 * (s64)(LAcc>>16)) + 0x8000) >> 16; - AuxR = (((s64)Wet*2 * (s64)(RAcc>>16)) + 0x8000) >> 16;*/ -/* - if (MainL>32767) MainL = 32767; - else if (MainL<-32768) MainL = -32768; - if (MainR>32767) MainR = 32767; - else if (MainR<-32768) MainR = -32768; - if (AuxL>32767) AuxL = 32767; - else if (AuxL<-32768) AuxR = -32768; - if (AuxR>32767) AuxR = 32767; - else if (AuxR<-32768) AuxR = -32768;*/ - /* - MainR = (Dry * RTrg + 0x10000) >> 15; - MainL = (Dry * LTrg + 0x10000) >> 15; - AuxR = (Wet * RTrg + 0x8000) >> 16; - AuxL = (Wet * LTrg + 0x8000) >> 16;*/ - - o1+=(/*(o1*0x7fff)+*/(i1*MainR)+0x4000)>>15; - a1+=(/*(a1*0x7fff)+*/(i1*MainL)+0x4000)>>15; - -/* o1=((s64)(((s64)o1*0xfffe)+((s64)i1*MainR*2)+0x8000)>>16); - - a1=((s64)(((s64)a1*0xfffe)+((s64)i1*MainL*2)+0x8000)>>16);*/ - - if(o1>32767) o1=32767; - else if(o1<-32768) o1=-32768; - - if(a1>32767) a1=32767; - else if(a1<-32768) a1=-32768; - - out[ptr^S]=o1; - aux1[ptr^S]=a1; - if (AuxIncRate) { - //a2=((s64)(((s64)a2*0xfffe)+((s64)i1*AuxR*2)+0x8000)>>16); - - //a3=((s64)(((s64)a3*0xfffe)+((s64)i1*AuxL*2)+0x8000)>>16); - a2+=(/*(a2*0x7fff)+*/(i1*AuxR)+0x4000)>>15; - a3+=(/*(a3*0x7fff)+*/(i1*AuxL)+0x4000)>>15; - - if(a2>32767) a2=32767; - else if(a2<-32768) a2=-32768; - - if(a3>32767) a3=32767; - else if(a3<-32768) a3=-32768; - - aux2[ptr^S]=a2; - aux3[ptr^S]=a3; - } - ptr++; - } - } - - /*LAcc = LAdderEnd; - RAcc = RAdderEnd;*/ - - *(s16 *)(hleMixerWorkArea + 0) = Wet; // 0-1 - *(s16 *)(hleMixerWorkArea + 2) = Dry; // 2-3 - *(s32 *)(hleMixerWorkArea + 4) = LTrg; // 4-5 - *(s32 *)(hleMixerWorkArea + 6) = RTrg; // 6-7 - *(s32 *)(hleMixerWorkArea + 8) = LRamp; // 8-9 (hleMixerWorkArea is a 16bit pointer) - *(s32 *)(hleMixerWorkArea + 10) = RRamp; // 10-11 - *(s32 *)(hleMixerWorkArea + 12) = LAdderEnd; // 12-13 - *(s32 *)(hleMixerWorkArea + 14) = RAdderEnd; // 14-15 - *(s32 *)(hleMixerWorkArea + 16) = LAdderStart; // 12-13 - *(s32 *)(hleMixerWorkArea + 18) = RAdderStart; // 14-15 - memcpy(rsp.RDRAM+addy, (u8 *)hleMixerWorkArea,80); -} - -static void RESAMPLE (u32 inst1, u32 inst2) { - unsigned char Flags=(u8)((inst1>>16)&0xff); - unsigned int Pitch=((inst1&0xffff))<<1; - u32 addy = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned int Accum=0; - unsigned int location; - s16 *lut/*, *lut2*/; - short *dst; - s16 *src; - dst=(short *)(BufferSpace); - src=(s16 *)(BufferSpace); - u32 srcPtr=(AudioInBuffer/2); - u32 dstPtr=(AudioOutBuffer/2); - s32 temp; - s32 accum; -/* - if (addy > (1024*1024*8)) - addy = (inst2 & 0xffffff); -*/ - srcPtr -= 4; - - if ((Flags & 0x1) == 0) { - //memcpy (src+srcPtr, rsp.RDRAM+addy, 0x8); - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = ((u16 *)rsp.RDRAM)[((addy/2)+x)^S]; - Accum = *(u16 *)(rsp.RDRAM+addy+10); - } else { - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = 0;//*(u16 *)(rsp.RDRAM+((addy+x)^2)); - } - - for(int i=0;i < ((AudioCount+0xf)&0xFFF0)/2;i++) { - //location = (((Accum * 0x40) >> 0x10) * 8); - // location is the fractional position between two samples - location = (Accum >> 0xa) * 4; - lut = (s16*)ResampleLUT + location; - - // mov eax, dword ptr [src+srcPtr]; - // movsx edx, word ptr [lut]; - // shl edx, 1 - // imul edx - // test eax, 08000h - // setz ecx - // shl ecx, 16 - // xor eax, 08000h - // add eax, ecx - // and edx, 0f000h - - // imul - temp = ((s32)*(s16*)(src+((srcPtr+0)^S))*((s32)((s16)lut[0]))); - accum = (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+1)^S))*((s32)((s16)lut[1]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+2)^S))*((s32)((s16)lut[2]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+3)^S))*((s32)((s16)lut[3]))); - accum += (s32)(temp >> 15); - - if (accum > 32767) accum = 32767; - if (accum < -32768) accum = -32768; - - dst[dstPtr^S] = (accum); - dstPtr++; - Accum += Pitch; - srcPtr += (Accum>>16); - Accum&=0xffff; - } - for (int x=0; x < 4; x++) - ((u16 *)rsp.RDRAM)[((addy/2)+x)^S] = src[(srcPtr+x)^S]; - //memcpy (RSWORK, src+srcPtr, 0x8); - *(u16 *)(rsp.RDRAM+addy+10) = Accum; -} - -static void SETVOL (u32 inst1, u32 inst2) { -// Might be better to unpack these depending on the flags... - u8 flags = (u8)((inst1 >> 16) & 0xff); - u16 vol = (s16)(inst1 & 0xffff); - //u16 voltarg =(u16)((inst2 >> 16)&0xffff); - u16 volrate = (u16)((inst2 & 0xffff)); - - if (flags & A_AUX) { - Env_Dry = (s16)vol; // m_MainVol - Env_Wet = (s16)volrate; // m_AuxVol - return; - } - - if(flags & A_VOL) { // Set the Source(start) Volumes - if(flags & A_LEFT) { - Vol_Left = (s16)vol; // m_LeftVolume - } else { // A_RIGHT - Vol_Right = (s16)vol; // m_RightVolume - } - return; - } - -//0x370 Loop Value (shared location) -//0x370 Target Volume (Left) -//u16 VolRamp_Left; // 0x0012(T8) - if(flags & A_LEFT) { // Set the Ramping values Target, Ramp - //loopval = (((u32)vol << 0x10) | (u32)voltarg); - VolTrg_Left = (s16)inst1; // m_LeftVol - //VolRamp_Left = (s32)inst2; - VolRamp_Left = (s32)inst2;//(u16)(inst2) | (s32)(s16)(inst2 << 0x10); - //fprintf (dfile, "Ramp Left: %f\n", (float)VolRamp_Left/65536.0); - //fprintf (dfile, "Ramp Left: %08X\n", inst2); - //VolRamp_Left = (s16)voltarg; // m_LeftVolTarget - //VolRate_Left = (s16)volrate; // m_LeftVolRate - } else { // A_RIGHT - VolTrg_Right = (s16)inst1; // m_RightVol - //VolRamp_Right = (s32)inst2; - VolRamp_Right = (s32)inst2;//(u16)(inst2 >> 0x10) | (s32)(s16)(inst2 << 0x10); - //fprintf (dfile, "Ramp Right: %f\n", (float)VolRamp_Right/65536.0); - //fprintf (dfile, "Ramp Right: %08X\n", inst2); - //VolRamp_Right = (s16)voltarg; // m_RightVolTarget - //VolRate_Right = (s16)volrate; // m_RightVolRate - } -} - -static void UNKNOWN (u32 inst1, u32 inst2) {} - -static void SETLOOP (u32 inst1, u32 inst2) { - loopval = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - //VolTrg_Left = (s16)(loopval>>16); // m_LeftVol - //VolRamp_Left = (s16)(loopval); // m_LeftVolTarget -} - -static void ADPCM (u32 inst1, u32 inst2) { // Work in progress! :) - unsigned char Flags=(u8)(inst1>>16)&0xff; - //unsigned short Gain=(u16)(inst1&0xffff); - unsigned int Address=(inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned short inPtr=0; - //short *out=(s16 *)(testbuff+(AudioOutBuffer>>2)); - short *out=(short *)(BufferSpace+AudioOutBuffer); - //unsigned char *in=(unsigned char *)(BufferSpace+AudioInBuffer); - short count=(short)AudioCount; - unsigned char icode; - unsigned char code; - int vscale; - unsigned short index; - unsigned short j; - int a[8]; - short *book1,*book2; -/* - if (Address > (1024*1024*8)) - Address = (inst2 & 0xffffff); -*/ - memset(out,0,32); - - if(!(Flags&0x1)) - { - if(Flags&0x2) { - memcpy(out,&rsp.RDRAM[loopval&MEMMASK],32); - } else { - memcpy(out,&rsp.RDRAM[Address],32); - } - } - - int l1=out[14^S]; - int l2=out[15^S]; - int inp1[8]; - int inp2[8]; - out+=16; - while(count>0) - { - // the first interation through, these values are - // either 0 in the case of A_INIT, from a special - // area of memory in the case of A_LOOP or just - // the values we calculated the last time - - code=BufferSpace[(AudioInBuffer+inPtr)^S8]; - index=code&0xf; - index<<=4; // index into the adpcm code table - book1=(short *)&adpcmtable[index]; - book2=book1+8; - code>>=4; // upper nibble is scale - vscale=(0x8000>>((12-code)-1)); // very strange. 0x8000 would be .5 in 16:16 format - // so this appears to be a fractional scale based - // on the 12 based inverse of the scale value. note - // that this could be negative, in which case we do - // not use the calculated vscale value... see the - // if(code>12) check below - - inPtr++; // coded adpcm data lies next - j=0; - while(j<8) // loop of 8, for 8 coded nibbles from 4 bytes - // which yields 8 short pcm values - { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp1[j]=(s16)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp1[j]=(s16)((icode&0xf)<<12); - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - j=0; - while(j<8) - { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp2[j]=(short)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp2[j]=(short)((icode&0xf)<<12); - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp1[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp1[0]; - a[1]+=(int)inp1[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp1[0]; - a[2]+=(int)book2[0]*inp1[1]; - a[2]+=(int)inp1[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp1[0]; - a[3]+=(int)book2[1]*inp1[1]; - a[3]+=(int)book2[0]*inp1[2]; - a[3]+=(int)inp1[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp1[0]; - a[4]+=(int)book2[2]*inp1[1]; - a[4]+=(int)book2[1]*inp1[2]; - a[4]+=(int)book2[0]*inp1[3]; - a[4]+=(int)inp1[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp1[0]; - a[5]+=(int)book2[3]*inp1[1]; - a[5]+=(int)book2[2]*inp1[2]; - a[5]+=(int)book2[1]*inp1[3]; - a[5]+=(int)book2[0]*inp1[4]; - a[5]+=(int)inp1[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp1[0]; - a[6]+=(int)book2[4]*inp1[1]; - a[6]+=(int)book2[3]*inp1[2]; - a[6]+=(int)book2[2]*inp1[3]; - a[6]+=(int)book2[1]*inp1[4]; - a[6]+=(int)book2[0]*inp1[5]; - a[6]+=(int)inp1[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp1[0]; - a[7]+=(int)book2[5]*inp1[1]; - a[7]+=(int)book2[4]*inp1[2]; - a[7]+=(int)book2[3]*inp1[3]; - a[7]+=(int)book2[2]*inp1[4]; - a[7]+=(int)book2[1]*inp1[5]; - a[7]+=(int)book2[0]*inp1[6]; - a[7]+=(int)inp1[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp2[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp2[0]; - a[1]+=(int)inp2[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp2[0]; - a[2]+=(int)book2[0]*inp2[1]; - a[2]+=(int)inp2[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp2[0]; - a[3]+=(int)book2[1]*inp2[1]; - a[3]+=(int)book2[0]*inp2[2]; - a[3]+=(int)inp2[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp2[0]; - a[4]+=(int)book2[2]*inp2[1]; - a[4]+=(int)book2[1]*inp2[2]; - a[4]+=(int)book2[0]*inp2[3]; - a[4]+=(int)inp2[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp2[0]; - a[5]+=(int)book2[3]*inp2[1]; - a[5]+=(int)book2[2]*inp2[2]; - a[5]+=(int)book2[1]*inp2[3]; - a[5]+=(int)book2[0]*inp2[4]; - a[5]+=(int)inp2[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp2[0]; - a[6]+=(int)book2[4]*inp2[1]; - a[6]+=(int)book2[3]*inp2[2]; - a[6]+=(int)book2[2]*inp2[3]; - a[6]+=(int)book2[1]*inp2[4]; - a[6]+=(int)book2[0]*inp2[5]; - a[6]+=(int)inp2[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp2[0]; - a[7]+=(int)book2[5]*inp2[1]; - a[7]+=(int)book2[4]*inp2[2]; - a[7]+=(int)book2[3]*inp2[3]; - a[7]+=(int)book2[2]*inp2[4]; - a[7]+=(int)book2[1]*inp2[5]; - a[7]+=(int)book2[0]*inp2[6]; - a[7]+=(int)inp2[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - count-=32; - } - out-=16; - memcpy(&rsp.RDRAM[Address],out,32); -} - -static void LOADBUFF (u32 inst1, u32 inst2) { // memcpy causes static... endianess issue :( - u32 v0; - //u32 cnt; - if (AudioCount == 0) - return; - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (BufferSpace+(AudioInBuffer&0xFFFC), rsp.RDRAM+v0, (AudioCount+3)&0xFFFC); -} - -static void SAVEBUFF (u32 inst1, u32 inst2) { // memcpy causes static... endianess issue :( - u32 v0; - //u32 cnt; - if (AudioCount == 0) - return; - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (rsp.RDRAM+v0, BufferSpace+(AudioOutBuffer&0xFFFC), (AudioCount+3)&0xFFFC); -} - -static void SETBUFF (u32 inst1, u32 inst2) { // Should work ;-) - if ((inst1 >> 0x10) & 0x8) { // A_AUX - Auxillary Sound Buffer Settings - AudioAuxA = u16(inst1); - AudioAuxC = u16((inst2 >> 0x10)); - AudioAuxE = u16(inst2); - } else { // A_MAIN - Main Sound Buffer Settings - AudioInBuffer = u16(inst1); // 0x00 - AudioOutBuffer = u16((inst2 >> 0x10)); // 0x02 - AudioCount = u16(inst2); // 0x04 - } -} - -static void DMEMMOVE (u32 inst1, u32 inst2) { // Doesn't sound just right?... will fix when HLE is ready - 03-11-01 - u32 v0, v1; - u32 cnt; - if ((inst2 & 0xffff)==0) - return; - v0 = (inst1 & 0xFFFF); - v1 = (inst2 >> 0x10); - //assert ((v1 & 0x3) == 0); - //assert ((v0 & 0x3) == 0); - u32 count = ((inst2+3) & 0xfffc); - //v0 = (v0) & 0xfffc; - //v1 = (v1) & 0xfffc; - - //memcpy (BufferSpace+v1, BufferSpace+v0, count-1); - for (cnt = 0; cnt < count; cnt++) { - *(u8 *)(BufferSpace+((cnt+v1)^S8)) = *(u8 *)(BufferSpace+((cnt+v0)^S8)); - } -} - -static void LOADADPCM (u32 inst1, u32 inst2) { // Loads an ADPCM table - Works 100% Now 03-13-01 - u32 v0; - v0 = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; -/* if (v0 > (1024*1024*8)) - v0 = (inst2 & 0xffffff);*/ - //memcpy (dmem+0x4c0, rsp.RDRAM+v0, inst1&0xffff); // Could prolly get away with not putting this in dmem - //assert ((inst1&0xffff) <= 0x80); - u16 *table = (u16 *)(rsp.RDRAM+v0); - for (u32 x = 0; x < ((inst1&0xffff)>>0x4); x++) { - adpcmtable[(0x0+(x<<3))^S] = table[0]; - adpcmtable[(0x1+(x<<3))^S] = table[1]; - - adpcmtable[(0x2+(x<<3))^S] = table[2]; - adpcmtable[(0x3+(x<<3))^S] = table[3]; - - adpcmtable[(0x4+(x<<3))^S] = table[4]; - adpcmtable[(0x5+(x<<3))^S] = table[5]; - - adpcmtable[(0x6+(x<<3))^S] = table[6]; - adpcmtable[(0x7+(x<<3))^S] = table[7]; - table += 8; - } -} - - -static void INTERLEAVE (u32 inst1, u32 inst2) { // Works... - 3-11-01 - u32 inL, inR; - u16 *outbuff = (u16 *)(AudioOutBuffer+BufferSpace); - u16 *inSrcR; - u16 *inSrcL; - u16 Left, Right, Left2, Right2; - - inL = inst2 & 0xFFFF; - inR = (inst2 >> 16) & 0xFFFF; - - inSrcR = (u16 *)(BufferSpace+inR); - inSrcL = (u16 *)(BufferSpace+inL); - - for (int x = 0; x < (AudioCount/4); x++) { - Left=*(inSrcL++); - Right=*(inSrcR++); - Left2=*(inSrcL++); - Right2=*(inSrcR++); - -#ifdef M64P_BIG_ENDIAN - *(outbuff++)=Right; - *(outbuff++)=Left; - *(outbuff++)=Right2; - *(outbuff++)=Left2; -#else - *(outbuff++)=Right2; - *(outbuff++)=Left2; - *(outbuff++)=Right; - *(outbuff++)=Left; -#endif - } -} - - -static void MIXER (u32 inst1, u32 inst2) { // Fixed a sign issue... 03-14-01 - u32 dmemin = (u16)(inst2 >> 0x10); - u32 dmemout = (u16)(inst2 & 0xFFFF); - //u8 flags = (u8)((inst1 >> 16) & 0xff); - s32 gain = (s16)(inst1 & 0xFFFF); - s32 temp; - - if (AudioCount == 0) - return; - - for (int x=0; x < AudioCount; x+=2) { // I think I can do this a lot easier - temp = (*(s16 *)(BufferSpace+dmemin+x) * gain) >> 15; - temp += *(s16 *)(BufferSpace+dmemout+x); - - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} - -// TOP Performance Hogs: -//Command: ADPCM - Calls: 48 - Total Time: 331226 - Avg Time: 6900.54 - Percent: 31.53% -//Command: ENVMIXER - Calls: 48 - Total Time: 408563 - Avg Time: 8511.73 - Percent: 38.90% -//Command: LOADBUFF - Calls: 56 - Total Time: 21551 - Avg Time: 384.84 - Percent: 2.05% -//Command: RESAMPLE - Calls: 48 - Total Time: 225922 - Avg Time: 4706.71 - Percent: 21.51% - -//Command: ADPCM - Calls: 48 - Total Time: 391600 - Avg Time: 8158.33 - Percent: 32.52% -//Command: ENVMIXER - Calls: 48 - Total Time: 444091 - Avg Time: 9251.90 - Percent: 36.88% -//Command: LOADBUFF - Calls: 58 - Total Time: 29945 - Avg Time: 516.29 - Percent: 2.49% -//Command: RESAMPLE - Calls: 48 - Total Time: 276354 - Avg Time: 5757.38 - Percent: 22.95% - - -extern "C" const acmd_callback_t ABI1[0x10] = { // TOP Performace Hogs: MIXER, RESAMPLE, ENVMIXER - SPNOOP , ADPCM , CLEARBUFF, ENVMIXER , LOADBUFF, RESAMPLE , SAVEBUFF, UNKNOWN, - SETBUFF, SETVOL, DMEMMOVE , LOADADPCM , MIXER , INTERLEAVE, UNKNOWN , SETLOOP -}; - -/* BACKUPS -void MIXER (u32 inst1, u32 inst2) { // Fixed a sign issue... 03-14-01 - u16 dmemin = (u16)(inst2 >> 0x10); - u16 dmemout = (u16)(inst2 & 0xFFFF); - u16 gain = (u16)(inst1 & 0xFFFF); - u8 flags = (u8)((inst1 >> 16) & 0xff); - u64 temp; - - if (AudioCount == 0) - return; - - for (int x=0; x < AudioCount; x+=2) { // I think I can do this a lot easier - temp = (s64)(*(s16 *)(BufferSpace+dmemout+x)) * (s64)((s16)(0x7FFF)*2); - - if (temp & 0x8000) - temp = (temp^0x8000) + 0x10000; - else - temp = (temp^0x8000); - - temp = (temp & 0xFFFFFFFFFFFF); - - temp += ((*(s16 *)(BufferSpace+dmemin+x) * (s64)((s16)gain*2))) & 0xFFFFFFFFFFFF; - - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} -*/ - - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode2.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode2.cpp deleted file mode 100644 index dd15689fcb..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode2.cpp +++ /dev/null @@ -1,930 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode2.cpp * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include -# include - -extern "C" { - #include "m64p_types.h" - #include "hle.h" - #include "alist_internal.h" -} - -extern u8 BufferSpace[0x10000]; - -static void SPNOOP (u32 inst1, u32 inst2) { - DebugMessage(M64MSG_ERROR, "Unknown/Unimplemented Audio Command %i in ABI 2", (int)(inst1 >> 24)); -} -extern u16 AudioInBuffer; // 0x0000(T8) -extern u16 AudioOutBuffer; // 0x0002(T8) -extern u16 AudioCount; // 0x0004(T8) -extern u32 loopval; // 0x0010(T8) -extern u32 SEGMENTS[0x10]; - -extern u16 adpcmtable[0x88]; - -extern const u16 ResampleLUT [0x200]; - -bool isMKABI = false; -bool isZeldaABI = false; - -extern "C" void init_ucode2() { isMKABI = isZeldaABI = false; } - -static void LOADADPCM2 (u32 inst1, u32 inst2) { // Loads an ADPCM table - Works 100% Now 03-13-01 - u32 v0; - v0 = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - u16 *table = (u16 *)(rsp.RDRAM+v0); // Zelda2 Specific... - - for (u32 x = 0; x < ((inst1&0xffff)>>0x4); x++) { - adpcmtable[(0x0+(x<<3))^S] = table[0]; - adpcmtable[(0x1+(x<<3))^S] = table[1]; - - adpcmtable[(0x2+(x<<3))^S] = table[2]; - adpcmtable[(0x3+(x<<3))^S] = table[3]; - - adpcmtable[(0x4+(x<<3))^S] = table[4]; - adpcmtable[(0x5+(x<<3))^S] = table[5]; - - adpcmtable[(0x6+(x<<3))^S] = table[6]; - adpcmtable[(0x7+(x<<3))^S] = table[7]; - table += 8; - } -} - -static void SETLOOP2 (u32 inst1, u32 inst2) { - loopval = inst2 & 0xffffff; // No segment? -} - -static void SETBUFF2 (u32 inst1, u32 inst2) { - AudioInBuffer = u16(inst1); // 0x00 - AudioOutBuffer = u16((inst2 >> 0x10)); // 0x02 - AudioCount = u16(inst2); // 0x04 -} - -static void ADPCM2 (u32 inst1, u32 inst2) { // Verified to be 100% Accurate... - unsigned char Flags=(u8)(inst1>>16)&0xff; - //unsigned short Gain=(u16)(inst1&0xffff); - unsigned int Address=(inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned short inPtr=0; - //short *out=(s16 *)(testbuff+(AudioOutBuffer>>2)); - short *out=(short *)(BufferSpace+AudioOutBuffer); - //unsigned char *in=(unsigned char *)(BufferSpace+AudioInBuffer); - short count=(short)AudioCount; - unsigned char icode; - unsigned char code; - int vscale; - unsigned short index; - unsigned short j; - int a[8]; - short *book1,*book2; - - u8 srange; - u8 mask1; - u8 mask2; - u8 shifter; - - memset(out,0,32); - - if (Flags & 0x4) { // Tricky lil Zelda MM and ABI2!!! hahaha I know your secrets! :DDD - srange = 0xE; - mask1 = 0xC0; - mask2 = 0x30; - shifter = 10; - } else { - srange = 0xC; - mask1 = 0xf0; - mask2 = 0x0f; - shifter = 12; - } - - if(!(Flags&0x1)) - { - if(Flags&0x2) - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(loopval+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[loopval],32); - } - else - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(Address+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[Address],32); - } - } - - int l1=out[14^S]; - int l2=out[15^S]; - int inp1[8]; - int inp2[8]; - out+=16; - while(count>0) { - code=BufferSpace[(AudioInBuffer+inPtr)^S8]; - index=code&0xf; - index<<=4; - book1=(short *)&adpcmtable[index]; - book2=book1+8; - code>>=4; - vscale=(0x8000>>((srange-code)-1)); - - inPtr++; - j=0; - - while(j<8) { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp1[j]=(s16)((icode&mask1) << 8); // this will in effect be signed - if(code>16); - //else int catchme=1; - j++; - - inp1[j]=(s16)((icode&mask2)<>16); - //else int catchme=1; - j++; - - if (Flags & 4) { - inp1[j]=(s16)((icode&0xC) << 12); // this will in effect be signed - if(code < 0xE) inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - - inp1[j]=(s16)((icode&0x3) << 14); - if(code < 0xE) inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - } // end flags - } // end while - - - - j=0; - while(j<8) { - icode=BufferSpace[(AudioInBuffer+inPtr)^S8]; - inPtr++; - - inp2[j]=(s16)((icode&mask1) << 8); - if(code>16); - //else int catchme=1; - j++; - - inp2[j]=(s16)((icode&mask2)<>16); - //else int catchme=1; - j++; - - if (Flags & 4) { - inp2[j]=(s16)((icode&0xC) << 12); - if(code < 0xE) inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - - inp2[j]=(s16)((icode&0x3) << 14); - if(code < 0xE) inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - //else int catchme=1; - j++; - } // end flags - } - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp1[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp1[0]; - a[1]+=(int)inp1[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp1[0]; - a[2]+=(int)book2[0]*inp1[1]; - a[2]+=(int)inp1[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp1[0]; - a[3]+=(int)book2[1]*inp1[1]; - a[3]+=(int)book2[0]*inp1[2]; - a[3]+=(int)inp1[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp1[0]; - a[4]+=(int)book2[2]*inp1[1]; - a[4]+=(int)book2[1]*inp1[2]; - a[4]+=(int)book2[0]*inp1[3]; - a[4]+=(int)inp1[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp1[0]; - a[5]+=(int)book2[3]*inp1[1]; - a[5]+=(int)book2[2]*inp1[2]; - a[5]+=(int)book2[1]*inp1[3]; - a[5]+=(int)book2[0]*inp1[4]; - a[5]+=(int)inp1[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp1[0]; - a[6]+=(int)book2[4]*inp1[1]; - a[6]+=(int)book2[3]*inp1[2]; - a[6]+=(int)book2[2]*inp1[3]; - a[6]+=(int)book2[1]*inp1[4]; - a[6]+=(int)book2[0]*inp1[5]; - a[6]+=(int)inp1[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp1[0]; - a[7]+=(int)book2[5]*inp1[1]; - a[7]+=(int)book2[4]*inp1[2]; - a[7]+=(int)book2[3]*inp1[3]; - a[7]+=(int)book2[2]*inp1[4]; - a[7]+=(int)book2[1]*inp1[5]; - a[7]+=(int)book2[0]*inp1[6]; - a[7]+=(int)inp1[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp2[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp2[0]; - a[1]+=(int)inp2[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp2[0]; - a[2]+=(int)book2[0]*inp2[1]; - a[2]+=(int)inp2[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp2[0]; - a[3]+=(int)book2[1]*inp2[1]; - a[3]+=(int)book2[0]*inp2[2]; - a[3]+=(int)inp2[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp2[0]; - a[4]+=(int)book2[2]*inp2[1]; - a[4]+=(int)book2[1]*inp2[2]; - a[4]+=(int)book2[0]*inp2[3]; - a[4]+=(int)inp2[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp2[0]; - a[5]+=(int)book2[3]*inp2[1]; - a[5]+=(int)book2[2]*inp2[2]; - a[5]+=(int)book2[1]*inp2[3]; - a[5]+=(int)book2[0]*inp2[4]; - a[5]+=(int)inp2[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp2[0]; - a[6]+=(int)book2[4]*inp2[1]; - a[6]+=(int)book2[3]*inp2[2]; - a[6]+=(int)book2[2]*inp2[3]; - a[6]+=(int)book2[1]*inp2[4]; - a[6]+=(int)book2[0]*inp2[5]; - a[6]+=(int)inp2[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp2[0]; - a[7]+=(int)book2[5]*inp2[1]; - a[7]+=(int)book2[4]*inp2[2]; - a[7]+=(int)book2[3]*inp2[3]; - a[7]+=(int)book2[2]*inp2[4]; - a[7]+=(int)book2[1]*inp2[5]; - a[7]+=(int)book2[0]*inp2[6]; - a[7]+=(int)inp2[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - count-=32; - } - out-=16; - memcpy(&rsp.RDRAM[Address],out,32); -} - -static void CLEARBUFF2 (u32 inst1, u32 inst2) { - u16 addr = (u16)(inst1 & 0xffff); - u16 count = (u16)(inst2 & 0xffff); - if (count > 0) - memset(BufferSpace+addr, 0, count); -} - -static void LOADBUFF2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (BufferSpace+(inst1&0xfffc), rsp.RDRAM+v0, (cnt+3)&0xFFFC); -} - -static void SAVEBUFF2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc);// + SEGMENTS[(inst2>>24)&0xf]; - memcpy (rsp.RDRAM+v0, BufferSpace+(inst1&0xfffc), (cnt+3)&0xFFFC); -} - - -static void MIXER2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u16 dmemin = (u16)(inst2 >> 0x10); - u16 dmemout = (u16)(inst2 & 0xFFFF); - u32 count = ((inst1 >> 12) & 0xFF0); - s32 gain = (s16)(inst1 & 0xFFFF); - s32 temp; - - for (unsigned int x=0; x < count; x+=2) { // I think I can do this a lot easier - - temp = (*(s16 *)(BufferSpace+dmemin+x) * gain) >> 15; - temp += *(s16 *)(BufferSpace+dmemout+x); - - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} - - -static void RESAMPLE2 (u32 inst1, u32 inst2) { - unsigned char Flags=(u8)((inst1>>16)&0xff); - unsigned int Pitch=((inst1&0xffff))<<1; - u32 addy = (inst2 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned int Accum=0; - unsigned int location; - s16 *lut; - short *dst; - s16 *src; - dst=(short *)(BufferSpace); - src=(s16 *)(BufferSpace); - u32 srcPtr=(AudioInBuffer/2); - u32 dstPtr=(AudioOutBuffer/2); - s32 temp; - s32 accum; - - if (addy > (1024*1024*8)) - addy = (inst2 & 0xffffff); - - srcPtr -= 4; - - if ((Flags & 0x1) == 0) { - for (int x=0; x < 4; x++) //memcpy (src+srcPtr, rsp.RDRAM+addy, 0x8); - src[(srcPtr+x)^S] = ((u16 *)rsp.RDRAM)[((addy/2)+x)^S]; - Accum = *(u16 *)(rsp.RDRAM+addy+10); - } else { - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = 0;//*(u16 *)(rsp.RDRAM+((addy+x)^2)); - } - - for(int i=0;i < ((AudioCount+0xf)&0xFFF0)/2;i++) { - location = (((Accum * 0x40) >> 0x10) * 8); - //location = (Accum >> 0xa) << 0x3; - lut = (s16 *)(((u8 *)ResampleLUT) + location); - - temp = ((s32)*(s16*)(src+((srcPtr+0)^S))*((s32)((s16)lut[0]))); - accum = (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+1)^S))*((s32)((s16)lut[1]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+2)^S))*((s32)((s16)lut[2]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+3)^S))*((s32)((s16)lut[3]))); - accum += (s32)(temp >> 15); - - if (accum > 32767) accum = 32767; - if (accum < -32768) accum = -32768; - - dst[dstPtr^S] = (s16)(accum); - dstPtr++; - Accum += Pitch; - srcPtr += (Accum>>16); - Accum&=0xffff; - } - for (int x=0; x < 4; x++) - ((u16 *)rsp.RDRAM)[((addy/2)+x)^S] = src[(srcPtr+x)^S]; - *(u16 *)(rsp.RDRAM+addy+10) = (u16)Accum; - //memcpy (RSWORK, src+srcPtr, 0x8); -} - -static void DMEMMOVE2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0, v1; - u32 cnt; - if ((inst2 & 0xffff)==0) - return; - v0 = (inst1 & 0xFFFF); - v1 = (inst2 >> 0x10); - //assert ((v1 & 0x3) == 0); - //assert ((v0 & 0x3) == 0); - u32 count = ((inst2+3) & 0xfffc); - //v0 = (v0) & 0xfffc; - //v1 = (v1) & 0xfffc; - - //memcpy (dmem+v1, dmem+v0, count-1); - for (cnt = 0; cnt < count; cnt++) { - *(u8 *)(BufferSpace+((cnt+v1)^S8)) = *(u8 *)(BufferSpace+((cnt+v0)^S8)); - } -} - -static u32 t3, s5, s6; -static u16 env[8]; - -static void ENVSETUP1 (u32 inst1, u32 inst2) { - u32 tmp; - - //fprintf (dfile, "ENVSETUP1: inst1 = %08X, inst2 = %08X\n", inst1, inst2); - t3 = inst1 & 0xFFFF; - tmp = (inst1 >> 0x8) & 0xFF00; - env[4] = (u16)tmp; - tmp += t3; - env[5] = (u16)tmp; - s5 = inst2 >> 0x10; - s6 = inst2 & 0xFFFF; - //fprintf (dfile, " t3 = %X / s5 = %X / s6 = %X / env[4] = %X / env[5] = %X\n", t3, s5, s6, env[4], env[5]); -} - -static void ENVSETUP2 (u32 inst1, u32 inst2) { - u32 tmp; - - //fprintf (dfile, "ENVSETUP2: inst1 = %08X, inst2 = %08X\n", inst1, inst2); - tmp = (inst2 >> 0x10); - env[0] = (u16)tmp; - tmp += s5; - env[1] = (u16)tmp; - tmp = inst2 & 0xffff; - env[2] = (u16)tmp; - tmp += s6; - env[3] = (u16)tmp; - //fprintf (dfile, " env[0] = %X / env[1] = %X / env[2] = %X / env[3] = %X\n", env[0], env[1], env[2], env[3]); -} - -static void ENVMIXER2 (u32 inst1, u32 inst2) { - //fprintf (dfile, "ENVMIXER: inst1 = %08X, inst2 = %08X\n", inst1, inst2); - - s16 *bufft6, *bufft7, *buffs0, *buffs1; - s16 *buffs3; - s32 count; - u32 adder; - - s16 vec9, vec10; - - s16 v2[8]; - - buffs3 = (s16 *)(BufferSpace + ((inst1 >> 0x0c)&0x0ff0)); - bufft6 = (s16 *)(BufferSpace + ((inst2 >> 0x14)&0x0ff0)); - bufft7 = (s16 *)(BufferSpace + ((inst2 >> 0x0c)&0x0ff0)); - buffs0 = (s16 *)(BufferSpace + ((inst2 >> 0x04)&0x0ff0)); - buffs1 = (s16 *)(BufferSpace + ((inst2 << 0x04)&0x0ff0)); - - - v2[0] = 0 - (s16)((inst1 & 0x2) >> 1); - v2[1] = 0 - (s16)((inst1 & 0x1)); - v2[2] = 0 - (s16)((inst1 & 0x8) >> 1); - v2[3] = 0 - (s16)((inst1 & 0x4) >> 1); - - count = (inst1 >> 8) & 0xff; - - if (!isMKABI) { - s5 *= 2; s6 *= 2; t3 *= 2; - adder = 0x10; - } else { - inst1 = 0; - adder = 0x8; - t3 = 0; - } - - - while (count > 0) { - int temp, x; - for (x=0; x < 0x8; x++) { - vec9 = (s16)(((s32)buffs3[x^S] * (u32)env[0]) >> 0x10) ^ v2[0]; - vec10 = (s16)(((s32)buffs3[x^S] * (u32)env[2]) >> 0x10) ^ v2[1]; - temp = bufft6[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft6[x^S] = temp; - temp = bufft7[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft7[x^S] = temp; - vec9 = (s16)(((s32)vec9 * (u32)env[4]) >> 0x10) ^ v2[2]; - vec10 = (s16)(((s32)vec10 * (u32)env[4]) >> 0x10) ^ v2[3]; - if (inst1 & 0x10) { - temp = buffs0[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } else { - temp = buffs0[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } - } - - if (!isMKABI) - for (x=0x8; x < 0x10; x++) { - vec9 = (s16)(((s32)buffs3[x^S] * (u32)env[1]) >> 0x10) ^ v2[0]; - vec10 = (s16)(((s32)buffs3[x^S] * (u32)env[3]) >> 0x10) ^ v2[1]; - temp = bufft6[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft6[x^S] = temp; - temp = bufft7[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - bufft7[x^S] = temp; - vec9 = (s16)(((s32)vec9 * (u32)env[5]) >> 0x10) ^ v2[2]; - vec10 = (s16)(((s32)vec10 * (u32)env[5]) >> 0x10) ^ v2[3]; - if (inst1 & 0x10) { - temp = buffs0[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } else { - temp = buffs0[x^S] + vec9; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs0[x^S] = temp; - temp = buffs1[x^S] + vec10; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - buffs1[x^S] = temp; - } - } - bufft6 += adder; bufft7 += adder; - buffs0 += adder; buffs1 += adder; - buffs3 += adder; count -= adder; - env[0] += (u16)s5; env[1] += (u16)s5; - env[2] += (u16)s6; env[3] += (u16)s6; - env[4] += (u16)t3; env[5] += (u16)t3; - } -} - -static void DUPLICATE2(u32 inst1, u32 inst2) { - unsigned short Count = (inst1 >> 16) & 0xff; - unsigned short In = inst1&0xffff; - unsigned short Out = (inst2>>16); - - unsigned short buff[64]; - - memcpy(buff,BufferSpace+In,128); - - while(Count) { - memcpy(BufferSpace+Out,buff,128); - Out+=128; - Count--; - } -} -/* -static void INTERL2 (u32 inst1, u32 inst2) { // Make your own... - short Count = inst1 & 0xffff; - unsigned short Out = inst2 & 0xffff; - unsigned short In = (inst2 >> 16); - - short *src,*dst,tmp; - src=(short *)&BufferSpace[In]; - dst=(short *)&BufferSpace[Out]; - while(Count) - { - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - *(dst++)=*(src++); - src++; - Count-=8; - } -} -*/ - -static void INTERL2 (u32 inst1, u32 inst2) { - short Count = inst1 & 0xffff; - unsigned short Out = inst2 & 0xffff; - unsigned short In = (inst2 >> 16); - - unsigned char *src,*dst/*,tmp*/; - src=(unsigned char *)(BufferSpace);//[In]; - dst=(unsigned char *)(BufferSpace);//[Out]; - while(Count) { - *(short *)(dst+(Out^S8)) = *(short *)(src+(In^S8)); - Out += 2; - In += 4; - Count--; - } -} - -static void INTERLEAVE2 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 inL, inR; - u16 *outbuff; - u16 *inSrcR; - u16 *inSrcL; - u16 Left, Right, Left2, Right2; - u32 count; - count = ((inst1 >> 12) & 0xFF0); - if (count == 0) { - outbuff = (u16 *)(AudioOutBuffer+BufferSpace); - count = AudioCount; - } else { - outbuff = (u16 *)((inst1&0xFFFF)+BufferSpace); - } - - inR = inst2 & 0xFFFF; - inL = (inst2 >> 16) & 0xFFFF; - - inSrcR = (u16 *)(BufferSpace+inR); - inSrcL = (u16 *)(BufferSpace+inL); - - for (u32 x = 0; x < (count/4); x++) { - Left=*(inSrcL++); - Right=*(inSrcR++); - Left2=*(inSrcL++); - Right2=*(inSrcR++); - -#ifdef M64P_BIG_ENDIAN - *(outbuff++)=Right; - *(outbuff++)=Left; - *(outbuff++)=Right2; - *(outbuff++)=Left2; -#else - *(outbuff++)=Right2; - *(outbuff++)=Left2; - *(outbuff++)=Right; - *(outbuff++)=Left; -#endif - } -} - -static void ADDMIXER (u32 inst1, u32 inst2) { - short Count = (inst1 >> 12) & 0x00ff0; - u16 InBuffer = (inst2 >> 16); - u16 OutBuffer = inst2 & 0xffff; - - s16 *inp, *outp; - s32 temp; - inp = (s16 *)(BufferSpace + InBuffer); - outp = (s16 *)(BufferSpace + OutBuffer); - for (int cntr = 0; cntr < Count; cntr+=2) { - temp = *outp + *inp; - if (temp > 32767) temp = 32767; if (temp < -32768) temp = -32768; - *(outp++) = temp; - inp++; - } -} - -static void HILOGAIN (u32 inst1, u32 inst2) { - u16 cnt = inst1 & 0xffff; - u16 out = (inst2 >> 16) & 0xffff; - s16 hi = (s16)((inst1 >> 4) & 0xf000); - u16 lo = (inst1 >> 20) & 0xf; - s16 *src; - - src = (s16 *)(BufferSpace+out); - s32 tmp, val; - - while(cnt) { - val = (s32)*src; - //tmp = ((val * (s32)hi) + ((u64)(val * lo) << 16) >> 16); - tmp = ((val * (s32)hi) >> 16) + (u32)(val * lo); - if ((s32)tmp > 32767) tmp = 32767; - else if ((s32)tmp < -32768) tmp = -32768; - *src = tmp; - src++; - cnt -= 2; - } -} - -static void FILTER2 (u32 inst1, u32 inst2) { - static int cnt = 0; - static s16 *lutt6; - static s16 *lutt5; - u8 *save = (rsp.RDRAM+(inst2&0xFFFFFF)); - u8 t4 = (u8)((inst1 >> 0x10) & 0xFF); - int x; - - if (t4 > 1) { // Then set the cnt variable - cnt = (inst1 & 0xFFFF); - lutt6 = (s16 *)save; -// memcpy (dmem+0xFE0, rsp.RDRAM+(inst2&0xFFFFFF), 0x10); - return; - } - - if (t4 == 0) { -// memcpy (dmem+0xFB0, rsp.RDRAM+(inst2&0xFFFFFF), 0x20); - lutt5 = (short *)(save+0x10); - } - - lutt5 = (short *)(save+0x10); - -// lutt5 = (short *)(dmem + 0xFC0); -// lutt6 = (short *)(dmem + 0xFE0); - for (x = 0; x < 8; x++) { - s32 a; - a = (lutt5[x] + lutt6[x]) >> 1; - lutt5[x] = lutt6[x] = (short)a; - } - short *inp1, *inp2; - s32 out1[8]; - s16 outbuff[0x3c0], *outp; - u32 inPtr = (u32)(inst1&0xffff); - inp1 = (short *)(save); - outp = outbuff; - inp2 = (short *)(BufferSpace+inPtr); - for (x = 0; x < cnt; x+=0x10) { - out1[1] = inp1[0]*lutt6[6]; - out1[1] += inp1[3]*lutt6[7]; - out1[1] += inp1[2]*lutt6[4]; - out1[1] += inp1[5]*lutt6[5]; - out1[1] += inp1[4]*lutt6[2]; - out1[1] += inp1[7]*lutt6[3]; - out1[1] += inp1[6]*lutt6[0]; - out1[1] += inp2[1]*lutt6[1]; // 1 - - out1[0] = inp1[3]*lutt6[6]; - out1[0] += inp1[2]*lutt6[7]; - out1[0] += inp1[5]*lutt6[4]; - out1[0] += inp1[4]*lutt6[5]; - out1[0] += inp1[7]*lutt6[2]; - out1[0] += inp1[6]*lutt6[3]; - out1[0] += inp2[1]*lutt6[0]; - out1[0] += inp2[0]*lutt6[1]; - - out1[3] = inp1[2]*lutt6[6]; - out1[3] += inp1[5]*lutt6[7]; - out1[3] += inp1[4]*lutt6[4]; - out1[3] += inp1[7]*lutt6[5]; - out1[3] += inp1[6]*lutt6[2]; - out1[3] += inp2[1]*lutt6[3]; - out1[3] += inp2[0]*lutt6[0]; - out1[3] += inp2[3]*lutt6[1]; - - out1[2] = inp1[5]*lutt6[6]; - out1[2] += inp1[4]*lutt6[7]; - out1[2] += inp1[7]*lutt6[4]; - out1[2] += inp1[6]*lutt6[5]; - out1[2] += inp2[1]*lutt6[2]; - out1[2] += inp2[0]*lutt6[3]; - out1[2] += inp2[3]*lutt6[0]; - out1[2] += inp2[2]*lutt6[1]; - - out1[5] = inp1[4]*lutt6[6]; - out1[5] += inp1[7]*lutt6[7]; - out1[5] += inp1[6]*lutt6[4]; - out1[5] += inp2[1]*lutt6[5]; - out1[5] += inp2[0]*lutt6[2]; - out1[5] += inp2[3]*lutt6[3]; - out1[5] += inp2[2]*lutt6[0]; - out1[5] += inp2[5]*lutt6[1]; - - out1[4] = inp1[7]*lutt6[6]; - out1[4] += inp1[6]*lutt6[7]; - out1[4] += inp2[1]*lutt6[4]; - out1[4] += inp2[0]*lutt6[5]; - out1[4] += inp2[3]*lutt6[2]; - out1[4] += inp2[2]*lutt6[3]; - out1[4] += inp2[5]*lutt6[0]; - out1[4] += inp2[4]*lutt6[1]; - - out1[7] = inp1[6]*lutt6[6]; - out1[7] += inp2[1]*lutt6[7]; - out1[7] += inp2[0]*lutt6[4]; - out1[7] += inp2[3]*lutt6[5]; - out1[7] += inp2[2]*lutt6[2]; - out1[7] += inp2[5]*lutt6[3]; - out1[7] += inp2[4]*lutt6[0]; - out1[7] += inp2[7]*lutt6[1]; - - out1[6] = inp2[1]*lutt6[6]; - out1[6] += inp2[0]*lutt6[7]; - out1[6] += inp2[3]*lutt6[4]; - out1[6] += inp2[2]*lutt6[5]; - out1[6] += inp2[5]*lutt6[2]; - out1[6] += inp2[4]*lutt6[3]; - out1[6] += inp2[7]*lutt6[0]; - out1[6] += inp2[6]*lutt6[1]; - outp[1] = /*CLAMP*/((out1[1]+0x4000) >> 0xF); - outp[0] = /*CLAMP*/((out1[0]+0x4000) >> 0xF); - outp[3] = /*CLAMP*/((out1[3]+0x4000) >> 0xF); - outp[2] = /*CLAMP*/((out1[2]+0x4000) >> 0xF); - outp[5] = /*CLAMP*/((out1[5]+0x4000) >> 0xF); - outp[4] = /*CLAMP*/((out1[4]+0x4000) >> 0xF); - outp[7] = /*CLAMP*/((out1[7]+0x4000) >> 0xF); - outp[6] = /*CLAMP*/((out1[6]+0x4000) >> 0xF); - inp1 = inp2; - inp2 += 8; - outp += 8; - } -// memcpy (rsp.RDRAM+(inst2&0xFFFFFF), dmem+0xFB0, 0x20); - memcpy (save, inp2-8, 0x10); - memcpy (BufferSpace+(inst1&0xffff), outbuff, cnt); -} - -static void SEGMENT2 (u32 inst1, u32 inst2) { - if (isZeldaABI) { - FILTER2 (inst1, inst2); - return; - } - if ((inst1 & 0xffffff) == 0) { - isMKABI = true; - //SEGMENTS[(inst2>>24)&0xf] = (inst2 & 0xffffff); - } else { - isMKABI = false; - isZeldaABI = true; - FILTER2 (inst1, inst2); - } -} - -static void UNKNOWN (u32 inst1, u32 inst2) { -} -/* -void (*ABI2[0x20])(void) = { - SPNOOP, ADPCM2, CLEARBUFF2, SPNOOP, SPNOOP, RESAMPLE2, SPNOOP, SEGMENT2, - SETBUFF2, SPNOOP, DMEMMOVE2, LOADADPCM2, MIXER2, INTERLEAVE2, HILOGAIN, SETLOOP2, - SPNOOP, INTERL2, ENVSETUP1, ENVMIXER2, LOADBUFF2, SAVEBUFF2, ENVSETUP2, SPNOOP, - SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP, SPNOOP -};*/ - -extern "C" const acmd_callback_t ABI2[0x20] = { - SPNOOP , ADPCM2, CLEARBUFF2, UNKNOWN, ADDMIXER, RESAMPLE2, UNKNOWN, SEGMENT2, - SETBUFF2 , DUPLICATE2, DMEMMOVE2, LOADADPCM2, MIXER2, INTERLEAVE2, HILOGAIN, SETLOOP2, - SPNOOP, INTERL2 , ENVSETUP1, ENVMIXER2, LOADBUFF2, SAVEBUFF2, ENVSETUP2, SPNOOP, - HILOGAIN , SPNOOP, DUPLICATE2 , UNKNOWN , SPNOOP , SPNOOP , SPNOOP , SPNOOP -}; -/* -void (*ABI2[0x20])(void) = { - SPNOOP , ADPCM2, CLEARBUFF2, SPNOOP, SPNOOP, RESAMPLE2 , SPNOOP , SEGMENT2, - SETBUFF2 , DUPLICATE2, DMEMMOVE2, LOADADPCM2, MIXER2, INTERLEAVE2, SPNOOP, SETLOOP2, - SPNOOP, INTERL2 , ENVSETUP1, ENVMIXER2, LOADBUFF2, SAVEBUFF2, ENVSETUP2, SPNOOP, - SPNOOP , SPNOOP, SPNOOP , SPNOOP , SPNOOP , SPNOOP , SPNOOP , SPNOOP -};*/ -/* NOTES: - - FILTER/SEGMENT - Still needs to be finished up... add FILTER? - UNKNOWWN #27 - Is this worth doing? Looks like a pain in the ass just for WaveRace64 -*/ - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode3.cpp deleted file mode 100644 index b63fbe27c2..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3.cpp +++ /dev/null @@ -1,834 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode3.cpp * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include -# include - -extern "C" { - #include "m64p_types.h" - #include "hle.h" - #include "alist_internal.h" -} - -/* -static void SPNOOP (u32 inst1, u32 inst2) { - DebugMessage(M64MSG_ERROR, "Unknown/Unimplemented Audio Command %i in ABI 3", (int)(inst1 >> 24)); -} -*/ - -extern const u16 ResampleLUT [0x200]; - -extern u32 loopval; - -extern s16 Env_Dry; -extern s16 Env_Wet; -extern s16 Vol_Left; -extern s16 Vol_Right; -extern s16 VolTrg_Left; -extern s32 VolRamp_Left; -//extern u16 VolRate_Left; -extern s16 VolTrg_Right; -extern s32 VolRamp_Right; -//extern u16 VolRate_Right; - - -extern short hleMixerWorkArea[256]; -extern u16 adpcmtable[0x88]; - -extern u8 BufferSpace[0x10000]; - -/* -static void SETVOL3 (u32 inst1, u32 inst2) { // Swapped Rate_Left and Vol - u8 Flags = (u8)(inst1 >> 0x10); - if (Flags & 0x4) { // 288 - if (Flags & 0x2) { // 290 - VolTrg_Left = *(s16*)&inst1; - VolRamp_Left = *(s32*)&inst2; - } else { - VolTrg_Right = *(s16*)&inst1; - VolRamp_Right = *(s32*)&inst2; - } - } else { - Vol_Left = *(s16*)&inst1; - Env_Dry = (s16)(*(s32*)&inst2 >> 0x10); - Env_Wet = *(s16*)&inst2; - } -} -*/ -static void SETVOL3 (u32 inst1, u32 inst2) { - u8 Flags = (u8)(inst1 >> 0x10); - if (Flags & 0x4) { // 288 - if (Flags & 0x2) { // 290 - Vol_Left = (s16)inst1; // 0x50 - Env_Dry = (s16)(inst2 >> 0x10); // 0x4E - Env_Wet = (s16)inst2; // 0x4C - } else { - VolTrg_Right = (s16)inst1; // 0x46 - //VolRamp_Right = (u16)(inst2 >> 0x10) | (s32)(s16)(inst2 << 0x10); - VolRamp_Right = (s32)inst2; // 0x48/0x4A - } - } else { - VolTrg_Left = (s16)inst1; // 0x40 - VolRamp_Left = (s32)inst2; // 0x42/0x44 - } -} - -static void ENVMIXER3 (u32 inst1, u32 inst2) { - u8 flags = (u8)((inst1 >> 16) & 0xff); - u32 addy = (inst2 & 0xFFFFFF); - - short *inp=(short *)(BufferSpace+0x4F0); - short *out=(short *)(BufferSpace+0x9D0); - short *aux1=(short *)(BufferSpace+0xB40); - short *aux2=(short *)(BufferSpace+0xCB0); - short *aux3=(short *)(BufferSpace+0xE20); - s32 MainR; - s32 MainL; - s32 AuxR; - s32 AuxL; - int i1,o1,a1,a2,a3; - //unsigned short AuxIncRate=1; - short zero[8]; - memset(zero,0,16); - - s32 LAdder, LAcc, LVol; - s32 RAdder, RAcc, RVol; - s16 RSig, LSig; // Most significant part of the Ramp Value - s16 Wet, Dry; - s16 LTrg, RTrg; - - Vol_Right = (s16)inst1; - - if (flags & A_INIT) { - LAdder = VolRamp_Left / 8; - LAcc = 0; - LVol = Vol_Left; - LSig = (s16)(VolRamp_Left >> 16); - - RAdder = VolRamp_Right / 8; - RAcc = 0; - RVol = Vol_Right; - RSig = (s16)(VolRamp_Right >> 16); - - Wet = (s16)Env_Wet; Dry = (s16)Env_Dry; // Save Wet/Dry values - LTrg = VolTrg_Left; RTrg = VolTrg_Right; // Save Current Left/Right Targets - } else { - memcpy((u8 *)hleMixerWorkArea, rsp.RDRAM+addy, 80); - Wet = *(s16 *)(hleMixerWorkArea + 0); // 0-1 - Dry = *(s16 *)(hleMixerWorkArea + 2); // 2-3 - LTrg = *(s16 *)(hleMixerWorkArea + 4); // 4-5 - RTrg = *(s16 *)(hleMixerWorkArea + 6); // 6-7 - LAdder = *(s32 *)(hleMixerWorkArea + 8); // 8-9 (hleMixerWorkArea is a 16bit pointer) - RAdder = *(s32 *)(hleMixerWorkArea + 10); // 10-11 - LAcc = *(s32 *)(hleMixerWorkArea + 12); // 12-13 - RAcc = *(s32 *)(hleMixerWorkArea + 14); // 14-15 - LVol = *(s32 *)(hleMixerWorkArea + 16); // 16-17 - RVol = *(s32 *)(hleMixerWorkArea + 18); // 18-19 - LSig = *(s16 *)(hleMixerWorkArea + 20); // 20-21 - RSig = *(s16 *)(hleMixerWorkArea + 22); // 22-23 - //u32 test = *(s32 *)(hleMixerWorkArea + 24); // 22-23 - //if (test != 0x13371337) - } - - - //if(!(flags&A_AUX)) { - // AuxIncRate=0; - // aux2=aux3=zero; - //} - - for (int y = 0; y < (0x170/2); y++) { - - // Left - LAcc += LAdder; - LVol += (LAcc >> 16); - LAcc &= 0xFFFF; - - // Right - RAcc += RAdder; - RVol += (RAcc >> 16); - RAcc &= 0xFFFF; -// **************************************************************** - // Clamp Left - if (LSig >= 0) { // VLT - if (LVol > LTrg) { - LVol = LTrg; - } - } else { // VGE - if (LVol < LTrg) { - LVol = LTrg; - } - } - - // Clamp Right - if (RSig >= 0) { // VLT - if (RVol > RTrg) { - RVol = RTrg; - } - } else { // VGE - if (RVol < RTrg) { - RVol = RTrg; - } - } -// **************************************************************** - MainL = ((Dry * LVol) + 0x4000) >> 15; - MainR = ((Dry * RVol) + 0x4000) >> 15; - - o1 = out [y^S]; - a1 = aux1[y^S]; - i1 = inp [y^S]; - - o1+=((i1*MainL)+0x4000)>>15; - a1+=((i1*MainR)+0x4000)>>15; - -// **************************************************************** - - if(o1>32767) o1=32767; - else if(o1<-32768) o1=-32768; - - if(a1>32767) a1=32767; - else if(a1<-32768) a1=-32768; - -// **************************************************************** - - out[y^S]=o1; - aux1[y^S]=a1; - -// **************************************************************** - //if (!(flags&A_AUX)) { - a2 = aux2[y^S]; - a3 = aux3[y^S]; - - AuxL = ((Wet * LVol) + 0x4000) >> 15; - AuxR = ((Wet * RVol) + 0x4000) >> 15; - - a2+=((i1*AuxL)+0x4000)>>15; - a3+=((i1*AuxR)+0x4000)>>15; - - if(a2>32767) a2=32767; - else if(a2<-32768) a2=-32768; - - if(a3>32767) a3=32767; - else if(a3<-32768) a3=-32768; - - aux2[y^S]=a2; - aux3[y^S]=a3; - } - //} - - *(s16 *)(hleMixerWorkArea + 0) = Wet; // 0-1 - *(s16 *)(hleMixerWorkArea + 2) = Dry; // 2-3 - *(s16 *)(hleMixerWorkArea + 4) = LTrg; // 4-5 - *(s16 *)(hleMixerWorkArea + 6) = RTrg; // 6-7 - *(s32 *)(hleMixerWorkArea + 8) = LAdder; // 8-9 (hleMixerWorkArea is a 16bit pointer) - *(s32 *)(hleMixerWorkArea + 10) = RAdder; // 10-11 - *(s32 *)(hleMixerWorkArea + 12) = LAcc; // 12-13 - *(s32 *)(hleMixerWorkArea + 14) = RAcc; // 14-15 - *(s32 *)(hleMixerWorkArea + 16) = LVol; // 16-17 - *(s32 *)(hleMixerWorkArea + 18) = RVol; // 18-19 - *(s16 *)(hleMixerWorkArea + 20) = LSig; // 20-21 - *(s16 *)(hleMixerWorkArea + 22) = RSig; // 22-23 - //*(u32 *)(hleMixerWorkArea + 24) = 0x13371337; // 22-23 - memcpy(rsp.RDRAM+addy, (u8 *)hleMixerWorkArea,80); -} - -static void CLEARBUFF3 (u32 inst1, u32 inst2) { - u16 addr = (u16)(inst1 & 0xffff); - u16 count = (u16)(inst2 & 0xffff); - memset(BufferSpace+addr+0x4f0, 0, count); -} - -static void MIXER3 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u16 dmemin = (u16)(inst2 >> 0x10) + 0x4f0; - u16 dmemout = (u16)(inst2 & 0xFFFF) + 0x4f0; - //u8 flags = (u8)((inst1 >> 16) & 0xff); - s32 gain = (s16)(inst1 & 0xFFFF); - s32 temp; - - for (int x=0; x < 0x170; x+=2) { // I think I can do this a lot easier - temp = (*(s16 *)(BufferSpace+dmemin+x) * gain) >> 15; - temp += *(s16 *)(BufferSpace+dmemout+x); - - if ((s32)temp > 32767) - temp = 32767; - if ((s32)temp < -32768) - temp = -32768; - - *(u16 *)(BufferSpace+dmemout+x) = (u16)(temp & 0xFFFF); - } -} - -static void LOADBUFF3 (u32 inst1, u32 inst2) { - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc); - u32 src = (inst1&0xffc)+0x4f0; - memcpy (BufferSpace+src, rsp.RDRAM+v0, cnt); -} - -static void SAVEBUFF3 (u32 inst1, u32 inst2) { - u32 v0; - u32 cnt = (((inst1 >> 0xC)+3)&0xFFC); - v0 = (inst2 & 0xfffffc); - u32 src = (inst1&0xffc)+0x4f0; - memcpy (rsp.RDRAM+v0, BufferSpace+src, cnt); -} - -static void LOADADPCM3 (u32 inst1, u32 inst2) { // Loads an ADPCM table - Works 100% Now 03-13-01 - u32 v0; - v0 = (inst2 & 0xffffff); - //memcpy (dmem+0x3f0, rsp.RDRAM+v0, inst1&0xffff); - //assert ((inst1&0xffff) <= 0x80); - u16 *table = (u16 *)(rsp.RDRAM+v0); - for (u32 x = 0; x < ((inst1&0xffff)>>0x4); x++) { - adpcmtable[(0x0+(x<<3))^S] = table[0]; - adpcmtable[(0x1+(x<<3))^S] = table[1]; - - adpcmtable[(0x2+(x<<3))^S] = table[2]; - adpcmtable[(0x3+(x<<3))^S] = table[3]; - - adpcmtable[(0x4+(x<<3))^S] = table[4]; - adpcmtable[(0x5+(x<<3))^S] = table[5]; - - adpcmtable[(0x6+(x<<3))^S] = table[6]; - adpcmtable[(0x7+(x<<3))^S] = table[7]; - table += 8; - } -} - -static void DMEMMOVE3 (u32 inst1, u32 inst2) { // Needs accuracy verification... - u32 v0, v1; - u32 cnt; - v0 = (inst1 & 0xFFFF) + 0x4f0; - v1 = (inst2 >> 0x10) + 0x4f0; - u32 count = ((inst2+3) & 0xfffc); - - //memcpy (dmem+v1, dmem+v0, count-1); - for (cnt = 0; cnt < count; cnt++) { - *(u8 *)(BufferSpace+((cnt+v1)^S8)) = *(u8 *)(BufferSpace+((cnt+v0)^S8)); - } -} - -static void SETLOOP3 (u32 inst1, u32 inst2) { - loopval = (inst2 & 0xffffff); -} - -static void ADPCM3 (u32 inst1, u32 inst2) { // Verified to be 100% Accurate... - unsigned char Flags=(u8)(inst2>>0x1c)&0xff; - //unsigned short Gain=(u16)(inst1&0xffff); - unsigned int Address=(inst1 & 0xffffff);// + SEGMENTS[(inst2>>24)&0xf]; - unsigned short inPtr=(inst2>>12)&0xf; - //short *out=(s16 *)(testbuff+(AudioOutBuffer>>2)); - short *out=(short *)(BufferSpace+(inst2&0xfff)+0x4f0); - //unsigned char *in=(unsigned char *)(BufferSpace+((inst2>>12)&0xf)+0x4f0); - short count=(short)((inst2 >> 16)&0xfff); - unsigned char icode; - unsigned char code; - int vscale; - unsigned short index; - unsigned short j; - int a[8]; - short *book1,*book2; - - memset(out,0,32); - - if(!(Flags&0x1)) - { - if(Flags&0x2) - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(loopval+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[loopval],32); - } - else - {/* - for(int i=0;i<16;i++) - { - out[i]=*(short *)&rsp.RDRAM[(Address+i*2)^2]; - }*/ - memcpy(out,&rsp.RDRAM[Address],32); - } - } - - int l1=out[14^S]; - int l2=out[15^S]; - int inp1[8]; - int inp2[8]; - out+=16; - while(count>0) - { - // the first interation through, these values are - // either 0 in the case of A_INIT, from a special - // area of memory in the case of A_LOOP or just - // the values we calculated the last time - - code=BufferSpace[(0x4f0+inPtr)^S8]; - index=code&0xf; - index<<=4; // index into the adpcm code table - book1=(short *)&adpcmtable[index]; - book2=book1+8; - code>>=4; // upper nibble is scale - vscale=(0x8000>>((12-code)-1)); // very strange. 0x8000 would be .5 in 16:16 format - // so this appears to be a fractional scale based - // on the 12 based inverse of the scale value. note - // that this could be negative, in which case we do - // not use the calculated vscale value... see the - // if(code>12) check below - - inPtr++; // coded adpcm data lies next - j=0; - while(j<8) // loop of 8, for 8 coded nibbles from 4 bytes - // which yields 8 short pcm values - { - icode=BufferSpace[(0x4f0+inPtr)^S8]; - inPtr++; - - inp1[j]=(s16)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp1[j]=(s16)((icode&0xf)<<12); - if(code<12) - inp1[j]=((int)((int)inp1[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - j=0; - while(j<8) - { - icode=BufferSpace[(0x4f0+inPtr)^S8]; - inPtr++; - - inp2[j]=(short)((icode&0xf0)<<8); // this will in effect be signed - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - - inp2[j]=(short)((icode&0xf)<<12); - if(code<12) - inp2[j]=((int)((int)inp2[j]*(int)vscale)>>16); - /*else - int catchme=1;*/ - j++; - } - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp1[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp1[0]; - a[1]+=(int)inp1[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp1[0]; - a[2]+=(int)book2[0]*inp1[1]; - a[2]+=(int)inp1[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp1[0]; - a[3]+=(int)book2[1]*inp1[1]; - a[3]+=(int)book2[0]*inp1[2]; - a[3]+=(int)inp1[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp1[0]; - a[4]+=(int)book2[2]*inp1[1]; - a[4]+=(int)book2[1]*inp1[2]; - a[4]+=(int)book2[0]*inp1[3]; - a[4]+=(int)inp1[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp1[0]; - a[5]+=(int)book2[3]*inp1[1]; - a[5]+=(int)book2[2]*inp1[2]; - a[5]+=(int)book2[1]*inp1[3]; - a[5]+=(int)book2[0]*inp1[4]; - a[5]+=(int)inp1[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp1[0]; - a[6]+=(int)book2[4]*inp1[1]; - a[6]+=(int)book2[3]*inp1[2]; - a[6]+=(int)book2[2]*inp1[3]; - a[6]+=(int)book2[1]*inp1[4]; - a[6]+=(int)book2[0]*inp1[5]; - a[6]+=(int)inp1[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp1[0]; - a[7]+=(int)book2[5]*inp1[1]; - a[7]+=(int)book2[4]*inp1[2]; - a[7]+=(int)book2[3]*inp1[3]; - a[7]+=(int)book2[2]*inp1[4]; - a[7]+=(int)book2[1]*inp1[5]; - a[7]+=(int)book2[0]*inp1[6]; - a[7]+=(int)inp1[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - //*(out+j)=a[j^S]; - } - //out += 0x10; - l1=a[6]; - l2=a[7]; - - a[0]= (int)book1[0]*(int)l1; - a[0]+=(int)book2[0]*(int)l2; - a[0]+=(int)inp2[0]*(int)2048; - - a[1] =(int)book1[1]*(int)l1; - a[1]+=(int)book2[1]*(int)l2; - a[1]+=(int)book2[0]*inp2[0]; - a[1]+=(int)inp2[1]*(int)2048; - - a[2] =(int)book1[2]*(int)l1; - a[2]+=(int)book2[2]*(int)l2; - a[2]+=(int)book2[1]*inp2[0]; - a[2]+=(int)book2[0]*inp2[1]; - a[2]+=(int)inp2[2]*(int)2048; - - a[3] =(int)book1[3]*(int)l1; - a[3]+=(int)book2[3]*(int)l2; - a[3]+=(int)book2[2]*inp2[0]; - a[3]+=(int)book2[1]*inp2[1]; - a[3]+=(int)book2[0]*inp2[2]; - a[3]+=(int)inp2[3]*(int)2048; - - a[4] =(int)book1[4]*(int)l1; - a[4]+=(int)book2[4]*(int)l2; - a[4]+=(int)book2[3]*inp2[0]; - a[4]+=(int)book2[2]*inp2[1]; - a[4]+=(int)book2[1]*inp2[2]; - a[4]+=(int)book2[0]*inp2[3]; - a[4]+=(int)inp2[4]*(int)2048; - - a[5] =(int)book1[5]*(int)l1; - a[5]+=(int)book2[5]*(int)l2; - a[5]+=(int)book2[4]*inp2[0]; - a[5]+=(int)book2[3]*inp2[1]; - a[5]+=(int)book2[2]*inp2[2]; - a[5]+=(int)book2[1]*inp2[3]; - a[5]+=(int)book2[0]*inp2[4]; - a[5]+=(int)inp2[5]*(int)2048; - - a[6] =(int)book1[6]*(int)l1; - a[6]+=(int)book2[6]*(int)l2; - a[6]+=(int)book2[5]*inp2[0]; - a[6]+=(int)book2[4]*inp2[1]; - a[6]+=(int)book2[3]*inp2[2]; - a[6]+=(int)book2[2]*inp2[3]; - a[6]+=(int)book2[1]*inp2[4]; - a[6]+=(int)book2[0]*inp2[5]; - a[6]+=(int)inp2[6]*(int)2048; - - a[7] =(int)book1[7]*(int)l1; - a[7]+=(int)book2[7]*(int)l2; - a[7]+=(int)book2[6]*inp2[0]; - a[7]+=(int)book2[5]*inp2[1]; - a[7]+=(int)book2[4]*inp2[2]; - a[7]+=(int)book2[3]*inp2[3]; - a[7]+=(int)book2[2]*inp2[4]; - a[7]+=(int)book2[1]*inp2[5]; - a[7]+=(int)book2[0]*inp2[6]; - a[7]+=(int)inp2[7]*(int)2048; - - for(j=0;j<8;j++) - { - a[j^S]>>=11; - if(a[j^S]>32767) a[j^S]=32767; - else if(a[j^S]<-32768) a[j^S]=-32768; - *(out++)=a[j^S]; - //*(out+j+0x1f8)=a[j^S]; - } - l1=a[6]; - l2=a[7]; - - count-=32; - } - out-=16; - memcpy(&rsp.RDRAM[Address],out,32); -} - -static void RESAMPLE3 (u32 inst1, u32 inst2) { - unsigned char Flags=(u8)((inst2>>0x1e)); - unsigned int Pitch=((inst2>>0xe)&0xffff)<<1; - u32 addy = (inst1 & 0xffffff); - unsigned int Accum=0; - unsigned int location; - s16 *lut; - short *dst; - s16 *src; - dst=(short *)(BufferSpace); - src=(s16 *)(BufferSpace); - u32 srcPtr=((((inst2>>2)&0xfff)+0x4f0)/2); - u32 dstPtr;//=(AudioOutBuffer/2); - s32 temp; - s32 accum; - - //if (addy > (1024*1024*8)) - // addy = (inst2 & 0xffffff); - - srcPtr -= 4; - - if (inst2 & 0x3) { - dstPtr = 0x660/2; - } else { - dstPtr = 0x4f0/2; - } - - if ((Flags & 0x1) == 0) { - for (int x=0; x < 4; x++) //memcpy (src+srcPtr, rsp.RDRAM+addy, 0x8); - src[(srcPtr+x)^S] = ((u16 *)rsp.RDRAM)[((addy/2)+x)^S]; - Accum = *(u16 *)(rsp.RDRAM+addy+10); - } else { - for (int x=0; x < 4; x++) - src[(srcPtr+x)^S] = 0;//*(u16 *)(rsp.RDRAM+((addy+x)^2)); - } - - for(int i=0;i < 0x170/2;i++) { - location = (((Accum * 0x40) >> 0x10) * 8); - //location = (Accum >> 0xa) << 0x3; - lut = (s16 *)(((u8 *)ResampleLUT) + location); - - temp = ((s32)*(s16*)(src+((srcPtr+0)^S))*((s32)((s16)lut[0]))); - accum = (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+1)^S))*((s32)((s16)lut[1]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+2)^S))*((s32)((s16)lut[2]))); - accum += (s32)(temp >> 15); - - temp = ((s32)*(s16*)(src+((srcPtr+3)^S))*((s32)((s16)lut[3]))); - accum += (s32)(temp >> 15); -/* temp = ((s64)*(s16*)(src+((srcPtr+0)^S))*((s64)((s16)lut[0]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum = (s32)(s16)temp; - - temp = ((s64)*(s16*)(src+((srcPtr+1)^S))*((s64)((s16)lut[1]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum += (s32)(s16)temp; - - temp = ((s64)*(s16*)(src+((srcPtr+2)^S))*((s64)((s16)lut[2]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum += (s32)(s16)temp; - - temp = ((s64)*(s16*)(src+((srcPtr+3)^S))*((s64)((s16)lut[3]<<1))); - if (temp & 0x8000) temp = (temp^0x8000) + 0x10000; - else temp = (temp^0x8000); - temp = (s32)(temp >> 16); - if ((s32)temp > 32767) temp = 32767; - if ((s32)temp < -32768) temp = -32768; - accum += (s32)(s16)temp;*/ - - if (accum > 32767) accum = 32767; - if (accum < -32768) accum = -32768; - - dst[dstPtr^S] = (accum); - dstPtr++; - Accum += Pitch; - srcPtr += (Accum>>16); - Accum&=0xffff; - } - for (int x=0; x < 4; x++) - ((u16 *)rsp.RDRAM)[((addy/2)+x)^S] = src[(srcPtr+x)^S]; - *(u16 *)(rsp.RDRAM+addy+10) = Accum; -} - -static void INTERLEAVE3 (u32 inst1, u32 inst2) { // Needs accuracy verification... - //u32 inL, inR; - u16 *outbuff = (u16 *)(BufferSpace + 0x4f0);//(u16 *)(AudioOutBuffer+dmem); - u16 *inSrcR; - u16 *inSrcL; - u16 Left, Right, Left2, Right2; - - //inR = inst2 & 0xFFFF; - //inL = (inst2 >> 16) & 0xFFFF; - - inSrcR = (u16 *)(BufferSpace+0xb40); - inSrcL = (u16 *)(BufferSpace+0x9d0); - - for (int x = 0; x < (0x170/4); x++) { - Left=*(inSrcL++); - Right=*(inSrcR++); - Left2=*(inSrcL++); - Right2=*(inSrcR++); - -#ifdef M64P_BIG_ENDIAN - *(outbuff++)=Right; - *(outbuff++)=Left; - *(outbuff++)=Right2; - *(outbuff++)=Left2; -#else - *(outbuff++)=Right2; - *(outbuff++)=Left2; - *(outbuff++)=Right; - *(outbuff++)=Left; -#endif -/* - Left=*(inSrcL++); - Right=*(inSrcR++); - *(outbuff++)=(u16)Left; - Left >>= 16; - *(outbuff++)=(u16)Right; - Right >>= 16; - *(outbuff++)=(u16)Left; - *(outbuff++)=(u16)Right;*/ - } -} - -//static void UNKNOWN (u32 inst1, u32 inst2); -/* -typedef struct { - unsigned char sync; - - unsigned char error_protection : 1; // 0=yes, 1=no - unsigned char lay : 2; // 4-lay = layerI, II or III - unsigned char version : 1; // 3=mpeg 1.0, 2=mpeg 2.5 0=mpeg 2.0 - unsigned char sync2 : 4; - - unsigned char extension : 1; // Unknown - unsigned char padding : 1; // padding - unsigned char sampling_freq : 2; // see table below - unsigned char bitrate_index : 4; // see table below - - unsigned char emphasis : 2; //see table below - unsigned char original : 1; // 0=no 1=yes - unsigned char copyright : 1; // 0=no 1=yes - unsigned char mode_ext : 2; // used with "joint stereo" mode - unsigned char mode : 2; // Channel Mode -} mp3struct; - -mp3struct mp3; -FILE *mp3dat; -*/ - -static void WHATISTHIS (u32 inst1, u32 inst2) { -} - -//static FILE *fp = fopen ("d:\\mp3info.txt", "wt"); -u32 setaddr; -static void MP3ADDY (u32 inst1, u32 inst2) { - setaddr = (inst2 & 0xffffff); -} - -extern "C" { - void rsp_run(void); - void mp3setup (unsigned int inst1, unsigned int inst2, unsigned int t8); -} - -extern u32 base, dmembase; -extern "C" { - extern char *pDMEM; -} -void MP3 (u32 inst1, u32 inst2); -/* - { -// return; - // Setup Registers... - mp3setup (inst1, inst2, 0xFA0); - - // Setup Memory Locations... - //u32 base = ((u32*)dmem)[0xFD0/4]; // Should be 000291A0 - memcpy (BufferSpace, dmembase+rsp.RDRAM, 0x10); - ((u32*)BufferSpace)[0x0] = base; - ((u32*)BufferSpace)[0x008/4] += base; - ((u32*)BufferSpace)[0xFFC/4] = loopval; - ((u32*)BufferSpace)[0xFF8/4] = dmembase; - - memcpy (imem+0x238, rsp.RDRAM+((u32*)BufferSpace)[0x008/4], 0x9C0); - ((u32*)BufferSpace)[0xFF4/4] = setaddr; - pDMEM = (char *)BufferSpace; - rsp_run (void); - dmembase = ((u32*)BufferSpace)[0xFF8/4]; - loopval = ((u32*)BufferSpace)[0xFFC/4]; -//0x1A98 SW S1, 0x0FF4 (R0) -//0x1A9C SW S0, 0x0FF8 (R0) -//0x1AA0 SW T7, 0x0FFC (R0) -//0x1AA4 SW T3, 0x0FF0 (R0) - //fprintf (fp, "mp3: inst1: %08X, inst2: %08X\n", inst1, inst2); -}*/ -/* -FFT = Fast Fourier Transform -DCT = Discrete Cosine Transform -MPEG-1 Layer 3 retains Layer 2's 1152-sample window, as well as the FFT polyphase filter for -backward compatibility, but adds a modified DCT filter. DCT's advantages over DFTs (discrete -Fourier transforms) include half as many multiply-accumulate operations and half the -generated coefficients because the sinusoidal portion of the calculation is absent, and DCT -generally involves simpler math. The finite lengths of a conventional DCTs' bandpass impulse -responses, however, may result in block-boundary effects. MDCTs overlap the analysis blocks -and lowpass-filter the decoded audio to remove aliases, eliminating these effects. MDCTs also -have a higher transform coding gain than the standard DCT, and their basic functions -correspond to better bandpass response. - -MPEG-1 Layer 3's DCT sub-bands are unequally sized, and correspond to the human auditory -system's critical bands. In Layer 3 decoders must support both constant- and variable-bit-rate -bit streams. (However, many Layer 1 and 2 decoders also handle variable bit rates). Finally, -Layer 3 encoders Huffman-code the quantized coefficients before archiving or transmission for -additional lossless compression. Bit streams range from 32 to 320 kbps, and 128-kbps rates -achieve near-CD quality, an important specification to enable dual-channel ISDN -(integrated-services-digital-network) to be the future high-bandwidth pipe to the home. - -*/ -static void DISABLE (u32 inst1, u32 inst2) { - //MessageBox (NULL, "Help", "ABI 3 Command 0", MB_OK); - //ChangeABI (5); -} - - -extern "C" const acmd_callback_t ABI3[0x10] = { - DISABLE , ADPCM3 , CLEARBUFF3, ENVMIXER3 , LOADBUFF3, RESAMPLE3 , SAVEBUFF3, MP3, - MP3ADDY, SETVOL3, DMEMMOVE3 , LOADADPCM3 , MIXER3 , INTERLEAVE3, WHATISTHIS , SETLOOP3 -}; - - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3mp3.cpp b/libmupen64plus/mupen64plus-rsp-hle/src/ucode3mp3.cpp deleted file mode 100644 index 21caa33772..0000000000 --- a/libmupen64plus/mupen64plus-rsp-hle/src/ucode3mp3.cpp +++ /dev/null @@ -1,604 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Mupen64plus-rsp-hle - ucode3mp3.h * - * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * - * Copyright (C) 2009 Richard Goedeken * - * Copyright (C) 2002 Hacktarux * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -# include -# include - -extern "C" { - #include "hle.h" - #include "alist_internal.h" -} - -static const u16 DeWindowLUT [0x420] = { - 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, - 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, - 0x0000, 0xFFF3, 0x005D, 0xFF38, 0x037A, 0xF736, 0x0B37, 0xC00E, - 0x7FFF, 0x3FF2, 0x0B37, 0x08CA, 0x037A, 0x00C8, 0x005D, 0x000D, - 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, - 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, - 0x0000, 0xFFF2, 0x005F, 0xFF1D, 0x0369, 0xF697, 0x0A2A, 0xBCE7, - 0x7FEB, 0x3CCB, 0x0C2B, 0x082B, 0x0385, 0x00AF, 0x005B, 0x000B, - 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, - 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, - 0x0000, 0xFFF1, 0x0061, 0xFF02, 0x0354, 0xF5F9, 0x0905, 0xB9C4, - 0x7FB0, 0x39A4, 0x0D08, 0x078C, 0x038C, 0x0098, 0x0058, 0x000A, - 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, - 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, - 0x0000, 0xFFEF, 0x0062, 0xFEE6, 0x033B, 0xF55C, 0x07C8, 0xB6A4, - 0x7F4D, 0x367E, 0x0DCE, 0x06EE, 0x038F, 0x0080, 0x0056, 0x0009, - 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, - 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, - 0x0000, 0xFFEE, 0x0063, 0xFECA, 0x031C, 0xF4C3, 0x0671, 0xB38C, - 0x7EC2, 0x335D, 0x0E7C, 0x0652, 0x038E, 0x006B, 0x0053, 0x0008, - 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, - 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, - 0x0000, 0xFFEC, 0x0064, 0xFEAC, 0x02F7, 0xF42C, 0x0502, 0xB07C, - 0x7E12, 0x3041, 0x0F14, 0x05B7, 0x038A, 0x0056, 0x0050, 0x0007, - 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, - 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, - 0x0000, 0xFFEB, 0x0064, 0xFE8E, 0x02CE, 0xF399, 0x037A, 0xAD75, - 0x7D3A, 0x2D2C, 0x0F97, 0x0520, 0x0382, 0x0043, 0x004D, 0x0007, - 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, - 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, - 0xFFFF, 0xFFE9, 0x0063, 0xFE6F, 0x029E, 0xF30B, 0x01D8, 0xAA7B, - 0x7C3D, 0x2A1F, 0x1004, 0x048B, 0x0377, 0x0030, 0x004A, 0x0006, - 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, - 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, - 0xFFFF, 0xFFE7, 0x0062, 0xFE4F, 0x0269, 0xF282, 0x001F, 0xA78D, - 0x7B1A, 0x271C, 0x105D, 0x03F9, 0x036A, 0x001F, 0x0046, 0x0006, - 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, - 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, - 0xFFFF, 0xFFE4, 0x0061, 0xFE2F, 0x022F, 0xF1FF, 0xFE4C, 0xA4AF, - 0x79D3, 0x2425, 0x10A2, 0x036C, 0x0359, 0x0010, 0x0043, 0x0005, - 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, - 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, - 0xFFFF, 0xFFE2, 0x005E, 0xFE10, 0x01EE, 0xF184, 0xFC61, 0xA1E1, - 0x7869, 0x2139, 0x10D3, 0x02E3, 0x0346, 0x0001, 0x0040, 0x0004, - 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, - 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, - 0xFFFF, 0xFFE0, 0x005B, 0xFDF0, 0x01A8, 0xF111, 0xFA5F, 0x9F27, - 0x76DB, 0x1E5C, 0x10F2, 0x025E, 0x0331, 0xFFF3, 0x003D, 0x0004, - 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, - 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, - 0xFFFF, 0xFFDE, 0x0057, 0xFDD0, 0x015B, 0xF0A7, 0xF845, 0x9C80, - 0x752C, 0x1B8E, 0x1100, 0x01DE, 0x0319, 0xFFE7, 0x003A, 0x0003, - 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, - 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, - 0xFFFE, 0xFFDB, 0x0053, 0xFDB0, 0x0108, 0xF046, 0xF613, 0x99EE, - 0x735C, 0x18D1, 0x10FD, 0x0163, 0x0300, 0xFFDC, 0x0037, 0x0003, - 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, - 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, - 0xFFFE, 0xFFD8, 0x004D, 0xFD90, 0x00B0, 0xEFF0, 0xF3CC, 0x9775, - 0x716C, 0x1624, 0x10EA, 0x00EE, 0x02E5, 0xFFD2, 0x0033, 0x0003, - 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, - 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, - 0xFFFE, 0xFFD6, 0x0047, 0xFD72, 0x0051, 0xEFA6, 0xF16F, 0x9514, - 0x6F5E, 0x138A, 0x10C8, 0x007E, 0x02CA, 0xFFC9, 0x0030, 0x0003, - 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, - 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, - 0xFFFE, 0xFFD3, 0x0040, 0xFD54, 0xFFEC, 0xEF68, 0xEEFC, 0x92CD, - 0x6D33, 0x1104, 0x1098, 0x0014, 0x02AC, 0xFFC0, 0x002D, 0x0002, - 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, - 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, - 0x0030, 0xFFC9, 0x02CA, 0x007E, 0x10C8, 0x138A, 0x6F5E, 0x9514, - 0xF16F, 0xEFA6, 0x0051, 0xFD72, 0x0047, 0xFFD6, 0xFFFE, 0x0003, - 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, - 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, - 0x0033, 0xFFD2, 0x02E5, 0x00EE, 0x10EA, 0x1624, 0x716C, 0x9775, - 0xF3CC, 0xEFF0, 0x00B0, 0xFD90, 0x004D, 0xFFD8, 0xFFFE, 0x0003, - 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, - 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, - 0x0037, 0xFFDC, 0x0300, 0x0163, 0x10FD, 0x18D1, 0x735C, 0x99EE, - 0xF613, 0xF046, 0x0108, 0xFDB0, 0x0053, 0xFFDB, 0xFFFE, 0x0003, - 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, - 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0003, - 0x003A, 0xFFE7, 0x0319, 0x01DE, 0x1100, 0x1B8E, 0x752C, 0x9C80, - 0xF845, 0xF0A7, 0x015B, 0xFDD0, 0x0057, 0xFFDE, 0xFFFF, 0x0004, - 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, - 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, - 0x003D, 0xFFF3, 0x0331, 0x025E, 0x10F2, 0x1E5C, 0x76DB, 0x9F27, - 0xFA5F, 0xF111, 0x01A8, 0xFDF0, 0x005B, 0xFFE0, 0xFFFF, 0x0004, - 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, - 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0004, - 0x0040, 0x0001, 0x0346, 0x02E3, 0x10D3, 0x2139, 0x7869, 0xA1E1, - 0xFC61, 0xF184, 0x01EE, 0xFE10, 0x005E, 0xFFE2, 0xFFFF, 0x0005, - 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, - 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0005, - 0x0043, 0x0010, 0x0359, 0x036C, 0x10A2, 0x2425, 0x79D3, 0xA4AF, - 0xFE4C, 0xF1FF, 0x022F, 0xFE2F, 0x0061, 0xFFE4, 0xFFFF, 0x0006, - 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, - 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, - 0x0046, 0x001F, 0x036A, 0x03F9, 0x105D, 0x271C, 0x7B1A, 0xA78D, - 0x001F, 0xF282, 0x0269, 0xFE4F, 0x0062, 0xFFE7, 0xFFFF, 0x0006, - 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, - 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0006, - 0x004A, 0x0030, 0x0377, 0x048B, 0x1004, 0x2A1F, 0x7C3D, 0xAA7B, - 0x01D8, 0xF30B, 0x029E, 0xFE6F, 0x0063, 0xFFE9, 0xFFFF, 0x0007, - 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, - 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, - 0x004D, 0x0043, 0x0382, 0x0520, 0x0F97, 0x2D2C, 0x7D3A, 0xAD75, - 0x037A, 0xF399, 0x02CE, 0xFE8E, 0x0064, 0xFFEB, 0x0000, 0x0007, - 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, - 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0007, - 0x0050, 0x0056, 0x038A, 0x05B7, 0x0F14, 0x3041, 0x7E12, 0xB07C, - 0x0502, 0xF42C, 0x02F7, 0xFEAC, 0x0064, 0xFFEC, 0x0000, 0x0008, - 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, - 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0008, - 0x0053, 0x006B, 0x038E, 0x0652, 0x0E7C, 0x335D, 0x7EC2, 0xB38C, - 0x0671, 0xF4C3, 0x031C, 0xFECA, 0x0063, 0xFFEE, 0x0000, 0x0009, - 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, - 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x0009, - 0x0056, 0x0080, 0x038F, 0x06EE, 0x0DCE, 0x367E, 0x7F4D, 0xB6A4, - 0x07C8, 0xF55C, 0x033B, 0xFEE6, 0x0062, 0xFFEF, 0x0000, 0x000A, - 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, - 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000A, - 0x0058, 0x0098, 0x038C, 0x078C, 0x0D08, 0x39A4, 0x7FB0, 0xB9C4, - 0x0905, 0xF5F9, 0x0354, 0xFF02, 0x0061, 0xFFF1, 0x0000, 0x000B, - 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, - 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000B, - 0x005B, 0x00AF, 0x0385, 0x082B, 0x0C2B, 0x3CCB, 0x7FEB, 0xBCE7, - 0x0A2A, 0xF697, 0x0369, 0xFF1D, 0x005F, 0xFFF2, 0x0000, 0x000D, - 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, - 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x000D, - 0x005D, 0x00C8, 0x037A, 0x08CA, 0x0B37, 0x3FF2, 0x7FFF, 0xC00E, - 0x0B37, 0xF736, 0x037A, 0xFF38, 0x005D, 0xFFF3, 0x0000, 0x0000 -}; - -//static u16 myVector[32][8]; - -static u8 mp3data[0x1000]; - -static s32 v[32]; - -static void MP3AB0 () { - // Part 2 - 100% Accurate - const u16 LUT2[8] = { 0xFEC4, 0xF4FA, 0xC5E4, 0xE1C4, - 0x1916, 0x4A50, 0xA268, 0x78AE }; - const u16 LUT3[4] = { 0xFB14, 0xD4DC, 0x31F2, 0x8E3A }; - int i; - - for (i = 0; i < 8; i++) { - v[16+i] = v[0+i] + v[8+i]; - v[24+i] = ((v[0+i] - v[8+i]) * LUT2[i]) >> 0x10; - } - - // Part 3: 4-wide butterflies - - for (i=0; i < 4; i++) { - v[0+i] = v[16+i] + v[20+i]; - v[4+i] = ((v[16+i] - v[20+i]) * LUT3[i]) >> 0x10; - - v[8+i] = v[24+i] + v[28+i]; - v[12+i] = ((v[24+i] - v[28+i]) * LUT3[i]) >> 0x10; - } - - // Part 4: 2-wide butterflies - 100% Accurate - - for (i = 0; i < 16; i+=4) { - v[16+i] = v[0+i] + v[2+i]; - v[18+i] = ((v[0+i] - v[2+i]) * 0xEC84) >> 0x10; - - v[17+i] = v[1+i] + v[3+i]; - v[19+i] = ((v[1+i] - v[3+i]) * 0x61F8) >> 0x10; - } -} - -static void InnerLoop (); - -static u32 inPtr, outPtr; - -static u32 t6;// = 0x08A0; // I think these are temporary storage buffers -static u32 t5;// = 0x0AC0; -static u32 t4;// = (inst1 & 0x1E); - -void MP3 (u32 inst1, u32 inst2) { - // Initialization Code - u32 readPtr; // s5 - u32 writePtr; // s6 - //u32 Count = 0x0480; // s4 - u32 tmp; - //u32 inPtr, outPtr; - - t6 = 0x08A0; // I think these are temporary storage buffers - t5 = 0x0AC0; - t4 = (inst1 & 0x1E); - - writePtr = inst2 & 0xFFFFFF; - readPtr = writePtr; - memcpy (mp3data+0xCE8, rsp.RDRAM+readPtr, 8); // Just do that for efficiency... may remove and use directly later anyway - readPtr += 8; // This must be a header byte or whatnot - - for (int cnt = 0; cnt < 0x480; cnt += 0x180) { - memcpy (mp3data+0xCF0, rsp.RDRAM+readPtr, 0x180); // DMA: 0xCF0 <- RDRAM[s5] : 0x180 - inPtr = 0xCF0; // s7 - outPtr = 0xE70; // s3 -// --------------- Inner Loop Start -------------------- - for (int cnt2 = 0; cnt2 < 0x180; cnt2 += 0x40) { - t6 &= 0xFFE0; - t5 &= 0xFFE0; - t6 |= t4; - t5 |= t4; - InnerLoop (); - t4 = (t4-2)&0x1E; - tmp = t6; - t6 = t5; - t5 = tmp; - //outPtr += 0x40; - inPtr += 0x40; - } -// --------------- Inner Loop End -------------------- - memcpy (rsp.RDRAM+writePtr, mp3data+0xe70, 0x180); - writePtr += 0x180; - readPtr += 0x180; - } -} - - - -static void InnerLoop () { - // Part 1: 100% Accurate - - int i; - v[0] = *(s16 *)(mp3data+inPtr+(0x00^S16)); v[31] = *(s16 *)(mp3data+inPtr+(0x3E^S16)); v[0] += v[31]; - v[1] = *(s16 *)(mp3data+inPtr+(0x02^S16)); v[30] = *(s16 *)(mp3data+inPtr+(0x3C^S16)); v[1] += v[30]; - v[2] = *(s16 *)(mp3data+inPtr+(0x06^S16)); v[28] = *(s16 *)(mp3data+inPtr+(0x38^S16)); v[2] += v[28]; - v[3] = *(s16 *)(mp3data+inPtr+(0x04^S16)); v[29] = *(s16 *)(mp3data+inPtr+(0x3A^S16)); v[3] += v[29]; - - v[4] = *(s16 *)(mp3data+inPtr+(0x0E^S16)); v[24] = *(s16 *)(mp3data+inPtr+(0x30^S16)); v[4] += v[24]; - v[5] = *(s16 *)(mp3data+inPtr+(0x0C^S16)); v[25] = *(s16 *)(mp3data+inPtr+(0x32^S16)); v[5] += v[25]; - v[6] = *(s16 *)(mp3data+inPtr+(0x08^S16)); v[27] = *(s16 *)(mp3data+inPtr+(0x36^S16)); v[6] += v[27]; - v[7] = *(s16 *)(mp3data+inPtr+(0x0A^S16)); v[26] = *(s16 *)(mp3data+inPtr+(0x34^S16)); v[7] += v[26]; - - v[8] = *(s16 *)(mp3data+inPtr+(0x1E^S16)); v[16] = *(s16 *)(mp3data+inPtr+(0x20^S16)); v[8] += v[16]; - v[9] = *(s16 *)(mp3data+inPtr+(0x1C^S16)); v[17] = *(s16 *)(mp3data+inPtr+(0x22^S16)); v[9] += v[17]; - v[10]= *(s16 *)(mp3data+inPtr+(0x18^S16)); v[19] = *(s16 *)(mp3data+inPtr+(0x26^S16)); v[10]+= v[19]; - v[11]= *(s16 *)(mp3data+inPtr+(0x1A^S16)); v[18] = *(s16 *)(mp3data+inPtr+(0x24^S16)); v[11]+= v[18]; - - v[12]= *(s16 *)(mp3data+inPtr+(0x10^S16)); v[23] = *(s16 *)(mp3data+inPtr+(0x2E^S16)); v[12]+= v[23]; - v[13]= *(s16 *)(mp3data+inPtr+(0x12^S16)); v[22] = *(s16 *)(mp3data+inPtr+(0x2C^S16)); v[13]+= v[22]; - v[14]= *(s16 *)(mp3data+inPtr+(0x16^S16)); v[20] = *(s16 *)(mp3data+inPtr+(0x28^S16)); v[14]+= v[20]; - v[15]= *(s16 *)(mp3data+inPtr+(0x14^S16)); v[21] = *(s16 *)(mp3data+inPtr+(0x2A^S16)); v[15]+= v[21]; - - // Part 2-4 - - MP3AB0 (); - - // Part 5 - 1-Wide Butterflies - 100% Accurate but need SSVs!!! - - u32 t0 = t6 + 0x100; - u32 t1 = t6 + 0x200; - u32 t2 = t5 + 0x100; - u32 t3 = t5 + 0x200; - /*RSP_GPR[0x8].W = t0; - RSP_GPR[0x9].W = t1; - RSP_GPR[0xA].W = t2; - RSP_GPR[0xB].W = t3; - - RSP_Vect[0].DW[1] = 0xB504A57E00016A09; - RSP_Vect[0].DW[0] = 0x0002D4130005A827; -*/ - - // 0x13A8 - v[1] = 0; - v[11] = ((v[16] - v[17]) * 0xB504) >> 0x10; - - v[16] = -v[16] -v[17]; - v[2] = v[18] + v[19]; - // ** Store v[11] -> (T6 + 0)** - *(s16 *)(mp3data+((t6+(short)0x0))) = (short)v[11]; - - - v[11] = -v[11]; - // ** Store v[16] -> (T3 + 0)** - *(s16 *)(mp3data+((t3+(short)0x0))) = (short)v[16]; - // ** Store v[11] -> (T5 + 0)** - *(s16 *)(mp3data+((t5+(short)0x0))) = (short)v[11]; - // 0x13E8 - Verified.... - v[2] = -v[2]; - // ** Store v[2] -> (T2 + 0)** - *(s16 *)(mp3data+((t2+(short)0x0))) = (short)v[2]; - v[3] = (((v[18] - v[19]) * 0x16A09) >> 0x10) + v[2]; - // ** Store v[3] -> (T0 + 0)** - *(s16 *)(mp3data+((t0+(short)0x0))) = (short)v[3]; - // 0x1400 - Verified - v[4] = -v[20] -v[21]; - v[6] = v[22] + v[23]; - v[5] = ((v[20] - v[21]) * 0x16A09) >> 0x10; - // ** Store v[4] -> (T3 + 0xFF80) - *(s16 *)(mp3data+((t3+(short)0xFF80))) = (short)v[4]; - v[7] = ((v[22] - v[23]) * 0x2D413) >> 0x10; - v[5] = v[5] - v[4]; - v[7] = v[7] - v[5]; - v[6] = v[6] + v[6]; - v[5] = v[5] - v[6]; - v[4] = -v[4] - v[6]; - // *** Store v[7] -> (T1 + 0xFF80) - *(s16 *)(mp3data+((t1+(short)0xFF80))) = (short)v[7]; - // *** Store v[4] -> (T2 + 0xFF80) - *(s16 *)(mp3data+((t2+(short)0xFF80))) = (short)v[4]; - // *** Store v[5] -> (T0 + 0xFF80) - *(s16 *)(mp3data+((t0+(short)0xFF80))) = (short)v[5]; - v[8] = v[24] + v[25]; - - - v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; - v[2] = v[8] + v[9]; - v[11] = ((v[26] - v[27]) * 0x2D413) >> 0x10; - v[13] = ((v[28] - v[29]) * 0x2D413) >> 0x10; - - v[10] = v[26] + v[27]; v[10] = v[10] + v[10]; - v[12] = v[28] + v[29]; v[12] = v[12] + v[12]; - v[14] = v[30] + v[31]; - v[3] = v[8] + v[10]; - v[14] = v[14] + v[14]; - v[13] = (v[13] - v[2]) + v[12]; - v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - (v[11] + v[2]); - v[14] = -(v[14] + v[14]) + v[3]; - v[17] = v[13] - v[10]; - v[9] = v[9] + v[14]; - // ** Store v[9] -> (T6 + 0x40) - *(s16 *)(mp3data+((t6+(short)0x40))) = (short)v[9]; - v[11] = v[11] - v[13]; - // ** Store v[17] -> (T0 + 0xFFC0) - *(s16 *)(mp3data+((t0+(short)0xFFC0))) = (short)v[17]; - v[12] = v[8] - v[12]; - // ** Store v[11] -> (T0 + 0x40) - *(s16 *)(mp3data+((t0+(short)0x40))) = (short)v[11]; - v[8] = -v[8]; - // ** Store v[15] -> (T1 + 0xFFC0) - *(s16 *)(mp3data+((t1+(short)0xFFC0))) = (short)v[15]; - v[10] = -v[10] -v[12]; - // ** Store v[12] -> (T2 + 0x40) - *(s16 *)(mp3data+((t2+(short)0x40))) = (short)v[12]; - // ** Store v[8] -> (T3 + 0xFFC0) - *(s16 *)(mp3data+((t3+(short)0xFFC0))) = (short)v[8]; - // ** Store v[14] -> (T5 + 0x40) - *(s16 *)(mp3data+((t5+(short)0x40))) = (short)v[14]; - // ** Store v[10] -> (T2 + 0xFFC0) - *(s16 *)(mp3data+((t2+(short)0xFFC0))) = (short)v[10]; - // 0x14FC - Verified... - - // Part 6 - 100% Accurate - - v[0] = *(s16 *)(mp3data+inPtr+(0x00^S16)); v[31] = *(s16 *)(mp3data+inPtr+(0x3E^S16)); v[0] -= v[31]; - v[1] = *(s16 *)(mp3data+inPtr+(0x02^S16)); v[30] = *(s16 *)(mp3data+inPtr+(0x3C^S16)); v[1] -= v[30]; - v[2] = *(s16 *)(mp3data+inPtr+(0x06^S16)); v[28] = *(s16 *)(mp3data+inPtr+(0x38^S16)); v[2] -= v[28]; - v[3] = *(s16 *)(mp3data+inPtr+(0x04^S16)); v[29] = *(s16 *)(mp3data+inPtr+(0x3A^S16)); v[3] -= v[29]; - - v[4] = *(s16 *)(mp3data+inPtr+(0x0E^S16)); v[24] = *(s16 *)(mp3data+inPtr+(0x30^S16)); v[4] -= v[24]; - v[5] = *(s16 *)(mp3data+inPtr+(0x0C^S16)); v[25] = *(s16 *)(mp3data+inPtr+(0x32^S16)); v[5] -= v[25]; - v[6] = *(s16 *)(mp3data+inPtr+(0x08^S16)); v[27] = *(s16 *)(mp3data+inPtr+(0x36^S16)); v[6] -= v[27]; - v[7] = *(s16 *)(mp3data+inPtr+(0x0A^S16)); v[26] = *(s16 *)(mp3data+inPtr+(0x34^S16)); v[7] -= v[26]; - - v[8] = *(s16 *)(mp3data+inPtr+(0x1E^S16)); v[16] = *(s16 *)(mp3data+inPtr+(0x20^S16)); v[8] -= v[16]; - v[9] = *(s16 *)(mp3data+inPtr+(0x1C^S16)); v[17] = *(s16 *)(mp3data+inPtr+(0x22^S16)); v[9] -= v[17]; - v[10]= *(s16 *)(mp3data+inPtr+(0x18^S16)); v[19] = *(s16 *)(mp3data+inPtr+(0x26^S16)); v[10]-= v[19]; - v[11]= *(s16 *)(mp3data+inPtr+(0x1A^S16)); v[18] = *(s16 *)(mp3data+inPtr+(0x24^S16)); v[11]-= v[18]; - - v[12]= *(s16 *)(mp3data+inPtr+(0x10^S16)); v[23] = *(s16 *)(mp3data+inPtr+(0x2E^S16)); v[12]-= v[23]; - v[13]= *(s16 *)(mp3data+inPtr+(0x12^S16)); v[22] = *(s16 *)(mp3data+inPtr+(0x2C^S16)); v[13]-= v[22]; - v[14]= *(s16 *)(mp3data+inPtr+(0x16^S16)); v[20] = *(s16 *)(mp3data+inPtr+(0x28^S16)); v[14]-= v[20]; - v[15]= *(s16 *)(mp3data+inPtr+(0x14^S16)); v[21] = *(s16 *)(mp3data+inPtr+(0x2A^S16)); v[15]-= v[21]; - - //0, 1, 3, 2, 7, 6, 4, 5, 7, 6, 4, 5, 0, 1, 3, 2 - const u16 LUT6[16] = { 0xFFB2, 0xFD3A, 0xF10A, 0xF854, - 0xBDAE, 0xCDA0, 0xE76C, 0xDB94, - 0x1920, 0x4B20, 0xAC7C, 0x7C68, - 0xABEC, 0x9880, 0xDAE8, 0x839C }; - for (i = 0; i < 16; i++) { - v[0+i] = (v[0+i] * LUT6[i]) >> 0x10; - } - v[0] = v[0] + v[0]; v[1] = v[1] + v[1]; - v[2] = v[2] + v[2]; v[3] = v[3] + v[3]; v[4] = v[4] + v[4]; - v[5] = v[5] + v[5]; v[6] = v[6] + v[6]; v[7] = v[7] + v[7]; - v[12] = v[12] + v[12]; v[13] = v[13] + v[13]; v[15] = v[15] + v[15]; - - MP3AB0 (); - - // Part 7: - 100% Accurate + SSV - Unoptimized - - v[0] = ( v[17] + v[16] ) >> 1; - v[1] = ((v[17] * (int)((short)0xA57E * 2)) + (v[16] * 0xB504)) >> 0x10; - v[2] = -v[18] -v[19]; - v[3] = ((v[18] - v[19]) * 0x16A09) >> 0x10; - v[4] = v[20] + v[21] + v[0]; - v[5] = (((v[20] - v[21]) * 0x16A09) >> 0x10) + v[1]; - v[6] = (((v[22] + v[23]) << 1) + v[0]) - v[2]; - v[7] = (((v[22] - v[23]) * 0x2D413) >> 0x10) + v[0] + v[1] + v[3]; - // 0x16A8 - // Save v[0] -> (T3 + 0xFFE0) - *(s16 *)(mp3data+((t3+(short)0xFFE0))) = (short)-v[0]; - v[8] = v[24] + v[25]; - v[9] = ((v[24] - v[25]) * 0x16A09) >> 0x10; - v[10] = ((v[26] + v[27]) << 1) + v[8]; - v[11] = (((v[26] - v[27]) * 0x2D413) >> 0x10) + v[8] + v[9]; - v[12] = v[4] - ((v[28] + v[29]) << 1); - // ** Store v12 -> (T2 + 0x20) - *(s16 *)(mp3data+((t2+(short)0x20))) = (short)v[12]; - v[13] = (((v[28] - v[29]) * 0x2D413) >> 0x10) - v[12] - v[5]; - v[14] = v[30] + v[31]; - v[14] = v[14] + v[14]; - v[14] = v[14] + v[14]; - v[14] = v[6] - v[14]; - v[15] = (((v[30] - v[31]) * 0x5A827) >> 0x10) - v[7]; - // Store v14 -> (T5 + 0x20) - *(s16 *)(mp3data+((t5+(short)0x20))) = (short)v[14]; - v[14] = v[14] + v[1]; - // Store v[14] -> (T6 + 0x20) - *(s16 *)(mp3data+((t6+(short)0x20))) = (short)v[14]; - // Store v[15] -> (T1 + 0xFFE0) - *(s16 *)(mp3data+((t1+(short)0xFFE0))) = (short)v[15]; - v[9] = v[9] + v[10]; - v[1] = v[1] + v[6]; - v[6] = v[10] - v[6]; - v[1] = v[9] - v[1]; - // Store v[6] -> (T5 + 0x60) - *(s16 *)(mp3data+((t5+(short)0x60))) = (short)v[6]; - v[10] = v[10] + v[2]; - v[10] = v[4] - v[10]; - // Store v[10] -> (T2 + 0xFFA0) - *(s16 *)(mp3data+((t2+(short)0xFFA0))) = (short)v[10]; - v[12] = v[2] - v[12]; - // Store v[12] -> (T2 + 0xFFE0) - *(s16 *)(mp3data+((t2+(short)0xFFE0))) = (short)v[12]; - v[5] = v[4] + v[5]; - v[4] = v[8] - v[4]; - // Store v[4] -> (T2 + 0x60) - *(s16 *)(mp3data+((t2+(short)0x60))) = (short)v[4]; - v[0] = v[0] - v[8]; - // Store v[0] -> (T3 + 0xFFA0) - *(s16 *)(mp3data+((t3+(short)0xFFA0))) = (short)v[0]; - v[7] = v[7] - v[11]; - // Store v[7] -> (T1 + 0xFFA0) - *(s16 *)(mp3data+((t1+(short)0xFFA0))) = (short)v[7]; - v[11] = v[11] - v[3]; - // Store v[1] -> (T6 + 0x60) - *(s16 *)(mp3data+((t6+(short)0x60))) = (short)v[1]; - v[11] = v[11] - v[5]; - // Store v[11] -> (T0 + 0x60) - *(s16 *)(mp3data+((t0+(short)0x60))) = (short)v[11]; - v[3] = v[3] - v[13]; - // Store v[3] -> (T0 + 0x20) - *(s16 *)(mp3data+((t0+(short)0x20))) = (short)v[3]; - v[13] = v[13] + v[2]; - // Store v[13] -> (T0 + 0xFFE0) - *(s16 *)(mp3data+((t0+(short)0xFFE0))) = (short)v[13]; - //v[2] = ; - v[2] = (v[5] - v[2]) - v[9]; - // Store v[2] -> (T0 + 0xFFA0) - *(s16 *)(mp3data+((t0+(short)0xFFA0))) = (short)v[2]; - // 0x7A8 - Verified... - - // Step 8 - Dewindowing - - //u64 *DW = (u64 *)&DeWindowLUT[0x10-(t4>>1)]; - u32 offset = 0x10-(t4>>1); - - u32 addptr = t6 & 0xFFE0; - offset = 0x10-(t4>>1); - - s32 v2=0, v4=0, v6=0, v8=0; - //s32 z2=0, z4=0, z6=0, z8=0; - - offset = 0x10-(t4>>1);// + x*0x40; - int x; - for (x = 0; x < 8; x++) { - v2 = v4 = v6 = v8 = 0; - - //addptr = t1; - - for (i = 7; i >= 0; i--) { - v2 += ((int)*(s16 *)(mp3data+(addptr)+0x00) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v4 += ((int)*(s16 *)(mp3data+(addptr)+0x10) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - v6 += ((int)*(s16 *)(mp3data+(addptr)+0x20) * (short)DeWindowLUT[offset+0x20] + 0x4000) >> 0xF; - v8 += ((int)*(s16 *)(mp3data+(addptr)+0x30) * (short)DeWindowLUT[offset+0x28] + 0x4000) >> 0xF; - addptr+=2; offset++; - } - s32 v0 = v2 + v4; - s32 v18 = v6 + v8; - //Clamp(v0); - //Clamp(v18); - // clamp??? - *(s16 *)(mp3data+(outPtr^S16)) = v0; - *(s16 *)(mp3data+((outPtr+2)^S16)) = v18; - outPtr+=4; - addptr += 0x30; - offset += 0x38; - } - - offset = 0x10-(t4>>1) + 8*0x40; - v2 = v4 = 0; - for (i = 0; i < 4; i++) { - v2 += ((int)*(s16 *)(mp3data+(addptr)+0x00) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v2 += ((int)*(s16 *)(mp3data+(addptr)+0x10) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - addptr+=2; offset++; - v4 += ((int)*(s16 *)(mp3data+(addptr)+0x00) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v4 += ((int)*(s16 *)(mp3data+(addptr)+0x10) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - addptr+=2; offset++; - } - s32 mult6 = *(s32 *)(mp3data+0xCE8); - s32 mult4 = *(s32 *)(mp3data+0xCEC); - if (t4 & 0x2) { - v2 = (v2 * *(u32 *)(mp3data+0xCE8)) >> 0x10; - *(s16 *)(mp3data+(outPtr^S16)) = v2; - } else { - v4 = (v4 * *(u32 *)(mp3data+0xCE8)) >> 0x10; - *(s16 *)(mp3data+(outPtr^S16)) = v4; - mult4 = *(u32 *)(mp3data+0xCE8); - } - addptr -= 0x50; - - for (x = 0; x < 8; x++) { - v2 = v4 = v6 = v8 = 0; - - offset = (0x22F-(t4>>1) + x*0x40); - - for (i = 0; i < 4; i++) { - v2 += ((int)*(s16 *)(mp3data+(addptr )+0x20) * (short)DeWindowLUT[offset+0x00] + 0x4000) >> 0xF; - v2 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x20) * (short)DeWindowLUT[offset+0x01] + 0x4000) >> 0xF; - v4 += ((int)*(s16 *)(mp3data+(addptr )+0x30) * (short)DeWindowLUT[offset+0x08] + 0x4000) >> 0xF; - v4 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x30) * (short)DeWindowLUT[offset+0x09] + 0x4000) >> 0xF; - v6 += ((int)*(s16 *)(mp3data+(addptr )+0x00) * (short)DeWindowLUT[offset+0x20] + 0x4000) >> 0xF; - v6 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x00) * (short)DeWindowLUT[offset+0x21] + 0x4000) >> 0xF; - v8 += ((int)*(s16 *)(mp3data+(addptr )+0x10) * (short)DeWindowLUT[offset+0x28] + 0x4000) >> 0xF; - v8 -= ((int)*(s16 *)(mp3data+((addptr+2))+0x10) * (short)DeWindowLUT[offset+0x29] + 0x4000) >> 0xF; - addptr+=4; offset+=2; - } - s32 v0 = v2 + v4; - s32 v18 = v6 + v8; - //Clamp(v0); - //Clamp(v18); - // clamp??? - *(s16 *)(mp3data+((outPtr+2)^S16)) = v0; - *(s16 *)(mp3data+((outPtr+4)^S16)) = v18; - outPtr+=4; - addptr -= 0x50; - } - - int tmp = outPtr; - s32 hi0 = mult6; - s32 hi1 = mult4; - s32 v; - - hi0 = (int)hi0 >> 0x10; - hi1 = (int)hi1 >> 0x10; - for (i = 0; i < 8; i++) { - // v0 - v = (*(s16 *)(mp3data+((tmp-0x40)^S16)) * hi0); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0x40)^S16)) = (s16)v; - // v17 - v = (*(s16 *)(mp3data+((tmp-0x30)^S16)) * hi0); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0x30)^S16)) = v; - // v2 - v = (*(s16 *)(mp3data+((tmp-0x1E)^S16)) * hi1); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0x1E)^S16)) = v; - // v4 - v = (*(s16 *)(mp3data+((tmp-0xE)^S16)) * hi1); - if (v > 32767) v = 32767; else if (v < -32767) v = -32767; - *(s16 *)((u8 *)mp3data+((tmp-0xE)^S16)) = v; - tmp += 2; - } -} - diff --git a/libmupen64plus/mupen64plus-rsp-hle/src/ucodes.h b/libmupen64plus/mupen64plus-rsp-hle/src/ucodes.h new file mode 100644 index 0000000000..1150cf6b18 --- /dev/null +++ b/libmupen64plus/mupen64plus-rsp-hle/src/ucodes.h @@ -0,0 +1,152 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-rsp-hle - ucodes.h * + * Mupen64Plus homepage: https://mupen64plus.org/ * + * Copyright (C) 2014 Bobby Smiles * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef UCODES_H +#define UCODES_H + +#include + +struct hle_t; + + +/* cic_x105 ucode */ +void cicx105_ucode(struct hle_t* hle); + + +/* audio list ucodes - audio */ +enum { N_SEGMENTS = 16 }; +struct alist_audio_t { + /* segments */ + uint32_t segments[N_SEGMENTS]; + + /* main buffers */ + uint16_t in; + uint16_t out; + uint16_t count; + + /* auxiliary buffers */ + uint16_t dry_right; + uint16_t wet_left; + uint16_t wet_right; + + /* gains */ + int16_t dry; + int16_t wet; + + /* envelopes (0:left, 1:right) */ + int16_t vol[2]; + int16_t target[2]; + int32_t rate[2]; + + /* ADPCM loop point address */ + uint32_t loop; + + /* storage for ADPCM table and polef coefficients */ + int16_t table[16 * 8]; +}; + +void alist_process_audio (struct hle_t* hle); +void alist_process_audio_ge(struct hle_t* hle); +void alist_process_audio_bc(struct hle_t* hle); + + +/* audio list ucodes - naudio */ +struct alist_naudio_t { + /* gains */ + int16_t dry; + int16_t wet; + + /* envelopes (0:left, 1:right) */ + int16_t vol[2]; + int16_t target[2]; + int32_t rate[2]; + + /* ADPCM loop point address */ + uint32_t loop; + + /* storage for ADPCM table and polef coefficients */ + int16_t table[16 * 8]; +}; + +void alist_process_naudio (struct hle_t* hle); +void alist_process_naudio_bk (struct hle_t* hle); +void alist_process_naudio_dk (struct hle_t* hle); +void alist_process_naudio_mp3 (struct hle_t* hle); +void alist_process_naudio_cbfd(struct hle_t* hle); + + +/* audio list ucodes - nead */ +struct alist_nead_t { + /* main buffers */ + uint16_t in; + uint16_t out; + uint16_t count; + + /* envmixer ramps */ + uint16_t env_values[3]; + uint16_t env_steps[3]; + + /* ADPCM loop point address */ + uint32_t loop; + + /* storage for ADPCM table and polef coefficients */ + int16_t table[16 * 8]; + + /* filter audio command state */ + uint16_t filter_count; + uint32_t filter_lut_address[2]; +}; + +void alist_process_nead_mk (struct hle_t* hle); +void alist_process_nead_sfj (struct hle_t* hle); +void alist_process_nead_sf (struct hle_t* hle); +void alist_process_nead_fz (struct hle_t* hle); +void alist_process_nead_wrjb(struct hle_t* hle); +void alist_process_nead_ys (struct hle_t* hle); +void alist_process_nead_1080(struct hle_t* hle); +void alist_process_nead_oot (struct hle_t* hle); +void alist_process_nead_mm (struct hle_t* hle); +void alist_process_nead_mmb (struct hle_t* hle); +void alist_process_nead_ac (struct hle_t* hle); +void alist_process_nead_mats(struct hle_t* hle); +void alist_process_nead_efz (struct hle_t* hle); + +/* mp3 ucode */ +void mp3_task(struct hle_t* hle, unsigned int index, uint32_t address); + + +/* musyx ucodes */ +void musyx_v1_task(struct hle_t* hle); +void musyx_v2_task(struct hle_t* hle); + + +/* jpeg ucodes */ +void jpeg_decode_PS0(struct hle_t* hle); +void jpeg_decode_PS(struct hle_t* hle); +void jpeg_decode_OB(struct hle_t* hle); + +/* Resident evil 2 ucode */ +void resize_bilinear_task(struct hle_t* hle); +void decode_video_frame_task(struct hle_t* hle); +void fill_video_double_buffer_task(struct hle_t* hle); + +#endif + diff --git a/output/dll/7z.dll b/output/dll/7z.dll index cea996e4f5..be29515b79 100644 Binary files a/output/dll/7z.dll and b/output/dll/7z.dll differ diff --git a/output/dll/libgambatte.dll b/output/dll/libgambatte.dll index 20e9164416..79c5fccd1b 100644 Binary files a/output/dll/libgambatte.dll and b/output/dll/libgambatte.dll differ diff --git a/output/dll/libsnes.wbx.gz b/output/dll/libsnes.wbx.gz index 67a8e75a6a..c644208284 100644 Binary files a/output/dll/libsnes.wbx.gz and b/output/dll/libsnes.wbx.gz differ diff --git a/output/dll/mgba.dll b/output/dll/mgba.dll index ab39aaeb38..77d932ca87 100644 Binary files a/output/dll/mgba.dll and b/output/dll/mgba.dll differ diff --git a/output/dll/mupen64plus-rsp-hle.dll b/output/dll/mupen64plus-rsp-hle.dll index 777026c971..e94e85f093 100644 Binary files a/output/dll/mupen64plus-rsp-hle.dll and b/output/dll/mupen64plus-rsp-hle.dll differ diff --git a/output/dll/mupen64plus-video-GLideN64.dll b/output/dll/mupen64plus-video-GLideN64.dll index dc59326a68..2d4981fdc4 100644 Binary files a/output/dll/mupen64plus-video-GLideN64.dll and b/output/dll/mupen64plus-video-GLideN64.dll differ diff --git a/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp b/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp index 638e8d202c..77a9bd4a9e 100644 --- a/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp +++ b/waterbox/libsnes/bsnes/snes/cpu/core/disassembler/disassembler.cpp @@ -441,7 +441,7 @@ void CPUcore::disassemble_opcode(char *output, uint32 addr) { strcat(s, t); strcat(s, " "); - sprintf(t, "V:%3d H:%4d", cpu.vcounter(), cpu.hcounter()); + sprintf(t, "V:%3d H:%3d", cpu.vcounter(), cpu.hdot()); strcat(s, t); } diff --git a/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp b/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp index 55d01886f0..b8ea3d64fc 100644 --- a/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp +++ b/waterbox/libsnes/bsnes/target-libsnes/libsnes_pwrap.cpp @@ -113,6 +113,7 @@ struct CPURegsComm { u8 p, nothing; u32 aa, rd; u8 sp, dp, db, mdr; + u16 v, h; } #ifndef _MSC_VER __attribute__((__packed__)) @@ -174,8 +175,6 @@ struct CommStruct uint32 region; uint32 mapper; - int32 padding4; - //=========================================================== //private stuff @@ -537,6 +536,8 @@ void QUERY_peek_cpu_regs() { comm.cpuregs.vector = SNES::cpu.regs.vector; comm.cpuregs.p = SNES::cpu.regs.p; comm.cpuregs.nothing = 0; + comm.cpuregs.v = SNES::cpu.vcounter(); + comm.cpuregs.h = SNES::cpu.hdot(); } void QUERY_peek_set_cdl() { for (int i = 0; i