diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 26c25a40c4..83763ccf7d 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -802,6 +802,10 @@ namespace BizHawk.Client.Common nextEmulator = new Octoshock(nextComm, null, null, rom.FileData, GetCoreSettings(), GetCoreSyncSettings()); 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) diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 25a58fb886..1d68950eea 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -838,46 +838,6 @@ - - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - GPGX.cs - - - @@ -918,6 +878,46 @@ + + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + GPGX.cs + + + diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ICodeDataLogger.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ICodeDataLogger.cs index 13e0849388..63d4cfe903 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ICodeDataLogger.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ICodeDataLogger.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) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs index 6c34b8c53f..671d2ff774 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs @@ -10,9 +10,9 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { public IDictionary 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(); @@ -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); } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs index 5287dc059e..a61ab25a4f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs @@ -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; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs index 31343e1db7..7bb4b11188 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs @@ -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) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs index 352d702170..747c798df6 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs @@ -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; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISettable.cs index c44199e244..e5ce32ed4c 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISettable.cs @@ -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; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs index 9ec418d7a7..da6febc406 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs @@ -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; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IVideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IVideoProvider.cs index 024be21c56..d60ff246dd 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IVideoProvider.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IVideoProvider.cs @@ -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; diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs index de0dc9b31b..ece6cb5a6e 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs @@ -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(Elf, Elf); - else - Core = BizInvoker.GetInvoker(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(Tracer); - - Elf.Seal(); - } - catch - { - Dispose(); - throw; - } - } - - /// - /// core callback for file loading - /// - /// string identifying file to be loaded - /// buffer to load file to - /// maximum length buffer can hold - /// actual size loaded, or 0 on failure - 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; - } - - /// - /// size of native input struct - /// - 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(Tracer); + } + catch + { + Dispose(); + throw; + } + } + + /// + /// core callback for file loading + /// + /// string identifying file to be loaded + /// buffer to load file to + /// maximum length buffer can hold + /// actual size loaded, or 0 on failure + 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; + } + + /// + /// size of native input struct + /// + 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; } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGXControlConverter.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGXControlConverter.cs index f1af5af62f..b61951db55 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGXControlConverter.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGXControlConverter.cs @@ -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 Converts = new List(); - - public ControllerDefinition ControllerDef { get; private set; } - - void AddToController(int idx, int player, IEnumerable 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; - } - - /// - /// must be set for proper lightgun operation - /// - public int ScreenWidth { get; set; } - /// - /// must be set for proper lightgun operation - /// - 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 Converts = new List(); + + public ControllerDefinition ControllerDef { get; private set; } + + void AddToController(int idx, int player, IEnumerable 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; + } + + /// + /// must be set for proper lightgun operation + /// + public int ScreenWidth { get; set; } + /// + /// must be set for proper lightgun operation + /// + public int ScreenHeight { get; set; } + + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GenDbgHlp.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GenDbgHlp.cs index 39487cfc7d..ae12e14b05 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GenDbgHlp.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GenDbgHlp.cs @@ -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 SymbolsByAddr = new List(); - Dictionary SymbolsByName = new Dictionary(); - - 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> bads = new List>(); - - 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(startsec, i)); - inrange = false; - } - } - } - } - if (inrange) - bads.Add(new Tuple(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 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 - { - 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 SymbolsByAddr = new List(); + Dictionary SymbolsByName = new Dictionary(); + + 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> bads = new List>(); + + 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(startsec, i)); + inrange = false; + } + } + } + } + if (inrange) + bads.Add(new Tuple(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 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 + { + 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); + } + } + + + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/LibGPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/LibGPGX.cs index d47a89eefe..9d5156c7b1 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/LibGPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/LibGPGX.cs @@ -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); - - - - /// - /// not every flag is valid for every device! - /// - [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]; - /// - /// digital inputs - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)] - public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES]; - /// - /// analog (x/y) - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)] - public readonly short[] analog = new short[MAX_DEVICES * 2]; - /// - /// gun horizontal offset - /// - public int x_offset; - /// - /// gun vertical offset - /// - 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); - - /// - /// regenerate whatever portions of the bg pattern cache are currently dirty. - /// - [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(); - - /// - /// mark the bg pattern cache as dirty - /// - [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); + + + + /// + /// not every flag is valid for every device! + /// + [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]; + /// + /// digital inputs + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)] + public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES]; + /// + /// analog (x/y) + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)] + public readonly short[] analog = new short[MAX_DEVICES * 2]; + /// + /// gun horizontal offset + /// + public int x_offset; + /// + /// gun vertical offset + /// + 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); + + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ISaveRam.cs deleted file mode 100644 index 29fb65e688..0000000000 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ISaveRam.cs +++ /dev/null @@ -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; - } -} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ICodeDataLogger.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ICodeDataLogger.cs similarity index 89% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ICodeDataLogger.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ICodeDataLogger.cs index 7c7a9ed1c4..0d144b94cd 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ICodeDataLogger.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ICodeDataLogger.cs @@ -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) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs similarity index 87% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDebuggable.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs index 077151bcc7..f945376111 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDebuggable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDebuggable.cs @@ -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 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(); @@ -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); } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDisassembler.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDisassembler.cs similarity index 95% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDisassembler.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDisassembler.cs index 473d033574..81e529e950 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDisassembler.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDisassembler.cs @@ -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 { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDriveLight.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDriveLight.cs similarity index 79% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDriveLight.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDriveLight.cs index e1d75c149b..fb62c2feb5 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IDriveLight.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IDriveLight.cs @@ -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 { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs similarity index 74% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IEmulator.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs index 9fb2ac0c05..dc927d14ef 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IEmulator.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IEmulator.cs @@ -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; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IInputPollable.cs similarity index 89% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IInputPollable.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IInputPollable.cs index b86379c443..b1d4e36496 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IInputPollable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IInputPollable.cs @@ -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 { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IMemoryDomains.cs similarity index 82% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IMemoryDomains.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IMemoryDomains.cs index 219d91abf1..c6b3ce55ee 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IMemoryDomains.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IMemoryDomains.cs @@ -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) diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs new file mode 100644 index 0000000000..2f84bf6039 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISaveRam.cs @@ -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; + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs similarity index 96% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ISettable.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs index 9abfe62a5d..ef01be0b9b 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ISettable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ISettable.cs @@ -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 { @@ -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; } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IStatable.cs similarity index 62% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IStatable.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IStatable.cs index 27e76ba578..24a1e1fe96 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IStatable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IStatable.cs @@ -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() + { + } } } diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ITraceable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ITraceable.cs similarity index 97% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ITraceable.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ITraceable.cs index bb9105f7d0..2bff6f65fa 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.ITraceable.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.ITraceable.cs @@ -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 { diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IVideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IVideoProvider.cs similarity index 93% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IVideoProvider.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IVideoProvider.cs index fbf9f27b95..ee12610d1f 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.IVideoProvider.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.IVideoProvider.cs @@ -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; diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs similarity index 85% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs index ebacfc21be..9cbb7ac2f2 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGX.cs @@ -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(Tracer); - } - catch - { - Dispose(); - throw; - } - } - - /// - /// core callback for file loading - /// - /// string identifying file to be loaded - /// buffer to load file to - /// maximum length buffer can hold - /// actual size loaded, or 0 on failure - 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; - } - - /// - /// size of native input struct - /// - 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(Elf, Elf); + else + Core = BizInvoker.GetInvoker(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(Tracer); + + Elf.Seal(); + } + catch + { + Dispose(); + throw; + } + } + + /// + /// core callback for file loading + /// + /// string identifying file to be loaded + /// buffer to load file to + /// maximum length buffer can hold + /// actual size loaded, or 0 on failure + 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; + } + + /// + /// size of native input struct + /// + 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; } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGXControlConverter.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGXControlConverter.cs similarity index 96% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGXControlConverter.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGXControlConverter.cs index 96c3e13ea8..4fa98126e3 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GPGXControlConverter.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GPGXControlConverter.cs @@ -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 Converts = new List(); - - public ControllerDefinition ControllerDef { get; private set; } - - void AddToController(int idx, int player, IEnumerable 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; - } - - /// - /// must be set for proper lightgun operation - /// - public int ScreenWidth { get; set; } - /// - /// must be set for proper lightgun operation - /// - 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 Converts = new List(); + + public ControllerDefinition ControllerDef { get; private set; } + + void AddToController(int idx, int player, IEnumerable 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; + } + + /// + /// must be set for proper lightgun operation + /// + public int ScreenWidth { get; set; } + /// + /// must be set for proper lightgun operation + /// + public int ScreenHeight { get; set; } + + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GenDbgHlp.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GenDbgHlp.cs similarity index 95% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GenDbgHlp.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GenDbgHlp.cs index 5d47bcb299..7bd1bb7abf 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/GenDbgHlp.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/GenDbgHlp.cs @@ -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 SymbolsByAddr = new List(); - Dictionary SymbolsByName = new Dictionary(); - - 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> bads = new List>(); - - 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(startsec, i)); - inrange = false; - } - } - } - } - if (inrange) - bads.Add(new Tuple(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 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 - { - 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 SymbolsByAddr = new List(); + Dictionary SymbolsByName = new Dictionary(); + + 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> bads = new List>(); + + 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(startsec, i)); + inrange = false; + } + } + } + } + if (inrange) + bads.Add(new Tuple(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 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 + { + 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); + } + } + + + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/LibGPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/LibGPGX.cs similarity index 59% rename from BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/LibGPGX.cs rename to BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/LibGPGX.cs index eff3a44385..b970d8ca08 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx32/LibGPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx64/LibGPGX.cs @@ -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); - - - - /// - /// not every flag is valid for every device! - /// - [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]; - /// - /// digital inputs - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)] - public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES]; - /// - /// analog (x/y) - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)] - public readonly short[] analog = new short[MAX_DEVICES * 2]; - /// - /// gun horizontal offset - /// - public int x_offset; - /// - /// gun vertical offset - /// - 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); + + + + /// + /// not every flag is valid for every device! + /// + [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]; + /// + /// digital inputs + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES)] + public readonly INPUT_KEYS[] pad = new INPUT_KEYS[MAX_DEVICES]; + /// + /// analog (x/y) + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_DEVICES * 2)] + public readonly short[] analog = new short[MAX_DEVICES * 2]; + /// + /// gun horizontal offset + /// + public int x_offset; + /// + /// gun vertical offset + /// + 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); + + /// + /// regenerate whatever portions of the bg pattern cache are currently dirty. + /// + [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(); + + /// + /// mark the bg pattern cache as dirty + /// + [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); + } +}