diff --git a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs index efb089ec67..a8a499b1c4 100644 --- a/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs +++ b/src/BizHawk.Emulation.Cores/Consoles/NEC/PCE/TerboGrafix.cs @@ -27,5 +27,8 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE }; DoInit(game, null, discs, "pce.wbx", null, firmwares); } + + // pce always has two layers, sgx always has 4, and mednafen knows this + public override string SystemId => SettingsInfo.LayerNames.Count == 4 ? "SGX" : "PCE"; } } diff --git a/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs index 2b5f8c4744..d799be00a9 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/LibWaterboxCore.cs @@ -90,13 +90,17 @@ namespace BizHawk.Emulation.Cores.Waterbox /// for a yuge endian domain, if true, bytes are stored word-swapped from their native ordering /// Swapped = 512, + /// + /// If true, Data is a function to call and not a pointer + /// + FunctionHook = 1024, } [StructLayout(LayoutKind.Sequential)] public struct MemoryArea { /// - /// pointer to the data in memory + /// pointer to the data in memory, or a function hook to call /// public IntPtr Data; /// @@ -110,99 +114,12 @@ namespace BizHawk.Emulation.Cores.Waterbox public MemoryDomainFlags Flags; } + [UnmanagedFunctionPointer(CC)] + public delegate void MemoryFunctionHook(IntPtr buffer, long address, long count, bool write); + [UnmanagedFunctionPointer(CC)] public delegate void EmptyCallback(); - public unsafe class WaterboxMemoryDomain : MemoryDomain - { - private readonly IntPtr _data; - private readonly IMonitor _monitor; - private readonly long _addressMangler; - - public override byte PeekByte(long addr) - { - if ((ulong)addr < (ulong)Size) - { - using (_monitor.EnterExit()) - { - return ((byte*)_data)[addr ^ _addressMangler]; - } - } - - throw new ArgumentOutOfRangeException(nameof(addr)); - } - - public override void PokeByte(long addr, byte val) - { - if (Writable) - { - if ((ulong)addr < (ulong)Size) - { - using (_monitor.EnterExit()) - { - ((byte*)_data)[addr ^ _addressMangler] = val; - } - } - else - { - throw new ArgumentOutOfRangeException(nameof(addr)); - } - } - } - - public override void BulkPeekByte(Range addresses, byte[] values) - { - if (_addressMangler != 0) - { - base.BulkPeekByte(addresses, values); - return; - } - - var start = (ulong)addresses.Start; - var count = addresses.Count(); - - if (start < (ulong)Size && (start + count) <= (ulong)Size) - { - using (_monitor.EnterExit()) - { - Marshal.Copy(Z.US((ulong)_data + start), values, 0, (int)count); - } - } - else - { - throw new ArgumentOutOfRangeException(nameof(addresses)); - } - } - - public WaterboxMemoryDomain(MemoryArea m, IMonitor monitor) - { - Name = Mershul.PtrToStringUtf8(m.Name); - EndianType = (m.Flags & MemoryDomainFlags.YugeEndian) != 0 ? Endian.Big : Endian.Little; - _data = m.Data; - Size = m.Size; - Writable = (m.Flags & MemoryDomainFlags.Writable) != 0; - if ((m.Flags & MemoryDomainFlags.WordSize1) != 0) - WordSize = 1; - else if ((m.Flags & MemoryDomainFlags.WordSize2) != 0) - WordSize = 2; - else if ((m.Flags & MemoryDomainFlags.WordSize4) != 0) - WordSize = 4; - else if ((m.Flags & MemoryDomainFlags.WordSize8) != 0) - WordSize = 8; - else - throw new InvalidOperationException("Unknown word size for memory domain"); - _monitor = monitor; - if ((m.Flags & MemoryDomainFlags.Swapped) != 0 && EndianType == Endian.Big) - { - _addressMangler = WordSize - 1; - } - else - { - _addressMangler = 0; - } - } - } - [BizImport(CC)] public abstract void FrameAdvance([In, Out] FrameInfo frame); diff --git a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs index d4e3d885b2..a3d5fc581f 100644 --- a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxCore.cs @@ -68,7 +68,7 @@ namespace BizHawk.Emulation.Cores.Waterbox .ToArray(); _saveramSize = (int)_saveramAreas.Sum(a => a.Size); - var memoryDomains = _memoryAreas.Select(a => new LibWaterboxCore.WaterboxMemoryDomain(a, _exe)); + var memoryDomains = _memoryAreas.Select(a => WaterboxMemoryDomain.Create(a, _exe)); var primaryIndex = _memoryAreas .Select((a, i) => new { a, i }) .Single(a => (a.a.Flags & LibWaterboxCore.MemoryDomainFlags.Primary) != 0).i; diff --git a/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs new file mode 100644 index 0000000000..8c032e4426 --- /dev/null +++ b/src/BizHawk.Emulation.Cores/Waterbox/WaterboxMemoryDomain.cs @@ -0,0 +1,205 @@ + + +using System; +using System.Runtime.InteropServices; +using BizHawk.BizInvoke; +using BizHawk.Common; +using BizHawk.Emulation.Common; +using static BizHawk.Emulation.Cores.Waterbox.LibWaterboxCore; + +namespace BizHawk.Emulation.Cores.Waterbox +{ + public abstract unsafe class WaterboxMemoryDomain : MemoryDomain + { + protected readonly IntPtr _data; + protected readonly IMonitor _monitor; + protected readonly long _addressMangler; + + public static MemoryDomain Create(MemoryArea m, IMonitor monitor) + { + return m.Flags.HasFlag(MemoryDomainFlags.FunctionHook) + ? (MemoryDomain)new WaterboxMemoryDomainFunc(m, monitor) + : new WaterboxMemoryDomainPointer(m, monitor); + } + + protected WaterboxMemoryDomain(MemoryArea m, IMonitor monitor) + { + Name = Mershul.PtrToStringUtf8(m.Name); + EndianType = (m.Flags & MemoryDomainFlags.YugeEndian) != 0 ? Endian.Big : Endian.Little; + _data = m.Data; + Size = m.Size; + Writable = (m.Flags & MemoryDomainFlags.Writable) != 0; + if ((m.Flags & MemoryDomainFlags.WordSize1) != 0) + WordSize = 1; + else if ((m.Flags & MemoryDomainFlags.WordSize2) != 0) + WordSize = 2; + else if ((m.Flags & MemoryDomainFlags.WordSize4) != 0) + WordSize = 4; + else if ((m.Flags & MemoryDomainFlags.WordSize8) != 0) + WordSize = 8; + else + throw new InvalidOperationException("Unknown word size for memory domain"); + _monitor = monitor; + if ((m.Flags & MemoryDomainFlags.Swapped) != 0 && EndianType == Endian.Big) + { + _addressMangler = WordSize - 1; + } + else + { + _addressMangler = 0; + } + } + } + + public unsafe class WaterboxMemoryDomainPointer : WaterboxMemoryDomain + { + internal WaterboxMemoryDomainPointer(MemoryArea m, IMonitor monitor) + : base(m, monitor) + { + if (m.Flags.HasFlag(MemoryDomainFlags.FunctionHook)) + throw new InvalidOperationException(); + } + + public override byte PeekByte(long addr) + { + if ((ulong)addr < (ulong)Size) + { + using (_monitor.EnterExit()) + { + return ((byte*)_data)[addr ^ _addressMangler]; + } + } + + throw new ArgumentOutOfRangeException(nameof(addr)); + } + + public override void PokeByte(long addr, byte val) + { + if (Writable) + { + if ((ulong)addr < (ulong)Size) + { + using (_monitor.EnterExit()) + { + ((byte*)_data)[addr ^ _addressMangler] = val; + } + } + else + { + throw new ArgumentOutOfRangeException(nameof(addr)); + } + } + } + + public override void BulkPeekByte(Range addresses, byte[] values) + { + if (_addressMangler != 0) + { + base.BulkPeekByte(addresses, values); + return; + } + + var start = (ulong)addresses.Start; + var count = addresses.Count(); + + if (start < (ulong)Size && (start + count) <= (ulong)Size) + { + using (_monitor.EnterExit()) + { + Marshal.Copy(Z.US((ulong)_data + start), values, 0, (int)count); + } + } + else + { + throw new ArgumentOutOfRangeException(nameof(addresses)); + } + } + } + + /// + /// For private use only! Don't touch + /// + public abstract class MemoryDomainAccessStub + { + [BizImport(CallingConvention.Cdecl)] + public abstract void Access(IntPtr buffer, long address, long count, bool write); + + private class StubResolver : IImportResolver + { + private readonly IntPtr _p; + public StubResolver(IntPtr p) + { + _p = p; + } + public IntPtr GetProcAddrOrThrow(string entryPoint) => _p; + public IntPtr GetProcAddrOrZero(string entryPoint) => _p; + } + + public static MemoryDomainAccessStub Create(IntPtr p, IMonitor monitor) + { + return BizInvoker.GetInvoker(new StubResolver(p), monitor, CallingConventionAdapters.Waterbox); + } + } + + public unsafe class WaterboxMemoryDomainFunc : WaterboxMemoryDomain + { + private readonly MemoryDomainAccessStub _access; + + internal WaterboxMemoryDomainFunc(MemoryArea m, IMonitor monitor) + : base(m, monitor) + { + if (!m.Flags.HasFlag(MemoryDomainFlags.FunctionHook)) + throw new InvalidOperationException(); + _access = MemoryDomainAccessStub.Create(m.Data, monitor); + } + + public override byte PeekByte(long addr) + { + if ((ulong)addr < (ulong)Size) + { + byte ret = 0; + _access.Access((IntPtr)(byte*)&ret, addr, 1, false); + return ret; + } + + throw new ArgumentOutOfRangeException(nameof(addr)); + } + + public override void PokeByte(long addr, byte val) + { + if (Writable) + { + if ((ulong)addr < (ulong)Size) + { + _access.Access((IntPtr)(byte*)&val, addr, 1, true); + } + else + { + throw new ArgumentOutOfRangeException(nameof(addr)); + } + } + } + + public override void BulkPeekByte(Range addresses, byte[] values) + { + if (_addressMangler != 0) + { + base.BulkPeekByte(addresses, values); + return; + } + + var start = (ulong)addresses.Start; + var count = addresses.Count(); + + if (start < (ulong)Size && (start + count) <= (ulong)Size) + { + fixed(byte* p = values) + _access.Access((IntPtr)p, (long)start, (long)count, false); + } + else + { + throw new ArgumentOutOfRangeException(nameof(addresses)); + } + } + } +} diff --git a/waterbox/emulibc/waterboxcore.h b/waterbox/emulibc/waterboxcore.h index 4dc8741469..47cd5ef31c 100644 --- a/waterbox/emulibc/waterboxcore.h +++ b/waterbox/emulibc/waterboxcore.h @@ -1,40 +1,43 @@ -#pragma once -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct -{ - uint32_t* VideoBuffer; - int16_t* SoundBuffer; - int64_t Cycles; - int32_t Width; - int32_t Height; - int32_t Samples; - int32_t Lagged; -} FrameInfo; - -typedef struct -{ - void* Data; - const char* Name; - int64_t Size; - int64_t Flags; -} MemoryArea; - -#define MEMORYAREA_FLAGS_WRITABLE 1 -#define MEMORYAREA_FLAGS_SAVERAMMABLE 2 -#define MEMORYAREA_FLAGS_ONEFILLED 4 -#define MEMORYAREA_FLAGS_PRIMARY 8 -#define MEMORYAREA_FLAGS_YUGEENDIAN 16 -#define MEMORYAREA_FLAGS_WORDSIZE1 32 -#define MEMORYAREA_FLAGS_WORDSIZE2 64 -#define MEMORYAREA_FLAGS_WORDSIZE4 128 -#define MEMORYAREA_FLAGS_WORDSIZE8 256 -#define MEMORYAREA_FLAGS_SWAPPED 512 - -#ifdef __cplusplus -} -#endif +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + uint32_t* VideoBuffer; + int16_t* SoundBuffer; + int64_t Cycles; + int32_t Width; + int32_t Height; + int32_t Samples; + int32_t Lagged; +} FrameInfo; + +typedef struct +{ + void* Data; + const char* Name; + int64_t Size; + int64_t Flags; +} MemoryArea; + +typedef void (*MemoryFunctionHook)(uint8_t* buffer, int64_t address, int64_t count, bool write); + +#define MEMORYAREA_FLAGS_WRITABLE 1 +#define MEMORYAREA_FLAGS_SAVERAMMABLE 2 +#define MEMORYAREA_FLAGS_ONEFILLED 4 +#define MEMORYAREA_FLAGS_PRIMARY 8 +#define MEMORYAREA_FLAGS_YUGEENDIAN 16 +#define MEMORYAREA_FLAGS_WORDSIZE1 32 +#define MEMORYAREA_FLAGS_WORDSIZE2 64 +#define MEMORYAREA_FLAGS_WORDSIZE4 128 +#define MEMORYAREA_FLAGS_WORDSIZE8 256 +#define MEMORYAREA_FLAGS_SWAPPED 512 +#define MEMORYAREA_FLAGS_FUNCTIONHOOK 1024 + +#ifdef __cplusplus +} +#endif diff --git a/waterbox/nyma/NymaCore.cpp b/waterbox/nyma/NymaCore.cpp index 5784017c7a..39d9086483 100644 --- a/waterbox/nyma/NymaCore.cpp +++ b/waterbox/nyma/NymaCore.cpp @@ -190,14 +190,6 @@ ECL_EXPORT void SetLayers(uint64_t layers) Game->SetLayerEnableMask(layers); } -ECL_EXPORT void GetMemoryAreas(MemoryArea* m) -{ - m[0].Data = (void*)pixels; - m[0].Name = "PEWP"; - m[0].Size = Game->fb_width * Game->fb_height * 4; - m[0].Flags = MEMORYAREA_FLAGS_PRIMARY | MEMORYAREA_FLAGS_WORDSIZE4; -} - ECL_EXPORT void SetInputCallback(void (*cb)()) {} diff --git a/waterbox/nyma/pce.cpp b/waterbox/nyma/pce.cpp index 80637d85f5..2c576a28eb 100644 --- a/waterbox/nyma/pce.cpp +++ b/waterbox/nyma/pce.cpp @@ -2,6 +2,10 @@ #include "nyma.h" #include #include "mednafen/src/pce/pce.h" +#include +#include "mednafen/src/pce/pcecd.h" +#include "mednafen/src/pce/huc.h" +#include "mednafen/src/hw_misc/arcade_card/arcade_card.h" using namespace MDFN_IEN_PCE; @@ -11,3 +15,124 @@ void SetupMDFNGameInfo() { Mednafen::MDFNGameInfo = &EmulatedPCE; } + +static bool IsSgx() +{ + return strcmp(EmulatedPCE.LayerNames, "BG0") == 0; +} + +// template +// static void AccessFunc(uint8_t* buffer, int64_t address, int64_t count, bool write) +// { +// if (write) +// { +// while (count--) +// WriteFunc(address++, *buffer++); +// } +// else +// { +// while (count--) +// *buffer++ = ReadFunc(address++); +// } +// } + +#define DEFUN(N,R,W)\ +static void Access##N(uint8_t* buffer, int64_t address, int64_t count, bool write)\ +{\ + if (write)\ + {\ + while (count--)\ + W(address++, *buffer++);\ + }\ + else\ + {\ + while (count--)\ + *buffer++ = R(address++);\ + }\ +} +#define DEFRG(N,R,W)\ +static void Access##N(uint8_t* buffer, int64_t address, int64_t count, bool write)\ +{\ + if (write)\ + {\ + W(address, count, buffer);\ + }\ + else\ + {\ + R(address, count, buffer);\ + }\ +} + +namespace MDFN_IEN_PCE +{ + extern ArcadeCard* arcade_card; +} + +DEFUN(ShortBus, HuCPU.PeekLogical, HuCPU.PokeLogical); +DEFUN(LongBus, HuCPU.PeekPhysical, HuCPU.PokePhysical); +DEFUN(MainMemory, PCE_PeekMainRAM, PCE_PokeMainRAM); +DEFUN(BRAM, HuC_PeekBRAM, HuC_PokeBRAM); +DEFRG(ADPCM, ADPCM_PeekRAM, ADPCM_PokeRAM); +DEFRG(Arcade, arcade_card->PeekRAM, arcade_card->PokeRAM); + +ECL_EXPORT void GetMemoryAreas(MemoryArea* m) +{ + int i = 0; + + m[i].Data = (void*)(MemoryFunctionHook)AccessLongBus; + m[i].Name = "System Bus (21 bit)"; + m[i].Size = 1 << 21; + m[i].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_FUNCTIONHOOK; + i++; + + m[i].Data = (void*)(MemoryFunctionHook)AccessShortBus; + m[i].Name = "System Bus"; + m[i].Size = 1 << 16; + m[i].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_FUNCTIONHOOK; + i++; + + m[i].Data = (void*)(MemoryFunctionHook)AccessMainMemory; + m[i].Name = "Main Memory"; + m[i].Size = IsSgx() ? 32768 : 8192; + m[i].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_FUNCTIONHOOK | MEMORYAREA_FLAGS_PRIMARY; + i++; + + // TODO: "ROM" + + if (HuC_IsBRAMAvailable()) + { + m[i].Data = (void*)(MemoryFunctionHook)AccessBRAM; + m[i].Name = "Battery RAM"; + m[i].Size = 2048; + m[i].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_ONEFILLED | MEMORYAREA_FLAGS_FUNCTIONHOOK | MEMORYAREA_FLAGS_SAVERAMMABLE; + i++; + } + + if (PCE_IsCD) + { + // TODO: "TurboCD RAM" (var CDRAM) + + m[i].Data = (void*)(MemoryFunctionHook)AccessADPCM; + m[i].Name = "ADPCM RAM"; + m[i].Size = 1 << 16; + m[i].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_FUNCTIONHOOK; + i++; + + // TODO: "Super System Card RAM" + // if (HuCPU.ReadMap[0x68] == SysCardRAMRead) + // {} + + if (arcade_card) + { + m[i].Data = (void*)(MemoryFunctionHook)AccessArcade; + m[i].Name = "Arcade Card RAM"; + m[i].Size = 1 << 16; + m[i].Flags = MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_FUNCTIONHOOK; + i++; + } + } + + // TODO: "Cart Battery RAM" + // if (IsPopulous) + // {} +}