rough in some stuff for game boy link cable recording. none of it is finished yet
This commit is contained in:
parent
cc73cdb6f9
commit
63f9752ea2
|
@ -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" />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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); }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue