LibsnesCore - break off memory domain stuff to a separate file
This commit is contained in:
parent
bc42af0fa6
commit
7ebddc6087
|
@ -4,14 +4,14 @@ namespace BizHawk.Emulation.Common
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This service manages the ability for a client to read/write to memory regions of the core,
|
/// This service manages the ability for a client to read/write to memory regions of the core,
|
||||||
/// It is a list of all avaialble memory domains
|
/// It is a list of all available memory domains
|
||||||
/// A memory domain is a byte array that respresents the memory of a distinct part of the emulated system.
|
/// A memory domain is a byte array that represents the memory of a distinct part of the emulated system.
|
||||||
/// All cores sould implement a SystemBus that represents the standard cpu bus range used for cheats for that system,
|
/// All cores should implement a SystemBus that represents the standard CPU bus range used for cheats for that system,
|
||||||
/// In order to have a cheat system available for the core
|
/// In order to have a cheat system available for the core
|
||||||
/// All domains should implement both peek and poke. However,
|
/// All domains should implement both peek and poke. However,
|
||||||
/// if a domain does not implement poke, it should throw NotImplementedException rather than silently fail
|
/// if a domain does not implement poke, it should throw NotImplementedException rather than silently fail
|
||||||
/// If this service is available the client will expose many RAM related tools such as the Hex Editor, RAM Search/Watch, and Cheats
|
/// If this service is available the client will expose many RAM related tools such as the Hex Editor, RAM Search/Watch, and Cheats
|
||||||
/// In addition, this is an essential service for effective lua scripting, and many other tools
|
/// In addition, this is an essential service for effective LUA scripting, and many other tools
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IMemoryDomains : IEnumerable<MemoryDomain>, IEmulatorService
|
public interface IMemoryDomains : IEnumerable<MemoryDomain>, IEmulatorService
|
||||||
{
|
{
|
||||||
|
|
|
@ -913,6 +913,9 @@
|
||||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesCore.IInputPollable.cs">
|
<Compile Include="Consoles\Nintendo\SNES\LibsnesCore.IInputPollable.cs">
|
||||||
<DependentUpon>LibsnesCore.cs</DependentUpon>
|
<DependentUpon>LibsnesCore.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="Consoles\Nintendo\SNES\LibsnesCore.IMemoryDomains.cs">
|
||||||
|
<DependentUpon>LibsnesCore.cs</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Consoles\Nintendo\SNES\LibsnesCore.IRegionable.cs">
|
<Compile Include="Consoles\Nintendo\SNES\LibsnesCore.IRegionable.cs">
|
||||||
<DependentUpon>LibsnesCore.cs</DependentUpon>
|
<DependentUpon>LibsnesCore.cs</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
@ -17,15 +17,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
|
|
||||||
public void NewCDL(ICodeDataLog cdl)
|
public void NewCDL(ICodeDataLog cdl)
|
||||||
{
|
{
|
||||||
cdl["CARTROM"] = new byte[MemoryDomains["CARTROM"].Size];
|
cdl["CARTROM"] = new byte[_memoryDomains["CARTROM"].Size];
|
||||||
|
|
||||||
if (MemoryDomains.Has("CARTRAM"))
|
if (_memoryDomains.Has("CARTRAM"))
|
||||||
{
|
{
|
||||||
cdl["CARTRAM"] = new byte[MemoryDomains["CARTRAM"].Size];
|
cdl["CARTRAM"] = new byte[_memoryDomains["CARTRAM"].Size];
|
||||||
}
|
}
|
||||||
|
|
||||||
cdl["WRAM"] = new byte[MemoryDomains["WRAM"].Size];
|
cdl["WRAM"] = new byte[_memoryDomains["WRAM"].Size];
|
||||||
cdl["APURAM"] = new byte[MemoryDomains["APURAM"].Size];
|
cdl["APURAM"] = new byte[_memoryDomains["APURAM"].Size];
|
||||||
|
|
||||||
cdl.SubType = "SNES";
|
cdl.SubType = "SNES";
|
||||||
cdl.SubVer = 0;
|
cdl.SubVer = 0;
|
||||||
|
|
|
@ -0,0 +1,155 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using BizHawk.Common;
|
||||||
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
|
{
|
||||||
|
public partial class LibsnesCore
|
||||||
|
{
|
||||||
|
private readonly List<MemoryDomain> _memoryDomainList = new List<MemoryDomain>();
|
||||||
|
private IMemoryDomains _memoryDomains;
|
||||||
|
|
||||||
|
private void SetupMemoryDomains(byte[] romData, byte[] sgbRomData)
|
||||||
|
{
|
||||||
|
// lets just do this entirely differently for SGB
|
||||||
|
if (IsSGB)
|
||||||
|
{
|
||||||
|
// NOTE: CGB has 32K of wram, and DMG has 8KB of wram. Not sure how to control this right now.. bsnes might not have any ready way of doign that? I couldnt spot it.
|
||||||
|
// You wouldnt expect a DMG game to access excess wram, but what if it tried to? maybe an oversight in bsnes?
|
||||||
|
MakeMemoryDomain("SGB WRAM", LibsnesApi.SNES_MEMORY.SGB_WRAM, MemoryDomain.Endian.Little);
|
||||||
|
|
||||||
|
var romDomain = new MemoryDomainByteArray("SGB CARTROM", MemoryDomain.Endian.Little, romData, true, 1);
|
||||||
|
_memoryDomainList.Add(romDomain);
|
||||||
|
|
||||||
|
// the last 1 byte of this is special.. its an interrupt enable register, instead of ram. weird. maybe its actually ram and just getting specially used?
|
||||||
|
MakeMemoryDomain("SGB HRAM", LibsnesApi.SNES_MEMORY.SGB_HRAM, MemoryDomain.Endian.Little);
|
||||||
|
|
||||||
|
MakeMemoryDomain("SGB CARTRAM", LibsnesApi.SNES_MEMORY.SGB_CARTRAM, MemoryDomain.Endian.Little);
|
||||||
|
|
||||||
|
MakeMemoryDomain("WRAM", LibsnesApi.SNES_MEMORY.WRAM, MemoryDomain.Endian.Little);
|
||||||
|
|
||||||
|
var sgbromDomain = new MemoryDomainByteArray("SGB.SFC ROM", MemoryDomain.Endian.Little, sgbRomData, true, 1);
|
||||||
|
_memoryDomainList.Add(sgbromDomain);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MakeMemoryDomain("WRAM", LibsnesApi.SNES_MEMORY.WRAM, MemoryDomain.Endian.Little);
|
||||||
|
|
||||||
|
MakeMemoryDomain("CARTROM", LibsnesApi.SNES_MEMORY.CARTRIDGE_ROM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||||
|
MakeMemoryDomain("CARTRAM", LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||||
|
MakeMemoryDomain("VRAM", LibsnesApi.SNES_MEMORY.VRAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||||
|
MakeMemoryDomain("OAM", LibsnesApi.SNES_MEMORY.OAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||||
|
MakeMemoryDomain("CGRAM", LibsnesApi.SNES_MEMORY.CGRAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||||
|
MakeMemoryDomain("APURAM", LibsnesApi.SNES_MEMORY.APURAM, MemoryDomain.Endian.Little, byteSize: 2);
|
||||||
|
|
||||||
|
if (!DeterministicEmulation)
|
||||||
|
{
|
||||||
|
_memoryDomainList.Add(new MemoryDomainDelegate(
|
||||||
|
"System Bus",
|
||||||
|
0x1000000,
|
||||||
|
MemoryDomain.Endian.Little,
|
||||||
|
addr => api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr),
|
||||||
|
(addr, val) => api.QUERY_poke(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr, val), wordSize: 2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// limited function bus
|
||||||
|
MakeFakeBus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_memoryDomains = new MemoryDomainList(_memoryDomainList);
|
||||||
|
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(_memoryDomains);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, MemoryDomain.Endian endian, int byteSize = 1)
|
||||||
|
{
|
||||||
|
int size = api.QUERY_get_memory_size(id);
|
||||||
|
int mask = size - 1;
|
||||||
|
bool pow2 = Util.IsPowerOfTwo(size);
|
||||||
|
|
||||||
|
// if this type of memory isnt available, dont make the memory domain (most commonly save ram)
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte* blockptr = api.QUERY_get_memory_data(id);
|
||||||
|
|
||||||
|
MemoryDomain md;
|
||||||
|
|
||||||
|
if (id == LibsnesApi.SNES_MEMORY.OAM)
|
||||||
|
{
|
||||||
|
// OAM is actually two differently sized banks of memory which arent truly considered adjacent.
|
||||||
|
// maybe a better way to visualize it is with an empty bus and adjacent banks
|
||||||
|
// so, we just throw away everything above its size of 544 bytes
|
||||||
|
if (size != 544)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("oam size isnt 544 bytes.. wtf?");
|
||||||
|
}
|
||||||
|
|
||||||
|
md = new MemoryDomainDelegate(
|
||||||
|
name,
|
||||||
|
size,
|
||||||
|
endian,
|
||||||
|
addr => addr < 544 ? blockptr[addr] : (byte)0x00,
|
||||||
|
(addr, value) => { if (addr < 544) { blockptr[addr] = value; } },
|
||||||
|
byteSize);
|
||||||
|
}
|
||||||
|
else if (pow2)
|
||||||
|
{
|
||||||
|
md = new MemoryDomainDelegate(
|
||||||
|
name,
|
||||||
|
size,
|
||||||
|
endian,
|
||||||
|
addr => blockptr[addr & mask],
|
||||||
|
(addr, value) => blockptr[addr & mask] = value,
|
||||||
|
byteSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
md = new MemoryDomainDelegate(
|
||||||
|
name,
|
||||||
|
size,
|
||||||
|
endian,
|
||||||
|
addr => blockptr[addr % size],
|
||||||
|
(addr, value) => blockptr[addr % size] = value,
|
||||||
|
byteSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
_memoryDomainList.Add(md);
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void MakeFakeBus()
|
||||||
|
{
|
||||||
|
int size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.WRAM);
|
||||||
|
if (size != 0x20000)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte* blockptr = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.WRAM);
|
||||||
|
|
||||||
|
var md = new MemoryDomainDelegate("System Bus", 0x1000000, MemoryDomain.Endian.Little,
|
||||||
|
addr =>
|
||||||
|
{
|
||||||
|
var a = FakeBusMap((int)addr);
|
||||||
|
if (a.HasValue)
|
||||||
|
{
|
||||||
|
return blockptr[a.Value];
|
||||||
|
}
|
||||||
|
|
||||||
|
return FakeBusRead((int)addr);
|
||||||
|
},
|
||||||
|
(addr, val) =>
|
||||||
|
{
|
||||||
|
var a = FakeBusMap((int)addr);
|
||||||
|
if (a.HasValue)
|
||||||
|
blockptr[a.Value] = val;
|
||||||
|
}, wordSize: 2);
|
||||||
|
_memoryDomainList.Add(md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -709,128 +709,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe void MakeFakeBus()
|
|
||||||
{
|
|
||||||
int size = api.QUERY_get_memory_size(LibsnesApi.SNES_MEMORY.WRAM);
|
|
||||||
if (size != 0x20000)
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
|
|
||||||
byte* blockptr = api.QUERY_get_memory_data(LibsnesApi.SNES_MEMORY.WRAM);
|
|
||||||
|
|
||||||
var md = new MemoryDomainDelegate("System Bus", 0x1000000, MemoryDomain.Endian.Little,
|
|
||||||
(addr) =>
|
|
||||||
{
|
|
||||||
var a = FakeBusMap((int)addr);
|
|
||||||
if (a.HasValue)
|
|
||||||
return blockptr[a.Value];
|
|
||||||
else
|
|
||||||
return FakeBusRead((int)addr);
|
|
||||||
},
|
|
||||||
(addr, val) =>
|
|
||||||
{
|
|
||||||
var a = FakeBusMap((int)addr);
|
|
||||||
if (a.HasValue)
|
|
||||||
blockptr[a.Value] = val;
|
|
||||||
}, wordSize: 2);
|
|
||||||
_memoryDomains.Add(md);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ----- Client Debugging API stuff -----
|
|
||||||
unsafe MemoryDomain MakeMemoryDomain(string name, LibsnesApi.SNES_MEMORY id, MemoryDomain.Endian endian, int byteSize = 1)
|
|
||||||
{
|
|
||||||
int size = api.QUERY_get_memory_size(id);
|
|
||||||
int mask = size - 1;
|
|
||||||
bool pow2 = Util.IsPowerOfTwo(size);
|
|
||||||
|
|
||||||
//if this type of memory isnt available, dont make the memory domain (most commonly save ram)
|
|
||||||
if (size == 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
byte* blockptr = api.QUERY_get_memory_data(id);
|
|
||||||
|
|
||||||
MemoryDomain md;
|
|
||||||
|
|
||||||
if(id == LibsnesApi.SNES_MEMORY.OAM)
|
|
||||||
{
|
|
||||||
//OAM is actually two differently sized banks of memory which arent truly considered adjacent.
|
|
||||||
//maybe a better way to visualize it is with an empty bus and adjacent banks
|
|
||||||
//so, we just throw away everything above its size of 544 bytes
|
|
||||||
if (size != 544) throw new InvalidOperationException("oam size isnt 544 bytes.. wtf?");
|
|
||||||
md = new MemoryDomainDelegate(name, size, endian,
|
|
||||||
(addr) => (addr < 544) ? blockptr[addr] : (byte)0x00,
|
|
||||||
(addr, value) => { if (addr < 544) blockptr[addr] = value; },
|
|
||||||
byteSize);
|
|
||||||
}
|
|
||||||
else if(pow2)
|
|
||||||
md = new MemoryDomainDelegate(name, size, endian,
|
|
||||||
(addr) => blockptr[addr & mask],
|
|
||||||
(addr, value) => blockptr[addr & mask] = value, byteSize);
|
|
||||||
else
|
|
||||||
md = new MemoryDomainDelegate(name, size, endian,
|
|
||||||
(addr) => blockptr[addr % size],
|
|
||||||
(addr, value) => blockptr[addr % size] = value, byteSize);
|
|
||||||
|
|
||||||
_memoryDomains.Add(md);
|
|
||||||
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetupMemoryDomains(byte[] romData, byte[] sgbRomData)
|
|
||||||
{
|
|
||||||
//lets just do this entirely differently for SGB
|
|
||||||
if (IsSGB)
|
|
||||||
{
|
|
||||||
//NOTE: CGB has 32K of wram, and DMG has 8KB of wram. Not sure how to control this right now.. bsnes might not have any ready way of doign that? I couldnt spot it.
|
|
||||||
//You wouldnt expect a DMG game to access excess wram, but what if it tried to? maybe an oversight in bsnes?
|
|
||||||
MakeMemoryDomain("SGB WRAM", LibsnesApi.SNES_MEMORY.SGB_WRAM, MemoryDomain.Endian.Little);
|
|
||||||
|
|
||||||
var romDomain = new MemoryDomainByteArray("SGB CARTROM", MemoryDomain.Endian.Little, romData, true, 1);
|
|
||||||
_memoryDomains.Add(romDomain);
|
|
||||||
|
|
||||||
//the last 1 byte of this is special.. its an interrupt enable register, instead of ram. weird. maybe its actually ram and just getting specially used?
|
|
||||||
MakeMemoryDomain("SGB HRAM", LibsnesApi.SNES_MEMORY.SGB_HRAM, MemoryDomain.Endian.Little);
|
|
||||||
|
|
||||||
MakeMemoryDomain("SGB CARTRAM", LibsnesApi.SNES_MEMORY.SGB_CARTRAM, MemoryDomain.Endian.Little);
|
|
||||||
|
|
||||||
MainMemory = MakeMemoryDomain("WRAM", LibsnesApi.SNES_MEMORY.WRAM, MemoryDomain.Endian.Little);
|
|
||||||
|
|
||||||
var sgbromDomain = new MemoryDomainByteArray("SGB.SFC ROM", MemoryDomain.Endian.Little, sgbRomData, true, 1);
|
|
||||||
_memoryDomains.Add(sgbromDomain);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MainMemory = MakeMemoryDomain("WRAM", LibsnesApi.SNES_MEMORY.WRAM, MemoryDomain.Endian.Little);
|
|
||||||
|
|
||||||
|
|
||||||
MakeMemoryDomain("CARTROM", LibsnesApi.SNES_MEMORY.CARTRIDGE_ROM, MemoryDomain.Endian.Little, byteSize: 2);
|
|
||||||
MakeMemoryDomain("CARTRAM", LibsnesApi.SNES_MEMORY.CARTRIDGE_RAM, MemoryDomain.Endian.Little, byteSize: 2);
|
|
||||||
MakeMemoryDomain("VRAM", LibsnesApi.SNES_MEMORY.VRAM, MemoryDomain.Endian.Little, byteSize: 2);
|
|
||||||
MakeMemoryDomain("OAM", LibsnesApi.SNES_MEMORY.OAM, MemoryDomain.Endian.Little, byteSize: 2);
|
|
||||||
MakeMemoryDomain("CGRAM", LibsnesApi.SNES_MEMORY.CGRAM, MemoryDomain.Endian.Little, byteSize: 2);
|
|
||||||
MakeMemoryDomain("APURAM", LibsnesApi.SNES_MEMORY.APURAM, MemoryDomain.Endian.Little, byteSize: 2);
|
|
||||||
|
|
||||||
if (!DeterministicEmulation)
|
|
||||||
{
|
|
||||||
_memoryDomains.Add(new MemoryDomainDelegate("System Bus", 0x1000000, MemoryDomain.Endian.Little,
|
|
||||||
(addr) => api.QUERY_peek(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr),
|
|
||||||
(addr, val) => api.QUERY_poke(LibsnesApi.SNES_MEMORY.SYSBUS, (uint)addr, val), wordSize: 2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// limited function bus
|
|
||||||
MakeFakeBus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryDomains = new MemoryDomainList(_memoryDomains);
|
|
||||||
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(MemoryDomains);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MemoryDomain MainMemory;
|
|
||||||
private List<MemoryDomain> _memoryDomains = new List<MemoryDomain>();
|
|
||||||
private IMemoryDomains MemoryDomains;
|
|
||||||
|
|
||||||
#region audio stuff
|
#region audio stuff
|
||||||
|
|
||||||
SpeexResampler resampler;
|
SpeexResampler resampler;
|
||||||
|
@ -845,7 +723,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
|
||||||
resampler.EnqueueSample((short)left, (short)right);
|
resampler.EnqueueSample((short)left, (short)right);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion audio stuff
|
#endregion audio stuff
|
||||||
|
|
||||||
void RefreshPalette()
|
void RefreshPalette()
|
||||||
|
|
Loading…
Reference in New Issue