BizHawk/BizHawk.Emulation.Cores/Consoles/Nintendo/GBA/VBANext.cs

253 lines
7.6 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
2014-08-13 20:33:59 +00:00
using System.ComponentModel;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Components.ARM;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
[CoreAttributes("VBA-Next", "many authors", true, true, "cd508312a29ed8c29dacac1b11c2dce56c338a54", "https://github.com/libretro/vba-next")]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))]
2015-01-17 20:48:31 +00:00
public partial class VBANext : IEmulator, IVideoProvider, ISyncSoundProvider, IInputPollable,
IGBAGPUViewable, ISaveRam, IStatable, IDebuggable, ISettable<object, VBANext.SyncSettings>
{
IntPtr Core;
[CoreConstructor("GBA")]
public VBANext(byte[] file, CoreComm comm, GameInfo game, bool deterministic, object syncsettings)
{
2014-12-13 21:49:15 +00:00
var ser = new BasicServiceProvider(this);
ser.Register<IDisassemblable>(new ArmV4Disassembler());
ServiceProvider = ser;
CoreComm = comm;
byte[] biosfile = CoreComm.CoreFileProvider.GetFirmware("GBA", "Bios", true, "GBA bios file is mandatory.");
if (file.Length > 32 * 1024 * 1024)
throw new ArgumentException("ROM is too big to be a GBA ROM!");
if (biosfile.Length != 16 * 1024)
throw new ArgumentException("BIOS file is not exactly 16K!");
2014-08-13 20:33:59 +00:00
LibVBANext.FrontEndSettings FES = new LibVBANext.FrontEndSettings();
FES.saveType = (LibVBANext.FrontEndSettings.SaveType)game.GetInt("saveType", 0);
FES.flashSize = (LibVBANext.FrontEndSettings.FlashSize)game.GetInt("flashSize", 0x10000);
FES.enableRtc = game.GetInt("rtcEnabled", 0) != 0;
FES.mirroringEnable = game.GetInt("mirroringEnabled", 0) != 0;
Console.WriteLine("GameDB loaded settings: saveType={0}, flashSize={1}, rtcEnabled={2}, mirroringEnabled={3}",
FES.saveType, FES.flashSize, FES.enableRtc, FES.mirroringEnable);
2014-08-13 20:33:59 +00:00
2015-01-17 20:48:31 +00:00
_syncSettings = (SyncSettings)syncsettings ?? new SyncSettings();
2014-08-13 20:33:59 +00:00
DeterministicEmulation = deterministic;
2015-01-17 20:48:31 +00:00
FES.skipBios = _syncSettings.SkipBios;
FES.RTCUseRealTime = _syncSettings.RTCUseRealTime;
FES.RTCwday = (int)_syncSettings.RTCInitialDay;
FES.RTCyear = _syncSettings.RTCInitialTime.Year % 100;
FES.RTCmonth = _syncSettings.RTCInitialTime.Month - 1;
FES.RTCmday = _syncSettings.RTCInitialTime.Day;
FES.RTChour = _syncSettings.RTCInitialTime.Hour;
FES.RTCmin = _syncSettings.RTCInitialTime.Minute;
FES.RTCsec = _syncSettings.RTCInitialTime.Second;
2014-08-13 20:33:59 +00:00
if (DeterministicEmulation)
{
// FES.skipBios = false; // this is OK; it is deterministic and probably accurate
2014-08-13 20:33:59 +00:00
FES.RTCUseRealTime = false;
}
Core = LibVBANext.Create();
if (Core == IntPtr.Zero)
throw new InvalidOperationException("Create() returned nullptr!");
try
{
if (!LibVBANext.LoadRom(Core, file, (uint)file.Length, biosfile, (uint)biosfile.Length, FES))
throw new InvalidOperationException("LoadRom() returned false!");
Tracer = new TraceBuffer()
{
Header = "ARM7: PC, machine code, mnemonic, operands, registers (r0-r16)"
};
ser.Register<ITraceable>(Tracer);
CoreComm.VsyncNum = 262144;
CoreComm.VsyncDen = 4389;
CoreComm.NominalWidth = 240;
CoreComm.NominalHeight = 160;
GameCode = Encoding.ASCII.GetString(file, 0xac, 4);
Console.WriteLine("Game code \"{0}\"", GameCode);
savebuff = new byte[LibVBANext.BinStateSize(Core)];
savebuff2 = new byte[savebuff.Length + 13];
2014-08-16 05:45:31 +00:00
InitMemoryDomains();
2014-08-21 23:51:25 +00:00
InitRegisters();
2014-08-24 00:04:57 +00:00
InitCallbacks();
// todo: hook me up as a setting
SetupColors();
}
catch
{
Dispose();
throw;
}
}
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public void FrameAdvance(bool render, bool rendersound = true)
{
Frame++;
if (Controller["Power"])
LibVBANext.Reset(Core);
2014-12-05 02:27:39 +00:00
SyncTraceCallback();
2014-08-24 00:04:57 +00:00
2015-06-04 23:30:24 +00:00
IsLagFrame = LibVBANext.FrameAdvance(Core, GetButtons(Controller), videobuff, soundbuff, out numsamp, videopalette);
if (IsLagFrame)
LagCount++;
}
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; set; }
private ITraceable Tracer { get; set; }
public string SystemId { get { return "GBA"; } }
2014-08-13 20:33:59 +00:00
public bool DeterministicEmulation { get; private set; }
public void ResetCounters()
{
Frame = 0;
LagCount = 0;
IsLagFrame = false;
}
public string BoardName { get { return null; } }
/// <summary>
/// set in the ROM internal header
/// </summary>
public string GameCode { get; private set; }
public CoreComm CoreComm { get; private set; }
public void Dispose()
{
if (Core != IntPtr.Zero)
{
LibVBANext.Destroy(Core);
Core = IntPtr.Zero;
}
}
#region Debugging
2014-08-24 00:04:57 +00:00
LibVBANext.StandardCallback padcb;
LibVBANext.AddressCallback fetchcb;
LibVBANext.AddressCallback readcb;
LibVBANext.AddressCallback writecb;
LibVBANext.TraceCallback tracecb;
2014-08-24 00:04:57 +00:00
2014-12-05 02:27:39 +00:00
private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } }
TraceInfo Trace(uint addr, uint opcode)
2014-11-19 03:16:36 +00:00
{
return new TraceInfo
{
Disassembly = string.Format("{2:X8}: {0:X8} {1}", opcode, Darm.DisassembleStuff(addr, opcode), addr).PadRight(54),
RegisterInfo = regs.TraceString()
};
2014-11-19 03:16:36 +00:00
}
2014-08-24 00:04:57 +00:00
void InitCallbacks()
{
padcb = new LibVBANext.StandardCallback(() => InputCallbacks.Call());
fetchcb = new LibVBANext.AddressCallback((addr) => MemoryCallbacks.CallExecutes(addr));
readcb = new LibVBANext.AddressCallback((addr) => MemoryCallbacks.CallReads(addr));
writecb = new LibVBANext.AddressCallback((addr) => MemoryCallbacks.CallWrites(addr));
tracecb = new LibVBANext.TraceCallback((addr, opcode) => Tracer.Put(Trace(addr, opcode)));
_inputCallbacks.ActiveChanged += SyncPadCallback;
2014-12-05 02:27:39 +00:00
_memorycallbacks.ActiveChanged += SyncMemoryCallbacks;
}
void SyncPadCallback()
{
2014-12-05 02:27:39 +00:00
LibVBANext.SetPadCallback(Core, InputCallbacks.Any() ? padcb : null);
2014-08-24 00:04:57 +00:00
}
2014-12-05 02:27:39 +00:00
void SyncMemoryCallbacks()
2014-08-24 00:04:57 +00:00
{
LibVBANext.SetFetchCallback(Core, MemoryCallbacks.HasExecutes ? fetchcb : null);
LibVBANext.SetReadCallback(Core, MemoryCallbacks.HasReads ? readcb : null);
LibVBANext.SetWriteCallback(Core, MemoryCallbacks.HasWrites ? writecb : null);
2014-12-05 02:27:39 +00:00
}
2014-12-05 02:39:42 +00:00
void SyncTraceCallback()
2014-12-05 02:27:39 +00:00
{
LibVBANext.SetTraceCallback(Core, Tracer.Enabled ? tracecb : null);
2014-08-24 00:04:57 +00:00
}
2014-08-21 23:51:25 +00:00
VBARegisterHelper regs;
void InitRegisters()
{
regs = new VBARegisterHelper(Core);
}
#endregion
#region Controller
public ControllerDefinition ControllerDefinition { get { return GBA.GBAController; } }
public IController Controller { get; set; }
2015-06-04 23:30:24 +00:00
public static LibVBANext.Buttons GetButtons(IController c)
{
LibVBANext.Buttons ret = 0;
foreach (string s in Enum.GetNames(typeof(LibVBANext.Buttons)))
{
2015-06-04 23:30:24 +00:00
if (c[s])
ret |= (LibVBANext.Buttons)Enum.Parse(typeof(LibVBANext.Buttons), s);
}
return ret;
}
#endregion
#region SoundProvider
short[] soundbuff = new short[2048];
int numsamp;
public ISoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
{
samples = soundbuff;
nsamp = numsamp;
}
public void DiscardSamples()
{
}
#endregion
}
}