GBHawk: remove C++ core, not worth it, need to rethink design

This commit is contained in:
alyosha-tas 2020-03-31 23:14:33 -04:00
parent 86950c9c2e
commit e6e70b6e35
28 changed files with 0 additions and 17099 deletions

View File

@ -1,185 +0,0 @@
using System.IO;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.LR35902;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
/*
public partial class GBHawk : ICodeDataLogger
{
private ICodeDataLog _cdl;
public void SetCDL(ICodeDataLog cdl)
{
_cdl = cdl;
if (cdl == null)
this.cpu.CDLCallback = null;
else this.cpu.CDLCallback = CDLCpuCallback;
}
public void NewCDL(ICodeDataLog cdl)
{
cdl["ROM"] = new byte[MemoryDomains["ROM"].Size];
cdl["HRAM"] = new byte[MemoryDomains["Zero Page RAM"].Size];
cdl["WRAM"] = new byte[MemoryDomains["Main RAM"].Size];
if (MemoryDomains.Has("Cart RAM"))
{
cdl["CartRAM"] = new byte[MemoryDomains["Cart RAM"].Size];
}
cdl.SubType = "GB";
cdl.SubVer = 0;
}
[FeatureNotImplemented]
void ICodeDataLogger.DisassembleCDL(Stream s, ICodeDataLog cdl)
{
}
public void SetCDL(LR35902.eCDLogMemFlags flags, string type, int cdladdr)
{
if (type == null) return;
byte val = (byte)flags;
_cdl[type][cdladdr] |= (byte)flags;
}
void CDLCpuCallback(ushort addr, LR35902.eCDLogMemFlags flags)
{
if (addr < 0x8000)
{
//don't record writes to the ROM, it's just noisy
//NOTE: in principle a mapper could mount a useful resource here, but I doubt it)
if ((flags & LR35902.eCDLogMemFlags.Write) != 0) return;
}
if (ppu.DMA_start)
{
// some of gekkio's tests require these to be accessible during DMA
if (addr < 0x8000)
{
if (ppu.DMA_addr < 0x80)
{
return;
}
else
{
mapper.MapCDL(addr, flags);
return;
}
}
else if ((addr >= 0xE000) && (addr < 0xF000))
{
SetCDL(flags, "WRAM", addr - 0xE000);
}
else if ((addr >= 0xF000) && (addr < 0xFE00))
{
SetCDL(flags, "WRAM", (RAM_Bank * 0x1000) + (addr - 0xF000));
}
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu.DMA_OAM_access)
{
return;
}
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
{
return;
}
else if ((addr >= 0xFF80))
{
SetCDL(flags, "HRAM", addr - 0xFF80);
}
}
if (addr < 0x900)
{
if (addr < 0x100)
{
// return Either BIOS ROM or Game ROM
if ((GB_bios_register & 0x1) == 0)
{
return;
}
else
{
mapper.MapCDL(addr, flags);
return;
}
}
else if (addr >= 0x200)
{
// return Either BIOS ROM or Game ROM
if (((GB_bios_register & 0x1) == 0) && is_GBC)
{
return;
}
else
{
mapper.MapCDL(addr, flags);
return;
}
}
else
{
mapper.MapCDL(addr, flags);
return;
}
}
else if (addr < 0x8000)
{
mapper.MapCDL(addr, flags);
return;
}
else if (addr < 0xA000)
{
return;
}
else if (addr < 0xC000)
{
mapper.MapCDL(addr, flags);
return;
}
else if (addr < 0xD000)
{
return;
}
else if (addr < 0xE000)
{
SetCDL(flags, "WRAM", (RAM_Bank * 0x1000) + (addr - 0xD000));
}
else if (addr < 0xF000)
{
SetCDL(flags, "WRAM", addr - 0xE000);
}
else if (addr < 0xFE00)
{
SetCDL(flags, "WRAM", (RAM_Bank * 0x1000) + (addr - 0xF000));
}
else if (addr < 0xFEA0)
{
return;
}
else if (addr < 0xFF00)
{
return;
}
else if (addr < 0xFF80)
{
return;
}
else if (addr < 0xFFFF)
{
SetCDL(flags, "HRAM", addr - 0xFF80);
}
else
{
return;
}
}
}
*/
}

View File

@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public partial class GBHawkNew : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
["PCl"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 0),
["PCh"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 1),
["SPl"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 2),
["SPh"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 3),
["A"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 4),
["F"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 5),
["B"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 6),
["C"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 7),
["D"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 8),
["E"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 9),
["H"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 10),
["L"] = LibGBHawk.GB_cpu_get_regs(GB_Pntr, 11),
["Flag I"] = LibGBHawk.GB_cpu_get_flags(GB_Pntr, 0),
["Flag C"] = LibGBHawk.GB_cpu_get_flags(GB_Pntr, 1),
["Flag H"] = LibGBHawk.GB_cpu_get_flags(GB_Pntr, 2),
["Flag N"] = LibGBHawk.GB_cpu_get_flags(GB_Pntr, 3),
["Flag Z"] = LibGBHawk.GB_cpu_get_flags(GB_Pntr, 4)
};
}
public void SetCpuRegister(string register, int value)
{
switch (register)
{
case ("PCl"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 0, (byte)value); break;
case ("PCh"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 1, (byte)value); break;
case ("SPl"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 2, (byte)value); break;
case ("SPh"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 3, (byte)value); break;
case ("A"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 4, (byte)value); break;
case ("F"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 5, (byte)value); break;
case ("B"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 6, (byte)value); break;
case ("C"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 7, (byte)value); break;
case ("D"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 8, (byte)value); break;
case ("E"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 9, (byte)value); break;
case ("H"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 10, (byte)value); break;
case ("L"): LibGBHawk.GB_cpu_set_regs(GB_Pntr, 11, (byte)value); break;
case ("Flag I"): LibGBHawk.GB_cpu_set_flags(GB_Pntr, 0, value > 0); break;
case ("Flag C"): LibGBHawk.GB_cpu_set_flags(GB_Pntr, 1, value > 0); break;
case ("Flag H"): LibGBHawk.GB_cpu_set_flags(GB_Pntr, 2, value > 0); break;
case ("Flag N"): LibGBHawk.GB_cpu_set_flags(GB_Pntr, 3, value > 0); break;
case ("Flag Z"): LibGBHawk.GB_cpu_set_flags(GB_Pntr, 4, value > 0); break;
}
}
public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(new[] { "System Bus" });
public bool CanStep(StepType type) => false;
[FeatureNotImplemented]
public void Step(StepType type) => throw new NotImplementedException();
public long TotalExecutedCycles => (long)LibGBHawk.GB_cpu_cycles(GB_Pntr);
}
}

View File

@ -1,220 +0,0 @@
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Common;
using System;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public partial class GBHawkNew : IEmulator, IVideoProvider, ISoundProvider
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition => _controllerDeck.Definition;
public byte controller_state;
public ushort Acc_X_state;
public ushort Acc_Y_state;
public bool in_vblank_old;
public bool in_vblank;
public bool vblank_rise;
public bool FrameAdvance(IController controller, bool render, bool rendersound)
{
//Console.WriteLine("-----------------------FRAME-----------------------");
//Update the color palette if a setting changed
if (_settings.Palette == GBSettings.PaletteType.BW)
{
color_palette[0] = color_palette_BW[0];
color_palette[1] = color_palette_BW[1];
color_palette[2] = color_palette_BW[2];
color_palette[3] = color_palette_BW[3];
}
else
{
color_palette[0] = color_palette_Gr[0];
color_palette[1] = color_palette_Gr[1];
color_palette[2] = color_palette_Gr[2];
color_palette[3] = color_palette_Gr[3];
}
if (Tracer.Enabled)
{
tracecb = MakeTrace;
}
else
{
tracecb = null;
}
LibGBHawk.GB_settracecallback(GB_Pntr, tracecb);
_frame++;
if (controller.IsPressed("P1 Power"))
{
HardReset();
}
_islag = true;
_islag = do_frame(controller);
if (_islag)
{
_lagcount++;
}
return true;
}
public bool do_frame(IController controller)
{
return LibGBHawk.GB_frame_advance(GB_Pntr, _controllerDeck.ReadPort1(controller),
_controllerDeck.ReadAccX1(controller),
_controllerDeck.ReadAccY1(controller), true, true);
}
public void do_single_step()
{
LibGBHawk.GB_do_single_step(GB_Pntr);
}
public void do_controller_check()
{
}
public void GetControllerState(IController controller)
{
InputCallbacks.Call();
controller_state = _controllerDeck.ReadPort1(controller);
Acc_X_state = _controllerDeck.ReadAccX1(controller);
Acc_Y_state = _controllerDeck.ReadAccY1(controller);
}
public int Frame => _frame;
public string SystemId => "GB";
public bool DeterministicEmulation { get; set; }
public void ResetCounters()
{
_frame = 0;
_lagcount = 0;
_islag = false;
}
public void Dispose()
{
DisposeSound();
if (GB_Pntr != IntPtr.Zero)
{
LibGBHawk.GB_destroy(GB_Pntr);
GB_Pntr = IntPtr.Zero;
}
}
#region Video provider
public int[] frame_buffer;
public int[] GetVideoBuffer()
{
LibGBHawk.GB_get_video(GB_Pntr, frame_buffer);
return frame_buffer;
}
public int VirtualWidth => 160;
public int VirtualHeight => 144;
public int BufferWidth => 160;
public int BufferHeight => 144;
public int BackgroundColor => unchecked((int)0xFF000000);
public int VsyncNumerator => 262144;
public int VsyncDenominator => 4389;
public static readonly uint[] color_palette_BW = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 };
public static readonly uint[] color_palette_Gr = { 0xFFA4C505, 0xFF88A905, 0xFF1D551D, 0xFF052505 };
public uint[] color_palette = new uint[4];
#endregion
#region Audio
public BlipBuffer blip_L = new BlipBuffer(25000);
public BlipBuffer blip_R = new BlipBuffer(25000);
public int[] Aud_L = new int[25000];
public int[] Aud_R = new int[25000];
public uint num_samp_L;
public uint num_samp_R;
const int blipbuffsize = 9000;
public bool CanProvideAsync => false;
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new NotSupportedException("Only sync mode is supported");
}
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async not supported");
}
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
uint f_clock = LibGBHawk.GB_get_audio(GB_Pntr, Aud_L, ref num_samp_L, Aud_R, ref num_samp_R);
for (int i = 0; i < num_samp_L; i++)
{
blip_L.AddDelta((uint)Aud_L[i * 2], Aud_L[i * 2 + 1]);
}
for (int i = 0; i < num_samp_R; i++)
{
blip_R.AddDelta((uint)Aud_R[i * 2], Aud_R[i * 2 + 1]);
}
blip_L.EndFrame(f_clock);
blip_R.EndFrame(f_clock);
nsamp = blip_L.SamplesAvailable();
samples = new short[nsamp * 2];
if (nsamp != 0)
{
blip_L.ReadSamplesLeft(samples, nsamp);
blip_R.ReadSamplesRight(samples, nsamp);
}
}
public void DiscardSamples()
{
blip_L.Clear();
blip_R.Clear();
}
public void DisposeSound()
{
blip_L.Clear();
blip_R.Clear();
blip_L.Dispose();
blip_R.Dispose();
blip_L = null;
blip_R = null;
}
#endregion
}
}

View File

@ -1,24 +0,0 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public partial class GBHawkNew : IInputPollable
{
public int LagCount
{
get => _lagcount;
set => _lagcount = value;
}
public bool IsLagFrame
{
get => _islag;
set => _islag = value;
}
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
public bool _islag = true;
private int _lagcount;
}
}

View File

@ -1,69 +0,0 @@
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public partial class GBHawkNew
{
private IMemoryDomains MemoryDomains;
public void SetupMemoryDomains()
{
var domains = new List<MemoryDomain>
{
new MemoryDomainDelegate(
"WRAM",
0x8000,
MemoryDomain.Endian.Little,
addr => LibGBHawk.GB_getram(GB_Pntr, (int)(addr & 0xFFFF)),
(addr, value) => LibGBHawk.GB_setram(GB_Pntr, (int)(addr & 0xFFFF), value),
1),
new MemoryDomainDelegate(
"ROM",
_rom.Length,
MemoryDomain.Endian.Little,
addr => _rom[addr],
(addr, value) => _rom[addr] = value,
1),
new MemoryDomainDelegate(
"VRAM",
0x4000,
MemoryDomain.Endian.Little,
addr => LibGBHawk.GB_getvram(GB_Pntr, (int)(addr & 0xFFFF)),
(addr, value) => LibGBHawk.GB_setvram(GB_Pntr, (int)(addr & 0xFFFF), value),
1),
new MemoryDomainDelegate(
"OAM",
0xA0,
MemoryDomain.Endian.Little,
addr => LibGBHawk.GB_getoam(GB_Pntr, (int)(addr & 0xFFFF)),
(addr, value) => LibGBHawk.GB_setoam(GB_Pntr, (int)(addr & 0xFFFF), value),
1),
new MemoryDomainDelegate(
"HRAM",
0x80,
MemoryDomain.Endian.Little,
addr => LibGBHawk.GB_gethram(GB_Pntr, (int)(addr & 0xFFFF)),
(addr, value) => LibGBHawk.GB_sethram(GB_Pntr, (int)(addr & 0xFFFF), value),
1),
new MemoryDomainDelegate(
"System Bus",
0X10000,
MemoryDomain.Endian.Little,
addr => LibGBHawk.GB_getsysbus(GB_Pntr, (int)(addr & 0xFFFF)),
(addr, value) => LibGBHawk.GB_setsysbus(GB_Pntr, (int)(addr & 0xFFFF), value),
1),
};
/*
if (cart_RAM != null)
{
var CartRam = new MemoryDomainByteArray("CartRAM", MemoryDomain.Endian.Little, cart_RAM, true, 1);
domains.Add(CartRam);
}
*/
MemoryDomains = new MemoryDomainList(domains);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
}
}
}

View File

@ -1,24 +0,0 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public partial class GBHawkNew : ISaveRam
{
public byte[] CloneSaveRam()
{
return null;// (byte[])cart_RAM?.Clone();
}
public void StoreSaveRam(byte[] data)
{
if (_syncSettings.Use_SRAM)
{
//Buffer.BlockCopy(data, 0, cart_RAM, 0, data.Length);
Console.WriteLine("loading SRAM here");
}
}
public bool SaveRamModified => false;//has_bat & _syncSettings.Use_SRAM;
}
}

View File

@ -1,155 +0,0 @@
using System;
using System.ComponentModel;
using Newtonsoft.Json;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public partial class GBHawkNew : IEmulator, ISettable<GBHawkNew.GBSettings, GBHawkNew.GBSyncSettings>
{
public GBSettings GetSettings()
{
return _settings.Clone();
}
public GBSyncSettings GetSyncSettings()
{
return _syncSettings.Clone();
}
public bool PutSettings(GBSettings o)
{
_settings = o;
return false;
}
public bool PutSyncSettings(GBSyncSettings o)
{
bool ret = GBSyncSettings.NeedsReboot(_syncSettings, o);
_syncSettings = o;
return ret;
}
private GBSettings _settings = new GBSettings();
public GBSyncSettings _syncSettings = new GBSyncSettings();
public class GBSettings
{
public enum PaletteType
{
BW,
Gr
}
[DisplayName("Color Mode")]
[Description("Pick Between Green scale and Grey scale colors")]
[DefaultValue(PaletteType.BW)]
public PaletteType Palette { get; set; }
public GBSettings Clone()
{
return (GBSettings)MemberwiseClone();
}
public GBSettings()
{
SettingsUtil.SetDefaultValues(this);
}
}
public class GBSyncSettings
{
[JsonIgnore]
public string Port1 = GBHawkNewControllerDeck.DefaultControllerName;
public enum ControllerType
{
Default,
Tilt
}
[JsonIgnore]
private ControllerType _GBController;
[DisplayName("Controller")]
[Description("Select Controller Type")]
[DefaultValue(ControllerType.Default)]
public ControllerType GBController
{
get => _GBController;
set
{
if (value == ControllerType.Default) { Port1 = GBHawkNewControllerDeck.DefaultControllerName; }
else { Port1 = "Gameboy Controller + Tilt"; }
_GBController = value;
}
}
public enum ConsoleModeType
{
Auto,
GB,
GBC
}
[DisplayName("Console Mode")]
[Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")]
[DefaultValue(ConsoleModeType.Auto)]
public ConsoleModeType ConsoleMode { get; set; }
[DisplayName("CGB in GBA")]
[Description("Emulate GBA hardware running a CGB game, instead of CGB hardware. Relevant only for titles that detect the presense of a GBA, such as Shantae.")]
[DefaultValue(false)]
public bool GBACGB { get; set; }
[DisplayName("RTC Initial Time")]
[Description("Set the initial RTC time in terms of elapsed seconds.")]
[DefaultValue(0)]
public int RTCInitialTime
{
get => _RTCInitialTime;
set => _RTCInitialTime = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value));
}
[DisplayName("RTC Offset")]
[Description("Set error in RTC clocking (-127 to 127)")]
[DefaultValue(0)]
public int RTCOffset
{
get => _RTCOffset;
set => _RTCOffset = Math.Max(-127, Math.Min(127, value));
}
[DisplayName("Use Existing SaveRAM")]
[Description("When true, existing SaveRAM will be loaded at boot up")]
[DefaultValue(false)]
public bool Use_SRAM { get; set; }
[JsonIgnore]
private int _RTCInitialTime;
[JsonIgnore]
private int _RTCOffset;
[JsonIgnore]
public ushort _DivInitialTime = 8;
public GBSyncSettings Clone()
{
return (GBSyncSettings)MemberwiseClone();
}
public GBSyncSettings()
{
SettingsUtil.SetDefaultValues(this);
}
public static bool NeedsReboot(GBSyncSettings x, GBSyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
}
}
}

View File

@ -1,29 +0,0 @@
using System.IO;
using BizHawk.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public partial class GBHawkNew
{
private void SyncState(Serializer ser)
{
ser.BeginSection("GB");
ser.Sync("Frame", ref _frame);
ser.Sync("LagCount", ref _lagCount);
ser.EndSection();
if (ser.IsReader)
{
ser.Sync(nameof(GB_core), ref GB_core, false);
LibGBHawk.GB_load_state(GB_Pntr, GB_core);
}
else
{
LibGBHawk.GB_save_state(GB_Pntr, GB_core);
ser.Sync(nameof(GB_core), ref GB_core, false);
}
}
}
}

View File

@ -1,211 +0,0 @@
using System;
using System.Text;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
[Core(
"GBHawkNew",
"",
isPorted: false,
isReleased: true)]
[ServiceNotApplicable(new[] { typeof(IDriveLight) })]
public partial class GBHawkNew : IEmulator, ISaveRam, IDebuggable, IInputPollable, IRegionable, IGameboyCommon,
ISettable<GBHawkNew.GBSettings, GBHawkNew.GBSyncSettings>
{
public IntPtr GB_Pntr { get; set; } = IntPtr.Zero;
byte[] GB_core = new byte[0x80000];
private int _frame = 0;
public int _lagCount = 0;
public bool is_GBC = false;
public byte[] _bios;
public readonly byte[] _rom;
[CoreConstructor(new[] { "GB", "GBC" })]
public GBHawkNew(CoreComm comm, GameInfo game, byte[] rom, /*string gameDbFn,*/ object settings, object syncSettings)
{
ServiceProvider = new BasicServiceProvider(this);
_settings = (GBSettings)settings ?? new GBSettings();
_syncSettings = (GBSyncSettings)syncSettings ?? new GBSyncSettings();
_controllerDeck = new GBHawkNewControllerDeck(_syncSettings.Port1);
byte[] Bios = null;
// Load up a BIOS and initialize the correct PPU
if (_syncSettings.ConsoleMode == GBSyncSettings.ConsoleModeType.Auto)
{
if (game.System == "GB")
{
Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load");
}
else
{
Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load");
is_GBC = true;
}
}
else if (_syncSettings.ConsoleMode == GBSyncSettings.ConsoleModeType.GB)
{
Bios = comm.CoreFileProvider.GetFirmware("GB", "World", true, "BIOS Not Found, Cannot Load");
}
else
{
Bios = comm.CoreFileProvider.GetFirmware("GBC", "World", true, "BIOS Not Found, Cannot Load");
is_GBC = true;
}
if (Bios == null)
{
throw new MissingFirmwareException("Missing Gamboy Bios");
}
_bios = Bios;
_rom = rom;
GB_Pntr = LibGBHawk.GB_create();
char[] MD5_temp = rom.HashMD5(0, rom.Length).ToCharArray();
LibGBHawk.GB_load_bios(GB_Pntr, _bios, is_GBC, _syncSettings.GBACGB);
LibGBHawk.GB_load(GB_Pntr, rom, (uint)rom.Length, MD5_temp, (uint)_syncSettings.RTCInitialTime, (uint)_syncSettings.RTCOffset);
blip_L.SetRates(4194304, 44100);
blip_R.SetRates(4194304, 44100);
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(this);
SetupMemoryDomains();
Header_Length = LibGBHawk.GB_getheaderlength(GB_Pntr);
Disasm_Length = LibGBHawk.GB_getdisasmlength(GB_Pntr);
Reg_String_Length = LibGBHawk.GB_getregstringlength(GB_Pntr);
var newHeader = new StringBuilder(Header_Length);
LibGBHawk.GB_getheader(GB_Pntr, newHeader, Header_Length);
Console.WriteLine(Header_Length + " " + Disasm_Length + " " + Reg_String_Length);
Tracer = new TraceBuffer { Header = newHeader.ToString() };
var serviceProvider = ServiceProvider as BasicServiceProvider;
serviceProvider.Register<ITraceable>(Tracer);
serviceProvider.Register<IStatable>(new StateSerializer(SyncState));
Console.WriteLine("MD5: " + rom.HashMD5(0, rom.Length));
Console.WriteLine("SHA1: " + rom.HashSHA1(0, rom.Length));
HardReset();
iptr0 = LibGBHawk.GB_get_ppu_pntrs(GB_Pntr, 0);
iptr1 = LibGBHawk.GB_get_ppu_pntrs(GB_Pntr, 1);
iptr2 = LibGBHawk.GB_get_ppu_pntrs(GB_Pntr, 2);
iptr3 = LibGBHawk.GB_get_ppu_pntrs(GB_Pntr, 3);
_scanlineCallback = null;
}
#region GPUViewer
public bool IsCGBMode() => is_GBC;
public IntPtr iptr0 = IntPtr.Zero;
public IntPtr iptr1 = IntPtr.Zero;
public IntPtr iptr2 = IntPtr.Zero;
public IntPtr iptr3 = IntPtr.Zero;
private GPUMemoryAreas _gpuMemory
{
get
{
return new GPUMemoryAreas(iptr0, iptr1, iptr2, iptr3);
}
}
public LibGBHawk.ScanlineCallback _scanlineCallback;
public GPUMemoryAreas GetGPU() => _gpuMemory;
public void SetScanlineCallback(ScanlineCallback callback, int line)
{
if ((callback == null) || (line == -2))
{
_scanlineCallback = null;
LibGBHawk.GB_setscanlinecallback(GB_Pntr, null, 0);
}
else
{
_scanlineCallback = delegate
{
callback(LibGBHawk.GB_get_LCDC(GB_Pntr));
};
LibGBHawk.GB_setscanlinecallback(GB_Pntr, _scanlineCallback, line);
}
if (line == -2)
{
callback(LibGBHawk.GB_get_LCDC(GB_Pntr));
}
}
private PrinterCallback _printerCallback = null;
public void SetPrinterCallback(PrinterCallback callback)
{
_printerCallback = null;
}
#endregion
public DisplayType Region => DisplayType.NTSC;
private readonly GBHawkNewControllerDeck _controllerDeck;
public void HardReset()
{
LibGBHawk.GB_Reset(GB_Pntr);
frame_buffer = new int[VirtualWidth * VirtualHeight];
}
private void ExecFetch(ushort addr)
{
uint flags = (uint)(MemoryCallbackFlags.AccessRead);
MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus");
}
#region Trace Logger
public ITraceable Tracer;
public LibGBHawk.TraceCallback tracecb;
// these will be constant values assigned during core construction
public int Header_Length;
public int Disasm_Length;
public int Reg_String_Length;
public void MakeTrace(int t)
{
StringBuilder new_d = new StringBuilder(Disasm_Length);
StringBuilder new_r = new StringBuilder(Reg_String_Length);
LibGBHawk.GB_getdisassembly(GB_Pntr, new_d, t, Disasm_Length);
LibGBHawk.GB_getregisterstate(GB_Pntr, new_r, t, Reg_String_Length);
Tracer.Put(new TraceInfo
{
Disassembly = new_d.ToString().PadRight(36),
RegisterInfo = new_r.ToString()
});
}
#endregion
}
}

View File

@ -1,81 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
public class GBHawkNewControllerDeck
{
public GBHawkNewControllerDeck(string controller1Name)
{
if (!ValidControllerTypes.ContainsKey(controller1Name))
{
throw new InvalidOperationException("Invalid controller type: " + controller1Name);
}
Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1);
Definition = new ControllerDefinition
{
Name = Port1.Definition.Name,
BoolButtons = Port1.Definition.BoolButtons
.ToList()
};
Definition.AxisControls.AddRange(Port1.Definition.AxisControls);
Definition.AxisRanges.AddRange(Port1.Definition.AxisRanges);
}
public byte ReadPort1(IController c)
{
return Port1.Read(c);
}
public ushort ReadAccX1(IController c)
{
return Port1.ReadAccX(c);
}
public ushort ReadAccY1(IController c)
{
return Port1.ReadAccY(c);
}
public ControllerDefinition Definition { get; }
public void SyncState(Serializer ser)
{
ser.BeginSection(nameof(Port1));
Port1.SyncState(ser);
ser.EndSection();
}
private readonly IPort Port1;
private static Dictionary<string, Type> _controllerTypes;
public static Dictionary<string, Type> ValidControllerTypes
{
get
{
if (_controllerTypes == null)
{
_controllerTypes = typeof(GBHawkNewControllerDeck).Assembly
.GetTypes()
.Where(t => typeof(IPort).IsAssignableFrom(t))
.Where(t => !t.IsAbstract && !t.IsInterface)
.ToDictionary(tkey => tkey.DisplayName());
}
return _controllerTypes;
}
}
public static string DefaultControllerName => typeof(StandardControls).DisplayName();
}
}

View File

@ -1,216 +0,0 @@
using System;
using System.ComponentModel;
using System.Linq;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
/// <summary>
/// Represents a GB add on
/// </summary>
public interface IPort
{
byte Read(IController c);
ushort ReadAccX(IController c);
ushort ReadAccY(IController c);
ControllerDefinition Definition { get; }
void SyncState(Serializer ser);
int PortNum { get; }
}
[DisplayName("Gameboy Controller")]
public class StandardControls : IPort
{
public StandardControls(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "Gameboy Controller H",
BoolButtons = BaseDefinition
.Select(b => "P" + PortNum + " " + b)
.ToList()
};
}
public int PortNum { get; }
public ControllerDefinition Definition { get; }
public byte Read(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0]))
{
result -= 4;
}
if (c.IsPressed(Definition.BoolButtons[1]))
{
result -= 8;
}
if (c.IsPressed(Definition.BoolButtons[2]))
{
result -= 2;
}
if (c.IsPressed(Definition.BoolButtons[3]))
{
result -= 1;
}
if (c.IsPressed(Definition.BoolButtons[4]))
{
result -= 128;
}
if (c.IsPressed(Definition.BoolButtons[5]))
{
result -= 64;
}
if (c.IsPressed(Definition.BoolButtons[6]))
{
result -= 32;
}
if (c.IsPressed(Definition.BoolButtons[7]))
{
result -= 16;
}
return result;
}
public ushort ReadAccX(IController c)
{
return 0;
}
public ushort ReadAccY(IController c)
{
return 0;
}
private static readonly string[] BaseDefinition =
{
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
};
public void SyncState(Serializer ser)
{
//nothing
}
}
[DisplayName("Gameboy Controller + Tilt")]
public class StandardTilt : IPort
{
public StandardTilt(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "Gameboy Controller + Tilt",
BoolButtons = BaseDefinition
.Select(b => "P" + PortNum + " " + b)
.ToList(),
AxisControls = { "P" + PortNum + " Tilt X", "P" + PortNum + " Tilt Y" },
AxisRanges = ControllerDefinition.CreateAxisRangePair(-45, 0, 45, ControllerDefinition.AxisPairOrientation.RightAndUp) //TODO verify direction against hardware
};
}
public int PortNum { get; }
public float theta, phi, theta_prev, phi_prev;
public ControllerDefinition Definition { get; }
public byte Read(IController c)
{
byte result = 0xFF;
if (c.IsPressed(Definition.BoolButtons[0]))
{
result -= 4;
}
if (c.IsPressed(Definition.BoolButtons[1]))
{
result -= 8;
}
if (c.IsPressed(Definition.BoolButtons[2]))
{
result -= 2;
}
if (c.IsPressed(Definition.BoolButtons[3]))
{
result -= 1;
}
if (c.IsPressed(Definition.BoolButtons[4]))
{
result -= 128;
}
if (c.IsPressed(Definition.BoolButtons[5]))
{
result -= 64;
}
if (c.IsPressed(Definition.BoolButtons[6]))
{
result -= 32;
}
if (c.IsPressed(Definition.BoolButtons[7]))
{
result -= 16;
}
return result;
}
// acc x is the result of rotating around body y AFTER rotating around body x
// therefore this control scheme gives decreasing sensitivity in X as Y rotation inscreases
public ushort ReadAccX(IController c)
{
theta_prev = theta;
phi_prev = phi;
theta = (float)(c.AxisValue(Definition.AxisControls[1]) * Math.PI / 180.0);
phi = (float)(c.AxisValue(Definition.AxisControls[0]) * Math.PI / 180.0);
float temp = (float)(Math.Cos(theta) * Math.Sin(phi));
// here we add in rates of change parameters.
// a typical rate of change for a fast rotation is guessed at 0.5 rad / frame
// since rotations about X have less of a moment arm compared to by, we take 1/5 of the effect as a baseline
float temp2 = (float)((phi - phi_prev) / 0.5 * 25);
return (ushort)(0x81D0 - Math.Floor(temp * 125) - temp2);
}
// acc y is just the sine of the angle
// we assume that ReadAccX is called first, which updates the the states
public ushort ReadAccY(IController c)
{
float temp = (float)Math.Sin(theta);
// here we add in rates of change parameters.
// a typical rate of change for a fast rotation is guessed at 0.5 rad / frame
// further it will be assumed that the resulting acceleration is roughly eqvuivalent to gravity
float temp2 = (float)((theta - theta_prev)/0.5 * 125);
return (ushort)(0x81D0 - Math.Floor(temp * 125) + temp2);
}
private static readonly string[] BaseDefinition =
{
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
};
public void SyncState(Serializer ser)
{
// since we need rate of change of angle, need to savestate them
ser.Sync(nameof(theta), ref theta);
}
}
}

View File

@ -1,349 +0,0 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkNew
{
/// <summary>
/// static bindings into GBHawk.dll
/// </summary>
public static class LibGBHawk
{
# region Core
/// <returns>opaque state pointer</returns>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GB_create();
/// <param name="core">opaque state pointer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_destroy(IntPtr core);
/// <summary>
/// Load BIOS and BASIC image. each must be 16K in size
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="bios">the rom data, can be disposed of once this function returns</param>
/// <param name="is_GBC">is it GBC console</param>
/// <param name="GBC_as_GBA">is it in GBA mode</param>
/// <returns>0 on success, negative value on failure.</returns>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GB_load_bios(IntPtr core, byte[] bios, bool is_GBC, bool GBC_as_GBA);
/// <summary>
/// Load ROM image.
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="romdata_1">the rom data, can be disposed of once this function returns</param>
/// <param name="length_1">length of romdata in bytes</param>
/// <param name="MD5">Hash used for mapper loading</param>
/// <param name="RTC_init">Initial RTC time</param>
/// <param name="RTC_offset">Clck offset for RTC</param>
/// <returns>0 on success, negative value on failure.</returns>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GB_load(IntPtr core, byte[] romdata_1, uint length_1, char[] MD5, uint RTC_init, uint RTC_offset);
/// <summary>
/// Reset.
/// </summary>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_Reset(IntPtr core);
/// <summary>
/// Advance a frame and send controller data.
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="ctrl1">controller data for player 1</param>
/// <param name="ctrl2">controller data for player 2</param>
/// <param name="render">length of romdata in bytes</param>
/// <param name="sound">Mapper number to load core with</param>
/// <returns>0 on success, negative value on failure.</returns>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool GB_frame_advance(IntPtr core, byte ctrl1, uint accx, uint accy, bool render, bool sound);
/// <summary>
/// do a singlt step in the core
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_do_single_step(IntPtr core);
/// <summary>
/// Get Video data
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="videobuf">where to send video to</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_get_video(IntPtr core, int[] videobuf);
/// <summary>
/// Get Video data
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="aud_buf">where to send left audio to</param>
/// <param name="n_samp">number of left samples</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint GB_get_audio(IntPtr core, int[] aud_buf_L, ref uint n_samp_L, int[] aud_buf_R, ref uint n_samp_R);
#endregion
#region State Save / Load
/// <summary>
/// Save State
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="saver">save buffer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_save_state(IntPtr core, byte[] saver);
/// <summary>
/// Load State
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="loader">load buffer</param>
[DllImport("GBHAWK.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_load_state(IntPtr core, byte[] loader);
#endregion
#region Memory Domain Functions
/// <summary>
/// Read the RAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">ram address</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte GB_getram(IntPtr core, int addr);
/// <summary>
/// Read the VRAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">vram address</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte GB_getvram(IntPtr core, int addr);
/// <summary>
/// Read the VRAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">vram address</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte GB_getoam(IntPtr core, int addr);
/// <summary>
/// Read the VRAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">vram address</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte GB_gethram(IntPtr core, int addr);
/// <summary>
/// Read the system bus
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">system bus address</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte GB_getsysbus(IntPtr core, int addr);
/// <summary>
/// Read the RAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">ram address</param>
/// <param name="value">write value</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_setram(IntPtr core, int addr, byte value);
/// <summary>
/// Read the VRAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">vram address</param>
/// <param name="value">write value</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_setvram(IntPtr core, int addr, byte value);
/// <summary>
/// Read the VRAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">vram address</param>
/// <param name="value">write value</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_setoam(IntPtr core, int addr, byte value);
/// <summary>
/// Read the VRAM
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">vram address</param>
/// <param name="value">write value</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_sethram(IntPtr core, int addr, byte value);
/// <summary>
/// Read the system bus
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="addr">system bus address</param>
/// <param name="value">write value</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_setsysbus(IntPtr core, int addr, byte value);
#endregion
#region Tracer
/// <summary>
/// type of the cpu trace callback
/// </summary>
/// <param name="t">type of event</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TraceCallback(int t);
/// <summary>
/// set a callback for trace logging
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="callback">null to clear</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_settracecallback(IntPtr core, TraceCallback callback);
/// <summary>
/// get the trace logger header length
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GB_getheaderlength(IntPtr core);
/// <summary>
/// get the trace logger disassembly length, a constant
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GB_getdisasmlength(IntPtr core);
/// <summary>
/// get the trace logger register string length, a constant
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GB_getregstringlength(IntPtr core);
/// <summary>
/// get the trace logger header
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="h">pointer to const char *</param>
/// <param name="callback">null to clear</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_getheader(IntPtr core, StringBuilder h, int l);
/// <summary>
/// get the register state from the cpu
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="r">pointer to const char *</param>
/// <param name="t">call type</param>
/// <param name="l">copy length, must be obtained from appropriate get legnth function</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_getregisterstate(IntPtr core, StringBuilder h, int t, int l);
/// <summary>
/// get the opcode disassembly
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="d">pointer to const char *</param>
/// <param name="t">call type</param>
/// <param name="l">copy length, must be obtained from appropriate get legnth function</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_getdisassembly(IntPtr core, StringBuilder h, int t, int l);
#endregion
#region PPU_Viewer
/// <summary>
/// type of the cpu trace callback
/// </summary>
/// <param name="lcdc">type of event</param>
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ScanlineCallback(byte lcdc);
/// <summary>
/// Get PPU Pointers
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="sel">region to get</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GB_get_ppu_pntrs(IntPtr core, int sel);
/// <summary>
/// Get PPU Pointers
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte GB_get_LCDC(IntPtr core);
/// <summary>
/// set a callback to occur when ly reaches a particular scanline (so at the beginning of the scanline).
/// when the LCD is active, typically 145 will be the first callback after the beginning of frame advance,
/// and 144 will be the last callback right before frame advance returns
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="callback">null to clear</param>
/// <param name="sl">0-153 inclusive</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_setscanlinecallback(IntPtr core, ScanlineCallback callback, int sl);
#endregion
#region debuggable funcitons
/// <summary>
/// get the current cpu cycle count
/// </summary>
/// <param name="core">opaque state pointer</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GB_cpu_cycles(IntPtr core);
/// <summary>
/// get the registers
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="reg">reg number (see the DLL)</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern byte GB_cpu_get_regs(IntPtr core, int reg);
/// <summary>
/// get the flags
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="flag">flag number (see the DLL)</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool GB_cpu_get_flags(IntPtr core, int flag);
/// <summary>
/// get the registers
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="reg">reg number (see the DLL)</param>
/// <param name="value">value to set</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_cpu_set_regs(IntPtr core, int reg, byte value);
/// <summary>
/// get the flags
/// </summary>
/// <param name="core">opaque state pointer</param>
/// <param name="flag">flag number (see the DLL)</param>
/// <param name="value">value to set</param>
[DllImport("GBHawk.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void GB_cpu_set_flags(IntPtr core, int flag, bool value);
#endregion
}
}

View File

@ -1,31 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29709.97
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GBHawk", "GBHawk\GBHawk.vcxproj", "{FA59603F-32AB-429A-9186-B46114851290}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x64.ActiveCfg = Debug|x64
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x64.Build.0 = Debug|x64
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x86.ActiveCfg = Debug|Win32
{FA59603F-32AB-429A-9186-B46114851290}.Debug|x86.Build.0 = Debug|Win32
{FA59603F-32AB-429A-9186-B46114851290}.Release|x64.ActiveCfg = Release|x64
{FA59603F-32AB-429A-9186-B46114851290}.Release|x64.Build.0 = Release|x64
{FA59603F-32AB-429A-9186-B46114851290}.Release|x86.ActiveCfg = Release|Win32
{FA59603F-32AB-429A-9186-B46114851290}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ED770D9B-8735-46CA-B51E-F85B00A9C744}
EndGlobalSection
EndGlobal

View File

@ -1,659 +0,0 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
#include "LR35902.h"
#include "GBAudio.h"
#include "Memory.h"
#include "Timer.h"
#include "SerialPort.h"
#include "Mappers.h"
#include "PPU.h"
namespace GBHawk
{
class GBCore
{
public:
GBCore()
{
ppu = nullptr;
mapper = nullptr;
};
PPU* ppu;
LR35902 cpu;
GBAudio psg;
MemoryManager MemMap;
Timer timer;
SerialPort serialport;
Mapper* mapper;
void Load_BIOS(uint8_t* bios, bool GBC_console, bool GBC_as_GBA)
{
MemMap.Load_BIOS(bios, GBC_console, GBC_as_GBA);
}
void Load_ROM(uint8_t* ext_rom_1, uint32_t ext_rom_size_1, string MD5, uint32_t RTC_initial, uint32_t RTC_offset)
{
MemMap.Load_ROM(ext_rom_1, ext_rom_size_1);
// After we load the ROM we need to initialize the rest of the components (ppu and mapper)
// tell the cpu the console type
cpu.is_GBC = MemMap.is_GBC;
//initialize system components
// initialize the proper ppu
if (MemMap.is_GBC)
{
if ((MemMap.header[0x43] != 0x80) && (MemMap.header[0x43] != 0xC0))
{
ppu = new GBC_GB_PPU();
}
else
{
ppu = new GBC_PPU();
}
}
else
{
ppu = new GB_PPU();
}
MemMap.ppu_pntr = &ppu[0];
ppu->mem_ctrl = &MemMap;
// initialize the proper mapper
Setup_Mapper(MD5, RTC_initial, RTC_offset);
MemMap.mapper_pntr = &mapper[0];
// set up pointers
MemMap.cpu_pntr = &cpu;
MemMap.psg_pntr = &psg;
MemMap.timer_pntr = &timer;
MemMap.serialport_pntr = &serialport;
cpu.mem_ctrl = &MemMap;
ppu->FlagI = &cpu.FlagI;
ppu->in_vblank = &MemMap.in_vblank;
ppu->vblank_rise = &MemMap.vblank_rise;
ppu->cpu_LY = &cpu.LY;
ppu->REG_FFFF = &MemMap.REG_FFFF;
ppu->REG_FF0F = &MemMap.REG_FF0F;
ppu->_scanlineCallbackLine = &MemMap._scanlineCallbackLine;
ppu->OAM = &MemMap.OAM[0];
ppu->VRAM = &MemMap.VRAM[0];
ppu->VRAM_Bank = &MemMap.VRAM_Bank;
ppu->cpu_halted = &cpu.halted;
ppu->_vidbuffer = &MemMap.vidbuffer[0];
ppu->color_palette = &MemMap.color_palette[0];
ppu->HDMA_transfer = &MemMap.HDMA_transfer;
ppu->GBC_compat = &MemMap.GBC_compat;
timer.FlagI = &cpu.FlagI;
timer.REG_FFFF = &MemMap.REG_FFFF;
timer.REG_FF0F = &MemMap.REG_FF0F;
timer.CPU_cycle_pntr = &cpu.TotalExecutedCycles;
serialport.GBC_compat = &MemMap.GBC_compat;
serialport.FlagI = &cpu.FlagI;
serialport.REG_FFFF = &MemMap.REG_FFFF;
serialport.REG_FF0F = &MemMap.REG_FF0F;
psg.is_GBC = &MemMap.is_GBC;
psg.double_speed = &MemMap.double_speed;
psg.timer_div_reg = &timer.divider_reg;
MemMap.mapper_pntr->addr_access = &MemMap.addr_access;
MemMap.mapper_pntr->Acc_X_state = &MemMap.Acc_X_state;
MemMap.mapper_pntr->Acc_Y_state = &MemMap.Acc_Y_state;
MemMap.mapper_pntr->ROM_Length = &MemMap.ROM_Length;
MemMap.mapper_pntr->Cart_RAM_Length = &MemMap.Cart_RAM_Length;
MemMap.mapper_pntr->ROM = &MemMap.ROM[0];
MemMap.mapper_pntr->Cart_RAM = &MemMap.Cart_RAM[0];
}
void Reset()
{
MemMap.GB_bios_register = 0; // bios enable
MemMap.GBC_compat = MemMap.is_GBC;
MemMap.in_vblank = true; // we start off in vblank since the LCD is off
MemMap.in_vblank_old = true;
MemMap.double_speed = false;
MemMap.VRAM_Bank = 0;
MemMap.RAM_Bank = 1; // RAM bank always starts as 1 (even writing zero still sets 1)
MemMap.Register_Reset();
timer.Reset();
ppu->Reset();
psg.Reset();
serialport.Reset();
mapper->Reset();
cpu.Reset();
}
bool FrameAdvance(uint8_t new_controller_1, uint32_t new_accx, uint32_t new_accy, bool render, bool rendersound)
{
int temp_check = 0;
/*
if (cpu.TotalExecutedCycles < 25280600) {
temp_check = 70224;
}
else if (cpu.TotalExecutedCycles < 25347500) {
temp_check = 1000;
}
else {
temp_check = 5;
}
*/
MemMap.new_controller_1 = new_controller_1;
MemMap.new_accx = new_accx;
MemMap.new_accy = new_accy;
temp_check = 70224;
for (int i = 0; i < temp_check; i++)
{
// These things do not change speed in GBC double spped mode
psg.tick();
ppu->tick();
if (MemMap.Use_MT) { mapper->Mapper_Tick(); }
if (!MemMap.HDMA_transfer)
{
// These things all tick twice as fast in GBC double speed mode
if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); }
serialport.serial_transfer_tick();
cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF);
timer.tick();
if (MemMap.double_speed)
{
if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); }
serialport.serial_transfer_tick();
cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF);
timer.tick();
}
}
else
{
cpu.TotalExecutedCycles++;
timer.tick();
if (MemMap.double_speed)
{
cpu.TotalExecutedCycles++;
timer.tick();
}
}
MemMap.REG_FF0F_OLD = MemMap.REG_FF0F;
}
// turn off the screen so the image doesnt persist
// but don't turn off blank_frame yet, it still needs to be true until the next VBL
// this doesn't run for GBC, some games, ex MIB the series 2, rely on the screens persistence while off to make video look smooth.
// But some GB gams, ex Battletoads, turn off the screen for a long time from the middle of the frame, so need to be cleared.
if (ppu->clear_screen)
{
for (int j = 0; j < (160 * 144); j++) { MemMap.frame_buffer[j] = (int)MemMap.color_palette[0]; }
ppu->clear_screen = false;
}
return MemMap.lagged;
}
void do_single_step()
{
// These things do not change speed in GBC double spped mode
psg.tick();
ppu->tick();
if (MemMap.Use_MT) { mapper->Mapper_Tick(); }
if (!MemMap.HDMA_transfer)
{
// These things all tick twice as fast in GBC double speed mode
// Note that DMA is halted when the CPU is halted
if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); }
serialport.serial_transfer_tick();
cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF);
timer.tick();
if (MemMap.double_speed)
{
if (ppu->DMA_start && !cpu.halted) { ppu->DMA_tick(); }
serialport.serial_transfer_tick();
cpu.ExecuteOne(&MemMap.REG_FF0F, &MemMap.REG_FFFF);
timer.tick();
}
}
else
{
cpu.TotalExecutedCycles++;
timer.tick();
if (MemMap.double_speed)
{
cpu.TotalExecutedCycles++;
timer.tick();
}
}
if (MemMap.in_vblank && !MemMap.in_vblank_old)
{
MemMap.vblank_rise = true;
}
MemMap.in_vblank_old = MemMap.in_vblank;
MemMap.REG_FF0F_OLD = MemMap.REG_FF0F;
}
void GetVideo(uint32_t* dest)
{
uint32_t* src = MemMap.frame_buffer;
uint32_t* dst = dest;
std::memcpy(dst, src, sizeof uint32_t * 160 * 144);
}
uint32_t GetAudio(int32_t* dest_L, int32_t* n_samp_L, int32_t* dest_R, int32_t* n_samp_R)
{
int32_t* src = psg.samples_L;
int32_t* dst = dest_L;
std::memcpy(dst, src, sizeof int32_t * psg.num_samples_L * 2);
n_samp_L[0] = psg.num_samples_L;
src = psg.samples_R;
dst = dest_R;
std::memcpy(dst, src, sizeof int32_t * psg.num_samples_R * 2);
n_samp_R[0] = psg.num_samples_R;
uint32_t temp_int = psg.master_audio_clock;
psg.master_audio_clock = 0;
psg.num_samples_L = 0;
psg.num_samples_R = 0;
return temp_int;
}
void Setup_Mapper(string MD5, uint32_t RTC_initial, uint32_t RTC_offset)
{
// setup up mapper based on header entry
string mppr;
switch (MemMap.header[0x47])
{
case 0x0: mapper = new Mapper_Default(); mppr = "NROM"; break;
case 0x1: mapper = new Mapper_MBC1(); mppr = "MBC1"; break;
case 0x2: mapper = new Mapper_MBC1(); mppr = "MBC1"; break;
case 0x3: mapper = new Mapper_MBC1(); mppr = "MBC1"; MemMap.has_bat = true; break;
case 0x5: mapper = new Mapper_MBC2(); mppr = "MBC2"; break;
case 0x6: mapper = new Mapper_MBC2(); mppr = "MBC2"; MemMap.has_bat = true; break;
case 0x8: mapper = new Mapper_Default(); mppr = "NROM"; break;
case 0x9: mapper = new Mapper_Default(); mppr = "NROM"; MemMap.has_bat = true; break;
case 0xB: mapper = new Mapper_MMM01(); mppr = "MMM01"; break;
case 0xC: mapper = new Mapper_MMM01(); mppr = "MMM01"; break;
case 0xD: mapper = new Mapper_MMM01(); mppr = "MMM01"; MemMap.has_bat = true; break;
case 0xF: mapper = new Mapper_MBC3(); mppr = "MBC3"; MemMap.has_bat = true; break;
case 0x10: mapper = new Mapper_MBC3(); mppr = "MBC3"; MemMap.has_bat = true; break;
case 0x11: mapper = new Mapper_MBC3(); mppr = "MBC3"; break;
case 0x12: mapper = new Mapper_MBC3(); mppr = "MBC3"; break;
case 0x13: mapper = new Mapper_MBC3(); mppr = "MBC3"; MemMap.has_bat = true; break;
case 0x19: mapper = new Mapper_MBC5(); mppr = "MBC5"; break;
case 0x1A: mapper = new Mapper_MBC5(); mppr = "MBC5"; MemMap.has_bat = true; break;
case 0x1B: mapper = new Mapper_MBC5(); mppr = "MBC5"; break;
case 0x1C: mapper = new Mapper_MBC5(); mppr = "MBC5"; break;
case 0x1D: mapper = new Mapper_MBC5(); mppr = "MBC5"; break;
case 0x1E: mapper = new Mapper_MBC5(); mppr = "MBC5"; MemMap.has_bat = true; break;
case 0x20: mapper = new Mapper_MBC6(); mppr = "MBC6"; break;
case 0x22: mapper = new Mapper_MBC7(); mppr = "MBC7"; MemMap.has_bat = true; break;
case 0xFC: mapper = new Mapper_Camera(); mppr = "CAM"; MemMap.has_bat = true; break;
case 0xFD: mapper = new Mapper_TAMA5(); mppr = "TAMA5"; MemMap.has_bat = true; break;
case 0xFE: mapper = new Mapper_HuC3(); mppr = "HuC3"; break;
case 0xFF: mapper = new Mapper_HuC1(); mppr = "HuC1"; break;
// Bootleg mappers
// NOTE: Sachen mapper selection does not account for scrambling, so if another bootleg mapper
// identifies itself as 0x31, this will need to be modified
case 0x31: mapper = new Mapper_Sachen2(); mppr = "Schn2"; break;
case 0x4:
case 0x7:
case 0xA:
case 0xE:
case 0x14:
case 0x15:
case 0x16:
case 0x17:
case 0x18:
case 0x1F:
case 0x21:
default:
// mapper not implemented
mapper = nullptr;
}
// special case for multi cart mappers
if ((MD5 == "97122B9B183AAB4079C8D36A4CE6E9C1") ||
(MD5 == "9FB9C42CF52DCFDCFBAD5E61AE1B5777") ||
(MD5 == "CF1F58AB72112716D3C615A553B2F481")
)
{
mapper = new Mapper_MBC1_Multi();
}
// Wisdom Tree does not identify their mapper, so use hash instead
if ((MD5 == "2C07CAEE51A1F0C91C72C7C6F380B0F6") || // Joshua
(MD5 == "37E017C8D1A45BAB609FB5B43FB64337") || // Spiritual Warfare
(MD5 == "AB1FA0ED0207B1D0D5F401F0CD17BEBF") || // Exodus
(MD5 == "BA2AC3587B3E1B36DE52E740274071B0") || // Bible - KJV
(MD5 == "8CDDB8B2DCD3EC1A3FDD770DF8BDA07C") // Bible - NIV
)
{
mapper = new Mapper_WT();
mppr = "Wtree";
}
// special case for bootlegs
if ((MD5 == "CAE0998A899DF2EE6ABA8E7695C2A096"))
{
mapper = new Mapper_RM8();
}
if ((MD5 == "D3C1924D847BC5D125BF54C2076BE27A"))
{
mapper = new Mapper_Sachen1();
mppr = "Schn1";
}
MemMap.Cart_RAM = nullptr;
switch (MemMap.header[0x49])
{
case 1:
MemMap.Cart_RAM = new uint8_t[0x800];
MemMap.Cart_RAM_Length = 0x800;
break;
case 2:
MemMap.Cart_RAM = new uint8_t[0x2000];
MemMap.Cart_RAM_Length = 0x2000;
break;
case 3:
MemMap.Cart_RAM = new uint8_t[0x8000];
MemMap.Cart_RAM_Length = 0x8000;
break;
case 4:
MemMap.Cart_RAM = new uint8_t[0x20000];
MemMap.Cart_RAM_Length = 0x20000;
break;
case 5:
MemMap.Cart_RAM = new uint8_t[0x10000];
MemMap.Cart_RAM_Length = 0x10000;
break;
case 0:
MemMap.has_bat = false;
break;
}
// Sachen maper not known to have RAM
if ((mppr == "Schn1") || (mppr == "Schn2"))
{
MemMap.Cart_RAM = nullptr;
MemMap.Use_MT = true;
}
// mbc2 carts have built in RAM
if (mppr == "MBC2")
{
MemMap.Cart_RAM = new uint8_t[0x200];
MemMap.Cart_RAM_Length = 0x200;
}
// mbc7 has 256 bytes of RAM, regardless of any header info
if (mppr == "MBC7")
{
MemMap.Cart_RAM = new uint8_t[0x100];
MemMap.Cart_RAM_Length = 0x100;
MemMap.has_bat = true;
}
// TAMA5 has 0x1000 bytes of RAM, regardless of any header info
if (mppr == "TAMA5")
{
MemMap.Cart_RAM = new uint8_t[0x20];
MemMap.Cart_RAM_Length = 0x20;
MemMap.has_bat = true;
}
if (MemMap.Cart_RAM != nullptr && (mppr != "MBC7"))
{
for (uint32_t i = 0; i < MemMap.Cart_RAM_Length; i++)
{
MemMap.Cart_RAM[i] = 0xFF;
}
}
// Extra RTC initialization for mbc3, HuC3, and TAMA5
if (mppr == "MBC3")
{
MemMap.Use_MT = true;
mapper->RTC_Get(RTC_offset, 5);
int days = (int)floor(RTC_initial / 86400.0);
int days_upper = ((days & 0x100) >> 8) | ((days & 0x200) >> 2);
mapper->RTC_Get(days_upper, 4);
mapper->RTC_Get(days & 0xFF, 3);
int remaining = RTC_initial - (days * 86400);
int hours = (int)floor(remaining / 3600.0);
mapper->RTC_Get(hours & 0xFF, 2);
remaining = remaining - (hours * 3600);
int minutes = (int)floor(remaining / 60.0);
mapper->RTC_Get(minutes & 0xFF, 1);
remaining = remaining - (minutes * 60);
mapper->RTC_Get(remaining & 0xFF, 0);
}
if (mppr == "HuC3")
{
MemMap.Use_MT = true;
int years = (int)floor(RTC_initial / 31536000.0);
mapper->RTC_Get(years, 24);
int remaining = RTC_initial - (years * 31536000);
int days = (int)floor(remaining / 86400.0);
int days_upper = (days >> 8) & 0xF;
mapper->RTC_Get(days_upper, 20);
mapper->RTC_Get(days & 0xFF, 12);
remaining = remaining - (days * 86400);
int minutes = (int)floor(remaining / 60.0);
int minutes_upper = (minutes >> 8) & 0xF;
mapper->RTC_Get(minutes_upper, 8);
mapper->RTC_Get(remaining & 0xFF, 0);
}
if (mppr == "TAMA5")
{
MemMap.Use_MT = true;
// currently no date / time input for TAMA5
}
}
#pragma region State Save / Load
void SaveState(uint8_t* saver)
{
saver = MemMap.SaveState(saver);
saver = ppu->SaveState(saver);
saver = cpu.SaveState(saver);
saver = psg.SaveState(saver);
saver = timer.SaveState(saver);
saver = serialport.SaveState(saver);
saver = mapper->SaveState(saver);
}
void LoadState(uint8_t* loader)
{
loader = MemMap.LoadState(loader);
loader = ppu->LoadState(loader);
loader = cpu.LoadState(loader);
loader = psg.LoadState(loader);
loader = timer.LoadState(loader);
loader = serialport.LoadState(loader);
loader = mapper->LoadState(loader);
}
#pragma endregion
#pragma region Memory Domain Functions
uint8_t GetRAM(uint32_t addr)
{
return MemMap.RAM[addr & 0x7FFF];
}
uint8_t GetVRAM(uint32_t addr)
{
return MemMap.VRAM[addr & 0x3FFF];
}
uint8_t GetOAM(uint32_t addr)
{
return MemMap.OAM[addr];
}
uint8_t GetHRAM(uint32_t addr)
{
return MemMap.ZP_RAM[addr & 0x7F];
}
uint8_t GetSysBus(uint32_t addr)
{
return MemMap.PeekMemory(addr);
}
void SetRAM(uint32_t addr, uint8_t value)
{
MemMap.RAM[addr & 0x7FFF] = value;
}
void SetVRAM(uint32_t addr, uint8_t value)
{
MemMap.VRAM[addr & 0x3FFF] = value;
}
void SetOAM(uint32_t addr, uint8_t value)
{
MemMap.OAM[addr] = value;
}
void SetHRAM(uint32_t addr, uint8_t value)
{
MemMap.ZP_RAM[addr & 0x7F] = value;
}
void SetSysBus(uint32_t addr, uint8_t value)
{
// make poke?
MemMap.WriteMemory(addr, value);
}
#pragma endregion
#pragma region Tracer
void SetTraceCallback(void (*callback)(int))
{
cpu.TraceCallback = callback;
}
void SetScanlineCallback(void (*callback)(void), int sl)
{
ppu->scanlineCallback = callback;
MemMap._scanlineCallbackLine = sl;
}
int GetHeaderLength()
{
return 105 + 1;
}
int GetDisasmLength()
{
return 48 + 1;
}
int GetRegStringLength()
{
return 81 + 1;
}
void GetHeader(char* h, int l)
{
memcpy(h, cpu.TraceHeader, l);
}
// the copy length l must be supplied ahead of time from GetRegStrngLength
void GetRegisterState(char* r, int t, int l)
{
if (t == 0)
{
memcpy(r, cpu.CPURegisterState().c_str(), l);
}
else
{
memcpy(r, cpu.No_Reg, l);
}
}
// the copy length l must be supplied ahead of time from GetDisasmLength
void GetDisassembly(char* d, int t, int l)
{
if (t == 0)
{
memcpy(d, cpu.CPUDisassembly().c_str(), l);
}
else if (t == 1)
{
memcpy(d, cpu.Un_halt_event, l);
}
else if (t == 2)
{
memcpy(d, cpu.IRQ_event, l);
}
else
{
memcpy(d, cpu.Un_halt_event, l);
}
}
#pragma endregion
};
}

File diff suppressed because it is too large Load Diff

View File

@ -1,260 +0,0 @@
// GBHawk.cpp : Defines the exported functions for the DLL.
//
#include "GBHawk.h"
#include "Core.h"
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace GBHawk;
#pragma region Core
// Create pointer to a core instance
GBHawk_EXPORT GBCore* GB_create()
{
return new GBCore();
}
// free the memory from the core pointer
GBHawk_EXPORT void GB_destroy(GBCore* p)
{
delete p->MemMap.bios_rom;
delete p->MemMap.ROM;
std::free(p);
}
// load bios into the core
GBHawk_EXPORT void GB_load_bios(GBCore* p, uint8_t* bios, bool GBC_console, bool GBC_as_GBA)
{
p->Load_BIOS(bios, GBC_console, GBC_as_GBA);
}
// load a rom into the core
GBHawk_EXPORT void GB_load(GBCore* p, uint8_t* rom_1, uint32_t size_1, char* MD5, uint32_t RTC_initial, uint32_t RTC_offset)
{
string MD5_s(MD5, 32);
p->Load_ROM(rom_1, size_1, MD5_s, RTC_initial, RTC_offset);
}
// Hard reset (note: does not change RTC, that only happens on load)
GBHawk_EXPORT void GB_Reset(GBCore* p)
{
p->Reset();
}
// advance a frame
GBHawk_EXPORT bool GB_frame_advance(GBCore* p, uint8_t new_ctrl1, uint32_t new_accx, uint32_t new_accy, bool render, bool sound)
{
return p->FrameAdvance(new_ctrl1, new_accx, new_accy, render, sound);
}
// advance a single step
GBHawk_EXPORT void GB_do_single_step(GBCore* p)
{
p->do_single_step();
}
// send video data to external video provider
GBHawk_EXPORT void GB_get_video(GBCore* p, uint32_t* dest)
{
p->GetVideo(dest);
}
// send audio data to external audio provider
GBHawk_EXPORT uint32_t GB_get_audio(GBCore* p, int32_t* dest_L, int32_t* n_samp_L, int32_t* dest_R, int32_t* n_samp_R)
{
return p->GetAudio(dest_L, n_samp_L, dest_R, n_samp_R);
}
#pragma endregion
#pragma region State Save / Load
// save state
GBHawk_EXPORT void GB_save_state(GBCore* p, uint8_t* saver)
{
p->SaveState(saver);
}
// load state
GBHawk_EXPORT void GB_load_state(GBCore* p, uint8_t* loader)
{
p->LoadState(loader);
}
#pragma endregion
#pragma region Memory Domain Functions
GBHawk_EXPORT uint8_t GB_getram(GBCore* p, uint32_t addr) {
return p->GetRAM(addr);
}
GBHawk_EXPORT uint8_t GB_getvram(GBCore* p, uint32_t addr) {
return p->GetVRAM(addr);
}
GBHawk_EXPORT uint8_t GB_getoam(GBCore* p, uint32_t addr) {
return p->GetOAM(addr);
}
GBHawk_EXPORT uint8_t GB_gethram(GBCore* p, uint32_t addr) {
return p->GetHRAM(addr);
}
GBHawk_EXPORT uint8_t GB_getsysbus(GBCore* p, uint32_t addr) {
return p->GetSysBus(addr);
}
GBHawk_EXPORT void GB_setram(GBCore* p, uint32_t addr, uint8_t value) {
p->SetRAM(addr, value);
}
GBHawk_EXPORT void GB_setvram(GBCore* p, uint32_t addr, uint8_t value) {
p->SetVRAM(addr, value);
}
GBHawk_EXPORT void GB_setoam(GBCore* p, uint32_t addr, uint8_t value) {
p->SetOAM(addr, value);
}
GBHawk_EXPORT void GB_sethram(GBCore* p, uint32_t addr, uint8_t value) {
p->SetHRAM(addr, value);
}
GBHawk_EXPORT void GB_setsysbus(GBCore* p, uint32_t addr, uint8_t value) {
p->SetSysBus(addr, value);
}
#pragma endregion
#pragma region Tracer
// set tracer callback
GBHawk_EXPORT void GB_settracecallback(GBCore* p, void (*callback)(int)) {
p->SetTraceCallback(callback);
}
// return the cpu trace header length
GBHawk_EXPORT int GB_getheaderlength(GBCore* p) {
return p->GetHeaderLength();
}
// return the cpu disassembly length
GBHawk_EXPORT int GB_getdisasmlength(GBCore* p) {
return p->GetDisasmLength();
}
// return the cpu register string length
GBHawk_EXPORT int GB_getregstringlength(GBCore* p) {
return p->GetRegStringLength();
}
// return the cpu trace header
GBHawk_EXPORT void GB_getheader(GBCore* p, char* h, int l) {
p->GetHeader(h, l);
}
// return the cpu register state
GBHawk_EXPORT void GB_getregisterstate(GBCore* p, char* r, int t, int l) {
p->GetRegisterState(r, t, l);
}
// return the cpu disassembly
GBHawk_EXPORT void GB_getdisassembly(GBCore* p, char* d, int t, int l) {
p->GetDisassembly(d, t, l);
}
#pragma endregion
#pragma region PPU Viewer
// set tracer callback
GBHawk_EXPORT uint8_t* GB_get_ppu_pntrs(GBCore* p, int sel) {
if (p->MemMap.is_GBC)
{
switch (sel)
{
case 0: return p->MemMap.VRAM; break;
case 1: return p->MemMap.OAM; break;
case 2: return (uint8_t*)p->ppu->OBJ_palette; break;
case 3: return (uint8_t*)p->ppu->BG_palette; break;
}
}
else
{
switch (sel)
{
case 0: return p->MemMap.VRAM; break;
case 1: return p->MemMap.OAM; break;
case 2: return (uint8_t*)p->MemMap.color_palette_OBJ; break;
case 3: return (uint8_t*)p->MemMap.color_palette_BG; break;
}
}
return nullptr;
}
// return LCDC state for the ppu viewer
GBHawk_EXPORT uint8_t GB_get_LCDC(GBCore* p) {
return p->ppu->LCDC;
}
// set scanline callback
GBHawk_EXPORT void GB_setscanlinecallback(GBCore* p, void (*callback)(void), int sl) {
p->SetScanlineCallback(callback, sl);
}
#pragma endregion
#pragma region Debuggable functions
// return cpu cycle count
GBHawk_EXPORT uint64_t GB_cpu_cycles(GBCore* p) {
return p->cpu.TotalExecutedCycles;
}
// return cpu registers
GBHawk_EXPORT uint8_t GB_cpu_get_regs(GBCore* p, int reg) {
return p->cpu.Regs[reg];
}
// return cpu flags
GBHawk_EXPORT bool GB_cpu_get_flags(GBCore* p, int reg) {
bool ret = false;
switch (reg)
{
case (0): ret = p->cpu.FlagI; break;
case (1): ret = p->cpu.FlagCget(); break;
case (2): ret = p->cpu.FlagHget(); break;
case (3): ret = p->cpu.FlagNget(); break;
case (4): ret = p->cpu.FlagZget(); break;
}
return ret;
}
// set cpu registers
GBHawk_EXPORT void GB_cpu_set_regs(GBCore* p, int reg, uint8_t value) {
p->cpu.Regs[reg] = value;
}
// set cpu flags
GBHawk_EXPORT void GB_cpu_set_flags(GBCore* p, int reg, bool value) {
switch (reg)
{
case (0): p->cpu.FlagI = value; break;
case (1): p->cpu.FlagCset(value); break;
case (2): p->cpu.FlagHset(value); break;
case (3): p->cpu.FlagNset(value); break;
case (4): p->cpu.FlagZset(value); break;
}
}
#pragma endregion

View File

@ -1,5 +0,0 @@
#ifdef _WIN32
#define GBHawk_EXPORT extern "C" __declspec(dllexport)
#elif __linux__
#define GBHawk_EXPORT extern "C"
#endif

View File

@ -1,177 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{FA59603F-32AB-429A-9186-B46114851290}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>GBHawk</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>..\..\..\output\dll\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;GBHAWK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="GBAudio.h" />
<ClInclude Include="Core.h" />
<ClInclude Include="GBHawk.h" />
<ClInclude Include="Mappers.h" />
<ClInclude Include="SerialPort.h" />
<ClInclude Include="Timer.h" />
<ClInclude Include="Memory.h" />
<ClInclude Include="PPU.h" />
<ClInclude Include="LR35902.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="GBHawk.cpp" />
<ClCompile Include="Memory.cpp" />
<ClCompile Include="LR35902.cpp" />
<ClCompile Include="PPU.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -1,31 +0,0 @@
#include <cstdint>
#include <iomanip>
#include <string>
#include "Memory.h"
#include "LR35902.h"
using namespace std;
namespace GBHawk
{
void LR35902::WriteMemory(uint32_t addr, uint8_t value)
{
mem_ctrl->WriteMemory(addr, value);
}
uint8_t LR35902::ReadMemory(uint32_t addr)
{
return mem_ctrl->ReadMemory(addr);
}
uint8_t LR35902::PeekMemory(uint32_t addr)
{
return mem_ctrl->PeekMemory(addr);
}
uint8_t LR35902::SpeedFunc(uint32_t val)
{
return mem_ctrl->SpeedFunc(val);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,999 +0,0 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
#include "Memory.h"
#include "LR35902.h"
#include "PPU.h"
#include "GBAudio.h"
#include "Mappers.h"
#include "SerialPort.h"
#include "Timer.h"
using namespace std;
namespace GBHawk
{
/*
$FFFF Interrupt Enable Flag
$FF80-$FFFE Zero Page - 127 bytes
$FF00-$FF7F Hardware I/O Registers
$FEA0-$FEFF Unusable Memory
$FE00-$FE9F OAM - Object Attribute Memory
$E000-$FDFF Echo RAM - Reserved, Do Not Use
$D000-$DFFF Internal RAM - Bank 1-7 (switchable - CGB only)
$C000-$CFFF Internal RAM - Bank 0 (fixed)
$A000-$BFFF Cartridge RAM (If Available)
$9C00-$9FFF BG Map Data 2
$9800-$9BFF BG Map Data 1
$8000-$97FF Character RAM
$4000-$7FFF Cartridge ROM - Switchable Banks 1-xx
$0150-$3FFF Cartridge ROM - Bank 0 (fixed)
$0100-$014F Cartridge Header Area
$0000-$00FF Restart and Interrupt Vectors
*/
/*
* VRAM is arranged as:
* 0x1800 Tiles
* 0x400 BG Map 1
* 0x400 BG Map 2
* 0x1800 Tiles
* 0x400 CA Map 1
* 0x400 CA Map 2
* Only the top set is available in GB (i.e. VRAM_Bank = 0)
*/
uint8_t MemoryManager::ReadMemory(uint32_t addr)
{
//uint flags = (uint)(MemoryCallbackFlags.AccessRead);
//MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus");
addr_access = addr;
if (ppu_pntr->DMA_start)
{
// some of gekkio's tests require these to be accessible during DMA
if (addr < 0x8000)
{
if (ppu_pntr->DMA_addr < 0x80)
{
return 0xFF;
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else if ((addr >= 0xE000) && (addr < 0xF000))
{
return RAM[addr - 0xE000];
}
else if ((addr >= 0xF000) && (addr < 0xFE00))
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu_pntr->DMA_OAM_access)
{
return OAM[addr - 0xFE00];
}
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
{
return Read_Registers(addr);
}
else if ((addr >= 0xFF80))
{
return ZP_RAM[addr - 0xFF80];
}
return 0xFF;
}
if (addr < 0x900)
{
if (addr < 0x100)
{
// return Either BIOS ROM or Game ROM
if ((GB_bios_register & 0x1) == 0)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else if (addr >= 0x200)
{
// return Either BIOS ROM or Game ROM
if (((GB_bios_register & 0x1) == 0) && is_GBC)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else if (addr < 0x8000)
{
return mapper_pntr->ReadMemory(addr);
}
else if (addr < 0xA000)
{
if (ppu_pntr->VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; }
else { return 0xFF; }
}
else if (addr < 0xC000)
{
return mapper_pntr->ReadMemory(addr);
}
else if (addr < 0xD000)
{
return RAM[addr - 0xC000];
}
else if (addr < 0xE000)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)];
}
else if (addr < 0xF000)
{
return RAM[addr - 0xE000];
}
else if (addr < 0xFE00)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if (addr < 0xFEA0)
{
if (ppu_pntr->OAM_access_read) { return OAM[addr - 0xFE00]; }
else { return 0xFF; }
}
else if (addr < 0xFF00)
{
// unmapped memory, returns 0xFF
return 0xFF;
}
else if (addr < 0xFF80)
{
return Read_Registers(addr);
}
else if (addr < 0xFFFF)
{
return ZP_RAM[addr - 0xFF80];
}
else
{
return Read_Registers(addr);
}
}
void MemoryManager::WriteMemory(uint32_t addr, uint8_t value)
{
//uint flags = (uint)(MemoryCallbackFlags.AccessWrite);
//MemoryCallbacks.CallMemoryCallbacks(addr, value, flags, "System Bus");
addr_access = addr;
if (ppu_pntr->DMA_start)
{
// some of gekkio's tests require this to be accessible during DMA
if ((addr >= 0xE000) && (addr < 0xF000))
{
RAM[addr - 0xE000] = value;
}
else if ((addr >= 0xF000) && (addr < 0xFE00))
{
RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value;
}
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu_pntr->DMA_OAM_access)
{
OAM[addr - 0xFE00] = value;
}
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
{
Write_Registers(addr, value);
}
else if ((addr >= 0xFF80))
{
ZP_RAM[addr - 0xFF80] = value;
}
return;
}
if (addr < 0x900)
{
if (addr < 0x100)
{
if ((GB_bios_register & 0x1) == 0)
{
// No Writing to BIOS
}
else
{
mapper_pntr->WriteMemory(addr, value);
}
}
else if (addr >= 0x200)
{
if (((GB_bios_register & 0x1) == 0) && is_GBC)
{
// No Writing to BIOS
}
else
{
mapper_pntr->WriteMemory(addr, value);
}
}
else
{
mapper_pntr->WriteMemory(addr, value);
}
}
else if (addr < 0x8000)
{
mapper_pntr->WriteMemory(addr, value);
}
else if (addr < 0xA000)
{
if (ppu_pntr->VRAM_access_write) { VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)] = value; }
}
else if (addr < 0xC000)
{
mapper_pntr->WriteMemory(addr, value);
}
else if (addr < 0xD000)
{
RAM[addr - 0xC000] = value;
}
else if (addr < 0xE000)
{
RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)] = value;
}
else if (addr < 0xF000)
{
RAM[addr - 0xE000] = value;
}
else if (addr < 0xFE00)
{
RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)] = value;
}
else if (addr < 0xFEA0)
{
if (ppu_pntr->OAM_access_write) { OAM[addr - 0xFE00] = value; }
}
else if (addr < 0xFF00)
{
// unmapped, writing has no effect
}
else if (addr < 0xFF80)
{
Write_Registers(addr, value);
}
else if (addr < 0xFFFF)
{
ZP_RAM[addr - 0xFF80] = value;
}
else
{
Write_Registers(addr, value);
}
}
uint8_t MemoryManager::PeekMemory(uint32_t addr)
{
if (ppu_pntr->DMA_start)
{
// some of gekkio's tests require these to be accessible during DMA
if (addr < 0x8000)
{
if (ppu_pntr->DMA_addr < 0x80)
{
return 0xFF;
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else if ((addr >= 0xE000) && (addr < 0xF000))
{
return RAM[addr - 0xE000];
}
else if ((addr >= 0xF000) && (addr < 0xFE00))
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && ppu_pntr->DMA_OAM_access)
{
return OAM[addr - 0xFE00];
}
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
{
return Read_Registers(addr);
}
else if ((addr >= 0xFF80))
{
return ZP_RAM[addr - 0xFF80];
}
return 0xFF;
}
if (addr < 0x900)
{
if (addr < 0x100)
{
// return Either BIOS ROM or Game ROM
if ((GB_bios_register & 0x1) == 0)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else if (addr >= 0x200)
{
// return Either BIOS ROM or Game ROM
if (((GB_bios_register & 0x1) == 0) && is_GBC)
{
return bios_rom[addr]; // Return BIOS
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else
{
return mapper_pntr->ReadMemory(addr);
}
}
else if (addr < 0x8000)
{
return mapper_pntr->PeekMemory(addr);
}
else if (addr < 0xA000)
{
if (ppu_pntr->VRAM_access_read) { return VRAM[(VRAM_Bank * 0x2000) + (addr - 0x8000)]; }
else { return 0xFF; }
}
else if (addr < 0xC000)
{
return mapper_pntr->PeekMemory(addr);
}
else if (addr < 0xD000)
{
return RAM[addr - 0xC000];
}
else if (addr < 0xE000)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xD000)];
}
else if (addr < 0xF000)
{
return RAM[addr - 0xE000];
}
else if (addr < 0xFE00)
{
return RAM[(RAM_Bank * 0x1000) + (addr - 0xF000)];
}
else if (addr < 0xFEA0)
{
if (ppu_pntr->OAM_access_read) { return OAM[addr - 0xFE00]; }
else { return 0xFF; }
}
else if (addr < 0xFF00)
{
// unmapped memory, returns 0xFF
return 0xFF;
}
else if (addr < 0xFF80)
{
return Read_Registers(addr);
}
else if (addr < 0xFFFF)
{
return ZP_RAM[addr - 0xFF80];
}
else
{
return Read_Registers(addr);
}
}
uint8_t MemoryManager::Read_Registers(uint32_t addr)
{
uint8_t ret = 0;
switch (addr)
{
// Read Input
case 0xFF00:
lagged = false;
ret = input_register;
break;
// Serial data port
case 0xFF01:
ret = serialport_pntr->ReadReg(addr);
break;
// Serial port control
case 0xFF02:
ret = serialport_pntr->ReadReg(addr);
break;
// Timer Registers
case 0xFF04:
case 0xFF05:
case 0xFF06:
case 0xFF07:
ret = timer_pntr->ReadReg(addr);
break;
// Interrupt flags
case 0xFF0F:
ret = REG_FF0F_OLD;
break;
// audio regs
case 0xFF10:
case 0xFF11:
case 0xFF12:
case 0xFF13:
case 0xFF14:
case 0xFF16:
case 0xFF17:
case 0xFF18:
case 0xFF19:
case 0xFF1A:
case 0xFF1B:
case 0xFF1C:
case 0xFF1D:
case 0xFF1E:
case 0xFF20:
case 0xFF21:
case 0xFF22:
case 0xFF23:
case 0xFF24:
case 0xFF25:
case 0xFF26:
case 0xFF30:
case 0xFF31:
case 0xFF32:
case 0xFF33:
case 0xFF34:
case 0xFF35:
case 0xFF36:
case 0xFF37:
case 0xFF38:
case 0xFF39:
case 0xFF3A:
case 0xFF3B:
case 0xFF3C:
case 0xFF3D:
case 0xFF3E:
case 0xFF3F:
ret = psg_pntr->ReadReg(addr);
break;
// PPU Regs
case 0xFF40:
case 0xFF41:
case 0xFF42:
case 0xFF43:
case 0xFF44:
case 0xFF45:
case 0xFF46:
case 0xFF47:
case 0xFF48:
case 0xFF49:
case 0xFF4A:
case 0xFF4B:
ret = ppu_pntr->ReadReg(addr);
break;
// Speed Control for GBC
case 0xFF4D:
if (GBC_compat)
{
ret = (uint8_t)(((double_speed ? 1 : 0) << 7) + ((speed_switch ? 1 : 0)));
}
else
{
ret = 0xFF;
}
break;
case 0xFF4F: // VBK
if (GBC_compat)
{
ret = (uint8_t)(0xFE | VRAM_Bank);
}
else
{
ret = 0xFF;
}
break;
// Bios control register. Not sure if it is readable
case 0xFF50:
ret = 0xFF;
break;
// PPU Regs for GBC
case 0xFF51:
case 0xFF52:
case 0xFF53:
case 0xFF54:
case 0xFF55:
if (GBC_compat)
{
ret = ppu_pntr->ReadReg(addr);
}
else
{
ret = 0xFF;
}
break;
case 0xFF56:
if (GBC_compat)
{
// can receive data
if ((IR_reg & 0xC0) == 0xC0)
{
ret = IR_reg;
}
else
{
ret = (uint8_t)(IR_reg | 2);
}
}
else
{
ret = 0xFF;
}
break;
case 0xFF68:
case 0xFF69:
case 0xFF6A:
case 0xFF6B:
if (GBC_compat)
{
ret = ppu_pntr->ReadReg(addr);
}
else
{
ret = 0xFF;
}
break;
// Speed Control for GBC
case 0xFF70:
if (GBC_compat)
{
ret = (uint8_t)RAM_Bank;
}
else
{
ret = 0xFF;
}
break;
case 0xFF6C:
if (GBC_compat) { ret = undoc_6C; }
else { ret = 0xFF; }
break;
case 0xFF72:
if (is_GBC) { ret = undoc_72; }
else { ret = 0xFF; }
break;
case 0xFF73:
if (is_GBC) { ret = undoc_73; }
else { ret = 0xFF; }
break;
case 0xFF74:
if (GBC_compat) { ret = undoc_74; }
else { ret = 0xFF; }
break;
case 0xFF75:
if (is_GBC) { ret = undoc_75; }
else { ret = 0xFF; }
break;
case 0xFF76:
if (is_GBC) { ret = undoc_76; }
else { ret = 0xFF; }
break;
case 0xFF77:
if (is_GBC) { ret = undoc_77; }
else { ret = 0xFF; }
break;
// interrupt control register
case 0xFFFF:
ret = REG_FFFF;
break;
default:
ret = 0xFF;
break;
}
return ret;
}
void MemoryManager::Write_Registers(uint32_t addr, uint8_t value)
{
// check for high to low transitions that trigger IRQs
uint8_t contr_prev = input_register;
switch (addr)
{
// select input
case 0xFF00:
input_register &= 0xCF;
input_register |= (uint8_t)(value & 0x30); // top 2 bits always 1
input_register &= 0xF0;
if ((input_register & 0x30) == 0x20)
{
input_register |= (uint8_t)(controller_state & 0xF);
}
else if ((input_register & 0x30) == 0x10)
{
input_register |= (uint8_t)((controller_state & 0xF0) >> 4);
}
else if ((input_register & 0x30) == 0x00)
{
// if both polls are set, then a bit is zero if either or both pins are zero
uint8_t temp = (uint8_t)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4));
input_register |= temp;
}
else
{
input_register |= 0xF;
}
// check for interrupts
if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) ||
((contr_prev & 4) > 0) && ((input_register & 4) == 0) ||
((contr_prev & 2) > 0) && ((input_register & 2) == 0) ||
((contr_prev & 1) > 0) && ((input_register & 1) == 0))
{
if (((REG_FFFF & 0x10) > 0)) { cpu_pntr->FlagI = true; }
REG_FF0F |= 0x10;
}
break;
// Serial data port
case 0xFF01:
serialport_pntr->WriteReg(addr, value);
break;
// Serial port control
case 0xFF02:
serialport_pntr->WriteReg(addr, value);
break;
// Timer Registers
case 0xFF04:
case 0xFF05:
case 0xFF06:
case 0xFF07:
timer_pntr->WriteReg(addr, value);
break;
// Interrupt flags
case 0xFF0F:
REG_FF0F = (uint8_t)(0xE0 | value);
// check if enabling any of the bits triggered an IRQ
for (int i = 0; i < 5; i++)
{
if (((REG_FFFF & (1 << i)) > 0) && ((REG_FF0F & (1 << i)) > 0))
{
cpu_pntr->FlagI = true;
}
}
// if no bits are in common between flags and enables, de-assert the IRQ
if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu_pntr->FlagI = false; }
break;
// audio regs
case 0xFF10:
case 0xFF11:
case 0xFF12:
case 0xFF13:
case 0xFF14:
case 0xFF16:
case 0xFF17:
case 0xFF18:
case 0xFF19:
case 0xFF1A:
case 0xFF1B:
case 0xFF1C:
case 0xFF1D:
case 0xFF1E:
case 0xFF20:
case 0xFF21:
case 0xFF22:
case 0xFF23:
case 0xFF24:
case 0xFF25:
case 0xFF26:
case 0xFF30:
case 0xFF31:
case 0xFF32:
case 0xFF33:
case 0xFF34:
case 0xFF35:
case 0xFF36:
case 0xFF37:
case 0xFF38:
case 0xFF39:
case 0xFF3A:
case 0xFF3B:
case 0xFF3C:
case 0xFF3D:
case 0xFF3E:
case 0xFF3F:
psg_pntr->WriteReg(addr, value);
break;
// PPU Regs
case 0xFF40:
case 0xFF41:
case 0xFF42:
case 0xFF43:
case 0xFF44:
case 0xFF45:
case 0xFF46:
ppu_pntr->WriteReg(addr, value);
break;
case 0xFF47:
case 0xFF48:
case 0xFF49:
ppu_pntr->WriteReg(addr, value);
compute_palettes();
break;
case 0xFF4A:
case 0xFF4B:
ppu_pntr->WriteReg(addr, value);
break;
// GBC compatibility register (I think)
case 0xFF4C:
if ((value != 0xC0) && (value != 0x80))// && (value != 0xFF) && (value != 0x04))
{
GBC_compat = false;
// cpu operation is a function of hardware only
//cpu.is_GBC = GBC_compat;
}
break;
// Speed Control for GBC
case 0xFF4D:
if (GBC_compat)
{
speed_switch = (value & 1) > 0;
}
break;
// VBK
case 0xFF4F:
if (GBC_compat && !ppu_pntr->HDMA_active)
{
VRAM_Bank = (uint8_t)(value & 1);
}
break;
// Bios control register. Writing 1 permanently disables BIOS until a power cycle occurs
case 0xFF50:
// Console.WriteLine(value);
if (GB_bios_register == 0)
{
GB_bios_register = value;
}
break;
// PPU Regs for GBC
case 0xFF51:
case 0xFF52:
case 0xFF53:
case 0xFF54:
case 0xFF55:
if (GBC_compat)
{
ppu_pntr->WriteReg(addr, value);
}
break;
case 0xFF56:
if (is_GBC)
{
IR_reg = (uint8_t)((value & 0xC1) | (IR_reg & 0x3E));
// send IR signal out
if ((IR_reg & 0x1) == 0x1) { IR_signal = (uint8_t)(0 | IR_mask); }
else { IR_signal = 2; }
// receive own signal if IR on and receive on
if ((IR_reg & 0xC1) == 0xC1) { IR_self = (uint8_t)(0 | IR_mask); }
else { IR_self = 2; }
IR_write = 8;
}
break;
case 0xFF68:
case 0xFF69:
case 0xFF6A:
case 0xFF6B:
//if (GBC_compat)
//{
ppu_pntr->WriteReg(addr, value);
//}
break;
// RAM Bank in GBC mode
case 0xFF70:
//Console.WriteLine(value);
if (GBC_compat)
{
RAM_Bank = value & 7;
if (RAM_Bank == 0) { RAM_Bank = 1; }
}
break;
case 0xFF6C:
if (GBC_compat) { undoc_6C |= (uint8_t)(value & 1); }
break;
case 0xFF72:
if (is_GBC) { undoc_72 = value; }
break;
case 0xFF73:
if (is_GBC) { undoc_73 = value; }
break;
case 0xFF74:
if (GBC_compat) { undoc_74 = value; }
break;
case 0xFF75:
if (is_GBC) { undoc_75 |= (uint8_t)(value & 0x70); }
break;
case 0xFF76:
// read only
break;
case 0xFF77:
// read only
break;
// interrupt control register
case 0xFFFF:
REG_FFFF = value;
// check if enabling any of the bits triggered an IRQ
for (int i = 0; i < 5; i++)
{
if (((REG_FFFF & (1 << i)) > 0) && ((REG_FF0F & (1 << i)) > 0))
{
cpu_pntr->FlagI = true;
}
}
// if no bits are in common between flags and enables, de-assert the IRQ
if (((REG_FF0F & 0x1F) & REG_FFFF) == 0) { cpu_pntr->FlagI = false; }
break;
default:
//Console.Write(addr);
//Console.Write(" ");
//Console.WriteLine(value);
break;
}
}
void MemoryManager::compute_palettes()
{
for (int i = 0; i < 4; i++)
{
color_palette_OBJ[i] = color_palette[(ppu_pntr->obj_pal_0 >> (i * 2)) & 3];
color_palette_OBJ[i + 4] = color_palette[(ppu_pntr->obj_pal_1 >> (i * 2)) & 3];
color_palette_BG[i] = color_palette[(ppu_pntr->BGP >> (i * 2)) & 3];
}
}
void MemoryManager::do_controller_check()
{
lagged = false;
// update the controller state on VBlank
controller_state = new_controller_1;
Acc_X_state = new_accx;
Acc_Y_state = new_accy;
// check if new input changed the input register and triggered IRQ
uint8_t contr_prev = input_register;
input_register &= 0xF0;
if ((input_register & 0x30) == 0x20)
{
input_register |= (uint8_t)(controller_state & 0xF);
}
else if ((input_register & 0x30) == 0x10)
{
input_register |= (uint8_t)((controller_state & 0xF0) >> 4);
}
else if ((input_register & 0x30) == 0x00)
{
// if both polls are set, then a bit is zero if either or both pins are zero
uint8_t temp = (uint8_t)((controller_state & 0xF) & ((controller_state & 0xF0) >> 4));
input_register |= temp;
}
else
{
input_register |= 0xF;
}
// check for interrupts
if (((contr_prev & 8) > 0) && ((input_register & 8) == 0) ||
((contr_prev & 4) > 0) && ((input_register & 4) == 0) ||
((contr_prev & 2) > 0) && ((input_register & 2) == 0) ||
((contr_prev & 1) > 0) && ((input_register & 1) == 0))
{
if ((REG_FFFF & 0x10) > 0) { cpu_pntr->FlagI = true; }
REG_FF0F |= 0x10;
}
}
void MemoryManager::SendVideoBuffer()
{
if (GBC_compat)
{
if (!ppu_pntr->blank_frame)
{
for (int j = 0; j < (160 * 144); j++) { frame_buffer[j] = vidbuffer[j]; }
}
ppu_pntr->blank_frame = false;
}
else
{
if (ppu_pntr->blank_frame)
{
for (int i = 0; i < (160 * 144); i++)
{
vidbuffer[i] = color_palette[0];
}
}
for (int j = 0; j < (160 * 144); j++) { frame_buffer[j] = vidbuffer[j]; }
ppu_pntr->blank_frame = false;
}
}
}

View File

@ -1,400 +0,0 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace GBHawk
{
class LR35902;
class Timer;
class PPU;
class GBAudio;
class SerialPort;
class Mapper;
class MemoryManager
{
public:
MemoryManager()
{
};
uint8_t ReadMemory(uint32_t addr);
uint8_t PeekMemory(uint32_t addr);
void WriteMemory(uint32_t addr, uint8_t value);
uint8_t Read_Registers(uint32_t addr);
void Write_Registers(uint32_t addr, uint8_t value);
void compute_palettes();
void do_controller_check();
void SendVideoBuffer();
#pragma region Declarations
PPU* ppu_pntr = nullptr;
GBAudio* psg_pntr = nullptr;
LR35902* cpu_pntr = nullptr;
Timer* timer_pntr = nullptr;
SerialPort* serialport_pntr = nullptr;
Mapper* mapper_pntr = nullptr;
uint8_t* ROM = nullptr;
uint8_t* Cart_RAM = nullptr;
uint8_t* bios_rom = nullptr;
// initialized by core loading, not savestated
uint32_t ROM_Length;
uint32_t ROM_Mapper;
uint32_t Cart_RAM_Length;
// passed in on frame advace, not stated
uint8_t new_controller_1;
uint32_t new_accx;
uint32_t new_accy;
// State
bool lagged;
bool is_GBC;
bool GBC_compat;
bool speed_switch, double_speed;
bool in_vblank;
bool in_vblank_old;
bool vblank_rise;
bool HDMA_transfer;
bool Use_MT;
bool has_bat;
uint8_t GB_bios_register;
uint8_t IR_reg, IR_mask, IR_signal, IR_receive, IR_self;
// several undocumented GBC Registers
uint8_t undoc_6C, undoc_72, undoc_73, undoc_74, undoc_75, undoc_76, undoc_77;
uint8_t controller_state;
uint8_t REG_FFFF, REG_FF0F, REG_FF0F_OLD;
int32_t _scanlineCallbackLine;
uint8_t input_register;
uint32_t RAM_Bank = 0;
uint32_t VRAM_Bank = 0;
uint32_t IR_write;
uint32_t addr_access;
uint32_t Acc_X_state;
uint32_t Acc_Y_state;
uint8_t ZP_RAM[0x80] = {};
uint8_t RAM[0x8000] = {};
uint8_t VRAM[0x4000] = {};
uint8_t OAM[0xA0] = {};
uint8_t header[0x50] = {};
uint32_t vidbuffer[160 * 144] = {};
uint32_t frame_buffer[160 * 144] = {};
uint32_t color_palette[4] = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 };
const uint8_t GBA_override[13] = { 0xFF, 0x00, 0xCD, 0x03, 0x35, 0xAA, 0x31, 0x90, 0x94, 0x00, 0x00, 0x00, 0x00 };
// these two arrays are computed on calls from the GPU Viewer to get the GB palettes
uint32_t color_palette_BG[4] = {};
uint32_t color_palette_OBJ[8] = {};
#pragma endregion
#pragma region Functions
// NOTE: only called when checks pass that the files are correct
void Load_BIOS(uint8_t* bios, bool GBC_console, bool GBC_as_GBA)
{
if (GBC_console)
{
bios_rom = new uint8_t[2304];
memcpy(bios_rom, bios, 2304);
is_GBC = true;
// set up IR variables if it's GBC
IR_mask = 0; IR_reg = 0x3E; IR_receive = 2; IR_self = 2; IR_signal = 2;
if (GBC_as_GBA)
{
for (int i = 0; i < 13; i++)
{
bios_rom[i + 0xF3] = (uint8_t)((GBA_override[i] + bios_rom[i + 0xF3]) & 0xFF);
}
IR_mask = 2;
}
}
else
{
bios_rom = new uint8_t[256];
memcpy(bios_rom, bios, 256);
}
}
void Load_ROM(uint8_t* ext_rom_1, uint32_t ext_rom_size_1)
{
ROM = new uint8_t[ext_rom_size_1];
memcpy(ROM, ext_rom_1, ext_rom_size_1);
ROM_Length = ext_rom_size_1;
std::memcpy(header, ext_rom_1 + 0x100, 0x50);
}
// Switch Speed (GBC only)
uint32_t SpeedFunc(uint32_t temp)
{
if (is_GBC)
{
if (speed_switch)
{
speed_switch = false;
uint32_t ret = double_speed ? 70224 * 2 : 70224 * 2; // actual time needs checking
double_speed = !double_speed;
return ret;
}
// if we are not switching speed, return 0
return 0;
}
// if we are in GB mode, return 0 indicating not switching speed
return 0;
}
void Register_Reset()
{
input_register = 0xCF; // not reading any input
REG_FFFF = 0;
REG_FF0F = 0xE0;
REG_FF0F_OLD = 0xE0;
//undocumented registers
undoc_6C = 0xFE;
undoc_72 = 0;
undoc_73 = 0;
undoc_74 = 0;
undoc_75 = 0x8F;
undoc_76 = 0;
undoc_77 = 0;
}
#pragma endregion
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
saver = bool_saver(lagged, saver);
saver = bool_saver(is_GBC, saver);
saver = bool_saver(GBC_compat, saver);
saver = bool_saver(speed_switch, saver);
saver = bool_saver(double_speed, saver);
saver = bool_saver(in_vblank, saver);
saver = bool_saver(in_vblank_old, saver);
saver = bool_saver(vblank_rise, saver);
saver = bool_saver(HDMA_transfer, saver);
saver = bool_saver(Use_MT, saver);
saver = bool_saver(has_bat, saver);
saver = byte_saver(GB_bios_register, saver);
saver = byte_saver(IR_reg, saver);
saver = byte_saver(IR_mask, saver);
saver = byte_saver(IR_signal, saver);
saver = byte_saver(IR_receive, saver);
saver = byte_saver(IR_self, saver);
saver = byte_saver(undoc_6C, saver);
saver = byte_saver(undoc_72, saver);
saver = byte_saver(undoc_73, saver);
saver = byte_saver(undoc_74, saver);
saver = byte_saver(undoc_75, saver);
saver = byte_saver(undoc_76, saver);
saver = byte_saver(undoc_77, saver);
saver = byte_saver(controller_state, saver);
saver = byte_saver(REG_FFFF, saver);
saver = byte_saver(REG_FF0F, saver);
saver = byte_saver(REG_FF0F_OLD, saver);
saver = byte_saver(input_register, saver);
saver = int_saver(_scanlineCallbackLine, saver);
saver = int_saver(RAM_Bank, saver);
saver = int_saver(VRAM_Bank, saver);
saver = int_saver(IR_write, saver);
saver = int_saver(addr_access, saver);
saver = int_saver(Acc_X_state, saver);
saver = int_saver(Acc_Y_state, saver);
saver = byte_array_saver(ZP_RAM, saver, 0x80);
saver = byte_array_saver(RAM, saver, 0x8000);
saver = byte_array_saver(VRAM, saver, 0x4000);
saver = byte_array_saver(OAM, saver, 0xA0);
saver = byte_array_saver(header, saver, 0x50);
saver = int_array_saver(vidbuffer, saver, 160 * 144);
saver = int_array_saver(frame_buffer, saver, 160 * 144);
if (Cart_RAM_Length != 0)
{
saver = byte_array_saver(Cart_RAM, saver, Cart_RAM_Length);
}
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
loader = bool_loader(&lagged, loader);
loader = bool_loader(&is_GBC, loader);
loader = bool_loader(&GBC_compat, loader);
loader = bool_loader(&speed_switch, loader);
loader = bool_loader(&double_speed, loader);
loader = bool_loader(&in_vblank, loader);
loader = bool_loader(&in_vblank_old, loader);
loader = bool_loader(&vblank_rise, loader);
loader = bool_loader(&HDMA_transfer, loader);
loader = bool_loader(&Use_MT, loader);
loader = bool_loader(&has_bat, loader);
loader = byte_loader(&GB_bios_register, loader);
loader = byte_loader(&IR_reg, loader);
loader = byte_loader(&IR_mask, loader);
loader = byte_loader(&IR_signal, loader);
loader = byte_loader(&IR_receive, loader);
loader = byte_loader(&IR_self, loader);
loader = byte_loader(&undoc_6C, loader);
loader = byte_loader(&undoc_72, loader);
loader = byte_loader(&undoc_73, loader);
loader = byte_loader(&undoc_74, loader);
loader = byte_loader(&undoc_75, loader);
loader = byte_loader(&undoc_76, loader);
loader = byte_loader(&undoc_77, loader);
loader = byte_loader(&controller_state, loader);
loader = byte_loader(&REG_FFFF, loader);
loader = byte_loader(&REG_FF0F, loader);
loader = byte_loader(&REG_FF0F_OLD, loader);
loader = byte_loader(&input_register, loader);
loader = sint_loader(&_scanlineCallbackLine, loader);
loader = int_loader(&RAM_Bank, loader);
loader = int_loader(&VRAM_Bank, loader);
loader = int_loader(&IR_write, loader);
loader = int_loader(&addr_access, loader);
loader = int_loader(&Acc_X_state, loader);
loader = int_loader(&Acc_Y_state, loader);
loader = byte_array_loader(ZP_RAM, loader, 0x80);
loader = byte_array_loader(RAM, loader, 0x8000);
loader = byte_array_loader(VRAM, loader, 0x4000);
loader = byte_array_loader(OAM, loader, 0xA0);
loader = byte_array_loader(header, loader, 0x50);
loader = int_array_loader(vidbuffer, loader, 160 * 144);
loader = int_array_loader(frame_buffer, loader, 160 * 144);
if (Cart_RAM_Length != 0)
{
loader = byte_array_loader(Cart_RAM, loader, Cart_RAM_Length);
}
return loader;
}
uint8_t* bool_saver(bool to_save, uint8_t* saver)
{
*saver = (uint8_t)(to_save ? 1 : 0); saver++;
return saver;
}
uint8_t* byte_saver(uint8_t to_save, uint8_t* saver)
{
*saver = to_save; saver++;
return saver;
}
uint8_t* int_saver(uint32_t to_save, uint8_t* saver)
{
*saver = (uint8_t)(to_save & 0xFF); saver++; *saver = (uint8_t)((to_save >> 8) & 0xFF); saver++;
*saver = (uint8_t)((to_save >> 16) & 0xFF); saver++; *saver = (uint8_t)((to_save >> 24) & 0xFF); saver++;
return saver;
}
uint8_t* byte_array_saver(uint8_t* to_save, uint8_t* saver, int length)
{
for (int i = 0; i < length; i++) { *saver = to_save[i]; saver++; }
return saver;
}
uint8_t* int_array_saver(uint32_t* to_save, uint8_t* saver, int length)
{
for (int i = 0; i < length; i++)
{
*saver = (uint8_t)(to_save[i] & 0xFF); saver++; *saver = (uint8_t)((to_save[i] >> 8) & 0xFF); saver++;
*saver = (uint8_t)((to_save[i] >> 16) & 0xFF); saver++; *saver = (uint8_t)((to_save[i] >> 24) & 0xFF); saver++;
}
return saver;
}
uint8_t* bool_loader(bool* to_load, uint8_t* loader)
{
to_load[0] = *to_load == 1; loader++;
return loader;
}
uint8_t* byte_loader(uint8_t* to_load, uint8_t* loader)
{
to_load[0] = *loader; loader++;
return loader;
}
uint8_t* int_loader(uint32_t* to_load, uint8_t* loader)
{
to_load[0] = *loader; loader++; to_load[0] |= (*loader << 8); loader++;
to_load[0] |= (*loader << 16); loader++; to_load[0] |= (*loader << 24); loader++;
return loader;
}
uint8_t* sint_loader(int32_t* to_load, uint8_t* loader)
{
to_load[0] = *loader; loader++; to_load[0] |= (*loader << 8); loader++;
to_load[0] |= (*loader << 16); loader++; to_load[0] |= (*loader << 24); loader++;
return loader;
}
uint8_t* byte_array_loader(uint8_t* to_load, uint8_t* loader, int length)
{
for (int i = 0; i < length; i++) { to_load[i] = *loader; loader++; }
return loader;
}
uint8_t* int_array_loader(uint32_t* to_load, uint8_t* loader, int length)
{
for (int i = 0; i < length; i++)
{
to_load[i] = *loader; loader++; to_load[i] |= (*loader << 8); loader++;
to_load[i] |= (*loader << 16); loader++; to_load[i] |= (*loader << 24); loader++;
}
return loader;
}
#pragma endregion
};
}

View File

@ -1,32 +0,0 @@
#include <cstdint>
#include <iomanip>
#include <string>
#include "Memory.h"
#include "PPU.h"
using namespace std;
namespace GBHawk
{
uint8_t PPU::ReadMemory(uint32_t addr)
{
return mem_ctrl->ReadMemory(addr);
}
void PPU::vblank_process()
{
in_vblank[0] = true;
vblank_rise[0] = true;
if (scanlineCallback && (_scanlineCallbackLine[0] == -1))
{
scanlineCallback();
}
mem_ctrl->do_controller_check();
// send the image on VBlank
mem_ctrl->SendVideoBuffer();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,210 +0,0 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace GBHawk
{
class SerialPort
{
public:
SerialPort()
{
};
bool* GBC_compat = nullptr;
bool* FlagI = nullptr;
uint8_t* REG_FFFF = nullptr;
uint8_t* REG_FF0F = nullptr;
bool serial_start;
bool can_pulse;
uint8_t serial_control;
uint8_t serial_data;
uint8_t going_out;
uint8_t coming_in;
uint32_t serial_clock;
uint32_t serial_bits;
uint32_t clk_rate;
uint8_t ReadReg(int addr)
{
switch (addr)
{
case 0xFF01:
return serial_data;
case 0xFF02:
return serial_control;
}
return 0xFF;
}
void WriteReg(int addr, uint8_t value)
{
switch (addr)
{
case 0xFF01:
serial_data = value;
break;
case 0xFF02:
if (((value & 0x80) > 0) && !serial_start)
{
serial_start = true;
serial_bits = 8;
if ((value & 1) > 0)
{
if (((value & 2) > 0) && GBC_compat[0])
{
clk_rate = 16;
}
else
{
clk_rate = 512;
}
serial_clock = clk_rate;
can_pulse = true;
}
else
{
clk_rate = -1;
serial_clock = clk_rate;
can_pulse = false;
}
}
else if (serial_start)
{
if ((value & 1) > 0)
{
if (((value & 2) > 0) && GBC_compat[0])
{
clk_rate = 16;
}
else
{
clk_rate = 512;
}
serial_clock = clk_rate;
can_pulse = true;
}
else
{
clk_rate = -1;
serial_clock = clk_rate;
can_pulse = false;
}
}
if (GBC_compat[0])
{
serial_control = (uint8_t)(0x7C | (value & 0x83)); // extra CGB bit
}
else
{
serial_control = (uint8_t)(0x7E | (value & 0x81)); // middle six bits always 1
}
break;
}
}
void serial_transfer_tick()
{
if (serial_start)
{
if (serial_clock > 0) { serial_clock--; }
if (serial_clock == 0)
{
if (serial_bits > 0)
{
serial_data = (uint8_t)((serial_data << 1) | coming_in);
serial_bits--;
if (serial_bits == 0)
{
serial_control &= 0x7F;
serial_start = false;
if ((REG_FFFF[0] & 0x8) > 0) { FlagI[0] = true; }
REG_FF0F[0] |= 0x08;
}
else
{
serial_clock = clk_rate;
if (clk_rate > 0) { can_pulse = true; }
}
}
}
}
}
void Reset()
{
serial_control = 0x7E;
serial_data = 0x00;
serial_start = false;
serial_clock = 0;
serial_bits = 0;
clk_rate = 16;
going_out = 0;
coming_in = 1;
can_pulse = false;
}
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
*saver = (uint8_t)(serial_start ? 1 : 0); saver++;
*saver = (uint8_t)(can_pulse ? 1 : 0); saver++;
*saver = serial_control; saver++;
*saver = serial_data; saver++;
*saver = going_out; saver++;
*saver = coming_in; saver++;
*saver = (uint8_t)(serial_clock & 0xFF); saver++; *saver = (uint8_t)((serial_clock >> 8) & 0xFF); saver++;
*saver = (uint8_t)((serial_clock >> 16) & 0xFF); saver++; *saver = (uint8_t)((serial_clock >> 24) & 0xFF); saver++;
*saver = (uint8_t)(serial_bits & 0xFF); saver++; *saver = (uint8_t)((serial_bits >> 8) & 0xFF); saver++;
*saver = (uint8_t)((serial_bits >> 16) & 0xFF); saver++; *saver = (uint8_t)((serial_bits >> 24) & 0xFF); saver++;
*saver = (uint8_t)(clk_rate & 0xFF); saver++; *saver = (uint8_t)((clk_rate >> 8) & 0xFF); saver++;
*saver = (uint8_t)((clk_rate >> 16) & 0xFF); saver++; *saver = (uint8_t)((clk_rate >> 24) & 0xFF); saver++;
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
serial_start = *loader == 1; loader++;
can_pulse = *loader == 1; loader++;
serial_control = *loader; loader++;
serial_data = *loader; loader++;
going_out = *loader; loader++;
coming_in = *loader; loader++;
serial_clock = *loader; loader++; serial_clock |= (*loader << 8); loader++;
serial_clock |= (*loader << 16); loader++; serial_clock |= (*loader << 24); loader++;
serial_bits = *loader; loader++; serial_bits |= (*loader << 8); loader++;
serial_bits |= (*loader << 16); loader++; serial_bits |= (*loader << 24); loader++;
clk_rate = *loader; loader++; clk_rate |= (*loader << 8); loader++;
clk_rate |= (*loader << 16); loader++; clk_rate |= (*loader << 24); loader++;
return loader;
}
#pragma endregion
};
}

View File

@ -1,221 +0,0 @@
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <string>
using namespace std;
namespace GBHawk
{
class Timer
{
public:
Timer()
{
};
bool* FlagI = nullptr;
uint8_t* REG_FFFF = nullptr;
uint8_t* REG_FF0F = nullptr;
uint64_t* CPU_cycle_pntr = nullptr;
bool old_state = false;
bool state = false;
bool reload_block = false;
uint8_t timer_reload = 0;
uint8_t timer = 0;
uint8_t timer_old = 0;
uint8_t timer_control = 0;
uint8_t pending_reload = 0;
uint32_t divider_reg = 0;
uint64_t next_free_cycle = 0;
uint8_t ReadReg(uint32_t addr)
{
uint8_t ret = 0;
switch (addr)
{
case 0xFF04: ret = (uint8_t)(divider_reg >> 8); break; // DIV register
case 0xFF05: ret = timer; break; // TIMA (Timer Counter)
case 0xFF06: ret = timer_reload; break; // TMA (Timer Modulo)
case 0xFF07: ret = timer_control; break; // TAC (Timer Control)
}
return ret;
}
void WriteReg(int addr, uint8_t value)
{
switch (addr)
{
// DIV register
case 0xFF04:
divider_reg = 0;
break;
// TIMA (Timer Counter)
case 0xFF05:
if (CPU_cycle_pntr[0] >= next_free_cycle)
{
timer_old = timer;
timer = value;
reload_block = true;
}
break;
// TMA (Timer Modulo)
case 0xFF06:
timer_reload = value;
if (CPU_cycle_pntr[0] < next_free_cycle)
{
timer = timer_reload;
timer_old = timer;
}
break;
// TAC (Timer Control)
case 0xFF07:
timer_control = (uint8_t)((timer_control & 0xf8) | (value & 0x7)); // only bottom 3 bits function
break;
}
}
void tick()
{
divider_reg++;
// pick a bit to test based on the current value of timer control
switch (timer_control & 3)
{
case 0:
state = (divider_reg & 0x200) > 0;
break;
case 1:
state = (divider_reg & 0x8) > 0;
break;
case 2:
state = (divider_reg & 0x20) > 0;
break;
case 3:
state = (divider_reg & 0x80) > 0;
break;
}
// And it with the state of the timer on/off bit
state &= (timer_control & 4) > 0;
// this procedure allows several glitchy timer ticks, since it only measures falling edge of the state
// so things like turning the timer off and resetting the divider will tick the timer
if (old_state && !state)
{
timer_old = timer;
timer++;
// if overflow happens, set the interrupt flag and reload the timer (if applicable)
if (timer < timer_old)
{
if ((timer_control & 4) > 0)
{
pending_reload = 4;
reload_block = false;
}
else
{
//TODO: Check if timer still gets reloaded if TAC diabled causes overflow
if ((REG_FFFF[0] & 0x4) > 0) { FlagI[0] = true; }
REG_FF0F[0] |= 0x04;
}
}
}
old_state = state;
if (pending_reload > 0)
{
pending_reload--;
if (pending_reload == 0 && !reload_block)
{
timer = timer_reload;
timer_old = timer;
next_free_cycle = 4 + CPU_cycle_pntr[0];
// set interrupts
if ((REG_FFFF[0] & 0x4) > 0) { FlagI[0] = true; }
REG_FF0F[0] |= 0x04;
}
}
}
void Reset()
{
divider_reg = 8; // probably always 8 but not confirmed for GB as far as I know
timer_reload = 0;
timer = 0;
timer_old = 0;
timer_control = 0xF8;
pending_reload = 0;
old_state = false;
state = false;
reload_block = false;
next_free_cycle = 0;
}
#pragma region State Save / Load
uint8_t* SaveState(uint8_t* saver)
{
*saver = (uint8_t)(old_state ? 1 : 0); saver++;
*saver = (uint8_t)(state ? 1 : 0); saver++;
*saver = (uint8_t)(reload_block ? 1 : 0); saver++;
*saver = timer_reload; saver++;
*saver = timer; saver++;
*saver = timer_old; saver++;
*saver = timer_control; saver++;
*saver = pending_reload; saver++;
*saver = (uint8_t)(divider_reg & 0xFF); saver++; *saver = (uint8_t)((divider_reg >> 8) & 0xFF); saver++;
*saver = (uint8_t)((divider_reg >> 16) & 0xFF); saver++; *saver = (uint8_t)((divider_reg >> 24) & 0xFF); saver++;
*saver = (uint8_t)(next_free_cycle & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 8) & 0xFF); saver++;
*saver = (uint8_t)((next_free_cycle >> 16) & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 24) & 0xFF); saver++;
*saver = (uint8_t)((next_free_cycle >> 32) & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 40) & 0xFF); saver++;
*saver = (uint8_t)((next_free_cycle >> 48) & 0xFF); saver++; *saver = (uint8_t)((next_free_cycle >> 56) & 0xFF); saver++;
return saver;
}
uint8_t* LoadState(uint8_t* loader)
{
old_state = *loader == 1; loader++;
state = *loader == 1; loader++;
reload_block = *loader == 1; loader++;
timer_reload = *loader; loader++;
timer = *loader; loader++;
timer_old = *loader; loader++;
timer_control = *loader; loader++;
pending_reload = *loader; loader++;
divider_reg = *loader; loader++; divider_reg |= (*loader << 8); loader++;
divider_reg |= (*loader << 16); loader++; divider_reg |= (*loader << 24); loader++;
next_free_cycle = *loader; loader++; next_free_cycle |= ((uint64_t)*loader << 8); loader++;
next_free_cycle |= ((uint64_t)*loader << 16); loader++; next_free_cycle |= ((uint64_t)*loader << 24); loader++;
next_free_cycle |= ((uint64_t)*loader << 32); loader++; next_free_cycle |= ((uint64_t)*loader << 40); loader++;
next_free_cycle |= ((uint64_t)*loader << 48); loader++; next_free_cycle |= ((uint64_t)*loader << 56); loader++;
return loader;
}
#pragma endregion
};
}

View File

@ -1,2 +0,0 @@
#define GBHawk_API __declspec(dllexport)
#define GBHawk_API __declspec(dllimport)