rough in some stuff for game boy link cable recording. none of it is finished yet

This commit is contained in:
goyuken 2012-12-29 01:25:06 +00:00
parent cc73cdb6f9
commit 63f9752ea2
15 changed files with 435 additions and 59 deletions

View File

@ -168,6 +168,7 @@
<Compile Include="Consoles\Intellivision\PSG.cs" />
<Compile Include="Consoles\Intellivision\STIC.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\GambatteLink.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\GBColors.cs" />
<Compile Include="Consoles\Nintendo\Gameboy\LibGambatte.cs" />
<Compile Include="Consoles\Nintendo\GBA\LibMeteor.cs" />

View File

@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Consoles.GB
/// <summary>
/// internal gambatte state
/// </summary>
IntPtr GambatteState = IntPtr.Zero;
internal IntPtr GambatteState = IntPtr.Zero;
/// <summary>
/// keep a copy of the input callback delegate so it doesn't get GCed

View File

@ -0,0 +1,276 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace BizHawk.Emulation.Consoles.GB
{
public class GambatteLink : IEmulator, IVideoProvider, ISyncSoundProvider
{
class ControlSplitter : IController
{
public ControllerDefinition Type
{
get;
private set;
}
public bool this[string button]
{
get { throw new NotImplementedException(); }
}
public bool IsPressed(string button)
{
throw new NotImplementedException();
}
public float GetFloat(string name)
{
throw new NotImplementedException();
}
public void UpdateControls(int frame)
{
}
}
Gameboy L;
Gameboy R;
Consoles.Nintendo.SNES.LibsnesCore.SnesSaveController LCont = new Nintendo.SNES.LibsnesCore.SnesSaveController(Gameboy.GbController);
Consoles.Nintendo.SNES.LibsnesCore.SnesSaveController RCont = new Nintendo.SNES.LibsnesCore.SnesSaveController(Gameboy.GbController);
public GambatteLink(CoreComm comm, GameInfo leftinfo, byte[] leftrom, GameInfo rightinfo, byte[] rightrom)
{
CoreComm = comm;
L = new Gameboy(new CoreComm(), leftinfo, leftrom);
R = new Gameboy(new CoreComm(), rightinfo, rightrom);
L.Controller = LCont;
R.Controller = RCont;
comm.VsyncNum = L.CoreComm.VsyncNum;
comm.VsyncDen = L.CoreComm.VsyncDen;
comm.RomStatusAnnotation = null;
comm.RomStatusDetails = "LEFT:\r\n" + L.CoreComm.RomStatusDetails + "RIGHT:\r\n" + R.CoreComm.RomStatusDetails;
comm.CpuTraceAvailable = false; // TODO
comm.NominalWidth = L.CoreComm.NominalWidth + R.CoreComm.NominalWidth;
comm.NominalHeight = L.CoreComm.NominalHeight;
Frame = 0;
LagCount = 0;
IsLagFrame = false;
}
public IVideoProvider VideoProvider { get { return this; } }
public ISoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public static readonly ControllerDefinition DualGbController = new ControllerDefinition
{
Name = "Dual Gameboy Controller",
BoolButtons =
{
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 A", "P1 B", "P1 Select", "P1 Start", "P1 Power",
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 A", "P2 B", "P2 Select", "P2 Start", "P2 Power",
}
};
public ControllerDefinition ControllerDefinition { get { return DualGbController; } }
public IController Controller { get; set; }
public void FrameAdvance(bool render, bool rendersound = true)
{
LCont.Clear();
RCont.Clear();
foreach (var s in DualGbController.BoolButtons)
{
if (Controller[s])
{
if (s.Contains("P1 "))
LCont.Set(s.Replace("P1 ", ""));
else if (s.Contains("P2 "))
RCont.Set(s.Replace("P2 ", ""));
}
}
Frame++;
L.FrameAdvance(render, rendersound);
R.FrameAdvance(render, rendersound);
IsLagFrame = L.IsLagFrame && R.IsLagFrame;
if (IsLagFrame)
LagCount++;
BlitFrameBuffer();
}
public int Frame { get; private set; }
public int LagCount { get; set; }
public bool IsLagFrame { get; private set; }
public string SystemId { get { return "DGB"; } }
public bool DeterministicEmulation { get { return true; } }
#region saveram
public byte[] ReadSaveRam()
{
byte[] lb = L.ReadSaveRam();
byte[] rb = R.ReadSaveRam();
byte[] ret = new byte[lb.Length + rb.Length];
Buffer.BlockCopy(lb, 0, ret, 0, lb.Length);
Buffer.BlockCopy(rb, 0, ret, lb.Length, rb.Length);
return ret;
}
public void StoreSaveRam(byte[] data)
{
byte[] lb = new byte[L.ReadSaveRam().Length];
byte[] rb = new byte[R.ReadSaveRam().Length];
Buffer.BlockCopy(data, 0, lb, 0, lb.Length);
Buffer.BlockCopy(data, lb.Length, rb, 0, rb.Length);
L.StoreSaveRam(lb);
R.StoreSaveRam(rb);
}
public void ClearSaveRam()
{
L.ClearSaveRam();
R.ClearSaveRam();
}
public bool SaveRamModified
{
get
{
return L.SaveRamModified || R.SaveRamModified;
}
set
{
throw new NotImplementedException();
}
}
#endregion
public void ResetFrameCounter()
{
Frame = 0;
LagCount = 0;
IsLagFrame = false;
}
#region savestates
public void SaveStateText(TextWriter writer)
{
var temp = SaveStateBinary();
temp.SaveAsHex(writer);
// write extra copy of stuff we don't use
writer.WriteLine("Frame {0}", Frame);
}
public void LoadStateText(TextReader reader)
{
string hex = reader.ReadLine();
byte[] state = new byte[hex.Length / 2];
state.ReadFromHex(hex);
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
}
public void SaveStateBinary(BinaryWriter writer)
{
L.SaveStateBinary(writer);
R.SaveStateBinary(writer);
// other variables
writer.Write(IsLagFrame);
writer.Write(LagCount);
writer.Write(Frame);
}
public void LoadStateBinary(BinaryReader reader)
{
L.LoadStateBinary(reader);
R.LoadStateBinary(reader);
// other variables
IsLagFrame = reader.ReadBoolean();
LagCount = reader.ReadInt32();
Frame = reader.ReadInt32();
}
public byte[] SaveStateBinary()
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
SaveStateBinary(bw);
bw.Flush();
return ms.ToArray();
}
#endregion
public CoreComm CoreComm { get; private set; }
public IList<MemoryDomain> MemoryDomains
{
get { throw new NotImplementedException(); }
}
public MemoryDomain MainMemory
{
get { throw new NotImplementedException(); }
}
public void Dispose()
{
if (L != null)
{
L.Dispose();
L = null;
}
if (R != null)
{
R.Dispose();
R = null;
}
}
int[] VideoBuffer = new int[160 * 2 * 144];
public int[] GetVideoBuffer() { return VideoBuffer; }
public int VirtualWidth { get { return 320; } }
public int BufferWidth { get { return 320; } }
public int BufferHeight { get { return 144; } }
public int BackgroundColor { get { return unchecked((int)0xff000000); } }
void BlitFrameBuffer()
{
var lb = L.GetVideoBuffer();
var rb = R.GetVideoBuffer();
int destpos = 0;
for (int y = 0; y < 144; y++)
{
Buffer.BlockCopy(lb, 160 * 4 * y, VideoBuffer, destpos, 160 * 4);
destpos += 160 * 4;
Buffer.BlockCopy(rb, 160 * 4 * y, VideoBuffer, destpos, 160 * 4);
destpos += 160 * 4;
}
}
public void GetSamples(out short[] samples, out int nsamp)
{
// TODO
samples = new short[735 * 2];
nsamp = 735;
}
public void DiscardSamples()
{
// TODO
}
}
}

