BizHawk/BizHawk.Emulation/Consoles/Nintendo/GBA/Meteor.cs

237 lines
6.3 KiB
C#
Raw Normal View History

2012-11-19 22:43:34 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace BizHawk.Emulation.Consoles.Nintendo.GBA
{
public class GBA : IEmulator, IVideoProvider, ISyncSoundProvider
{
public static readonly ControllerDefinition GBAController =
new ControllerDefinition
{
Name = "GBA Controller",
BoolButtons =
{
"Up", "Down", "Left", "Right", "Select", "Start", "B", "A", "L", "R"//, "Reset", "Power",
}
};
public ControllerDefinition ControllerDefinition { get { return GBAController; } }
public IController Controller { get; set; }
public void Load(byte[] rom, byte[] bios)
2012-11-19 22:43:34 +00:00
{
if (bios.Length != 16384)
throw new Exception("GBA bios must be exactly 16384 bytes!");
2012-11-19 22:43:34 +00:00
Init();
LibMeteor.libmeteor_reset();
LibMeteor.libmeteor_loadbios(bios, (uint)bios.Length);
2012-11-19 22:43:34 +00:00
LibMeteor.libmeteor_loadrom(rom, (uint)rom.Length);
}
public void FrameAdvance(bool render, bool rendersound = true)
{
Controller.UpdateControls(Frame++);
IsLagFrame = true;
2012-11-19 22:43:34 +00:00
LibMeteor.libmeteor_frameadvance();
if (IsLagFrame)
LagCount++;
2012-11-19 22:43:34 +00:00
}
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; private set; }
public string SystemId { get { return "GBA"; } }
public bool DeterministicEmulation { get { return true; } }
2012-11-19 22:43:34 +00:00
public void ResetFrameCounter()
2012-11-19 22:43:34 +00:00
{
Frame = 0;
LagCount = 0;
2012-11-19 22:43:34 +00:00
}
#region saveram
2012-11-19 22:43:34 +00:00
public byte[] ReadSaveRam()
{
return new byte[0];
}
public void StoreSaveRam(byte[] data)
{
}
public void ClearSaveRam()
{
}
public bool SaveRamModified { get { return false; } set { } }
#endregion
2012-11-19 22:43:34 +00:00
#region savestates
2012-11-19 22:43:34 +00:00
public void SaveStateText(System.IO.TextWriter writer)
{
}
public void LoadStateText(System.IO.TextReader reader)
{
}
public void SaveStateBinary(System.IO.BinaryWriter writer)
{
}
public void LoadStateBinary(System.IO.BinaryReader reader)
{
}
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
#endregion
2012-11-19 22:43:34 +00:00
public CoreInputComm CoreInputComm { get; set; }
CoreOutputComm _CoreOutputComm = new CoreOutputComm
{
VsyncNum = 262144,
VsyncDen = 4389
};
public CoreOutputComm CoreOutputComm { get { return _CoreOutputComm; } }
public IList<MemoryDomain> MemoryDomains
{
get { return null; }
}
public MemoryDomain MainMemory
{
get { return null; }
}
/// <summary>like libsnes, the library is single-instance</summary>
2012-11-19 22:43:34 +00:00
static GBA attachedcore;
/// <summary>hold pointer to message callback so it won't get GCed</summary>
2012-11-20 01:28:31 +00:00
LibMeteor.MessageCallback messagecallback;
/// <summary>hold pointer to input callback so it won't get GCed</summary>
LibMeteor.InputCallback inputcallback;
LibMeteor.Buttons GetInput()
{
// libmeteor bitflips everything itself, so 0 == off, 1 == on
IsLagFrame = false;
LibMeteor.Buttons ret = 0;
if (Controller["Up"]) ret |= LibMeteor.Buttons.BTN_UP;
if (Controller["Down"]) ret |= LibMeteor.Buttons.BTN_DOWN;
if (Controller["Left"]) ret |= LibMeteor.Buttons.BTN_LEFT;
if (Controller["Right"]) ret |= LibMeteor.Buttons.BTN_RIGHT;
if (Controller["Select"]) ret |= LibMeteor.Buttons.BTN_SELECT;
if (Controller["Start"]) ret |= LibMeteor.Buttons.BTN_START;
if (Controller["B"]) ret |= LibMeteor.Buttons.BTN_B;
if (Controller["A"]) ret |= LibMeteor.Buttons.BTN_A;
if (Controller["L"]) ret |= LibMeteor.Buttons.BTN_L;
if (Controller["R"]) ret |= LibMeteor.Buttons.BTN_R;
return ret;
}
void PrintMessage(string msg, bool abort)
{
if (!abort)
Console.Write(msg.Replace("\n", "\r\n"));
else
throw new Exception("libmeteor abort:\n " + msg);
}
2012-11-19 22:43:34 +00:00
void Init()
{
if (attachedcore != null)
attachedcore.Dispose();
messagecallback = PrintMessage;
inputcallback = GetInput;
2012-11-20 01:28:31 +00:00
LibMeteor.libmeteor_setmessagecallback(messagecallback);
LibMeteor.libmeteor_setkeycallback(inputcallback);
2012-11-20 01:28:31 +00:00
2012-11-19 22:43:34 +00:00
LibMeteor.libmeteor_init();
videobuffer = new int[240 * 160];
videohandle = GCHandle.Alloc(videobuffer, GCHandleType.Pinned);
2012-11-20 01:28:31 +00:00
soundbuffer = new short[2048]; // nominal length of one frame is something like 1480 shorts?
2012-11-19 22:43:34 +00:00
soundhandle = GCHandle.Alloc(soundbuffer, GCHandleType.Pinned);
if (!LibMeteor.libmeteor_setbuffers
(videohandle.AddrOfPinnedObject(), (uint)(sizeof(int) * videobuffer.Length),
soundhandle.AddrOfPinnedObject(), (uint)(sizeof(short) * soundbuffer.Length)))
2012-11-20 01:28:31 +00:00
throw new Exception("libmeteor_setbuffers() returned false??");
2012-11-19 22:43:34 +00:00
attachedcore = this;
}
bool disposed = false;
public void Dispose()
{
if (!disposed)
{
disposed = true;
videohandle.Free();
soundhandle.Free();
// guarantee crash if it gets accessed
LibMeteor.libmeteor_setbuffers(IntPtr.Zero, 240 * 160 * 4, IntPtr.Zero, 4);
messagecallback = null;
inputcallback = null;
LibMeteor.libmeteor_setmessagecallback(messagecallback);
LibMeteor.libmeteor_setkeycallback(inputcallback);
2012-11-19 22:43:34 +00:00
}
}
#region IVideoProvider
public IVideoProvider VideoProvider { get { return this; } }
int[] videobuffer;
GCHandle videohandle;
public int[] GetVideoBuffer() { return videobuffer; }
public int VirtualWidth { get { return 240; } }
public int BufferWidth { get { return 240; } }
public int BufferHeight { get { return 160; } }
public int BackgroundColor { get { return unchecked((int)0xff000000); } }
#endregion
#region ISoundProvider
short[] soundbuffer;
GCHandle soundhandle;
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)
{
uint nbytes = LibMeteor.libmeteor_emptysound();
samples = soundbuffer;
nsamp = (int)(nbytes / 4);
}
public void DiscardSamples()
{
LibMeteor.libmeteor_emptysound();
}
#endregion
}
}