From f47c2d15588be661fde2a2b66dd0edbd47027364 Mon Sep 17 00:00:00 2001 From: alyosha-tas Date: Thu, 3 Jan 2019 12:10:53 -0600 Subject: [PATCH] GBHawk: Start link support --- BizHawk.Client.Common/RomLoader.cs | 19 +- .../BizHawk.Emulation.Cores.csproj | 25 +++ .../Nintendo/GBHawk/GBHawk.IEmulator.cs | 2 +- .../Nintendo/GBHawk/GBHawk.IStatable.cs | 4 +- .../Consoles/Nintendo/GBHawk/GBHawk.cs | 3 +- .../GBHawkLink/GBHawkLink.ICodeDataLog.cs | 186 ++++++++++++++++++ .../GBHawkLink/GBHawkLink.IDebuggable.cs | 77 ++++++++ .../GBHawkLink/GBHawkLink.IEmulator.cs | 128 ++++++++++++ .../GBHawkLink/GBHawkLink.IInputPollable.cs | 24 +++ .../GBHawkLink/GBHawkLink.IMemoryDomains.cs | 129 ++++++++++++ .../GBHawkLink/GBHawkLink.ISaveRam.cs | 34 ++++ .../GBHawkLink/GBHawkLink.ISettable.cs | 91 +++++++++ .../GBHawkLink/GBHawkLink.IStatable.cs | 63 ++++++ .../Nintendo/GBHawkLink/GBHawkLink.cs | 88 +++++++++ .../GBHawkLink/GBHawkLinkControllerDeck.cs | 84 ++++++++ .../GBHawkLink/GBHawkLinkControllers.cs | 94 +++++++++ .../Consoles/Nintendo/GBHawkLink/ReadMe.txt | 1 + 17 files changed, 1044 insertions(+), 8 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ICodeDataLog.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IDebuggable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IEmulator.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IInputPollable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IMemoryDomains.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IStatable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLinkControllerDeck.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLinkControllers.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/ReadMe.txt diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index ef007eeac6..3a4b1d69a6 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -14,6 +14,7 @@ using BizHawk.Emulation.Cores.Computers.Commodore64; 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.SNES; using BizHawk.Emulation.Cores.PCEngine; using BizHawk.Emulation.Cores.Sega.Saturn; @@ -630,7 +631,20 @@ namespace BizHawk.Client.Common var left = Database.GetGameInfo(leftBytes, "left.gb"); var right = Database.GetGameInfo(rightBytes, "right.gb"); - nextEmulator = new GambatteLink( + if (Global.Config.GB_UseGBHawk) + { + nextEmulator = new GBHawkLink( + nextComm, + left, + leftBytes, + right, + rightBytes, + GetCoreSettings(), + GetCoreSyncSettings()); + } + else + { + nextEmulator = new GambatteLink( nextComm, left, leftBytes, @@ -639,7 +653,8 @@ namespace BizHawk.Client.Common GetCoreSettings(), GetCoreSyncSettings(), Deterministic); - + } + // other stuff todo break; case "AppleII": diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index f57d61d6eb..744a7d8c27 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -789,6 +789,31 @@ VBANext.cs + + + + GBHawkLink.cs + + + GBHawkLink.cs + + + GBHawkLink.cs + + + GBHawkLink.cs + + + GBHawkLink.cs + + + GBHawkLink.cs + + + GBHawkLink.cs + + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs index be2812ef86..01f04f8338 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IEmulator.cs @@ -159,7 +159,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk } ticker++; - //if (ticker > 10000000) { vblank_rise = true; }//throw new Exception("ERROR: Unable to Resolve Frame"); } + // if (ticker > 10000000) { vblank_rise = true; }//throw new Exception("ERROR: Unable to Resolve Frame"); } in_vblank_old = in_vblank; REG_FF0F_OLD = REG_FF0F; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs index 161f06c92d..5091c6ceb8 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.IStatable.cs @@ -107,9 +107,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk ser.Sync("cart_RAM", ref cart_RAM, false); } - - - ser.EndSection(); + ser.EndSection(); } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs index d7c8edcf4f..00e89a039e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawk/GBHawk.cs @@ -3,7 +3,6 @@ using BizHawk.Common.BufferExtensions; using BizHawk.Emulation.Common; using BizHawk.Emulation.Common.Components.LR35902; -using BizHawk.Common.NumberExtensions; using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy; using System.Runtime.InteropServices; @@ -275,7 +274,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk private readonly GBHawkControllerDeck _controllerDeck; - private void HardReset() + public void HardReset() { GB_bios_register = 0; // bios enable GBC_compat = is_GBC; diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ICodeDataLog.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ICodeDataLog.cs new file mode 100644 index 0000000000..cfc841d321 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ICodeDataLog.cs @@ -0,0 +1,186 @@ +using System; +using System.IO; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.LR35902; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + public partial class GBHawkLink : 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/GBHawkLink/GBHawkLink.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IDebuggable.cs new file mode 100644 index 0000000000..e9b191d02a --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IDebuggable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + public partial class GBHawkLink : 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/GBHawkLink/GBHawkLink.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IEmulator.cs new file mode 100644 index 0000000000..790ab1fa06 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IEmulator.cs @@ -0,0 +1,128 @@ +using BizHawk.Common.NumberExtensions; +using BizHawk.Emulation.Common; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + public partial class GBHawkLink : IEmulator, IVideoProvider + { + public IEmulatorServiceProvider ServiceProvider { get; } + + public ControllerDefinition ControllerDefinition => _controllerDeck.Definition; + + public byte controller_state; + public bool in_vblank_old; + public bool in_vblank; + public bool vblank_rise; + + public void FrameAdvance(IController controller, bool render, bool rendersound) + { + //Console.WriteLine("-----------------------FRAME-----------------------"); + if (_tracer.Enabled) + { + L.cpu.TraceCallback = s => _tracer.Put(s); + } + else + { + L.cpu.TraceCallback = null; + } + + _frame++; + + if (controller.IsPressed("Power")) + { + HardReset(); + } + + _islag = true; + + GetControllerState(controller); + + do_frame(); + + _islag = L._islag; + + if (_islag) + { + _lagcount++; + } + } + + public void do_frame() + { + L.do_frame(); + R.do_frame(); + } + + public void GetControllerState(IController controller) + { + InputCallbacks.Call(); + L.controller_state = _controllerDeck.ReadPort1(controller); + R.controller_state = _controllerDeck.ReadPort2(controller); + } + + public int Frame => _frame; + + public string SystemId => "GB"; + + public bool DeterministicEmulation { get; set; } + + public void ResetCounters() + { + _frame = 0; + _lagcount = 0; + _islag = false; + } + + public CoreComm CoreComm { get; } + + public void Dispose() + { + L.Dispose(); + R.Dispose(); + } + + #region Video provider + + public int _frameHz = 60; + + public int[] _vidbuffer = new int[160 * 2 * 144]; + public int[] buff_L; + public int[] buff_R; + + public int[] GetVideoBuffer() + { + // combine the 2 video buffers from the instances + buff_L = L.GetVideoBuffer(); + buff_R = R.GetVideoBuffer(); + + 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 * 320 + j + 160] = buff_R[i * 160 + j]; + } + } + + return _vidbuffer; + } + + public int VirtualWidth => 160 * 2; + public int VirtualHeight => 144; + public int BufferWidth => 160 * 2; + public int BufferHeight => 144; + 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 + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IInputPollable.cs new file mode 100644 index 0000000000..021a1ac3a0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IInputPollable.cs @@ -0,0 +1,24 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + public partial class GBHawkLink : 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/GBHawkLink/GBHawkLink.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IMemoryDomains.cs new file mode 100644 index 0000000000..d28fdaba55 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IMemoryDomains.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + public partial class GBHawkLink + { + 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/GBHawkLink/GBHawkLink.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs new file mode 100644 index 0000000000..e8a6a9d0b5 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISaveRam.cs @@ -0,0 +1,34 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + public partial class GBHawkLink : ISaveRam + { + public byte[] CloneSaveRam() + { + if (L.cart_RAM != null) + { + return (byte[])L.cart_RAM.Clone(); + } + else + { + return null; + } + } + + public void StoreSaveRam(byte[] data) + { + Buffer.BlockCopy(data, 0, L.cart_RAM, 0, data.Length); + Console.WriteLine("loading SRAM here"); + } + + public bool SaveRamModified + { + get + { + return L.has_bat & _syncSettings.L.Use_SRAM; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs new file mode 100644 index 0000000000..dd2711ded1 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.ISettable.cs @@ -0,0 +1,91 @@ +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.GBHawkLink +{ + public partial class GBHawkLink : IEmulator, IStatable, ISettable + { + public GBLinkSettings GetSettings() + { + return new GBLinkSettings + ( + L.GetSettings(), + R.GetSettings() + ); + } + + public GBLinkSyncSettings GetSyncSettings() + { + return new GBLinkSyncSettings + ( + L.GetSyncSettings(), + R.GetSyncSettings() + ); + } + + public bool PutSettings(GBLinkSettings o) + { + return L.PutSettings(o.L) || R.PutSettings(o.R); + } + + public bool PutSyncSettings(GBLinkSyncSettings o) + { + return L.PutSyncSettings(o.L) || R.PutSyncSettings(o.R); + } + + private GBLinkSettings _settings = new GBLinkSettings(); + public GBLinkSyncSettings _syncSettings = new GBLinkSyncSettings(); + + public class GBLinkSettings + { + public GBHawk.GBHawk.GBSettings L; + public GBHawk.GBHawk.GBSettings R; + + public GBLinkSettings() + { + L = new GBHawk.GBHawk.GBSettings(); + R = new GBHawk.GBHawk.GBSettings(); + } + + public GBLinkSettings(GBHawk.GBHawk.GBSettings L, GBHawk.GBHawk.GBSettings R) + { + this.L = L; + this.R = R; + } + + public GBLinkSettings Clone() + { + return new GBLinkSettings(L.Clone(), R.Clone()); + } + } + + public class GBLinkSyncSettings + { + public GBHawk.GBHawk.GBSyncSettings L; + public GBHawk.GBHawk.GBSyncSettings R; + + public GBLinkSyncSettings() + { + L = new GBHawk.GBHawk.GBSyncSettings(); + R = new GBHawk.GBHawk.GBSyncSettings(); + } + + public GBLinkSyncSettings(GBHawk.GBHawk.GBSyncSettings L, GBHawk.GBHawk.GBSyncSettings R) + { + this.L = L; + this.R = R; + } + + public GBLinkSyncSettings Clone() + { + return new GBLinkSyncSettings(L.Clone(), R.Clone()); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IStatable.cs new file mode 100644 index 0000000000..e078e962a9 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.IStatable.cs @@ -0,0 +1,63 @@ +using System.IO; +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + public partial class GBHawkLink : 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); + _controllerDeck.SyncState(ser); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs new file mode 100644 index 0000000000..c4f2ffacda --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLink.cs @@ -0,0 +1,88 @@ +using System; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Common.Components.LR35902; + +using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy; +using BizHawk.Emulation.Cores.Nintendo.GBHawk; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink +{ + [Core( + "GBHawkLink", + "", + isPorted: false, + isReleased: true)] + [ServiceNotApplicable(typeof(IDriveLight))] + public partial class GBHawkLink : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, ILinkable, + 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 R; + + //[CoreConstructor("GB", "GBC")] + public GBHawkLink(CoreComm comm, GameInfo game_L, byte[] rom_L, GameInfo game_R, byte[] rom_R, /*string gameDbFn,*/ object settings, object syncSettings) + { + var ser = new BasicServiceProvider(this); + + GBLinkSettings linkSettings = (GBLinkSettings)settings ?? new GBLinkSettings(); + GBLinkSyncSettings linkSyncSettings = (GBLinkSyncSettings)syncSettings ?? new GBLinkSyncSettings(); + _controllerDeck = new GBHawkLinkControllerDeck(GBHawkControllerDeck.DefaultControllerName, GBHawkControllerDeck.DefaultControllerName); + + CoreComm = comm; + + L = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, + game_L, rom_L, linkSettings.L, linkSyncSettings.L); + + R = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, + game_R, rom_R, linkSettings.R, linkSyncSettings.R); + + ser.Register(this); + ser.Register(L.audio); + + _tracer = new TraceBuffer { Header = L.cpu.TraceHeader }; + ser.Register(_tracer); + + ServiceProvider = ser; + + SetupMemoryDomains(); + + HardReset(); + + 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]; + + 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]; + } + + public void HardReset() + { + L.HardReset(); + R.HardReset(); + } + + public DisplayType Region => DisplayType.NTSC; + + public int _frame = 0; + + private readonly GBHawkLinkControllerDeck _controllerDeck; + + private readonly ITraceable _tracer; + + bool ILinkable.LinkConnected { get; } + + private void ExecFetch(ushort addr) + { + MemoryCallbacks.CallExecutes(addr, "System Bus"); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLinkControllerDeck.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLinkControllerDeck.cs new file mode 100644 index 0000000000..fd2e5c7ae8 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLinkControllerDeck.cs @@ -0,0 +1,84 @@ +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.GBHawkLink +{ + public class GBHawkLinkControllerDeck + { + public GBHawkLinkControllerDeck(string controller1Name, string controller2Name) + { + if (!ValidControllerTypes.ContainsKey(controller1Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller1Name); + } + + if (!ValidControllerTypes.ContainsKey(controller2Name)) + { + throw new InvalidOperationException("Invalid controller type: " + controller2Name); + } + + Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1); + Port2 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller2Name], 2); + + Definition = new ControllerDefinition + { + Name = Port1.Definition.Name, + BoolButtons = Port1.Definition.BoolButtons + .Concat(Port2.Definition.BoolButtons) + .ToList() + }; + } + + public byte ReadPort1(IController c) + { + return Port1.Read(c); + } + + public byte ReadPort2(IController c) + { + return Port2.Read(c); + } + + public ControllerDefinition Definition { get; } + + public void SyncState(Serializer ser) + { + ser.BeginSection("Port1"); + Port1.SyncState(ser); + ser.EndSection(); + + ser.BeginSection("Port2"); + Port2.SyncState(ser); + ser.EndSection(); + } + + private readonly IPort Port1; + private readonly IPort Port2; + + private static Dictionary _controllerTypes; + + public static Dictionary ValidControllerTypes + { + get + { + if (_controllerTypes == null) + { + _controllerTypes = typeof(GBHawkLinkControllerDeck).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/GBHawkLink/GBHawkLinkControllers.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLinkControllers.cs new file mode 100644 index 0000000000..b1617ce740 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/GBHawkLinkControllers.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.GBHawkLink +{ + /// + /// 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/GBHawkLink/ReadMe.txt b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/ReadMe.txt new file mode 100644 index 0000000000..bc60bf4b01 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBHawkLink/ReadMe.txt @@ -0,0 +1 @@ +TODO: