pce - implement most memory domains our existing core does

The mednafen debugger system makes this easy, unfortunately that has a lot of other baggage, so do it this way
This commit is contained in:
nattthebear 2020-05-25 11:22:46 -04:00
parent 1000b74f2d
commit f9b5b9e374
7 changed files with 385 additions and 140 deletions

View File

@ -27,5 +27,8 @@ namespace BizHawk.Emulation.Cores.Consoles.NEC.PCE
};
DoInit<LibNymaCore>(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";
}
}

View File

@ -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
/// </summary>
Swapped = 512,
/// <summary>
/// If true, Data is a function to call and not a pointer
/// </summary>
FunctionHook = 1024,
}
[StructLayout(LayoutKind.Sequential)]
public struct MemoryArea
{
/// <summary>
/// pointer to the data in memory
/// pointer to the data in memory, or a function hook to call
/// </summary>
public IntPtr Data;
/// <summary>
@ -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<long> 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);

View File

@ -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;

View File

@ -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<long> 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));
}
}
}
/// <summary>
/// For private use only! Don't touch
/// </summary>
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<MemoryDomainAccessStub>(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<long> 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));
}
}
}
}

View File

@ -1,40 +1,43 @@
#pragma once
#include <stdint.h>
#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 <stdint.h>
#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

View File

@ -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)())
{}

View File

@ -2,6 +2,10 @@
#include "nyma.h"
#include <emulibc.h>
#include "mednafen/src/pce/pce.h"
#include <waterboxcore.h>
#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<auto ReadFunc, auto WriteFunc>
// 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)
// {}
}