force usage of bios on gambatte when recording a movie

(except when the rom cant be booted with bios, then enable bios sync setting is respected)
also cleanup this code a lot
This commit is contained in:
CasualPokePlayer 2023-09-11 22:52:48 -07:00
parent ed0dbb77ae
commit d788e603fd
12 changed files with 345 additions and 310 deletions

View File

@ -42,12 +42,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private void CDCallbackProc(int addr, LibGambatte.CDLog_AddrType addrtype, LibGambatte.CDLog_Flags flags)
{
if (_cdl == null || !_cdl.Active)
if (_cdl is not { Active: true })
{
return;
}
string key = addrtype switch
var key = addrtype switch
{
LibGambatte.CDLog_AddrType.ROM => "ROM",
LibGambatte.CDLog_AddrType.HRAM => "HRAM",

View File

@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
int[] data = new int[10];
var data = new int[10];
LibGambatte.gambatte_getregs(GambatteState, data);
return new Dictionary<string, RegisterValue>
@ -38,14 +38,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
if (register.Length == 9 && register.Substring(4, 5).ToUpperInvariant() == " BANK")
{
LibGambatte.BankType type = (LibGambatte.BankType)Enum.Parse(typeof(LibGambatte.BankType), register.Substring(0, 4).ToUpperInvariant());
var type = (LibGambatte.BankType)Enum.Parse(typeof(LibGambatte.BankType), register.Substring(0, 4).ToUpperInvariant());
LibGambatte.gambatte_setbank(GambatteState, type, value);
}
else
{
int[] data = new int[10];
var data = new int[10];
LibGambatte.gambatte_getregs(GambatteState, data);
LibGambatte.RegIndices index = (LibGambatte.RegIndices)Enum.Parse(typeof(LibGambatte.RegIndices), register.ToUpperInvariant());
var index = (LibGambatte.RegIndices)Enum.Parse(typeof(LibGambatte.RegIndices), register.ToUpperInvariant());
data[(int)index] = value & (index <= LibGambatte.RegIndices.SP ? 0xffff : 0xff);
LibGambatte.gambatte_setregs(GambatteState, data);
}
@ -58,7 +58,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public long TotalExecutedCycles => Math.Max((long)_cycleCount, (long)callbackCycleCount);
private MemoryCallbackSystem _memorycallbacks = new MemoryCallbackSystem(new[] { "System Bus", "ROM", "VRAM", "SRAM", "WRAM", "OAM", "HRAM" });
private MemoryCallbackSystem _memorycallbacks = new(new[] { "System Bus", "ROM", "VRAM", "SRAM", "WRAM", "OAM", "HRAM" });
public IMemoryCallbackSystem MemoryCallbacks => _memorycallbacks;
private LibGambatte.MemoryCallback _readcb;
@ -87,75 +87,80 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "System Bus");
var bank = LibGambatte.gambatte_getaddrbank(GambatteState, (ushort)address);
if (address < 0x4000u) // usually rom bank 0 for most mbcs, some mbcs might have this at a different rom bank
switch (address)
{
address += (uint)(bank * 0x4000);
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "ROM");
}
else if (address < 0x8000u) // rom bank x
{
address += (uint)(bank * 0x4000);
address -= 0x4000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "ROM");
}
else if (address < 0xA000u) // vram (may be banked on CGB in CGB enhanced mode)
{
if (IsCGBMode && !IsCGBDMGMode)
// usually rom bank 0 for most mbcs, some mbcs might have this at a different rom bank
case < 0x4000u:
address += (uint)(bank * 0x4000);
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "ROM");
break;
// rom bank x
case < 0x8000u:
address += (uint)(bank * 0x4000);
address -= 0x4000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "ROM");
break;
// vram (may be banked on CGB in CGB enhanced mode)
case < 0xA000u:
{
if (IsCGBMode && !IsCGBDMGMode)
{
address += (uint)(bank * 0x2000);
}
address -= 0x8000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "VRAM");
break;
}
// sram (may be banked)
case < 0xC000u:
address += (uint)(bank * 0x2000);
}
address -= 0x8000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "VRAM");
}
else if (address < 0xC000u) // sram (may be banked)
{
address += (uint)(bank * 0x2000);
address -= 0xA000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "SRAM");
}
else if (address < 0xD000u) // wram bank 0
{
address -= 0xC000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "WRAM");
}
else if (address < 0xE000u) // wram bank x (always one for dmg/cgb in dmg mode)
{
if (IsCGBMode && !IsCGBDMGMode)
address -= 0xA000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "SRAM");
break;
// wram bank 0
case < 0xD000u:
address -= 0xC000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "WRAM");
break;
// wram bank x (always one for dmg/cgb in dmg mode)
case < 0xE000u:
{
address += (uint)(bank * 0x1000);
if (IsCGBMode && !IsCGBDMGMode)
{
address += (uint)(bank * 0x1000);
}
address -= 0xD000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "WRAM");
break;
}
address -= 0xD000u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "WRAM");
}
else if (address < 0xFE00u) // echo ram
{
// do we do something here?
}
else if (address < 0xFEA0u) // oam
{
address -= 0xFE00u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "OAM");
}
else if (address < 0xFF00u) // "extra" oam
{
// do we do something here?
}
else if (address < 0xFF80u) // mmio
{
// do we do something here?
}
else if (address < 0xFFFF) // hram
{
address -= 0xFF80u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "HRAM");
}
else if (address == 0xFFFF) // ie reg
{
// do we do something here?
}
else
{
throw new InvalidOperationException("Core accessed invalid address???");
// echo ram
case < 0xFE00u:
// do we do something here?
break;
// oam
case < 0xFEA0u:
address -= 0xFE00u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "OAM");
break;
// "extra" oam
case < 0xFF00u:
// do we do something here?
break;
// mmio
case < 0xFF80u:
// do we do something here?
break;
// hram
case < 0xFFFF:
address -= 0xFF80u;
MemoryCallbacks.CallMemoryCallbacks(address, 0, rawFlags, which + "HRAM");
break;
// ie reg
case 0xFFFF:
// do we do something here?
break;
default:
throw new InvalidOperationException("Core accessed invalid address???");
}
}
};

View File

@ -7,7 +7,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
public partial class Gameboy : IEmulator, IBoardInfo
{
public IEmulatorServiceProvider ServiceProvider { get; }
private readonly BasicServiceProvider _serviceProvider;
public IEmulatorServiceProvider ServiceProvider => _serviceProvider;
public ControllerDefinition ControllerDefinition { get; }
@ -120,11 +121,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
}
break;
default:
throw new InvalidOperationException();
}
if (IsSgb)
{
ProcessSgbSound((int)samplesEmittedInFrame, rendersound && !Muted);
ProcessSgbSound(rendersound && !Muted);
}
ProcessMbcSound(rendersound && !Muted);

View File

@ -9,7 +9,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public bool LinkConnected
{
get => _linkConnected;
set { return; }
set { }
}
}
}

View File

@ -7,17 +7,17 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
public partial class Gameboy
{
private readonly List<MemoryDomain> _memoryDomains = new List<MemoryDomain>();
private readonly List<MemoryDomain> _memoryDomains = new();
internal IMemoryDomains MemoryDomains { get; private set; }
private void CreateMemoryDomain(LibGambatte.MemoryAreas which, string name)
{
IntPtr data = IntPtr.Zero;
int length = 0;
var data = IntPtr.Zero;
var length = 0;
if (!LibGambatte.gambatte_getmemoryarea(GambatteState, which, ref data, ref length))
{
throw new Exception($"{nameof(LibGambatte.gambatte_getmemoryarea)}() failed!");
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_getmemoryarea)}() failed!");
}
// if length == 0, it's an empty block; (usually rambank on some carts); that's ok
@ -51,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
CreateMemoryDomain(LibGambatte.MemoryAreas.cartram, "CartRAM");
MemoryDomains = new MemoryDomainList(_memoryDomains);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
_serviceProvider.Register(MemoryDomains);
}
}
}

View File

@ -6,43 +6,33 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
public partial class Gameboy : ISaveRam
{
public bool SaveRamModified
{
get
{
if (LibGambatte.gambatte_getsavedatalength(GambatteState) == 0)
{
return false;
}
return true; // need to wire more stuff into the core to actually know this
}
}
// need to wire more stuff into the core to actually know this
public bool SaveRamModified => LibGambatte.gambatte_getsavedatalength(GambatteState) != 0;
public byte[] CloneSaveRam()
{
int length = LibGambatte.gambatte_getsavedatalength(GambatteState);
var length = LibGambatte.gambatte_getsavedatalength(GambatteState);
if (length > 0)
{
byte[] ret = new byte[length];
var ret = new byte[length];
LibGambatte.gambatte_savesavedata(GambatteState, ret);
return ret;
}
return new byte[0];
return null;
}
public void StoreSaveRam(byte[] data)
{
int expected = LibGambatte.gambatte_getsavedatalength(GambatteState);
var expected = LibGambatte.gambatte_getsavedatalength(GambatteState);
if (data.Length != expected) throw new ArgumentException(message: "Size of saveram data does not match expected!", paramName: nameof(data));
LibGambatte.gambatte_loadsavedata(GambatteState, data);
if (DeterministicEmulation)
{
ulong dividers = _syncSettings.InitialTime * (0x400000UL + (ulong)_syncSettings.RTCDivisorOffset) / 2UL;
var dividers = _syncSettings.InitialTime * (0x400000UL + (ulong)_syncSettings.RTCDivisorOffset) / 2UL;
LibGambatte.gambatte_settime(GambatteState, dividers);
}
}

View File

@ -9,9 +9,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public bool CanProvideAsync => false;
public void DiscardSamples()
{
_soundoutbuffcontains = 0;
}
=> _soundoutbuffcontains = 0;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
@ -30,9 +28,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
=> throw new InvalidOperationException("Async mode is not supported.");
internal bool Muted => _settings.Muted;
@ -41,18 +37,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private readonly short[] _sgbsoundbuff = new short[2048 * 2];
private readonly short[] _mbcsoundbuff = new short[(35112 + 2064) * 2];
private int _soundoutbuffcontains = 0;
private int _soundoutbuffcontains;
private readonly short[] _soundoutbuff = new short[2048];
private int _latchL = 0;
private int _latchR = 0;
private int _sgbLatchL = 0;
private int _sgbLatchR = 0;
private int _mbcLatchL = 0;
private int _mbcLatchR = 0;
private int _latchL, _latchR;
private int _sgbLatchL, _sgbLatchR;
private int _mbcLatchL, _mbcLatchR;
private BlipBuffer _blipL, _blipR;
private uint _blipAccumulate;
@ -65,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (curr != _latchL)
{
int diff = _latchL - curr;
var diff = _latchL - curr;
_latchL = curr;
_blipL.AddDelta(_blipAccumulate, diff >> 1);
}
@ -74,7 +65,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (curr != _latchR)
{
int diff = _latchR - curr;
var diff = _latchR - curr;
_latchR = curr;
_blipR.AddDelta(_blipAccumulate, diff >> 1);
}
@ -83,18 +74,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
}
private void ProcessSgbSound(int nsamp, bool processSound)
private void ProcessSgbSound(bool processSound)
{
int remainder = LibGambatte.gambatte_generatesgbsamples(GambatteState, _sgbsoundbuff, out uint samples);
var remainder = LibGambatte.gambatte_generatesgbsamples(GambatteState, _sgbsoundbuff, out var samples);
if (remainder < 0)
{
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_generatesgbsamples)}() returned negative (spc error???)");
}
uint t = 65 - (uint)remainder;
for (int i = 0; i < samples; i++, t += 65)
var t = 65 - (uint)remainder;
for (var i = 0; i < samples; i++, t += 65)
{
int ls = _sgbsoundbuff[i * 2] - _sgbLatchL;
int rs = _sgbsoundbuff[(i * 2) + 1] - _sgbLatchR;
var ls = _sgbsoundbuff[i * 2] - _sgbLatchL;
var rs = _sgbsoundbuff[i * 2 + 1] - _sgbLatchR;
if (ls != 0 && processSound)
{
_blipL.AddDelta(t, ls);
@ -104,13 +95,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
_blipR.AddDelta(t, rs);
}
_sgbLatchL = _sgbsoundbuff[i * 2];
_sgbLatchR = _sgbsoundbuff[(i * 2) + 1];
_sgbLatchR = _sgbsoundbuff[i * 2 + 1];
}
}
private void ProcessMbcSound(bool processSound)
{
int nsamp = LibGambatte.gambatte_generatembcsamples(GambatteState, _mbcsoundbuff);
var nsamp = LibGambatte.gambatte_generatembcsamples(GambatteState, _mbcsoundbuff);
if (nsamp == 0)
{
return;
@ -118,8 +109,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
for (uint i = 0; i < nsamp; i++)
{
int ls = _mbcsoundbuff[i * 2] - _mbcLatchL;
int rs = _mbcsoundbuff[(i * 2) + 1] - _mbcLatchR;
var ls = _mbcsoundbuff[i * 2] - _mbcLatchL;
var rs = _mbcsoundbuff[i * 2 + 1] - _mbcLatchR;
if (ls != 0 && processSound)
{
_blipL.AddDelta(i, ls);
@ -151,9 +142,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private void InitSound()
{
_blipL = new BlipBuffer(1024);
_blipL = new(1024);
_blipL.SetRates(TICKSPERSECOND, 44100);
_blipR = new BlipBuffer(1024);
_blipR = new(1024);
_blipR.SetRates(TICKSPERSECOND, 44100);
}

View File

@ -16,12 +16,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public void SaveStateText(TextWriter writer)
{
var s = SaveState();
ser.Serialize(writer, s);
_ser.Serialize(writer, s);
}
public void LoadStateText(TextReader reader)
{
var s = (TextState<TextStateData>)ser.Deserialize(reader, typeof(TextState<TextStateData>));
var s = (TextState<TextStateData>)_ser.Deserialize(reader, typeof(TextState<TextStateData>));
LoadState(s);
reader.ReadToEnd();
}
@ -37,7 +37,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
#else
if (!LibGambatte.gambatte_newstatesave(GambatteState, _stateBuf, _stateBuf.Length))
{
throw new Exception($"{nameof(LibGambatte.gambatte_newstatesave)}() returned false");
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_newstatesave)}() returned false");
}
#endif
@ -56,7 +56,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public void LoadStateBinary(BinaryReader reader)
{
int length = reader.ReadInt32();
var length = reader.ReadInt32();
if (length != _stateBuf.Length)
{
throw new InvalidOperationException("Savestate buffer size mismatch!");
@ -67,12 +67,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
#if USE_UPSTREAM_STATES
if (!LibGambatte.gambatte_loadstate(GambatteState, _stateBuf, _stateBuf.Length))
{
throw new Exception($"{nameof(LibGambatte.gambatte_loadstate)}() returned false");
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadstate)}() returned false");
}
#else
if (!LibGambatte.gambatte_newstateload(GambatteState, _stateBuf, _stateBuf.Length))
{
throw new Exception($"{nameof(LibGambatte.gambatte_newstateload)}() returned false");
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_newstateload)}() returned false");
}
#endif
@ -89,15 +89,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private byte[] _stateBuf;
private void NewSaveCoreSetBuff()
{
#if USE_UPSTREAM_STATES
_stateBuf = new byte[LibGambatte.gambatte_savestate(GambatteState, null, 160, null)];
=> _stateBuf = new byte[LibGambatte.gambatte_savestate(GambatteState, null, 160, null)];
#else
_stateBuf = new byte[LibGambatte.gambatte_newstatelen(GambatteState)];
=> _stateBuf = new byte[LibGambatte.gambatte_newstatelen(GambatteState)];
#endif
}
private readonly JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented };
private readonly JsonSerializer _ser = new() { Formatting = Formatting.Indented };
// other data in the text state besides core
internal class TextStateData

View File

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.LR35902;
@ -12,32 +13,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private void MakeTrace(IntPtr _s)
{
int[] s = new int[14];
System.Runtime.InteropServices.Marshal.Copy(_s, s, 0, 14);
ushort PC = (ushort)s[1];
var s = new int[14];
Marshal.Copy(_s, s, 0, 14);
var PC = (ushort)s[1];
Tracer.Put(new(
disassembly: LR35902.Disassemble(
PC,
addr => {
addr =>
{
if (addr == PC)
{
//opcode
return (byte)((s[12] >> 16) & 0xFF);
}
else
if (addr == ((PC + 1) & 0xFFFF))
{
if (addr == ((PC + 1) & 0xFFFF))
{
//high operand
return (byte)((s[12] >> 8) & 0xFF);
}
else
{
//low operand
return (byte)(s[12] & 0xFF);
}
//high operand
return (byte)((s[12] >> 8) & 0xFF);
}
//low operand
return (byte)(s[12] & 0xFF);
},
_settings.RgbdsSyntax,
out _).PadRight(36),

View File

@ -22,7 +22,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private static int[] CreateVideoBuffer()
{
var b = new int[160 * 144];
for (int i = 0; i < (160 * 144); i++)
for (var i = 0; i < (160 * 144); i++)
{
b[i] = -1; // GB/C screen is disabled on bootup, so it always starts as white, not black
}
@ -30,17 +30,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
public int[] GetVideoBuffer()
{
return (IsSgb && _settings.ShowBorder) ? SgbVideoBuffer : VideoBuffer;
}
=> IsSgb && _settings.ShowBorder ? SgbVideoBuffer : VideoBuffer;
public int VirtualWidth => (IsSgb && _settings.ShowBorder) ? 256 : 160;
public int VirtualWidth => IsSgb && _settings.ShowBorder ? 256 : 160;
public int VirtualHeight => (IsSgb && _settings.ShowBorder) ? 224 : 144;
public int VirtualHeight => IsSgb && _settings.ShowBorder ? 224 : 144;
public int BufferWidth => (IsSgb && _settings.ShowBorder) ? 256 : 160;
public int BufferWidth => IsSgb && _settings.ShowBorder ? 256 : 160;
public int BufferHeight => (IsSgb && _settings.ShowBorder) ? 224 : 144;
public int BufferHeight => IsSgb && _settings.ShowBorder ? 224 : 144;
public int BackgroundColor => 0;

View File

@ -20,14 +20,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
[CoreConstructor(VSystemID.Raw.GB)]
[CoreConstructor(VSystemID.Raw.GBC)]
[CoreConstructor(VSystemID.Raw.SGB)]
public Gameboy(CoreComm comm, GameInfo game, byte[] file, GambatteSettings settings, GambatteSyncSettings syncSettings, bool deterministic)
public Gameboy(CoreComm comm, IGameInfo game, byte[] file, GambatteSettings settings, GambatteSyncSettings syncSettings, bool deterministic)
{
var ser = new BasicServiceProvider(this);
ser.Register<IDisassemblable>(_disassembler);
ServiceProvider = ser;
_serviceProvider = new(this);
_serviceProvider.Register<IDisassemblable>(_disassembler);
const string TRACE_HEADER = "LR35902: PC, opcode, registers (A, F, B, C, D, E, H, L, LY, SP, CY)";
Tracer = new TraceBuffer(TRACE_HEADER);
ser.Register<ITraceable>(Tracer);
_serviceProvider.Register(Tracer);
InitMemoryCallbacks();
DeterministicEmulation = deterministic;
@ -45,7 +44,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
_syncSettings = syncSettings ?? new GambatteSyncSettings();
LibGambatte.LoadFlags flags = LibGambatte.LoadFlags.READONLY_SAV;
var flags = LibGambatte.LoadFlags.READONLY_SAV;
switch (_syncSettings.ConsoleMode)
{
@ -57,10 +56,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
case GambatteSyncSettings.ConsoleModeType.GBA:
flags |= LibGambatte.LoadFlags.CGB_MODE | LibGambatte.LoadFlags.GBA_FLAG;
break;
default:
case GambatteSyncSettings.ConsoleModeType.Auto:
if (game.System == VSystemID.Raw.GBC)
flags |= LibGambatte.LoadFlags.CGB_MODE;
break;
default:
throw new InvalidOperationException();
}
if (game.System == VSystemID.Raw.SGB)
@ -71,7 +72,68 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
IsCgb = (flags & LibGambatte.LoadFlags.CGB_MODE) == LibGambatte.LoadFlags.CGB_MODE;
if (_syncSettings.EnableBIOS)
bool ForceBios()
{
// if we're not recording a movie, we don't need to force a bios
if (!DeterministicEmulation)
{
return false;
}
// if this rom can't be booted with a bios, don't force one
// SGB bios on the GB side technically doesn't care at all here and will always boot the rom
// (there are checks on the SNES side but Gambatte's HLE doesn't bother with that)
if (IsSgb)
{
return true;
}
// we need the rom loaded in for this; we'll reload it for real later on with proper flags
if (LibGambatte.gambatte_loadbuf(GambatteState, file, (uint)file.Length, flags) != 0)
{
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbuf)}() returned non-zero (is this not a gb or gbc rom?)");
}
// header checksum must pass for bios to boot the ROM
var unused = new byte[32];
LibGambatte.gambatte_pakinfo(GambatteState, unused, out _, out _, out _, out var hcok);
if (hcok == 0)
{
return false;
}
// CGB bios checks top half of nintendo logo, DMG bios checks entire logo
// TODO: this check should probably be moved into the C++ side (in pakinfo)
const int logoStart = 0x104;
var logoEnd = 0x134;
if (IsCgb)
{
logoEnd -= (logoEnd - logoStart) / 2;
}
var nintendoLogo = new byte[]
{
0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D,
0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E, 0xDC, 0xCC, 0x6E, 0xE6, 0xDD, 0xDD, 0xD9, 0x99,
0xBB, 0xBB, 0x67, 0x63, 0x6E, 0x0E, 0xEC, 0xCC, 0xDD, 0xDC, 0x99, 0x9F, 0xBB, 0xB9, 0x33, 0x3E
};
for (var i = logoStart; i < logoEnd; i++)
{
if (nintendoLogo[i - logoStart] != LibGambatte.gambatte_cpuread(GambatteState, (ushort)i))
{
return false;
}
}
// if we get here, the rom can boot with a bios
return true;
}
var useBios = _syncSettings.EnableBIOS || ForceBios();
if (useBios)
{
FirmwareID fwid = new(
IsCgb ? "GBC" : "GB",
@ -80,7 +142,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
: _syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA
? "AGB"
: "World");
var bios = comm.CoreFileProvider.GetFirmwareOrThrow(fwid, "BIOS Not Found, Cannot Load. Change SyncSettings to run without BIOS."); // https://github.com/TASEmulators/BizHawk/issues/2832 tho
var bios = comm.CoreFileProvider.GetFirmwareOrThrow(fwid, "BIOS Not Found, cannot Load. Change GB Settings to run without BIOS.");
if (LibGambatte.gambatte_loadbiosbuf(GambatteState, bios, (uint)bios.Length) != 0)
{
throw new InvalidOperationException($"{nameof(LibGambatte.gambatte_loadbiosbuf)}() returned non-zero (bios error)");
@ -88,11 +150,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
else
{
if (DeterministicEmulation) // throw a warning if a movie is being recorded with the bios disabled
{
comm.ShowMessage("Detected disabled BIOS during movie recording. It is recommended to use a BIOS for movie recording. Change Sync Settings to run with a BIOS.");
}
flags |= LibGambatte.LoadFlags.NO_BIOS;
}
@ -123,31 +180,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
LagCount = 0;
IsLagFrame = false;
InputCallback = new LibGambatte.InputGetter(ControllerCallback);
InputCallback = ControllerCallback;
LibGambatte.gambatte_setinputgetter(GambatteState, InputCallback, IntPtr.Zero);
if (_syncSettings.EnableRemote)
{
RemoteCallback = new LibGambatte.RemoteCallback(RemoteInputCallback);
RemoteCallback = RemoteInputCallback;
LibGambatte.gambatte_setremotecallback(GambatteState, RemoteCallback);
}
InitMemoryDomains();
byte[] mbcBuf = new byte[32];
uint rambanks = 0;
uint rombanks = 0;
uint crc = 0;
uint headerchecksumok = 0;
LibGambatte.gambatte_pakinfo(GambatteState, mbcBuf, ref rambanks, ref rombanks, ref crc, ref headerchecksumok);
var mbcBuf = new byte[32];
LibGambatte.gambatte_pakinfo(GambatteState, mbcBuf, out var rambanks, out var rombanks, out var crc, out var headerchecksumok);
byte[] romNameBuf = new byte[32];
var romNameBuf = new byte[32];
LibGambatte.gambatte_romtitle(GambatteState, romNameBuf);
string romname = Encoding.ASCII.GetString(romNameBuf);
var romname = Encoding.ASCII.GetString(romNameBuf).TrimEnd();
RomDetails = $"{game.Name}\r\n{SHA1Checksum.ComputePrefixedHex(file)}\r\n{MD5Checksum.ComputePrefixedHex(file)}\r\n\r\n";
BoardName = Encoding.ASCII.GetString(mbcBuf);
BoardName = Encoding.ASCII.GetString(mbcBuf).TrimEnd();
RomDetails += $"Core reported Header Name: {romname}\r\n";
RomDetails += $"Core reported RAM Banks: {rambanks}\r\n";
@ -155,16 +208,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
RomDetails += $"Core reported CRC32: {crc:X8}\r\n";
RomDetails += $"Core reported Header Checksum Status: {(headerchecksumok != 0 ? "OK" : "BAD")}\r\n";
if (_syncSettings.EnableBIOS && headerchecksumok == 0)
if (useBios && headerchecksumok == 0)
{
comm.ShowMessage("Core reports the header checksum is bad. This ROM will not boot with the official BIOS.\n" +
"Disable BIOS in sync settings to boot this game");
"Disable BIOS in GB settings to boot this game");
}
if (!_syncSettings.EnableBIOS && IsCGBMode && IsCGBDMGMode) //TODO doesn't IsCGBDMGMode imply IsCGBMode?
{
// without a bios, we need to set the palette for cgbdmg ourselves
int[] cgbDmgColors = new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 };
var cgbDmgColors = new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 };
if (file[0x14B] == 0x01 || (file[0x14B] == 0x33 && file[0x144] == '0' && file[0x145] == '1')) // Nintendo licencees get special palettes
{
cgbDmgColors = ColorsFromTitleHash(file);
@ -172,14 +225,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
ChangeDMGColors(cgbDmgColors);
}
if (!DeterministicEmulation && _syncSettings.RealTimeRTC)
{
LibGambatte.gambatte_settimemode(GambatteState, false);
}
LibGambatte.gambatte_settimemode(GambatteState, DeterministicEmulation || !_syncSettings.RealTimeRTC);
if (DeterministicEmulation)
{
ulong dividers = _syncSettings.InitialTime * (0x400000UL + (ulong)_syncSettings.RTCDivisorOffset) / 2UL;
var dividers = _syncSettings.InitialTime * (0x400000UL + (ulong)_syncSettings.RTCDivisorOffset) / 2UL;
LibGambatte.gambatte_settime(GambatteState, dividers);
}
@ -187,7 +237,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
LibGambatte.gambatte_setcartbuspulluptime(GambatteState, (uint)_syncSettings.CartBusPullUpTime);
_cdCallback = new LibGambatte.CDCallback(CDCallbackProc);
_cdCallback = CDCallbackProc;
ControllerDefinition = CreateControllerDefinition(
sgb: IsSgb,
@ -242,12 +292,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <summary>
/// remote command to send
/// </summary>
private byte RemoteCommand = 0;
private byte RemoteCommand;
/// <summary>
/// internal gambatte state
/// </summary>
internal IntPtr GambatteState { get; private set; } = IntPtr.Zero;
internal IntPtr GambatteState { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
@ -259,13 +309,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <summary>
/// total cycles actually executed
/// </summary>
private ulong _cycleCount = 0;
private ulong callbackCycleCount = 0;
private ulong _cycleCount;
private ulong callbackCycleCount;
/// <summary>
/// number of extra cycles we overran in the last frame
/// </summary>
private uint frameOverflow = 0;
private uint frameOverflow;
public long CycleCount => (long)_cycleCount;
public double ClockRate => TICKSPERSECOND;
@ -290,8 +340,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
if (sgb)
{
for (int i = 0; i < 4; i++)
for (var i = 0; i < 4; i++)
{
// ReSharper disable once AccessToModifiedClosure
ret.BoolButtons.AddRange(
new[] { "Up", "Down", "Left", "Right", "Start", "Select", "B", "A" }
.Select(s => $"P{i + 1} {s}"));
@ -309,10 +360,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
InputCallbacks.Call();
IsLagFrame = false;
if (IsSgb)
{
int index = LibGambatte.gambatte_getjoypadindex(GambatteState);
uint b = (uint)CurrentButtons;
var index = LibGambatte.gambatte_getjoypadindex(GambatteState);
var b = (uint)CurrentButtons;
b >>= index * 8;
b &= 0xFF;
if ((b & 0x30) == 0x30) // snes software side blocks l+r
@ -323,12 +375,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
b &= ~0xC0u;
}
return (LibGambatte.Buttons)b;
}
else
{
return CurrentButtons;
}
return CurrentButtons;
}
private byte RemoteInputCallback()
@ -348,7 +399,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public bool IsCGBDMGMode
=> LibGambatte.gambatte_iscgbdmg(GambatteState);
private InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
private InputCallbackSystem _inputCallbacks = new();
// low priority TODO: due to certain aspects of the core implementation,
// we don't smartly use the ActiveChanged event here.
@ -433,131 +484,131 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
private static int[] ColorsFromTitleHash(byte[] romdata)
{
int titleHash = 0;
for (int i = 0; i < 16; i++)
var titleHash = 0;
for (var i = 0; i < 16; i++)
{
titleHash += romdata[0x134 + i];
}
return (titleHash & 0xFF) switch
{
0x01 or 0x10 or 0x29 or 0x52 or 0x5D or 0x68 or 0x6D or 0xF6 => new int[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000 },
0x0C or 0x16 or 0x35 or 0x67 or 0x75 or 0x92 or 0x99 or 0xB7 => new int[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
0x14 => new int[] { 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x15 or 0xDB => new int[] { 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000 },
0x17 or 0x8B => new int[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x19 => new int[] { 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x1D => new int[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000 },
0x34 => new int[] { 0xFFFFFF, 0x7BFF00, 0xB57300, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x36 => new int[] { 0x52DE00, 0xFF8400, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x39 or 0x43 or 0x97 => new int[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x3C => new int[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x3D => new int[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x3E or 0xE0 => new int[] { 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x49 or 0x5C => new int[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0x0000FF, 0xFFFFFF, 0xFFFF7B, 0x0084FF },
0x4B or 0x90 or 0x9A or 0xBD => new int[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x4E => new int[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFFFF7B, 0x0084FF, 0xFF0000 },
0x58 => new int[] { 0xFFFFFF, 0xA5A5A5, 0x525252, 0x000000, 0xFFFFFF, 0xA5A5A5, 0x525252, 0x000000, 0xFFFFFF, 0xA5A5A5, 0x525252, 0x000000 },
0x59 => new int[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x69 or 0xF2 => new int[] { 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x6B => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x6F => new int[] { 0xFFFFFF, 0xFFCE00, 0x9C6300, 0x000000, 0xFFFFFF, 0xFFCE00, 0x9C6300, 0x000000, 0xFFFFFF, 0xFFCE00, 0x9C6300, 0x000000 },
0x70 => new int[] { 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x00FF00, 0x318400, 0x004A00, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x71 or 0xFF => new int[] { 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000 },
0x86 or 0xA8 => new int[] { 0xFFFF9C, 0x94B5FF, 0x639473, 0x003A3A, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x88 => new int[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xA59CFF, 0xFFFF00, 0x006300, 0x000000 },
0x8C => new int[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000 },
0x95 => new int[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x9C => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000 },
0x9D => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
0xA2 or 0xF7 => new int[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0xAA => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000 },
0xC9 => new int[] { 0xFFFFCE, 0x63EFEF, 0x9C8431, 0x5A5A5A, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0xCE or 0xD1 or 0xF0 => new int[] { 0x6BFF00, 0xFFFFFF, 0xFF524A, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
0xE8 => new int[] { 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF },
0x0D => (romdata[0x137]) switch
0x01 or 0x10 or 0x29 or 0x52 or 0x5D or 0x68 or 0x6D or 0xF6 => new[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000 },
0x0C or 0x16 or 0x35 or 0x67 or 0x75 or 0x92 or 0x99 or 0xB7 => new[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
0x14 => new[] { 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x15 or 0xDB => new[] { 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000 },
0x17 or 0x8B => new[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x19 => new[] { 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x1D => new[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000 },
0x34 => new[] { 0xFFFFFF, 0x7BFF00, 0xB57300, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x36 => new[] { 0x52DE00, 0xFF8400, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x39 or 0x43 or 0x97 => new[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x3C => new[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x3D => new[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x3E or 0xE0 => new[] { 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x49 or 0x5C => new[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0x0000FF, 0xFFFFFF, 0xFFFF7B, 0x0084FF },
0x4B or 0x90 or 0x9A or 0xBD => new[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x4E => new[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFFFF7B, 0x0084FF, 0xFF0000 },
0x58 => new[] { 0xFFFFFF, 0xA5A5A5, 0x525252, 0x000000, 0xFFFFFF, 0xA5A5A5, 0x525252, 0x000000, 0xFFFFFF, 0xA5A5A5, 0x525252, 0x000000 },
0x59 => new[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x69 or 0xF2 => new[] { 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x6B => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x6F => new[] { 0xFFFFFF, 0xFFCE00, 0x9C6300, 0x000000, 0xFFFFFF, 0xFFCE00, 0x9C6300, 0x000000, 0xFFFFFF, 0xFFCE00, 0x9C6300, 0x000000 },
0x70 => new[] { 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x00FF00, 0x318400, 0x004A00, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x71 or 0xFF => new[] { 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFF9C00, 0xFF0000, 0x000000 },
0x86 or 0xA8 => new[] { 0xFFFF9C, 0x94B5FF, 0x639473, 0x003A3A, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x88 => new[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xA59CFF, 0xFFFF00, 0x006300, 0x000000 },
0x8C => new[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000 },
0x95 => new[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x9C => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000 },
0x9D => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
0xA2 or 0xF7 => new[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0xAA => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000 },
0xC9 => new[] { 0xFFFFCE, 0x63EFEF, 0x9C8431, 0x5A5A5A, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0xCE or 0xD1 or 0xF0 => new[] { 0x6BFF00, 0xFFFFFF, 0xFF524A, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
0xE8 => new[] { 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF },
0x0D => romdata[0x137] switch
{
0x45 => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000 },
0x52 => new int[] { 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x45 => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000 },
0x52 => new[] { 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0xFFFF00, 0xFF0000, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0x18 => (romdata[0x137]) switch
0x18 => romdata[0x137] switch
{
0x4B => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x4B => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0x27 => (romdata[0x137]) switch
0x27 => romdata[0x137] switch
{
0x42 => new int[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0x0000FF, 0xFFFFFF, 0xFFFF7B, 0x0084FF },
0x4E => new int[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x42 => new[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0x0000FF, 0xFFFFFF, 0xFFFF7B, 0x0084FF },
0x4E => new[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0x28 => (romdata[0x137]) switch
0x28 => romdata[0x137] switch
{
0x41 => new int[] { 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF },
0x46 => new int[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x41 => new[] { 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF },
0x46 => new[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0x46 => (romdata[0x137]) switch
0x46 => romdata[0x137] switch
{
0x45 => new int[] { 0xB5B5FF, 0xFFFF94, 0xAD5A42, 0x000000, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A },
0x52 => new int[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFF00, 0xFF0000, 0x630000, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x45 => new[] { 0xB5B5FF, 0xFFFF94, 0xAD5A42, 0x000000, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A },
0x52 => new[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFF00, 0xFF0000, 0x630000, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0x61 => (romdata[0x137]) switch
0x61 => romdata[0x137] switch
{
0x41 => new int[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x45 => new int[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x41 => new[] { 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x45 => new[] { 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0x66 => (romdata[0x137]) switch
0x66 => romdata[0x137] switch
{
0x45 => new int[] { 0xFFFFFF, 0x7BFF00, 0xB57300, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x45 => new[] { 0xFFFFFF, 0x7BFF00, 0xB57300, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0x6A => (romdata[0x137]) switch
0x6A => romdata[0x137] switch
{
0x49 => new int[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x4B => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x49 => new[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x4B => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFC542, 0xFFD600, 0x943A00, 0x4A0000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0xA5 => (romdata[0x137]) switch
0xA5 => romdata[0x137] switch
{
0x41 => new int[] { 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF },
0x52 => new int[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x41 => new[] { 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF, 0x000000, 0x008484, 0xFFDE00, 0xFFFFFF },
0x52 => new[] { 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000, 0xFFFFFF, 0x7BFF31, 0x008400, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0xB3 => (romdata[0x137]) switch
0xB3 => romdata[0x137] switch
{
0x42 => new int[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0x0000FF, 0xFFFFFF, 0xFFFF7B, 0x0084FF },
0x52 => new int[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x55 => new int[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x42 => new[] { 0xA59CFF, 0xFFFF00, 0x006300, 0x000000, 0xFF6352, 0xD60000, 0x630000, 0x000000, 0x0000FF, 0xFFFFFF, 0xFFFF7B, 0x0084FF },
0x52 => new[] { 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x52FF00, 0xFF4200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
0x55 => new[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0xBF => (romdata[0x137]) switch
0xBF => romdata[0x137] switch
{
0x20 => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x43 => new int[] { 0x6BFF00, 0xFFFFFF, 0xFF524A, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x20 => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x43 => new[] { 0x6BFF00, 0xFFFFFF, 0xFF524A, 0x000000, 0xFFFFFF, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0xC6 => (romdata[0x137]) switch
0xC6 => romdata[0x137] switch
{
0x41 => new int[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x41 => new[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFF7300, 0x944200, 0x000000, 0xFFFFFF, 0x5ABDFF, 0xFF0000, 0x0000FF },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0xD3 => (romdata[0x137]) switch
0xD3 => romdata[0x137] switch
{
0x49 => new int[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x52 => new int[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x49 => new[] { 0xFFFFFF, 0xADAD84, 0x42737B, 0x000000, 0xFFFFFF, 0xFFAD63, 0x843100, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
0x52 => new[] { 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x8C8CDE, 0x52528C, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
0xF4 => (romdata[0x137]) switch
0xF4 => romdata[0x137] switch
{
0x20 => new int[] { 0xFFFFFF, 0x7BFF00, 0xB57300, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x2D => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x20 => new[] { 0xFFFFFF, 0x7BFF00, 0xB57300, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
0x2D => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0x63A5FF, 0x0000FF, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
},
_ => new int[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
_ => new[] { 0xFFFFFF, 0x7BFF31, 0x0063C5, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000, 0xFFFFFF, 0xFF8484, 0x943A3A, 0x000000 },
};
}
@ -567,7 +618,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
var _bgpal = IntPtr.Zero;
var _sppal = IntPtr.Zero;
var _oam = IntPtr.Zero;
int unused = 0;
var unused = 0;
if (!LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.vram, ref _vram, ref unused)
|| !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.bgpal, ref _bgpal, ref unused)
|| !LibGambatte.gambatte_getmemoryarea(GambatteState, LibGambatte.MemoryAreas.sppal, ref _sppal, ref unused)
@ -609,20 +660,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
endofframecallback = null;
if (callback == null || line == -1 || line == -2)
if (callback == null || line is -1 or -2)
{
scanlinecb = null;
LibGambatte.gambatte_setscanlinecallback(GambatteState, null, 0);
if (line == -1)
switch (line)
{
endofframecallback = callback;
}
else if (line == -2)
{
callback(LibGambatte.gambatte_cpuread(GambatteState, 0xFF40));
case -1:
endofframecallback = callback;
break;
case -2:
callback(LibGambatte.gambatte_cpuread(GambatteState, 0xFF40));
break;
}
}
else if (line >= 0 && line <= 153)
else if (line is >= 0 and <= 153)
{
scanlinecb = () => callback(LibGambatte.gambatte_cpuread(GambatteState, 0xFF40));
LibGambatte.gambatte_setscanlinecallback(GambatteState, scanlinecb, line);
@ -648,7 +700,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
if (callback != null)
{
printer = new GambattePrinter(this, callback);
printer = new(this, callback);
_linkConnected = true;
}
else
@ -670,7 +722,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// </summary>
public void ChangeDMGColors(int[] colors)
{
for (int i = 0; i < 12; i++)
for (var i = 0; i < 12; i++)
{
LibGambatte.gambatte_setdmgpalettecolor(GambatteState, (LibGambatte.PalType)(i / 4), (uint)i % 4, (uint)colors[i]);
}
@ -678,7 +730,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public void SetCGBColors(GBColors.ColorType type)
{
int[] lut = GBColors.GetLut(type, IsSgb, _syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA);
var lut = GBColors.GetLut(type, IsSgb, _syncSettings.ConsoleMode is GambatteSyncSettings.ConsoleModeType.GBA);
LibGambatte.gambatte_setcgbpalette(GambatteState, lut);
}
}

View File

@ -417,7 +417,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
/// <param name="crc">core reported crc32</param>
/// <param name="headerchecksumok">core reported header checksum status</param>
[DllImport("libgambatte", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_pakinfo(IntPtr core, byte[] mbc, ref uint rambanks, ref uint rombanks, ref uint crc, ref uint headerchecksumok);
public static extern void gambatte_pakinfo(IntPtr core, byte[] mbc, out uint rambanks, out uint rombanks, out uint crc, out uint headerchecksumok);
/// <summary>
/// memory areas that gambatte_getmemoryarea() can return