GGHawkLink: Initial Commit

- Make necessary access changes in SMS
- Implmement multi-disk bundler support
- Initial Video and sound support
This commit is contained in:
alyosha-tas 2019-02-09 09:55:45 -06:00
parent 4a73565ab2
commit 4f17934d6c
19 changed files with 1240 additions and 13 deletions

View File

@ -17,6 +17,7 @@ using BizHawk.Emulation.Cores.Nintendo.GBHawk;
using BizHawk.Emulation.Cores.Nintendo.GBHawkLink;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using BizHawk.Emulation.Cores.PCEngine;
using BizHawk.Emulation.Cores.Sega.GGHawkLink;
using BizHawk.Emulation.Cores.Sega.Saturn;
using BizHawk.Emulation.Cores.Sony.PSP;
using BizHawk.Emulation.Cores.Sony.PSX;
@ -805,6 +806,22 @@ namespace BizHawk.Client.Common
}
nextEmulator = new GPGX(nextComm, null, genDiscs, GetCoreSettings<GPGX>(), GetCoreSyncSettings<GPGX>());
break;
case "Game Gear":
var leftBytesGG = xmlGame.Assets.First().Value;
var rightBytesGG = xmlGame.Assets.Skip(1).First().Value;
var leftGG = Database.GetGameInfo(leftBytesGG, "left.gg");
var rightGG = Database.GetGameInfo(rightBytesGG, "right.gg");
nextEmulator = new GGHawkLink(
nextComm,
leftGG,
leftBytesGG,
rightGG,
rightBytesGG,
GetCoreSettings<GGHawkLink>(),
GetCoreSyncSettings<GGHawkLink>());
break;
default:
return false;
}

View File

@ -145,7 +145,8 @@
"PSX",
"SAT",
"ZXSpectrum",
"AmstradCPC"});
"AmstradCPC",
"Game Gear"});
this.SystemDropDown.Location = new System.Drawing.Point(425, 75);
this.SystemDropDown.Name = "SystemDropDown";
this.SystemDropDown.Size = new System.Drawing.Size(69, 21);

View File

@ -1336,6 +1336,31 @@
</Compile>
<Compile Include="Consoles\PC Engine\PCEngine.TurboCD.cs" />
<Compile Include="Consoles\PC Engine\ScsiCDBus.cs" />
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.cs" />
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.ICodeDataLog.cs" />
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.IDebuggable.cs">
<DependentUpon>GGHawkLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.IEmulator.cs">
<DependentUpon>GGHawkLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.IInputPollable.cs">
<DependentUpon>GGHawkLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.IMemoryDomains.cs">
<DependentUpon>GGHawkLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.ISaveRam.cs">
<DependentUpon>GGHawkLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.ISettable.cs">
<DependentUpon>GGHawkLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLink.IStatable.cs">
<DependentUpon>GGHawkLink.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLinkControllerDeck.cs" />
<Compile Include="Consoles\Sega\GGHawkLink\GGHawkLinkControllers.cs" />
<Compile Include="Consoles\Sega\gpgx64\GPGX.ISoundProvider.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>

View File

@ -39,7 +39,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawkLink
linkSettings = (GBLinkSettings)settings ?? new GBLinkSettings();
linkSyncSettings = (GBLinkSyncSettings)syncSettings ?? new GBLinkSyncSettings();
_controllerDeck = new GBHawkLinkControllerDeck(GBHawkControllerDeck.DefaultControllerName, GBHawkControllerDeck.DefaultControllerName);
_controllerDeck = new GBHawkLinkControllerDeck(GBHawkLinkControllerDeck.DefaultControllerName, GBHawkLinkControllerDeck.DefaultControllerName);
CoreComm = comm;

View File

@ -0,0 +1,115 @@
using System;
using System.IO;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80A;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public sealed partial class GGHawkLink : ICodeDataLogger
{
public void SetCDL(ICodeDataLog cdl)
{
CDL = cdl;
if (cdl == null)
{
L.Cpu.ReadMemory = L.ReadMemory;
L.Cpu.WriteMemory = L.WriteMemory;
L.Cpu.FetchMemory = L.FetchMemory;
}
else
{
L.Cpu.ReadMemory = ReadMemory_CDL;
L.Cpu.WriteMemory = L.WriteMemory;
L.Cpu.FetchMemory = FetchMemory_CDL;
}
}
public void NewCDL(ICodeDataLog cdl)
{
cdl["ROM"] = new byte[MemoryDomains["ROM"].Size];
cdl["Main RAM"] = new byte[MemoryDomains["Main RAM"].Size];
if (MemoryDomains.Has("Save RAM"))
{
cdl["Save RAM"] = new byte[MemoryDomains["Save RAM"].Size];
}
if (MemoryDomains.Has("Cart (Volatile) RAM"))
{
cdl["Cart (Volatile) RAM"] = new byte[MemoryDomains["Cart (Volatile) RAM"].Size];
}
cdl.SubType = "SMS";
cdl.SubVer = 0;
}
[FeatureNotImplemented]
public void DisassembleCDL(Stream s, ICodeDataLog cdl)
{
}
private enum CDLog_AddrType
{
None,
ROM,
MainRAM,
SaveRAM,
CartRAM, //"Cart (Volatile) RAM" aka ExtRam
}
[Flags]
private enum CDLog_Flags
{
ExecFirst = 0x01,
ExecOperand = 0x02,
Data = 0x04
};
private struct CDLog_MapResults
{
public CDLog_AddrType Type;
public int Address;
}
private delegate CDLog_MapResults MapMemoryDelegate(ushort addr, bool write);
private MapMemoryDelegate MapMemory;
private ICodeDataLog CDL;
private void RunCDL(ushort address, CDLog_Flags flags)
{
if (MapMemory != null)
{
CDLog_MapResults results = MapMemory(address, false);
switch (results.Type)
{
case CDLog_AddrType.None: break;
case CDLog_AddrType.ROM: CDL["ROM"][results.Address] |= (byte)flags; break;
case CDLog_AddrType.MainRAM: CDL["Main RAM"][results.Address] |= (byte)flags; break;
case CDLog_AddrType.SaveRAM: CDL["Save RAM"][results.Address] |= (byte)flags; break;
case CDLog_AddrType.CartRAM: CDL["Cart (Volatile) RAM"][results.Address] |= (byte)flags; break;
}
}
}
/// <summary>
/// A wrapper for FetchMemory which inserts CDL logic
/// </summary>
private byte FetchMemory_CDL(ushort address)
{
RunCDL(address, CDLog_Flags.ExecFirst);
return L.ReadMemory(address);
}
/// <summary>
/// A wrapper for ReadMemory which inserts CDL logic
/// </summary>
private byte ReadMemory_CDL(ushort address)
{
RunCDL(address, CDLog_Flags.Data);
return L.ReadMemory(address);
}
}
}

View File

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public partial class GGHawkLink : IDebuggable
{
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
{
return new Dictionary<string, RegisterValue>
{
["A"] = L.Cpu.Regs[L.Cpu.A],
["AF"] = L.Cpu.Regs[L.Cpu.F] + (L.Cpu.Regs[L.Cpu.A] << 8),
["B"] = L.Cpu.Regs[L.Cpu.B],
["BC"] = L.Cpu.Regs[L.Cpu.C] + (L.Cpu.Regs[L.Cpu.B] << 8),
["C"] = L.Cpu.Regs[L.Cpu.C],
["D"] = L.Cpu.Regs[L.Cpu.D],
["DE"] = L.Cpu.Regs[L.Cpu.E] + (L.Cpu.Regs[L.Cpu.D] << 8),
["E"] = L.Cpu.Regs[L.Cpu.E],
["F"] = L.Cpu.Regs[L.Cpu.F],
["H"] = L.Cpu.Regs[L.Cpu.H],
["HL"] = L.Cpu.Regs[L.Cpu.L] + (L.Cpu.Regs[L.Cpu.H] << 8),
["I"] = L.Cpu.Regs[L.Cpu.I],
["IX"] = L.Cpu.Regs[L.Cpu.Ixl] + (L.Cpu.Regs[L.Cpu.Ixh] << 8),
["IY"] = L.Cpu.Regs[L.Cpu.Iyl] + (L.Cpu.Regs[L.Cpu.Iyh] << 8),
["L"] = L.Cpu.Regs[L.Cpu.L],
["PC"] = L.Cpu.Regs[L.Cpu.PCl] + (L.Cpu.Regs[L.Cpu.PCh] << 8),
["R"] = L.Cpu.Regs[L.Cpu.R],
["Shadow AF"] = L.Cpu.Regs[L.Cpu.F_s] + (L.Cpu.Regs[L.Cpu.A_s] << 8),
["Shadow BC"] = L.Cpu.Regs[L.Cpu.C_s] + (L.Cpu.Regs[L.Cpu.B_s] << 8),
["Shadow DE"] = L.Cpu.Regs[L.Cpu.E_s] + (L.Cpu.Regs[L.Cpu.D_s] << 8),
["Shadow HL"] = L.Cpu.Regs[L.Cpu.L_s] + (L.Cpu.Regs[L.Cpu.H_s] << 8),
["SP"] = L.Cpu.Regs[L.Cpu.SPl] + (L.Cpu.Regs[L.Cpu.SPh] << 8),
["Flag C"] = L.Cpu.FlagC,
["Flag N"] = L.Cpu.FlagN,
["Flag P/V"] = L.Cpu.FlagP,
["Flag 3rd"] = L.Cpu.Flag3,
["Flag H"] = L.Cpu.FlagH,
["Flag 5th"] = L.Cpu.Flag5,
["Flag Z"] = L.Cpu.FlagZ,
["Flag S"] = L.Cpu.FlagS
};
}
public void SetCpuRegister(string register, int value)
{
switch (register)
{
default:
throw new InvalidOperationException();
case "A":
L.Cpu.Regs[L.Cpu.A] = (ushort)value;
break;
case "AF":
L.Cpu.Regs[L.Cpu.F] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.A] = (ushort)(value & 0xFF00);
break;
case "B":
L.Cpu.Regs[L.Cpu.B] = (ushort)value;
break;
case "BC":
L.Cpu.Regs[L.Cpu.C] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.B] = (ushort)(value & 0xFF00);
break;
case "C":
L.Cpu.Regs[L.Cpu.C] = (ushort)value;
break;
case "D":
L.Cpu.Regs[L.Cpu.D] = (ushort)value;
break;
case "DE":
L.Cpu.Regs[L.Cpu.E] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.D] = (ushort)(value & 0xFF00);
break;
case "E":
L.Cpu.Regs[L.Cpu.E] = (ushort)value;
break;
case "F":
L.Cpu.Regs[L.Cpu.F] = (ushort)value;
break;
case "H":
L.Cpu.Regs[L.Cpu.H] = (ushort)value;
break;
case "HL":
L.Cpu.Regs[L.Cpu.L] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.H] = (ushort)(value & 0xFF00);
break;
case "I":
L.Cpu.Regs[L.Cpu.I] = (ushort)value;
break;
case "IX":
L.Cpu.Regs[L.Cpu.Ixl] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.Ixh] = (ushort)(value & 0xFF00);
break;
case "IY":
L.Cpu.Regs[L.Cpu.Iyl] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.Iyh] = (ushort)(value & 0xFF00);
break;
case "L":
L.Cpu.Regs[L.Cpu.L] = (ushort)value;
break;
case "PC":
L.Cpu.Regs[L.Cpu.PCl] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.PCh] = (ushort)(value & 0xFF00);
break;
case "R":
L.Cpu.Regs[L.Cpu.R] = (ushort)value;
break;
case "Shadow AF":
L.Cpu.Regs[L.Cpu.F_s] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.A_s] = (ushort)(value & 0xFF00);
break;
case "Shadow BC":
L.Cpu.Regs[L.Cpu.C_s] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.B_s] = (ushort)(value & 0xFF00);
break;
case "Shadow DE":
L.Cpu.Regs[L.Cpu.E_s] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.D_s] = (ushort)(value & 0xFF00);
break;
case "Shadow HL":
L.Cpu.Regs[L.Cpu.L_s] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.H_s] = (ushort)(value & 0xFF00);
break;
case "SP":
L.Cpu.Regs[L.Cpu.SPl] = (ushort)(value & 0xFF);
L.Cpu.Regs[L.Cpu.SPh] = (ushort)(value & 0xFF00);
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)L.Cpu.TotalExecutedCycles; }
}
}
}

View File

@ -0,0 +1,275 @@
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Common;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public partial class GGHawkLink : IEmulator, IVideoProvider, ISoundProvider
{
public IEmulatorServiceProvider ServiceProvider { get; }
public ControllerDefinition ControllerDefinition => _controllerDeck.Definition;
public bool FrameAdvance(IController controller, bool render, bool rendersound)
{
//Console.WriteLine("-----------------------FRAME-----------------------");
if (_tracer.Enabled)
{
L.Cpu.TraceCallback = s => _tracer.Put(s);
}
else
{
L.Cpu.TraceCallback = null;
}
_frame++;
if (controller.IsPressed("Power"))
{
HardReset();
}
bool cablediscosignalNew = controller.IsPressed("Toggle Cable");
if (cablediscosignalNew && !_cablediscosignal)
{
_cableconnected ^= true;
Console.WriteLine("Cable connect status to {0}", _cableconnected);
LinkConnected = _cableconnected;
}
_cablediscosignal = cablediscosignalNew;
_islag = true;
GetControllerState(controller);
do_frame(controller, render, rendersound);
_islag = L._isLag;
if (_islag)
{
_lagcount++;
}
return true;
}
public void do_frame(IController controller, bool render, bool rendersound)
{
/*
L.do_controller_check();
R.do_controller_check();
// advance one full frame
for (int i = 0; i < 70224; i++)
{
L.do_single_step();
R.do_single_step();
// the signal to shift out a bit is when serial_clock = 1
if (((L.serialport.serial_clock == 1) || (L.serialport.serial_clock == 2)) && !do_r_next)
{
if (LinkConnected)
{
L.serialport.send_external_bit((byte)(L.serialport.serial_data & 0x80));
if ((R.serialport.clk_rate == -1) && R.serialport.serial_start)
{
R.serialport.serial_clock = L.serialport.serial_clock;
R.serialport.send_external_bit((byte)(R.serialport.serial_data & 0x80));
R.serialport.coming_in = L.serialport.going_out;
}
L.serialport.coming_in = R.serialport.going_out;
}
}
else if ((R.serialport.serial_clock == 1) || (R.serialport.serial_clock == 2))
{
do_r_next = false;
if (LinkConnected)
{
R.serialport.send_external_bit((byte)(R.serialport.serial_data & 0x80));
if ((L.serialport.clk_rate == -1) && L.serialport.serial_start)
{
L.serialport.serial_clock = R.serialport.serial_clock;
L.serialport.send_external_bit((byte)(L.serialport.serial_data & 0x80));
L.serialport.coming_in = R.serialport.going_out;
}
R.serialport.coming_in = L.serialport.going_out;
}
if (R.serialport.serial_clock == 2) { do_r_next = true; }
}
else
{
do_r_next = false;
}
// if we hit a frame boundary, update video
if (L.vblank_rise)
{
buff_L = L.GetVideoBuffer();
L.vblank_rise = false;
FillVideoBuffer();
}
if (R.vblank_rise)
{
buff_R = R.GetVideoBuffer();
R.vblank_rise = false;
FillVideoBuffer();
}
}
*/
L.FrameAdvance(controller, render, rendersound);
R.FrameAdvance(controller, render, rendersound);
buff_L = L.Vdp.GetVideoBuffer();
buff_R = R.Vdp.GetVideoBuffer();
FillVideoBuffer();
}
public void GetControllerState(IController controller)
{
InputCallbacks.Call();
//L.controller_state = _controllerDeck.ReadPort1(controller);
//R.controller_state = _controllerDeck.ReadPort2(controller);
}
public int Frame => _frame;
public string SystemId => "DGB";
public bool DeterministicEmulation { get; set; }
public void ResetCounters()
{
_frame = 0;
_lagcount = 0;
_islag = false;
}
public CoreComm CoreComm { get; }
public void Dispose()
{
L.Dispose();
R.Dispose();
}
#region Video provider
public int _frameHz = 60;
public int[] _vidbuffer = new int[160 * 2 * 144];
public int[] buff_L = new int[160 * 144];
public int[] buff_R = new int[160 * 144];
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] = buff_L[i * 160 + j];
_vidbuffer[i * 320 + j + 160] = buff_R[i * 160 + j];
}
}
}
public int VirtualWidth => 160 * 2;
public int VirtualHeight => 144;
public int BufferWidth => 160 * 2;
public int BufferHeight => 144;
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_L = new short[735 * 2];
short[] temp_samp_R = new short[735 * 2];
int nsamp_L = 735;
int nsamp_R = 735;
L.PSG.GetSamples(temp_samp_L);
R.PSG.GetSamples(temp_samp_R);
if (linkSettings.AudioSet == GGLinkSettings.AudioSrc.Left)
{
samples = temp_samp_L;
nsamp = nsamp_L;
}
else if (linkSettings.AudioSet == GGLinkSettings.AudioSrc.Right)
{
samples = temp_samp_R;
nsamp = nsamp_R;
}
else
{
samples = new short[0];
nsamp = 0;
}
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
L.PSG.DiscardSamples();
R.PSG.DiscardSamples();
}
private void GetSamples(short[] samples)
{
}
public void DisposeSound()
{
}
#endregion
}
}