View File

@ -377,5 +377,14 @@ namespace BizHawk.Emulation.Consoles.GB
/// <param name="val">byte to write</param>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void gambatte_cpuwrite(IntPtr core, ushort addr, byte val);
/// <summary>
/// link cable stuff; never touch for normal operation
/// </summary>
/// <param name="core">opaque state pointe</param>
/// <param name="which">todo</param>
/// <returns>todo</returns>
[DllImport("libgambatte.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int gambatte_linkstatus(IntPtr core, int which);
}
}

View File

@ -509,7 +509,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
/// <summary>
/// can freeze a copy of a controller input set and serialize\deserialize it
/// </summary>
class SnesSaveController : IController
public class SnesSaveController : IController
{
// this is all rather general, so perhaps should be moved out of LibsnesCore
@ -579,6 +579,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
}
}
public void Clear()
{
buttons.Clear();
}
public void Set(string button)
{
buttons[button] = 1.0f;
}
public bool this[string button]
{
get { return buttons[button] != 0; }
@ -596,7 +606,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo.SNES
public void UpdateControls(int frame)
{
throw new NotImplementedException();
//throw new NotImplementedException();
}
}

View File

@ -1952,65 +1952,77 @@ namespace BizHawk.MultiClient
break;
case "GB":
case "GBC":
if (!Global.Config.GB_AsSGB)
if (false) // DEBUG
{
if (Global.Config.GB_ForceDMG) game.AddOption("ForceDMG");
if (Global.Config.GB_GBACGB) game.AddOption("GBACGB");
if (Global.Config.GB_MulticartCompat) game.AddOption("MulitcartCompat");
Emulation.Consoles.GB.Gameboy gb = new Emulation.Consoles.GB.Gameboy(nextComm, game, rom.FileData);
nextEmulator = gb;
if (gb.IsCGBMode())
{
gb.SetCGBColors(Global.Config.CGBColors);
}
else
{
try
{
using (StreamReader f = new StreamReader(Global.Config.GB_PaletteFile))
{
int[] colors = GBtools.ColorChooserForm.LoadPalFile(f);
if (colors != null)
gb.ChangeDMGColors(colors);
}
}
catch { }
}
GambatteLink gbl = new GambatteLink(nextComm, game, rom.FileData, game, rom.FileData);
nextEmulator = gbl;
// other stuff todo
}
else
{
// todo: get these bioses into a gamedb?? then we could demand different filenames for different regions?
string sgbromPath = Path.Combine(Global.Config.FirmwaresPath, "sgb.sfc"); //Path.Combine(PathManager.MakeAbsolutePath(Global.Config.PathSNESFirmwares, "SNES"), "sgb.sfc");
byte[] sgbrom = null;
try
if (!Global.Config.GB_AsSGB)
{
if (File.Exists(sgbromPath))
if (Global.Config.GB_ForceDMG) game.AddOption("ForceDMG");
if (Global.Config.GB_GBACGB) game.AddOption("GBACGB");
if (Global.Config.GB_MulticartCompat) game.AddOption("MulitcartCompat");
Emulation.Consoles.GB.Gameboy gb = new Emulation.Consoles.GB.Gameboy(nextComm, game, rom.FileData);
nextEmulator = gb;
if (gb.IsCGBMode())
{
sgbrom = File.ReadAllBytes(sgbromPath);
gb.SetCGBColors(Global.Config.CGBColors);
}
else
{
MessageBox.Show("Couldn't open sgb.sfc from the configured SNES firmwares path, which is:\n\n" + sgbromPath + "\n\nPlease make sure it is available and try again.\n\nWe're going to disable SGB for now; please re-enable it when you've set up the file.");
Global.Config.GB_AsSGB = false;
game.System = "GB";
goto RETRY;
try
{
using (StreamReader f = new StreamReader(Global.Config.GB_PaletteFile))
{
int[] colors = GBtools.ColorChooserForm.LoadPalFile(f);
if (colors != null)
gb.ChangeDMGColors(colors);
}
}
catch { }
}
}
catch (Exception)
else
{
// failed to load SGB bios. to avoid catch-22, disable SGB mode
Global.Config.GB_AsSGB = false;
throw;
}
if (sgbrom != null)
{
game.System = "SNES";
game.AddOption("SGB");
nextComm.SNES_ExePath = SNES_Prepare(Global.Config.SNESProfile);
var snes = new LibsnesCore(nextComm);
nextEmulator = snes;
game.FirmwareHash = Util.BytesToHexString(System.Security.Cryptography.SHA1.Create().ComputeHash(sgbrom));
snes.Load(game, rom.FileData, sgbrom, deterministicemulation);
// todo: get these bioses into a gamedb?? then we could demand different filenames for different regions?
string sgbromPath = Path.Combine(Global.Config.FirmwaresPath, "sgb.sfc"); //Path.Combine(PathManager.MakeAbsolutePath(Global.Config.PathSNESFirmwares, "SNES"), "sgb.sfc");
byte[] sgbrom = null;
try
{
if (File.Exists(sgbromPath))
{
sgbrom = File.ReadAllBytes(sgbromPath);
}
else
{
MessageBox.Show("Couldn't open sgb.sfc from the configured SNES firmwares path, which is:\n\n" + sgbromPath + "\n\nPlease make sure it is available and try again.\n\nWe're going to disable SGB for now; please re-enable it when you've set up the file.");
Global.Config.GB_AsSGB = false;
game.System = "GB";
goto RETRY;
}
}
catch (Exception)
{
// failed to load SGB bios. to avoid catch-22, disable SGB mode
Global.Config.GB_AsSGB = false;
throw;
}
if (sgbrom != null)
{
game.System = "SNES";
game.AddOption("SGB");
nextComm.SNES_ExePath = SNES_Prepare(Global.Config.SNESProfile);
var snes = new LibsnesCore(nextComm);
nextEmulator = snes;
game.FirmwareHash = Util.BytesToHexString(System.Security.Cryptography.SHA1.Create().ComputeHash(sgbrom));
snes.Load(game, rom.FileData, sgbrom, deterministicemulation);
}
}
}
break;

