GBHawk: remove C++ core, not worth it, need to rethink design
This commit is contained in:
parent
86950c9c2e
commit
e6e70b6e35
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -1,5 +0,0 @@
|
|||
#ifdef _WIN32
|
||||
#define GBHawk_EXPORT extern "C" __declspec(dllexport)
|
||||
#elif __linux__
|
||||
#define GBHawk_EXPORT extern "C"
|
||||
#endif
|
|
@ -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>
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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(®_FFFF, loader);
|
||||
loader = byte_loader(®_FF0F, loader);
|
||||
loader = byte_loader(®_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
|
||||
};
|
||||
}
|
|
@ -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
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
#define GBHawk_API __declspec(dllexport)
|
||||
#define GBHawk_API __declspec(dllimport)
|
Loading…
Reference in New Issue