View File

@ -0,0 +1,24 @@
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public partial class GGHawkLink : 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;
}
}

View File

@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public partial class GGHawkLink
{
private IMemoryDomains MemoryDomains;
public void SetupMemoryDomains()
{
var domains = new List<MemoryDomain>
{
new MemoryDomainDelegate(
"Main RAM L",
L.SystemRam.Length,
MemoryDomain.Endian.Little,
addr => L.SystemRam[addr],
(addr, value) => L.SystemRam[addr] = value,
1),
new MemoryDomainDelegate(
"Main RAM R",
R.SystemRam.Length,
MemoryDomain.Endian.Little,
addr => R.SystemRam[addr],
(addr, value) => R.SystemRam[addr] = value,
1),
new MemoryDomainDelegate(
"System Bus L",
0X10000,
MemoryDomain.Endian.Little,
addr => PeekSystemBusL(addr),
(addr, value) => PokeSystemBusL(addr, value),
1),
new MemoryDomainDelegate(
"System Bus R",
0X10000,
MemoryDomain.Endian.Little,
addr => PeekSystemBusR(addr),
(addr, value) => PokeSystemBusR(addr, value),
1),
new MemoryDomainDelegate(
"ROM L",
L.RomData.Length,
MemoryDomain.Endian.Little,
addr => L.RomData[addr],
(addr, value) => L.RomData[addr] = value,
1),
new MemoryDomainDelegate(
"ROM R",
R.RomData.Length,
MemoryDomain.Endian.Little,
addr => R.RomData[addr],
(addr, value) => R.RomData[addr] = value,
1),
new MemoryDomainDelegate(
"VRAM L",
L.Vdp.VRAM.Length,
MemoryDomain.Endian.Little,
addr => L.Vdp.VRAM[addr],
(addr, value) => L.Vdp.VRAM[addr] = value,
1),
new MemoryDomainDelegate(
"VRAM R",
R.Vdp.VRAM.Length,
MemoryDomain.Endian.Little,
addr => R.Vdp.VRAM[addr],
(addr, value) => R.Vdp.VRAM[addr] = value,
1)
};
if (L.SaveRAM != null)
{
var CartRamL = new MemoryDomainByteArray("Cart RAM L", MemoryDomain.Endian.Little, L.SaveRAM, true, 1);
domains.Add(CartRamL);
}
if (R.SaveRAM != null)
{
var CartRamR = new MemoryDomainByteArray("Cart RAM R", MemoryDomain.Endian.Little, R.SaveRAM, true, 1);
domains.Add(CartRamR);
}
MemoryDomains = new MemoryDomainList(domains);
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
}
private byte PeekSystemBusL(long addr)
{
ushort addr2 = (ushort)(addr & 0xFFFF);
return L.ReadMemory(addr2);
}
private byte PeekSystemBusR(long addr)
{
ushort addr2 = (ushort)(addr & 0xFFFF);
return R.ReadMemory(addr2);
}
private void PokeSystemBusL(long addr, byte value)
{
ushort addr2 = (ushort)(addr & 0xFFFF);
L.WriteMemory(addr2, value);
}
private void PokeSystemBusR(long addr, byte value)
{
ushort addr2 = (ushort)(addr & 0xFFFF);
R.WriteMemory(addr2, value);
}
}
}

