2017-04-25 16:06:50 +00:00
|
|
|
|
using System;
|
|
|
|
|
using BizHawk.Emulation.Common;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
|
|
|
|
|
{
|
2017-05-01 02:01:37 +00:00
|
|
|
|
public partial class GambatteLink : IEmulator, IBoardInfo
|
2017-04-25 16:06:50 +00:00
|
|
|
|
{
|
|
|
|
|
public IEmulatorServiceProvider ServiceProvider { get; }
|
|
|
|
|
|
|
|
|
|
public ControllerDefinition ControllerDefinition => DualGbController;
|
|
|
|
|
|
2017-05-02 01:09:11 +00:00
|
|
|
|
public void FrameAdvance(IController controller, bool render, bool rendersound = true)
|
2017-04-25 16:06:50 +00:00
|
|
|
|
{
|
|
|
|
|
LCont.Clear();
|
|
|
|
|
RCont.Clear();
|
|
|
|
|
|
|
|
|
|
foreach (var s in DualGbController.BoolButtons)
|
|
|
|
|
{
|
2017-05-02 01:09:11 +00:00
|
|
|
|
if (controller.IsPressed(s))
|
2017-04-25 16:06:50 +00:00
|
|
|
|
{
|
|
|
|
|
if (s.Contains("P1 "))
|
|
|
|
|
{
|
|
|
|
|
LCont.Set(s.Replace("P1 ", string.Empty));
|
|
|
|
|
}
|
|
|
|
|
else if (s.Contains("P2 "))
|
|
|
|
|
{
|
|
|
|
|
RCont.Set(s.Replace("P2 ", string.Empty));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-02 01:09:11 +00:00
|
|
|
|
bool cablediscosignalNew = controller.IsPressed("Toggle Cable");
|
2017-04-25 16:06:50 +00:00
|
|
|
|
if (cablediscosignalNew && !_cablediscosignal)
|
|
|
|
|
{
|
|
|
|
|
_cableconnected ^= true;
|
|
|
|
|
Console.WriteLine("Cable connect status to {0}", _cableconnected);
|
|
|
|
|
LinkConnected = _cableconnected;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_cablediscosignal = cablediscosignalNew;
|
|
|
|
|
|
|
|
|
|
Frame++;
|
2017-05-02 01:09:11 +00:00
|
|
|
|
L.FrameAdvancePrep(controller);
|
|
|
|
|
R.FrameAdvancePrep(controller);
|
2017-04-25 16:06:50 +00:00
|
|
|
|
|
|
|
|
|
unsafe
|
|
|
|
|
{
|
|
|
|
|
fixed (int* leftvbuff = &VideoBuffer[0])
|
|
|
|
|
{
|
|
|
|
|
// use pitch to have both cores write to the same video buffer, interleaved
|
|
|
|
|
int* rightvbuff = leftvbuff + 160;
|
|
|
|
|
const int Pitch = 160 * 2;
|
|
|
|
|
|
|
|
|
|
fixed (short* leftsbuff = LeftBuffer, rightsbuff = RightBuffer)
|
|
|
|
|
{
|
|
|
|
|
const int Step = 32; // could be 1024 for GB
|
|
|
|
|
|
|
|
|
|
int nL = _overflowL;
|
|
|
|
|
int nR = _overflowR;
|
|
|
|
|
|
|
|
|
|
// slowly step our way through the frame, while continually checking and resolving link cable status
|
|
|
|
|
for (int target = 0; target < SampPerFrame;)
|
|
|
|
|
{
|
|
|
|
|
target += Step;
|
|
|
|
|
if (target > SampPerFrame)
|
|
|
|
|
{
|
|
|
|
|
target = SampPerFrame; // don't run for slightly too long depending on step
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// gambatte_runfor() aborts early when a frame is produced, but we don't want that, hence the while()
|
|
|
|
|
while (nL < target)
|
|
|
|
|
{
|
|
|
|
|
uint nsamp = (uint)(target - nL);
|
|
|
|
|
if (LibGambatte.gambatte_runfor(L.GambatteState, leftsbuff + (nL * 2), ref nsamp) > 0)
|
|
|
|
|
{
|
|
|
|
|
LibGambatte.gambatte_blitto(L.GambatteState, leftvbuff, Pitch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nL += (int)nsamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (nR < target)
|
|
|
|
|
{
|
|
|
|
|
uint nsamp = (uint)(target - nR);
|
|
|
|
|
if (LibGambatte.gambatte_runfor(R.GambatteState, rightsbuff + (nR * 2), ref nsamp) > 0)
|
|
|
|
|
{
|
|
|
|
|
LibGambatte.gambatte_blitto(R.GambatteState, rightvbuff, Pitch);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nR += (int)nsamp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// poll link cable statuses, but not when the cable is disconnected
|
|
|
|
|
if (!_cableconnected)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LibGambatte.gambatte_linkstatus(L.GambatteState, 256) != 0) // ClockTrigger
|
|
|
|
|
{
|
|
|
|
|
LibGambatte.gambatte_linkstatus(L.GambatteState, 257); // ack
|
|
|
|
|
int lo = LibGambatte.gambatte_linkstatus(L.GambatteState, 258); // GetOut
|
|
|
|
|
int ro = LibGambatte.gambatte_linkstatus(R.GambatteState, 258);
|
|
|
|
|
LibGambatte.gambatte_linkstatus(L.GambatteState, ro & 0xff); // ShiftIn
|
|
|
|
|
LibGambatte.gambatte_linkstatus(R.GambatteState, lo & 0xff); // ShiftIn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LibGambatte.gambatte_linkstatus(R.GambatteState, 256) != 0) // ClockTrigger
|
|
|
|
|
{
|
|
|
|
|
LibGambatte.gambatte_linkstatus(R.GambatteState, 257); // ack
|
|
|
|
|
int lo = LibGambatte.gambatte_linkstatus(L.GambatteState, 258); // GetOut
|
|
|
|
|
int ro = LibGambatte.gambatte_linkstatus(R.GambatteState, 258);
|
|
|
|
|
LibGambatte.gambatte_linkstatus(L.GambatteState, ro & 0xff); // ShiftIn
|
|
|
|
|
LibGambatte.gambatte_linkstatus(R.GambatteState, lo & 0xff); // ShiftIn
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_overflowL = nL - SampPerFrame;
|
|
|
|
|
_overflowR = nR - SampPerFrame;
|
|
|
|
|
if (_overflowL < 0 || _overflowR < 0)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception("Timing problem?");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rendersound)
|
|
|
|
|
{
|
|
|
|
|
PrepSound();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// copy extra samples back to beginning
|
|
|
|
|
for (int i = 0; i < _overflowL * 2; i++)
|
|
|
|
|
{
|
|
|
|
|
LeftBuffer[i] = LeftBuffer[i + (SampPerFrame * 2)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < _overflowR * 2; i++)
|
|
|
|
|
{
|
|
|
|
|
RightBuffer[i] = RightBuffer[i + (SampPerFrame * 2)];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
L.FrameAdvancePost();
|
|
|
|
|
R.FrameAdvancePost();
|
|
|
|
|
IsLagFrame = L.IsLagFrame && R.IsLagFrame;
|
|
|
|
|
if (IsLagFrame)
|
|
|
|
|
{
|
|
|
|
|
LagCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Frame { get; private set; }
|
|
|
|
|
|
|
|
|
|
public string SystemId => "DGB";
|
|
|
|
|
|
|
|
|
|
public bool DeterministicEmulation => L.DeterministicEmulation && R.DeterministicEmulation;
|
|
|
|
|
|
|
|
|
|
public string BoardName => L.BoardName + '|' + R.BoardName;
|
|
|
|
|
|
|
|
|
|
public void ResetCounters()
|
|
|
|
|
{
|
|
|
|
|
Frame = 0;
|
|
|
|
|
LagCount = 0;
|
|
|
|
|
IsLagFrame = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public CoreComm CoreComm { get; }
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
if (!_disposed)
|
|
|
|
|
{
|
|
|
|
|
L.Dispose();
|
|
|
|
|
L = null;
|
|
|
|
|
|
|
|
|
|
R.Dispose();
|
|
|
|
|
R = null;
|
|
|
|
|
|
|
|
|
|
_blipLeft.Dispose();
|
|
|
|
|
_blipLeft = null;
|
|
|
|
|
|
|
|
|
|
_blipRight.Dispose();
|
|
|
|
|
_blipRight = null;
|
|
|
|
|
|
|
|
|
|
_disposed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|