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:
parent
1000b74f2d
commit
f9b5b9e374
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)())
|
||||
{}
|
||||
|
||||
|
|
|
@ -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)
|
||||
// {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue