GBHawk: Start 4x support
This commit is contained in:
parent
211e65dbf5
commit
71527da3ed
|
@ -839,6 +839,31 @@
|
|||
<DependentUpon>VBANext.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBA\VBARegisterHelper.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.ICodeDataLog.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.IDebuggable.cs">
|
||||
<DependentUpon>GBHawkLink4x.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.IEmulator.cs">
|
||||
<DependentUpon>GBHawkLink4x.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.IInputPollable.cs">
|
||||
<DependentUpon>GBHawkLink4x.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.IMemoryDomains.cs">
|
||||
<DependentUpon>GBHawkLink4x.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.ISaveRam.cs">
|
||||
<DependentUpon>GBHawkLink4x.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.ISettable.cs">
|
||||
<DependentUpon>GBHawkLink4x.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4x.IStatable.cs">
|
||||
<DependentUpon>GBHawkLink4x.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4xControllerDeck.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink4x\GBHawkLink4xControllers.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink3x\GBHawkLink3x.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink3x\GBHawkLink3x.ICodeDataLog.cs" />
|
||||
<Compile Include="Consoles\Nintendo\GBHawkLink3x\GBHawkLink3x.IDebuggable.cs">
|
||||
|
@ -940,7 +965,7 @@
|
|||
<Compile Include="Consoles\Nintendo\GBHawk\GBC_PPU.cs">
|
||||
<DependentUpon>PPU.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawk\GBC_PPU_GB.cs">
|
||||
<Compile Include="Consoles\Nintendo\GBHawk\GBC_PPU_GB.cs">
|
||||
<DependentUpon>PPU.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Nintendo\GBHawk\GB_PPU.cs">
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink
|
|||
|
||||
if (R.cart_RAM != null)
|
||||
{
|
||||
for (int i = 0; i < L.cart_RAM.Length; i++)
|
||||
for (int i = 0; i < R.cart_RAM.Length; i++)
|
||||
{
|
||||
temp[index] = R.cart_RAM[i];
|
||||
index++;
|
||||
|
|
|
@ -51,7 +51,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink3x
|
|||
|
||||
if (R.cart_RAM != null)
|
||||
{
|
||||
for (int i = 0; i < L.cart_RAM.Length; i++)
|
||||
for (int i = 0; i < R.cart_RAM.Length; i++)
|
||||
{
|
||||
temp[index] = R.cart_RAM[i];
|
||||
index++;
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Common.Components.LR35902;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x : ICodeDataLogger
|
||||
{
|
||||
private ICodeDataLog _cdl;
|
||||
|
||||
public void SetCDL(ICodeDataLog cdl)
|
||||
{
|
||||
_cdl = cdl;
|
||||
if (cdl == null)
|
||||
this.A.cpu.CDLCallback = null;
|
||||
else this.A.cpu.CDLCallback = CDLCpuCallback;
|
||||
}
|
||||
|
||||
public void NewCDL(ICodeDataLog cdl)
|
||||
{
|
||||
cdl["ROM"] = new byte[MemoryDomains["ROM A"].Size];
|
||||
cdl["HRAM"] = new byte[MemoryDomains["Zero Page RAM A"].Size];
|
||||
|
||||
cdl["WRAM"] = new byte[MemoryDomains["Main RAM A"].Size];
|
||||
|
||||
if (MemoryDomains.Has("Cart RAM A"))
|
||||
{
|
||||
cdl["CartRAM"] = new byte[MemoryDomains["Cart RAM A"].Size];
|
||||
}
|
||||
|
||||
cdl.SubType = "GB";
|
||||
cdl.SubVer = 0;
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
void ICodeDataLogger.DisassembleCDL(Stream s, ICodeDataLog cdl)
|
||||
{
|
||||
}
|
||||
|
||||
public void SetCDL(LR35902.eCDLogMemFlags flags, string type, int cdladdr)
|
||||
{
|
||||
if (type == null) return;
|
||||
byte val = (byte)flags;
|
||||
_cdl[type][cdladdr] |= (byte)flags;
|
||||
}
|
||||
|
||||
void CDLCpuCallback(ushort addr, LR35902.eCDLogMemFlags flags)
|
||||
{
|
||||
if (addr < 0x8000)
|
||||
{
|
||||
//don't record writes to the ROM, it's just noisy
|
||||
//NOTE: in principle a mapper could mount a useful resource here, but I doubt it)
|
||||
if ((flags & LR35902.eCDLogMemFlags.Write) != 0) return;
|
||||
}
|
||||
|
||||
if (A.ppu.DMA_start)
|
||||
{
|
||||
// some of gekkio's tests require these to be accessible during DMA
|
||||
if (addr < 0x8000)
|
||||
{
|
||||
if (A.ppu.DMA_addr < 0x80)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
A.mapper.MapCDL(addr, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((addr >= 0xE000) && (addr < 0xF000))
|
||||
{
|
||||
SetCDL(flags, "WRAM", addr - 0xE000);
|
||||
}
|
||||
else if ((addr >= 0xF000) && (addr < 0xFE00))
|
||||
{
|
||||
SetCDL(flags, "WRAM", (A.RAM_Bank * 0x1000) + (addr - 0xF000));
|
||||
}
|
||||
else if ((addr >= 0xFE00) && (addr < 0xFEA0) && A.ppu.DMA_OAM_access)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if ((addr >= 0xFF00) && (addr < 0xFF80)) // The game GOAL! Requires Hardware Regs to be accessible
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if ((addr >= 0xFF80))
|
||||
{
|
||||
SetCDL(flags, "HRAM", addr - 0xFF80);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (addr < 0x900)
|
||||
{
|
||||
if (addr < 0x100)
|
||||
{
|
||||
// return Either BIOS ROM or Game ROM
|
||||
if ((A.GB_bios_register & 0x1) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
A.mapper.MapCDL(addr, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (addr >= 0x200)
|
||||
{
|
||||
// return Either BIOS ROM or Game ROM
|
||||
if (((A.GB_bios_register & 0x1) == 0) && A.is_GBC)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
A.mapper.MapCDL(addr, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
A.mapper.MapCDL(addr, flags);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (addr < 0x8000)
|
||||
{
|
||||
A.mapper.MapCDL(addr, flags);
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xA000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xC000)
|
||||
{
|
||||
A.mapper.MapCDL(addr, flags);
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xD000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xE000)
|
||||
{
|
||||
SetCDL(flags, "WRAM", (A.RAM_Bank * 0x1000) + (addr - 0xD000));
|
||||
}
|
||||
else if (addr < 0xF000)
|
||||
{
|
||||
SetCDL(flags, "WRAM", addr - 0xE000);
|
||||
}
|
||||
else if (addr < 0xFE00)
|
||||
{
|
||||
SetCDL(flags, "WRAM", (A.RAM_Bank * 0x1000) + (addr - 0xF000));
|
||||
}
|
||||
else if (addr < 0xFEA0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xFF00)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xFF80)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (addr < 0xFFFF)
|
||||
{
|
||||
SetCDL(flags, "HRAM", addr - 0xFF80);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x : IDebuggable
|
||||
{
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
return new Dictionary<string, RegisterValue>
|
||||
{
|
||||
/*
|
||||
["A"] = cpu.A,
|
||||
["X"] = cpu.X,
|
||||
["Y"] = cpu.Y,
|
||||
["S"] = cpu.S,
|
||||
["PC"] = cpu.PC,
|
||||
["Flag C"] = cpu.FlagC,
|
||||
["Flag Z"] = cpu.FlagZ,
|
||||
["Flag I"] = cpu.FlagI,
|
||||
["Flag D"] = cpu.FlagD,
|
||||
["Flag B"] = cpu.FlagB,
|
||||
["Flag V"] = cpu.FlagV,
|
||||
["Flag N"] = cpu.FlagN,
|
||||
["Flag T"] = cpu.FlagT
|
||||
*/
|
||||
};
|
||||
}
|
||||
|
||||
public void SetCpuRegister(string register, int value)
|
||||
{
|
||||
switch (register)
|
||||
{
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
case "A":
|
||||
//cpu.A = (byte)value;
|
||||
break;
|
||||
case "X":
|
||||
//cpu.X = (byte)value;
|
||||
break;
|
||||
case "Y":
|
||||
//cpu.Y = (byte)value;
|
||||
break;
|
||||
case "S":
|
||||
//cpu.S = (byte)value;
|
||||
break;
|
||||
case "PC":
|
||||
//cpu.PC = (ushort)value;
|
||||
break;
|
||||
case "Flag I":
|
||||
//cpu.FlagI = value > 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public IMemoryCallbackSystem MemoryCallbacks { get; } = new MemoryCallbackSystem(new[] { "System Bus" });
|
||||
|
||||
public bool CanStep(StepType type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
[FeatureNotImplemented]
|
||||
public void Step(StepType type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public long TotalExecutedCycles
|
||||
{
|
||||
get { return (long)A.cpu.TotalExecutedCycles; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,493 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Nintendo.GBHawk;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x : IEmulator, IVideoProvider, ISoundProvider
|
||||
{
|
||||
public IEmulatorServiceProvider ServiceProvider { get; }
|
||||
|
||||
public ControllerDefinition ControllerDefinition => _controllerDeck.Definition;
|
||||
|
||||
public bool FrameAdvance(IController controller, bool render, bool rendersound)
|
||||
{
|
||||
//Console.WriteLine("-----------------------FRAME-----------------------");
|
||||
//Update the color palette if a setting changed
|
||||
if (Link4xSettings.Palette_A == GBHawk.GBHawk.GBSettings.PaletteType.BW)
|
||||
{
|
||||
A.color_palette[0] = color_palette_BW[0];
|
||||
A.color_palette[1] = color_palette_BW[1];
|
||||
A.color_palette[2] = color_palette_BW[2];
|
||||
A.color_palette[3] = color_palette_BW[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
A.color_palette[0] = color_palette_Gr[0];
|
||||
A.color_palette[1] = color_palette_Gr[1];
|
||||
A.color_palette[2] = color_palette_Gr[2];
|
||||
A.color_palette[3] = color_palette_Gr[3];
|
||||
}
|
||||
|
||||
if (Link4xSettings.Palette_B == GBHawk.GBHawk.GBSettings.PaletteType.BW)
|
||||
{
|
||||
B.color_palette[0] = color_palette_BW[0];
|
||||
B.color_palette[1] = color_palette_BW[1];
|
||||
B.color_palette[2] = color_palette_BW[2];
|
||||
B.color_palette[3] = color_palette_BW[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
B.color_palette[0] = color_palette_Gr[0];
|
||||
B.color_palette[1] = color_palette_Gr[1];
|
||||
B.color_palette[2] = color_palette_Gr[2];
|
||||
B.color_palette[3] = color_palette_Gr[3];
|
||||
}
|
||||
|
||||
if (Link4xSettings.Palette_C == GBHawk.GBHawk.GBSettings.PaletteType.BW)
|
||||
{
|
||||
C.color_palette[0] = color_palette_BW[0];
|
||||
C.color_palette[1] = color_palette_BW[1];
|
||||
C.color_palette[2] = color_palette_BW[2];
|
||||
C.color_palette[3] = color_palette_BW[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
C.color_palette[0] = color_palette_Gr[0];
|
||||
C.color_palette[1] = color_palette_Gr[1];
|
||||
C.color_palette[2] = color_palette_Gr[2];
|
||||
C.color_palette[3] = color_palette_Gr[3];
|
||||
}
|
||||
|
||||
if (Link4xSettings.Palette_D == GBHawk.GBHawk.GBSettings.PaletteType.BW)
|
||||
{
|
||||
D.color_palette[0] = color_palette_BW[0];
|
||||
D.color_palette[1] = color_palette_BW[1];
|
||||
D.color_palette[2] = color_palette_BW[2];
|
||||
D.color_palette[3] = color_palette_BW[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
D.color_palette[0] = color_palette_Gr[0];
|
||||
D.color_palette[1] = color_palette_Gr[1];
|
||||
D.color_palette[2] = color_palette_Gr[2];
|
||||
D.color_palette[3] = color_palette_Gr[3];
|
||||
}
|
||||
|
||||
if (_tracer.Enabled)
|
||||
{
|
||||
A.cpu.TraceCallback = s => _tracer.Put(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
A.cpu.TraceCallback = null;
|
||||
}
|
||||
|
||||
_frame++;
|
||||
|
||||
if (controller.IsPressed("Power"))
|
||||
{
|
||||
HardReset();
|
||||
}
|
||||
|
||||
if (controller.IsPressed("Toggle Cable LC") | controller.IsPressed("Toggle Cable CR") | controller.IsPressed("Toggle Cable RL"))
|
||||
{
|
||||
// if any connection exists, disconnect it
|
||||
// otherwise connect in order of precedence
|
||||
// only one event can happen per frame, either a connection or disconnection
|
||||
if (_cableconnected_LC | _cableconnected_CR | _cableconnected_RL)
|
||||
{
|
||||
_cableconnected_LC = _cableconnected_CR = _cableconnected_RL = false;
|
||||
do_2_next = false;
|
||||
}
|
||||
else if (controller.IsPressed("Toggle Cable LC"))
|
||||
{
|
||||
_cableconnected_LC = true;
|
||||
}
|
||||
else if (controller.IsPressed("Toggle Cable CR"))
|
||||
{
|
||||
_cableconnected_CR = true;
|
||||
}
|
||||
else if (controller.IsPressed("Toggle Cable RL"))
|
||||
{
|
||||
_cableconnected_RL = true;
|
||||
}
|
||||
|
||||
Console.WriteLine("Cable connect status:");
|
||||
Console.WriteLine("LC: " + _cableconnected_LC);
|
||||
Console.WriteLine("CR: " + _cableconnected_CR);
|
||||
Console.WriteLine("RL: " + _cableconnected_RL);
|
||||
}
|
||||
|
||||
_islag = true;
|
||||
|
||||
GetControllerState(controller);
|
||||
|
||||
do_frame_fill = false;
|
||||
do_frame();
|
||||
if (do_frame_fill)
|
||||
{
|
||||
FillVideoBuffer();
|
||||
}
|
||||
|
||||
_islag = A._islag & B._islag & C._islag & D._islag;
|
||||
|
||||
if (_islag)
|
||||
{
|
||||
_lagcount++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void do_frame()
|
||||
{
|
||||
// advance one full frame
|
||||
for (int i = 0; i < 70224; i++)
|
||||
{
|
||||
A.do_single_step();
|
||||
B.do_single_step();
|
||||
C.do_single_step();
|
||||
D.do_single_step();
|
||||
|
||||
if (_cableconnected_LC)
|
||||
{
|
||||
// the signal to shift out a bit is when serial_clock = 1
|
||||
if (((A.serialport.serial_clock == 1) || (A.serialport.serial_clock == 2)) && (A.serialport.clk_rate > 0) && !do_2_next)
|
||||
{
|
||||
A.serialport.going_out = (byte)(A.serialport.serial_data >> 7);
|
||||
|
||||
if ((C.serialport.clk_rate == -1) && C.serialport.serial_start && A.serialport.can_pulse)
|
||||
{
|
||||
C.serialport.serial_clock = A.serialport.serial_clock;
|
||||
C.serialport.going_out = (byte)(C.serialport.serial_data >> 7);
|
||||
C.serialport.coming_in = A.serialport.going_out;
|
||||
}
|
||||
|
||||
A.serialport.coming_in = C.serialport.going_out;
|
||||
A.serialport.can_pulse = false;
|
||||
}
|
||||
else if (((C.serialport.serial_clock == 1) || (C.serialport.serial_clock == 2)) && (C.serialport.clk_rate > 0))
|
||||
{
|
||||
do_2_next = false;
|
||||
|
||||
C.serialport.going_out = (byte)(C.serialport.serial_data >> 7);
|
||||
|
||||
if ((A.serialport.clk_rate == -1) && A.serialport.serial_start && C.serialport.can_pulse)
|
||||
{
|
||||
A.serialport.serial_clock = C.serialport.serial_clock;
|
||||
A.serialport.going_out = (byte)(A.serialport.serial_data >> 7);
|
||||
A.serialport.coming_in = C.serialport.going_out;
|
||||
}
|
||||
|
||||
C.serialport.coming_in = A.serialport.going_out;
|
||||
C.serialport.can_pulse = false;
|
||||
|
||||
if (C.serialport.serial_clock == 2) { do_2_next = true; }
|
||||
}
|
||||
else
|
||||
{
|
||||
do_2_next = false;
|
||||
}
|
||||
}
|
||||
else if (_cableconnected_CR)
|
||||
{
|
||||
// the signal to shift out a bit is when serial_clock = 1
|
||||
if (((C.serialport.serial_clock == 1) || (C.serialport.serial_clock == 2)) && (C.serialport.clk_rate > 0) && !do_2_next)
|
||||
{
|
||||
C.serialport.going_out = (byte)(C.serialport.serial_data >> 7);
|
||||
|
||||
if ((D.serialport.clk_rate == -1) && D.serialport.serial_start && C.serialport.can_pulse)
|
||||
{
|
||||
D.serialport.serial_clock = C.serialport.serial_clock;
|
||||
D.serialport.going_out = (byte)(D.serialport.serial_data >> 7);
|
||||
D.serialport.coming_in = C.serialport.going_out;
|
||||
}
|
||||
|
||||
C.serialport.coming_in = D.serialport.going_out;
|
||||
C.serialport.can_pulse = false;
|
||||
}
|
||||
else if (((D.serialport.serial_clock == 1) || (D.serialport.serial_clock == 2)) && (D.serialport.clk_rate > 0))
|
||||
{
|
||||
do_2_next = false;
|
||||
|
||||
D.serialport.going_out = (byte)(D.serialport.serial_data >> 7);
|
||||
|
||||
if ((C.serialport.clk_rate == -1) && C.serialport.serial_start && D.serialport.can_pulse)
|
||||
{
|
||||
C.serialport.serial_clock = D.serialport.serial_clock;
|
||||
C.serialport.going_out = (byte)(C.serialport.serial_data >> 7);
|
||||
C.serialport.coming_in = D.serialport.going_out;
|
||||
}
|
||||
|
||||
D.serialport.coming_in = C.serialport.going_out;
|
||||
D.serialport.can_pulse = false;
|
||||
|
||||
if (D.serialport.serial_clock == 2) { do_2_next = true; }
|
||||
}
|
||||
else
|
||||
{
|
||||
do_2_next = false;
|
||||
}
|
||||
}
|
||||
else if (_cableconnected_RL)
|
||||
{
|
||||
// the signal to shift out a bit is when serial_clock = 1
|
||||
if (((D.serialport.serial_clock == 1) || (D.serialport.serial_clock == 2)) && (D.serialport.clk_rate > 0) && !do_2_next)
|
||||
{
|
||||
D.serialport.going_out = (byte)(D.serialport.serial_data >> 7);
|
||||
|
||||
if ((A.serialport.clk_rate == -1) && A.serialport.serial_start && D.serialport.can_pulse)
|
||||
{
|
||||
A.serialport.serial_clock = D.serialport.serial_clock;
|
||||
A.serialport.going_out = (byte)(A.serialport.serial_data >> 7);
|
||||
A.serialport.coming_in = D.serialport.going_out;
|
||||
}
|
||||
|
||||
D.serialport.coming_in = A.serialport.going_out;
|
||||
D.serialport.can_pulse = false;
|
||||
}
|
||||
else if (((A.serialport.serial_clock == 1) || (A.serialport.serial_clock == 2)) && (A.serialport.clk_rate > 0))
|
||||
{
|
||||
do_2_next = false;
|
||||
|
||||
A.serialport.going_out = (byte)(A.serialport.serial_data >> 7);
|
||||
|
||||
if ((D.serialport.clk_rate == -1) && D.serialport.serial_start && A.serialport.can_pulse)
|
||||
{
|
||||
D.serialport.serial_clock = A.serialport.serial_clock;
|
||||
D.serialport.going_out = (byte)(D.serialport.serial_data >> 7);
|
||||
D.serialport.coming_in = A.serialport.going_out;
|
||||
}
|
||||
|
||||
A.serialport.coming_in = D.serialport.going_out;
|
||||
A.serialport.can_pulse = false;
|
||||
|
||||
if (A.serialport.serial_clock == 2) { do_2_next = true; }
|
||||
}
|
||||
else
|
||||
{
|
||||
do_2_next = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if we hit a frame boundary, update video
|
||||
if (A.vblank_rise)
|
||||
{
|
||||
// update the controller state on VBlank
|
||||
A.controller_state = A_controller;
|
||||
|
||||
// check if controller state caused interrupt
|
||||
A.do_controller_check();
|
||||
|
||||
// send the image on VBlank
|
||||
A.SendVideoBuffer();
|
||||
|
||||
A.vblank_rise = false;
|
||||
do_frame_fill = true;
|
||||
}
|
||||
if (B.vblank_rise)
|
||||
{
|
||||
// update the controller state on VBlank
|
||||
B.controller_state = B_controller;
|
||||
|
||||
// check if controller state caused interrupt
|
||||
B.do_controller_check();
|
||||
|
||||
// send the image on VBlank
|
||||
B.SendVideoBuffer();
|
||||
|
||||
B.vblank_rise = false;
|
||||
do_frame_fill = true;
|
||||
}
|
||||
if (C.vblank_rise)
|
||||
{
|
||||
// update the controller state on VBlank
|
||||
C.controller_state = C_controller;
|
||||
|
||||
// check if controller state caused interrupt
|
||||
C.do_controller_check();
|
||||
|
||||
// send the image on VBlank
|
||||
C.SendVideoBuffer();
|
||||
|
||||
C.vblank_rise = false;
|
||||
do_frame_fill = true;
|
||||
}
|
||||
if (D.vblank_rise)
|
||||
{
|
||||
// update the controller state on VBlank
|
||||
D.controller_state = D_controller;
|
||||
|
||||
// check if controller state caused interrupt
|
||||
D.do_controller_check();
|
||||
|
||||
// send the image on VBlank
|
||||
D.SendVideoBuffer();
|
||||
|
||||
D.vblank_rise = false;
|
||||
do_frame_fill = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetControllerState(IController controller)
|
||||
{
|
||||
InputCallbacks.Call();
|
||||
A_controller = _controllerDeck.ReadPort1(controller);
|
||||
B_controller = _controllerDeck.ReadPort2(controller);
|
||||
C_controller = _controllerDeck.ReadPort3(controller);
|
||||
D_controller = _controllerDeck.ReadPort4(controller);
|
||||
}
|
||||
|
||||
public int Frame => _frame;
|
||||
|
||||
public string SystemId => "GB4x";
|
||||
|
||||
public bool DeterministicEmulation { get; set; }
|
||||
|
||||
public void ResetCounters()
|
||||
{
|
||||
_frame = 0;
|
||||
_lagcount = 0;
|
||||
_islag = false;
|
||||
}
|
||||
|
||||
public CoreComm CoreComm { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
A.Dispose();
|
||||
B.Dispose();
|
||||
C.Dispose();
|
||||
D.Dispose();
|
||||
}
|
||||
|
||||
#region Video provider
|
||||
|
||||
public int _frameHz = 60;
|
||||
|
||||
public int[] _vidbuffer = new int[160 * 2 * 144 * 2];
|
||||
|
||||
public int[] GetVideoBuffer()
|
||||
{
|
||||
return _vidbuffer;
|
||||
}
|
||||
|
||||
public void FillVideoBuffer()
|
||||
{
|
||||
// combine the 2 video buffers from the instances
|
||||
for (int i = 0; i < 144; i++)
|
||||
{
|
||||
for (int j = 0; j < 160; j++)
|
||||
{
|
||||
_vidbuffer[i * 320 + j] = A.frame_buffer[i * 160 + j];
|
||||
_vidbuffer[(i + 144) * 320 + j] = C.frame_buffer[i * 160 + j];
|
||||
_vidbuffer[(i + 144) * 320 + j + 160] = C.frame_buffer[i * 160 + j];
|
||||
_vidbuffer[i * 320 + j + 160] = D.frame_buffer[i * 160 + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int VirtualWidth => 160 * 2;
|
||||
public int VirtualHeight => 144 * 2;
|
||||
public int BufferWidth => 160 * 2;
|
||||
public int BufferHeight => 144 * 2;
|
||||
public int BackgroundColor => unchecked((int)0xFF000000);
|
||||
public int VsyncNumerator => _frameHz;
|
||||
public int VsyncDenominator => 1;
|
||||
|
||||
public static readonly uint[] color_palette_BW = { 0xFFFFFFFF , 0xFFAAAAAA, 0xFF555555, 0xFF000000 };
|
||||
public static readonly uint[] color_palette_Gr = { 0xFFA4C505, 0xFF88A905, 0xFF1D551D, 0xFF052505 };
|
||||
|
||||
public uint[] color_palette = new uint[4];
|
||||
|
||||
#endregion
|
||||
|
||||
#region audio
|
||||
|
||||
public bool CanProvideAsync => false;
|
||||
|
||||
public void SetSyncMode(SyncSoundMode mode)
|
||||
{
|
||||
if (mode != SyncSoundMode.Sync)
|
||||
{
|
||||
throw new InvalidOperationException("Only Sync mode is supported_");
|
||||
}
|
||||
}
|
||||
|
||||
public SyncSoundMode SyncMode => SyncSoundMode.Sync;
|
||||
|
||||
public void GetSamplesSync(out short[] samples, out int nsamp)
|
||||
{
|
||||
short[] temp_samp_A;
|
||||
short[] temp_samp_B;
|
||||
short[] temp_samp_C;
|
||||
short[] temp_samp_D;
|
||||
|
||||
int nsamp_A;
|
||||
int nsamp_B;
|
||||
int nsamp_C;
|
||||
int nsamp_D;
|
||||
|
||||
A.audio.GetSamplesSync(out temp_samp_A, out nsamp_A);
|
||||
B.audio.GetSamplesSync(out temp_samp_B, out nsamp_B);
|
||||
C.audio.GetSamplesSync(out temp_samp_C, out nsamp_C);
|
||||
D.audio.GetSamplesSync(out temp_samp_D, out nsamp_D);
|
||||
|
||||
if (Link4xSettings.AudioSet == GBLink4xSettings.AudioSrc.A)
|
||||
{
|
||||
samples = temp_samp_A;
|
||||
nsamp = nsamp_A;
|
||||
}
|
||||
else if (Link4xSettings.AudioSet == GBLink4xSettings.AudioSrc.B)
|
||||
{
|
||||
samples = temp_samp_C;
|
||||
nsamp = nsamp_C;
|
||||
}
|
||||
else if (Link4xSettings.AudioSet == GBLink4xSettings.AudioSrc.C)
|
||||
{
|
||||
samples = temp_samp_C;
|
||||
nsamp = nsamp_C;
|
||||
}
|
||||
else if (Link4xSettings.AudioSet == GBLink4xSettings.AudioSrc.D)
|
||||
{
|
||||
samples = temp_samp_D;
|
||||
nsamp = nsamp_D;
|
||||
}
|
||||
else
|
||||
{
|
||||
samples = new short[0];
|
||||
nsamp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetSamplesAsync(short[] samples)
|
||||
{
|
||||
throw new NotSupportedException("Async is not available");
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
A.audio.DiscardSamples();
|
||||
C.audio.DiscardSamples();
|
||||
D.audio.DiscardSamples();
|
||||
}
|
||||
|
||||
private void GetSamples(short[] samples)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void DisposeSound()
|
||||
{
|
||||
A.audio.DisposeSound();
|
||||
C.audio.DisposeSound();
|
||||
D.audio.DisposeSound();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x : IInputPollable
|
||||
{
|
||||
public int LagCount
|
||||
{
|
||||
get { return _lagcount; }
|
||||
set { _lagcount = value; }
|
||||
}
|
||||
|
||||
public bool IsLagFrame
|
||||
{
|
||||
get { return _islag; }
|
||||
set { _islag = value; }
|
||||
}
|
||||
|
||||
public IInputCallbackSystem InputCallbacks { get; } = new InputCallbackSystem();
|
||||
|
||||
public bool _islag = true;
|
||||
private int _lagcount;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x
|
||||
{
|
||||
private IMemoryDomains MemoryDomains;
|
||||
|
||||
public void SetupMemoryDomains()
|
||||
{
|
||||
var domains = new List<MemoryDomain>
|
||||
{
|
||||
new MemoryDomainDelegate(
|
||||
"Main RAM A",
|
||||
A.RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => A.RAM[addr],
|
||||
(addr, value) => A.RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"Main RAM B",
|
||||
B.RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => B.RAM[addr],
|
||||
(addr, value) => B.RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"Main RAM C",
|
||||
C.RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => C.RAM[addr],
|
||||
(addr, value) => C.RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"Main RAM D",
|
||||
D.RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => D.RAM[addr],
|
||||
(addr, value) => D.RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"Zero Page RAM A",
|
||||
A.ZP_RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => A.ZP_RAM[addr],
|
||||
(addr, value) => A.ZP_RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"Zero Page RAM B",
|
||||
B.ZP_RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => B.ZP_RAM[addr],
|
||||
(addr, value) => B.ZP_RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"Zero Page RAM C",
|
||||
C.ZP_RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => C.ZP_RAM[addr],
|
||||
(addr, value) => C.ZP_RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"Zero Page RAM D",
|
||||
D.ZP_RAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => D.ZP_RAM[addr],
|
||||
(addr, value) => D.ZP_RAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"System Bus A",
|
||||
0X10000,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => PeekSystemBusA(addr),
|
||||
(addr, value) => PokeSystemBusA(addr, value),
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"System Bus B",
|
||||
0X10000,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => PeekSystemBusB(addr),
|
||||
(addr, value) => PokeSystemBusB(addr, value),
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"System Bus C",
|
||||
0X10000,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => PeekSystemBusC(addr),
|
||||
(addr, value) => PokeSystemBusC(addr, value),
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"System Bus D",
|
||||
0X10000,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => PeekSystemBusD(addr),
|
||||
(addr, value) => PokeSystemBusD(addr, value),
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"ROM A",
|
||||
A._rom.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => A._rom[addr],
|
||||
(addr, value) => A._rom[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"ROM B",
|
||||
B._rom.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => B._rom[addr],
|
||||
(addr, value) => B._rom[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"ROM C",
|
||||
C._rom.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => C._rom[addr],
|
||||
(addr, value) => C._rom[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"ROM D",
|
||||
D._rom.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => D._rom[addr],
|
||||
(addr, value) => D._rom[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"VRAM A",
|
||||
A.VRAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => A.VRAM[addr],
|
||||
(addr, value) => A.VRAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"VRAM B",
|
||||
B.VRAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => B.VRAM[addr],
|
||||
(addr, value) => B.VRAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"VRAM C",
|
||||
C.VRAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => C.VRAM[addr],
|
||||
(addr, value) => C.VRAM[addr] = value,
|
||||
1),
|
||||
new MemoryDomainDelegate(
|
||||
"VRAM D",
|
||||
D.VRAM.Length,
|
||||
MemoryDomain.Endian.Little,
|
||||
addr => D.VRAM[addr],
|
||||
(addr, value) => D.VRAM[addr] = value,
|
||||
1)
|
||||
};
|
||||
|
||||
if (A.cart_RAM != null)
|
||||
{
|
||||
var CartRamA = new MemoryDomainByteArray("Cart RAM L", MemoryDomain.Endian.Little, A.cart_RAM, true, 1);
|
||||
domains.Add(CartRamA);
|
||||
}
|
||||
|
||||
if (B.cart_RAM != null)
|
||||
{
|
||||
var CartRamB = new MemoryDomainByteArray("Cart RAM B", MemoryDomain.Endian.Little, B.cart_RAM, true, 1);
|
||||
domains.Add(CartRamB);
|
||||
}
|
||||
|
||||
if (C.cart_RAM != null)
|
||||
{
|
||||
var CartRamC = new MemoryDomainByteArray("Cart RAM C", MemoryDomain.Endian.Little, C.cart_RAM, true, 1);
|
||||
domains.Add(CartRamC);
|
||||
}
|
||||
|
||||
if (D.cart_RAM != null)
|
||||
{
|
||||
var CartRamD = new MemoryDomainByteArray("Cart RAM D", MemoryDomain.Endian.Little, D.cart_RAM, true, 1);
|
||||
domains.Add(CartRamD);
|
||||
}
|
||||
|
||||
MemoryDomains = new MemoryDomainList(domains);
|
||||
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
|
||||
}
|
||||
|
||||
private byte PeekSystemBusA(long addr)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
return A.PeekMemory(addr2);
|
||||
}
|
||||
|
||||
private byte PeekSystemBusB(long addr)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
return B.PeekMemory(addr2);
|
||||
}
|
||||
|
||||
private byte PeekSystemBusC(long addr)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
return C.PeekMemory(addr2);
|
||||
}
|
||||
|
||||
private byte PeekSystemBusD(long addr)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
return D.PeekMemory(addr2);
|
||||
}
|
||||
|
||||
private void PokeSystemBusA(long addr, byte value)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
A.WriteMemory(addr2, value);
|
||||
}
|
||||
|
||||
private void PokeSystemBusB(long addr, byte value)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
B.WriteMemory(addr2, value);
|
||||
}
|
||||
|
||||
private void PokeSystemBusC(long addr, byte value)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
C.WriteMemory(addr2, value);
|
||||
}
|
||||
|
||||
private void PokeSystemBusD(long addr, byte value)
|
||||
{
|
||||
ushort addr2 = (ushort)(addr & 0xFFFF);
|
||||
D.WriteMemory(addr2, value);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x : ISaveRam
|
||||
{
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
if ((A.cart_RAM != null) || (B.cart_RAM != null) || (C.cart_RAM != null) || (D.cart_RAM != null))
|
||||
{
|
||||
int Len1 = 0;
|
||||
int Len2 = 0;
|
||||
int Len3 = 0;
|
||||
int Len4 = 0;
|
||||
int index = 0;
|
||||
|
||||
if (A.cart_RAM != null)
|
||||
{
|
||||
Len1 = A.cart_RAM.Length;
|
||||
}
|
||||
|
||||
if (B.cart_RAM != null)
|
||||
{
|
||||
Len2 = B.cart_RAM.Length;
|
||||
}
|
||||
|
||||
if (C.cart_RAM != null)
|
||||
{
|
||||
Len3 = C.cart_RAM.Length;
|
||||
}
|
||||
|
||||
if (D.cart_RAM != null)
|
||||
{
|
||||
Len4 = D.cart_RAM.Length;
|
||||
}
|
||||
|
||||
byte[] temp = new byte[Len1 + Len2 + Len3 + Len4];
|
||||
|
||||
if (A.cart_RAM != null)
|
||||
{
|
||||
for (int i = 0; i < A.cart_RAM.Length; i++)
|
||||
{
|
||||
temp[index] = A.cart_RAM[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (B.cart_RAM != null)
|
||||
{
|
||||
for (int i = 0; i < B.cart_RAM.Length; i++)
|
||||
{
|
||||
temp[index] = B.cart_RAM[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (C.cart_RAM != null)
|
||||
{
|
||||
for (int i = 0; i < C.cart_RAM.Length; i++)
|
||||
{
|
||||
temp[index] = C.cart_RAM[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (D.cart_RAM != null)
|
||||
{
|
||||
for (int i = 0; i < D.cart_RAM.Length; i++)
|
||||
{
|
||||
temp[index] = D.cart_RAM[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
if (Link4xSyncSettings.Use_SRAM)
|
||||
{
|
||||
int temp = 0;
|
||||
|
||||
if (A.cart_RAM != null)
|
||||
{
|
||||
Buffer.BlockCopy(data, temp, A.cart_RAM, 0, A.cart_RAM.Length);
|
||||
temp += A.cart_RAM.Length;
|
||||
}
|
||||
|
||||
if (B.cart_RAM != null)
|
||||
{
|
||||
Buffer.BlockCopy(data, temp, B.cart_RAM, 0, B.cart_RAM.Length);
|
||||
temp += B.cart_RAM.Length;
|
||||
}
|
||||
|
||||
if (C.cart_RAM != null)
|
||||
{
|
||||
Buffer.BlockCopy(data, temp, C.cart_RAM, 0, C.cart_RAM.Length);
|
||||
temp += C.cart_RAM.Length;
|
||||
}
|
||||
|
||||
if (D.cart_RAM != null)
|
||||
{
|
||||
Buffer.BlockCopy(data, temp, D.cart_RAM, 0, D.cart_RAM.Length);
|
||||
}
|
||||
|
||||
Console.WriteLine("loading SRAM here");
|
||||
}
|
||||
}
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
return (A.has_bat || B.has_bat || C.has_bat || D.has_bat) & Link4xSyncSettings.Use_SRAM;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Nintendo.GBHawk;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x : IEmulator, IStatable, ISettable<GBHawkLink4x.GBLink4xSettings, GBHawkLink4x.GBLink4xSyncSettings>
|
||||
{
|
||||
public GBLink4xSettings GetSettings()
|
||||
{
|
||||
return Link4xSettings.Clone();
|
||||
}
|
||||
|
||||
public GBLink4xSyncSettings GetSyncSettings()
|
||||
{
|
||||
return Link4xSyncSettings.Clone();
|
||||
}
|
||||
|
||||
public bool PutSettings(GBLink4xSettings o)
|
||||
{
|
||||
Link4xSettings = o;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool PutSyncSettings(GBLink4xSyncSettings o)
|
||||
{
|
||||
bool ret = GBLink4xSyncSettings.NeedsReboot(Link4xSyncSettings, o);
|
||||
Link4xSyncSettings = o;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private GBLink4xSettings Link4xSettings = new GBLink4xSettings();
|
||||
public GBLink4xSyncSettings Link4xSyncSettings = new GBLink4xSyncSettings();
|
||||
|
||||
public class GBLink4xSettings
|
||||
{
|
||||
[DisplayName("Color Mode")]
|
||||
[Description("Pick Between Green scale and Grey scale colors")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSettings.PaletteType.BW)]
|
||||
public GBHawk.GBHawk.GBSettings.PaletteType Palette_A { get; set; }
|
||||
|
||||
[DisplayName("Color Mode")]
|
||||
[Description("Pick Between Green scale and Grey scale colors")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSettings.PaletteType.BW)]
|
||||
public GBHawk.GBHawk.GBSettings.PaletteType Palette_B { get; set; }
|
||||
|
||||
[DisplayName("Color Mode")]
|
||||
[Description("Pick Between Green scale and Grey scale colors")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSettings.PaletteType.BW)]
|
||||
public GBHawk.GBHawk.GBSettings.PaletteType Palette_C { get; set; }
|
||||
|
||||
[DisplayName("Color Mode")]
|
||||
[Description("Pick Between Green scale and Grey scale colors")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSettings.PaletteType.BW)]
|
||||
public GBHawk.GBHawk.GBSettings.PaletteType Palette_D { get; set; }
|
||||
|
||||
public enum AudioSrc
|
||||
{
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
None
|
||||
}
|
||||
|
||||
[DisplayName("Audio Selection")]
|
||||
[Description("Choose Audio Source. Both will produce Stereo sound.")]
|
||||
[DefaultValue(AudioSrc.A)]
|
||||
public AudioSrc AudioSet { get; set; }
|
||||
|
||||
public GBLink4xSettings Clone()
|
||||
{
|
||||
return (GBLink4xSettings)MemberwiseClone();
|
||||
}
|
||||
}
|
||||
|
||||
public class GBLink4xSyncSettings
|
||||
{
|
||||
[DisplayName("Console Mode A")]
|
||||
[Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSyncSettings.ConsoleModeType.Auto)]
|
||||
public GBHawk.GBHawk.GBSyncSettings.ConsoleModeType ConsoleMode_A { get; set; }
|
||||
|
||||
[DisplayName("Console Mode B")]
|
||||
[Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSyncSettings.ConsoleModeType.Auto)]
|
||||
public GBHawk.GBHawk.GBSyncSettings.ConsoleModeType ConsoleMode_B { get; set; }
|
||||
|
||||
[DisplayName("Console Mode C")]
|
||||
[Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSyncSettings.ConsoleModeType.Auto)]
|
||||
public GBHawk.GBHawk.GBSyncSettings.ConsoleModeType ConsoleMode_C { get; set; }
|
||||
|
||||
[DisplayName("Console Mode D")]
|
||||
[Description("Pick which console to run, 'Auto' chooses from ROM extension, 'GB' and 'GBC' chooses the respective system")]
|
||||
[DefaultValue(GBHawk.GBHawk.GBSyncSettings.ConsoleModeType.Auto)]
|
||||
public GBHawk.GBHawk.GBSyncSettings.ConsoleModeType ConsoleMode_D { get; set; }
|
||||
|
||||
[DisplayName("CGB in GBA")]
|
||||
[Description("Emulate GBA hardware running a CGB game, instead of CGB hardware. Relevant only for titles that detect the presense of a GBA, such as Shantae.")]
|
||||
[DefaultValue(false)]
|
||||
public bool GBACGB { get; set; }
|
||||
|
||||
[DisplayName("RTC Initial Time A")]
|
||||
[Description("Set the initial RTC time in terms of elapsed seconds.")]
|
||||
[DefaultValue(0)]
|
||||
public int RTCInitialTime_A
|
||||
{
|
||||
get { return _RTCInitialTime_A; }
|
||||
set { _RTCInitialTime_A = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); }
|
||||
}
|
||||
|
||||
[DisplayName("RTC Initial Time B")]
|
||||
[Description("Set the initial RTC time in terms of elapsed seconds.")]
|
||||
[DefaultValue(0)]
|
||||
public int RTCInitialTime_B
|
||||
{
|
||||
get { return _RTCInitialTime_B; }
|
||||
set { _RTCInitialTime_B = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); }
|
||||
}
|
||||
|
||||
[DisplayName("RTC Initial Time C")]
|
||||
[Description("Set the initial RTC time in terms of elapsed seconds.")]
|
||||
[DefaultValue(0)]
|
||||
public int RTCInitialTime_C
|
||||
{
|
||||
get { return _RTCInitialTime_C; }
|
||||
set { _RTCInitialTime_C = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); }
|
||||
}
|
||||
|
||||
[DisplayName("RTC Initial Time D")]
|
||||
[Description("Set the initial RTC time in terms of elapsed seconds.")]
|
||||
[DefaultValue(0)]
|
||||
public int RTCInitialTime_D
|
||||
{
|
||||
get { return _RTCInitialTime_D; }
|
||||
set { _RTCInitialTime_D = Math.Max(0, Math.Min(1024 * 24 * 60 * 60, value)); }
|
||||
}
|
||||
|
||||
[DisplayName("Timer Div Initial Time A")]
|
||||
[Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")]
|
||||
[DefaultValue(8)]
|
||||
public int DivInitialTime_A
|
||||
{
|
||||
get { return _DivInitialTime_A; }
|
||||
set { _DivInitialTime_A = Math.Min((ushort)65535, (ushort)value); }
|
||||
}
|
||||
|
||||
[DisplayName("Timer Div Initial Time B")]
|
||||
[Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")]
|
||||
[DefaultValue(8)]
|
||||
public int DivInitialTime_B
|
||||
{
|
||||
get { return _DivInitialTime_B; }
|
||||
set { _DivInitialTime_B = Math.Min((ushort)65535, (ushort)value); }
|
||||
}
|
||||
|
||||
[DisplayName("Timer Div Initial Time C")]
|
||||
[Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")]
|
||||
[DefaultValue(8)]
|
||||
public int DivInitialTime_C
|
||||
{
|
||||
get { return _DivInitialTime_C; }
|
||||
set { _DivInitialTime_C = Math.Min((ushort)65535, (ushort)value); }
|
||||
}
|
||||
|
||||
[DisplayName("Timer Div Initial Time D")]
|
||||
[Description("Don't change from 0 unless it's hardware accurate. GBA GBC mode is known to be 8.")]
|
||||
[DefaultValue(8)]
|
||||
public int DivInitialTime_D
|
||||
{
|
||||
get { return _DivInitialTime_D; }
|
||||
set { _DivInitialTime_D = Math.Min((ushort)65535, (ushort)value); }
|
||||
}
|
||||
|
||||
[DisplayName("Use Existing SaveRAM")]
|
||||
[Description("When true, existing SaveRAM will be loaded at boot up")]
|
||||
[DefaultValue(false)]
|
||||
public bool Use_SRAM { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
private int _RTCInitialTime_A;
|
||||
private int _RTCInitialTime_B;
|
||||
private int _RTCInitialTime_C;
|
||||
private int _RTCInitialTime_D;
|
||||
[JsonIgnore]
|
||||
public ushort _DivInitialTime_A = 8;
|
||||
public ushort _DivInitialTime_B = 8;
|
||||
public ushort _DivInitialTime_C = 8;
|
||||
public ushort _DivInitialTime_D = 8;
|
||||
|
||||
public GBLink4xSyncSettings Clone()
|
||||
{
|
||||
return (GBLink4xSyncSettings)MemberwiseClone();
|
||||
}
|
||||
|
||||
public static bool NeedsReboot(GBLink4xSyncSettings x, GBLink4xSyncSettings y)
|
||||
{
|
||||
return !DeepEquality.DeepEquals(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Nintendo.GBHawk;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public partial class GBHawkLink4x : IStatable
|
||||
{
|
||||
public bool BinarySaveStatesPreferred => true;
|
||||
|
||||
public void SaveStateText(TextWriter writer)
|
||||
{
|
||||
A.SaveStateText(writer);
|
||||
B.SaveStateText(writer);
|
||||
C.SaveStateText(writer);
|
||||
D.SaveStateText(writer);
|
||||
SyncState(new Serializer(writer));
|
||||
}
|
||||
|
||||
public void LoadStateText(TextReader reader)
|
||||
{
|
||||
A.LoadStateText(reader);
|
||||
B.LoadStateText(reader);
|
||||
C.LoadStateText(reader);
|
||||
D.LoadStateText(reader);
|
||||
SyncState(new Serializer(reader));
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter bw)
|
||||
{
|
||||
A.SaveStateBinary(bw);
|
||||
B.SaveStateBinary(bw);
|
||||
C.SaveStateBinary(bw);
|
||||
D.SaveStateBinary(bw);
|
||||
// other variables
|
||||
SyncState(new Serializer(bw));
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader br)
|
||||
{
|
||||
A.LoadStateBinary(br);
|
||||
B.LoadStateBinary(br);
|
||||
C.LoadStateBinary(br);
|
||||
D.LoadStateBinary(br);
|
||||
// other variables
|
||||
SyncState(new Serializer(br));
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
BinaryWriter bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
//private JsonSerializer ser = new JsonSerializer { Formatting = Formatting.Indented };
|
||||
|
||||
private void SyncState(Serializer ser)
|
||||
{
|
||||
ser.Sync("Lag", ref _lagcount);
|
||||
ser.Sync("Frame", ref _frame);
|
||||
ser.Sync("IsLag", ref _islag);
|
||||
ser.Sync(nameof(_cableconnected_LC), ref _cableconnected_LC);
|
||||
ser.Sync(nameof(_cableconnected_CR), ref _cableconnected_CR);
|
||||
ser.Sync(nameof(_cableconnected_RL), ref _cableconnected_RL);
|
||||
ser.Sync(nameof(do_2_next), ref do_2_next);
|
||||
ser.Sync(nameof(A_controller), ref A_controller);
|
||||
ser.Sync(nameof(B_controller), ref B_controller);
|
||||
ser.Sync(nameof(C_controller), ref C_controller);
|
||||
ser.Sync(nameof(D_controller), ref D_controller);
|
||||
_controllerDeck.SyncState(ser);
|
||||
|
||||
if (ser.IsReader)
|
||||
{
|
||||
FillVideoBuffer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
using System;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
using BizHawk.Emulation.Cores.Nintendo.GBHawk;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
[Core(
|
||||
"GBHawkLink4x",
|
||||
"",
|
||||
isPorted: false,
|
||||
isReleased: true)]
|
||||
[ServiceNotApplicable(typeof(IDriveLight))]
|
||||
public partial class GBHawkLink4x : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable,
|
||||
ISettable<GBHawkLink4x.GBLink4xSettings, GBHawkLink4x.GBLink4xSyncSettings>
|
||||
{
|
||||
// we want to create two GBHawk instances that we will run concurrently
|
||||
public GBHawk.GBHawk A;
|
||||
public GBHawk.GBHawk B;
|
||||
public GBHawk.GBHawk C;
|
||||
public GBHawk.GBHawk D;
|
||||
|
||||
// if true, the link cable is currently connected
|
||||
private bool _cableconnected_LC = false;
|
||||
private bool _cableconnected_CR = false;
|
||||
private bool _cableconnected_RL = false;
|
||||
|
||||
private bool do_2_next = false;
|
||||
|
||||
public byte A_controller, B_controller, C_controller, D_controller;
|
||||
|
||||
public bool do_frame_fill;
|
||||
|
||||
//[CoreConstructor("GB", "GBC")]
|
||||
public GBHawkLink4x(CoreComm comm, GameInfo game_A, byte[] rom_A, GameInfo game_B, byte[] rom_B, GameInfo game_C, byte[] rom_C, GameInfo game_D, byte[] rom_D, /*string gameDbFn,*/ object settings, object syncSettings)
|
||||
{
|
||||
var ser = new BasicServiceProvider(this);
|
||||
|
||||
Link4xSettings = (GBLink4xSettings)settings ?? new GBLink4xSettings();
|
||||
Link4xSyncSettings = (GBLink4xSyncSettings)syncSettings ?? new GBLink4xSyncSettings();
|
||||
_controllerDeck = new GBHawkLink4xControllerDeck(GBHawkLink4xControllerDeck.DefaultControllerName, GBHawkLink4xControllerDeck.DefaultControllerName,
|
||||
GBHawkLink4xControllerDeck.DefaultControllerName, GBHawkLink4xControllerDeck.DefaultControllerName);
|
||||
|
||||
CoreComm = comm;
|
||||
|
||||
var temp_set_A = new GBHawk.GBHawk.GBSettings();
|
||||
var temp_set_B = new GBHawk.GBHawk.GBSettings();
|
||||
var temp_set_C = new GBHawk.GBHawk.GBSettings();
|
||||
var temp_set_D = new GBHawk.GBHawk.GBSettings();
|
||||
|
||||
var temp_sync_A = new GBHawk.GBHawk.GBSyncSettings();
|
||||
var temp_sync_B = new GBHawk.GBHawk.GBSyncSettings();
|
||||
var temp_sync_C = new GBHawk.GBHawk.GBSyncSettings();
|
||||
var temp_sync_D = new GBHawk.GBHawk.GBSyncSettings();
|
||||
|
||||
temp_sync_A.ConsoleMode = Link4xSyncSettings.ConsoleMode_A;
|
||||
temp_sync_B.ConsoleMode = Link4xSyncSettings.ConsoleMode_B;
|
||||
temp_sync_C.ConsoleMode = Link4xSyncSettings.ConsoleMode_C;
|
||||
temp_sync_D.ConsoleMode = Link4xSyncSettings.ConsoleMode_D;
|
||||
|
||||
temp_sync_A.DivInitialTime = Link4xSyncSettings.DivInitialTime_A;
|
||||
temp_sync_B.DivInitialTime = Link4xSyncSettings.DivInitialTime_B;
|
||||
temp_sync_C.DivInitialTime = Link4xSyncSettings.DivInitialTime_C;
|
||||
temp_sync_D.DivInitialTime = Link4xSyncSettings.DivInitialTime_D;
|
||||
temp_sync_A.RTCInitialTime = Link4xSyncSettings.RTCInitialTime_A;
|
||||
temp_sync_B.RTCInitialTime = Link4xSyncSettings.RTCInitialTime_B;
|
||||
temp_sync_C.RTCInitialTime = Link4xSyncSettings.RTCInitialTime_C;
|
||||
temp_sync_D.RTCInitialTime = Link4xSyncSettings.RTCInitialTime_D;
|
||||
|
||||
A = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider },
|
||||
game_A, rom_A, temp_set_A, temp_sync_A);
|
||||
|
||||
B = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider },
|
||||
game_B, rom_B, temp_set_B, temp_sync_B);
|
||||
|
||||
C = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider },
|
||||
game_C, rom_C, temp_set_C, temp_sync_C);
|
||||
|
||||
D = new GBHawk.GBHawk(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider },
|
||||
game_D, rom_D, temp_set_D, temp_sync_D);
|
||||
|
||||
ser.Register<IVideoProvider>(this);
|
||||
ser.Register<ISoundProvider>(this);
|
||||
|
||||
_tracer = new TraceBuffer { Header = A.cpu.TraceHeader };
|
||||
ser.Register<ITraceable>(_tracer);
|
||||
|
||||
ServiceProvider = ser;
|
||||
|
||||
SetupMemoryDomains();
|
||||
|
||||
HardReset();
|
||||
}
|
||||
|
||||
public void HardReset()
|
||||
{
|
||||
A.HardReset();
|
||||
B.HardReset();
|
||||
C.HardReset();
|
||||
D.HardReset();
|
||||
}
|
||||
|
||||
public DisplayType Region => DisplayType.NTSC;
|
||||
|
||||
public int _frame = 0;
|
||||
|
||||
private readonly GBHawkLink4xControllerDeck _controllerDeck;
|
||||
|
||||
private readonly ITraceable _tracer;
|
||||
|
||||
private void ExecFetch(ushort addr)
|
||||
{
|
||||
uint flags = (uint)(MemoryCallbackFlags.AccessExecute);
|
||||
MemoryCallbacks.CallMemoryCallbacks(addr, 0, flags, "System Bus");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Common.ReflectionExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
public class GBHawkLink4xControllerDeck
|
||||
{
|
||||
public GBHawkLink4xControllerDeck(string controller1Name, string controller2Name, string controller3Name, string controller4Name)
|
||||
{
|
||||
if (!ValidControllerTypes.ContainsKey(controller1Name))
|
||||
{
|
||||
throw new InvalidOperationException("Invalid controller type: " + controller1Name);
|
||||
}
|
||||
|
||||
if (!ValidControllerTypes.ContainsKey(controller2Name))
|
||||
{
|
||||
throw new InvalidOperationException("Invalid controller type: " + controller2Name);
|
||||
}
|
||||
|
||||
if (!ValidControllerTypes.ContainsKey(controller3Name))
|
||||
{
|
||||
throw new InvalidOperationException("Invalid controller type: " + controller3Name);
|
||||
}
|
||||
|
||||
if (!ValidControllerTypes.ContainsKey(controller4Name))
|
||||
{
|
||||
throw new InvalidOperationException("Invalid controller type: " + controller4Name);
|
||||
}
|
||||
|
||||
Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1);
|
||||
Port2 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller2Name], 2);
|
||||
Port3 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller3Name], 3);
|
||||
Port4 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller3Name], 4);
|
||||
|
||||
Definition = new ControllerDefinition
|
||||
{
|
||||
Name = Port1.Definition.Name,
|
||||
BoolButtons = Port1.Definition.BoolButtons
|
||||
.Concat(Port2.Definition.BoolButtons)
|
||||
.Concat(Port3.Definition.BoolButtons)
|
||||
.Concat(Port4.Definition.BoolButtons)
|
||||
.Concat(new[] { "Toggle Cable LC" } )
|
||||
.Concat(new[] { "Toggle Cable CR" } )
|
||||
.Concat(new[] { "Toggle Cable RL" } )
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public byte ReadPort1(IController c)
|
||||
{
|
||||
return Port1.Read(c);
|
||||
}
|
||||
|
||||
public byte ReadPort2(IController c)
|
||||
{
|
||||
return Port2.Read(c);
|
||||
}
|
||||
|
||||
public byte ReadPort3(IController c)
|
||||
{
|
||||
return Port3.Read(c);
|
||||
}
|
||||
|
||||
public byte ReadPort4(IController c)
|
||||
{
|
||||
return Port4.Read(c);
|
||||
}
|
||||
|
||||
public ControllerDefinition Definition { get; }
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
ser.BeginSection(nameof(Port1));
|
||||
Port1.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.BeginSection(nameof(Port2));
|
||||
Port2.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.BeginSection(nameof(Port3));
|
||||
Port3.SyncState(ser);
|
||||
ser.EndSection();
|
||||
|
||||
ser.BeginSection(nameof(Port4));
|
||||
Port4.SyncState(ser);
|
||||
ser.EndSection();
|
||||
}
|
||||
|
||||
private readonly IPort Port1;
|
||||
private readonly IPort Port2;
|
||||
private readonly IPort Port3;
|
||||
private readonly IPort Port4;
|
||||
|
||||
private static Dictionary<string, Type> _controllerTypes;
|
||||
|
||||
public static Dictionary<string, Type> ValidControllerTypes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_controllerTypes == null)
|
||||
{
|
||||
_controllerTypes = typeof(GBHawkLink4xControllerDeck).Assembly
|
||||
.GetTypes()
|
||||
.Where(t => typeof(IPort).IsAssignableFrom(t))
|
||||
.Where(t => !t.IsAbstract && !t.IsInterface)
|
||||
.ToDictionary(tkey => tkey.DisplayName());
|
||||
}
|
||||
|
||||
return _controllerTypes;
|
||||
}
|
||||
}
|
||||
|
||||
public static string DefaultControllerName => typeof(StandardControls).DisplayName();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink4x
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a GB add on
|
||||
/// </summary>
|
||||
public interface IPort
|
||||
{
|
||||
byte Read(IController c);
|
||||
|
||||
ControllerDefinition Definition { get; }
|
||||
|
||||
void SyncState(Serializer ser);
|
||||
|
||||
int PortNum { get; }
|
||||
}
|
||||
|
||||
[DisplayName("Gameboy Controller")]
|
||||
public class StandardControls : IPort
|
||||
{
|
||||
public StandardControls(int portNum)
|
||||
{
|
||||
PortNum = portNum;
|
||||
Definition = new ControllerDefinition
|
||||
{
|
||||
Name = "Gameboy Controller H",
|
||||
BoolButtons = BaseDefinition
|
||||
.Select(b => "P" + PortNum + " " + b)
|
||||
.ToList()
|
||||
};
|
||||
}
|
||||
|
||||
public int PortNum { get; }
|
||||
|
||||
public ControllerDefinition Definition { get; }
|
||||
|
||||
public byte Read(IController c)
|
||||
{
|
||||
byte result = 0xFF;
|
||||
|
||||
if (c.IsPressed(Definition.BoolButtons[0]))
|
||||
{
|
||||
result -= 4;
|
||||
}
|
||||
if (c.IsPressed(Definition.BoolButtons[1]))
|
||||
{
|
||||
result -= 8;
|
||||
}
|
||||
if (c.IsPressed(Definition.BoolButtons[2]))
|
||||
{
|
||||
result -= 2;
|
||||
}
|
||||
if (c.IsPressed(Definition.BoolButtons[3]))
|
||||
{
|
||||
result -= 1;
|
||||
}
|
||||
if (c.IsPressed(Definition.BoolButtons[4]))
|
||||
{
|
||||
result -= 128;
|
||||
}
|
||||
if (c.IsPressed(Definition.BoolButtons[5]))
|
||||
{
|
||||
result -= 64;
|
||||
}
|
||||
if (c.IsPressed(Definition.BoolButtons[6]))
|
||||
{
|
||||
result -= 32;
|
||||
}
|
||||
if (c.IsPressed(Definition.BoolButtons[7]))
|
||||
{
|
||||
result -= 16;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static readonly string[] BaseDefinition =
|
||||
{
|
||||
"Up", "Down", "Left", "Right", "Start", "Select", "B", "A", "Power"
|
||||
};
|
||||
|
||||
public void SyncState(Serializer ser)
|
||||
{
|
||||
//nothing
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
TODO:
|
Loading…
Reference in New Issue