mGBA update, various cleanups and some reorg
This commit is contained in:
parent
f6503adfb8
commit
f3ee3e7956
|
@ -14,7 +14,7 @@
|
||||||
[submodule "mgba"]
|
[submodule "mgba"]
|
||||||
path = submodules/mgba
|
path = submodules/mgba
|
||||||
url = https://github.com/TASEmulators/mgba.git
|
url = https://github.com/TASEmulators/mgba.git
|
||||||
branch = bizhawk-0.10
|
branch = bizhawk-0.11
|
||||||
[submodule "waterbox/melon/melonDS"]
|
[submodule "waterbox/melon/melonDS"]
|
||||||
path = waterbox/melon/melonDS
|
path = waterbox/melon/melonDS
|
||||||
url = https://github.com/TASEmulators/melonDS.git
|
url = https://github.com/TASEmulators/melonDS.git
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using BizHawk.BizInvoke;
|
using BizHawk.BizInvoke;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
public abstract class LibmGBA
|
public abstract class LibmGBA
|
||||||
{
|
{
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum Buttons : int
|
public enum Buttons : ushort
|
||||||
{
|
{
|
||||||
A = 1,
|
A = 1,
|
||||||
B = 2,
|
B = 2,
|
||||||
|
@ -91,7 +92,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
CHB = 32
|
CHB = 32
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum mWatchpointType
|
public enum mWatchpointType : int
|
||||||
{
|
{
|
||||||
WATCHPOINT_WRITE = 1,
|
WATCHPOINT_WRITE = 1,
|
||||||
WATCHPOINT_READ = 2,
|
WATCHPOINT_READ = 2,
|
||||||
|
@ -100,16 +101,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public class OverrideInfo
|
public struct OverrideInfo
|
||||||
{
|
{
|
||||||
public SaveType Savetype;
|
public SaveType Savetype;
|
||||||
public Hardware Hardware;
|
public Hardware Hardware;
|
||||||
public uint IdleLoop = IDLE_LOOP_NONE;
|
public uint IdleLoop;
|
||||||
public const uint IDLE_LOOP_NONE = unchecked((uint)0xffffffff);
|
public bool VbaBugCompat;
|
||||||
|
public bool DetectPokemonRomHacks;
|
||||||
|
|
||||||
|
public const uint IDLE_LOOP_NONE = 0xffffffffu;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
public class MemoryAreas
|
public struct MemoryAreas
|
||||||
{
|
{
|
||||||
public IntPtr bios;
|
public IntPtr bios;
|
||||||
public IntPtr wram;
|
public IntPtr wram;
|
||||||
|
@ -122,29 +126,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
public IntPtr sram;
|
public IntPtr sram;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizDestroy(IntPtr ctx);
|
public abstract void BizDestroy(IntPtr ctx);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract IntPtr BizCreate(byte[] bios, byte[] data, int length, [In]OverrideInfo dbinfo, bool skipBios);
|
public abstract IntPtr BizCreate(byte[] bios, byte[] data, int length, ref OverrideInfo overrides, bool skipBios);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizReset(IntPtr ctx);
|
public abstract void BizReset(IntPtr ctx);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract bool BizAdvance(IntPtr ctx, Buttons keys, int[] vbuff, ref int nsamp, short[] sbuff,
|
public abstract bool BizAdvance(IntPtr ctx, Buttons keys, int[] vbuff, ref int nsamp, short[] sbuff,
|
||||||
long time, short gyrox, short gyroy, short gyroz, byte luma);
|
long time, short gyrox, short gyroy, short gyroz, byte luma);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizSetPalette(IntPtr ctx, int[] palette);
|
public abstract void BizSetPalette(IntPtr ctx, int[] palette);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizGetMemoryAreas(IntPtr ctx, [Out]MemoryAreas dst);
|
public abstract void BizGetMemoryAreas(IntPtr ctx, out MemoryAreas dst);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract int BizGetSaveRam(IntPtr ctx, byte[] dest, int maxsize);
|
public abstract int BizGetSaveRam(IntPtr ctx, byte[] dest, int maxsize);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizPutSaveRam(IntPtr ctx, byte[] src, int size);
|
public abstract void BizPutSaveRam(IntPtr ctx, byte[] src, int size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -153,8 +157,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
/// <param name="p">private parameter to be passed to BizFinishGetState</param>
|
/// <param name="p">private parameter to be passed to BizFinishGetState</param>
|
||||||
/// <param name="size">size of buffer to be allocated for BizFinishGetState</param>
|
/// <param name="size">size of buffer to be allocated for BizFinishGetState</param>
|
||||||
/// <returns>if false, operation failed and BizFinishGetState should not be called</returns>
|
/// <returns>if false, operation failed and BizFinishGetState should not be called</returns>
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract bool BizStartGetState(IntPtr ctx, ref IntPtr p, ref int size);
|
public abstract bool BizStartGetState(IntPtr ctx, out IntPtr p, out int size);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// finish a savestate operation. if StartGetState returned true, this must be called else memory leaks
|
/// finish a savestate operation. if StartGetState returned true, this must be called else memory leaks
|
||||||
|
@ -162,57 +166,61 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
/// <param name="p">returned by BizStartGetState</param>
|
/// <param name="p">returned by BizStartGetState</param>
|
||||||
/// <param name="dest">buffer of length size</param>
|
/// <param name="dest">buffer of length size</param>
|
||||||
/// <param name="size">returned by BizStartGetState</param>
|
/// <param name="size">returned by BizStartGetState</param>
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizFinishGetState(IntPtr p, byte[] dest, int size);
|
public abstract void BizFinishGetState(IntPtr p, byte[] dest, int size);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract bool BizPutState(IntPtr ctx, byte[] src, int size);
|
public abstract bool BizPutState(IntPtr ctx, byte[] src, int size);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizSetLayerMask(IntPtr ctx, Layers mask);
|
public abstract void BizSetLayerMask(IntPtr ctx, Layers mask);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizSetSoundMask(IntPtr ctx, Sounds mask);
|
public abstract void BizSetSoundMask(IntPtr ctx, Sounds mask);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizGetRegisters(IntPtr ctx, int[] dest);
|
public abstract void BizGetRegisters(IntPtr ctx, int[] dest);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizSetRegister(IntPtr ctx, int index, int value);
|
public abstract void BizSetRegister(IntPtr ctx, int index, int value);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract ulong BizGetGlobalTime(IntPtr ctx);
|
public abstract ulong BizGetGlobalTime(IntPtr ctx);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract void BizWriteBus(IntPtr ctx, uint addr, byte val);
|
public abstract void BizWriteBus(IntPtr ctx, uint addr, byte val);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract byte BizReadBus(IntPtr ctx, uint addr);
|
public abstract byte BizReadBus(IntPtr ctx, uint addr);
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(cc)]
|
[UnmanagedFunctionPointer(cc)]
|
||||||
public delegate void InputCallback();
|
public delegate void InputCallback();
|
||||||
[BizImport(cc, Compatibility = true)]
|
|
||||||
|
[BizImport(cc)]
|
||||||
public abstract void BizSetInputCallback(IntPtr ctx, InputCallback cb);
|
public abstract void BizSetInputCallback(IntPtr ctx, InputCallback cb);
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(cc)]
|
[UnmanagedFunctionPointer(cc)]
|
||||||
public delegate void TraceCallback(string msg);
|
public delegate void TraceCallback(string msg);
|
||||||
[BizImport(cc, Compatibility = true)]
|
|
||||||
|
[BizImport(cc)]
|
||||||
public abstract void BizSetTraceCallback(IntPtr ctx, TraceCallback cb);
|
public abstract void BizSetTraceCallback(IntPtr ctx, TraceCallback cb);
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(cc)]
|
[UnmanagedFunctionPointer(cc)]
|
||||||
public delegate void MemCallback(uint addr, mWatchpointType type, uint oldValue, uint newValue);
|
public delegate void MemCallback(uint addr, mWatchpointType type, uint oldValue, uint newValue);
|
||||||
[BizImport(cc, Compatibility = true)]
|
|
||||||
|
[BizImport(cc)]
|
||||||
public abstract void BizSetMemCallback(IntPtr ctx, MemCallback cb);
|
public abstract void BizSetMemCallback(IntPtr ctx, MemCallback cb);
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(cc)]
|
[UnmanagedFunctionPointer(cc)]
|
||||||
public delegate void ExecCallback(uint pc);
|
public delegate void ExecCallback(uint pc);
|
||||||
[BizImport(cc, Compatibility = true)]
|
|
||||||
|
[BizImport(cc)]
|
||||||
public abstract void BizSetExecCallback(IntPtr ctx, ExecCallback cb);
|
public abstract void BizSetExecCallback(IntPtr ctx, ExecCallback cb);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract int BizSetWatchpoint(IntPtr ctx, uint addr, mWatchpointType type);
|
public abstract long BizSetWatchpoint(IntPtr ctx, uint addr, mWatchpointType type);
|
||||||
|
|
||||||
[BizImport(cc, Compatibility = true)]
|
[BizImport(cc)]
|
||||||
public abstract bool BizClearWatchpoint(IntPtr ctx, int id);
|
public abstract bool BizClearWatchpoint(IntPtr ctx, long id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
@ -13,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
var ret = new Dictionary<string, RegisterValue>();
|
var ret = new Dictionary<string, RegisterValue>();
|
||||||
for (var i = 0; i < RegisterNames.Length; i++)
|
for (var i = 0; i < RegisterNames.Length; i++)
|
||||||
{
|
{
|
||||||
ret[RegisterNames[i]] = new RegisterValue(values[i]);
|
ret[RegisterNames[i]] = new(values[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -41,7 +42,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
"R15" => 15,
|
"R15" => 15,
|
||||||
"CPSR" => 16,
|
"CPSR" => 16,
|
||||||
"SPSR" => 17,
|
"SPSR" => 17,
|
||||||
_=> -1
|
_ => -1
|
||||||
};
|
};
|
||||||
|
|
||||||
if (index != -1)
|
if (index != -1)
|
||||||
|
@ -50,7 +51,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMemoryCallbackSystem MemoryCallbacks { get; }
|
private readonly MGBAMemoryCallbackSystem _memoryCallbacks;
|
||||||
|
|
||||||
|
public IMemoryCallbackSystem MemoryCallbacks => _memoryCallbacks;
|
||||||
|
|
||||||
public bool CanStep(StepType type) => false;
|
public bool CanStep(StepType type) => false;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using BizHawk.Common;
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
{
|
||||||
|
public partial class MGBAHawk : IEmulator
|
||||||
|
{
|
||||||
|
public IEmulatorServiceProvider ServiceProvider { get; }
|
||||||
|
|
||||||
|
public ControllerDefinition ControllerDefinition => GBAController;
|
||||||
|
|
||||||
|
public bool FrameAdvance(IController controller, bool render, bool renderSound = true)
|
||||||
|
{
|
||||||
|
if (controller.IsPressed("Power"))
|
||||||
|
{
|
||||||
|
LibmGBA.BizReset(Core);
|
||||||
|
|
||||||
|
// BizReset caused memorydomain pointers to change.
|
||||||
|
WireMemoryDomainPointers();
|
||||||
|
}
|
||||||
|
|
||||||
|
LibmGBA.BizSetTraceCallback(Core, Tracer.IsEnabled() ? _tracecb : null);
|
||||||
|
|
||||||
|
IsLagFrame = LibmGBA.BizAdvance(
|
||||||
|
Core,
|
||||||
|
LibmGBA.GetButtons(controller),
|
||||||
|
render ? _videobuff : _dummyvideobuff,
|
||||||
|
ref _nsamp,
|
||||||
|
renderSound ? _soundbuff : _dummysoundbuff,
|
||||||
|
RTCTime(),
|
||||||
|
(short)controller.AxisValue("Tilt X"),
|
||||||
|
(short)controller.AxisValue("Tilt Y"),
|
||||||
|
(short)controller.AxisValue("Tilt Z"),
|
||||||
|
(byte)(255 - controller.AxisValue("Light Sensor")));
|
||||||
|
|
||||||
|
if (IsLagFrame)
|
||||||
|
{
|
||||||
|
LagCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this should be called in hblank on the appropriate line, but until we implement that, just do it here
|
||||||
|
_scanlinecb?.Invoke();
|
||||||
|
|
||||||
|
Frame++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Frame { get; private set; }
|
||||||
|
|
||||||
|
public string SystemId => VSystemID.Raw.GBA;
|
||||||
|
|
||||||
|
public bool DeterministicEmulation { get; }
|
||||||
|
|
||||||
|
public void ResetCounters()
|
||||||
|
{
|
||||||
|
Frame = 0;
|
||||||
|
LagCount = 0;
|
||||||
|
IsLagFrame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Core != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
LibmGBA.BizDestroy(Core);
|
||||||
|
Core = IntPtr.Zero;
|
||||||
|
_memoryCallbacks.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly ControllerDefinition GBAController = new ControllerDefinition("GBA Controller")
|
||||||
|
{
|
||||||
|
BoolButtons = { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "L", "R", "Power" }
|
||||||
|
}.AddXYZTriple("Tilt {0}", (-32767).RangeTo(32767), 0)
|
||||||
|
.AddAxis("Light Sensor", 0.RangeTo(255), 0)
|
||||||
|
.MakeImmutable();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
{
|
||||||
|
public partial class MGBAHawk : IGBAGPUViewable
|
||||||
|
{
|
||||||
|
public GBAGPUMemoryAreas GetMemoryAreas()
|
||||||
|
=> _gpumem;
|
||||||
|
|
||||||
|
public void SetScanlineCallback(Action callback, int scanline)
|
||||||
|
=> _scanlinecb = callback;
|
||||||
|
|
||||||
|
private Action _scanlinecb;
|
||||||
|
|
||||||
|
private GBAGPUMemoryAreas _gpumem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,12 +7,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
private readonly LibmGBA.InputCallback InputCallback;
|
private readonly LibmGBA.InputCallback InputCallback;
|
||||||
public int LagCount { get; set; }
|
public int LagCount { get; set; }
|
||||||
public bool IsLagFrame { get; set; }
|
public bool IsLagFrame { get; set; }
|
||||||
private void InputCb()
|
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
|
||||||
{
|
|
||||||
// most things are already handled in the core, this is just for event.oninputpoll
|
|
||||||
InputCallbacks.Call();
|
|
||||||
}
|
|
||||||
private InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
|
|
||||||
public IInputCallbackSystem InputCallbacks => _inputCallbacks;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,23 +21,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
||||||
private void CreateMemoryDomains(int romsize)
|
private void CreateMemoryDomains(int romsize)
|
||||||
{
|
{
|
||||||
var le = MemoryDomain.Endian.Little;
|
const MemoryDomain.Endian le = MemoryDomain.Endian.Little;
|
||||||
|
|
||||||
var mm = new List<MemoryDomain>();
|
var mm = new List<MemoryDomain>
|
||||||
mm.Add(_iwram = new MemoryDomainIntPtr("IWRAM", le, IntPtr.Zero, 32 * 1024, true, 4));
|
{
|
||||||
mm.Add(_ewram = new MemoryDomainIntPtr("EWRAM", le, IntPtr.Zero, 256 * 1024, true, 4));
|
(_iwram = new MemoryDomainIntPtr("IWRAM", le, IntPtr.Zero, 32 * 1024, true, 4)),
|
||||||
mm.Add(_bios = new MemoryDomainIntPtr("BIOS", le, IntPtr.Zero, 16 * 1024, false, 4));
|
(_ewram = new MemoryDomainIntPtr("EWRAM", le, IntPtr.Zero, 256 * 1024, true, 4)),
|
||||||
mm.Add(_palram = new MemoryDomainIntPtr("PALRAM", le, IntPtr.Zero, 1024, true, 4));
|
(_bios = new MemoryDomainIntPtr("BIOS", le, IntPtr.Zero, 16 * 1024, false, 4)),
|
||||||
mm.Add(_vram = new MemoryDomainIntPtr("VRAM", le, IntPtr.Zero, 96 * 1024, true, 4));
|
(_palram = new MemoryDomainIntPtr("PALRAM", le, IntPtr.Zero, 1024, true, 4)),
|
||||||
mm.Add(_oam = new MemoryDomainIntPtr("OAM", le, IntPtr.Zero, 1024, true, 4));
|
(_vram = new MemoryDomainIntPtr("VRAM", le, IntPtr.Zero, 96 * 1024, true, 4)),
|
||||||
mm.Add(_rom = new MemoryDomainIntPtr("ROM", le, IntPtr.Zero, romsize, true, 4));
|
(_oam = new MemoryDomainIntPtr("OAM", le, IntPtr.Zero, 1024, true, 4)),
|
||||||
// 128 KB is the max size for GBA savedata
|
(_rom = new MemoryDomainIntPtr("ROM", le, IntPtr.Zero, romsize, true, 4)),
|
||||||
// mGBA does not know a game's save type (and as a result actual savedata size) on startup.
|
// 128 KB is the max size for GBA savedata
|
||||||
// Instead, BizHawk's savedata buffer will be accessed directly for a consistent interface.
|
// mGBA does not know a game's save type (and as a result actual savedata size) on startup.
|
||||||
mm.Add(_sram = new MemoryDomainIntPtr("SRAM", le, IntPtr.Zero, 128 * 1024, true, 4));
|
// Instead, BizHawk's savedata buffer will be accessed directly for a consistent interface.
|
||||||
mm.Add(_cwram = new MemoryDomainDelegate("Combined WRAM", (256 + 32) * 1024, le, null, null, 4));
|
(_sram = new MemoryDomainIntPtr("SRAM", le, IntPtr.Zero, 128 * 1024, true, 4)),
|
||||||
|
(_cwram = new MemoryDomainDelegate("Combined WRAM", (256 + 32) * 1024, le, null, null, 4)),
|
||||||
|
|
||||||
mm.Add(new MemoryDomainDelegate("System Bus", 0x10000000, le,
|
new MemoryDomainDelegate("System Bus", 0x10000000, le,
|
||||||
addr =>
|
addr =>
|
||||||
{
|
{
|
||||||
var a = (uint)addr;
|
var a = (uint)addr;
|
||||||
|
@ -49,16 +50,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
var a = (uint)addr;
|
var a = (uint)addr;
|
||||||
if (a >= 0x10000000) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
|
if (a >= 0x10000000) throw new ArgumentOutOfRangeException(paramName: nameof(addr), a, message: "address out of range");
|
||||||
LibmGBA.BizWriteBus(Core, a, val);
|
LibmGBA.BizWriteBus(Core, a, val);
|
||||||
}, 4));
|
}, 4)
|
||||||
|
};
|
||||||
|
|
||||||
_memoryDomains = new MemoryDomainList(mm);
|
_memoryDomains = new(mm);
|
||||||
WireMemoryDomainPointers();
|
WireMemoryDomainPointers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WireMemoryDomainPointers()
|
private void WireMemoryDomainPointers()
|
||||||
{
|
{
|
||||||
var s = new LibmGBA.MemoryAreas();
|
LibmGBA.BizGetMemoryAreas(Core, out var s);
|
||||||
LibmGBA.BizGetMemoryAreas(Core, s);
|
|
||||||
|
|
||||||
_iwram.Data = s.iwram;
|
_iwram.Data = s.iwram;
|
||||||
_ewram.Data = s.wram;
|
_ewram.Data = s.wram;
|
||||||
|
@ -96,7 +97,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_gpumem = new GBAGPUMemoryAreas
|
_gpumem = new()
|
||||||
{
|
{
|
||||||
mmio = s.mmio,
|
mmio = s.mmio,
|
||||||
oam = s.oam,
|
oam = s.oam,
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
{
|
{
|
||||||
public partial class MGBAHawk : ISaveRam
|
public partial class MGBAHawk : ISaveRam
|
||||||
{
|
{
|
||||||
|
private readonly byte[] _saveScratch = new byte[262144];
|
||||||
|
|
||||||
public byte[] CloneSaveRam()
|
public byte[] CloneSaveRam()
|
||||||
{
|
{
|
||||||
int len = LibmGBA.BizGetSaveRam(Core, _saveScratch, _saveScratch.Length);
|
int len = LibmGBA.BizGetSaveRam(Core, _saveScratch, _saveScratch.Length);
|
||||||
|
@ -16,6 +18,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
throw new InvalidOperationException("Save buffer not long enough");
|
throw new InvalidOperationException("Save buffer not long enough");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
len = TruncateRTCIfUsingDeterministicTime(len);
|
||||||
|
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
|
@ -26,14 +30,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly byte[] _legacyHeader = Encoding.ASCII.GetBytes("GBABATT\0");
|
||||||
|
|
||||||
public void StoreSaveRam(byte[] data)
|
public void StoreSaveRam(byte[] data)
|
||||||
{
|
{
|
||||||
if (data.Take(8).SequenceEqual(Encoding.ASCII.GetBytes("GBABATT\0")))
|
if (data.AsSpan().Slice(0, 8).SequenceEqual(_legacyHeader))
|
||||||
{
|
{
|
||||||
data = LegacyFix(data);
|
data = LegacyFix(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
LibmGBA.BizPutSaveRam(Core, data, data.Length);
|
LibmGBA.BizPutSaveRam(Core, data, TruncateRTCIfUsingDeterministicTime(data.Length));
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SaveRamModified => LibmGBA.BizGetSaveRam(Core, _saveScratch, _saveScratch.Length) > 0;
|
public bool SaveRamModified => LibmGBA.BizGetSaveRam(Core, _saveScratch, _saveScratch.Length) > 0;
|
||||||
|
@ -61,5 +67,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
// well, isn't this a sticky situation!
|
// well, isn't this a sticky situation!
|
||||||
return flash; // woops
|
return flash; // woops
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// probably don't want RTC data in the save if a user is not using real time
|
||||||
|
private int TruncateRTCIfUsingDeterministicTime(int len)
|
||||||
|
=> (!DeterministicEmulation && _syncSettings.RTCUseRealTime) ? len : len & ~0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
@ -11,9 +11,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
public partial class MGBAHawk : ISettable<MGBAHawk.Settings, MGBAHawk.SyncSettings>
|
public partial class MGBAHawk : ISettable<MGBAHawk.Settings, MGBAHawk.SyncSettings>
|
||||||
{
|
{
|
||||||
public Settings GetSettings()
|
public Settings GetSettings()
|
||||||
{
|
=> _settings.Clone();
|
||||||
return _settings.Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PutSettingsDirtyBits PutSettings(Settings o)
|
public PutSettingsDirtyBits PutSettings(Settings o)
|
||||||
{
|
{
|
||||||
|
@ -35,17 +33,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
LibmGBA.BizSetSoundMask(Core, smask);
|
LibmGBA.BizSetSoundMask(Core, smask);
|
||||||
|
|
||||||
var palette = new int[65536];
|
var palette = new int[65536];
|
||||||
GBColors.ColorType c = GBColors.ColorType.vivid;
|
var c = o.ColorType switch
|
||||||
switch (o.ColorType)
|
|
||||||
{
|
{
|
||||||
case Settings.ColorTypes.SameBoy: c = GBColors.ColorType.sameboy; break;
|
Settings.ColorTypes.SameBoy => GBColors.ColorType.sameboy,
|
||||||
case Settings.ColorTypes.Gambatte: c = GBColors.ColorType.gambatte; break;
|
Settings.ColorTypes.Gambatte => GBColors.ColorType.gambatte,
|
||||||
case Settings.ColorTypes.Vivid: c = GBColors.ColorType.vivid; break;
|
Settings.ColorTypes.Vivid => GBColors.ColorType.vivid,
|
||||||
case Settings.ColorTypes.VbaVivid: c = GBColors.ColorType.vbavivid; break;
|
Settings.ColorTypes.VbaVivid => GBColors.ColorType.vbavivid,
|
||||||
case Settings.ColorTypes.VbaGbNew: c = GBColors.ColorType.vbagbnew; break;
|
Settings.ColorTypes.VbaGbNew => GBColors.ColorType.vbagbnew,
|
||||||
case Settings.ColorTypes.VbaGbOld: c = GBColors.ColorType.vbabgbold; break;
|
Settings.ColorTypes.VbaGbOld => GBColors.ColorType.vbabgbold,
|
||||||
case Settings.ColorTypes.BizhawkGba: c = GBColors.ColorType.gba; break;
|
Settings.ColorTypes.BizhawkGba => GBColors.ColorType.gba,
|
||||||
}
|
_ => GBColors.ColorType.vivid,
|
||||||
|
};
|
||||||
GBColors.GetLut(c, palette, agb: true);
|
GBColors.GetLut(c, palette, agb: true);
|
||||||
for (var i = 32768; i < 65536; i++)
|
for (var i = 32768; i < 65536; i++)
|
||||||
palette[i] = palette[i - 32768];
|
palette[i] = palette[i - 32768];
|
||||||
|
@ -126,21 +124,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||||
public ColorTypes ColorType { get; set; }
|
public ColorTypes ColorType { get; set; }
|
||||||
|
|
||||||
public Settings Clone()
|
|
||||||
{
|
|
||||||
return (Settings)MemberwiseClone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Settings()
|
public Settings()
|
||||||
{
|
=> SettingsUtil.SetDefaultValues(this);
|
||||||
SettingsUtil.SetDefaultValues(this);
|
|
||||||
}
|
public Settings Clone()
|
||||||
|
=> (Settings)MemberwiseClone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SyncSettings GetSyncSettings()
|
public SyncSettings GetSyncSettings()
|
||||||
{
|
=> _syncSettings.Clone();
|
||||||
return _syncSettings.Clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PutSettingsDirtyBits PutSyncSettings(SyncSettings o)
|
public PutSettingsDirtyBits PutSyncSettings(SyncSettings o)
|
||||||
{
|
{
|
||||||
|
@ -210,20 +202,24 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
[DefaultValue(false)]
|
[DefaultValue(false)]
|
||||||
public bool OverrideGbPlayerDetect { get; set; }
|
public bool OverrideGbPlayerDetect { get; set; }
|
||||||
|
|
||||||
public SyncSettings()
|
[DisplayName("VBA Bug Compatibility Mode")]
|
||||||
{
|
[Description("Enables a compatibility mode for buggy Pokemon romhacks which rely on VBA bugs. Generally you don't need to enable yourself as Pokemon romhack detection will enable this itself.")]
|
||||||
SettingsUtil.SetDefaultValues(this);
|
[DefaultValue(false)]
|
||||||
}
|
public bool OverrideVbaBugCompat { get; set; }
|
||||||
|
|
||||||
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
|
[DisplayName("Detect Pokemon Romhacks")]
|
||||||
{
|
[Description("Detects Pokemon romhacks and enables compatibility options due to their generally buggy nature. Will override other override settings.")]
|
||||||
return !DeepEquality.DeepEquals(x, y);
|
[DefaultValue(true)]
|
||||||
}
|
public bool OverridePokemonRomhackDetect { get; set; }
|
||||||
|
|
||||||
|
public SyncSettings()
|
||||||
|
=> SettingsUtil.SetDefaultValues(this);
|
||||||
|
|
||||||
public SyncSettings Clone()
|
public SyncSettings Clone()
|
||||||
{
|
=> (SyncSettings)MemberwiseClone();
|
||||||
return (SyncSettings)MemberwiseClone();
|
|
||||||
}
|
public static bool NeedsReboot(SyncSettings x, SyncSettings y)
|
||||||
|
=> !DeepEquality.DeepEquals(x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
|
@ -7,18 +7,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
{
|
{
|
||||||
public partial class MGBAHawk : IStatable
|
public partial class MGBAHawk : IStatable
|
||||||
{
|
{
|
||||||
private byte[] _savebuff = new byte[0];
|
private byte[] _savebuff = Array.Empty<byte>();
|
||||||
|
|
||||||
public void SaveStateBinary(BinaryWriter writer)
|
public void SaveStateBinary(BinaryWriter writer)
|
||||||
{
|
{
|
||||||
IntPtr p = IntPtr.Zero;
|
if (!LibmGBA.BizStartGetState(Core, out var p, out var size))
|
||||||
int size = 0;
|
|
||||||
if (!LibmGBA.BizStartGetState(Core, ref p, ref size))
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Core failed to save!");
|
throw new InvalidOperationException("Core failed to save!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size != _savebuff.Length)
|
if (size > _savebuff.Length)
|
||||||
{
|
{
|
||||||
_savebuff = new byte[size];
|
_savebuff = new byte[size];
|
||||||
}
|
}
|
||||||
|
@ -37,7 +35,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
public void LoadStateBinary(BinaryReader reader)
|
public void LoadStateBinary(BinaryReader reader)
|
||||||
{
|
{
|
||||||
int length = reader.ReadInt32();
|
int length = reader.ReadInt32();
|
||||||
if (length != _savebuff.Length)
|
if (length > _savebuff.Length)
|
||||||
{
|
{
|
||||||
_savebuff = new byte[length];
|
_savebuff = new byte[length];
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
{
|
||||||
|
public partial class MGBAHawk
|
||||||
|
{
|
||||||
|
private ITraceable Tracer { get; }
|
||||||
|
|
||||||
|
private readonly LibmGBA.TraceCallback _tracecb;
|
||||||
|
|
||||||
|
private void MakeTrace(string msg)
|
||||||
|
{
|
||||||
|
var disasm = msg.Split('|')[1];
|
||||||
|
var split = disasm.Split(':');
|
||||||
|
var machineCode = split[0].PadLeft(8);
|
||||||
|
var instruction = split[1].Trim();
|
||||||
|
var regs = GetCpuFlagsAndRegisters();
|
||||||
|
var wordSize = (regs["CPSR"].Value & 32) == 0 ? 4UL : 2UL;
|
||||||
|
var pc = regs["R15"].Value - wordSize * 2;
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (var i = 0; i < RegisterNames.Length; i++)
|
||||||
|
{
|
||||||
|
sb.Append($" { RegisterNames[i] }:{ regs[RegisterNames[i]].Value:X8}");
|
||||||
|
}
|
||||||
|
|
||||||
|
Tracer.Put(new(
|
||||||
|
disassembly: $"{pc:X8}: { machineCode } { instruction }".PadRight(50),
|
||||||
|
registerInfo: sb.ToString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,10 +4,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
{
|
{
|
||||||
public partial class MGBAHawk : IVideoProvider
|
public partial class MGBAHawk : IVideoProvider
|
||||||
{
|
{
|
||||||
public int[] GetVideoBuffer()
|
private readonly int[] _videobuff = new int[240 * 160];
|
||||||
{
|
private readonly int[] _dummyvideobuff = new int[240 * 160];
|
||||||
return _videobuff;
|
|
||||||
}
|
public int[] GetVideoBuffer() => _videobuff;
|
||||||
|
|
||||||
public int VirtualWidth => 240;
|
public int VirtualWidth => 240;
|
||||||
public int VirtualHeight => 160;
|
public int VirtualHeight => 160;
|
||||||
|
@ -17,13 +17,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
||||||
public int BackgroundColor => unchecked((int)0xff000000);
|
public int BackgroundColor => unchecked((int)0xff000000);
|
||||||
|
|
||||||
|
|
||||||
public int VsyncNumerator => 262144;
|
public int VsyncNumerator => 262144;
|
||||||
|
|
||||||
public int VsyncDenominator => 4389;
|
public int VsyncDenominator => 4389;
|
||||||
|
|
||||||
private readonly int[] _videobuff = new int[240 * 160];
|
|
||||||
|
|
||||||
private readonly int[] _dummyvideobuff = new int[240 * 160];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,16 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
|
||||||
using BizHawk.BizInvoke;
|
using BizHawk.BizInvoke;
|
||||||
using BizHawk.Common;
|
using BizHawk.Common;
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
{
|
{
|
||||||
[PortedCore(CoreNames.Mgba, "endrift", "0.9.1", "https://mgba.io/")]
|
[PortedCore(CoreNames.Mgba, "endrift", "0.10", "https://mgba.io/")]
|
||||||
[ServiceNotApplicable(new[] { typeof(IDriveLight), typeof(IRegionable) })]
|
[ServiceNotApplicable(new[] { typeof(IDriveLight), typeof(IRegionable) })]
|
||||||
public partial class MGBAHawk : IEmulator, IVideoProvider, ISoundProvider, IGBAGPUViewable,
|
public partial class MGBAHawk
|
||||||
ISaveRam, IStatable, IInputPollable, ISettable<MGBAHawk.Settings, MGBAHawk.SyncSettings>,
|
|
||||||
IDebuggable
|
|
||||||
{
|
{
|
||||||
private static readonly LibmGBA LibmGBA;
|
private static readonly LibmGBA LibmGBA;
|
||||||
public static LibmGBA ZZHacky => LibmGBA;
|
|
||||||
|
|
||||||
static MGBAHawk()
|
static MGBAHawk()
|
||||||
{
|
{
|
||||||
|
@ -22,24 +19,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
LibmGBA = BizInvoker.GetInvoker<LibmGBA>(resolver, CallingConventionAdapters.Native);
|
LibmGBA = BizInvoker.GetInvoker<LibmGBA>(resolver, CallingConventionAdapters.Native);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CoreConstructor(VSystemID.Raw.GBA)]
|
private IntPtr Core;
|
||||||
public MGBAHawk(byte[] file, CoreComm comm, SyncSettings syncSettings, Settings settings, bool deterministic, GameInfo game)
|
|
||||||
{
|
|
||||||
_syncSettings = syncSettings ?? new SyncSettings();
|
|
||||||
_settings = settings ?? new Settings();
|
|
||||||
|
|
||||||
var bios = comm.CoreFileProvider.GetFirmware(new("GBA", "Bios"));
|
[CoreConstructor(VSystemID.Raw.GBA)]
|
||||||
|
public MGBAHawk(CoreLoadParameters<Settings, SyncSettings> lp)
|
||||||
|
{
|
||||||
|
_syncSettings = lp.SyncSettings ?? new();
|
||||||
|
_settings = lp.Settings ?? new();
|
||||||
|
|
||||||
|
var bios = lp.Comm.CoreFileProvider.GetFirmware(new("GBA", "Bios"));
|
||||||
if (bios is { Length: not 0x4000 }) throw new InvalidOperationException("BIOS must be exactly 16384 bytes!");
|
if (bios is { Length: not 0x4000 }) throw new InvalidOperationException("BIOS must be exactly 16384 bytes!");
|
||||||
if (deterministic && bios is null) throw new MissingFirmwareException("A BIOS is required for deterministic recordings!");
|
if (lp.DeterministicEmulationRequested && bios is null) throw new MissingFirmwareException("A BIOS is required for deterministic recordings!");
|
||||||
DeterministicEmulation = deterministic
|
DeterministicEmulation = lp.DeterministicEmulationRequested
|
||||||
|| (bios is not null && !_syncSettings.RTCUseRealTime); // in this case, the core is deterministic even though it wasn't asked to be
|
|| (bios is not null && !_syncSettings.RTCUseRealTime); // in this case, the core is deterministic even though it wasn't asked to be
|
||||||
|
var rom = lp.Roms[0].FileData;
|
||||||
|
var overrides = GetOverrideInfo(_syncSettings);
|
||||||
|
|
||||||
Core = LibmGBA.BizCreate(
|
Core = LibmGBA.BizCreate(
|
||||||
bios,
|
bios,
|
||||||
file,
|
rom,
|
||||||
file.Length,
|
rom.Length,
|
||||||
GetOverrideInfo(_syncSettings),
|
ref overrides,
|
||||||
skipBios: _syncSettings.SkipBios);
|
skipBios: _syncSettings.SkipBios);
|
||||||
|
|
||||||
if (Core == IntPtr.Zero)
|
if (Core == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"{nameof(LibmGBA.BizCreate)}() returned NULL! Bad BIOS? and/or ROM?");
|
throw new InvalidOperationException($"{nameof(LibmGBA.BizCreate)}() returned NULL! Bad BIOS? and/or ROM?");
|
||||||
|
@ -47,7 +49,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
CreateMemoryDomains(file.Length);
|
CreateMemoryDomains(rom.Length);
|
||||||
var ser = new BasicServiceProvider(this);
|
var ser = new BasicServiceProvider(this);
|
||||||
ser.Register<IDisassemblable>(new ArmV4Disassembler());
|
ser.Register<IDisassemblable>(new ArmV4Disassembler());
|
||||||
ser.Register<IMemoryDomains>(_memoryDomains);
|
ser.Register<IMemoryDomains>(_memoryDomains);
|
||||||
|
@ -57,9 +59,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
||||||
const string TRACE_HEADER = "ARM7: PC, machine code, mnemonic, operands, registers";
|
const string TRACE_HEADER = "ARM7: PC, machine code, mnemonic, operands, registers";
|
||||||
Tracer = new TraceBuffer(TRACE_HEADER);
|
Tracer = new TraceBuffer(TRACE_HEADER);
|
||||||
_tracecb = msg => Tracer.Put(_traceInfo(msg));
|
_tracecb = MakeTrace;
|
||||||
ser.Register(Tracer);
|
ser.Register(Tracer);
|
||||||
MemoryCallbacks = new MGBAMemoryCallbackSystem(this);
|
_memoryCallbacks = new(LibmGBA, Core);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -67,117 +69,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputCallback = new LibmGBA.InputCallback(InputCb);
|
// most things are already handled in the core, this is just for event.oninputpoll
|
||||||
|
InputCallback = InputCallbacks.Call;
|
||||||
LibmGBA.BizSetInputCallback(Core, InputCallback);
|
LibmGBA.BizSetInputCallback(Core, InputCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEmulatorServiceProvider ServiceProvider { get; }
|
|
||||||
|
|
||||||
public ControllerDefinition ControllerDefinition => GBAController;
|
|
||||||
|
|
||||||
private ITraceable Tracer { get; }
|
|
||||||
|
|
||||||
private LibmGBA.TraceCallback _tracecb { get; set; }
|
|
||||||
|
|
||||||
private TraceInfo _traceInfo(string msg)
|
|
||||||
{
|
|
||||||
var disasm = msg.Split('|')[1];
|
|
||||||
var split = disasm.Split(':');
|
|
||||||
var machineCode = split[0].PadLeft(8);
|
|
||||||
var instruction = split[1].Trim();
|
|
||||||
var regs = GetCpuFlagsAndRegisters();
|
|
||||||
var wordSize = (regs["CPSR"].Value & 32) == 0 ? 4UL : 2UL;
|
|
||||||
var pc = regs["R15"].Value - wordSize * 2;
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
|
|
||||||
for (var i = 0; i < RegisterNames.Length; i++)
|
|
||||||
{
|
|
||||||
sb.Append($" { RegisterNames[i] }:{ regs[RegisterNames[i]].Value:X8}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new(
|
|
||||||
disassembly: $"{pc:X8}: { machineCode } { instruction }".PadRight(50),
|
|
||||||
registerInfo: sb.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool FrameAdvance(IController controller, bool render, bool renderSound = true)
|
|
||||||
{
|
|
||||||
if (controller.IsPressed("Power"))
|
|
||||||
{
|
|
||||||
LibmGBA.BizReset(Core);
|
|
||||||
|
|
||||||
// BizReset caused memorydomain pointers to change.
|
|
||||||
WireMemoryDomainPointers();
|
|
||||||
}
|
|
||||||
|
|
||||||
LibmGBA.BizSetTraceCallback(Core, Tracer.IsEnabled() ? _tracecb : null);
|
|
||||||
|
|
||||||
IsLagFrame = LibmGBA.BizAdvance(
|
|
||||||
Core,
|
|
||||||
LibmGBA.GetButtons(controller),
|
|
||||||
render ? _videobuff : _dummyvideobuff,
|
|
||||||
ref _nsamp,
|
|
||||||
renderSound ? _soundbuff : _dummysoundbuff,
|
|
||||||
RTCTime(),
|
|
||||||
(short)controller.AxisValue("Tilt X"),
|
|
||||||
(short)controller.AxisValue("Tilt Y"),
|
|
||||||
(short)controller.AxisValue("Tilt Z"),
|
|
||||||
(byte)(255 - controller.AxisValue("Light Sensor")));
|
|
||||||
|
|
||||||
if (IsLagFrame)
|
|
||||||
{
|
|
||||||
LagCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this should be called in hblank on the appropriate line, but until we implement that, just do it here
|
|
||||||
_scanlinecb?.Invoke();
|
|
||||||
|
|
||||||
Frame++;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Frame { get; private set; }
|
|
||||||
|
|
||||||
public string SystemId => VSystemID.Raw.GBA;
|
|
||||||
|
|
||||||
public bool DeterministicEmulation { get; }
|
|
||||||
|
|
||||||
public void ResetCounters()
|
|
||||||
{
|
|
||||||
Frame = 0;
|
|
||||||
LagCount = 0;
|
|
||||||
IsLagFrame = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (Core != IntPtr.Zero)
|
|
||||||
{
|
|
||||||
LibmGBA.BizDestroy(Core);
|
|
||||||
Core = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public GBAGPUMemoryAreas GetMemoryAreas()
|
|
||||||
{
|
|
||||||
return _gpumem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetScanlineCallback(Action callback, int scanline)
|
|
||||||
{
|
|
||||||
_scanlinecb = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly byte[] _saveScratch = new byte[262144];
|
|
||||||
internal IntPtr Core;
|
|
||||||
|
|
||||||
private static LibmGBA.OverrideInfo GetOverrideInfo(SyncSettings syncSettings)
|
private static LibmGBA.OverrideInfo GetOverrideInfo(SyncSettings syncSettings)
|
||||||
{
|
{
|
||||||
var ret = new LibmGBA.OverrideInfo
|
var ret = new LibmGBA.OverrideInfo
|
||||||
{
|
{
|
||||||
Savetype = syncSettings.OverrideSaveType,
|
Savetype = syncSettings.OverrideSaveType,
|
||||||
Hardware = LibmGBA.Hardware.None
|
Hardware = LibmGBA.Hardware.None,
|
||||||
|
IdleLoop = LibmGBA.OverrideInfo.IDLE_LOOP_NONE,
|
||||||
|
VbaBugCompat = syncSettings.OverrideVbaBugCompat,
|
||||||
|
DetectPokemonRomHacks = syncSettings.OverridePokemonRomhackDetect
|
||||||
};
|
};
|
||||||
|
|
||||||
if (syncSettings.OverrideRtc is SyncSettings.HardwareSelection.Autodetect)
|
if (syncSettings.OverrideRtc is SyncSettings.HardwareSelection.Autodetect)
|
||||||
|
@ -233,27 +138,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Action _scanlinecb;
|
private static readonly DateTime _epoch = new(1970, 1, 1);
|
||||||
|
|
||||||
private GBAGPUMemoryAreas _gpumem;
|
|
||||||
|
|
||||||
private long RTCTime()
|
private long RTCTime()
|
||||||
{
|
{
|
||||||
if (!DeterministicEmulation && _syncSettings.RTCUseRealTime)
|
if (!DeterministicEmulation && _syncSettings.RTCUseRealTime)
|
||||||
{
|
{
|
||||||
return (long)DateTime.Now.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
return (long)DateTime.Now.Subtract(_epoch).TotalSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
long baseTime = (long)_syncSettings.RTCInitialTime.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
|
long baseTime = (long)_syncSettings.RTCInitialTime.Subtract(_epoch).TotalSeconds;
|
||||||
long increment = Frame * 4389L >> 18;
|
long increment = Frame * 4389L >> 18;
|
||||||
return baseTime + increment;
|
return baseTime + increment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly ControllerDefinition GBAController = new ControllerDefinition("GBA Controller")
|
|
||||||
{
|
|
||||||
BoolButtons = { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "L", "R", "Power" }
|
|
||||||
}.AddXYZTriple("Tilt {0}", (-32767).RangeTo(32767), 0)
|
|
||||||
.AddAxis("Light Sensor", 0.RangeTo(255), 0)
|
|
||||||
.MakeImmutable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
{
|
{
|
||||||
public class MGBAMemoryCallbackSystem : IMemoryCallbackSystem
|
public class MGBAMemoryCallbackSystem : IMemoryCallbackSystem, IDisposable
|
||||||
{
|
{
|
||||||
private readonly MGBAHawk _mgba;
|
private LibmGBA _mgba;
|
||||||
|
private IntPtr _core;
|
||||||
private readonly LibmGBA.MemCallback _readWriteCallback;
|
private readonly LibmGBA.MemCallback _readWriteCallback;
|
||||||
private readonly LibmGBA.ExecCallback _executeCallback;
|
private readonly LibmGBA.ExecCallback _executeCallback;
|
||||||
private readonly Dictionary<uint, MemoryCallbackDelegate> _readCallbacks = new();
|
private readonly Dictionary<uint, MemoryCallbackDelegate> _readCallbacks = new();
|
||||||
|
@ -16,9 +18,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
private readonly Dictionary<uint, MemoryCallbackDelegate> _execCallbacks = new();
|
private readonly Dictionary<uint, MemoryCallbackDelegate> _execCallbacks = new();
|
||||||
private readonly List<CallbackContainer> _callbacks = new();
|
private readonly List<CallbackContainer> _callbacks = new();
|
||||||
|
|
||||||
public MGBAMemoryCallbackSystem(MGBAHawk mgba)
|
public MGBAMemoryCallbackSystem(LibmGBA mgba, IntPtr core)
|
||||||
{
|
{
|
||||||
_mgba = mgba;
|
_mgba = mgba;
|
||||||
|
_core = core;
|
||||||
_readWriteCallback = RunReadWriteCallback;
|
_readWriteCallback = RunReadWriteCallback;
|
||||||
_executeCallback = RunExecCallback;
|
_executeCallback = RunExecCallback;
|
||||||
}
|
}
|
||||||
|
@ -63,12 +66,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
||||||
if (container.Callback.Type == MemoryCallbackType.Execute)
|
if (container.Callback.Type == MemoryCallbackType.Execute)
|
||||||
{
|
{
|
||||||
MGBAHawk.ZZHacky.BizSetExecCallback(_mgba.Core, _executeCallback);
|
_mgba.BizSetExecCallback(_core, _executeCallback);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
container.ID = MGBAHawk.ZZHacky.BizSetWatchpoint(_mgba.Core, callback.Address.Value, container.WatchPointType);
|
container.ID = _mgba.BizSetWatchpoint(_core, callback.Address.Value, container.WatchPointType);
|
||||||
MGBAHawk.ZZHacky.BizSetMemCallback(_mgba.Core, _readWriteCallback);
|
_mgba.BizSetMemCallback(_core, _readWriteCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
var cbDict = container.Callback.Type switch
|
var cbDict = container.Callback.Type switch
|
||||||
|
@ -101,12 +104,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
||||||
if (!HasExecutes)
|
if (!HasExecutes)
|
||||||
{
|
{
|
||||||
MGBAHawk.ZZHacky.BizSetExecCallback(_mgba.Core, null);
|
_mgba.BizSetExecCallback(_core, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!MGBAHawk.ZZHacky.BizClearWatchpoint(_mgba.Core, cb.ID))
|
if (!_mgba.BizClearWatchpoint(_core, cb.ID))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Unable to clear watchpoint???");
|
throw new InvalidOperationException("Unable to clear watchpoint???");
|
||||||
}
|
}
|
||||||
|
@ -126,7 +129,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
|
|
||||||
if (!HasReads && !HasWrites)
|
if (!HasReads && !HasWrites)
|
||||||
{
|
{
|
||||||
MGBAHawk.ZZHacky.BizSetMemCallback(_mgba.Core, null);
|
_mgba.BizSetMemCallback(_core, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,31 +199,37 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
|
||||||
cb?.Invoke(pc, 0, (uint)MemoryCallbackFlags.AccessExecute);
|
cb?.Invoke(pc, 0, (uint)MemoryCallbackFlags.AccessExecute);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
internal class CallbackContainer
|
public void Dispose()
|
||||||
{
|
|
||||||
public CallbackContainer(IMemoryCallback callBack)
|
|
||||||
{
|
{
|
||||||
Callback = callBack;
|
_mgba = null;
|
||||||
|
_core = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMemoryCallback Callback { get; }
|
private class CallbackContainer
|
||||||
|
|
||||||
// the core returns this when setting a wp and needs it to clear that wp
|
|
||||||
public int ID { get; set; }
|
|
||||||
|
|
||||||
public LibmGBA.mWatchpointType WatchPointType
|
|
||||||
{
|
{
|
||||||
get
|
public CallbackContainer(IMemoryCallback callBack)
|
||||||
{
|
{
|
||||||
return Callback.Type switch
|
Callback = callBack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IMemoryCallback Callback { get; }
|
||||||
|
|
||||||
|
// the core returns this when setting a wp and needs it to clear that wp
|
||||||
|
public long ID { get; set; }
|
||||||
|
|
||||||
|
public LibmGBA.mWatchpointType WatchPointType
|
||||||
|
{
|
||||||
|
get
|
||||||
{
|
{
|
||||||
MemoryCallbackType.Read => LibmGBA.mWatchpointType.WATCHPOINT_READ,
|
return Callback.Type switch
|
||||||
MemoryCallbackType.Write => LibmGBA.mWatchpointType.WATCHPOINT_WRITE,
|
{
|
||||||
MemoryCallbackType.Execute => throw new NotImplementedException("Executes can not be used from watch points."),
|
MemoryCallbackType.Read => LibmGBA.mWatchpointType.WATCHPOINT_READ,
|
||||||
_ => throw new InvalidOperationException("Invalid callback type"),
|
MemoryCallbackType.Write => LibmGBA.mWatchpointType.WATCHPOINT_WRITE,
|
||||||
};
|
MemoryCallbackType.Execute => throw new NotImplementedException("Executes can not be used from watch points."),
|
||||||
|
_ => throw new InvalidOperationException("Invalid callback type"),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 707a6f8fbd07a64911667914d295c8b055e9cce3
|
Subproject commit f59c9650fd141fd68a9b3f51f7860bb3791aa193
|
Loading…
Reference in New Issue