View File

@ -0,0 +1,81 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public partial class GGHawkLink : ISaveRam
{
public byte[] CloneSaveRam()
{
if ((L.SaveRAM != null) || (R.SaveRAM != null))
{
int Len1 = 0;
int Len2 = 0;
int index = 0;
if (L.SaveRAM != null)
{
Len1 = L.SaveRAM.Length;
}
if (R.SaveRAM != null)
{
Len2 = R.SaveRAM.Length;
}
byte[] temp = new byte[Len1 + Len2];
if (L.SaveRAM != null)
{
for (int i = 0; i < L.SaveRAM.Length; i++)
{
temp[index] = L.SaveRAM[i];
index++;
}
}
if (R.SaveRAM != null)
{
for (int i = 0; i < L.SaveRAM.Length; i++)
{
temp[index] = R.SaveRAM[i];
index++;
}
}
return temp;
}
else
{
return null;
}
}
public void StoreSaveRam(byte[] data)
{
if ((L.SaveRAM != null) && (R.SaveRAM == null))
{
Buffer.BlockCopy(data, 0, L.SaveRAM, 0, L.SaveRAM.Length);
}
else if ((R.SaveRAM != null) && (L.SaveRAM == null))
{
Buffer.BlockCopy(data, 0, R.SaveRAM, 0, R.SaveRAM.Length);
}
else if ((R.SaveRAM != null) && (L.SaveRAM != null))
{
Buffer.BlockCopy(data, 0, L.SaveRAM, 0, L.SaveRAM.Length);
Buffer.BlockCopy(data, L.SaveRAM.Length, R.SaveRAM, 0, R.SaveRAM.Length);
}
Console.WriteLine("loading SRAM here");
}
public bool SaveRamModified
{
get
{
return linkSyncSettings.Use_SRAM;
}
}
}
}

View File

@ -0,0 +1,78 @@
using System;
using System.ComponentModel;
using Newtonsoft.Json;
using BizHawk.Common;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public partial class GGHawkLink : IEmulator, IStatable, ISettable<GGHawkLink.GGLinkSettings, GGHawkLink.GGLinkSyncSettings>
{
public GGLinkSettings GetSettings()
{
return linkSettings.Clone();
}
public GGLinkSyncSettings GetSyncSettings()
{
return linkSyncSettings.Clone();
}
public bool PutSettings(GGLinkSettings o)
{
linkSettings = o;
return false;
}
public bool PutSyncSettings(GGLinkSyncSettings o)
{
bool ret = GGLinkSyncSettings.NeedsReboot(linkSyncSettings, o);
linkSyncSettings = o;
return ret;
}
private GGLinkSettings linkSettings = new GGLinkSettings();
public GGLinkSyncSettings linkSyncSettings = new GGLinkSyncSettings();
public class GGLinkSettings
{
public enum AudioSrc
{
Left,
Right,
Both
}
[DisplayName("Audio Selection")]
[Description("Choose Audio Source. Both will produce Stereo sound.")]
[DefaultValue(AudioSrc.Left)]
public AudioSrc AudioSet { get; set; }
public GGLinkSettings Clone()
{
return (GGLinkSettings)MemberwiseClone();
}
}
public class GGLinkSyncSettings
{
[DisplayName("Use Existing SaveRAM")]
[Description("When true, existing SaveRAM will be loaded at boot up")]
[DefaultValue(false)]
public bool Use_SRAM { get; set; }
public GGLinkSyncSettings Clone()
{
return (GGLinkSyncSettings)MemberwiseClone();
}
public static bool NeedsReboot(GGLinkSyncSettings x, GGLinkSyncSettings y)
{
return !DeepEquality.DeepEquals(x, y);
}
}
}
}

View File

@ -0,0 +1,68 @@
using System.IO;
using Newtonsoft.Json;
using BizHawk.Common;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public partial class GGHawkLink : IStatable
{
public bool BinarySaveStatesPreferred => true;
public void SaveStateText(TextWriter writer)
{
L.SaveStateText(writer);
R.SaveStateText(writer);
SyncState(new Serializer(writer));
}
public void LoadStateText(TextReader reader)
{
L.LoadStateText(reader);
R.LoadStateText(reader);
SyncState(new Serializer(reader));
}
public void SaveStateBinary(BinaryWriter bw)
{
L.SaveStateBinary(bw);
R.SaveStateBinary(bw);
// other variables
SyncState(new Serializer(bw));
}
public void LoadStateBinary(BinaryReader br)
{
L.LoadStateBinary(br);
R.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("_cableconnected", ref _cableconnected);
ser.Sync("_cablediscosignal", ref _cablediscosignal);
ser.Sync("do_r_next", ref do_r_next);
_controllerDeck.SyncState(ser);
LinkConnected = _cableconnected;
}
}
}

View File

@ -0,0 +1,91 @@
using System;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80A;
using BizHawk.Emulation.Cores.Sega.MasterSystem;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
[Core(
"GGHawkLink",
"",
isPorted: false,
isReleased: false)]
[ServiceNotApplicable(typeof(IDriveLight))]
public partial class GGHawkLink : IEmulator, ISaveRam, IDebuggable, IStatable, IInputPollable, IRegionable, ILinkable,
ISettable<GGHawkLink.GGLinkSettings, GGHawkLink.GGLinkSyncSettings>
{
// we want to create two GG instances that we will run concurrently
public SMS L;
public SMS R;
// if true, the link cable is currently connected
private bool _cableconnected = true;
// if true, the link cable toggle signal is currently asserted
private bool _cablediscosignal = false;
private bool do_r_next = false;
public GGHawkLink(CoreComm comm, GameInfo game_L, byte[] rom_L, GameInfo game_R, byte[] rom_R, /*string gameDbFn,*/ object settings, object syncSettings)
{
var ser = new BasicServiceProvider(this);
linkSettings = (GGLinkSettings)settings ?? new GGLinkSettings();
linkSyncSettings = (GGLinkSyncSettings)syncSettings ?? new GGLinkSyncSettings();
_controllerDeck = new GGHawkLinkControllerDeck(GGHawkLinkControllerDeck.DefaultControllerName, GGHawkLinkControllerDeck.DefaultControllerName);
CoreComm = comm;
var temp_set_L = new SMS.SMSSettings();
var temp_set_R = new SMS.SMSSettings();
var temp_sync_L = new SMS.SMSSyncSettings();
var temp_sync_R = new SMS.SMSSyncSettings();
L = new SMS(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider },
game_L, rom_L, temp_set_L, temp_sync_L);
R = new SMS(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider },
game_R, rom_R, temp_set_R, temp_sync_R);
ser.Register<IVideoProvider>(this);
ser.Register<ISoundProvider>(this);
_tracer = new TraceBuffer { Header = L.Cpu.TraceHeader };
ser.Register<ITraceable>(_tracer);
ServiceProvider = ser;
SetupMemoryDomains();
HardReset();
LinkConnected = _cableconnected;
}
public void HardReset()
{
L.HardReset();
R.HardReset();
}
public DisplayType Region => DisplayType.NTSC;
public int _frame = 0;
private readonly GGHawkLinkControllerDeck _controllerDeck;
private readonly ITraceable _tracer;
public bool LinkConnected { get; private set; }
private void ExecFetch(ushort addr)
{
MemoryCallbacks.CallExecutes(addr, "System Bus");
}
}
}

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Common;
using BizHawk.Common.ReflectionExtensions;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.GGHawkLink
{
public class GGHawkLinkControllerDeck
{
public GGHawkLinkControllerDeck(string controller1Name, string controller2Name)
{
if (!ValidControllerTypes.ContainsKey(controller1Name))
{
throw new InvalidOperationException("Invalid controller type: " + controller1Name);
}
if (!ValidControllerTypes.ContainsKey(controller2Name))
{
throw new InvalidOperationException("Invalid controller type: " + controller2Name);
}
Port1 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller1Name], 1);
Port2 = (IPort)Activator.CreateInstance(ValidControllerTypes[controller2Name], 2);
Definition = new ControllerDefinition
{
Name = Port1.Definition.Name,
BoolButtons = Port1.Definition.BoolButtons
.Concat(Port2.Definition.BoolButtons)
.Concat(new[] { "Toggle Cable" } )
.ToList()
};
}
public byte ReadPort1(IController c)
{
return Port1.Read(c);
}
public byte ReadPort2(IController c)
{
return Port2.Read(c);
}
public ControllerDefinition Definition { get; }
public void SyncState(Serializer ser)
{
ser.BeginSection("Port1");
Port1.SyncState(ser);
ser.EndSection();
ser.BeginSection("Port2");
Port2.SyncState(ser);
ser.EndSection();
}
private readonly IPort Port1;
private readonly IPort Port2;
private static Dictionary<string, Type> _controllerTypes;
public static Dictionary<string, Type> ValidControllerTypes
{
get
{
if (_controllerTypes == null)
{
_controllerTypes = typeof(GGHawkLinkControllerDeck).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();
}
}

View File

@ -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.Sega.GGHawkLink
{
/// <summary>
/// Represents a GG add on
/// </summary>
public interface IPort
{
byte Read(IController c);
ControllerDefinition Definition { get; }
void SyncState(Serializer ser);
int PortNum { get; }
}
[DisplayName("Game Gear Controller")]
public class StandardControls : IPort
{
public StandardControls(int portNum)
{
PortNum = portNum;
Definition = new ControllerDefinition
{
Name = "GG Controller",
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", "B1", "B2", "Start"
};
public void SyncState(Serializer ser)
{
//nothing
}
}
}

View File

@ -0,0 +1 @@
TODO:

View File

@ -18,8 +18,8 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
public IInputCallbackSystem InputCallbacks { get; private set; }
private int _lagCount = 0;
private bool _lagged = true;
private bool _isLag = false;
public int _lagCount = 0;
public bool _lagged = true;
public bool _isLag = false;
}
}

