From c3a4077ffb0ac6b5e2e8c948d6a9c991e9077a90 Mon Sep 17 00:00:00 2001 From: adelikat Date: Fri, 16 Jan 2015 18:37:42 +0000 Subject: [PATCH] Break up Gambatte.cs into separate files --- .../BizHawk.Emulation.Cores.csproj | 21 + .../Nintendo/Gameboy/Gambatte.IDebuggable.cs | 82 +++ .../Gameboy/Gambatte.IMemoryDomains.cs | 56 ++ .../Nintendo/Gameboy/Gambatte.ISaveRam.cs | 68 +++ .../Nintendo/Gameboy/Gambatte.ISettable.cs | 137 +++++ .../Nintendo/Gameboy/Gambatte.IStatable.cs | 125 ++++ .../Nintendo/Gameboy/Gambatte.ITraceable.cs | 39 ++ .../Gameboy/Gambatte.IVideoProvider.cs | 44 ++ .../Consoles/Nintendo/Gameboy/Gambatte.cs | 552 ++---------------- 9 files changed, 607 insertions(+), 517 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IMemoryDomains.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index f736eb514a..48df255029 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -300,6 +300,27 @@ + + Gambatte.cs + + + Gambatte.cs + + + Gambatte.cs + + + Gambatte.cs + + + Gambatte.cs + + + Gambatte.cs + + + Gambatte.cs + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs new file mode 100644 index 0000000000..e411e24480 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IDebuggable.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + public partial class Gameboy : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + int[] data = new int[10]; + LibGambatte.gambatte_getregs(GambatteState, data); + + return new Dictionary + { + { "PC", (ushort)(data[(int)LibGambatte.RegIndicies.PC] & 0xffff) }, + { "SP", (ushort)(data[(int)LibGambatte.RegIndicies.SP] & 0xffff) }, + { "A", (byte)(data[(int)LibGambatte.RegIndicies.A] & 0xff) }, + { "B", (byte)(data[(int)LibGambatte.RegIndicies.B] & 0xff) }, + { "C", (byte)(data[(int)LibGambatte.RegIndicies.C] & 0xff) }, + { "D", (byte)(data[(int)LibGambatte.RegIndicies.D] & 0xff) }, + { "E", (byte)(data[(int)LibGambatte.RegIndicies.E] & 0xff) }, + { "F", (byte)(data[(int)LibGambatte.RegIndicies.F] & 0xff) }, + { "H", (byte)(data[(int)LibGambatte.RegIndicies.H] & 0xff) }, + { "L", (byte)(data[(int)LibGambatte.RegIndicies.L] & 0xff) } + }; + } + + [FeatureNotImplemented] + public void SetCpuRegister(string register, int value) + { + throw new NotImplementedException(); + } + + public bool CanStep(StepType type) + { + return false; + } + + [FeatureNotImplemented] + public void Step(StepType type) { throw new NotImplementedException(); } + + public IMemoryCallbackSystem MemoryCallbacks + { + get { return _memorycallbacks; } + } + + private LibGambatte.MemoryCallback readcb; + private LibGambatte.MemoryCallback writecb; + private LibGambatte.MemoryCallback execcb; + + private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(); + + /// + /// for use in dual core + /// + /// + internal void ConnectMemoryCallbackSystem(MemoryCallbackSystem mcs) + { + _memorycallbacks = mcs; + } + + private void InitMemoryCallbacks() + { + readcb = (addr) => MemoryCallbacks.CallReads(addr); + writecb = (addr) => MemoryCallbacks.CallWrites(addr); + execcb = (addr) => MemoryCallbacks.CallExecutes(addr); + _memorycallbacks.ActiveChanged += RefreshMemoryCallbacks; + } + + private void RefreshMemoryCallbacks() + { + var mcs = MemoryCallbacks; + + LibGambatte.gambatte_setreadcallback(GambatteState, mcs.HasReads ? readcb : null); + LibGambatte.gambatte_setwritecallback(GambatteState, mcs.HasWrites ? writecb : null); + LibGambatte.gambatte_setexeccallback(GambatteState, mcs.HasExecutes ? execcb : null); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IMemoryDomains.cs new file mode 100644 index 0000000000..bfa0464e12 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IMemoryDomains.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + public partial class Gameboy + { + private List _memoryDomains = new List(); + internal IMemoryDomains MemoryDomains { get; set; } + + void CreateMemoryDomain(LibGambatte.MemoryAreas which, string name) + { + IntPtr data = IntPtr.Zero; + int length = 0; + + if (!LibGambatte.gambatte_getmemoryarea(GambatteState, which, ref data, ref length)) + throw new Exception("gambatte_getmemoryarea() failed!"); + + // if length == 0, it's an empty block; (usually rambank on some carts); that's ok + if (data != IntPtr.Zero && length > 0) + _memoryDomains.Add(MemoryDomain.FromIntPtr(name, length, MemoryDomain.Endian.Little, data)); + } + + private void InitMemoryDomains() + { + CreateMemoryDomain(LibGambatte.MemoryAreas.wram, "WRAM"); + CreateMemoryDomain(LibGambatte.MemoryAreas.rom, "ROM"); + CreateMemoryDomain(LibGambatte.MemoryAreas.vram, "VRAM"); + CreateMemoryDomain(LibGambatte.MemoryAreas.cartram, "Cart RAM"); + CreateMemoryDomain(LibGambatte.MemoryAreas.oam, "OAM"); + CreateMemoryDomain(LibGambatte.MemoryAreas.hram, "HRAM"); + + // also add a special memory domain for the system bus, where calls get sent directly to the core each time + + _memoryDomains.Add(new MemoryDomain("System Bus", 65536, MemoryDomain.Endian.Little, + delegate(int addr) + { + if (addr < 0 || addr >= 65536) + throw new ArgumentOutOfRangeException(); + return LibGambatte.gambatte_cpuread(GambatteState, (ushort)addr); + }, + delegate(int addr, byte val) + { + if (addr < 0 || addr >= 65536) + throw new ArgumentOutOfRangeException(); + LibGambatte.gambatte_cpuwrite(GambatteState, (ushort)addr, val); + })); + + MemoryDomains = new MemoryDomainList(_memoryDomains); + (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs new file mode 100644 index 0000000000..78d1568da0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISaveRam.cs @@ -0,0 +1,68 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + public partial class Gameboy : ISaveRam + { + public bool SaveRamModified + { + get + { + if (LibGambatte.gambatte_savesavedatalength(GambatteState) == 0) + return false; + else + return true; // need to wire more stuff into the core to actually know this + } + } + + public byte[] CloneSaveRam() + { + int length = LibGambatte.gambatte_savesavedatalength(GambatteState); + + if (length > 0) + { + byte[] ret = new byte[length]; + LibGambatte.gambatte_savesavedata(GambatteState, ret); + return ret; + } + else + return new byte[0]; + } + + public void StoreSaveRam(byte[] data) + { + int expected = LibGambatte.gambatte_savesavedatalength(GambatteState); + switch (data.Length - expected) + { + case 0: + break; + default: + throw new ArgumentException("Size of saveram data does not match expected!"); + case 44: + data = FixRTC(data, 44); + break; + case 40: + data = FixRTC(data, 40); + break; + } + LibGambatte.gambatte_loadsavedata(GambatteState, data); + } + + private byte[] FixRTC(byte[] data, int offset) + { + // length - offset is the start of the VBA-only data; so + // length - offset - 4 is the start of the RTC block + int idx = data.Length - offset - 4; + + byte[] ret = new byte[idx + 4]; + Buffer.BlockCopy(data, 0, ret, 0, idx); + data[idx] = (byte)zerotime; + data[idx + 1] = (byte)(zerotime >> 8); + data[idx + 2] = (byte)(zerotime >> 16); + data[idx + 3] = (byte)(zerotime >> 24); + + return ret; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs new file mode 100644 index 0000000000..310449816e --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ISettable.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using Newtonsoft.Json; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + public partial class Gameboy : ISettable + { + public GambatteSettings GetSettings() + { + return _settings.Clone(); + } + + public bool PutSettings(GambatteSettings o) + { + _settings = o; + if (IsCGBMode()) + SetCGBColors(_settings.CGBColors); + else + ChangeDMGColors(_settings.GBPalette); + return false; + } + + public GambatteSyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSyncSettings(GambatteSyncSettings o) + { + bool ret = GambatteSyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret; + } + + private GambatteSettings _settings; + private GambatteSyncSettings _syncSettings; + + public class GambatteSettings + { + private static readonly int[] DefaultPalette = new[] + { + 10798341, 8956165, 1922333, 337157, + 10798341, 8956165, 1922333, 337157, + 10798341, 8956165, 1922333, 337157 + }; + + public int[] GBPalette; + public GBColors.ColorType CGBColors; + /// + /// true to mute all audio + /// + public bool Muted; + + public GambatteSettings() + { + GBPalette = (int[])DefaultPalette.Clone(); + CGBColors = GBColors.ColorType.gambatte; + } + + public GambatteSettings Clone() + { + var ret = (GambatteSettings)MemberwiseClone(); + ret.GBPalette = (int[])GBPalette.Clone(); + return ret; + } + } + + public class GambatteSyncSettings + { + [DisplayName("Force DMG Mode")] + [Description("Force the game to run on DMG hardware, even if it's detected as a CGB game. Relevant for games that are \"CGB Enhanced\" but do not require CGB.")] + [DefaultValue(false)] + public bool ForceDMG { 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("Multicart Compatibility")] + [Description("Use special compatibility hacks for certain multicart games. Relevant only for specific multicarts.")] + [DefaultValue(false)] + public bool MulticartCompat { get; set; } + + [DisplayName("Realtime RTC")] + [Description("If true, the real time clock in MBC3 games will reflect real time, instead of emulated time. Ignored (treated as false) when a movie is recording.")] + [DefaultValue(false)] + public bool RealTimeRTC { get; set; } + + [DisplayName("RTC Initial Time")] + [Description("Set the initial RTC time in terms of elapsed seconds. Only used when RealTimeRTC is false.")] + [DefaultValue(0)] + public int RTCInitialTime + { + get { return _RTCInitialTime; } + set { _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } + } + + [JsonIgnore] + int _RTCInitialTime; + + [DisplayName("Equal Length Frames")] + [Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")] + [DefaultValue(true)] + public bool EqualLengthFrames + { + get { return _EqualLengthFrames; } + set { _EqualLengthFrames = value; } + } + + [JsonIgnore] + [DeepEqualsIgnore] + private bool _EqualLengthFrames; + + public GambatteSyncSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public GambatteSyncSettings Clone() + { + return (GambatteSyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(GambatteSyncSettings x, GambatteSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs new file mode 100644 index 0000000000..8d3b1d1816 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IStatable.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Newtonsoft.Json; + +using BizHawk.Emulation.Common; + + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + public partial class Gameboy : IStatable + { + public bool BinarySaveStatesPreferred { get { return true; } } + + public void SaveStateText(System.IO.TextWriter writer) + { + var s = SaveState(); + ser.Serialize(writer, s); + // write extra copy of stuff we don't use + writer.WriteLine(); + writer.WriteLine("Frame {0}", Frame); + } + + public void LoadStateText(System.IO.TextReader reader) + { + var s = (TextState)ser.Deserialize(reader, typeof(TextState)); + LoadState(s); + } + + public void SaveStateBinary(System.IO.BinaryWriter writer) + { + if (!LibGambatte.gambatte_newstatesave(GambatteState, savebuff, savebuff.Length)) + throw new Exception("gambatte_newstatesave() returned false"); + + writer.Write(savebuff.Length); + writer.Write(savebuff); + + // other variables + writer.Write(IsLagFrame); + writer.Write(LagCount); + writer.Write(Frame); + writer.Write(frameOverflow); + writer.Write(_cycleCount); + } + + public void LoadStateBinary(System.IO.BinaryReader reader) + { + int length = reader.ReadInt32(); + if (length != savebuff.Length) + throw new InvalidOperationException("Savestate buffer size mismatch!"); + + reader.Read(savebuff, 0, savebuff.Length); + + if (!LibGambatte.gambatte_newstateload(GambatteState, savebuff, savebuff.Length)) + throw new Exception("gambatte_newstateload() returned false"); + + // other variables + IsLagFrame = reader.ReadBoolean(); + LagCount = reader.ReadInt32(); + Frame = reader.ReadInt32(); + frameOverflow = reader.ReadUInt32(); + _cycleCount = reader.ReadUInt64(); + } + + public byte[] SaveStateBinary() + { + MemoryStream ms = new MemoryStream(savebuff2); + BinaryWriter bw = new BinaryWriter(ms); + SaveStateBinary(bw); + bw.Flush(); + if (ms.Position != savebuff2.Length) + throw new InvalidOperationException(); + ms.Close(); + return savebuff2; + } + + private byte[] savebuff; + private byte[] savebuff2; + + private void NewSaveCoreSetBuff() + { + savebuff = new byte[LibGambatte.gambatte_newstatelen(GambatteState)]; + savebuff2 = new byte[savebuff.Length + 4 + 21]; + } + + private JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented }; + + // other data in the text state besides core + internal class TextStateData + { + public int Frame; + public int LagCount; + public bool IsLagFrame; + public ulong _cycleCount; + public uint frameOverflow; + } + + internal TextState SaveState() + { + var s = new TextState(); + s.Prepare(); + var ff = s.GetFunctionPointersSave(); + LibGambatte.gambatte_newstatesave_ex(GambatteState, ref ff); + s.ExtraData.IsLagFrame = IsLagFrame; + s.ExtraData.LagCount = LagCount; + s.ExtraData.Frame = Frame; + s.ExtraData.frameOverflow = frameOverflow; + s.ExtraData._cycleCount = _cycleCount; + return s; + } + + internal void LoadState(TextState s) + { + s.Prepare(); + var ff = s.GetFunctionPointersLoad(); + LibGambatte.gambatte_newstateload_ex(GambatteState, ref ff); + IsLagFrame = s.ExtraData.IsLagFrame; + LagCount = s.ExtraData.LagCount; + Frame = s.ExtraData.Frame; + frameOverflow = s.ExtraData.frameOverflow; + _cycleCount = s.ExtraData._cycleCount; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs new file mode 100644 index 0000000000..73129071f7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.ITraceable.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + public partial class Gameboy + { + private ITraceable Tracer { get; set; } + private LibGambatte.TraceCallback tracecb; + + private void MakeTrace(IntPtr _s) + { + int[] s = new int[13]; + System.Runtime.InteropServices.Marshal.Copy(_s, s, 0, 13); + ushort unused; + + Tracer.Put(string.Format( + "{13} SP:{2:x2} A:{3:x2} B:{4:x2} C:{5:x2} D:{6:x2} E:{7:x2} F:{8:x2} H:{9:x2} L:{10:x2} {11} Cy:{0}", + s[0], + s[1] & 0xffff, + s[2] & 0xffff, + s[3] & 0xff, + s[4] & 0xff, + s[5] & 0xff, + s[6] & 0xff, + s[7] & 0xff, + s[8] & 0xff, + s[9] & 0xff, + s[10] & 0xff, + s[11] != 0 ? "skip" : "", + s[12] & 0xff, + Common.Components.Z80GB.NewDisassembler.Disassemble((ushort)s[1], (addr) => LibGambatte.gambatte_cpuread(GambatteState, addr), out unused).PadRight(30) + )); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs new file mode 100644 index 0000000000..62832b83fb --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.IVideoProvider.cs @@ -0,0 +1,44 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Nintendo.Gameboy +{ + public partial class Gameboy + { + /// + /// stored image of most recent frame + /// + private int[] VideoBuffer = new int[160 * 144]; + + public int[] GetVideoBuffer() + { + return VideoBuffer; + } + + public int VirtualWidth + { + // only sgb changes this, which we don't emulate here + get { return 160; } + } + + public int VirtualHeight + { + get { return 144; } + } + + public int BufferWidth + { + get { return 160; } + } + + public int BufferHeight + { + get { return 144; } + } + + public int BackgroundColor + { + get { return 0; } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs index 8e1968c194..94e05d6e2e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Gambatte.cs @@ -23,35 +23,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy portedUrl: "http://gambatte.sourceforge.net/" )] [ServiceNotApplicable(typeof(IDriveLight))] - public class Gameboy : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IStatable, IInputPollable, + public partial class Gameboy : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IStatable, IInputPollable, IDebuggable, ISettable { - #region ALL SAVESTATEABLE STATE GOES HERE - - /// - /// internal gambatte state - /// - internal IntPtr GambatteState = IntPtr.Zero; - - public int Frame { get; set; } - public int LagCount { get; set; } - public bool IsLagFrame { get; private set; } - - // all cycle counts are relative to a 2*1024*1024 mhz refclock - - /// - /// total cycles actually executed - /// - private ulong _cycleCount = 0; - - /// - /// number of extra cycles we overran in the last frame - /// - private uint frameOverflow = 0; - public ulong CycleCount { get { return _cycleCount; } } - - #endregion - /// /// the nominal length of one frame /// @@ -152,18 +126,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy try { - this._SyncSettings = (GambatteSyncSettings)SyncSettings ?? new GambatteSyncSettings(); + this._syncSettings = (GambatteSyncSettings)SyncSettings ?? new GambatteSyncSettings(); // copy over non-loadflag syncsettings now; they won't take effect if changed later - zerotime = (uint)this._SyncSettings.RTCInitialTime; - real_rtc_time = DeterministicEmulation ? false : this._SyncSettings.RealTimeRTC; + zerotime = (uint)this._syncSettings.RTCInitialTime; + real_rtc_time = DeterministicEmulation ? false : this._syncSettings.RealTimeRTC; LibGambatte.LoadFlags flags = 0; - if (this._SyncSettings.ForceDMG) + if (this._syncSettings.ForceDMG) flags |= LibGambatte.LoadFlags.FORCE_DMG; - if (this._SyncSettings.GBACGB) + if (this._syncSettings.GBACGB) flags |= LibGambatte.LoadFlags.GBA_CGB; - if (this._SyncSettings.MulticartCompat) + if (this._syncSettings.MulticartCompat) flags |= LibGambatte.LoadFlags.MULTICART_COMPAT; if (LibGambatte.gambatte_load(GambatteState, file, (uint)file.Length, GetCurrentTime(), flags) != 0) @@ -210,6 +184,32 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public IEmulatorServiceProvider ServiceProvider { get; private set; } + #region ALL SAVESTATEABLE STATE GOES HERE + + /// + /// internal gambatte state + /// + internal IntPtr GambatteState = IntPtr.Zero; + + public int Frame { get; set; } + public int LagCount { get; set; } + public bool IsLagFrame { get; private set; } + + // all cycle counts are relative to a 2*1024*1024 mhz refclock + + /// + /// total cycles actually executed + /// + private ulong _cycleCount = 0; + + /// + /// number of extra cycles we overran in the last frame + /// + private uint frameOverflow = 0; + public ulong CycleCount { get { return _cycleCount; } } + + #endregion + #region controller public static readonly ControllerDefinition GbController = new ControllerDefinition @@ -237,36 +237,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy #endregion - #region debug - - public IDictionary GetCpuFlagsAndRegisters() - { - int[] data = new int[10]; - LibGambatte.gambatte_getregs(GambatteState, data); - - return new Dictionary - { - { "PC", (ushort)(data[(int)LibGambatte.RegIndicies.PC] & 0xffff) }, - { "SP", (ushort)(data[(int)LibGambatte.RegIndicies.SP] & 0xffff) }, - { "A", (byte)(data[(int)LibGambatte.RegIndicies.A] & 0xff) }, - { "B", (byte)(data[(int)LibGambatte.RegIndicies.B] & 0xff) }, - { "C", (byte)(data[(int)LibGambatte.RegIndicies.C] & 0xff) }, - { "D", (byte)(data[(int)LibGambatte.RegIndicies.D] & 0xff) }, - { "E", (byte)(data[(int)LibGambatte.RegIndicies.E] & 0xff) }, - { "F", (byte)(data[(int)LibGambatte.RegIndicies.F] & 0xff) }, - { "H", (byte)(data[(int)LibGambatte.RegIndicies.H] & 0xff) }, - { "L", (byte)(data[(int)LibGambatte.RegIndicies.L] & 0xff) } - }; - } - - [FeatureNotImplemented] - public void SetCpuRegister(string register, int value) - { - throw new NotImplementedException(); - } - - private ITraceable Tracer { get; set; } - /// /// true if the emulator is currently emulating CGB /// @@ -290,13 +260,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy _inputCallbacks = ics; } - public bool CanStep(StepType type) { return false; } - - [FeatureNotImplemented] - public void Step(StepType type) { throw new NotImplementedException(); } - - #endregion - internal void FrameAdvancePrep() { Frame++; @@ -346,7 +309,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public void FrameAdvance(bool render, bool rendersound) { FrameAdvancePrep(); - if (_SyncSettings.EqualLengthFrames) + if (_syncSettings.EqualLengthFrames) { while (true) { @@ -482,70 +445,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy public bool DeterministicEmulation { get; private set; } - #region saveram - - public byte[] CloneSaveRam() - { - int length = LibGambatte.gambatte_savesavedatalength(GambatteState); - - if (length > 0) - { - byte[] ret = new byte[length]; - LibGambatte.gambatte_savesavedata(GambatteState, ret); - return ret; - } - else - return new byte[0]; - } - - private byte[] FixRTC(byte[] data, int offset) - { - // length - offset is the start of the VBA-only data; so - // length - offset - 4 is the start of the RTC block - int idx = data.Length - offset - 4; - - byte[] ret = new byte[idx + 4]; - Buffer.BlockCopy(data, 0, ret, 0, idx); - data[idx] = (byte)zerotime; - data[idx + 1] = (byte)(zerotime >> 8); - data[idx + 2] = (byte)(zerotime >> 16); - data[idx + 3] = (byte)(zerotime >> 24); - - return ret; - } - - public void StoreSaveRam(byte[] data) - { - int expected = LibGambatte.gambatte_savesavedatalength(GambatteState); - switch (data.Length - expected) - { - case 0: - break; - default: - throw new ArgumentException("Size of saveram data does not match expected!"); - case 44: - data = FixRTC(data, 44); - break; - case 40: - data = FixRTC(data, 40); - break; - } - LibGambatte.gambatte_loadsavedata(GambatteState, data); - } - - public bool SaveRamModified - { - get - { - if (LibGambatte.gambatte_savesavedatalength(GambatteState) == 0) - return false; - else - return true; // need to wire more stuff into the core to actually know this - } - } - - #endregion - public void ResetCounters() { Frame = 0; @@ -557,236 +456,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy frameOverflow = 0; } - #region savestates - - byte[] savebuff; - byte[] savebuff2; - - void NewSaveCoreSetBuff() - { - savebuff = new byte[LibGambatte.gambatte_newstatelen(GambatteState)]; - savebuff2 = new byte[savebuff.Length + 4 + 21]; - } - - JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented }; - - // other data in the text state besides core - internal class TextStateData - { - public int Frame; - public int LagCount; - public bool IsLagFrame; - public ulong _cycleCount; - public uint frameOverflow; - } - - internal TextState SaveState() - { - var s = new TextState(); - s.Prepare(); - var ff = s.GetFunctionPointersSave(); - LibGambatte.gambatte_newstatesave_ex(GambatteState, ref ff); - s.ExtraData.IsLagFrame = IsLagFrame; - s.ExtraData.LagCount = LagCount; - s.ExtraData.Frame = Frame; - s.ExtraData.frameOverflow = frameOverflow; - s.ExtraData._cycleCount = _cycleCount; - return s; - } - - internal void LoadState(TextState s) - { - s.Prepare(); - var ff = s.GetFunctionPointersLoad(); - LibGambatte.gambatte_newstateload_ex(GambatteState, ref ff); - IsLagFrame = s.ExtraData.IsLagFrame; - LagCount = s.ExtraData.LagCount; - Frame = s.ExtraData.Frame; - frameOverflow = s.ExtraData.frameOverflow; - _cycleCount = s.ExtraData._cycleCount; - } - - public void SaveStateText(System.IO.TextWriter writer) - { - var s = SaveState(); - ser.Serialize(writer, s); - // write extra copy of stuff we don't use - writer.WriteLine(); - writer.WriteLine("Frame {0}", Frame); - } - - public void LoadStateText(System.IO.TextReader reader) - { - var s = (TextState)ser.Deserialize(reader, typeof(TextState)); - LoadState(s); - } - - public void SaveStateBinary(System.IO.BinaryWriter writer) - { - if (!LibGambatte.gambatte_newstatesave(GambatteState, savebuff, savebuff.Length)) - throw new Exception("gambatte_newstatesave() returned false"); - - writer.Write(savebuff.Length); - writer.Write(savebuff); - - // other variables - writer.Write(IsLagFrame); - writer.Write(LagCount); - writer.Write(Frame); - writer.Write(frameOverflow); - writer.Write(_cycleCount); - } - - public void LoadStateBinary(System.IO.BinaryReader reader) - { - int length = reader.ReadInt32(); - if (length != savebuff.Length) - throw new InvalidOperationException("Savestate buffer size mismatch!"); - - reader.Read(savebuff, 0, savebuff.Length); - - if (!LibGambatte.gambatte_newstateload(GambatteState, savebuff, savebuff.Length)) - throw new Exception("gambatte_newstateload() returned false"); - - // other variables - IsLagFrame = reader.ReadBoolean(); - LagCount = reader.ReadInt32(); - Frame = reader.ReadInt32(); - frameOverflow = reader.ReadUInt32(); - _cycleCount = reader.ReadUInt64(); - } - - public byte[] SaveStateBinary() - { - MemoryStream ms = new MemoryStream(savebuff2); - BinaryWriter bw = new BinaryWriter(ms); - SaveStateBinary(bw); - bw.Flush(); - if (ms.Position != savebuff2.Length) - throw new InvalidOperationException(); - ms.Close(); - return savebuff2; - } - - public bool BinarySaveStatesPreferred { get { return true; } } - - #endregion - - #region memorycallback - - LibGambatte.MemoryCallback readcb; - LibGambatte.MemoryCallback writecb; - LibGambatte.MemoryCallback execcb; - - private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(); - public IMemoryCallbackSystem MemoryCallbacks { get { return _memorycallbacks; } } - - /// - /// for use in dual core - /// - /// - public void ConnectMemoryCallbackSystem(MemoryCallbackSystem mcs) - { - _memorycallbacks = mcs; - } - - void InitMemoryCallbacks() - { - readcb = (addr) => MemoryCallbacks.CallReads(addr); - writecb = (addr) => MemoryCallbacks.CallWrites(addr); - execcb = (addr) => MemoryCallbacks.CallExecutes(addr); - _memorycallbacks.ActiveChanged += RefreshMemoryCallbacks; - } - - void RefreshMemoryCallbacks() - { - var mcs = MemoryCallbacks; - - LibGambatte.gambatte_setreadcallback(GambatteState, mcs.HasReads ? readcb : null); - LibGambatte.gambatte_setwritecallback(GambatteState, mcs.HasWrites ? writecb : null); - LibGambatte.gambatte_setexeccallback(GambatteState, mcs.HasExecutes ? execcb : null); - } - - #endregion - public CoreComm CoreComm { get; set; } - LibGambatte.TraceCallback tracecb; - - void MakeTrace(IntPtr _s) - { - int[] s = new int[13]; - System.Runtime.InteropServices.Marshal.Copy(_s, s, 0, 13); - ushort unused; - - Tracer.Put(string.Format( - "{13} SP:{2:x2} A:{3:x2} B:{4:x2} C:{5:x2} D:{6:x2} E:{7:x2} F:{8:x2} H:{9:x2} L:{10:x2} {11} Cy:{0}", - s[0], - s[1] & 0xffff, - s[2] & 0xffff, - s[3] & 0xff, - s[4] & 0xff, - s[5] & 0xff, - s[6] & 0xff, - s[7] & 0xff, - s[8] & 0xff, - s[9] & 0xff, - s[10] & 0xff, - s[11] != 0 ? "skip" : "", - s[12] & 0xff, - Common.Components.Z80GB.NewDisassembler.Disassemble((ushort)s[1], (addr) => LibGambatte.gambatte_cpuread(GambatteState, addr), out unused).PadRight(30) - )); - } - - #region MemoryDomains - - void CreateMemoryDomain(LibGambatte.MemoryAreas which, string name) - { - IntPtr data = IntPtr.Zero; - int length = 0; - - if (!LibGambatte.gambatte_getmemoryarea(GambatteState, which, ref data, ref length)) - throw new Exception("gambatte_getmemoryarea() failed!"); - - // if length == 0, it's an empty block; (usually rambank on some carts); that's ok - if (data != IntPtr.Zero && length > 0) - _MemoryDomains.Add(MemoryDomain.FromIntPtr(name, length, MemoryDomain.Endian.Little, data)); - } - - private void InitMemoryDomains() - { - CreateMemoryDomain(LibGambatte.MemoryAreas.wram, "WRAM"); - CreateMemoryDomain(LibGambatte.MemoryAreas.rom, "ROM"); - CreateMemoryDomain(LibGambatte.MemoryAreas.vram, "VRAM"); - CreateMemoryDomain(LibGambatte.MemoryAreas.cartram, "Cart RAM"); - CreateMemoryDomain(LibGambatte.MemoryAreas.oam, "OAM"); - CreateMemoryDomain(LibGambatte.MemoryAreas.hram, "HRAM"); - - // also add a special memory domain for the system bus, where calls get sent directly to the core each time - - _MemoryDomains.Add(new MemoryDomain("System Bus", 65536, MemoryDomain.Endian.Little, - delegate(int addr) - { - if (addr < 0 || addr >= 65536) - throw new ArgumentOutOfRangeException(); - return LibGambatte.gambatte_cpuread(GambatteState, (ushort)addr); - }, - delegate(int addr, byte val) - { - if (addr < 0 || addr >= 65536) - throw new ArgumentOutOfRangeException(); - LibGambatte.gambatte_cpuwrite(GambatteState, (ushort)addr, val); - })); - - MemoryDomains = new MemoryDomainList(_MemoryDomains); - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); - } - - private List _MemoryDomains = new List(); - internal IMemoryDomains MemoryDomains { get; set; } - - #endregion - #region ppudebug public bool GetGPUMemoryAreas(out IntPtr vram, out IntPtr bgpal, out IntPtr sppal, out IntPtr oam) @@ -867,46 +538,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy DisposeSound(); } - #region IVideoProvider - - /// - /// stored image of most recent frame - /// - int[] VideoBuffer = new int[160 * 144]; - - public int[] GetVideoBuffer() - { - return VideoBuffer; - } - - public int VirtualWidth - { - // only sgb changes this, which we don't emulate here - get { return 160; } - } - - public int VirtualHeight - { - get { return 144; } - } - - public int BufferWidth - { - get { return 160; } - } - - public int BufferHeight - { - get { return 144; } - } - - public int BackgroundColor - { - get { return 0; } - } - - #endregion - #region palette /// @@ -1020,120 +651,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy nsamp = soundoutbuffcontains; } - public bool Muted { get { return _Settings.Muted; } } - - #endregion - - #region Settings - - GambatteSettings _Settings; - GambatteSyncSettings _SyncSettings; - - public GambatteSettings GetSettings() { return _Settings.Clone(); } - public GambatteSyncSettings GetSyncSettings() { return _SyncSettings.Clone(); } - public bool PutSettings(GambatteSettings o) - { - _Settings = o; - if (IsCGBMode()) - SetCGBColors(_Settings.CGBColors); - else - ChangeDMGColors(_Settings.GBPalette); - return false; - } - - public bool PutSyncSettings(GambatteSyncSettings o) - { - bool ret = GambatteSyncSettings.NeedsReboot(_SyncSettings, o); - _SyncSettings = o; - return ret; - } - - public class GambatteSettings - { - private static readonly int[] DefaultPalette = new[] - { - 10798341, 8956165, 1922333, 337157, - 10798341, 8956165, 1922333, 337157, - 10798341, 8956165, 1922333, 337157 - }; - - public int[] GBPalette; - public GBColors.ColorType CGBColors; - /// - /// true to mute all audio - /// - public bool Muted; - - public GambatteSettings() - { - GBPalette = (int[])DefaultPalette.Clone(); - CGBColors = GBColors.ColorType.gambatte; - } - - public GambatteSettings Clone() - { - var ret = (GambatteSettings)MemberwiseClone(); - ret.GBPalette = (int[])GBPalette.Clone(); - return ret; - } - } - - public class GambatteSyncSettings - { - [DisplayName("Force DMG Mode")] - [Description("Force the game to run on DMG hardware, even if it's detected as a CGB game. Relevant for games that are \"CGB Enhanced\" but do not require CGB.")] - [DefaultValue(false)] - public bool ForceDMG { 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("Multicart Compatibility")] - [Description("Use special compatibility hacks for certain multicart games. Relevant only for specific multicarts.")] - [DefaultValue(false)] - public bool MulticartCompat { get; set; } - - [DisplayName("Realtime RTC")] - [Description("If true, the real time clock in MBC3 games will reflect real time, instead of emulated time. Ignored (treated as false) when a movie is recording.")] - [DefaultValue(false)] - public bool RealTimeRTC { get; set; } - - [DisplayName("RTC Initial Time")] - [Description("Set the initial RTC time in terms of elapsed seconds. Only used when RealTimeRTC is false.")] - [DefaultValue(0)] - public int RTCInitialTime - { - get { return _RTCInitialTime; } - set { _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); } - } - [JsonIgnore] - int _RTCInitialTime; - - [DisplayName("Equal Length Frames")] - [Description("When false, emulation frames sync to vblank. Only useful for high level TASing.")] - [DefaultValue(true)] - public bool EqualLengthFrames { get { return _EqualLengthFrames; } set { _EqualLengthFrames = value; } } - [JsonIgnore] - [DeepEqualsIgnore] - private bool _EqualLengthFrames; - - public GambatteSyncSettings() - { - SettingsUtil.SetDefaultValues(this); - } - - public GambatteSyncSettings Clone() - { - return (GambatteSyncSettings)MemberwiseClone(); - } - - public static bool NeedsReboot(GambatteSyncSettings x, GambatteSyncSettings y) - { - return !DeepEquality.DeepEquals(x, y); - } - } + public bool Muted { get { return _settings.Muted; } } #endregion }