2012-09-08 21:36:04 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
2012-09-09 02:06:07 +00:00
|
|
|
|
namespace BizHawk.Emulation.Consoles.GB
|
2012-09-08 21:36:04 +00:00
|
|
|
|
{
|
2012-09-08 22:01:47 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// a gameboy/gameboy color emulator wrapped around native C++ libgambatte
|
|
|
|
|
/// </summary>
|
2012-09-09 02:06:07 +00:00
|
|
|
|
public class Gameboy : IEmulator, IVideoProvider, ISoundProvider
|
2012-09-08 21:36:04 +00:00
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// internal gambatte state
|
|
|
|
|
/// </summary>
|
|
|
|
|
IntPtr GambatteState = IntPtr.Zero;
|
|
|
|
|
|
|
|
|
|
|
2012-09-09 02:06:07 +00:00
|
|
|
|
public Gameboy(byte[] romdata)
|
2012-09-08 21:36:04 +00:00
|
|
|
|
{
|
|
|
|
|
// use temp file until we hack up the libgambatte api to take data directly
|
|
|
|
|
|
|
|
|
|
using (FileStream fs = new FileStream("gambattetmp.gb", FileMode.OpenOrCreate, FileAccess.Write))
|
|
|
|
|
{
|
|
|
|
|
fs.Write(romdata, 0, romdata.Length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GambatteState = LibGambatte.gambatte_create();
|
|
|
|
|
|
|
|
|
|
if (GambatteState == IntPtr.Zero)
|
|
|
|
|
throw new Exception("gambatte_create() returned null???");
|
|
|
|
|
|
|
|
|
|
if (LibGambatte.gambatte_load(GambatteState, "gambattetmp.gb", 0) != 0)
|
|
|
|
|
throw new Exception("gambatte_load() returned non-zero (is this not a gb or gbc rom?)");
|
|
|
|
|
|
2012-09-09 00:41:11 +00:00
|
|
|
|
|
|
|
|
|
InitSound();
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IVideoProvider VideoProvider
|
|
|
|
|
{
|
|
|
|
|
get { return this; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ISoundProvider SoundProvider
|
|
|
|
|
{
|
2012-09-09 00:41:11 +00:00
|
|
|
|
get { return this; }
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-09-09 02:06:07 +00:00
|
|
|
|
public static readonly ControllerDefinition GbController = new ControllerDefinition
|
2012-09-08 21:36:04 +00:00
|
|
|
|
{
|
|
|
|
|
Name = "Gameboy Controller",
|
|
|
|
|
BoolButtons =
|
|
|
|
|
{
|
|
|
|
|
"Up", "Down", "Left", "Right", "A", "B", "Select", "Start"
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public ControllerDefinition ControllerDefinition
|
|
|
|
|
{
|
|
|
|
|
get { return GbController; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IController Controller { get; set; }
|
|
|
|
|
|
|
|
|
|
|
2012-09-09 00:41:11 +00:00
|
|
|
|
|
2012-09-08 21:36:04 +00:00
|
|
|
|
uint[] videoscratch = new uint[160 * 144];
|
|
|
|
|
public void FrameAdvance(bool render)
|
|
|
|
|
{
|
|
|
|
|
uint nsamp = 35112;
|
|
|
|
|
|
2012-09-09 00:41:11 +00:00
|
|
|
|
LibGambatte.gambatte_runfor(GambatteState, videoscratch, 160, soundbuff, ref nsamp);
|
|
|
|
|
|
|
|
|
|
soundbuffcontains = (int)nsamp;
|
2012-09-08 21:36:04 +00:00
|
|
|
|
|
|
|
|
|
// can't convert uint[] to int[], so we do this instead
|
|
|
|
|
// TODO: lie in the p/invoke layer and claim unsigned* is really int*
|
|
|
|
|
|
|
|
|
|
Buffer.BlockCopy(videoscratch, 0, VideoBuffer, 0, VideoBuffer.Length * sizeof(int));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Frame
|
|
|
|
|
{
|
|
|
|
|
get { return 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int LagCount { get; set; }
|
|
|
|
|
|
|
|
|
|
public bool IsLagFrame
|
|
|
|
|
{
|
|
|
|
|
get { return false; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string SystemId
|
|
|
|
|
{
|
|
|
|
|
get { return "GB"; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool DeterministicEmulation { get; set; }
|
|
|
|
|
|
|
|
|
|
public byte[] ReadSaveRam
|
|
|
|
|
{
|
2012-09-08 22:01:47 +00:00
|
|
|
|
get { return new byte[0]; }
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SaveRamModified
|
|
|
|
|
{
|
2012-09-08 22:01:47 +00:00
|
|
|
|
get;
|
|
|
|
|
set;
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void ResetFrameCounter()
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SaveStateText(System.IO.TextWriter writer)
|
|
|
|
|
{
|
2012-09-08 21:48:46 +00:00
|
|
|
|
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadStateText(System.IO.TextReader reader)
|
|
|
|
|
{
|
2012-09-08 21:48:46 +00:00
|
|
|
|
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SaveStateBinary(System.IO.BinaryWriter writer)
|
|
|
|
|
{
|
2012-09-08 21:48:46 +00:00
|
|
|
|
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void LoadStateBinary(System.IO.BinaryReader reader)
|
|
|
|
|
{
|
2012-09-08 21:48:46 +00:00
|
|
|
|
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public byte[] SaveStateBinary()
|
|
|
|
|
{
|
2012-09-08 21:48:46 +00:00
|
|
|
|
return new byte[0];
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-09-08 21:48:46 +00:00
|
|
|
|
public CoreInputComm CoreInputComm { get; set; }
|
2012-09-08 21:36:04 +00:00
|
|
|
|
|
|
|
|
|
CoreOutputComm GbOutputComm = new CoreOutputComm
|
|
|
|
|
{
|
2012-09-09 00:41:11 +00:00
|
|
|
|
VsyncNum = 262144,
|
|
|
|
|
VsyncDen = 4389,
|
2012-09-08 21:36:04 +00:00
|
|
|
|
RomStatusAnnotation = "Bizwhackin it up",
|
|
|
|
|
RomStatusDetails = "LEVAR BURTON"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
public CoreOutputComm CoreOutputComm
|
|
|
|
|
{
|
|
|
|
|
get { return GbOutputComm; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IList<MemoryDomain> MemoryDomains
|
|
|
|
|
{
|
|
|
|
|
get { throw new NotImplementedException(); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MemoryDomain MainMemory
|
|
|
|
|
{
|
|
|
|
|
get { throw new NotImplementedException(); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
LibGambatte.gambatte_destroy(GambatteState);
|
|
|
|
|
GambatteState = IntPtr.Zero;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region IVideoProvider
|
|
|
|
|
|
2012-09-08 22:01:47 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// stored image of most recent frame
|
|
|
|
|
/// </summary>
|
2012-09-08 21:36:04 +00:00
|
|
|
|
int[] VideoBuffer = new int[160 * 144];
|
|
|
|
|
|
|
|
|
|
public int[] GetVideoBuffer()
|
|
|
|
|
{
|
|
|
|
|
return VideoBuffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int VirtualWidth
|
|
|
|
|
{
|
|
|
|
|
// only sgb changes this
|
|
|
|
|
get { return 160; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int BufferWidth
|
|
|
|
|
{
|
|
|
|
|
get { return 160; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int BufferHeight
|
|
|
|
|
{
|
|
|
|
|
get { return 144; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int BackgroundColor
|
|
|
|
|
{
|
|
|
|
|
get { return 0; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2012-09-09 00:41:11 +00:00
|
|
|
|
|
|
|
|
|
#region ISoundProvider
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// sample pairs before resampling
|
|
|
|
|
/// </summary>
|
|
|
|
|
short[] soundbuff = new short[(35112 + 2064) * 2];
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// how many sample pairs are in soundbuff
|
|
|
|
|
/// </summary>
|
|
|
|
|
int soundbuffcontains = 0;
|
|
|
|
|
|
|
|
|
|
Sound.Utilities.SpeexResampler resampler;
|
|
|
|
|
Sound.MetaspuSoundProvider metaspu;
|
|
|
|
|
|
|
|
|
|
void InitSound()
|
|
|
|
|
{
|
|
|
|
|
metaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_V);
|
2012-09-09 12:23:40 +00:00
|
|
|
|
resampler = new Sound.Utilities.SpeexResampler(2, 2097152, 44100, 2097152, 44100, metaspu.buffer.enqueue_samples);
|
2012-09-09 00:41:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void GetSamples(short[] samples)
|
|
|
|
|
{
|
|
|
|
|
resampler.EnqueueSamples(soundbuff, soundbuffcontains);
|
|
|
|
|
//for (int i = 0; soundbuffcontains >= 0; soundbuffcontains--)
|
|
|
|
|
//{
|
|
|
|
|
// resampler.EnqueueSample(soundbuff[i], soundbuff[i + 1]);
|
|
|
|
|
// i += 2;
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
soundbuffcontains = 0;
|
|
|
|
|
resampler.Flush();
|
|
|
|
|
metaspu.GetSamples(samples);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DiscardSamples()
|
|
|
|
|
{
|
|
|
|
|
metaspu.DiscardSamples();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int MaxVolume { get; set; }
|
|
|
|
|
#endregion
|
2012-09-08 21:36:04 +00:00
|
|
|
|
}
|
|
|
|
|
}
|