View File

@ -492,6 +492,10 @@ namespace BizHawk.MultiClient
{
return GetGBAControllersAsMnemonic();
}
else if (ControlType == "Dual Gameboy Controller")
{
return "|.|"; // TODO
}
StringBuilder input = new StringBuilder("|");
@ -907,6 +911,10 @@ namespace BizHawk.MultiClient
SetAtari7800AsMnemonic(mnemonic);
return;
}
else if (ControlType == "Dual Gameboy Controller")
{
return; // TODO
}
MnemonicChecker c = new MnemonicChecker(mnemonic);

View File

@ -158,7 +158,8 @@ public:
unsigned char ExternalRead(unsigned short addr);
void ExternalWrite(unsigned short addr, unsigned char val);
int LinkStatus(int which);
private:
struct Priv;
Priv *const p_;

View File

@ -51,6 +51,7 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include;src;src\common</AdditionalIncludeDirectories>
<DisableSpecificWarnings>4244;4373;4800;4804</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -67,6 +68,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBGAMBATTE_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>include;src;src\common</AdditionalIncludeDirectories>
<DisableSpecificWarnings>4244;4373;4800;4804</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@ -234,3 +234,9 @@ __declspec(dllexport) void gambatte_cpuwrite(void *core, unsigned short addr, un
GB *g = (GB *) core;
g->ExternalWrite(addr, val);
}
__declspec(dllexport) int gambatte_linkstatus(void *core, int which)
{
GB *g = (GB *) core;
return g->LinkStatus(which);
}

