2013-11-04 02:11:40 +00:00
|
|
|
|
using System;
|
2013-11-06 02:15:29 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.ObjectModel;
|
2013-11-04 02:11:40 +00:00
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Common
|
|
|
|
|
{
|
|
|
|
|
public class MemoryDomain
|
|
|
|
|
{
|
|
|
|
|
public enum Endian { Big, Little, Unknown }
|
|
|
|
|
|
|
|
|
|
public readonly string Name;
|
2014-09-09 21:34:41 +00:00
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Special note: if this is 0, the memorydomain is 0x100000000 (full 32bits) in size.
|
|
|
|
|
/// This was judged to be less of a mess than using a bunch of longs everywhere.
|
|
|
|
|
/// </summary>
|
2013-11-04 02:11:40 +00:00
|
|
|
|
public readonly int Size;
|
|
|
|
|
public readonly Endian EndianType;
|
|
|
|
|
|
|
|
|
|
public readonly Func<int, byte> PeekByte;
|
|
|
|
|
public readonly Action<int, byte> PokeByte;
|
|
|
|
|
|
|
|
|
|
public MemoryDomain(string name, int size, Endian endian, Func<int, byte> peekByte, Action<int, byte> pokeByte)
|
|
|
|
|
{
|
|
|
|
|
Name = name;
|
|
|
|
|
Size = size;
|
|
|
|
|
EndianType = endian;
|
|
|
|
|
PeekByte = peekByte;
|
|
|
|
|
PokeByte = pokeByte;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-15 21:21:17 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// create a memorydomain that references an unmanaged memory block
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name"></param>
|
|
|
|
|
/// <param name="size"></param>
|
|
|
|
|
/// <param name="endian"></param>
|
|
|
|
|
/// <param name="data">must remain valid as long as the MemoryDomain exists!</param>
|
2014-08-16 05:45:31 +00:00
|
|
|
|
/// <param name="writable">if false, writes will be ignored</param>
|
2014-08-15 21:21:17 +00:00
|
|
|
|
/// <returns></returns>
|
2014-08-16 05:45:31 +00:00
|
|
|
|
public unsafe static MemoryDomain FromIntPtr(string name, int size, Endian endian, IntPtr data, bool writable = true)
|
2014-08-15 21:21:17 +00:00
|
|
|
|
{
|
|
|
|
|
if (data == IntPtr.Zero)
|
|
|
|
|
throw new ArgumentNullException("data");
|
|
|
|
|
if (size <= 0)
|
|
|
|
|
throw new ArgumentOutOfRangeException("size");
|
2014-09-08 14:50:36 +00:00
|
|
|
|
byte* p = (byte*)data;
|
2014-08-15 21:21:17 +00:00
|
|
|
|
return new MemoryDomain
|
|
|
|
|
(
|
|
|
|
|
name,
|
|
|
|
|
size,
|
|
|
|
|
endian,
|
|
|
|
|
delegate(int addr)
|
|
|
|
|
{
|
|
|
|
|
if (addr < 0 || addr >= size)
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
return p[addr];
|
|
|
|
|
},
|
|
|
|
|
delegate(int addr, byte val)
|
|
|
|
|
{
|
2014-08-16 05:45:31 +00:00
|
|
|
|
if (writable)
|
|
|
|
|
{
|
|
|
|
|
if (addr < 0 || addr >= size)
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
p[addr] = val;
|
|
|
|
|
}
|
2014-08-15 21:21:17 +00:00
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 14:50:36 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// create a memorydomain that references an unmanaged memory block with 16 bit swaps
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="name"></param>
|
|
|
|
|
/// <param name="size"></param>
|
|
|
|
|
/// <param name="endian"></param>
|
|
|
|
|
/// <param name="data">must remain valid as long as the MemoryDomain exists!</param>
|
|
|
|
|
/// <param name="writable">if false, writes will be ignored</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public unsafe static MemoryDomain FromIntPtrSwap16(string name, int size, Endian endian, IntPtr data, bool writable = true)
|
|
|
|
|
{
|
|
|
|
|
if (data == IntPtr.Zero)
|
|
|
|
|
throw new ArgumentNullException("data");
|
|
|
|
|
if (size <= 0)
|
|
|
|
|
throw new ArgumentOutOfRangeException("size");
|
|
|
|
|
byte* p = (byte*)data;
|
|
|
|
|
return new MemoryDomain
|
|
|
|
|
(
|
|
|
|
|
name,
|
|
|
|
|
size,
|
|
|
|
|
endian,
|
|
|
|
|
delegate(int addr)
|
|
|
|
|
{
|
|
|
|
|
if (addr < 0 || addr >= size)
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
return p[addr ^ 1];
|
|
|
|
|
},
|
|
|
|
|
delegate(int addr, byte val)
|
|
|
|
|
{
|
|
|
|
|
if (writable)
|
|
|
|
|
{
|
|
|
|
|
if (addr < 0 || addr >= size)
|
|
|
|
|
throw new ArgumentOutOfRangeException();
|
|
|
|
|
p[addr ^ 1] = val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 02:11:40 +00:00
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return Name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ushort PeekWord(int addr, bool bigEndian)
|
|
|
|
|
{
|
|
|
|
|
Endian endian = bigEndian ? Endian.Big : Endian.Little;
|
|
|
|
|
switch (endian)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case Endian.Big:
|
|
|
|
|
return (ushort)((PeekByte(addr) << 8) | (PeekByte(addr + 1)));
|
|
|
|
|
case Endian.Little:
|
|
|
|
|
return (ushort)((PeekByte(addr)) | (PeekByte(addr + 1) << 8));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public uint PeekDWord(int addr, bool bigEndian)
|
|
|
|
|
{
|
|
|
|
|
Endian endian = bigEndian ? Endian.Big : Endian.Little;
|
|
|
|
|
switch (endian)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case Endian.Big:
|
|
|
|
|
return (uint)((PeekByte(addr) << 24)
|
|
|
|
|
| (PeekByte(addr + 1) << 16)
|
|
|
|
|
| (PeekByte(addr + 2) << 8)
|
|
|
|
|
| (PeekByte(addr + 3) << 0));
|
|
|
|
|
case Endian.Little:
|
|
|
|
|
return (uint)((PeekByte(addr) << 0)
|
|
|
|
|
| (PeekByte(addr + 1) << 8)
|
|
|
|
|
| (PeekByte(addr + 2) << 16)
|
|
|
|
|
| (PeekByte(addr + 3) << 24));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PokeWord(int addr, ushort val, bool bigEndian)
|
|
|
|
|
{
|
|
|
|
|
Endian endian = bigEndian ? Endian.Big : Endian.Little;
|
|
|
|
|
switch (endian)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case Endian.Big:
|
|
|
|
|
PokeByte(addr + 0, (byte)(val >> 8));
|
|
|
|
|
PokeByte(addr + 1, (byte)(val));
|
|
|
|
|
break;
|
|
|
|
|
case Endian.Little:
|
|
|
|
|
PokeByte(addr + 0, (byte)(val));
|
|
|
|
|
PokeByte(addr + 1, (byte)(val >> 8));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void PokeDWord(int addr, uint val, bool bigEndian)
|
|
|
|
|
{
|
|
|
|
|
Endian endian = bigEndian ? Endian.Big : Endian.Little;
|
|
|
|
|
switch (endian)
|
|
|
|
|
{
|
|
|
|
|
default:
|
|
|
|
|
case Endian.Big:
|
|
|
|
|
PokeByte(addr + 0, (byte)(val >> 24));
|
|
|
|
|
PokeByte(addr + 1, (byte)(val >> 16));
|
|
|
|
|
PokeByte(addr + 2, (byte)(val >> 8));
|
|
|
|
|
PokeByte(addr + 3, (byte)(val));
|
|
|
|
|
break;
|
|
|
|
|
case Endian.Little:
|
|
|
|
|
PokeByte(addr + 0, (byte)(val));
|
|
|
|
|
PokeByte(addr + 1, (byte)(val >> 8));
|
|
|
|
|
PokeByte(addr + 2, (byte)(val >> 16));
|
|
|
|
|
PokeByte(addr + 3, (byte)(val >> 24));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-06 02:15:29 +00:00
|
|
|
|
public class MemoryDomainList : ReadOnlyCollection<MemoryDomain>
|
|
|
|
|
{
|
2013-11-15 01:52:03 +00:00
|
|
|
|
private readonly int _mainMemoryIndex;
|
2013-11-06 02:15:29 +00:00
|
|
|
|
|
|
|
|
|
public MemoryDomainList(IList<MemoryDomain> domains)
|
|
|
|
|
: base(domains)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MemoryDomainList(IList<MemoryDomain> domains, int mainMemoryIndex)
|
|
|
|
|
: this(domains)
|
|
|
|
|
{
|
|
|
|
|
_mainMemoryIndex = mainMemoryIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MemoryDomain this[string name]
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.FirstOrDefault(x => x.Name == name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MemoryDomain MainMemory
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this[_mainMemoryIndex];
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-13 14:21:08 +00:00
|
|
|
|
|
|
|
|
|
public bool HasSystemBus
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.Any(x => x.Name == "System Bus" || x.Name == "BUS"); // Have to account for "BUS" because some developers don't like consistency!
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MemoryDomain SystemBus
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return this.FirstOrDefault(x => x.Name == "System Bus" || x.Name == "BUS");
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-06 02:15:29 +00:00
|
|
|
|
}
|
2013-11-04 02:11:40 +00:00
|
|
|
|
}
|