Add SameBoy as a GB/C core (#3009)
* basics for sameboy * lol * bleh * lol * push this in * push this in * settings, and also update sameboy bootroms * bleh * build linux * remove some debugging shiz * fix the order of this * debug stuff also do gpu palettes right * use new key mask API * push shit in * bleh * add in replacement impl for joypad, use until opposing directions are allowed upstream * update * finally get this working without needing GB_INTERNAL * hook up acc controls * oops * oops x2 * oh right this doesn't use this * finish this up * also mark this as released * cleanups * Nitpicks Co-authored-by: YoshiRulz <OSSYoshiRulz@gmail.com>
This commit is contained in:
parent
69d51aba50
commit
2348e2885d
|
@ -40,3 +40,6 @@
|
|||
[submodule "submodules/libfwunpack"]
|
||||
path = submodules/libfwunpack
|
||||
url = https://github.com/TASEmulators/fwunpack
|
||||
[submodule "submodules/sameboy/libsameboy"]
|
||||
path = submodules/sameboy/libsameboy
|
||||
url = https://github.com/LIJI32/SameBoy
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -29,7 +29,7 @@ namespace BizHawk.Client.Common
|
|||
(new[] { VSystemID.Raw.SGB },
|
||||
new[] { CoreNames.Gambatte, CoreNames.Bsnes, CoreNames.Bsnes115}),
|
||||
(new[] { VSystemID.Raw.GB, VSystemID.Raw.GBC },
|
||||
new[] { CoreNames.Gambatte, CoreNames.GbHawk, CoreNames.SubGbHawk }),
|
||||
new[] { CoreNames.Gambatte, CoreNames.Sameboy, CoreNames.GbHawk, CoreNames.SubGbHawk }),
|
||||
(new[] { VSystemID.Raw.GBL },
|
||||
new[] { CoreNames.GambatteLink, CoreNames.GBHawkLink, CoreNames.GBHawkLink3x, CoreNames.GBHawkLink4x }),
|
||||
(new[] { VSystemID.Raw.PCE, VSystemID.Raw.PCECD, VSystemID.Raw.SGX },
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
|
||||
using BizHawk.Emulation.Cores.Nintendo.Sameboy;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SubNESHawk;
|
||||
using BizHawk.Emulation.Cores.Nintendo.SubGBHawk;
|
||||
|
||||
|
@ -363,6 +364,12 @@ namespace BizHawk.Client.Common
|
|||
coreValue = ((Gameboy)Movie.Emulator).CycleCount;
|
||||
movieHasValue = Movie.HeaderEntries.TryGetValue(HeaderKeys.CycleCount, out movieValueStr);
|
||||
}
|
||||
else if (Movie.Core == CoreNames.Sameboy)
|
||||
{
|
||||
valueName = "cycle";
|
||||
coreValue = ((Sameboy)Movie.Emulator).CycleCount;
|
||||
movieHasValue = Movie.HeaderEntries.TryGetValue(HeaderKeys.CycleCount, out movieValueStr);
|
||||
}
|
||||
else if (Movie.Core == CoreNames.SubGbHawk)
|
||||
{
|
||||
valueName = "cycle";
|
||||
|
|
|
@ -83,6 +83,10 @@ namespace BizHawk.Client.Common
|
|||
{
|
||||
Header[HeaderKeys.CycleCount] = gameboy.CycleCount.ToString();
|
||||
}
|
||||
else if (Emulator is Emulation.Cores.Nintendo.Sameboy.Sameboy sameboy)
|
||||
{
|
||||
Header[HeaderKeys.CycleCount] = sameboy.CycleCount.ToString();
|
||||
}
|
||||
else if (Emulator is Emulation.Cores.Nintendo.SubGBHawk.SubGBHawk subGb)
|
||||
{
|
||||
Header[HeaderKeys.CycleCount] = subGb.CycleCount.ToString();
|
||||
|
|
|
@ -14,6 +14,7 @@ using BizHawk.Emulation.Common;
|
|||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Nintendo.GBA;
|
||||
using BizHawk.Emulation.Cores.Nintendo.N64;
|
||||
using BizHawk.Emulation.Cores.Nintendo.Sameboy;
|
||||
using BizHawk.WinForms.Controls;
|
||||
|
||||
namespace BizHawk.Client.EmuHawk
|
||||
|
@ -186,6 +187,18 @@ namespace BizHawk.Client.EmuHawk
|
|||
Text = "Firmware",
|
||||
},
|
||||
new ToolStripSeparatorEx(),
|
||||
new DebugVSystemMenuItem("GB")
|
||||
{
|
||||
DropDownItems =
|
||||
{
|
||||
new DebugVSystemChildItem(
|
||||
"Debug SameBoy States",
|
||||
() => ((Sameboy) Emulator).DebugSameBoyState())
|
||||
{
|
||||
RequiresCore = CoreNames.Sameboy,
|
||||
},
|
||||
},
|
||||
},
|
||||
new DebugVSystemMenuItem("GBA")
|
||||
{
|
||||
DropDownItems =
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
<Compile Update="Consoles/Nintendo/NDS/MelonDS.*.cs" DependentUpon="MelonDS.cs" />
|
||||
<Compile Update="Consoles/Nintendo/SubNESHawk/SubNESHawk.*.cs" DependentUpon="SubNESHawk.cs" />
|
||||
<Compile Update="Consoles/Nintendo/QuickNES/QuickNES.*.cs" DependentUpon="QuickNES.cs" />
|
||||
<Compile Update="Consoles/Nintendo/Sameboy/SameBoy.*.cs" DependentUpon="SameBoy.cs" />
|
||||
<Compile Update="Consoles/Nintendo/SNES/LibsnesCore.*.cs" DependentUpon="LibsnesCore.cs" />
|
||||
<Compile Update="Consoles/PC Engine/PCEngine.*.cs" DependentUpon="PCEngine.cs" />
|
||||
<Compile Update="Consoles/Sega/GGHawkLink/GGHawkLink.*.cs" DependentUpon="GGHawkLink.cs" />
|
||||
|
|
|
@ -198,7 +198,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|||
|
||||
_cdCallback = new LibGambatte.CDCallback(CDCallbackProc);
|
||||
|
||||
ControllerDefinition = CreateControllerDefinition(IsSgb, _syncSettings.FrameLength is GambatteSyncSettings.FrameLengthType.UserDefinedFrames);
|
||||
ControllerDefinition = CreateControllerDefinition(sgb: IsSgb, sub: _syncSettings.FrameLength is GambatteSyncSettings.FrameLengthType.UserDefinedFrames, tilt: false);
|
||||
|
||||
NewSaveCoreSetBuff();
|
||||
}
|
||||
|
@ -263,11 +263,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|||
public long CycleCount => (long)_cycleCount;
|
||||
public double ClockRate => TICKSPERSECOND;
|
||||
|
||||
public static ControllerDefinition CreateControllerDefinition(bool sgb, bool sub)
|
||||
public static ControllerDefinition CreateControllerDefinition(bool sgb, bool sub, bool tilt)
|
||||
{
|
||||
var ret = sub
|
||||
? new ControllerDefinition("Subframe Gameboy Controller").AddAxis("Input Length", 0.RangeTo(35112), 35112)
|
||||
: new ControllerDefinition("Gameboy Controller");
|
||||
var ret = new ControllerDefinition((sub ? "Subframe " : "") + "Gameboy Controller" + (tilt ? " + Tilt" : ""));
|
||||
if (sub)
|
||||
{
|
||||
ret.AddAxis("Input Length", 0.RangeTo(35112), 35112);
|
||||
}
|
||||
if (tilt)
|
||||
{
|
||||
ret.AddXYPair($"Tilt {{0}}", AxisPairOrientation.RightAndUp, (-90).RangeTo(90), 0);
|
||||
}
|
||||
if (sgb)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|||
_linkedCores[i] = new Gameboy(lp.Comm, lp.Roms[i].Game, lp.Roms[i].RomData, _settings._linkedSettings[i], _syncSettings._linkedSyncSettings[i], lp.DeterministicEmulationRequested);
|
||||
_linkedCores[i].ConnectInputCallbackSystem(_inputCallbacks);
|
||||
_linkedCores[i].ConnectMemoryCallbackSystem(_memoryCallbacks, i);
|
||||
_linkedConts[i] = new SaveController(Gameboy.CreateControllerDefinition(false, false));
|
||||
_linkedConts[i] = new SaveController(Gameboy.CreateControllerDefinition(sgb: false, sub: false, tilt: false));
|
||||
_linkedBlips[i] = new BlipBuffer(1024);
|
||||
_linkedBlips[i].SetRates(2097152 * 2, 44100);
|
||||
_linkedOverflow[i] = 0;
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
/// <summary>
|
||||
/// static bindings into libsameboy.dll
|
||||
/// </summary>
|
||||
public static class LibSameboy
|
||||
{
|
||||
[Flags]
|
||||
public enum Buttons : uint
|
||||
{
|
||||
RIGHT = 0x01,
|
||||
LEFT = 0x02,
|
||||
UP = 0x04,
|
||||
DOWN = 0x08,
|
||||
A = 0x10,
|
||||
B = 0x20,
|
||||
SELECT = 0x40,
|
||||
START = 0x80,
|
||||
}
|
||||
|
||||
// mirror of GB_direct_access_t
|
||||
public enum MemoryAreas : uint
|
||||
{
|
||||
ROM,
|
||||
RAM,
|
||||
CART_RAM,
|
||||
VRAM,
|
||||
HRAM,
|
||||
IO,
|
||||
BOOTROM,
|
||||
OAM,
|
||||
BGP,
|
||||
OBP,
|
||||
IE,
|
||||
BGPRGB,
|
||||
OBPRGB,
|
||||
}
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern IntPtr sameboy_create(byte[] romdata, int romlength, byte[] biosdata, int bioslength, Sameboy.SameboySyncSettings.GBModel model, bool realtime);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_destroy(IntPtr core);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void SampleCallback(IntPtr core, IntPtr sample);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setsamplecallback(IntPtr core, SampleCallback callback);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void InputCallback();
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setinputcallback(IntPtr core, InputCallback callback);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_frameadvance(IntPtr core, Buttons buttons, ushort x, ushort y, int[] videobuf, bool render, bool border);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_reset(IntPtr core);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool sameboy_iscgbdmg(IntPtr core);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_savesram(IntPtr core, byte[] dest);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_loadsram(IntPtr core, byte[] data, int len);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int sameboy_sramlen(IntPtr core);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_savestate(IntPtr core, byte[] data);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool sameboy_loadstate(IntPtr core, byte[] data, int len);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int sameboy_statelen(IntPtr core);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool sameboy_getmemoryarea(IntPtr core, MemoryAreas which, ref IntPtr data, ref int length);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern byte sameboy_cpuread(IntPtr core, ushort addr);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_cpuwrite(IntPtr core, ushort addr, byte val);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern long sameboy_getcyclecount(IntPtr core);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setcyclecount(IntPtr core, long newcc);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void TraceCallback(ushort pc);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_settracecallback(IntPtr core, TraceCallback callback);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_getregs(IntPtr core, int[] buf);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setreg(IntPtr core, int which, int value);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void MemoryCallback(ushort address);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setmemorycallback(IntPtr core, int which, MemoryCallback callback);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setprintercallback(IntPtr core, PrinterCallback callback);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setpalette(IntPtr core, Sameboy.SameboySettings.GBPaletteType which);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setcolorcorrection(IntPtr core, Sameboy.SameboySettings.ColorCorrectionMode which);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setlighttemperature(IntPtr core, int temperature);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_sethighpassfilter(IntPtr core, Sameboy.SameboySettings.HighPassFilterMode which);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setinterferencevolume(IntPtr core, int volume);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setrtcdivisoroffset(IntPtr core, int offset);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setbgwinenabled(IntPtr core, bool enabled);
|
||||
|
||||
[DllImport("libsameboy", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void sameboy_setobjenabled(IntPtr core, bool enabled);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy : IDebuggable
|
||||
{
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
int[] data = new int[10];
|
||||
LibSameboy.sameboy_getregs(SameboyState, data);
|
||||
|
||||
return new Dictionary<string, RegisterValue>
|
||||
{
|
||||
["PC"] = (ushort)(data[0] & 0xFFFF),
|
||||
["A"] = (byte)(data[1] & 0xFF),
|
||||
["F"] = (byte)(data[2] & 0xFF),
|
||||
["B"] = (byte)(data[3] & 0xFF),
|
||||
["C"] = (byte)(data[4] & 0xFF),
|
||||
["D"] = (byte)(data[5] & 0xFF),
|
||||
["E"] = (byte)(data[6] & 0xFF),
|
||||
["H"] = (byte)(data[7] & 0xFF),
|
||||
["L"] = (byte)(data[8] & 0xFF),
|
||||
["SP"] = (ushort)(data[9] & 0xFFFF),
|
||||
};
|
||||
}
|
||||
|
||||
public void SetCpuRegister(string register, int value)
|
||||
{
|
||||
LibSameboy.sameboy_setreg(SameboyState, register.ToUpper() switch
|
||||
{
|
||||
"PC" => 0,
|
||||
"A" => 1,
|
||||
"F" => 2,
|
||||
"B" => 3,
|
||||
"C" => 4,
|
||||
"D" => 5,
|
||||
"E" => 6,
|
||||
"H" => 7,
|
||||
"L" => 8,
|
||||
"SP" => 9,
|
||||
_ => throw new InvalidOperationException("Invalid Register?"),
|
||||
},
|
||||
value);
|
||||
}
|
||||
|
||||
public bool CanStep(StepType type) => false;
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void Step(StepType type) => throw new NotImplementedException();
|
||||
|
||||
public long TotalExecutedCycles => CycleCount;
|
||||
|
||||
private const string systemBusScope = "System Bus";
|
||||
|
||||
private readonly MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { systemBusScope });
|
||||
|
||||
public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks;
|
||||
|
||||
private LibSameboy.MemoryCallback _readcb;
|
||||
private LibSameboy.MemoryCallback _writecb;
|
||||
private LibSameboy.MemoryCallback _execcb;
|
||||
|
||||
private void InitMemoryCallbacks()
|
||||
{
|
||||
LibSameboy.MemoryCallback CreateCallback(MemoryCallbackFlags flags, Func<bool> getHasCBOfType)
|
||||
{
|
||||
var rawFlags = (uint)flags;
|
||||
return (address) =>
|
||||
{
|
||||
if (getHasCBOfType())
|
||||
{
|
||||
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, systemBusScope);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_readcb = CreateCallback(MemoryCallbackFlags.AccessRead, () => MemoryCallbacks.HasReads);
|
||||
_writecb = CreateCallback(MemoryCallbackFlags.AccessWrite, () => MemoryCallbacks.HasWrites);
|
||||
_execcb = CreateCallback(MemoryCallbackFlags.AccessExecute, () => MemoryCallbacks.HasExecutes);
|
||||
|
||||
_memorycallbacks.ActiveChanged += SetMemoryCallbacks;
|
||||
}
|
||||
|
||||
private void SetMemoryCallbacks()
|
||||
{
|
||||
LibSameboy.sameboy_setmemorycallback(SameboyState, 0, MemoryCallbacks.HasReads ? _readcb : null);
|
||||
LibSameboy.sameboy_setmemorycallback(SameboyState, 1, MemoryCallbacks.HasWrites ? _writecb : null);
|
||||
LibSameboy.sameboy_setmemorycallback(SameboyState, 2, MemoryCallbacks.HasExecutes ? _execcb : null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy : IEmulator
|
||||
{
|
||||
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
|
||||
|
||||
public ControllerDefinition ControllerDefinition { get; }
|
||||
|
||||
private static readonly IReadOnlyList<string> GB_BUTTON_ORDER_IN_BITMASK = new[] { "Start", "Select", "B", "A", "Down", "Up", "Left", "Right", };
|
||||
|
||||
private LibSameboy.Buttons FrameAdvancePrep(IController controller)
|
||||
{
|
||||
uint b = 0;
|
||||
for (var i = 0; i < 8; i++)
|
||||
{
|
||||
b <<= 1;
|
||||
if (controller.IsPressed(GB_BUTTON_ORDER_IN_BITMASK[i])) b |= 1;
|
||||
}
|
||||
|
||||
if (controller.IsPressed("Power"))
|
||||
{
|
||||
LibSameboy.sameboy_reset(SameboyState);
|
||||
}
|
||||
|
||||
IsLagFrame = true;
|
||||
|
||||
LibSameboy.sameboy_settracecallback(SameboyState, Tracer.IsEnabled() ? _tracecb : null);
|
||||
|
||||
return (LibSameboy.Buttons)b;
|
||||
}
|
||||
|
||||
// copy pasting GBHawk here...
|
||||
|
||||
private readonly bool _hasAcc;
|
||||
private double theta, phi, theta_prev, phi_prev, phi_prev_2;
|
||||
|
||||
private ushort GetAccX(IController c)
|
||||
{
|
||||
if (!_hasAcc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
theta_prev = theta;
|
||||
phi_prev_2 = phi_prev;
|
||||
phi_prev = phi;
|
||||
|
||||
theta = c.AxisValue("Tilt Y") * Math.PI / 180.0;
|
||||
phi = c.AxisValue("Tilt X") * Math.PI / 180.0;
|
||||
|
||||
double temp = (double)(Math.Cos(theta) * Math.Sin(phi));
|
||||
|
||||
double temp2 = (double)((phi - 2 * phi_prev + phi_prev_2) * 59.7275 * 59.7275 * 0.1);
|
||||
|
||||
return (ushort)(0x8370 - Math.Floor(temp * 216) - temp2);
|
||||
}
|
||||
|
||||
private ushort GetAccY()
|
||||
{
|
||||
if (!_hasAcc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
double temp = (double)Math.Sin(theta);
|
||||
|
||||
double temp2 = (double)(Math.Pow((theta - theta_prev) * 59.7275, 2) * 0.15);
|
||||
|
||||
return (ushort)(0x8370 - Math.Floor(temp * 216) + temp2);
|
||||
}
|
||||
|
||||
public bool FrameAdvance(IController controller, bool render, bool rendersound)
|
||||
{
|
||||
var buttons = FrameAdvancePrep(controller);
|
||||
|
||||
LibSameboy.sameboy_frameadvance(SameboyState, buttons, GetAccX(controller), GetAccY(), VideoBuffer, render, _settings.ShowBorder);
|
||||
|
||||
if (!rendersound)
|
||||
{
|
||||
DiscardSamples();
|
||||
}
|
||||
|
||||
FrameAdvancePost();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void FrameAdvancePost()
|
||||
{
|
||||
if (IsLagFrame)
|
||||
{
|
||||
LagCount++;
|
||||
}
|
||||
|
||||
Frame++;
|
||||
|
||||
if (_scanlinecbline == -1)
|
||||
{
|
||||
_scanlinecb?.Invoke(LibSameboy.sameboy_cpuread(SameboyState, 0xFF40));
|
||||
}
|
||||
}
|
||||
|
||||
public int Frame { get; private set; } = 0;
|
||||
|
||||
public string SystemId => VSystemID.Raw.GB;
|
||||
|
||||
public bool DeterministicEmulation { get; }
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
Frame = 0;
|
||||
LagCount = 0;
|
||||
IsLagFrame = false;
|
||||
CycleCount = 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (SameboyState != IntPtr.Zero)
|
||||
{
|
||||
LibSameboy.sameboy_destroy(SameboyState);
|
||||
SameboyState = IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy
|
||||
{
|
||||
private readonly List<MemoryDomain> _memoryDomains = new List<MemoryDomain>();
|
||||
|
||||
private IMemoryDomains MemoryDomains { get; set; }
|
||||
|
||||
private void CreateMemoryDomain(LibSameboy.MemoryAreas which, string name)
|
||||
{
|
||||
IntPtr data = IntPtr.Zero;
|
||||
int length = 0;
|
||||
|
||||
if (!LibSameboy.sameboy_getmemoryarea(SameboyState, which, ref data, ref length))
|
||||
{
|
||||
throw new Exception($"{nameof(LibSameboy.sameboy_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(new MemoryDomainIntPtr(name, MemoryDomain.Endian.Little, data, length, true, 1));
|
||||
}
|
||||
}
|
||||
|
||||
private void InitMemoryDomains()
|
||||
{
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.ROM, "ROM");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.RAM, "WRAM");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.CART_RAM, "CartRAM");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.VRAM, "VRAM");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.HRAM, "HRAM");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.IO, "MMIO");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.BOOTROM, "BIOS");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.OAM, "OAM");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.BGP, "BGP");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.OBP, "OBP");
|
||||
CreateMemoryDomain(LibSameboy.MemoryAreas.IE, "IE");
|
||||
|
||||
// also add a special memory domain for the system bus, where calls get sent directly to the core each time
|
||||
_memoryDomains.Add(new MemoryDomainDelegate("System Bus", 65536, MemoryDomain.Endian.Little,
|
||||
delegate(long addr)
|
||||
{
|
||||
if (addr < 0 || addr >= 65536)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
return LibSameboy.sameboy_cpuread(SameboyState, (ushort)addr);
|
||||
},
|
||||
delegate(long addr, byte val)
|
||||
{
|
||||
if (addr < 0 || addr >= 65536)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
LibSameboy.sameboy_cpuwrite(SameboyState, (ushort)addr, val);
|
||||
}, 1));
|
||||
|
||||
MemoryDomains = new MemoryDomainList(_memoryDomains);
|
||||
_serviceProvider.Register(MemoryDomains);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy : ISaveRam
|
||||
{
|
||||
public bool SaveRamModified => LibSameboy.sameboy_sramlen(SameboyState) != 0;
|
||||
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
int length = LibSameboy.sameboy_sramlen(SameboyState);
|
||||
|
||||
if (length > 0)
|
||||
{
|
||||
byte[] ret = new byte[length];
|
||||
LibSameboy.sameboy_savesram(SameboyState, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return Array.Empty<byte>();
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
int expected = LibSameboy.sameboy_sramlen(SameboyState);
|
||||
if (data.Length - expected != 0)
|
||||
{
|
||||
throw new ArgumentException("Size of saveram data does not match expected!");
|
||||
}
|
||||
|
||||
LibSameboy.sameboy_loadsram(SameboyState, data, data.Length);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy : ISettable<Sameboy.SameboySettings, Sameboy.SameboySyncSettings>
|
||||
{
|
||||
private SameboySettings _settings;
|
||||
private SameboySyncSettings _syncSettings;
|
||||
|
||||
public SameboySettings GetSettings() => _settings.Clone();
|
||||
|
||||
public PutSettingsDirtyBits PutSettings(SameboySettings o)
|
||||
{
|
||||
LibSameboy.sameboy_setpalette(SameboyState, o.GBPalette);
|
||||
LibSameboy.sameboy_setcolorcorrection(SameboyState, o.ColorCorrection);
|
||||
LibSameboy.sameboy_setlighttemperature(SameboyState, o.LightTemperature);
|
||||
LibSameboy.sameboy_sethighpassfilter(SameboyState, o.HighPassFilter);
|
||||
LibSameboy.sameboy_setinterferencevolume(SameboyState, o.InterferenceVolume);
|
||||
LibSameboy.sameboy_setbgwinenabled(SameboyState, o.EnableBGWIN);
|
||||
LibSameboy.sameboy_setobjenabled(SameboyState, o.EnableOBJ);
|
||||
_disassembler.UseRGBDSSyntax = o.UseRGBDSSyntax;
|
||||
_settings = o;
|
||||
return PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
public SameboySyncSettings GetSyncSettings() => _syncSettings.Clone();
|
||||
|
||||
public PutSettingsDirtyBits PutSyncSettings(SameboySyncSettings o)
|
||||
{
|
||||
bool ret = SameboySyncSettings.NeedsReboot(_syncSettings, o);
|
||||
_syncSettings = o;
|
||||
return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None;
|
||||
}
|
||||
|
||||
public class SameboySettings
|
||||
{
|
||||
public enum GBPaletteType : uint
|
||||
{
|
||||
[Display(Name = "Greyscale")]
|
||||
GREY,
|
||||
[Display(Name = "Lime (Game Boy)")]
|
||||
DMG,
|
||||
[Display(Name = "Olive (Game Boy Pocket)")]
|
||||
MGB,
|
||||
[Display(Name = "Teal (Game Boy Light)")]
|
||||
GBL,
|
||||
}
|
||||
|
||||
[DisplayName("GB Mono Palette")]
|
||||
[Description("Selects which palette to use in GB mode. Does nothing in GBC mode.")]
|
||||
[DefaultValue(GBPaletteType.GREY)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public GBPaletteType GBPalette { get; set; }
|
||||
|
||||
public enum ColorCorrectionMode : uint
|
||||
{
|
||||
[Display(Name = "Disabled")]
|
||||
DISABLED,
|
||||
[Display(Name = "Correct Color Curves")]
|
||||
CORRECT_CURVES,
|
||||
[Display(Name = "Emulate Hardware")]
|
||||
EMULATE_HARDWARE,
|
||||
[Display(Name = "Preserve Brightness")]
|
||||
PRESERVE_BRIGHTNESS,
|
||||
[Display(Name = "Reduce Contrast")]
|
||||
REDUCE_CONTRAST,
|
||||
[Display(Name = "Harsh Reality")]
|
||||
LOW_CONTRAST,
|
||||
}
|
||||
|
||||
[DisplayName("GBC Color Correction")]
|
||||
[Description("Selects which color correction method to use in GBC mode. Does nothing in GB mode.")]
|
||||
[DefaultValue(ColorCorrectionMode.EMULATE_HARDWARE)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public ColorCorrectionMode ColorCorrection { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private int _lighttemperature;
|
||||
|
||||
[DisplayName("Ambient Light Temperature")]
|
||||
[Description("Simulates an ambient light's effect on non-backlit screens. Does nothing in GB mode.")]
|
||||
[DefaultValue(0)]
|
||||
public int LightTemperature
|
||||
{
|
||||
get => _lighttemperature;
|
||||
set => _lighttemperature = Math.Max(-10, Math.Min(10, value));
|
||||
}
|
||||
|
||||
[DisplayName("Show Border")]
|
||||
[Description("")]
|
||||
[DefaultValue(false)]
|
||||
public bool ShowBorder { get; set; }
|
||||
|
||||
public enum HighPassFilterMode : uint
|
||||
{
|
||||
[Display(Name = "None (Keep DC Offset)")]
|
||||
HIGHPASS_OFF,
|
||||
[Display(Name = "Accurate")]
|
||||
HIGHPASS_ACCURATE,
|
||||
[Display(Name = "Preserve Waveform")]
|
||||
HIGHPASS_REMOVE_DC_OFFSET,
|
||||
}
|
||||
|
||||
[DisplayName("High Pass Filter")]
|
||||
[Description("Selects which high pass filter to use for audio.")]
|
||||
[DefaultValue(HighPassFilterMode.HIGHPASS_ACCURATE)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public HighPassFilterMode HighPassFilter { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private int _interferencevolume;
|
||||
|
||||
[DisplayName("Audio Interference Volume")]
|
||||
[Description("Sets the volume of audio interference.")]
|
||||
[DefaultValue(0)]
|
||||
public int InterferenceVolume
|
||||
{
|
||||
get => _interferencevolume;
|
||||
set => _interferencevolume = Math.Max(0, Math.Min(100, value));
|
||||
}
|
||||
|
||||
[DisplayName("Enable Background/Window")]
|
||||
[Description("")]
|
||||
[DefaultValue(true)]
|
||||
public bool EnableBGWIN { get; set; }
|
||||
|
||||
[DisplayName("Enable Objects")]
|
||||
[Description("")]
|
||||
[DefaultValue(true)]
|
||||
public bool EnableOBJ { get; set; }
|
||||
|
||||
[DisplayName("Use RGBDS Syntax")]
|
||||
[Description("Uses RGBDS syntax for disassembling.")]
|
||||
[DefaultValue(true)]
|
||||
public bool UseRGBDSSyntax { get; set; }
|
||||
|
||||
public SameboySettings() => SettingsUtil.SetDefaultValues(this);
|
||||
|
||||
public SameboySettings Clone() => MemberwiseClone() as SameboySettings;
|
||||
}
|
||||
|
||||
public class SameboySyncSettings
|
||||
{
|
||||
[DisplayName("Use official BIOS")]
|
||||
[Description("When false, SameBoy's internal bios is used. The official bios should be used for TASing.")]
|
||||
[DefaultValue(false)]
|
||||
public bool EnableBIOS { get; set; }
|
||||
|
||||
public enum GBModel : short
|
||||
{
|
||||
Auto = -1,
|
||||
// GB_MODEL_DMG_0 = 0x000,
|
||||
// GB_MODEL_DMG_A = 0x001,
|
||||
[Display(Name = "DMG-B")]
|
||||
GB_MODEL_DMG_B = 0x002,
|
||||
// GB_MODEL_DMG_C = 0x003,
|
||||
[Display(Name = "MGB")]
|
||||
GB_MODEL_MGB = 0x100,
|
||||
[Display(Name = "CGB-0 (Experimental)")]
|
||||
GB_MODEL_CGB_0 = 0x200,
|
||||
[Display(Name = "CGB-A (Experimental)")]
|
||||
GB_MODEL_CGB_A = 0x201,
|
||||
[Display(Name = "CGB-B (Experimental)")]
|
||||
GB_MODEL_CGB_B = 0x202,
|
||||
[Display(Name = "CGB-C (Experimental)")]
|
||||
GB_MODEL_CGB_C = 0x203,
|
||||
[Display(Name = "CGB-D")]
|
||||
GB_MODEL_CGB_D = 0x204,
|
||||
[Display(Name = "CGB-E")]
|
||||
GB_MODEL_CGB_E = 0x205,
|
||||
[Display(Name = "AGB")]
|
||||
GB_MODEL_AGB = 0x206,
|
||||
}
|
||||
|
||||
[DisplayName("Console Mode")]
|
||||
[Description("Pick which console to run, 'Auto' chooses from ROM header. DMG-B, CGB-E, and AGB are the best options for GB, GBC, and GBA, respectively.")]
|
||||
[DefaultValue(GBModel.Auto)]
|
||||
[TypeConverter(typeof(DescribableEnumConverter))]
|
||||
public GBModel ConsoleMode { get; set; }
|
||||
|
||||
[DisplayName("Use Real Time")]
|
||||
[Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")]
|
||||
[DefaultValue(false)]
|
||||
public bool UseRealTime { get; set; }
|
||||
|
||||
[DisplayName("RTC Divisor Offset")]
|
||||
[Description("CPU clock frequency relative to real time clock. Base value is 2^22 Hz. Used in cycle-based RTC to sync on real hardware to account for RTC imperfections.")]
|
||||
[DefaultValue(0)]
|
||||
public int RTCDivisorOffset { get; set; }
|
||||
|
||||
public SameboySyncSettings() => SettingsUtil.SetDefaultValues(this);
|
||||
|
||||
public SameboySyncSettings Clone() => MemberwiseClone() as SameboySyncSettings;
|
||||
|
||||
public static bool NeedsReboot(SameboySyncSettings x, SameboySyncSettings y) => !DeepEquality.DeepEquals(x, y);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy : ISoundProvider
|
||||
{
|
||||
public bool CanProvideAsync => false;
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
_soundoutbuffcontains = 0;
|
||||
}
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
samples = _soundoutbuff;
|
||||
nsamp = _soundoutbuffcontains;
|
||||
DiscardSamples();
|
||||
}
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode == SyncSoundMode.Async)
|
||||
{
|
||||
throw new NotSupportedException("Async mode is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new InvalidOperationException("Async mode is not supported.");
|
||||
}
|
||||
|
||||
private int _soundoutbuffcontains = 0;
|
||||
|
||||
private readonly short[] _soundoutbuff = new short[2048];
|
||||
|
||||
private unsafe void QueueSample(IntPtr core, IntPtr sample)
|
||||
{
|
||||
short* s = (short*)sample;
|
||||
_soundoutbuff[_soundoutbuffcontains * 2] = s[0];
|
||||
_soundoutbuff[_soundoutbuffcontains * 2 + 1] = s[1];
|
||||
_soundoutbuffcontains++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy : IStatable
|
||||
{
|
||||
private readonly byte[] _stateBuf;
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
LibSameboy.sameboy_savestate(SameboyState, _stateBuf);
|
||||
|
||||
writer.Write(_stateBuf.Length);
|
||||
writer.Write(_stateBuf);
|
||||
|
||||
// other variables
|
||||
writer.Write(IsLagFrame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(Frame);
|
||||
writer.Write(IsCgb);
|
||||
writer.Write(CycleCount);
|
||||
writer.Write(theta);
|
||||
writer.Write(phi);
|
||||
writer.Write(theta_prev);
|
||||
writer.Write(phi_prev);
|
||||
writer.Write(phi_prev_2);
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
int length = reader.ReadInt32();
|
||||
if (length != _stateBuf.Length)
|
||||
{
|
||||
throw new InvalidOperationException("Savestate buffer size mismatch!");
|
||||
}
|
||||
|
||||
reader.Read(_stateBuf, 0, _stateBuf.Length);
|
||||
|
||||
if (LibSameboy.sameboy_loadstate(SameboyState, _stateBuf, _stateBuf.Length))
|
||||
{
|
||||
throw new Exception($"{nameof(LibSameboy.sameboy_loadstate)}() returned true");
|
||||
}
|
||||
|
||||
// other variables
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
LagCount = reader.ReadInt32();
|
||||
Frame = reader.ReadInt32();
|
||||
IsCgb = reader.ReadBoolean();
|
||||
CycleCount = reader.ReadInt64();
|
||||
theta = reader.ReadDouble();
|
||||
phi = reader.ReadDouble();
|
||||
theta_prev = reader.ReadDouble();
|
||||
phi_prev = reader.ReadDouble();
|
||||
phi_prev_2 = reader.ReadDouble();
|
||||
}
|
||||
|
||||
public void DebugSameBoyState()
|
||||
{
|
||||
LibSameboy.sameboy_savestate(SameboyState, _stateBuf);
|
||||
Directory.CreateDirectory("sameboy_debug");
|
||||
File.WriteAllBytes($"sameboy_debug/debug_state{Frame}.bin", _stateBuf);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Components.LR35902;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy
|
||||
{
|
||||
private ITraceable Tracer { get; }
|
||||
private readonly LibSameboy.TraceCallback _tracecb;
|
||||
|
||||
private void MakeTrace(ushort pc)
|
||||
{
|
||||
int[] s = new int[10];
|
||||
LibSameboy.sameboy_getregs(SameboyState, s);
|
||||
|
||||
Tracer.Put(new(
|
||||
disassembly: LR35902.Disassemble(
|
||||
pc,
|
||||
addr => LibSameboy.sameboy_cpuread(SameboyState, addr),
|
||||
_settings.UseRGBDSSyntax,
|
||||
out _).PadRight(36),
|
||||
registerInfo: string.Format(
|
||||
"A:{0:x2} F:{1:x2} B:{2:x2} C:{3:x2} D:{4:x2} E:{5:x2} H:{6:x2} L:{7:x2} SP:{8:x4} LY:{9:x2} Cy:{10}",
|
||||
s[1] & 0xFF,
|
||||
s[2] & 0xFF,
|
||||
s[3] & 0xFF,
|
||||
s[4] & 0xFF,
|
||||
s[5] & 0xFF,
|
||||
s[6] & 0xFF,
|
||||
s[7] & 0xFF,
|
||||
s[8] & 0xFF,
|
||||
s[9] & 0xFFFF,
|
||||
LibSameboy.sameboy_cpuread(SameboyState, 0xFF44),
|
||||
CycleCount + 485808
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
public partial class Sameboy : IVideoProvider
|
||||
{
|
||||
private readonly int[] VideoBuffer = CreateVideoBuffer();
|
||||
|
||||
private static int[] CreateVideoBuffer()
|
||||
{
|
||||
var b = new int[256 * 224];
|
||||
for (int i = 0; i < (256 * 224); i++)
|
||||
{
|
||||
b[i] = -1;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return VideoBuffer;
|
||||
}
|
||||
|
||||
public int VirtualWidth => _settings.ShowBorder ? 256 : 160;
|
||||
|
||||
public int VirtualHeight => _settings.ShowBorder ? 224 : 144;
|
||||
|
||||
public int BufferWidth => _settings.ShowBorder ? 256 : 160;
|
||||
|
||||
public int BufferHeight => _settings.ShowBorder ? 224 : 144;
|
||||
|
||||
public int BackgroundColor => 0;
|
||||
|
||||
public int VsyncNumerator => 262144;
|
||||
|
||||
public int VsyncDenominator => 4389;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
|
||||
using BizHawk.Emulation.Cores.Properties;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.Sameboy
|
||||
{
|
||||
/// <summary>
|
||||
/// a gameboy/gameboy color emulator wrapped around native C libsameboy
|
||||
/// </summary>
|
||||
[PortedCore(CoreNames.Sameboy, "LIJI32", "0.14.7", "https://github.com/LIJI32/SameBoy")]
|
||||
[ServiceNotApplicable(new[] { typeof(IDriveLight) })]
|
||||
public partial class Sameboy : ICycleTiming, IInputPollable, ILinkable, IRomInfo, IBoardInfo, IGameboyCommon
|
||||
{
|
||||
private readonly BasicServiceProvider _serviceProvider;
|
||||
|
||||
private readonly Gameboy.GBDisassembler _disassembler;
|
||||
|
||||
private IntPtr SameboyState { get; set; } = IntPtr.Zero;
|
||||
|
||||
public bool IsCgb { get; set; }
|
||||
|
||||
public bool IsCGBMode() => IsCgb;
|
||||
|
||||
public bool IsCGBDMGMode() => LibSameboy.sameboy_iscgbdmg(SameboyState);
|
||||
|
||||
private readonly LibSameboy.SampleCallback _samplecb;
|
||||
private readonly LibSameboy.InputCallback _inputcb;
|
||||
|
||||
[CoreConstructor(VSystemID.Raw.GB)]
|
||||
[CoreConstructor(VSystemID.Raw.GBC)]
|
||||
public Sameboy(CoreComm comm, GameInfo game, byte[] file, SameboySettings settings, SameboySyncSettings syncSettings, bool deterministic)
|
||||
{
|
||||
_serviceProvider = new BasicServiceProvider(this);
|
||||
|
||||
_settings = settings ?? new SameboySettings();
|
||||
_syncSettings = syncSettings ?? new SameboySyncSettings();
|
||||
|
||||
var model = _syncSettings.ConsoleMode;
|
||||
if (model is SameboySyncSettings.GBModel.Auto)
|
||||
{
|
||||
model = game.System == VSystemID.Raw.GBC
|
||||
? SameboySyncSettings.GBModel.GB_MODEL_CGB_E
|
||||
: SameboySyncSettings.GBModel.GB_MODEL_DMG_B;
|
||||
}
|
||||
|
||||
IsCgb = model >= SameboySyncSettings.GBModel.GB_MODEL_CGB_0;
|
||||
|
||||
byte[] bios;
|
||||
if (_syncSettings.EnableBIOS)
|
||||
{
|
||||
FirmwareID fwid = new(
|
||||
IsCgb ? "GBC" : "GB",
|
||||
_syncSettings.ConsoleMode is SameboySyncSettings.GBModel.GB_MODEL_AGB
|
||||
? "AGB"
|
||||
: "World");
|
||||
bios = comm.CoreFileProvider.GetFirmwareOrThrow(fwid, "BIOS Not Found, Cannot Load. Change SyncSettings to run without BIOS.");
|
||||
}
|
||||
else
|
||||
{
|
||||
bios = Util.DecompressGzipFile(new MemoryStream(IsCgb
|
||||
? _syncSettings.ConsoleMode is SameboySyncSettings.GBModel.GB_MODEL_AGB ? Resources.SameboyAgbBoot.Value : Resources.SameboyCgbBoot.Value
|
||||
: Resources.SameboyDmgBoot.Value));
|
||||
}
|
||||
|
||||
DeterministicEmulation = false;
|
||||
|
||||
bool realtime = true;
|
||||
if (!_syncSettings.UseRealTime || deterministic)
|
||||
{
|
||||
realtime = false;
|
||||
DeterministicEmulation = true;
|
||||
}
|
||||
|
||||
SameboyState = LibSameboy.sameboy_create(file, file.Length, bios, bios.Length, model, realtime);
|
||||
|
||||
InitMemoryDomains();
|
||||
InitMemoryCallbacks();
|
||||
|
||||
_samplecb = QueueSample;
|
||||
LibSameboy.sameboy_setsamplecallback(SameboyState, _samplecb);
|
||||
_inputcb = InputCallback;
|
||||
LibSameboy.sameboy_setinputcallback(SameboyState, _inputcb);
|
||||
_tracecb = MakeTrace;
|
||||
LibSameboy.sameboy_settracecallback(SameboyState, null);
|
||||
|
||||
LibSameboy.sameboy_setscanlinecallback(SameboyState, null, 0);
|
||||
LibSameboy.sameboy_setprintercallback(SameboyState, null);
|
||||
|
||||
const string TRACE_HEADER = "SM83: PC, opcode, registers (A, F, B, C, D, E, H, L, SP, LY, CY)";
|
||||
Tracer = new TraceBuffer(TRACE_HEADER);
|
||||
_serviceProvider.Register(Tracer);
|
||||
|
||||
_disassembler = new Gameboy.GBDisassembler();
|
||||
_serviceProvider.Register<IDisassemblable>(_disassembler);
|
||||
|
||||
PutSettings(_settings);
|
||||
|
||||
_stateBuf = new byte[LibSameboy.sameboy_statelen(SameboyState)];
|
||||
|
||||
RomDetails = $"{game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(file)}\r\n{MD5Checksum.ComputePrefixedHex(file)}\r\n";
|
||||
BoardName = MapperName(file);
|
||||
|
||||
_hasAcc = BoardName is "MBC7 ROM+ACCEL+EEPROM";
|
||||
ControllerDefinition = Gameboy.Gameboy.CreateControllerDefinition(sgb: false, sub: false, tilt: _hasAcc);
|
||||
|
||||
LibSameboy.sameboy_setrtcdivisoroffset(SameboyState, _syncSettings.RTCDivisorOffset);
|
||||
CycleCount = 0;
|
||||
}
|
||||
|
||||
public double ClockRate => 2097152;
|
||||
|
||||
public long CycleCount
|
||||
{
|
||||
get => LibSameboy.sameboy_getcyclecount(SameboyState);
|
||||
private set => LibSameboy.sameboy_setcyclecount(SameboyState, value);
|
||||
}
|
||||
|
||||
public int LagCount { get; set; } = 0;
|
||||
|
||||
public bool IsLagFrame { get; set; } = false;
|
||||
|
||||
public IInputCallbackSystem InputCallbacks => _inputCallbacks;
|
||||
|
||||
private readonly InputCallbackSystem _inputCallbacks = new();
|
||||
|
||||
private void InputCallback()
|
||||
{
|
||||
IsLagFrame = false;
|
||||
_inputCallbacks.Call();
|
||||
}
|
||||
|
||||
public bool LinkConnected
|
||||
{
|
||||
get => _printercb != null;
|
||||
set {}
|
||||
}
|
||||
|
||||
public string RomDetails { get; }
|
||||
|
||||
private static string MapperName(byte[] romdata)
|
||||
{
|
||||
return (romdata[0x147]) switch
|
||||
{
|
||||
0x00 => "Plain ROM",
|
||||
0x01 => "MBC1 ROM",
|
||||
0x02 => "MBC1 ROM+RAM",
|
||||
0x03 => "MBC1 ROM+RAM+BATTERY",
|
||||
0x05 => "MBC2 ROM",
|
||||
0x06 => "MBC2 ROM+BATTERY",
|
||||
0x08 => "Plain ROM+RAM",
|
||||
0x09 => "Plain ROM+RAM+BATTERY",
|
||||
0x0F => "MBC3 ROM+TIMER+BATTERY",
|
||||
0x10 => "MBC3 ROM+TIMER+RAM+BATTERY",
|
||||
0x11 => "MBC3 ROM",
|
||||
0x12 => "MBC3 ROM+RAM",
|
||||
0x13 => "MBC3 ROM+RAM+BATTERY",
|
||||
0x19 => "MBC5 ROM",
|
||||
0x1A => "MBC5 ROM+RAM",
|
||||
0x1B => "MBC5 ROM+RAM+BATTERY",
|
||||
0x1C => "MBC5 ROM+RUMBLE",
|
||||
0x1D => "MBC5 ROM+RUMBLE+RAM",
|
||||
0x1E => "MBC5 ROM+RUMBLE+RAM+BATTERY",
|
||||
0x22 => "MBC7 ROM+ACCEL+EEPROM",
|
||||
0xFC => "Pocket Camera ROM+RAM+BATTERY",
|
||||
0xFE => "HuC3 ROM+RAM+BATTERY",
|
||||
0xFF => "HuC1 ROM+RAM+BATTERY",
|
||||
_ => "UNKNOWN",
|
||||
};
|
||||
}
|
||||
|
||||
public string BoardName { get; }
|
||||
|
||||
public IGPUMemoryAreas LockGPU()
|
||||
{
|
||||
var _vram = IntPtr.Zero;
|
||||
var _bgpal = IntPtr.Zero;
|
||||
var _sppal = IntPtr.Zero;
|
||||
var _oam = IntPtr.Zero;
|
||||
int unused = 0;
|
||||
if (!LibSameboy.sameboy_getmemoryarea(SameboyState, LibSameboy.MemoryAreas.VRAM, ref _vram, ref unused)
|
||||
|| !LibSameboy.sameboy_getmemoryarea(SameboyState, LibSameboy.MemoryAreas.BGPRGB, ref _bgpal, ref unused)
|
||||
|| !LibSameboy.sameboy_getmemoryarea(SameboyState, LibSameboy.MemoryAreas.OBPRGB, ref _sppal, ref unused)
|
||||
|| !LibSameboy.sameboy_getmemoryarea(SameboyState, LibSameboy.MemoryAreas.OAM, ref _oam, ref unused))
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected error in sameboy_getmemoryarea");
|
||||
}
|
||||
|
||||
return new GPUMemoryAreas()
|
||||
{
|
||||
Vram = _vram,
|
||||
Oam = _oam,
|
||||
Sppal = _sppal,
|
||||
Bgpal = _bgpal
|
||||
};
|
||||
}
|
||||
|
||||
private class GPUMemoryAreas : IGPUMemoryAreas
|
||||
{
|
||||
public IntPtr Vram { get; init; }
|
||||
|
||||
public IntPtr Oam { get; init; }
|
||||
|
||||
public IntPtr Sppal { get; init; }
|
||||
|
||||
public IntPtr Bgpal { get; init; }
|
||||
|
||||
public void Dispose() {}
|
||||
}
|
||||
|
||||
private ScanlineCallback _scanlinecb;
|
||||
private int _scanlinecbline;
|
||||
|
||||
public void SetScanlineCallback(ScanlineCallback callback, int line)
|
||||
{
|
||||
_scanlinecb = callback;
|
||||
_scanlinecbline = line;
|
||||
|
||||
LibSameboy.sameboy_setscanlinecallback(SameboyState, _scanlinecbline >= 0 ? callback : null, line);
|
||||
|
||||
if (_scanlinecbline == -2)
|
||||
{
|
||||
_scanlinecb(LibSameboy.sameboy_cpuread(SameboyState, 0xFF40));
|
||||
}
|
||||
}
|
||||
|
||||
private PrinterCallback _printercb;
|
||||
|
||||
public void SetPrinterCallback(PrinterCallback callback)
|
||||
{
|
||||
_printercb = callback;
|
||||
LibSameboy.sameboy_setprintercallback(SameboyState, _printercb);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,6 +44,7 @@ namespace BizHawk.Emulation.Cores
|
|||
public const string PceHawk = "PCEHawk";
|
||||
public const string PicoDrive = "PicoDrive";
|
||||
public const string QuickNes = "QuickNes";
|
||||
public const string Sameboy = "SameBoy";
|
||||
public const string Saturnus = "Saturnus";
|
||||
public const string SMSHawk = "SMSHawk";
|
||||
public const string Snes9X = "Snes9x";
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,5 @@
|
|||
.sconsign.dblite
|
||||
BizInterface.os
|
||||
config.log
|
||||
libsameboy.dll.a
|
||||
.sconf_temp
|
|
@ -0,0 +1,499 @@
|
|||
#include "gb.h"
|
||||
#include "stdio.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
IS_DMG = 0,
|
||||
IS_CGB = 1,
|
||||
IS_AGB = 2,
|
||||
RTC_ACCURATE = 4,
|
||||
} LoadFlags;
|
||||
|
||||
typedef void (*input_callback_t)(void);
|
||||
typedef void (*trace_callback_t)(u16);
|
||||
typedef void (*memory_callback_t)(u16);
|
||||
typedef void (*printer_callback_t)(u32*, u8, u8, u8, u8);
|
||||
typedef void (*scanline_callback_t)(u32);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GB_gameboy_t gb;
|
||||
u32 vbuf[256 * 224];
|
||||
u32 bg_pal[0x20];
|
||||
u32 obj_pal[0x20];
|
||||
input_callback_t input_cb;
|
||||
trace_callback_t trace_cb;
|
||||
memory_callback_t read_cb;
|
||||
memory_callback_t write_cb;
|
||||
memory_callback_t exec_cb;
|
||||
printer_callback_t printer_cb;
|
||||
scanline_callback_t scanline_cb;
|
||||
u32 scanline_sl;
|
||||
bool vblank_occured;
|
||||
u64 cc;
|
||||
} biz_t;
|
||||
|
||||
static u8 PeekIO(biz_t* biz, u8 addr)
|
||||
{
|
||||
u8* io = GB_get_direct_access(&biz->gb, GB_DIRECT_ACCESS_IO, NULL, NULL);
|
||||
return io[addr];
|
||||
}
|
||||
|
||||
static u32 rgb_cb(GB_gameboy_t *gb, u8 r, u8 g, u8 b)
|
||||
{
|
||||
return (0xFF << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
|
||||
static void vblank_cb(GB_gameboy_t *gb)
|
||||
{
|
||||
((biz_t*)gb)->vblank_occured = true;
|
||||
}
|
||||
|
||||
static u8 ReadCallbackRelay(GB_gameboy_t* gb, u16 addr, u8 data)
|
||||
{
|
||||
((biz_t*)gb)->read_cb(addr);
|
||||
return data;
|
||||
}
|
||||
|
||||
static bool WriteCallbackRelay(GB_gameboy_t* gb, u16 addr, u8 data)
|
||||
{
|
||||
((biz_t*)gb)->write_cb(addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ExecCallbackRelay(GB_gameboy_t* gb, u16 addr, u8 opcode)
|
||||
{
|
||||
biz_t* biz = (biz_t*)gb;
|
||||
if (biz->trace_cb)
|
||||
{
|
||||
biz->trace_cb(addr);
|
||||
}
|
||||
if (biz->exec_cb)
|
||||
{
|
||||
biz->exec_cb(addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void PrinterCallbackRelay(GB_gameboy_t* gb, u32* image, u8 height, u8 top_margin, u8 bottom_margin, u8 exposure)
|
||||
{
|
||||
((biz_t*)gb)->printer_cb(image, height, top_margin, bottom_margin, exposure);
|
||||
}
|
||||
|
||||
static void ScanlineCallbackRelay(GB_gameboy_t* gb, u8 line)
|
||||
{
|
||||
biz_t* biz = (biz_t*)gb;
|
||||
if (line == biz->scanline_sl)
|
||||
{
|
||||
biz->scanline_cb(PeekIO(biz, GB_IO_LCDC));
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT biz_t* sameboy_create(u8* romdata, u32 romlen, u8* biosdata, u32 bioslen, GB_model_t model, bool realtime)
|
||||
{
|
||||
biz_t* biz = calloc(1, sizeof (biz_t));
|
||||
GB_random_seed(0);
|
||||
GB_init(&biz->gb, model);
|
||||
GB_load_rom_from_buffer(&biz->gb, romdata, romlen);
|
||||
GB_load_boot_rom_from_buffer(&biz->gb, biosdata, bioslen);
|
||||
GB_set_sample_rate(&biz->gb, 44100);
|
||||
GB_set_rgb_encode_callback(&biz->gb, rgb_cb);
|
||||
GB_set_vblank_callback(&biz->gb, vblank_cb);
|
||||
GB_set_rtc_mode(&biz->gb, realtime ? GB_RTC_MODE_SYNC_TO_HOST : GB_RTC_MODE_ACCURATE);
|
||||
GB_set_allow_illegal_inputs(&biz->gb, true);
|
||||
return biz;
|
||||
}
|
||||
|
||||
EXPORT void sameboy_destroy(biz_t* biz)
|
||||
{
|
||||
GB_free(&biz->gb);
|
||||
free(biz);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setsamplecallback(biz_t* biz, GB_sample_callback_t callback)
|
||||
{
|
||||
GB_apu_set_sample_callback(&biz->gb, callback);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setinputcallback(biz_t* biz, input_callback_t callback)
|
||||
{
|
||||
biz->input_cb = callback;
|
||||
}
|
||||
|
||||
static double FromRawToG(u16 raw)
|
||||
{
|
||||
return (raw - 0x81D0) / (0x70 * 1.0);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_frameadvance(biz_t* biz, GB_key_mask_t keys, u16 x, u16 y, u32* vbuf, bool render, bool border)
|
||||
{
|
||||
GB_set_key_mask(&biz->gb, keys);
|
||||
if (GB_has_accelerometer(&biz->gb))
|
||||
{
|
||||
GB_set_accelerometer_values(&biz->gb, FromRawToG(x), FromRawToG(y));
|
||||
}
|
||||
GB_set_pixels_output(&biz->gb, biz->vbuf);
|
||||
GB_set_border_mode(&biz->gb, border ? GB_BORDER_ALWAYS : GB_BORDER_NEVER);
|
||||
GB_set_rendering_disabled(&biz->gb, !render);
|
||||
|
||||
// todo: switch this hack over to joyp_accessed when upstream fixes problems with it
|
||||
if ((PeekIO(biz, GB_IO_JOYP) & 0x30) != 0x30)
|
||||
{
|
||||
biz->input_cb();
|
||||
}
|
||||
|
||||
u32 cycles = 0;
|
||||
biz->vblank_occured = false;
|
||||
do
|
||||
{
|
||||
u8 oldjoyp = PeekIO(biz, GB_IO_JOYP) & 0x30;
|
||||
u32 ret = GB_run(&biz->gb) >> 2;
|
||||
cycles += ret;
|
||||
biz->cc += ret;
|
||||
u8 newjoyp = PeekIO(biz, GB_IO_JOYP) & 0x30;
|
||||
if (oldjoyp != newjoyp && newjoyp != 0x30)
|
||||
{
|
||||
biz->input_cb();
|
||||
}
|
||||
}
|
||||
while (!biz->vblank_occured && cycles < 35112);
|
||||
|
||||
if (biz->vblank_occured && render)
|
||||
{
|
||||
memcpy(vbuf, biz->vbuf, sizeof biz->vbuf);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void sameboy_reset(biz_t* biz)
|
||||
{
|
||||
GB_random_seed(0);
|
||||
GB_reset(&biz->gb);
|
||||
}
|
||||
|
||||
EXPORT bool sameboy_iscgbdmg(biz_t* biz)
|
||||
{
|
||||
return !GB_is_cgb_in_cgb_mode(&biz->gb);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_savesram(biz_t* biz, u8* dest)
|
||||
{
|
||||
GB_save_battery_to_buffer(&biz->gb, dest, GB_save_battery_size(&biz->gb));
|
||||
}
|
||||
|
||||
EXPORT void sameboy_loadsram(biz_t* biz, u8* data, u32 len)
|
||||
{
|
||||
GB_load_battery_from_buffer(&biz->gb, data, len);
|
||||
}
|
||||
|
||||
EXPORT u32 sameboy_sramlen(biz_t* biz)
|
||||
{
|
||||
return GB_save_battery_size(&biz->gb);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_savestate(biz_t* biz, u8* data)
|
||||
{
|
||||
GB_save_state_to_buffer(&biz->gb, data);
|
||||
}
|
||||
|
||||
EXPORT u32 sameboy_loadstate(biz_t* biz, u8* data, u32 len)
|
||||
{
|
||||
return GB_load_state_from_buffer(&biz->gb, data, len);
|
||||
}
|
||||
|
||||
EXPORT u32 sameboy_statelen(biz_t* biz)
|
||||
{
|
||||
return GB_get_save_state_size(&biz->gb);
|
||||
}
|
||||
|
||||
static void UpdatePal(biz_t* biz, bool bg)
|
||||
{
|
||||
u32* pal = bg ? biz->bg_pal : biz->obj_pal;
|
||||
if (GB_is_cgb_in_cgb_mode(&biz->gb))
|
||||
{
|
||||
u16* rawPal = GB_get_direct_access(&biz->gb, bg ? GB_DIRECT_ACCESS_BGP : GB_DIRECT_ACCESS_OBP, NULL, NULL);
|
||||
for (u32 i = 0; i < 0x20; i++)
|
||||
{
|
||||
pal[i] = GB_convert_rgb15(&biz->gb, rawPal[i] & 0x7FFF, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bg)
|
||||
{
|
||||
u32 bgPal[4];
|
||||
if (GB_is_cgb(&biz->gb))
|
||||
{
|
||||
u16* rawPal = GB_get_direct_access(&biz->gb, GB_DIRECT_ACCESS_BGP, NULL, NULL);
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
bgPal[i] = GB_convert_rgb15(&biz->gb, rawPal[i] & 0x7FFF, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const GB_palette_t* rawPal = GB_get_palette(&biz->gb);
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
bgPal[i] = rgb_cb(&biz->gb, rawPal->colors[i].r, rawPal->colors[i].g, rawPal->colors[i].b);
|
||||
}
|
||||
}
|
||||
u8 bgp = PeekIO(biz, GB_IO_BGP);
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
pal[i] = bgPal[(bgp >> (i * 2)) & 3];
|
||||
}
|
||||
for (u32 i = 4; i < 0x20; i++)
|
||||
{
|
||||
pal[i] = GB_convert_rgb15(&biz->gb, 0x7FFF, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 obj0Pal[4];
|
||||
u32 obj1Pal[4];
|
||||
if (GB_is_cgb(&biz->gb))
|
||||
{
|
||||
u16* rawPal = GB_get_direct_access(&biz->gb, GB_DIRECT_ACCESS_OBP, NULL, NULL);
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
obj0Pal[i] = GB_convert_rgb15(&biz->gb, rawPal[i + 0] & 0x7FFF, false);
|
||||
obj1Pal[i] = GB_convert_rgb15(&biz->gb, rawPal[i + 4] & 0x7FFF, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const GB_palette_t* rawPal = GB_get_palette(&biz->gb);
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
obj0Pal[i] = rgb_cb(&biz->gb, rawPal->colors[i].r, rawPal->colors[i].g, rawPal->colors[i].b);
|
||||
obj1Pal[i] = rgb_cb(&biz->gb, rawPal->colors[i].r, rawPal->colors[i].g, rawPal->colors[i].b);
|
||||
}
|
||||
}
|
||||
u8 obp0 = PeekIO(biz, GB_IO_OBP0);
|
||||
u8 obp1 = PeekIO(biz, GB_IO_OBP1);
|
||||
for (u32 i = 0; i < 4; i++)
|
||||
{
|
||||
pal[i + 0] = obj0Pal[(obp0 >> (i * 2)) & 3];
|
||||
pal[i + 4] = obj1Pal[(obp1 >> (i * 2)) & 3];
|
||||
}
|
||||
for (u32 i = 8; i < 0x20; i++)
|
||||
{
|
||||
pal[i] = GB_convert_rgb15(&biz->gb, 0x7FFF, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT bool sameboy_getmemoryarea(biz_t* biz, GB_direct_access_t which, void** data, size_t* len)
|
||||
{
|
||||
if (which == GB_DIRECT_ACCESS_IE + 1)
|
||||
{
|
||||
UpdatePal(biz, true);
|
||||
*data = biz->bg_pal;
|
||||
*len = sizeof biz->bg_pal;
|
||||
return true;
|
||||
}
|
||||
else if (which == GB_DIRECT_ACCESS_IE + 2)
|
||||
{
|
||||
UpdatePal(biz, false);
|
||||
*data = biz->obj_pal;
|
||||
*len = sizeof biz->obj_pal;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (which > GB_DIRECT_ACCESS_IE || which < GB_DIRECT_ACCESS_ROM)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = GB_get_direct_access(&biz->gb, which, len, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
EXPORT u8 sameboy_cpuread(biz_t* biz, u16 addr)
|
||||
{
|
||||
GB_set_read_memory_callback(&biz->gb, NULL);
|
||||
u8 ret = GB_safe_read_memory(&biz->gb, addr);
|
||||
GB_set_read_memory_callback(&biz->gb, biz->read_cb ? ReadCallbackRelay : NULL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT void sameboy_cpuwrite(biz_t* biz, u16 addr, u8 value)
|
||||
{
|
||||
GB_set_write_memory_callback(&biz->gb, NULL);
|
||||
GB_write_memory(&biz->gb, addr, value);
|
||||
GB_set_write_memory_callback(&biz->gb, biz->write_cb ? WriteCallbackRelay : NULL);
|
||||
}
|
||||
|
||||
EXPORT u64 sameboy_getcyclecount(biz_t* biz)
|
||||
{
|
||||
return biz->cc;
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setcyclecount(biz_t* biz, u64 newCc)
|
||||
{
|
||||
biz->cc = newCc;
|
||||
}
|
||||
|
||||
EXPORT void sameboy_settracecallback(biz_t* biz, trace_callback_t callback)
|
||||
{
|
||||
biz->trace_cb = callback;
|
||||
GB_set_execution_callback(&biz->gb, (callback || biz->exec_cb) ? ExecCallbackRelay : NULL);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_getregs(biz_t* biz, u32* buf)
|
||||
{
|
||||
GB_registers_t* regs = GB_get_registers(&biz->gb);
|
||||
buf[0] = regs->pc & 0xFFFF;
|
||||
buf[1] = regs->a & 0xFF;
|
||||
buf[2] = regs->f & 0xFF;
|
||||
buf[3] = regs->b & 0xFF;
|
||||
buf[4] = regs->c & 0xFF;
|
||||
buf[5] = regs->d & 0xFF;
|
||||
buf[6] = regs->e & 0xFF;
|
||||
buf[7] = regs->h & 0xFF;
|
||||
buf[8] = regs->l & 0xFF;
|
||||
buf[9] = regs->sp & 0xFFFF;
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setreg(biz_t* biz, u32 which, u32 value)
|
||||
{
|
||||
GB_registers_t* regs = GB_get_registers(&biz->gb);
|
||||
switch (which)
|
||||
{
|
||||
case 0:
|
||||
regs->pc = value & 0xFFFF;
|
||||
break;
|
||||
case 1:
|
||||
regs->a = value & 0xFF;
|
||||
break;
|
||||
case 2:
|
||||
regs->f = value & 0xFF;
|
||||
break;
|
||||
case 3:
|
||||
regs->b = value & 0xFF;
|
||||
break;
|
||||
case 4:
|
||||
regs->c = value & 0xFF;
|
||||
break;
|
||||
case 5:
|
||||
regs->d = value & 0xFF;
|
||||
break;
|
||||
case 6:
|
||||
regs->e = value & 0xFF;
|
||||
break;
|
||||
case 7:
|
||||
regs->h = value & 0xFF;
|
||||
break;
|
||||
case 8:
|
||||
regs->l = value & 0xFF;
|
||||
break;
|
||||
case 9:
|
||||
regs->sp = value & 0xFFFF;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setmemorycallback(biz_t* biz, u32 which, memory_callback_t callback)
|
||||
{
|
||||
switch (which)
|
||||
{
|
||||
case 0:
|
||||
biz->read_cb = callback;
|
||||
GB_set_read_memory_callback(&biz->gb, callback ? ReadCallbackRelay : NULL);
|
||||
break;
|
||||
case 1:
|
||||
biz->write_cb = callback;
|
||||
GB_set_write_memory_callback(&biz->gb, callback ? WriteCallbackRelay : NULL);
|
||||
break;
|
||||
case 2:
|
||||
biz->exec_cb = callback;
|
||||
GB_set_execution_callback(&biz->gb, (callback || biz->trace_cb) ? ExecCallbackRelay : NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setprintercallback(biz_t* biz, printer_callback_t callback)
|
||||
{
|
||||
biz->printer_cb = callback;
|
||||
if (callback)
|
||||
{
|
||||
GB_connect_printer(&biz->gb, PrinterCallbackRelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
GB_disconnect_serial(&biz->gb);
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setscanlinecallback(biz_t* biz, scanline_callback_t callback, u32 sl)
|
||||
{
|
||||
biz->scanline_cb = callback;
|
||||
biz->scanline_sl = sl;
|
||||
GB_set_lcd_line_callback(&biz->gb, callback ? ScanlineCallbackRelay : NULL);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setpalette(biz_t* biz, u32 which)
|
||||
{
|
||||
switch (which)
|
||||
{
|
||||
case 0:
|
||||
GB_set_palette(&biz->gb, &GB_PALETTE_GREY);
|
||||
break;
|
||||
case 1:
|
||||
GB_set_palette(&biz->gb, &GB_PALETTE_DMG);
|
||||
break;
|
||||
case 2:
|
||||
GB_set_palette(&biz->gb, &GB_PALETTE_MGB);
|
||||
break;
|
||||
case 3:
|
||||
GB_set_palette(&biz->gb, &GB_PALETTE_GBL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setcolorcorrection(biz_t* biz, GB_color_correction_mode_t which)
|
||||
{
|
||||
GB_set_color_correction_mode(&biz->gb, which);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setlighttemperature(biz_t* biz, int temperature)
|
||||
{
|
||||
GB_set_light_temperature(&biz->gb, temperature / 10.0);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_sethighpassfilter(biz_t* biz, GB_highpass_mode_t which)
|
||||
{
|
||||
GB_set_highpass_filter_mode(&biz->gb, which);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setinterferencevolume(biz_t* biz, int volume)
|
||||
{
|
||||
GB_set_interference_volume(&biz->gb, volume / 100.0);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setrtcdivisoroffset(biz_t* biz, int offset)
|
||||
{
|
||||
double base = GB_get_unmultiplied_clock_rate(&biz->gb) * 2.0;
|
||||
GB_set_rtc_multiplier(&biz->gb, (base + offset) / base);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setbgwinenabled(biz_t* biz, bool enabled)
|
||||
{
|
||||
GB_set_background_rendering_disabled(&biz->gb, !enabled);
|
||||
}
|
||||
|
||||
EXPORT void sameboy_setobjenabled(biz_t* biz, bool enabled)
|
||||
{
|
||||
GB_set_object_rendering_disabled(&biz->gb, !enabled);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
global_cflags = ARGUMENTS.get('CFLAGS', '-I./libsameboy/Core -Wall -Wextra -O3 -std=gnu11 -fomit-frame-pointer -flto')
|
||||
global_cflags += ARGUMENTS.get('CFLAGS', ' -Wno-strict-aliasing -Wno-multichar -Wno-implicit-fallthrough -Wno-sign-compare')
|
||||
global_cflags += ARGUMENTS.get('CFLAGS', ' -Wno-unused-parameter -Wno-int-in-bool-context -Wno-missing-field-initializers -Wno-overflow')
|
||||
global_defines = ' -D_GNU_SOURCE -D_USE_MATH_DEFINES -DNDEBUG -DGB_INTERNAL -DGB_DISABLE_DEBUGGER -DGB_DISABLE_CHEATS -DGB_DISABLE_TIMEKEEPING -DGB_DISABLE_REWIND -DGB_VERSION='
|
||||
vars = Variables()
|
||||
vars.Add('CC')
|
||||
|
||||
import os
|
||||
env = Environment(ENV = os.environ,
|
||||
CFLAGS = global_cflags + global_defines,
|
||||
variables = vars)
|
||||
|
||||
sourceFiles = Split('''
|
||||
libsameboy/Core/apu.c
|
||||
libsameboy/Core/random.c
|
||||
libsameboy/Core/camera.c
|
||||
libsameboy/Core/rumble.c
|
||||
libsameboy/Core/save_state.c
|
||||
libsameboy/Core/display.c
|
||||
libsameboy/Core/sgb.c
|
||||
libsameboy/Core/gb.c
|
||||
libsameboy/Core/sm83_cpu.c
|
||||
libsameboy/Core/mbc.c
|
||||
libsameboy/Core/memory.c
|
||||
libsameboy/Core/timing.c
|
||||
libsameboy/Core/printer.c
|
||||
libsameboy/Core/joypad.c
|
||||
''')
|
||||
|
||||
conf = env.Configure()
|
||||
|
||||
conf.Finish()
|
||||
|
||||
shlib = env.SharedLibrary('sameboy', sourceFiles + ['BizInterface.c'],
|
||||
LINKFLAGS = env['LINKFLAGS'] + ' -s -Wno-attributes',
|
||||
SHLIBPREFIX = "lib")
|
||||
|
||||
env.Default(shlib)
|
|
@ -0,0 +1,3 @@
|
|||
scons
|
||||
mv ./libsameboy.dll ../../Assets/dll/
|
||||
mv ./libsameboy.so ../../Assets/dll/
|
|
@ -0,0 +1 @@
|
|||
Subproject commit b7f03dea8dd98fa90ce5d7c7e8e05ff4cee81362
|
Loading…
Reference in New Issue