View File

@ -62,6 +62,9 @@ extern "C"
__declspec(dllexport) unsigned char gambatte_cpuread(void *core, unsigned short addr);
__declspec(dllexport) void gambatte_cpuwrite(void *core, unsigned short addr, unsigned char val);
__declspec(dllexport) int gambatte_linkstatus(void *core, int which);
}

View File

@ -122,6 +122,7 @@ public:
unsigned char ExternalRead(unsigned short addr) { return memory.peek(addr); }
void ExternalWrite(unsigned short addr, unsigned char val) { memory.write(addr, val, cycleCounter_); }
int LinkStatus(int which) { return memory.LinkStatus(which); }
};
}

View File

@ -260,4 +260,8 @@ void GB::setGameShark(const std::string &codes) {
p_->cpu.setGameShark(codes);
}
int GB::LinkStatus(int which) {
return p_->cpu.LinkStatus(which);
}
}

View File

@ -37,7 +37,9 @@ Memory::Memory(const Interrupter &interrupter_in)
dmaDestination(0),
oamDmaPos(0xFE),
serialCnt(0),
blanklcd(false)
blanklcd(false),
LINKCABLE(false),
linkClockTrigger(false)
{
intreq.setEventTime<BLIT>(144*456ul);
intreq.setEventTime<END>(0);
@ -123,16 +125,26 @@ void Memory::setEndtime(const unsigned long cycleCounter, const unsigned long in
}
void Memory::updateSerial(const unsigned long cc) {
if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
if (intreq.eventTime(SERIAL) <= cc) {
ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
ioamhram[0x102] &= 0x7F;
intreq.setEventTime<SERIAL>(DISABLED_TIME);
intreq.flagIrq(8);
} else {
const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2);
ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF;
serialCnt = targetCnt;
if (!LINKCABLE) {
if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
if (intreq.eventTime(SERIAL) <= cc) {
ioamhram[0x101] = (((ioamhram[0x101] + 1) << serialCnt) - 1) & 0xFF;
ioamhram[0x102] &= 0x7F;
intreq.setEventTime<SERIAL>(DISABLED_TIME);
intreq.flagIrq(8);
} else {
const int targetCnt = serialCntFrom(intreq.eventTime(SERIAL) - cc, ioamhram[0x102] & isCgb() * 2);
ioamhram[0x101] = (((ioamhram[0x101] + 1) << (serialCnt - targetCnt)) - 1) & 0xFF;
serialCnt = targetCnt;
}
}
}
else {
if (intreq.eventTime(SERIAL) != DISABLED_TIME) {
if (intreq.eventTime(SERIAL) <= cc) {
linkClockTrigger = true;
intreq.setEventTime<SERIAL>(DISABLED_TIME);
}
}
}
}
@ -1062,5 +1074,31 @@ bool Memory::getMemoryArea(int which, unsigned char **data, int *length) {
}
}
int Memory::LinkStatus(int which)
{
switch (which)
{
case 256: // ClockSignaled
return linkClockTrigger;
case 257: // AckClockSignal
linkClockTrigger = false;
return 0;
case 258: // GetOut
return ioamhram[0x101] & 0xff;
case 259: // connect link cable
LINKCABLE = true;
return 0;
default: // ShiftIn
if (ioamhram[0x102] & 0x80) // was enabled
{
ioamhram[0x101] = which;
ioamhram[0x102] &= 0x7F;
intreq.flagIrq(8);
}
return 0;
}
return -1;
}
}

View File

@ -52,6 +52,9 @@ class Memory {
unsigned char serialCnt;
bool blanklcd;
bool LINKCABLE;
bool linkClockTrigger;
void updateInput();
void decEventCycles(MemEventId eventId, unsigned long dec);
@ -176,6 +179,8 @@ public:
void setCgbPalette(unsigned *lut);
void setGameGenie(const std::string &codes) { cart.setGameGenie(codes); }
void setGameShark(const std::string &codes) { interrupter.setGameShark(codes); }
int LinkStatus(int which);
};
}