From 79c4b1e846569a1ef05662d231e393a9d7713e16 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Sun, 8 Sep 2019 16:35:39 -0400 Subject: [PATCH] GBHawkLink3x: Initial Commits --- ...izHawkSystemIdToCoreSystemEnumConverter.cs | 3 + BizHawk.Client.Common/Api/CoreSystem.cs | 3 +- BizHawk.Client.Common/Global.cs | 4 + BizHawk.Client.Common/RomLoader.cs | 22 ++ BizHawk.Client.Common/SystemInfo.cs | 5 + BizHawk.Client.Common/config/PathEntry.cs | 14 + .../movie/bk2/Bk2MnemonicConstants.cs | 6 + .../DATTools/DATParserBase.cs | 4 +- BizHawk.Client.EmuHawk/MainForm.Designer.cs | 22 +- BizHawk.Client.EmuHawk/MainForm.Events.cs | 9 + BizHawk.Client.EmuHawk/MainForm.cs | 4 + .../MultiDiskBundler.Designer.cs | 1 + .../BizHawk.Emulation.Cores.csproj | 25 ++ .../GBHawkLink3x/GBHawkLink3x.ICodeDataLog.cs | 185 ++++++++++ .../GBHawkLink3x/GBHawkLink3x.IDebuggable.cs | 77 +++++ .../GBHawkLink3x/GBHawkLink3x.IEmulator.cs | 325 ++++++++++++++++++ .../GBHawkLink3x.IInputPollable.cs | 24 ++ .../GBHawkLink3x.IMemoryDomains.cs | 128 +++++++ .../GBHawkLink3x/GBHawkLink3x.ISaveRam.cs | 81 +++++ .../GBHawkLink3x/GBHawkLink3x.ISettable.cs | 176 ++++++++++ .../GBHawkLink3x/GBHawkLink3x.IStatable.cs | 66 ++++ .../Nintendo/GBHawkLink3x/GBHawkLink3x.cs | 105 ++++++ .../GBHawkLink3xControllerDeck.cs | 104 ++++++ .../GBHawkLink3x/GBHawkLink3xControllers.cs | 94 +++++ .../Consoles/Nintendo/GBHawkLink3x/ReadMe.txt | 1 + 25 files changed, 1485 insertions(+), 3 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ICodeDataLog.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IDebuggable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IInputPollable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IStatable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllerDeck.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllers.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/ReadMe.txt diff --git a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs index e05823994b..5b1b162fe5 100644 --- a/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs +++ b/BizHawk.Client.ApiHawk/Classes/BizHawkSystemIdToCoreSystemEnumConverter.cs @@ -111,6 +111,9 @@ namespace BizHawk.Client.ApiHawk case "ChannelF": return CoreSystem.ChannelF; + case "GB3x": + return CoreSystem.GB3x; + case "VB": case "NGP": case "DNGP": diff --git a/BizHawk.Client.Common/Api/CoreSystem.cs b/BizHawk.Client.Common/Api/CoreSystem.cs index 05b63c7f96..6c8c6a3805 100644 --- a/BizHawk.Client.Common/Api/CoreSystem.cs +++ b/BizHawk.Client.Common/Api/CoreSystem.cs @@ -34,6 +34,7 @@ ZXSpectrum, AmstradCPC, GGL, - ChannelF + ChannelF, + GB3x } } diff --git a/BizHawk.Client.Common/Global.cs b/BizHawk.Client.Common/Global.cs index c8ef120a66..367feb4083 100644 --- a/BizHawk.Client.Common/Global.cs +++ b/BizHawk.Client.Common/Global.cs @@ -136,6 +136,8 @@ namespace BizHawk.Client.Common return SystemInfo.Saturn; case "DGB": return SystemInfo.DualGB; + case "GB3x": + return SystemInfo.GB3x; case "WSWAN": return SystemInfo.WonderSwan; case "Lynx": @@ -148,6 +150,8 @@ namespace BizHawk.Client.Common return SystemInfo.Libretro; case "VB": return SystemInfo.VirtualBoy; + case "VEC": + return SystemInfo.Vectrex; case "NGP": return SystemInfo.NeoGeoPocket; case "ZXSpectrum": diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 378f07f9ea..fbce59cd72 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -15,6 +15,7 @@ using BizHawk.Emulation.Cores.Consoles.Sega.gpgx; using BizHawk.Emulation.Cores.Nintendo.Gameboy; using BizHawk.Emulation.Cores.Nintendo.GBHawk; using BizHawk.Emulation.Cores.Nintendo.GBHawkLink; +using BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x; using BizHawk.Emulation.Cores.Nintendo.SNES; using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.GGHawkLink; @@ -659,6 +660,27 @@ namespace BizHawk.Client.Common } // other stuff todo + break; + case "GB3x": + var leftBytes3x = xmlGame.Assets.First().Value; + var centerBytes3x = xmlGame.Assets.Skip(1).First().Value; + var rightBytes3x = xmlGame.Assets.Skip(2).First().Value; + + var left3x = Database.GetGameInfo(leftBytes3x, "left.gb"); + var center3x = Database.GetGameInfo(centerBytes3x, "center.gb"); + var right3x = Database.GetGameInfo(rightBytes3x, "right.gb"); + + nextEmulator = new GBHawkLink3x( + nextComm, + left3x, + leftBytes3x, + center3x, + centerBytes3x, + right3x, + rightBytes3x, + GetCoreSettings(), + GetCoreSyncSettings()); + break; case "AppleII": var assets = xmlGame.Assets.Select(a => Database.GetGameInfo(a.Value, a.Key)); diff --git a/BizHawk.Client.Common/SystemInfo.cs b/BizHawk.Client.Common/SystemInfo.cs index 6b631d0851..97b1460aeb 100644 --- a/BizHawk.Client.Common/SystemInfo.cs +++ b/BizHawk.Client.Common/SystemInfo.cs @@ -73,6 +73,11 @@ namespace BizHawk.Client.Common /// public static SystemInfo Coleco { get; } = new SystemInfo("ColecoVision", CoreSystem.ColecoVision, 1); + /// + /// Gets the instance for Dual Gameboy + /// + public static SystemInfo GB3x { get; } = new SystemInfo("Game Boy Link 3x", CoreSystem.GB3x, 3, StandardButtons); + /// /// Gets the instance for Dual Gameboy /// diff --git a/BizHawk.Client.Common/config/PathEntry.cs b/BizHawk.Client.Common/config/PathEntry.cs index 7df0046165..0590a8ee7d 100644 --- a/BizHawk.Client.Common/config/PathEntry.cs +++ b/BizHawk.Client.Common/config/PathEntry.cs @@ -388,6 +388,20 @@ namespace BizHawk.Client.Common 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 }, + + new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Base", Path = Path.Combine(".", "GB3x"), Ordinal = 0 }, + new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "ROM", Path = ".", Ordinal = 1 }, + new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, + new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 }, + new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, + new PathEntry { System = "GB3x", SystemDisplayName = "GB3x", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, + + new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Base", Path = Path.Combine(".", "VEC"), Ordinal = 0 }, + new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "ROM", Path = ".", Ordinal = 1 }, + new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Savestates", Path = Path.Combine(".", "State"), Ordinal = 2 }, + new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Save RAM", Path = Path.Combine(".", "SaveRAM"), Ordinal = 3 }, + new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Screenshots", Path = Path.Combine(".", "Screenshots"), Ordinal = 4 }, + new PathEntry { System = "VEC", SystemDisplayName = "VEC", Type = "Cheats", Path = Path.Combine(".", "Cheats"), Ordinal = 5 }, }; } } diff --git a/BizHawk.Client.Common/movie/bk2/Bk2MnemonicConstants.cs b/BizHawk.Client.Common/movie/bk2/Bk2MnemonicConstants.cs index 308b08a1c5..7e0931986d 100644 --- a/BizHawk.Client.Common/movie/bk2/Bk2MnemonicConstants.cs +++ b/BizHawk.Client.Common/movie/bk2/Bk2MnemonicConstants.cs @@ -338,6 +338,12 @@ namespace BizHawk.Client.Common { ["Toggle Cable"] = 'L' }, + ["GB3x"] = new Dictionary + { + ["Toggle Cable LC"] = 'L', + ["Toggle Cable CR"] = 'C', + ["Toggle Cable RL"] = 'R' + }, ["Lynx"] = new Dictionary { ["Option 1"] = '1', diff --git a/BizHawk.Client.DBMan/DATTools/DATParserBase.cs b/BizHawk.Client.DBMan/DATTools/DATParserBase.cs index a65f72afbc..06572572bf 100644 --- a/BizHawk.Client.DBMan/DATTools/DATParserBase.cs +++ b/BizHawk.Client.DBMan/DATTools/DATParserBase.cs @@ -215,6 +215,8 @@ namespace BizHawk.Client.DBMan VB, UZE, NGP, - ChannelF + ChannelF, + VEC, + GB3x } } diff --git a/BizHawk.Client.EmuHawk/MainForm.Designer.cs b/BizHawk.Client.EmuHawk/MainForm.Designer.cs index 2f3d849436..175e6a6578 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Designer.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Designer.cs @@ -355,6 +355,8 @@ this.DGBsettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.DGBHawkSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.DGBHawksettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.GB3xSubMenu = new System.Windows.Forms.ToolStripMenuItem(); + this.GB3xsettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.GGLSubMenu = new System.Windows.Forms.ToolStripMenuItem(); this.GGLsettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.VectrexSubMenu = new System.Windows.Forms.ToolStripMenuItem(); @@ -512,7 +514,8 @@ this.SaturnSubMenu, this.DGBSubMenu, this.DGBHawkSubMenu, - this.GGLSubMenu, + this.GB3xSubMenu, + this.GGLSubMenu, this.GenesisSubMenu, this.wonderSwanToolStripMenuItem, this.AppleSubMenu, @@ -3218,6 +3221,21 @@ this.DGBHawksettingsToolStripMenuItem.Text = "Settings..."; this.DGBHawksettingsToolStripMenuItem.Click += new System.EventHandler(this.DgbHawkSettingsMenuItem_Click); // + // GB3xSubMenu + // + this.GB3xSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.GB3xsettingsToolStripMenuItem}); + this.GB3xSubMenu.Name = "GB3xSubMenu"; + this.GB3xSubMenu.Size = new System.Drawing.Size(59, 19); + this.GB3xSubMenu.Text = "&GB Link 3x"; + // + // GB3xsettingsToolStripMenuItem + // + this.GB3xsettingsToolStripMenuItem.Name = "GB3xsettingsToolStripMenuItem"; + this.GB3xsettingsToolStripMenuItem.Size = new System.Drawing.Size(125, 22); + this.GB3xsettingsToolStripMenuItem.Text = "Settings..."; + this.GB3xsettingsToolStripMenuItem.Click += new System.EventHandler(this.GB3xSettingsMenuItem_Click); + // // GGLSubMenu // this.GGLSubMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -4577,6 +4595,8 @@ private System.Windows.Forms.ToolStripMenuItem DGBsettingsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem DGBHawkSubMenu; private System.Windows.Forms.ToolStripMenuItem DGBHawksettingsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem GB3xSubMenu; + private System.Windows.Forms.ToolStripMenuItem GB3xsettingsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem GGLSubMenu; private System.Windows.Forms.ToolStripMenuItem GGLsettingsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem VectrexSubMenu; diff --git a/BizHawk.Client.EmuHawk/MainForm.Events.cs b/BizHawk.Client.EmuHawk/MainForm.Events.cs index 4480b7247a..fd36e4abfe 100644 --- a/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -2386,6 +2386,15 @@ namespace BizHawk.Client.EmuHawk #endregion + #region GB3x + + private void GB3xSettingsMenuItem_Click(object sender, EventArgs e) + { + GenericCoreConfig.DoDialog(this, "Gameboy Settings"); + } + + #endregion + #region GGL private void GGLSettingsMenuItem_Click(object sender, EventArgs e) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index d18bd4fde7..26ba88de83 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -1729,6 +1729,7 @@ namespace BizHawk.Client.EmuHawk SaturnSubMenu.Visible = false; DGBSubMenu.Visible = false; DGBHawkSubMenu.Visible = false; + GB3xSubMenu.Visible = false; GGLSubMenu.Visible = false; GenesisSubMenu.Visible = false; wonderSwanToolStripMenuItem.Visible = false; @@ -1860,6 +1861,9 @@ namespace BizHawk.Client.EmuHawk case "VEC": VectrexSubMenu.Visible = true; break; + case "GB3x": + GB3xSubMenu.Visible = true; + break; } } diff --git a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs index 8b3762e715..60407635a2 100644 --- a/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs +++ b/BizHawk.Client.EmuHawk/tools/MultiDiskBundler/MultiDiskBundler.Designer.cs @@ -141,6 +141,7 @@ "AppleII", "C64", "GB", + "GB3x", "PCFX", "PSX", "SAT", diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 7ff964b5fb..dd4ad43cd7 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -839,6 +839,31 @@ VBANext.cs + + + + GBHawkLink3x.cs + + + GBHawkLink3x.cs + + + GBHawkLink3x.cs + + + GBHawkLink3x.cs + + + GBHawkLink3x.cs + + + GBHawkLink3x.cs + + + GBHawkLink3x.cs + + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ICodeDataLog.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ICodeDataLog.cs new file mode 100644 index 0000000000..7064ccdecb --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ICodeDataLog.cs @@ -0,0 +1,185 @@ +using System; +using System.IO; + +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.LR35902; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x : ICodeDataLogger + { + private ICodeDataLog _cdl; + + public void SetCDL(ICodeDataLog cdl) + { + _cdl = cdl; + if (cdl == null) + this.L.cpu.CDLCallback = null; + else this.L.cpu.CDLCallback = CDLCpuCallback; + } + + public void NewCDL(ICodeDataLog cdl) + { + cdl["ROM"] = new byte[MemoryDomains["ROM L"].Size]; + cdl["HRAM"] = new byte[MemoryDomains["Zero Page RAM L"].Size]; + + cdl["WRAM"] = new byte[MemoryDomains["Main RAM L"].Size]; + + if (MemoryDomains.Has("Cart RAM L")) + { + cdl["CartRAM"] = new byte[MemoryDomains["Cart RAM L"].Size]; + } + + cdl.SubType = "GB"; + cdl.SubVer = 0; + } + + [FeatureNotImplemented] + void ICodeDataLogger.DisassembleCDL(Stream s, ICodeDataLog cdl) + { + } + + public void SetCDL(LR35902.eCDLogMemFlags flags, string type, int cdladdr) + { + if (type == null) return; + byte val = (byte)flags; + _cdl[type][cdladdr] |= (byte)flags; + } + + void CDLCpuCallback(ushort addr, LR35902.eCDLogMemFlags flags) + { + if (addr < 0x8000) + { + //don't record writes to the ROM, it's just noisy + //NOTE: in principle a mapper could mount a useful resource here, but I doubt it) + if ((flags & LR35902.eCDLogMemFlags.Write) != 0) return; + } + + if (L.ppu.DMA_start) + { + // some of gekkio's tests require these to be accessible during DMA + if (addr < 0x8000) + { + if (L.ppu.DMA_addr < 0x80) + { + return; + } + else + { + L.mapper.MapCDL(addr, flags); + return; + } + } + else if ((addr >= 0xE000) && (addr < 0xF000)) + { + SetCDL(flags, "WRAM", addr - 0xE000); + } + else if ((addr >= 0xF000) && (addr < 0xFE00)) + { + SetCDL(flags, "WRAM", (L.RAM_Bank * 0x1000) + (addr - 0xF000)); + } + else if ((addr >= 0xFE00) && (addr < 0xFEA0) && L.ppu.DMA_OAM_access) + { + return; + } + else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible + { + return; + } + else if ((addr >= 0xFF80)) + { + SetCDL(flags, "HRAM", addr - 0xFF80); + } + + } + + if (addr < 0x900) + { + if (addr < 0x100) + { + // return Either BIOS ROM or Game ROM + if ((L.GB_bios_register & 0x1) == 0) + { + return; + } + else + { + L.mapper.MapCDL(addr, flags); + return; + } + } + else if (addr >= 0x200) + { + // return Either BIOS ROM or Game ROM + if (((L.GB_bios_register & 0x1) == 0) && L.is_GBC) + { + return; + } + else + { + L.mapper.MapCDL(addr, flags); + return; + } + } + else + { + L.mapper.MapCDL(addr, flags); + return; + } + } + else if (addr < 0x8000) + { + L.mapper.MapCDL(addr, flags); + return; + } + else if (addr < 0xA000) + { + return; + } + else if (addr < 0xC000) + { + L.mapper.MapCDL(addr, flags); + return; + } + else if (addr < 0xD000) + { + return; + } + else if (addr < 0xE000) + { + SetCDL(flags, "WRAM", (L.RAM_Bank * 0x1000) + (addr - 0xD000)); + } + else if (addr < 0xF000) + { + SetCDL(flags, "WRAM", addr - 0xE000); + } + else if (addr < 0xFE00) + { + SetCDL(flags, "WRAM", (L.RAM_Bank * 0x1000) + (addr - 0xF000)); + } + else if (addr < 0xFEA0) + { + return; + } + else if (addr < 0xFF00) + { + return; + } + else if (addr < 0xFF80) + { + return; + } + else if (addr < 0xFFFF) + { + SetCDL(flags, "HRAM", addr - 0xFF80); + } + else + { + return; + } + + } + + + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IDebuggable.cs new file mode 100644 index 0000000000..ca2133a5ab --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IDebuggable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + return new Dictionary + { + /* + ["A"] = cpu.A, + ["X"] = cpu.X, + ["Y"] = cpu.Y, + ["S"] = cpu.S, + ["PC"] = cpu.PC, + ["Flag C"] = cpu.FlagC, + ["Flag Z"] = cpu.FlagZ, + ["Flag I"] = cpu.FlagI, + ["Flag D"] = cpu.FlagD, + ["Flag B"] = cpu.FlagB, + ["Flag V"] = cpu.FlagV, + ["Flag N"] = cpu.FlagN, + ["Flag T"] = cpu.FlagT + */ + }; + } + + public void SetCpuRegister(string register, int value) + { + switch (register) + { + default: + throw new InvalidOperationException(); + case "A": + //cpu.A = (byte)value; + break; + case "X": + //cpu.X = (byte)value; + break; + case "Y": + //cpu.Y = (byte)value; + break; + case "S": + //cpu.S = (byte)value; + break; + case "PC": + //cpu.PC = (ushort)value; + break; + case "Flag I": + //cpu.FlagI = value > 0; + break; + } + } + + public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(new[] { "System Bus" }); + + public bool CanStep(StepType type) + { + return false; + } + + [FeatureNotImplemented] + public void Step(StepType type) + { + throw new NotImplementedException(); + } + + public long TotalExecutedCycles + { + get { return (long)L.cpu.TotalExecutedCycles; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs new file mode 100644 index 0000000000..3a5763d3df --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IEmulator.cs @@ -0,0 +1,325 @@ +using System; + +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x : IEmulator, IVideoProvider, ISoundProvider + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; + + public bool FrameAdvance(IController controller, bool render, bool rendersound) + { + //Console.WriteLine("-----------------------FRAME-----------------------"); + //Update the color palette if a setting changed + if (Link3xSettings.Palette_L == GBHawk.GBHawk.GBSettings.PaletteType.BW) + { + L.color_palette[0] = color_palette_BW[0]; + L.color_palette[1] = color_palette_BW[1]; + L.color_palette[2] = color_palette_BW[2]; + L.color_palette[3] = color_palette_BW[3]; + } + else + { + L.color_palette[0] = color_palette_Gr[0]; + L.color_palette[1] = color_palette_Gr[1]; + L.color_palette[2] = color_palette_Gr[2]; + L.color_palette[3] = color_palette_Gr[3]; + } + + if (Link3xSettings.Palette_C == GBHawk.GBHawk.GBSettings.PaletteType.BW) + { + C.color_palette[0] = color_palette_BW[0]; + C.color_palette[1] = color_palette_BW[1]; + C.color_palette[2] = color_palette_BW[2]; + C.color_palette[3] = color_palette_BW[3]; + } + else + { + C.color_palette[0] = color_palette_Gr[0]; + C.color_palette[1] = color_palette_Gr[1]; + C.color_palette[2] = color_palette_Gr[2]; + C.color_palette[3] = color_palette_Gr[3]; + } + + if (Link3xSettings.Palette_R == GBHawk.GBHawk.GBSettings.PaletteType.BW) + { + R.color_palette[0] = color_palette_BW[0]; + R.color_palette[1] = color_palette_BW[1]; + R.color_palette[2] = color_palette_BW[2]; + R.color_palette[3] = color_palette_BW[3]; + } + else + { + R.color_palette[0] = color_palette_Gr[0]; + R.color_palette[1] = color_palette_Gr[1]; + R.color_palette[2] = color_palette_Gr[2]; + R.color_palette[3] = color_palette_Gr[3]; + } + + if (_tracer.Enabled) + { + L.cpu.TraceCallback = s => _tracer.Put(s); + } + else + { + L.cpu.TraceCallback = null; + } + + _frame++; + + if (controller.IsPressed("Power")) + { + HardReset(); + } + + bool cablediscosignalNew = controller.IsPressed("Toggle Cable"); + if (cablediscosignalNew && !_cablediscosignal) + { + _cableconnected ^= true; + Console.WriteLine("Cable connect status to {0}", _cableconnected); + } + + _cablediscosignal = cablediscosignalNew; + + _islag = true; + + GetControllerState(controller); + + do_frame(); + + _islag = L._islag; + + if (_islag) + { + _lagcount++; + } + + return true; + } + + public void do_frame() + { + L.do_controller_check(); + C.do_controller_check(); + R.do_controller_check(); + + // advance one full frame + for (int i = 0; i < 70224; i++) + { + L.do_single_step(); + C.do_single_step(); + R.do_single_step(); + + // the signal to shift out a bit is when serial_clock = 1 + if (((L.serialport.serial_clock == 1) || (L.serialport.serial_clock == 2)) && !do_r_next) + { + if (_cableconnected) + { + L.serialport.send_external_bit((byte)(L.serialport.serial_data & 0x80)); + + if ((R.serialport.clk_rate == -1) && R.serialport.serial_start) + { + R.serialport.serial_clock = L.serialport.serial_clock; + R.serialport.send_external_bit((byte)(R.serialport.serial_data & 0x80)); + R.serialport.coming_in = L.serialport.going_out; + } + + L.serialport.coming_in = R.serialport.going_out; + } + } + else if ((R.serialport.serial_clock == 1) || (R.serialport.serial_clock == 2)) + { + do_r_next = false; + + if (_cableconnected) + { + R.serialport.send_external_bit((byte)(R.serialport.serial_data & 0x80)); + + if ((L.serialport.clk_rate == -1) && L.serialport.serial_start) + { + L.serialport.serial_clock = R.serialport.serial_clock; + L.serialport.send_external_bit((byte)(L.serialport.serial_data & 0x80)); + L.serialport.coming_in = R.serialport.going_out; + } + + R.serialport.coming_in = L.serialport.going_out; + } + + if (R.serialport.serial_clock == 2) { do_r_next = true; } + } + else + { + do_r_next = false; + } + + // if we hit a frame boundary, update video + if (L.vblank_rise) + { + buff_L = L.GetVideoBuffer(); + L.vblank_rise = false; + FillVideoBuffer(); + } + if (C.vblank_rise) + { + buff_C = C.GetVideoBuffer(); + C.vblank_rise = false; + FillVideoBuffer(); + } + if (R.vblank_rise) + { + buff_R = R.GetVideoBuffer(); + R.vblank_rise = false; + FillVideoBuffer(); + } + } + } + + public void GetControllerState(IController controller) + { + InputCallbacks.Call(); + L.controller_state = _controllerDeck.ReadPort1(controller); + C.controller_state = _controllerDeck.ReadPort2(controller); + R.controller_state = _controllerDeck.ReadPort3(controller); + } + + public int Frame => _frame; + + public string SystemId => "GB3x"; + + public bool DeterministicEmulation { get; set; } + + public void ResetCounters() + { + _frame = 0; + _lagcount = 0; + _islag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + L.Dispose(); + C.Dispose(); + R.Dispose(); + } + + #region Video provider + + public int _frameHz = 60; + + public int[] _vidbuffer = new int[160 * 2 * 144 * 2]; + public int[] buff_L = new int[160 * 144]; + public int[] buff_C = new int[160 * 144]; + public int[] buff_R = new int[160 * 144]; + + public int[] GetVideoBuffer() + { + return _vidbuffer; + } + + public void FillVideoBuffer() + { + // combine the 2 video buffers from the instances + for (int i = 0; i < 144; i++) + { + for (int j = 0; j < 160; j++) + { + _vidbuffer[i * 320 + j] = buff_L[i * 160 + j]; + _vidbuffer[(i + 144) * 320 + j + 80] = buff_C[i * 160 + j]; + _vidbuffer[i * 320 + j + 160] = buff_R[i * 160 + j]; + } + } + } + + public int VirtualWidth => 160 * 2; + public int VirtualHeight => 144 * 2; + public int BufferWidth => 160 * 2; + public int BufferHeight => 144 * 2; + public int BackgroundColor => unchecked((int)0xFF000000); + public int VsyncNumerator => _frameHz; + public int VsyncDenominator => 1; + + public static readonly uint[] color_palette_BW = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 }; + public static readonly uint[] color_palette_Gr = { 0xFFA4C505, 0xFF88A905, 0xFF1D551D, 0xFF052505 }; + + public uint[] color_palette = new uint[4]; + + #endregion + + #region audio + + public bool CanProvideAsync => false; + + public void SetSyncMode(SyncSoundMode mode) + { + if (mode != SyncSoundMode.Sync) + { + throw new InvalidOperationException("Only Sync mode is supported_"); + } + } + + public SyncSoundMode SyncMode => SyncSoundMode.Sync; + + public void GetSamplesSync(out short[] samples, out int nsamp) + { + short[] temp_samp_L; + short[] temp_samp_C; + short[] temp_samp_R; + + int nsamp_L; + int nsamp_C; + int nsamp_R; + + L.audio.GetSamplesSync(out temp_samp_L, out nsamp_L); + C.audio.GetSamplesSync(out temp_samp_C, out nsamp_C); + R.audio.GetSamplesSync(out temp_samp_R, out nsamp_R); + + if (Link3xSettings.AudioSet == GBLink3xSettings.AudioSrc.Left) + { + samples = temp_samp_L; + nsamp = nsamp_L; + } + else if (Link3xSettings.AudioSet == GBLink3xSettings.AudioSrc.Right) + { + samples = temp_samp_R; + nsamp = nsamp_R; + } + else + { + samples = new short[0]; + nsamp = 0; + } + } + + public void GetSamplesAsync(short[] samples) + { + throw new NotSupportedException("Async is not available"); + } + + public void DiscardSamples() + { + L.audio.DiscardSamples(); + C.audio.DiscardSamples(); + R.audio.DiscardSamples(); + } + + private void GetSamples(short[] samples) + { + + } + + public void DisposeSound() + { + L.audio.DisposeSound(); + C.audio.DisposeSound(); + R.audio.DisposeSound(); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IInputPollable.cs new file mode 100644 index 0000000000..cb537a8349 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IInputPollable.cs @@ -0,0 +1,24 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x : IInputPollable + { + public int LagCount + { + get { return _lagcount; } + set { _lagcount = value; } + } + + public bool IsLagFrame + { + get { return _islag; } + set { _islag = value; } + } + + public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem(); + + public bool _islag = true; + private int _lagcount; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs new file mode 100644 index 0000000000..33b36df759 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IMemoryDomains.cs @@ -0,0 +1,128 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x + { + private IMemoryDomains MemoryDomains; + + public void SetupMemoryDomains() + { + var domains = new List + { + new MemoryDomainDelegate( + "Main RAM L", + L.RAM.Length, + MemoryDomain.Endian.Little, + addr => L.RAM[addr], + (addr, value) => L.RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "Main RAM R", + R.RAM.Length, + MemoryDomain.Endian.Little, + addr => R.RAM[addr], + (addr, value) => R.RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "Zero Page RAM L", + L.ZP_RAM.Length, + MemoryDomain.Endian.Little, + addr => L.ZP_RAM[addr], + (addr, value) => L.ZP_RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "Zero Page RAM R", + R.ZP_RAM.Length, + MemoryDomain.Endian.Little, + addr => R.ZP_RAM[addr], + (addr, value) => R.ZP_RAM[addr] = value, + 1), + new MemoryDomainDelegate( + "System Bus L", + 0X10000, + MemoryDomain.Endian.Little, + addr => PeekSystemBusL(addr), + (addr, value) => PokeSystemBusL(addr, value), + 1), + new MemoryDomainDelegate( + "System Bus R", + 0X10000, + MemoryDomain.Endian.Little, + addr => PeekSystemBusR(addr), + (addr, value) => PokeSystemBusR(addr, value), + 1), + new MemoryDomainDelegate( + "ROM L", + L._rom.Length, + MemoryDomain.Endian.Little, + addr => L._rom[addr], + (addr, value) => L._rom[addr] = value, + 1), + new MemoryDomainDelegate( + "ROM R", + R._rom.Length, + MemoryDomain.Endian.Little, + addr => R._rom[addr], + (addr, value) => R._rom[addr] = value, + 1), + new MemoryDomainDelegate( + "VRAM L", + L.VRAM.Length, + MemoryDomain.Endian.Little, + addr => L.VRAM[addr], + (addr, value) => L.VRAM[addr] = value, + 1), + new MemoryDomainDelegate( + "VRAM R", + R.VRAM.Length, + MemoryDomain.Endian.Little, + addr => R.VRAM[addr], + (addr, value) => R.VRAM[addr] = value, + 1) + }; + + if (L.cart_RAM != null) + { + var CartRamL = new MemoryDomainByteArray("Cart RAM L", MemoryDomain.Endian.Little, L.cart_RAM, true, 1); + domains.Add(CartRamL); + } + + if (R.cart_RAM != null) + { + var CartRamR = new MemoryDomainByteArray("Cart RAM R", MemoryDomain.Endian.Little, R.cart_RAM, true, 1); + domains.Add(CartRamR); + } + + MemoryDomains = new MemoryDomainList(domains); + (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + } + + private byte PeekSystemBusL(long addr) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + return L.PeekMemory(addr2); + } + + private byte PeekSystemBusR(long addr) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + return R.PeekMemory(addr2); + } + + private void PokeSystemBusL(long addr, byte value) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + L.WriteMemory(addr2, value); + } + + private void PokeSystemBusR(long addr, byte value) + { + ushort addr2 = (ushort)(addr & 0xFFFF); + R.WriteMemory(addr2, value); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs new file mode 100644 index 0000000000..3b40a8dfb3 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISaveRam.cs @@ -0,0 +1,81 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x : ISaveRam + { + public byte[] CloneSaveRam() + { + if ((L.cart_RAM != null) || (R.cart_RAM != null)) + { + int Len1 = 0; + int Len2 = 0; + int index = 0; + + if (L.cart_RAM != null) + { + Len1 = L.cart_RAM.Length; + } + + if (R.cart_RAM != null) + { + Len2 = R.cart_RAM.Length; + } + + byte[] temp = new byte[Len1 + Len2]; + + if (L.cart_RAM != null) + { + for (int i = 0; i < L.cart_RAM.Length; i++) + { + temp[index] = L.cart_RAM[i]; + index++; + } + } + + if (R.cart_RAM != null) + { + for (int i = 0; i < L.cart_RAM.Length; i++) + { + temp[index] = R.cart_RAM[i]; + index++; + } + } + + return temp; + } + else + { + return null; + } + } + + public void StoreSaveRam(byte[] data) + { + if ((L.cart_RAM != null) && (R.cart_RAM == null)) + { + Buffer.BlockCopy(data, 0, L.cart_RAM, 0, L.cart_RAM.Length); + } + else if ((R.cart_RAM != null) && (L.cart_RAM == null)) + { + Buffer.BlockCopy(data, 0, R.cart_RAM, 0, R.cart_RAM.Length); + } + else if ((R.cart_RAM != null) && (L.cart_RAM != null)) + { + Buffer.BlockCopy(data, 0, L.cart_RAM, 0, L.cart_RAM.Length); + Buffer.BlockCopy(data, L.cart_RAM.Length, R.cart_RAM, 0, R.cart_RAM.Length); + } + + Console.WriteLine("loading SRAM here"); + } + + public bool SaveRamModified + { + get + { + return (L.has_bat || R.has_bat) & Link3xSyncSettings.Use_SRAM; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs new file mode 100644 index 0000000000..255341e138 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.ISettable.cs @@ -0,0 +1,176 @@ +using System; +using System.ComponentModel; + +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x : IEmulator, IStatable, ISettable + { + public GBLink3xSettings GetSettings() + { + return Link3xSettings.Clone(); + } + + public GBLink3xSyncSettings GetSyncSettings() + { + return Link3xSyncSettings.Clone(); + } + + public bool PutSettings(GBLink3xSettings o) + { + Link3xSettings = o; + return false; + } + + public bool PutSyncSettings(GBLink3xSyncSettings o) + { + bool ret = GBLink3xSyncSettings.NeedsReboot(Link3xSyncSettings, o); + Link3xSyncSettings = o; + return ret; + } + + private GBLink3xSettings Link3xSettings = new GBLink3xSettings(); + public GBLink3xSyncSettings Link3xSyncSettings = new GBLink3xSyncSettings(); + + public class GBLink3xSettings + { + [DisplayName("Color Mode")] + [Description("Pick Between Green scale and Grey scale colors")] + [DefaultValue(GBHawk.GBHawk.GBSettings.PaletteType.BW)] + public GBHawk.GBHawk.GBSettings.PaletteType Palette_L { get; set; } + + [DisplayName("Color Mode")] + [Description("Pick Between Green scale and Grey scale colors")] + [DefaultValue(GBHawk.GBHawk.GBSettings.PaletteType.BW)] + public GBHawk.GBHawk.GBSettings.PaletteType Palette_C { get; set; } + + [DisplayName("Color Mode")] + [Description("Pick Between Green scale and Grey scale colors")] + [DefaultValue(GBHawk.GBHawk.GBSettings.PaletteType.BW)] + public GBHawk.GBHawk.GBSettings.PaletteType Palette_R { get; set; } + + public enum AudioSrc + { + Left, + Right, + Both + } + + [DisplayName("Audio Selection")] + [Description("Choose Audio Source. Both will produce Stereo sound.")] + [DefaultValue(AudioSrc.Left)] + public AudioSrc AudioSet { get; set; } + + public GBLink3xSettings Clone() + { + return (GBLink3xSettings)MemberwiseClone(); + } + } + + public class GBLink3xSyncSettings + { + [DisplayName("Console Mode L")] + [Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")] + [DefaultValue(GBHawk.GBHawk.GBSyncSettings.ConsoleModeType.Auto)] + public GBHawk.GBHawk.GBSyncSettings.ConsoleModeType ConsoleMode_L { get; set; } + + [DisplayName("Console Mode C")] + [Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")] + [DefaultValue(GBHawk.GBHawk.GBSyncSettings.ConsoleModeType.Auto)] + public GBHawk.GBHawk.GBSyncSettings.ConsoleModeType ConsoleMode_C { get; set; } + + [DisplayName("Console Mode R")] + [Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")] + [DefaultValue(GBHawk.GBHawk.GBSyncSettings.ConsoleModeType.Auto)] + public GBHawk.GBHawk.GBSyncSettings.ConsoleModeType ConsoleMode_R { 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 L")] + [Description("Set the initial RTC time in terms of elapsed seconds.")] + [DefaultValue(0)] + public int RTCInitialTime_L + { + get { return _RTCInitialTime_L; } + set { _RTCInitialTime_L = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } + } + + [DisplayName("RTC Initial Time C")] + [Description("Set the initial RTC time in terms of elapsed seconds.")] + [DefaultValue(0)] + public int RTCInitialTime_C + { + get { return _RTCInitialTime_C; } + set { _RTCInitialTime_C = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } + } + + [DisplayName("RTC Initial Time R")] + [Description("Set the initial RTC time in terms of elapsed seconds.")] + [DefaultValue(0)] + public int RTCInitialTime_R + { + get { return _RTCInitialTime_R; } + set { _RTCInitialTime_R = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } + } + + [DisplayName("Timer Div Initial Time L")] + [Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] + [DefaultValue(8)] + public int DivInitialTime_L + { + get { return _DivInitialTime_L; } + set { _DivInitialTime_L = Math.Min((ushort)65535, (ushort)value); } + } + + [DisplayName("Timer Div Initial Time C")] + [Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] + [DefaultValue(8)] + public int DivInitialTime_C + { + get { return _DivInitialTime_C; } + set { _DivInitialTime_C = Math.Min((ushort)65535, (ushort)value); } + } + + [DisplayName("Timer Div Initial Time R")] + [Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")] + [DefaultValue(8)] + public int DivInitialTime_R + { + get { return _DivInitialTime_R; } + set { _DivInitialTime_R = Math.Min((ushort)65535, (ushort)value); } + } + + [DisplayName("Use Existing SaveRAM")] + [Description("When true, existing SaveRAM will be loaded at boot up")] + [DefaultValue(false)] + public bool Use_SRAM { get; set; } + + [JsonIgnore] + private int _RTCInitialTime_L; + private int _RTCInitialTime_C; + private int _RTCInitialTime_R; + [JsonIgnore] + public ushort _DivInitialTime_L = 8; + public ushort _DivInitialTime_C = 8; + public ushort _DivInitialTime_R = 8; + + public GBLink3xSyncSettings Clone() + { + return (GBLink3xSyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(GBLink3xSyncSettings x, GBLink3xSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IStatable.cs new file mode 100644 index 0000000000..ed391a92fd --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.IStatable.cs @@ -0,0 +1,66 @@ +using System.IO; +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public partial class GBHawkLink3x : IStatable + { + public bool BinarySaveStatesPreferred => true; + + public void SaveStateText(TextWriter writer) + { + L.SaveStateText(writer); + R.SaveStateText(writer); + SyncState(new Serializer(writer)); + } + + public void LoadStateText(TextReader reader) + { + L.LoadStateText(reader); + R.LoadStateText(reader); + SyncState(new Serializer(reader)); + } + + public void SaveStateBinary(BinaryWriter bw) + { + L.SaveStateBinary(bw); + R.SaveStateBinary(bw); + // other variables + SyncState(new Serializer(bw)); + } + + public void LoadStateBinary(BinaryReader br) + { + L.LoadStateBinary(br); + R.LoadStateBinary(br); + // other variables + SyncState(new Serializer(br)); + } + + public byte[] SaveStateBinary() + { + MemoryStream ms = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + return ms.ToArray(); + } + + //private JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented }; + + private void SyncState(Serializer ser) + { + ser.Sync("Lag", ref _lagcount); + ser.Sync("Frame", ref _frame); + ser.Sync("IsLag", ref _islag); + ser.Sync(nameof(_cableconnected), ref _cableconnected); + ser.Sync(nameof(_cablediscosignal), ref _cablediscosignal); + ser.Sync(nameof(do_r_next), ref do_r_next); + _controllerDeck.SyncState(ser); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs new file mode 100644 index 0000000000..4bb3722fe8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3x.cs @@ -0,0 +1,105 @@ +using System; + +using BizHawk.Emulation.Common; + +using BizHawk.Emulation.Cores.Nintendo.GBHawk; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + [Core( + "GBHawkLink3x", + "", + isPorted: false, + isReleased: true)] + [ServiceNotApplicable(typeof(IDriveLight))] + public partial class GBHawkLink3x : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, + ISettable + { + // we want to create two GBHawk instances that we will run concurrently + // maybe up to 4 eventually? + public GBHawk.GBHawk L; + public GBHawk.GBHawk C; + public GBHawk.GBHawk R; + + // if true, the link cable is currently connected + private bool _cableconnected = true; + + // if true, the link cable toggle signal is currently asserted + private bool _cablediscosignal = false; + + private bool do_r_next = false; + + //[CoreConstructor("GB", "GBC")] + public GBHawkLink3x(CoreComm comm, GameInfo game_L, byte[] rom_L, GameInfo game_C, byte[] rom_C, GameInfo game_R, byte[] rom_R, /*string gameDbFn,*/ object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + + Link3xSettings = (GBLink3xSettings)settings ?? new GBLink3xSettings(); + Link3xSyncSettings = (GBLink3xSyncSettings)syncSettings ?? new GBLink3xSyncSettings(); + _controllerDeck = new GBHawkLink3xControllerDeck(GBHawkLink3xControllerDeck.DefaultControllerName, GBHawkLink3xControllerDeck.DefaultControllerName, GBHawkLink3xControllerDeck.DefaultControllerName); + + CoreComm = comm; + + var temp_set_L = new GBHawk.GBHawk.GBSettings(); + var temp_set_C = new GBHawk.GBHawk.GBSettings(); + var temp_set_R = new GBHawk.GBHawk.GBSettings(); + + var temp_sync_L = new GBHawk.GBHawk.GBSyncSettings(); + var temp_sync_C = new GBHawk.GBHawk.GBSyncSettings(); + var temp_sync_R = new GBHawk.GBHawk.GBSyncSettings(); + + temp_sync_L.ConsoleMode = Link3xSyncSettings.ConsoleMode_L; + temp_sync_C.ConsoleMode = Link3xSyncSettings.ConsoleMode_C; + temp_sync_R.ConsoleMode = Link3xSyncSettings.ConsoleMode_R; + + temp_sync_L.DivInitialTime = Link3xSyncSettings.DivInitialTime_L; + temp_sync_C.DivInitialTime = Link3xSyncSettings.DivInitialTime_C; + temp_sync_R.DivInitialTime = Link3xSyncSettings.DivInitialTime_R; + temp_sync_L.RTCInitialTime = Link3xSyncSettings.RTCInitialTime_L; + temp_sync_C.RTCInitialTime = Link3xSyncSettings.RTCInitialTime_C; + temp_sync_R.RTCInitialTime = Link3xSyncSettings.RTCInitialTime_R; + + L = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, + game_L, rom_L, temp_set_L, temp_sync_L); + + C = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, + game_C, rom_C, temp_set_C, temp_sync_C); + + R = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, + game_R, rom_R, temp_set_R, temp_sync_R); + + ser.Register(this); + ser.Register(this); + + _tracer = new TraceBuffer { Header = L.cpu.TraceHeader }; + ser.Register(_tracer); + + ServiceProvider = ser; + + SetupMemoryDomains(); + + HardReset(); + } + + public void HardReset() + { + L.HardReset(); + C.HardReset(); + R.HardReset(); + } + + public DisplayType Region => DisplayType.NTSC; + + public int _frame = 0; + + private readonly GBHawkLink3xControllerDeck _controllerDeck; + + private readonly ITraceable _tracer; + + private void ExecFetch(ushort addr) + { + uint flags = (uint)(MemoryCallbackFlags.AccessExecute); + MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus"); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllerDeck.cs new file mode 100644 index 0000000000..ea26952ef1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllerDeck.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Common.ReflectionExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + public class GBHawkLink3xControllerDeck + { + public GBHawkLink3xControllerDeck(string controller1Name, string controller2Name, string controller3Name) + { + if (!ValidControllerTypes.ContainsKey(controller1Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller1Name); + } + + if (!ValidControllerTypes.ContainsKey(controller2Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller2Name); + } + + if (!ValidControllerTypes.ContainsKey(controller3Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller3Name); + } + + Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1); + Port2 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller2Name], 2); + Port3 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller3Name], 3); + + Definition = new ControllerDefinition + { + Name = Port1.Definition.Name, + BoolButtons = Port1.Definition.BoolButtons + .Concat(Port2.Definition.BoolButtons) + .Concat(Port3.Definition.BoolButtons) + .Concat(new[] { "Toggle Cable LC" } ) + .Concat(new[] { "Toggle Cable CR" } ) + .Concat(new[] { "Toggle Cable RL" } ) + .ToList() + }; + } + + public byte ReadPort1(IController c) + { + return Port1.Read(c); + } + + public byte ReadPort2(IController c) + { + return Port2.Read(c); + } + + public byte ReadPort3(IController c) + { + return Port3.Read(c); + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + ser.BeginSection(nameof(Port1)); + Port1.SyncState(ser); + ser.EndSection(); + + ser.BeginSection(nameof(Port2)); + Port2.SyncState(ser); + ser.EndSection(); + + ser.BeginSection(nameof(Port3)); + Port3.SyncState(ser); + ser.EndSection(); + } + + private readonly IPort Port1; + private readonly IPort Port2; + private readonly IPort Port3; + + private static Dictionary _controllerTypes; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(GBHawkLink3xControllerDeck).Assembly + .GetTypes() + .Where(t => typeof(IPort).IsAssignableFrom(t)) + .Where(t => !t.IsAbstract && !t.IsInterface) + .ToDictionary(tkey => tkey.DisplayName()); + } + + return _controllerTypes; + } + } + + public static string DefaultControllerName => typeof(StandardControls).DisplayName(); + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllers.cs new file mode 100644 index 0000000000..06500bfb75 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/GBHawkLink3xControllers.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x +{ + /// + /// Represents a GB add on + /// + public interface IPort + { + byte Read(IController c); + + ControllerDefinition Definition { get; } + + void SyncState(Serializer ser); + + int PortNum { get; } + } + + [DisplayName("Gameboy Controller")] + public class StandardControls : IPort + { + public StandardControls(int portNum) + { + PortNum = portNum; + Definition = new ControllerDefinition + { + Name = "Gameboy Controller H", + BoolButtons = BaseDefinition + .Select(b => "P" + PortNum + " " + b) + .ToList() + }; + } + + public int PortNum { get; } + + public ControllerDefinition Definition { get; } + + public byte Read(IController c) + { + byte result = 0xFF; + + if (c.IsPressed(Definition.BoolButtons[0])) + { + result -= 4; + } + if (c.IsPressed(Definition.BoolButtons[1])) + { + result -= 8; + } + if (c.IsPressed(Definition.BoolButtons[2])) + { + result -= 2; + } + if (c.IsPressed(Definition.BoolButtons[3])) + { + result -= 1; + } + if (c.IsPressed(Definition.BoolButtons[4])) + { + result -= 128; + } + if (c.IsPressed(Definition.BoolButtons[5])) + { + result -= 64; + } + if (c.IsPressed(Definition.BoolButtons[6])) + { + result -= 32; + } + if (c.IsPressed(Definition.BoolButtons[7])) + { + result -= 16; + } + + return result; + } + + private static readonly string[] BaseDefinition = + { + "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power" + }; + + public void SyncState(Serializer ser) + { + //nothing + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/ReadMe.txt new file mode 100644 index 0000000000..bc60bf4b01 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink3x/ReadMe.txt @@ -0,0 +1 @@ +TODO: