From e9d4a4448e692f0b871acbf73f10d28ce5c10ebd Mon Sep 17 00:00:00 2001 From: adelikat Date: Sun, 21 Feb 2016 09:08:41 -0500 Subject: [PATCH] GPGX - Break up services into separate files --- .../BizHawk.Emulation.Cores.csproj | 34 +- .../Sega/gpgx/GPGX.ICodeDataLogger.cs | 53 ++ .../Consoles/Sega/gpgx/GPGX.IDebuggable.cs | 77 +++ .../Consoles/Sega/gpgx/GPGX.IDriveLight.cs | 12 + .../Consoles/Sega/gpgx/GPGX.IEmulator.cs | 106 +++ .../Consoles/Sega/gpgx/GPGX.IInputPollable.cs | 23 + .../Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs | 95 +++ .../Consoles/Sega/gpgx/GPGX.ISaveRam.cs | 79 +++ .../Consoles/Sega/gpgx/GPGX.ISettable.cs | 113 ++++ .../Consoles/Sega/gpgx/GPGX.IStatable.cs | 79 +++ .../Consoles/Sega/gpgx/GPGX.IVideoProvider.cs | 77 +++ .../Consoles/Sega/gpgx/GPGX.cs | 627 +----------------- 12 files changed, 758 insertions(+), 617 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ICodeDataLogger.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDriveLight.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IInputPollable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISettable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IVideoProvider.cs diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 663d0ecca5..2095bf3957 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -770,7 +770,39 @@ - + + 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 new file mode 100644 index 0000000000..63d4cfe903 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ICodeDataLogger.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + 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); + } + + public void NewCDL(CodeDataLog cdl) + { + cdl["MD CART"] = new byte[MemoryDomains["MD CART"].Size]; + cdl["68K RAM"] = new byte[MemoryDomains["68K RAM"].Size]; + cdl["Z80 RAM"] = new byte[MemoryDomains["Z80 RAM"].Size]; + + if (MemoryDomains.Has("SRAM")) + cdl["SRAM"] = new byte[MemoryDomains["SRAM"].Size]; + + cdl.SubType = "GEN"; + cdl.SubVer = 0; + } + + // TODO: we have Disassembling now + // not supported + public void DisassembleCDL(Stream s, CodeDataLog cdl) { } + + private CodeDataLog CDL; + private void CDCallbackProc(int addr, LibGPGX.CDLog_AddrType addrtype, LibGPGX.CDLog_Flags flags) + { + //TODO - hard reset makes CDL go nuts. + + if (CDL == null) return; + if (!CDL.Active) return; + string key; + switch (addrtype) + { + case LibGPGX.CDLog_AddrType.MDCART: key = "MD CART"; break; + case LibGPGX.CDLog_AddrType.RAM68k: key = "68K RAM"; break; + case LibGPGX.CDLog_AddrType.RAMZ80: key = "Z80 RAM"; break; + case LibGPGX.CDLog_AddrType.SRAM: key = "SRAM"; break; + default: throw new InvalidOperationException("Lagrangian earwax incident"); + } + CDL[key][addr] |= (byte)flags; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs new file mode 100644 index 0000000000..3d0f4a87b7 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDebuggable.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX : IDebuggable + { + public IDictionary GetCpuFlagsAndRegisters() + { + LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[LibGPGX.gpgx_getmaxnumregs()]; + + int n = LibGPGX.gpgx_getregs(regs); + if (n > regs.Length) + throw new InvalidOperationException("A buffer overrun has occured!"); + var ret = new Dictionary(); + for (int i = 0; i < n; i++) + { + // el hacko + string name = Marshal.PtrToStringAnsi(regs[i].Name); + byte size = 32; + if (name.Contains("68K SR") || name.StartsWith("Z80")) + size = 16; + ret[Marshal.PtrToStringAnsi(regs[i].Name)] = + new RegisterValue { BitSize = size, Value = (ulong)regs[i].Value }; + } + + return ret; + } + + [FeatureNotImplemented] + public void SetCpuRegister(string register, int value) + { + throw new NotImplementedException(); + } + + public IMemoryCallbackSystem MemoryCallbacks + { + get { return _memoryCallbacks; } + } + + public bool CanStep(StepType type) { return false; } + + [FeatureNotImplemented] + public void Step(StepType type) { throw new NotImplementedException(); } + + private readonly MemoryCallbackSystem _memoryCallbacks = new MemoryCallbackSystem(); + + private LibGPGX.mem_cb ExecCallback; + private LibGPGX.mem_cb ReadCallback; + private LibGPGX.mem_cb WriteCallback; + private LibGPGX.CDCallback CDCallback; + + private void InitMemCallbacks() + { + ExecCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallExecutes(a)); + ReadCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallReads(a)); + WriteCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallWrites(a)); + _memoryCallbacks.ActiveChanged += RefreshMemCallbacks; + } + + private void RefreshMemCallbacks() + { + LibGPGX.gpgx_set_mem_callback( + MemoryCallbacks.HasReads ? ReadCallback : null, + MemoryCallbacks.HasWrites ? WriteCallback : null, + MemoryCallbacks.HasExecutes ? ExecCallback : null); + } + + private void KillMemCallbacks() + { + LibGPGX.gpgx_set_mem_callback(null, null, null); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDriveLight.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDriveLight.cs new file mode 100644 index 0000000000..bc2cefba4d --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IDriveLight.cs @@ -0,0 +1,12 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX : IDriveLight + { + public bool DriveLightEnabled { get; private set; } + public bool DriveLightOn { get; private set; } + + private bool _drivelight; + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs new file mode 100644 index 0000000000..a61ab25a4f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IEmulator.cs @@ -0,0 +1,106 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX : IEmulator + { + public IEmulatorServiceProvider ServiceProvider { get; private set; } + + public ISoundProvider SoundProvider { get { return null; } } + + public ISyncSoundProvider SyncSoundProvider + { + get { return this; } + } + + public bool StartAsyncSound() + { + return false; + } + + public void EndAsyncSound() { } + + public ControllerDefinition ControllerDefinition { get; private set; } + + public IController Controller { get; set; } + + // TODO: use render and rendersound + public void FrameAdvance(bool render, bool rendersound = true) + { + if (Controller["Reset"]) + LibGPGX.gpgx_reset(false); + if (Controller["Power"]) + LibGPGX.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!"); + + ControlConverter.ScreenWidth = vwidth; + ControlConverter.ScreenHeight = vheight; + ControlConverter.Convert(Controller, input); + + if (!LibGPGX.gpgx_put_control(input, inputsize)) + throw new Exception("gpgx_put_control() failed!"); + + IsLagFrame = true; + Frame++; + _drivelight = false; + + LibGPGX.gpgx_advance(); + UpdateVideo(); + update_audio(); + + if (IsLagFrame) + LagCount++; + + if (CD != null) + DriveLightOn = _drivelight; + } + + public int Frame { get; private set; } + + public string SystemId + { + get { return "GEN"; } + } + + public bool DeterministicEmulation + { + get { return true; } + } + + public string BoardName + { + get { return null; } + } + + public void ResetCounters() + { + Frame = 0; + IsLagFrame = false; + LagCount = 0; + } + + public CoreComm CoreComm { get; private set; } + + public void Dispose() + { + if (!disposed) + { + 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.IInputPollable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IInputPollable.cs new file mode 100644 index 0000000000..f899a8698c --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IInputPollable.cs @@ -0,0 +1,23 @@ +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX : IInputPollable + { + public int LagCount { get; set; } + + public bool IsLagFrame { get; set; } + + public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } } + + private LibGPGX.input_cb InputCallback = null; + + private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem(); + + private void input_callback() + { + InputCallbacks.Call(); + IsLagFrame = false; + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs new file mode 100644 index 0000000000..45da66bf9b --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IMemoryDomains.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX + { + private IMemoryDomains MemoryDomains; + + private unsafe void SetMemoryDomains() + { + var mm = new List(); + for (int i = LibGPGX.MIN_MEM_DOMAIN; i <= LibGPGX.MAX_MEM_DOMAIN; i++) + { + IntPtr area = IntPtr.Zero; + int size = 0; + 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); + if (name == "VRAM") + { + // vram pokes need to go through hook which invalidates cached tiles + byte* p = (byte*)area; + mm.Add(new MemoryDomain(name, size, MemoryDomain.Endian.Unknown, + delegate (long addr) + { + if (addr < 0 || addr >= 65536) + throw new ArgumentOutOfRangeException(); + return p[addr ^ 1]; + }, + delegate (long addr, byte val) + { + if (addr < 0 || addr >= 65536) + throw new ArgumentOutOfRangeException(); + LibGPGX.gpgx_poke_vram(((int)addr) ^ 1, val); + }, + byteSize: 2)); + } + + else + { + var byteSize = name.Contains("Z80") ? 1 : 2; + mm.Add(MemoryDomain.FromIntPtrSwap16(name, size, MemoryDomain.Endian.Big, area, writable: true, byteSize: byteSize)); + } + } + var m68Bus = new MemoryDomain("M68K BUS", 0x1000000, MemoryDomain.Endian.Big, + delegate (long addr) + { + var a = (uint)addr; + if (a >= 0x1000000) + throw new ArgumentOutOfRangeException(); + return LibGPGX.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); + }, 2); + + mm.Add(m68Bus); + + var s68Bus = new MemoryDomain("S68K BUS", 0x1000000, MemoryDomain.Endian.Big, + delegate (long addr) + { + var a = (uint)addr; + if (a >= 0x1000000) + throw new ArgumentOutOfRangeException(); + return LibGPGX.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); + }, 2); + + if (IsSegaCD) + { + mm.Add(s68Bus); + } + + MemoryDomains = new MemoryDomainList(mm); + MemoryDomains.SystemBus = IsSegaCD ? s68Bus : m68Bus; + + (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs new file mode 100644 index 0000000000..747c798df6 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISaveRam.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; +using System.Runtime.InteropServices; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + 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/gpgx/GPGX.ISettable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISettable.cs new file mode 100644 index 0000000000..9400ece21f --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.ISettable.cs @@ -0,0 +1,113 @@ +using System.ComponentModel; + +using BizHawk.Common; +using BizHawk.Emulation.Common; + + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX : ISettable + { + public GPGXSettings GetSettings() + { + return _settings.Clone(); + } + + public GPGXSyncSettings GetSyncSettings() + { + return _syncSettings.Clone(); + } + + public bool PutSettings(GPGXSettings o) + { + _settings = o; + LibGPGX.gpgx_set_draw_mask(_settings.GetDrawMask()); + return false; + } + + public bool PutSyncSettings(GPGXSyncSettings o) + { + bool ret = GPGXSyncSettings.NeedsReboot(_syncSettings, o); + _syncSettings = o; + return ret; + } + + private GPGXSyncSettings _syncSettings; + private GPGXSettings _settings; + + public class GPGXSettings + { + [DisplayName("Background Layer A")] + [Description("True to draw BG layer A")] + [DefaultValue(true)] + public bool DrawBGA { get; set; } + + [DisplayName("Background Layer B")] + [Description("True to draw BG layer B")] + [DefaultValue(true)] + public bool DrawBGB { get; set; } + + [DisplayName("Background Layer W")] + [Description("True to draw BG layer W")] + [DefaultValue(true)] + public bool DrawBGW { get; set; } + + [DisplayName("Pad screen to 320")] + [Description("Set to True to pads the screen out to be 320 when in 256 wide video modes")] + [DefaultValue(false)] + public bool PadScreen320 { get; set; } + + public GPGXSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public GPGXSettings Clone() + { + return (GPGXSettings)MemberwiseClone(); + } + + public LibGPGX.DrawMask GetDrawMask() + { + LibGPGX.DrawMask ret = 0; + if (DrawBGA) ret |= LibGPGX.DrawMask.BGA; + if (DrawBGB) ret |= LibGPGX.DrawMask.BGB; + if (DrawBGW) ret |= LibGPGX.DrawMask.BGW; + return ret; + } + } + + public class GPGXSyncSettings + { + [DisplayName("Use Six Button Controllers")] + [Description("Controls the type of any attached normal controllers; six button controllers are used if true, otherwise three button controllers. Some games don't work correctly with six button controllers. Not relevant if other controller types are connected.")] + [DefaultValue(true)] + public bool UseSixButton { get; set; } + + [DisplayName("Control Type")] + [Description("Sets the type of controls that are plugged into the console. Some games will automatically load with a different control type.")] + [DefaultValue(ControlType.Normal)] + public ControlType ControlType { get; set; } + + [DisplayName("Autodetect Region")] + [Description("Sets the region of the emulated console. Many games can run on multiple regions and will behave differently on different ones. Some games may require a particular region.")] + [DefaultValue(LibGPGX.Region.Autodetect)] + public LibGPGX.Region Region { get; set; } + + public GPGXSyncSettings() + { + SettingsUtil.SetDefaultValues(this); + } + + public GPGXSyncSettings Clone() + { + return (GPGXSyncSettings)MemberwiseClone(); + } + + public static bool NeedsReboot(GPGXSyncSettings x, GPGXSyncSettings y) + { + return !DeepEquality.DeepEquals(x, y); + } + } + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs new file mode 100644 index 0000000000..da6febc406 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IStatable.cs @@ -0,0 +1,79 @@ +using System; +using System.IO; + +using BizHawk.Common.BufferExtensions; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX : IStatable + { + public bool BinarySaveStatesPreferred + { + get { return true; } + } + + public void SaveStateText(TextWriter writer) + { + var temp = SaveStateBinary(); + temp.SaveAsHexFast(writer); + // write extra copy of stuff we don't use + writer.WriteLine("Frame {0}", Frame); + } + + public void LoadStateText(TextReader reader) + { + string hex = reader.ReadLine(); + byte[] state = new byte[hex.Length / 2]; + state.ReadFromHexFast(hex); + 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"); + } + + // other variables + Frame = reader.ReadInt32(); + LagCount = reader.ReadInt32(); + IsLagFrame = reader.ReadBoolean(); + 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); + // other variables + writer.Write(Frame); + writer.Write(LagCount); + writer.Write(IsLagFrame); + } + + 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 new file mode 100644 index 0000000000..d60ff246dd --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.IVideoProvider.cs @@ -0,0 +1,77 @@ +using System; +using BizHawk.Emulation.Common; + +namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx +{ + public partial class GPGX : IVideoProvider + { + public int[] GetVideoBuffer() { return vidbuff; } + + public int VirtualWidth { get { return 320; } } + + public int VirtualHeight { get { return 224; } } + + public int BufferWidth { get { return vwidth; } } + + public int BufferHeight { get { return vheight; } } + + public int BackgroundColor { get { return unchecked((int)0xff000000); } } + + private int[] vidbuff = new int[0]; + private int vwidth; + private int vheight; + + private void UpdateVideoInitial() + { + // hack: you should call update_video() here, but that gives you 256x192 on frame 0 + // and we know that we only use GPGX to emulate genesis games that will always be 320x224 immediately afterwards + + // so instead, just assume a 320x224 size now; if that happens to be wrong, it'll be fixed soon enough. + + vwidth = 320; + vheight = 224; + vidbuff = new int[vwidth * vheight]; + for (int i = 0; i < vidbuff.Length; i++) + vidbuff[i] = unchecked((int)0xff000000); + } + + private unsafe void UpdateVideo() + { + int gppitch, gpwidth, gpheight; + IntPtr src = IntPtr.Zero; + + LibGPGX.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src); + + vwidth = gpwidth; + vheight = gpheight; + + if (_settings.PadScreen320 && vwidth == 256) + vwidth = 320; + + int xpad = (vwidth - gpwidth) / 2; + int xpad2 = vwidth - gpwidth - xpad; + + if (vidbuff.Length < vwidth * vheight) + vidbuff = new int[vwidth * vheight]; + + int rinc = (gppitch / 4) - gpwidth; + fixed (int* pdst_ = &vidbuff[0]) + { + int* pdst = pdst_; + int* psrc = (int*)src; + + for (int j = 0; j < gpheight; j++) + { + for (int i = 0; i < xpad; i++) + *pdst++ = unchecked((int)0xff000000); + for (int i = 0; i < gpwidth; i++) + *pdst++ = *psrc++;// | unchecked((int)0xff000000); + for (int i = 0; i < xpad2; i++) + *pdst++ = unchecked((int)0xff000000); + psrc += rinc; + } + } + } + + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs index c5e7499d99..7f6f48326a 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/gpgx/GPGX.cs @@ -1,18 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using BizHawk.Common.BufferExtensions; -using BizHawk.Emulation.Common; -using BizHawk.Common; - using System.Runtime.InteropServices; -using System.IO; - -using System.ComponentModel; - +using BizHawk.Emulation.Common; namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx { @@ -26,19 +15,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx singleInstance: true )] public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable, - IInputPollable, IDebuggable, ISettable, IDriveLight, ICodeDataLogger, IDisassemblable + IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable { static GPGX AttachedCore = null; DiscSystem.Disc CD; DiscSystem.DiscSectorReader DiscSectorReader; byte[] romfile; - bool drivelight; bool disposed = false; LibGPGX.load_archive_cb LoadCallback = null; - LibGPGX.input_cb InputCallback = null; LibGPGX.InputData input = new LibGPGX.InputData(); @@ -56,7 +43,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx [CoreConstructor("GEN")] public GPGX(CoreComm comm, byte[] file, object Settings, object SyncSettings) - :this(comm, file, null, Settings, SyncSettings) + : this(comm, file, null, Settings, SyncSettings) { } @@ -77,7 +64,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx try { - _SyncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings(); + _syncSettings = (GPGXSyncSettings)SyncSettings ?? new GPGXSyncSettings(); CoreComm = comm; if (AttachedCore != null) @@ -96,7 +83,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx LibGPGX.INPUT_SYSTEM system_a = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE; LibGPGX.INPUT_SYSTEM system_b = LibGPGX.INPUT_SYSTEM.SYSTEM_NONE; - switch (this._SyncSettings.ControlType) + switch (_syncSettings.ControlType) { case ControlType.None: default: @@ -131,7 +118,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx } - if (!LibGPGX.gpgx_init(romextension, LoadCallback, this._SyncSettings.UseSixButton, system_a, system_b, this._SyncSettings.Region)) + if (!LibGPGX.gpgx_init(romextension, LoadCallback, this._syncSettings.UseSixButton, system_a, system_b, this._syncSettings.Region)) throw new Exception("gpgx_init() failed"); { @@ -149,15 +136,15 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx 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]; + _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 - update_video_initial(); + UpdateVideoInitial(); SetMemoryDomains(); @@ -182,53 +169,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx } } - void ICodeDataLogger.SetCDL(CodeDataLog cdl) - { - CDL = cdl; - if(cdl == null) LibGPGX.gpgx_set_cd_callback(null); - else LibGPGX.gpgx_set_cd_callback(CDCallback); - } - - void ICodeDataLogger.NewCDL(CodeDataLog cdl) - { - cdl["MD CART"] = new byte[MemoryDomains["MD CART"].Size]; - cdl["68K RAM"] = new byte[MemoryDomains["68K RAM"].Size]; - cdl["Z80 RAM"] = new byte[MemoryDomains["Z80 RAM"].Size]; - - if (MemoryDomains.Has("SRAM")) - cdl["SRAM"] = new byte[MemoryDomains["SRAM"].Size]; - - cdl.SubType = "GEN"; - cdl.SubVer = 0; - } - - //not supported - void ICodeDataLogger.DisassembleCDL(Stream s, CodeDataLog cdl) { } - - CodeDataLog CDL; - void CDCallbackProc(int addr, LibGPGX.CDLog_AddrType addrtype, LibGPGX.CDLog_Flags flags) - { - //TODO - hard reset makes CDL go nuts. - - if (CDL == null) return; - if (!CDL.Active) return; - string key; - switch (addrtype) - { - case LibGPGX.CDLog_AddrType.MDCART: key = "MD CART"; break; - case LibGPGX.CDLog_AddrType.RAM68k: key = "68K RAM"; break; - case LibGPGX.CDLog_AddrType.RAMZ80: key = "Z80 RAM"; break; - case LibGPGX.CDLog_AddrType.SRAM: key = "SRAM"; break; - default: throw new InvalidOperationException("Lagrangian earwax incident"); - } - CDL[key][addr] |= (byte)flags; - } - - public IEmulatorServiceProvider ServiceProvider { get; private set; } - - public bool DriveLightEnabled { get; private set;} - public bool DriveLightOn { get; private set; } - /// /// core callback for file loading /// @@ -352,7 +292,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx byte[] data = new byte[2048]; DiscSectorReader.ReadLBA_2048(lba, data, 0); Marshal.Copy(data, 0, dest, 2048); - drivelight = true; + _drivelight = true; } } @@ -398,9 +338,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx return retdata; } - - #region controller - /// /// size of native input struct /// @@ -408,10 +345,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx GPGXControlConverter ControlConverter; - public ControllerDefinition ControllerDefinition { get; private set; } - public IController Controller { get; set; } - - void SetControllerDefinition() + private void SetControllerDefinition() { inputsize = Marshal.SizeOf(typeof(LibGPGX.InputData)); if (!LibGPGX.gpgx_get_control(input, inputsize)) @@ -426,391 +360,17 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx return (LibGPGX.INPUT_DEVICE[])input.dev.Clone(); } - // core callback for input - void input_callback() - { - InputCallbacks.Call(); - IsLagFrame = false; - } - - private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem(); - - public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } } - - #endregion - - // TODO: use render and rendersound - public void FrameAdvance(bool render, bool rendersound = true) - { - if (Controller["Reset"]) - LibGPGX.gpgx_reset(false); - if (Controller["Power"]) - LibGPGX.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!"); - - ControlConverter.ScreenWidth = vwidth; - ControlConverter.ScreenHeight = vheight; - ControlConverter.Convert(Controller, input); - - if (!LibGPGX.gpgx_put_control(input, inputsize)) - throw new Exception("gpgx_put_control() failed!"); - - IsLagFrame = true; - Frame++; - drivelight = false; - - LibGPGX.gpgx_advance(); - update_video(); - update_audio(); - - if (IsLagFrame) - LagCount++; - - if (CD != null) - DriveLightOn = drivelight; - } - - public int Frame { get; private set; } - public int LagCount { get; set; } - public bool IsLagFrame { get; set; } - - public string SystemId { get { return "GEN"; } } - public bool DeterministicEmulation { get { return true; } } - public string BoardName { get { return null; } } - - public CoreComm CoreComm { get; private set; } - - #region saveram - - byte[] DisposedSaveRam = null; - - public byte[] CloneSaveRam() - { - if (disposed) - { - if (DisposedSaveRam != null) - { - return (byte[])DisposedSaveRam.Clone(); - } - else - { - 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; - } - } - } - - #endregion - - public void ResetCounters() - { - Frame = 0; - IsLagFrame = false; - LagCount = 0; - } - - #region savestates - - private byte[] savebuff; - private byte[] savebuff2; - - public void SaveStateText(System.IO.TextWriter writer) - { - var temp = SaveStateBinary(); - temp.SaveAsHexFast(writer); - // write extra copy of stuff we don't use - writer.WriteLine("Frame {0}", Frame); - } - - public void LoadStateText(System.IO.TextReader reader) - { - string hex = reader.ReadLine(); - byte[] state = new byte[hex.Length / 2]; - state.ReadFromHexFast(hex); - LoadStateBinary(new System.IO.BinaryReader(new System.IO.MemoryStream(state))); - } - - public void SaveStateBinary(System.IO.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); - // other variables - writer.Write(Frame); - writer.Write(LagCount); - writer.Write(IsLagFrame); - } - - public void LoadStateBinary(System.IO.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"); - // other variables - Frame = reader.ReadInt32(); - LagCount = reader.ReadInt32(); - IsLagFrame = reader.ReadBoolean(); - update_video(); - } - - public byte[] SaveStateBinary() - { - var ms = new System.IO.MemoryStream(savebuff2, true); - var bw = new System.IO.BinaryWriter(ms); - SaveStateBinary(bw); - bw.Flush(); - ms.Close(); - return savebuff2; - } - - public bool BinarySaveStatesPreferred { get { return true; } } - - #endregion - - #region debugging tools - - private IMemoryDomains MemoryDomains; - - unsafe void SetMemoryDomains() - { - var mm = new List(); - for (int i = LibGPGX.MIN_MEM_DOMAIN; i <= LibGPGX.MAX_MEM_DOMAIN; i++) - { - IntPtr area = IntPtr.Zero; - int size = 0; - 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); - if (name == "VRAM") - { - // vram pokes need to go through hook which invalidates cached tiles - byte* p = (byte*)area; - mm.Add(new MemoryDomain(name, size, MemoryDomain.Endian.Unknown, - delegate(long addr) - { - if (addr < 0 || addr >= 65536) - throw new ArgumentOutOfRangeException(); - return p[addr ^ 1]; - }, - delegate(long addr, byte val) - { - if (addr < 0 || addr >= 65536) - throw new ArgumentOutOfRangeException(); - LibGPGX.gpgx_poke_vram(((int)addr) ^ 1, val); - }, - byteSize: 2)); - } - - else - { - var byteSize = name.Contains("Z80") ? 1 : 2; - mm.Add(MemoryDomain.FromIntPtrSwap16(name, size, MemoryDomain.Endian.Big, area, writable: true, byteSize: byteSize)); - } - } - var m68Bus = new MemoryDomain("M68K BUS", 0x1000000, MemoryDomain.Endian.Big, - delegate (long addr) - { - var a = (uint)addr; - if (a >= 0x1000000) - throw new ArgumentOutOfRangeException(); - return LibGPGX.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); - }, 2); - - mm.Add(m68Bus); - - var s68Bus = new MemoryDomain("S68K BUS", 0x1000000, MemoryDomain.Endian.Big, - delegate (long addr) - { - var a = (uint)addr; - if (a >= 0x1000000) - throw new ArgumentOutOfRangeException(); - return LibGPGX.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); - }, 2); - - if (IsSegaCD) - { - mm.Add(s68Bus); - } - - MemoryDomains = new MemoryDomainList(mm); - MemoryDomains.SystemBus = IsSegaCD ? s68Bus : m68Bus; - - (ServiceProvider as BasicServiceProvider).Register(MemoryDomains); - } - public bool IsSegaCD { get { return CD != null; } } - public IDictionary GetCpuFlagsAndRegisters() - { - LibGPGX.RegisterInfo[] regs = new LibGPGX.RegisterInfo[LibGPGX.gpgx_getmaxnumregs()]; - - int n = LibGPGX.gpgx_getregs(regs); - if (n > regs.Length) - throw new InvalidOperationException("A buffer overrun has occured!"); - var ret = new Dictionary(); - for (int i = 0; i < n; i++) - { - // el hacko - string name = Marshal.PtrToStringAnsi(regs[i].Name); - byte size = 32; - if (name.Contains("68K SR") || name.StartsWith("Z80")) - size = 16; - ret[Marshal.PtrToStringAnsi(regs[i].Name)] = - new RegisterValue { BitSize = size, Value = (ulong)regs[i].Value }; - } - return ret; - } - - public bool CanStep(StepType type) { return false; } - - [FeatureNotImplemented] - public void Step(StepType type) { throw new NotImplementedException(); } - - [FeatureNotImplemented] - public void SetCpuRegister(string register, int value) - { - throw new NotImplementedException(); - } - public void UpdateVDPViewContext(LibGPGX.VDPView view) { LibGPGX.gpgx_get_vdp_view(view); LibGPGX.gpgx_flush_vram(); // fully regenerate internal caches as needed } - private readonly MemoryCallbackSystem _memoryCallbacks = new MemoryCallbackSystem(); - public IMemoryCallbackSystem MemoryCallbacks { get { return _memoryCallbacks; } } - - LibGPGX.mem_cb ExecCallback; - LibGPGX.mem_cb ReadCallback; - LibGPGX.mem_cb WriteCallback; - LibGPGX.CDCallback CDCallback; - - void InitMemCallbacks() - { - ExecCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallExecutes(a)); - ReadCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallReads(a)); - WriteCallback = new LibGPGX.mem_cb(a => MemoryCallbacks.CallWrites(a)); - _memoryCallbacks.ActiveChanged += RefreshMemCallbacks; - } - - void RefreshMemCallbacks() - { - LibGPGX.gpgx_set_mem_callback( - MemoryCallbacks.HasReads ? ReadCallback : null, - MemoryCallbacks.HasWrites ? WriteCallback : null, - MemoryCallbacks.HasExecutes ? ExecCallback : null); - } - - void KillMemCallbacks() - { - LibGPGX.gpgx_set_mem_callback(null, null, null); - } - - #endregion - - public void Dispose() - { - if (!disposed) - { - if (AttachedCore != this) - throw new Exception(); - if (SaveRamModified) - DisposedSaveRam = CloneSaveRam(); - KillMemCallbacks(); - if (CD != null) - { - CD.Dispose(); - } - AttachedCore = null; - disposed = true; - } - } - - #region SoundProvider - short[] samples = new short[4096]; int nsamp = 0; - public ISoundProvider SoundProvider { get { return null; } } - public ISyncSoundProvider SyncSoundProvider { get { return this; } } - public bool StartAsyncSound() { return false; } - public void EndAsyncSound() { } - public void GetSamples(out short[] samples, out int nsamp) { nsamp = this.nsamp; @@ -833,171 +393,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx } } - #endregion - - #region VideoProvider - public DisplayType Region { get; private set; } - - int[] vidbuff = new int[0]; - int vwidth; - int vheight; - public int[] GetVideoBuffer() { return vidbuff; } - public int VirtualWidth { get { return 320; } } - public int VirtualHeight { get { return 224; } } - public int BufferWidth { get { return vwidth; } } - public int BufferHeight { get { return vheight; } } - public int BackgroundColor { get { return unchecked((int)0xff000000); } } - - void update_video_initial() - { - // hack: you should call update_video() here, but that gives you 256x192 on frame 0 - // and we know that we only use GPGX to emulate genesis games that will always be 320x224 immediately afterwards - - // so instead, just assume a 320x224 size now; if that happens to be wrong, it'll be fixed soon enough. - - vwidth = 320; - vheight = 224; - vidbuff = new int[vwidth * vheight]; - for (int i = 0; i < vidbuff.Length; i++) - vidbuff[i] = unchecked((int)0xff000000); - } - - unsafe void update_video() - { - int gppitch, gpwidth, gpheight; - IntPtr src = IntPtr.Zero; - - LibGPGX.gpgx_get_video(out gpwidth, out gpheight, out gppitch, ref src); - - vwidth = gpwidth; - vheight = gpheight; - - if (_Settings.PadScreen320 && vwidth == 256) - vwidth = 320; - - int xpad = (vwidth - gpwidth) / 2; - int xpad2 = vwidth - gpwidth - xpad; - - if (vidbuff.Length < vwidth * vheight) - vidbuff = new int[vwidth * vheight]; - - int rinc = (gppitch / 4) - gpwidth; - fixed (int* pdst_ = &vidbuff[0]) - { - int* pdst = pdst_; - int* psrc = (int*)src; - - for (int j = 0; j < gpheight; j++) - { - for(int i=0;i