View File

@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
public bool SaveRamModified { get; private set; }
private byte[] SaveRAM;
public byte[] SaveRAM;
private byte SaveRamBank;
}
}

View File

@ -213,21 +213,26 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
Cpu.Regs[Cpu.SPh] = 0xDF;
}
public void HardReset()
{
}
// Constants
private const int BankSize = 16384;
// ROM
private byte[] RomData;
public byte[] RomData;
private byte RomBank0, RomBank1, RomBank2, RomBank3;
private byte Bios_bank;
private byte RomBanks;
private byte[] BiosRom;
// Machine resources
private Z80A Cpu;
private byte[] SystemRam;
public Z80A Cpu;
public byte[] SystemRam;
public VDP Vdp;
private SN76489 PSG;
public SN76489 PSG;
private YM2413 YM2413;
public bool IsGameGear { get; set; }
public bool IsGameGear_C { get; set; }
@ -281,21 +286,21 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
return DisplayType.NTSC;
}
private byte ReadMemory(ushort addr)
public byte ReadMemory(ushort addr)
{
MemoryCallbacks.CallReads(addr, "System Bus");
return ReadMemoryMapper(addr);
}
private void WriteMemory(ushort addr, byte value)
public void WriteMemory(ushort addr, byte value)
{
WriteMemoryMapper(addr, value);
MemoryCallbacks.CallWrites(addr, "System Bus");
}
private byte FetchMemory(ushort addr)
public byte FetchMemory(ushort addr)
{
return ReadMemoryMapper(addr);
}