gpgx32: flipped namespaces, added excplicit core picking
diff is messed up after folder renaming, but no file was changed except for namespaces and waterbox's core name
This commit is contained in:
parent
7b26a6b573
commit
35282ba577
|
@ -802,6 +802,10 @@ namespace BizHawk.Client.Common
|
|||
nextEmulator = new Octoshock(nextComm, null, null, rom.FileData, GetCoreSettings<Octoshock>(), GetCoreSyncSettings<Octoshock>());
|
||||
nextEmulator.CoreComm.RomStatusDetails = "PSX etc.";
|
||||
break;
|
||||
case "GEN":
|
||||
// discard "Genplus-gx64", auto-added due to implementing IEmulator
|
||||
core = CoreInventory.Instance["GEN", "Genplus-gx"];
|
||||
break;
|
||||
}
|
||||
|
||||
if (core != null)
|
||||
|
|
|
@ -838,46 +838,6 @@
|
|||
<Compile Include="Consoles\Sega\Genesis\Genesis.Input.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\Genesis.IO.cs" />
|
||||
<Compile Include="Consoles\Sega\Genesis\Native68000\Musashi.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx32\GenDbgHlp.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.ICodeDataLogger.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IDebuggable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IDisassembler.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IDriveLight.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IEmulator.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IInputPollable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IMemoryDomains.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.ISaveRam.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.ISettable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IStatable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.ITraceable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGX.IVideoProvider.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx32\GPGXControlConverter.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx32\LibGPGX.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx\GenDbgHlp.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx\GPGX.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx\GPGX.ICodeDataLogger.cs">
|
||||
|
@ -918,6 +878,46 @@
|
|||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx\GPGXControlConverter.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx\LibGPGX.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx64\GenDbgHlp.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.ICodeDataLogger.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IDebuggable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IDisassembler.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IDriveLight.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IEmulator.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IInputPollable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IMemoryDomains.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.ISaveRam.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.ISettable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IStatable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.ITraceable.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGX.IVideoProvider.cs">
|
||||
<DependentUpon>GPGX.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Consoles\Sega\gpgx64\GPGXControlConverter.cs" />
|
||||
<Compile Include="Consoles\Sega\gpgx64\LibGPGX.cs" />
|
||||
<Compile Include="Consoles\Sega\Saturn\FilePiping.cs" />
|
||||
<Compile Include="Consoles\Sega\Saturn\LibYabause.cs" />
|
||||
<Compile Include="Consoles\Sega\Saturn\Yabause.cs" />
|
||||
|
|
|
@ -10,8 +10,8 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
public void SetCDL(CodeDataLog cdl)
|
||||
{
|
||||
CDL = cdl;
|
||||
if (cdl == null) Core.gpgx_set_cd_callback(null);
|
||||
else Core.gpgx_set_cd_callback(CDCallback);
|
||||
if (cdl == null) LibGPGX.gpgx_set_cd_callback(null);
|
||||
else LibGPGX.gpgx_set_cd_callback(CDCallback);
|
||||
}
|
||||
|
||||
public void NewCDL(CodeDataLog cdl)
|
||||
|
|
|
@ -10,9 +10,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
{
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[Core.gpgx_getmaxnumregs()];
|
||||
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[LibGPGX.gpgx_getmaxnumregs()];
|
||||
|
||||
int n = Core.gpgx_getregs(regs);
|
||||
int n = LibGPGX.gpgx_getregs(regs);
|
||||
if (n > regs.Length)
|
||||
throw new InvalidOperationException("A buffer overrun has occured!");
|
||||
var ret = new Dictionary<string, RegisterValue>();
|
||||
|
@ -63,7 +63,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
|
||||
private void RefreshMemCallbacks()
|
||||
{
|
||||
Core.gpgx_set_mem_callback(
|
||||
LibGPGX.gpgx_set_mem_callback(
|
||||
MemoryCallbacks.HasReads ? ReadCallback : null,
|
||||
MemoryCallbacks.HasWrites ? WriteCallback : null,
|
||||
MemoryCallbacks.HasExecutes ? ExecCallback : null);
|
||||
|
@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
|
||||
private void KillMemCallbacks()
|
||||
{
|
||||
Core.gpgx_set_mem_callback(null, null, null);
|
||||
LibGPGX.gpgx_set_mem_callback(null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,26 +29,26 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
public void FrameAdvance(bool render, bool rendersound = true)
|
||||
{
|
||||
if (Controller["Reset"])
|
||||
Core.gpgx_reset(false);
|
||||
LibGPGX.gpgx_reset(false);
|
||||
if (Controller["Power"])
|
||||
Core.gpgx_reset(true);
|
||||
LibGPGX.gpgx_reset(true);
|
||||
|
||||
// this shouldn't be needed, as nothing has changed
|
||||
// if (!Core.gpgx_get_control(input, inputsize))
|
||||
// throw new Exception("gpgx_get_control() failed!");
|
||||
// do we really have to get each time? nothing has changed
|
||||
if (!LibGPGX.gpgx_get_control(input, inputsize))
|
||||
throw new Exception("gpgx_get_control() failed!");
|
||||
|
||||
ControlConverter.ScreenWidth = vwidth;
|
||||
ControlConverter.ScreenHeight = vheight;
|
||||
ControlConverter.Convert(Controller, input);
|
||||
|
||||
if (!Core.gpgx_put_control(input, inputsize))
|
||||
if (!LibGPGX.gpgx_put_control(input, inputsize))
|
||||
throw new Exception("gpgx_put_control() failed!");
|
||||
|
||||
IsLagFrame = true;
|
||||
Frame++;
|
||||
_drivelight = false;
|
||||
|
||||
Core.gpgx_advance();
|
||||
LibGPGX.gpgx_advance();
|
||||
UpdateVideo();
|
||||
update_audio();
|
||||
|
||||
|
@ -89,10 +89,16 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (Elf != null)
|
||||
Elf.Dispose();
|
||||
if (AttachedCore != this)
|
||||
throw new Exception();
|
||||
if (SaveRamModified)
|
||||
_disposedSaveRam = CloneSaveRam();
|
||||
KillMemCallbacks();
|
||||
if (CD != null)
|
||||
{
|
||||
CD.Dispose();
|
||||
}
|
||||
AttachedCore = null;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
{
|
||||
IntPtr area = IntPtr.Zero;
|
||||
int size = 0;
|
||||
IntPtr pname = Core.gpgx_get_memdom(i, ref area, ref size);
|
||||
IntPtr pname = LibGPGX.gpgx_get_memdom(i, ref area, ref size);
|
||||
if (area == IntPtr.Zero || pname == IntPtr.Zero || size == 0)
|
||||
continue;
|
||||
string name = Marshal.PtrToStringAnsi(pname);
|
||||
|
@ -26,17 +26,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
// vram pokes need to go through hook which invalidates cached tiles
|
||||
byte* p = (byte*)area;
|
||||
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Unknown,
|
||||
delegate(long addr)
|
||||
delegate (long addr)
|
||||
{
|
||||
if (addr < 0 || addr >= 65536)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return p[addr ^ 1];
|
||||
},
|
||||
delegate(long addr, byte val)
|
||||
delegate (long addr, byte val)
|
||||
{
|
||||
if (addr < 0 || addr >= 65536)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
Core.gpgx_poke_vram(((int)addr) ^ 1, val);
|
||||
LibGPGX.gpgx_poke_vram(((int)addr) ^ 1, val);
|
||||
},
|
||||
wordSize: 2));
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
else
|
||||
{
|
||||
// TODO: are the Z80 domains really Swap16 in the core? Check this
|
||||
//var byteSize = name.Contains("Z80") ? 1 : 2;
|
||||
mm.Add(MemoryDomain.FromIntPtrSwap16(name, size,
|
||||
MemoryDomain.Endian.Big, area, name != "MD CART" && name != "CD BOOT ROM"));
|
||||
}
|
||||
|
@ -54,14 +55,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return Core.gpgx_peek_m68k_bus(a);
|
||||
return LibGPGX.gpgx_peek_m68k_bus(a);
|
||||
},
|
||||
delegate (long addr, byte val)
|
||||
{
|
||||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
Core.gpgx_write_m68k_bus(a, val);
|
||||
LibGPGX.gpgx_write_m68k_bus(a, val);
|
||||
}, 2);
|
||||
|
||||
mm.Add(m68Bus);
|
||||
|
@ -72,14 +73,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return Core.gpgx_peek_s68k_bus(a);
|
||||
return LibGPGX.gpgx_peek_s68k_bus(a);
|
||||
},
|
||||
delegate (long addr, byte val)
|
||||
{
|
||||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
Core.gpgx_write_s68k_bus(a, val);
|
||||
LibGPGX.gpgx_write_s68k_bus(a, val);
|
||||
}, 2);
|
||||
|
||||
if (IsSegaCD)
|
||||
|
|
|
@ -11,30 +11,69 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
{
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = Core.gpgx_get_sram(ref size);
|
||||
if (size == 0 || area == IntPtr.Zero)
|
||||
return new byte[0];
|
||||
if (disposed)
|
||||
{
|
||||
if (_disposedSaveRam != null)
|
||||
{
|
||||
return (byte[])_disposedSaveRam.Clone();
|
||||
}
|
||||
|
||||
byte[] ret = new byte[size];
|
||||
Marshal.Copy(area, ret, 0, size);
|
||||
return ret;
|
||||
return new byte[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_sram(ref area, ref size);
|
||||
if (size <= 0 || area == IntPtr.Zero)
|
||||
return new byte[0];
|
||||
LibGPGX.gpgx_sram_prepread();
|
||||
|
||||
byte[] ret = new byte[size];
|
||||
Marshal.Copy(area, ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
if (!Core.gpgx_put_sram(data, data.Length))
|
||||
throw new Exception("Core rejected saveram");
|
||||
if (disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(typeof(GPGX).ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_sram(ref area, ref size);
|
||||
if (size <= 0 || area == IntPtr.Zero)
|
||||
return;
|
||||
if (size != data.Length)
|
||||
throw new Exception("Unexpected saveram size");
|
||||
|
||||
Marshal.Copy(data, 0, area, size);
|
||||
LibGPGX.gpgx_sram_commitwrite();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = Core.gpgx_get_sram(ref size);
|
||||
return size > 0 && area != IntPtr.Zero;
|
||||
if (disposed)
|
||||
{
|
||||
return _disposedSaveRam != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_sram(ref area, ref size);
|
||||
return size > 0 && area != IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _disposedSaveRam = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
{
|
||||
bool ret = GPGXSettings.NeedsReboot(_settings, o);
|
||||
_settings = o;
|
||||
Core.gpgx_set_draw_mask(_settings.GetDrawMask());
|
||||
LibGPGX.gpgx_set_draw_mask(_settings.GetDrawMask());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
private bool _PadScreen320;
|
||||
|
||||
[DisplayName("Pad screen to 320")]
|
||||
[Description("When using 1:1 aspect ratio, enable to make screen width constant (320) between game modes")]
|
||||
[Description("Set to True to pads the screen out to be 320 when in 256 wide video modes")]
|
||||
[DefaultValue(false)]
|
||||
public bool PadScreen320 { get { return _PadScreen320; } set { _PadScreen320 = value; } }
|
||||
|
||||
|
|
|
@ -29,42 +29,51 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var ms = new MemoryStream(_savebuff2, true);
|
||||
var bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
ms.Close();
|
||||
return _savebuff2;
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
Elf.LoadStateBinary(reader);
|
||||
int newlen = reader.ReadInt32();
|
||||
if (newlen != _savebuff.Length)
|
||||
{
|
||||
throw new Exception("Unexpected state size");
|
||||
}
|
||||
|
||||
reader.Read(_savebuff, 0, _savebuff.Length);
|
||||
if (!LibGPGX.gpgx_state_load(_savebuff, _savebuff.Length))
|
||||
{
|
||||
throw new Exception("gpgx_state_load() returned false");
|
||||
}
|
||||
|
||||
// other variables
|
||||
Frame = reader.ReadInt32();
|
||||
LagCount = reader.ReadInt32();
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
// any managed pointers that we sent to the core need to be resent now!
|
||||
Core.gpgx_set_input_callback(InputCallback);
|
||||
RefreshMemCallbacks();
|
||||
Core.gpgx_set_cdd_callback(cd_callback_handle);
|
||||
Core.gpgx_invalidate_pattern_cache();
|
||||
UpdateVideo();
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
Elf.SaveStateBinary(writer);
|
||||
if (!LibGPGX.gpgx_state_save(_savebuff, _savebuff.Length))
|
||||
throw new Exception("gpgx_state_save() returned false");
|
||||
|
||||
writer.Write(_savebuff.Length);
|
||||
writer.Write(_savebuff);
|
||||
// other variables
|
||||
writer.Write(Frame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(IsLagFrame);
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
ms.Close();
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
private void InitStateBuffers()
|
||||
{
|
||||
}
|
||||
private byte[] _savebuff;
|
||||
private byte[] _savebuff2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
|||
int gppitch, gpwidth, gpheight;
|
||||
IntPtr src = IntPtr.Zero;
|
||||
|
||||
Core.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src);
|
||||
LibGPGX.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src);
|
||||
|
||||
vwidth = gpwidth;
|
||||
vheight = gpheight;
|
||||
|
|
|
@ -1,404 +1,403 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.Common.BizInvoke;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
[CoreAttributes(
|
||||
"Genplus-gx",
|
||||
"",
|
||||
isPorted: true,
|
||||
isReleased: true,
|
||||
portedVersion: "r874",
|
||||
portedUrl: "https://code.google.com/p/genplus-gx/",
|
||||
singleInstance: false
|
||||
)]
|
||||
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
|
||||
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
|
||||
{
|
||||
LibGPGX Core;
|
||||
ElfRunner Elf;
|
||||
|
||||
DiscSystem.Disc CD;
|
||||
DiscSystem.DiscSectorReader DiscSectorReader;
|
||||
byte[] romfile;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
LibGPGX.load_archive_cb LoadCallback = null;
|
||||
|
||||
LibGPGX.InputData input = new LibGPGX.InputData();
|
||||
|
||||
public enum ControlType
|
||||
{
|
||||
None,
|
||||
OnePlayer,
|
||||
Normal,
|
||||
Xea1p,
|
||||
Activator,
|
||||
Teamplayer,
|
||||
Wayplay,
|
||||
Mouse
|
||||
};
|
||||
|
||||
[CoreConstructor("GEN")]
|
||||
public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings)
|
||||
: this(comm, file, null, Settings, SyncSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object Settings, object SyncSettings)
|
||||
{
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
// this can influence some things internally (autodetect romtype, etc)
|
||||
string romextension = "GEN";
|
||||
|
||||
// three or six button?
|
||||
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
|
||||
|
||||
//hack, don't use
|
||||
if (rom != null && rom.Length > 32 * 1024 * 1024)
|
||||
{
|
||||
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Elf = new ElfRunner(Path.Combine(comm.CoreFileProvider.DllPath(), "gpgx.elf"), 8 * 1024 * 1024, 36 * 1024 * 1024, 4 * 1024 * 1024);
|
||||
if (Elf.ShouldMonitor)
|
||||
Core = BizInvoker.GetInvoker<LibGPGX>(Elf, Elf);
|
||||
else
|
||||
Core = BizInvoker.GetInvoker<LibGPGX>(Elf);
|
||||
|
||||
_syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings();
|
||||
_settings = (GPGXSettings)Settings ?? new GPGXSettings();
|
||||
|
||||
CoreComm = comm;
|
||||
|
||||
LoadCallback = new LibGPGX.load_archive_cb(load_archive);
|
||||
|
||||
this.romfile = rom;
|
||||
this.CD = CD;
|
||||
if (CD != null)
|
||||
{
|
||||
this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD);
|
||||
cd_callback_handle = new LibGPGX.cd_read_cb(CDRead);
|
||||
Core.gpgx_set_cdd_callback(cd_callback_handle);
|
||||
}
|
||||
|
||||
LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
|
||||
switch (_syncSettings.ControlType)
|
||||
{
|
||||
case ControlType.None:
|
||||
default:
|
||||
break;
|
||||
case ControlType.Activator:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
break;
|
||||
case ControlType.Normal:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.OnePlayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.Xea1p:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
|
||||
break;
|
||||
case ControlType.Teamplayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
break;
|
||||
case ControlType.Wayplay:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
break;
|
||||
case ControlType.Mouse:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
// seems like mouse in port 1 would be supported, but not both at the same time
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings()))
|
||||
throw new Exception("gpgx_init() failed");
|
||||
|
||||
{
|
||||
int fpsnum = 60;
|
||||
int fpsden = 1;
|
||||
Core.gpgx_get_fps(ref fpsnum, ref fpsden);
|
||||
CoreComm.VsyncNum = fpsnum;
|
||||
CoreComm.VsyncDen = fpsden;
|
||||
Region = CoreComm.VsyncRate > 55 ? DisplayType.NTSC : DisplayType.PAL;
|
||||
}
|
||||
|
||||
// compute state size
|
||||
InitStateBuffers();
|
||||
|
||||
SetControllerDefinition();
|
||||
|
||||
// pull the default video size from the core
|
||||
UpdateVideoInitial();
|
||||
|
||||
SetMemoryDomains();
|
||||
|
||||
InputCallback = new LibGPGX.input_cb(input_callback);
|
||||
Core.gpgx_set_input_callback(InputCallback);
|
||||
|
||||
if (CD != null)
|
||||
DriveLightEnabled = true;
|
||||
|
||||
// process the non-init settings now
|
||||
PutSettings(_settings);
|
||||
|
||||
//TODO - this hits performance, we need to make it controllable
|
||||
CDCallback = new LibGPGX.CDCallback(CDCallbackProc);
|
||||
|
||||
InitMemCallbacks();
|
||||
KillMemCallbacks();
|
||||
|
||||
Tracer = new GPGXTraceBuffer(this, MemoryDomains, this);
|
||||
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
|
||||
|
||||
Elf.Seal();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// core callback for file loading
|
||||
/// </summary>
|
||||
/// <param name="filename">string identifying file to be loaded</param>
|
||||
/// <param name="buffer">buffer to load file to</param>
|
||||
/// <param name="maxsize">maximum length buffer can hold</param>
|
||||
/// <returns>actual size loaded, or 0 on failure</returns>
|
||||
int load_archive(string filename, IntPtr buffer, int maxsize)
|
||||
{
|
||||
byte[] srcdata = null;
|
||||
|
||||
if (buffer == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filename == "PRIMARY_ROM")
|
||||
{
|
||||
if (romfile == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
|
||||
return 0;
|
||||
}
|
||||
srcdata = romfile;
|
||||
}
|
||||
else if (filename == "PRIMARY_CD" || filename == "SECONDARY_CD")
|
||||
{
|
||||
if (filename == "PRIMARY_CD" && romfile != null)
|
||||
{
|
||||
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CD == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
|
||||
return 0;
|
||||
}
|
||||
srcdata = GetCDData();
|
||||
if (srcdata.Length != maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use fromtend firmware interface
|
||||
|
||||
string firmwareID = null;
|
||||
switch (filename)
|
||||
{
|
||||
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
|
||||
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
|
||||
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (firmwareID != null)
|
||||
{
|
||||
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
|
||||
srcdata = CoreComm.CoreFileProvider.GetFirmware("GEN", firmwareID, false, "GPGX firmwares are usually required.");
|
||||
if (srcdata == null)
|
||||
{
|
||||
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Unrecognized firmware request {0}", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcdata != null)
|
||||
{
|
||||
if (srcdata.Length > maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
|
||||
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
|
||||
return srcdata.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception();
|
||||
//Console.WriteLine("Couldn't satisfy firmware request {0} for unknown reasons", filename);
|
||||
//return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CDRead(int lba, IntPtr dest, bool audio)
|
||||
{
|
||||
if (audio)
|
||||
{
|
||||
byte[] data = new byte[2352];
|
||||
if (lba < CD.Session1.LeadoutLBA)
|
||||
{
|
||||
DiscSectorReader.ReadLBA_2352(lba, data, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// audio seems to read slightly past the end of disks; probably innoculous
|
||||
// just send back 0s.
|
||||
// Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount);
|
||||
}
|
||||
Marshal.Copy(data, 0, dest, 2352);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
DiscSectorReader.ReadLBA_2048(lba, data, 0);
|
||||
Marshal.Copy(data, 0, dest, 2048);
|
||||
_drivelight = true;
|
||||
}
|
||||
}
|
||||
|
||||
LibGPGX.cd_read_cb cd_callback_handle;
|
||||
|
||||
unsafe byte[] GetCDData()
|
||||
{
|
||||
LibGPGX.CDData ret = new LibGPGX.CDData();
|
||||
int size = Marshal.SizeOf(ret);
|
||||
|
||||
var ses = CD.Session1;
|
||||
int ntrack = ses.InformationTrackCount;
|
||||
|
||||
// bet you a dollar this is all wrong
|
||||
//zero 07-jul-2015 - throws a dollar in the pile, since he probably messed it up worse
|
||||
for (int i = 0; i < LibGPGX.CD_MAX_TRACKS; i++)
|
||||
{
|
||||
if (i < ntrack)
|
||||
{
|
||||
ret.tracks[i].start = ses.Tracks[i + 1].LBA;
|
||||
ret.tracks[i].end = ses.Tracks[i + 2].LBA;
|
||||
if (i == ntrack - 1)
|
||||
{
|
||||
ret.end = ret.tracks[i].end;
|
||||
ret.last = ntrack;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.tracks[i].start = 0;
|
||||
ret.tracks[i].end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] retdata = new byte[size];
|
||||
|
||||
fixed (byte* p = &retdata[0])
|
||||
{
|
||||
Marshal.StructureToPtr(ret, (IntPtr)p, false);
|
||||
}
|
||||
return retdata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// size of native input struct
|
||||
/// </summary>
|
||||
int inputsize;
|
||||
|
||||
GPGXControlConverter ControlConverter;
|
||||
|
||||
private void SetControllerDefinition()
|
||||
{
|
||||
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
|
||||
if (!Core.gpgx_get_control(input, inputsize))
|
||||
throw new Exception("gpgx_get_control() failed");
|
||||
|
||||
ControlConverter = new GPGXControlConverter(input);
|
||||
ControllerDefinition = ControlConverter.ControllerDef;
|
||||
}
|
||||
|
||||
public LibGPGX.INPUT_DEVICE[] GetDevices()
|
||||
{
|
||||
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
|
||||
}
|
||||
|
||||
public bool IsSegaCD { get { return CD != null; } }
|
||||
|
||||
public void UpdateVDPViewContext(LibGPGX.VDPView view)
|
||||
{
|
||||
Core.gpgx_get_vdp_view(view);
|
||||
Core.gpgx_flush_vram(); // fully regenerate internal caches as needed
|
||||
}
|
||||
|
||||
short[] samples = new short[4096];
|
||||
int nsamp = 0;
|
||||
|
||||
public void GetSamples(out short[] samples, out int nsamp)
|
||||
{
|
||||
nsamp = this.nsamp;
|
||||
samples = this.samples;
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
void update_audio()
|
||||
{
|
||||
IntPtr src = IntPtr.Zero;
|
||||
Core.gpgx_get_audio(ref nsamp, ref src);
|
||||
if (src != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Copy(src, samples, 0, nsamp * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public DisplayType Region { get; private set; }
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
[CoreAttributes(
|
||||
"Genplus-gx",
|
||||
"",
|
||||
isPorted: true,
|
||||
isReleased: true,
|
||||
portedVersion: "r874",
|
||||
portedUrl: "https://code.google.com/p/genplus-gx/",
|
||||
singleInstance: true
|
||||
)]
|
||||
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
|
||||
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
|
||||
{
|
||||
static GPGX AttachedCore = null;
|
||||
|
||||
DiscSystem.Disc CD;
|
||||
DiscSystem.DiscSectorReader DiscSectorReader;
|
||||
byte[] romfile;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
LibGPGX.load_archive_cb LoadCallback = null;
|
||||
|
||||
LibGPGX.InputData input = new LibGPGX.InputData();
|
||||
|
||||
public enum ControlType
|
||||
{
|
||||
None,
|
||||
OnePlayer,
|
||||
Normal,
|
||||
Xea1p,
|
||||
Activator,
|
||||
Teamplayer,
|
||||
Wayplay,
|
||||
Mouse
|
||||
};
|
||||
|
||||
[CoreConstructor("GEN")]
|
||||
public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings)
|
||||
: this(comm, file, null, Settings, SyncSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object Settings, object SyncSettings)
|
||||
{
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
// this can influence some things internally
|
||||
string romextension = "GEN";
|
||||
|
||||
// three or six button?
|
||||
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
|
||||
|
||||
//hack, don't use
|
||||
if (rom != null && rom.Length > 32 * 1024 * 1024)
|
||||
{
|
||||
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings();
|
||||
_settings = (GPGXSettings)Settings ?? new GPGXSettings();
|
||||
|
||||
CoreComm = comm;
|
||||
if (AttachedCore != null)
|
||||
{
|
||||
AttachedCore.Dispose();
|
||||
AttachedCore = null;
|
||||
}
|
||||
AttachedCore = this;
|
||||
|
||||
LoadCallback = new LibGPGX.load_archive_cb(load_archive);
|
||||
|
||||
this.romfile = rom;
|
||||
this.CD = CD;
|
||||
this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD);
|
||||
|
||||
LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
|
||||
switch (_syncSettings.ControlType)
|
||||
{
|
||||
case ControlType.None:
|
||||
default:
|
||||
break;
|
||||
case ControlType.Activator:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
break;
|
||||
case ControlType.Normal:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.OnePlayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.Xea1p:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
|
||||
break;
|
||||
case ControlType.Teamplayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
break;
|
||||
case ControlType.Wayplay:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
break;
|
||||
case ControlType.Mouse:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
// seems like mouse in port 1 would be supported, but not both at the same time
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!LibGPGX.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings()))
|
||||
throw new Exception("gpgx_init() failed");
|
||||
|
||||
{
|
||||
int fpsnum = 60;
|
||||
int fpsden = 1;
|
||||
LibGPGX.gpgx_get_fps(ref fpsnum, ref fpsden);
|
||||
CoreComm.VsyncNum = fpsnum;
|
||||
CoreComm.VsyncDen = fpsden;
|
||||
Region = CoreComm.VsyncRate > 55 ? DisplayType.NTSC : DisplayType.PAL;
|
||||
}
|
||||
|
||||
// compute state size
|
||||
{
|
||||
byte[] tmp = new byte[LibGPGX.gpgx_state_max_size()];
|
||||
int size = LibGPGX.gpgx_state_size(tmp, tmp.Length);
|
||||
if (size <= 0)
|
||||
throw new Exception("Couldn't Determine GPGX internal state size!");
|
||||
_savebuff = new byte[size];
|
||||
_savebuff2 = new byte[_savebuff.Length + 13];
|
||||
Console.WriteLine("GPGX Internal State Size: {0}", size);
|
||||
}
|
||||
|
||||
SetControllerDefinition();
|
||||
|
||||
// pull the default video size from the core
|
||||
UpdateVideoInitial();
|
||||
|
||||
SetMemoryDomains();
|
||||
|
||||
InputCallback = new LibGPGX.input_cb(input_callback);
|
||||
LibGPGX.gpgx_set_input_callback(InputCallback);
|
||||
|
||||
if (CD != null)
|
||||
DriveLightEnabled = true;
|
||||
|
||||
// process the non-init settings now
|
||||
PutSettings(_settings);
|
||||
|
||||
//TODO - this hits performance, we need to make it controllable
|
||||
CDCallback = new LibGPGX.CDCallback(CDCallbackProc);
|
||||
|
||||
InitMemCallbacks();
|
||||
KillMemCallbacks();
|
||||
|
||||
Tracer = new GPGXTraceBuffer(this, MemoryDomains, this);
|
||||
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// core callback for file loading
|
||||
/// </summary>
|
||||
/// <param name="filename">string identifying file to be loaded</param>
|
||||
/// <param name="buffer">buffer to load file to</param>
|
||||
/// <param name="maxsize">maximum length buffer can hold</param>
|
||||
/// <returns>actual size loaded, or 0 on failure</returns>
|
||||
int load_archive(string filename, IntPtr buffer, int maxsize)
|
||||
{
|
||||
byte[] srcdata = null;
|
||||
|
||||
if (buffer == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filename == "PRIMARY_ROM")
|
||||
{
|
||||
if (romfile == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
|
||||
return 0;
|
||||
}
|
||||
srcdata = romfile;
|
||||
}
|
||||
else if (filename == "PRIMARY_CD" || filename == "SECONDARY_CD")
|
||||
{
|
||||
if (filename == "PRIMARY_CD" && romfile != null)
|
||||
{
|
||||
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CD == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
|
||||
return 0;
|
||||
}
|
||||
srcdata = GetCDData();
|
||||
if (srcdata.Length != maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use fromtend firmware interface
|
||||
|
||||
string firmwareID = null;
|
||||
switch (filename)
|
||||
{
|
||||
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
|
||||
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
|
||||
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (firmwareID != null)
|
||||
{
|
||||
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
|
||||
srcdata = CoreComm.CoreFileProvider.GetFirmware("GEN", firmwareID, false, "GPGX firmwares are usually required.");
|
||||
if (srcdata == null)
|
||||
{
|
||||
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Unrecognized firmware request {0}", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcdata != null)
|
||||
{
|
||||
if (srcdata.Length > maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
|
||||
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
|
||||
return srcdata.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception();
|
||||
//Console.WriteLine("Couldn't satisfy firmware request {0} for unknown reasons", filename);
|
||||
//return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CDRead(int lba, IntPtr dest, bool audio)
|
||||
{
|
||||
if (audio)
|
||||
{
|
||||
byte[] data = new byte[2352];
|
||||
if (lba < CD.Session1.LeadoutLBA)
|
||||
{
|
||||
DiscSectorReader.ReadLBA_2352(lba, data, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// audio seems to read slightly past the end of disks; probably innoculous
|
||||
// just send back 0s.
|
||||
// Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount);
|
||||
}
|
||||
Marshal.Copy(data, 0, dest, 2352);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
DiscSectorReader.ReadLBA_2048(lba, data, 0);
|
||||
Marshal.Copy(data, 0, dest, 2048);
|
||||
_drivelight = true;
|
||||
}
|
||||
}
|
||||
|
||||
LibGPGX.cd_read_cb cd_callback_handle;
|
||||
|
||||
unsafe byte[] GetCDData()
|
||||
{
|
||||
LibGPGX.CDData ret = new LibGPGX.CDData();
|
||||
int size = Marshal.SizeOf(ret);
|
||||
|
||||
ret.readcallback = cd_callback_handle = new LibGPGX.cd_read_cb(CDRead);
|
||||
|
||||
var ses = CD.Session1;
|
||||
int ntrack = ses.InformationTrackCount;
|
||||
|
||||
// bet you a dollar this is all wrong
|
||||
//zero 07-jul-2015 - throws a dollar in the pile, since he probably messed it up worse
|
||||
for (int i = 0; i < LibGPGX.CD_MAX_TRACKS; i++)
|
||||
{
|
||||
if (i < ntrack)
|
||||
{
|
||||
ret.tracks[i].start = ses.Tracks[i + 1].LBA;
|
||||
ret.tracks[i].end = ses.Tracks[i + 2].LBA;
|
||||
if (i == ntrack - 1)
|
||||
{
|
||||
ret.end = ret.tracks[i].end;
|
||||
ret.last = ntrack;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.tracks[i].start = 0;
|
||||
ret.tracks[i].end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] retdata = new byte[size];
|
||||
|
||||
fixed (byte* p = &retdata[0])
|
||||
{
|
||||
Marshal.StructureToPtr(ret, (IntPtr)p, false);
|
||||
}
|
||||
return retdata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// size of native input struct
|
||||
/// </summary>
|
||||
int inputsize;
|
||||
|
||||
GPGXControlConverter ControlConverter;
|
||||
|
||||
private void SetControllerDefinition()
|
||||
{
|
||||
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
|
||||
if (!LibGPGX.gpgx_get_control(input, inputsize))
|
||||
throw new Exception("gpgx_get_control() failed");
|
||||
|
||||
ControlConverter = new GPGXControlConverter(input);
|
||||
ControllerDefinition = ControlConverter.ControllerDef;
|
||||
}
|
||||
|
||||
public LibGPGX.INPUT_DEVICE[] GetDevices()
|
||||
{
|
||||
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
|
||||
}
|
||||
|
||||
public bool IsSegaCD { get { return CD != null; } }
|
||||
|
||||
public void UpdateVDPViewContext(LibGPGX.VDPView view)
|
||||
{
|
||||
LibGPGX.gpgx_get_vdp_view(view);
|
||||
LibGPGX.gpgx_flush_vram(); // fully regenerate internal caches as needed
|
||||
}
|
||||
|
||||
short[] samples = new short[4096];
|
||||
int nsamp = 0;
|
||||
|
||||
public void GetSamples(out short[] samples, out int nsamp)
|
||||
{
|
||||
nsamp = this.nsamp;
|
||||
samples = this.samples;
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
void update_audio()
|
||||
{
|
||||
IntPtr src = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_audio(ref nsamp, ref src);
|
||||
if (src != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Copy(src, samples, 0, nsamp * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public DisplayType Region { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,264 +1,264 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
public class GPGXControlConverter
|
||||
{
|
||||
// this isn't all done
|
||||
|
||||
struct CName
|
||||
{
|
||||
public string Name;
|
||||
public LibGPGX.INPUT_KEYS Key;
|
||||
public CName(string Name, LibGPGX.INPUT_KEYS Key)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Key = Key;
|
||||
}
|
||||
}
|
||||
|
||||
static CName[] Genesis3 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
};
|
||||
|
||||
static CName[] Genesis6 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
|
||||
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
|
||||
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
|
||||
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
|
||||
};
|
||||
|
||||
static CName[] Mouse =
|
||||
{
|
||||
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
|
||||
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
|
||||
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
|
||||
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
|
||||
};
|
||||
|
||||
static CName[] Lightgun =
|
||||
{
|
||||
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
|
||||
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
|
||||
};
|
||||
|
||||
static CName[] Activator =
|
||||
{
|
||||
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
|
||||
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
|
||||
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
|
||||
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
|
||||
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
|
||||
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
|
||||
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
|
||||
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
|
||||
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
|
||||
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
|
||||
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
|
||||
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
|
||||
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
|
||||
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
|
||||
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
|
||||
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
|
||||
};
|
||||
|
||||
static CName[] XEA1P =
|
||||
{
|
||||
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
|
||||
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
|
||||
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
|
||||
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
|
||||
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
|
||||
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
|
||||
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
|
||||
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
|
||||
};
|
||||
|
||||
static ControllerDefinition.FloatRange MouseRange = new ControllerDefinition.FloatRange(-256, 0, 255);
|
||||
// lightgun needs to be transformed to match the current screen resolution
|
||||
static ControllerDefinition.FloatRange LightgunRange = new ControllerDefinition.FloatRange(0, 5000, 10000);
|
||||
|
||||
static ControllerDefinition.FloatRange XEA1PRange = new ControllerDefinition.FloatRange(-128, 0, 127);
|
||||
|
||||
LibGPGX.InputData target = null;
|
||||
IController source = null;
|
||||
|
||||
List<Action> Converts = new List<Action>();
|
||||
|
||||
public ControllerDefinition ControllerDef { get; private set; }
|
||||
|
||||
void AddToController(int idx, int player, IEnumerable<CName> Buttons)
|
||||
{
|
||||
foreach (var Button in Buttons)
|
||||
{
|
||||
string Name = string.Format("P{0} {1}", player, Button.Name);
|
||||
ControllerDef.BoolButtons.Add(Name);
|
||||
var ButtonFlag = Button.Key;
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
if (source.IsPressed(Name))
|
||||
target.pad[idx] |= ButtonFlag;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DoMouseAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Mouse X", player);
|
||||
string NY = string.Format("P{0} Mouse Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)source.GetFloat(NX);
|
||||
target.analog[(2 * idx) + 1] = (short)source.GetFloat(NY);
|
||||
});
|
||||
}
|
||||
|
||||
void DoLightgunAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Lightgun X", player);
|
||||
string NY = string.Format("P{0} Lightgun Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX) / 10000.0f * (ScreenWidth - 1));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY) / 10000.0f * (ScreenHeight - 1));
|
||||
});
|
||||
}
|
||||
|
||||
void DoXEA1PAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Stick X", player);
|
||||
string NY = string.Format("P{0} Stick Y", player);
|
||||
string NZ = string.Format("P{0} Stick Z", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatControls.Add(NZ);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY));
|
||||
// +2 is correct in how gpgx internally does this
|
||||
target.analog[(2 * idx) + 2] = (short)(source.GetFloat(NZ));
|
||||
});
|
||||
}
|
||||
|
||||
public GPGXControlConverter(LibGPGX.InputData input)
|
||||
{
|
||||
Console.WriteLine("Genesis Controller report:");
|
||||
foreach (var e in input.system)
|
||||
Console.WriteLine("S:{0}", e);
|
||||
foreach (var e in input.dev)
|
||||
Console.WriteLine("D:{0}", e);
|
||||
|
||||
int player = 1;
|
||||
|
||||
ControllerDef = new ControllerDefinition();
|
||||
|
||||
ControllerDef.BoolButtons.Add("Power");
|
||||
ControllerDef.BoolButtons.Add("Reset");
|
||||
|
||||
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
|
||||
{
|
||||
switch (input.dev[i])
|
||||
{
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B:
|
||||
AddToController(i, player, Genesis3);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B:
|
||||
AddToController(i, player, Genesis6);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE:
|
||||
AddToController(i, player, Mouse);
|
||||
DoMouseAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_NONE:
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN:
|
||||
// supports menacers and justifiers
|
||||
AddToController(i, player, Lightgun);
|
||||
DoLightgunAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
|
||||
throw new Exception("Master System only device? Something went wrong.");
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
|
||||
AddToController(i, player, Activator);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P:
|
||||
AddToController(i, player, XEA1P);
|
||||
DoXEA1PAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PICO:
|
||||
// PICO isn't finished on the unmanaged side either
|
||||
throw new Exception("Sega PICO not implemented yet!");
|
||||
default:
|
||||
throw new Exception("Unknown Genesis control device! Something went wrong.");
|
||||
}
|
||||
}
|
||||
|
||||
ControllerDef.Name = "GPGX Genesis Controller";
|
||||
}
|
||||
|
||||
public void Convert(IController source, LibGPGX.InputData target)
|
||||
{
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
target.ClearAllBools();
|
||||
foreach (var f in Converts)
|
||||
f();
|
||||
this.source = null;
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenWidth { get; set; }
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenHeight { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
public class GPGXControlConverter
|
||||
{
|
||||
// this isn't all done
|
||||
|
||||
struct CName
|
||||
{
|
||||
public string Name;
|
||||
public LibGPGX.INPUT_KEYS Key;
|
||||
public CName(string Name, LibGPGX.INPUT_KEYS Key)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Key = Key;
|
||||
}
|
||||
}
|
||||
|
||||
static CName[] Genesis3 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
};
|
||||
|
||||
static CName[] Genesis6 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
|
||||
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
|
||||
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
|
||||
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
|
||||
};
|
||||
|
||||
static CName[] Mouse =
|
||||
{
|
||||
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
|
||||
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
|
||||
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
|
||||
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
|
||||
};
|
||||
|
||||
static CName[] Lightgun =
|
||||
{
|
||||
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
|
||||
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
|
||||
};
|
||||
|
||||
static CName[] Activator =
|
||||
{
|
||||
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
|
||||
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
|
||||
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
|
||||
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
|
||||
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
|
||||
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
|
||||
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
|
||||
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
|
||||
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
|
||||
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
|
||||
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
|
||||
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
|
||||
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
|
||||
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
|
||||
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
|
||||
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
|
||||
};
|
||||
|
||||
static CName[] XEA1P =
|
||||
{
|
||||
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
|
||||
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
|
||||
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
|
||||
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
|
||||
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
|
||||
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
|
||||
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
|
||||
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
|
||||
};
|
||||
|
||||
static ControllerDefinition.FloatRange MouseRange = new ControllerDefinition.FloatRange(-256, 0, 255);
|
||||
// lightgun needs to be transformed to match the current screen resolution
|
||||
static ControllerDefinition.FloatRange LightgunRange = new ControllerDefinition.FloatRange(0, 5000, 10000);
|
||||
|
||||
static ControllerDefinition.FloatRange XEA1PRange = new ControllerDefinition.FloatRange(-128, 0, 127);
|
||||
|
||||
LibGPGX.InputData target = null;
|
||||
IController source = null;
|
||||
|
||||
List<Action> Converts = new List<Action>();
|
||||
|
||||
public ControllerDefinition ControllerDef { get; private set; }
|
||||
|
||||
void AddToController(int idx, int player, IEnumerable<CName> Buttons)
|
||||
{
|
||||
foreach (var Button in Buttons)
|
||||
{
|
||||
string Name = string.Format("P{0} {1}", player, Button.Name);
|
||||
ControllerDef.BoolButtons.Add(Name);
|
||||
var ButtonFlag = Button.Key;
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
if (source.IsPressed(Name))
|
||||
target.pad[idx] |= ButtonFlag;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DoMouseAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Mouse X", player);
|
||||
string NY = string.Format("P{0} Mouse Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)source.GetFloat(NX);
|
||||
target.analog[(2 * idx) + 1] = (short)source.GetFloat(NY);
|
||||
});
|
||||
}
|
||||
|
||||
void DoLightgunAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Lightgun X", player);
|
||||
string NY = string.Format("P{0} Lightgun Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX) / 10000.0f * (ScreenWidth - 1));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY) / 10000.0f * (ScreenHeight - 1));
|
||||
});
|
||||
}
|
||||
|
||||
void DoXEA1PAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Stick X", player);
|
||||
string NY = string.Format("P{0} Stick Y", player);
|
||||
string NZ = string.Format("P{0} Stick Z", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatControls.Add(NZ);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY));
|
||||
// +2 is correct in how gpgx internally does this
|
||||
target.analog[(2 * idx) + 2] = (short)(source.GetFloat(NZ));
|
||||
});
|
||||
}
|
||||
|
||||
public GPGXControlConverter(LibGPGX.InputData input)
|
||||
{
|
||||
Console.WriteLine("Genesis Controller report:");
|
||||
foreach (var e in input.system)
|
||||
Console.WriteLine("S:{0}", e);
|
||||
foreach (var e in input.dev)
|
||||
Console.WriteLine("D:{0}", e);
|
||||
|
||||
int player = 1;
|
||||
|
||||
ControllerDef = new ControllerDefinition();
|
||||
|
||||
ControllerDef.BoolButtons.Add("Power");
|
||||
ControllerDef.BoolButtons.Add("Reset");
|
||||
|
||||
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
|
||||
{
|
||||
switch (input.dev[i])
|
||||
{
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B:
|
||||
AddToController(i, player, Genesis3);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B:
|
||||
AddToController(i, player, Genesis6);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE:
|
||||
AddToController(i, player, Mouse);
|
||||
DoMouseAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_NONE:
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN:
|
||||
// supports menacers and justifiers
|
||||
AddToController(i, player, Lightgun);
|
||||
DoLightgunAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
|
||||
throw new Exception("Master System only device? Something went wrong.");
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
|
||||
AddToController(i, player, Activator);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P:
|
||||
AddToController(i, player, XEA1P);
|
||||
DoXEA1PAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PICO:
|
||||
// PICO isn't finished on the unmanaged side either
|
||||
throw new Exception("Sega PICO not implemented yet!");
|
||||
default:
|
||||
throw new Exception("Unknown Genesis control device! Something went wrong.");
|
||||
}
|
||||
}
|
||||
|
||||
ControllerDef.Name = "GPGX Genesis Controller";
|
||||
}
|
||||
|
||||
public void Convert(IController source, LibGPGX.InputData target)
|
||||
{
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
target.ClearAllBools();
|
||||
foreach (var f in Converts)
|
||||
f();
|
||||
this.source = null;
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenWidth { get; set; }
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenHeight { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,214 +1,214 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
/*
|
||||
* how to use:
|
||||
* 0) get https://code.google.com/p/pdbparse/
|
||||
* 1) set modulename to the name of the dll file.
|
||||
* 2) set symbolname to the name of a file that you produced by executing the following command:
|
||||
* pdb_print_gvars.py [module pdb file] 0x00000000 > [output file]
|
||||
* 3) set start to an address (relative to the beginning of the dll) to start scanning
|
||||
* 4) set length to the byte length of the scan area
|
||||
* 5) instantiate a GenDbWind, and use it to control the scanner while you manipulate the dll into various configurations.
|
||||
*
|
||||
* ideas for modification:
|
||||
* 1) unhardcode config parameters and allow modifying them through the interface
|
||||
* 2) read section sizes and positions from the dll itself instead of the start\length params
|
||||
* 3) support an ignore list of symbols
|
||||
*/
|
||||
|
||||
public class GenDbgHlp : IDisposable
|
||||
{
|
||||
private static class Win32
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
}
|
||||
|
||||
// config
|
||||
const string modulename = "libgenplusgx.dll";
|
||||
const string symbolname = @"D:\encodes\bizhawksrc\genplus-gx\libretro\msvc\Debug\vars.txt";
|
||||
const int start = 0x0c7d8000 - 0x0c540000;
|
||||
const int length = 0x01082000;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
Win32.FreeLibrary(DllBase);
|
||||
DllBase = IntPtr.Zero;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
IntPtr DllBase;
|
||||
|
||||
List<Symbol> SymbolsByAddr = new List<Symbol>();
|
||||
Dictionary<string, Symbol> SymbolsByName = new Dictionary<string, Symbol>();
|
||||
|
||||
byte[][] data = new byte[10][];
|
||||
|
||||
public void SaveState(int statenum)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
|
||||
if (data[statenum] == null)
|
||||
data[statenum] = new byte[length];
|
||||
|
||||
Marshal.Copy(DllBase + start, data[statenum], 0, length);
|
||||
Console.WriteLine("State {0} saved", statenum);
|
||||
}
|
||||
|
||||
|
||||
unsafe public void Cmp(int statex, int statey)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
|
||||
|
||||
byte[] x = data[statex];
|
||||
byte[] y = data[statey];
|
||||
|
||||
if (x == null || y == null)
|
||||
{
|
||||
Console.WriteLine("Missing State!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool inrange = false;
|
||||
int startsec = 0;
|
||||
|
||||
fixed (byte* p0 = &x[0])
|
||||
fixed (byte* p1 = &y[0])
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (!inrange)
|
||||
{
|
||||
if (p0[i] != p1[i])
|
||||
{
|
||||
startsec = i;
|
||||
inrange = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p0[i] == p1[i])
|
||||
{
|
||||
bads.Add(new Tuple<int, int>(startsec, i));
|
||||
inrange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inrange)
|
||||
bads.Add(new Tuple<int, int>(startsec, length));
|
||||
|
||||
for (int i = 0; i < bads.Count; i++)
|
||||
{
|
||||
IntPtr addr = (IntPtr)(bads[i].Item1 + start);
|
||||
int len = bads[i].Item2 - bads[i].Item1;
|
||||
|
||||
var ss = Find(addr, len);
|
||||
Console.WriteLine("0x{0:X8}[0x{1}]", (int)addr, len);
|
||||
foreach (var sym in ss)
|
||||
Console.WriteLine(sym);
|
||||
Console.WriteLine();
|
||||
}
|
||||
if (bads.Count == 0)
|
||||
Console.WriteLine("Clean!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public GenDbgHlp()
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(symbolname))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
Symbol sym = Symbol.FromString(line);
|
||||
SymbolsByAddr.Add(sym);
|
||||
SymbolsByName.Add(sym.name, sym);
|
||||
}
|
||||
SymbolsByAddr.Sort();
|
||||
}
|
||||
|
||||
DllBase = Win32.LoadLibrary(modulename);
|
||||
if (DllBase == IntPtr.Zero)
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
public List<Symbol> Find(IntPtr addr, int length)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
Symbol min = new Symbol { addr = addr };
|
||||
Symbol max = new Symbol { addr = addr + length };
|
||||
|
||||
int minidx = SymbolsByAddr.BinarySearch(min);
|
||||
if (minidx < 0)
|
||||
{
|
||||
minidx = ~minidx;
|
||||
// inexact matches return the first larger value, so find the next smallset one
|
||||
if (minidx > 0)
|
||||
minidx--;
|
||||
}
|
||||
int maxidx = SymbolsByAddr.BinarySearch(max);
|
||||
if (maxidx < 0)
|
||||
{
|
||||
maxidx = ~maxidx;
|
||||
if (maxidx > 0)
|
||||
maxidx--;
|
||||
}
|
||||
return SymbolsByAddr.GetRange(minidx, maxidx - minidx + 1);
|
||||
}
|
||||
|
||||
|
||||
public struct Symbol : IComparable<Symbol>
|
||||
{
|
||||
public IntPtr addr;
|
||||
public string section;
|
||||
public string name;
|
||||
|
||||
public static Symbol FromString(string s)
|
||||
{
|
||||
string[] ss = s.Split(',');
|
||||
if (ss.Length != 4)
|
||||
throw new Exception();
|
||||
if (!ss[1].StartsWith("0x"))
|
||||
throw new Exception();
|
||||
Symbol ret = new Symbol
|
||||
{
|
||||
addr = (IntPtr)int.Parse(ss[1].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier),
|
||||
section = ss[3],
|
||||
name = ss[0]
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int CompareTo(Symbol other)
|
||||
{
|
||||
return (int)this.addr - (int)other.addr;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("0x{0:X8} {1} ({2})", (int)addr, name, section);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
/*
|
||||
* how to use:
|
||||
* 0) get https://code.google.com/p/pdbparse/
|
||||
* 1) set modulename to the name of the dll file.
|
||||
* 2) set symbolname to the name of a file that you produced by executing the following command:
|
||||
* pdb_print_gvars.py [module pdb file] 0x00000000 > [output file]
|
||||
* 3) set start to an address (relative to the beginning of the dll) to start scanning
|
||||
* 4) set length to the byte length of the scan area
|
||||
* 5) instantiate a GenDbWind, and use it to control the scanner while you manipulate the dll into various configurations.
|
||||
*
|
||||
* ideas for modification:
|
||||
* 1) unhardcode config parameters and allow modifying them through the interface
|
||||
* 2) read section sizes and positions from the dll itself instead of the start\length params
|
||||
* 3) support an ignore list of symbols
|
||||
*/
|
||||
|
||||
public class GenDbgHlp : IDisposable
|
||||
{
|
||||
private static class Win32
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
}
|
||||
|
||||
// config
|
||||
const string modulename = "libgenplusgx.dll";
|
||||
const string symbolname = @"D:\encodes\bizhawksrc\genplus-gx\libretro\msvc\Debug\vars.txt";
|
||||
const int start = 0x0c7d8000 - 0x0c540000;
|
||||
const int length = 0x01082000;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
Win32.FreeLibrary(DllBase);
|
||||
DllBase = IntPtr.Zero;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
IntPtr DllBase;
|
||||
|
||||
List<Symbol> SymbolsByAddr = new List<Symbol>();
|
||||
Dictionary<string, Symbol> SymbolsByName = new Dictionary<string, Symbol>();
|
||||
|
||||
byte[][] data = new byte[10][];
|
||||
|
||||
public void SaveState(int statenum)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
|
||||
if (data[statenum] == null)
|
||||
data[statenum] = new byte[length];
|
||||
|
||||
Marshal.Copy(DllBase + start, data[statenum], 0, length);
|
||||
Console.WriteLine("State {0} saved", statenum);
|
||||
}
|
||||
|
||||
|
||||
unsafe public void Cmp(int statex, int statey)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
|
||||
|
||||
byte[] x = data[statex];
|
||||
byte[] y = data[statey];
|
||||
|
||||
if (x == null || y == null)
|
||||
{
|
||||
Console.WriteLine("Missing State!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool inrange = false;
|
||||
int startsec = 0;
|
||||
|
||||
fixed (byte* p0 = &x[0])
|
||||
fixed (byte* p1 = &y[0])
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (!inrange)
|
||||
{
|
||||
if (p0[i] != p1[i])
|
||||
{
|
||||
startsec = i;
|
||||
inrange = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p0[i] == p1[i])
|
||||
{
|
||||
bads.Add(new Tuple<int, int>(startsec, i));
|
||||
inrange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inrange)
|
||||
bads.Add(new Tuple<int, int>(startsec, length));
|
||||
|
||||
for (int i = 0; i < bads.Count; i++)
|
||||
{
|
||||
IntPtr addr = (IntPtr)(bads[i].Item1 + start);
|
||||
int len = bads[i].Item2 - bads[i].Item1;
|
||||
|
||||
var ss = Find(addr, len);
|
||||
Console.WriteLine("0x{0:X8}[0x{1}]", (int)addr, len);
|
||||
foreach (var sym in ss)
|
||||
Console.WriteLine(sym);
|
||||
Console.WriteLine();
|
||||
}
|
||||
if (bads.Count == 0)
|
||||
Console.WriteLine("Clean!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public GenDbgHlp()
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(symbolname))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
Symbol sym = Symbol.FromString(line);
|
||||
SymbolsByAddr.Add(sym);
|
||||
SymbolsByName.Add(sym.name, sym);
|
||||
}
|
||||
SymbolsByAddr.Sort();
|
||||
}
|
||||
|
||||
DllBase = Win32.LoadLibrary(modulename);
|
||||
if (DllBase == IntPtr.Zero)
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
public List<Symbol> Find(IntPtr addr, int length)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
Symbol min = new Symbol { addr = addr };
|
||||
Symbol max = new Symbol { addr = addr + length };
|
||||
|
||||
int minidx = SymbolsByAddr.BinarySearch(min);
|
||||
if (minidx < 0)
|
||||
{
|
||||
minidx = ~minidx;
|
||||
// inexact matches return the first larger value, so find the next smallset one
|
||||
if (minidx > 0)
|
||||
minidx--;
|
||||
}
|
||||
int maxidx = SymbolsByAddr.BinarySearch(max);
|
||||
if (maxidx < 0)
|
||||
{
|
||||
maxidx = ~maxidx;
|
||||
if (maxidx > 0)
|
||||
maxidx--;
|
||||
}
|
||||
return SymbolsByAddr.GetRange(minidx, maxidx - minidx + 1);
|
||||
}
|
||||
|
||||
|
||||
public struct Symbol : IComparable<Symbol>
|
||||
{
|
||||
public IntPtr addr;
|
||||
public string section;
|
||||
public string name;
|
||||
|
||||
public static Symbol FromString(string s)
|
||||
{
|
||||
string[] ss = s.Split(',');
|
||||
if (ss.Length != 4)
|
||||
throw new Exception();
|
||||
if (!ss[1].StartsWith("0x"))
|
||||
throw new Exception();
|
||||
Symbol ret = new Symbol
|
||||
{
|
||||
addr = (IntPtr)int.Parse(ss[1].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier),
|
||||
section = ss[3],
|
||||
name = ss[0]
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int CompareTo(Symbol other)
|
||||
{
|
||||
return (int)this.addr - (int)other.addr;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("0x{0:X8} {1} ({2})", (int)addr, name, section);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,354 +1,355 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using BizHawk.Emulation.Common.BizInvoke;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
public abstract class LibGPGX
|
||||
{
|
||||
public const string DllName = "libgenplusgx.dll";
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_get_video(out int w, out int h, out int pitch, ref IntPtr buffer);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_get_audio(ref int n, ref IntPtr buffer);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_advance();
|
||||
|
||||
public enum Region : int
|
||||
{
|
||||
Autodetect = 0,
|
||||
USA = 1,
|
||||
Europe = 2,
|
||||
Japan_NTSC = 3,
|
||||
Japan_PAL = 4
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InitSettings
|
||||
{
|
||||
public enum FilterType : byte
|
||||
{
|
||||
None = 0,
|
||||
LowPass = 1,
|
||||
ThreeBand = 2
|
||||
}
|
||||
public FilterType Filter;
|
||||
public ushort LowPassRange;
|
||||
public short LowFreq;
|
||||
public short HighFreq;
|
||||
public short LowGain;
|
||||
public short MidGain;
|
||||
public short HighGain;
|
||||
public uint BackdropColor;
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility=true)]
|
||||
public abstract bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_get_fps(ref int num, ref int den);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract bool gpgx_get_control([Out]InputData dest, int bytes);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract bool gpgx_put_control([In]InputData src, int bytes);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract IntPtr gpgx_get_sram(ref int size);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract bool gpgx_put_sram(byte[] data, int size);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_clear_sram();
|
||||
|
||||
public const int MIN_MEM_DOMAIN = 0;
|
||||
public const int MAX_MEM_DOMAIN = 13;
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
// apparently, if you use built in string marshalling, the interop will assume that
|
||||
// the unmanaged char pointer was allocated in hglobal and try to free it that way
|
||||
public abstract IntPtr gpgx_get_memdom(int which, ref IntPtr area, ref int size);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_reset(bool hard);
|
||||
|
||||
public const int MAX_DEVICES = 8;
|
||||
|
||||
public enum INPUT_SYSTEM : byte
|
||||
{
|
||||
SYSTEM_NONE = 0, // unconnected port
|
||||
SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad
|
||||
SYSTEM_MOUSE = 2, // Sega Mouse
|
||||
SYSTEM_MENACER = 3, // Sega Menacer -- port B only
|
||||
SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only
|
||||
SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only
|
||||
SYSTEM_ACTIVATOR = 6, // Sega Activator
|
||||
SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System
|
||||
SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System
|
||||
SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System
|
||||
SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System
|
||||
SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer
|
||||
SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports
|
||||
};
|
||||
|
||||
public enum INPUT_DEVICE : byte
|
||||
{
|
||||
DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player)
|
||||
DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD2B = 0x02, // 2-buttons Control Pad
|
||||
DEVICE_MOUSE = 0x03, // Sega Mouse
|
||||
DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers
|
||||
DEVICE_PADDLE = 0x05, // Sega Paddle Control
|
||||
DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad
|
||||
DEVICE_PICO = 0x07, // PICO tablet
|
||||
DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet
|
||||
DEVICE_XE_A1P = 0x09, // XE-A1P analog controller
|
||||
DEVICE_ACTIVATOR = 0x0a,// Activator
|
||||
};
|
||||
|
||||
|
||||
public enum CDLog_AddrType
|
||||
{
|
||||
MDCART, RAM68k, RAMZ80, SRAM,
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum CDLog_Flags
|
||||
{
|
||||
Exec68k = 0x01,
|
||||
Data68k = 0x04,
|
||||
ExecZ80First = 0x08,
|
||||
ExecZ80Operand = 0x10,
|
||||
DataZ80 = 0x20,
|
||||
DMASource = 0x40,
|
||||
};
|
||||
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void input_cb();
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_input_callback(input_cb cb);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void mem_cb(uint addr);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void CDCallback(int addr, CDLog_AddrType addrtype, CDLog_Flags flags);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_mem_callback(mem_cb read, mem_cb write, mem_cb exec);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_cd_callback(CDCallback cd);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// not every flag is valid for every device!
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum INPUT_KEYS : ushort
|
||||
{
|
||||
/* Default Input bitmasks */
|
||||
INPUT_MODE = 0x0800,
|
||||
INPUT_X = 0x0400,
|
||||
INPUT_Y = 0x0200,
|
||||
INPUT_Z = 0x0100,
|
||||
INPUT_START = 0x0080,
|
||||
INPUT_A = 0x0040,
|
||||
INPUT_C = 0x0020,
|
||||
INPUT_B = 0x0010,
|
||||
INPUT_RIGHT = 0x0008,
|
||||
INPUT_LEFT = 0x0004,
|
||||
INPUT_DOWN = 0x0002,
|
||||
INPUT_UP = 0x0001,
|
||||
|
||||
/* Master System specific bitmasks */
|
||||
INPUT_BUTTON2 = 0x0020,
|
||||
INPUT_BUTTON1 = 0x0010,
|
||||
|
||||
/* Mega Mouse specific bitmask */
|
||||
INPUT_MOUSE_START = 0x0080,
|
||||
INPUT_MOUSE_CENTER = 0x0040,
|
||||
INPUT_MOUSE_RIGHT = 0x0020,
|
||||
INPUT_MOUSE_LEFT = 0x0010,
|
||||
|
||||
/* Pico hardware specific bitmask */
|
||||
INPUT_PICO_PEN = 0x0080,
|
||||
INPUT_PICO_RED = 0x0010,
|
||||
|
||||
/* XE-1AP specific bitmask */
|
||||
INPUT_XE_E1 = 0x0800,
|
||||
INPUT_XE_E2 = 0x0400,
|
||||
INPUT_XE_START = 0x0200,
|
||||
INPUT_XE_SELECT = 0x0100,
|
||||
INPUT_XE_A = 0x0080,
|
||||
INPUT_XE_B = 0x0040,
|
||||
INPUT_XE_C = 0x0020,
|
||||
INPUT_XE_D = 0x0010,
|
||||
|
||||
/* Activator specific bitmasks */
|
||||
INPUT_ACTIVATOR_8U = 0x8000,
|
||||
INPUT_ACTIVATOR_8L = 0x4000,
|
||||
INPUT_ACTIVATOR_7U = 0x2000,
|
||||
INPUT_ACTIVATOR_7L = 0x1000,
|
||||
INPUT_ACTIVATOR_6U = 0x0800,
|
||||
INPUT_ACTIVATOR_6L = 0x0400,
|
||||
INPUT_ACTIVATOR_5U = 0x0200,
|
||||
INPUT_ACTIVATOR_5L = 0x0100,
|
||||
INPUT_ACTIVATOR_4U = 0x0080,
|
||||
INPUT_ACTIVATOR_4L = 0x0040,
|
||||
INPUT_ACTIVATOR_3U = 0x0020,
|
||||
INPUT_ACTIVATOR_3L = 0x0010,
|
||||
INPUT_ACTIVATOR_2U = 0x0008,
|
||||
INPUT_ACTIVATOR_2L = 0x0004,
|
||||
INPUT_ACTIVATOR_1U = 0x0002,
|
||||
INPUT_ACTIVATOR_1L = 0x0001,
|
||||
|
||||
/* Menacer */
|
||||
INPUT_MENACER_TRIGGER = 0x0040,
|
||||
INPUT_MENACER_START = 0x0080,
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InputData
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// digital inputs
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// analog (x/y)
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
|
||||
public readonly short[] analog = new short[MAX_DEVICES * 2];
|
||||
/// <summary>
|
||||
/// gun horizontal offset
|
||||
/// </summary>
|
||||
public int x_offset;
|
||||
/// <summary>
|
||||
/// gun vertical offset
|
||||
/// </summary>
|
||||
public int y_offset;
|
||||
|
||||
public void ClearAllBools()
|
||||
{
|
||||
for (int i = 0; i < pad.Length; i++)
|
||||
pad[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public const int CD_MAX_TRACKS = 100;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void cd_read_cb(int lba, IntPtr dest, bool audio);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CDTrack
|
||||
{
|
||||
public int start;
|
||||
public int end;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class CDData
|
||||
{
|
||||
public int end;
|
||||
public int last;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CD_MAX_TRACKS)]
|
||||
public readonly CDTrack[] tracks = new CDTrack[CD_MAX_TRACKS];
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_cdd_callback(cd_read_cb cddcb);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct VDPNameTable
|
||||
{
|
||||
public int Width; // in cells
|
||||
public int Height; // in cells
|
||||
public int Baseaddr;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class VDPView
|
||||
{
|
||||
public IntPtr VRAM;
|
||||
public IntPtr PatternCache;
|
||||
public IntPtr ColorCache;
|
||||
public VDPNameTable NTA;
|
||||
public VDPNameTable NTB;
|
||||
public VDPNameTable NTW;
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void gpgx_get_vdp_view([Out] VDPView view);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_poke_vram(int addr, byte value);
|
||||
|
||||
/// <summary>
|
||||
/// regenerate whatever portions of the bg pattern cache are currently dirty.
|
||||
/// </summary>
|
||||
[BizImport(CallingConvention.Cdecl)] // the core will handle this itself; you only need to call this when using the cache for your own purposes
|
||||
public abstract void gpgx_flush_vram();
|
||||
|
||||
/// <summary>
|
||||
/// mark the bg pattern cache as dirty
|
||||
/// </summary>
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_invalidate_pattern_cache();
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RegisterInfo
|
||||
{
|
||||
public int Value;
|
||||
public IntPtr Name;
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract int gpgx_getmaxnumregs();
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract int gpgx_getregs([Out] RegisterInfo[] regs);
|
||||
|
||||
[Flags]
|
||||
public enum DrawMask : int
|
||||
{
|
||||
BGA = 1,
|
||||
BGB = 2,
|
||||
BGW = 4,
|
||||
Obj = 8,
|
||||
Backdrop = 16
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_draw_mask(DrawMask mask);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_write_m68k_bus(uint addr, byte data);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_write_s68k_bus(uint addr, byte data);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract byte gpgx_peek_m68k_bus(uint addr);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract byte gpgx_peek_s68k_bus(uint addr);
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
|
||||
{
|
||||
public static class LibGPGX
|
||||
{
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_video(out int w, out int h, out int pitch, ref IntPtr buffer);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_audio(ref int n, ref IntPtr buffer);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_advance();
|
||||
|
||||
public enum Region : int
|
||||
{
|
||||
Autodetect = 0,
|
||||
USA = 1,
|
||||
Europe = 2,
|
||||
Japan_NTSC = 3,
|
||||
Japan_PAL = 4
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InitSettings
|
||||
{
|
||||
public enum FilterType : byte
|
||||
{
|
||||
None = 0,
|
||||
LowPass = 1,
|
||||
ThreeBand = 2
|
||||
}
|
||||
public FilterType Filter;
|
||||
public ushort LowPassRange;
|
||||
public short LowFreq;
|
||||
public short HighFreq;
|
||||
public short LowGain;
|
||||
public short MidGain;
|
||||
public short HighGain;
|
||||
public uint BackdropColor;
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_fps(ref int num, ref int den);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_state_max_size();
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_state_size(byte[] dest, int size);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_state_save(byte[] dest, int size);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_state_load(byte[] src, int size);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_get_control([Out]InputData dest, int bytes);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_put_control([In]InputData src, int bytes);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_sram(ref IntPtr area, ref int size);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_clear_sram();
|
||||
|
||||
public const int MIN_MEM_DOMAIN = 0;
|
||||
public const int MAX_MEM_DOMAIN = 13;
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
// apparently, if you use built in string marshalling, the interop will assume that
|
||||
// the unmanaged char pointer was allocated in hglobal and try to free it that way
|
||||
public static extern IntPtr gpgx_get_memdom(int which, ref IntPtr area, ref int size);
|
||||
|
||||
// call this before reading sram returned by gpgx_get_sram()
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_sram_prepread();
|
||||
|
||||
// call this after writing sram returned by gpgx_get_sram()
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_sram_commitwrite();
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_reset(bool hard);
|
||||
|
||||
public const int MAX_DEVICES = 8;
|
||||
|
||||
public enum INPUT_SYSTEM : byte
|
||||
{
|
||||
SYSTEM_NONE = 0, // unconnected port
|
||||
SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad
|
||||
SYSTEM_MOUSE = 2, // Sega Mouse
|
||||
SYSTEM_MENACER = 3, // Sega Menacer -- port B only
|
||||
SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only
|
||||
SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only
|
||||
SYSTEM_ACTIVATOR = 6, // Sega Activator
|
||||
SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System
|
||||
SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System
|
||||
SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System
|
||||
SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System
|
||||
SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer
|
||||
SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports
|
||||
};
|
||||
|
||||
public enum INPUT_DEVICE : byte
|
||||
{
|
||||
DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player)
|
||||
DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD2B = 0x02, // 2-buttons Control Pad
|
||||
DEVICE_MOUSE = 0x03, // Sega Mouse
|
||||
DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers
|
||||
DEVICE_PADDLE = 0x05, // Sega Paddle Control
|
||||
DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad
|
||||
DEVICE_PICO = 0x07, // PICO tablet
|
||||
DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet
|
||||
DEVICE_XE_A1P = 0x09, // XE-A1P analog controller
|
||||
DEVICE_ACTIVATOR = 0x0a,// Activator
|
||||
};
|
||||
|
||||
|
||||
public enum CDLog_AddrType
|
||||
{
|
||||
MDCART, RAM68k, RAMZ80, SRAM,
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum CDLog_Flags
|
||||
{
|
||||
Exec68k = 0x01,
|
||||
Data68k = 0x04,
|
||||
ExecZ80First = 0x08,
|
||||
ExecZ80Operand = 0x10,
|
||||
DataZ80 = 0x20,
|
||||
DMASource = 0x40,
|
||||
};
|
||||
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void input_cb();
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_input_callback(input_cb cb);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void mem_cb(uint addr);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void CDCallback(int addr, CDLog_AddrType addrtype, CDLog_Flags flags);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_mem_callback(mem_cb read, mem_cb write, mem_cb exec);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_cd_callback(CDCallback cd);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// not every flag is valid for every device!
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum INPUT_KEYS : ushort
|
||||
{
|
||||
/* Default Input bitmasks */
|
||||
INPUT_MODE = 0x0800,
|
||||
INPUT_X = 0x0400,
|
||||
INPUT_Y = 0x0200,
|
||||
INPUT_Z = 0x0100,
|
||||
INPUT_START = 0x0080,
|
||||
INPUT_A = 0x0040,
|
||||
INPUT_C = 0x0020,
|
||||
INPUT_B = 0x0010,
|
||||
INPUT_RIGHT = 0x0008,
|
||||
INPUT_LEFT = 0x0004,
|
||||
INPUT_DOWN = 0x0002,
|
||||
INPUT_UP = 0x0001,
|
||||
|
||||
/* Master System specific bitmasks */
|
||||
INPUT_BUTTON2 = 0x0020,
|
||||
INPUT_BUTTON1 = 0x0010,
|
||||
|
||||
/* Mega Mouse specific bitmask */
|
||||
INPUT_MOUSE_START = 0x0080,
|
||||
INPUT_MOUSE_CENTER = 0x0040,
|
||||
INPUT_MOUSE_RIGHT = 0x0020,
|
||||
INPUT_MOUSE_LEFT = 0x0010,
|
||||
|
||||
/* Pico hardware specific bitmask */
|
||||
INPUT_PICO_PEN = 0x0080,
|
||||
INPUT_PICO_RED = 0x0010,
|
||||
|
||||
/* XE-1AP specific bitmask */
|
||||
INPUT_XE_E1 = 0x0800,
|
||||
INPUT_XE_E2 = 0x0400,
|
||||
INPUT_XE_START = 0x0200,
|
||||
INPUT_XE_SELECT = 0x0100,
|
||||
INPUT_XE_A = 0x0080,
|
||||
INPUT_XE_B = 0x0040,
|
||||
INPUT_XE_C = 0x0020,
|
||||
INPUT_XE_D = 0x0010,
|
||||
|
||||
/* Activator specific bitmasks */
|
||||
INPUT_ACTIVATOR_8U = 0x8000,
|
||||
INPUT_ACTIVATOR_8L = 0x4000,
|
||||
INPUT_ACTIVATOR_7U = 0x2000,
|
||||
INPUT_ACTIVATOR_7L = 0x1000,
|
||||
INPUT_ACTIVATOR_6U = 0x0800,
|
||||
INPUT_ACTIVATOR_6L = 0x0400,
|
||||
INPUT_ACTIVATOR_5U = 0x0200,
|
||||
INPUT_ACTIVATOR_5L = 0x0100,
|
||||
INPUT_ACTIVATOR_4U = 0x0080,
|
||||
INPUT_ACTIVATOR_4L = 0x0040,
|
||||
INPUT_ACTIVATOR_3U = 0x0020,
|
||||
INPUT_ACTIVATOR_3L = 0x0010,
|
||||
INPUT_ACTIVATOR_2U = 0x0008,
|
||||
INPUT_ACTIVATOR_2L = 0x0004,
|
||||
INPUT_ACTIVATOR_1U = 0x0002,
|
||||
INPUT_ACTIVATOR_1L = 0x0001,
|
||||
|
||||
/* Menacer */
|
||||
INPUT_MENACER_TRIGGER = 0x0040,
|
||||
INPUT_MENACER_START = 0x0080,
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InputData
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// digital inputs
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// analog (x/y)
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
|
||||
public readonly short[] analog = new short[MAX_DEVICES * 2];
|
||||
/// <summary>
|
||||
/// gun horizontal offset
|
||||
/// </summary>
|
||||
public int x_offset;
|
||||
/// <summary>
|
||||
/// gun vertical offset
|
||||
/// </summary>
|
||||
public int y_offset;
|
||||
|
||||
public void ClearAllBools()
|
||||
{
|
||||
for (int i = 0; i < pad.Length; i++)
|
||||
pad[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public const int CD_MAX_TRACKS = 100;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void cd_read_cb(int lba, IntPtr dest, bool audio);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CDTrack
|
||||
{
|
||||
public int start;
|
||||
public int end;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class CDData
|
||||
{
|
||||
public int end;
|
||||
public int last;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CD_MAX_TRACKS)]
|
||||
public readonly CDTrack[] tracks = new CDTrack[CD_MAX_TRACKS];
|
||||
public cd_read_cb readcallback;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct VDPNameTable
|
||||
{
|
||||
public int Width; // in cells
|
||||
public int Height; // in cells
|
||||
public int Baseaddr;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class VDPView
|
||||
{
|
||||
public IntPtr VRAM;
|
||||
public IntPtr PatternCache;
|
||||
public IntPtr ColorCache;
|
||||
public VDPNameTable NTA;
|
||||
public VDPNameTable NTB;
|
||||
public VDPNameTable NTW;
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_vdp_view([Out] VDPView view);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_poke_vram(int addr, byte value);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_flush_vram();
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RegisterInfo
|
||||
{
|
||||
public int Value;
|
||||
public IntPtr Name;
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_getmaxnumregs();
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_getregs([Out] RegisterInfo[] regs);
|
||||
|
||||
[Flags]
|
||||
public enum DrawMask : int
|
||||
{
|
||||
BGA = 1,
|
||||
BGB = 2,
|
||||
BGW = 4,
|
||||
Obj = 8,
|
||||
Backdrop = 16
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_draw_mask(DrawMask mask);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_write_m68k_bus(uint addr, byte data);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_write_s68k_bus(uint addr, byte data);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern byte gpgx_peek_m68k_bus(uint addr);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern byte gpgx_peek_s68k_bus(uint addr);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Common.BufferExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
{
|
||||
public partial class GPGX : ISaveRam
|
||||
{
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
if (_disposedSaveRam != null)
|
||||
{
|
||||
return (byte[])_disposedSaveRam.Clone();
|
||||
}
|
||||
|
||||
return new byte[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_sram(ref area, ref size);
|
||||
if (size <= 0 || area == IntPtr.Zero)
|
||||
return new byte[0];
|
||||
LibGPGX.gpgx_sram_prepread();
|
||||
|
||||
byte[] ret = new byte[size];
|
||||
Marshal.Copy(area, ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(typeof(GPGX).ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_sram(ref area, ref size);
|
||||
if (size <= 0 || area == IntPtr.Zero)
|
||||
return;
|
||||
if (size != data.Length)
|
||||
throw new Exception("Unexpected saveram size");
|
||||
|
||||
Marshal.Copy(data, 0, area, size);
|
||||
LibGPGX.gpgx_sram_commitwrite();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
return _disposedSaveRam != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_sram(ref area, ref size);
|
||||
return size > 0 && area != IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] _disposedSaveRam = null;
|
||||
}
|
||||
}
|
|
@ -3,15 +3,15 @@ using System.IO;
|
|||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : ICodeDataLogger
|
||||
{
|
||||
public void SetCDL(CodeDataLog cdl)
|
||||
{
|
||||
CDL = cdl;
|
||||
if (cdl == null) LibGPGX.gpgx_set_cd_callback(null);
|
||||
else LibGPGX.gpgx_set_cd_callback(CDCallback);
|
||||
if (cdl == null) Core.gpgx_set_cd_callback(null);
|
||||
else Core.gpgx_set_cd_callback(CDCallback);
|
||||
}
|
||||
|
||||
public void NewCDL(CodeDataLog cdl)
|
|
@ -4,15 +4,15 @@ using System.Runtime.InteropServices;
|
|||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : IDebuggable
|
||||
{
|
||||
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
||||
{
|
||||
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[LibGPGX.gpgx_getmaxnumregs()];
|
||||
LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[Core.gpgx_getmaxnumregs()];
|
||||
|
||||
int n = LibGPGX.gpgx_getregs(regs);
|
||||
int n = Core.gpgx_getregs(regs);
|
||||
if (n > regs.Length)
|
||||
throw new InvalidOperationException("A buffer overrun has occured!");
|
||||
var ret = new Dictionary<string, RegisterValue>();
|
||||
|
@ -63,7 +63,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
|
||||
private void RefreshMemCallbacks()
|
||||
{
|
||||
LibGPGX.gpgx_set_mem_callback(
|
||||
Core.gpgx_set_mem_callback(
|
||||
MemoryCallbacks.HasReads ? ReadCallback : null,
|
||||
MemoryCallbacks.HasWrites ? WriteCallback : null,
|
||||
MemoryCallbacks.HasExecutes ? ExecCallback : null);
|
||||
|
@ -71,7 +71,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
|
||||
private void KillMemCallbacks()
|
||||
{
|
||||
LibGPGX.gpgx_set_mem_callback(null, null, null);
|
||||
Core.gpgx_set_mem_callback(null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores.Components.M68000;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : IDisassemblable
|
||||
{
|
|
@ -1,6 +1,6 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : IDriveLight
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : IEmulator
|
||||
{
|
||||
|
@ -29,26 +29,26 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
public void FrameAdvance(bool render, bool rendersound = true)
|
||||
{
|
||||
if (Controller["Reset"])
|
||||
LibGPGX.gpgx_reset(false);
|
||||
Core.gpgx_reset(false);
|
||||
if (Controller["Power"])
|
||||
LibGPGX.gpgx_reset(true);
|
||||
Core.gpgx_reset(true);
|
||||
|
||||
// do we really have to get each time? nothing has changed
|
||||
if (!LibGPGX.gpgx_get_control(input, inputsize))
|
||||
throw new Exception("gpgx_get_control() failed!");
|
||||
// this shouldn't be needed, as nothing has changed
|
||||
// if (!Core.gpgx_get_control(input, inputsize))
|
||||
// throw new Exception("gpgx_get_control() failed!");
|
||||
|
||||
ControlConverter.ScreenWidth = vwidth;
|
||||
ControlConverter.ScreenHeight = vheight;
|
||||
ControlConverter.Convert(Controller, input);
|
||||
|
||||
if (!LibGPGX.gpgx_put_control(input, inputsize))
|
||||
if (!Core.gpgx_put_control(input, inputsize))
|
||||
throw new Exception("gpgx_put_control() failed!");
|
||||
|
||||
IsLagFrame = true;
|
||||
Frame++;
|
||||
_drivelight = false;
|
||||
|
||||
LibGPGX.gpgx_advance();
|
||||
Core.gpgx_advance();
|
||||
UpdateVideo();
|
||||
update_audio();
|
||||
|
||||
|
@ -89,16 +89,10 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
{
|
||||
if (!disposed)
|
||||
{
|
||||
if (AttachedCore != this)
|
||||
throw new Exception();
|
||||
if (SaveRamModified)
|
||||
_disposedSaveRam = CloneSaveRam();
|
||||
KillMemCallbacks();
|
||||
if (Elf != null)
|
||||
Elf.Dispose();
|
||||
if (CD != null)
|
||||
{
|
||||
CD.Dispose();
|
||||
}
|
||||
AttachedCore = null;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : IInputPollable
|
||||
{
|
|
@ -4,7 +4,7 @@ using System.Runtime.InteropServices;
|
|||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX
|
||||
{
|
||||
|
@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
{
|
||||
IntPtr area = IntPtr.Zero;
|
||||
int size = 0;
|
||||
IntPtr pname = LibGPGX.gpgx_get_memdom(i, ref area, ref size);
|
||||
IntPtr pname = Core.gpgx_get_memdom(i, ref area, ref size);
|
||||
if (area == IntPtr.Zero || pname == IntPtr.Zero || size == 0)
|
||||
continue;
|
||||
string name = Marshal.PtrToStringAnsi(pname);
|
||||
|
@ -26,17 +26,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
// vram pokes need to go through hook which invalidates cached tiles
|
||||
byte* p = (byte*)area;
|
||||
mm.Add(new MemoryDomainDelegate(name, size, MemoryDomain.Endian.Unknown,
|
||||
delegate (long addr)
|
||||
delegate(long addr)
|
||||
{
|
||||
if (addr < 0 || addr >= 65536)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return p[addr ^ 1];
|
||||
},
|
||||
delegate (long addr, byte val)
|
||||
delegate(long addr, byte val)
|
||||
{
|
||||
if (addr < 0 || addr >= 65536)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
LibGPGX.gpgx_poke_vram(((int)addr) ^ 1, val);
|
||||
Core.gpgx_poke_vram(((int)addr) ^ 1, val);
|
||||
},
|
||||
wordSize: 2));
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
else
|
||||
{
|
||||
// TODO: are the Z80 domains really Swap16 in the core? Check this
|
||||
//var byteSize = name.Contains("Z80") ? 1 : 2;
|
||||
mm.Add(MemoryDomain.FromIntPtrSwap16(name, size,
|
||||
MemoryDomain.Endian.Big, area, name != "MD CART" && name != "CD BOOT ROM"));
|
||||
}
|
||||
|
@ -55,14 +54,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return LibGPGX.gpgx_peek_m68k_bus(a);
|
||||
return Core.gpgx_peek_m68k_bus(a);
|
||||
},
|
||||
delegate (long addr, byte val)
|
||||
{
|
||||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
LibGPGX.gpgx_write_m68k_bus(a, val);
|
||||
Core.gpgx_write_m68k_bus(a, val);
|
||||
}, 2);
|
||||
|
||||
mm.Add(m68Bus);
|
||||
|
@ -73,14 +72,14 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
return LibGPGX.gpgx_peek_s68k_bus(a);
|
||||
return Core.gpgx_peek_s68k_bus(a);
|
||||
},
|
||||
delegate (long addr, byte val)
|
||||
{
|
||||
var a = (uint)addr;
|
||||
if (a >= 0x1000000)
|
||||
throw new ArgumentOutOfRangeException();
|
||||
LibGPGX.gpgx_write_s68k_bus(a, val);
|
||||
Core.gpgx_write_s68k_bus(a, val);
|
||||
}, 2);
|
||||
|
||||
if (IsSegaCD)
|
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
using BizHawk.Common.BufferExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : ISaveRam
|
||||
{
|
||||
public byte[] CloneSaveRam()
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = Core.gpgx_get_sram(ref size);
|
||||
if (size == 0 || area == IntPtr.Zero)
|
||||
return new byte[0];
|
||||
|
||||
byte[] ret = new byte[size];
|
||||
Marshal.Copy(area, ret, 0, size);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void StoreSaveRam(byte[] data)
|
||||
{
|
||||
if (!Core.gpgx_put_sram(data, data.Length))
|
||||
throw new Exception("Core rejected saveram");
|
||||
}
|
||||
|
||||
public bool SaveRamModified
|
||||
{
|
||||
get
|
||||
{
|
||||
int size = 0;
|
||||
IntPtr area = Core.gpgx_get_sram(ref size);
|
||||
return size > 0 && area != IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ using BizHawk.Emulation.Common;
|
|||
using Newtonsoft.Json;
|
||||
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : ISettable<GPGX.GPGXSettings, GPGX.GPGXSyncSettings>
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
{
|
||||
bool ret = GPGXSettings.NeedsReboot(_settings, o);
|
||||
_settings = o;
|
||||
LibGPGX.gpgx_set_draw_mask(_settings.GetDrawMask());
|
||||
Core.gpgx_set_draw_mask(_settings.GetDrawMask());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
private bool _PadScreen320;
|
||||
|
||||
[DisplayName("Pad screen to 320")]
|
||||
[Description("Set to True to pads the screen out to be 320 when in 256 wide video modes")]
|
||||
[Description("When using 1:1 aspect ratio, enable to make screen width constant (320) between game modes")]
|
||||
[DefaultValue(false)]
|
||||
public bool PadScreen320 { get { return _PadScreen320; } set { _PadScreen320 = value; } }
|
||||
|
|
@ -4,7 +4,7 @@ using System.IO;
|
|||
using BizHawk.Common.BufferExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : IStatable
|
||||
{
|
||||
|
@ -29,51 +29,42 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
||||
}
|
||||
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var ms = new MemoryStream(_savebuff2, true);
|
||||
var bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
ms.Close();
|
||||
return _savebuff2;
|
||||
}
|
||||
|
||||
public void LoadStateBinary(BinaryReader reader)
|
||||
{
|
||||
int newlen = reader.ReadInt32();
|
||||
if (newlen != _savebuff.Length)
|
||||
{
|
||||
throw new Exception("Unexpected state size");
|
||||
}
|
||||
|
||||
reader.Read(_savebuff, 0, _savebuff.Length);
|
||||
if (!LibGPGX.gpgx_state_load(_savebuff, _savebuff.Length))
|
||||
{
|
||||
throw new Exception("gpgx_state_load() returned false");
|
||||
}
|
||||
|
||||
Elf.LoadStateBinary(reader);
|
||||
// other variables
|
||||
Frame = reader.ReadInt32();
|
||||
LagCount = reader.ReadInt32();
|
||||
IsLagFrame = reader.ReadBoolean();
|
||||
// any managed pointers that we sent to the core need to be resent now!
|
||||
Core.gpgx_set_input_callback(InputCallback);
|
||||
RefreshMemCallbacks();
|
||||
Core.gpgx_set_cdd_callback(cd_callback_handle);
|
||||
Core.gpgx_invalidate_pattern_cache();
|
||||
UpdateVideo();
|
||||
}
|
||||
|
||||
public void SaveStateBinary(BinaryWriter writer)
|
||||
{
|
||||
if (!LibGPGX.gpgx_state_save(_savebuff, _savebuff.Length))
|
||||
throw new Exception("gpgx_state_save() returned false");
|
||||
|
||||
writer.Write(_savebuff.Length);
|
||||
writer.Write(_savebuff);
|
||||
Elf.SaveStateBinary(writer);
|
||||
// other variables
|
||||
writer.Write(Frame);
|
||||
writer.Write(LagCount);
|
||||
writer.Write(IsLagFrame);
|
||||
}
|
||||
|
||||
private byte[] _savebuff;
|
||||
private byte[] _savebuff2;
|
||||
public byte[] SaveStateBinary()
|
||||
{
|
||||
var ms = new MemoryStream();
|
||||
var bw = new BinaryWriter(ms);
|
||||
SaveStateBinary(bw);
|
||||
bw.Flush();
|
||||
ms.Close();
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
private void InitStateBuffers()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ using System.Text;
|
|||
|
||||
using BizHawk.Common.NumberExtensions;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX
|
||||
{
|
|
@ -1,7 +1,7 @@
|
|||
using System;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public partial class GPGX : IVideoProvider
|
||||
{
|
||||
|
@ -40,7 +40,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
|||
int gppitch, gpwidth, gpheight;
|
||||
IntPtr src = IntPtr.Zero;
|
||||
|
||||
LibGPGX.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src);
|
||||
Core.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src);
|
||||
|
||||
vwidth = gpwidth;
|
||||
vheight = gpheight;
|
|
@ -1,403 +1,404 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
{
|
||||
[CoreAttributes(
|
||||
"Genplus-gx",
|
||||
"",
|
||||
isPorted: true,
|
||||
isReleased: true,
|
||||
portedVersion: "r874",
|
||||
portedUrl: "https://code.google.com/p/genplus-gx/",
|
||||
singleInstance: true
|
||||
)]
|
||||
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
|
||||
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
|
||||
{
|
||||
static GPGX AttachedCore = null;
|
||||
|
||||
DiscSystem.Disc CD;
|
||||
DiscSystem.DiscSectorReader DiscSectorReader;
|
||||
byte[] romfile;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
LibGPGX.load_archive_cb LoadCallback = null;
|
||||
|
||||
LibGPGX.InputData input = new LibGPGX.InputData();
|
||||
|
||||
public enum ControlType
|
||||
{
|
||||
None,
|
||||
OnePlayer,
|
||||
Normal,
|
||||
Xea1p,
|
||||
Activator,
|
||||
Teamplayer,
|
||||
Wayplay,
|
||||
Mouse
|
||||
};
|
||||
|
||||
[CoreConstructor("GEN")]
|
||||
public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings)
|
||||
: this(comm, file, null, Settings, SyncSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object Settings, object SyncSettings)
|
||||
{
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
// this can influence some things internally
|
||||
string romextension = "GEN";
|
||||
|
||||
// three or six button?
|
||||
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
|
||||
|
||||
//hack, don't use
|
||||
if (rom != null && rom.Length > 32 * 1024 * 1024)
|
||||
{
|
||||
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings();
|
||||
_settings = (GPGXSettings)Settings ?? new GPGXSettings();
|
||||
|
||||
CoreComm = comm;
|
||||
if (AttachedCore != null)
|
||||
{
|
||||
AttachedCore.Dispose();
|
||||
AttachedCore = null;
|
||||
}
|
||||
AttachedCore = this;
|
||||
|
||||
LoadCallback = new LibGPGX.load_archive_cb(load_archive);
|
||||
|
||||
this.romfile = rom;
|
||||
this.CD = CD;
|
||||
this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD);
|
||||
|
||||
LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
|
||||
switch (_syncSettings.ControlType)
|
||||
{
|
||||
case ControlType.None:
|
||||
default:
|
||||
break;
|
||||
case ControlType.Activator:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
break;
|
||||
case ControlType.Normal:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.OnePlayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.Xea1p:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
|
||||
break;
|
||||
case ControlType.Teamplayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
break;
|
||||
case ControlType.Wayplay:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
break;
|
||||
case ControlType.Mouse:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
// seems like mouse in port 1 would be supported, but not both at the same time
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!LibGPGX.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings()))
|
||||
throw new Exception("gpgx_init() failed");
|
||||
|
||||
{
|
||||
int fpsnum = 60;
|
||||
int fpsden = 1;
|
||||
LibGPGX.gpgx_get_fps(ref fpsnum, ref fpsden);
|
||||
CoreComm.VsyncNum = fpsnum;
|
||||
CoreComm.VsyncDen = fpsden;
|
||||
Region = CoreComm.VsyncRate > 55 ? DisplayType.NTSC : DisplayType.PAL;
|
||||
}
|
||||
|
||||
// compute state size
|
||||
{
|
||||
byte[] tmp = new byte[LibGPGX.gpgx_state_max_size()];
|
||||
int size = LibGPGX.gpgx_state_size(tmp, tmp.Length);
|
||||
if (size <= 0)
|
||||
throw new Exception("Couldn't Determine GPGX internal state size!");
|
||||
_savebuff = new byte[size];
|
||||
_savebuff2 = new byte[_savebuff.Length + 13];
|
||||
Console.WriteLine("GPGX Internal State Size: {0}", size);
|
||||
}
|
||||
|
||||
SetControllerDefinition();
|
||||
|
||||
// pull the default video size from the core
|
||||
UpdateVideoInitial();
|
||||
|
||||
SetMemoryDomains();
|
||||
|
||||
InputCallback = new LibGPGX.input_cb(input_callback);
|
||||
LibGPGX.gpgx_set_input_callback(InputCallback);
|
||||
|
||||
if (CD != null)
|
||||
DriveLightEnabled = true;
|
||||
|
||||
// process the non-init settings now
|
||||
PutSettings(_settings);
|
||||
|
||||
//TODO - this hits performance, we need to make it controllable
|
||||
CDCallback = new LibGPGX.CDCallback(CDCallbackProc);
|
||||
|
||||
InitMemCallbacks();
|
||||
KillMemCallbacks();
|
||||
|
||||
Tracer = new GPGXTraceBuffer(this, MemoryDomains, this);
|
||||
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// core callback for file loading
|
||||
/// </summary>
|
||||
/// <param name="filename">string identifying file to be loaded</param>
|
||||
/// <param name="buffer">buffer to load file to</param>
|
||||
/// <param name="maxsize">maximum length buffer can hold</param>
|
||||
/// <returns>actual size loaded, or 0 on failure</returns>
|
||||
int load_archive(string filename, IntPtr buffer, int maxsize)
|
||||
{
|
||||
byte[] srcdata = null;
|
||||
|
||||
if (buffer == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filename == "PRIMARY_ROM")
|
||||
{
|
||||
if (romfile == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
|
||||
return 0;
|
||||
}
|
||||
srcdata = romfile;
|
||||
}
|
||||
else if (filename == "PRIMARY_CD" || filename == "SECONDARY_CD")
|
||||
{
|
||||
if (filename == "PRIMARY_CD" && romfile != null)
|
||||
{
|
||||
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CD == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
|
||||
return 0;
|
||||
}
|
||||
srcdata = GetCDData();
|
||||
if (srcdata.Length != maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use fromtend firmware interface
|
||||
|
||||
string firmwareID = null;
|
||||
switch (filename)
|
||||
{
|
||||
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
|
||||
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
|
||||
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (firmwareID != null)
|
||||
{
|
||||
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
|
||||
srcdata = CoreComm.CoreFileProvider.GetFirmware("GEN", firmwareID, false, "GPGX firmwares are usually required.");
|
||||
if (srcdata == null)
|
||||
{
|
||||
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Unrecognized firmware request {0}", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcdata != null)
|
||||
{
|
||||
if (srcdata.Length > maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
|
||||
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
|
||||
return srcdata.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception();
|
||||
//Console.WriteLine("Couldn't satisfy firmware request {0} for unknown reasons", filename);
|
||||
//return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CDRead(int lba, IntPtr dest, bool audio)
|
||||
{
|
||||
if (audio)
|
||||
{
|
||||
byte[] data = new byte[2352];
|
||||
if (lba < CD.Session1.LeadoutLBA)
|
||||
{
|
||||
DiscSectorReader.ReadLBA_2352(lba, data, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// audio seems to read slightly past the end of disks; probably innoculous
|
||||
// just send back 0s.
|
||||
// Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount);
|
||||
}
|
||||
Marshal.Copy(data, 0, dest, 2352);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
DiscSectorReader.ReadLBA_2048(lba, data, 0);
|
||||
Marshal.Copy(data, 0, dest, 2048);
|
||||
_drivelight = true;
|
||||
}
|
||||
}
|
||||
|
||||
LibGPGX.cd_read_cb cd_callback_handle;
|
||||
|
||||
unsafe byte[] GetCDData()
|
||||
{
|
||||
LibGPGX.CDData ret = new LibGPGX.CDData();
|
||||
int size = Marshal.SizeOf(ret);
|
||||
|
||||
ret.readcallback = cd_callback_handle = new LibGPGX.cd_read_cb(CDRead);
|
||||
|
||||
var ses = CD.Session1;
|
||||
int ntrack = ses.InformationTrackCount;
|
||||
|
||||
// bet you a dollar this is all wrong
|
||||
//zero 07-jul-2015 - throws a dollar in the pile, since he probably messed it up worse
|
||||
for (int i = 0; i < LibGPGX.CD_MAX_TRACKS; i++)
|
||||
{
|
||||
if (i < ntrack)
|
||||
{
|
||||
ret.tracks[i].start = ses.Tracks[i + 1].LBA;
|
||||
ret.tracks[i].end = ses.Tracks[i + 2].LBA;
|
||||
if (i == ntrack - 1)
|
||||
{
|
||||
ret.end = ret.tracks[i].end;
|
||||
ret.last = ntrack;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.tracks[i].start = 0;
|
||||
ret.tracks[i].end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] retdata = new byte[size];
|
||||
|
||||
fixed (byte* p = &retdata[0])
|
||||
{
|
||||
Marshal.StructureToPtr(ret, (IntPtr)p, false);
|
||||
}
|
||||
return retdata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// size of native input struct
|
||||
/// </summary>
|
||||
int inputsize;
|
||||
|
||||
GPGXControlConverter ControlConverter;
|
||||
|
||||
private void SetControllerDefinition()
|
||||
{
|
||||
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
|
||||
if (!LibGPGX.gpgx_get_control(input, inputsize))
|
||||
throw new Exception("gpgx_get_control() failed");
|
||||
|
||||
ControlConverter = new GPGXControlConverter(input);
|
||||
ControllerDefinition = ControlConverter.ControllerDef;
|
||||
}
|
||||
|
||||
public LibGPGX.INPUT_DEVICE[] GetDevices()
|
||||
{
|
||||
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
|
||||
}
|
||||
|
||||
public bool IsSegaCD { get { return CD != null; } }
|
||||
|
||||
public void UpdateVDPViewContext(LibGPGX.VDPView view)
|
||||
{
|
||||
LibGPGX.gpgx_get_vdp_view(view);
|
||||
LibGPGX.gpgx_flush_vram(); // fully regenerate internal caches as needed
|
||||
}
|
||||
|
||||
short[] samples = new short[4096];
|
||||
int nsamp = 0;
|
||||
|
||||
public void GetSamples(out short[] samples, out int nsamp)
|
||||
{
|
||||
nsamp = this.nsamp;
|
||||
samples = this.samples;
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
void update_audio()
|
||||
{
|
||||
IntPtr src = IntPtr.Zero;
|
||||
LibGPGX.gpgx_get_audio(ref nsamp, ref src);
|
||||
if (src != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Copy(src, samples, 0, nsamp * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public DisplayType Region { get; private set; }
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common;
|
||||
using System.IO;
|
||||
using BizHawk.Emulation.Common.BizInvoke;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
[CoreAttributes(
|
||||
"Genplus-gx64",
|
||||
"",
|
||||
isPorted: true,
|
||||
isReleased: true,
|
||||
portedVersion: "r874",
|
||||
portedUrl: "https://code.google.com/p/genplus-gx/",
|
||||
singleInstance: false
|
||||
)]
|
||||
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
|
||||
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
|
||||
{
|
||||
LibGPGX Core;
|
||||
ElfRunner Elf;
|
||||
|
||||
DiscSystem.Disc CD;
|
||||
DiscSystem.DiscSectorReader DiscSectorReader;
|
||||
byte[] romfile;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
LibGPGX.load_archive_cb LoadCallback = null;
|
||||
|
||||
LibGPGX.InputData input = new LibGPGX.InputData();
|
||||
|
||||
public enum ControlType
|
||||
{
|
||||
None,
|
||||
OnePlayer,
|
||||
Normal,
|
||||
Xea1p,
|
||||
Activator,
|
||||
Teamplayer,
|
||||
Wayplay,
|
||||
Mouse
|
||||
};
|
||||
|
||||
[CoreConstructor("GEN")]
|
||||
public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings)
|
||||
: this(comm, file, null, Settings, SyncSettings)
|
||||
{
|
||||
}
|
||||
|
||||
public GPGX(CoreComm comm, byte[] rom, DiscSystem.Disc CD, object Settings, object SyncSettings)
|
||||
{
|
||||
ServiceProvider = new BasicServiceProvider(this);
|
||||
// this can influence some things internally (autodetect romtype, etc)
|
||||
string romextension = "GEN";
|
||||
|
||||
// three or six button?
|
||||
// http://www.sega-16.com/forum/showthread.php?4398-Forgotten-Worlds-giving-you-GAME-OVER-immediately-Fix-inside&highlight=forgotten%20worlds
|
||||
|
||||
//hack, don't use
|
||||
if (rom != null && rom.Length > 32 * 1024 * 1024)
|
||||
{
|
||||
throw new InvalidOperationException("ROM too big! Did you try to load a CD as a ROM?");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Elf = new ElfRunner(Path.Combine(comm.CoreFileProvider.DllPath(), "gpgx.elf"), 8 * 1024 * 1024, 36 * 1024 * 1024, 4 * 1024 * 1024);
|
||||
if (Elf.ShouldMonitor)
|
||||
Core = BizInvoker.GetInvoker<LibGPGX>(Elf, Elf);
|
||||
else
|
||||
Core = BizInvoker.GetInvoker<LibGPGX>(Elf);
|
||||
|
||||
_syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings();
|
||||
_settings = (GPGXSettings)Settings ?? new GPGXSettings();
|
||||
|
||||
CoreComm = comm;
|
||||
|
||||
LoadCallback = new LibGPGX.load_archive_cb(load_archive);
|
||||
|
||||
this.romfile = rom;
|
||||
this.CD = CD;
|
||||
if (CD != null)
|
||||
{
|
||||
this.DiscSectorReader = new DiscSystem.DiscSectorReader(CD);
|
||||
cd_callback_handle = new LibGPGX.cd_read_cb(CDRead);
|
||||
Core.gpgx_set_cdd_callback(cd_callback_handle);
|
||||
}
|
||||
|
||||
LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE;
|
||||
|
||||
switch (_syncSettings.ControlType)
|
||||
{
|
||||
case ControlType.None:
|
||||
default:
|
||||
break;
|
||||
case ControlType.Activator:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_ACTIVATOR;
|
||||
break;
|
||||
case ControlType.Normal:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.OnePlayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
break;
|
||||
case ControlType.Xea1p:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_XE_A1P;
|
||||
break;
|
||||
case ControlType.Teamplayer:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_TEAMPLAYER;
|
||||
break;
|
||||
case ControlType.Wayplay:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_WAYPLAY;
|
||||
break;
|
||||
case ControlType.Mouse:
|
||||
system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_MD_GAMEPAD;
|
||||
// seems like mouse in port 1 would be supported, but not both at the same time
|
||||
system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_MOUSE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (!Core.gpgx_init(romextension, LoadCallback, _syncSettings.UseSixButton, system_a, system_b, _syncSettings.Region, _settings.GetNativeSettings()))
|
||||
throw new Exception("gpgx_init() failed");
|
||||
|
||||
{
|
||||
int fpsnum = 60;
|
||||
int fpsden = 1;
|
||||
Core.gpgx_get_fps(ref fpsnum, ref fpsden);
|
||||
CoreComm.VsyncNum = fpsnum;
|
||||
CoreComm.VsyncDen = fpsden;
|
||||
Region = CoreComm.VsyncRate > 55 ? DisplayType.NTSC : DisplayType.PAL;
|
||||
}
|
||||
|
||||
// compute state size
|
||||
InitStateBuffers();
|
||||
|
||||
SetControllerDefinition();
|
||||
|
||||
// pull the default video size from the core
|
||||
UpdateVideoInitial();
|
||||
|
||||
SetMemoryDomains();
|
||||
|
||||
InputCallback = new LibGPGX.input_cb(input_callback);
|
||||
Core.gpgx_set_input_callback(InputCallback);
|
||||
|
||||
if (CD != null)
|
||||
DriveLightEnabled = true;
|
||||
|
||||
// process the non-init settings now
|
||||
PutSettings(_settings);
|
||||
|
||||
//TODO - this hits performance, we need to make it controllable
|
||||
CDCallback = new LibGPGX.CDCallback(CDCallbackProc);
|
||||
|
||||
InitMemCallbacks();
|
||||
KillMemCallbacks();
|
||||
|
||||
Tracer = new GPGXTraceBuffer(this, MemoryDomains, this);
|
||||
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
|
||||
|
||||
Elf.Seal();
|
||||
}
|
||||
catch
|
||||
{
|
||||
Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// core callback for file loading
|
||||
/// </summary>
|
||||
/// <param name="filename">string identifying file to be loaded</param>
|
||||
/// <param name="buffer">buffer to load file to</param>
|
||||
/// <param name="maxsize">maximum length buffer can hold</param>
|
||||
/// <returns>actual size loaded, or 0 on failure</returns>
|
||||
int load_archive(string filename, IntPtr buffer, int maxsize)
|
||||
{
|
||||
byte[] srcdata = null;
|
||||
|
||||
if (buffer == IntPtr.Zero)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because buffer == NULL", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (filename == "PRIMARY_ROM")
|
||||
{
|
||||
if (romfile == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request PRIMARY_ROM because none was provided.");
|
||||
return 0;
|
||||
}
|
||||
srcdata = romfile;
|
||||
}
|
||||
else if (filename == "PRIMARY_CD" || filename == "SECONDARY_CD")
|
||||
{
|
||||
if (filename == "PRIMARY_CD" && romfile != null)
|
||||
{
|
||||
Console.WriteLine("Declined to satisfy firmware request PRIMARY_CD because PRIMARY_ROM was provided.");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (CD == null)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because none was provided.", filename);
|
||||
return 0;
|
||||
}
|
||||
srcdata = GetCDData();
|
||||
if (srcdata.Length != maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because of struct size.", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// use fromtend firmware interface
|
||||
|
||||
string firmwareID = null;
|
||||
switch (filename)
|
||||
{
|
||||
case "CD_BIOS_EU": firmwareID = "CD_BIOS_EU"; break;
|
||||
case "CD_BIOS_JP": firmwareID = "CD_BIOS_JP"; break;
|
||||
case "CD_BIOS_US": firmwareID = "CD_BIOS_US"; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (firmwareID != null)
|
||||
{
|
||||
// this path will be the most common PEBKAC error, so be a bit more vocal about the problem
|
||||
srcdata = CoreComm.CoreFileProvider.GetFirmware("GEN", firmwareID, false, "GPGX firmwares are usually required.");
|
||||
if (srcdata == null)
|
||||
{
|
||||
Console.WriteLine("Frontend couldn't satisfy firmware request GEN:{0}", firmwareID);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Unrecognized firmware request {0}", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (srcdata != null)
|
||||
{
|
||||
if (srcdata.Length > maxsize)
|
||||
{
|
||||
Console.WriteLine("Couldn't satisfy firmware request {0} because {1} > {2}", filename, srcdata.Length, maxsize);
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
Marshal.Copy(srcdata, 0, buffer, srcdata.Length);
|
||||
Console.WriteLine("Firmware request {0} satisfied at size {1}", filename, srcdata.Length);
|
||||
return srcdata.Length;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception();
|
||||
//Console.WriteLine("Couldn't satisfy firmware request {0} for unknown reasons", filename);
|
||||
//return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CDRead(int lba, IntPtr dest, bool audio)
|
||||
{
|
||||
if (audio)
|
||||
{
|
||||
byte[] data = new byte[2352];
|
||||
if (lba < CD.Session1.LeadoutLBA)
|
||||
{
|
||||
DiscSectorReader.ReadLBA_2352(lba, data, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// audio seems to read slightly past the end of disks; probably innoculous
|
||||
// just send back 0s.
|
||||
// Console.WriteLine("!!{0} >= {1}", lba, CD.LBACount);
|
||||
}
|
||||
Marshal.Copy(data, 0, dest, 2352);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] data = new byte[2048];
|
||||
DiscSectorReader.ReadLBA_2048(lba, data, 0);
|
||||
Marshal.Copy(data, 0, dest, 2048);
|
||||
_drivelight = true;
|
||||
}
|
||||
}
|
||||
|
||||
LibGPGX.cd_read_cb cd_callback_handle;
|
||||
|
||||
unsafe byte[] GetCDData()
|
||||
{
|
||||
LibGPGX.CDData ret = new LibGPGX.CDData();
|
||||
int size = Marshal.SizeOf(ret);
|
||||
|
||||
var ses = CD.Session1;
|
||||
int ntrack = ses.InformationTrackCount;
|
||||
|
||||
// bet you a dollar this is all wrong
|
||||
//zero 07-jul-2015 - throws a dollar in the pile, since he probably messed it up worse
|
||||
for (int i = 0; i < LibGPGX.CD_MAX_TRACKS; i++)
|
||||
{
|
||||
if (i < ntrack)
|
||||
{
|
||||
ret.tracks[i].start = ses.Tracks[i + 1].LBA;
|
||||
ret.tracks[i].end = ses.Tracks[i + 2].LBA;
|
||||
if (i == ntrack - 1)
|
||||
{
|
||||
ret.end = ret.tracks[i].end;
|
||||
ret.last = ntrack;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.tracks[i].start = 0;
|
||||
ret.tracks[i].end = 0;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] retdata = new byte[size];
|
||||
|
||||
fixed (byte* p = &retdata[0])
|
||||
{
|
||||
Marshal.StructureToPtr(ret, (IntPtr)p, false);
|
||||
}
|
||||
return retdata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// size of native input struct
|
||||
/// </summary>
|
||||
int inputsize;
|
||||
|
||||
GPGXControlConverter ControlConverter;
|
||||
|
||||
private void SetControllerDefinition()
|
||||
{
|
||||
inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData));
|
||||
if (!Core.gpgx_get_control(input, inputsize))
|
||||
throw new Exception("gpgx_get_control() failed");
|
||||
|
||||
ControlConverter = new GPGXControlConverter(input);
|
||||
ControllerDefinition = ControlConverter.ControllerDef;
|
||||
}
|
||||
|
||||
public LibGPGX.INPUT_DEVICE[] GetDevices()
|
||||
{
|
||||
return (LibGPGX.INPUT_DEVICE[])input.dev.Clone();
|
||||
}
|
||||
|
||||
public bool IsSegaCD { get { return CD != null; } }
|
||||
|
||||
public void UpdateVDPViewContext(LibGPGX.VDPView view)
|
||||
{
|
||||
Core.gpgx_get_vdp_view(view);
|
||||
Core.gpgx_flush_vram(); // fully regenerate internal caches as needed
|
||||
}
|
||||
|
||||
short[] samples = new short[4096];
|
||||
int nsamp = 0;
|
||||
|
||||
public void GetSamples(out short[] samples, out int nsamp)
|
||||
{
|
||||
nsamp = this.nsamp;
|
||||
samples = this.samples;
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
public void DiscardSamples()
|
||||
{
|
||||
this.nsamp = 0;
|
||||
}
|
||||
|
||||
void update_audio()
|
||||
{
|
||||
IntPtr src = IntPtr.Zero;
|
||||
Core.gpgx_get_audio(ref nsamp, ref src);
|
||||
if (src != IntPtr.Zero)
|
||||
{
|
||||
Marshal.Copy(src, samples, 0, nsamp * 2);
|
||||
}
|
||||
}
|
||||
|
||||
public DisplayType Region { get; private set; }
|
||||
}
|
||||
}
|
|
@ -1,264 +1,264 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
{
|
||||
public class GPGXControlConverter
|
||||
{
|
||||
// this isn't all done
|
||||
|
||||
struct CName
|
||||
{
|
||||
public string Name;
|
||||
public LibGPGX.INPUT_KEYS Key;
|
||||
public CName(string Name, LibGPGX.INPUT_KEYS Key)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Key = Key;
|
||||
}
|
||||
}
|
||||
|
||||
static CName[] Genesis3 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
};
|
||||
|
||||
static CName[] Genesis6 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
|
||||
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
|
||||
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
|
||||
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
|
||||
};
|
||||
|
||||
static CName[] Mouse =
|
||||
{
|
||||
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
|
||||
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
|
||||
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
|
||||
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
|
||||
};
|
||||
|
||||
static CName[] Lightgun =
|
||||
{
|
||||
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
|
||||
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
|
||||
};
|
||||
|
||||
static CName[] Activator =
|
||||
{
|
||||
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
|
||||
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
|
||||
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
|
||||
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
|
||||
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
|
||||
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
|
||||
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
|
||||
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
|
||||
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
|
||||
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
|
||||
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
|
||||
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
|
||||
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
|
||||
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
|
||||
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
|
||||
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
|
||||
};
|
||||
|
||||
static CName[] XEA1P =
|
||||
{
|
||||
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
|
||||
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
|
||||
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
|
||||
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
|
||||
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
|
||||
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
|
||||
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
|
||||
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
|
||||
};
|
||||
|
||||
static ControllerDefinition.FloatRange MouseRange = new ControllerDefinition.FloatRange(-256, 0, 255);
|
||||
// lightgun needs to be transformed to match the current screen resolution
|
||||
static ControllerDefinition.FloatRange LightgunRange = new ControllerDefinition.FloatRange(0, 5000, 10000);
|
||||
|
||||
static ControllerDefinition.FloatRange XEA1PRange = new ControllerDefinition.FloatRange(-128, 0, 127);
|
||||
|
||||
LibGPGX.InputData target = null;
|
||||
IController source = null;
|
||||
|
||||
List<Action> Converts = new List<Action>();
|
||||
|
||||
public ControllerDefinition ControllerDef { get; private set; }
|
||||
|
||||
void AddToController(int idx, int player, IEnumerable<CName> Buttons)
|
||||
{
|
||||
foreach (var Button in Buttons)
|
||||
{
|
||||
string Name = string.Format("P{0} {1}", player, Button.Name);
|
||||
ControllerDef.BoolButtons.Add(Name);
|
||||
var ButtonFlag = Button.Key;
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
if (source.IsPressed(Name))
|
||||
target.pad[idx] |= ButtonFlag;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DoMouseAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Mouse X", player);
|
||||
string NY = string.Format("P{0} Mouse Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)source.GetFloat(NX);
|
||||
target.analog[(2 * idx) + 1] = (short)source.GetFloat(NY);
|
||||
});
|
||||
}
|
||||
|
||||
void DoLightgunAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Lightgun X", player);
|
||||
string NY = string.Format("P{0} Lightgun Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX) / 10000.0f * (ScreenWidth - 1));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY) / 10000.0f * (ScreenHeight - 1));
|
||||
});
|
||||
}
|
||||
|
||||
void DoXEA1PAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Stick X", player);
|
||||
string NY = string.Format("P{0} Stick Y", player);
|
||||
string NZ = string.Format("P{0} Stick Z", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatControls.Add(NZ);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY));
|
||||
// +2 is correct in how gpgx internally does this
|
||||
target.analog[(2 * idx) + 2] = (short)(source.GetFloat(NZ));
|
||||
});
|
||||
}
|
||||
|
||||
public GPGXControlConverter(LibGPGX.InputData input)
|
||||
{
|
||||
Console.WriteLine("Genesis Controller report:");
|
||||
foreach (var e in input.system)
|
||||
Console.WriteLine("S:{0}", e);
|
||||
foreach (var e in input.dev)
|
||||
Console.WriteLine("D:{0}", e);
|
||||
|
||||
int player = 1;
|
||||
|
||||
ControllerDef = new ControllerDefinition();
|
||||
|
||||
ControllerDef.BoolButtons.Add("Power");
|
||||
ControllerDef.BoolButtons.Add("Reset");
|
||||
|
||||
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
|
||||
{
|
||||
switch (input.dev[i])
|
||||
{
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B:
|
||||
AddToController(i, player, Genesis3);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B:
|
||||
AddToController(i, player, Genesis6);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE:
|
||||
AddToController(i, player, Mouse);
|
||||
DoMouseAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_NONE:
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN:
|
||||
// supports menacers and justifiers
|
||||
AddToController(i, player, Lightgun);
|
||||
DoLightgunAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
|
||||
throw new Exception("Master System only device? Something went wrong.");
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
|
||||
AddToController(i, player, Activator);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P:
|
||||
AddToController(i, player, XEA1P);
|
||||
DoXEA1PAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PICO:
|
||||
// PICO isn't finished on the unmanaged side either
|
||||
throw new Exception("Sega PICO not implemented yet!");
|
||||
default:
|
||||
throw new Exception("Unknown Genesis control device! Something went wrong.");
|
||||
}
|
||||
}
|
||||
|
||||
ControllerDef.Name = "GPGX Genesis Controller";
|
||||
}
|
||||
|
||||
public void Convert(IController source, LibGPGX.InputData target)
|
||||
{
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
target.ClearAllBools();
|
||||
foreach (var f in Converts)
|
||||
f();
|
||||
this.source = null;
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenWidth { get; set; }
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenHeight { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
using BizHawk.Common;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public class GPGXControlConverter
|
||||
{
|
||||
// this isn't all done
|
||||
|
||||
struct CName
|
||||
{
|
||||
public string Name;
|
||||
public LibGPGX.INPUT_KEYS Key;
|
||||
public CName(string Name, LibGPGX.INPUT_KEYS Key)
|
||||
{
|
||||
this.Name = Name;
|
||||
this.Key = Key;
|
||||
}
|
||||
}
|
||||
|
||||
static CName[] Genesis3 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
};
|
||||
|
||||
static CName[] Genesis6 =
|
||||
{
|
||||
new CName("Up", LibGPGX.INPUT_KEYS.INPUT_UP),
|
||||
new CName("Down", LibGPGX.INPUT_KEYS.INPUT_DOWN),
|
||||
new CName("Left", LibGPGX.INPUT_KEYS.INPUT_LEFT),
|
||||
new CName("Right", LibGPGX.INPUT_KEYS.INPUT_RIGHT),
|
||||
new CName("A", LibGPGX.INPUT_KEYS.INPUT_A),
|
||||
new CName("B", LibGPGX.INPUT_KEYS.INPUT_B),
|
||||
new CName("C", LibGPGX.INPUT_KEYS.INPUT_C),
|
||||
new CName("Start", LibGPGX.INPUT_KEYS.INPUT_START),
|
||||
new CName("X", LibGPGX.INPUT_KEYS.INPUT_X),
|
||||
new CName("Y", LibGPGX.INPUT_KEYS.INPUT_Y),
|
||||
new CName("Z", LibGPGX.INPUT_KEYS.INPUT_Z),
|
||||
new CName("Mode", LibGPGX.INPUT_KEYS.INPUT_MODE),
|
||||
};
|
||||
|
||||
static CName[] Mouse =
|
||||
{
|
||||
new CName("Mouse Left", LibGPGX.INPUT_KEYS.INPUT_MOUSE_LEFT),
|
||||
new CName("Mouse Center", LibGPGX.INPUT_KEYS.INPUT_MOUSE_CENTER),
|
||||
new CName("Mouse Right", LibGPGX.INPUT_KEYS.INPUT_MOUSE_RIGHT),
|
||||
new CName("Mouse Start", LibGPGX.INPUT_KEYS.INPUT_MOUSE_START),
|
||||
};
|
||||
|
||||
static CName[] Lightgun =
|
||||
{
|
||||
new CName("Lightgun Trigger", LibGPGX.INPUT_KEYS.INPUT_MENACER_TRIGGER),
|
||||
new CName("Lightgun Start", LibGPGX.INPUT_KEYS.INPUT_MENACER_START),
|
||||
};
|
||||
|
||||
static CName[] Activator =
|
||||
{
|
||||
new CName("1L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1L),
|
||||
new CName("1U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_1U),
|
||||
new CName("2L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2L),
|
||||
new CName("2U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_2U),
|
||||
new CName("3L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3L),
|
||||
new CName("3U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_3U),
|
||||
new CName("4L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4L),
|
||||
new CName("4U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_4U),
|
||||
new CName("5L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5L),
|
||||
new CName("5U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_5U),
|
||||
new CName("6L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6L),
|
||||
new CName("6U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_6U),
|
||||
new CName("7L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7L),
|
||||
new CName("7U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_7U),
|
||||
new CName("8L", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8L),
|
||||
new CName("8U", LibGPGX.INPUT_KEYS.INPUT_ACTIVATOR_8U),
|
||||
};
|
||||
|
||||
static CName[] XEA1P =
|
||||
{
|
||||
new CName("XE A", LibGPGX.INPUT_KEYS.INPUT_XE_A),
|
||||
new CName("XE B", LibGPGX.INPUT_KEYS.INPUT_XE_B),
|
||||
new CName("XE C", LibGPGX.INPUT_KEYS.INPUT_XE_C),
|
||||
new CName("XE D", LibGPGX.INPUT_KEYS.INPUT_XE_D),
|
||||
new CName("XE Start", LibGPGX.INPUT_KEYS.INPUT_XE_START),
|
||||
new CName("XE Select", LibGPGX.INPUT_KEYS.INPUT_XE_SELECT),
|
||||
new CName("XE E1", LibGPGX.INPUT_KEYS.INPUT_XE_E1),
|
||||
new CName("XE E2", LibGPGX.INPUT_KEYS.INPUT_XE_E2),
|
||||
};
|
||||
|
||||
static ControllerDefinition.FloatRange MouseRange = new ControllerDefinition.FloatRange(-256, 0, 255);
|
||||
// lightgun needs to be transformed to match the current screen resolution
|
||||
static ControllerDefinition.FloatRange LightgunRange = new ControllerDefinition.FloatRange(0, 5000, 10000);
|
||||
|
||||
static ControllerDefinition.FloatRange XEA1PRange = new ControllerDefinition.FloatRange(-128, 0, 127);
|
||||
|
||||
LibGPGX.InputData target = null;
|
||||
IController source = null;
|
||||
|
||||
List<Action> Converts = new List<Action>();
|
||||
|
||||
public ControllerDefinition ControllerDef { get; private set; }
|
||||
|
||||
void AddToController(int idx, int player, IEnumerable<CName> Buttons)
|
||||
{
|
||||
foreach (var Button in Buttons)
|
||||
{
|
||||
string Name = string.Format("P{0} {1}", player, Button.Name);
|
||||
ControllerDef.BoolButtons.Add(Name);
|
||||
var ButtonFlag = Button.Key;
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
if (source.IsPressed(Name))
|
||||
target.pad[idx] |= ButtonFlag;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void DoMouseAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Mouse X", player);
|
||||
string NY = string.Format("P{0} Mouse Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
ControllerDef.FloatRanges.Add(MouseRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)source.GetFloat(NX);
|
||||
target.analog[(2 * idx) + 1] = (short)source.GetFloat(NY);
|
||||
});
|
||||
}
|
||||
|
||||
void DoLightgunAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Lightgun X", player);
|
||||
string NY = string.Format("P{0} Lightgun Y", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
ControllerDef.FloatRanges.Add(LightgunRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX) / 10000.0f * (ScreenWidth - 1));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY) / 10000.0f * (ScreenHeight - 1));
|
||||
});
|
||||
}
|
||||
|
||||
void DoXEA1PAnalog(int idx, int player)
|
||||
{
|
||||
string NX = string.Format("P{0} Stick X", player);
|
||||
string NY = string.Format("P{0} Stick Y", player);
|
||||
string NZ = string.Format("P{0} Stick Z", player);
|
||||
ControllerDef.FloatControls.Add(NX);
|
||||
ControllerDef.FloatControls.Add(NY);
|
||||
ControllerDef.FloatControls.Add(NZ);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
ControllerDef.FloatRanges.Add(XEA1PRange);
|
||||
Converts.Add(delegate()
|
||||
{
|
||||
target.analog[(2 * idx) + 0] = (short)(source.GetFloat(NX));
|
||||
target.analog[(2 * idx) + 1] = (short)(source.GetFloat(NY));
|
||||
// +2 is correct in how gpgx internally does this
|
||||
target.analog[(2 * idx) + 2] = (short)(source.GetFloat(NZ));
|
||||
});
|
||||
}
|
||||
|
||||
public GPGXControlConverter(LibGPGX.InputData input)
|
||||
{
|
||||
Console.WriteLine("Genesis Controller report:");
|
||||
foreach (var e in input.system)
|
||||
Console.WriteLine("S:{0}", e);
|
||||
foreach (var e in input.dev)
|
||||
Console.WriteLine("D:{0}", e);
|
||||
|
||||
int player = 1;
|
||||
|
||||
ControllerDef = new ControllerDefinition();
|
||||
|
||||
ControllerDef.BoolButtons.Add("Power");
|
||||
ControllerDef.BoolButtons.Add("Reset");
|
||||
|
||||
for (int i = 0; i < LibGPGX.MAX_DEVICES; i++)
|
||||
{
|
||||
switch (input.dev[i])
|
||||
{
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD3B:
|
||||
AddToController(i, player, Genesis3);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD6B:
|
||||
AddToController(i, player, Genesis6);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_MOUSE:
|
||||
AddToController(i, player, Mouse);
|
||||
DoMouseAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_NONE:
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_LIGHTGUN:
|
||||
// supports menacers and justifiers
|
||||
AddToController(i, player, Lightgun);
|
||||
DoLightgunAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PAD2B:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PADDLE:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_SPORTSPAD:
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_TEREBI:
|
||||
throw new Exception("Master System only device? Something went wrong.");
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_ACTIVATOR:
|
||||
AddToController(i, player, Activator);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_XE_A1P:
|
||||
AddToController(i, player, XEA1P);
|
||||
DoXEA1PAnalog(i, player);
|
||||
player++;
|
||||
break;
|
||||
case LibGPGX.INPUT_DEVICE.DEVICE_PICO:
|
||||
// PICO isn't finished on the unmanaged side either
|
||||
throw new Exception("Sega PICO not implemented yet!");
|
||||
default:
|
||||
throw new Exception("Unknown Genesis control device! Something went wrong.");
|
||||
}
|
||||
}
|
||||
|
||||
ControllerDef.Name = "GPGX Genesis Controller";
|
||||
}
|
||||
|
||||
public void Convert(IController source, LibGPGX.InputData target)
|
||||
{
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
target.ClearAllBools();
|
||||
foreach (var f in Converts)
|
||||
f();
|
||||
this.source = null;
|
||||
this.target = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenWidth { get; set; }
|
||||
/// <summary>
|
||||
/// must be set for proper lightgun operation
|
||||
/// </summary>
|
||||
public int ScreenHeight { get; set; }
|
||||
|
||||
}
|
||||
}
|
|
@ -1,214 +1,214 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
{
|
||||
/*
|
||||
* how to use:
|
||||
* 0) get https://code.google.com/p/pdbparse/
|
||||
* 1) set modulename to the name of the dll file.
|
||||
* 2) set symbolname to the name of a file that you produced by executing the following command:
|
||||
* pdb_print_gvars.py [module pdb file] 0x00000000 > [output file]
|
||||
* 3) set start to an address (relative to the beginning of the dll) to start scanning
|
||||
* 4) set length to the byte length of the scan area
|
||||
* 5) instantiate a GenDbWind, and use it to control the scanner while you manipulate the dll into various configurations.
|
||||
*
|
||||
* ideas for modification:
|
||||
* 1) unhardcode config parameters and allow modifying them through the interface
|
||||
* 2) read section sizes and positions from the dll itself instead of the start\length params
|
||||
* 3) support an ignore list of symbols
|
||||
*/
|
||||
|
||||
public class GenDbgHlp : IDisposable
|
||||
{
|
||||
private static class Win32
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
}
|
||||
|
||||
// config
|
||||
const string modulename = "libgenplusgx.dll";
|
||||
const string symbolname = @"D:\encodes\bizhawksrc\genplus-gx\libretro\msvc\Debug\vars.txt";
|
||||
const int start = 0x0c7d8000 - 0x0c540000;
|
||||
const int length = 0x01082000;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
Win32.FreeLibrary(DllBase);
|
||||
DllBase = IntPtr.Zero;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
IntPtr DllBase;
|
||||
|
||||
List<Symbol> SymbolsByAddr = new List<Symbol>();
|
||||
Dictionary<string, Symbol> SymbolsByName = new Dictionary<string, Symbol>();
|
||||
|
||||
byte[][] data = new byte[10][];
|
||||
|
||||
public void SaveState(int statenum)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
|
||||
if (data[statenum] == null)
|
||||
data[statenum] = new byte[length];
|
||||
|
||||
Marshal.Copy(DllBase + start, data[statenum], 0, length);
|
||||
Console.WriteLine("State {0} saved", statenum);
|
||||
}
|
||||
|
||||
|
||||
unsafe public void Cmp(int statex, int statey)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
|
||||
|
||||
byte[] x = data[statex];
|
||||
byte[] y = data[statey];
|
||||
|
||||
if (x == null || y == null)
|
||||
{
|
||||
Console.WriteLine("Missing State!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool inrange = false;
|
||||
int startsec = 0;
|
||||
|
||||
fixed (byte* p0 = &x[0])
|
||||
fixed (byte* p1 = &y[0])
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (!inrange)
|
||||
{
|
||||
if (p0[i] != p1[i])
|
||||
{
|
||||
startsec = i;
|
||||
inrange = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p0[i] == p1[i])
|
||||
{
|
||||
bads.Add(new Tuple<int, int>(startsec, i));
|
||||
inrange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inrange)
|
||||
bads.Add(new Tuple<int, int>(startsec, length));
|
||||
|
||||
for (int i = 0; i < bads.Count; i++)
|
||||
{
|
||||
IntPtr addr = (IntPtr)(bads[i].Item1 + start);
|
||||
int len = bads[i].Item2 - bads[i].Item1;
|
||||
|
||||
var ss = Find(addr, len);
|
||||
Console.WriteLine("0x{0:X8}[0x{1}]", (int)addr, len);
|
||||
foreach (var sym in ss)
|
||||
Console.WriteLine(sym);
|
||||
Console.WriteLine();
|
||||
}
|
||||
if (bads.Count == 0)
|
||||
Console.WriteLine("Clean!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public GenDbgHlp()
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(symbolname))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
Symbol sym = Symbol.FromString(line);
|
||||
SymbolsByAddr.Add(sym);
|
||||
SymbolsByName.Add(sym.name, sym);
|
||||
}
|
||||
SymbolsByAddr.Sort();
|
||||
}
|
||||
|
||||
DllBase = Win32.LoadLibrary(modulename);
|
||||
if (DllBase == IntPtr.Zero)
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
public List<Symbol> Find(IntPtr addr, int length)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
Symbol min = new Symbol { addr = addr };
|
||||
Symbol max = new Symbol { addr = addr + length };
|
||||
|
||||
int minidx = SymbolsByAddr.BinarySearch(min);
|
||||
if (minidx < 0)
|
||||
{
|
||||
minidx = ~minidx;
|
||||
// inexact matches return the first larger value, so find the next smallset one
|
||||
if (minidx > 0)
|
||||
minidx--;
|
||||
}
|
||||
int maxidx = SymbolsByAddr.BinarySearch(max);
|
||||
if (maxidx < 0)
|
||||
{
|
||||
maxidx = ~maxidx;
|
||||
if (maxidx > 0)
|
||||
maxidx--;
|
||||
}
|
||||
return SymbolsByAddr.GetRange(minidx, maxidx - minidx + 1);
|
||||
}
|
||||
|
||||
|
||||
public struct Symbol : IComparable<Symbol>
|
||||
{
|
||||
public IntPtr addr;
|
||||
public string section;
|
||||
public string name;
|
||||
|
||||
public static Symbol FromString(string s)
|
||||
{
|
||||
string[] ss = s.Split(',');
|
||||
if (ss.Length != 4)
|
||||
throw new Exception();
|
||||
if (!ss[1].StartsWith("0x"))
|
||||
throw new Exception();
|
||||
Symbol ret = new Symbol
|
||||
{
|
||||
addr = (IntPtr)int.Parse(ss[1].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier),
|
||||
section = ss[3],
|
||||
name = ss[0]
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int CompareTo(Symbol other)
|
||||
{
|
||||
return (int)this.addr - (int)other.addr;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("0x{0:X8} {1} ({2})", (int)addr, name, section);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
/*
|
||||
* how to use:
|
||||
* 0) get https://code.google.com/p/pdbparse/
|
||||
* 1) set modulename to the name of the dll file.
|
||||
* 2) set symbolname to the name of a file that you produced by executing the following command:
|
||||
* pdb_print_gvars.py [module pdb file] 0x00000000 > [output file]
|
||||
* 3) set start to an address (relative to the beginning of the dll) to start scanning
|
||||
* 4) set length to the byte length of the scan area
|
||||
* 5) instantiate a GenDbWind, and use it to control the scanner while you manipulate the dll into various configurations.
|
||||
*
|
||||
* ideas for modification:
|
||||
* 1) unhardcode config parameters and allow modifying them through the interface
|
||||
* 2) read section sizes and positions from the dll itself instead of the start\length params
|
||||
* 3) support an ignore list of symbols
|
||||
*/
|
||||
|
||||
public class GenDbgHlp : IDisposable
|
||||
{
|
||||
private static class Win32
|
||||
{
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr LoadLibrary(string dllToLoad);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool FreeLibrary(IntPtr hModule);
|
||||
}
|
||||
|
||||
// config
|
||||
const string modulename = "libgenplusgx.dll";
|
||||
const string symbolname = @"D:\encodes\bizhawksrc\genplus-gx\libretro\msvc\Debug\vars.txt";
|
||||
const int start = 0x0c7d8000 - 0x0c540000;
|
||||
const int length = 0x01082000;
|
||||
|
||||
bool disposed = false;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!disposed)
|
||||
{
|
||||
Win32.FreeLibrary(DllBase);
|
||||
DllBase = IntPtr.Zero;
|
||||
disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
IntPtr DllBase;
|
||||
|
||||
List<Symbol> SymbolsByAddr = new List<Symbol>();
|
||||
Dictionary<string, Symbol> SymbolsByName = new Dictionary<string, Symbol>();
|
||||
|
||||
byte[][] data = new byte[10][];
|
||||
|
||||
public void SaveState(int statenum)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
|
||||
if (data[statenum] == null)
|
||||
data[statenum] = new byte[length];
|
||||
|
||||
Marshal.Copy(DllBase + start, data[statenum], 0, length);
|
||||
Console.WriteLine("State {0} saved", statenum);
|
||||
}
|
||||
|
||||
|
||||
unsafe public void Cmp(int statex, int statey)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
List<Tuple<int, int>> bads = new List<Tuple<int, int>>();
|
||||
|
||||
byte[] x = data[statex];
|
||||
byte[] y = data[statey];
|
||||
|
||||
if (x == null || y == null)
|
||||
{
|
||||
Console.WriteLine("Missing State!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool inrange = false;
|
||||
int startsec = 0;
|
||||
|
||||
fixed (byte* p0 = &x[0])
|
||||
fixed (byte* p1 = &y[0])
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
if (!inrange)
|
||||
{
|
||||
if (p0[i] != p1[i])
|
||||
{
|
||||
startsec = i;
|
||||
inrange = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (p0[i] == p1[i])
|
||||
{
|
||||
bads.Add(new Tuple<int, int>(startsec, i));
|
||||
inrange = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inrange)
|
||||
bads.Add(new Tuple<int, int>(startsec, length));
|
||||
|
||||
for (int i = 0; i < bads.Count; i++)
|
||||
{
|
||||
IntPtr addr = (IntPtr)(bads[i].Item1 + start);
|
||||
int len = bads[i].Item2 - bads[i].Item1;
|
||||
|
||||
var ss = Find(addr, len);
|
||||
Console.WriteLine("0x{0:X8}[0x{1}]", (int)addr, len);
|
||||
foreach (var sym in ss)
|
||||
Console.WriteLine(sym);
|
||||
Console.WriteLine();
|
||||
}
|
||||
if (bads.Count == 0)
|
||||
Console.WriteLine("Clean!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
public GenDbgHlp()
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(symbolname))
|
||||
{
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
Symbol sym = Symbol.FromString(line);
|
||||
SymbolsByAddr.Add(sym);
|
||||
SymbolsByName.Add(sym.name, sym);
|
||||
}
|
||||
SymbolsByAddr.Sort();
|
||||
}
|
||||
|
||||
DllBase = Win32.LoadLibrary(modulename);
|
||||
if (DllBase == IntPtr.Zero)
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
public List<Symbol> Find(IntPtr addr, int length)
|
||||
{
|
||||
if (disposed) throw new ObjectDisposedException(this.GetType().ToString());
|
||||
Symbol min = new Symbol { addr = addr };
|
||||
Symbol max = new Symbol { addr = addr + length };
|
||||
|
||||
int minidx = SymbolsByAddr.BinarySearch(min);
|
||||
if (minidx < 0)
|
||||
{
|
||||
minidx = ~minidx;
|
||||
// inexact matches return the first larger value, so find the next smallset one
|
||||
if (minidx > 0)
|
||||
minidx--;
|
||||
}
|
||||
int maxidx = SymbolsByAddr.BinarySearch(max);
|
||||
if (maxidx < 0)
|
||||
{
|
||||
maxidx = ~maxidx;
|
||||
if (maxidx > 0)
|
||||
maxidx--;
|
||||
}
|
||||
return SymbolsByAddr.GetRange(minidx, maxidx - minidx + 1);
|
||||
}
|
||||
|
||||
|
||||
public struct Symbol : IComparable<Symbol>
|
||||
{
|
||||
public IntPtr addr;
|
||||
public string section;
|
||||
public string name;
|
||||
|
||||
public static Symbol FromString(string s)
|
||||
{
|
||||
string[] ss = s.Split(',');
|
||||
if (ss.Length != 4)
|
||||
throw new Exception();
|
||||
if (!ss[1].StartsWith("0x"))
|
||||
throw new Exception();
|
||||
Symbol ret = new Symbol
|
||||
{
|
||||
addr = (IntPtr)int.Parse(ss[1].Substring(2), System.Globalization.NumberStyles.AllowHexSpecifier),
|
||||
section = ss[3],
|
||||
name = ss[0]
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int CompareTo(Symbol other)
|
||||
{
|
||||
return (int)this.addr - (int)other.addr;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("0x{0:X8} {1} ({2})", (int)addr, name, section);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -1,355 +1,354 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx32
|
||||
{
|
||||
public static class LibGPGX
|
||||
{
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_video(out int w, out int h, out int pitch, ref IntPtr buffer);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_audio(ref int n, ref IntPtr buffer);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_advance();
|
||||
|
||||
public enum Region : int
|
||||
{
|
||||
Autodetect = 0,
|
||||
USA = 1,
|
||||
Europe = 2,
|
||||
Japan_NTSC = 3,
|
||||
Japan_PAL = 4
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InitSettings
|
||||
{
|
||||
public enum FilterType : byte
|
||||
{
|
||||
None = 0,
|
||||
LowPass = 1,
|
||||
ThreeBand = 2
|
||||
}
|
||||
public FilterType Filter;
|
||||
public ushort LowPassRange;
|
||||
public short LowFreq;
|
||||
public short HighFreq;
|
||||
public short LowGain;
|
||||
public short MidGain;
|
||||
public short HighGain;
|
||||
public uint BackdropColor;
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_fps(ref int num, ref int den);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_state_max_size();
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_state_size(byte[] dest, int size);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_state_save(byte[] dest, int size);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_state_load(byte[] src, int size);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_get_control([Out]InputData dest, int bytes);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern bool gpgx_put_control([In]InputData src, int bytes);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_sram(ref IntPtr area, ref int size);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_clear_sram();
|
||||
|
||||
public const int MIN_MEM_DOMAIN = 0;
|
||||
public const int MAX_MEM_DOMAIN = 13;
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
// apparently, if you use built in string marshalling, the interop will assume that
|
||||
// the unmanaged char pointer was allocated in hglobal and try to free it that way
|
||||
public static extern IntPtr gpgx_get_memdom(int which, ref IntPtr area, ref int size);
|
||||
|
||||
// call this before reading sram returned by gpgx_get_sram()
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_sram_prepread();
|
||||
|
||||
// call this after writing sram returned by gpgx_get_sram()
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_sram_commitwrite();
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_reset(bool hard);
|
||||
|
||||
public const int MAX_DEVICES = 8;
|
||||
|
||||
public enum INPUT_SYSTEM : byte
|
||||
{
|
||||
SYSTEM_NONE = 0, // unconnected port
|
||||
SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad
|
||||
SYSTEM_MOUSE = 2, // Sega Mouse
|
||||
SYSTEM_MENACER = 3, // Sega Menacer -- port B only
|
||||
SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only
|
||||
SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only
|
||||
SYSTEM_ACTIVATOR = 6, // Sega Activator
|
||||
SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System
|
||||
SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System
|
||||
SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System
|
||||
SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System
|
||||
SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer
|
||||
SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports
|
||||
};
|
||||
|
||||
public enum INPUT_DEVICE : byte
|
||||
{
|
||||
DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player)
|
||||
DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD2B = 0x02, // 2-buttons Control Pad
|
||||
DEVICE_MOUSE = 0x03, // Sega Mouse
|
||||
DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers
|
||||
DEVICE_PADDLE = 0x05, // Sega Paddle Control
|
||||
DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad
|
||||
DEVICE_PICO = 0x07, // PICO tablet
|
||||
DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet
|
||||
DEVICE_XE_A1P = 0x09, // XE-A1P analog controller
|
||||
DEVICE_ACTIVATOR = 0x0a,// Activator
|
||||
};
|
||||
|
||||
|
||||
public enum CDLog_AddrType
|
||||
{
|
||||
MDCART, RAM68k, RAMZ80, SRAM,
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum CDLog_Flags
|
||||
{
|
||||
Exec68k = 0x01,
|
||||
Data68k = 0x04,
|
||||
ExecZ80First = 0x08,
|
||||
ExecZ80Operand = 0x10,
|
||||
DataZ80 = 0x20,
|
||||
DMASource = 0x40,
|
||||
};
|
||||
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void input_cb();
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_input_callback(input_cb cb);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void mem_cb(uint addr);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void CDCallback(int addr, CDLog_AddrType addrtype, CDLog_Flags flags);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_mem_callback(mem_cb read, mem_cb write, mem_cb exec);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_cd_callback(CDCallback cd);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// not every flag is valid for every device!
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum INPUT_KEYS : ushort
|
||||
{
|
||||
/* Default Input bitmasks */
|
||||
INPUT_MODE = 0x0800,
|
||||
INPUT_X = 0x0400,
|
||||
INPUT_Y = 0x0200,
|
||||
INPUT_Z = 0x0100,
|
||||
INPUT_START = 0x0080,
|
||||
INPUT_A = 0x0040,
|
||||
INPUT_C = 0x0020,
|
||||
INPUT_B = 0x0010,
|
||||
INPUT_RIGHT = 0x0008,
|
||||
INPUT_LEFT = 0x0004,
|
||||
INPUT_DOWN = 0x0002,
|
||||
INPUT_UP = 0x0001,
|
||||
|
||||
/* Master System specific bitmasks */
|
||||
INPUT_BUTTON2 = 0x0020,
|
||||
INPUT_BUTTON1 = 0x0010,
|
||||
|
||||
/* Mega Mouse specific bitmask */
|
||||
INPUT_MOUSE_START = 0x0080,
|
||||
INPUT_MOUSE_CENTER = 0x0040,
|
||||
INPUT_MOUSE_RIGHT = 0x0020,
|
||||
INPUT_MOUSE_LEFT = 0x0010,
|
||||
|
||||
/* Pico hardware specific bitmask */
|
||||
INPUT_PICO_PEN = 0x0080,
|
||||
INPUT_PICO_RED = 0x0010,
|
||||
|
||||
/* XE-1AP specific bitmask */
|
||||
INPUT_XE_E1 = 0x0800,
|
||||
INPUT_XE_E2 = 0x0400,
|
||||
INPUT_XE_START = 0x0200,
|
||||
INPUT_XE_SELECT = 0x0100,
|
||||
INPUT_XE_A = 0x0080,
|
||||
INPUT_XE_B = 0x0040,
|
||||
INPUT_XE_C = 0x0020,
|
||||
INPUT_XE_D = 0x0010,
|
||||
|
||||
/* Activator specific bitmasks */
|
||||
INPUT_ACTIVATOR_8U = 0x8000,
|
||||
INPUT_ACTIVATOR_8L = 0x4000,
|
||||
INPUT_ACTIVATOR_7U = 0x2000,
|
||||
INPUT_ACTIVATOR_7L = 0x1000,
|
||||
INPUT_ACTIVATOR_6U = 0x0800,
|
||||
INPUT_ACTIVATOR_6L = 0x0400,
|
||||
INPUT_ACTIVATOR_5U = 0x0200,
|
||||
INPUT_ACTIVATOR_5L = 0x0100,
|
||||
INPUT_ACTIVATOR_4U = 0x0080,
|
||||
INPUT_ACTIVATOR_4L = 0x0040,
|
||||
INPUT_ACTIVATOR_3U = 0x0020,
|
||||
INPUT_ACTIVATOR_3L = 0x0010,
|
||||
INPUT_ACTIVATOR_2U = 0x0008,
|
||||
INPUT_ACTIVATOR_2L = 0x0004,
|
||||
INPUT_ACTIVATOR_1U = 0x0002,
|
||||
INPUT_ACTIVATOR_1L = 0x0001,
|
||||
|
||||
/* Menacer */
|
||||
INPUT_MENACER_TRIGGER = 0x0040,
|
||||
INPUT_MENACER_START = 0x0080,
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InputData
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// digital inputs
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// analog (x/y)
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
|
||||
public readonly short[] analog = new short[MAX_DEVICES * 2];
|
||||
/// <summary>
|
||||
/// gun horizontal offset
|
||||
/// </summary>
|
||||
public int x_offset;
|
||||
/// <summary>
|
||||
/// gun vertical offset
|
||||
/// </summary>
|
||||
public int y_offset;
|
||||
|
||||
public void ClearAllBools()
|
||||
{
|
||||
for (int i = 0; i < pad.Length; i++)
|
||||
pad[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public const int CD_MAX_TRACKS = 100;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void cd_read_cb(int lba, IntPtr dest, bool audio);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CDTrack
|
||||
{
|
||||
public int start;
|
||||
public int end;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class CDData
|
||||
{
|
||||
public int end;
|
||||
public int last;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CD_MAX_TRACKS)]
|
||||
public readonly CDTrack[] tracks = new CDTrack[CD_MAX_TRACKS];
|
||||
public cd_read_cb readcallback;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct VDPNameTable
|
||||
{
|
||||
public int Width; // in cells
|
||||
public int Height; // in cells
|
||||
public int Baseaddr;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class VDPView
|
||||
{
|
||||
public IntPtr VRAM;
|
||||
public IntPtr PatternCache;
|
||||
public IntPtr ColorCache;
|
||||
public VDPNameTable NTA;
|
||||
public VDPNameTable NTB;
|
||||
public VDPNameTable NTW;
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_get_vdp_view([Out] VDPView view);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_poke_vram(int addr, byte value);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_flush_vram();
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RegisterInfo
|
||||
{
|
||||
public int Value;
|
||||
public IntPtr Name;
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_getmaxnumregs();
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern int gpgx_getregs([Out] RegisterInfo[] regs);
|
||||
|
||||
[Flags]
|
||||
public enum DrawMask : int
|
||||
{
|
||||
BGA = 1,
|
||||
BGB = 2,
|
||||
BGW = 4,
|
||||
Obj = 8,
|
||||
Backdrop = 16
|
||||
}
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_set_draw_mask(DrawMask mask);
|
||||
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_write_m68k_bus(uint addr, byte data);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void gpgx_write_s68k_bus(uint addr, byte data);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern byte gpgx_peek_m68k_bus(uint addr);
|
||||
[DllImport("libgenplusgx.dll", CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern byte gpgx_peek_s68k_bus(uint addr);
|
||||
|
||||
}
|
||||
}
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.InteropServices;
|
||||
using BizHawk.Emulation.Common.BizInvoke;
|
||||
|
||||
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
|
||||
{
|
||||
public abstract class LibGPGX
|
||||
{
|
||||
public const string DllName = "libgenplusgx.dll";
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_get_video(out int w, out int h, out int pitch, ref IntPtr buffer);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_get_audio(ref int n, ref IntPtr buffer);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int load_archive_cb(string filename, IntPtr buffer, int maxsize);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_advance();
|
||||
|
||||
public enum Region : int
|
||||
{
|
||||
Autodetect = 0,
|
||||
USA = 1,
|
||||
Europe = 2,
|
||||
Japan_NTSC = 3,
|
||||
Japan_PAL = 4
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InitSettings
|
||||
{
|
||||
public enum FilterType : byte
|
||||
{
|
||||
None = 0,
|
||||
LowPass = 1,
|
||||
ThreeBand = 2
|
||||
}
|
||||
public FilterType Filter;
|
||||
public ushort LowPassRange;
|
||||
public short LowFreq;
|
||||
public short HighFreq;
|
||||
public short LowGain;
|
||||
public short MidGain;
|
||||
public short HighGain;
|
||||
public uint BackdropColor;
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility=true)]
|
||||
public abstract bool gpgx_init(string feromextension, load_archive_cb feload_archive_cb, bool sixbutton, INPUT_SYSTEM system_a, INPUT_SYSTEM system_b, Region region, [In]InitSettings settings);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_get_fps(ref int num, ref int den);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract bool gpgx_get_control([Out]InputData dest, int bytes);
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract bool gpgx_put_control([In]InputData src, int bytes);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract IntPtr gpgx_get_sram(ref int size);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract bool gpgx_put_sram(byte[] data, int size);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_clear_sram();
|
||||
|
||||
public const int MIN_MEM_DOMAIN = 0;
|
||||
public const int MAX_MEM_DOMAIN = 13;
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
// apparently, if you use built in string marshalling, the interop will assume that
|
||||
// the unmanaged char pointer was allocated in hglobal and try to free it that way
|
||||
public abstract IntPtr gpgx_get_memdom(int which, ref IntPtr area, ref int size);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_reset(bool hard);
|
||||
|
||||
public const int MAX_DEVICES = 8;
|
||||
|
||||
public enum INPUT_SYSTEM : byte
|
||||
{
|
||||
SYSTEM_NONE = 0, // unconnected port
|
||||
SYSTEM_MD_GAMEPAD = 1, // single 3-buttons or 6-buttons Control Pad
|
||||
SYSTEM_MOUSE = 2, // Sega Mouse
|
||||
SYSTEM_MENACER = 3, // Sega Menacer -- port B only
|
||||
SYSTEM_JUSTIFIER = 4, // Konami Justifiers -- port B only
|
||||
SYSTEM_XE_A1P = 5, // XE-A1P analog controller -- port A only
|
||||
SYSTEM_ACTIVATOR = 6, // Sega Activator
|
||||
SYSTEM_MS_GAMEPAD = 7, // single 2-buttons Control Pad -- Master System
|
||||
SYSTEM_LIGHTPHASER = 8, // Sega Light Phaser -- Master System
|
||||
SYSTEM_PADDLE = 9, // Sega Paddle Control -- Master System
|
||||
SYSTEM_SPORTSPAD = 10, // Sega Sports Pad -- Master System
|
||||
SYSTEM_TEAMPLAYER = 11, // Multi Tap -- Sega TeamPlayer
|
||||
SYSTEM_WAYPLAY = 12, // Multi Tap -- EA 4-Way Play -- use both ports
|
||||
};
|
||||
|
||||
public enum INPUT_DEVICE : byte
|
||||
{
|
||||
DEVICE_NONE = 0xff, // unconnected device = fixed ID for Team Player)
|
||||
DEVICE_PAD3B = 0x00, // 3-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD6B = 0x01, // 6-buttons Control Pad = fixed ID for Team Player)
|
||||
DEVICE_PAD2B = 0x02, // 2-buttons Control Pad
|
||||
DEVICE_MOUSE = 0x03, // Sega Mouse
|
||||
DEVICE_LIGHTGUN = 0x04, // Sega Light Phaser, Menacer or Konami Justifiers
|
||||
DEVICE_PADDLE = 0x05, // Sega Paddle Control
|
||||
DEVICE_SPORTSPAD = 0x06,// Sega Sports Pad
|
||||
DEVICE_PICO = 0x07, // PICO tablet
|
||||
DEVICE_TEREBI = 0x08, // Terebi Oekaki tablet
|
||||
DEVICE_XE_A1P = 0x09, // XE-A1P analog controller
|
||||
DEVICE_ACTIVATOR = 0x0a,// Activator
|
||||
};
|
||||
|
||||
|
||||
public enum CDLog_AddrType
|
||||
{
|
||||
MDCART, RAM68k, RAMZ80, SRAM,
|
||||
};
|
||||
|
||||
[Flags]
|
||||
public enum CDLog_Flags
|
||||
{
|
||||
Exec68k = 0x01,
|
||||
Data68k = 0x04,
|
||||
ExecZ80First = 0x08,
|
||||
ExecZ80Operand = 0x10,
|
||||
DataZ80 = 0x20,
|
||||
DMASource = 0x40,
|
||||
};
|
||||
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void input_cb();
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_input_callback(input_cb cb);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void mem_cb(uint addr);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void CDCallback(int addr, CDLog_AddrType addrtype, CDLog_Flags flags);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_mem_callback(mem_cb read, mem_cb write, mem_cb exec);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_cd_callback(CDCallback cd);
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// not every flag is valid for every device!
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum INPUT_KEYS : ushort
|
||||
{
|
||||
/* Default Input bitmasks */
|
||||
INPUT_MODE = 0x0800,
|
||||
INPUT_X = 0x0400,
|
||||
INPUT_Y = 0x0200,
|
||||
INPUT_Z = 0x0100,
|
||||
INPUT_START = 0x0080,
|
||||
INPUT_A = 0x0040,
|
||||
INPUT_C = 0x0020,
|
||||
INPUT_B = 0x0010,
|
||||
INPUT_RIGHT = 0x0008,
|
||||
INPUT_LEFT = 0x0004,
|
||||
INPUT_DOWN = 0x0002,
|
||||
INPUT_UP = 0x0001,
|
||||
|
||||
/* Master System specific bitmasks */
|
||||
INPUT_BUTTON2 = 0x0020,
|
||||
INPUT_BUTTON1 = 0x0010,
|
||||
|
||||
/* Mega Mouse specific bitmask */
|
||||
INPUT_MOUSE_START = 0x0080,
|
||||
INPUT_MOUSE_CENTER = 0x0040,
|
||||
INPUT_MOUSE_RIGHT = 0x0020,
|
||||
INPUT_MOUSE_LEFT = 0x0010,
|
||||
|
||||
/* Pico hardware specific bitmask */
|
||||
INPUT_PICO_PEN = 0x0080,
|
||||
INPUT_PICO_RED = 0x0010,
|
||||
|
||||
/* XE-1AP specific bitmask */
|
||||
INPUT_XE_E1 = 0x0800,
|
||||
INPUT_XE_E2 = 0x0400,
|
||||
INPUT_XE_START = 0x0200,
|
||||
INPUT_XE_SELECT = 0x0100,
|
||||
INPUT_XE_A = 0x0080,
|
||||
INPUT_XE_B = 0x0040,
|
||||
INPUT_XE_C = 0x0020,
|
||||
INPUT_XE_D = 0x0010,
|
||||
|
||||
/* Activator specific bitmasks */
|
||||
INPUT_ACTIVATOR_8U = 0x8000,
|
||||
INPUT_ACTIVATOR_8L = 0x4000,
|
||||
INPUT_ACTIVATOR_7U = 0x2000,
|
||||
INPUT_ACTIVATOR_7L = 0x1000,
|
||||
INPUT_ACTIVATOR_6U = 0x0800,
|
||||
INPUT_ACTIVATOR_6L = 0x0400,
|
||||
INPUT_ACTIVATOR_5U = 0x0200,
|
||||
INPUT_ACTIVATOR_5L = 0x0100,
|
||||
INPUT_ACTIVATOR_4U = 0x0080,
|
||||
INPUT_ACTIVATOR_4L = 0x0040,
|
||||
INPUT_ACTIVATOR_3U = 0x0020,
|
||||
INPUT_ACTIVATOR_3L = 0x0010,
|
||||
INPUT_ACTIVATOR_2U = 0x0008,
|
||||
INPUT_ACTIVATOR_2L = 0x0004,
|
||||
INPUT_ACTIVATOR_1U = 0x0002,
|
||||
INPUT_ACTIVATOR_1L = 0x0001,
|
||||
|
||||
/* Menacer */
|
||||
INPUT_MENACER_TRIGGER = 0x0040,
|
||||
INPUT_MENACER_START = 0x0080,
|
||||
};
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class InputData
|
||||
{
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
|
||||
public readonly INPUT_SYSTEM[] system = new INPUT_SYSTEM[2];
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_DEVICE[] dev = new INPUT_DEVICE[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// digital inputs
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)]
|
||||
public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES];
|
||||
/// <summary>
|
||||
/// analog (x/y)
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)]
|
||||
public readonly short[] analog = new short[MAX_DEVICES * 2];
|
||||
/// <summary>
|
||||
/// gun horizontal offset
|
||||
/// </summary>
|
||||
public int x_offset;
|
||||
/// <summary>
|
||||
/// gun vertical offset
|
||||
/// </summary>
|
||||
public int y_offset;
|
||||
|
||||
public void ClearAllBools()
|
||||
{
|
||||
for (int i = 0; i < pad.Length; i++)
|
||||
pad[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public const int CD_MAX_TRACKS = 100;
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void cd_read_cb(int lba, IntPtr dest, bool audio);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct CDTrack
|
||||
{
|
||||
public int start;
|
||||
public int end;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class CDData
|
||||
{
|
||||
public int end;
|
||||
public int last;
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CD_MAX_TRACKS)]
|
||||
public readonly CDTrack[] tracks = new CDTrack[CD_MAX_TRACKS];
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_cdd_callback(cd_read_cb cddcb);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct VDPNameTable
|
||||
{
|
||||
public int Width; // in cells
|
||||
public int Height; // in cells
|
||||
public int Baseaddr;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class VDPView
|
||||
{
|
||||
public IntPtr VRAM;
|
||||
public IntPtr PatternCache;
|
||||
public IntPtr ColorCache;
|
||||
public VDPNameTable NTA;
|
||||
public VDPNameTable NTB;
|
||||
public VDPNameTable NTW;
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract void gpgx_get_vdp_view([Out] VDPView view);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_poke_vram(int addr, byte value);
|
||||
|
||||
/// <summary>
|
||||
/// regenerate whatever portions of the bg pattern cache are currently dirty.
|
||||
/// </summary>
|
||||
[BizImport(CallingConvention.Cdecl)] // the core will handle this itself; you only need to call this when using the cache for your own purposes
|
||||
public abstract void gpgx_flush_vram();
|
||||
|
||||
/// <summary>
|
||||
/// mark the bg pattern cache as dirty
|
||||
/// </summary>
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_invalidate_pattern_cache();
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RegisterInfo
|
||||
{
|
||||
public int Value;
|
||||
public IntPtr Name;
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract int gpgx_getmaxnumregs();
|
||||
|
||||
[BizImport(CallingConvention.Cdecl, Compatibility = true)]
|
||||
public abstract int gpgx_getregs([Out] RegisterInfo[] regs);
|
||||
|
||||
[Flags]
|
||||
public enum DrawMask : int
|
||||
{
|
||||
BGA = 1,
|
||||
BGB = 2,
|
||||
BGW = 4,
|
||||
Obj = 8,
|
||||
Backdrop = 16
|
||||
}
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_set_draw_mask(DrawMask mask);
|
||||
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_write_m68k_bus(uint addr, byte data);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract void gpgx_write_s68k_bus(uint addr, byte data);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract byte gpgx_peek_m68k_bus(uint addr);
|
||||
[BizImport(CallingConvention.Cdecl)]
|
||||
public abstract byte gpgx_peek_s68k_bus(uint addr);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue