diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibmGBA.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibmGBA.cs index aadcef7abd..644b987178 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibmGBA.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/LibmGBA.cs @@ -21,6 +21,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA [DllImport(dll, CallingConvention = cc)] public static extern void BizReset(IntPtr ctx); + [DllImport(dll, CallingConvention = cc)] + public static extern void BizSkipBios(IntPtr ctx); + [DllImport(dll, CallingConvention = cc)] public static extern bool BizLoad(IntPtr ctx, byte[] data, int length); diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs index cf1c64217a..4eff95be9b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/MGBAHawk.cs @@ -6,21 +6,32 @@ using BizHawk.Common; using BizHawk.Emulation.Common; using System.Runtime.InteropServices; using System.IO; +using System.ComponentModel; namespace BizHawk.Emulation.Cores.Nintendo.GBA { [CoreAttributes("mGBA", "endrift", true, false, "NOT DONE", "NOT DONE", false)] - public class MGBAHawk : IEmulator, IVideoProvider, ISyncSoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable + public class MGBAHawk : IEmulator, IVideoProvider, ISyncSoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable, ISettable { IntPtr core; [CoreConstructor("GBA")] - public MGBAHawk(byte[] file, CoreComm comm) + public MGBAHawk(byte[] file, CoreComm comm, SyncSettings syncSettings, bool deterministic) { - byte[] bios = null; - if (true) // TODO: config me + _syncSettings = syncSettings ?? new SyncSettings(); + DeterministicEmulation = deterministic; + + byte[] bios = comm.CoreFileProvider.GetFirmware("GBA", "Bios", false); + DeterministicEmulation &= bios != null; + + if (DeterministicEmulation != deterministic) { - bios = comm.CoreFileProvider.GetFirmware("GBA", "Bios", true); + throw new InvalidOperationException("A BIOS is required for deterministic recordings!"); + } + if (!DeterministicEmulation && bios != null && !_syncSettings.RTCUseRealTime && !_syncSettings.SkipBios) + { + // in these situations, this core is deterministic even though it wasn't asked to be + DeterministicEmulation = true; } if (bios != null && bios.Length != 16384) @@ -39,6 +50,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA throw new InvalidOperationException("BizLoad() returned FALSE! Bad ROM?"); } + if (!DeterministicEmulation && _syncSettings.SkipBios) + { + LibmGBA.BizSkipBios(core); + } + var ser = new BasicServiceProvider(this); ser.Register(new ArmV4Disassembler()); ser.Register(CreateMemoryDomains(file.Length)); @@ -46,6 +62,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA ServiceProvider = ser; CoreComm = comm; + CoreComm.VsyncNum = 262144; + CoreComm.VsyncDen = 4389; + CoreComm.NominalWidth = 240; + CoreComm.NominalHeight = 160; + InitStates(); } catch @@ -66,27 +87,31 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA LibmGBA.BizReset(core); IsLagFrame = LibmGBA.BizAdvance(core, VBANext.GetButtons(Controller), videobuff, ref nsamp, soundbuff, - 0, // TODO RTC hookup + RTCTime(), (short)Controller.GetFloat("Tilt X"), (short)Controller.GetFloat("Tilt Y"), (short)Controller.GetFloat("Tilt Z"), (byte)(255 - Controller.GetFloat("Light Sensor"))); + if (IsLagFrame) - _lagCount++; + LagCount++; + // this should be called in hblank on the appropriate line, but until we implement that, just do it here + if (_scanlinecb != null) + _scanlinecb(); } public int Frame { get; private set; } public string SystemId { get { return "GBA"; } } - public bool DeterministicEmulation { get { return true; } } + public bool DeterministicEmulation { get; private set; } public string BoardName { get { return null; } } public void ResetCounters() { Frame = 0; - _lagCount = 0; + LagCount = 0; IsLagFrame = false; } @@ -168,6 +193,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA #endregion + private Action _scanlinecb; + private GBAGPUMemoryAreas _gpumem; public GBAGPUMemoryAreas GetMemoryAreas() @@ -178,6 +205,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA [FeatureNotImplemented] public void SetScanlineCallback(Action callback, int scanline) { + _scanlinecb = callback; } #region ISaveRam @@ -296,7 +324,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA // other variables IsLagFrame = reader.ReadBoolean(); - _lagCount = reader.ReadInt32(); + LagCount = reader.ReadInt32(); Frame = reader.ReadInt32(); } @@ -312,13 +340,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA return savebuff2; } - public int LagCount - { - get { return _lagCount; } - } - - private int _lagCount; - + public int LagCount { get; private set; } public bool IsLagFrame { get; private set; } [FeatureNotImplemented] @@ -326,5 +348,73 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA { get { throw new NotImplementedException(); } } + + private long RTCTime() + { + if (!DeterministicEmulation && _syncSettings.RTCUseRealTime) + { + return (long)DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + } + long basetime = (long)_syncSettings.RTCInitialTime.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + long increment = Frame * 4389L >> 18; + return basetime + increment; + } + + public object GetSettings() + { + return null; + } + + public SyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSettings(object o) + { + return false; + } + + public bool PutSyncSettings(SyncSettings o) + { + bool ret = SyncSettings.NeedsReboot(o, _syncSettings); + _syncSettings = o; + return ret; + } + + private SyncSettings _syncSettings; + + public class SyncSettings + { + [DisplayName("Skip BIOS")] + [Description("Skips the BIOS intro. Not applicable when a BIOS is not provided.")] + [DefaultValue(true)] + public bool SkipBios { get; set; } + + [DisplayName("RTC Use Real Time")] + [Description("Causes the internal clock to reflect your system clock. Only relevant when a game has an RTC chip. Forced to false for movie recording.")] + [DefaultValue(true)] + public bool RTCUseRealTime { get; set; } + + [DisplayName("RTC Initial Time")] + [Description("The initial time of emulation. Only relevant when a game has an RTC chip and \"RTC Use Real Time\" is false.")] + [DefaultValue(typeof(DateTime), "2010-01-01")] + public DateTime RTCInitialTime { get; set; } + + public SyncSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public static bool NeedsReboot(SyncSettings x, SyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + + public SyncSettings Clone() + { + return (SyncSettings)MemberwiseClone(); + } + } } } diff --git a/BizHawk.Emulation.Cores/CoreInventory.cs b/BizHawk.Emulation.Cores/CoreInventory.cs index 2c03d9eaa1..153900a623 100644 --- a/BizHawk.Emulation.Cores/CoreInventory.cs +++ b/BizHawk.Emulation.Cores/CoreInventory.cs @@ -33,8 +33,11 @@ namespace BizHawk.Emulation.Cores Type expectedtype; if (!paramtypes.TryGetValue(pname, out expectedtype)) throw new InvalidOperationException(string.Format("Unexpected parameter name {0} in constructor for {1}", p.Name, Type)); - if (expectedtype != p.ParameterType) - throw new InvalidOperationException(string.Format("Unexpected type mismatch in parameter {0} in constructor for {1}", p.Name, Type)); + + // disabling the typecheck here doesn't really hurt anything, because the Invoke call will still catch any forbidden casts + // it does allow us to write "MySettingsType settings" instead of "object settings" + // if (expectedtype != p.ParameterType) + // throw new InvalidOperationException(string.Format("Unexpected type mismatch in parameter {0} in constructor for {1}", p.Name, Type)); parammap.Add(pname, i); } } diff --git a/output/dll/mgba.dll b/output/dll/mgba.dll index 99bea41e75..2fd267109c 100644 Binary files a/output/dll/mgba.dll and b/output/dll/mgba.dll differ