diff --git a/Assets/gamedb/gamedb.txt b/Assets/gamedb/gamedb.txt index f2c2ba9c25..ba89a10f60 100644 --- a/Assets/gamedb/gamedb.txt +++ b/Assets/gamedb/gamedb.txt @@ -27,6 +27,7 @@ #include gamedb_zxspectrum.txt #include gamedb_amstradcpc.txt #include gamedb_ngp.txt +#include gamedb_channelf.txt ; ************ TI-83 ************ diff --git a/Assets/gamedb/gamedb_channelf.txt b/Assets/gamedb/gamedb_channelf.txt new file mode 100644 index 0000000000..e57abc0c17 --- /dev/null +++ b/Assets/gamedb/gamedb_channelf.txt @@ -0,0 +1,58 @@ +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +;;; Type: NO-INTRO +;;; Source: Fairchild - Channel F - 20120223-000000 +;;; FileGen: 2019-04-16 13:59:49 (UTC) +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +;;; Bad Dumps +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +; +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +;;; Hacks +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +; +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +;;; Over Dumps +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +; +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +;;; Translated +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +; +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +;;; Believed Good +;;;;;;;;;;--------------------------------------------------;;;;;;;;;; +F7BF7D55A7660FFA80D08AD1BA903FF7 Alien Invasion (USA) ChannelF USA +D89B48AE8C906488CAAC2B2AE1D63D88 Backgammon, Acey-Deucey (USA) ChannelF USA +4FA83F734C139963AA02BDBB7A52E500 Baseball (USA) ChannelF USA +25E231E7A464A32B4715BFB47AF89240 Bowling (USA) ChannelF USA +BB7F7BBBE21F142591CDCAFA98D7F6E4 Casino Poker (USA) ChannelF USA +35D61D40EF7EC337CBA092AABAC74DBD Checkers (USA) ChannelF USA +54CF17C48707467295749490D458EAFB Demonstration Cartridge (USA) ChannelF USA +F6916B665893AA8138CDE57C37E50ADA Demonstration Cartridge 2 (USA) ChannelF USA +4F11F13CBCA685CB20E888F87B3B1586 Desert Fox, Shooting Gallery (USA) ChannelF USA +6FFEDAED3C5CD8BA74D98901849CC451 Dodge It (USA) ChannelF USA +F80AF74B09D058B90E719BB7DFBDD50E Drag Race (USA) ChannelF USA +9E0711B140E22729687DB1E1354980AB Galactic Space Wars, Lunar Lander (USA) ChannelF USA +0124CD0B61DF5502AABD59029CCB6D5A Hangman (USA) ChannelF USA +4C10FA5C7316C59EFA241043FC67DFA8 Magic Numbers - Mind Reader + Nim (USA) ChannelF USA +A8E6103FCAE4D0F9E14D9EDCFC3FC493 Math Quiz I - Addition + Subtraction (USA) ChannelF USA +86B77EAFDF7B806E19E01724987E384F Math Quiz II - Multiplication + Division (USA) ChannelF USA +6565DF74539476D66FD78DE1BAC0259C Maze, Jailbreak, Blind-man's-bluff, Trailblazer (USA) ChannelF USA +53E4CC2DA0F2C167E0692B794CB7692C Maze, Jailbreak, Blind-man's-bluff, Trailblazer (USA) (Alt 1) ChannelF USA +2B3CA549E27579E4519A765FD8F52D0F Memory Match 1 & 2 (USA) ChannelF USA +1FBD86DCCA0E4619963B902C48AE77F2 Muehle, Tontauben-Schiessen, Kreatives Malspiel, Videoscope (Germany) ChannelF Germany +C2A44D22D3865B036479E9311C74D3AD Ordtaevling (Sweden) ChannelF Sweden +E90339B7068C6227D54F3C0CA637E017 Pinball Challenge (USA) ChannelF USA +5CBCDA1C44F0DAD02B0DFE209B6325D5 Pinball Challenge (USA) (Alt 1) ChannelF USA +9A894D745356A050F95410983C3BC54A Pro Football (USA) ChannelF USA +913ECBAA30816C6D78DE8651251761FC Rat' Mal (Germany) ChannelF Germany +3783B6AC359E21B99CFA17773AA811C6 Robot War, Torpedo Alley (USA) ChannelF USA +5568205F926333914DEDC8EF8BF16AF2 Schach (Germany) ChannelF Germany +DFB66EE145FAB65062FDEADAFC8DC34C Slot Machine (USA) ChannelF USA +4CB12EDAE37DF23851884B82CA410754 Sonar Search (USA) ChannelF USA +32CCA8FF09041A39251D7AADE21EE22F Space War (USA) ChannelF USA +1B409FE1154584F4D1AB76B344A73D99 Spitfire (USA) ChannelF USA +7E5C26A6D1F9A90C68669A9800BA522D Tic-Tac-Toe, Shooting Gallery, Doodle, Quadra-Doodle (USA) ChannelF USA +B074C867F235FB69CED96C6916673B45 Video Blackjack (USA) ChannelF USA +90A9B3952568F91502A7088BFB0AE07E Video Whizball (USA) ChannelF USA diff --git a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs index a8a3751112..e05823994b 100644 --- a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs +++ b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs @@ -108,6 +108,9 @@ namespace BizHawk.Client.ApiHawk case "GGL": return CoreSystem.GGL; + case "ChannelF": + return CoreSystem.ChannelF; + case "VB": case "NGP": case "DNGP": diff --git a/BizHawk.Client.Common/Api/CoreSystem.cs b/BizHawk.Client.Common/Api/CoreSystem.cs index 48ba2e4f1a..05b63c7f96 100644 --- a/BizHawk.Client.Common/Api/CoreSystem.cs +++ b/BizHawk.Client.Common/Api/CoreSystem.cs @@ -33,6 +33,7 @@ NeoGeoPocket, ZXSpectrum, AmstradCPC, - GGL + GGL, + ChannelF } } diff --git a/BizHawk.Client.Common/Global.cs b/BizHawk.Client.Common/Global.cs index c233e7a7ec..c8ef120a66 100644 --- a/BizHawk.Client.Common/Global.cs +++ b/BizHawk.Client.Common/Global.cs @@ -154,6 +154,8 @@ namespace BizHawk.Client.Common return SystemInfo.ZXSpectrum; case "AmstradCPC": return SystemInfo.AmstradCPC; + case "ChannelF": + return SystemInfo.ChannelF; } } } diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 0e10ef3bf4..0c3ff35062 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -29,6 +29,7 @@ using BizHawk.Emulation.Cores.Consoles.Sega.Saturn; using BizHawk.Emulation.Cores.Consoles.NEC.PCFX; using BizHawk.Emulation.Cores.Computers.AmstradCPC; using BizHawk.Emulation.Cores.Consoles.Vectrex; +using BizHawk.Emulation.Cores.Consoles.ChannelF; namespace BizHawk.Client.Common { @@ -1079,6 +1080,9 @@ namespace BizHawk.Client.Common Deterministic); nextEmulator = zx; break; + case "ChannelF": + nextEmulator = new ChannelF(nextComm, game, rom.FileData, GetCoreSettings(), GetCoreSyncSettings()); + break; case "AmstradCPC": var cpc = new AmstradCPC(nextComm, Enumerable.Repeat(rom.RomData, 1), Enumerable.Repeat(rom.GameInfo, 1).ToList(), GetCoreSettings(), GetCoreSyncSettings()); nextEmulator = cpc; diff --git a/BizHawk.Client.Common/SystemInfo.cs b/BizHawk.Client.Common/SystemInfo.cs index a109e7ddf9..6b631d0851 100644 --- a/BizHawk.Client.Common/SystemInfo.cs +++ b/BizHawk.Client.Common/SystemInfo.cs @@ -204,10 +204,15 @@ namespace BizHawk.Client.Common public static SystemInfo AmstradCPC { get; } = new SystemInfo("Amstrad CPC", CoreSystem.AmstradCPC, 2); /// - /// Gets the instance for AmstradCPC + /// Gets the instance for GGL /// public static SystemInfo GGL { get; } = new SystemInfo("Game Gear Linked", CoreSystem.GGL, 2); + /// + /// Gets the instance for ChannelF + /// + public static SystemInfo ChannelF { get; } = new SystemInfo("Channel F", CoreSystem.ChannelF, 2); + #endregion Get SystemInfo /// diff --git a/BizHawk.Client.Common/config/PathEntry.cs b/BizHawk.Client.Common/config/PathEntry.cs index 3cc3f6d04c..7df0046165 100644 --- a/BizHawk.Client.Common/config/PathEntry.cs +++ b/BizHawk.Client.Common/config/PathEntry.cs @@ -382,6 +382,12 @@ namespace BizHawk.Client.Common new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 }, new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, new PathEntry { System = "PCFX", SystemDisplayName = "PCFX", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, + + new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Base", Path = Path.Combine(".", "ZXSpectrum"), Ordinal = 0 }, + new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "ROM", Path = ".", Ordinal = 1 }, + new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, + new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, + new PathEntry { System = "ChannelF", SystemDisplayName = "Fairchild Channel F", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, }; } } diff --git a/BizHawk.Client.DBMan/DATTools/DATParserBase.cs b/BizHawk.Client.DBMan/DATTools/DATParserBase.cs index f13196463c..7034724261 100644 --- a/BizHawk.Client.DBMan/DATTools/DATParserBase.cs +++ b/BizHawk.Client.DBMan/DATTools/DATParserBase.cs @@ -218,6 +218,7 @@ namespace BizHawk.Client.DBMan Lynx, VB, UZE, - NGP + NGP, + ChannelF } } diff --git a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs index af5da08b62..25b246d55f 100644 --- a/BizHawk.Client.EmuHawk/config/ControllerConfig.cs +++ b/BizHawk.Client.EmuHawk/config/ControllerConfig.cs @@ -176,7 +176,7 @@ namespace BizHawk.Client.EmuHawk tt.TabPages[pageidx].Controls.Add(createpanel(settings, cat.Value, tt.Size)); // zxhawk hack - it uses multiple categoryLabels - if (Global.Emulator.SystemId == "ZXSpectrum" || Global.Emulator.SystemId == "AmstradCPC") + if (Global.Emulator.SystemId == "ZXSpectrum" || Global.Emulator.SystemId == "AmstradCPC" || Global.Emulator.SystemId == "ChannelF") pageidx++; } @@ -184,7 +184,7 @@ namespace BizHawk.Client.EmuHawk if (buckets[0].Count > 0) { // ZXHawk needs to skip this bit - if (Global.Emulator.SystemId == "ZXSpectrum" || Global.Emulator.SystemId == "AmstradCPC") + if (Global.Emulator.SystemId == "ZXSpectrum" || Global.Emulator.SystemId == "AmstradCPC" || Global.Emulator.SystemId == "ChannelF") return; string tabname = (Global.Emulator.SystemId == "C64") ? "Keyboard" : "Console"; // hack @@ -275,6 +275,11 @@ namespace BizHawk.Client.EmuHawk tableLayoutPanel1.ColumnStyles[1].Width = Properties.Resources.ZXSpectrumKeyboards.Width; } + if (controlName == "ChannelF Controller") + { + + } + if (controlName == "AmstradCPC Controller") { /* diff --git a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs index cbfbb76aaa..826df7d025 100644 --- a/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs +++ b/BizHawk.Client.EmuHawk/config/FirmwaresConfig.cs @@ -53,7 +53,8 @@ namespace BizHawk.Client.EmuHawk { "PCFX", "PC-FX" }, { "32X", "32X" }, { "ZXSpectrum", "ZX Spectrum" }, - { "AmstradCPC", "Amstrad CPC" } + { "AmstradCPC", "Amstrad CPC" }, + { "ChannelF", "Channel F" } }; public string TargetSystem = null; diff --git a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs index 79e2233317..713ad82bb6 100644 --- a/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs +++ b/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs @@ -61,6 +61,10 @@ namespace BizHawk.Emulation.Common FirmwareAndOption("A584272F21DC82C14B7D4F1ED440E23A976E71F0", 32768, "ZXSpectrum", "PentagonROM", "pentagon.rom", "Russian Pentagon Clone ROM"); FirmwareAndOption("282EB7BC819AAD2A12FD954E76F7838A4E1A7929", 16384, "ZXSpectrum", "TRDOSROM", "trdos.rom", "TRDOS ROM"); + // Channel F + FirmwareAndOption("81193965A374D77B99B4743D317824B53C3E3C78", 1024, "ChannelF", "ChannelF_sl131253", "ChannelF-SL31253.rom", "Channel F Rom0"); + FirmwareAndOption("8F70D1B74483BA3A37E86CF16C849D601A8C3D2C", 1024, "ChannelF", "ChannelF_sl131254", "ChannelF-SL31254.rom", "Channel F Rom1"); + // for saturn, we think any bios region can pretty much run any iso // so, we're going to lay this out carefully so that we choose things in a sensible order, but prefer the correct region var ss_100_j = File("2B8CB4F87580683EB4D760E4ED210813D667F0A2", 524288, "saturn-1.00-(J).bin", "Bios v1.00 (J)"); diff --git a/BizHawk.Emulation.Common/SystemLookup.cs b/BizHawk.Emulation.Common/SystemLookup.cs index 0e4cc90d06..88f7fe8e6c 100644 --- a/BizHawk.Emulation.Common/SystemLookup.cs +++ b/BizHawk.Emulation.Common/SystemLookup.cs @@ -34,7 +34,8 @@ namespace BizHawk.Emulation.Common new SystemInfo { SystemId = "AppleII", FullName = "Apple II" }, new SystemInfo { SystemId = "INTV", FullName = "Intellivision" }, new SystemInfo { SystemId = "ZXSpectrum", FullName = "Sinclair ZX Spectrum" }, - new SystemInfo { SystemId = "AmstradCPC", FullName = "Amstrad CPC" } + new SystemInfo { SystemId = "AmstradCPC", FullName = "Amstrad CPC" }, + new SystemInfo { SystemId = "ChannelF", FullName = "Fairchild Channel F"}, }; public SystemInfo this[string systemId] diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index b7fa5086d0..0e3a43dcd8 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -595,6 +595,21 @@ + + + + + + + + + + + + + + + diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.Controllers.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.Controllers.cs new file mode 100644 index 0000000000..b0fa1ec6bb --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.Controllers.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public partial class ChannelF + { + public ControllerDefinition ChannelFControllerDefinition + { + get + { + ControllerDefinition definition = new ControllerDefinition(); + definition.Name = "ChannelF Controller"; + + // sticks + List stickL = new List + { + // P1 (left) stick + "P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Button Up", "P1 Button Down", "P1 Rotate Left", "P1 Rotate Right" + }; + + foreach (var s in stickL) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Left Controller"; + } + + List stickR = new List + { + // P1 (left) stick + "P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Button Up", "P2 Button Down", "P2 Rotate Left", "P2 Rotate Right" + }; + + foreach (var s in stickR) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Right Controller"; + } + + // console + List consoleButtons = new List + { + "RESET", "START", "HOLD", "MODE", "TIME" + }; + + foreach (var s in consoleButtons) + { + definition.BoolButtons.Add(s); + definition.CategoryLabels[s] = "Console"; + } + + return definition; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IEmulator.cs new file mode 100644 index 0000000000..459d8d8d7c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IEmulator.cs @@ -0,0 +1,61 @@ +using System; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public partial class ChannelF : IEmulator + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition { get; set; } + + public string SystemId => "ChannelF"; + + public bool DeterministicEmulation { get; set; } + + private static double cpuFreq = 1.7897725; + private static double refreshRate = 60; + + public int ClockPerFrame; + public int FrameClock = 0; + + private void CalcClock() + { + var c = (cpuFreq * 1000000) / refreshRate; + ClockPerFrame = (int) c; + } + + public bool FrameAdvance(IController controller, bool render, bool renderSound) + { + while (FrameClock++ < ClockPerFrame) + { + CPU.ExecuteOne(); + } + + FrameClock = 0; + _frame++; + return true; + } + + private int _frame; + private int _lagcount; + private bool _islag; + + public void ResetCounters() + { + _frame = 0; + _lagcount = 0; + _islag = false; + } + + public int Frame => _frame; + + public CoreComm CoreComm { get; } + + public void Dispose() + { + + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IO.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IO.cs new file mode 100644 index 0000000000..7ccc2ede1b --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IO.cs @@ -0,0 +1,80 @@ +using System; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + + public partial class ChannelF + { + public byte[] BIOS01 = new byte[1024]; + public byte[] BIOS02 = new byte[1024]; + + public byte[] FrameBuffer = new byte[2048]; + + public byte[] Cartridge = new byte[0x2000 - 0x800]; + + public byte ReadBus(ushort addr) + { + if (addr < 0x400) + { + // Rom0 + return BIOS01[addr]; + } + else if (addr < 0x800) + { + // Rom1 + return BIOS02[addr - 0x400]; + } + else if (addr < 0x2000) + { + // Cart + return Cartridge[addr - 0x800]; + } + else if (addr < 0x2000 + 2048) + { + // Framebuffer + return FrameBuffer[addr - 0x2000]; + } + else + { + return 0xFF; + } + } + + public void WriteBus(ushort addr, byte value) + { + if (addr < 0x400) + { + // Rom0 + } + else if (addr < 0x800) + { + // Rom1 + } + else if (addr < 0x2000) + { + // Cart + } + else if (addr < 0x2000 + 2048) + { + // Framebuffer + FrameBuffer[addr - 0x2000] = value; + } + else + { + + } + } + + public byte ReadPort(ushort addr) + { + return 0xff; + } + + public void WritePort(ushort addr, byte value) + { + + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISettable.cs new file mode 100644 index 0000000000..d23b8b87b1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.ISettable.cs @@ -0,0 +1,79 @@ +using System; +using System.ComponentModel; +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Nintendo.NES; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public partial class ChannelF : ISettable + { + internal ChannelFSettings Settings = new ChannelFSettings(); + internal ChannelFSyncSettings SyncSettings = new ChannelFSyncSettings(); + + public ChannelFSettings GetSettings() + { + return Settings.Clone(); + } + + public ChannelFSyncSettings GetSyncSettings() + { + return SyncSettings.Clone(); + } + + public bool PutSettings(ChannelFSettings o) + { + Settings = o; + return false; + } + + public bool PutSyncSettings(ChannelFSyncSettings o) + { + bool ret = ChannelFSyncSettings.NeedsReboot(SyncSettings, o); + SyncSettings = o; + return ret; + } + + public class ChannelFSettings + { + [DisplayName("Default Background Color")] + [Description("The default BG color")] + [DefaultValue(0)] + public int BackgroundColor { get; set; } + + public ChannelFSettings Clone() + { + return (ChannelFSettings)MemberwiseClone(); + } + + public ChannelFSettings() + { + SettingsUtil.SetDefaultValues(this); + } + } + + public class ChannelFSyncSettings + { + [DisplayName("Deterministic Emulation")] + [Description("If true, the core agrees to behave in a completely deterministic manner")] + [DefaultValue(true)] + public bool DeterministicEmulation { get; set; } + + public ChannelFSyncSettings Clone() + { + return (ChannelFSyncSettings) MemberwiseClone(); + } + + public ChannelFSyncSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public static bool NeedsReboot(ChannelFSyncSettings x, ChannelFSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IStatable.cs new file mode 100644 index 0000000000..d0c2cd3208 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IStatable.cs @@ -0,0 +1,99 @@ +using System; +using System.IO; +using BizHawk.Common; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public partial class ChannelF : IStatable + { + public bool BinarySaveStatesPreferred => 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(nameof(_machineType), ref _machineType); + + _cpu.SyncState(ser); + ser.BeginSection(nameof(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(nameof(_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(nameof(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/Consoles/Fairchild/ChannelF/ChannelF.IVideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IVideoProvider.cs new file mode 100644 index 0000000000..f04ca98ea0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.IVideoProvider.cs @@ -0,0 +1,26 @@ +using System; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public partial class ChannelF : IVideoProvider + { + public int _frameHz = 60; + + public int[] _vidbuffer = new int[102 * 58]; + + public int[] GetVideoBuffer() + { + return _vidbuffer; + } + + public int VirtualWidth => 102 * 2; + public int VirtualHeight => 58 * 2; + public int BufferWidth => 102; + public int BufferHeight => 58; + public int BackgroundColor => unchecked((int)0xFF000000); + public int VsyncNumerator => _frameHz; + public int VsyncDenominator => 1; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.InputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.InputPollable.cs new file mode 100644 index 0000000000..1112bf1b21 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.InputPollable.cs @@ -0,0 +1,26 @@ +using System; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public partial class ChannelF : 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/Consoles/Fairchild/ChannelF/ChannelF.MemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.MemoryDomains.cs new file mode 100644 index 0000000000..002ef63130 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.MemoryDomains.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public partial class ChannelF + { + internal IMemoryDomains memoryDomains; + private readonly Dictionary _byteArrayDomains = new Dictionary(); + private bool _memoryDomainsInit = false; + + private void SetupMemoryDomains() + { + + } + + private void SyncAllByteArrayDomains() + { + + } + + 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/Consoles/Fairchild/ChannelF/ChannelF.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.cs new file mode 100644 index 0000000000..957f7d6c5a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/ChannelF.cs @@ -0,0 +1,48 @@ +using System; +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + [Core( + "ChannelFHawk", + "Asnivor", + isPorted: false, + isReleased: false)] + [ServiceNotApplicable(typeof(IDriveLight))] + public partial class ChannelF + { + public ChannelF(CoreComm comm, GameInfo game, byte[] rom, object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + ServiceProvider = ser; + CoreComm = comm; + InputCallbacks = new InputCallbackSystem(); + + ControllerDefinition = ChannelFControllerDefinition; + + CPU = new F3850 + { + ReadMemory = ReadBus, + WriteMemory = WriteBus, + ReadHardware = ReadPort, + WriteHardware = WritePort, + DummyReadMemory = ReadBus + }; + + byte[] bios01 = comm.CoreFileProvider.GetFirmware("ChannelF", "ChannelF_sl131253", true); + byte[] bios02 = comm.CoreFileProvider.GetFirmware("ChannelF", "ChannelF_sl131254", true); + + BIOS01 = bios01; + BIOS02 = bios02; + + Array.Copy(rom, 0, Cartridge, 0, rom.Length); + + CalcClock(); + + ser.Register(this); + } + + public F3850 CPU; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Disassembler.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Disassembler.cs new file mode 100644 index 0000000000..bb63cbe99e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Disassembler.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + /// + /// Disassembler + /// + public sealed partial class F3850 : IDisassemblable + { + + + #region IDisassemblable + + public string Cpu + { + get { return "F3850"; } + set { } + } + + public string PCRegisterName + { + get { return "PC"; } + } + + public IEnumerable AvailableCpus + { + get { yield return "F3850"; } + } + + public string Disassemble(MemoryDomain m, uint addr, out int length) + { + length = 0; + string ret = "";// Disassemble((ushort)addr, a => m.PeekByte(a), out length); + return ret; + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Execute.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Execute.cs new file mode 100644 index 0000000000..9bd5b37b3b --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Execute.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public sealed partial class F3850 + { + public const int MaxInstructionLength = 40; + + public long TotalExecutedCycles; + + public int instr_pntr = 0; + public ushort[] cur_instr = new ushort[MaxInstructionLength]; // fixed size - do not change at runtime + public ushort[] cur_romc = new ushort[MaxInstructionLength]; // fixed size - do not change at runtime + public byte opcode; + public byte databus; + public ushort iobus; + + public void FetchInstruction() + { + switch (opcode) + { + case 0x00: LR_A_KU(); break; // LR A, (KU) + case 0x01: LR_A_KL(); break; // LR A, (KL) + case 0x02: LR_A_QU(); break; // LR A, (QU) + case 0x03: LR_A_QL(); break; // LR A, (QL) + case 0x04: LR_KU_A(); break; // LR KU, (A) + case 0x05: LR_KL_A(); break; // LR KL, (A) + case 0x06: LR_QU_A(); break; // LR QU, (A) + case 0x07: LR_QL_A(); break; // LR QL, (A) + case 0x08: LR_K_P(); break; // LR K, (P) + case 0x09: LR_P_K(); break; // LR P, (K) + case 0x0A: LR_A_IS(); break; // LR A, (ISAR) + case 0x0B: LR_IS_A(); break; // LR ISAR, (A) + case 0x0C: LR_PK(); break; // LR PC1, (PC0); LR PC0l <- (r13); LR PC0h, (r12) + case 0x0D: LR_P0_Q(); break; // LR PC0l, (r15); LR PC0h <- (r14) + case 0x0E: LR_Q_DC(); break; // LR r14, (DC0h); r15 <- (DC0l) + case 0x0F: LR_DC_Q(); break; // LR DC0h, (r14); DC0l <- (r15) + case 0x10: LR_DC_Q(); break; // LR DC0h, (r10); DC0l <- (r11) + case 0x11: LR_H_DC(); break; // LR r10, (DC0h); r11 <- (DC0l) + case 0x12: SHIFT_R(1); break; // Shift (A) right one bit position (zero fill) + case 0x13: SHIFT_L(1); break; // Shift (A) left one bit position (zero fill) + case 0x14: SHIFT_R(4); break; // Shift (A) right four bit positions (zero fill) + case 0x15: SHIFT_L(4); break; // Shift (A) left four bit positions (zero fill) + case 0x16: LM(); break; // A <- ((DC0)) + case 0x17: ST(); break; // (DC) <- (A) + case 0x18: COM(); break; // A <- A ^ 255 + case 0x1A: DI(); break; // Clear ICB + case 0x1B: EI(); break; // Set ICB + case 0x1C: POP(); break; // PC0 <- PC1 + case 0x1D: LR_W_J(); break; // W <- (r9) + case 0x1E: LR_J_W(); break; // r9 <- (W) + case 0x1F: INC(); break; // A <- (A) + 1 + + default: ILLEGAL(); break; // Illegal Opcode + } + } + + + + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Operations.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Operations.cs new file mode 100644 index 0000000000..6888816acc --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Operations.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + public sealed partial class F3850 + { + private void IncrementBySignedByte(ushort dest, byte src) + { + if (src >= 0x80) + { + dest -= (ushort)(src & 0x80); + } + else + { + dest += (ushort)(src & 0x80); + } + } + + private void IncrementBySignedByte(byte dest, byte src) + { + if (src >= 0x80) + { + dest -= (byte)(src & 0x80); + } + else + { + dest += (byte)(src & 0x80); + } + } + + public void LoadReg_Func(ushort dest, ushort src) + { + if (dest == DB) + { + // byte storage + Regs[dest] = (ushort)(Regs[src] & 0xFF); + } + else if (dest == W) + { + // mask for status register + Regs[dest] = (ushort)(Regs[src] & 0x1F); + } + else + { + Regs[dest] = Regs[src]; + } + } + + public void ShiftRight_Func(ushort src, ushort index) + { + int shft = (Regs[src] >> index) & 0xFF; + FlagO = false; + FlagC = false; + FlagZ = shft == 0; + FlagS = (~shft & 0x80) != 0; + Regs[src] = (ushort)shft; + } + + public void ShiftLeft_Func(ushort src, ushort index) + { + int shft = (Regs[src] << index) & 0xFF; + FlagO = false; + FlagC = false; + FlagZ = shft == 0; + FlagS = (~shft & 0x80) != 0; + Regs[src] = (ushort)shft; + } + + public void COM_Func(ushort src) + { + byte b = (byte)Regs[src]; + var r = (byte)~b; + FlagO = false; + FlagC = false; + FlagZ = r == 0; + FlagS = (~r & 0x80) != 0; + Regs[src] = (ushort)r; + } + + public void ADD8_Func(ushort dest, ushort src) + { + int Reg16_d = Regs[dest]; + Reg16_d += Regs[src]; + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + FlagO = (Regs[dest].Bit(7) == Regs[src].Bit(7)) && (Regs[dest].Bit(7) != ans.Bit(7)); + FlagS = ans > 127; + + Regs[dest] = ans; + } + + public void INC8_Func(ushort src) + { + int Reg16_d = Regs[src]; + Reg16_d += 1; + + FlagC = Reg16_d.Bit(8); + FlagZ = (Reg16_d & 0xFF) == 0; + + ushort ans = (ushort)(Reg16_d & 0xFF); + + Regs[src] = ans; + + FlagS = Regs[src].Bit(7); + FlagO = Regs[src] == 0x80; + } + + public void Read_Func(ushort dest, ushort src) + { + Regs[dest] = Regs[src]; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Registers.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Registers.cs new file mode 100644 index 0000000000..5dffa31a5e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Registers.cs @@ -0,0 +1,231 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms.VisualStyles; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + /// + /// Internal Registers + /// + public sealed partial class F3850 + { + /// + /// Accumulator + /// + public ushort A = 0; + /// + /// Status Register + /// + public ushort W = 1; + /// + /// Indirect Scratchpad Address Register + /// (6bit) + /// + public ushort ISAR = 2; + /// + /// Primary Program Counter (low byte) + /// + public ushort PC0l = 3; + /// + /// Primary Program Counter (high byte) + /// + public ushort PC0h = 4; + /// + /// Backup Program Counter (low byte) + /// + public ushort PC1l = 5; + /// + /// Backup Program Counter (high byte) + /// + public ushort PC1h = 6; + /// + /// Data Counter (low byte) + /// + public ushort DC0l = 7; + /// + /// Data counter (high byte) + /// + public ushort DC0h = 8; + /// + /// Temporary Arithmetic Storage + /// + public ushort ALU = 9; + /// + /// Data Bus + /// + public ushort DB = 10; + /// + /// IO Bus + /// + public ushort IO = 11; + + /// + /// Registers (counters and scratchpad) + /// + public ushort[] Regs = new ushort[100]; + + // scratchpad registers + public ushort SR0 = 20; + public ushort SR1 = 21; + public ushort SR2 = 22; + public ushort SR3 = 23; + public ushort SR4 = 24; + public ushort SR5 = 25; + public ushort SR6 = 26; + public ushort SR7 = 27; + public ushort SR8 = 28; + public ushort SR9 = 29; + public ushort SR10 = 30; + public ushort SR11 = 31; + public ushort SR12 = 32; + public ushort SR13 = 33; + public ushort SR14 = 34; + public ushort SR15 = 35; + public ushort SR16 = 36; + public ushort SR17 = 37; + public ushort SR18 = 38; + public ushort SR19 = 39; + public ushort SR20 = 40; + public ushort SR21 = 41; + public ushort SR22 = 42; + public ushort SR23 = 43; + public ushort SR24 = 44; + public ushort SR25 = 45; + public ushort SR26 = 46; + public ushort SR27 = 47; + public ushort SR28 = 48; + public ushort SR29 = 49; + public ushort SR30 = 50; + public ushort SR31 = 51; + public ushort SR32 = 52; + public ushort SR33 = 53; + public ushort SR34 = 54; + public ushort SR35 = 55; + public ushort SR36 = 56; + public ushort SR37 = 57; + public ushort SR38 = 58; + public ushort SR39 = 59; + public ushort SR40 = 60; + public ushort SR41 = 61; + public ushort SR42 = 62; + public ushort SR43 = 63; + public ushort SR44 = 64; + public ushort SR45 = 65; + public ushort SR46 = 66; + public ushort SR47 = 67; + public ushort SR48 = 68; + public ushort SR49 = 69; + public ushort SR50 = 70; + public ushort SR51 = 71; + public ushort SR52 = 72; + public ushort SR53 = 73; + public ushort SR54 = 74; + public ushort SR55 = 75; + public ushort SR56 = 76; + public ushort SR57 = 77; + public ushort SR58 = 78; + public ushort SR59 = 79; + public ushort SR60 = 80; + public ushort SR61 = 81; + public ushort SR62 = 82; + public ushort SR63 = 83; + public ushort SR64 = 84; + + /// + /// Status Register - Sign Flag + /// + public bool FlagS + { + get { return (Regs[W] & 0x01) != 0; } + set { Regs[W] = (ushort)((Regs[W] & ~0x01) | (value ? 0x01 : 0x00)); } + } + + /// + /// Status Register - Carry Flag + /// + public bool FlagC + { + get { return (Regs[W] & 0x02) != 0; } + set { Regs[W] = (ushort)((Regs[W] & ~0x02) | (value ? 0x02 : 0x00)); } + } + + /// + /// Status Register - Zero Flag + /// + public bool FlagZ + { + get { return (Regs[W] & 0x04) != 0; } + set { Regs[W] = (ushort)((Regs[W] & ~0x04) | (value ? 0x04 : 0x00)); } + } + + /// + /// Status Register - Overflow Flag + /// + public bool FlagO + { + get { return (Regs[W] & 0x08) != 0; } + set { Regs[W] = (ushort)((Regs[W] & ~0x08) | (value ? 0x08 : 0x00)); } + } + + /// + /// Status Register - Interrupt Master Enable Flag + /// + public bool FlagICB + { + get { return (Regs[W] & 0x10) != 0; } + set { Regs[W] = (ushort)((Regs[W] & ~0x10) | (value ? 0x10 : 0x00)); } + } + + /// + /// Access to the full 16-bit Primary Program Counter + /// + public ushort RegPC0 + { + get { return (ushort)(Regs[PC0l] | (Regs[PC0h] << 8)); } + set + { + Regs[PC0l] = (ushort)(value & 0xFF); + Regs[PC0h] = (ushort)((value >> 8) & 0xFF); + } + } + + /// + /// Access to the full 16-bit Backup Program Counter + /// + public ushort RegPC1 + { + get { return (ushort)(Regs[PC1l] | (Regs[PC1h] << 8)); } + set + { + Regs[PC1l] = (ushort)(value & 0xFF); + Regs[PC1h] = (ushort)((value >> 8) & 0xFF); + } + } + + /// + /// Access to the full 16-bit Data Counter + /// + public ushort RegDC0 + { + get { return (ushort)(Regs[DC0l] | (Regs[DC0h] << 8)); } + set + { + Regs[DC0l] = (ushort)(value & 0xFF); + Regs[DC0h] = (ushort)((value >> 8) & 0xFF); + } + } + + private void ResetRegisters() + { + for (var i = 0; i < Regs.Length; i++) + { + Regs[i] = 0; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Tables.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Tables.cs new file mode 100644 index 0000000000..d6a1f5fbf0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.Tables.cs @@ -0,0 +1,420 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + /// + /// Vectors of Instruction Operations + /// + public sealed partial class F3850 + { + private void LR_A_KU() + { + PopulateCURINSTR( + OP_LR_8, A, SR12, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_A_KL() + { + PopulateCURINSTR( + OP_LR_8, A, SR13, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_A_QU() + { + PopulateCURINSTR( + OP_LR_8, A, SR14, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_A_QL() + { + PopulateCURINSTR( + OP_LR_8, A, SR15, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_KU_A() + { + PopulateCURINSTR( + OP_LR_8, SR12, A, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_KL_A() + { + PopulateCURINSTR( + OP_LR_8, SR13, A, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_QU_A() + { + PopulateCURINSTR( + OP_LR_8, SR14, A, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_QL_A() + { + PopulateCURINSTR( + OP_LR_8, SR15, A, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_K_P() + { + PopulateCURINSTR( + ROMC_07, // L + IDLE, + IDLE, + OP_LR_8, SR12, DB, + IDLE, + IDLE, + ROMC_0B, // L + IDLE, + IDLE, + OP_LR_8, SR13, DB, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_P_K() + { + PopulateCURINSTR( + OP_LR_8, DB, SR12, // L + IDLE, + IDLE, + ROMC_15, + IDLE, + IDLE, + OP_LR_8, DB, SR13, // L + IDLE, + IDLE, + ROMC_18, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_A_IS() + { + PopulateCURINSTR( + OP_LR_8, A, ISAR, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_IS_A() + { + PopulateCURINSTR( + OP_LR_8, ISAR, A, // S + ROMC_00_S, + IDLE, + END); + } + + private void LR_PK() + { + PopulateCURINSTR( + OP_LR_8, DB, SR13, // L + IDLE, + IDLE, + ROMC_12, + IDLE, + IDLE, + OP_LR_8, DB, SR12, // L + IDLE, + IDLE, + ROMC_14, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_P0_Q() + { + PopulateCURINSTR( + OP_LR_8, DB, SR15, // L + IDLE, + IDLE, + ROMC_17, + IDLE, + IDLE, + OP_LR_8, DB, SR14, // L + IDLE, + IDLE, + ROMC_14, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_Q_DC() + { + PopulateCURINSTR( + ROMC_06, // L + IDLE, + IDLE, + OP_LR_8, SR14, DB, + IDLE, + IDLE, + ROMC_09, // L + IDLE, + IDLE, + OP_LR_8, SR15, DB, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_DC_Q() + { + PopulateCURINSTR( + OP_LR_8, DB, SR14, // L + IDLE, + IDLE, + ROMC_16, + IDLE, + IDLE, + OP_LR_8, DB, SR15, // L + IDLE, + IDLE, + ROMC_19, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_DC_H() + { + PopulateCURINSTR( + OP_LR_8, DB, SR10, // L + IDLE, + IDLE, + ROMC_16, + IDLE, + IDLE, + OP_LR_8, DB, SR11, // L + IDLE, + IDLE, + ROMC_19, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_H_DC() + { + PopulateCURINSTR( + ROMC_06, // L + IDLE, + IDLE, + OP_LR_8, SR10, DB, + IDLE, + IDLE, + ROMC_09, // L + IDLE, + IDLE, + OP_LR_8, SR11, DB, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void SHIFT_R(ushort index) + { + PopulateCURINSTR( + OP_SHFT_R, A, index, // S + ROMC_00_S, + IDLE, + END); + } + + private void SHIFT_L(ushort index) + { + PopulateCURINSTR( + OP_SHFT_L, A, index, // S + ROMC_00_S, + IDLE, + END); + } + + private void LM() + { + PopulateCURINSTR( + ROMC_02, // L + IDLE, + IDLE, + OP_LR_8, DB, A, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void ST() + { + PopulateCURINSTR( + OP_LR_8, DB, A, // L + IDLE, + IDLE, + ROMC_05, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void COM() + { + PopulateCURINSTR( + OP_COM, // S + ROMC_00_S, + IDLE, + END); + } + + private void LNK() + { + PopulateCURINSTR( + OP_LNK, // S + ROMC_00_S, + IDLE, + END); + } + + private void DI() + { + PopulateCURINSTR( + ROMC_1C_S, // S + OP_DI, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void EI() + { + PopulateCURINSTR( + ROMC_1C_S, // S + OP_EI, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void POP() + { + PopulateCURINSTR( + ROMC_04, // S + IDLE, + IDLE, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_W_J() + { + PopulateCURINSTR( + ROMC_1C_S, // S + IDLE, + OP_LR_8, W, SR9, + IDLE, + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + + private void LR_J_W() + { + PopulateCURINSTR( + OP_LR_8, SR9, W, // S + ROMC_00_S, + IDLE, + END); + } + + private void INC() + { + PopulateCURINSTR( + OP_INC8, A, // S + ROMC_00_S, + IDLE, + END); + } + + + private void ILLEGAL() + { + PopulateCURINSTR( + ROMC_00_S, // S + IDLE, + IDLE, + END); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.cs b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.cs new file mode 100644 index 0000000000..217463a018 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Fairchild/ChannelF/F8/F3850.cs @@ -0,0 +1,480 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.ChannelF +{ + /// + /// Fairchild F3850 (F8) CPU (Channel F-specific implementation) + /// + /// The F8 microprocessor is made up of separate interchangeable devices + /// The Channel F has: + /// * x1 F3850 CPU (central processing unit) + /// * x2 F3851 PSU (program storage unit) + /// The CPU does not have its own data counters or program counters, rather each F8 component connected to the CPU + /// holds their own PCs and SPs and are all connected to the ROMC (ROM control) pins that are serviced by the CPU. + /// Every device must respond to changes in the CPU ROMC pins output and they each update their PCs and DCs in the same way. + /// e.g. SPs and PCs should always be identical + /// Each device has a factory ROM mask applied and with every ROMC change observed is able to know whether it should respond (via the shared data bus) + /// or not based on the value within its counters. + /// + /// For this reason we will hold the PCs and SPs within the F3850 implementation. + /// + /// We are currently also *not* using a separate F3851 implementation. In reality the F3851 chip has/does: + /// * 1024 byte masked ROM + /// * x2 16-bit program counters + /// * x1 16-bit data counter + /// * Programmable timer + /// * Interrupt logic + /// + /// However, the Channel F does not use the timer or interrupt logic at all (as far as I can see) so we can hopefully just + /// maintain the PC and DC here in the CPU and move the ROMs into the core. + /// + public sealed partial class F3850 + { + // operations that can take place in an instruction + + //public const ushort OP = 1; + //public const ushort LR_8 = 2; + //public const ushort LR_16 = 3; + + + public const ushort ROMC_00_S = 40; + public const ushort ROMC_00_L = 41; + public const ushort ROMC_01 = 1; + public const ushort ROMC_02 = 2; + public const ushort ROMC_03_S = 3; + public const ushort ROMC_03_L = 33; + public const ushort ROMC_04 = 4; + public const ushort ROMC_05 = 5; + public const ushort ROMC_06 = 6; + public const ushort ROMC_07 = 7; + public const ushort ROMC_08 = 8; + public const ushort ROMC_09 = 9; + public const ushort ROMC_0A = 10; + public const ushort ROMC_0B = 11; + public const ushort ROMC_0C = 12; + public const ushort ROMC_0D = 13; + public const ushort ROMC_0E = 14; + public const ushort ROMC_0F = 15; + public const ushort ROMC_10 = 16; + public const ushort ROMC_11 = 17; + public const ushort ROMC_12 = 18; + public const ushort ROMC_13 = 19; + public const ushort ROMC_14 = 20; + public const ushort ROMC_15 = 21; + public const ushort ROMC_16 = 22; + public const ushort ROMC_17 = 23; + public const ushort ROMC_18 = 24; + public const ushort ROMC_19 = 25; + public const ushort ROMC_1A = 26; + public const ushort ROMC_1B = 27; + public const ushort ROMC_1C_S = 28; + public const ushort ROMC_1C_L = 34; + public const ushort ROMC_1D = 29; + public const ushort ROMC_1E = 30; + public const ushort ROMC_1F = 31; + + public const ushort IDLE = 0; + public const ushort END = 51; + + public const ushort OP_LR_8 = 100; + public const ushort OP_SHFT_R = 101; + public const ushort OP_SHFT_L = 102; + public const ushort OP_COM = 103; + public const ushort OP_LNK = 104; + public const ushort OP_DI = 105; + public const ushort OP_EI = 106; + public const ushort OP_INC8 = 107; + + + public F3850() + { + Reset(); + } + + public void Reset() + { + ResetRegisters(); + TotalExecutedCycles = 0; + instr_pntr = 0; + + PopulateCURINSTR( + ROMC_08, // S + IDLE, + IDLE, + IDLE, + IDLE, + IDLE, + ROMC_00_S, + IDLE, + IDLE, + IDLE, + END); + + FlagICB = false; + } + + public IMemoryCallbackSystem MemoryCallbacks { get; set; } + + // Memory Access + public Func ReadMemory; + public Action WriteMemory; + public Func PeekMemory; + public Func DummyReadMemory; + + // Hardware I/O Port Access + public Func ReadHardware; + public Action WriteHardware; + + public Action OnExecFetch; + + public void SetCallbacks + ( + Func ReadMemory, + Func DummyReadMemory, + Func PeekMemory, + Action WriteMemory, + Func ReadHardware, + Action WriteHardware + ) + { + this.ReadMemory = ReadMemory; + this.DummyReadMemory = DummyReadMemory; + this.PeekMemory = PeekMemory; + this.WriteMemory = WriteMemory; + this.ReadHardware = ReadHardware; + this.WriteHardware = WriteHardware; + } + + /// + /// Runs a single CPU clock cycle + /// + public void ExecuteOne() + { + switch (cur_instr[instr_pntr++]) + { + // always the last tick within an opcode instruction cycle + case END: + opcode = databus; + instr_pntr = 0; + FetchInstruction(); + break; + + // used as timing 'padding' + case IDLE: + break; + + // load one register into another (or databus) + case OP_LR_8: + LoadReg_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + + // Shift register n bit positions to the right (zero fill) + case OP_SHFT_R: + ShiftRight_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + + // Shift register n bit positions to the left (zero fill) + case OP_SHFT_L: + ShiftLeft_Func(cur_instr[instr_pntr++], cur_instr[instr_pntr++]); + break; + + // A <- A ^ 255 + case OP_COM: + COM_Func(A); + break; + + // A <- (A) + (C) + case OP_LNK: + ADD8_Func(Regs[A], (ushort)(FlagC ? 1 : 0)); + break; + + case OP_DI: + FlagICB = false; + break; + + case OP_EI: + FlagICB = true; + break; + + case OP_INC8: + INC8_Func(cur_instr[instr_pntr++]); + break; + + + + // instruction fetch + // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; + // then all devices increments the content of PC0. + // CYCLE LENGTH: S + case ROMC_00_S: + databus = ReadMemory(RegPC0++); + break; + + // instruction fetch + // The device whose address space includes the contents of the PC0 register must place on the data bus the op code addressed by PC0; + // then all devices increments the content of PC0. + // CYCLE LENGTH: L + case ROMC_00_L: + databus = ReadMemory(RegPC0++); + break; + + // The device whose address space includes the contents of the PC0 register must place on the data bus the contents of the memory location + // addressed by by PC0; then all devices add the 8-bit value on the data bus, as a signed binary number, to PC0 + // CYCLE LENGTH: L + case ROMC_01: + databus = ReadMemory(RegPC0); + IncrementBySignedByte(RegPC0, databus); + break; + + // The device whose DC0 address addresses a memory word within the address space of that device must place on the data bus the contents + // of the memory location addressed by DC0; then all devices increment DC0 + // CYCLE LENGTH: L + case ROMC_02: + databus = ReadMemory(RegDC0++); + break; + + // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches + // CYCLE LENGTH: S + case ROMC_03_S: + databus = ReadMemory(RegPC0++); + iobus = databus; + break; + + // Similar to 0x00, except that it is used for Immediate Operand fetches (using PC0) instead of instruction fetches + // CYCLE LENGTH: L + case ROMC_03_L: + databus = ReadMemory(RegPC0++); + iobus = databus; + break; + + // Copy the contents of PC1 into PC0 + // CYCLE LENGTH: S + case ROMC_04: + RegPC0 = RegPC1; + break; + + // Store the data bus contents into the memory location pointed to by DC0; increment DC0 + // CYCLE LENGTH: L + case ROMC_05: + WriteMemory(RegDC0++, databus); + break; + + // Place the high order byte of DC0 on the data bus + // CYCLE LENGTH: L + case ROMC_06: + databus = (byte)Regs[DC0h]; + break; + + // Place the high order byte of PC1 on the data bus + // CYCLE LENGTH: L + case ROMC_07: + databus = (byte)Regs[PC1h]; + break; + + // All devices copy the contents of PC0 into PC1. The CPU outputs zero on the data bus in this ROMC state. + // Load the data bus into both halves of PC0, this clearing the register. + // CYCLE LENGTH: L + case ROMC_08: + RegPC1 = RegPC0; + databus = 0; + Regs[PC0h] = 0; + Regs[PC0l] = 0; + break; + + // The device whose address space includes the contents of the DC0 register must place the low order byte of DC0 onto the data bus + // CYCLE LENGTH: L + case ROMC_09: + databus = (byte)Regs[DC0l]; + break; + + // All devices add the 8-bit value on the data bus, treated as a signed binary number, to the data counter + // CYCLE LENGTH: L + case ROMC_0A: + IncrementBySignedByte(RegDC0, databus); + break; + + // The device whose address space includes the value in PC1 must place the low order byte of PC1 on the data bus + // CYCLE LENGTH: L + case ROMC_0B: + databus = (byte)Regs[PC1l]; + break; + + // The device whose address space includes the contents of the PC0 register must place the contents of the memory word addressed by PC0 + // onto the data bus; then all devices move the value that has just been placed on the data bus into the low order byte of PC0 + // CYCLE LENGTH: L + case ROMC_0C: + databus = ReadMemory(RegPC0); + Regs[PC0l] = databus; + break; + + // All devices store in PC1 the current contents of PC0, incremented by 1; PC1 is unaltered + // CYCLE LENGTH: S + case ROMC_0D: + RegPC1 = (ushort)(RegPC0 + 1); + break; + + // The device whose address space includes the contents of PC0 must place the contents of the word addressed by PC0 onto the data bus. + // The value on the data bus is then moved to the low order byte of DC0 by all devices + // CYCLE LENGTH: L + case ROMC_0E: + databus = ReadMemory(RegPC0); + Regs[DC0l] = databus; + break; + + // The interrupting device with the highest priority must place the low order byte of the interrupt vector on the data bus. + // All devices must copy the contents of PC0 into PC1. All devices must move the contents of the data bus into the low order byte of PC0 + // CYCLE LENGTH: L + case ROMC_0F: + throw new NotImplementedException("ROMC 0x0F not implemented"); + break; + + // Inhibit any modification to the interrupt priority logic + // CYCLE LENGTH: L + case ROMC_10: + throw new NotImplementedException("ROMC 0x10 not implemented"); + break; + + // The device whose memory space includes the contents of PC0 must place the contents of the addressed memory word on the data bus. + // All devices must then move the contents of the data bus to the upper byte of DC0 + // CYCLE LENGTH: L + case ROMC_11: + databus = ReadMemory(RegPC0); + Regs[DC0h] = databus; + break; + + // All devices copy the contents of PC0 into PC1. All devices then move the contents of the data bus into the low order byte of PC0 + // CYCLE LENGTH: L + case ROMC_12: + RegPC1 = RegPC0; + Regs[PC0l] = databus; + break; + + // The interrupting device with the highest priority must move the high order half of the interrupt vector onto the data bus. + // All devices must move the conetnts of the data bus into the high order byte of of PC0. The interrupting device resets its + // interrupt circuitry (so that it is no longer requesting CPU servicing and can respond to another interrupt) + // CYCLE LENGTH: L + case ROMC_13: + throw new NotImplementedException("ROMC 0x13 not implemented"); + break; + + // All devices move the contents of the data bus into the high order byte of PC0 + // CYCLE LENGTH: L + case ROMC_14: + Regs[PC0h] = databus; + break; + + // All devices move the contents of the data bus into the high order byte of PC1 + // CYCLE LENGTH: L + case ROMC_15: + Regs[PC1h] = databus; + break; + + // All devices move the contents of the data bus into the high order byte of DC0 + // CYCLE LENGTH: L + case ROMC_16: + Regs[DC0h] = databus; + break; + + // All devices move the contents of the data bus into the low order byte of PC0 + // CYCLE LENGTH: L + case ROMC_17: + Regs[PC0l] = databus; + break; + + // All devices move the contents of the data bus into the low order byte of PC1 + // CYCLE LENGTH: L + case ROMC_18: + Regs[PC1l] = databus; + break; + + // All devices move the contents of the data bus into the low order byte of DC0 + // CYCLE LENGTH: L + case ROMC_19: + Regs[DC0l] = databus; + break; + + // During the prior cycle, an I/O port timer or interrupt control register was addressed; the device containing the addressed + // port must move the current contents of the data bus into the addressed port + // CYCLE LENGTH: L + case ROMC_1A: + WriteHardware(iobus, databus); + break; + + // During the prior cycle, the data bus specified the address of an I/O port. The device containing the addressed I/O port + // must place the contents of the I/O port on the data bus. (Note that the contents of the timer and interrupt control + // registers cannot be read back onto the data bus) + // CYCLE LENGTH: L + case ROMC_1B: + databus = ReadHardware(iobus); + break; + + // None + // CYCLE LENGTH: S + case ROMC_1C_S: + break; + + // None + // CYCLE LENGTH: L + case ROMC_1C_L: + break; + + // Devices with DC0 and DC1 registers must switch registers. Devices without a DC1 register perform no operation + // CYCLE LENGTH: S + case ROMC_1D: + // we have no DC1 in this implementation + break; + + // The device whose address space includes the contents of PC0 must place the low order byte of PC0 onto the data bus + // CYCLE LENGTH: L + case ROMC_1E: + databus = (byte)Regs[PC0l]; + break; + + // The device whose address space includes the contents of PC0 must place the high order byte of PC0 onto the data bus + // CYCLE LENGTH: L + case ROMC_1F: + databus = (byte)Regs[PC0h]; + break; + } + + TotalExecutedCycles++; + } + + + /// + /// Optimization method to set cur_instr + /// + private void PopulateCURINSTR(ushort d0 = 0, ushort d1 = 0, ushort d2 = 0, ushort d3 = 0, ushort d4 = 0, ushort d5 = 0, ushort d6 = 0, ushort d7 = 0, ushort d8 = 0, + ushort d9 = 0, ushort d10 = 0, ushort d11 = 0, ushort d12 = 0, ushort d13 = 0, ushort d14 = 0, ushort d15 = 0, ushort d16 = 0, ushort d17 = 0, ushort d18 = 0, + ushort d19 = 0, ushort d20 = 0, ushort d21 = 0, ushort d22 = 0, ushort d23 = 0, ushort d24 = 0, ushort d25 = 0, ushort d26 = 0, ushort d27 = 0, ushort d28 = 0, + ushort d29 = 0, ushort d30 = 0, ushort d31 = 0, ushort d32 = 0, ushort d33 = 0, ushort d34 = 0, ushort d35 = 0, ushort d36 = 0, ushort d37 = 0) + { + cur_instr[0] = d0; cur_instr[1] = d1; cur_instr[2] = d2; + cur_instr[3] = d3; cur_instr[4] = d4; cur_instr[5] = d5; + cur_instr[6] = d6; cur_instr[7] = d7; cur_instr[8] = d8; + cur_instr[9] = d9; cur_instr[10] = d10; cur_instr[11] = d11; + cur_instr[12] = d12; cur_instr[13] = d13; cur_instr[14] = d14; + cur_instr[15] = d15; cur_instr[16] = d16; cur_instr[17] = d17; + cur_instr[18] = d18; cur_instr[19] = d19; cur_instr[20] = d20; + cur_instr[21] = d21; cur_instr[22] = d22; cur_instr[23] = d23; + cur_instr[24] = d24; cur_instr[25] = d25; cur_instr[26] = d26; + cur_instr[27] = d27; cur_instr[28] = d28; cur_instr[29] = d29; + cur_instr[30] = d30; cur_instr[31] = d31; cur_instr[32] = d32; + cur_instr[33] = d33; cur_instr[34] = d34; cur_instr[35] = d35; + cur_instr[36] = d36; cur_instr[37] = d37; + } + + public void SyncState(Serializer ser) + { + ser.BeginSection(nameof(F3850)); + + ser.EndSection(); + } + } +}